Skip to content

Commit 257e8b4

Browse files
authored
Merge pull request #1106 from compas-dev/rhino_brep
Rhino brep
2 parents 4993b32 + dcc89c5 commit 257e8b4

File tree

17 files changed

+630
-186
lines changed

17 files changed

+630
-186
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
* Added conversion function `frame_to_rhino_plane` to `compas_rhino.conversions`.
13+
* Added `RhinoSurface.from_frame` to `compas_rhino.geometry`.
14+
* Added representation for trims with `compas.geometry.BrepTrim`.
15+
1216
### Changed
1317

1418
### Removed
@@ -24,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2428

2529
* Fixed strange point values in RhinoNurbsCurve caused by conversion `ControlPoint` to COMPAS instead of `ControlPoint.Location`.
2630
* Fixed flipped order of NURBS point count values when creating RhinoNurbsSurface from parameters.
31+
* Changed serialization format and reconstruction procedure of `RhinoBrep`.
2732

2833
### Removed
2934

src/compas/geometry/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@
8686
BrepEdge
8787
BrepLoop
8888
BrepFace
89+
BrepTrim
90+
BrepTrimIsoStatus
8991
9092
BrepType
9193
BrepOrientation
@@ -892,6 +894,8 @@
892894
BrepFace,
893895
BrepLoop,
894896
BrepEdge,
897+
BrepTrim,
898+
BrepTrimIsoStatus,
895899
BrepType,
896900
BrepOrientation,
897901
BrepError,
@@ -1202,6 +1206,8 @@
12021206
"BrepEdge",
12031207
"BrepVertex",
12041208
"BrepFace",
1209+
"BrepTrim",
1210+
"BrepTrimIsoStatus",
12051211
"BrepType",
12061212
"BrepOrientation",
12071213
"BrepError",

src/compas/geometry/brep/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from .loop import BrepLoop
66
from .face import BrepFace
77
from .vertex import BrepVertex
8+
from .trim import BrepTrim
9+
from .trim import BrepTrimIsoStatus
810

911

1012
class BrepError(Exception):
@@ -31,6 +33,8 @@ class BrepTrimmingError(BrepError):
3133
"BrepLoop",
3234
"BrepFace",
3335
"BrepVertex",
36+
"BrepTrim",
37+
"BrepTrimIsoStatus",
3438
"BrepOrientation",
3539
"BrepType",
3640
"BrepError",

src/compas/geometry/brep/brep.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ class Brep(Geometry):
127127
The vertices of the Brep.
128128
edges : list[:class:`~compas.geometry.BrepEdge`], read-only
129129
The edges of the Brep.
130+
trims : list[:class:`~compas.geometry.BrepTrim`], read-only
131+
The trims of the Brep.
130132
loops : list[:class:`~compas.geometry.BrepLoop`], read-only
131133
The loops of the Brep.
132134
faces : list[:class:`~compas.geometry.BrepFace`], read-only
@@ -314,6 +316,10 @@ def vertices(self):
314316
def edges(self):
315317
raise NotImplementedError
316318

319+
@property
320+
def trims(self):
321+
raise NotImplementedError
322+
317323
@property
318324
def loops(self):
319325
raise NotImplementedError

src/compas/geometry/brep/trim.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from compas.data import Data
2+
3+
4+
class BrepTrimIsoStatus(object):
5+
"""An enumeration of isoparametric curve direction on the surface."""
6+
7+
NONE = 0
8+
X = 1
9+
Y = 2
10+
WEST = 3
11+
SOUTH = 4
12+
EAST = 5
13+
NORTH = 6
14+
15+
16+
class BrepTrim(Data):
17+
"""An interface for a Brep Trim
18+
19+
Attributes
20+
----------
21+
curve : :class:`~compas.geometry.NurbsCurve`, read_only
22+
Returns the geometry for this trim as a 2d curve.
23+
iso_status : literal(NONE|X|Y|WEST|SOUTH|EAST|NORTH)
24+
The isoparametric curve direction on the surface.
25+
is_reversed : bool
26+
True if this trim is reversed from its associated edge curve and False otherwise.
27+
28+
"""
29+
30+
@property
31+
def curve(self):
32+
raise NotImplementedError
33+
34+
@property
35+
def iso_status(self):
36+
raise NotImplementedError
37+
38+
@property
39+
def is_reversed(self):
40+
raise NotImplementedError

src/compas_rhino/conversions/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
line_to_rhino
5959
plane_to_rhino
6060
frame_to_rhino
61+
frame_to_rhino_plane
6162
circle_to_rhino
6263
ellipse_to_rhino
6364
polyline_to_rhino
@@ -134,6 +135,7 @@
134135
line_to_rhino,
135136
plane_to_rhino,
136137
frame_to_rhino,
138+
frame_to_rhino_plane,
137139
circle_to_rhino,
138140
ellipse_to_rhino,
139141
polyline_to_rhino,
@@ -196,6 +198,7 @@
196198
"line_to_rhino",
197199
"plane_to_rhino",
198200
"frame_to_rhino",
201+
"frame_to_rhino_plane",
199202
"circle_to_rhino",
200203
"ellipse_to_rhino",
201204
"polyline_to_rhino",

src/compas_rhino/conversions/_primitives.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,21 @@ def plane_to_compas_frame(plane):
160160
)
161161

162162

163+
def frame_to_rhino_plane(frame):
164+
"""Convert a COMPAS frame to a Rhino plane.
165+
166+
Parameters
167+
----------
168+
frame : :class:`~compas.geometry.Frame`
169+
170+
Returns
171+
-------
172+
:rhino:`Rhino.Geometry.Plane`
173+
174+
"""
175+
return RhinoPlane(point_to_rhino(frame.point), vector_to_rhino(frame.xaxis), vector_to_rhino(frame.yaxis))
176+
177+
163178
def frame_to_rhino(frame):
164179
"""Convert a COMPAS frame to a Rhino plane.
165180

src/compas_rhino/geometry/__init__.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
RhinoBrepEdge
2222
RhinoBrepFace
2323
RhinoBrepLoop
24-
24+
RhinoBrepTrim
25+
RhinoBrepBuilder
26+
RhinoFaceBuilder
27+
RhinoLoopBuilder
2528
2629
Plugins
2730
=======
@@ -91,7 +94,10 @@
9194
from .brep import RhinoBrepVertex
9295
from .brep import RhinoBrepFace
9396
from .brep import RhinoBrepEdge
94-
97+
from .brep import RhinoBrepTrim
98+
from .brep import RhinoBrepBuilder
99+
from .brep import RhinoFaceBuilder
100+
from .brep import RhinoLoopBuilder
95101

96102
__all__ = [
97103
"RhinoGeometry",
@@ -116,4 +122,8 @@
116122
"RhinoBrepEdge",
117123
"RhinoBrepFace",
118124
"RhinoBrepLoop",
125+
"RhinoBrepTrim",
126+
"RhinoBrepBuilder",
127+
"RhinoFaceBuilder",
128+
"RhinoLoopBuilder",
119129
]

src/compas_rhino/geometry/brep/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
from .edge import RhinoBrepEdge
66
from .vertex import RhinoBrepVertex
77
from .loop import RhinoBrepLoop
8+
from .trim import RhinoBrepTrim
9+
from .builder import RhinoBrepBuilder
10+
from .builder import RhinoFaceBuilder
11+
from .builder import RhinoLoopBuilder
812

913
import Rhino
1014

@@ -15,6 +19,10 @@
1519
"RhinoBrepEdge",
1620
"RhinoBrepLoop",
1721
"RhinoBrepFace",
22+
"RhinoBrepTrim",
23+
"RhinoBrepBuilder",
24+
"RhinoFaceBuilder",
25+
"RhinoLoopBuilder",
1826
"new_brep",
1927
"from_native",
2028
"from_box",

src/compas_rhino/geometry/brep/brep.py

Lines changed: 21 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
from compas.geometry import Frame
22
from compas.geometry import Brep
3-
from compas.geometry import BrepInvalidError
43
from compas.geometry import BrepTrimmingError
54
from compas.geometry import Plane
65

76
from compas_rhino.conversions import box_to_rhino
8-
from compas_rhino.conversions import point_to_rhino
97
from compas_rhino.conversions import xform_to_rhino
108
from compas_rhino.conversions import frame_to_rhino
119
from compas_rhino.conversions import cylinder_to_rhino
1210

1311
import Rhino
1412

13+
from .builder import RhinoBrepBuilder
1514
from .face import RhinoBrepFace
1615
from .edge import RhinoBrepEdge
1716
from .vertex import RhinoBrepVertex
@@ -36,6 +35,8 @@ class RhinoBrep(Brep):
3635
The list of vertex geometries as points in 3D space.
3736
edges : list[:class:`~compas_rhino.geometry.RhinoBrepEdge`], read-only
3837
The list of edges which comprise this brep.
38+
trims : list[:class:`~compas_rhino.geometry.RhinoBrepTrim`], read-only
39+
The list of trims which comprise this brep.
3940
loops : list[:class:`~compas_rhino.geometry.RhinoBrepLoop`], read-only
4041
The list of loops which comprise this brep.
4142
faces : list[:class:`~compas_rhino.geometry.RhinoBrepFace`], read-only
@@ -70,18 +71,22 @@ def __init__(self, brep=None):
7071

7172
@property
7273
def data(self):
73-
faces = []
74-
for face in self.faces:
75-
faces.append(face.data)
76-
return {"faces": faces}
74+
return {
75+
"vertices": [v.data for v in self.vertices],
76+
"edges": [e.data for e in self.edges],
77+
"faces": [f.data for f in self.faces],
78+
}
7779

7880
@data.setter
7981
def data(self, data):
80-
faces = []
81-
for facedata in data["faces"]:
82-
face = RhinoBrepFace.from_data(facedata)
83-
faces.append(face)
84-
self._create_native_brep(faces)
82+
builder = RhinoBrepBuilder()
83+
for v_data in data["vertices"]:
84+
RhinoBrepVertex.from_data(v_data, builder)
85+
for e_data in data["edges"]:
86+
RhinoBrepEdge.from_data(e_data, builder)
87+
for f_data in data["faces"]:
88+
RhinoBrepFace.from_data(f_data, builder)
89+
self._brep = builder.result
8590

8691
# ==============================================================================
8792
# Properties
@@ -102,6 +107,11 @@ def points(self):
102107

103108
@property
104109
def edges(self):
110+
if self._brep:
111+
return [RhinoBrepEdge(edge) for edge in self._brep.Edges]
112+
113+
@property
114+
def trims(self):
105115
if self._brep:
106116
return [RhinoBrepEdge(trim) for trim in self._brep.Trims]
107117

@@ -326,88 +336,3 @@ def split(self, cutter):
326336
"""
327337
resulting_breps = self._brep.Split(cutter.native_brep, TOLERANCE)
328338
return [RhinoBrep.from_native(brep) for brep in resulting_breps]
329-
330-
# ==============================================================================
331-
# Other Methods
332-
# ==============================================================================
333-
334-
def _create_native_brep(self, faces):
335-
# Source: https://github.com/mcneel/rhino-developer-samples/blob/3179a8386a64602ee670cc832c77c561d1b0944b/rhinocommon/cs/SampleCsCommands/SampleCsTrimmedPlane.cs
336-
# Things need to be defined in a valid brep:
337-
# 1- Vertices
338-
# 2- 3D Curves (geometry)
339-
# 3- Edges (topology - reference curve geometry)
340-
# 4- Surfaces (geometry)
341-
# 5- Faces (topology - reference surface geometry)
342-
# 6- Loops (2D parameter space of faces)
343-
# 4- Trims and 2D curves (2D parameter space of edges)
344-
self._brep = Rhino.Geometry.Brep()
345-
for face in faces:
346-
rhino_face, rhino_surface = self._create_brep_face(face)
347-
for loop in face.loops:
348-
rhino_loop = self._brep.Loops.Add(Rhino.Geometry.BrepLoopType.Outer, rhino_face)
349-
for edge in loop.edges:
350-
start_vertex, end_vertex = self._add_edge_vertices(edge)
351-
rhino_edge = self._add_edge(edge, start_vertex, end_vertex)
352-
rhino_2d_curve = self._create_trim_curve(rhino_edge, rhino_surface)
353-
self._add_trim(rhino_2d_curve, rhino_edge, rhino_loop)
354-
355-
self._brep.Repair(TOLERANCE)
356-
self._brep.JoinNakedEdges(
357-
TOLERANCE
358-
) # without this, Brep.Trim() led to some weird results on de-serialized Breps
359-
self._validate_brep()
360-
361-
def _validate_brep(self):
362-
if self._brep.IsValid:
363-
return
364-
365-
error_message = ""
366-
valid_topo, log_topo = self._brep.IsValidTopology()
367-
valid_tol, log_tol = self._brep.IsValidTolerancesAndFlags()
368-
valid_geo, log_geo = self._brep.IsValidGeometry()
369-
if not valid_geo:
370-
error_message += "Invalid geometry:\n{}\n".format(log_geo)
371-
if not valid_topo:
372-
error_message += "Invalid topology:\n{}\n".format(log_topo)
373-
if not valid_tol:
374-
error_message += "Invalid tolerances:\n{}\n".format(log_tol)
375-
376-
raise BrepInvalidError(error_message)
377-
378-
def _create_brep_face(self, face):
379-
# Geometry
380-
surface_index = self._brep.AddSurface(face.native_surface)
381-
brep_surface = self._brep.Surfaces.Item[surface_index]
382-
# Topology
383-
brep_face = self._brep.Faces.Add(surface_index)
384-
return brep_face, brep_surface
385-
386-
def _add_edge_vertices(self, edge):
387-
start_vertex = self._brep.Vertices.Add(point_to_rhino(edge.start_vertex.point), TOLERANCE)
388-
end_vertex = self._brep.Vertices.Add(point_to_rhino(edge.end_vertex.point), TOLERANCE)
389-
return start_vertex, end_vertex
390-
391-
def _add_edge(self, edge, start_vertex, end_vertex):
392-
# Geometry
393-
curve_index = self._brep.AddEdgeCurve(edge.curve)
394-
# Topology
395-
rhino_edge = self._brep.Edges.Add(start_vertex, end_vertex, curve_index, TOLERANCE)
396-
return rhino_edge
397-
398-
def _add_trim(self, rhino_trim_curve, rhino_edge, rhino_loop):
399-
# Geometry
400-
trim_curve_index = self._brep.AddTrimCurve(rhino_trim_curve)
401-
# Topology
402-
trim = self._brep.Trims.Add(rhino_edge, True, rhino_loop, trim_curve_index)
403-
trim.IsoStatus = getattr(
404-
Rhino.Geometry.IsoStatus, "None"
405-
) # IsoStatus.None makes lint, IDE and even Python angry
406-
trim.TrimType = Rhino.Geometry.BrepTrimType.Boundary
407-
trim.SetTolerances(TOLERANCE, TOLERANCE)
408-
409-
@staticmethod
410-
def _create_trim_curve(rhino_edge, rhino_surface):
411-
curve_2d = rhino_surface.Pullback(rhino_edge.EdgeCurve, TOLERANCE)
412-
curve_2d.Reverse()
413-
return curve_2d

0 commit comments

Comments
 (0)