156
156
Geom_Surface ,
157
157
Geom_Plane ,
158
158
Geom_BSplineCurve ,
159
+ Geom_Curve ,
159
160
)
160
161
from OCP .Geom2d import Geom2d_Line
161
162
238
239
from OCP .BRepOffsetAPI import BRepOffsetAPI_MakeFilling
239
240
from OCP .BRepOffset import BRepOffset_MakeOffset , BRepOffset_Mode
240
241
241
- from OCP .BOPAlgo import BOPAlgo_GlueEnum
242
+ from OCP .BOPAlgo import (
243
+ BOPAlgo_GlueEnum ,
244
+ BOPAlgo_Builder ,
245
+ BOPAlgo_BOP ,
246
+ BOPAlgo_FUSE ,
247
+ BOPAlgo_CUT ,
248
+ BOPAlgo_COMMON ,
249
+ )
242
250
243
251
from OCP .IFSelect import IFSelect_ReturnStatus
244
252
296
304
297
305
from OCP .GeomAdaptor import GeomAdaptor_Surface
298
306
307
+ from OCP .OSD import OSD_ThreadPool
308
+
299
309
from math import pi , sqrt , inf , radians , cos
300
310
301
311
import warnings
302
312
303
313
from ..utils import deprecate
304
314
305
315
Real = Union [float , int ]
316
+ GlueLiteral = Literal ["partial" , "full" , None ]
306
317
307
318
TOLERANCE = 1e-6
308
319
@@ -1735,6 +1746,9 @@ class Mixin1DProtocol(ShapeProtocol, Protocol):
1735
1746
def _approxCurve (self ) -> Geom_BSplineCurve :
1736
1747
...
1737
1748
1749
+ def _curve (self ) -> Geom_Curve :
1750
+ ...
1751
+
1738
1752
def _geomAdaptor (self ) -> Union [BRepAdaptor_Curve , BRepAdaptor_CompCurve ]:
1739
1753
...
1740
1754
@@ -1763,6 +1777,9 @@ def curvatureAt(
1763
1777
) -> float :
1764
1778
...
1765
1779
1780
+ def paramsLength (self , locations : Iterable [float ]) -> List [float ]:
1781
+ ...
1782
+
1766
1783
1767
1784
T1D = TypeVar ("T1D" , bound = Mixin1DProtocol )
1768
1785
@@ -1811,6 +1828,20 @@ def _approxCurve(self: Mixin1DProtocol) -> Geom_BSplineCurve:
1811
1828
1812
1829
return rv
1813
1830
1831
+ def _curve (self : Mixin1DProtocol ) -> Geom_Curve :
1832
+ """
1833
+ Return the underlying curve.
1834
+ """
1835
+
1836
+ curve = self ._geomAdaptor ()
1837
+
1838
+ if isinstance (curve , BRepAdaptor_Curve ):
1839
+ rv = curve .Curve ().Curve () # get the underlying curve object
1840
+ else :
1841
+ rv = self ._approxCurve () # approximate the adaptor as a real curve
1842
+
1843
+ return rv
1844
+
1814
1845
def paramAt (self : Mixin1DProtocol , d : Union [Real , Vector ]) -> float :
1815
1846
"""
1816
1847
Compute parameter value at the specified normalized distance or a point.
@@ -1822,11 +1853,7 @@ def paramAt(self: Mixin1DProtocol, d: Union[Real, Vector]) -> float:
1822
1853
curve = self ._geomAdaptor ()
1823
1854
1824
1855
if isinstance (d , Vector ):
1825
- # handle comp curves (i.e. wire adaptors)
1826
- if isinstance (curve , BRepAdaptor_Curve ):
1827
- curve_ = curve .Curve ().Curve () # get the underlying curve object
1828
- else :
1829
- curve_ = self ._approxCurve () # approximate the adaptor as a real curve
1856
+ curve_ = self ._curve ()
1830
1857
1831
1858
rv = GeomAPI_ProjectPointOnCurve (
1832
1859
d .toPnt (), curve_ , curve .FirstParameter (), curve .LastParameter (),
@@ -1838,14 +1865,67 @@ def paramAt(self: Mixin1DProtocol, d: Union[Real, Vector]) -> float:
1838
1865
1839
1866
return rv
1840
1867
1868
+ def params (self : Mixin1DProtocol , pts : Iterable [Vector ], tol = 1e-6 ) -> List [float ]:
1869
+ """
1870
+ Computes u values closest to given vectors.
1871
+
1872
+ :param pts: the points to compute the parameters at.
1873
+ :return: list of u values.
1874
+ """
1875
+
1876
+ us = []
1877
+
1878
+ curve = self ._geomAdaptor ()
1879
+ curve_ = self ._curve ()
1880
+ umin = curve .FirstParameter ()
1881
+ umax = curve .LastParameter ()
1882
+
1883
+ # get the first point
1884
+ it = iter (pts )
1885
+ pt = next (it )
1886
+
1887
+ proj = GeomAPI_ProjectPointOnCurve (pt .toPnt (), curve_ , umin , umax )
1888
+
1889
+ u = proj .LowerDistanceParameter ()
1890
+ us .append (u )
1891
+
1892
+ for pt in it :
1893
+ proj .Perform (pt .toPnt ())
1894
+ u = proj .LowerDistanceParameter ()
1895
+
1896
+ us .append (u )
1897
+
1898
+ return us
1899
+
1900
+ def paramsLength (self : Mixin1DProtocol , locations : Iterable [float ]) -> List [float ]:
1901
+ """
1902
+ Computes u values at given relative lengths.
1903
+
1904
+ :param locations: list of distances.
1905
+ :returns: list of u values.
1906
+ :param pts: the points to compute the parameters at.
1907
+ """
1908
+
1909
+ us = []
1910
+
1911
+ curve = self ._geomAdaptor ()
1912
+
1913
+ L = GCPnts_AbscissaPoint .Length_s (curve )
1914
+
1915
+ for d in locations :
1916
+ u = GCPnts_AbscissaPoint (curve , L * d , curve .FirstParameter ()).Parameter ()
1917
+ us .append (u )
1918
+
1919
+ return us
1920
+
1841
1921
def tangentAt (
1842
1922
self : Mixin1DProtocol , locationParam : float = 0.5 , mode : ParamMode = "length" ,
1843
1923
) -> Vector :
1844
1924
"""
1845
1925
Compute tangent vector at the specified location.
1846
1926
1847
1927
:param locationParam: distance or parameter value (default: 0.5)
1848
- :param mode: position calculation mode (default: parameter )
1928
+ :param mode: position calculation mode (default: length )
1849
1929
:return: tangent vector
1850
1930
"""
1851
1931
@@ -1863,6 +1943,35 @@ def tangentAt(
1863
1943
1864
1944
return Vector (gp_Dir (res ))
1865
1945
1946
+ def tangents (
1947
+ self : Mixin1DProtocol , locations : Iterable [float ], mode : ParamMode = "length" ,
1948
+ ) -> List [Vector ]:
1949
+ """
1950
+ Compute tangent vectors at the specified locations.
1951
+
1952
+ :param locations: list of distances or parameters.
1953
+ :param mode: position calculation mode (default: length).
1954
+ :return: list of tangent vectors
1955
+ """
1956
+
1957
+ curve = self ._geomAdaptor ()
1958
+ params : Iterable [float ]
1959
+
1960
+ if mode == "length" :
1961
+ params = self .paramsLength (locations )
1962
+ else :
1963
+ params = locations
1964
+
1965
+ rv = []
1966
+ tmp = gp_Pnt ()
1967
+ res = gp_Vec ()
1968
+
1969
+ for param in params :
1970
+ curve .D1 (param , tmp , res )
1971
+ rv .append (Vector (gp_Dir (res )))
1972
+
1973
+ return rv
1974
+
1866
1975
def normal (self : Mixin1DProtocol ) -> Vector :
1867
1976
"""
1868
1977
Calculate the normal Vector. Only possible for planar curves.
@@ -5432,46 +5541,142 @@ def _bool_op(
5432
5541
builder .Build ()
5433
5542
5434
5543
5435
- def fuse (s1 : Shape , s2 : Shape , tol : float = 0.0 ) -> Shape :
5544
+ def _set_glue (builder : BOPAlgo_Builder , glue : GlueLiteral ):
5545
+
5546
+ if glue :
5547
+ builder .SetGlue (
5548
+ BOPAlgo_GlueEnum .BOPAlgo_GlueFull
5549
+ if glue == "full"
5550
+ else BOPAlgo_GlueEnum .BOPAlgo_GlueShift
5551
+ )
5552
+
5553
+
5554
+ def _set_builder_options (builder : BOPAlgo_Builder , tol : float ):
5555
+
5556
+ builder .SetRunParallel (True )
5557
+ builder .SetUseOBB (True )
5558
+ builder .SetNonDestructive (True )
5559
+
5560
+ if tol :
5561
+ builder .SetFuzzyValue (tol )
5562
+
5563
+
5564
+ def setThreads (n : int ):
5436
5565
"""
5437
- Fuse two shapes .
5566
+ Set number of threads to be used by boolean operations .
5438
5567
"""
5439
5568
5440
- builder = BRepAlgoAPI_Fuse ()
5441
- _bool_op (s1 , s2 , builder , tol )
5569
+ pool = OSD_ThreadPool .DefaultPool_s ()
5570
+ pool .Init (n )
5571
+
5572
+
5573
+ def fuse (
5574
+ s1 : Shape , s2 : Shape , * shapes : Shape , tol : float = 0.0 , glue : GlueLiteral = None ,
5575
+ ) -> Shape :
5576
+ """
5577
+ Fuse at least two shapes.
5578
+ """
5579
+
5580
+ builder = BOPAlgo_BOP ()
5581
+ builder .SetOperation (BOPAlgo_FUSE )
5582
+
5583
+ _set_glue (builder , glue )
5584
+ _set_builder_options (builder , tol )
5585
+
5586
+ builder .AddArgument (s1 .wrapped )
5587
+ builder .AddTool (s2 .wrapped )
5588
+
5589
+ for s in shapes :
5590
+ builder .AddTool (s .wrapped )
5591
+
5592
+ builder .Perform ()
5442
5593
5443
5594
return _compound_or_shape (builder .Shape ())
5444
5595
5445
5596
5446
- def cut (s1 : Shape , s2 : Shape , tol : float = 0.0 ) -> Shape :
5597
+ def cut (s1 : Shape , s2 : Shape , tol : float = 0.0 , glue : GlueLiteral = None ) -> Shape :
5447
5598
"""
5448
5599
Subtract two shapes.
5449
5600
"""
5450
5601
5451
- builder = BRepAlgoAPI_Cut ()
5452
- _bool_op (s1 , s2 , builder , tol )
5602
+ builder = BOPAlgo_BOP ()
5603
+ builder .SetOperation (BOPAlgo_CUT )
5604
+
5605
+ _set_glue (builder , glue )
5606
+ _set_builder_options (builder , tol )
5607
+
5608
+ builder .AddArgument (s1 .wrapped )
5609
+ builder .AddTool (s2 .wrapped )
5610
+
5611
+ builder .Perform ()
5453
5612
5454
5613
return _compound_or_shape (builder .Shape ())
5455
5614
5456
5615
5457
- def intersect (s1 : Shape , s2 : Shape , tol : float = 0.0 ) -> Shape :
5616
+ def intersect (
5617
+ s1 : Shape , s2 : Shape , tol : float = 0.0 , glue : GlueLiteral = None
5618
+ ) -> Shape :
5458
5619
"""
5459
5620
Intersect two shapes.
5460
5621
"""
5461
5622
5462
- builder = BRepAlgoAPI_Common ()
5463
- _bool_op (s1 , s2 , builder , tol )
5623
+ builder = BOPAlgo_BOP ()
5624
+ builder .SetOperation (BOPAlgo_COMMON )
5625
+
5626
+ _set_glue (builder , glue )
5627
+ _set_builder_options (builder , tol )
5628
+
5629
+ builder .AddArgument (s1 .wrapped )
5630
+ builder .AddTool (s2 .wrapped )
5631
+
5632
+ builder .Perform ()
5464
5633
5465
5634
return _compound_or_shape (builder .Shape ())
5466
5635
5467
5636
5468
- def split (s1 : Shape , s2 : Shape ) -> Shape :
5637
+ def split (s1 : Shape , s2 : Shape , tol : float = 0.0 ) -> Shape :
5469
5638
"""
5470
5639
Split one shape with another.
5471
5640
"""
5472
5641
5473
5642
builder = BRepAlgoAPI_Splitter ()
5474
- _bool_op (s1 , s2 , builder )
5643
+ _bool_op (s1 , s2 , builder , tol )
5644
+
5645
+ return _compound_or_shape (builder .Shape ())
5646
+
5647
+
5648
+ def imprint (
5649
+ * shapes : Shape ,
5650
+ tol : float = 0.0 ,
5651
+ glue : GlueLiteral = "full" ,
5652
+ history : Optional [Dict [Union [Shape , str ], Shape ]] = None ,
5653
+ ) -> Shape :
5654
+ """
5655
+ Imprint arbitrary number of shapes.
5656
+ """
5657
+
5658
+ builder = BOPAlgo_Builder ()
5659
+
5660
+ _set_glue (builder , glue )
5661
+ _set_builder_options (builder , tol )
5662
+
5663
+ for s in shapes :
5664
+ builder .AddArgument (s .wrapped )
5665
+
5666
+ builder .Perform ()
5667
+
5668
+ # fill history if provided
5669
+ if history is not None :
5670
+ images = builder .Images ()
5671
+
5672
+ # collect shapes present in the history dict
5673
+ for k , v in history .items ():
5674
+ if isinstance (k , str ):
5675
+ history [k ] = _compound_or_shape (list (images .Find (v .wrapped )))
5676
+
5677
+ # store all top-level shape relations
5678
+ for s in shapes :
5679
+ history [s ] = _compound_or_shape (list (images .Find (s .wrapped )))
5475
5680
5476
5681
return _compound_or_shape (builder .Shape ())
5477
5682
0 commit comments