From a3ab8fb6c144a68172907b0f0f38084caf7b9b91 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Thu, 10 Apr 2025 19:30:10 +0200 Subject: [PATCH 01/19] added missing properties to RhinoBrep --- CHANGELOG.md | 10 +++ src/compas_rhino/geometry/brep/brep.py | 113 +++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f34aa0fb038..21804d3f2159 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added `compas.geometry.angle_vectors_projected`. * Added `compas.geometry.Brep.from_curves`. * Added `compas_rhino.geometry.RhinoBrep.from_curves`. +* Added missing property `centroid` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `curves` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `is_closed` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `is_compound` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `is_compoundsolid` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `is_orientable` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `is_surface` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `is_valid` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `orientation` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `surfaces` in `compas_rhino.geometry.RhinoBrep`. ### Changed diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index 0e8697b4a0c0..4b39741a055d 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -27,6 +27,8 @@ from compas_rhino.conversions import sphere_to_rhino from compas_rhino.conversions import transformation_to_rhino from compas_rhino.conversions import vector_to_rhino +from compas_rhino.geometry import RhinoNurbsCurve +from compas_rhino.geometry import RhinoNurbsSurface from .builder import _RhinoBrepBuilder from .edge import RhinoBrepEdge @@ -62,6 +64,36 @@ class RhinoBrep(Brep): The calculated area of this brep. volume : float, read-only The calculated volume of this brep. + centroid : :class:`compas.geometry.Point`, read-only + The calculated centroid of this brep. + curves : list[:class:`compas_rhino.geometry.RhinoNurbsCurve`], read-only + The list of curves which comprise this brep. + is_closed : bool, read-only + True if this brep is closed, False otherwise. + is_compound : bool, read-only + True if this brep is compound, False otherwise. + is_compoundsolid : bool, read-only + True if this brep is compound solid, False otherwise. + is_convex : bool, read-only + True if this brep is convex, False otherwise. + is_infinite : bool, read-only + True if this brep is infinite, False otherwise. + is_orientable : bool, read-only + True if this brep is orientable, False otherwise. + is_shell : bool, read-only + True if this brep is a shell, False otherwise. + is_surface : bool, read-only + True if this brep is a surface, False otherwise. + is_valid : bool, read-only + True if this brep is valid, False otherwise. + orientation : literal(:class:`~compas.geometry.BrepOrientation`), read-only + The orientation of this brep. One of: FORWARD, REVERSED, INTERNAL, EXTERNAL. + shells : list[:class:`compas_rhino.geometry.RhinoBrep`], read-only + The list of shells which comprise this brep. + solids : list[:class:`compas_rhino.geometry.RhinoBrep`], read-only + The list of solids which comprise this brep. + surfaces : list[:class:`compas_rhino.geometry.RhinoNurbsSurface`], read-only + The list of surfaces which comprise this brep. """ @@ -183,6 +215,87 @@ def volume(self): if self._brep: return self._brep.GetVolume() + @property + def centroid(self): + assert self._brep + centroid = Rhino.Geometry.AreaMassProperties.Compute(self._brep).Centroid + return Point(*centroid) + + @property + def curves(self): + assert self._brep + return [RhinoNurbsCurve.from_native(c.ToNurbsCurve()) for c in self._brep.Curves3D] + + @property + def is_closed(self): + assert self._brep + return self._brep.IsSolid + + @property + def is_compound(self): + # TODO: clarify. according to the internets compound brep is actually a container for several breps, not sure that's possible with a Rhino Brep. + return False + + @property + def is_compoundsolid(self): + # TODO: see above + return False + + @property + def is_convex(self): + raise NotImplementedError("Convexity check is not implemented for Rhino Breps.") + + @property + def is_infinite(self): + pass + + @property + def is_orientable(self): + assert self._brep + return self._brep.SolidOrientation in (Rhino.Geometry.BrepSolidOrientation.Inward, Rhino.Geometry.BrepSolidOrientation.Outward) + + @property + def is_shell(self): + # not sure how to get this one + raise NotImplementedError + + @property + def is_surface(self): + assert self._brep + return self._brep.IsSurface + + @property + def is_valid(self): + assert self._brep + return self.IsValid + + @property + def orientation(self): + assert self._brep + # TODO: align this with compas.geometry.BrepOrientation + return self._brep.SolidOrientation + + @property + def shells(self): + # TODO: can create shell from brep but have to specify which faces to eliminate in order to hollow out the brep, doesn't seem like the intention. + # TODO: is this about traversing a compound brep? + raise NotImplementedError("Shells are not implemented for Rhino Breps.") + + @property + def solids(self): + # TODO: same as above + raise NotImplementedError("Solids are not implemented for Rhino Breps.") + + @property + def surfaces(self): + assert self._brep + return [[RhinoNurbsSurface.from_native(s.ToNurbsSurface()) for s in self._brep.Surfaces]] + + @property + def type(self): + # TODO: seems like an OCC specific thinh, rename to occ_type and remove from interface? + raise NotImplementedError("Type is not implemented for Rhino Breps.") + # ============================================================================== # Constructors # ============================================================================== From ebdcb280f7db693f5a844b51298f73bb27fb0202 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 16 Apr 2025 11:38:30 +0200 Subject: [PATCH 02/19] implemented RhinoBrep.from_sweep --- CHANGELOG.md | 1 + src/compas/geometry/brep/brep.py | 4 +- src/compas_rhino/geometry/brep/__init__.py | 5 +++ src/compas_rhino/geometry/brep/brep.py | 47 ++++++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21804d3f2159..b4cfeefcb6a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added missing property `is_valid` in `compas_rhino.geometry.RhinoBrep`. * Added missing property `orientation` in `compas_rhino.geometry.RhinoBrep`. * Added missing property `surfaces` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_sweep` in `compas_rhino.geometry.RhinoBrep`. ### Changed diff --git a/src/compas/geometry/brep/brep.py b/src/compas/geometry/brep/brep.py index ffa07063f448..a82d47249479 100644 --- a/src/compas/geometry/brep/brep.py +++ b/src/compas/geometry/brep/brep.py @@ -569,7 +569,7 @@ def from_step(cls, filename): return from_step(filename) @classmethod - def from_sweep(cls, profile, path): + def from_sweep(cls, profile, path, *args, **kwargs): """Construct a BRep by sweeping a profile along a path. Parameters @@ -584,7 +584,7 @@ def from_sweep(cls, profile, path): :class:`compas.geometry.Brep` """ - return from_sweep(profile, path) + return from_sweep(profile, path, *args, **kwargs) @classmethod def from_torus(cls, torus): diff --git a/src/compas_rhino/geometry/brep/__init__.py b/src/compas_rhino/geometry/brep/__init__.py index 58dfd984b870..bdda1dd71ee7 100644 --- a/src/compas_rhino/geometry/brep/__init__.py +++ b/src/compas_rhino/geometry/brep/__init__.py @@ -65,6 +65,11 @@ def from_step(*args, **kwargs): return RhinoBrep.from_step(*args, **kwargs) +@plugin(category="factories", requires=["Rhino"]) +def from_sweep(*args, **kwargs): + return RhinoBrep.from_sweep(*args, **kwargs) + + @plugin(category="factories", requires=["Rhino"]) def new_brep(*args, **kwargs): return object.__new__(RhinoBrep) diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index 4b39741a055d..94baa1de192d 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -11,6 +11,7 @@ from compas.geometry import BrepFilletError from compas.geometry import BrepTrimmingError from compas.geometry import Frame +from compas.geometry import Line from compas.geometry import Plane from compas.geometry import Point from compas.geometry import Polyline @@ -19,6 +20,7 @@ from compas_rhino.conversions import curve_to_compas from compas_rhino.conversions import curve_to_rhino from compas_rhino.conversions import cylinder_to_rhino +from compas_rhino.conversions import line_to_rhino_curve from compas_rhino.conversions import mesh_to_compas from compas_rhino.conversions import mesh_to_rhino from compas_rhino.conversions import plane_to_rhino @@ -578,6 +580,51 @@ def from_step(cls, filepath): compas_rhino.objects.delete_object(guid) return cls.from_native(geometry) + @classmethod + def from_sweep(cls, profile, path, is_closed=False, tolerance=None): + """Construct one or more RhinoBrep(s) from a sweep operation. + + Parameters + ---------- + profile : :class:`compas.geometry.Curve` + Curve describing the cross-section of the surface created by the sweep operation. + path : :class:`compas.geometry.Curve` + Curve describing the edge of the sweep surface. The profile curve is sweeped along this curve. + is_closed : bool, optional + If True, the resulting surface will be closed, if possible. Defaults to False. + tolerance : float, optional + The precision to use for the operation. Defaults to `TOL.absolute`. + + Returns + ------- + list of :class:`compas_rhino.geometry.RhinoBrep` + + """ + tolerance = tolerance or TOL.absolute + if hasattr(profile, "native_curve"): + profile = curve_to_rhino(profile) + elif isinstance(profile, Polyline): + profile = polyline_to_rhino_curve(profile) + elif isinstance(profile, Line): + profile = line_to_rhino_curve(profile) + else: + raise TypeError("Unsupported profile type: {}".format(type(profile))) + + if hasattr(path, "native_curve"): + path = curve_to_rhino(path) + elif isinstance(path, Polyline): + path = polyline_to_rhino_curve(path) + elif isinstance(path, Line): + path = line_to_rhino_curve(path) + else: + raise TypeError("Unsupported path type: {}".format(type(path))) + + results = Rhino.Geometry.Brep.CreateFromSweep(path, profile, is_closed, tolerance) + if not results: + raise BrepError("Sweep operation ended with no result") + + return [cls.from_native(result) for result in results] + # ============================================================================== # Conversions # ============================================================================== From 13c76c7d6d31e559e82d206a934fde31af9e1d02 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 16 Apr 2025 13:33:25 +0200 Subject: [PATCH 03/19] added RhinoBrep.from_cone --- CHANGELOG.md | 1 + src/compas/geometry/brep/brep.py | 4 ++-- src/compas_rhino/geometry/brep/__init__.py | 5 +++++ src/compas_rhino/geometry/brep/brep.py | 18 ++++++++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4cfeefcb6a9..46553d581008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added missing property `orientation` in `compas_rhino.geometry.RhinoBrep`. * Added missing property `surfaces` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_sweep` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_cone` in `compas_rhino.geometry.RhinoBrep`. ### Changed diff --git a/src/compas/geometry/brep/brep.py b/src/compas/geometry/brep/brep.py index a82d47249479..d209ba7e2067 100644 --- a/src/compas/geometry/brep/brep.py +++ b/src/compas/geometry/brep/brep.py @@ -342,7 +342,7 @@ def from_breps(cls, breps): raise NotImplementedError @classmethod - def from_cone(cls, cone): + def from_cone(cls, cone, *args, **kwargs): """Construct a Brep from a COMPAS cone. Parameters @@ -354,7 +354,7 @@ def from_cone(cls, cone): :class:`compas.geometry.Brep` """ - return from_cone(cone) + return from_cone(cone, *args, **kwargs) @classmethod def from_curves(cls, curves): diff --git a/src/compas_rhino/geometry/brep/__init__.py b/src/compas_rhino/geometry/brep/__init__.py index bdda1dd71ee7..426f7f94f858 100644 --- a/src/compas_rhino/geometry/brep/__init__.py +++ b/src/compas_rhino/geometry/brep/__init__.py @@ -25,6 +25,11 @@ def from_box(*args, **kwargs): return RhinoBrep.from_box(*args, **kwargs) +@plugin(category="factories", requires=["Rhino"]) +def from_cone(*args, **kwargs): + return RhinoBrep.from_cone(*args, **kwargs) + + @plugin(category="factories", requires=["Rhino"]) def from_cylinder(*args, **kwargs): return RhinoBrep.from_cylinder(*args, **kwargs) diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index 94baa1de192d..d6f45d968eb6 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -17,6 +17,7 @@ from compas.geometry import Polyline from compas.tolerance import TOL from compas_rhino.conversions import box_to_rhino +from compas_rhino.conversions import cone_to_rhino from compas_rhino.conversions import curve_to_compas from compas_rhino.conversions import curve_to_rhino from compas_rhino.conversions import cylinder_to_rhino @@ -400,6 +401,23 @@ def from_box(cls, box): rhino_box = box_to_rhino(box) return cls.from_native(rhino_box.ToBrep()) + @classmethod + def from_cone(cls, cone, cap_bottom=True): + """Create a RhinoBrep from a cone. + + Parameters + ---------- + cone : :class:`compas.geometry.Cone` + The cone geometry of the brep. + + Returns + ------- + :class:`compas_rhino.geometry.RhinoBrep` + + """ + rhino_cone = cone_to_rhino(cone) + return cls.from_native(rhino_cone.ToBrep(cap_bottom)) + @classmethod def from_cylinder(cls, cylinder): """Create a RhinoBrep from a box. From 1c1a21455fc37aa69d8e8894a124e62269efb3ad Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 16 Apr 2025 14:11:47 +0200 Subject: [PATCH 04/19] added RhinoBrep.from_plane --- CHANGELOG.md | 1 + src/compas_rhino/geometry/brep/__init__.py | 5 ++++ src/compas_rhino/geometry/brep/brep.py | 33 ++++++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46553d581008..100a53883fad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added missing property `surfaces` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_sweep` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_cone` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_plane` in `compas_rhino.geometry.RhinoBrep`. ### Changed diff --git a/src/compas_rhino/geometry/brep/__init__.py b/src/compas_rhino/geometry/brep/__init__.py index 426f7f94f858..420b36b53407 100644 --- a/src/compas_rhino/geometry/brep/__init__.py +++ b/src/compas_rhino/geometry/brep/__init__.py @@ -60,6 +60,11 @@ def from_native(*args, **kwargs): return RhinoBrep.from_native(*args, **kwargs) +@plugin(category="factories", requires=["Rhino"]) +def from_plane(*args, **kwargs): + return RhinoBrep.from_plane(*args, **kwargs) + + @plugin(category="factories", requires=["Rhino"]) def from_sphere(*args, **kwargs): return RhinoBrep.from_sphere(*args, **kwargs) diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index d6f45d968eb6..45b35f3020ca 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -21,6 +21,7 @@ from compas_rhino.conversions import curve_to_compas from compas_rhino.conversions import curve_to_rhino from compas_rhino.conversions import cylinder_to_rhino +from compas_rhino.conversions import frame_to_rhino_plane from compas_rhino.conversions import line_to_rhino_curve from compas_rhino.conversions import mesh_to_compas from compas_rhino.conversions import mesh_to_rhino @@ -560,6 +561,38 @@ def from_native(cls, rhino_brep): brep._brep = rhino_brep return brep + @classmethod + def from_plane(cls, plane, domain_u=(-1, +1), domain_v=(-1, +1)): + """Create a RhinoBrep from a plane. + + Parameters + ---------- + plane : :class:`compas.geometry.Plane` or :class:`compas.geometry.Frame` + The source plane. + domain_u : tuple of float, optional + The U domain of the plane. Defaults to (-1, +1). + domain_v : tuple of float, optional + The V domain of the plane. Defaults to (-1, +1). + + Notes + ----- + When using a Rhino Plane, to maintain the original orientation data + use :meth:`~compas_rhino.conversions.plane_to_compas_frame` and :meth:`~compas_rhino.conversions.frame_to_rhino_plane`. + + Returns + ------- + :class:`compas_rhino.geometry.RhinoBrep` + + """ + if isinstance(plane, Frame): + rhino_plane = frame_to_rhino_plane(plane) + else: + rhino_plane = plane_to_rhino(plane) + u = Rhino.Geometry.Interval(domain_u[0], domain_u[1]) + v = Rhino.Geometry.Interval(domain_v[0], domain_v[1]) + surface = Rhino.Geometry.PlaneSurface(rhino_plane, u, v) + return cls.from_native(surface.ToBrep()) + @classmethod def from_sphere(cls, sphere): """Create a RhinoBrep from a sphere. From 7cb6b8ac55735ba9138a39cf10c8a1b0699a5b1c Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 16 Apr 2025 14:47:10 +0200 Subject: [PATCH 05/19] added RhinoBrep.from_brepfaces --- CHANGELOG.md | 1 + src/compas_rhino/geometry/brep/__init__.py | 5 +++++ src/compas_rhino/geometry/brep/brep.py | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 100a53883fad..3cfc550d26b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added implementation for `Brep.from_sweep` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_cone` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_plane` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_brepfaces` in `compas_rhino.geometry.RhinoBrep`. ### Changed diff --git a/src/compas_rhino/geometry/brep/__init__.py b/src/compas_rhino/geometry/brep/__init__.py index 420b36b53407..1e79523ec692 100644 --- a/src/compas_rhino/geometry/brep/__init__.py +++ b/src/compas_rhino/geometry/brep/__init__.py @@ -25,6 +25,11 @@ def from_box(*args, **kwargs): return RhinoBrep.from_box(*args, **kwargs) +@plugin(category="factories", requires=["Rhino"]) +def from_brepfaces(*args, **kwargs): + return RhinoBrep.from_brepfaces(*args, **kwargs) + + @plugin(category="factories", requires=["Rhino"]) def from_cone(*args, **kwargs): return RhinoBrep.from_cone(*args, **kwargs) diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index 45b35f3020ca..97224b9f2613 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -402,6 +402,24 @@ def from_box(cls, box): rhino_box = box_to_rhino(box) return cls.from_native(rhino_box.ToBrep()) + @classmethod + def from_brepfaces(cls, faces): + """Create a Brep from a list of Brep faces forming an open or closed shell. + + Parameters + ---------- + faces : list[:class:`compas.geometry.BrepFace`] + + Returns + ------- + :class:`compas.geometry.Brep` + + """ + brep = Rhino.Geometry.Brep() + for face in faces: + brep.Faces.Add(face.native_face.UnderlyingSurface()) + return cls.from_native(brep) + @classmethod def from_cone(cls, cone, cap_bottom=True): """Create a RhinoBrep from a cone. From 242fe6e307e7452542a272b1b520838ae1935662 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 16 Apr 2025 15:04:10 +0200 Subject: [PATCH 06/19] added RhinoBrep.from_breps --- CHANGELOG.md | 1 + src/compas/geometry/brep/__init__.py | 5 +++++ src/compas/geometry/brep/brep.py | 9 +++++---- src/compas_rhino/geometry/brep/__init__.py | 5 +++++ src/compas_rhino/geometry/brep/brep.py | 18 ++++++++++++++++++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cfc550d26b2..b356f6f81125 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added implementation for `Brep.from_cone` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_plane` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_brepfaces` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_breps` in `compas_rhino.geometry.RhinoBrep`. ### Changed diff --git a/src/compas/geometry/brep/__init__.py b/src/compas/geometry/brep/__init__.py index 5abec46e466f..0a5d25b52c50 100644 --- a/src/compas/geometry/brep/__init__.py +++ b/src/compas/geometry/brep/__init__.py @@ -27,6 +27,11 @@ def from_brepfaces(*args, **kwargs): raise PluginNotInstalledError +@pluggable(category="factories") +def from_breps(*args, **kwargs): + raise PluginNotInstalledError + + @pluggable(category="factories") def from_cone(*args, **kwargs): raise PluginNotInstalledError diff --git a/src/compas/geometry/brep/brep.py b/src/compas/geometry/brep/brep.py index d209ba7e2067..7553cfc8dd2d 100644 --- a/src/compas/geometry/brep/brep.py +++ b/src/compas/geometry/brep/brep.py @@ -5,6 +5,7 @@ from . import from_boolean_union from . import from_box from . import from_brepfaces +from . import from_breps from . import from_cone from . import from_curves from . import from_cylinder @@ -327,19 +328,19 @@ def from_brepfaces(cls, faces): return from_brepfaces(faces) @classmethod - def from_breps(cls, breps): + def from_breps(cls, breps, *args, **kwargs): """Construct one compound Brep from a list of other Breps. Parameters ---------- - breps : list[:class:`compas.geometry.Brep`] + breps : list of :class:`compas.geometry.Brep` Returns ------- - :class:`compas.geometry.Brep` + list of :class:`compas.geometry.Brep` """ - raise NotImplementedError + return from_breps(breps, *args, **kwargs) @classmethod def from_cone(cls, cone, *args, **kwargs): diff --git a/src/compas_rhino/geometry/brep/__init__.py b/src/compas_rhino/geometry/brep/__init__.py index 1e79523ec692..bd79156b22ed 100644 --- a/src/compas_rhino/geometry/brep/__init__.py +++ b/src/compas_rhino/geometry/brep/__init__.py @@ -30,6 +30,11 @@ def from_brepfaces(*args, **kwargs): return RhinoBrep.from_brepfaces(*args, **kwargs) +@plugin(category="factories", requires=["Rhino"]) +def from_breps(*args, **kwargs): + return RhinoBrep.from_breps(*args, **kwargs) + + @plugin(category="factories", requires=["Rhino"]) def from_cone(*args, **kwargs): return RhinoBrep.from_cone(*args, **kwargs) diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index 97224b9f2613..65b37b875654 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -420,6 +420,24 @@ def from_brepfaces(cls, faces): brep.Faces.Add(face.native_face.UnderlyingSurface()) return cls.from_native(brep) + @classmethod + def from_breps(cls, breps, tolerance=None): + """Joins the breps at any overlapping edges to form as few as possible resulting breps. There may be more than one brep in the result array. + + Parameters + ---------- + breps : list of :class:`compas.geometry.Brep` + + Returns + ------- + list of :class:`compas.geometry.Brep` + + """ + tolerance = tolerance or TOL + rhino_breps = [b.native_brep for b in breps] + resulting_breps = Rhino.Geometry.Brep.JoinBreps(rhino_breps, tolerance.absolute, tolerance.angular) + return [cls.from_native(brep) for brep in resulting_breps] + @classmethod def from_cone(cls, cone, cap_bottom=True): """Create a RhinoBrep from a cone. From df0dd94617ad2143b0c2e694acebc0af2154b8c6 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 16 Apr 2025 15:11:19 +0200 Subject: [PATCH 07/19] added RhinoBrep.from_torus --- CHANGELOG.md | 1 + src/compas_rhino/geometry/brep/__init__.py | 5 +++++ src/compas_rhino/geometry/brep/brep.py | 17 +++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b356f6f81125..b58805e454bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added implementation for `Brep.from_plane` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_brepfaces` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_breps` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_torus` in `compas_rhino.geometry.RhinoBrep`. ### Changed diff --git a/src/compas_rhino/geometry/brep/__init__.py b/src/compas_rhino/geometry/brep/__init__.py index bd79156b22ed..7296b3dce062 100644 --- a/src/compas_rhino/geometry/brep/__init__.py +++ b/src/compas_rhino/geometry/brep/__init__.py @@ -90,6 +90,11 @@ def from_sweep(*args, **kwargs): return RhinoBrep.from_sweep(*args, **kwargs) +@plugin(category="factories", requires=["Rhino"]) +def from_torus(*args, **kwargs): + return RhinoBrep.from_torus(*args, **kwargs) + + @plugin(category="factories", requires=["Rhino"]) def new_brep(*args, **kwargs): return object.__new__(RhinoBrep) diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index 65b37b875654..8c61faa03e6a 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -29,6 +29,7 @@ from compas_rhino.conversions import point_to_rhino from compas_rhino.conversions import polyline_to_rhino_curve from compas_rhino.conversions import sphere_to_rhino +from compas_rhino.conversions import torus_to_rhino from compas_rhino.conversions import transformation_to_rhino from compas_rhino.conversions import vector_to_rhino from compas_rhino.geometry import RhinoNurbsCurve @@ -712,6 +713,22 @@ def from_sweep(cls, profile, path, is_closed=False, tolerance=None): return [cls.from_native(result) for result in results] + @classmethod + def from_torus(cls, torus): + """Construct a RhinoBrep from a COMPAS torus. + + Parameters + ---------- + torus : :class:`compas.geometry.Torus` + + Returns + ------- + :class:`compas.geometry.BRep` + + """ + rhino_torus = torus_to_rhino(torus) + return cls.from_native(rhino_torus.ToBrep()) + # ============================================================================== # Conversions # ============================================================================== From 08e922df22c6f7408964e009d10664533fd925f6 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Thu, 17 Apr 2025 15:10:22 +0200 Subject: [PATCH 08/19] added RhinoBrep.from_polygons --- CHANGELOG.md | 1 + src/compas/geometry/brep/brep.py | 4 ++-- src/compas_rhino/geometry/brep/__init__.py | 5 ++++ src/compas_rhino/geometry/brep/brep.py | 28 +++++++++++++++++++--- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b58805e454bb..380bc8f2f522 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added implementation for `Brep.from_brepfaces` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_breps` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_torus` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_polygons` in `compas_rhino.geometry.RhinoBrep`. ### Changed diff --git a/src/compas/geometry/brep/brep.py b/src/compas/geometry/brep/brep.py index 7553cfc8dd2d..65eabaf15328 100644 --- a/src/compas/geometry/brep/brep.py +++ b/src/compas/geometry/brep/brep.py @@ -525,7 +525,7 @@ def from_planes(cls, planes): return from_planes(planes) @classmethod - def from_polygons(cls, polygons): + def from_polygons(cls, polygons, *args, **kwargs): """Construct a Brep from a set of polygons. Parameters @@ -537,7 +537,7 @@ def from_polygons(cls, polygons): :class:`compas.geometry.Brep` """ - return from_polygons(polygons) + return from_polygons(polygons, *args, **kwargs) @classmethod def from_sphere(cls, sphere): diff --git a/src/compas_rhino/geometry/brep/__init__.py b/src/compas_rhino/geometry/brep/__init__.py index 7296b3dce062..ad21279f3785 100644 --- a/src/compas_rhino/geometry/brep/__init__.py +++ b/src/compas_rhino/geometry/brep/__init__.py @@ -75,6 +75,11 @@ def from_plane(*args, **kwargs): return RhinoBrep.from_plane(*args, **kwargs) +@plugin(category="factories", requires=["Rhino"]) +def from_polygons(*args, **kwargs): + return RhinoBrep.from_polygons(*args, **kwargs) + + @plugin(category="factories", requires=["Rhino"]) def from_sphere(*args, **kwargs): return RhinoBrep.from_sphere(*args, **kwargs) diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index 8c61faa03e6a..66ff5629f7d7 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -474,7 +474,7 @@ def from_cylinder(cls, cylinder): return cls.from_native(rhino_cylinder.ToBrep(True, True)) @classmethod - def from_curves(cls, curves): + def from_curves(cls, curves, tolerance=None): """Create a RhinoBreps from a list of planar face boundary curves. Parameters @@ -487,6 +487,7 @@ def from_curves(cls, curves): list of :class:`~compas_rhino.geometry.RhinoBrep` """ + tolerance = tolerance or TOL.absolute if not isinstance(curves, list): curves = [curves] faces = [] @@ -495,13 +496,13 @@ def from_curves(cls, curves): rhino_curve = polyline_to_rhino_curve(curve) else: rhino_curve = curve_to_rhino(curve) - face = Rhino.Geometry.Brep.CreatePlanarBreps(rhino_curve, TOL.absolute) + face = Rhino.Geometry.Brep.CreatePlanarBreps(rhino_curve, tolerance) if face is None: raise BrepError("Failed to create face from curve: {} ".format(curve)) if len(face) > 1: raise BrepError("Failed to create single face from curve: {} ".format(curve)) faces.append(face[0]) - rhino_brep = Rhino.Geometry.Brep.JoinBreps(faces, TOL.absolute) + rhino_brep = Rhino.Geometry.Brep.JoinBreps(faces, tolerance) if rhino_brep is None: raise BrepError("Failed to create Brep from faces: {} ".format(faces)) return [cls.from_native(brep) for brep in rhino_brep] @@ -630,6 +631,27 @@ def from_plane(cls, plane, domain_u=(-1, +1), domain_v=(-1, +1)): surface = Rhino.Geometry.PlaneSurface(rhino_plane, u, v) return cls.from_native(surface.ToBrep()) + @classmethod + def from_polygons(cls, polygons, tolerance=None, *args, **kwargs): + """Create a RhinoBrep from a list of polygons. + + Parameters + ---------- + polygons : list of :class:`compas.geometry.Polygon` + The source polygons. + + Returns + ------- + list of :class:`compas_rhino.geometry.RhinoBrep` + + """ + tolerance = tolerance or TOL.absolute + polylines = [] + for polygon in polygons: + points = polygon.points + [polygon.points[0]] # make a closed polyline from the polygon + polylines.append(Polyline(points=[*points])) + return cls.from_curves(polylines, tolerance) + @classmethod def from_sphere(cls, sphere): """Create a RhinoBrep from a sphere. From f6097d756f05ac3fd0aa9326e426fcb1c25ab4c3 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Thu, 17 Apr 2025 16:04:20 +0200 Subject: [PATCH 09/19] added RhinoBrep.from_pipe --- CHANGELOG.md | 1 + src/compas/geometry/brep/brep.py | 9 ++--- src/compas_rhino/geometry/brep/__init__.py | 5 +++ src/compas_rhino/geometry/brep/brep.py | 46 ++++++++++++++++++++++ 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 380bc8f2f522..75c32bcab130 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added implementation for `Brep.from_breps` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_torus` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_polygons` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_pipe` in `compas_rhino.geometry.RhinoBrep`. ### Changed diff --git a/src/compas/geometry/brep/brep.py b/src/compas/geometry/brep/brep.py index 65eabaf15328..85fc6c66924d 100644 --- a/src/compas/geometry/brep/brep.py +++ b/src/compas/geometry/brep/brep.py @@ -468,8 +468,8 @@ def from_native(cls, native_brep): return from_native(native_brep) @classmethod - def from_pipe(cls, curve, radius, thickness=None): - """Construct a Brep by extruding a closed curve along a path curve. + def from_pipe(cls, path, radius, *args, **kwargs): + """Construct a Brep by extruding a circle curve along the path curve. Parameters ---------- @@ -477,16 +477,13 @@ def from_pipe(cls, curve, radius, thickness=None): The curve to extrude radius : float The radius of the pipe. - thickness : float, optional - The thickness of the pipe. - The thickness should be smaller than the radius. Returns ------- :class:`compas.geometry.Brep` """ - return from_pipe(curve, radius, thickness=thickness) + return from_pipe(path, radius, *args, **kwargs) @classmethod def from_plane(cls, plane, domain_u=(-1, +1), domain_v=(-1, +1)): diff --git a/src/compas_rhino/geometry/brep/__init__.py b/src/compas_rhino/geometry/brep/__init__.py index ad21279f3785..4e7284c0d706 100644 --- a/src/compas_rhino/geometry/brep/__init__.py +++ b/src/compas_rhino/geometry/brep/__init__.py @@ -80,6 +80,11 @@ def from_polygons(*args, **kwargs): return RhinoBrep.from_polygons(*args, **kwargs) +@plugin(category="factories", requires=["Rhino"]) +def from_pipe(*args, **kwargs): + return RhinoBrep.from_pipe(*args, **kwargs) + + @plugin(category="factories", requires=["Rhino"]) def from_sphere(*args, **kwargs): return RhinoBrep.from_sphere(*args, **kwargs) diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index 66ff5629f7d7..ab4142de2f5e 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -599,6 +599,52 @@ def from_native(cls, rhino_brep): brep._brep = rhino_brep return brep + @classmethod + def from_pipe(cls, path, radius, cap_mode="none", tolerance=None, *args, **kwargs): + """Construct a Brep by extruding a circle curve along the path curve. + + Parameters + ---------- + curve : :class:`compas.geometry.Curve` + The curve to extrude + radius : float + The radius of the pipe. + cap_mode : literal('none', 'flat', 'round'), optional + The type of end caps to create. Defaults to 'none'. + tolerance : :class:`~compas.tolerance.Tolerance`, optional + A Tolerance instance to use for the operation. Defaults to `TOL`. + + Returns + ------- + :class:`compas.geometry.Brep` + + """ + tolerance = tolerance or TOL + + if cap_mode == "none": + cap_mode = Rhino.Geometry.PipeCapMode.NONE + elif cap_mode == "flat": + cap_mode = Rhino.Geometry.PipeCapMode.Flat + elif cap_mode == "round": + cap_mode = Rhino.Geometry.PipeCapMode.Round + else: + raise ValueError("Invalid cap_ends value. Must be 'none', 'flat' or 'round'.") + + if hasattr(path, "native_curve"): + path = curve_to_rhino(path) + elif isinstance(path, Polyline): + path = polyline_to_rhino_curve(path) + elif isinstance(path, Line): + path = line_to_rhino_curve(path) + else: + raise TypeError("Unsupported path curve type: {}".format(type(path))) + + result = Rhino.Geometry.Brep.CreatePipe(path, radius, False, cap_mode, True, tolerance.absolute, tolerance.angular) + if result is None: + raise BrepError("Failed to create pipe from curve: {} and radius: {}".format(path, radius)) + + return [cls.from_native(brep) for brep in result] + @classmethod def from_plane(cls, plane, domain_u=(-1, +1), domain_v=(-1, +1)): """Create a RhinoBrep from a plane. From 3aa022ca1d054d30a93884ccaf2da61690e24e78 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Thu, 17 Apr 2025 16:48:59 +0200 Subject: [PATCH 10/19] added RhinoBrep.from_iges --- CHANGELOG.md | 1 + src/compas_rhino/geometry/brep/__init__.py | 5 ++++ src/compas_rhino/geometry/brep/brep.py | 28 ++++++++++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75c32bcab130..002b051ba2d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added implementation for `Brep.from_torus` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_polygons` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_pipe` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_iges` in `compas_rhino.geometry.RhinoBrep`. ### Changed diff --git a/src/compas_rhino/geometry/brep/__init__.py b/src/compas_rhino/geometry/brep/__init__.py index 4e7284c0d706..ef8c7e99500b 100644 --- a/src/compas_rhino/geometry/brep/__init__.py +++ b/src/compas_rhino/geometry/brep/__init__.py @@ -55,6 +55,11 @@ def from_curves(*args, **kwargs): return RhinoBrep.from_curves(*args, **kwargs) +@plugin(category="factories", requires=["Rhino"]) +def from_iges(*args, **kwargs): + return RhinoBrep.from_iges(*args, **kwargs) + + @plugin(category="factories", requires=["Rhino"]) def from_loft(*args, **kwargs): return RhinoBrep.from_loft(*args, **kwargs) diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index ab4142de2f5e..4660ce7ab5f0 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -539,6 +539,24 @@ def from_extrusion(cls, curve, vector, cap_ends=True): rhino_brep = capped return cls.from_native(rhino_brep) + @classmethod + def from_iges(cls, filepath): + """Construct a RhinoBrep from a IGES file. + + Parameters + ---------- + filepath : str + The path to the step file. + + Returns + ------- + :class:`compas_rhino.geometry.RhinoBrep` + + """ + if not filepath.endswith(".igs"): + raise ValueError("Expected file with .igs extension") + return cls._import_brep_from_file(filepath) + @classmethod def from_loft(cls, curves): """Construct a Brep by lofting a set of curves. @@ -729,12 +747,18 @@ def from_step(cls, filepath): :class:`compas_rhino.geometry.RhinoBrep` """ + if not not filepath.endswith(".step"): + raise ValueError("Expected file with .igs extension") + return cls._import_brep_from_file(filepath) + + @staticmethod + def _import_brep_from_file(filepath): rs.Command('_-Import "' + filepath + '" _Enter', False) - guid = rs.LastCreatedObjects()[0] + guid = rs.LastCreatedObjects()[0] # this fails, could be Rhino bug obj = compas_rhino.objects.find_object(guid) geometry = obj.Geometry.Duplicate() compas_rhino.objects.delete_object(guid) - return cls.from_native(geometry) + return RhinoBrep.from_native(geometry) @classmethod def from_sweep(cls, profile, path, is_closed=False, tolerance=None): From b472018c622d7f82c9000139297acf81ced0aaa3 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Thu, 17 Apr 2025 16:54:41 +0200 Subject: [PATCH 11/19] removed double negation --- src/compas_rhino/geometry/brep/brep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index 4660ce7ab5f0..df76ccbce9a9 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -747,7 +747,7 @@ def from_step(cls, filepath): :class:`compas_rhino.geometry.RhinoBrep` """ - if not not filepath.endswith(".step"): + if not filepath.endswith(".step"): raise ValueError("Expected file with .igs extension") return cls._import_brep_from_file(filepath) From 3971f742ee1bd0124668eddf8f96bec1ed5c0434 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Thu, 17 Apr 2025 16:57:10 +0200 Subject: [PATCH 12/19] don't fail silently --- src/compas_rhino/geometry/brep/brep.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index df76ccbce9a9..fef60c01bff9 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -252,7 +252,8 @@ def is_convex(self): @property def is_infinite(self): - pass + # TODO: what does this exactly mean? couldn't find in the Rhino API + raise NotImplementedError @property def is_orientable(self): From a216c007290e1e3a1e138cd3e08b1ba8b94c155b Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 18 Jun 2025 11:52:22 +0200 Subject: [PATCH 13/19] added export rhino brep to STEP --- CHANGELOG.md | 1 + src/compas_rhino/geometry/brep/brep.py | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d07b6a0de1f4..e4e4742a6572 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added test function `test_to_points` in `test_graph.py`. * Added test function `test_to_points` in `test_volmesh.py`. * Added test functions `test_to_points`, `test_compute_aabb`, and `test_compute_obb` in `test_mesh.py`. +* Added `to_step` method to `RhinoBrep`. ### Changed diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index fef60c01bff9..deaf9f3c6409 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -749,11 +749,12 @@ def from_step(cls, filepath): """ if not filepath.endswith(".step"): - raise ValueError("Expected file with .igs extension") + raise ValueError("Expected file with .step extension") return cls._import_brep_from_file(filepath) @staticmethod def _import_brep_from_file(filepath): + # TODO: this only seems to work in ScriptEditor (AKA rhino, not GH) rs.Command('_-Import "' + filepath + '" _Enter', False) guid = rs.LastCreatedObjects()[0] # this fails, could be Rhino bug obj = compas_rhino.objects.find_object(guid) @@ -878,6 +879,20 @@ def to_meshes(self, u=16, v=16): meshes = [mesh_to_compas(m) for m in rg_meshes] return meshes + def to_step(self, filepath): + if not filepath.endswith(".step"): + raise ValueError("Attempted to export STEP but file ends with {} extension".format(filepath.split(".")[-1])) + self._export_brep_to_file(filepath, self._brep) + + @staticmethod + def _export_brep_to_file(filepath, brep): + objects = Rhino.RhinoDoc.ActiveDoc.Objects + obj_id = objects.Add(brep) + obj = objects.Find(obj_id) + obj.Select(True) + rs.Command('_-Export "' + filepath + '" _Enter', False) + objects.Delete(obj_id, True) + def transform(self, matrix): """Transform this Brep by given transformation matrix From 7516ccb22b6dba0423667603f08135c3b7e68418 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 18 Jun 2025 12:04:21 +0200 Subject: [PATCH 14/19] fixed changelog --- CHANGELOG.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4e4742a6572..cb876025197f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added test function `test_to_points` in `test_graph.py`. * Added test function `test_to_points` in `test_volmesh.py`. * Added test functions `test_to_points`, `test_compute_aabb`, and `test_compute_obb` in `test_mesh.py`. +* Added missing property `centroid` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `curves` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `is_closed` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `is_compound` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `is_compoundsolid` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `is_orientable` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `is_surface` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `is_valid` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `orientation` in `compas_rhino.geometry.RhinoBrep`. +* Added missing property `surfaces` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_sweep` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_cone` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_plane` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_brepfaces` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_breps` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_torus` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_polygons` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_pipe` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.from_iges` in `compas_rhino.geometry.RhinoBrep`. * Added `to_step` method to `RhinoBrep`. ### Changed @@ -68,25 +87,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added `compas.geometry.angle_vectors_projected`. * Added `compas.geometry.Brep.from_curves`. * Added `compas_rhino.geometry.RhinoBrep.from_curves`. -* Added missing property `centroid` in `compas_rhino.geometry.RhinoBrep`. -* Added missing property `curves` in `compas_rhino.geometry.RhinoBrep`. -* Added missing property `is_closed` in `compas_rhino.geometry.RhinoBrep`. -* Added missing property `is_compound` in `compas_rhino.geometry.RhinoBrep`. -* Added missing property `is_compoundsolid` in `compas_rhino.geometry.RhinoBrep`. -* Added missing property `is_orientable` in `compas_rhino.geometry.RhinoBrep`. -* Added missing property `is_surface` in `compas_rhino.geometry.RhinoBrep`. -* Added missing property `is_valid` in `compas_rhino.geometry.RhinoBrep`. -* Added missing property `orientation` in `compas_rhino.geometry.RhinoBrep`. -* Added missing property `surfaces` in `compas_rhino.geometry.RhinoBrep`. -* Added implementation for `Brep.from_sweep` in `compas_rhino.geometry.RhinoBrep`. -* Added implementation for `Brep.from_cone` in `compas_rhino.geometry.RhinoBrep`. -* Added implementation for `Brep.from_plane` in `compas_rhino.geometry.RhinoBrep`. -* Added implementation for `Brep.from_brepfaces` in `compas_rhino.geometry.RhinoBrep`. -* Added implementation for `Brep.from_breps` in `compas_rhino.geometry.RhinoBrep`. -* Added implementation for `Brep.from_torus` in `compas_rhino.geometry.RhinoBrep`. -* Added implementation for `Brep.from_polygons` in `compas_rhino.geometry.RhinoBrep`. -* Added implementation for `Brep.from_pipe` in `compas_rhino.geometry.RhinoBrep`. -* Added implementation for `Brep.from_iges` in `compas_rhino.geometry.RhinoBrep`. ### Changed From 7a6ad222ee37d0f36fcf1e3f92ab903bf78ab17e Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 18 Jun 2025 13:21:37 +0200 Subject: [PATCH 15/19] fixed criminal inconsistency --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb876025197f..152b674a5b60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added implementation for `Brep.from_polygons` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_pipe` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_iges` in `compas_rhino.geometry.RhinoBrep`. -* Added `to_step` method to `RhinoBrep`. +* Added implementation for `Brep.to_step` in `compas_rhino.geometry.RhinoBrep`. ### Changed From 7b3fac669716887fef44b3726afc2242f7f0d1ab Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Mon, 4 Aug 2025 10:35:50 +0200 Subject: [PATCH 16/19] removed OCC specific properties from interface --- CHANGELOG.md | 7 +++++-- src/compas/geometry/brep/brep.py | 20 -------------------- src/compas_rhino/geometry/brep/brep.py | 25 ------------------------- 3 files changed, 5 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 152b674a5b60..9ff40643530d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Added + * Implemented `to_points` method in `compas.datastructures.Mesh`, which before raised a `NotImplementedError`. * Implemented `compute_aabb` method in `compas.datastructures.Datastructure`, which before raised a `NotImplementedError`. Made use of the `compas.geometry.bbox.bounding_box` function. * Implemented `compute_obb` method in `compas.datastructures.Datastructure`, which before raised a `NotImplementedError`. Made use of the `compas.geometry.bbox_numpy.oriented_bounding_box_numpy` function. @@ -20,8 +21,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added missing property `centroid` in `compas_rhino.geometry.RhinoBrep`. * Added missing property `curves` in `compas_rhino.geometry.RhinoBrep`. * Added missing property `is_closed` in `compas_rhino.geometry.RhinoBrep`. -* Added missing property `is_compound` in `compas_rhino.geometry.RhinoBrep`. -* Added missing property `is_compoundsolid` in `compas_rhino.geometry.RhinoBrep`. * Added missing property `is_orientable` in `compas_rhino.geometry.RhinoBrep`. * Added missing property `is_surface` in `compas_rhino.geometry.RhinoBrep`. * Added missing property `is_valid` in `compas_rhino.geometry.RhinoBrep`. @@ -42,6 +41,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +* Removed property `is_compound` from `compas.geometry.Brep` as OCC specific. +* Removed property `is_compoundsolid` from `compas.geometry.Brep` as OCC specific. +* Removed property `solids` from `compas.geometry.Brep` as OCC specific. +* Removed property `shells` from `compas.geometry.Brep` as OCC specific. ## [2.13.0] 2025-06-04 diff --git a/src/compas/geometry/brep/brep.py b/src/compas/geometry/brep/brep.py index 85fc6c66924d..dd5c7174867e 100644 --- a/src/compas/geometry/brep/brep.py +++ b/src/compas/geometry/brep/brep.py @@ -177,30 +177,10 @@ def native_brep(self): def orientation(self): raise NotImplementedError - @property - def type(self): - raise NotImplementedError - @property def is_valid(self): raise NotImplementedError - @property - def is_shell(self): - raise NotImplementedError - - @property - def is_solid(self): - raise NotImplementedError - - @property - def is_compound(self): - raise NotImplementedError - - @property - def is_compoundsolid(self): - raise NotImplementedError - @property def is_orientable(self): raise NotImplementedError diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index deaf9f3c6409..3c63db10adb2 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -236,16 +236,6 @@ def is_closed(self): assert self._brep return self._brep.IsSolid - @property - def is_compound(self): - # TODO: clarify. according to the internets compound brep is actually a container for several breps, not sure that's possible with a Rhino Brep. - return False - - @property - def is_compoundsolid(self): - # TODO: see above - return False - @property def is_convex(self): raise NotImplementedError("Convexity check is not implemented for Rhino Breps.") @@ -281,26 +271,11 @@ def orientation(self): # TODO: align this with compas.geometry.BrepOrientation return self._brep.SolidOrientation - @property - def shells(self): - # TODO: can create shell from brep but have to specify which faces to eliminate in order to hollow out the brep, doesn't seem like the intention. - # TODO: is this about traversing a compound brep? - raise NotImplementedError("Shells are not implemented for Rhino Breps.") - - @property - def solids(self): - # TODO: same as above - raise NotImplementedError("Solids are not implemented for Rhino Breps.") - @property def surfaces(self): assert self._brep return [[RhinoNurbsSurface.from_native(s.ToNurbsSurface()) for s in self._brep.Surfaces]] - @property - def type(self): - # TODO: seems like an OCC specific thinh, rename to occ_type and remove from interface? - raise NotImplementedError("Type is not implemented for Rhino Breps.") # ============================================================================== # Constructors From e532fd9d26e9268466d45d5d68f6ce0b1eb72d28 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Mon, 4 Aug 2025 10:39:39 +0200 Subject: [PATCH 17/19] module functions instead of static functions --- src/compas_rhino/geometry/brep/brep.py | 45 +++++++++++++------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index 3c63db10adb2..aba8315ce919 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -42,6 +42,25 @@ from .vertex import RhinoBrepVertex +def _export_brep_to_file(brep, filepath): + objects = Rhino.RhinoDoc.ActiveDoc.Objects + obj_id = objects.Add(brep) + obj = objects.Find(obj_id) + obj.Select(True) + rs.Command('_-Export "' + filepath + '" _Enter', False) + objects.Delete(obj_id, True) + + +def _import_brep_from_file(filepath): + # TODO: this only seems to work in ScriptEditor (AKA rhino, not GH) + rs.Command('_-Import "' + filepath + '" _Enter', False) + guid = rs.LastCreatedObjects()[0] # this fails, could be Rhino bug + obj = compas_rhino.objects.find_object(guid) + geometry = obj.Geometry.Duplicate() + compas_rhino.objects.delete_object(guid) + return RhinoBrep.from_native(geometry) + + class RhinoBrep(Brep): """Rhino Brep backend class. @@ -276,7 +295,6 @@ def surfaces(self): assert self._brep return [[RhinoNurbsSurface.from_native(s.ToNurbsSurface()) for s in self._brep.Surfaces]] - # ============================================================================== # Constructors # ============================================================================== @@ -531,7 +549,7 @@ def from_iges(cls, filepath): """ if not filepath.endswith(".igs"): raise ValueError("Expected file with .igs extension") - return cls._import_brep_from_file(filepath) + return _import_brep_from_file(filepath) @classmethod def from_loft(cls, curves): @@ -725,17 +743,7 @@ def from_step(cls, filepath): """ if not filepath.endswith(".step"): raise ValueError("Expected file with .step extension") - return cls._import_brep_from_file(filepath) - - @staticmethod - def _import_brep_from_file(filepath): - # TODO: this only seems to work in ScriptEditor (AKA rhino, not GH) - rs.Command('_-Import "' + filepath + '" _Enter', False) - guid = rs.LastCreatedObjects()[0] # this fails, could be Rhino bug - obj = compas_rhino.objects.find_object(guid) - geometry = obj.Geometry.Duplicate() - compas_rhino.objects.delete_object(guid) - return RhinoBrep.from_native(geometry) + return _import_brep_from_file(filepath) @classmethod def from_sweep(cls, profile, path, is_closed=False, tolerance=None): @@ -857,16 +865,7 @@ def to_meshes(self, u=16, v=16): def to_step(self, filepath): if not filepath.endswith(".step"): raise ValueError("Attempted to export STEP but file ends with {} extension".format(filepath.split(".")[-1])) - self._export_brep_to_file(filepath, self._brep) - - @staticmethod - def _export_brep_to_file(filepath, brep): - objects = Rhino.RhinoDoc.ActiveDoc.Objects - obj_id = objects.Add(brep) - obj = objects.Find(obj_id) - obj.Select(True) - rs.Command('_-Export "' + filepath + '" _Enter', False) - objects.Delete(obj_id, True) + _export_brep_to_file(self._brep, filepath) def transform(self, matrix): """Transform this Brep by given transformation matrix From d03d83d19edd374d4c467b8c84704dd1741f4023 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Thu, 28 Aug 2025 15:18:42 +0200 Subject: [PATCH 18/19] added partial implementation of Brep.to_viewmesh() --- CHANGELOG.md | 1 + src/compas_rhino/geometry/brep/brep.py | 28 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89b295d812d4..6ef513873a86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added implementation for `Brep.from_pipe` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.from_iges` in `compas_rhino.geometry.RhinoBrep`. * Added implementation for `Brep.to_step` in `compas_rhino.geometry.RhinoBrep`. +* Added implementation for `Brep.to_viewmesh()` in `compas_rhino.geometry.RhinoBrep`. ### Changed diff --git a/src/compas_rhino/geometry/brep/brep.py b/src/compas_rhino/geometry/brep/brep.py index aba8315ce919..fce43a366514 100644 --- a/src/compas_rhino/geometry/brep/brep.py +++ b/src/compas_rhino/geometry/brep/brep.py @@ -6,6 +6,7 @@ import rhinoscriptsyntax as rs # type: ignore import compas_rhino.objects +from compas.datastructures import Mesh from compas.geometry import Brep from compas.geometry import BrepError from compas.geometry import BrepFilletError @@ -61,6 +62,13 @@ def _import_brep_from_file(filepath): return RhinoBrep.from_native(geometry) +def _join_meshes(meshes): + result = Mesh() + for mesh in meshes: + result.join(mesh) + return result + + class RhinoBrep(Brep): """Rhino Brep backend class. @@ -862,6 +870,26 @@ def to_meshes(self, u=16, v=16): meshes = [mesh_to_compas(m) for m in rg_meshes] return meshes + def to_viewmesh(self, linear_deflection: float = 0.001): + """ + Convert the Brep to a single view mesh. + + Note + ---- + Edges as polylines is not currently implemented for RhinoBrep. Therefore, an empty list will be returned. + + Parameters + ---------- + linear_deflection : float, optional + The maximum linear deflection between the geometry and its discrete representation. + + Returns + ------- + tuple[:class:`compas.datastructures.Mesh`, list[:class:`compas.geometry.Polyline`]] + + """ + return _join_meshes(self.to_meshes()), [] + def to_step(self, filepath): if not filepath.endswith(".step"): raise ValueError("Attempted to export STEP but file ends with {} extension".format(filepath.split(".")[-1])) From cbdbc455ce5fa72920aba9e8f9c649bebd9df6a8 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Fri, 29 Aug 2025 11:12:16 +0200 Subject: [PATCH 19/19] cannot get the wiki icon anymore without user agent --- tests/compas/test_iotools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/compas/test_iotools.py b/tests/compas/test_iotools.py index 2154559add34..faada0e99917 100644 --- a/tests/compas/test_iotools.py +++ b/tests/compas/test_iotools.py @@ -10,7 +10,7 @@ BASE_FOLDER = os.path.dirname(__file__) IMAGE_FILE_SIZE = 252391 TEXT_FILE_SIZE = 747 -REMOTE_IMAGE_FILE_SIZE = 2734 +REMOTE_IMAGE_FILE_SIZE = 6518 @pytest.fixture @@ -30,7 +30,7 @@ def url_text(): @pytest.fixture def url_image(): - return "https://en.wikipedia.org/favicon.ico" + return "https://github.com/favicon.ico" def test_open_file_path_binary(path_image):