Skip to content

Commit 64b7048

Browse files
Rework Workplane.split to support arbitrary shapes
1 parent 5af6171 commit 64b7048

File tree

2 files changed

+91
-24
lines changed

2 files changed

+91
-24
lines changed

cadquery/cq.py

Lines changed: 83 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
Dict,
3636
)
3737
from typing_extensions import Literal
38-
38+
from inspect import Parameter, Signature
3939

4040
from .occ_impl.geom import Vector, Plane, Location
4141
from .occ_impl.shapes import (
@@ -264,7 +264,15 @@ def _collectProperty(self, propName: str) -> List[CQObject]:
264264

265265
return list(all.values())
266266

267+
@overload
267268
def split(self: T, keepTop: bool = False, keepBottom: bool = False) -> T:
269+
...
270+
271+
@overload
272+
def split(self: T, splitter: Union[T, Shape]) -> T:
273+
...
274+
275+
def split(self: T, *args, **kwargs) -> T:
268276
"""
269277
Splits a solid on the stack into two parts, optionally keeping the separate parts.
270278
@@ -284,28 +292,64 @@ def split(self: T, keepTop: bool = False, keepBottom: bool = False) -> T:
284292
c = c.faces(">Y").workplane(-0.5).split(keepTop=True)
285293
"""
286294

287-
if (not keepTop) and (not keepBottom):
288-
raise ValueError("You have to keep at least one half")
295+
# split using an object
296+
if len(args) == 1 and isinstance(args[0], (Workplane, Shape)):
289297

290-
solid = self.findSolid()
298+
arg = args[0]
291299

292-
maxDim = solid.BoundingBox().DiagonalLength * 10.0
293-
topCutBox = self.rect(maxDim, maxDim)._extrude(maxDim)
294-
bottomCutBox = self.rect(maxDim, maxDim)._extrude(-maxDim)
295-
296-
top = solid.cut(bottomCutBox)
297-
bottom = solid.cut(topCutBox)
300+
solid = self.findSolid()
301+
tools = (
302+
(arg,)
303+
if isinstance(arg, Shape)
304+
else [v for v in arg.vals() if isinstance(v, Shape)]
305+
)
306+
rv = [solid.split(*tools)]
298307

299-
if keepTop and keepBottom:
300-
# Put both on the stack, leave original unchanged.
301-
return self.newObject([top, bottom])
308+
# split using the current wokrplane
302309
else:
303-
# Put the one we are keeping on the stack, and also update the
304-
# context solidto the one we kept.
305-
if keepTop:
306-
return self.newObject([top])
310+
311+
# boilerplate for arg/kwarg parsing
312+
sig = Signature(
313+
(
314+
Parameter(
315+
"keepTop", Parameter.POSITIONAL_OR_KEYWORD, default=False
316+
),
317+
Parameter(
318+
"keepBottom", Parameter.POSITIONAL_OR_KEYWORD, default=False
319+
),
320+
)
321+
)
322+
323+
bound_args = sig.bind(*args, **kwargs)
324+
bound_args.apply_defaults()
325+
326+
keepTop = bound_args.arguments["keepTop"]
327+
keepBottom = bound_args.arguments["keepBottom"]
328+
329+
if (not keepTop) and (not keepBottom):
330+
raise ValueError("You have to keep at least one half")
331+
332+
solid = self.findSolid()
333+
334+
maxDim = solid.BoundingBox().DiagonalLength * 10.0
335+
topCutBox = self.rect(maxDim, maxDim)._extrude(maxDim)
336+
bottomCutBox = self.rect(maxDim, maxDim)._extrude(-maxDim)
337+
338+
top = solid.cut(bottomCutBox)
339+
bottom = solid.cut(topCutBox)
340+
341+
if keepTop and keepBottom:
342+
# Put both on the stack, leave original unchanged.
343+
rv = [top, bottom]
307344
else:
308-
return self.newObject([bottom])
345+
# Put the one we are keeping on the stack, and also update the
346+
# context solidto the one we kept.
347+
if keepTop:
348+
rv = [top]
349+
else:
350+
rv = [bottom]
351+
352+
return self.newObject(rv)
309353

310354
@deprecate()
311355
def combineSolids(
@@ -1903,6 +1947,10 @@ def parametricCurve(
19031947
N: int = 400,
19041948
start: float = 0,
19051949
stop: float = 1,
1950+
tol: float = 1e-6,
1951+
minDeg: int = 1,
1952+
maxDeg: int = 6,
1953+
smoothing: Optional[Tuple[float, float, float]] = (1, 1, 1),
19061954
makeWire: bool = True,
19071955
) -> T:
19081956
"""
@@ -1913,6 +1961,10 @@ def parametricCurve(
19131961
:param N: number of points for discretization
19141962
:param start: starting value of the parameter t
19151963
:param stop: final value of the parameter t
1964+
:param tol: tolerance of the algorithm (default: 1e-3)
1965+
:param minDeg: minimum spline degree (default: 1)
1966+
:param maxDeg: maximum spline degree (default: 6)
1967+
:param smoothing: optional parameters for the variational smoothing algorithm (default: (1,1,1))
19161968
:param makeWire: convert the resulting spline edge to a wire
19171969
:return: a Workplane object with the current point unchanged
19181970
@@ -1923,7 +1975,9 @@ def parametricCurve(
19231975
(func(start + diff * t / N) for t in range(N + 1)), False
19241976
)
19251977

1926-
e = Edge.makeSplineApprox(allPoints)
1978+
e = Edge.makeSplineApprox(
1979+
allPoints, tol=tol, smoothing=smoothing, minDeg=minDeg, maxDeg=maxDeg
1980+
)
19271981

19281982
if makeWire:
19291983
rv_w = Wire.assembleEdges([e])
@@ -1940,6 +1994,9 @@ def parametricSurface(
19401994
start: float = 0,
19411995
stop: float = 1,
19421996
tol: float = 1e-2,
1997+
minDeg: int = 1,
1998+
maxDeg: int = 6,
1999+
smoothing: Optional[Tuple[float, float, float]] = (1, 1, 1),
19432000
) -> T:
19442001
"""
19452002
Create a spline surface approximating the provided function.
@@ -1949,7 +2006,10 @@ def parametricSurface(
19492006
:param N: number of points for discretization in one direction
19502007
:param start: starting value of the parameters u,v
19512008
:param stop: final value of the parameters u,v
1952-
:param tol: tolerance used by the approximation algorithm
2009+
:param tol: tolerance used by the approximation algorithm (default: 1e-3)
2010+
:param minDeg: minimum spline degree (default: 1)
2011+
:param maxDeg: maximum spline degree (default: 3)
2012+
:param smoothing: optional parameters for the variational smoothing algorithm (default: (1,1,1))
19532013
:return: a Workplane object with the current point unchanged
19542014
19552015
This method might be unstable and may require tuning of the tol parameter.
@@ -1970,7 +2030,9 @@ def parametricSurface(
19702030
)
19712031
)
19722032

1973-
f = Face.makeSplineApprox(allPoints, tol=tol)
2033+
f = Face.makeSplineApprox(
2034+
allPoints, tol=tol, smoothing=smoothing, minDeg=minDeg, maxDeg=maxDeg
2035+
)
19742036

19752037
return self.newObject([f])
19762038

tests/test_cadquery.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4463,8 +4463,13 @@ def testParametricSurface(self):
44634463

44644464
from math import pi, cos
44654465

4466-
r = Workplane().parametricSurface(
4467-
lambda u, v: (u, v, cos(2 * pi * u) * cos(2 * pi * v))
4466+
r1 = Workplane().parametricSurface(
4467+
lambda u, v: (u, v, cos(pi * u) * cos(pi * v)), start=-1, stop=1
44684468
)
44694469

4470-
self.assertTrue(r.faces().val().isValid())
4470+
self.assertTrue(r1.faces().val().isValid())
4471+
4472+
r2 = Workplane().box(1, 1, 3).split(r1)
4473+
4474+
self.assertTrue(r2.solids().val().isValid())
4475+
self.assertEqual(r2.solids().size(), 2)

0 commit comments

Comments
 (0)