Hello, thank you very much for your response. I was initially confused about whether to use the assigned fusion object or the real value because of what was provided in the documentation. I have done the edit you suggested but the error is unfortunately still present. My code is a bit long, with other issues I wanted to deal with later but here it is:
#Author-
#Description-
import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__)))
from math import *
import adsk.core, adsk.fusion, adsk.cam, traceback, sympy
_ui = adsk.core.UserInterface.cast(None)
handlers = []
t = sympy.var('t')
handlers_dict = {
'x(t)' : sympy.lambdify(t, "t", "math"),
'y(t)' : sympy.lambdify(t, "t", "math"),
'z(t)' : sympy.lambdify(t, "t", "math"),
'tmin' : 0,
'tmax' : 1,
'unit' : 'Centimeter',
'precision' : 1,
'circle radius' : 7,
't position' : 0,
'precision position' : 0,
}
class MyCommandInputChangedHandler(adsk.core.InputChangedEventHandler):
def __init__(self):
super().__init__()
def notify(self, args):
try:
eventArgs = adsk.core.InputChangedEventArgs.cast(args)
#inputs = eventArgs.inputs
cmdInput = eventArgs.input
# onInputChange for slider controller
#tableInput = inputs.itemById('table')
if cmdInput.id == 'x_t':
handlers_dict['x(t)'] = sympy.sympify(str(cmdInput.value).replace('^', '**'))
elif cmdInput.id == 'y_t':
handlers_dict['y(t)'] = sympy.sympify(str(cmdInput.value).replace('^', '**'))
elif cmdInput.id == 'z_t':
handlers_dict['z(t)'] = sympy.sympify(str(cmdInput.value).replace('^', '**'))
elif cmdInput.id == 'tmin':
handlers_dict['tmin'] = sympy.lambdify(t, str(cmdInput.value), "math")(1)
elif cmdInput.id == 'tmax':
handlers_dict['tmax'] = sympy.lambdify(t, str(cmdInput.value), "math")(1)
elif cmdInput.id == 'unit':
handlers_dict['unit'] = cmdInput.value
elif cmdInput.id == 'precision':
handlers_dict['precision'] = float(cmdInput.value)
elif cmdInput.id == 'circle_radius':
handlers_dict['circle radius'] = float(cmdInput.value)
elif cmdInput.id == 't_position':
handlers_dict['t position'] = float(cmdInput.value)
elif cmdInput.id == 'precision_position':
handlers_dict['precision position'] = float(cmdInput.value)
except:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
def __init__(self):
super().__init__()
def notify(self, args):
try:
# When the command is done, terminate the script
# This will release all globals which will remove all event handlers
adsk.terminate()
except:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
class MyCommandExecuteHandler(adsk.core.CommandEventHandler):
unit_dict = {
'Centimeter' : 1,
'Millimeter' : 0.1,
'Meter' : 10,
'Inch' : 2.54,
'Foot' : 30.48,
}
def __init__(self):
super().__init__()
def notify(self, args):
try:
eventArgs = adsk.core.CommandEventArgs.cast(args)
inputs = eventArgs.command.commandInputs
self.x, self.y, self.z = sympy.var('x y z')
self.x_t, self.y_t, self.z_t, self.tmin, self.tmax, self.unit, \
self.precision, self.circle_radius, self.t_position, self.precision_position = \
handlers_dict['x(t)'], handlers_dict['y(t)'], handlers_dict['z(t)'], \
handlers_dict['tmin'], handlers_dict['tmax'], handlers_dict['unit'], \
handlers_dict['precision'], handlers_dict['circle radius'], \
handlers_dict['t position'], handlers_dict['precision position']
self.convert = lambda x: x * self.unit_dict[self.unit]#the default units used are cm
app = adsk.core.Application.get()
self.design = app.activeProduct
self.points = adsk.core.ObjectCollection.create()
components = self.design.rootComponent.occurrences
self.component = components.addNewComponent(adsk.core.Matrix3D.create()).component
self.sketch = self.component.sketches.add(self.component.xYConstructionPlane)
self.draw()
except:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def draw_circle(self):
r_p = (
self.x_t.diff(t),
self.y_t.diff(t),
self.z_t.diff(t),
)
idx = int(self.t_position - self.tmin + self.precision_position)
position = adsk.core.Point3D.create(
self.convert(float(self.x_t.subs(t, self.indexes[idx]))),
self.convert(float(self.y_t.subs(t, self.indexes[idx]))),
self.convert(float(self.z_t.subs(t, self.indexes[idx])))
)
my_vect = [ float(r_p[i].subs(t, self.indexes[idx])) for i in range(3) ]
normal = adsk.core.Vector3D.create(my_vect[0], my_vect[1], my_vect[2])
normal_plane = adsk.core.Plane.create(position, normal)
planes = self.design.rootComponent.constructionPlanes
planeInput = planes.createInput()
planeInput.setByPlane(normal_plane)
error_occ = False
while True:
try:
construction_plane = planes.add(planeInput)
except RuntimeError as ex:
error_occ = True
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
message = template.format(type(ex).__name__, ex.args)
choice = _ui.messageBox(
message + str('Make sure to select "Do not capture Design History"'),
"Expected Error" ,
3
)
if choice == 2:
self.design.designType = 0 #could set to 1 to get back to parametric modelingt
_ui.messageBox("Design type turned to Direct Design.")
else:
if error_occ:
self.design.designType = 1 #reactivating it
_ui.messageBox("Design type turned back to parametric modeling.")
break
circle_sketch = self.component.sketches.add(construction_plane)
sketchCircles = circle_sketch.sketchCurves.sketchCircles
centerPoint = self.points.asArray()[idx]
circle = sketchCircles.addByCenterRadius(position, self.circle_radius)
prof = self.component.createOpenProfile(circle, False)
return prof
def sweep(self, prof):
path = self.design.rootComponent.features.createPath(self.spline)
sweeps = self.design.rootComponent.features.sweepFeatures
sweepInput = sweeps.createInput(prof, path, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)#3 for new body 1 to cut
sweepInput.isSolid = False
sweeps.add(sweepInput)
def draw_curve(self):
try:
self.linspace(
self.tmin,
self.tmax,
int((self.tmax - self.tmin)*self.precision),
)
for index in self.indexes:
self.points.add(
adsk.core.Point3D.create(
self.convert(float(self.x_t.subs(t, index))),
self.convert(float(self.y_t.subs(t, index))),
self.convert(float(self.z_t.subs(t, index)))
)
)
self.spline = self.sketch.sketchCurves.sketchFittedSplines.add(self.points)
except:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def linspace(self, start, end, n):
self.indexes = [start+(end-start)/n*i for i in range(n+1)]
def draw(self):
try:
self.draw_curve()
prof = self.draw_circle()
self.sweep(prof)
except:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
class MyCommandValidateInputsHandler(adsk.core.ValidateInputsEventHandler):
def __init__(self):
super().__init__()
def notify(self, args):
pass
class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
def __init__(self):
super().__init__()
def notify(self, args):
try:
# Get the command that was created.
cmd = adsk.core.Command.cast(args.command)
# Get the CommandInputs collection associated with the command.
inputs = cmd.commandInputs
# Create a tab input.
tabCmdInput1 = inputs.addTabCommandInput('tab_1', '3D parametric curve')
tab1ChildInputs = tabCmdInput1.children
# Create group input.
EqCmdGroup = tab1ChildInputs.addGroupCommandInput('eqs', 'x,y,z equations')
EqCmdGroup.isExpanded = True
EqCmdGroup.isEnabledCheckBoxDisplayed = False
EqCmdGroupChildren = EqCmdGroup.children
# Create a message that spans the entire width of the dialog by leaving out the "name" argument.
message = '<div align="center">A "full width" message using <a href="http:fusion360.autodesk.com">html.</a></div>'
EqCmdGroupChildren.addTextBoxCommandInput('fullWidth_textBox', '', message, 1, True)
# Create a string value input.
EqCmdGroupChildren.addStringValueInput('x_t', 'x(t)', 'Enter your x(t) equation here'),
EqCmdGroupChildren.addStringValueInput('y_t', 'y(t)', 'Enter your y(t) equation here'),
EqCmdGroupChildren.addStringValueInput('z_t', 'z(t)', 'Enter your z(t) equation here')
InterCmdGroup = tab1ChildInputs.addGroupCommandInput('t_inter', 'Range for t')
InterCmdGroup.isExpanded = True
InterCmdGroup.isEnabledCheckBoxDisplayed = False
InterCmdGroupChildren = InterCmdGroup.children
InterCmdGroupChildren.addStringValueInput('tmin', 'tmin', 'Enter the expression for tmin'),
InterCmdGroupChildren.addStringValueInput('tmax', 'tmax', 'Enter the expression for tmax')
ScaleCmdGroup = tab1ChildInputs.addGroupCommandInput('scale', 'Units and precision')
ScaleCmdGroup.isExpanded = True
ScaleCmdGroup.isEnabledCheckBoxDisplayed = False
ScaleCmdGroupChildren = ScaleCmdGroup.children
unit = ScaleCmdGroupChildren.addDropDownCommandInput('unit', 'unit', adsk.core.DropDownStyles.LabeledIconDropDownStyle)
unitItems = unit.listItems
#Centimeter, Millimiter, Meter, Inch, Foot
unitItems.add('Centimeter', True)
unitItems.add('Millimiter', False)
unitItems.add('Meter', False)
unitItems.add('Inch', False)
unitItems.add('Foot', False)
ScaleCmdGroupChildren.addStringValueInput('precision', 'precision', 'Enter the number of points per step in t'),
SweepCmdGroup = tab1ChildInputs.addGroupCommandInput('Sweep', 'Sweep parameters')
SweepCmdGroup.isExpanded = True
SweepCmdGroup.isEnabledCheckBoxDisplayed = False
SweepCmdGroupChildren = SweepCmdGroup.children
SweepCmdGroupChildren.addStringValueInput('circle_radius', 'Circle radius', 'Enter the radius of the circle used for the sweep')
SweepCmdGroupChildren.addStringValueInput('t_position', 't-position', 'Value of t at which we start the sweep')
SweepCmdGroupChildren.addStringValueInput('precision-position', 'Segmtent position', f'On which segment of t does the circle lie (between 0 and {handlers_dict["precision"]-1})')
# Connect to the command destroyed event.
onDestroy = MyCommandDestroyHandler()
cmd.destroy.add(onDestroy)
handlers.append(onDestroy)
# Connect to the input changed event.
onInputChanged = MyCommandInputChangedHandler()
cmd.inputChanged.add(onInputChanged)
handlers.append(onInputChanged)
# Connect to the execute event.
onExecute = MyCommandExecuteHandler()
cmd.execute.add(onExecute)
handlers.append(onExecute)
# Connect to the input validation.
onValidateInputs = MyCommandValidateInputsHandler()
cmd.validateInputs.add(onValidateInputs)
handlers.append(onValidateInputs)
except:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def run(context):
try:
app = adsk.core.Application.get()
global _ui
_ui = app.userInterface
"""
if not design:
ui.messageBox('No active Fusion design', 'No Design')
return
"""
cmdDef = _ui.commandDefinitions.itemById('cmdInputsParametricCurve')
if not cmdDef:
cmdDef = _ui.commandDefinitions.addButtonDefinition('cmdInputsParametricCurve', 'Parametric Curve', 'Command innputs for our parametrization script')
onCommandCreated = MyCommandCreatedHandler()
cmdDef.commandCreated.add(onCommandCreated)
handlers.append(onCommandCreated)
cmdDef.execute()
adsk.autoTerminate(False)
except:
if _ui:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
I am using the simply python module which has a dependency on mpmath (I put a copy of the two modules in my script folder, hence the line with sys.path.append(...).