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,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
286440if __name__ == '__main__' :
287-
441+
288442 surface = RhinoSurface .from_selection ()
289443
290444 points = []
0 commit comments