cadquery icon indicating copy to clipboard operation
cadquery copied to clipboard

Multiple split with Faces produce inconsistent results

Open chemiskyy opened this issue 1 year ago • 3 comments

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")

image_480

It is however expected to keep all the split solids a grid of [5,5,5] provide expected results : image_480

chemiskyy avatar Dec 12 '23 15:12 chemiskyy

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)

image

kmarchais avatar Dec 12 '23 17:12 kmarchais

As found by @chemiskyy, using transformGeometry is creating the problem. Here is an example of a sphere with and without transformGeometry: image image

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.

kmarchais avatar Dec 13 '23 10:12 kmarchais

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)

adam-urbanczyk avatar Jan 02 '24 06:01 adam-urbanczyk