11from __future__ import annotations
22
3- from typing import Optional , Tuple , List , Dict
3+ from typing import Generator , Optional , Tuple , List , Dict
44
55from compas .geometry import Point , Vector , Line , Frame , Box
66from compas .geometry import Transformation
1212from compas_occ .interop import compas_point_to_occ_point
1313from compas_occ .interop import compas_vector_from_occ_vector
1414from compas_occ .interop import compas_vector_to_occ_vector
15-
15+ from compas_occ . interop import compas_frame_from_occ_position
1616from compas_occ .interop import array2_from_points2
1717from compas_occ .interop import array1_from_floats1
1818from compas_occ .interop import array2_from_floats2
1919from compas_occ .interop import array1_from_integers1
2020from compas_occ .interop import floats2_from_array2
2121from compas_occ .interop import points2_from_array2
2222
23- from compas_occ . geometry .curves import NurbsCurve
24- from compas_occ . geometry . surfaces ._surface import Surface
23+ from . .curves import NurbsCurve
24+ from ._surface import Surface
2525
2626from OCC .Core .gp import gp_Trsf
2727from OCC .Core .gp import gp_Pnt
2828from OCC .Core .gp import gp_Vec
29- # from OCC.Core.gp import gp_Dir
30-
3129from OCC .Core .Geom import Geom_BSplineSurface
3230from OCC .Core .Geom import Geom_Line
3331from OCC .Core .GeomAPI import GeomAPI_IntCS
3432from OCC .Core .GeomAPI import GeomAPI_ProjectPointOnSurf
35-
3633from OCC .Core .GeomLProp import GeomLProp_SLProps
37-
3834from OCC .Core .TopoDS import topods_Face
3935from OCC .Core .TopoDS import TopoDS_Shape
4036from OCC .Core .TopoDS import TopoDS_Face
41-
4237from OCC .Core .BRep import BRep_Tool_Surface
43- from OCC .Core .BRepBuilderAPI import BRepBuilderAPI_MakeFace
44- from OCC .Core .TColgp import TColgp_Array2OfPnt
45-
46- from OCC .Core .TColStd import TColStd_Array1OfReal
47- from OCC .Core .TColStd import TColStd_Array2OfReal
48- from OCC .Core .TColStd import TColStd_Array1OfInteger
49-
50- from OCC .Core .STEPControl import STEPControl_Writer
51- from OCC .Core .STEPControl import STEPControl_AsIs
52-
53- from OCC .Core .Interface import Interface_Static_SetCVal
54- from OCC .Core .IFSelect import IFSelect_RetDone
55-
5638from OCC .Core .GeomFill import GeomFill_BSplineCurves
5739from OCC .Core .GeomFill import GeomFill_CoonsStyle
58-
59- from OCC .Core .Tesselator import ShapeTesselator
60-
6140from OCC .Core .GeomAdaptor import GeomAdaptor_Surface
6241from OCC .Core .Bnd import Bnd_Box
42+ from OCC .Core .Bnd import Bnd_OBB
6343from OCC .Core .BndLib import BndLib_AddSurface_Add
44+ from OCC .Core .BndLib import BndLib_AddSurface_AddOptimal
45+ from OCC .Core .BRepBndLib import brepbndlib_AddOBB
6446
6547Point .from_occ = classmethod (compas_point_from_occ_point )
6648Point .to_occ = compas_point_to_occ_point
6749Vector .from_occ = classmethod (compas_vector_from_occ_vector )
6850Vector .to_occ = compas_vector_to_occ_vector
51+ Frame .from_occ = classmethod (compas_frame_from_occ_position )
6952Line .to_occ = compas_line_to_occ_line
7053
7154
7255class Points :
7356 def __init__ (self , surface ):
7457 self .occ_surface = surface
75- # self._points = None
7658
7759 @property
7860 def points (self ):
79- # if not self._points:
80- # self._points =
81- # return self._points
8261 return points2_from_array2 (self .occ_surface .Poles ())
8362
8463 def __getitem__ (self , index ):
@@ -157,13 +136,29 @@ def DATASCHEMA(self):
157136 def JSONSCHEMANAME (self ):
158137 raise NotImplementedError
159138
160- def __init__ (self , name = None ) -> None :
139+ def __init__ (self , name : str = None ) -> None :
161140 super ().__init__ (name = name )
162141 self .occ_surface = None
163142 self ._points = None
164143
165144 def __eq__ (self , other : NurbsSurface ) -> bool :
166- raise NotImplementedError
145+ for a , b in zip (flatten (self .points ), flatten (other .points )):
146+ if a != b :
147+ return False
148+ for a , b in zip (flatten (self .weights ), flatten (other .weights )):
149+ if a != b :
150+ return False
151+ for a , b in zip (self .u_knots , self .v_knots ):
152+ if a != b :
153+ return False
154+ for a , b in zip (self .u_mults , self .v_mults ):
155+ if a != b :
156+ return False
157+ if self .u_degree != self .v_degree :
158+ return False
159+ if self .is_u_periodic != self .is_v_periodic :
160+ return False
161+ return True
167162
168163 def __str__ (self ):
169164 lines = [
@@ -374,6 +369,11 @@ def from_fill(cls, curve1: NurbsCurve, curve2: NurbsCurve) -> NurbsSurface:
374369
375370 def to_step (self , filepath : str , schema : str = "AP203" ) -> None :
376371 """Write the surface geometry to a STP file."""
372+ from OCC .Core .STEPControl import STEPControl_Writer
373+ from OCC .Core .STEPControl import STEPControl_AsIs
374+ from OCC .Core .Interface import Interface_Static_SetCVal
375+ from OCC .Core .IFSelect import IFSelect_RetDone
376+
377377 step_writer = STEPControl_Writer ()
378378 Interface_Static_SetCVal ("write.step.schema" , schema )
379379 step_writer .Transfer (self .occ_face , STEPControl_AsIs )
@@ -383,6 +383,8 @@ def to_step(self, filepath: str, schema: str = "AP203") -> None:
383383
384384 def to_tesselation (self ) -> Mesh :
385385 """Convert the surface to a triangle mesh."""
386+ from OCC .Core .Tesselator import ShapeTesselator
387+
386388 tess = ShapeTesselator (self .occ_shape )
387389 tess .Compute ()
388390 vertices = []
@@ -395,16 +397,22 @@ def to_tesselation(self) -> Mesh:
395397
396398 def to_mesh (self , u : int = 100 , v : Optional [int ] = None ) -> Mesh :
397399 """Convert the surface to a quad mesh."""
398- quads = []
400+ from itertools import product
401+ from functools import lru_cache
402+
403+ @lru_cache (maxsize = None )
404+ def point_at (i , j ):
405+ return self .point_at (i , j )
406+
399407 v = v or u
400408 U , V = meshgrid (self .u_space (u ), self .v_space (v ))
401- for i in range ( v - 1 ):
402- for j in range ( u - 1 ):
403- a = self . point_at (U [i + 0 ][j + 0 ], V [i + 0 ][j + 0 ])
404- b = self . point_at (U [i + 0 ][j + 1 ], V [i + 0 ][j + 1 ])
405- c = self . point_at (U [i + 1 ][j + 1 ], V [i + 1 ][j + 1 ])
406- d = self . point_at ( U [ i + 1 ][ j + 0 ], V [ i + 1 ][ j + 0 ])
407- quads . append ([ a , b , c , d ])
409+ quads = [[
410+ point_at ( U [ i + 0 ][ j + 0 ], V [ i + 0 ][ j + 0 ]),
411+ point_at (U [i + 0 ][j + 1 ], V [i + 0 ][j + 1 ]),
412+ point_at (U [i + 1 ][j + 1 ], V [i + 1 ][j + 1 ]),
413+ point_at (U [i + 1 ][j + 0 ], V [i + 1 ][j + 0 ])
414+ ] for i , j in product ( range ( v - 1 ), range ( u - 1 ))]
415+
408416 return Mesh .from_polygons (quads )
409417
410418 # ==============================================================================
@@ -413,37 +421,13 @@ def to_mesh(self, u: int = 100, v: Optional[int] = None) -> Mesh:
413421
414422 @property
415423 def occ_shape (self ) -> TopoDS_Shape :
424+ from OCC .Core .BRepBuilderAPI import BRepBuilderAPI_MakeFace
416425 return BRepBuilderAPI_MakeFace (self .occ_surface , 1e-6 ).Shape ()
417426
418427 @property
419428 def occ_face (self ) -> TopoDS_Face :
420429 return topods_Face (self .occ_shape )
421430
422- @property
423- def occ_points (self ) -> TColgp_Array2OfPnt :
424- return self .occ_surface .Poles ()
425-
426- @property
427- def occ_weights (self ) -> TColStd_Array2OfReal :
428- weights = self .occ_surface .Weights ()
429- return weights
430-
431- @property
432- def occ_u_knots (self ) -> TColStd_Array1OfReal :
433- return self .occ_surface .UKnots ()
434-
435- @property
436- def occ_v_knots (self ) -> TColStd_Array1OfReal :
437- return self .occ_surface .VKnots ()
438-
439- @property
440- def occ_u_mults (self ) -> TColStd_Array1OfInteger :
441- return self .occ_surface .UMultiplicities ()
442-
443- @property
444- def occ_v_mults (self ) -> TColStd_Array1OfInteger :
445- return self .occ_surface .VMultiplicities ()
446-
447431 # ==============================================================================
448432 # Properties
449433 # ==============================================================================
@@ -456,7 +440,7 @@ def points(self) -> List[List[Point]]:
456440
457441 @property
458442 def weights (self ) -> List [List [float ]]:
459- weights = self .occ_weights
443+ weights = self .occ_surface . Weights ()
460444 if not weights :
461445 weights = [[1.0 ] * len (self .points [0 ]) for _ in range (len (self .points ))]
462446 else :
@@ -465,19 +449,19 @@ def weights(self) -> List[List[float]]:
465449
466450 @property
467451 def u_knots (self ) -> List [float ]:
468- return list (self .occ_u_knots )
452+ return list (self .occ_surface . UKnots () )
469453
470454 @property
471455 def v_knots (self ) -> List [float ]:
472- return list (self .occ_v_knots )
456+ return list (self .occ_surface . VKnots () )
473457
474458 @property
475459 def u_mults (self ) -> List [int ]:
476- return list (self .occ_u_mults )
460+ return list (self .occ_surface . UMultiplicities () )
477461
478462 @property
479463 def v_mults (self ) -> List [int ]:
480- return list (self .occ_v_mults )
464+ return list (self .occ_surface . VMultiplicities () )
481465
482466 @property
483467 def u_degree (self ) -> int :
@@ -546,13 +530,13 @@ def intersections_with_line(self, line: Line) -> List[Point]:
546530 points .append (point )
547531 return points
548532
549- def u_space (self , n : int = 10 ) -> List [float ]:
533+ def u_space (self , n : int = 10 ) -> Generator [float , None , None ]:
550534 """Compute evenly spaced parameters over the surface domain in the U direction.
551535 """
552536 umin , umax = self .u_domain
553537 return linspace (umin , umax , n )
554538
555- def v_space (self , n : int = 10 ) -> List [float ]:
539+ def v_space (self , n : int = 10 ) -> Generator [float , None , None ]:
556540 """Compute evenly spaced parameters over the surface domain in the V direction.
557541 """
558542 vmin , vmax = self .v_domain
@@ -568,6 +552,19 @@ def v_isocurve(self, v: float) -> NurbsCurve:
568552 occ_curve = self .occ_surface .VIso (v )
569553 return NurbsCurve .from_occ (occ_curve )
570554
555+ def boundary (self ) -> List [NurbsCurve ]:
556+ """Compute the boundary curves of the surface."""
557+ umin , umax , vmin , vmax = self .occ_surface .Bounds ()
558+ curves = [
559+ self .v_isocurve (vmin ),
560+ self .u_isocurve (umax ),
561+ self .v_isocurve (vmax ),
562+ self .u_isocurve (umin )
563+ ]
564+ curves [- 2 ].reverse ()
565+ curves [- 1 ].reverse ()
566+ return curves
567+
571568 def xyz (self , nu : int = 10 , nv : int = 10 ) -> List [Point ]:
572569 """Compute point locations corresponding to evenly spaced parameters over the surface domain.
573570 """
@@ -606,14 +603,21 @@ def closest_point(self, point, distance=None) -> Point:
606603 pnt = projector .NearestPoint ()
607604 return Point .from_occ (pnt )
608605
609- def aabb (self , precision : float = 0.0 ) -> Box :
606+ def aabb (self , precision : float = 0.0 , optimal : bool = False ) -> Box :
610607 """Compute the axis aligned bounding box of the surface."""
611608 box = Bnd_Box ()
612- BndLib_AddSurface_Add (GeomAdaptor_Surface (self .occ_surface ), precision , box )
609+ if optimal :
610+ add = BndLib_AddSurface_AddOptimal
611+ else :
612+ add = BndLib_AddSurface_Add
613+ add (GeomAdaptor_Surface (self .occ_surface ), precision , box )
613614 return Box .from_diagonal ((
614615 Point .from_occ (box .CornerMin ()),
615616 Point .from_occ (box .CornerMax ())
616617 ))
617618
618619 def obb (self , precision : float = 0.0 ) -> Box :
619620 """Compute the oriented bounding box of the surface."""
621+ box = Bnd_OBB ()
622+ brepbndlib_AddOBB (self .occ_shape , box , True , True , True )
623+ return Box (Frame .from_occ (box .Position ()), box .XHSize (), box .YHSize (), box .ZHSize ())
0 commit comments