# Assuming you have not changed the general structure of the template no modification is needed in this file. # from . import commands from ast import arg from glob import glob from ...lib import fusion360utils as futil import threading import adsk.core import adsk.fusion import adsk.cam import traceback import json from ...lib import hid CMD_NAME = 'View Controller backend' app = adsk.core.Application.get() ui = app.userInterface myThread = None stopFlag = None cameraViewEvent = 'CameraViewEventId' updateConstantsEvent = "UpdateConstantsEventId" customEvents = [] l_handlers = [] X_ROTATION = 0 Y_ROTATION = 0 Z_ROTATION = 0 X_PAN = 0 Y_PAN = 0 Z_PAN = 0 class UpdateConstantsEventHandler(adsk.core.CustomEventHandler): def __init__(self): super().__init__() def notify(self, args: adsk.core.CustomEventArgs) -> None: futil.log(f'{CMD_NAME} UpdateConstants notity...') eventArgs = json.loads(args.additionalInfo) global X_ROTATION, Y_ROTATION, Z_ROTATION global X_PAN, Y_PAN, Z_PAN X_ROTATION = float(eventArgs["X_rotation"]) Y_ROTATION = float(eventArgs["Y_rotation"]) Z_ROTATION = float(eventArgs["Z_rotation"]) X_PAN = float(eventArgs["X_pan"]) Y_PAN = float(eventArgs["Y_pan"]) Z_PAN = float(eventArgs["Z_pan"]) myThread.start() # The event handler that responds to the custom event being fired. class CameraViewEventHandler(adsk.core.CustomEventHandler): def __init__(self): super().__init__() self.viewport = app.activeViewport def notify(self, args: adsk.core.CustomEventArgs): global app try: # Make sure a command isn't running before changes are made. activeCmd = ui.activeCommand if activeCmd != 'SelectCommand': ui.commandDefinitions.itemById('SelectCommand').execute() camera = self.viewport.camera eye = camera.eye target = camera.target up = camera.upVector zoom = camera.viewExtents front = eye.vectorTo(target) right = up.crossProduct(front) rotate_matrix = adsk.core.Matrix3D.create() rotate_matrix.setWithCoordinateSystem(target, right, front, up) mat = rotate_matrix.asArray() # Get the value from the JSON data passed through the event. eventArgs: dict = json.loads(args.additionalInfo) X_radians = float(eventArgs.get('X_rotation', 0)) * X_ROTATION if X_radians: rotate_matrix.setToRotation(X_radians, front, target) eye.transformBy(rotate_matrix) up.transformBy(rotate_matrix) Y_radians = float(eventArgs.get('Y_rotation', 0)) * Y_ROTATION if Y_radians: rotate_matrix.setToRotation(Y_radians, right, target) eye.transformBy(rotate_matrix) up.transformBy(rotate_matrix) Z_radians = float(eventArgs.get('Z_rotation', 0)) * Z_ROTATION if Z_radians: rotate_matrix.setToRotation(Z_radians, up, target) eye.transformBy(rotate_matrix) up.transformBy(rotate_matrix) X_pan = float(eventArgs.get('X_pan', 0)) * X_PAN Y_pan = float(eventArgs.get('Y_pan', 0)) * Y_PAN Z_pan = float(eventArgs.get('Z_pan', 0)) * Z_PAN if any([X_pan, Z_pan]): move_vector = adsk.core.Vector3D.create(X_pan, 0, Z_pan) move_vector.transformBy(rotate_matrix) eye.translateBy(move_vector) target.translateBy(move_vector) if Y_pan: zoom = zoom + Y_pan * 10 camera.isSmoothTransition = False camera.eye = eye camera.upVector = up camera.target = target camera.viewExtents = zoom app.activeViewport.camera = camera app.activeViewport.refresh() except: if ui: ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) def rotate(self, X_radians: float, Y_radians: float, Z_radians: float): pass # The class for the new thread. class ControllerThread(threading.Thread): def __init__(self, event): threading.Thread.__init__(self) self.stopped = event def run(self): futil.log(f'{CMD_NAME} ControllerThread started') gamepad = hid.Device(vid=1356, pid=1476) gamepad.nonblocking = True # Every five seconds fire a custom event, passing a random number. while not self.stopped.wait(0.01): # futil.log(f'{CMD_NAME} Thread start sending data') report = list(gamepad.read(128)) args = { 'X_rotation': 0.0, 'Y_rotation': self.convert(report[4], 10), 'Z_rotation': self.convert(report[3], 10), 'X_pan': self.convert(report[1], 10), 'Y_pan': self.convert(report[2], 10), 'Z_pan': 0.0 } app.fireCustomEvent(cameraViewEvent, json.dumps(args)) def convert(self, input, dedzone) -> float: normal: float = input - 127 if abs(normal) < dedzone: return 0.0 normal = normal / 128 return normal def start(): futil.log(f'{CMD_NAME} Start...') # Register the custom event and connect the handler. global customEvents customEvents.append(app.registerCustomEvent(cameraViewEvent)) onCameraViewEvent = CameraViewEventHandler() customEvents[0].add(onCameraViewEvent) l_handlers.append(onCameraViewEvent) futil.log(f'{CMD_NAME} CameraViewEvent register') customEvents.append(app.registerCustomEvent(updateConstantsEvent)) onUpadateConstantEvent = UpdateConstantsEventHandler() customEvents[1].add(onUpadateConstantEvent) l_handlers.append(onUpadateConstantEvent) futil.log(f'{CMD_NAME} UpdateConstantsEvent register') # Create a new thread for the other processing. global stopFlag, myThread stopFlag = threading.Event() myThread = ControllerThread(stopFlag) # myThread.start() def stop(): try: for event, handler in zip(customEvents, l_handlers): event.remove(handler) # if local_handlers.count: # customEvent.remove(local_handlers[0]) stopFlag.set() app.unregisterCustomEvent(cameraViewEvent) app.unregisterCustomEvent(updateConstantsEvent) ui.messageBox('Stop addin') except: futil.handle_error('stop') # if ui: # ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))