From 87df71e8c4eecf65ac20c2cf1c4c2c9256b1828e Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Tue, 29 Nov 2022 11:47:45 +0100 Subject: [PATCH 01/17] added frame_to_rhino_plane --- CHANGELOG.md | 2 ++ src/compas_rhino/conversions/__init__.py | 3 +++ src/compas_rhino/conversions/_primitives.py | 15 +++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 394c96598f20..4dd177cf58f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +* Added conversion function `frame_to_rhino_plane` to `compas_rhino.conversions`. + ### Changed * Fixed strange point values in RhinoNurbsCurve caused by conversion `ControlPoint` to COMPAS instead of `ControlPoint.Location`. diff --git a/src/compas_rhino/conversions/__init__.py b/src/compas_rhino/conversions/__init__.py index 6cd84431f074..14de864cc5b9 100644 --- a/src/compas_rhino/conversions/__init__.py +++ b/src/compas_rhino/conversions/__init__.py @@ -58,6 +58,7 @@ line_to_rhino plane_to_rhino frame_to_rhino + frame_to_rhino_plane circle_to_rhino ellipse_to_rhino polyline_to_rhino @@ -134,6 +135,7 @@ line_to_rhino, plane_to_rhino, frame_to_rhino, + frame_to_rhino_plane, circle_to_rhino, ellipse_to_rhino, polyline_to_rhino, @@ -196,6 +198,7 @@ "line_to_rhino", "plane_to_rhino", "frame_to_rhino", + "frame_to_rhino_plane", "circle_to_rhino", "ellipse_to_rhino", "polyline_to_rhino", diff --git a/src/compas_rhino/conversions/_primitives.py b/src/compas_rhino/conversions/_primitives.py index 301107c38854..2f0ede30556b 100644 --- a/src/compas_rhino/conversions/_primitives.py +++ b/src/compas_rhino/conversions/_primitives.py @@ -160,6 +160,21 @@ def plane_to_compas_frame(plane): ) +def frame_to_rhino_plane(frame): + """Convert a COMPAS frame to a Rhino plane. + + Parameters + ---------- + frame : :class:`~compas.geometry.Frame` + + Returns + ------- + :rhino:`Rhino.Geometry.Plane` + + """ + return RhinoPlane(point_to_rhino(frame.point), vector_to_rhino(frame.xaxis), vector_to_rhino(frame.yaxis)) + + def frame_to_rhino(frame): """Convert a COMPAS frame to a Rhino plane. From b7335896337b214c553ed959954229d40859587a Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Tue, 29 Nov 2022 14:58:24 +0100 Subject: [PATCH 02/17] added RhinoSurface.from_frame --- CHANGELOG.md | 1 + src/compas_rhino/geometry/surfaces/surface.py | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3489d1a31610..611649f18514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added * Added conversion function `frame_to_rhino_plane` to `compas_rhino.conversions`. +* Added `RhinoSuface.from_frame` to `compas_rhino.geometry`. ### Changed diff --git a/src/compas_rhino/geometry/surfaces/surface.py b/src/compas_rhino/geometry/surfaces/surface.py index 8f1aad8183d4..8c5fe0e161ef 100644 --- a/src/compas_rhino/geometry/surfaces/surface.py +++ b/src/compas_rhino/geometry/surfaces/surface.py @@ -8,6 +8,7 @@ from compas_rhino.conversions import point_to_compas from compas_rhino.conversions import vector_to_compas from compas_rhino.conversions import plane_to_compas_frame +from compas_rhino.conversions import frame_to_rhino_plane from compas_rhino.conversions import plane_to_rhino from compas_rhino.conversions import box_to_compas from compas_rhino.conversions import xform_to_rhino @@ -187,6 +188,51 @@ def from_plane(cls, plane, box): rhino_surface = Rhino.Geometry.PlaneSurface.CreateThroughBox(plane, box) return cls.from_rhino(rhino_surface) + @classmethod + def from_frame(cls, frame, u_interval, v_interval, uv_degrees, uv_point_counts): + """Creates a NURBS surface from a frame and parametric domain information. + + Parameters + ---------- + frame : :class:`~compas.geometry.Frame` + A frame with point at the center of the wanted plannar surface and + x and y axes the direction of u and v respectively. + u_interval : tuple(float, float) + The parametric domain of the U parameter. u_interval[0] => u_interval[1]. + v_interval : tuple(float, float) + The parametric domain of the V parameter. v_interval[0] => v_interval[1]. + uv_degrees : tuple(int, int) + Degree of U and V parameters. uv_degree[0] => degree_u, uv_degree[1] => degree_v + uv_point_counts : + Number of control points in each parameter. [0] => cp_count_u, [1] => cp_count_v + + Returns + ------- + :rhino:`Rhino.Geometry.NurbsSurface` + + """ + # so that parameteric surface starts correctly at corner of the wanted plane section + rhino_plane = frame_to_rhino_plane(frame) + u_size = abs(u_interval[1] - u_interval[0]) + v_size = abs(v_interval[1] - v_interval[0]) + rhino_plane.Origin = rhino_plane.PointAt(-u_size / 2.0, -v_size / 2.0) # TODO: shift to plane corner + surface = Rhino.Geometry.NurbsSurface.CreateFromPlane( + rhino_plane, + Rhino.Geometry.Interval(*u_interval), + Rhino.Geometry.Interval(*v_interval), + uv_degrees[0], + uv_degrees[1], + uv_point_counts[0], + uv_point_counts[1], + ) + if not surface: + raise ValueError( + "Failed creating NurbsSurface from frame:{} u_interval:{} v_interval:{} uv_degrees:{} uv_point_count:{}".format( + frame, u_interval, v_interval, uv_degrees, uv_point_counts + ) + ) + return cls.from_rhino(surface) + # ============================================================================== # Conversions # ============================================================================== From 1f0816a4224888d6f46d9941798627a163388b3c Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Mon, 5 Dec 2022 18:20:49 +0100 Subject: [PATCH 03/17] WIP: builder serialization --- src/compas_rhino/geometry/brep/brep.py | 25 +++--- src/compas_rhino/geometry/brep/builder.py | 95 +++++++++++++++++++++++ src/compas_rhino/geometry/brep/edge.py | 93 ++++++++++++---------- src/compas_rhino/geometry/brep/face.py | 85 +++++++++++--------- src/compas_rhino/geometry/brep/loop.py | 24 ++++-- src/compas_rhino/geometry/brep/trim.py | 42 ++++++++++ src/compas_rhino/geometry/brep/vertex.py | 14 +++- 7 files changed, 282 insertions(+), 96 deletions(-) create mode 100644 src/compas_rhino/geometry/brep/builder.py create mode 100644 src/compas_rhino/geometry/brep/trim.py diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index 917b01826746..d05b902a3398 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -12,6 +12,7 @@ import Rhino +from .builder import RhinoBrepBuilder from .face import RhinoBrepFace from .edge import RhinoBrepEdge from .vertex import RhinoBrepVertex @@ -70,18 +71,22 @@ def __init__(self, brep=None): @property def data(self): - faces = [] - for face in self.faces: - faces.append(face.data) - return {"faces": faces} + return { + "vertices": [v.data for v in self.vertices], + "edges": [e.data for e in self.edges], + "faces": [f.data for f in self.faces], + } @data.setter def data(self, data): - faces = [] - for facedata in data["faces"]: - face = RhinoBrepFace.from_data(facedata) - faces.append(face) - self._create_native_brep(faces) + builder = RhinoBrepBuilder() + for v_data in data["vertices"]: + RhinoBrepVertex.from_data(v_data, builder) + for e_data in data["edges"]: + RhinoBrepEdge.from_data(e_data, builder) + for f_data in data["faces"]: + RhinoBrepFace.from_data(f_data, builder) + self._brep = builder.result # ============================================================================== # Properties @@ -103,7 +108,7 @@ def points(self): @property def edges(self): if self._brep: - return [RhinoBrepEdge(trim) for trim in self._brep.Trims] + return [RhinoBrepEdge(trim) for trim in self._brep.Edges] @property def loops(self): diff --git a/src/compas_rhino/geometry/brep/builder.py b/src/compas_rhino/geometry/brep/builder.py new file mode 100644 index 000000000000..4790a09a4ee7 --- /dev/null +++ b/src/compas_rhino/geometry/brep/builder.py @@ -0,0 +1,95 @@ +from compas_rhino.conversions import point_to_rhino + + +import Rhino + + +TOLERANCE = 1e-6 + + +class BrepReconstructionError(BaseException): + pass + + +class RhinoLoopBuilder(object): + def __init__(self, loop=None, instance=None): + self.loop = loop + self.instance = instance + + def add_trim(self, curve, edge_index, is_reversed, iso_status): + c_index = self.instance.AddTrimCurve(curve) + edge = self.instance.Edges[edge_index] + trim = self.instance.Trims.Add(edge, is_reversed, self.loop, c_index) + trim.IsoStatus = iso_status + trim.SetTolerances(TOLERANCE, TOLERANCE) + return trim + + @property + def result(self): + return self.loop + + +class RhinoFaceBuilder(object): + def __init__(self, face=None, instance=None): + self.face = face + self.instance = instance + + @property + def result(self): + return self.face + + def add_loop(self, loop_type): + loop = self.instance.Loops.Add(loop_type, self.face) + return RhinoLoopBuilder(loop, self.instance) + + +class RhinoBrepBuilder(object): + """Reconstructs a Rhino.Geometry.Brep from COMPAS types""" + + def __init__(self): + self._instance = Rhino.Geometry.Brep() + + @property + def result(self): + is_valid, log = self._instance.IsValidWithLog() + if not is_valid: + raise BrepReconstructionError("Brep reconstruction failed!\n{}".format(log)) + return self._instance + + def add_vertex(self, point): + """Add vertext to a new Brep + + point : :class:`~compas.geometry.Point` + + Returns + ------- + :rhino:`Rhino.Geometry.BrepVertex` + + """ + return self._instance.Vertices.Add(point_to_rhino(point), TOLERANCE) + + def add_edge(self, edge_curve, start_vertex, end_vertex): + """Add edge to the new Brep + + edge_curve : :class:`~compas_rhino.geometry.RhinoNurbsCurve` + start_vertex: int + index of the vertex at the start of this edge + end_vertex: int + index of the vertex at the end of this edge + + Returns + ------- + :rhino:`Rhino.Geometry.BrepEdge` + + """ + curve_index = self._instance.AddEdgeCurve(edge_curve) + s_vertex = self._instance.Vertices[start_vertex] + e_vertex = self._instance.Vertices[end_vertex] + return self._instance.Edges.Add(s_vertex, e_vertex, curve_index, TOLERANCE) + + def add_face(self, surface): + surface_index = self._instance.AddSurface(surface.rhino_surface) + face = self._instance.Faces.Add(surface_index) + return RhinoFaceBuilder(face=face, instance=self._instance) + + diff --git a/src/compas_rhino/geometry/brep/edge.py b/src/compas_rhino/geometry/brep/edge.py index 159afc66bd9a..9cf363e78156 100644 --- a/src/compas_rhino/geometry/brep/edge.py +++ b/src/compas_rhino/geometry/brep/edge.py @@ -1,6 +1,5 @@ from compas.geometry import BrepEdge from compas.geometry import Line -from compas.geometry import Point from compas.geometry import Circle from compas.geometry import Ellipse from compas_rhino.geometry import RhinoNurbsCurve @@ -39,20 +38,22 @@ class RhinoBrepEdge(BrepEdge): """ - def __init__(self, rhino_trim=None): + def __init__(self, rhino_edge=None, builder=None): super(RhinoBrepEdge, self).__init__() + self._builder = builder self._edge = None self._curve = None + self._curve_type = None self._start_vertex = None self._end_vertex = None - if rhino_trim: - self._set_edge(rhino_trim) + if rhino_edge: + self._set_edge(rhino_edge) - def _set_edge(self, rhino_trim): - self._edge = rhino_trim.Edge - self._curve = self._edge.EdgeCurve - self._start_vertex = RhinoBrepVertex(rhino_trim.StartVertex) - self._end_vertex = RhinoBrepVertex(rhino_trim.EndVertex) + def _set_edge(self, rhino_edge): + self._edge = rhino_edge + self._curve = RhinoNurbsCurve.from_rhino(rhino_edge.EdgeCurve.ToNurbsCurve()) + self._start_vertex = RhinoBrepVertex(rhino_edge.StartVertex) + self._end_vertex = RhinoBrepVertex(rhino_edge.EndVertex) # ============================================================================== # Data @@ -60,39 +61,25 @@ def _set_edge(self, rhino_trim): @property def data(self): - if self.is_line: - type_ = "line" - curve = curve_to_compas_line(self._curve) - elif self.is_circle: - type_ = "circle" - curve = curve_to_compas_circle(self._curve) - elif self.is_ellipse: - type_ = "ellipse" - curve = curve_to_compas_ellipse(self._curve) - else: - type_ = "nurbs" - curve = RhinoNurbsCurve.from_rhino(self._curve) + curve_type, curve = self._get_curve_geometry() return { - "type": type_, - "value": curve.data, - "points": [self.start_vertex.point.data, self.end_vertex.point.data], + "curve_type": curve_type, + "curve": curve.data, + "start_vertex": self._edge.StartVertex.VertexIndex, + "end_vertex": self._edge.EndVertex.VertexIndex } @data.setter def data(self, value): - curve_type = value["type"] - if curve_type == "line": - self._curve = line_to_rhino_curve(Line.from_data(value["value"])) # this returns a Nurbs Curve, why? - elif curve_type == "circle": - self._curve = circle_to_rhino_curve(Circle.from_data(value["value"])) # this returns a Nurbs Curve, why? - elif curve_type == "ellipse": - self._curve = ellipse_to_rhino_curve(Ellipse.from_data(value["value"])) - else: - self._curve = RhinoNurbsCurve.from_data(value["value"]).rhino_curve + edge_curve = self._create_curve_from_data(value["curve_type"], value["curve"]) + edge = self._builder.add_edge(edge_curve, value["start_vertex"], value["end_vertex"]) + self._set_edge(edge) - self._start_vertex, self._end_vertex = RhinoBrepVertex(), RhinoBrepVertex() - self._start_vertex._point = Point.from_data(value["points"][0]) - self._end_vertex._point = Point.from_data(value["points"][1]) + @classmethod + def from_data(cls, data, builder): + obj = cls(builder=builder) + obj.data = data + return obj # ============================================================================== # Properties @@ -116,12 +103,40 @@ def vertices(self): @property def is_circle(self): - return self._curve.IsCircle() + return self._edge.EdgeCurve.IsCircle() @property def is_line(self): - return self._curve.IsLinear() + return self._edge.EdgeCurve.IsLinear() @property def is_ellipse(self): - return self._curve.IsEllipse() + return self._edge.EdgeCurve.IsEllipse() + + def _get_curve_geometry(self): + curve = self._edge.EdgeCurve + if self.is_line: + type_ = "line" + curve = curve_to_compas_line(curve) + # TODO: there is an edge/trim direction issue when creating and edge from circle + # elif self.is_circle: + # type_ = "circle" + # curve = curve_to_compas_circle(curve) + # elif self.is_ellipse: + # type_ = "ellipse" + # curve = curve_to_compas_ellipse(curve) + else: + type_ = "nurbs" + curve = self._curve + return type_, curve + + @staticmethod + def _create_curve_from_data(curve_type, curve_data): + if curve_type == "line": + return line_to_rhino_curve(Line.from_data(curve_data)) + elif curve_type == "circle": + return circle_to_rhino_curve(Circle.from_data(curve_data)) + elif curve_type == "ellipse": + return ellipse_to_rhino_curve(Ellipse.from_data(curve_data)) + else: + return RhinoNurbsCurve.from_data(curve_data).rhino_curve diff --git a/src/compas_rhino/geometry/brep/face.py b/src/compas_rhino/geometry/brep/face.py index aa2b973a3b8b..e2a49e50b149 100644 --- a/src/compas_rhino/geometry/brep/face.py +++ b/src/compas_rhino/geometry/brep/face.py @@ -1,11 +1,15 @@ from compas.geometry import BrepFace from compas.geometry import Sphere from compas.geometry import Cylinder +from compas.geometry import Frame from compas_rhino.geometry import RhinoNurbsSurface from compas_rhino.conversions import plane_to_compas +from compas_rhino.conversions import plane_to_compas_frame from compas_rhino.conversions import sphere_to_compas from compas_rhino.conversions import cylinder_to_compas +from Rhino.Geometry import Interval + from .loop import RhinoBrepLoop @@ -29,8 +33,9 @@ class RhinoBrepFace(BrepFace): """ - def __init__(self, rhino_face=None): + def __init__(self, rhino_face=None, builder=None): super(RhinoBrepFace, self).__init__() + self._builder = builder self._loops = None self._surface = None self._face = None @@ -39,8 +44,8 @@ def __init__(self, rhino_face=None): def _set_face(self, native_face): self._face = native_face - self._loops = [RhinoBrepLoop(loop) for loop in self._face.Loops] - self._surface = self._face.UnderlyingSurface() + self._loops = [RhinoBrepLoop(loop) for loop in native_face.Loops] + self._surface = RhinoNurbsSurface.from_rhino(self._face.UnderlyingSurface().ToNurbsSurface()) # ============================================================================== # Data @@ -48,35 +53,30 @@ def _set_face(self, native_face): @property def data(self): - boundary = self._loops[0].data - holes = [loop.data for loop in self._loops[1:]] - surface_type, surface = self._get_surface_geometry(self._surface) - surface_data = {"value": surface.data, "type": surface_type} - return {"boundary": boundary, "surface": surface_data, "holes": holes} + surface_type, surface, uv_domain = self._get_surface_geometry(self._face.UnderlyingSurface()) + return { + "surface_type": surface_type, + "surface": surface.data, + "uv_domain": uv_domain, + "loops": [l.data for l in self._loops] + } @data.setter def data(self, value): - boundary = RhinoBrepLoop.from_data(value["boundary"]) - holes = [RhinoBrepLoop.from_data(loop) for loop in value["holes"]] - self._loops = [boundary] + holes - # TODO: using the new serialization mechanism, surface.to_nurbs() should replace all this branching.. # TODO: given that Plane, Sphere, Cylinder etc. all implement to_nurbs() - surface_data = value["surface"] - type_ = surface_data["type"] - surface = surface_data["value"] - if type_ == "plane": - surface = self._make_surface_from_loop(boundary) - elif type_ == "sphere": - surface = RhinoNurbsSurface.from_sphere(Sphere.from_data(surface)) - elif type_ == "cylinder": - surface = RhinoNurbsSurface.from_cylinder(Cylinder.from_data(surface)) - elif type_ == "nurbs": - surface = RhinoNurbsSurface.from_data(surface) - elif type_ == "torus": - raise NotImplementedError("Support for torus surface is not yet implemented!") - self._surface = surface.rhino_surface - + self._surface = self._make_surface_from_data(value["surface_type"], value["surface"], value["uv_domain"]) + face_builder = self._builder.add_face(self._surface) + for loop_data in value["loops"]: + RhinoBrepLoop.from_data(loop_data, face_builder) + self._set_face(face_builder.result) + + @classmethod + def from_data(cls, data, builder): + obj = cls(builder=builder) + obj.data = data + return obj + # ============================================================================== # Properties # ============================================================================== @@ -107,23 +107,34 @@ def is_plane(self): @staticmethod def _get_surface_geometry(surface): - success, cast_surface = surface.TryGetPlane() - if success: - return "plane", plane_to_compas(cast_surface) + uv_domain = [[surface.Domain(0)[0], surface.Domain(0)[1]], [surface.Domain(1)[0], surface.Domain(1)[1]]] success, cast_surface = surface.TryGetSphere() if success: - return "sphere", sphere_to_compas(cast_surface) + return "sphere", sphere_to_compas(cast_surface), uv_domain success, cast_surface = surface.TryGetCylinder() if success: - return "cylinder", cylinder_to_compas(cast_surface) + return "cylinder", cylinder_to_compas(cast_surface), uv_domain success, cast_surface = surface.TryGetTorus() if success: raise NotImplementedError("Support for torus surface is not yet implemented!") - return "nurbs", RhinoNurbsSurface.from_rhino(surface.ToNurbsSurface()) + success, cast_surface = surface.TryGetPlane() + if success: + return "plane", plane_to_compas_frame(cast_surface), uv_domain + return "nurbs", RhinoNurbsSurface.from_rhino(surface.ToNurbsSurface()), uv_domain @staticmethod - def _make_surface_from_loop(loop): - # order of corners determines the normal of the resulting surface - corners = [loop.edges[i].start_vertex.point for i in range(4)] - surface = RhinoNurbsSurface.from_corners(corners) + def _make_surface_from_data(surface_type, surface_data, uv_domain): + if surface_type == "plane": + frame = Frame.from_data(surface_data) + surface = RhinoNurbsSurface.from_frame(frame, uv_domain[0], uv_domain[1], (1, 1), (2, 2)) + elif surface_type == "sphere": + surface = RhinoNurbsSurface.from_sphere(Sphere.from_data(surface_data)) + elif surface_type == "cylinder": + surface = RhinoNurbsSurface.from_cylinder(Cylinder.from_data(surface_data)) + elif surface_type == "nurbs": + surface = RhinoNurbsSurface.from_data(surface_data) + elif surface_type == "torus": + raise NotImplementedError("Support for torus surface is not yet implemented!") + surface.rhino_surface.SetDomain(0, Interval(*uv_domain[0])) + surface.rhino_surface.SetDomain(1, Interval(*uv_domain[1])) return surface diff --git a/src/compas_rhino/geometry/brep/loop.py b/src/compas_rhino/geometry/brep/loop.py index 2317246f4a0a..1d8fc016db93 100644 --- a/src/compas_rhino/geometry/brep/loop.py +++ b/src/compas_rhino/geometry/brep/loop.py @@ -2,7 +2,7 @@ import Rhino -from .edge import RhinoBrepEdge +from .trim import RhinoBrepTrim class LoopType(object): @@ -43,18 +43,20 @@ class RhinoBrepLoop(BrepLoop): """ - def __init__(self, rhino_loop=None): + def __init__(self, rhino_loop=None, builder=None): super(RhinoBrepLoop, self).__init__() + self._builder = builder self._loop = None - self._edges = None self._type = LoopType.UNKNOWN + self._trims = None if rhino_loop: self._set_loop(rhino_loop) def _set_loop(self, native_loop): self._loop = native_loop self._type = int(self._loop.LoopType) - self._edges = [RhinoBrepEdge(trim) for trim in self._loop.Trims] + self._trims = [RhinoBrepTrim(trim) for trim in self._loop.Trims] + # ============================================================================== # Data @@ -62,11 +64,21 @@ def _set_loop(self, native_loop): @property def data(self): - return [e.data for e in self._edges] + return {"type": str(self._loop.LoopType), "trims": [t.data for t in self._trims]} @data.setter def data(self, value): - self._edges = [RhinoBrepEdge.from_data(e_data) for e_data in value] + self._type = Rhino.Geometry.BrepLoopType.Outer if value["type"] == "Outer" else Rhino.Geometry.BrepLoopType.Inner + loop_builder = self._builder.add_loop(self._type) + for trim_data in value["trims"]: + RhinoBrepTrim.from_data(trim_data, loop_builder) + self._set_loop(loop_builder.result) + + @classmethod + def from_data(cls, data, builder): + obj = cls(builder=builder) + obj.data = data + return obj # ============================================================================== # Properties diff --git a/src/compas_rhino/geometry/brep/trim.py b/src/compas_rhino/geometry/brep/trim.py new file mode 100644 index 000000000000..2f9cc8d71ba2 --- /dev/null +++ b/src/compas_rhino/geometry/brep/trim.py @@ -0,0 +1,42 @@ +from compas.data import Data +from compas_rhino.geometry import RhinoNurbsCurve + + +import Rhino + + +class RhinoBrepTrim(Data): + def __init__(self, rhino_trim=None, builder=None): + super(RhinoBrepTrim, self).__init__() + self._builder = builder + self._trim = None + self._curve = None + self._is_reversed = None + if rhino_trim: + self._set_trim(rhino_trim) + + def _set_trim(self, rhino_trim): + self._trim = rhino_trim + + @property + def data(self): + return { + "edge": self._trim.Edge.EdgeIndex, + "curve": RhinoNurbsCurve.from_rhino(self._trim.TrimCurve.ToNurbsCurve()).data, + "iso": str(self._trim.IsoStatus), + "is_reversed": "true" if self._trim.IsReversed() else "false" + } + + @data.setter + def data(self, value): + curve = RhinoNurbsCurve.from_data(value["curve"]).rhino_curve + iso_status = getattr(Rhino.Geometry.IsoStatus, value["iso"]) + is_reversed = True if value["is_reversed"] == "true" else False + trim = self._builder.add_trim(curve, value["edge"], is_reversed, iso_status) + self._set_trim(trim) + + @classmethod + def from_data(cls, data, builder): + obj = cls(builder=builder) + obj.data = data + return obj diff --git a/src/compas_rhino/geometry/brep/vertex.py b/src/compas_rhino/geometry/brep/vertex.py index 204846e39161..55882efb0041 100644 --- a/src/compas_rhino/geometry/brep/vertex.py +++ b/src/compas_rhino/geometry/brep/vertex.py @@ -13,8 +13,9 @@ class RhinoBrepVertex(BrepVertex): """ - def __init__(self, rhino_vertex=None): + def __init__(self, rhino_vertex=None, builder=None): super(RhinoBrepVertex, self).__init__() + self._builder = builder self._vertex = None self._point = None if rhino_vertex: @@ -35,10 +36,15 @@ def data(self): } @data.setter - def data(self, data): - # Rhino.BrepVertex has no public constructor - # Vertex creation is via Brep.Vertices.Add(Rhino.Point3D) + def data(self, data): self._point = Point.from_data(data["point"]) + self._vertex = self._builder.add_vertex(self._point) + + @classmethod + def from_data(cls, data, builder): + obj = cls(builder=builder) + obj.data = data + return obj # ============================================================================== # Properties From 88c27cebcfddd075d74494c514de862baf11020d Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Mon, 5 Dec 2022 18:22:24 +0100 Subject: [PATCH 04/17] remove reconstruction logic from RhinoBrep --- src/compas_rhino/geometry/brep/brep.py | 85 -------------------------- 1 file changed, 85 deletions(-) diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index d05b902a3398..3a61b128981d 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -331,88 +331,3 @@ def split(self, cutter): """ resulting_breps = self._brep.Split(cutter.native_brep, TOLERANCE) return [RhinoBrep.from_native(brep) for brep in resulting_breps] - - # ============================================================================== - # Other Methods - # ============================================================================== - - def _create_native_brep(self, faces): - # Source: https://github.com/mcneel/rhino-developer-samples/blob/3179a8386a64602ee670cc832c77c561d1b0944b/rhinocommon/cs/SampleCsCommands/SampleCsTrimmedPlane.cs - # Things need to be defined in a valid brep: - # 1- Vertices - # 2- 3D Curves (geometry) - # 3- Edges (topology - reference curve geometry) - # 4- Surfaces (geometry) - # 5- Faces (topology - reference surface geometry) - # 6- Loops (2D parameter space of faces) - # 4- Trims and 2D curves (2D parameter space of edges) - self._brep = Rhino.Geometry.Brep() - for face in faces: - rhino_face, rhino_surface = self._create_brep_face(face) - for loop in face.loops: - rhino_loop = self._brep.Loops.Add(Rhino.Geometry.BrepLoopType.Outer, rhino_face) - for edge in loop.edges: - start_vertex, end_vertex = self._add_edge_vertices(edge) - rhino_edge = self._add_edge(edge, start_vertex, end_vertex) - rhino_2d_curve = self._create_trim_curve(rhino_edge, rhino_surface) - self._add_trim(rhino_2d_curve, rhino_edge, rhino_loop) - - self._brep.Repair(TOLERANCE) - self._brep.JoinNakedEdges( - TOLERANCE - ) # without this, Brep.Trim() led to some weird results on de-serialized Breps - self._validate_brep() - - def _validate_brep(self): - if self._brep.IsValid: - return - - error_message = "" - valid_topo, log_topo = self._brep.IsValidTopology() - valid_tol, log_tol = self._brep.IsValidTolerancesAndFlags() - valid_geo, log_geo = self._brep.IsValidGeometry() - if not valid_geo: - error_message += "Invalid geometry:\n{}\n".format(log_geo) - if not valid_topo: - error_message += "Invalid topology:\n{}\n".format(log_topo) - if not valid_tol: - error_message += "Invalid tolerances:\n{}\n".format(log_tol) - - raise BrepInvalidError(error_message) - - def _create_brep_face(self, face): - # Geometry - surface_index = self._brep.AddSurface(face.native_surface) - brep_surface = self._brep.Surfaces.Item[surface_index] - # Topology - brep_face = self._brep.Faces.Add(surface_index) - return brep_face, brep_surface - - def _add_edge_vertices(self, edge): - start_vertex = self._brep.Vertices.Add(point_to_rhino(edge.start_vertex.point), TOLERANCE) - end_vertex = self._brep.Vertices.Add(point_to_rhino(edge.end_vertex.point), TOLERANCE) - return start_vertex, end_vertex - - def _add_edge(self, edge, start_vertex, end_vertex): - # Geometry - curve_index = self._brep.AddEdgeCurve(edge.curve) - # Topology - rhino_edge = self._brep.Edges.Add(start_vertex, end_vertex, curve_index, TOLERANCE) - return rhino_edge - - def _add_trim(self, rhino_trim_curve, rhino_edge, rhino_loop): - # Geometry - trim_curve_index = self._brep.AddTrimCurve(rhino_trim_curve) - # Topology - trim = self._brep.Trims.Add(rhino_edge, True, rhino_loop, trim_curve_index) - trim.IsoStatus = getattr( - Rhino.Geometry.IsoStatus, "None" - ) # IsoStatus.None makes lint, IDE and even Python angry - trim.TrimType = Rhino.Geometry.BrepTrimType.Boundary - trim.SetTolerances(TOLERANCE, TOLERANCE) - - @staticmethod - def _create_trim_curve(rhino_edge, rhino_surface): - curve_2d = rhino_surface.Pullback(rhino_edge.EdgeCurve, TOLERANCE) - curve_2d.Reverse() - return curve_2d From e2d7897d72bc4cdbe223a63bcb41c0391c5ba180 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Tue, 6 Dec 2022 10:10:23 +0100 Subject: [PATCH 05/17] added trim interface. second level import for builders --- src/compas/geometry/brep/trim.py | 40 ++++++++++++++++++++++ src/compas_rhino/geometry/__init__.py | 14 ++++++-- src/compas_rhino/geometry/brep/__init__.py | 8 +++++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 src/compas/geometry/brep/trim.py diff --git a/src/compas/geometry/brep/trim.py b/src/compas/geometry/brep/trim.py new file mode 100644 index 000000000000..a5fd8837d15a --- /dev/null +++ b/src/compas/geometry/brep/trim.py @@ -0,0 +1,40 @@ +from compas.data import Data + + +class TrimIsoStatus(object): + """An enumeration of isoparametric curve direction on the surface.""" + NONE = 0 + X = 1 + Y = 2 + West = 3 + South = 4 + East = 5 + North = 6 + + +class BrepTrim(Data): + """An interface for a Brep Trim + + Attributes + ---------- + curve : :class:`~compas.geometry.NurbsCurve`, read_only + Returns the geometry for this trim as a 2d curve. + iso_status : literal(NONE|X|Y|West|South|East|North) + The isoparametric curve direction on the surface. + is_reversed : bool + True if this trim is reversed from its associated edge curve and False otherwise. + + """ + + @property + def curve(self): + raise NotImplementedError + + @property + def iso_status(self): + raise NotImplementedError + + @property + def is_reversed(self): + raise NotImplementedError + diff --git a/src/compas_rhino/geometry/__init__.py b/src/compas_rhino/geometry/__init__.py index b78886491eff..6b37b1205a33 100644 --- a/src/compas_rhino/geometry/__init__.py +++ b/src/compas_rhino/geometry/__init__.py @@ -21,7 +21,10 @@ RhinoBrepEdge RhinoBrepFace RhinoBrepLoop - + RhinoBrepTrim + RhinoBrepBuilder + RhinoFaceBuilder + RhinoLoopBuilder Plugins ======= @@ -91,7 +94,10 @@ from .brep import RhinoBrepVertex from .brep import RhinoBrepFace from .brep import RhinoBrepEdge - +from .brep import RhinoBrepTrim +from .brep import RhinoBrepBuilder +from .brep import RhinoFaceBuilder +from .brep import RhinoLoopBuilder __all__ = [ "RhinoGeometry", @@ -116,4 +122,8 @@ "RhinoBrepEdge", "RhinoBrepFace", "RhinoBrepLoop", + "RhinoBrepTrim", + "RhinoBrepBuilder", + "RhinoFaceBuilder", + "RhinoLoopBuilder", ] diff --git a/src/compas_rhino/geometry/brep/__init__.py b/src/compas_rhino/geometry/brep/__init__.py index 43416bc31259..0c057d1864f6 100644 --- a/src/compas_rhino/geometry/brep/__init__.py +++ b/src/compas_rhino/geometry/brep/__init__.py @@ -5,6 +5,10 @@ from .edge import RhinoBrepEdge from .vertex import RhinoBrepVertex from .loop import RhinoBrepLoop +from .trim import RhinoBrepTrim +from .builder import RhinoBrepBuilder +from .builder import RhinoFaceBuilder +from .builder import RhinoLoopBuilder import Rhino @@ -15,6 +19,10 @@ "RhinoBrepEdge", "RhinoBrepLoop", "RhinoBrepFace", + "RhinoBrepTrim", + "RhinoBrepBuilder", + "RhinoFaceBuilder", + "RhinoLoopBuilder", "new_brep", "from_native", "from_box", From 83c38d6695d6e5eaba1dde3009cfc0de8ccb9c2b Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Tue, 6 Dec 2022 10:11:12 +0100 Subject: [PATCH 06/17] docstrings and missing properties --- src/compas_rhino/geometry/brep/builder.py | 15 +++++++ src/compas_rhino/geometry/brep/edge.py | 19 ++++++++- src/compas_rhino/geometry/brep/face.py | 17 +++++++- src/compas_rhino/geometry/brep/loop.py | 15 +++++++ src/compas_rhino/geometry/brep/trim.py | 48 ++++++++++++++++++++++- src/compas_rhino/geometry/brep/vertex.py | 15 +++++++ 6 files changed, 124 insertions(+), 5 deletions(-) diff --git a/src/compas_rhino/geometry/brep/builder.py b/src/compas_rhino/geometry/brep/builder.py index 4790a09a4ee7..8d5db2b86a7e 100644 --- a/src/compas_rhino/geometry/brep/builder.py +++ b/src/compas_rhino/geometry/brep/builder.py @@ -8,15 +8,30 @@ class BrepReconstructionError(BaseException): + """Indicates that the Brep reconstruction operation has resulted with an invalid Brep.""" pass class RhinoLoopBuilder(object): + """Builds a Brep loop. + + Attributes + ---------- + result : :rhino: Rhino.Geometry.BrepTrim + The created loop. + + """ def __init__(self, loop=None, instance=None): self.loop = loop self.instance = instance def add_trim(self, curve, edge_index, is_reversed, iso_status): + """Add trim to the new Brep. + + Parameters + ========== + curve: + """ c_index = self.instance.AddTrimCurve(curve) edge = self.instance.Edges[edge_index] trim = self.instance.Trims.Add(edge, is_reversed, self.loop, c_index) diff --git a/src/compas_rhino/geometry/brep/edge.py b/src/compas_rhino/geometry/brep/edge.py index 9cf363e78156..081efa741811 100644 --- a/src/compas_rhino/geometry/brep/edge.py +++ b/src/compas_rhino/geometry/brep/edge.py @@ -4,8 +4,8 @@ from compas.geometry import Ellipse from compas_rhino.geometry import RhinoNurbsCurve from compas_rhino.conversions import curve_to_compas_line -from compas_rhino.conversions import curve_to_compas_circle -from compas_rhino.conversions import curve_to_compas_ellipse +# from compas_rhino.conversions import curve_to_compas_circle +# from compas_rhino.conversions import curve_to_compas_ellipse from compas_rhino.conversions import line_to_rhino_curve from compas_rhino.conversions import circle_to_rhino_curve from compas_rhino.conversions import ellipse_to_rhino_curve @@ -77,6 +77,21 @@ def data(self, value): @classmethod def from_data(cls, data, builder): + """Construct an object of this type from the provided data. + + Parameters + ---------- + data : dict + The data dictionary. + builder : :class:`~compas_rhino.geometry.BrepBuilder` + The object reconstructing the current Brep. + + Returns + ------- + :class:`~compas.data.Data` + An instance of this object type if the data contained in the dict has the correct schema. + + """ obj = cls(builder=builder) obj.data = data return obj diff --git a/src/compas_rhino/geometry/brep/face.py b/src/compas_rhino/geometry/brep/face.py index e2a49e50b149..b66ddc748c96 100644 --- a/src/compas_rhino/geometry/brep/face.py +++ b/src/compas_rhino/geometry/brep/face.py @@ -3,7 +3,6 @@ from compas.geometry import Cylinder from compas.geometry import Frame from compas_rhino.geometry import RhinoNurbsSurface -from compas_rhino.conversions import plane_to_compas from compas_rhino.conversions import plane_to_compas_frame from compas_rhino.conversions import sphere_to_compas from compas_rhino.conversions import cylinder_to_compas @@ -73,6 +72,22 @@ def data(self, value): @classmethod def from_data(cls, data, builder): + """Construct an object of this type from the provided data. + + Parameters + ---------- + data : dict + The data dictionary. + builder : :class:`~compas_rhino.geometry.BrepBuilder` + The object reconstructing the current Brep. + + Returns + ------- + :class:`~compas.data.Data` + An instance of this object type if the data contained in the dict has the correct schema. + + """ + obj = cls(builder=builder) obj.data = data return obj diff --git a/src/compas_rhino/geometry/brep/loop.py b/src/compas_rhino/geometry/brep/loop.py index 1d8fc016db93..59cd053a37d5 100644 --- a/src/compas_rhino/geometry/brep/loop.py +++ b/src/compas_rhino/geometry/brep/loop.py @@ -76,6 +76,21 @@ def data(self, value): @classmethod def from_data(cls, data, builder): + """Construct an object of this type from the provided data. + + Parameters + ---------- + data : dict + The data dictionary. + builder : :class:`~compas_rhino.geometry.BrepFaceBuilder` + The object reconstructing the current BrepFace. + + Returns + ------- + :class:`~compas.data.Data` + An instance of this object type if the data contained in the dict has the correct schema. + + """ obj = cls(builder=builder) obj.data = data return obj diff --git a/src/compas_rhino/geometry/brep/trim.py b/src/compas_rhino/geometry/brep/trim.py index 2f9cc8d71ba2..2623237b3a97 100644 --- a/src/compas_rhino/geometry/brep/trim.py +++ b/src/compas_rhino/geometry/brep/trim.py @@ -1,22 +1,39 @@ -from compas.data import Data +from compas.geometry import BrepTrim from compas_rhino.geometry import RhinoNurbsCurve import Rhino -class RhinoBrepTrim(Data): +class RhinoBrepTrim(BrepTrim): + """An interface for a Brep Trim + + Attributes + ---------- + curve : :class:`~compas.geometry.NurbsCurve`, read_only + Returns the geometry for this trim as a 2d curve. + iso_status : literal(NONE|X|Y|West|South|East|North) + The isoparametric curve direction on the surface. + is_reversed : bool + True if this trim is reversed from its associated edge curve and False otherwise. + + """ def __init__(self, rhino_trim=None, builder=None): + super(RhinoBrepTrim, self).__init__() self._builder = builder self._trim = None self._curve = None self._is_reversed = None + self._iso_type = None if rhino_trim: self._set_trim(rhino_trim) def _set_trim(self, rhino_trim): self._trim = rhino_trim + self._curve = RhinoNurbsCurve.from_rhino(rhino_trim.TrimCurve.ToNurbsCurve()) + self._is_reversed = rhino_trim.IsReversed() + self._iso_type = int(rhino_trim.IsoStatus) @property def data(self): @@ -37,6 +54,33 @@ def data(self, value): @classmethod def from_data(cls, data, builder): + """Construct an object of this type from the provided data. + + Parameters + ---------- + data : dict + The data dictionary. + builder : :class:`~compas_rhino.geometry.BrepLoopBuilder` + The object reconstructing the current BrepLoop. + + Returns + ------- + :class:`~compas.data.Data` + An instance of this object type if the data contained in the dict has the correct schema. + + """ obj = cls(builder=builder) obj.data = data return obj + + @property + def curve(self): + return self._curve + + @property + def is_reverse(self): + return self._curve + + @property + def iso_status(self): + return self._iso_type diff --git a/src/compas_rhino/geometry/brep/vertex.py b/src/compas_rhino/geometry/brep/vertex.py index 55882efb0041..ab6f2dea446b 100644 --- a/src/compas_rhino/geometry/brep/vertex.py +++ b/src/compas_rhino/geometry/brep/vertex.py @@ -42,6 +42,21 @@ def data(self, data): @classmethod def from_data(cls, data, builder): + """Construct an object of this type from the provided data. + + Parameters + ---------- + data : dict + The data dictionary. + builder : :class:`~compas_rhino.geometry.BrepBuilder` + The object reconstructing the current Brep. + + Returns + ------- + :class:`~compas.data.Data` + An instance of this object type if the data contained in the dict has the correct schema. + + """ obj = cls(builder=builder) obj.data = data return obj From 164b11d3058b2dcbd093005c7473d34e98637866 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Tue, 6 Dec 2022 10:38:30 +0100 Subject: [PATCH 07/17] added docstrings --- src/compas_rhino/geometry/brep/builder.py | 53 ++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/compas_rhino/geometry/brep/builder.py b/src/compas_rhino/geometry/brep/builder.py index 8d5db2b86a7e..9aa6b43f4cb0 100644 --- a/src/compas_rhino/geometry/brep/builder.py +++ b/src/compas_rhino/geometry/brep/builder.py @@ -30,7 +30,20 @@ def add_trim(self, curve, edge_index, is_reversed, iso_status): Parameters ========== - curve: + curve : :rhino:`Rhino.Geometry.NurbsCurve` + The curve representing the geometry of this trim. + edge_index : int + The index of the already added edge which corresponds with this trim. + is_reversed : bool + True if this trim is reversed in direction from its associated edge. + iso_status : :rhino:`Rhino.Geometry.IsoStatus` + The iso status of this trim. + + Returns + ======= + :rhino:`Rhino.Geometry.BrepTrim` + The newly added BrepTrim instance. + """ c_index = self.instance.AddTrimCurve(curve) edge = self.instance.Edges[edge_index] @@ -45,6 +58,16 @@ def result(self): class RhinoFaceBuilder(object): + """Builds a BrepFace. + + Serves as context for reconstructing the loop elements associated with this face. + + Attributes + ========== + result : :rhino:`Rhino.Geometry.BrepFace` + The resulting BrepFace. + + """ def __init__(self, face=None, instance=None): self.face = face self.instance = instance @@ -54,6 +77,20 @@ def result(self): return self.face def add_loop(self, loop_type): + """Add a new loop to this face. + + Returns a new builder to be used by all the trims of the newly added loop. + + Parameters + ========== + loop_type : :rhino:`Rhino.Geometry.BrepLoopType` + The enumeration value representing the type of this loop. + + Returns + ======= + :class:`compas_rhino.geometry.RhinoLoopBuilder` + + """ loop = self.instance.Loops.Add(loop_type, self.face) return RhinoLoopBuilder(loop, self.instance) @@ -103,6 +140,20 @@ def add_edge(self, edge_curve, start_vertex, end_vertex): return self._instance.Edges.Add(s_vertex, e_vertex, curve_index, TOLERANCE) def add_face(self, surface): + """Creates and adds a new face to the brep. + + Returns a new builder to be used by all the loops related to his new face to add themselves. + + Parameters + ========== + surface : :rhino:`Rhino.Geometry.Surface` + The surface of this face. + + Returns + ======= + :class:`compas_rhino.geometry.RhinoFaceBuilder` + + """ surface_index = self._instance.AddSurface(surface.rhino_surface) face = self._instance.Faces.Add(surface_index) return RhinoFaceBuilder(face=face, instance=self._instance) From 3f193fb1c13757f24e55483def873e3e623b5839 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Tue, 6 Dec 2022 10:54:46 +0100 Subject: [PATCH 08/17] 2nd level import for BrepTrim. --- src/compas/geometry/__init__.py | 6 ++++++ src/compas/geometry/brep/__init__.py | 4 ++++ src/compas/geometry/brep/trim.py | 2 +- src/compas_rhino/geometry/brep/builder.py | 19 +++++++++++-------- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/compas/geometry/__init__.py b/src/compas/geometry/__init__.py index 8ea134b45fa4..2ee8957d603c 100644 --- a/src/compas/geometry/__init__.py +++ b/src/compas/geometry/__init__.py @@ -86,6 +86,8 @@ BrepEdge BrepLoop BrepFace + BrepTrim + BrepTrimIsoStatus BrepType BrepOrientation @@ -892,6 +894,8 @@ BrepFace, BrepLoop, BrepEdge, + BrepTrim, + BrepTrimIsoStatus, BrepType, BrepOrientation, BrepError, @@ -1202,6 +1206,8 @@ "BrepEdge", "BrepVertex", "BrepFace", + "BrepTrim", + "BrepTrimIsoStatus", "BrepType", "BrepOrientation", "BrepError", diff --git a/src/compas/geometry/brep/__init__.py b/src/compas/geometry/brep/__init__.py index 0e47dd38d2d7..63c4272235f4 100644 --- a/src/compas/geometry/brep/__init__.py +++ b/src/compas/geometry/brep/__init__.py @@ -5,6 +5,8 @@ from .loop import BrepLoop from .face import BrepFace from .vertex import BrepVertex +from .trim import BrepTrim +from .trim import BrepTrimIsoStatus class BrepError(Exception): @@ -31,6 +33,8 @@ class BrepTrimmingError(BrepError): "BrepLoop", "BrepFace", "BrepVertex", + "BrepTrim", + "BrepTrimIsoStatus", "BrepOrientation", "BrepType", "BrepError", diff --git a/src/compas/geometry/brep/trim.py b/src/compas/geometry/brep/trim.py index a5fd8837d15a..d2a90d1b0015 100644 --- a/src/compas/geometry/brep/trim.py +++ b/src/compas/geometry/brep/trim.py @@ -1,7 +1,7 @@ from compas.data import Data -class TrimIsoStatus(object): +class BrepTrimIsoStatus(object): """An enumeration of isoparametric curve direction on the surface.""" NONE = 0 X = 1 diff --git a/src/compas_rhino/geometry/brep/builder.py b/src/compas_rhino/geometry/brep/builder.py index 9aa6b43f4cb0..82058a040220 100644 --- a/src/compas_rhino/geometry/brep/builder.py +++ b/src/compas_rhino/geometry/brep/builder.py @@ -1,3 +1,4 @@ +from compas.geometry import BrepInvalidError from compas_rhino.conversions import point_to_rhino @@ -7,11 +8,6 @@ TOLERANCE = 1e-6 -class BrepReconstructionError(BaseException): - """Indicates that the Brep reconstruction operation has resulted with an invalid Brep.""" - pass - - class RhinoLoopBuilder(object): """Builds a Brep loop. @@ -96,7 +92,14 @@ def add_loop(self, loop_type): class RhinoBrepBuilder(object): - """Reconstructs a Rhino.Geometry.Brep from COMPAS types""" + """Reconstructs a Rhino.Geometry.Brep from COMPAS types + + Attributes + ========== + result : :rhino:`Rhino.Geometry.Brep` + The Brep resulting from the reconstruction, if successful. + + """ def __init__(self): self._instance = Rhino.Geometry.Brep() @@ -105,7 +108,7 @@ def __init__(self): def result(self): is_valid, log = self._instance.IsValidWithLog() if not is_valid: - raise BrepReconstructionError("Brep reconstruction failed!\n{}".format(log)) + raise BrepInvalidError("Brep reconstruction failed!\n{}".format(log)) return self._instance def add_vertex(self, point): @@ -152,7 +155,7 @@ def add_face(self, surface): Returns ======= :class:`compas_rhino.geometry.RhinoFaceBuilder` - + """ surface_index = self._instance.AddSurface(surface.rhino_surface) face = self._instance.Faces.Add(surface_index) From f1f52149c800cf6096c733e67940c6e455f39638 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Tue, 6 Dec 2022 11:42:09 +0100 Subject: [PATCH 09/17] formatting --- src/compas/geometry/brep/trim.py | 6 +++--- src/compas_rhino/geometry/brep/brep.py | 4 +--- src/compas_rhino/geometry/brep/builder.py | 18 +++++++++--------- src/compas_rhino/geometry/brep/edge.py | 9 +++++---- src/compas_rhino/geometry/brep/face.py | 12 ++++++------ src/compas_rhino/geometry/brep/loop.py | 5 +++-- src/compas_rhino/geometry/brep/trim.py | 5 +++-- src/compas_rhino/geometry/brep/vertex.py | 2 +- 8 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/compas/geometry/brep/trim.py b/src/compas/geometry/brep/trim.py index d2a90d1b0015..24321cb69c5f 100644 --- a/src/compas/geometry/brep/trim.py +++ b/src/compas/geometry/brep/trim.py @@ -3,8 +3,9 @@ class BrepTrimIsoStatus(object): """An enumeration of isoparametric curve direction on the surface.""" - NONE = 0 - X = 1 + + NONE = 0 + X = 1 Y = 2 West = 3 South = 4 @@ -37,4 +38,3 @@ def iso_status(self): @property def is_reversed(self): raise NotImplementedError - diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index 3a61b128981d..c2176eaeddfb 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -1,11 +1,9 @@ from compas.geometry import Frame from compas.geometry import Brep -from compas.geometry import BrepInvalidError from compas.geometry import BrepTrimmingError from compas.geometry import Plane from compas_rhino.conversions import box_to_rhino -from compas_rhino.conversions import point_to_rhino from compas_rhino.conversions import xform_to_rhino from compas_rhino.conversions import frame_to_rhino from compas_rhino.conversions import cylinder_to_rhino @@ -75,7 +73,7 @@ def data(self): "vertices": [v.data for v in self.vertices], "edges": [e.data for e in self.edges], "faces": [f.data for f in self.faces], - } + } @data.setter def data(self, data): diff --git a/src/compas_rhino/geometry/brep/builder.py b/src/compas_rhino/geometry/brep/builder.py index 82058a040220..16ada81f96d0 100644 --- a/src/compas_rhino/geometry/brep/builder.py +++ b/src/compas_rhino/geometry/brep/builder.py @@ -10,13 +10,14 @@ class RhinoLoopBuilder(object): """Builds a Brep loop. - + Attributes ---------- result : :rhino: Rhino.Geometry.BrepTrim The created loop. """ + def __init__(self, loop=None, instance=None): self.loop = loop self.instance = instance @@ -64,6 +65,7 @@ class RhinoFaceBuilder(object): The resulting BrepFace. """ + def __init__(self, face=None, instance=None): self.face = face self.instance = instance @@ -93,14 +95,14 @@ def add_loop(self, loop_type): class RhinoBrepBuilder(object): """Reconstructs a Rhino.Geometry.Brep from COMPAS types - + Attributes ========== result : :rhino:`Rhino.Geometry.Brep` The Brep resulting from the reconstruction, if successful. - - """ - + + """ + def __init__(self): self._instance = Rhino.Geometry.Brep() @@ -119,7 +121,7 @@ def add_vertex(self, point): Returns ------- :rhino:`Rhino.Geometry.BrepVertex` - + """ return self._instance.Vertices.Add(point_to_rhino(point), TOLERANCE) @@ -151,7 +153,7 @@ def add_face(self, surface): ========== surface : :rhino:`Rhino.Geometry.Surface` The surface of this face. - + Returns ======= :class:`compas_rhino.geometry.RhinoFaceBuilder` @@ -160,5 +162,3 @@ def add_face(self, surface): surface_index = self._instance.AddSurface(surface.rhino_surface) face = self._instance.Faces.Add(surface_index) return RhinoFaceBuilder(face=face, instance=self._instance) - - diff --git a/src/compas_rhino/geometry/brep/edge.py b/src/compas_rhino/geometry/brep/edge.py index 081efa741811..e52f51e459af 100644 --- a/src/compas_rhino/geometry/brep/edge.py +++ b/src/compas_rhino/geometry/brep/edge.py @@ -4,6 +4,7 @@ from compas.geometry import Ellipse from compas_rhino.geometry import RhinoNurbsCurve from compas_rhino.conversions import curve_to_compas_line + # from compas_rhino.conversions import curve_to_compas_circle # from compas_rhino.conversions import curve_to_compas_ellipse from compas_rhino.conversions import line_to_rhino_curve @@ -66,7 +67,7 @@ def data(self): "curve_type": curve_type, "curve": curve.data, "start_vertex": self._edge.StartVertex.VertexIndex, - "end_vertex": self._edge.EndVertex.VertexIndex + "end_vertex": self._edge.EndVertex.VertexIndex, } @data.setter @@ -142,11 +143,11 @@ def _get_curve_geometry(self): # curve = curve_to_compas_ellipse(curve) else: type_ = "nurbs" - curve = self._curve - return type_, curve + curve = self._curve + return type_, curve @staticmethod - def _create_curve_from_data(curve_type, curve_data): + def _create_curve_from_data(curve_type, curve_data): if curve_type == "line": return line_to_rhino_curve(Line.from_data(curve_data)) elif curve_type == "circle": diff --git a/src/compas_rhino/geometry/brep/face.py b/src/compas_rhino/geometry/brep/face.py index b66ddc748c96..63e7cefce5f3 100644 --- a/src/compas_rhino/geometry/brep/face.py +++ b/src/compas_rhino/geometry/brep/face.py @@ -54,10 +54,10 @@ def _set_face(self, native_face): def data(self): surface_type, surface, uv_domain = self._get_surface_geometry(self._face.UnderlyingSurface()) return { - "surface_type": surface_type, - "surface": surface.data, - "uv_domain": uv_domain, - "loops": [l.data for l in self._loops] + "surface_type": surface_type, + "surface": surface.data, + "uv_domain": uv_domain, + "loops": [loop.data for loop in self._loops], } @data.setter @@ -91,7 +91,7 @@ def from_data(cls, data, builder): obj = cls(builder=builder) obj.data = data return obj - + # ============================================================================== # Properties # ============================================================================== @@ -151,5 +151,5 @@ def _make_surface_from_data(surface_type, surface_data, uv_domain): elif surface_type == "torus": raise NotImplementedError("Support for torus surface is not yet implemented!") surface.rhino_surface.SetDomain(0, Interval(*uv_domain[0])) - surface.rhino_surface.SetDomain(1, Interval(*uv_domain[1])) + surface.rhino_surface.SetDomain(1, Interval(*uv_domain[1])) return surface diff --git a/src/compas_rhino/geometry/brep/loop.py b/src/compas_rhino/geometry/brep/loop.py index 59cd053a37d5..6c3ff5a30386 100644 --- a/src/compas_rhino/geometry/brep/loop.py +++ b/src/compas_rhino/geometry/brep/loop.py @@ -57,7 +57,6 @@ def _set_loop(self, native_loop): self._type = int(self._loop.LoopType) self._trims = [RhinoBrepTrim(trim) for trim in self._loop.Trims] - # ============================================================================== # Data # ============================================================================== @@ -68,7 +67,9 @@ def data(self): @data.setter def data(self, value): - self._type = Rhino.Geometry.BrepLoopType.Outer if value["type"] == "Outer" else Rhino.Geometry.BrepLoopType.Inner + self._type = ( + Rhino.Geometry.BrepLoopType.Outer if value["type"] == "Outer" else Rhino.Geometry.BrepLoopType.Inner + ) loop_builder = self._builder.add_loop(self._type) for trim_data in value["trims"]: RhinoBrepTrim.from_data(trim_data, loop_builder) diff --git a/src/compas_rhino/geometry/brep/trim.py b/src/compas_rhino/geometry/brep/trim.py index 2623237b3a97..442afd1157c1 100644 --- a/src/compas_rhino/geometry/brep/trim.py +++ b/src/compas_rhino/geometry/brep/trim.py @@ -18,6 +18,7 @@ class RhinoBrepTrim(BrepTrim): True if this trim is reversed from its associated edge curve and False otherwise. """ + def __init__(self, rhino_trim=None, builder=None): super(RhinoBrepTrim, self).__init__() @@ -33,7 +34,7 @@ def _set_trim(self, rhino_trim): self._trim = rhino_trim self._curve = RhinoNurbsCurve.from_rhino(rhino_trim.TrimCurve.ToNurbsCurve()) self._is_reversed = rhino_trim.IsReversed() - self._iso_type = int(rhino_trim.IsoStatus) + self._iso_type = int(rhino_trim.IsoStatus) @property def data(self): @@ -41,7 +42,7 @@ def data(self): "edge": self._trim.Edge.EdgeIndex, "curve": RhinoNurbsCurve.from_rhino(self._trim.TrimCurve.ToNurbsCurve()).data, "iso": str(self._trim.IsoStatus), - "is_reversed": "true" if self._trim.IsReversed() else "false" + "is_reversed": "true" if self._trim.IsReversed() else "false", } @data.setter diff --git a/src/compas_rhino/geometry/brep/vertex.py b/src/compas_rhino/geometry/brep/vertex.py index ab6f2dea446b..7ba92dcb2597 100644 --- a/src/compas_rhino/geometry/brep/vertex.py +++ b/src/compas_rhino/geometry/brep/vertex.py @@ -36,7 +36,7 @@ def data(self): } @data.setter - def data(self, data): + def data(self, data): self._point = Point.from_data(data["point"]) self._vertex = self._builder.add_vertex(self._point) From a1673b708ae78614dfad92aa6d8eebf90a28e6a3 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Tue, 6 Dec 2022 11:46:54 +0100 Subject: [PATCH 10/17] updated changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 611649f18514..e1be0aab28c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added conversion function `frame_to_rhino_plane` to `compas_rhino.conversions`. * Added `RhinoSuface.from_frame` to `compas_rhino.geometry`. +* Added representation for trims with `compas.geometry.BrepTrim`. ### Changed * Fixed strange point values in RhinoNurbsCurve caused by conversion `ControlPoint` to COMPAS instead of `ControlPoint.Location`. * Fixed flipped order of NURBS point count values when creating RhinoNurbsSurface from parameters. +* Changed serialization format and reconstruction procedure of `RhinoBrep`. ### Removed From 6cf43432ab13170991400744b818c42c9dbd4e59 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Tue, 6 Dec 2022 11:53:08 +0100 Subject: [PATCH 11/17] added trims property to Brep --- src/compas/geometry/brep/brep.py | 6 ++++++ src/compas_rhino/geometry/brep/brep.py | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/compas/geometry/brep/brep.py b/src/compas/geometry/brep/brep.py index 905ad57fc12e..1f2b58d1006b 100644 --- a/src/compas/geometry/brep/brep.py +++ b/src/compas/geometry/brep/brep.py @@ -127,6 +127,8 @@ class Brep(Geometry): The vertices of the Brep. edges : list[:class:`~compas.geometry.BrepEdge`], read-only The edges of the Brep. + trims : list[:class:`~compas.geometry.BrepTrim`], read-only + The trims of the Brep. loops : list[:class:`~compas.geometry.BrepLoop`], read-only The loops of the Brep. faces : list[:class:`~compas.geometry.BrepFace`], read-only @@ -314,6 +316,10 @@ def vertices(self): def edges(self): raise NotImplementedError + @property + def trims(self): + raise NotImplementedError + @property def loops(self): raise NotImplementedError diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index c2176eaeddfb..11a042e39bc7 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -35,6 +35,8 @@ class RhinoBrep(Brep): The list of vertex geometries as points in 3D space. edges : list[:class:`~compas_rhino.geometry.RhinoBrepEdge`], read-only The list of edges which comprise this brep. + trims : list[:class:`~compas_rhino.geometry.RhinoBrepTrim`], read-only + The list of trims which comprise this brep. loops : list[:class:`~compas_rhino.geometry.RhinoBrepLoop`], read-only The list of loops which comprise this brep. faces : list[:class:`~compas_rhino.geometry.RhinoBrepFace`], read-only @@ -106,7 +108,12 @@ def points(self): @property def edges(self): if self._brep: - return [RhinoBrepEdge(trim) for trim in self._brep.Edges] + return [RhinoBrepEdge(edge) for edge in self._brep.Edges] + + @property + def trims(self): + if self._brep: + return [RhinoBrepEdge(trim) for trim in self._brep.Trims] @property def loops(self): From d8aa744d03ea6ae17adb90f3ffbd0c772c070129 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Tue, 6 Dec 2022 19:12:47 +0100 Subject: [PATCH 12/17] capitalized "enum" members --- src/compas/geometry/brep/trim.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compas/geometry/brep/trim.py b/src/compas/geometry/brep/trim.py index 24321cb69c5f..d2ef2de5ddee 100644 --- a/src/compas/geometry/brep/trim.py +++ b/src/compas/geometry/brep/trim.py @@ -7,10 +7,10 @@ class BrepTrimIsoStatus(object): NONE = 0 X = 1 Y = 2 - West = 3 - South = 4 - East = 5 - North = 6 + WEST = 3 + SOUTH = 4 + EAST = 5 + NORTH = 6 class BrepTrim(Data): @@ -20,7 +20,7 @@ class BrepTrim(Data): ---------- curve : :class:`~compas.geometry.NurbsCurve`, read_only Returns the geometry for this trim as a 2d curve. - iso_status : literal(NONE|X|Y|West|South|East|North) + iso_status : literal(NONE|X|Y|WEST|SOUTH|EAST|NORTH) The isoparametric curve direction on the surface. is_reversed : bool True if this trim is reversed from its associated edge curve and False otherwise. From 63fb80c96769d473616270389136a4c89291ee8b Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Tue, 6 Dec 2022 19:20:37 +0100 Subject: [PATCH 13/17] renamed builder attributes to make more sense --- src/compas_rhino/geometry/brep/builder.py | 64 ++++++++++++++--------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/src/compas_rhino/geometry/brep/builder.py b/src/compas_rhino/geometry/brep/builder.py index 16ada81f96d0..d4d2e26c7ff2 100644 --- a/src/compas_rhino/geometry/brep/builder.py +++ b/src/compas_rhino/geometry/brep/builder.py @@ -11,16 +11,23 @@ class RhinoLoopBuilder(object): """Builds a Brep loop. + Parameters + ========== + loop : :rhino:`Rhino.Geometry.BrepLoop` + The loop currently being constructed. + brep : :rhino:`Rhino.Geometry.Brep` + The parent brep object. + Attributes - ---------- + ========== result : :rhino: Rhino.Geometry.BrepTrim The created loop. """ - def __init__(self, loop=None, instance=None): - self.loop = loop - self.instance = instance + def __init__(self, loop=None, brep=None): + self._loop = loop + self._brep = brep def add_trim(self, curve, edge_index, is_reversed, iso_status): """Add trim to the new Brep. @@ -42,16 +49,16 @@ def add_trim(self, curve, edge_index, is_reversed, iso_status): The newly added BrepTrim instance. """ - c_index = self.instance.AddTrimCurve(curve) - edge = self.instance.Edges[edge_index] - trim = self.instance.Trims.Add(edge, is_reversed, self.loop, c_index) + c_index = self._brep.AddTrimCurve(curve) + edge = self._brep.Edges[edge_index] + trim = self._brep.Trims.Add(edge, is_reversed, self._loop, c_index) trim.IsoStatus = iso_status trim.SetTolerances(TOLERANCE, TOLERANCE) return trim @property def result(self): - return self.loop + return self._loop class RhinoFaceBuilder(object): @@ -59,6 +66,13 @@ class RhinoFaceBuilder(object): Serves as context for reconstructing the loop elements associated with this face. + Parameters + ========== + face : :rhino:`Rhino.Geometry.BrepFace` + The face currently being constructed. + brep : :rhino:`Rhino.Geometry.Brep` + The parent brep. + Attributes ========== result : :rhino:`Rhino.Geometry.BrepFace` @@ -66,13 +80,13 @@ class RhinoFaceBuilder(object): """ - def __init__(self, face=None, instance=None): - self.face = face - self.instance = instance + def __init__(self, face=None, brep=None): + self._face = face + self._brep = brep @property def result(self): - return self.face + return self._face def add_loop(self, loop_type): """Add a new loop to this face. @@ -89,8 +103,8 @@ def add_loop(self, loop_type): :class:`compas_rhino.geometry.RhinoLoopBuilder` """ - loop = self.instance.Loops.Add(loop_type, self.face) - return RhinoLoopBuilder(loop, self.instance) + loop = self._brep.Loops.Add(loop_type, self._face) + return RhinoLoopBuilder(loop, self._brep) class RhinoBrepBuilder(object): @@ -104,14 +118,14 @@ class RhinoBrepBuilder(object): """ def __init__(self): - self._instance = Rhino.Geometry.Brep() + self._brep = Rhino.Geometry.Brep() @property def result(self): - is_valid, log = self._instance.IsValidWithLog() + is_valid, log = self._brep.IsValidWithLog() if not is_valid: raise BrepInvalidError("Brep reconstruction failed!\n{}".format(log)) - return self._instance + return self._brep def add_vertex(self, point): """Add vertext to a new Brep @@ -123,7 +137,7 @@ def add_vertex(self, point): :rhino:`Rhino.Geometry.BrepVertex` """ - return self._instance.Vertices.Add(point_to_rhino(point), TOLERANCE) + return self._brep.Vertices.Add(point_to_rhino(point), TOLERANCE) def add_edge(self, edge_curve, start_vertex, end_vertex): """Add edge to the new Brep @@ -139,10 +153,10 @@ def add_edge(self, edge_curve, start_vertex, end_vertex): :rhino:`Rhino.Geometry.BrepEdge` """ - curve_index = self._instance.AddEdgeCurve(edge_curve) - s_vertex = self._instance.Vertices[start_vertex] - e_vertex = self._instance.Vertices[end_vertex] - return self._instance.Edges.Add(s_vertex, e_vertex, curve_index, TOLERANCE) + curve_index = self._brep.AddEdgeCurve(edge_curve) + s_vertex = self._brep.Vertices[start_vertex] + e_vertex = self._brep.Vertices[end_vertex] + return self._brep.Edges.Add(s_vertex, e_vertex, curve_index, TOLERANCE) def add_face(self, surface): """Creates and adds a new face to the brep. @@ -159,6 +173,6 @@ def add_face(self, surface): :class:`compas_rhino.geometry.RhinoFaceBuilder` """ - surface_index = self._instance.AddSurface(surface.rhino_surface) - face = self._instance.Faces.Add(surface_index) - return RhinoFaceBuilder(face=face, instance=self._instance) + surface_index = self._brep.AddSurface(surface.rhino_surface) + face = self._brep.Faces.Add(surface_index) + return RhinoFaceBuilder(face=face, brep=self._brep) From 1798393afa569ab45148d8dc851c6e3ae6b9dead Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 7 Dec 2022 17:12:52 +0100 Subject: [PATCH 14/17] fixed typo in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1be0aab28c0..42d3039af424 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added * Added conversion function `frame_to_rhino_plane` to `compas_rhino.conversions`. -* Added `RhinoSuface.from_frame` to `compas_rhino.geometry`. +* Added `RhinoSurface.from_frame` to `compas_rhino.geometry`. * Added representation for trims with `compas.geometry.BrepTrim`. ### Changed From b48b9f17aeb06ca1050a80d211a0c39f111fc4cc Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 7 Dec 2022 17:19:09 +0100 Subject: [PATCH 15/17] simplified call to RhinoNurbsSurface.from_frame --- src/compas_rhino/geometry/brep/face.py | 7 ++++--- src/compas_rhino/geometry/surfaces/surface.py | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/compas_rhino/geometry/brep/face.py b/src/compas_rhino/geometry/brep/face.py index 63e7cefce5f3..7034980bd45c 100644 --- a/src/compas_rhino/geometry/brep/face.py +++ b/src/compas_rhino/geometry/brep/face.py @@ -139,9 +139,10 @@ def _get_surface_geometry(surface): @staticmethod def _make_surface_from_data(surface_type, surface_data, uv_domain): + u_domain, v_domain = uv_domain if surface_type == "plane": frame = Frame.from_data(surface_data) - surface = RhinoNurbsSurface.from_frame(frame, uv_domain[0], uv_domain[1], (1, 1), (2, 2)) + surface = RhinoNurbsSurface.from_frame(frame, u_domain, v_domain) elif surface_type == "sphere": surface = RhinoNurbsSurface.from_sphere(Sphere.from_data(surface_data)) elif surface_type == "cylinder": @@ -150,6 +151,6 @@ def _make_surface_from_data(surface_type, surface_data, uv_domain): surface = RhinoNurbsSurface.from_data(surface_data) elif surface_type == "torus": raise NotImplementedError("Support for torus surface is not yet implemented!") - surface.rhino_surface.SetDomain(0, Interval(*uv_domain[0])) - surface.rhino_surface.SetDomain(1, Interval(*uv_domain[1])) + surface.rhino_surface.SetDomain(0, Interval(*u_domain)) + surface.rhino_surface.SetDomain(1, Interval(*v_domain)) return surface diff --git a/src/compas_rhino/geometry/surfaces/surface.py b/src/compas_rhino/geometry/surfaces/surface.py index 8c5fe0e161ef..25544e748e06 100644 --- a/src/compas_rhino/geometry/surfaces/surface.py +++ b/src/compas_rhino/geometry/surfaces/surface.py @@ -189,7 +189,7 @@ def from_plane(cls, plane, box): return cls.from_rhino(rhino_surface) @classmethod - def from_frame(cls, frame, u_interval, v_interval, uv_degrees, uv_point_counts): + def from_frame(cls, frame, u_interval, v_interval, uv_degrees=(1, 1), uv_point_counts=(2, 2)): """Creates a NURBS surface from a frame and parametric domain information. Parameters @@ -202,9 +202,9 @@ def from_frame(cls, frame, u_interval, v_interval, uv_degrees, uv_point_counts): v_interval : tuple(float, float) The parametric domain of the V parameter. v_interval[0] => v_interval[1]. uv_degrees : tuple(int, int) - Degree of U and V parameters. uv_degree[0] => degree_u, uv_degree[1] => degree_v + Degree of U and V parameters. Default is 1 in both directions for a simple planar surface. uv_point_counts : - Number of control points in each parameter. [0] => cp_count_u, [1] => cp_count_v + Number of control points in each parameter. Default is 2 in both directions for a simple planar surface. Returns ------- From 8b11fbaab682da05bfc4fdbf43f1a98f4dd17217 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 7 Dec 2022 17:28:09 +0100 Subject: [PATCH 16/17] added missing type in docstring --- src/compas_rhino/geometry/surfaces/surface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compas_rhino/geometry/surfaces/surface.py b/src/compas_rhino/geometry/surfaces/surface.py index 25544e748e06..148bd6a50da2 100644 --- a/src/compas_rhino/geometry/surfaces/surface.py +++ b/src/compas_rhino/geometry/surfaces/surface.py @@ -203,7 +203,7 @@ def from_frame(cls, frame, u_interval, v_interval, uv_degrees=(1, 1), uv_point_c The parametric domain of the V parameter. v_interval[0] => v_interval[1]. uv_degrees : tuple(int, int) Degree of U and V parameters. Default is 1 in both directions for a simple planar surface. - uv_point_counts : + uv_point_counts : tuple(int, int) Number of control points in each parameter. Default is 2 in both directions for a simple planar surface. Returns From dcc89c56c00db7513bb6bce793d768ab996c42aa Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 7 Dec 2022 17:44:36 +0100 Subject: [PATCH 17/17] changed signature of RhionNurbsSurface.from_frame --- src/compas_rhino/geometry/surfaces/surface.py | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/compas_rhino/geometry/surfaces/surface.py b/src/compas_rhino/geometry/surfaces/surface.py index 148bd6a50da2..07f03a44f1ac 100644 --- a/src/compas_rhino/geometry/surfaces/surface.py +++ b/src/compas_rhino/geometry/surfaces/surface.py @@ -189,7 +189,7 @@ def from_plane(cls, plane, box): return cls.from_rhino(rhino_surface) @classmethod - def from_frame(cls, frame, u_interval, v_interval, uv_degrees=(1, 1), uv_point_counts=(2, 2)): + def from_frame(cls, frame, u_interval, v_interval, u_degree=1, v_degree=1, u_point_count=2, v_point_count=2): """Creates a NURBS surface from a frame and parametric domain information. Parameters @@ -201,10 +201,14 @@ def from_frame(cls, frame, u_interval, v_interval, uv_degrees=(1, 1), uv_point_c The parametric domain of the U parameter. u_interval[0] => u_interval[1]. v_interval : tuple(float, float) The parametric domain of the V parameter. v_interval[0] => v_interval[1]. - uv_degrees : tuple(int, int) - Degree of U and V parameters. Default is 1 in both directions for a simple planar surface. - uv_point_counts : tuple(int, int) - Number of control points in each parameter. Default is 2 in both directions for a simple planar surface. + u_degree : int + Degree of the U parameter. Default is 1 in both directions for a simple planar surface. + v_degree : int + Degree of the V parameter. Default is 1 in both directions for a simple planar surface. + u_point_count : int + Number of control points in the U direction. Default is 2 in both directions for a simple planar surface. + v_point_count : int + Number of control points in the V direction. Default is 2 in both directions for a simple planar surface. Returns ------- @@ -220,16 +224,16 @@ def from_frame(cls, frame, u_interval, v_interval, uv_degrees=(1, 1), uv_point_c rhino_plane, Rhino.Geometry.Interval(*u_interval), Rhino.Geometry.Interval(*v_interval), - uv_degrees[0], - uv_degrees[1], - uv_point_counts[0], - uv_point_counts[1], + v_degree, + u_degree, + v_point_count, + u_point_count, ) if not surface: + msg = "Failed creating NurbsSurface from " + msg += "frame:{} u_interval:{} v_interval:{} u_degree:{} v_degree:{} u_point_count:{} v_point_count:{}" raise ValueError( - "Failed creating NurbsSurface from frame:{} u_interval:{} v_interval:{} uv_degrees:{} uv_point_count:{}".format( - frame, u_interval, v_interval, uv_degrees, uv_point_counts - ) + msg.format(frame, u_interval, v_interval, u_degree, v_degree, u_point_count, v_point_count) ) return cls.from_rhino(surface)