API to probe solid body from the top

API to probe solid body from the top

pavlo.sulimenko
Explorer Explorer
1,243 Views
6 Replies
Message 1 of 7

API to probe solid body from the top

pavlo.sulimenko
Explorer
Explorer

Hello,

I am looking into ways of converting a fusion360 model (body) to a point cloud with a python script, as if it was probed from the top, but I am struggling to find suitable API functions for this. The only solution I came up with is sketching intersect, drawing a line from the top and looking for crossing, which seems very inefficient. A way to directly brobe top side of the body at given x and y coordinates would be much better.

Thanks in advance

0 Likes
Accepted solutions (1)
1,244 Views
6 Replies
Replies (6)
Message 2 of 7

tykapl.breuil
Advocate
Advocate

I am not sure what you mean by probe but maybe using the TemporaryBRepManager is a good idea, you can find the documentation here:

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-2CA08FA9-8C23-4449-87C4-1CCBA87941C1

I am especially thinking about those two methods :

-planeIntersection

-imprintOverlapBodies

I'm not sure it is exactly what you're looking for but it might help.

 

Message 3 of 7

kandennti
Mentor
Mentor
Accepted solution

Hi @pavlo.sulimenko .

 

I'm sure there are other ways, but I made a sample using the Component.findBRepUsingRay method.

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-7CE5A6D9-BB79-4A6C-9634-587BFF5FE077 

 

1.png

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core


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

        msg: str = 'Select Body'
        selFiltter: str = 'Bodies'
        sel: adsk.core.Selection = selectEnt(msg, selFiltter)
        if not sel:
            return

        targetBody: adsk.fusion.BRepBody = sel.entity

        # get Points from Top
        pnts = getPointsFromTop(targetBody, 10)

        # dump points - TextCommand Window
        app.log(u'TextCommandWindow.Clear')
        p: adsk.core.Point3D
        for p in pnts:
            app.log(f'{p.asArray()}')

        # dump points - Sketch Points
        comp: adsk.fusion.Component = targetBody.parentComponent
        skt: adsk.fusion.Sketch = comp.sketches.add(
            comp.xYConstructionPlane
        )
        skt.name = 'from the top'
        skt.isComputeDeferred = True
        sktPnts: adsk.fusion.SketchPoints = skt.sketchPoints
        for p in pnts:
           sktPnts.add(p)
        skt.isComputeDeferred = False

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

def getPointsFromTop(
    targetBody: adsk.fusion.BRepBody,
    stepCount: int = 10) -> list:

    comp: adsk.fusion.Component = targetBody.parentComponent

    bBox: adsk.core.BoundingBox3D = targetBody.boundingBox
    minPnt: adsk.core.Point3D = bBox.minPoint
    maxPnt: adsk.core.Point3D = bBox.maxPoint

    stepX = (bBox.maxPoint.x - bBox.minPoint.x) / stepCount
    stepY = (bBox.maxPoint.y - bBox.minPoint.y) / stepCount

    tempPnts = []
    for idxX in range(stepCount + 1):
        for idxY in range(stepCount + 1):
            tempPnts.append(
                adsk.core.Point3D.create(
                    minPnt.x + stepX * idxX,
                    minPnt.y + stepY * idxY,
                    maxPnt.z + 1
                )
            )

    rayDirection: adsk.core.Vector3D = adsk.core.Vector3D.create(
        0, 0, -1
    )

    pnts = []
    hitPnts: adsk.core.ObjectCollection = adsk.core.ObjectCollection.create() 
    for pnt in tempPnts:
        hitPnts.clear()

        bodies: adsk.core.ObjectCollection = comp.findBRepUsingRay(
            pnt,
            rayDirection,
            adsk.fusion.BRepEntityTypes.BRepBodyEntityType,
            -1.0,
            True,
            hitPnts
        )

        if bodies.count < 1:
            continue

        bodyLst = [b for b in bodies]
        hitPntLst = [p for p in hitPnts]

        for body, pnt in zip(bodyLst, hitPntLst):
            if body == targetBody:
                pnts.append(pnt)
                continue

    return pnts


def selectEnt(
        msg: str,
        filterStr: str) -> adsk.core.Selection:

    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        sel = ui.selectEntity(msg, filterStr)
        return sel
    except:
        return None

 

After executing the script, select the body.
However, if the body is not the root component, it will not be processed correctly.

Message 4 of 7

BrianEkins
Mentor
Mentor

I was going to suggest that you can use the same function that @kandennti suggested. I believe his code will also work on any body, whether it is in the root component or not if you change the line in the GetPointsFromTop function from:

comp: adsk.fusion.Component = targetBody.parentComponent

to: 

app: adsk.core.Application = adsk.core.Application.get()
des: adsk.fusion.Design = app.activeProduct
comp: des.rootComponent

 

I thought of one other possibility too but its success depends on the shape of your model and any expectation on the pattern of the points. You can use the TriangleMeshCalculator to create a mesh representation of the body and then go through the calculated vertices of the mesh and check the normal of each point. If it's pointing towards the "top" direction, then it's on part of the model that's facing up. If you have any internal surfaces, like a horizontal hole or a groove in the part, you'll also get points on the portion of those surfaces that are facing up, even though from the top they are covered by another surface. But for certain parts, this could work well and should be very fast, with good control over the density of the points.

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

kandennti
Mentor
Mentor
0 Likes
Message 6 of 7

pavlo.sulimenko
Explorer
Explorer

@kandennti Wow, thanks a lot for posting complete script. I tested it and it just works as I intended.
@BrianEkins thanks for your addition, I'll try your workaround for non-root components.

Message 7 of 7

pavlo.sulimenko
Explorer
Explorer

@BrianEkins your proposal did not work, but I found another fix. Since the probing is only done with the selected body, the code block at the end for filtering the points from another bodies, which did not work correctly with bodies under components is actually unnecessary, so the final code which works with any body looks as follows, thanks again @kandennti 

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core


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

        msg: str = 'Select Body'
        selFiltter: str = 'Bodies'
        sel: adsk.core.Selection = selectEnt(msg, selFiltter)
        if not sel:
            return

        targetBody: adsk.fusion.BRepBody = sel.entity

        # get Points from Top
        pnts = getPointsFromTop(targetBody, 20)

        # dump points - TextCommand Window
        app.log(u'TextCommandWindow.Clear')
        p: adsk.core.Point3D
        for p in pnts:
            app.log(f'{p.asArray()}')

        # dump points - Sketch Points
        comp: adsk.fusion.Component = targetBody.parentComponent
        skt: adsk.fusion.Sketch = comp.sketches.add(
            comp.xYConstructionPlane
        )
        skt.name = 'from the top'
        skt.isComputeDeferred = True
        sktPnts: adsk.fusion.SketchPoints = skt.sketchPoints
        for p in pnts:
           sktPnts.add(p)
        skt.isComputeDeferred = False

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

def getPointsFromTop(
    targetBody: adsk.fusion.BRepBody,
    stepCount: int = 10) -> list:

    comp: adsk.fusion.Component = targetBody.parentComponent

    bBox: adsk.core.BoundingBox3D = targetBody.boundingBox
    minPnt: adsk.core.Point3D = bBox.minPoint
    maxPnt: adsk.core.Point3D = bBox.maxPoint

    stepX = (bBox.maxPoint.x - bBox.minPoint.x) / stepCount
    stepY = (bBox.maxPoint.y - bBox.minPoint.y) / stepCount

    tempPnts = []
    for idxX in range(stepCount + 1):
        for idxY in range(stepCount + 1):
            tempPnts.append(
                adsk.core.Point3D.create(
                    minPnt.x + stepX * idxX,
                    minPnt.y + stepY * idxY,
                    maxPnt.z + 1
                )
            )

    rayDirection: adsk.core.Vector3D = adsk.core.Vector3D.create(
        0, 0, -1
    )

    pnts = []
    hitPnts: adsk.core.ObjectCollection = adsk.core.ObjectCollection.create() 
    for pnt in tempPnts:
        hitPnts.clear()

        bodies: adsk.core.ObjectCollection = comp.findBRepUsingRay(
            pnt,
            rayDirection,
            adsk.fusion.BRepEntityTypes.BRepBodyEntityType,
            -1.0,
            True,
            hitPnts
        )

        if bodies.count < 1:
            continue

        hitPntLst = [p for p in hitPnts]

        for pnt in hitPnts:
            pnts.append(pnt)

    return pnts


def selectEnt(
        msg: str,
        filterStr: str) -> adsk.core.Selection:

    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        sel = ui.selectEntity(msg, filterStr)
        return sel
    except:
        return None