Fusion Guide
intermediate9 min read

Creating Sketches and Features with the API

Create sketches, curves, constraints and dimensions, then extrude, revolve, fillet, pattern and combine, with complete runnable Python examples.

Overview#

Modeling by script follows the same logic as modeling by hand: pick a plane, draw a sketch, close a profile, and feed the profile to a feature. The API mirrors this structure exactly, which makes scripts easy to reason about once you can do it manually. This page builds up from a single sketch circle to a complete parametric part, covering sketch geometry, constraints, dimensions, profile selection, and the most used features.

Every example assumes the standard preamble:

import adsk.core, adsk.fusion, traceback
app = adsk.core.Application.get()
ui = app.userInterface
design = adsk.fusion.Design.cast(app.activeProduct)
root = design.rootComponent

Remember: the API length unit is centimetres, so 1.0 equals 10 mm.

Where to find it#

The relevant reference pages are Sketches, Sketch, SketchCurves, SketchLines, SketchCircles, SketchArcs, SketchPoints, GeometricConstraints, SketchDimensions, Profile, Features, ExtrudeFeatures, ExtrudeFeatureInput, RevolveFeatures, FilletFeatures, ChamferFeatures, CombineFeatures, HoleFeatures, and RectangularPatternFeatures. In the Fusion interface the equivalents are Create Sketch, the sketch palette, and the Solid > Create panel.

Step by step#

1. Create a sketch on a plane#

A sketch needs a planar reference: a construction plane or a planar face.

sketches = root.sketches
sketch = sketches.add(root.xYConstructionPlane)   # also xZConstructionPlane, yZConstructionPlane
sketch.name = 'Base profile'

To sketch on a face, pass the BRepFace instead: sketches.add(some_planar_face). For a sketch in a component other than the root, use component.sketches and a plane from that component. Sketches added with a face reference are associative to that face.

2. Draw curves#

Sketch geometry lives in sketch.sketchCurves, with sub-collections for lines, arcs, circles, ellipses, and splines. Points are adsk.core.Point3D (the z coordinate is in the sketch plane's local space, normally 0).

curves = sketch.sketchCurves
p = adsk.core.Point3D.create

# Circle: center, radius (cm)
circle = curves.sketchCircles.addByCenterRadius(p(0, 0, 0), 2.0)

# Line between two points
line = curves.sketchLines.addByTwoPoints(p(0, 0, 0), p(5, 0, 0))

# Two-point rectangle returns a SketchLineList with four lines
rect = curves.sketchLines.addTwoPointRectangle(p(-3, -2, 0), p(3, 2, 0))

# Three-point arc
arc = curves.sketchArcs.addByThreePoints(p(0, 0, 0), p(1, 1, 0), p(2, 0, 0))

Rectangle methods return a SketchLineList of four lines. > Note: not confirmed in Autodesk's documentation: that the rectangle lines arrive with constraints already applied (check in the sketch after running). Other useful methods: addCenterPointRectangle, addThreePointRectangle, sketchCircles.addByTwoPoints / addByThreePoints, sketchArcs.addByCenterStartSweep, and sketchCurves.sketchFittedSplines.add(points) where points is an ObjectCollection of Point3D.

3. Constrain and dimension#

# Make the circle concentric with the sketch origin
sketch.geometricConstraints.addCoincident(circle.centerSketchPoint, sketch.originPoint)

# Add a diameter dimension
dim = sketch.sketchDimensions.addDiameterDimension(circle, p(3, 3, 0))
dim.parameter.expression = '30 mm'

addDiameterDimension(entity, textPoint) places the dimension text at textPoint. Setting dim.parameter.expression is how you drive the size. Other dimension helpers include addDistanceDimension(pointOne, pointTwo, orientation, textPoint) (orientation from adsk.fusion.DimensionOrientations), addRadialDimension, and addAngularDimension. GeometricConstraints offers addCoincident, addCollinear, addConcentric, addEqual, addHorizontal, addVertical, addHorizontalPoints, addVerticalPoints, addMidPoint, addParallel, addPerpendicular, addSymmetry, addTangent, addSmooth, addOffset, addPolygon, and the pattern constraints. There is no addFix: a sketch entity is fixed through its isFixed property.

A constraint call can fail if the geometry already violates it, or if it would over-constrain the sketch. Wrap risky calls and handle exceptions.

4. Find profiles#

When a sketch contains closed loops, Fusion computes profiles in sketch.profiles. Profiles are what extrude and revolve consume.

prof = sketch.profiles.item(0)          # a Profile

With nested closed curves (a circle inside a rectangle), profiles returns separate regions: the annulus between the shapes and the inner disc. To pick the right one, test properties, for example by area:

def largest_profile(sketch):
    best, best_area = None, -1
    for pr in sketch.profiles:
        area = pr.areaProperties().area
        if area > best_area:
            best, best_area = pr, area
    return best

profile.areaProperties() returns an AreaProperties object with area, centroid, and more. For several profiles at once, build an ObjectCollection and add each.

5. Extrude#

extrudes = root.features.extrudeFeatures
ext_input = extrudes.createInput(prof, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)

distance = adsk.core.ValueInput.createByString('15 mm')
ext_input.setDistanceExtent(False, distance)    # False = not symmetric
ext = extrudes.add(ext_input)
body = ext.bodies.item(0)
body.name = 'Boss'

Operations come from adsk.fusion.FeatureOperations: NewBodyFeatureOperation, JoinFeatureOperation, CutFeatureOperation, IntersectFeatureOperation, NewComponentFeatureOperation.

setDistanceExtent, setAllExtent, setTwoSidesDistanceExtent, setOneSideToExtent, setTwoSidesToExtent and the taperAngle property on ExtrudeFeatureInput are all RETIRED. They still work, but the reference replaces them with setOneSideExtent(extent, direction, taperAngle), setTwoSidesExtent(...) and setSymmetricExtent(distance, isFullLength), which take an extent definition object. Autodesk's current extrude sample builds the extent like this:

ext_input = extrudes.createInput(prof, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
extent = adsk.fusion.DistanceExtentDefinition.create(adsk.core.ValueInput.createByString('15 mm'))
ext_input.setOneSideExtent(extent, adsk.fusion.ExtentDirections.PositiveExtentDirection)
# taper: setOneSideExtent(extent, direction, adsk.core.ValueInput.createByString('2 deg'))
ext = extrudes.add(ext_input)

Other extent definitions are ThroughAllExtentDefinition.create() and ToEntityExtentDefinition.create(entity, isChained), and start definitions such as OffsetStartDefinition and FromEntityStartDefinition go on ext_input.startExtent. extrudes.addSimple(profile, distance, operation) creates a plain distance extrude in one call. The older setters are kept in the examples below only where Autodesk's own samples still use them.

For ExtentDirections the values are PositiveExtentDirection, NegativeExtentDirection and SymmetricExtentDirection. Use NegativeExtentDirection to extrude the other way.

6. Revolve#

revolves = root.features.revolveFeatures
rev_input = revolves.createInput(prof, axis_line, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
angle = adsk.core.ValueInput.createByString('360 deg')
rev_input.setAngleExtent(False, angle)
rev = revolves.add(rev_input)

The axis argument can be a SketchLine, a construction axis, or a linear edge.

7. Fillet and chamfer#

Fillets operate on edges, gathered into an ObjectCollection. FilletFeatureInput.addConstantRadiusEdgeSet is retired; Autodesk's sample uses edgeSetInputs.

edges = adsk.core.ObjectCollection.create()
for e in body.edges:
    edges.add(e)

fillets = root.features.filletFeatures
fillet_input = fillets.createInput()
fillet_input.edgeSetInputs.addConstantRadiusEdgeSet(
    edges, adsk.core.ValueInput.createByString('1 mm'), True)  # True = tangent chain
fillets.add(fillet_input)

edgeSetInputs also provides variable-radius and chord-length edge sets. Chamfers use chamferFeatures.createInput2() (the older createInput is retired) and chamfer_input.chamferEdgeSets.addEqualDistanceChamferEdgeSet(edges, distance, tangentChain).

8. Pattern, mirror, and combine#

# Rectangular pattern of a body feature
inputs = adsk.core.ObjectCollection.create()
inputs.add(ext)                                    # pattern the feature
quantity = adsk.core.ValueInput.createByString('4')
spacing = adsk.core.ValueInput.createByString('20 mm')
pat_input = root.features.rectangularPatternFeatures.createInput(
    inputs, root.xConstructionAxis,
    quantity, spacing,
    adsk.fusion.PatternDistanceType.SpacingPatternDistanceType)
root.features.rectangularPatternFeatures.add(pat_input)

# Combine two bodies
tools = adsk.core.ObjectCollection.create()
tools.add(tool_body)
comb_input = root.features.combineFeatures.createInput(target_body, tools)
comb_input.operation = adsk.fusion.FeatureOperations.CutFeatureOperation
root.features.combineFeatures.add(comb_input)

Autodesk's sample patterns a body (inputs.add(body)) and adds a second direction with pat_input.setDirectionTwo(yAxis, quantity, distance). > Note: not confirmed in Autodesk's documentation: which input types each pattern accepts beyond what the samples show; check the reference for the pattern you use.

A complete example: parametric plate with holes#

import adsk.core, adsk.fusion, traceback

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

        doc = app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)
        design = adsk.fusion.Design.cast(doc.products.itemByProductType('DesignProductType'))
        root = design.rootComponent

        # Parameters first, so every feature can reference them.
        params = design.userParameters
        params.add('plate_w', adsk.core.ValueInput.createByString('80 mm'), 'mm', 'Plate width')
        params.add('plate_d', adsk.core.ValueInput.createByString('50 mm'), 'mm', 'Plate depth')
        params.add('plate_t', adsk.core.ValueInput.createByString('6 mm'),  'mm', 'Thickness')
        params.add('hole_d',  adsk.core.ValueInput.createByString('6 mm'),  'mm', 'Hole diameter')

        # Plate sketch: a center rectangle dimensioned by parameters.
        sk = root.sketches.add(root.xYConstructionPlane)
        sk.name = 'Plate'
        pt = adsk.core.Point3D.create
        lines = sk.sketchCurves.sketchLines
        rect = lines.addCenterPointRectangle(pt(0, 0, 0), pt(4, 2.5, 0))

        # Do not assume line order: find one horizontal and one vertical side by geometry.
        horiz = vert = None
        for ln in rect:
            g = ln.geometry                      # Line3D
            if abs(g.startPoint.y - g.endPoint.y) < 1e-6:
                horiz = horiz or ln
            else:
                vert = vert or ln

        dims = sk.sketchDimensions
        d_w = dims.addDistanceDimension(horiz.startSketchPoint, horiz.endSketchPoint,
                                         adsk.fusion.DimensionOrientations.HorizontalDimensionOrientation,
                                         pt(0, -3.5, 0))
        d_w.parameter.expression = 'plate_w'
        d_d = dims.addDistanceDimension(vert.startSketchPoint, vert.endSketchPoint,
                                         adsk.fusion.DimensionOrientations.VerticalDimensionOrientation,
                                         pt(5, 0, 0))
        d_d.parameter.expression = 'plate_d'

        ext_in = root.features.extrudeFeatures.createInput(
            sk.profiles.item(0), adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
        ext_in.setOneSideExtent(
            adsk.fusion.DistanceExtentDefinition.create(adsk.core.ValueInput.createByString('plate_t')),
            adsk.fusion.ExtentDirections.PositiveExtentDirection)
        plate = root.features.extrudeFeatures.add(ext_in)
        plate.bodies.item(0).name = 'Plate'

        # Four corner holes on the top face (plate sits at z = 0 .. plate_t).
        top_face = None
        for f in plate.bodies.item(0).faces:
            if isinstance(f.geometry, adsk.core.Plane):
                n = f.geometry.normal
                if abs(n.z - 1) < 1e-6 and f.centroid.z > 0.1:
                    top_face = f
                    break

        hs = root.sketches.add(top_face)
        hs.name = 'Holes'
        # Sketch points are in the sketch's model-space-to-sketch mapping.
        for sx, sy in ((3, 1.8), (-3, 1.8), (3, -1.8), (-3, -1.8)):
            model_pt = adsk.core.Point3D.create(sx, sy, top_face.centroid.z)
            sp = hs.modelToSketchSpace(model_pt)
            c = hs.sketchCurves.sketchCircles.addByCenterRadius(sp, 0.3)
        # Dimension the first hole's diameter and make others equal.
        circles = hs.sketchCurves.sketchCircles
        dd = hs.sketchDimensions.addDiameterDimension(circles.item(0), adsk.core.Point3D.create(0, 0, 0))
        dd.parameter.expression = 'hole_d'
        for i in range(1, circles.count):
            hs.geometricConstraints.addEqual(circles.item(0), circles.item(i))

        # Cut every hole profile.
        cut_profiles = adsk.core.ObjectCollection.create()
        for pr in hs.profiles:
            cut_profiles.add(pr)
        cut_in = root.features.extrudeFeatures.createInput(
            cut_profiles, adsk.fusion.FeatureOperations.CutFeatureOperation)
        # Through-all, going into the plate from the top face.
        cut_in.setOneSideExtent(adsk.fusion.ThroughAllExtentDefinition.create(),
                                adsk.fusion.ExtentDirections.NegativeExtentDirection)
        root.features.extrudeFeatures.add(cut_in)

        ui.messageBox('Plate created. Change plate_w in the Parameters dialog to see it update.')
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

Every call in this script was checked against the reference and Autodesk's samples (the extrude input now uses setOneSideExtent and ThroughAllExtentDefinition, and the dimension lines are found by geometry instead of by index). Two behaviours depend on Fusion and were not confirmed in the documentation: that the extrude direction NegativeExtentDirection points into the plate for a sketch created on the top face, and the in-plane orientation of that sketch (which is why the hole centres go through modelToSketchSpace). If the cut does nothing, try PositiveExtentDirection. A cleaner approach to holes is root.features.holeFeatures with createSimpleInput(diameter) and setPositionBySketchPoints(...), as in Autodesk's hole sample (which also shows setDistanceExtent and setToTappedHole(threadInfo)).

Options reference#

Class / methodWhat it doesNotes
sketches.add(planeOrFace)New sketchSketch is empty
sketchCircles.addByCenterRadius(pt, r)Circler in cm
sketchLines.addTwoPointRectangleRectangleReturns four lines, constrained
geometricConstraints.addCoincidentJoin point to point/curveFails if impossible
sketchDimensions.addDiameterDimensionDiameter dimensionSet .parameter.expression
sketch.profilesClosed regionsRecomputed when geometry changes
extrudeFeatures.createInput(profile, op)Extrude inputProfile may be collection
setOneSideExtent(DistanceExtentDefinition, direction[, taper])Distance extentUse a string ValueInput for a parametric link; setDistanceExtent is retired
revolveFeatures.createInput(profile, axis, op)Revolve inputsetAngleExtent
filletFeatures.createInput()Fillet inputedgeSetInputs.addConstantRadiusEdgeSet
combineFeatures.createInput(target, tools)BooleanSet operation
feature.timelineObjectTimeline entryFor rollback, grouping

Tips and pitfalls#

Tip: Set sketch.isComputeDeferred = True before adding many curves and set it back to False afterwards; the reference says it turns sketch computing off temporarily to increase performance. Caveat from the same topic: when editing an existing sketch that features depend on, deferring can give bad results in those features, so do not defer there (or roll the timeline back first).

Tip: Prefer ValueInput.createByString with unit text or parameter names over createByReal. It keeps your model parametric and avoids centimetre mistakes.

Warning: General guidance, not Autodesk data: do not cache a sketch.profiles index across geometry changes; re-fetch the profile (Autodesk's own samples pick profiles.item(0) or item(1) only right after drawing).

Warning: Create sketches from the component that owns the plane or face. If you work in a component other than the root with geometry from another one, ExtrudeFeatureInput.creationOccurrence exists for that case.

Warning: Feature creation can fail when the result would be invalid, such as a cut that removes nothing. Check feature.healthState (HealthyFeatureHealthState, WarningFeatureHealthState, ErrorFeatureHealthState) and errorOrWarningMessage as Autodesk's extrude sample does, and catch exceptions per feature in generator scripts.

Practice exercise#

Write a script that creates a washer: a parametric ring with outer diameter 20 mm, inner diameter 10 mm and thickness 2 mm. Use two concentric circles in one sketch, dimension them with user parameters od, id_, and thk, find the annular profile by comparing areas (it is the profile with the larger area but not the full disc), extrude it as a new body, and apply a 0.3 mm fillet on the outer edges. Expected result: a 2 mm thick ring whose size changes when you edit the parameters.

  • Object model
  • Parameters
  • Event handlers
  • Packaging add-ins

Sources

Checked against these Autodesk help topics (opens autodesk.com):

Related pages