66import compas_rhino
77
88from compas_rhino .geometry import RhinoGeometry
9+ from compas_rhino .geometry import RhinoCurve
10+
11+ from compas_rhino .utilities import delete_objects
912
1013from compas .geometry import subtract_vectors
14+ from compas .geometry import angle_vectors
15+
1116
1217try :
1318 import rhinoscriptsyntax as rs
@@ -229,6 +234,33 @@ 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+
247+ kinks = []
248+ borders = self .borders (type = 0 )
249+
250+ for border in borders :
251+ border = RhinoCurve (border )
252+ extremities = map (lambda x : rs .EvaluateCurve (border .guid , rs .CurveParameter (border .guid , x )), [0. , 1. ])
253+
254+ if border .is_closed ():
255+ start_tgt , end_tgt = border .tangents (extremities )
256+ if angle_vectors (start_tgt , end_tgt ) > threshold :
257+ kinks += extremities
258+
259+ else :
260+ kinks += extremities
261+
262+ return list (set (kinks ))
263+
232264 def project_point (self , point , direction = (0 , 0 , 1 )):
233265 projections = rs .ProjectPointToSurface (point , self .guid , direction )
234266 if not projections :
@@ -242,13 +274,6 @@ def project_points(self, points, direction=(0, 0, 1), include_none=True):
242274 projections [:] = [self .closest_point (point ) if not point else point for point in projections ]
243275 return map (list , projections )
244276
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-
252277 def pull_point (self , point ):
253278 pass
254279
@@ -278,7 +303,143 @@ def pull_mesh(self, mesh, fixed=None, d=1.0):
278303 def pull_meshes (self , meshes ):
279304 pass
280305
306+ def closest_point (self , xyz ):
307+ """Return the XYZ coordinates of the closest point on the surface from input XYZ-coordinates.
308+
309+ Parameters
310+ ----------
311+ xyz : list
312+ XYZ coordinates.
313+
314+ Returns
315+ -------
316+ list
317+ The XYZ coordinates of the closest point on the surface.
318+
319+ """
320+
321+ return rs .EvaluateSurface (self .guid , * rs .SurfaceClosestPoint (self .guid , xyz ))
322+
323+ def closest_points (self , points ):
324+ return [self .closest_point (point ) for point in points ]
325+
326+ def closest_point_on_boundaries (self , xyz ):
327+ """Return the XYZ coordinates of the closest point on the boundaries of the surface from input XYZ-coordinates.
328+
329+ Parameters
330+ ----------
331+ xyz : list
332+ XYZ coordinates.
333+
334+ Returns
335+ -------
336+ list
337+ The XYZ coordinates of the closest point on the boundaries of the surface.
338+
339+ """
340+
341+ borders = self .borders (type = 0 )
342+ proj_dist = {tuple (proj_xyz ): distance_point_point (xyz , proj_xyz ) for proj_xyz in [RhinoCurve (border ).closest_point (xyz ) for border in borders ]}
343+ delete_objects (borders )
344+ return min (proj_dist , key = proj_dist .get )
345+
346+ def closest_points_on_boundaries (self , points ):
347+ return [self .closest_point_on_boundaries (point ) for point in points ]
348+
349+ # --------------------------------------------------------------------------
350+ # mapping
351+ # --------------------------------------------------------------------------
352+
353+ def map_uv0 (self , xyz ):
354+ """Return the UV(0) point from the mapping of a XYZ point based on the UV parameterisation of the surface.
355+
356+ Parameters
357+ ----------
358+ xyz : list
359+ XYZ coordinates.
360+
361+ Returns
362+ -------
363+ list
364+ The UV(0) coordinates of the mapped point.
365+
366+ """
367+
368+ return rs .SurfaceClosestPoint (self .guid , xyz ) + (0. ,)
369+
370+ def remap_xyz_point (self , uv ):
371+ """Return the XYZ point from the re-mapping of a UV point based on the UV parameterisation of the surface.
372+
373+ Parameters
374+ ----------
375+ uv : list
376+ UV(0) coordinates.
377+
378+ Returns
379+ -------
380+ list
381+ The XYZ coordinates of the re-mapped point.
382+
383+ """
384+
385+ return tuple (rs .EvaluateSurface (self .guid , * uv ))
386+
387+ def remap_xyz_line (self , line ):
388+ """Return the XYZ points from the re-mapping of a UV line based on the UV parameterisation of the surface.
389+
390+ Parameters
391+ ----------
392+ uv : list
393+ List of UV(0) coordinates.
394+
395+ Returns
396+ -------
397+ list
398+ The list of XYZ coordinates of the re-mapped line.
399+
400+ """
401+
402+ return (self .remap_xyz_point (line [0 ][:2 ]), self .remap_xyz_point (line [1 ][:2 ]))
403+
404+ def remap_xyz_polyline (self , polyline ):
405+ """Return the XYZ points from the re-mapping of a UV polyline based on the UV parameterisation of the surface.
406+
407+ Parameters
408+ ----------
409+ uv : list
410+ List of UV(0) coordinates.
411+
412+ Returns
413+ -------
414+ list
415+ The list of XYZ coordinates of the re-mapped polyline.
416+
417+ """
418+
419+ return [self .remap_xyz_point (vertex [:2 ]) for vertex in polyline ]
420+
421+ def remap_xyz_mesh (self , mesh , cls = None ):
422+ """Return the mesh from the re-mapping of a UV mesh based on the UV parameterisation of the surface.
423+
424+ Parameters
425+ ----------
426+ mesh : Mesh
427+ A mesh.
428+
429+ Returns
430+ -------
431+ Mesh, cls
432+ The re-mapped mesh.
433+
434+ """
435+
436+ if cls is None :
437+ cls = type (mesh )
281438
439+ vertices , faces = mesh .to_vertices_and_faces ()
440+ vertices = [self .remap_xyz_point (uv0 [:2 ]) for uv0 in vertices ]
441+ return cls .from_vertices_and_faces (vertices , faces )
442+
282443# ==============================================================================
283444# Main
284445# ==============================================================================
0 commit comments