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: 

Aligning component Origin to Body

4 REPLIES 4
SOLVED
Reply
Message 1 of 5
corijn
469 Views, 4 Replies

Aligning component Origin to Body

I'm running into edge cases with my Bill of Material script where a the body of a component was drawn at an angle, making the origin's axis alignment not match the actual board alignment. Simplest way to put it would be I have bodies that were created at 45 degree angles, not rotated to 45 degree angles as a component. This means their bounding box is completely off. See attached screenshot. Not even a recalculated "closest fitting bounding box" fixes this if the reference coordinate system is misaligned.

 

I'd like to programmatically fix this when my BOM script runs. I'd imagine something like calculating surface normal vectors for all faces, picking the one with the largest surface area, then transforming the body by that vector, then transforming  the component to the reverse, effectively keeping things in place but rotating the origin.

 

I'm a bit too green with Fusion's Python API so perhaps somebody can point me in the right direction for this?

 
Labels (1)
4 REPLIES 4
Message 2 of 5
kandennti
in reply to: corijn

Hi @corijn .

 

Although it does not the Minimal Box, we have created a sample to get a BoundingBox with the orientation of the origin of the component to which the selected body belongs.

# Fusion360API Python script
import adsk.core, adsk.fusion, traceback

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

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

        # get OrientedBoundingBox3D
        body = sel.entity
        bBox :adsk.core.OrientedBoundingBox3D = getBoundingBox(body)

        # show info
        unitsMgr = des.unitsManager
        defLenUnit = unitsMgr.defaultLengthUnits
        covUnit = unitsMgr.convert(1, unitsMgr.internalUnits, defLenUnit)

        ui.messageBox('{}\nlength:{:.3f}{}\nwidth:{:.3f}{}\nheight:{:.3f}{}'.format(
            body.name,
            covUnit * bBox.length, defLenUnit,
            covUnit * bBox.width, defLenUnit,
            covUnit * bBox.height, defLenUnit,
        ))

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


def getBoundingBox(
    body :adsk.fusion.BRepBody) -> adsk.core.OrientedBoundingBox3D:

    # get occ
    occ :adsk.fusion.Occurrence = body.assemblyContext

    # get Vector3D
    mat :adsk.core.Matrix3D = adsk.core.Matrix3D.create()
    if occ:
        mat = occ.transform

    _, xAxis, yAxis, _ = mat.getAsCoordinateSystem()

    # get MeasureManager
    app :adsk.fusion.Application = adsk.core.Application.get()
    measMgr :adsk.core.MeasureManager = app.measureManager

    return measMgr.getOrientedBoundingBox(body, yAxis, xAxis)


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

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

 

Message 3 of 5
corijn
in reply to: kandennti

Hi, thanks for your response. I tried it out but it seems to be doing the same thing: it's using the assembly context/orientation of the component. I would need to extract that Vector3D (xAxis, yAxis) from the body itself, by analyzing its geometry. Something like comparing edge or surface orientations and deriving a useable axis alignment from those. Your example does show the use of:

measMgr.getOrientedBoundingBox(body, yAxis, xAxis)

 which is part of what I'm looking for....

Message 4 of 5
kandennti
in reply to: corijn

@corijn .

 

Changed to use the getPrincipalAxes method to create a Minimal Box.

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-E05D2232-BCD2-4588-8C1A-9FC766AAFA41 

# Fusion360API Python script
import adsk.core, adsk.fusion, traceback

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

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

        # get OrientedBoundingBox3D
        body = sel.entity
        bBox :adsk.core.OrientedBoundingBox3D = getBoundingBox(body)

        # Confirmation
        initBoundingBox(body, bBox)

        # show info
        unitsMgr = des.unitsManager
        defLenUnit = unitsMgr.defaultLengthUnits
        covUnit = unitsMgr.convert(1, unitsMgr.internalUnits, defLenUnit)

        ui.messageBox('{}\nlength:{:.3f}{}\nwidth:{:.3f}{}\nheight:{:.3f}{}'.format(
            body.name,
            covUnit * bBox.length, defLenUnit,
            covUnit * bBox.width, defLenUnit,
            covUnit * bBox.height, defLenUnit,
        ))

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


def getBoundingBox(
    body :adsk.fusion.BRepBody) -> adsk.core.OrientedBoundingBox3D:

    # get vecter3D
    prop :adsk.fusion.PhysicalProperties = body.physicalProperties
    _, xAxis, yAxis, _ = prop.getPrincipalAxes()

    # get MeasureManager
    app :adsk.fusion.Application = adsk.core.Application.get()
    measMgr :adsk.core.MeasureManager = app.measureManager

    return measMgr.getOrientedBoundingBox(body, yAxis, xAxis)


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

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


def initBoundingBox(
    body :adsk.fusion.BRepBody,
    bBox :adsk.core.OrientedBoundingBox3D):

    # brepBody
    tmpMgr :adsk.fusion.TemporaryBRepManager = adsk.fusion.TemporaryBRepManager.get()
    bRepBox = tmpMgr.createBox(bBox)

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

    # add body
    box = adsk.fusion.BRepBody.cast(None)
    if des.designType == adsk.fusion.DesignTypes.DirectDesignType:
        box = root.bRepBodies.add(bRepBox)
        box.opacity = 0.5
    else:
        baseFeatures = root.features.baseFeatures
        baseFeature = baseFeatures.add()
        baseFeature.startEdit()

        try:
            box = root.bRepBodies.add(bRepBox, baseFeature)
            box.opacity = 0.5
        except:
            pass
        finally:
            baseFeature.finishEdit()

 

The initBoundingBox function is for confirmation, so please delete it if you do not need it.

Message 5 of 5
corijn
in reply to: kandennti

Fantastic! I have been googling so much but this GetPrincipalAxes is the perfect solution, no need to iterate the mesh vertices myself! Thank you very much!

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

Post to forums