Extruding profile collection in single operation?

BradAndersonJr
Enthusiast
Enthusiast

Extruding profile collection in single operation?

BradAndersonJr
Enthusiast
Enthusiast

Hey!

 

I'm at the final step of my current project and have simply hit a wall...

 

Using the Extrude API I'm trying to add all my fret sketch profiles to a single collection within a for loop, and then use that profile collection to extrude all of the sketches at once.  Currently I'm unable to extrude the entire collection at once.  Looking at the results in the UI, which I examine the Extrude Feature in from the Timeline it shows it only extruded 1 profile.

 

The left side is the results from my script with the right being the desired results (achieved via the UI).

 

apiExample.png

 

Here's what I have for my script, it's a bit amateur and needs to be cleaned up, but the code in question is at the end of the script.

import adsk.core, adsk.fusion, traceback
import math
from math import sqrt

#User Defined Parameters - To be better organized

imp = 2.54 #Imperial unit conversion

h = 0.125*imp #Overall depth of fretboard
nutWidth = 1.625 #Neck width at nut
nutRadius = 10 #Neck Radius
endWidth = 2.1875 #Neck Width at end
endRadius = 16 #End Radius
endCurve = 3 #Fretboard end curve
f = 22 #Number of Frets
scaleLength = 25.5
tangWidth = 0.025
tangDepth = 0.1
filletRadius = adsk.core.ValueInput.createByReal((.5))
        
#Equation for fret spacin
for fretNumber in range(1,f+2):
    fretDistance = scaleLength-(scaleLength/(2**(fretNumber/12.0)))

#This calculates and rounds the total length of the fretboard using the scale length and number of frets
L = ((round((float('%.3f'%(fretDistance)))*4)/4)*2.54)

print(L/imp)

#Equation for defining the proper radii
endR = endRadius-sqrt((endRadius**2-(endWidth/2)**2))
nutR = nutRadius-sqrt((nutRadius**2-(nutWidth/2)**2))
endC = endCurve-sqrt((endCurve**2-(endWidth/2)**2))

# Points defined for curves
endTopL = adsk.core.Point3D.create((endWidth/-2)*imp, h-endR*imp, endC)
endTopC = adsk.core.Point3D.create(0, h, 0)
endTopR = adsk.core.Point3D.create((endWidth/2)*imp, h-endR*imp, endC)

endBotL = adsk.core.Point3D.create((endWidth/-2)*imp, 0, endC)
endBotC = adsk.core.Point3D.create(0, 0, 0)
endBotR = adsk.core.Point3D.create((endWidth/2)*imp, 0, endC)

nutTopL = adsk.core.Point3D.create((nutWidth/-2)*imp, h-nutR*imp, L)
nutTopC = adsk.core.Point3D.create(0, h, L)
nutTopR = adsk.core.Point3D.create((nutWidth/2)*imp, h-nutR*imp, L)

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        
        # Create a document
        doc = app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)
        
        product = app.activeProduct
        design = adsk.fusion.Design.cast(product)
        rootComp = design.rootComponent

        sketchesObj = rootComp.sketches
                
        #create curve for bridge-end top arc
        sketch1 = sketchesObj.add(rootComp.yZConstructionPlane)
        sketchArcsObj1 = sketch1.sketchCurves.sketchArcs
        sketchArcsObj1.addByThreePoints(endTopL, endTopC, endTopR)
        test1 = sketchArcsObj1.addByThreePoints(endTopL, endTopC, endTopR)
        openProfile1 = adsk.fusion.Path.create(test1, adsk.fusion.ChainedCurveOptions.noChainedCurves)
        sketch1.name = 'Top/Rear Curve'
        sketch1.isVisible = False
        
        #create curve for bridge-end bottom arc
        sketch2 = sketchesObj.add(rootComp.yZConstructionPlane)
        sketchArcsObj2 = sketch2.sketchCurves.sketchArcs
        sketchArcsObj2.addByThreePoints(endBotL, endBotC, endBotR)
        test2 = sketchArcsObj2.addByThreePoints(endBotL, endBotC, endBotR)
        openProfile2 = adsk.fusion.Path.create(test2, adsk.fusion.ChainedCurveOptions.noChainedCurves)
        sketch2.name = 'Bottom/Rear Curve'
        sketch2.isVisible = False

        #create curve for nut-end top arc
        sketch3 = sketchesObj.add(rootComp.yZConstructionPlane)
        sketchArcsObj3 = sketch3.sketchCurves.sketchArcs
        sketchArcsObj3.addByThreePoints(nutTopL, nutTopC, nutTopR)
        test3 = sketchArcsObj3.addByThreePoints(nutTopL, nutTopC, nutTopR)
        openProfile3 = adsk.fusion.Path.create(test3, adsk.fusion.ChainedCurveOptions.noChainedCurves)        
        sketch3.name = 'Top/Nut Curve'
        sketch3.isVisible = False
        
        #create line for nut-end bottom arc
        sketch4 = sketchesObj.add(rootComp.yZConstructionPlane)
        line = sketch4.sketchCurves.sketchLines;
        nutLine = line.addByTwoPoints(adsk.core.Point3D.create((nutWidth/-2)*imp, 0, L), adsk.core.Point3D.create((nutWidth/2)*imp, 0, L))
        openProfile4 = adsk.fusion.Path.create(nutLine, adsk.fusion.ChainedCurveOptions.noChainedCurves) 
        sketch4.name = 'Bottom/Rear Line'
        sketch4.isVisible = False
                
        # Create surface for bridge-end of fretboard
        loftFeats = rootComp.features.loftFeatures
        loftInput1 = loftFeats.createInput(adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
        loftSectionsObj1 = loftInput1.loftSections
        loftSectionsObj1.add(openProfile2)
        loftSectionsObj1.add(openProfile1)
        loftInput1.isSolid = False
        
        loft1 = loftFeats.add(loftInput1)
        l1 = loft1.faces[0]

        # Create surface for nut-end of fretboard
        loftInput2 = loftFeats.createInput(adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
        loftSectionsObj2 = loftInput2.loftSections
        loftSectionsObj2.add(openProfile3)
        loftSectionsObj2.add(openProfile4)
        loftInput2.isSolid = False
       
        loft2 = loftFeats.add(loftInput2)
        l2 = loft2.faces[0]

        # Create surface using previous surfaces
        loftInput3 = loftFeats.createInput(adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
        loftSectionsObj3 = loftInput3.loftSections
        loftSectionsObj3.add(l1)
        loftSectionsObj3.add(l2)
        loftInput3.isSolid = False

        loft3 = loftFeats.add(loftInput3)
        l3 = loft3.faces[0]
        
        # Get surface bodies and add them to object collection
        surface1 = loft1.bodies.item(0)
        surface2 = loft2.bodies.item(0)
        surface3 = loft3.bodies.item(0)
        surfaces = adsk.core.ObjectCollection.create()
        surfaces.add(surface1)
        surfaces.add(surface2)
        surfaces.add(surface3)
        
        # Define tolerance with 1 mm.
        tolerance = adsk.core.ValueInput.createByReal(0.1)
        
        # Create a stitch input to be able to define the input needed for an stitch.
        features = rootComp.features
        stitches = features.stitchFeatures
        stitchInput = stitches.createInput(surfaces, tolerance, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
        
        # Create a stitch feature.
        stitch = stitches.add(stitchInput)
                
        #Select edges of bridge-end of fretboard to make fillets
        fretboardEndFace = stitch.bodies.item(0)
        fretboardEndEdge1 = fretboardEndFace.edges.item(1)
        fretboardEndEdge2 = fretboardEndFace.edges.item(3)
        
        #Create collection
        endEdges = adsk.core.ObjectCollection.create()
        endEdges.add(fretboardEndEdge1)
        endEdges.add(fretboardEndEdge2)
  
        #Creating fillets
        fillets = rootComp.features.filletFeatures
        filletInput = fillets.createInput()
        filletInput.addConstantRadiusEdgeSet(endEdges, filletRadius, True)
        filletInput.isG2 = False
        filletInput.isRollingBallCorner = True
        fillet = fillets.add(filletInput)

        # Get the body created by the stitch
        face = stitch.bodies.item(0)
        topFace = face.faces.item(6)
        
        # Create input entities for offset feature
        inputEntities = adsk.core.ObjectCollection.create()
        inputEntities.add(topFace)
        
        # Distance for offset feature
        distance = adsk.core.ValueInput.createByString('-0.05 in')
        
        # Create an input for offset feature
        offsetFeatures = features.offsetFeatures
        offsetInput = offsetFeatures.createInput(inputEntities, distance, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
               
        #Get the surface body.
        extrudeFeature = offsetFeatures.add(offsetInput)
        surface = extrudeFeature.bodies.item(0)
        surface.name = 'Reference surface for fret cuts'
        surface.isVisible = False
        
        #Get an edge from surface, and add it to object collection.
        extend = surface.edges
        ext = extend.item(0)
        inputEdges = adsk.core.ObjectCollection.create()
        inputEdges.add(ext)
        
        #Define a distance to extend with 1 cm.
        distance2 = adsk.core.ValueInput.createByReal(1.0)
        
        #Create an extend input to be able to define the input needed for an extend.
        extendFeatures = features.extendFeatures
        extendFeatureInput = extendFeatures.createInput(inputEdges, distance2, adsk.fusion.SurfaceExtendTypes.NaturalSurfaceExtendType)
        
        #Create an extend feature.
        extendFeature = extendFeatures.add(extendFeatureInput)

        #Get extrude features
        extrudes = rootComp.features.extrudeFeatures
        
        #Create distance value inputs
        mm10 = adsk.core.ValueInput.createByString("0 mm")
        mm100 = adsk.core.ValueInput.createByString("10 mm")

        #Create sketch for fret lines
        sketch5 = sketchesObj.add(rootComp.yZConstructionPlane)
        frets = sketch5.sketchCurves.sketchLines;
        sketch5.name = 'Fret Lines [ ' + str(f) + ' frets ]'
        sketch5.isVisible = True

        #Create sketch for fret cuts
        sketch6 = sketchesObj.add(rootComp.xZConstructionPlane) #xY or xZ
        cuts = sketch6.sketchCurves.sketchLines;
        sketch6.name = 'Fret Cuts [ ' + str(f) + ' frets ]'
        sketch6.isVisible = True

        #Create loop for fret spacing and creation
        for fretNumber in range(1,f+1):
            fretDistance = scaleLength-(scaleLength/(2**(fretNumber/12.0)))
            #Create fret lines for fret spacing reference            
            fretLines = frets.addByTwoPoints(adsk.core.Point3D.create(endWidth*imp/2, h, (-fretDistance*imp+L)), adsk.core.Point3D.create(-endWidth*imp/2, h, (-fretDistance*imp+L)))
            fretLines.isConstruction = True

            #Create fret cuts
            cutLines = cuts.addTwoPointRectangle(adsk.core.Point3D.create((-fretDistance*imp+L-(tangWidth*imp/2)), -endWidth*imp/2, h), adsk.core.Point3D.create((-fretDistance*imp+L+(tangWidth*imp/2)), endWidth*imp/2, h))

            #Create an object collection to use an input.
            profs = adsk.core.ObjectCollection.create()
                
            #Add all of the profiles to the collection.
            for prof in sketch6.profiles:
                profs.add(prof)
    
            print(prof)
    
            print('%.3f'%(fretDistance)) 
    
        #Extrude Sample 3: Create an extrusion that starts from an entity and goes the specified distance.
        extrudeInput = extrudes.createInput(prof, adsk.fusion.FeatureOperations.CutFeatureOperation)
        #Create a distance extent definition
        extent_distance_2 = adsk.fusion.DistanceExtentDefinition.create(mm100)
        #Create a start extent that starts from a brep face with an offset of 10 mm.
        start_from = adsk.fusion.FromEntityStartDefinition.create(extendFeature.faces.item(0), mm10)
        #taperAngle should be 0 because extrude start face is not a planar face in this case
        extrudeInput.setOneSideExtent(extent_distance_2, adsk.fusion.ExtentDirections.PositiveExtentDirection)        
        extrudeInput.startExtent = start_from
            
        #Create the extrusion
        extrude = extrudes.add(extrudeInput)
        fretboard = extrude.bodies.item(0)
        fretboard.name = 'Fretboard with [ ' + str(f) + ' frets ]'

#        ui.messageBox('Fretboard has been created: \n' + str(f) + ' frets!' '\nTotal length at ' + str(L/imp) + ' ins!')

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

I feel I'm ALMOST there, I'm just not seeing it...

 

Thanks!

Brad Anderson Jr
Fusion 360 Hobbyist
Fusion 360 Luthiers Facebook Group
0 Likes
Reply
Accepted solutions (1)
1,555 Views
2 Replies
Replies (2)

BrianEkins
Mentor
Mentor
Accepted solution

I see a couple of issues with your code.  The first is an efficiency/clarity issue and the second is what's really causing the problem.

 

The first is that for every fret you're creating a new object collection and the populating with all of the profiles.  For the first fret you create an ObjectCollection and add the single profile.  For the next fret, you create a new ObjectCollection and then add the two profiles.  It should still work because in the end you have an ObjectCollection with all of the profiles but you did work that you're just throwing away with each iteration.  You can just move the creation and population of the collection to outside the for loop.

 

        #Create loop for fret spacing and creation
        for fretNumber in range(1,f+1):
            fretDistance = scaleLength-(scaleLength/(2**(fretNumber/12.0)))
            #Create fret lines for fret spacing reference            
            fretLines = frets.addByTwoPoints(adsk.core.Point3D.create(endWidth*imp/2, h, (-fretDistance*imp+L)), adsk.core.Point3D.create(-endWidth*imp/2, h, (-fretDistance*imp+L)))
            fretLines.isConstruction = True

            #Create fret cuts
            cutLines = cuts.addTwoPointRectangle(adsk.core.Point3D.create((-fretDistance*imp+L-(tangWidth*imp/2)), -endWidth*imp/2, h), adsk.core.Point3D.create((-fretDistance*imp+L+(tangWidth*imp/2)), endWidth*imp/2, h))

         #Create an object collection to use an input.
         profs = adsk.core.ObjectCollection.create()
                
         #Add all of the profiles to the collection.
         for prof in sketch6.profiles:
             profs.add(prof)

The second issue is that you're using the "prof" variable to create the ExtrudeFeatureInput, which is a single profile.  Instead, you want to use "profs", which is the collection of all of the profiles.

 

I didn't try running your code, but hopefully, that gets it going.

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
2 Likes

BradAndersonJr
Enthusiast
Enthusiast

I knew it!  I knew it'd be something silly I was just overlooking.  A prime example of step away every one in a while...

 

Thanks again Brian!  Taking that chunk out of the loop and using the RIGHT variable, profs instead of prof did the trick!

 

 

Brad Anderson Jr
Fusion 360 Hobbyist
Fusion 360 Luthiers Facebook Group
0 Likes