35
35
Dict ,
36
36
)
37
37
from typing_extensions import Literal
38
-
38
+ from inspect import Parameter , Signature
39
39
40
40
from .occ_impl .geom import Vector , Plane , Location
41
41
from .occ_impl .shapes import (
@@ -264,7 +264,15 @@ def _collectProperty(self, propName: str) -> List[CQObject]:
264
264
265
265
return list (all .values ())
266
266
267
+ @overload
267
268
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 :
268
276
"""
269
277
Splits a solid on the stack into two parts, optionally keeping the separate parts.
270
278
@@ -284,28 +292,64 @@ def split(self: T, keepTop: bool = False, keepBottom: bool = False) -> T:
284
292
c = c.faces(">Y").workplane(-0.5).split(keepTop=True)
285
293
"""
286
294
287
- if (not keepTop ) and (not keepBottom ):
288
- raise ValueError ("You have to keep at least one half" )
289
-
290
- solid = self .findSolid ()
295
+ # split using an object
296
+ if len (args ) == 1 and isinstance (args [0 ], (Workplane , Shape )):
291
297
292
- maxDim = solid .BoundingBox ().DiagonalLength * 10.0
293
- topCutBox = self .rect (maxDim , maxDim )._extrude (maxDim )
294
- bottomCutBox = self .rect (maxDim , maxDim )._extrude (- maxDim )
298
+ arg = args [0 ]
295
299
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 )]
298
307
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
302
309
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 ]
307
344
else :
308
- return self .newObject ([bottom ])
345
+ # Put the one we are keeping on the stack, and also update the
346
+ # context solid to the one we kept.
347
+ if keepTop :
348
+ rv = [top ]
349
+ else :
350
+ rv = [bottom ]
351
+
352
+ return self .newObject (rv )
309
353
310
354
@deprecate ()
311
355
def combineSolids (
@@ -1729,6 +1773,20 @@ def slot2D(self: T, length: float, diameter: float, angle: float = 0) -> T:
1729
1773
1730
1774
return self .eachpoint (lambda loc : slot .moved (loc ), True )
1731
1775
1776
+ def _toVectors (
1777
+ self , pts : Iterable [VectorLike ], includeCurrent : bool
1778
+ ) -> List [Vector ]:
1779
+
1780
+ vecs = [self .plane .toWorldCoords (p ) for p in pts ]
1781
+
1782
+ if includeCurrent :
1783
+ gstartPoint = self ._findFromPoint (False )
1784
+ allPoints = [gstartPoint ] + vecs
1785
+ else :
1786
+ allPoints = vecs
1787
+
1788
+ return allPoints
1789
+
1732
1790
def spline (
1733
1791
self : T ,
1734
1792
listOfXYTuple : Iterable [VectorLike ],
@@ -1742,10 +1800,9 @@ def spline(
1742
1800
makeWire : bool = False ,
1743
1801
) -> T :
1744
1802
"""
1745
- Create a spline interpolated through the provided points.
1803
+ Create a spline interpolated through the provided points (2D or 3D) .
1746
1804
1747
1805
:param listOfXYTuple: points to interpolate through
1748
- :type listOfXYTuple: list of 2-tuple
1749
1806
:param tangents: vectors specifying the direction of the tangent to the
1750
1807
curve at each of the specified interpolation points.
1751
1808
@@ -1807,13 +1864,7 @@ def spline(
1807
1864
that cannot be correctly interpreted as a spline.
1808
1865
"""
1809
1866
1810
- vecs = [self .plane .toWorldCoords (p ) for p in listOfXYTuple ]
1811
-
1812
- if includeCurrent :
1813
- gstartPoint = self ._findFromPoint (False )
1814
- allPoints = [gstartPoint ] + vecs
1815
- else :
1816
- allPoints = vecs
1867
+ allPoints = self ._toVectors (listOfXYTuple , includeCurrent )
1817
1868
1818
1869
if tangents :
1819
1870
tangents_g : Optional [Sequence [Vector ]] = [
@@ -1844,31 +1895,141 @@ def spline(
1844
1895
1845
1896
return self .newObject ([rv_w if makeWire else e ])
1846
1897
1898
+ def splineApprox (
1899
+ self : T ,
1900
+ points : Iterable [VectorLike ],
1901
+ tol : Optional [float ] = 1e-6 ,
1902
+ minDeg : int = 1 ,
1903
+ maxDeg : int = 6 ,
1904
+ smoothing : Optional [Tuple [float , float , float ]] = (1 , 1 , 1 ),
1905
+ forConstruction : bool = False ,
1906
+ includeCurrent : bool = False ,
1907
+ makeWire : bool = False ,
1908
+ ) -> T :
1909
+ """
1910
+ Create a spline interpolated through the provided points (2D or 3D).
1911
+
1912
+ :param points: points to interpolate through
1913
+ :param tol: tolerance of the algorithm (default: 1e-6)
1914
+ :param minDeg: minimum spline degree (default: 1)
1915
+ :param maxDeg: maximum spline degree (default: 6)
1916
+ :param smoothing: optional parameters for the variational smoothing algorithm (default: (1,1,1))
1917
+ :param includeCurrent: use current point as a starting point of the curve
1918
+ :param makeWire: convert the resulting spline edge to a wire
1919
+ :return: a Workplane object with the current point at the end of the spline
1920
+
1921
+ *WARNING* for advanced users.
1922
+ """
1923
+
1924
+ allPoints = self ._toVectors (points , includeCurrent )
1925
+
1926
+ e = Edge .makeSplineApprox (
1927
+ allPoints ,
1928
+ minDeg = minDeg ,
1929
+ maxDeg = maxDeg ,
1930
+ smoothing = smoothing ,
1931
+ ** ({"tol" : tol } if tol else {}),
1932
+ )
1933
+
1934
+ if makeWire :
1935
+ rv_w = Wire .assembleEdges ([e ])
1936
+ if not forConstruction :
1937
+ self ._addPendingWire (rv_w )
1938
+ else :
1939
+ if not forConstruction :
1940
+ self ._addPendingEdge (e )
1941
+
1942
+ return self .newObject ([rv_w if makeWire else e ])
1943
+
1847
1944
def parametricCurve (
1848
1945
self : T ,
1849
1946
func : Callable [[float ], VectorLike ],
1850
1947
N : int = 400 ,
1851
1948
start : float = 0 ,
1852
1949
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 ),
1853
1954
makeWire : bool = True ,
1854
1955
) -> T :
1855
1956
"""
1856
- Create a spline interpolated through the provided points .
1957
+ Create a spline curve approximating the provided function .
1857
1958
1858
- :param func: function f(t) that will generate (x,y) pairs
1859
- :type func: float --> (float,float)
1959
+ :param func: function f(t) that will generate (x,y,z ) pairs
1960
+ :type func: float --> (float,float,float )
1860
1961
:param N: number of points for discretization
1861
1962
:param start: starting value of the parameter t
1862
1963
: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))
1863
1968
:param makeWire: convert the resulting spline edge to a wire
1864
1969
:return: a Workplane object with the current point unchanged
1865
1970
1866
1971
"""
1867
1972
1868
1973
diff = stop - start
1869
- allPoints = [func (start + diff * t / N ) for t in range (N + 1 )]
1974
+ allPoints = self ._toVectors (
1975
+ (func (start + diff * t / N ) for t in range (N + 1 )), False
1976
+ )
1977
+
1978
+ e = Edge .makeSplineApprox (
1979
+ allPoints , tol = tol , smoothing = smoothing , minDeg = minDeg , maxDeg = maxDeg
1980
+ )
1981
+
1982
+ if makeWire :
1983
+ rv_w = Wire .assembleEdges ([e ])
1984
+ self ._addPendingWire (rv_w )
1985
+ else :
1986
+ self ._addPendingEdge (e )
1987
+
1988
+ return self .newObject ([rv_w if makeWire else e ])
1989
+
1990
+ def parametricSurface (
1991
+ self : T ,
1992
+ func : Callable [[float , float ], VectorLike ],
1993
+ N : int = 20 ,
1994
+ start : float = 0 ,
1995
+ stop : float = 1 ,
1996
+ tol : float = 1e-2 ,
1997
+ minDeg : int = 1 ,
1998
+ maxDeg : int = 6 ,
1999
+ smoothing : Optional [Tuple [float , float , float ]] = (1 , 1 , 1 ),
2000
+ ) -> T :
2001
+ """
2002
+ Create a spline surface approximating the provided function.
2003
+
2004
+ :param func: function f(u,v) that will generate (x,y,z) pairs
2005
+ :type func: (float,float) --> (float,float,float)
2006
+ :param N: number of points for discretization in one direction
2007
+ :param start: starting value of the parameters u,v
2008
+ :param stop: final value of the parameters u,v
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))
2013
+ :return: a Workplane object with the current point unchanged
2014
+
2015
+ This method might be unstable and may require tuning of the tol parameter.
2016
+
2017
+ """
2018
+
2019
+ diff = stop - start
2020
+ allPoints = []
2021
+
2022
+ for i in range (N + 1 ):
2023
+ generator = (
2024
+ func (start + diff * i / N , start + diff * j / N ) for j in range (N + 1 )
2025
+ )
2026
+ allPoints .append (self ._toVectors (generator , False ))
2027
+
2028
+ f = Face .makeSplineApprox (
2029
+ allPoints , tol = tol , smoothing = smoothing , minDeg = minDeg , maxDeg = maxDeg
2030
+ )
1870
2031
1871
- return self .spline ( allPoints , includeCurrent = False , makeWire = makeWire )
2032
+ return self .newObject ([ f ] )
1872
2033
1873
2034
def ellipseArc (
1874
2035
self : T ,
0 commit comments