Community
Fusion API and Scripts
Got a new add-in to share? Need something specialized to be scripted? Ask questions or share what you’ve discovered with the community.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Use Selection Input to select a single triangle in a mesh?

6 REPLIES 6
SOLVED
Reply
Message 1 of 7
Joshua.mursic
384 Views, 6 Replies

Use Selection Input to select a single triangle in a mesh?

Is there a way to have the user select a single triangle in a meshbody? I am trying to add sprues to a mesh body in the location that the user wants. By selecting the mesh body I will build a cylinder that follows the normal of the triangle that is selected. I see that there is a way to select an entire mesh, but I don't see a single triangle selection option in the list of options. 

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-03033DE6-AD8E-46B3-B4E6-DADA8D389E4E

 

meshTriangleSelection = inputs.addSelectionInput('meshTriangleSelection', 'Build Sprue', 'Select point')
meshTriangleSelection.addSelectionFilter('MeshBodies')
meshTriangleSelection.setSelectionLimits(0)

 

the mesh selection palette has an option to select a mesh face.

Joshuamursic_0-1684846141032.png

 

I am having a hard time finding a way to replicate this palettes functionality so that I can create a sprue on the surface where the user selects.

 

6 REPLIES 6
Message 2 of 7
kandennti
in reply to: Joshua.mursic

Hi @Joshua.mursic .

 

Perhaps the ability to select mesh faces is not provided in the API.

Message 3 of 7

I came up with this as a solution:

def constructSupport():
    design:adsk.fusion.Design = app.activeProduct
    root = design.rootComponent
    design.designType = adsk.fusion.DesignTypes.DirectDesignType

    # select PlanarFaces
    try:
        selection :adsk.core.Selection = ui.selectEntity(
            'Select Face', 'MeshBodies')
    except:
        return

    # Click Point
    point :adsk.core.Point3D = selection.point
    mesh:adsk.fusion.MeshBody = selection.entity
    nodes = mesh.displayMesh.nodeCoordinates
    closePoint1 = nodes[0]
    closePoint2 = nodes[0]
    closePoint3 = nodes[0]
    for node in nodes:
        if point.distanceTo(node) < point.distanceTo(closePoint1):
            closePoint3 = closePoint2
            closePoint2 = closePoint1
            closePoint1 = node
    
    # Create Sketch Plane
    vec1 = adsk.core.Vector3D.create(closePoint1.x-closePoint2.x,closePoint1.y-closePoint2.y,closePoint1.z-closePoint2.z)
    vec2 = adsk.core.Vector3D.create(closePoint1.x-closePoint3.x,closePoint1.y-closePoint3.y,closePoint1.z-closePoint3.z)
    normalVec = vec1.crossProduct(vec2)
    plane = adsk.core.Plane.create(point,normalVec)

    planeInput:adsk.fusion.ConstructionPlaneInput = root.constructionPlanes.createInput()
    planeInput.setByPlane(plane)
    sketchPlane = root.constructionPlanes.add(planeInput)

    # Create Sketch
    newSketch:adsk.fusion.Sketch = root.sketches.add(sketchPlane)

    # Get the transformation Matrix for the sketch
    matrix :adsk.core.Matrix3D = newSketch.transform
    matrix.invert()
    point.transformBy(matrix)

    # Draw Sketch
    newSketch.sketchCurves.sketchCircles.addByCenterRadius(point,0.1)
Message 4 of 7
Jorge_Jaramillo
in reply to: kandennti

Hi,

 

I can see there is a "ParaMesh.TriangleSelection"  text command, but I get the following error when I used it:

No active ParaMeshFacetSelection in SelectionSet

 

@kandennti: Do you know how to use it?

 

Regards,

Jorge Jaramillo

 

Message 5 of 7
kandennti
in reply to: Joshua.mursic

@Joshua.mursic .

 

I have created a sample script that makes the triangular faces of the mesh body appear to be selected, rather than actually selecting them.
What is actually displayed are custom graphics.

# Fusion360API Python script

import traceback
import adsk
import adsk.core as core
import adsk.fusion as fusion


_app: core.Application = None
_ui: core.UserInterface = None
_handlers = []

CMD_INFO = {
    'id': 'kantoku_test',
    'name': 'test',
    'tooltip': 'test'
}

_meshIpt: core.SelectionCommandInput = None

class MyCommandCreatedHandler(core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: core.CommandCreatedEventArgs):
        try:
            global _handlers
            cmd: core.Command = core.Command.cast(args.command)
            inputs: core.CommandInputs = cmd.commandInputs

            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            onExecutePreview = MyExecutePreviewHandler()
            cmd.executePreview.add(onExecutePreview)
            _handlers.append(onExecutePreview)

            global _meshIpt
            _meshIpt = inputs.addSelectionInput(
                '_meshIptId',
                'mesh',
                'Select Mesh',
            )
            _meshIpt.addSelectionFilter(core.SelectionCommandInput.MeshBodies)

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


class MyExecutePreviewHandler(core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: core.CommandEventArgs):
        sel: core.Selection = _meshIpt.selection(0)

        meshBody: fusion.MeshBody = sel.entity
        point: core.Point3D = sel.point

        _meshIpt.clearSelection()

        meshBody.opacity = 0.5
        cgBodies = get_triangle_bodies(meshBody, point)
        [_meshIpt.addSelection(b) for b in cgBodies]


class MyCommandDestroyHandler(core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: core.CommandEventArgs):
        adsk.terminate()


def get_triangle_bodies(
    mesh: fusion.MeshBody,
    point: core.Point3D,
) -> list[fusion.BRepBody]:

    tmpMgr: fusion.TemporaryBRepManager = fusion.TemporaryBRepManager.get()

    # *******
    def init_lines(
        pnts: list[core.Point3D],
    ) -> list[core.Line3D]:

        points = list(pnts)
        points.append(pnts[0])
        
        lineLst = []
        for p1, p2 in zip(points, points[1:]):
            try:
                line = core.Line3D.create(p1, p2)
                lineLst.append(line)
            except:
                pass

        return lineLst


    def init_pointSets(
        idxs: list,
        nodePoints: list[core.Point3D],
    ) -> tuple:

        return tuple([nodePoints[i] for i in idxs])


    def init_wireBodies(
        crvs: list,
    ) -> list[fusion.BRepBody]:

        try:
            wireBody, _ = tmpMgr.createWireFromCurves(crvs)
            return wireBody
        except:
            pass


    def init_tri_faceBodies(
        wireBodies: list[fusion.BRepBody],
    ) -> list[fusion.BRepBody]:

        triBodies = []
        for b in wireBodies:
            try:
                triBodies.append(tmpMgr.createFaceFromPlanarWires([b]))
            except:
                pass

        return triBodies


    def is_inner(
        point: core.Point3D,
        pointSet: tuple[core.Point3D],
    ) -> bool:

        vecAB: core.Vector3D = pointSet[1].vectorTo(pointSet[0])
        vecBP: core.Vector3D = point.vectorTo(pointSet[1])
        vecBC: core.Vector3D = pointSet[2].vectorTo(pointSet[1])
        vecCP: core.Vector3D = point.vectorTo(pointSet[2])
        vecCA: core.Vector3D = pointSet[0].vectorTo(pointSet[2])
        vecAP: core.Vector3D = point.vectorTo(pointSet[0])

        vec1: core.Vector3D = vecAB.crossProduct(vecBP)
        vec2: core.Vector3D = vecBC.crossProduct(vecCP)
        vec3: core.Vector3D = vecCA.crossProduct(vecAP)

        dot12: float = vec1.dotProduct(vec2)
        dot13: float = vec1.dotProduct(vec3)

        return True if dot12 > 0 and dot13 > 0 else False


    def draw_cg_bodies(
        comp: fusion.Component,
        bodyLst: list,
    ) -> list:

        cgGroup: fusion.CustomGraphicsGroup = comp.customGraphicsGroups.add()

        cgBodies = [cgGroup.addBRepBody(b) for b in bodyLst]

        blue = fusion.CustomGraphicsSolidColorEffect.create(
            core.Color.create(0,0,120,255)
        )
        for b in cgBodies:
            b.color = blue 

        return  cgBodies

    # *******
    triMesh: fusion.TriangleMesh = mesh.displayMesh

    nodeIndices = triMesh.nodeIndices
    triIndices = [nodeIndices[i: i + 3] for i in range(0, len(nodeIndices), 3)]

    nodePoints = [p for p in triMesh.nodeCoordinates]

    pointSets = [init_pointSets(idxs, nodePoints) for idxs in triIndices]

    triPointSets = [pnts for pnts in pointSets if is_inner(point, pnts)]
    if len(triPointSets) < 1: return

    lineSets = [init_lines(triPointSet) for triPointSet in triPointSets]
    triWires = [init_wireBodies(lines) for lines in lineSets]
    triBodies = init_tri_faceBodies(triWires)
    cgBodies = draw_cg_bodies(mesh.parentComponent, triBodies)

    return cgBodies


def run(context):
    try:
        global _app, _ui
        _app = core.Application.get()
        _ui = _app.userInterface

        cmdDef: core.CommandDefinition = _ui.commandDefinitions.itemById(
            CMD_INFO['id']
        )

        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition(
                CMD_INFO['id'],
                CMD_INFO['name'],
                CMD_INFO['tooltip']
            )

        global _handlers
        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()))

 

But there is a problem. Here is a video of the actual test.

 

The first MeshBody selected was created from a sphere.
This one shows the targeted triangular surface.
The next meshBody selected is created from a cylinder.
This one shows a completely different surface.

 

The difference between the two MeshBodies is whether the MeshBody has one or multiple Face Groups.

 

Finally, when I run the script after changing the Face Group of the cylinder's MeshBody to one, the targeted face is displayed.

 

 

My guess, since I haven't looked into it in detail, is that MeshBody.displayMesh is not returning all the information.

Message 6 of 7
kandennti
in reply to: Jorge_Jaramillo

@Jorge_Jaramillo .

 

'ParaMesh.TriangleSelection', I could not figure out how to use it.
'ParaMesh.SelectionPalette' in direct mode, I have confirmed that the "mesh selection palette" is displayed.

 

It may be possible to change the settings in the dialog, but I don't know how to select the triangles in the MeshBody.

 

 

The only text command I know of to select elements in CAD space is "Commands.Pick".

https://github.com/kantoku-code/Fusion360_Small_Tools_for_Developers/blob/master/TextCommands/TextCo... 

 

Create the appropriate body, press home on the view cube and execute the following text command

1.png

One surface has been selected.

 

Next, move the body to the edge of the screen and execute the same command, but nothing is selected.

1.png

 

I think <BRect> is not a value in 3DCAD space, but a value on the screen.
I haven't looked into it in detail or tried it, but the return value of the Viewport.viewToModelSpace method might be useful.

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-277144B6-5E80-4D56-A31A-3DC1697893C5 

 

 

When I ran this text command on a MeshBody with the "mesh selection palette" displayed, I expected it to select the triangles, but it did not.
Therefore, I have determined that there is no way to select triangles at this time.

 

Message 7 of 7

Thanks for all of the feedback it has been very helpful. I modified my original code to this: 

 

def constructSupport():
    design:adsk.fusion.Design = app.activeProduct
    root = design.rootComponent
    features = root.features
    extrudes = features.extrudeFeatures
    design.designType = adsk.fusion.DesignTypes.DirectDesignType

    # select PlanarFaces
    try:
        selection :adsk.core.Selection = ui.selectEntity(
            'Select Face', 'MeshBodies')
    except:
        return

    # Click Point
    point :adsk.core.Point3D = selection.point
    mesh:adsk.fusion.MeshBody = selection.entity
    nodes = mesh.displayMesh.nodeCoordinates
    distanceArray = []
    for node in nodes:
        distance = point.distanceTo(node)
        distanceArray.append([distance,node])
    
    sortedDistance = sorted(distanceArray,key=lambda dis: dis[0])
    point1 = sortedDistance[0][1]
    point2 = sortedDistance[1][1]
    point3 = sortedDistance[2][1]
    
    # Create Sketch Plane
    vec1 = adsk.core.Vector3D.create(point1.x-point2.x,point1.y-point2.y,point1.z-point2.z)
    vec2 = adsk.core.Vector3D.create(point1.x-point3.x,point1.y-point3.y,point1.z-point3.z)
    normalVec = vec1.crossProduct(vec2)
    plane = adsk.core.Plane.create(point,normalVec)
    planeInput:adsk.fusion.ConstructionPlaneInput = root.constructionPlanes.createInput()
    planeInput.setByPlane(plane)
    sketchPlane = root.constructionPlanes.add(planeInput)

    # Create Sketch
    newSketch:adsk.fusion.Sketch = root.sketches.add(sketchPlane)
    # Get the transformation Matrix for the sketch
    matrix :adsk.core.Matrix3D = newSketch.transform
    matrix.invert()
    point.transformBy(matrix)
    # Draw Sketch
    newSketch.sketchCurves.sketchCircles.addByCenterRadius(point,0.1)

    #Draw close points
    if True:
        newSketch.sketchPoints.add(point1)
        newSketch.sketchPoints.add(point2)
        newSketch.sketchPoints.add(point3)
        newSketch.sketchPoints.add(point)

    # Creates Extrude
    extrudeDistance = adsk.fusion.DistanceExtentDefinition.create(adsk.core.ValueInput.createByReal(-0.4))
    extrudeInput = extrudes.createInput(newSketch.profiles[0], adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
    extrudeInput.setOneSideExtent(extrudeDistance,adsk.fusion.ExtentDirections.PositiveExtentDirection)
    extrudeInput.isSolid = True
    extrudes.add(extrudeInput)
    #Delete Sketch
    newSketch.deleteMe()
    sketchPlane.deleteMe()

 

This is working well to pick a triangle. However I am having a hard time making sure the sprue is facing the correct direction. 

 

Joshuamursic_0-1685450539254.png

 

you can see in the image, sometimes the sprue is facing the inside of the mesh instead of outside. Any ideas on how to ensure that the sprue direction vector is in the positive direction?

 

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report