Skip to content

Commit 277cbba

Browse files
authored
Merge pull request #1508 from ericgozzi/brepFaces
Some RhinoBrep methods
2 parents 19d53e0 + 29680b1 commit 277cbba

File tree

15 files changed

+240
-36
lines changed

15 files changed

+240
-36
lines changed

AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@
4343
- Panayiotis Papacharalambous <<papacharalambous@arch.ethz.ch>> [@papachap](https://github.com/papachap)
4444
- Oliver Bucklin <<obucklin@arch.ethz.ch>> [@obucklin](https://github.com/obucklin)
4545
- Dominik Reisach <<reisach@arch.ethz.ch>> [@dominikreisach](https://github.com/dominikreisach)
46+
- Eric Gozzi <<eric.gozzi@arch.ethz.ch>> [@ericgozzi](https://github.com/ericgozzi)

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3030
* Added support for `.stp` file extension in addition to `.step` for `RhinoBrep.from_step()` and `RhinoBrep.to_step()` methods.
3131
* Added `volume()` method to `compas.datastructures.Mesh` for computing the volume of closed meshes using signed volume of triangles.
3232
* Added functions `warning`, `message`, `error` and `remark` to `compas_ghpython`.
33+
* Added method `RhinoBrep.closest_point()`.
34+
* Added attributes `RhinoBrepEdge.domain` and `RhinoBrepEdge.index` and methods `RhinoBrepEdge.closest_point()` and `RhinoBrepEdge.point_at`.
35+
* Added method `RhinoBrepFace.point_at()`, `RhinoBrepFace.closest_point()`, `RhinoBrepFace.is_point_on_face()` and `RhinoBrepFace.is_point_on_boundary()`.
36+
* Added method `RhinoBrepLoop.to_curve()`.
37+
* Added attribute `RhinoBrepTrim.edge`.
38+
* Added attribute `RhinoBrepVertex.index`.
39+
* Added method `RhinoCurve.to_polyline()`.
3340

3441
### Changed
3542

src/compas/geometry/shapes/torus.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,4 +294,3 @@ def scale(self, factor):
294294
"""
295295
self.radius_axis *= factor
296296
self.radius_pipe *= factor
297-

src/compas_rhino/geometry/brep/brep.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from compas_rhino.conversions import mesh_to_compas
2828
from compas_rhino.conversions import mesh_to_rhino
2929
from compas_rhino.conversions import plane_to_rhino
30+
from compas_rhino.conversions import point_to_compas
3031
from compas_rhino.conversions import point_to_rhino
3132
from compas_rhino.conversions import polyline_to_rhino_curve
3233
from compas_rhino.conversions import sphere_to_rhino
@@ -1116,3 +1117,21 @@ def cap_planar_holes(self, tolerance=None):
11161117
self._brep = result
11171118
else:
11181119
raise BrepError("Failed to cap planar holes")
1120+
1121+
def closest_point(self, point):
1122+
"""
1123+
Returns the closest point on the Brep to the given point.
1124+
1125+
Parameters
1126+
----------
1127+
point : :class:`compas.geometry.Point`
1128+
The point to find the closest point on the Brep to.
1129+
1130+
Returns
1131+
-------
1132+
:class:`compas.geometry.Point`
1133+
The closest point on the Brep to the given point.
1134+
1135+
"""
1136+
rgpoint = self._brep.ClosestPoint(point_to_rhino(point))
1137+
return point_to_compas(rgpoint)

src/compas_rhino/geometry/brep/edge.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ class RhinoBrepEdge(BrepEdge):
4949
True if the geometry of this edge is a line, False otherwise.
5050
native_edge : :class:`Rhino.Geometry.BrepEdge`
5151
The underlying BrepEdge object.
52+
domain : tuple
53+
The domain of the edge.
54+
index : int
55+
The index of the edge.
5256
5357
"""
5458

@@ -153,6 +157,17 @@ def is_ellipse(self):
153157
def length(self):
154158
return self._mass_props.Length
155159

160+
@property
161+
def domain(self):
162+
rhino_domain = self._edge.Domain
163+
min = rhino_domain.Min
164+
max = rhino_domain.Max
165+
return (min, max)
166+
167+
@property
168+
def index(self):
169+
return self._edge.EdgeIndex
170+
156171
# ==============================================================================
157172
# Methods
158173
# ==============================================================================
@@ -200,3 +215,40 @@ def _create_curve__from_data__(curve_type, curve_data, frame_data, domain):
200215
raise ValueError("Unknown curve type: {}".format(curve_type))
201216
curve.Domain = Rhino.Geometry.Interval(*domain)
202217
return curve
218+
219+
def closest_point(self, point):
220+
"""
221+
Returns the parameter of the closest point on the edge to the given point.
222+
223+
Parameters
224+
----------
225+
point : :class:`compas.geometry.Point`
226+
The point to project onto the edge.
227+
228+
Returns
229+
-------
230+
float
231+
The parameter of the closest point on the edge.
232+
"""
233+
rgpoint = Rhino.Geometry.Point3d(point.x, point.y, point.z)
234+
success, parameter = self._edge.ClosestPoint(rgpoint)
235+
if not success:
236+
raise ValueError("Failed to find closest point on edge")
237+
return parameter
238+
239+
def point_at(self, parameter):
240+
"""
241+
Returns the point on the edge at the given parameter.
242+
243+
Parameters
244+
----------
245+
parameter : float
246+
The parameter of the point on the edge.
247+
248+
Returns
249+
-------
250+
:class:`compas.geometry.Point`
251+
The point on the edge at the given parameter.
252+
"""
253+
rgpoint = self._edge.PointAt(parameter)
254+
return point_to_compas(rgpoint)

src/compas_rhino/geometry/brep/face.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from compas_rhino.conversions import cylinder_to_rhino
1515
from compas_rhino.conversions import frame_to_rhino_plane
1616
from compas_rhino.conversions import plane_to_compas_frame
17+
from compas_rhino.conversions import point_to_compas
1718
from compas_rhino.conversions import sphere_to_compas
1819
from compas_rhino.conversions import sphere_to_rhino
1920
from compas_rhino.geometry import RhinoNurbsSurface
@@ -310,3 +311,84 @@ def frame_at(self, u, v):
310311
if not success:
311312
raise ValueError("Failed to get frame at uv parameters: ({},{}).".format(u, v))
312313
return plane_to_compas_frame(rhino_plane)
314+
315+
def point_at(self, u, v):
316+
"""Returns the point at the given uv parameters.
317+
318+
Parameters
319+
----------
320+
u : float
321+
The u parameter.
322+
v : float
323+
The v parameter.
324+
325+
Returns
326+
-------
327+
:class:`compas.geometry.Point`
328+
The point at the given uv parameters.
329+
"""
330+
rgpoint = self._face.PointAt(u, v)
331+
return point_to_compas(rgpoint)
332+
333+
def closest_point(self, point):
334+
"""Returns the closest point on the face to a give point.
335+
336+
Parameters
337+
----------
338+
point : :class:`compas.geometry.Point`
339+
The point to find the closest point on the face to.
340+
341+
Returns
342+
-------
343+
tuple[float, float]
344+
The u and v parameters of the closest point on the face.
345+
346+
"""
347+
rgpoint = Rhino.Geometry.Point3d(point.x, point.y, point.z)
348+
success, u, v = self._face.ClosestPoint(rgpoint)
349+
if success:
350+
return (u, v)
351+
else:
352+
raise ValueError("Failed to find closest point on face.")
353+
354+
def is_point_on_face(self, u, v):
355+
"""Returns True if the point at the given uv parameters is on the face (inside or on the boundary).
356+
357+
Parameters
358+
----------
359+
u : float
360+
The u parameter.
361+
v : float
362+
The v parameter.
363+
364+
Returns
365+
-------
366+
bool
367+
True if the point is on the face (inside or on the boundary), False otherwise.
368+
"""
369+
relation = self._face.IsPointOnFace(u, v)
370+
if relation in (Rhino.Geometry.PointFaceRelation.Interior, Rhino.Geometry.PointFaceRelation.Boundary):
371+
return True
372+
if relation == Rhino.Geometry.PointFaceRelation.Exterior:
373+
return False
374+
375+
def is_point_on_boundary(self, u, v):
376+
"""Returns True if the point is on the boundary of the face.
377+
378+
Parameters
379+
----------
380+
u : float
381+
The u parameter.
382+
v : float
383+
The v parameter.
384+
385+
Returns
386+
-------
387+
bool
388+
True if the point is on the boundary of the face, False otherwise.
389+
"""
390+
relation = self._face.IsPointOnFace(u, v)
391+
if relation == Rhino.Geometry.PointFaceRelation.Boundary:
392+
return True
393+
else:
394+
return False

src/compas_rhino/geometry/brep/loop.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import Rhino # type: ignore
66

77
from compas.geometry import BrepLoop
8+
from compas.geometry import Curve
89

910
from .edge import RhinoBrepEdge
1011
from .trim import RhinoBrepTrim
@@ -136,3 +137,8 @@ def native_loop(self, rhino_loop):
136137
self._loop = rhino_loop
137138
self._type = int(self._loop.LoopType)
138139
self._trims = [RhinoBrepTrim(trim) for trim in self._loop.Trims]
140+
141+
def to_curve(self):
142+
curve = self._loop.To3dCurve()
143+
curve = Curve.from_native(curve)
144+
return curve

src/compas_rhino/geometry/brep/trim.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from compas.geometry import BrepTrim
88
from compas_rhino.geometry import RhinoNurbsCurve
99

10+
from .edge import RhinoBrepEdge
11+
1012
from .vertex import RhinoBrepVertex
1113

1214

@@ -29,7 +31,8 @@ class RhinoBrepTrim(BrepTrim):
2931
The end vertex of this trim.
3032
vertices : list[:class:`compas_rhino.geometry.RhinoBrepVertex`], read-only
3133
The list of vertices which comprise this trim (start and end).
32-
34+
edge : :class:compas_rhino.geometry.RhinoBrepEdge
35+
The edge associated with this trim.
3336
"""
3437

3538
def __init__(self, rhino_trim=None):
@@ -118,6 +121,10 @@ def iso_status(self):
118121
def native_trim(self):
119122
return self._trim
120123

124+
@property
125+
def edge(self):
126+
return RhinoBrepEdge(self._trim.Edge)
127+
121128
@native_trim.setter
122129
def native_trim(self, rhino_trim):
123130
self._trim = rhino_trim

src/compas_rhino/geometry/brep/vertex.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class RhinoBrepVertex(BrepVertex):
1616
The underlying Rhino BrepBertex object.
1717
point : :class:`compas.geometry.Point`, read-only
1818
The geometry of this vertex as a point in 3D space.
19+
index : int
20+
The index of the vertex.
1921
2022
"""
2123

@@ -66,6 +68,10 @@ def __from_data__(cls, data, builder):
6668
def point(self):
6769
return self._point
6870

71+
@property
72+
def index(self):
73+
return self._vertex.VertexIndex
74+
6975
@property
7076
def native_vertex(self):
7177
return self._vertex

src/compas_rhino/geometry/curves/curve.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
from compas_rhino.conversions import plane_to_rhino
1212
from compas_rhino.conversions import point_to_compas
1313
from compas_rhino.conversions import point_to_rhino
14+
from compas_rhino.conversions import polyline_to_compas
1415
from compas_rhino.conversions import transformation_to_rhino
1516
from compas_rhino.conversions import vector_to_compas
17+
from compas_rhino.conversions import ConversionError
1618

1719

1820
class RhinoCurve(Curve):
@@ -147,6 +149,33 @@ def from_rhino(cls, native_curve):
147149
# Conversions
148150
# ==============================================================================
149151

152+
def to_polyline(self, tolerance=1, angle_tolerance=1, minimum_lenght=0, maximum_length=1):
153+
"""
154+
Convert the curve to a polyline.
155+
156+
Parameters
157+
----------
158+
tolerance : float, optional
159+
The tolerance. This is the maximum deviation from line midpoints to the curve.
160+
angle_tolerance : float, optional
161+
The angle tolerance in radians. This is the maximum deviation of the line directions.
162+
minimum_lenght : float, optional
163+
The minimum segment length.
164+
maximum_length : float, optional
165+
The maximum segment length.
166+
167+
Returns
168+
-------
169+
:class:`compas.geometry.Polyline`
170+
The polyline representation of the curve.
171+
"""
172+
curve_polyline = self.native_curve.ToPolyline(tolerance, angle_tolerance, minimum_lenght, maximum_length)
173+
polyline_created, polyline = curve_polyline.TryGetPolyline()
174+
if polyline_created:
175+
return polyline_to_compas(polyline)
176+
else:
177+
raise ConversionError("The curve cannot be converted to a polyline.")
178+
150179
# ==============================================================================
151180
# Methods
152181
# ==============================================================================

0 commit comments

Comments
 (0)