This script for Autodesk Fusion rotates a body around a spiral track, repeatedly cutting it with a selected tool.
This was used successfully to create jaws for a scroll chuck. I didn't continue developing it once I achieved my goal.
| import math | |
| import traceback | |
| import adsk.core, adsk.fusion | |
| def spiral_cut(target, tool, pitch, stepAngle, revolutions): | |
| # Create an ObjectCollection for the tool bodies | |
| tool_collection = adsk.core.ObjectCollection.create() | |
| tool_collection.add(tool) | |
| # And for the target bodies | |
| target_collection = adsk.core.ObjectCollection.create() | |
| target_collection.add(target) | |
| i = 0 | |
| cum_angle = 0 | |
| while i < 100 and cum_angle <= 2 * math.pi * revolutions: | |
| i += 1 | |
| # Combine operation | |
| try: | |
| combineFeatures = target.parentComponent.features.combineFeatures | |
| combineInput = combineFeatures.createInput(target, tool_collection) | |
| combineInput.isKeepToolBodies = True | |
| combineInput.operation = adsk.fusion.FeatureOperations.CutFeatureOperation | |
| combineFeatures.add(combineInput) | |
| except RuntimeError as e: | |
| if 'Compute Failed' in str(e): | |
| pass # Ignore and continue | |
| else: | |
| raise | |
| # Rotate along the track | |
| # FIXME: Fails if our angle is 360 degrees | |
| rotation = adsk.core.Matrix3D.create() | |
| rotation.setToRotation( | |
| stepAngle, | |
| adsk.core.Vector3D.create(0, 0, 1), # around Z axis | |
| adsk.core.Point3D.create(0, 0, 0) # origin | |
| ) | |
| moveFeatures = target.parentComponent.features.moveFeatures | |
| moveInput = moveFeatures.createInput(target_collection, rotation) | |
| moveFeatures.add(moveInput) | |
| cum_angle += stepAngle | |
| # And translate | |
| translation = adsk.core.Matrix3D.create() | |
| translation.translation = adsk.core.Vector3D.create( | |
| pitch * stepAngle / (2*math.pi) * math.cos(cum_angle + math.pi/2), | |
| pitch * stepAngle / (2*math.pi) * math.sin(cum_angle + math.pi/2), | |
| 0 | |
| ) | |
| moveFeatures = target.parentComponent.features.moveFeatures | |
| moveInput = moveFeatures.createInput(target_collection, translation) | |
| moveFeatures.add(moveInput) | |
| def run(context): | |
| global app, ui | |
| app = adsk.core.Application.get() | |
| ui = app.userInterface | |
| try: | |
| # Create a command definition | |
| cmdDef = ui.commandDefinitions.itemById('spiralCutCmd') | |
| if not cmdDef: | |
| cmdDef = ui.commandDefinitions.addButtonDefinition( | |
| 'spiralCutCmd', | |
| 'Spiral Cut', | |
| 'Perform a spiral cut', | |
| '' | |
| ) | |
| # Add a command created event handler | |
| onCommandCreated = SpiralCutCreatedHandler() | |
| cmdDef.commandCreated.add(onCommandCreated) | |
| handlers.append(onCommandCreated) # prevent GC | |
| # Execute the command and keep script running while user interacts | |
| cmdDef.execute() | |
| adsk.autoTerminate(False) | |
| except: | |
| if ui: | |
| ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) | |
| class SpiralCutCreatedHandler(adsk.core.CommandCreatedEventHandler): | |
| def notify(self, args): | |
| global design | |
| try: | |
| eventArgs = adsk.core.CommandCreatedEventArgs.cast(args) | |
| cmd = eventArgs.command | |
| inputs = cmd.commandInputs | |
| # Verify that a Fusion design is active. | |
| design = adsk.fusion.Design.cast(app.activeProduct) | |
| if not design: | |
| ui.messageBox('The DESIGN workspace must be active when ' | |
| 'running this command.') | |
| return | |
| # Selection input for Jaw component | |
| targetInput = inputs.addSelectionInput( | |
| 'targetInput', | |
| 'Target', | |
| 'Select the target body to cut' | |
| ) | |
| targetInput.addSelectionFilter('SolidBodies') | |
| toolInput = inputs.addSelectionInput( | |
| 'toolInput', | |
| 'Tool', | |
| 'Select the cutting tool body' | |
| ) | |
| toolInput.addSelectionFilter('SolidBodies') | |
| pitch = '4 mm' | |
| pitchInput = inputs.addValueInput( | |
| 'pitchInput', | |
| 'Pitch', | |
| design.unitsManager.defaultLengthUnits, | |
| adsk.core.ValueInput.createByString(pitch) | |
| ) | |
| stepAngle = '60 deg' | |
| stepAngleInput = inputs.addValueInput( | |
| 'stepAngleInput', | |
| 'Step Angle', | |
| 'deg', | |
| adsk.core.ValueInput.createByString(stepAngle) | |
| ) | |
| revolutions = 7 | |
| revolutionsInput = inputs.addStringValueInput( | |
| 'revolutionsInput', | |
| 'Revolutions', | |
| str(revolutions) | |
| ) | |
| # Wire up additional event handlers | |
| onExecute = SpiralCutExecuteHandler() | |
| cmd.execute.add(onExecute) | |
| handlers.append(onExecute) | |
| onDestroy = SpiralCutDestroyHandler() | |
| cmd.destroy.add(onDestroy) | |
| handlers.append(onDestroy) | |
| except: | |
| if ui: | |
| ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) | |
| class SpiralCutExecuteHandler(adsk.core.CommandEventHandler): | |
| def notify(self, args): | |
| try: | |
| eventArgs = adsk.core.CommandEventArgs.cast(args) | |
| cmd = eventArgs.command | |
| inputs = cmd.commandInputs | |
| def findInputByName(name): | |
| for input in inputs: | |
| if input.id == name: | |
| return input | |
| return None | |
| target = findInputByName('targetInput').selection(0).entity | |
| tool = findInputByName('toolInput').selection(0).entity | |
| pitch = findInputByName('pitchInput').value | |
| stepAngle = findInputByName('stepAngleInput').value | |
| revolutions = int(findInputByName('revolutionsInput').value) | |
| firstIndex = design.timeline.markerPosition | |
| try: | |
| spiral_cut( | |
| target, | |
| tool, | |
| pitch, | |
| stepAngle, | |
| revolutions | |
| ) | |
| except: | |
| # Display the dialog, but continue to make timeline group | |
| if ui: | |
| ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) | |
| lastIndex = design.timeline.markerPosition - 1 | |
| ui.messageBox(f'{lastIndex - firstIndex + 1} timeline items were added') | |
| if lastIndex > firstIndex: | |
| timelineGroup = design.timeline.timelineGroups.add(\ | |
| firstIndex, lastIndex) | |
| timelineGroup.name = 'Spiral Cut' | |
| except: | |
| if ui: | |
| ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) | |
| class SpiralCutDestroyHandler(adsk.core.CommandEventHandler): | |
| def notify(self, args): | |
| try: | |
| adsk.terminate() | |
| except: | |
| if ui: | |
| ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) | |
| # List to keep handlers alive | |
| handlers = [] |