Strange joint behavior with parameterization

jonrbloom
Advocate Advocate
975 Views
7 Replies
Message 1 of 8

Strange joint behavior with parameterization

jonrbloom
Advocate
Advocate

I am trying to script a parameterized assembly of several components.

All works well with the initial parameter settings, but when I change the parameter values some of the joints do not seem to follow the adjusted component geometry.

 

I've attached a distilled demo showing the problem. The simple demo creates two occurrences of single component, and joins them with a rigid joint located on one of the component vertices. After changing the User Parameter, I would expect the occurrences to still be joined by the corner, but this is not what happens.

 

Creating the same (at least I think it's the same) joint through the GUI results in expected behavior.

 

I would really appreciate some guidance on what I am doing wrong here, as it has be baffled 🙂

 

Thanks

 

import adsk.core, adsk.fusion, adsk.cam, traceback, math


def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface


        design = app.activeProduct


        # Simple parameter to control the depth
        value = adsk.core.ValueInput.createByString("600mm")
        design.userParameters.add("Depth", value, design.unitsManager.defaultLengthUnits, "")


        # Get the root component of the active design.
        rootComp = design.rootComponent


        transform = adsk.core.Matrix3D.create()


        occ1: adsk.fusion.Occurrence = rootComp.occurrences.addNewComponent(transform)
        comp = occ1.component
        comp.name = "Comp"

        # Create a new sketch on the xy plane.
        sketches = comp.sketches
        xyPlane = comp.xYConstructionPlane
        sketch = sketches.add(xyPlane)

        lines = sketch.sketchCurves.sketchLines
        recLines = lines.addTwoPointRectangle(adsk.core.Point3D.create(0, 0, 0),
                                              adsk.core.Point3D.create(2, 90, 0))


        # 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
        distance1 = adsk.core.ValueInput.createByString("Depth")
        extInput1.setDistanceExtent(False, distance1)


        # Create the extrusion
        ext1 = adsk.fusion.ExtrudeFeature.cast(extrudes1.add(extInput1))
        ext1.extentOne.distance.expression = "Depth"
        
        # New occurance of same component
        occ2 = rootComp.occurrences.addExistingComponent(comp, transform)


        compJointFace: adsk.fusion.BRepFace = ext1.sideFaces.item(0)
        compJointEdge: adsk.fusion.BRepEdge = compJointFace.edges.item(0)

        face1 = compJointFace.createForAssemblyContext(occ1)
        edge1 = compJointEdge.createForAssemblyContext(occ1)

        face2 = compJointFace.createForAssemblyContext(occ2)
        edge2 = compJointEdge.createForAssemblyContext(occ2)
        
        geo0 = adsk.fusion.JointGeometry.createByPlanarFace(face1, edge1, adsk.fusion.JointKeyPointTypes.StartKeyPoint)
        geo1 = adsk.fusion.JointGeometry.createByPlanarFace(face2, edge2, adsk.fusion.JointKeyPointTypes.StartKeyPoint)

        jointInput: adsk.fusion.JointInput = rootComp.joints.createInput(geo1, geo0)
        jointInput.setAsRigidJointMotion()
        jointInput.angle = adsk.core.ValueInput.createByReal(math.pi/2)
        joint: adsk.fusion.Joint = rootComp.joints.add(jointInput)

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

 

0 Likes
976 Views
7 Replies
Replies (7)
Message 2 of 8

kandennti
Mentor
Mentor

Hi @jonrbloom .

 

I ran the script and experienced at least three different results.

1.png

All images are from the same direction. The joint is not stable.
It is expected that the Planar Face alone will not be sufficient for Joint conditions.


Stable when using createByCurve instead of createByPlanarFace,
I was able to respond to parameter changes.

~
        # New occurance of same component
        occ2 = rootComp.occurrences.addExistingComponent(comp, transform)

        # compJointFace: adsk.fusion.BRepFace = ext1.sideFaces.item(0)
        # compJointEdge: adsk.fusion.BRepEdge = compJointFace.edges.item(0)
        compJointEdge: adsk.fusion.BRepEdge = ext1.startFaces.item(0).edges.item(0)

        # face1 = compJointFace.createForAssemblyContext(occ1)
        edge1 = compJointEdge.createForAssemblyContext(occ1)

        # face2 = compJointFace.createForAssemblyContext(occ2)
        edge2 = compJointEdge.createForAssemblyContext(occ2)


        # geo0 = adsk.fusion.JointGeometry.createByPlanarFace(face1, edge1, adsk.fusion.JointKeyPointTypes.StartKeyPoint)
        # geo1 = adsk.fusion.JointGeometry.createByPlanarFace(face2, edge2, adsk.fusion.JointKeyPointTypes.StartKeyPoint)
        startKP = adsk.fusion.JointKeyPointTypes.StartKeyPoint
        geo0 = adsk.fusion.JointGeometry.createByCurve(edge1, startKP)
        geo1 = adsk.fusion.JointGeometry.createByCurve(edge2, startKP)

        jointInput: adsk.fusion.JointInput = rootComp.joints.createInput(geo1, geo0)
~

 

 

0 Likes
Message 3 of 8

jonrbloom
Advocate
Advocate

Thanks


@kandennti wrote:

Hi @jonrbloom .

 

I ran the script and experienced at least three different results.

1.png

All images are from the same direction. The joint is not stable.
It is expected that the Planar Face alone will not be sufficient for Joint conditions.


Stable when using createByCurve instead of createByPlanarFace,
I was able to respond to parameter changes.

 

~
        # New occurance of same component
        occ2 = rootComp.occurrences.addExistingComponent(comp, transform)

        # compJointFace: adsk.fusion.BRepFace = ext1.sideFaces.item(0)
        # compJointEdge: adsk.fusion.BRepEdge = compJointFace.edges.item(0)
        compJointEdge: adsk.fusion.BRepEdge = ext1.startFaces.item(0).edges.item(0)

        # face1 = compJointFace.createForAssemblyContext(occ1)
        edge1 = compJointEdge.createForAssemblyContext(occ1)

        # face2 = compJointFace.createForAssemblyContext(occ2)
        edge2 = compJointEdge.createForAssemblyContext(occ2)


        # geo0 = adsk.fusion.JointGeometry.createByPlanarFace(face1, edge1, adsk.fusion.JointKeyPointTypes.StartKeyPoint)
        # geo1 = adsk.fusion.JointGeometry.createByPlanarFace(face2, edge2, adsk.fusion.JointKeyPointTypes.StartKeyPoint)
        startKP = adsk.fusion.JointKeyPointTypes.StartKeyPoint
        geo0 = adsk.fusion.JointGeometry.createByCurve(edge1, startKP)
        geo1 = adsk.fusion.JointGeometry.createByCurve(edge2, startKP)

        jointInput: adsk.fusion.JointInput = rootComp.joints.createInput(geo1, geo0)
~

 

 

 


Thanks.

I don't understand your comment about a planar face on its own not being enough. createByPlanarFace has both a face and an edge. createByCurve takes an edge, and presumably extracts the face from that edge. How are they different?

 

0 Likes
Message 4 of 8

kandennti
Mentor
Mentor

I don't know why.
However, since the result changes depending on the number of tabs in the document when executing the script, it cannot be used with confidence.

0 Likes
Message 5 of 8

jonrbloom
Advocate
Advocate

Using createByCurve does however appear to fix the 'broken joint after changing parameter' issue. I'll see how it fares in the full-fledged script (will take a while to refactor).

 

However, now that you got me looking for it, I am still seeing variable joint alignment from run to run. Perhaps there is some uncertainty in the order in which the edges are ordered. In my real script I identify the edges by axis orientation, rather than assuming a specific item() index. So if that is what's happening this part of the problem should go away.

 

Full updated script attached, just in case I missed something. But I believe I made the suggested change correctly.

#Author-me
#Description-

import adsk.core, adsk.fusion, adsk.cam, traceback, math

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface

        design = app.activeProduct

        # Simple parameter to control the depth
        value = adsk.core.ValueInput.createByString("600mm")
        design.userParameters.add("Depth", value, design.unitsManager.defaultLengthUnits, "")

        # Get the root component of the active design.
        rootComp = design.rootComponent

        transform = adsk.core.Matrix3D.create()

        occ1: adsk.fusion.Occurrence = rootComp.occurrences.addNewComponent(transform)
        comp = occ1.component
        comp.name = "Comp"

        # Create a new sketch on the xy plane.
        sketches = comp.sketches
        xyPlane = comp.xYConstructionPlane
        sketch = sketches.add(xyPlane)

        lines = sketch.sketchCurves.sketchLines
        recLines = lines.addTwoPointRectangle(adsk.core.Point3D.create(0, 0, 0),
                                              adsk.core.Point3D.create(2, 90, 0))

        # 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
        distance1 = adsk.core.ValueInput.createByString("Depth")
        extInput1.setDistanceExtent(False, distance1)

        # Create the extrusion
        ext1 = adsk.fusion.ExtrudeFeature.cast(extrudes1.add(extInput1))
        ext1.extentOne.distance.expression = "Depth"
        
        # New occurance of same component
        occ2 = rootComp.occurrences.addExistingComponent(comp, transform)

        compJointFace: adsk.fusion.BRepFace = ext1.sideFaces.item(0)
        compJointEdge: adsk.fusion.BRepEdge = compJointFace.edges.item(0)

        edge1 = compJointEdge.createForAssemblyContext(occ1)
        edge2 = compJointEdge.createForAssemblyContext(occ2)
        
        geo0 = adsk.fusion.JointGeometry.createByCurve(edge1, adsk.fusion.JointKeyPointTypes.StartKeyPoint)
        geo1 = adsk.fusion.JointGeometry.createByCurve(edge2, adsk.fusion.JointKeyPointTypes.StartKeyPoint)

        jointInput: adsk.fusion.JointInput = rootComp.joints.createInput(geo1, geo0)
        jointInput.setAsRigidJointMotion()
        jointInput.angle = adsk.core.ValueInput.createByReal(math.pi/2)
        joint: adsk.fusion.Joint = rootComp.joints.add(jointInput)

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

0 Likes
Message 6 of 8

BrianEkins
Mentor
Mentor

A planar joint only constrains 3 degrees of freedom so the parts still have 3 degrees they can move relative to each other.  I'm not sure that's what the issue is here but it's also not clear to me what your expected result is.

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
0 Likes
Message 7 of 8

jonrbloom
Advocate
Advocate

Hi @BrianEkins. Thanks for replying.

 

I've been digging more to isolate this issue a little better. I believe I have eliminated the non-repeatability problem by explicitly locating the faces/edges I want based on orientation (This is actually I was always doing with my script proper - I just simplified the demo script a bit too much).

 

Now I get reliable geometry until I change the parameter. When I increase the Depth UserParam, the joint completely falls apart.

 

I had to drop @kandennti's suggestion to use createByCurve because this does not allow me to control which faces the joint aligns to - It seems to choose any face it likes from the supplied edge.

 

What I am trying to do is model the sides to a timber case. For this I need to butt the thin edge of one plywood sheet to the face of the second, as presented when the demo script attached is run.

 

If createByPlanarFace is not the correct solution for this, please can you tell what is? As mentioned, when I assemble the components in the GUI, by choosing face+vertex I get exactly the result I expect.

#Author-me
#Description-

import adsk.core, adsk.fusion, adsk.cam, traceback, math

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface

        design = app.activeProduct

        # Simple parameter to control the depth
        value = adsk.core.ValueInput.createByString("600mm")
        design.userParameters.add("Depth", value, design.unitsManager.defaultLengthUnits, "")

        # Get the root component of the active design.
        rootComp = design.rootComponent

        transform = adsk.core.Matrix3D.create()

        occ1: adsk.fusion.Occurrence = rootComp.occurrences.addNewComponent(transform)
        comp = occ1.component
        comp.name = "Comp"

        # Create a new sketch on the xy plane.
        sketches = comp.sketches
        xyPlane = comp.xYConstructionPlane
        sketch = sketches.add(xyPlane)

        lines = sketch.sketchCurves.sketchLines
        recLines = lines.addTwoPointRectangle(adsk.core.Point3D.create(0, 0, 0),
                                              adsk.core.Point3D.create(2, 90, 0))

        # 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
        distance1 = adsk.core.ValueInput.createByString("Depth")
        extInput1.setDistanceExtent(False, distance1)

        # Create the extrusion
        ext1 = adsk.fusion.ExtrudeFeature.cast(extrudes1.add(extInput1))
        ext1.extentOne.distance.expression = "Depth"
        
        # New occurance of same component
        occ2 = rootComp.occurrences.addExistingComponent(comp, transform)

        # Locate the face that lies parallel the YZ Plane, and faces viewer
        origin: adsk.core.Point3D = adsk.core.Point3D.create(0,0,0)
        for f in ext1.sideFaces:
            plane: adsk.core.Plane = f.geometry
            if plane.normal.isEqualTo(adsk.core.Vector3D.create(1, 0, 0)):
                yzFace = f

        # Locate the edge that is aligned with the y-axis, but not on the y-axis
        for e in yzFace.edges:
            compJointEdge: adsk.fusion.BRepEdge = e
            line: adsk.core.Line3D = compJointEdge.geometry
            if line.startPoint.x != 0 or line.endPoint.x != 0:
                edgeDirection: adsk.core.Vector3D = compJointEdge.geometry.asInfiniteLine().direction
                if edgeDirection.isParallelTo(comp.yConstructionAxis.geometry.direction):
                    break

        face1 = yzFace.createForAssemblyContext(occ1)
        edge1 = compJointEdge.createForAssemblyContext(occ1)
        face2 = ext1.startFaces.item(0).createForAssemblyContext(occ2)
        edge2 = compJointEdge.createForAssemblyContext(occ2)
        
        geo0 = adsk.fusion.JointGeometry.createByPlanarFace(face1, edge1, adsk.fusion.JointKeyPointTypes.StartKeyPoint)
        geo1 = adsk.fusion.JointGeometry.createByPlanarFace(face2, edge2, adsk.fusion.JointKeyPointTypes.StartKeyPoint)

        jointInput: adsk.fusion.JointInput = rootComp.joints.createInput(geo1, geo0)
        jointInput.setAsRigidJointMotion()
        joint: adsk.fusion.Joint = rootComp.joints.add(jointInput)

        camera = app.activeViewport.camera
        camera.isFitView = True
        app.activeViewport.camera = camera

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
0 Likes
Message 8 of 8

jonrbloom
Advocate
Advocate

@BrianEkins - I mistook your comment re Planar Joint as referring to the method used to create (createByPlanarFace). Just realized you mean if I use setAsPlanarJointMotion, right?

 

In this case I am creating a Rigid Joint, but via createByPlanarFace. I should be able to rely on this having all 6 degrees locked down I hope? Indeed this is the case until I modify the UserParameter. The components remain locked together exactly as I intend. But when I adjust the parameter, the Joint stays in the same place, and the components slide past it.

0 Likes