Hi all,
I'm new to the Fusion 360 API and am looking for a way to automate the exporting of the surface (or just the points along the edge of the surface) when doing a plane cut along a body.
I've managed to make a plane cut along a spiral path along a defined spline and am automating that. But then I need to export the export the intersection of the plane and the body to get the cross-sectional area etc. (shown in blue in the second image. I need to do this in Python to automate the extraction of the surface along 100 points along that spiral.
I've included images below to help demonstrate what's needed. Any pointers will be greatly appreciated.
Thanks!
Iwan
Hi @ivr22 .
It looked like fun, so I gave it a try.
# Fusion360API Python script
import adsk.core, adsk.fusion, traceback
def run(context):
ui = adsk.core.UserInterface.cast(None)
try:
app :adsk.fusion.Application = adsk.core.Application.get()
ui = app.userInterface
# select body
msg :str = 'Select Body'
selFiltter :str = 'Bodies'
sel :adsk.core.Selection = selectEnt(msg ,selFiltter)
if not sel: return
body :adsk.fusion.BRepBody = sel.entity
# select guide curve
msg = 'Select Guide Curve (SketchCurves)'
selFiltter = 'SketchCurves'
sel :adsk.core.Selection = selectEnt(msg ,selFiltter)
if not sel: return
crv :adsk.fusion.SketchCurve = sel.entity
# create Cross Section Surfaces
createCrossSections(body, crv, 100)
# finish
ui.messageBox('Done')
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def createCrossSections(
body :adsk.fusion.BRepBody,
sktCrv :adsk.fusion.SketchCurve,
count :int) -> list:
# -- support function --
def getParams(
crv :adsk.core.Curve3D) -> list:
# get Evaluator
eva :adsk.core.CurveEvaluator3D = crv.evaluator
# get start end point
_, startPnt, endPnt = eva.getEndPoints()
# get start/end parameter
_, sPrm = eva.getParameterAtPoint(startPnt)
_, ePrm = eva.getParameterAtPoint(endPnt)
# get length
_, length = eva.getLengthAtParameter(sPrm, ePrm)
# get step
step = length / (count + 1)
# get parameter
prms = []
for idx in range(1,count+1):
_, prm = eva.getParameterAtLength(sPrm, step * idx)
prms.append(prm)
return prms
def getPlanes(
crv :adsk.core.Curve3D,
parameters :list) -> list:
# get Evaluator
eva :adsk.core.CurveEvaluator3D = crv.evaluator
# get tangent
_, tangents = eva.getTangents(parameters)
tans = [t for t in tangents]
# point3d
_, points = eva.getPointsAtParameters(parameters)
pnts = [p for p in points]
# plane
Plane = adsk.core.Plane
return [Plane.create(p, v) for p, v in zip(pnts, tans)]
def initCrossSections(
body :adsk.fusion.BRepBody,
planes :list) -> list:
tmpMgr :adsk.fusion.TemporaryBRepManager = adsk.fusion.TemporaryBRepManager.get()
surfs = []
for pln in planes:
wire = tmpMgr.planeIntersection(body, pln)
if not wire:
continue
surf = tmpMgr.createFaceFromPlanarWires([wire])
if not surf:
continue
surfs.append(surf)
return surfs
def addSurfs(
comp :adsk.fusion.Component,
surfs :list) -> list:
bodies = comp.bRepBodies
des :adsk.fusion.Design = comp.parentDesign
breps =[]
if des.designType == adsk.fusion.DesignTypes.DirectDesignType:
breps = [bodies.add(surf) for surf in surfs]
else:
baseFeatures = comp.features.baseFeatures
baseFeature = baseFeatures.add()
baseFeature.startEdit()
for surf in surfs:
try:
breps.append(bodies.add(surf, baseFeature))
except:
pass
baseFeature.finishEdit()
return breps
# ----
# Curve
crv = sktCrv.worldGeometry
# Params
prms = getParams(crv)
# Planes
plns = getPlanes(crv, prms)
# CrossSections
surfs = initCrossSections(body, plns)
# add surfaces
comp = sktCrv.parentSketch.parentComponent
breps = addSurfs(comp, surfs)
return breps
def selectEnt(
msg :str,
filtterStr :str) -> adsk.core.Selection :
try:
app = adsk.core.Application.get()
ui = app.userInterface
sel = ui.selectEntity(msg, filtterStr)
return sel
except:
return None
However, I don't think you can get the same results with anything other than the root component.
Also, if you have a cut that has multiple cross sections, you will have multiple surfaces.
That's amazing. Thank you so much.
To characterise the slices, I'm looking to export them to Python or Matlab to either get the edge of the slices as splines or surfaces. I can then filter to the surface of interest (of the multiple cuts) characterise them. Any idea on how to do this?
I'm also sometimes getting this error message when trying to run the code. Any idea of why this error would appear?
Thank you so much. This is extremely helpful for my research.
I'm sorry, but I don't understand English at all, so I didn't understand your question about the filter.
As for the part where the error is occurring, that part uses the method here.
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-18D6210D-FF0A-4CA8-8244-CFDC30213A4D
In the documentation, it says something like this.
"Each wire must be closed, they should not overlap, and they should all lie on the same plane."
I think the boundary to create the surface is probably overlapping, but I can't find the correct cause without trying with actual data.
Thank you. Can we replace the createFacefromPlanarWires with the create plane along edge function (https://help.autodesk.com/view/fusion360/ENU/?guid=SLD-CONSTRUCT-PLANE-ALONG-PATH) or is that not included in the API?
Also my question about faces was about exporting the faces you made as splines (or something similar) for further analysis in Python or Matlab. Do you know if that's possible?
Thanks!
@ivr22 .
It is possible to create a "plane along path". However, you will need to modify it drastically.
I've never used Matlab before, is it possible to import Iges/Step files?
@ivr22 .
Fixed.
# Fusion360API Python script
import adsk.core, adsk.fusion, traceback
def run(context):
ui = adsk.core.UserInterface.cast(None)
try:
app :adsk.fusion.Application = adsk.core.Application.get()
ui = app.userInterface
# Parametric mode only
des :adsk.fusion.Design = app.activeProduct
if des.designType != adsk.fusion.DesignTypes.ParametricDesignType:
ui.messageBox('Parametric mode only')
return
# select body
msg :str = 'Select Body'
selFiltter :str = 'Bodies'
sel :adsk.core.Selection = selectEnt(msg ,selFiltter)
if not sel: return
body :adsk.fusion.BRepBody = sel.entity
# select guide curve
msg = 'Select Guide Curve (SketchCurves)'
selFiltter = 'SketchCurves'
sel :adsk.core.Selection = selectEnt(msg ,selFiltter)
if not sel: return
crv :adsk.fusion.SketchCurve = sel.entity
# create Cross Section Surfaces
createCrossSections(body, crv, 100)
# finish
ui.messageBox('Done')
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def createCrossSections(
body :adsk.fusion.BRepBody,
sktCrv :adsk.fusion.SketchCurve,
count :int) -> list:
# -- support function --
def getRatios(
count :int) -> list:
step = 1 / count
ratios = [v * step for v in range(count)]
ratios.append(1)
return ratios
def getPlanes(
sktCrv :adsk.fusion.SketchCurve ,
ratios :list) -> list:
comp :adsk.fusion.Component = sktCrv.parentSketch.parentComponent
conPlanes :adsk.fusion.ConstructionPlanes = comp.constructionPlanes
plnIpt :adsk.fusion.ConstructionPlaneInput = conPlanes.createInput()
valIpt :adsk.core.ValueInput
plns = []
for ratio in ratios:
valIpt = adsk.core.ValueInput.createByReal(ratio)
plnIpt.setByDistanceOnPath(sktCrv, valIpt)
plns.append(conPlanes.add(plnIpt))
adsk.doEvents()
return plns
def initSketch(
plane :adsk.fusion.ConstructionPlane) -> adsk.fusion.Sketch:
skts :adsk.fusion.Sketches = plane.component.sketches
return skts.add(plane)
def initCrossSections(
body :adsk.fusion.BRepBody,
planes :list):
skts = []
for plane in planes:
skt :adsk.fusion.Sketch = initSketch(plane)
skt.projectCutEdges(body)
if skt.profiles.count > 0:
skts.append(skt)
else:
skt.deleteMe()
adsk.doEvents()
return skts
def initSurfs(
skts :list) -> list:
comp :adsk.fusion.Component = skts[0].parentComponent
PatchFeats :adsk.fusion.PatchFeatures = comp.features.patchFeatures
NEWBODY = adsk.fusion.FeatureOperations.NewBodyFeatureOperation
surfs = []
for skt in skts:
if skt.profiles.count < 1:
continue
for pro in skt.profiles:
PatchIpt :adsk.fusion.PatchFeatureInput = PatchFeats.createInput(pro, NEWBODY)
PatchFeat :adsk.fusion.PatchFeature = PatchFeats.add(PatchIpt)
surfs.extend([f for f in PatchFeat.faces])
adsk.doEvents()
return surfs
# ----
# ratio
ratios = getRatios(count)
# Planes
plns = getPlanes(sktCrv, ratios)
# CrossSections
skts = initCrossSections(body, plns)
if len(skts) < 1:
return []
# add surfaces
surfs = initSurfs(skts)
return surfs
def selectEnt(
msg :str,
filtterStr :str) -> adsk.core.Selection :
try:
app = adsk.core.Application.get()
ui = app.userInterface
sel = ui.selectEntity(msg, filtterStr)
return sel
except:
return None
・It works only in parametric mode, because it cannot create the correct plane in direct mode.
・Processing is very slow compared to the first one.
Nothing has been done about exporting.