Cycloidal Drive Script

This script works with Fusion 360, tested 04/16/2024. However, if it's good, I don't know!


import adsk.core, adsk.fusion, adsk.cam, traceback
import math

def createCycloidalDrive(centerPoint, numTeeth, baseCircleRadius, eccentricity):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        design = adsk.fusion.Design.cast(app.activeProduct)
        rootComp = design.rootComponent
        
        # Convert input parameters
        num_teeth = numTeeth
        eccentricity = eccentricity
        base_circle_radius = baseCircleRadius
        rolling_circle_radius = base_circle_radius / num_teeth
        
        # Calculate cycloidal curve points
        points = adsk.core.ObjectCollection.create()
        for i in range(361):  # 0 to 360 degrees in steps of 1 degree
            angle_rad = math.radians(i)
            # Parametric equations for cycloidal curve
            x = (base_circle_radius + rolling_circle_radius) * math.cos(angle_rad) - eccentricity * math.cos(((base_circle_radius + rolling_circle_radius) / rolling_circle_radius) * angle_rad) + centerPoint.worldGeometry.x
            y = (base_circle_radius + rolling_circle_radius) * math.sin(angle_rad) - eccentricity * math.sin(((base_circle_radius + rolling_circle_radius) / rolling_circle_radius) * angle_rad) + centerPoint.worldGeometry.y
            point = adsk.core.Point3D.create(x, y, centerPoint.worldGeometry.z)  # Use the Z coordinate of the center point
            points.add(point)
        
        # Create the cycloidal curve in the active sketch
        activeComponent = design.activeComponent
        activeSketch = None
        for sketch in activeComponent.sketches:
            if sketch.isVisible:
                activeSketch = sketch
                break
        if activeSketch:
            activeSketch.sketchCurves.sketchFittedSplines.add(points)
        else:
            ui.messageBox('Active sketch not found. Please ensure you are editing a sketch.', 'No Active Sketch')
        
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()), 'Error')

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        
        # Create a command definition
        # cmdDef = ui.commandDefinitions.addButtonDefinition('createCycloidalDriveCmd', 'Create Cycloidal Drive', 'Create a cycloidal drive with specified parameters')
        # Check if the command definition already exists
        solidTab = ui.allToolbarTabs.itemById('SketchTab')
        createPanel = solidTab.toolbarPanels.itemById('SketchCreatePanel')  # Example panel ID
        cmdDef = ui.commandDefinitions.itemById('createCycloidalDriveCmd')
        if not cmdDef:
            cmdDef = ui.commandDefinitions.addButtonDefinition('createCycloidalDriveCmd', 'Create Cycloidal Drive', 'Create a cycloidal drive with specified parameters')
        createPanel.controls.addCommand(cmdDef)
        
        # Add an event handler for the command created event
        onCommandCreated = CommandCreatedEventHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        handlers.append(onCommandCreated)  # Keep a reference to prevent garbage collection

        cmdDef.execute()

        adsk.autoTerminate(False)

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

class CommandCreatedEventHandler(adsk.core.CommandCreatedEventHandler):
    def notify(self, args):
        eventArgs = adsk.core.CommandCreatedEventArgs.cast(args)
        cmd = eventArgs.command
        inputs = cmd.commandInputs
        
        # Add inputs for parameters
        # inputs.addValueInput('numTeeth', 'Number of Teeth', 'cm', adsk.core.ValueInput.createByReal(10))
        inputs.addIntegerSpinnerCommandInput('numTeeth', 'Number of Teeth', 1, 100, 1, 10)
        inputs.addValueInput('baseCircleRadius', 'Base Circle Radius', 'cm', adsk.core.ValueInput.createByReal(10.0))
        inputs.addValueInput('eccentricity', 'Eccentricity', 'cm', adsk.core.ValueInput.createByReal(5.0))
        
        # Add selection input for the center point
        selectionInput = inputs.addSelectionInput('centerPoint', 'Center Point', 'Select the center point')
        selectionInput.setSelectionLimits(1, 1)  # Limit to a single selection
        selectionInput.addSelectionFilter('SketchPoints')  # Limit selection to sketch points
        
        # Add an event handler for when the command is executed
        onExecute = CommandExecuteHandler()
        cmd.execute.add(onExecute)
        handlers.append(onExecute)  # Keep a reference to prevent garbage collection

class CommandExecuteHandler(adsk.core.CommandEventHandler):
    def notify(self, args):
        eventArgs = adsk.core.CommandEventArgs.cast(args)
        inputs = eventArgs.command.commandInputs
        numTeeth = inputs.itemById('numTeeth').value
        baseCircleRadius = inputs.itemById('baseCircleRadius').value
        eccentricity = inputs.itemById('eccentricity').value
        centerPoint = inputs.itemById('centerPoint').selection(0).entity
        createCycloidalDrive(centerPoint, numTeeth, baseCircleRadius, eccentricity)

handlers = []  # Global list to keep handlers alive