Skip to content

Commit b7fab86

Browse files
authored
Merge pull request #256 from robin-oval/feature/rhino_object_functions
Feature/rhino object functions
2 parents fb29781 + 8c1dd8c commit b7fab86

File tree

2 files changed

+185
-8
lines changed

2 files changed

+185
-8
lines changed

src/compas_rhino/geometry/curve.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,17 @@ def from_selection(cls):
6565
guid = compas_rhino.select_curve()
6666
return cls(guid)
6767

68+
def length(self):
69+
"""Return the length of the curve.
70+
71+
Returns
72+
-------
73+
float
74+
The curve's length.
75+
76+
"""
77+
return rs.CurveLength(self.guid)
78+
6879
def is_line(self):
6980
"""Determine if the curve is a line.
7081
@@ -101,6 +112,18 @@ def is_polyline(self):
101112
rs.CurveDegree(self.guid) == 1 and
102113
len(rs.CurvePoints(self.guid)) > 2)
103114

115+
def is_closed(self):
116+
"""Assess if the curve is closed.
117+
118+
Returns
119+
-------
120+
bool
121+
True if the curve is closed. False otherwise.
122+
123+
"""
124+
125+
return rs.IsCurveClosed(self.guid)
126+
104127
def control_points(self):
105128
"""Get the control points of a curve.
106129

src/compas_rhino/geometry/surface.py

Lines changed: 162 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66
import compas_rhino
77

88
from compas_rhino.geometry import RhinoGeometry
9+
from compas_rhino.geometry import RhinoCurve
10+
11+
from compas_rhino.utilities import delete_objects
912

1013
from compas.geometry import subtract_vectors
14+
from compas.geometry import angle_vectors
15+
1116

1217
try:
1318
import rhinoscriptsyntax as rs
@@ -229,6 +234,32 @@ def borders(self, type=1):
229234
curves = rs.ExplodeCurves(border, delete_input=True)
230235
return curves
231236

237+
def kinks(self, threshold=1e-3):
238+
"""Return the XYZ coordinates of kinks, i.e. tangency discontinuities, along the surface's boundaries.
239+
240+
Returns
241+
-------
242+
list
243+
The list of XYZ coordinates of surface boundary kinks.
244+
245+
"""
246+
kinks = []
247+
borders = self.borders(type=0)
248+
249+
for border in borders:
250+
border = RhinoCurve(border)
251+
extremities = map(lambda x: rs.EvaluateCurve(border.guid, rs.CurveParameter(border.guid, x)), [0., 1.])
252+
253+
if border.is_closed():
254+
start_tgt, end_tgt = border.tangents(extremities)
255+
if angle_vectors(start_tgt, end_tgt) > threshold:
256+
kinks += extremities
257+
258+
else:
259+
kinks += extremities
260+
261+
return list(set(kinks))
262+
232263
def project_point(self, point, direction=(0, 0, 1)):
233264
projections = rs.ProjectPointToSurface(point, self.guid, direction)
234265
if not projections:
@@ -242,13 +273,6 @@ def project_points(self, points, direction=(0, 0, 1), include_none=True):
242273
projections[:] = [self.closest_point(point) if not point else point for point in projections]
243274
return map(list, projections)
244275

245-
def closest_point(self, point, maxdist=None):
246-
point = self.geometry.ClosestPoint(Point3d(*point))
247-
return list(point)
248-
249-
def closest_points(self, points, maxdist=None):
250-
return [self.closest_point(point) for point in points]
251-
252276
def pull_point(self, point):
253277
pass
254278

@@ -278,13 +302,143 @@ def pull_mesh(self, mesh, fixed=None, d=1.0):
278302
def pull_meshes(self, meshes):
279303
pass
280304

305+
def closest_point(self, xyz):
306+
"""Return the XYZ coordinates of the closest point on the surface from input XYZ-coordinates.
307+
308+
Parameters
309+
----------
310+
xyz : list
311+
XYZ coordinates.
312+
313+
Returns
314+
-------
315+
list
316+
The XYZ coordinates of the closest point on the surface.
317+
318+
"""
319+
320+
return rs.EvaluateSurface(self.guid, *rs.SurfaceClosestPoint(self.guid, xyz))
321+
322+
def closest_points(self, points):
323+
return [self.closest_point(point) for point in points]
324+
325+
def closest_point_on_boundaries(self, xyz):
326+
"""Return the XYZ coordinates of the closest point on the boundaries of the surface from input XYZ-coordinates.
327+
328+
Parameters
329+
----------
330+
xyz : list
331+
XYZ coordinates.
332+
333+
Returns
334+
-------
335+
list
336+
The XYZ coordinates of the closest point on the boundaries of the surface.
337+
338+
"""
339+
borders = self.borders(type=0)
340+
proj_dist = {tuple(proj_xyz): distance_point_point(xyz, proj_xyz) for proj_xyz in [RhinoCurve(border).closest_point(xyz) for border in borders]}
341+
delete_objects(borders)
342+
return min(proj_dist, key=proj_dist.get)
343+
344+
def closest_points_on_boundaries(self, points):
345+
return [self.closest_point_on_boundaries(point) for point in points]
346+
347+
# --------------------------------------------------------------------------
348+
# mapping
349+
# --------------------------------------------------------------------------
350+
351+
def point_xyz_to_uv(self, xyz):
352+
"""Return the UV point from the mapping of a XYZ point based on the UV parameterisation of the surface.
353+
354+
Parameters
355+
----------
356+
xyz : list
357+
(x, y, z) coordinates.
358+
359+
Returns
360+
-------
361+
list
362+
The (u, v, 0) coordinates of the mapped point.
363+
364+
"""
365+
return rs.SurfaceClosestPoint(self.guid, xyz) + (0.,)
366+
367+
def point_uv_to_xyz(self, uv):
368+
"""Return the XYZ point from the inverse mapping of a UV point based on the UV parameterisation of the surface.
369+
370+
Parameters
371+
----------
372+
uv : list
373+
(u, v, 0) coordinates.
374+
375+
Returns
376+
-------
377+
list
378+
The (x, y, z) coordinates of the inverse-mapped point.
379+
380+
"""
381+
return tuple(rs.EvaluateSurface(self.guid, *uv))
382+
383+
def line_uv_to_xyz(self, line):
384+
"""Return the XYZ points from the inverse mapping of a UV line based on the UV parameterisation of the surface.
385+
386+
Parameters
387+
----------
388+
uv : list
389+
List of (u, v, 0) coordinates.
390+
391+
Returns
392+
-------
393+
list
394+
The list of XYZ coordinates of the inverse-mapped line.
395+
396+
"""
397+
return (self.point_uv_to_xyz(line[0][:2]), self.point_uv_to_xyz(line[1][:2]))
398+
399+
def polyline_uv_to_xyz(self, polyline):
400+
"""Return the XYZ points from the inverse mapping of a UV polyline based on the UV parameterisation of the surface.
401+
402+
Parameters
403+
----------
404+
uv : list
405+
List of (u, v, 0) coordinates.
406+
407+
Returns
408+
-------
409+
list
410+
The list of (x, y, z) coordinates of the inverse-mapped polyline.
411+
412+
"""
413+
return [self.point_uv_to_xyz(vertex[:2]) for vertex in polyline]
281414

415+
def mesh_uv_to_xyz(self, mesh, cls=None):
416+
"""Return the mesh from the inverse mapping of a UV mesh based on the UV parameterisation of the surface.
417+
418+
Parameters
419+
----------
420+
mesh : Mesh
421+
A mesh.
422+
423+
Returns
424+
-------
425+
Mesh, cls
426+
The inverse-mapped mesh.
427+
428+
"""
429+
if cls is None:
430+
cls = type(mesh)
431+
432+
vertices, faces = mesh.to_vertices_and_faces()
433+
vertices = [self.point_uv_to_xyz(uv0[:2]) for uv0 in vertices]
434+
return cls.from_vertices_and_faces(vertices, faces)
435+
282436
# ==============================================================================
283437
# Main
284438
# ==============================================================================
285439

286440
if __name__ == '__main__':
287-
441+
288442
surface = RhinoSurface.from_selection()
289443

290444
points = []

0 commit comments

Comments
 (0)