Aligning component Origin to Body

Aligning component Origin to Body

corijn
Explorer Explorer
1,201 Views
4 Replies
Message 1 of 5

Aligning component Origin to Body

corijn
Explorer
Explorer

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?

 
0 Likes
Accepted solutions (1)
1,202 Views
4 Replies
Replies (4)
Message 2 of 5

kandennti
Mentor
Mentor

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

 

0 Likes
Message 3 of 5

corijn
Explorer
Explorer

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....

0 Likes
Message 4 of 5

kandennti
Mentor
Mentor
Accepted solution

@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.

0 Likes
Message 5 of 5

corijn
Explorer
Explorer

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!

0 Likes