1919
2020from io import BytesIO
2121
22+
2223from vtkmodules .vtkCommonDataModel import vtkPolyData
2324from vtkmodules .vtkFiltersCore import vtkTriangleFilter , vtkPolyDataNormals
2425
5960)
6061
6162# Array of points (used for B-spline construction):
62- from OCP .TColgp import TColgp_HArray1OfPnt , TColgp_HArray2OfPnt , TColgp_Array1OfPnt
63+ from OCP .TColgp import (
64+ TColgp_HArray1OfPnt ,
65+ TColgp_HArray2OfPnt ,
66+ TColgp_Array1OfPnt ,
67+ TColgp_HArray1OfPnt2d ,
68+ )
6369
6470# Array of vectors (used for B-spline interpolation):
6571from OCP .TColgp import TColgp_Array1OfVec
162168)
163169from OCP .Geom2d import Geom2d_Line
164170
171+ from OCP .Geom2dAPI import Geom2dAPI_Interpolate
172+
165173from OCP .BRepLib import BRepLib , BRepLib_FindSurface
166174
167175from OCP .BRepOffsetAPI import (
260268
261269from OCP .ShapeAnalysis import (
262270 ShapeAnalysis_FreeBounds ,
271+ ShapeAnalysis_Edge ,
263272 ShapeAnalysis_Wire ,
264273 ShapeAnalysis_Surface ,
265274)
@@ -1827,6 +1836,9 @@ def _curve_and_param(
18271836 ) -> Tuple [Union [BRepAdaptor_Curve , BRepAdaptor_CompCurve ], float ]:
18281837 ...
18291838
1839+ def bounds (self ) -> Tuple [float , float ]:
1840+ ...
1841+
18301842 def paramAt (self , d : float ) -> float :
18311843 ...
18321844
@@ -1857,6 +1869,13 @@ def paramsLength(self, locations: Iterable[float]) -> List[float]:
18571869class Mixin1D (object ):
18581870 def _bounds (self : Mixin1DProtocol ) -> Tuple [float , float ]:
18591871
1872+ return self .bounds ()
1873+
1874+ def bounds (self : Mixin1DProtocol ) -> Tuple [float , float ]:
1875+ """
1876+ Parametric bounds of the curve.
1877+ """
1878+
18601879 curve = self ._geomAdaptor ()
18611880 return curve .FirstParameter (), curve .LastParameter ()
18621881
@@ -2370,6 +2389,13 @@ def trim(self, u0: Real, u1: Real) -> "Edge":
23702389
23712390 return self .__class__ (bldr .Shape ())
23722391
2392+ def hasPCurve (self , f : "Face" ) -> bool :
2393+ """
2394+ Check if self has a pcurve defined on f.
2395+ """
2396+
2397+ return ShapeAnalysis_Edge ().HasPCurve (self .wrapped , f .wrapped )
2398+
23732399 @classmethod
23742400 def makeCircle (
23752401 cls ,
@@ -3043,6 +3069,13 @@ def _geomAdaptor(self) -> Geom_Surface:
30433069
30443070 def _uvBounds (self ) -> Tuple [float , float , float , float ]:
30453071
3072+ return self .uvBounds ()
3073+
3074+ def uvBounds (self ) -> Tuple [float , float , float , float ]:
3075+ """
3076+ Parametric bounds (u_min, u_max, v_min, v_max).
3077+ """
3078+
30463079 return BRepTools .UVBounds_s (self .wrapped )
30473080
30483081 def paramAt (self , pt : VectorLike ) -> Tuple [float , float ]:
@@ -3494,14 +3527,9 @@ def thicken(self, thickness: float) -> "Solid":
34943527 return Solid (builder .Shape ())
34953528
34963529 @classmethod
3497- def constructOn (cls , f : "Face" , outer : "Wire" , * inner : "Wire" ) -> "Face" :
3498-
3499- bldr = BRepBuilderAPI_MakeFace (f ._geomAdaptor (), outer .wrapped )
3500-
3501- for w in inner :
3502- bldr .Add (TopoDS .Wire_s (w .wrapped ))
3530+ def constructOn (cls , f : "Face" , outer : "Wire" , * inner : "Wire" ) -> Self :
35033531
3504- return cls ( bldr . Face ()). fix ( )
3532+ return f . trim ( outer , * inner )
35053533
35063534 def project (self , other : "Face" , d : VectorLike ) -> "Face" :
35073535
@@ -3519,9 +3547,10 @@ def toArcs(self, tolerance: float = 1e-3) -> "Face":
35193547
35203548 return self .__class__ (BRepAlgo .ConvertFace_s (self .wrapped , tolerance ))
35213549
3522- def trim (self , u0 : Real , u1 : Real , v0 : Real , v1 : Real , tol : Real = 1e-6 ) -> "Face" :
3550+ @multimethod
3551+ def trim (self , u0 : Real , u1 : Real , v0 : Real , v1 : Real , tol : Real = 1e-6 ) -> Self :
35233552 """
3524- Trim the face in the parametric space to (u0, u1).
3553+ Trim the face in the (u,v) space to (u0, u1)x(v1, v2 ).
35253554
35263555 NB: this operation is done on the base geometry.
35273556 """
@@ -3530,6 +3559,59 @@ def trim(self, u0: Real, u1: Real, v0: Real, v1: Real, tol: Real = 1e-6) -> "Fac
35303559
35313560 return self .__class__ (bldr .Shape ())
35323561
3562+ @trim .register
3563+ def _ (
3564+ self ,
3565+ pt1 : Tuple [Real , Real ],
3566+ pt2 : Tuple [Real , Real ],
3567+ pt3 : Tuple [Real , Real ],
3568+ * pts : Tuple [Real , Real ],
3569+ ) -> Self :
3570+ """
3571+ Trim the face using a polyline defined in the (u,v) space.
3572+ """
3573+
3574+ segs_uv = []
3575+ geom = self ._geomAdaptor ()
3576+
3577+ # build (u,v) segments
3578+ for el1 , el2 in zip ((pt1 , pt2 , pt3 , * pts ), (pt2 , pt3 , * pts , pt1 )):
3579+ segs_uv .append (GCE2d_MakeSegment (gp_Pnt2d (* el1 ), gp_Pnt2d (* el2 )).Value ())
3580+
3581+ # convert to edges
3582+ edges = []
3583+
3584+ for seg in segs_uv :
3585+ edges .append (BRepBuilderAPI_MakeEdge (seg , geom ).Edge ())
3586+
3587+ # convert to a wire
3588+ builder = BRepBuilderAPI_MakeWire ()
3589+
3590+ tmp = TopTools_ListOfShape ()
3591+ for edge in edges :
3592+ tmp .Append (edge )
3593+
3594+ builder .Add (tmp )
3595+
3596+ w = builder .Wire ()
3597+ BRepLib .BuildCurves3d_s (w )
3598+
3599+ # construct the final trimmed face
3600+ return self .constructOn (self , Wire (w ))
3601+
3602+ @trim .register
3603+ def _ (self , outer : Wire , * inner : Wire ) -> Self :
3604+ """
3605+ Trim using wires. The provided wires need to have a pcurve on self.
3606+ """
3607+
3608+ bldr = BRepBuilderAPI_MakeFace (self ._geomAdaptor (), outer .wrapped )
3609+
3610+ for w in inner :
3611+ bldr .Add (TopoDS .Wire_s (w .wrapped ))
3612+
3613+ return self .__class__ (bldr .Face ()).fix ()
3614+
35333615 def isoline (self , param : Real , direction : Literal ["u" , "v" ] = "v" ) -> Edge :
35343616 """
35353617 Construct an isoline.
@@ -4875,22 +4957,23 @@ def _get_wires(s: Shape) -> Iterable[Shape]:
48754957 raise ValueError (f"Required type(s): Edge, Wire; encountered { t } " )
48764958
48774959
4878- def _get_edges (s : Shape ) -> Iterable [Shape ]:
4960+ def _get_edges (* shapes : Shape ) -> Iterable [Shape ]:
48794961 """
4880- Get wires or wires from edges .
4962+ Get edges or edges from wires .
48814963 """
48824964
4883- t = s .ShapeType ()
4884-
4885- if t == "Edge" :
4886- yield s
4887- elif t == "Wire" :
4888- yield from _get_edges (s .edges ())
4889- elif t == "Compound" :
4890- for el in s :
4891- yield from _get_edges (el )
4892- else :
4893- raise ValueError (f"Required type(s): Edge, Wire; encountered { t } " )
4965+ for s in shapes :
4966+ t = s .ShapeType ()
4967+
4968+ if t == "Edge" :
4969+ yield s
4970+ elif t == "Wire" :
4971+ yield from _get_edges (s .edges ())
4972+ elif t == "Compound" :
4973+ for el in s :
4974+ yield from _get_edges (el )
4975+ else :
4976+ raise ValueError (f"Required type(s): Edge, Wire; encountered { t } " )
48944977
48954978
48964979def _get_wire_lists (s : Sequence [Shape ]) -> List [List [Union [Wire , Vertex ]]]:
@@ -5017,7 +5100,7 @@ def _compound_or_shape(s: Union[TopoDS_Shape, List[TopoDS_Shape]]) -> Shape:
50175100
50185101def _pts_to_harray (pts : Sequence [VectorLike ]) -> TColgp_HArray1OfPnt :
50195102 """
5020- Convert a sequence of Vecotor to a TColgp harray (OCCT specific).
5103+ Convert a sequence of Vector to a TColgp harray (OCCT specific).
50215104 """
50225105
50235106 rv = TColgp_HArray1OfPnt (1 , len (pts ))
@@ -5028,6 +5111,19 @@ def _pts_to_harray(pts: Sequence[VectorLike]) -> TColgp_HArray1OfPnt:
50285111 return rv
50295112
50305113
5114+ def _pts_to_harray2D (pts : Sequence [Tuple [Real , Real ]]) -> TColgp_HArray1OfPnt2d :
5115+ """
5116+ Convert a sequence of 2d points to a TColgp harray (OCCT specific).
5117+ """
5118+
5119+ rv = TColgp_HArray1OfPnt2d (1 , len (pts ))
5120+
5121+ for i , p in enumerate (pts ):
5122+ rv .SetValue (i + 1 , gp_Pnt2d (* p ))
5123+
5124+ return rv
5125+
5126+
50315127def _floats_to_harray (vals : Sequence [float ]) -> TColStd_HArray1OfReal :
50325128 """
50335129 Convert a sequence of floats to a TColstd harray (OCCT specific).
@@ -5129,6 +5225,91 @@ def _adaptor_curve_to_edge(crv: Adaptor3d_Curve, p1: float, p2: float) -> TopoDS
51295225ShapeHistory = Dict [Union [Shape , str ], Shape ]
51305226
51315227
5228+ @multimethod
5229+ def edgeOn (
5230+ base : Shape ,
5231+ pts : Sequence [Tuple [Real , Real ]],
5232+ periodic : bool = False ,
5233+ tol : float = 1e-6 ,
5234+ ) -> Shape :
5235+ """
5236+ Build an edge on a face from points in (u,v) space.
5237+ """
5238+
5239+ f = _get_one (base , "Face" )
5240+
5241+ # interpolate the u,v points
5242+ spline_bldr = Geom2dAPI_Interpolate (_pts_to_harray2D (pts ), periodic , tol )
5243+ spline_bldr .Perform ()
5244+
5245+ # build the final edge
5246+ rv = BRepBuilderAPI_MakeEdge (spline_bldr .Curve (), f ._geomAdaptor ()).Edge ()
5247+ BRepLib .BuildCurves3d_s (rv )
5248+
5249+ return _compound_or_shape (rv )
5250+
5251+
5252+ @edgeOn .register
5253+ def _ (
5254+ fbase : Shape , edg : Shape , * edgs : Shape , tol : float = 1e-6 , N : int = 20 ,
5255+ ):
5256+ """
5257+ Map one or more edges onto a base face in the u,v space.
5258+ """
5259+
5260+ f = _get_one (fbase , "Face" )
5261+
5262+ rvs : List [TopoDS_Shape ] = []
5263+
5264+ for el in _get_edges (edg , * edgs ):
5265+
5266+ # sample the original curve
5267+ pts3D , params = el .sample (N )
5268+
5269+ # convert to 2D points ignoring the z coord
5270+ pts = [(el .x , el .y ) for el in pts3D ]
5271+
5272+ # handle periodicity
5273+ t0 , t1 = el ._bounds ()
5274+ el_crv = el ._geomAdaptor ()
5275+
5276+ periodic = False
5277+
5278+ # periodic (and closed)
5279+ if el_crv .IsPeriodic () and el_crv .IsClosed ():
5280+ periodic = True
5281+ params .append (t0 + el_crv .Period ())
5282+
5283+ # only closed
5284+ elif el_crv .IsClosed ():
5285+ pts .append (pts [0 ])
5286+ params .append (t1 )
5287+
5288+ # interpolate the u,v points
5289+ spline_bldr = Geom2dAPI_Interpolate (
5290+ _pts_to_harray2D (pts ), _floats_to_harray (params ), periodic , tol
5291+ )
5292+ spline_bldr .Perform ()
5293+
5294+ # build the final edge
5295+ rv = BRepBuilderAPI_MakeEdge (spline_bldr .Curve (), f ._geomAdaptor ()).Edge ()
5296+ BRepLib .BuildCurves3d_s (rv )
5297+
5298+ rvs .append (rv )
5299+
5300+ return _compound_or_shape (rvs )
5301+
5302+
5303+ def wireOn (base : Shape , w : Shape , tol = 1e-6 , N = 20 ) -> Shape :
5304+ """
5305+ Map a wire onto a base face in the u,v space.
5306+ """
5307+
5308+ rvs = [edgeOn (base , e , tol = tol , N = N ) for e in w .Edges ()]
5309+
5310+ return wire (rvs )
5311+
5312+
51325313@multimethod
51335314def wire (* s : Shape ) -> Shape :
51345315 """
@@ -5177,6 +5358,36 @@ def face(s: Sequence[Shape]) -> Shape:
51775358 return face (* s )
51785359
51795360
5361+ def faceOn (base : Shape , * fcs : Shape , tol = 1e-6 , N = 20 ) -> Shape :
5362+ """
5363+ Build face(s) on base by mapping planar face(s) onto the (u,v) space of base.
5364+ """
5365+
5366+ rv : Shape
5367+ rvs = []
5368+
5369+ # get a face
5370+ fbase = _get_one (base , "Face" )
5371+
5372+ # iterate over all faces
5373+ for el in fcs :
5374+ for fc in el .Faces ():
5375+ # construct pcurves and trim in one go
5376+ rvs .append (
5377+ fbase .trim (
5378+ wireOn (fbase , fc .outerWire (), tol = tol , N = N ),
5379+ * (wireOn (fbase , w , tol = tol , N = N ) for w in fc .innerWires ()),
5380+ )
5381+ )
5382+
5383+ if len (rvs ) == 1 :
5384+ rv = rvs [0 ]
5385+ else :
5386+ rv = compound (rvs )
5387+
5388+ return rv
5389+
5390+
51805391def _process_sewing_history (
51815392 builder : BRepBuilderAPI_Sewing , faces : List [Face ], history : Optional [ShapeHistory ],
51825393):
0 commit comments