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: 

Using the API to copy holes from one component to another

6 REPLIES 6
SOLVED
Reply
Message 1 of 7
bloudraak
313 Views, 6 Replies

Using the API to copy holes from one component to another

Let's assume I created a Fusion 360 component that represents a small board computer. The component represents the PCB outline and an arbitrary number of mounting holes. Let's assume I'm creating a new component, which means a tray on which the PCB needs to be mounted. To add spacers/standoffs to the tray, I need to have corresponding points or holes that match the PCB. For this example, the PCB will have four holes.

 

When looking at the holeFeatures of the top face of the PCB, only one is returned instead of 4. I'm assuming this may be because there are four occurrences of the same feature. If that's the case, I can't figure out how to get the other holes.

 

I was able to create a self-contained script to reproduce the issue. 

 

# Author-
# Description-

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


def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        doc = setup(app)
        design = doc.products[0]
        components = design.allComponents
        tray = components.itemByName('Tray')
        component = components.itemByName('Component')
        move_component(component, 2, 2, 0.16)

        face = get_top_face(tray)
        device_holes = component.features.holeFeatures
        sketch = tray.sketches.addWithoutEdges(face)
        sketch.name = 'Standoffs'
        offset_sketch_points = sketch.sketchPoints

        distance = adsk.core.ValueInput.createByString("1.52 mm")
        fastener_diameter = adsk.core.ValueInput.createByString('4 mm')

        points = adsk.core.ObjectCollection.create()
        for h in device_holes:
            px = h.position.x + 2
            py = h.position.y + 2
            pt = offset_sketch_points.add(adsk.core.Point3D.create(px, py, 0))
            points.add(pt)

        holes = tray.features.holeFeatures
        hole = holes.createSimpleInput(fastener_diameter)  # TODO Fix
        hole.setPositionBySketchPoints(points)
        hole.setDistanceExtent(distance)
        hole = holes.add(hole)

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


def get_top_face(component):
    body: adsk.fusion.BRepBody = component.bRepBodies[0]
    faces: adsk.fusion.BRepFaces = body.faces
    return max(faces, key=(lambda f: f.centroid.z))


def move_component(item, x, y, z):
    objects = adsk.core.ObjectCollection.create()
    objects.add(item.bRepBodies.item(0))

    vector = adsk.core.Vector3D.create(x, y, z)
    transform = adsk.core.Matrix3D.create()
    transform.translation = vector

    features = item.features.moveFeatures
    feature = features.createInput(objects, transform)
    features.add(feature)


def setup(app):
    lib = app.materialLibraries.itemByName('Fusion 360 Material Library')
    pcb = lib.materials.itemByName('FR4')
    steel = lib.materials.itemByName('Steel')
    doc = app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)
    design = doc.products[0]
    units_mgr = design.unitsManager
    units_mgr.distanceDisplayUnits = adsk.fusion.DistanceUnits.MillimeterDistanceUnits
    root = design.rootComponent
    setup_component(root, 'Component', 6, 8, 1, pcb)
    setup_tray(root, 17, 17, 2, 4, 6, steel)

    # doc.saveAs("Align Test", folder, '', '')
    return doc


def setup_tray(parent, width, depth, edge, space_width, space_depth, material):
    occurrences = parent.occurrences
    component = occurrences.addNewComponent(adsk.core.Matrix3D.create()).component
    component.name = "Tray"
    component.material = material
    sketch = component.sketches.add(component.xYConstructionPlane)
    sketch.name = component.name
    lines = sketch.sketchCurves.sketchLines
    p1 = adsk.core.Point3D.create(0, 0, 0)
    p2 = adsk.core.Point3D.create(width, depth, 0)
    rectangle = lines.addTwoPointRectangle(p1, p2)
    sketch.geometricConstraints.addHorizontal(rectangle.item(0))
    sketch.geometricConstraints.addHorizontal(rectangle.item(2))
    sketch.geometricConstraints.addVertical(rectangle.item(1))
    sketch.geometricConstraints.addVertical(rectangle.item(3))
    sketch.sketchDimensions.addDistanceDimension(rectangle.item(0).startSketchPoint,
                                                 rectangle.item(0).endSketchPoint,
                                                 adsk.fusion.DimensionOrientations.HorizontalDimensionOrientation,
                                                 adsk.core.Point3D.create(width / 2, -1))
    sketch.sketchDimensions.addDistanceDimension(rectangle.item(3).startSketchPoint,
                                                 rectangle.item(3).endSketchPoint,
                                                 adsk.fusion.DimensionOrientations.VerticalDimensionOrientation,
                                                 adsk.core.Point3D.create(-1, depth / 2))
    extrudes = component.features.extrudeFeatures
    prof = sketch.profiles.item(0)
    distance = adsk.core.ValueInput.createByString("1.66 mm")
    extrude = extrudes.addSimple(prof, distance, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
    body = extrude.bodies.item(0)
    body.name = sketch.name


def setup_component(parent, name, width, depth, edge, material):
    occurrences = parent.occurrences
    component = occurrences.addNewComponent(adsk.core.Matrix3D.create()).component
    component.name = name
    component.material = material
    sketch = component.sketches.add(component.xYConstructionPlane)
    sketch.name = component.name
    lines = sketch.sketchCurves.sketchLines
    p1 = adsk.core.Point3D.create(0, 0, 0)
    p2 = adsk.core.Point3D.create(width, depth, 0)
    rectangle = lines.addTwoPointRectangle(p1, p2)
    sketch.geometricConstraints.addHorizontal(rectangle.item(0))
    sketch.geometricConstraints.addHorizontal(rectangle.item(2))
    sketch.geometricConstraints.addVertical(rectangle.item(1))
    sketch.geometricConstraints.addVertical(rectangle.item(3))
    sketch.sketchDimensions.addDistanceDimension(rectangle.item(0).startSketchPoint,
                                                 rectangle.item(0).endSketchPoint,
                                                 adsk.fusion.DimensionOrientations.HorizontalDimensionOrientation,
                                                 adsk.core.Point3D.create(width / 2, -1))
    sketch.sketchDimensions.addDistanceDimension(rectangle.item(3).startSketchPoint,
                                                 rectangle.item(3).endSketchPoint,
                                                 adsk.fusion.DimensionOrientations.VerticalDimensionOrientation,
                                                 adsk.core.Point3D.create(-1, depth / 2))
    extrudes = component.features.extrudeFeatures
    prof = sketch.profiles.item(0)
    distance = adsk.core.ValueInput.createByString("1.6 mm")
    extrude = extrudes.addSimple(prof, distance, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
    body = extrude.bodies.item(0)
    body.name = sketch.name
    face = body.faces.item(4)  # Top Face
    sketch = component.sketches.addWithoutEdges(face)
    sketch.name = 'Mounting'
    offset_sketch_points = sketch.sketchPoints
    point1 = offset_sketch_points.add(adsk.core.Point3D.create(edge, edge, 0))
    point2 = offset_sketch_points.add(adsk.core.Point3D.create(edge, depth - edge, 0))
    point3 = offset_sketch_points.add(adsk.core.Point3D.create(width - edge, edge, 0))
    point4 = offset_sketch_points.add(adsk.core.Point3D.create(width - edge, depth - edge, 0))
    points = adsk.core.ObjectCollection.create()
    points.add(point1)
    points.add(point2)
    points.add(point3)
    points.add(point4)
    holes = component.features.holeFeatures
    fastener_diameter = '3 mm'
    hole = holes.createSimpleInput(adsk.core.ValueInput.createByString(fastener_diameter))
    hole.setPositionBySketchPoints(points)
    hole.setDistanceExtent(distance)
    hole = holes.add(hole)

And like my actual code, this only creates one hole, which is not sufficient to mount expensive electronics. 

 

How does one get the positions of the other holes? Ignore the setup method; it's just there to aid with the components. 

Software Engineer
https://wernerstrydom.com
6 REPLIES 6
Message 2 of 7
kandennti
in reply to: bloudraak

Hi @bloudraak .

 

It seems that the HoleFeature.position property only holds the first hole position.

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-13eb2982-2ab0-4136-8126-6568c71d7295 

 

Considering that multiple points are available in the GUI, a List or ObjectCollection would be preferable, but it seems that this is not currently possible.

 

Therefore, I changed the setup_component function to use one point for each HoleFeature, and was able to create four holes.

・・・
def setup_component(parent, name, width, depth, edge, material):
・・・
    point4 = offset_sketch_points.add(
        adsk.core.Point3D.create(width - edge, depth - edge, 0))

    # Change to one point per HoleFeature.
    points: adsk.core.ObjectCollection = adsk.core.ObjectCollection.create()
    holes: adsk.fusion.HoleFeatures = component.features.holeFeatures
    fastener_diameter = '3 mm'

    for point in [point1, point2, point3, point4]:
        points.clear()
        points.add(point)

        hole: adsk.fusion.HoleFeatureInput = holes.createSimpleInput(
            adsk.core.ValueInput.createByString(fastener_diameter))
        hole.setPositionBySketchPoints(points)
        hole.setDistanceExtent(distance)
        hole = holes.add(hole)
Message 3 of 7
bloudraak
in reply to: kandennti

While that’s certainly possible within the scope of the code provided, the reality is that I’m actually adding the holes using the UI and that the component is saved in its own file. And if the holes were created together, would run into this issue when creating a tray for that component.  

the reason this is automated is because there’s 40+ component types, and some there may be as many as 20 components of a certain type. So I need to automate the designs of trays so I can run some tests to see which ones work. 

The setup function was merely done to make the issue self contained, so anyone can reproduce it.

 

 

 

 

Software Engineer
https://wernerstrydom.com
Message 4 of 7
kandennti
in reply to: bloudraak

@bloudraak .

 

Since it is not provided as an API, it seems that the only way is to find a shape that can be determined as a hole from the geometry.

Message 5 of 7
bloudraak
in reply to: kandennti

Do you know of any examples of how to find that on the bottom plane of a body, component, or sketch? While the holes are user-added, the body, component, and sketches all have consistent names since they are generated as part of another script.  The sketch may get modified, while holes are definitely being modified.  

Software Engineer
https://wernerstrydom.com
Message 6 of 7
kandennti
in reply to: bloudraak

@bloudraak .

 

I looked carefully at the holeFeature properties and found all the sketch point information in holePositionDefinition.sketchPoints.

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-23e8741b-7d01-4860-b4f6-7e0f3523283a 

 

I think this will give you the result you are looking for.

・・・
def run(context):
・・・
        distance = adsk.core.ValueInput.createByString("1.52 mm")
        fastener_diameter = adsk.core.ValueInput.createByString('4 mm')

        points = adsk.core.ObjectCollection.create()
        sktPnt: adsk.fusion.SketchPoint
        for h in device_holes:
            for sktPnt in h.holePositionDefinition.sketchPoints:
                wPnt: adsk.core.Point3D = sktPnt.worldGeometry
                pt = offset_sketch_points.add(
                    adsk.core.Point3D.create(
                        wPnt.x + 2,
                        wPnt.y + 2,
                        0
                    )
                )
                points.add(pt)
            # px = h.position.x + 2
            # py = h.position.y + 2
            # pt = offset_sketch_points.add(adsk.core.Point3D.create(px, py, 0))
            # points.add(pt)

        holes = tray.features.holeFeatures
        hole = holes.createSimpleInput(fastener_diameter)  # TODO Fix
・・・
Message 7 of 7
bloudraak
in reply to: kandennti

That worked for me! Thanks. 

Software Engineer
https://wernerstrydom.com

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