Message 1 of 3
Fusion 360 API Combine Error - "Failed to get owner occurrence transform"
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
I am experiencing an issue with the Fusion 360 API, when using the Combine Feature to cut a body.
The operation generates incorrect results, and the timeline shows that the operation has generated an error.
Reviewing the warning tells me "Failed to get owner occurrence transform", and "Failed to get target transform".
However, when I simply open the feature (double-click in the timeline) and close, without changing any of the inputs, the warning goes away, and my geometry is assembled correctly.
My actual script is large. So I spent a couple of hours cutting it down to something more digestible. It's still a little long, but this is the best I could manage. Hopefully, it's enough to home in on the error.
See screencast, and attached script.
#Author-Jon Bloomfield
#Description-Custom Cabinet Builder
import adsk.core, adsk.fusion, adsk.cam, traceback, math, re
def run(context):
global design
ui = None
try:
app = adsk.core.Application.get()
ui = app.userInterface
design = app.activeProduct
rootComp = design.rootComponent
door = buildShakerDoor(rootComp, "Door")
camera = app.activeViewport.camera
camera.isFitView = True
app.activeViewport.camera = camera
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def buildShakerDoor(parentComp: adsk.fusion.Component,
name: str):
# Create a component under parent component
transform = adsk.core.Matrix3D.create()
occ: adsk.fusion.Occurrence = parentComp.occurrences.addNewComponent(transform)
comp = occ.component
comp.name = "Door"
# The Left Stile
left = createNewComp(comp, "DoorStile")
stileComp: adsk.fusion.Component = left.component
ext1 = buildBox(stileComp, stileComp.xYConstructionPlane, 60, 2, 5)
# Get inside face
stileRearFace = ext1.endFaces.item(0)
# Get top edge of the face
stileTopEdge = getEdgesByAxis(stileRearFace,
stileComp.yConstructionAxis,
stileComp.xYConstructionPlane)[0]
# The Upper Rail
top = createNewComp(comp, "DoorRail")
railComp: adsk.fusion.Component = top.component
ext2 = buildBox(railComp, railComp.xYConstructionPlane,
40.5, 2, 5)
# Get rear face of rail
railRearFace = ext2.endFaces.item(0)
# Get top edge of the rear face
railTopEdge = getEdgesByAxis(railRearFace,
railComp.yConstructionAxis,
railComp.yZConstructionPlane)[0]
# Join top rail to left stile
makeJoint(comp,
stileRearFace.createForAssemblyContext(left),
stileTopEdge.createForAssemblyContext(left),
adsk.fusion.JointKeyPointTypes.EndKeyPoint,
railRearFace.createForAssemblyContext(top),
railTopEdge.createForAssemblyContext(top),
adsk.fusion.JointKeyPointTypes.EndKeyPoint,
isFlipped = False,
jointAngle=adsk.core.ValueInput.createByReal(math.pi/2),
jointOffsetX=adsk.core.ValueInput.createByReal(0.5))
# Scribe the rail ends by using a Cut-Combine
toolBodies = adsk.core.ObjectCollection.create()
toolBodies.add(left.component.bRepBodies[0].createForAssemblyContext(left))
combines1 = comp.features.combineFeatures
combInput1 = combines1.createInput(targetBody=top.component.bRepBodies[0].createForAssemblyContext(top),
toolBodies=toolBodies)
combInput1.isKeepToolBodies = True
combInput1.operation = adsk.fusion.FeatureOperations.CutFeatureOperation
# Create the combine feature
comb1 = combines1.add(combInput1)
return occ
#######################################################################################
### Utilities
#######################################################################################
def makeJoint(parentComp,
face1,
edge1,
joint1Type,
face2,
edge2,
joint2Type,
jointAngle=None,
isFlipped=False,
jointOffsetX=None,
jointOffsetY=None,
jointOffsetZ=None):
geo0 = adsk.fusion.JointGeometry.createByPlanarFace(face1, edge1, joint1Type)
geo1 = adsk.fusion.JointGeometry.createByPlanarFace(face2, edge2, joint2Type)
jointInput: adsk.fusion.JointInput = parentComp.joints.createInput(geo1, geo0)
jointInput.setAsRigidJointMotion()
if jointAngle != None:
jointInput.angle = jointAngle
if jointOffsetZ != None:
jointInput.offset = jointOffsetZ
jointInput.isFlipped = isFlipped
joint: adsk.fusion.Joint = parentComp.joints.add(jointInput)
if jointOffsetX or jointOffsetY:
# Joint is missing essential properties for X/Y offset
# Get them by identifying the zOffset (aka offset) parameter and indexing
# back from there
[pre, idx] = joint.offset.name.split('d')
nameIdx = int(idx)
if jointOffsetX:
offsetX = joint.parentComponent.modelParameters.itemByName('d{}'.format(nameIdx-2))
offsetX.expression = str(jointOffsetX.realValue) + "cm"
# Self-test to make sure we hit the mark
assert(offsetX.role == "alignOffsetX")
if jointOffsetY:
offsetY = joint.parentComponent.modelParameters.itemByName('d{}'.format(nameIdx-1))
offsetY.expression = str(jointOffsetY.realValue) + "cm"
# Self-test to make sure we hit the mark
assert(offsetY.role == "alignOffsetY")
return joint
def createNewComp(parentComp, compName) -> adsk.fusion.Occurrence:
allOccs = parentComp.occurrences
# Create a component under parentComp
occ = allOccs.addNewComponent(adsk.core.Matrix3D.create())
comp = occ.component
comp.name = compName
return occ
def buildBox(comp, plane, width, height, depth):
# Create a new sketch on the plane.
sketches = comp.sketches
sketch = sketches.add(plane)
sketch.name = "Profile"
distanceInput = adsk.core.ValueInput.createByReal(depth)
sketchLines = sketch.sketchCurves.sketchLines
origin = adsk.core.Point3D.create(0, 0, 0)
topRight = adsk.core.Point3D.create( width, height, 0)
sketchLines.addTwoPointRectangle(origin, topRight)
# Get the profile
prof = sketch.profiles.item(0)
# Create an extrusion input
extrudes1 = comp.features.extrudeFeatures
extInput1 = extrudes1.createInput(prof, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
# Set the distance extent
extInput1.setDistanceExtent(False, distanceInput)
# Set the extrude type to be solid
extInput1.isSolid = True
# Create the extrusion
ext1 = adsk.fusion.ExtrudeFeature.cast(extrudes1.add(extInput1))
return ext1
# Return the distance between an edge and plane
# edge: (BRepEdge) The line to evaluate
# plane: (Plane3D) The plane
def edgeToPlane(edge: adsk.fusion.BRepEdge,
plane: adsk.core.Plane):
start: adsk.core.Point3D = edge.geometry.startPoint
end: adsk.core.Point3D = edge.geometry.endPoint
orthEnd: adsk.core.Vector3D = plane.normal
orthEnd.add(start.asVector())
orthLine = adsk.core.Line3D.create(start, orthEnd.asPoint())
orthogonal = orthLine.asInfiniteLine()
isectPoint: adsk.core.Point3D = plane.intersectWithLine(orthogonal)
distance = isectPoint.distanceTo(start)
return distance
# Return all the edges from 'face' that are parallel to 'axis'
# Edges are sorted by distance from 'plane'
def getEdgesByAxis(face: adsk.fusion.BRepFace,
axis: adsk.fusion.ConstructionAxis,
plane: adsk.fusion.ConstructionPlane):
def distanceSort(e):
return e[1]
constrAxis: adsk.core.InfiniteLine3D = axis.geometry
axisDirection = constrAxis.direction
edgeList = []
edges: adsk.fusion.BRepEdges = face.edges
for e in edges:
edge = adsk.fusion.BRepEdge.cast(e)
edgeDirection: adsk.core.Vector3D = edge.geometry.asInfiniteLine().direction
if edgeDirection.isParallelTo(axisDirection):
dist = edgeToPlane(edge, plane.geometry)
edgeList.append( (edge, dist) )
edgeList.sort(key = distanceSort)
finalList = []
for i in range(len(edgeList)):
finalList.append(edgeList[i][0])
return finalList