Using Temporary B-Rep in Fusion 360

In the December 2017 update of Fusion 360, a capability referred to as “Temporary B-Rep” was added to the API.  This is one of those features that when you first see it you might say, “why would I ever use that?”.  It is a feature that’s not used very often but when you do need it, it is a great tool.  It can be used for many different things so there’s not a good simple answer to where it would best be used.  However, there was a question posted on the Fusion 360 API forum that I thought would be a good demonstration of the capabilities of temporary B-Rep.

What the user in the forum is trying to accomplish is to create a series of holes in a circular cylinder.  The problem he ran into is that he knows exactly where the hole should be but it’s difficult to create a hole at an arbitrary location.  Another possible option is to create a cylinder by extruding a circle, but it has the same issues as the hole feature in defining the position.  A cylinder primitive might seem like a good choice, but they’re not supported by the API.

I made a choice not to expose any of the solid primitives in the API because of how the primitives have been designed in Fusion 360.  If you use the user-interface to create a cylinder primitive, the first thing it asks is for you to select a plane.  You then choose a location on the plane and define the diameter and length of the cylinder.  It’s really creating a temporary sketch on the selected plane and creating a circle that is extruded to create the cylinder.  Because the sketch it temporary you don’t have access to the circle so you can’t control the position of the cylinder.  Because of the dependency on a plane and the temporary sketch, it would make it difficult to expose the placement in the API and wouldn’t really be any different than creating a sketch and extruding it anyway.  And then you have full control over the sketch and resulting cylinder.  Anyway, cylinder primitives are not a solution to this problem either.

Let’s look at temporary B-Rep.  When you’re designing, Fusion 360 is using a modeling engine to create the actual geometry you see.  Fusion 360 provides the user-interface to gather the needed information to pass to the engine and it does the real modeling work.  Fusion 360 then takes the result and displays it.  Fusion 360 is also adding the parametric intelligence to be able to update the model when changes are made.

The temporary B-Rep capability provides direct access to the modeling engine, bypassing Fusion 360.  This means that the geometry you create as temporary B-Rep won’t be displayed or saved because Fusion 360 doesn’t even know it exists.  The geometry you create at temporary B-Rep exists in its own world.  Let’s look at how this can be applied to the problem posed in the forum.  Using the temporary B-Rep functionality I can create a solid cylinder at any location in space without any dependency on planes or sketches.  It’s not constrained by any parametric rules or user-interface choices.  In fact, the method to create a cylinder takes two points and two radii (to create a cylinder or a cone).  Because the geometry is independent of Fusion 360, it doesn’t have all of the overhead that parametric geometry has so it’s also much faster.  It also isn’t displayed anywhere so you save the time that’s needed to build and display a mesh that represents the cylinder.

It probably still seems a bit worthless if I can create geometry but can’t use it in Fusion 360.  Well, you can but you have to first use it to create a real body inside Fusion 360.  Then you can use that body just that same as any other body.  In a parametric model, you create a body by using features and defining the shape of the body one feature at a time.  If you change any of the inputs, the features will recompute and rebuild the body.  You can’t create a parametric body using any arbitrary shape.  However, you can create a non-parametric body.  This is what happens when you import a body using a translator (STEP, IGES, etc.) and then change the model to be parametric.  A non-parametric body is created and that body exists within a base feature.  A base feature is a special feature that acts as a non-parametric island within a parametric part.  A base feature exists in the timeline and can interact with the parametric model, but it’s content never changes as the result of a recompute.

For a solution to creating the holes in the cylinder, we can create the cylinders that represent the holes as temporary B-Rep bodies.  Then within a base feature, we can create a real body using the temporary body as input.  And then finally we can use the Combine feature to subtract the holes from the cylinder.  The picture above is the result of running the code below.  On my laptop it creates the entire model containing 2000 holes in less than 20 seconds.  I suspect if you tried creating 2000 standard hole features you could go get lunch before it finishes.

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

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        
        des = adsk.fusion.Design.cast(app.activeProduct)
        root = des.rootComponent
        
        # Create a new sketch.
        sk = root.sketches.add(root.xYConstructionPlane)
        
        # Draw two circles on the sketch to use to create a tube.
        insideRad = 15
        outsideRad = 20
        circs = sk.sketchCurves.sketchCircles
        circs.addByCenterRadius(adsk.core.Point3D.create(0,0,0), insideRad)
        circs.addByCenterRadius(adsk.core.Point3D.create(0,0,0), outsideRad)

        # Find the "outer" profile.
        prof = adsk.fusion.Profile.cast(None)
        for prof in sk.profiles:
            if prof.profileLoops.count == 2:
                break

        # Create an extrusion.
        ext = root.features.extrudeFeatures.addSimple(prof, adsk.core.ValueInput.createByReal(1520), 
                                                      adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
        cylinderBody = ext.bodies.item(0)

        # Create a series of temporary solid bodies that represent the cylinders.                                                      
        tempBRep = adsk.fusion.TemporaryBRepManager.get()
 
        cylinders = []  
        radius = 2
        angle = 0
        offset = 2.5
        targetBody = adsk.fusion.BRepBody.cast(None)
        for i in range(2000):
            # Compute the coordinates of the first point of the cylinder.
            x = (insideRad-1) * math.cos(angle)
            y = (insideRad-1) * math.sin(angle)
            pnt1 = adsk.core.Point3D.create(x, y, offset)
                        
            # Compute the coordinates of the second point of the cylinder.
            x = (outsideRad+1) * math.cos(angle)
            y = (outsideRad+1) * math.sin(angle)
            pnt2 = adsk.core.Point3D.create(x, y, offset)
            
            # Create the temporary cylinder.
            cylinder = tempBRep.createCylinderOrCone(pnt1, radius, pnt2, radius)
            if not targetBody:
                targetBody = cylinder
            else:
                # Union the cylinder with the previous cylinder.
                tempBRep.booleanOperation(targetBody, cylinder, adsk.fusion.BooleanTypes.UnionBooleanType)
            
            # Update the position values.
            offset += .75
            angle += 0.3
            
        # Create a new base feature.
        baseFeat = root.features.baseFeatures.add()
        baseFeat.startEdit()
        
        cylindersBody = root.bRepBodies.add(targetBody, baseFeat)
        
        baseFeat.finishEdit()
        
        toolBodies = adsk.core.ObjectCollection.create()
        toolBodies.add(baseFeat.bodies.item(0))
        combineInput = root.features.combineFeatures.createInput(cylinderBody, toolBodies)
        combineInput.operation = adsk.fusion.FeatureOperations.CutFeatureOperation
        root.features.combineFeatures.add(combineInput)
        
        app.activeViewport.fit()
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

 

 

Leave a Comment