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: 

Move a component to world position

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

Move a component to world position

Hello, I am having a hard time understanding how to move a component to be aligned with the world. I have this situation:

 

Joshuamursic_0-1689016997582.png

and I am trying to move the top coordinate system to be the same as the bottom one. I am using this code.

import adsk.core,adsk.fusion

app = adsk.core.Application.get()
ui = app.userInterface

def move():
    occ:adsk.fusion.Occurrence = ui.activeSelections.item(0).entity
    occCoordinateSystem = occ.transform2.getAsCoordinateSystem()
    trans = adsk.core.Matrix3D.create()
    trans.setToAlignCoordinateSystems(occCoordinateSystem[0],
                                        occCoordinateSystem[1],
                                        occCoordinateSystem[2],
                                        occCoordinateSystem[3],
                                        adsk.core.Point3D.create(0,0,0),
                                        adsk.core.Vector3D.create(1,0,0),
                                        adsk.core.Vector3D.create(0,1,0),
                                        adsk.core.Vector3D.create(0,0,1))

    occ.transform2 = trans

 And I am getting these results:

Joshuamursic_1-1689017477572.png

 

How do I correctly move these coordinate systems to be the same?

6 REPLIES 6
Message 2 of 7

Hi,

 

The following code do the trick:

 

def move_comp_to_comp():
    # select components 1 and 2
    comp1 = root.occurrences.item(0) # 1 is source
    comp2 = root.occurrences.item(1) # 2 is target

    # multiply m1 by its inverted matrix to position it on origin
    m1 = comp1.transform2.copy()
    m1i = comp1.transform2.copy()
    m1i.invert()
    m1.transformBy(m1i)

    # multiply m1 by m2 to position on target
    m2 = comp2.transform2.copy()
    m1.transformBy(m2)

    # move component 1 with matrix m1
    comp1.transform2 = m1

 

 

It used matrix of first component to calculate rotation and placement from current position/orientation to origin; then use matrix of the second component to rotate and place to target.

 

I hope this could help.

 

Regards,

Jorge Jaramillo

 

Message 3 of 7

Thank you very much, @Jorge_Jaramillo this works great, but I am having some trouble implementing it. The function I am working on is to get the silhouette of a mesh body at a particular axis.  In the code below I transform the mesh so that the Z axis is aligned with the Y axis and then us graham scan to get the concave hull of the mesh body.

 

The problem I think, is that the node coordinates for the mesh are still relative to the component that it is in. So even if I transform it, the silhouette will always be along the Z axis of the component.

I want to be able to create a silhouette of the mesh at any axis that I provide. but I am not sure how to accomplish this. Any help would be really appreciated.

 

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

app = adsk.core.Application.get()
ui  = app.userInterface

def run(context):
    design:adsk.fusion.Design = app.activeProduct
    root = design.rootComponent
    stockBlockOcc = root.occurrences.item(0)
    stockBlock = stockBlockOcc.bRepBodies.itemByName("Stock")
    
    #Select an occurence
    selection:adsk.core.Selection = ui.activeSelections[0]
    occurence:adsk.fusion.Occurrence = selection.entity
    originalMatrix = occurence.transform2
    
    # Move occurence to origin and rotate it so that the occurence Z is aligned with world Y
    mat = adsk.core.Matrix3D.create()
    mat.setWithArray([1,0,0,0,
                      0,0,1,0,
                      0,-1,0,0,
                      0,0,0,1])
    move_occ_to_occ(occurence,mat)

    # Create the point list for graham scan
    mesh = occurence.component.meshBodies.item(0)
    nodes = mesh.displayMesh.nodeCoordinates
    pts = []
    for node in nodes:
        pts.append([node.x,node.y])

    # Get silhouette of the mesh xy points
    xySilhouettePoints = graham_scan(pts)

    # Get the z center of the mesh
    centerPoint = boundingBoxCenter(occurence.boundingBox)
    zHeight = centerPoint.z

    # Create the spline point collection
    spline = adsk.core.ObjectCollection.create()
    for point in xySilhouettePoints:
        pnt = adsk.core.Point3D.create(point[0],point[1],zHeight)
        spline.add(pnt)

    # Create the sketch
    sketch = occurence.component.sketches.add(root.xYConstructionPlane)
    sketch.isComputeDeferred = True
    sketch.arePointsShown = False
    sketch.sketchCurves.sketchFittedSplines.add(spline)
    sketch.isComputeDeferred = False


def graham_scan(points):
    def left_turn(p1, p2, p3):
        return (p2[0] - p1[0]) * (p3[1] - p1[1]) - (p2[1] - p1[1]) * (p3[0] - p1[0]) > 0

    pivot = min(points, key=lambda point: (point[1], point[0]))
    points.remove(pivot)
    points.sort(key=lambda point: (math.atan2(point[1] - pivot[1], point[0] - pivot[0]), (point[1] - pivot[1])**2 + (point[0] - pivot[0])**2))
    stack = [pivot, points[0], points[1]]

    for point in points[2:]:
        while len(stack) > 1 and not left_turn(stack[-2], stack[-1], point):
            stack.pop()
        stack.append(point)

    return stack


def move_occ_to_occ(occ1:adsk.fusion.Occurrence,occ2:adsk.fusion.Occurrence=None):
    '''Move a occurence to origin or to another occurence or matrix is provided'''

    # multiply m1 by its inverted matrix to position it on origin
    m1 = occ1.transform2.copy()
    m1i = occ1.transform2.copy()
    m1i.invert()
    m1.transformBy(m1i)

    # multiply m1 by m2 to position on target
    if occ2 != None:
        if occ2.objectType == "adsk::core::Occurrence":
            m2 = occ2.transform2.copy()
            m1.transformBy(m2)
        if occ2.objectType == "adsk::core::Matrix3D":
            m2 = occ2
            m1.transformBy(m2)

    # move component 1 with matrix m1
    occ1.transform2 = m1


def boundingBoxCenter(boundingBox:adsk.core.BoundingBox3D):
    '''Returns the center point of a bounding box'''
    maxP = boundingBox.maxPoint
    minP = boundingBox.minPoint
    xCenter = maxP.x - (maxP.x - minP.x)/2
    yCenter = maxP.y - (maxP.y - minP.y)/2
    zCenter = maxP.z - (maxP.z - minP.z)/2
    centerPoint = adsk.core.Point3D.create(xCenter,yCenter,zCenter)
    return centerPoint

 

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

Hi @Joshua.mursic -San.

 

I have not found the answer yet, but I will describe what I noticed.
One is that the move_occ_to_occ function changes the transform2, but the position is not fixed, so the coordinate values after the change cannot be obtained.

 

A relatively recent topic can be found here.

https://forums.autodesk.com/t5/fusion-360-api-and-scripts/adding-sketch-to-face-reverts-component-tr... 

・・・
def move_occ_to_occ(occ1:adsk.fusion.Occurrence,occ2:adsk.fusion.Occurrence=None):
・・・
    # move component 1 with matrix m1
    occ1.transform2 = m1

    occ1.component.parentDesign.snapshots.add()
・・・

 

Another issue is proxy.
A relatively recent topic is here.

https://forums.autodesk.com/t5/fusion-360-api-and-scripts/getting-point-location-after-transform/m-p... 

However, I have not been able to confirm that the createForAssemblyContext method of MeshBody is functioning properly.
I will write the result after some tests.

Message 5 of 7
kandennti
in reply to: kandennti

@Joshua.mursic -San.

 

The attached data is in this state.

1.png

The occurrence is misaligned with the root component.
For the MeshBody in this state of occurrence, I think it is correct that the position of the nodes of the mesh is different between NativeObject and Proxy.
Therefore, I created a script that outputs the first coordinate value of displayMesh.nodeCoordinates.

# Fusion360API Python script

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

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

        occ: fusion.Occurrence = root.occurrences[0]

        # native
        meshBody: fusion.MeshBody = occ.component.meshBodies[0]
        nativeNodeList = list(meshBody.displayMesh.nodeCoordinates)
        print(f"Native:{nativeNodeList[0].asArray()}")

        # proxy
        meshProxy: fusion.MeshBody = meshBody.createForAssemblyContext(occ)
        proxyNodeList = list(meshProxy.displayMesh.nodeCoordinates)
        print(f"Proxy:{proxyNodeList[0].asArray()}")

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

 

Here is the execution result.

Native:(6.0, -3.0, 1.0)
Proxy:(6.0, -3.0, 1.0)

Unlike the expected result, it returns the same value. I feel that perhaps the Proxy in the MeshBody is not working.

 

What I can do now is to transform the Point3D obtained by displayMesh.nodeCoordinates with transformBy.
I wanted to make a sample, but it is nighttime in Japan right now and I am sleepy....

 

 

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

@Joshua.mursic .

 

I did not move any components, but projected the nodes of the mesh to create a silhouette.
The graham_scan function was used as is.

# Fusion360API Python script

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

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

        # select body
        msg: str = 'Select Mesh Body'
        selFilter: str = 'MeshBodies'
        sel: core.Selection = selectEnt(msg, selFilter)
        if not sel: return

        meshBody: fusion.MeshBody = sel.entity

        # select constructionLine
        msg: str = 'Select Construction Line'
        selFilter: str = 'ConstructionLines'
        sel: core.Selection = selectEnt(msg, selFilter)
        if not sel: return

        constAxis: fusion.ConstructionAxis = sel.entity

        # get projected points and planes
        points, plane = get_project_mesh_nodes(
            meshBody,
            constAxis.geometry.direction,
        )

        # create sketch
        skt: fusion.Sketch = create_sketch(
            plane,
            meshBody.assemblyContext
        )

        # create silhouette
        create_silhouette(
            points,
            skt,
        )

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


def get_project_mesh_nodes(
    meshBody: fusion.MeshBody,
    vec: core.Vector3D,
) -> tuple[list[core.Point3D], core.Plane]:

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

    occ: fusion.Occurrence = meshBody.assemblyContext
    comp: fusion.Component = None
    mat: core.Matrix3D = None

    if occ:
        comp = occ.component
        mat = occ.transform2
    else:
        comp = app.activeProduct.rootComponent
        mat = core.Matrix3D.create()

    origin: core.Point3D = comp.originConstructionPoint.geometry
    origin.transformBy(mat)

    mat.invert()

    plane: core.Plane = core.Plane.create(
        origin,
        vec,
    )
    plane.transformBy(mat)

    nodes: list[core.Point3D] = meshBody.displayMesh.nodeCoordinates

    res: core.MeasureResults = None
    points = []
    for p in nodes:
        res = measMgr.measureMinimumDistance(plane, p)
        points.append(res.positionOne)

    return points, plane


def create_silhouette(
    points: list[core.Point3D],
    skt: fusion.Sketch,
) -> fusion.SketchFittedSpline:

    points = [skt.modelToSketchSpace(p) for p in points]
    points2d = [(p.x, p.y) for p in points]

    graham_scan(points2d)
    xySilhouettePoints = graham_scan(points2d)

    points3d = [core.Point3D.create(p[0], p[1], 0) for p in xySilhouettePoints]
    splinePoints: core.ObjectCollection = core.ObjectCollection.createWithArray(
        points3d
    )
    splinePoints.add(splinePoints[0])

    skt.isComputeDeferred = True
    skt.arePointsShown = False
    crv: fusion.SketchFittedSpline = skt.sketchCurves.sketchFittedSplines.add(
        splinePoints
    )
    skt.isComputeDeferred = False

    return crv


def create_sketch(
    plane: core.Plane,
    occ: fusion.Occurrence,
) -> fusion.Sketch:

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

    comp: fusion.Component = None
    if occ:
        comp = occ.component
        mat: core.Matrix3D = occ.transform2
        plane.transformBy(mat)
    else:
        comp = app.activeProduct.rootComponent

    baseFeat: fusion.BaseFeature = None
    if des.designType == fusion.DesignTypes.ParametricDesignType:
        baseFeat = comp.features.baseFeatures.add()

    planes: fusion.ConstructionPlanes = comp.constructionPlanes
    planeIpt: fusion.ConstructionPlaneInput = planes.createInput()
    constPlane: fusion.ConstructionPlane = None
    if baseFeat:
        try:
            baseFeat.startEdit()
            planeIpt.setByPlane(plane)
            constPlane = planes.add(planeIpt)
        except:
            pass
        finally:
            baseFeat.finishEdit()
    else:
        planeIpt.setByPlane(plane)
        constPlane = planes.add(planeIpt)

    return comp.sketches.add(
        constPlane
    )


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

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


def graham_scan(points):
    def left_turn(p1, p2, p3):
        return (p2[0] - p1[0]) * (p3[1] - p1[1]) - (p2[1] - p1[1]) * (p3[0] - p1[0]) > 0

    pivot = min(points, key=lambda point: (point[1], point[0]))
    points.remove(pivot)
    points.sort(key=lambda point: (math.atan2(point[1] - pivot[1], point[0] - pivot[0]), (point[1] - pivot[1])**2 + (point[0] - pivot[0])**2))
    stack = [pivot, points[0], points[1]]

    for point in points[2:]:
        while len(stack) > 1 and not left_turn(stack[-2], stack[-1], point):
            stack.pop()
        stack.append(point)

    return stack

 

It is certainly faster than this method.

https://forums.autodesk.com/t5/fusion-360-api-and-scripts/silhouette-of-a-mesh-body/m-p/11958582#M19... 

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

Thank you for the code @kandennti, that works really well.

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