Skip to content

Instantly share code, notes, and snippets.

@rgov
Created September 21, 2024 23:04
Show Gist options
  • Save rgov/28c444f5dd911f28bb82c5f0eca0ee7f to your computer and use it in GitHub Desktop.
Save rgov/28c444f5dd911f28bb82c5f0eca0ee7f to your computer and use it in GitHub Desktop.
Autodesk Fusion spiral cut script

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 = []
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment