Hallo,
I'm new to Fusion and this forum so please tell me if I need to change anything in this question.
The problem I need to solve is how to split an eye model, which is constructed with the Fusion UI and consists of several asymmetric parts, into tiny cubes, like a rubics cube (at a later date probably into radial symmetric parts as well).
At the moment my code generates several construction planes in all three dimensions (see picture one).
My problem is that when I use these construction planes as splitTools to split all the bodies that are part of my eye model there will be planes that don't intersect with some of the bodies. This generates of course an error: "No intersection between target(s) and split tools." which stops the code.
So I had some ideas how to circumvent this problem by:
- checking beforehand if the construction plane and body have an intersection, but I only found such a function for planes and curves.
- somehow catching the error thrown by the split function and telling the program to just continue, no idea how to do that
- or create one combined splitting tool, because that combined splitting tool will have an intersection which each body. But it seems like it's not possible to combine construction planes.
Maybe my code is helpful(its also attached):
import adsk.core, adsk.fusion, adsk.cam, traceback
def run(context):
ui = None
try:
app = adsk.core.Application.get()
ui = app.userInterface
product = app.activeProduct
design = adsk.fusion.Design.cast(product)
# Get the root component of the active design
rootComp = design.rootComponent
#Creating construction planes where I want them
#Create a new Sketch on the xy-Plane
sketches = rootComp.sketches
# Make all xyz-Planes known
xyPlane = rootComp.xYConstructionPlane
sketch = sketches.add(xyPlane)
xzPlane = rootComp.xZConstructionPlane
sketch = sketches.add(xyPlane)
yzPlane = rootComp.yZConstructionPlane
sketch = sketches.add(xyPlane)
#-------------------------------
#-------------------------------
#creates new Planes in each of the three directions, in mm steps, since
#the diameter of the eye is about 23mm the loop goes from -15mm to +15mm
#this should be enough to include the 1mm thick eye applicator
#will be used to cut our eye-mdel into several cubes, to be used as
#sensitive scorers
for xyz_planes in [xyPlane, xzPlane, yzPlane]:
for i in range(-15, 16): #cm, will be converted into mm later
# Get construction planes
planes = rootComp.constructionPlanes
# Create construction plane input
planeInput = planes.createInput()
# Add construction plane by offset
offsetValue = adsk.core.ValueInput.createByReal(i/100.)#in mm now
planeInput.setByOffset(xyz_planes, offsetValue)
planeOne = planes.add(planeInput)
#Creating Planes end
# the following is just how it would work in an ideal world
# simply take each body and split it with every possible
# splitting tool and ignore any errors that occur
allBodies = rootComp.bRepBodies
for body in allBodies:
#Splitting
#Create SplitBodyFeatureInput
splitBodyFeats = rootComp.features.splitBodyFeatures
splitBodyInput = splitBodyFeats.createInput(body, planeOne, True) #1: body to be split, 2: SplittingTool, #3:SplittingToolExtended
# Create split body feature
split_ Object = splitBodyFeats.add(splitBodyInput)
#Splitting end
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
I would be really thankful for any help regarding this problem. As well as general improvement ideas for my code since I'm a beginner. And is there a way to copy code here and not lose all the indentations?
Greetings,
Saskia
Solved! Go to Solution.
Solved by BrianEkins. Go to Solution.
Hi @Anonymous .
See here for how to maintain indentation.
I would like to work on this theme because it seems to be a fun theme,
but I am busy with my work and I cannot secure time....
Since I can't edit my original post anymore, here's the code so far with better indentation (in my opinion):
import adsk.core, adsk.fusion, adsk.cam, traceback
def run(context):
ui = None
try:
app = adsk.core.Application.get()
ui = app.userInterface
product = app.activeProduct
design = adsk.fusion.Design.cast(product)
# Get the root component of the active design
rootComp = design.rootComponent
#Creating Linear Dimension (Plane) where I want them
#Create a new Sketch on the xy-Plane
sketches = rootComp.sketches
# Make all xyz-Planes known
xyPlane = rootComp.xYConstructionPlane
sketch = sketches.add(xyPlane)
xzPlane = rootComp.xZConstructionPlane
sketch = sketches.add(xyPlane)
yzPlane = rootComp.yZConstructionPlane
sketch = sketches.add(xyPlane)
#-------------------------------
#-------------------------------
#creates new Planes in each of the three directions,in mm steps, since
#the diameter of the eye is about 23mm the loop goes from -15mm to +15mm
#this should be enough to include the 1mm thick eye applicator
#will be used to cut our eye-model into several cubes, to be used as
#sensitive scorers
for xyz_planes in [xyPlane, xzPlane, yzPlane]:
for i in range(-15, 16): #cm, will be converted into mm later
# Get construction planes
planes = rootComp.constructionPlanes
# Create construction plane input
planeInput = planes.createInput()
# Add construction plane by offset
offsetValue = adsk.core.ValueInput.createByReal(i/100.)#in mm now
planeInput.setByOffset(xyz_planes, offsetValue)
planeOne = planes.add(planeInput)
#Creating Planes end
# the following is just how it would work in an ideal world
# simply take each body and split it with every possible
# splitting tool and ignore any errors that occur
allBodies = rootComp.bRepBodies
for body in allBodies:
#Splitting
#Create SplitBodyFeatureInput
splitBodyFeats = rootComp.features.splitBodyFeatures
splitBodyInput = splitBodyFeats.createInput(body, planeOne, True) #1: body to be split, 2: SplittingTool, 3:SplittingToolExtended
# Create split body feature
split_Object = splitBodyFeats.add(splitBodyInput)
#Splitting end
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
I didn't try running your code but the simplest solution will be to try to do the split but continue if it should fail. I added a try except around the code the does the split.
for body in allBodies:
#Splitting
try:
#Create SplitBodyFeatureInput
splitBodyFeats = rootComp.features.splitBodyFeatures
splitBodyInput = splitBodyFeats.createInput(body, planeOne, True) #1: body to be split, 2: SplittingTool, 3:SplittingToolExtended
# Create split body feature
split_Object = splitBodyFeats.add(splitBodyInput)
except:
pass
Hey @BrianEkins,
thank you very much, the pass command does exactly what I needed. I've never heard of it before, so I didn't even know to look for it.
My code works now but I will continue to look for other solutions since the running time needs to be improved 🙂
Thank you again,
Saskia
Instead of split, I used TemporaryBRepManager's booleanOperation Method.
#Fusion360API Python script
import adsk.core, adsk.fusion, traceback
import time
_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)
# cube size - unit Cm
_min = [-1.5, -1.5, -1.5]
_max = [1.5, 1.5, 1.5]
_step = 0.1
def run(context):
try:
global _app, _ui
_app = adsk.core.Application.get()
_ui = _app.userInterface
des :adsk.fusion.Design = _app.activeProduct
parametricMode = adsk.fusion.DesignTypes.ParametricDesignType
if des.designType != parametricMode:
des.designType = parametricMode
root :adsk.fusion.Component = des.rootComponent
# select target body
msg :str = 'Please select the body to split'
selFiltter :str = 'SolidBodies'
sel :adsk.core.Selection = selectEnt(msg ,selFiltter)
if not sel: return
targetBody :adsk.fusion.BRepBody = sel.entity
# time
startTime = time.time()
# cube
pnts = initCenterPoints()
print('Points : {}s'.format(time.time() - startTime))
cubes = initCubes(pnts)
print('cubes : {}s'.format(time.time() - startTime))
# Intersection
inters = initIntersection(targetBody, cubes)
print('Intersections : {}s'.format(time.time() - startTime))
# baseFeature
baseFeatures = root.features.baseFeatures
baseFeature = baseFeatures.add()
baseFeature.startEdit()
for body in inters:
root.bRepBodies.add(body, baseFeature)
baseFeature.finishEdit()
targetBody.isLightBulbOn = False
print('baseFeature : {}s'.format(time.time() - startTime))
# finish
_ui.messageBox('Done\n{}s'.format(time.time() - startTime))
except:
if _ui:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def initIntersection(
targetBody :adsk.fusion.BRepBody,
cubes :list) -> list:
tmpMgr = adsk.fusion.TemporaryBRepManager.get()
tgt = tmpMgr.copy(targetBody)
typeIntersect = adsk.fusion.BooleanTypes.IntersectionBooleanType
inters = []
for cube in cubes:
tmpMgr.booleanOperation(cube, tgt, typeIntersect)
if cube.volume > 0:
inters.append(cube)
return inters
def initCubes(
centerPnts :list) -> list:
tmpMgr = adsk.fusion.TemporaryBRepManager.get()
box3D = adsk.core.OrientedBoundingBox3D
vec3D = adsk.core.Vector3D
vecX = vec3D.create(1.0, 0.0, 0.0)
vecY = vec3D.create(0.0, 1.0, 0.0)
cubes = []
global _step
for pnt in centerPnts:
cube = box3D.create(pnt, vecX, vecY, _step, _step, _step)
cubes.append(tmpMgr.createBox(cube))
return cubes
def initCenterPoints() -> list:
pnt3D = adsk.core.Point3D
pnts = []
global _min, _max, _step
half = _step / 2
for z in dRange(_min[2], _max[2], _step):
for y in dRange(_min[1], _max[1], _step):
for x in dRange(_min[0], _max[0], _step):
pnts.append(pnt3D.create(x + half, y + half, z + half))
return pnts
def dRange(start, end, step):
from decimal import Decimal
start = Decimal(str(start))
end = Decimal(str(end))
step = Decimal(str(step))
for i in range(int((end - start) / step)):
yield float(start + i * step)
def selectEnt(
msg :str,
filtterStr :str) -> adsk.core.Selection :
global _ui
try:
sel = _ui.selectEntity(msg, filtterStr)
return sel
except:
return None
Hello @kandennti,
First of all thank you very much for your code. It worked amazingly fast and taught me quite a lot!
I was able to adept it and the idea to use temporary bRepBodies to cut objects into radial-symmetric parts as well, which was a huge step for me.
But in the very last step of my program something doesn't work like I expected it to. I'm not sure whether to put it on the forum as a new question but since it belongs to this problem I decided to put it here.
I would like to make sure that the newly created objects, be that cube-shaped objects or some other form have the same material as the original object. My idea on how to realise this worked but only in one of two different cases.
I broke the problem down to a minimal working example that only needs an object in the UI with a material different from Steel (which seems to be the norm). There are comments and ui messages on the lines that don't work as I expected, maybe you can tell me what I did wrong.
Thank you again,
Saskia
import adsk.core, adsk.fusion, traceback
import time
import math
_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)
def run(context):
try:
global _app, _ui, _construct_doc
_app = adsk.core.Application.get()
_ui = _app.userInterface
des :adsk.fusion.Design = _app.activeProduct
parametricMode = adsk.fusion.DesignTypes.ParametricDesignType
if des.designType != parametricMode:
des.designType = parametricMode
root :adsk.fusion.Component = des.rootComponent
msg :str = 'Please select the body to split'
selFiltter :str = 'SolidBodies'
sel :adsk.core.Selection = selectEnt(msg ,selFiltter)
if not sel: return
targetBody :adsk.fusion.BRepBody = sel.entity
tmpMgr = adsk.fusion.TemporaryBRepManager.get()
targetmaterial = targetBody.material
# case 1
# only one of the cases should be run at the same time
# correct material is assigned -> works, except for the position of the
# sphere, which is (0,0,0) after baseFeature.finishEdit() in tempbRep2bRep
sphere_list = []
sphere_list.append(tmpMgr.createSphere(adsk.core.Point3D.create(4,0,0), 0.5))
sphere_list.append(tmpMgr.createSphere(adsk.core.Point3D.create(1,0,0), 0.3))
tempbRep2bRep(root, sphere_list, targetmaterial)
_ui.messageBox('spherelist' + str(sphere_list).format(traceback.format_exc()))
# case 2
# does not assign the correct material
tempBodies = []
for body in root.bRepBodies:
tempBodies.append(tmpMgr.copy(body))
tempbRep2bRep(root, tempBodies, targetmaterial)
_ui.messageBox('tempBodies' + str(tempBodies).format(traceback.format_exc()))
except:
if _ui:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def tempbRep2bRep(root, body_list :list, body_material):
# turn the temporary bRepBodies in body_list into real bRepbodies in the UI
# with assigned material
baseFeatures = root.features.baseFeatures
baseFeature = baseFeatures.add()
baseFeature.startEdit()
for body in body_list:
real_body = root.bRepBodies.add(body, baseFeature)
real_body.material = body_material
_ui.messageBox('In both cases the position and material is right as of now'.format(traceback.format_exc()))
baseFeature.finishEdit()
_ui.messageBox('Now the position of the spheres in case 1 is changed to (0,0,0) and the material in case 2 is changed as well to steel instead of the targetmaterial'.format(traceback.format_exc()))
return
def selectEnt(
msg :str,
filtterStr :str) -> adsk.core.Selection :
global _ui
try:
sel = _ui.selectEntity(msg, filtterStr)
return sel
except:
return None
I found two measures.
One worked correctly when run in direct mode.
The other is to create a new occurrence and add it to component.material
This was possible by setting the material.
import adsk.core, adsk.fusion, traceback
_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)
def run(context):
try:
global _app, _ui, _construct_doc
_app = adsk.core.Application.get()
_ui = _app.userInterface
des :adsk.fusion.Design = _app.activeProduct
parametricMode = adsk.fusion.DesignTypes.ParametricDesignType
if des.designType != parametricMode:
des.designType = parametricMode
root :adsk.fusion.Component = des.rootComponent
msg :str = 'Please select the body to split'
selFiltter :str = 'SolidBodies'
sel :adsk.core.Selection = selectEnt(msg ,selFiltter)
if not sel: return
targetBody :adsk.fusion.BRepBody = sel.entity
targetmaterial = targetBody.material
# create Occurrence & setting component material
mat :adsk.core.Matrix3D = adsk.core.Matrix3D.create()
occ :adsk.fusion.Occurrence = root.occurrences.addNewComponent(mat)
occ.component.material = targetmaterial
tmpMgr = adsk.fusion.TemporaryBRepManager.get()
# case 1
sphere_list = []
sphere_list.append(tmpMgr.createSphere(adsk.core.Point3D.create(4,0,0), 0.5))
sphere_list.append(tmpMgr.createSphere(adsk.core.Point3D.create(1,0,0), 0.3))
tempbRep2bRep(occ, sphere_list, targetmaterial)
_ui.messageBox('spherelist' + str(sphere_list).format(traceback.format_exc()))
# case 2
tempBodies = []
cloneBody = adsk.fusion.BRepBody.cast(None)
for body in root.bRepBodies:
cloneBody = tmpMgr.copy(body)
tempBodies.append(cloneBody)
tempbRep2bRep(occ, tempBodies, targetmaterial)
_ui.messageBox('tempBodies' + str(tempBodies).format(traceback.format_exc()))
except:
if _ui:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def tempbRep2bRep(
occ :adsk.fusion.Occurrence,
body_list :list,
body_material :adsk.core.Material):
comp :adsk.fusion.Component = occ.component
baseFeatures = comp.features.baseFeatures
baseFeature = baseFeatures.add()
baseFeature.startEdit()
[comp.bRepBodies.add(body, baseFeature) for body in body_list]
baseFeature.finishEdit()
_ui.messageBox('In both cases the position and material is right as of now'.format(traceback.format_exc()))
_ui.messageBox('Now the position of the spheres in case 1 is changed to (0,0,0) and the material in case 2 is changed as well to steel instead of the targetmaterial'.format(traceback.format_exc()))
return
def selectEnt(
msg :str,
filtterStr :str) -> adsk.core.Selection :
global _ui
try:
sel = _ui.selectEntity(msg, filtterStr)
return sel
except:
return None
There is one problem here.The component.material property is a read / write property.
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-149E4821-BE47-4D18-A351-A8339FC3E0BB
This time I used this.
If there are multiple Bodies in the component and multiple materials are used, changing the component.material will result in all the materials being set.
(This time we are creating a new occurrence for that reason)
I don't think this is the desired result.
Shouldn't component.material be read only?
Can't find what you're looking for? Ask the community or share your knowledge.