I couldn't attach the script, here it is in-line. I have it in a file named: SweepByReference.py.
# -*- coding: utf-8 -*-
"""
Created on Tue Aug 28 07:56:27 2018
@author: metd01567
"""
# TODO: refactor: exception handling needs work, e.g. clean up stray temp body after failure, or avoid creating it until all reasonable validation is done
# consider adding a validate input method, and move as much validation code into it, and consider if other checks are needed
import adsk.core, adsk.fusion, traceback
# Global set of event handlers to keep them referenced for the duration of the command
_handlers = []
# Event handler that reacts to when the command is destroyed. This terminates the script.
class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
def __init__(self):
super().__init__()
def notify(self, args):
try:
app = adsk.core.Application.get()
ui = app.userInterface
# When the command is done, terminate the script
# This will release all globals which will remove all event handlers
adsk.terminate()
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
# Event handler that reacts when the command definition is executed which
# results in the command being created and this event being fired.
class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
def __init__(self):
super().__init__()
def notify(self, args):
try:
# check workspace, must be in Model
app = adsk.core.Application.get()
ui = app.userInterface
product = app.activeProduct
design = adsk.fusion.Design.cast(product)
if not design:
ui.messageBox('This script is not supported in current workspace, please change to MODEL workspace and try again.')
return False
# Get the command that was created.
cmd = adsk.core.Command.cast(args.command)
# Connect to the command destroyed event.
onDestroy = MyCommandDestroyHandler()
cmd.destroy.add(onDestroy)
_handlers.append(onDestroy)
onExecute = MyExecuteHandler()
cmd.execute.add(onExecute)
_handlers.append(onExecute)
# Get the CommandInputs collection associated with the command.
inputs = cmd.commandInputs
childInputs = inputs
###################################
# add controls
# Create a selection input for sketches
sketchSelectionInput = childInputs.addSelectionInput('sketchSelection', 'Profile Sketch', 'Sketch should have a single profile that intersects x,y: 0,0, sketch origin will be swept along the selected path')
sketchSelectionInput.setSelectionLimits(1, 1)
sketchSelectionInput.addSelectionFilter("Sketches")
# Create a selection input for paths
pathSelectionInput = childInputs.addSelectionInput('pathSelection', 'Paths', 'select any segment of the path, the complete chain will be used')
pathSelectionInput.setSelectionLimits(1)
pathSelectionInput.addSelectionFilter("SketchCurves")
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def run(context):
try:
app = adsk.core.Application.get()
ui = app.userInterface
# Get the existing command definition or create it if it doesn't already exist.
cmdDef = ui.commandDefinitions.itemById('sweepByReference')
if not cmdDef:
cmdDef = ui.commandDefinitions.addButtonDefinition('sweepByReference', 'Sweep By Reference', 'Command to sweep a profile from a reference sketch.')
# Connect to the command created event.
onCommandCreated = MyCommandCreatedHandler()
cmdDef.commandCreated.add(onCommandCreated)
_handlers.append(onCommandCreated)
# Execute the command definition.
cmdDef.execute()
# Prevent this module from being terminated when the script returns, because we are waiting for event handlers to fire.
adsk.autoTerminate(False)
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
# Event handler that reacts to any changes the user makes to any of the command inputs.
class MyExecuteHandler(adsk.core.CommandEventHandler):
def __init__(self):
super().__init__()
def notify(self, args):
try:
app = adsk.core.Application.get()
ui = app.userInterface
doSweeps(args)
adsk.terminate()
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
# get the sketch input, and iterate through the selected paths
def doSweeps(args):
# TODO: refactor, failure of the existence checks would be a software error, and logged vs prompting user
app = adsk.core.Application.get()
ui = app.userInterface
eventArgs = adsk.core.CommandEventArgs.cast(args)
inputs = eventArgs.command.commandInputs
###############################################
# look for the sketch selection input, and verify that it is a sketch
sketchSelectionInput = inputs.itemById('sketchSelection')
if sketchSelectionInput == None:
ui.messageBox('software error, the sketch selection was lost')
return
selectedSketchEntity = sketchSelectionInput.selection(0).entity
selectedSketch = adsk.fusion.Sketch.cast(selectedSketchEntity)
if selectedSketch == None:
ui.messageBox('software error: sketch selection could not be used')
return
###############################################
# collect path selections
pathSelectionInput = inputs.itemById('pathSelection')
if pathSelectionInput == None:
ui.messageBox('software error, the path selection was lost')
return
###############################################
# sweep each path
# TODO: find out why direct iteration of the selection input fails.
# The sketch went invisible after first path was swept, and then the second index was not there
# Even after disabling "Auto hide sketch on feature creation" in preferences->General->Design
#
# If it turns out selection inputs do sometimes change, maybe pulling the entities first is good practice.
paths = adsk.core.ObjectCollection.create()
for thisEntity in range(pathSelectionInput.selectionCount):
paths.add(pathSelectionInput.selection(thisEntity).entity)
for thisPath in paths:
selectedPath = adsk.fusion.SketchCurve.cast(thisPath)
if selectedPath == None:
ui.messageBox('software error: path selection could not be used')
return
doSweep(selectedSketch, selectedPath)
app.activeViewport.refresh()
# TODO: refactor, function is too big
def doSweep(selectedSketch, selectedPath):
###############################################
# set up infrastructure
###############################################
app = adsk.core.Application.get()
ui = app.userInterface
product = app.activeProduct
design = adsk.fusion.Design.cast(product)
comp = design.rootComponent
###############################################
# derive usable entities and parameters, and verify inputs meet requirements
###############################################
###############################################
# process path
# convert the path selection object to a usable path - chained curve will follow connected segments
feats = comp.features
chainedOption = adsk.fusion.ChainedCurveOptions.connectedChainedCurves
if adsk.fusion.BRepEdge.cast(selectedPath):
chainedOption = adsk.fusion.ChainedCurveOptions.tangentChainedCurves
path = adsk.fusion.Path.create(selectedPath, chainedOption)
path = feats.createPath(selectedPath)
###############################################
# process the "reference" sketch containing a single profile
if selectedSketch.profiles.count != 1:
ui.messageBox('reference sketch must have a single profile, ' + str(selectedSketch.profiles.count) + ' were found\n\noperation not complete')
return
# grab the profile
refProfile = selectedSketch.profiles[0]
###############################################
# create a thin extrusion and make it normal to the sweep path
###############################################
# extrude a thin body from the reference profile. thickness is arbitrary, but keep it well above the model's resolution
thickness = adsk.core.ValueInput.createByReal(0.1)
extrude = comp.features.extrudeFeatures.addSimple(refProfile, thickness, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
# name the body for user convenience
tempBody = extrude.bodies.item(0)
if tempBody == None:
ui.messageBox('temp extrude failed\n\noperation not complete')
return
tempBody.name = "generatedSweepProfile"
###############################################
# derive the move transform parameters:
###############################################
# translate from the reference sketch's control point (which must be at 0,0) to the start point of the path
firstEntity = path.item(0)
(returnValue, startPoint, endPoint) = firstEntity.curve.evaluator.getEndPoints()
if not returnValue:
ui.messageBox('could not fetch start point of path')
return
moveVector = adsk.core.Vector3D.create(startPoint.x, startPoint.y, 0)
# TODO: fixme, User's concept of path orientation isn't known. additional input required.
# rotate to the tangent at the path start point
(returnValue, pathTangent) = firstEntity.curve.evaluator.getTangent(0)
if not returnValue:
ui.messageBox('could not fetch path tangent')
return
# create the move transform
transform = adsk.core.Matrix3D.create()
transform.setToRotateTo(selectedSketch.referencePlane.geometry.normal, pathTangent)
transform.translation = moveVector
# move the temp body
bodies = adsk.core.ObjectCollection.create()
bodies.add(tempBody)
moveInput = comp.features.moveFeatures.createInput(bodies, transform)
comp.features.moveFeatures.add(moveInput)
###############################################
# now perform the sweep
###############################################
# extract the start face
sweepFace = extrude.startFaces.item(0)
if sweepFace == None:
ui.messageBox('extrude has no faces\n\noperation not complete')
return
# Set up the sweep operation
sweepFeats = feats.sweepFeatures
sweepInput = sweepFeats.createInput(sweepFace, path, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
sweepInput.orientation = adsk.fusion.SweepOrientationTypes.PerpendicularOrientationType
sweepFeats.add(sweepInput)