Fusion360-Addons/python/Samples/Bolt/Bolt.py
2021-10-31 19:12:13 +01:00

339 lines
14 KiB
Python

#Author-Autodesk Inc.
#Description-Create bolt
import adsk.core, adsk.fusion, traceback
import math
defaultBoltName = 'Bolt'
defaultHeadDiameter = 0.75
defaultBodyDiameter = 0.5
defaultHeadHeight = 0.3125
defaultBodyLength = 2.0
defaultCutAngle = 30.0 * (math.pi / 180)
defaultChamferDistance = 0.03845
defaultFilletRadius = 0.02994
# global set of event handlers to keep them referenced for the duration of the command
handlers = []
app = adsk.core.Application.get()
if app:
ui = app.userInterface
newComp = None
def createNewComponent():
# Get the active design.
product = app.activeProduct
design = adsk.fusion.Design.cast(product)
rootComp = design.rootComponent
allOccs = rootComp.occurrences
newOcc = allOccs.addNewComponent(adsk.core.Matrix3D.create())
return newOcc.component
class BoltCommandExecuteHandler(adsk.core.CommandEventHandler):
def __init__(self):
super().__init__()
def notify(self, args):
try:
unitsMgr = app.activeProduct.unitsManager
command = args.firingEvent.sender
inputs = command.commandInputs
bolt = Bolt()
for input in inputs:
if input.id == 'boltName':
bolt.boltName = input.value
elif input.id == 'headDiameter':
bolt.headDiameter = unitsMgr.evaluateExpression(input.expression, "cm")
elif input.id == 'bodyDiameter':
bolt.bodyDiameter = unitsMgr.evaluateExpression(input.expression, "cm")
elif input.id == 'headHeight':
bolt.headHeight = unitsMgr.evaluateExpression(input.expression, "cm")
elif input.id == 'bodyLength':
bolt.bodyLength = adsk.core.ValueInput.createByString(input.expression)
elif input.id == 'cutAngle':
bolt.cutAngle = unitsMgr.evaluateExpression(input.expression, "deg")
elif input.id == 'chamferDistance':
bolt.chamferDistance = adsk.core.ValueInput.createByString(input.expression)
elif input.id == 'filletRadius':
bolt.filletRadius = adsk.core.ValueInput.createByString(input.expression)
bolt.buildBolt();
args.isValidResult = True
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
class BoltCommandDestroyHandler(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:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
class BoltCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
def __init__(self):
super().__init__()
def notify(self, args):
try:
cmd = args.command
cmd.isRepeatable = False
onExecute = BoltCommandExecuteHandler()
cmd.execute.add(onExecute)
onExecutePreview = BoltCommandExecuteHandler()
cmd.executePreview.add(onExecutePreview)
onDestroy = BoltCommandDestroyHandler()
cmd.destroy.add(onDestroy)
# keep the handler referenced beyond this function
handlers.append(onExecute)
handlers.append(onExecutePreview)
handlers.append(onDestroy)
#define the inputs
inputs = cmd.commandInputs
inputs.addStringValueInput('boltName', 'Bolt Name', defaultBoltName)
initHeadDiameter = adsk.core.ValueInput.createByReal(defaultHeadDiameter)
inputs.addValueInput('headDiameter', 'Head Diameter','cm',initHeadDiameter)
initBodyDiameter = adsk.core.ValueInput.createByReal(defaultBodyDiameter)
inputs.addValueInput('bodyDiameter', 'Body Diameter', 'cm', initBodyDiameter)
initHeadHeight = adsk.core.ValueInput.createByReal(defaultHeadHeight)
inputs.addValueInput('headHeight', 'Head Height', 'cm', initHeadHeight)
initBodyLength = adsk.core.ValueInput.createByReal(defaultBodyLength)
inputs.addValueInput('bodyLength', 'Body Length', 'cm', initBodyLength)
#to do the thread length
initCutAngle = adsk.core.ValueInput.createByReal(defaultCutAngle)
inputs.addValueInput('cutAngle', 'Cut Angle', 'deg', initCutAngle)
initChamferDistance = adsk.core.ValueInput.createByReal(defaultChamferDistance)
inputs.addValueInput('chamferDistance', 'Chamfer Distance', 'cm', initChamferDistance)
initFilletRadius = adsk.core.ValueInput.createByReal(defaultFilletRadius)
inputs.addValueInput('filletRadius', 'Fillet Radius', 'cm', initFilletRadius)
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
class Bolt:
def __init__(self):
self._boltName = defaultBoltName
self._headDiameter = defaultHeadDiameter
self._bodyDiameter = defaultBodyDiameter
self._headHeight = defaultHeadHeight
self._bodyLength = adsk.core.ValueInput.createByReal(defaultBodyLength)
self._cutAngle = defaultCutAngle
self._chamferDistance = adsk.core.ValueInput.createByReal(defaultChamferDistance)
self._filletRadius = adsk.core.ValueInput.createByReal(defaultFilletRadius)
#properties
@property
def boltName(self):
return self._boltName
@boltName.setter
def boltName(self, value):
self._boltName = value
@property
def headDiameter(self):
return self._headDiameter
@headDiameter.setter
def headDiameter(self, value):
self._headDiameter = value
@property
def bodyDiameter(self):
return self._bodyDiameter
@bodyDiameter.setter
def bodyDiameter(self, value):
self._bodyDiameter = value
@property
def headHeight(self):
return self._headHeight
@headHeight.setter
def headHeight(self, value):
self._headHeight = value
@property
def bodyLength(self):
return self._bodyLength
@bodyLength.setter
def bodyLength(self, value):
self._bodyLength = value
@property
def cutAngle(self):
return self._cutAngle
@cutAngle.setter
def cutAngle(self, value):
self._cutAngle = value
@property
def chamferDistance(self):
return self._chamferDistance
@chamferDistance.setter
def chamferDistance(self, value):
self._chamferDistance = value
@property
def filletRadius(self):
return self._filletRadius
@filletRadius.setter
def filletRadius(self, value):
self._filletRadius = value
def buildBolt(self):
global newComp
newComp = createNewComponent()
if newComp is None:
ui.messageBox('New component failed to create', 'New Component Failed')
return
# Create a new sketch.
sketches = newComp.sketches
xyPlane = newComp.xYConstructionPlane
xzPlane = newComp.xZConstructionPlane
sketch = sketches.add(xyPlane)
center = adsk.core.Point3D.create(0, 0, 0)
vertices = []
for i in range(0, 6):
vertex = adsk.core.Point3D.create(center.x + (self.headDiameter/2) * math.cos(math.pi * i / 3), center.y + (self.headDiameter/2) * math.sin(math.pi * i / 3),0)
vertices.append(vertex)
for i in range(0, 6):
sketch.sketchCurves.sketchLines.addByTwoPoints(vertices[(i+1) %6], vertices[i])
extrudes = newComp.features.extrudeFeatures
prof = sketch.profiles[0]
extInput = extrudes.createInput(prof, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
distance = adsk.core.ValueInput.createByReal(self.headHeight)
extInput.setDistanceExtent(False, distance)
headExt = extrudes.add(extInput)
fc = headExt.faces[0]
bd = fc.body
bd.name = self.boltName
#create the body
bodySketch = sketches.add(xyPlane)
bodySketch.sketchCurves.sketchCircles.addByCenterRadius(center, self.bodyDiameter/2)
bodyProf = bodySketch.profiles[0]
bodyExtInput = extrudes.createInput(bodyProf, adsk.fusion.FeatureOperations.JoinFeatureOperation)
bodyExtInput.setAllExtent(adsk.fusion.ExtentDirections.NegativeExtentDirection)
bodyExtInput.setDistanceExtent(False, self.bodyLength)
bodyExt = extrudes.add(bodyExtInput)
# create chamfer
edgeCol = adsk.core.ObjectCollection.create()
edges = bodyExt.endFaces[0].edges
for edgeI in edges:
edgeCol.add(edgeI)
chamferFeats = newComp.features.chamferFeatures
chamferInput = chamferFeats.createInput(edgeCol, True)
chamferInput.setToEqualDistance(self.chamferDistance)
chamferFeats.add(chamferInput)
# create fillet
edgeCol.clear()
loops = headExt.endFaces[0].loops
edgeLoop = None
for edgeLoop in loops:
#since there two edgeloops in the start face of head, one consists of one circle edge while the other six edges
if(len(edgeLoop.edges) == 1):
break
edgeCol.add(edgeLoop.edges[0])
filletFeats = newComp.features.filletFeatures
filletInput = filletFeats.createInput()
filletInput.addConstantRadiusEdgeSet(edgeCol, self.filletRadius, True)
filletFeats.add(filletInput)
#create revolve feature 1
revolveSketchOne = sketches.add(xzPlane)
radius = self.headDiameter/2
point1 = revolveSketchOne.modelToSketchSpace(adsk.core.Point3D.create(center.x + radius*math.cos(math.pi/6), 0, center.y))
point2 = revolveSketchOne.modelToSketchSpace(adsk.core.Point3D.create(center.x + radius, 0, center.y))
point3 = revolveSketchOne.modelToSketchSpace(adsk.core.Point3D.create(point2.x, 0, (point2.x - point1.x) * math.tan(self.cutAngle)))
revolveSketchOne.sketchCurves.sketchLines.addByTwoPoints(point1, point2)
revolveSketchOne.sketchCurves.sketchLines.addByTwoPoints(point2, point3)
revolveSketchOne.sketchCurves.sketchLines.addByTwoPoints(point3, point1)
#revolve feature 2
point4 = revolveSketchOne.modelToSketchSpace(adsk.core.Point3D.create(center.x + radius*math.cos(math.pi/6), 0, self.headHeight - center.y))
point5 = revolveSketchOne.modelToSketchSpace(adsk.core.Point3D.create(center.x + radius, 0, self.headHeight - center.y))
point6 = revolveSketchOne.modelToSketchSpace(adsk.core.Point3D.create(center.x + point2.x, 0, self.headHeight - center.y - (point5.x - point4.x) * math.tan(self.cutAngle)))
revolveSketchOne.sketchCurves.sketchLines.addByTwoPoints(point4, point5)
revolveSketchOne.sketchCurves.sketchLines.addByTwoPoints(point5, point6)
revolveSketchOne.sketchCurves.sketchLines.addByTwoPoints(point6, point4)
zaxis = newComp.zConstructionAxis
revolves = newComp.features.revolveFeatures
revProf1 = revolveSketchOne.profiles[0]
revInput1 = revolves.createInput(revProf1, zaxis, adsk.fusion.FeatureOperations.CutFeatureOperation)
revAngle = adsk.core.ValueInput.createByReal(math.pi*2)
revInput1.setAngleExtent(False,revAngle)
revolves.add(revInput1)
revProf2 = revolveSketchOne.profiles[1]
revInput2 = revolves.createInput(revProf2, zaxis, adsk.fusion.FeatureOperations.CutFeatureOperation)
revInput2.setAngleExtent(False,revAngle)
revolves.add(revInput2)
sideFace = bodyExt.sideFaces[0]
threads = newComp.features.threadFeatures
threadDataQuery = threads.threadDataQuery
defaultThreadType = threadDataQuery.defaultMetricThreadType
recommendData = threadDataQuery.recommendThreadData(self.bodyDiameter, False, defaultThreadType)
if recommendData[0] :
threadInfo = threads.createThreadInfo(False, defaultThreadType, recommendData[1], recommendData[2])
faces = adsk.core.ObjectCollection.create()
faces.add(sideFace)
threadInput = threads.createInput(faces, threadInfo)
threads.add(threadInput)
def run(context):
try:
product = app.activeProduct
design = adsk.fusion.Design.cast(product)
if not design:
ui.messageBox('It is not supported in current workspace, please change to MODEL workspace and try again.')
return
commandDefinitions = ui.commandDefinitions
#check the command exists or not
cmdDef = commandDefinitions.itemById('Bolt')
if not cmdDef:
cmdDef = commandDefinitions.addButtonDefinition('Bolt',
'Create Bolt',
'Create a bolt.',
'./resources') # relative resource file path is specified
onCommandCreated = BoltCommandCreatedHandler()
cmdDef.commandCreated.add(onCommandCreated)
# keep the handler referenced beyond this function
handlers.append(onCommandCreated)
inputs = adsk.core.NamedValues.create()
cmdDef.execute(inputs)
# prevent this module from being terminate 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()))