cadquery
cadquery copied to clipboard
Multiple split with Faces produce inconsistent results
We are trying to generate multiple cuts to 'raster' basic geometries The sample code below produces inconsistent results with missing parts of the split ellipsoid
import cadquery as cq
import numpy as np
a_x=0.15
a_y=0.31
a_z=0.4
grid=[4, 4, 4]
transform_mat = cq.Matrix(
[
[a_x, 0, 0, 0.],
[0, a_y, 0, 0.],
[0, 0, a_z, 0.],
]
)
sphere = cq.Solid.makeSphere(1.0, cq.Vector(0, 0, 0), angleDegrees1=-90)
ellipsoid = sphere.transformGeometry(transform_mat)
solidList = []
for solid in ellipsoid.Solids():
wk_plane = cq.Workplane().add(solid)
xgrid = np.linspace(-0.5, 0.5, num=grid[0], endpoint=False)[1:]
ygrid = np.linspace(-0.5, 0.5, num=grid[0], endpoint=False)[1:]
zgrid = np.linspace(-0.5, 0.5, num=grid[0], endpoint=False)[1:]
print(ygrid)
for i in xgrid:
Plane_x = cq.Face.makePlane(None, None, basePnt=(i, 0, 0), dir=(1, 0, 0))
wk_plane = wk_plane.split(cq.Workplane().add(Plane_x))
print('x:', len(wk_plane.val().Solids()))
for j in ygrid:
Plane_y = cq.Face.makePlane(None, None, basePnt=(0, j, 0), dir=(0, 1, 0))
wk_plane = wk_plane.solids().split(cq.Workplane().add(Plane_y), keepBottom=True, keepTop=True)
print('y:', len(wk_plane.val().Solids()))
for k in zgrid:
Plane_z = cq.Face.makePlane(None, None, basePnt=(0, 0, k), dir=(0, 0, 1))
wk_plane = wk_plane.split(cq.Workplane().add(Plane_z), keepBottom=True, keepTop=True)
print('z:', len(wk_plane.val().Solids()))
for subsolid in wk_plane.val().Solids():
solidList.append(subsolid)
compound = cq.Compound.makeCompound(solidList)
cq.exporters.export(compound, "compound.step")
It is however expected to keep all the split solids
a grid of [5,5,5] provide expected results :
The following code is the same example simplified that can run in CQ-editor.
Splitting in only one dimension works fine but as soon as we split in two or three dimensions (for dim in [0, 1] for example), some parts are missing as shown in the screenshot below.
import cadquery as cq
sphere = cq.Workplane().sphere(1.0).val()
a_x = 0.15
a_y = 0.31
a_z = 0.4
transform_mat = cq.Matrix(
[
[a_x, 0, 0, 0.0],
[0, a_y, 0, 0.0],
[0, 0, a_z, 0.0],
]
)
ellipsoid = sphere.transformGeometry(transform_mat)
workplane = cq.Workplane().add(ellipsoid)
for dim in range(3):
direction = cq.Vector([int(dim == i) for i in range(3)])
for pos in [-0.25, 0.0, 0.25]:
point = pos * direction
plane = cq.Face.makePlane(basePnt=point, dir=direction)
workplane = workplane.split(plane)
As found by @chemiskyy, using transformGeometry is creating the problem.
Here is an example of a sphere with and without transformGeometry:
In our case, we have to use transformGeometry to create the ellipsoid shape. A workaround found by @chemiskyy is to generate the sphere with a random direction.
a_x = 0.15
a_y = 0.31
a_z = 0.4
transform_mat = cq.Matrix(
[
[a_x, 0, 0, 0.],
[0, a_y, 0, 0.],
[0, 0, a_z, 0.],
[0, 0, 0, 1.],
]
)
sphere = cq.Solid.makeSphere(
0.25,
cq.Vector(0, 0, 0),
dir=cq.Vector(0.1, 0.1, 0.1), # problem with the default direction
angleDegrees1=-90,
)
ellipsoid = sphere.transformGeometry(transform_mat)
Plane_x = cq.Face.makePlane(None, None, basePnt=(0., 0, 0), dir=(1, 0, 0))
Plane_y = cq.Face.makePlane(None, None, basePnt=(0., 0, 0), dir=(0, 1, 0))
ellipsoid = ellipsoid.split(Plane_x)
ellipsoid = ellipsoid.split(Plane_y)
workplane = cq.Workplane().add(ellipsoid)
So perhaps BRepAlgoAPI_Splitter is not behaving as expected when attempting to split in the same direction as the one used to create the sphere.
This works better, but not quite there:
import cadquery as cq
sphere = cq.Workplane().sphere(1.0).val()
a_x = 0.15
a_y = 0.31
a_z = 0.4
transform_mat = cq.Matrix(
[
[a_x, 0, 0, 0.0],
[0, a_y, 0, 0.0],
[0, 0, a_z, 0.0],
]
)
ellipsoid = sphere.transformGeometry(transform_mat)
workplane = cq.Workplane().add(ellipsoid)
splitters = []
for dim in range(3):
direction = cq.Vector([int(dim == i) for i in range(3)])
for pos in [-0.25, 0.0, 0.25]:
point = pos * direction
plane = cq.Face.makePlane(20,20,basePnt=point, dir=direction)
splitters.append(plane)
res = workplane.val().split(*splitters)
show_object(res)