Skip to content

Commit 556c868

Browse files
committed
cached meshing, remove occ wrappers, boundary, optimal aabb
1 parent 79fd87d commit 556c868

File tree

1 file changed

+77
-73
lines changed
  • src/compas_occ/geometry/surfaces

1 file changed

+77
-73
lines changed

src/compas_occ/geometry/surfaces/nurbs.py

Lines changed: 77 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from typing import Optional, Tuple, List, Dict
3+
from typing import Generator, Optional, Tuple, List, Dict
44

55
from compas.geometry import Point, Vector, Line, Frame, Box
66
from compas.geometry import Transformation
@@ -12,73 +12,52 @@
1212
from compas_occ.interop import compas_point_to_occ_point
1313
from compas_occ.interop import compas_vector_from_occ_vector
1414
from compas_occ.interop import compas_vector_to_occ_vector
15-
15+
from compas_occ.interop import compas_frame_from_occ_position
1616
from compas_occ.interop import array2_from_points2
1717
from compas_occ.interop import array1_from_floats1
1818
from compas_occ.interop import array2_from_floats2
1919
from compas_occ.interop import array1_from_integers1
2020
from compas_occ.interop import floats2_from_array2
2121
from 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

2626
from OCC.Core.gp import gp_Trsf
2727
from OCC.Core.gp import gp_Pnt
2828
from OCC.Core.gp import gp_Vec
29-
# from OCC.Core.gp import gp_Dir
30-
3129
from OCC.Core.Geom import Geom_BSplineSurface
3230
from OCC.Core.Geom import Geom_Line
3331
from OCC.Core.GeomAPI import GeomAPI_IntCS
3432
from OCC.Core.GeomAPI import GeomAPI_ProjectPointOnSurf
35-
3633
from OCC.Core.GeomLProp import GeomLProp_SLProps
37-
3834
from OCC.Core.TopoDS import topods_Face
3935
from OCC.Core.TopoDS import TopoDS_Shape
4036
from OCC.Core.TopoDS import TopoDS_Face
41-
4237
from 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-
5638
from OCC.Core.GeomFill import GeomFill_BSplineCurves
5739
from OCC.Core.GeomFill import GeomFill_CoonsStyle
58-
59-
from OCC.Core.Tesselator import ShapeTesselator
60-
6140
from OCC.Core.GeomAdaptor import GeomAdaptor_Surface
6241
from OCC.Core.Bnd import Bnd_Box
42+
from OCC.Core.Bnd import Bnd_OBB
6343
from 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

6547
Point.from_occ = classmethod(compas_point_from_occ_point)
6648
Point.to_occ = compas_point_to_occ_point
6749
Vector.from_occ = classmethod(compas_vector_from_occ_vector)
6850
Vector.to_occ = compas_vector_to_occ_vector
51+
Frame.from_occ = classmethod(compas_frame_from_occ_position)
6952
Line.to_occ = compas_line_to_occ_line
7053

7154

7255
class 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

Comments
 (0)