156156 Geom_Surface ,
157157 Geom_Plane ,
158158 Geom_BSplineCurve ,
159+ Geom_Curve ,
159160)
160161from OCP .Geom2d import Geom2d_Line
161162
238239from OCP .BRepOffsetAPI import BRepOffsetAPI_MakeFilling
239240from OCP .BRepOffset import BRepOffset_MakeOffset , BRepOffset_Mode
240241
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+ )
242250
243251from OCP .IFSelect import IFSelect_ReturnStatus
244252
296304
297305from OCP .GeomAdaptor import GeomAdaptor_Surface
298306
307+ from OCP .OSD import OSD_ThreadPool
308+
299309from math import pi , sqrt , inf , radians , cos
300310
301311import warnings
302312
303313from ..utils import deprecate
304314
305315Real = Union [float , int ]
316+ GlueLiteral = Literal ["partial" , "full" , None ]
306317
307318TOLERANCE = 1e-6
308319
@@ -1735,6 +1746,9 @@ class Mixin1DProtocol(ShapeProtocol, Protocol):
17351746 def _approxCurve (self ) -> Geom_BSplineCurve :
17361747 ...
17371748
1749+ def _curve (self ) -> Geom_Curve :
1750+ ...
1751+
17381752 def _geomAdaptor (self ) -> Union [BRepAdaptor_Curve , BRepAdaptor_CompCurve ]:
17391753 ...
17401754
@@ -1763,6 +1777,9 @@ def curvatureAt(
17631777 ) -> float :
17641778 ...
17651779
1780+ def paramsLength (self , locations : Iterable [float ]) -> List [float ]:
1781+ ...
1782+
17661783
17671784T1D = TypeVar ("T1D" , bound = Mixin1DProtocol )
17681785
@@ -1811,6 +1828,20 @@ def _approxCurve(self: Mixin1DProtocol) -> Geom_BSplineCurve:
18111828
18121829 return rv
18131830
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+
18141845 def paramAt (self : Mixin1DProtocol , d : Union [Real , Vector ]) -> float :
18151846 """
18161847 Compute parameter value at the specified normalized distance or a point.
@@ -1822,11 +1853,7 @@ def paramAt(self: Mixin1DProtocol, d: Union[Real, Vector]) -> float:
18221853 curve = self ._geomAdaptor ()
18231854
18241855 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 ()
18301857
18311858 rv = GeomAPI_ProjectPointOnCurve (
18321859 d .toPnt (), curve_ , curve .FirstParameter (), curve .LastParameter (),
@@ -1838,14 +1865,67 @@ def paramAt(self: Mixin1DProtocol, d: Union[Real, Vector]) -> float:
18381865
18391866 return rv
18401867
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+
18411921 def tangentAt (
18421922 self : Mixin1DProtocol , locationParam : float = 0.5 , mode : ParamMode = "length" ,
18431923 ) -> Vector :
18441924 """
18451925 Compute tangent vector at the specified location.
18461926
18471927 :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 )
18491929 :return: tangent vector
18501930 """
18511931
@@ -1863,6 +1943,35 @@ def tangentAt(
18631943
18641944 return Vector (gp_Dir (res ))
18651945
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+
18661975 def normal (self : Mixin1DProtocol ) -> Vector :
18671976 """
18681977 Calculate the normal Vector. Only possible for planar curves.
@@ -5432,46 +5541,142 @@ def _bool_op(
54325541 builder .Build ()
54335542
54345543
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 ):
54365565 """
5437- Fuse two shapes .
5566+ Set number of threads to be used by boolean operations .
54385567 """
54395568
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 ()
54425593
54435594 return _compound_or_shape (builder .Shape ())
54445595
54455596
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 :
54475598 """
54485599 Subtract two shapes.
54495600 """
54505601
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 ()
54535612
54545613 return _compound_or_shape (builder .Shape ())
54555614
54565615
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 :
54585619 """
54595620 Intersect two shapes.
54605621 """
54615622
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 ()
54645633
54655634 return _compound_or_shape (builder .Shape ())
54665635
54675636
5468- def split (s1 : Shape , s2 : Shape ) -> Shape :
5637+ def split (s1 : Shape , s2 : Shape , tol : float = 0.0 ) -> Shape :
54695638 """
54705639 Split one shape with another.
54715640 """
54725641
54735642 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 )))
54755680
54765681 return _compound_or_shape (builder .Shape ())
54775682
0 commit comments