From 1cb4f41b227c7ebfbe96afb8a223c9ae9f1926ab Mon Sep 17 00:00:00 2001 From: Robin Oval Date: Mon, 18 Feb 2019 22:52:12 +0100 Subject: [PATCH 1/4] rhino curve length and is_closed --- src/compas_rhino/geometry/curve.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/compas_rhino/geometry/curve.py b/src/compas_rhino/geometry/curve.py index 26254a953196..f4b8d7ae611c 100644 --- a/src/compas_rhino/geometry/curve.py +++ b/src/compas_rhino/geometry/curve.py @@ -65,6 +65,17 @@ def from_selection(cls): guid = compas_rhino.select_curve() return cls(guid) + def length(self): + """Return the length of the curve. + + Returns + ------- + float + The curve's length. + + """ + return rs.CurveLength(self.guid) + def is_line(self): """Determine if the curve is a line. @@ -101,6 +112,18 @@ def is_polyline(self): rs.CurveDegree(self.guid) == 1 and len(rs.CurvePoints(self.guid)) > 2) + def is_closed(self): + """Assess if the curve is closed. + + Returns + ------- + bool + True if the curve is closed. False otherwise. + + """ + + return rs.IsCurveClosed(self.guid) + def control_points(self): """Get the control points of a curve. From 0653495baef13921bbfcf54654aafce8b50654f1 Mon Sep 17 00:00:00 2001 From: Robin Oval Date: Mon, 18 Feb 2019 22:52:55 +0100 Subject: [PATCH 2/4] rhino surface kinks, closest point on surface or boundaries and mapping/remapping objects on surface --- src/compas_rhino/geometry/surface.py | 175 +++++++++++++++++++++++++-- 1 file changed, 168 insertions(+), 7 deletions(-) diff --git a/src/compas_rhino/geometry/surface.py b/src/compas_rhino/geometry/surface.py index 8395b96d00fb..aa76d7cdbbc2 100644 --- a/src/compas_rhino/geometry/surface.py +++ b/src/compas_rhino/geometry/surface.py @@ -6,8 +6,13 @@ import compas_rhino from compas_rhino.geometry import RhinoGeometry +from compas_rhino.geometry import RhinoCurve + +from compas_rhino.utilities import delete_objects from compas.geometry import subtract_vectors +from compas.geometry import angle_vectors + try: import rhinoscriptsyntax as rs @@ -229,6 +234,33 @@ def borders(self, type=1): curves = rs.ExplodeCurves(border, delete_input=True) return curves + def kinks(self, threshold=1e-3): + """Return the XYZ coordinates of kinks, i.e. tangency discontinuities, along the surface's boundaries. + + Returns + ------- + list + The list of XYZ coordinates of surface boundary kinks. + + """ + + kinks = [] + borders = self.borders(type = 0) + + for border in borders: + border = RhinoCurve(border) + extremities = map(lambda x: rs.EvaluateCurve(border.guid, rs.CurveParameter(border.guid, x)), [0., 1.]) + + if border.is_closed(): + start_tgt, end_tgt = border.tangents(extremities) + if angle_vectors(start_tgt, end_tgt) > threshold: + kinks += extremities + + else: + kinks += extremities + + return list(set(kinks)) + def project_point(self, point, direction=(0, 0, 1)): projections = rs.ProjectPointToSurface(point, self.guid, direction) if not projections: @@ -242,13 +274,6 @@ def project_points(self, points, direction=(0, 0, 1), include_none=True): projections[:] = [self.closest_point(point) if not point else point for point in projections] return map(list, projections) - def closest_point(self, point, maxdist=None): - point = self.geometry.ClosestPoint(Point3d(*point)) - return list(point) - - def closest_points(self, points, maxdist=None): - return [self.closest_point(point) for point in points] - def pull_point(self, point): pass @@ -278,7 +303,143 @@ def pull_mesh(self, mesh, fixed=None, d=1.0): def pull_meshes(self, meshes): pass + def closest_point(self, xyz): + """Return the XYZ coordinates of the closest point on the surface from input XYZ-coordinates. + + Parameters + ---------- + xyz : list + XYZ coordinates. + + Returns + ------- + list + The XYZ coordinates of the closest point on the surface. + + """ + + return rs.EvaluateSurface(self.guid, *rs.SurfaceClosestPoint(self.guid, xyz)) + + def closest_points(self, points): + return [self.closest_point(point) for point in points] + + def closest_point_on_boundaries(self, xyz): + """Return the XYZ coordinates of the closest point on the boundaries of the surface from input XYZ-coordinates. + + Parameters + ---------- + xyz : list + XYZ coordinates. + + Returns + ------- + list + The XYZ coordinates of the closest point on the boundaries of the surface. + + """ + + borders = self.borders(type = 0) + proj_dist = {tuple(proj_xyz): distance_point_point(xyz, proj_xyz) for proj_xyz in [RhinoCurve(border).closest_point(xyz) for border in borders]} + delete_objects(borders) + return min(proj_dist, key = proj_dist.get) + + def closest_points_on_boundaries(self, points): + return [self.closest_point_on_boundaries(point) for point in points] + + # -------------------------------------------------------------------------- + # mapping + # -------------------------------------------------------------------------- + + def map_uv0(self, xyz): + """Return the UV(0) point from the mapping of a XYZ point based on the UV parameterisation of the surface. + + Parameters + ---------- + xyz : list + XYZ coordinates. + + Returns + ------- + list + The UV(0) coordinates of the mapped point. + + """ + + return rs.SurfaceClosestPoint(self.guid, xyz) + (0.,) + + def remap_xyz_point(self, uv): + """Return the XYZ point from the re-mapping of a UV point based on the UV parameterisation of the surface. + + Parameters + ---------- + uv : list + UV(0) coordinates. + + Returns + ------- + list + The XYZ coordinates of the re-mapped point. + + """ + + return tuple(rs.EvaluateSurface(self.guid, *uv)) + + def remap_xyz_line(self, line): + """Return the XYZ points from the re-mapping of a UV line based on the UV parameterisation of the surface. + + Parameters + ---------- + uv : list + List of UV(0) coordinates. + + Returns + ------- + list + The list of XYZ coordinates of the re-mapped line. + + """ + + return (self.remap_xyz_point(line[0][:2]), self.remap_xyz_point(line[1][:2])) + + def remap_xyz_polyline(self, polyline): + """Return the XYZ points from the re-mapping of a UV polyline based on the UV parameterisation of the surface. + + Parameters + ---------- + uv : list + List of UV(0) coordinates. + + Returns + ------- + list + The list of XYZ coordinates of the re-mapped polyline. + + """ + + return [self.remap_xyz_point(vertex[:2]) for vertex in polyline] + + def remap_xyz_mesh(self, mesh, cls=None): + """Return the mesh from the re-mapping of a UV mesh based on the UV parameterisation of the surface. + + Parameters + ---------- + mesh : Mesh + A mesh. + + Returns + ------- + Mesh, cls + The re-mapped mesh. + + """ + + if cls is None: + cls = type(mesh) + vertices, faces = mesh.to_vertices_and_faces() + vertices = [self.remap_xyz_point(uv0[:2]) for uv0 in vertices] + return cls.from_vertices_and_faces(vertices, faces) + # ============================================================================== # Main # ============================================================================== From c3b0e28d967376808d2c233a49a8882765567791 Mon Sep 17 00:00:00 2001 From: Robin Oval Date: Thu, 7 Mar 2019 17:30:41 +0100 Subject: [PATCH 3/4] remove blank spaces and lines --- src/compas_rhino/geometry/surface.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/compas_rhino/geometry/surface.py b/src/compas_rhino/geometry/surface.py index aa76d7cdbbc2..cd22c6dffb5c 100644 --- a/src/compas_rhino/geometry/surface.py +++ b/src/compas_rhino/geometry/surface.py @@ -243,9 +243,8 @@ def kinks(self, threshold=1e-3): The list of XYZ coordinates of surface boundary kinks. """ - kinks = [] - borders = self.borders(type = 0) + borders = self.borders(type=0) for border in borders: border = RhinoCurve(border) @@ -337,11 +336,10 @@ def closest_point_on_boundaries(self, xyz): The XYZ coordinates of the closest point on the boundaries of the surface. """ - - borders = self.borders(type = 0) + borders = self.borders(type=0) proj_dist = {tuple(proj_xyz): distance_point_point(xyz, proj_xyz) for proj_xyz in [RhinoCurve(border).closest_point(xyz) for border in borders]} delete_objects(borders) - return min(proj_dist, key = proj_dist.get) + return min(proj_dist, key=proj_dist.get) def closest_points_on_boundaries(self, points): return [self.closest_point_on_boundaries(point) for point in points] @@ -364,7 +362,6 @@ def map_uv0(self, xyz): The UV(0) coordinates of the mapped point. """ - return rs.SurfaceClosestPoint(self.guid, xyz) + (0.,) def remap_xyz_point(self, uv): @@ -381,7 +378,6 @@ def remap_xyz_point(self, uv): The XYZ coordinates of the re-mapped point. """ - return tuple(rs.EvaluateSurface(self.guid, *uv)) def remap_xyz_line(self, line): @@ -398,7 +394,6 @@ def remap_xyz_line(self, line): The list of XYZ coordinates of the re-mapped line. """ - return (self.remap_xyz_point(line[0][:2]), self.remap_xyz_point(line[1][:2])) def remap_xyz_polyline(self, polyline): @@ -415,7 +410,6 @@ def remap_xyz_polyline(self, polyline): The list of XYZ coordinates of the re-mapped polyline. """ - return [self.remap_xyz_point(vertex[:2]) for vertex in polyline] def remap_xyz_mesh(self, mesh, cls=None): @@ -432,7 +426,6 @@ def remap_xyz_mesh(self, mesh, cls=None): The re-mapped mesh. """ - if cls is None: cls = type(mesh) From 8c1dd8c12636c3788a0c023c2113665282ffc21b Mon Sep 17 00:00:00 2001 From: Robin Oval Date: Thu, 7 Mar 2019 17:42:35 +0100 Subject: [PATCH 4/4] renaming mapping functions for clarity --- src/compas_rhino/geometry/surface.py | 46 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/compas_rhino/geometry/surface.py b/src/compas_rhino/geometry/surface.py index cd22c6dffb5c..b22a34bd0f6b 100644 --- a/src/compas_rhino/geometry/surface.py +++ b/src/compas_rhino/geometry/surface.py @@ -348,72 +348,72 @@ def closest_points_on_boundaries(self, points): # mapping # -------------------------------------------------------------------------- - def map_uv0(self, xyz): - """Return the UV(0) point from the mapping of a XYZ point based on the UV parameterisation of the surface. + def point_xyz_to_uv(self, xyz): + """Return the UV point from the mapping of a XYZ point based on the UV parameterisation of the surface. Parameters ---------- xyz : list - XYZ coordinates. + (x, y, z) coordinates. Returns ------- list - The UV(0) coordinates of the mapped point. + The (u, v, 0) coordinates of the mapped point. """ return rs.SurfaceClosestPoint(self.guid, xyz) + (0.,) - def remap_xyz_point(self, uv): - """Return the XYZ point from the re-mapping of a UV point based on the UV parameterisation of the surface. + def point_uv_to_xyz(self, uv): + """Return the XYZ point from the inverse mapping of a UV point based on the UV parameterisation of the surface. Parameters ---------- uv : list - UV(0) coordinates. + (u, v, 0) coordinates. Returns ------- list - The XYZ coordinates of the re-mapped point. + The (x, y, z) coordinates of the inverse-mapped point. """ return tuple(rs.EvaluateSurface(self.guid, *uv)) - def remap_xyz_line(self, line): - """Return the XYZ points from the re-mapping of a UV line based on the UV parameterisation of the surface. + def line_uv_to_xyz(self, line): + """Return the XYZ points from the inverse mapping of a UV line based on the UV parameterisation of the surface. Parameters ---------- uv : list - List of UV(0) coordinates. + List of (u, v, 0) coordinates. Returns ------- list - The list of XYZ coordinates of the re-mapped line. + The list of XYZ coordinates of the inverse-mapped line. """ - return (self.remap_xyz_point(line[0][:2]), self.remap_xyz_point(line[1][:2])) + return (self.point_uv_to_xyz(line[0][:2]), self.point_uv_to_xyz(line[1][:2])) - def remap_xyz_polyline(self, polyline): - """Return the XYZ points from the re-mapping of a UV polyline based on the UV parameterisation of the surface. + def polyline_uv_to_xyz(self, polyline): + """Return the XYZ points from the inverse mapping of a UV polyline based on the UV parameterisation of the surface. Parameters ---------- uv : list - List of UV(0) coordinates. + List of (u, v, 0) coordinates. Returns ------- list - The list of XYZ coordinates of the re-mapped polyline. + The list of (x, y, z) coordinates of the inverse-mapped polyline. """ - return [self.remap_xyz_point(vertex[:2]) for vertex in polyline] + return [self.point_uv_to_xyz(vertex[:2]) for vertex in polyline] - def remap_xyz_mesh(self, mesh, cls=None): - """Return the mesh from the re-mapping of a UV mesh based on the UV parameterisation of the surface. + def mesh_uv_to_xyz(self, mesh, cls=None): + """Return the mesh from the inverse mapping of a UV mesh based on the UV parameterisation of the surface. Parameters ---------- @@ -423,14 +423,14 @@ def remap_xyz_mesh(self, mesh, cls=None): Returns ------- Mesh, cls - The re-mapped mesh. + The inverse-mapped mesh. """ if cls is None: cls = type(mesh) vertices, faces = mesh.to_vertices_and_faces() - vertices = [self.remap_xyz_point(uv0[:2]) for uv0 in vertices] + vertices = [self.point_uv_to_xyz(uv0[:2]) for uv0 in vertices] return cls.from_vertices_and_faces(vertices, faces) # ============================================================================== @@ -438,7 +438,7 @@ def remap_xyz_mesh(self, mesh, cls=None): # ============================================================================== if __name__ == '__main__': - + surface = RhinoSurface.from_selection() points = []