From 7a6ce7adb2c4b913cf80e4a78ff38f20062152da Mon Sep 17 00:00:00 2001 From: tomvanmele Date: Thu, 4 Jul 2024 13:21:58 +0200 Subject: [PATCH 1/2] Similar API changes as for surfaces... --- CHANGELOG.md | 21 +- src/compas/geometry/curves/arc.py | 7 - src/compas/geometry/curves/bezier.py | 7 - src/compas/geometry/curves/conic.py | 7 - src/compas/geometry/curves/curve.py | 77 +++---- src/compas/geometry/curves/nurbs.py | 216 +++++++++---------- src/compas/geometry/curves/polyline.py | 7 - src/compas_rhino/geometry/curves/__init__.py | 33 ++- src/compas_rhino/geometry/curves/curve.py | 105 +++++---- src/compas_rhino/geometry/curves/nurbs.py | 173 ++++++--------- 10 files changed, 287 insertions(+), 366 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16409feb29e..cbdbd402355 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,15 +29,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added `compas_rhino.geometry.surfaces.nurbs.NurbsSurface.from_frame`. * Added `compas_rhino.geometry.surfaces.nurbs.NurbsSurface.from_sphere`. * Added `compas_rhino.geometry.surfaces.nurbs.NurbsSurface.from_torus`. +* Added `compas.geometry.curves.curve.Curve.from_native`. +* Added `compas_rhino.geometry.curves.curve.Curve.from_native`. +* Added `compas_rhino.geometry.curves.nurbs.NurbsCurve.from_native`. ### Changed * Fixed bug in `compas.geometry.curves.curve.Curve.reversed` by adding missing parenthesis. * Fixed all doctests so we can run `invoke test --doctest`. -* Changed `compas.geometry.surfaces.surface.Surface.__new__` to prevent instantiation of `compas.geometry.surfaces.surface.Surface` directly. -* Changed `compas.geometry.surfaces.nurbs.NurbsSurface.__new__` to prevent instantiation of `compas.geometry.surfaces.nurbs.NurbsSurface` directly. +* Changed `compas.geometry.surfaces.surface.Surface.__new__` to prevent instantiation of `Surface` directly. +* Changed `compas.geometry.surfaces.nurbs.NurbsSurface.__new__` to prevent instantiation of `NurbsSurface` directly. * Fixed bug in `compas.geometry.surfaces.nurbs.NurbsSurface.__data__`. -* Changed `compas.geometry.surfaces.nurbs.new_nurbssurface_from_...` to `compas.geometry.surfaces.nurbs.nurbssurface_from_...`. +* Changed `compas.geometry.surfaces.nurbs.new_nurbssurface_from_...` to `nurbssurface_from_...`. +* Changed `compas.geometry.curves.curve.Curve.__new__` to prevent instantiation of `Curve` directly. +* Changed `compas.geometry.curves.nurbs.new_nurbscurve_from_...` to `nurbscurve_from_...`. +* Changed `compas.geometry.curves.nurbs.NurbsCurve.__new__` to prevent instantiation of `NurbsCurve` directly. +* Changed `compas_rhino.geometry.curves.new_nurbscurve_from_...` to `nurbscurve_from_...`. ### Removed @@ -58,6 +65,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Removed `compas_rhino.geometry.surfaces.surface.Surface.from_frame`. * Removed `compas_rhino.geometry.surfaces.surface.Surface.from_sphere`. * Removed `compas_rhino.geometry.surfaces.surface.Surface.from_torus`. +* Removed `compas.geometry.curves.arc.Arc.__new__`. +* Removed `compas.geometry.curves.bezier.Bezier.__new__`. +* Removed `compas.geometry.curves.conic.Conic.__new__`. +* Removed `compas.geometry.curves.polyline.Polyline.__new__`. +* Removed `compas.geometry.curves.curve.new_curve`. +* Removed `compas.geometry.curves.curve.new_nurbscurve`. +* Removed `compas_rhino.geometry.curves.new_curve`. +* Removed `compas_rhino.geometry.curves.new_nurbscurve`. ## [2.2.1] 2024-06-25 diff --git a/src/compas/geometry/curves/arc.py b/src/compas/geometry/curves/arc.py index ef6659a0aba..b98dac9f1f7 100644 --- a/src/compas/geometry/curves/arc.py +++ b/src/compas/geometry/curves/arc.py @@ -116,13 +116,6 @@ class Arc(Curve): """ - # overwriting the __new__ method is necessary - # to avoid triggering the plugin mechanism of the base curve class - def __new__(cls, *args, **kwargs): - curve = object.__new__(cls) - curve.__init__(*args, **kwargs) - return curve - DATASCHEMA = { "value": { "type": "object", diff --git a/src/compas/geometry/curves/bezier.py b/src/compas/geometry/curves/bezier.py index edafd01b94c..e8c4fde34ad 100644 --- a/src/compas/geometry/curves/bezier.py +++ b/src/compas/geometry/curves/bezier.py @@ -162,13 +162,6 @@ class Bezier(Curve): """ - # overwriting the __new__ method is necessary - # to avoid triggering the plugin mechanism of the base curve class - def __new__(cls, *args, **kwargs): - curve = object.__new__(cls) - curve.__init__(*args, **kwargs) - return curve - DATASCHEMA = { "type": "object", "properties": { diff --git a/src/compas/geometry/curves/conic.py b/src/compas/geometry/curves/conic.py index 037cf28a2c8..93e6b604354 100644 --- a/src/compas/geometry/curves/conic.py +++ b/src/compas/geometry/curves/conic.py @@ -7,10 +7,3 @@ class Conic(Curve): """Base class for curves that are conic sections.""" - - # overwriting the __new__ method is necessary - # to avoid triggering the plugin mechanism of the base curve class - def __new__(cls, *args, **kwargs): - curve = object.__new__(cls) - curve.__init__(*args, **kwargs) - return curve diff --git a/src/compas/geometry/curves/curve.py b/src/compas/geometry/curves/curve.py index 9ff3074cfaf..af869d82315 100644 --- a/src/compas/geometry/curves/curve.py +++ b/src/compas/geometry/curves/curve.py @@ -7,14 +7,13 @@ from compas.geometry import Plane from compas.geometry import Transformation from compas.itertools import linspace +from compas.plugins import PluginNotInstalledError from compas.plugins import pluggable @pluggable(category="factories") -def new_curve(cls, *args, **kwargs): - curve = object.__new__(cls) - curve.__init__(*args, **kwargs) - return curve +def curve_from_native(cls, *args, **kwargs): + raise PluginNotInstalledError class Curve(Geometry): @@ -66,7 +65,9 @@ class Curve(Geometry): """ def __new__(cls, *args, **kwargs): - return new_curve(cls, *args, **kwargs) + if cls is Curve: + raise TypeError("Making an instance of `Curve` using `Curve()` is not allowed. Please use one of the factory methods instead (`Curve.from_...`)") + return object.__new__(cls) def __init__(self, frame=None, name=None): super(Curve, self).__init__(name=name) @@ -132,8 +133,25 @@ def is_periodic(self): # ============================================================================== @classmethod - def from_step(cls, filepath): - """Load a curve from a STP file. + def from_native(cls, curve): + """Construct a parametric curve from a native curve geometry. + + Parameters + ---------- + curve + A native curve object. + + Returns + ------- + :class:`compas.geometry.Curve` + A COMPAS curve. + + """ + return curve_from_native(cls, curve) + + @classmethod + def from_obj(cls, filepath): + """Load a curve from an OBJ file. Parameters ---------- @@ -148,8 +166,8 @@ def from_step(cls, filepath): raise NotImplementedError @classmethod - def from_obj(cls, filepath): - """Load a curve from an OBJ file. + def from_step(cls, filepath): + """Load a curve from a STP file. Parameters ---------- @@ -465,47 +483,6 @@ def reversed(self): copy.reverse() return copy - # def space(self, n=10): - # """Compute evenly spaced parameters over the curve domain. - - # Parameters - # ---------- - # n : int, optional - # The number of values in the parameter space. - - # Returns - # ------- - # list[float] - - # See Also - # -------- - # :meth:`locus` - - # """ - # start, end = self.domain - # return linspace(start, end, n) - - # def locus(self, resolution=100): - # """Compute the locus of points on the curve. - - # Parameters - # ---------- - # resolution : int - # The number of intervals at which a point on the - # curve should be computed. - - # Returns - # ------- - # list[:class:`compas.geometry.Point`] - # Points along the curve. - - # See Also - # -------- - # :meth:`space` - - # """ - # return [self.point_at(t) for t in self.space(resolution)] - def closest_point(self, point, return_parameter=False): """Compute the closest point on the curve to a given point. diff --git a/src/compas/geometry/curves/nurbs.py b/src/compas/geometry/curves/nurbs.py index ca4e741629c..499107e46d1 100644 --- a/src/compas/geometry/curves/nurbs.py +++ b/src/compas/geometry/curves/nurbs.py @@ -13,34 +13,27 @@ @pluggable(category="factories") -def new_nurbscurve(cls, *args, **kwargs): - curve = object.__new__(NurbsCurve) - curve.__init__(*args, **kwargs) - return curve - - -@pluggable(category="factories") -def new_nurbscurve_from_native(cls, *args, **kwargs): +def nurbscurve_from_interpolation(cls, *args, **kwargs): raise PluginNotInstalledError @pluggable(category="factories") -def new_nurbscurve_from_parameters(cls, *args, **kwargs): +def nurbscurve_from_native(cls, *args, **kwargs): raise PluginNotInstalledError @pluggable(category="factories") -def new_nurbscurve_from_points(cls, *args, **kwargs): +def nurbscurve_from_parameters(cls, *args, **kwargs): raise PluginNotInstalledError @pluggable(category="factories") -def new_nurbscurve_from_interpolation(cls, *args, **kwargs): +def nurbscurve_from_points(cls, *args, **kwargs): raise PluginNotInstalledError @pluggable(category="factories") -def new_nurbscurve_from_step(cls, *args, **kwargs): +def nurbscurve_from_step(cls, *args, **kwargs): raise PluginNotInstalledError @@ -73,9 +66,6 @@ class NurbsCurve(Curve): """ - def __new__(cls, *args, **kwargs): - return new_nurbscurve(cls, *args, **kwargs) - DATASCHEMA = { "type": "object", "properties": { @@ -108,7 +98,7 @@ def __data__(self): @classmethod def __from_data__(cls, data): return cls.from_parameters( - data["points"], + data["points"], # conversion is not needed because point data can be provided in raw form as well data["weights"], data["knots"], data["multiplicities"], @@ -116,8 +106,10 @@ def __from_data__(cls, data): data["is_periodic"], ) - def __init__(self, name=None): - super(NurbsCurve, self).__init__(name=name) + def __new__(cls, *args, **kwargs): + if cls is Curve: + raise TypeError("Making an instance of `NurbsCurve` using `NurbsCurve()` is not allowed. Please use one of the factory methods instead (`NurbsCurve.from_...`)") + return object.__new__(cls) def __repr__(self): return "{0}(points={1!r}, weigths={2}, knots={3}, multiplicities={4}, degree={5}, is_periodic={6})".format( @@ -174,100 +166,6 @@ def is_rational(self): # Constructors # ============================================================================== - @classmethod - def from_native(cls, curve): - """Construct a NURBS curve from a curve object. - - Parameters - ---------- - curve : :class:`Rhino.Geometry.NurbsCurve` - A *Rhino* curve object. - - Returns - ------- - :class:`compas.geometry.NurbsCurve` - A COMPAS NURBS curve. - - """ - return new_nurbscurve_from_native(cls, curve) - - @classmethod - def from_step(cls, filepath): - """Load a NURBS curve from an STP file. - - Parameters - ---------- - filepath : str - The path to the file. - - Returns - ------- - :class:`compas.geometry.NurbsCurve` - """ - return new_nurbscurve_from_step(cls, filepath) - - @classmethod - def from_parameters(cls, points, weights, knots, multiplicities, degree, is_periodic=False): - """Construct a NURBS curve from explicit curve parameters. - - Parameters - ---------- - points : list[[float, float, float] | :class:`compas.geometry.Point`] - The control points. - weights : list[float] - The weights of the control points. - knots : list[float] - The curve knots, without multiplicity. - multiplicities : list[int] - Multiplicity of the knots. - degree : int - Degree of the curve. - is_periodic : bool, optional - Flag indicating that the curve is periodic. - - Returns - ------- - :class:`compas.geometry.NurbsCurve` - - """ - return new_nurbscurve_from_parameters(cls, points, weights, knots, multiplicities, degree, is_periodic=False) - - @classmethod - def from_points(cls, points, degree=3): - """Construct a NURBS curve from control points. - - Parameters - ---------- - points : list[[float, float, float] | :class:`compas.geometry.Point`] - The control points. - degree : int, optional - The degree of the curve. - - Returns - ------- - :class:`compas.geometry.NurbsCurve` - - """ - return new_nurbscurve_from_points(cls, points, degree=degree) - - @classmethod - def from_interpolation(cls, points, precision=1e-3): - """Construct a NURBS curve by interpolating a set of points. - - Parameters - ---------- - points : list[[float, float, float] | :class:`compas.geometry.Point`] - A list of interpolation points. - precision : int, optional - The desired precision of the interpolation. - - Returns - ------- - :class:`compas.geometry.NurbsCurve` - - """ - return new_nurbscurve_from_interpolation(cls, points, precision=1e-3) - @classmethod def from_arc(cls, arc): """Construct a NURBS curve from an arc. @@ -350,6 +248,24 @@ def from_ellipse(cls, ellipse): weights = [1, w, 1, w, 1, w, 1, w, 1] return cls.from_parameters(points=points, weights=weights, knots=knots, multiplicities=mults, degree=2) + @classmethod + def from_interpolation(cls, points, precision=1e-3): + """Construct a NURBS curve by interpolating a set of points. + + Parameters + ---------- + points : list[[float, float, float] | :class:`compas.geometry.Point`] + A list of interpolation points. + precision : int, optional + The desired precision of the interpolation. + + Returns + ------- + :class:`compas.geometry.NurbsCurve` + + """ + return nurbscurve_from_interpolation(cls, points, precision=1e-3) + @classmethod def from_line(cls, line): """Construct a NURBS curve from a line. @@ -371,6 +287,82 @@ def from_line(cls, line): degree=1, ) + @classmethod + def from_native(cls, curve): + """Construct a NURBS curve from a CAD-native curve geometry. + + Parameters + ---------- + curve + A CAD-native curve geometry. + + Returns + ------- + :class:`compas.geometry.NurbsCurve` + A COMPAS NURBS curve. + + """ + return nurbscurve_from_native(cls, curve) + + @classmethod + def from_parameters(cls, points, weights, knots, multiplicities, degree, is_periodic=False): + """Construct a NURBS curve from explicit curve parameters. + + Parameters + ---------- + points : list[[float, float, float] | :class:`compas.geometry.Point`] + The control points. + weights : list[float] + The weights of the control points. + knots : list[float] + The curve knots, without multiplicity. + multiplicities : list[int] + Multiplicity of the knots. + degree : int + Degree of the curve. + is_periodic : bool, optional + Flag indicating that the curve is periodic. + + Returns + ------- + :class:`compas.geometry.NurbsCurve` + + """ + return nurbscurve_from_parameters(cls, points, weights, knots, multiplicities, degree, is_periodic=False) + + @classmethod + def from_points(cls, points, degree=3): + """Construct a NURBS curve from control points. + + Parameters + ---------- + points : list[[float, float, float] | :class:`compas.geometry.Point`] + The control points. + degree : int, optional + The degree of the curve. + + Returns + ------- + :class:`compas.geometry.NurbsCurve` + + """ + return nurbscurve_from_points(cls, points, degree=degree) + + @classmethod + def from_step(cls, filepath): + """Load a NURBS curve from an STP file. + + Parameters + ---------- + filepath : str + The path to the file. + + Returns + ------- + :class:`compas.geometry.NurbsCurve` + """ + return nurbscurve_from_step(cls, filepath) + # ============================================================================== # Conversions # ============================================================================== diff --git a/src/compas/geometry/curves/polyline.py b/src/compas/geometry/curves/polyline.py index 55c4069f063..37303e0c843 100644 --- a/src/compas/geometry/curves/polyline.py +++ b/src/compas/geometry/curves/polyline.py @@ -71,13 +71,6 @@ class Polyline(Curve): """ - # overwriting the __new__ method is necessary - # to avoid triggering the plugin mechanism of the base curve class - def __new__(cls, *args, **kwargs): - curve = object.__new__(cls) - curve.__init__(*args, **kwargs) - return curve - DATASCHEMA = { "type": "object", "properties": { diff --git a/src/compas_rhino/geometry/curves/__init__.py b/src/compas_rhino/geometry/curves/__init__.py index c21247dd46d..4098c6cc16d 100644 --- a/src/compas_rhino/geometry/curves/__init__.py +++ b/src/compas_rhino/geometry/curves/__init__.py @@ -1,43 +1,34 @@ -from .curve import RhinoCurve +from .nurbs import RhinoCurve from .nurbs import RhinoNurbsCurve from compas.plugins import plugin @plugin(category="factories", requires=["Rhino"]) -def new_curve(cls, *args, **kwargs): - curve = object.__new__(RhinoCurve) - curve.__init__(*args, **kwargs) - return curve +def curve_from_native(cls, *args, **kwargs): + return RhinoCurve.from_native(*args, **kwargs) @plugin(category="factories", requires=["Rhino"]) -def new_nurbscurve(cls, *args, **kwargs): - curve = object.__new__(RhinoNurbsCurve) - curve.__init__(*args, **kwargs) - return curve +def nurbscurve_from_interpolation(cls, *args, **kwargs): + return RhinoNurbsCurve.from_interpolation(*args, **kwargs) @plugin(category="factories", requires=["Rhino"]) -def new_nurbscurve_from_native(cls, *args, **kwargs): - return RhinoNurbsCurve.from_rhino(*args, **kwargs) +def nurbscurve_from_native(cls, *args, **kwargs): + return RhinoNurbsCurve.from_native(*args, **kwargs) @plugin(category="factories", requires=["Rhino"]) -def new_nurbscurve_from_parameters(cls, *args, **kwargs): +def nurbscurve_from_parameters(cls, *args, **kwargs): return RhinoNurbsCurve.from_parameters(*args, **kwargs) @plugin(category="factories", requires=["Rhino"]) -def new_nurbscurve_from_points(cls, *args, **kwargs): +def nurbscurve_from_points(cls, *args, **kwargs): return RhinoNurbsCurve.from_points(*args, **kwargs) -@plugin(category="factories", requires=["Rhino"]) -def new_nurbscurve_from_interpolation(cls, *args, **kwargs): - return RhinoNurbsCurve.from_interpolation(*args, **kwargs) - - -@plugin(category="factories", requires=["Rhino"]) -def new_nurbscurve_from_step(cls, *args, **kwargs): - return RhinoNurbsCurve.from_step(*args, **kwargs) +# @plugin(category="factories", requires=["Rhino"]) +# def nurbscurve_from_step(cls, *args, **kwargs): +# return RhinoNurbsCurve.from_step(*args, **kwargs) diff --git a/src/compas_rhino/geometry/curves/curve.py b/src/compas_rhino/geometry/curves/curve.py index 8ef652a8e23..6b3eab7c5fa 100644 --- a/src/compas_rhino/geometry/curves/curve.py +++ b/src/compas_rhino/geometry/curves/curve.py @@ -18,6 +18,8 @@ class RhinoCurve(Curve): Parameters ---------- + native_curve : :rhino:`Curve` + A Rhino curve. name : str, optional Name of the curve. @@ -38,17 +40,17 @@ class RhinoCurve(Curve): Other Attributes ---------------- - rhino_curve : :rhino:`Curve` + native_curve : :rhino:`Curve` The underlying Rhino curve. """ - def __init__(self, name=None): + def __init__(self, native_curve, name=None): super(RhinoCurve, self).__init__(name=name) - self._rhino_curve = None + self._native_curve = native_curve def __eq__(self, other): - return self.rhino_curve.IsEqual(other.rhino_curve) # type: ignore + return self.native_curve.IsEqual(other.native_curve) # type: ignore # ============================================================================== # Data @@ -60,62 +62,84 @@ def __eq__(self, other): @property def rhino_curve(self): - return self._rhino_curve + return self._native_curve - @rhino_curve.setter - def rhino_curve(self, curve): - self._rhino_curve = curve + @property + def native_curve(self): + return self._native_curve + + @native_curve.setter + def native_curve(self, curve): + self._native_curve = curve @property def dimension(self): - if self.rhino_curve: - return self.rhino_curve.Dimension + if self.native_curve: + return self.native_curve.Dimension @property def domain(self): - if self.rhino_curve: - return self.rhino_curve.Domain.T0, self.rhino_curve.Domain.T1 + if self.native_curve: + return self.native_curve.Domain.T0, self.native_curve.Domain.T1 @property def start(self): - if self.rhino_curve: - return point_to_compas(self.rhino_curve.PointAtStart) + if self.native_curve: + return point_to_compas(self.native_curve.PointAtStart) @property def end(self): - if self.rhino_curve: - return point_to_compas(self.rhino_curve.PointAtEnd) + if self.native_curve: + return point_to_compas(self.native_curve.PointAtEnd) @property def is_closed(self): - if self.rhino_curve: - return self.rhino_curve.IsClosed + if self.native_curve: + return self.native_curve.IsClosed @property def is_periodic(self): - if self.rhino_curve: - return self.rhino_curve.IsPeriodic + if self.native_curve: + return self.native_curve.IsPeriodic # ============================================================================== # Constructors # ============================================================================== @classmethod - def from_rhino(cls, rhino_curve): + def from_native(cls, native_curve): """Construct a curve from an existing Rhino curve. Parameters ---------- - rhino_curve : Rhino.Geometry.Curve + native_curve : Rhino.Geometry.Curve Returns ------- :class:`compas_rhino.geometry.RhinoCurve` """ - curve = cls() - curve.rhino_curve = rhino_curve - return curve + return cls(native_curve) + + @classmethod + def from_rhino(cls, native_curve): + """Construct a curve from an existing Rhino curve. + + Parameters + ---------- + native_curve : Rhino.Geometry.Curve + + Returns + ------- + :class:`compas_rhino.geometry.RhinoCurve` + + Warnings + -------- + .. deprecated:: 2.3 + Use `from_native` instead. + + """ + return cls(native_curve) # ============================================================================== # Conversions @@ -134,9 +158,8 @@ def copy(self): """ cls = type(self) - curve = cls() - curve.rhino_curve = self.rhino_curve.Duplicate() # type: ignore - return curve + native_curve = self.native_curve.Duplicate() + return cls.from_native(native_curve) def transform(self, T): """Transform this curve. @@ -151,7 +174,7 @@ def transform(self, T): None """ - self.rhino_curve.Transform(transformation_to_rhino(T)) # type: ignore + self.native_curve.Transform(transformation_to_rhino(T)) # type: ignore def reverse(self): """Reverse the parametrisation of the curve. @@ -161,7 +184,7 @@ def reverse(self): None """ - self.rhino_curve.Reverse() # type: ignore + self.native_curve.Reverse() # type: ignore def point_at(self, t): """Compute a point on the curve. @@ -177,7 +200,7 @@ def point_at(self, t): the corresponding point on the curve. """ - point = self.rhino_curve.PointAt(t) # type: ignore + point = self.native_curve.PointAt(t) # type: ignore return point_to_compas(point) def tangent_at(self, t): @@ -194,7 +217,7 @@ def tangent_at(self, t): The corresponding tangent vector. """ - vector = self.rhino_curve.TangentAt(t) # type: ignore + vector = self.native_curve.TangentAt(t) # type: ignore return vector_to_compas(vector) def curvature_at(self, t): @@ -211,7 +234,7 @@ def curvature_at(self, t): The corresponding curvature vector. """ - vector = self.rhino_curve.CurvatureAt(t) # type: ignore + vector = self.native_curve.CurvatureAt(t) # type: ignore return vector_to_compas(vector) def frame_at(self, t): @@ -228,7 +251,7 @@ def frame_at(self, t): The corresponding local frame. """ - t, plane = self.rhino_curve.FrameAt(t) # type: ignore + t, plane = self.native_curve.FrameAt(t) # type: ignore return plane_to_compas_frame(plane) def torsion_at(self, t): @@ -245,7 +268,7 @@ def torsion_at(self, t): The torsion value. """ - return self.rhino_curve.TorsionAt(t) # type: ignore + return self.native_curve.TorsionAt(t) # type: ignore # ============================================================================== # Methods continued @@ -268,7 +291,7 @@ def closest_point(self, point, return_parameter=False): If `return_parameter` is True, the closest point and the corresponding parameter are returned. """ - result, t = self.rhino_curve.ClosestPoint(point_to_rhino(point)) # type: ignore + result, t = self.native_curve.ClosestPoint(point_to_rhino(point)) # type: ignore if not result: return point = self.point_at(t) @@ -295,7 +318,7 @@ def divide_by_count(self, count, return_points=False): If `return_points` is True, a list of points in addition to the parameters of the discretisation. """ - params = self.rhino_curve.DivideByCount(count, True) # type: ignore + params = self.native_curve.DivideByCount(count, True) # type: ignore if return_points: points = [self.point_at(t) for t in params] return params, points @@ -320,7 +343,7 @@ def divide_by_length(self, length, return_points=False): If `return_points` is True, a list of points in addition to the parameters of the discretisation. """ - params = self.rhino_curve.DivideByLength(length, True) # type: ignore + params = self.native_curve.DivideByLength(length, True) # type: ignore if return_points: points = [self.point_at(t) for t in params] return params, points @@ -334,7 +357,7 @@ def aabb(self): :class:`compas.geometry.Box` """ - box = self.rhino_curve.getBoundingBox(True) # type: ignore + box = self.native_curve.getBoundingBox(True) # type: ignore return box_to_compas(box) def length(self, precision=1e-8): @@ -346,7 +369,7 @@ def length(self, precision=1e-8): Required precision of the calculated length. """ - return self.rhino_curve.GetLength(precision) # type: ignore + return self.native_curve.GetLength(precision) # type: ignore def fair(self, tol=1e-3): raise NotImplementedError @@ -370,7 +393,7 @@ def offset(self, distance, direction, tolerance=1e-3): point = self.point_at(self.domain[0]) # type: ignore plane = Plane(point, direction) plane = plane_to_rhino(plane) - self.rhino_curve = self.rhino_curve.Offset(plane, distance, tolerance, 0)[0] # type: ignore + self.native_curve = self.native_curve.Offset(plane, distance, tolerance, 0)[0] # type: ignore def smooth(self): raise NotImplementedError diff --git a/src/compas_rhino/geometry/curves/nurbs.py b/src/compas_rhino/geometry/curves/nurbs.py index f1e86b9466a..c6e893eb82e 100644 --- a/src/compas_rhino/geometry/curves/nurbs.py +++ b/src/compas_rhino/geometry/curves/nurbs.py @@ -8,17 +8,16 @@ from compas.geometry import NurbsCurve from compas.geometry import Point -from compas_rhino.conversions import line_to_rhino from compas_rhino.conversions import point_to_compas from compas_rhino.conversions import point_to_rhino from .curve import RhinoCurve -def rhino_curve_from_parameters(points, weights, knots, multiplicities, degree): - rhino_curve = Rhino.Geometry.NurbsCurve(3, True, degree + 1, len(points)) +def native_curve_from_parameters(points, weights, knots, multiplicities, degree): + native_curve = Rhino.Geometry.NurbsCurve(3, True, degree + 1, len(points)) for index, (point, weight) in enumerate(zip(points, weights)): - rhino_curve.Points.SetPoint(index, point_to_rhino(point), weight) + native_curve.Points.SetPoint(index, point_to_rhino(point), weight) knotvector = [knot for knot, mult in zip(knots, multiplicities) for _ in range(mult)] # account for superfluous knots # https://developer.rhino3d.com/guides/opennurbs/superfluous-knots/ @@ -28,11 +27,11 @@ def rhino_curve_from_parameters(points, weights, knots, multiplicities, degree): if len(knotvector) == k: knotvector[:] = knotvector[1:-1] for index, knot in enumerate(knotvector): - rhino_curve.Knots[index] = knot - return rhino_curve + native_curve.Knots[index] = knot + return native_curve -class RhinoNurbsCurve(NurbsCurve, RhinoCurve): +class RhinoNurbsCurve(RhinoCurve, NurbsCurve): """Class representing a NURBS curve based on the NurbsCurve of Rhino.Geometry. Parameters @@ -69,10 +68,6 @@ class RhinoNurbsCurve(NurbsCurve, RhinoCurve): """ - # def __init__(self, name=None): - # super(RhinoNurbsCurve, self).__init__(name=name) - # self.rhino_curve = None - # ============================================================================== # Data # ============================================================================== @@ -104,9 +99,8 @@ def __from_data__(cls, data): # is_periodic = data['is_periodic'] # have not found a way to actually set this # not sure if that is actually possible... - curve = cls() - curve.rhino_curve = rhino_curve_from_parameters(points, weights, knots, multiplicities, degree) - return curve + native_curve = native_curve_from_parameters(points, weights, knots, multiplicities, degree) + return cls.from_native(native_curve) # ============================================================================== # Rhino Properties @@ -118,176 +112,133 @@ def __from_data__(cls, data): @property def points(self): - if self.rhino_curve: - return [point_to_compas(point.Location) for point in self.rhino_curve.Points] + if self.native_curve: + return [point_to_compas(point.Location) for point in self.native_curve.Points] @property def weights(self): - if self.rhino_curve: - return [point.Weight for point in self.rhino_curve.Points] + if self.native_curve: + return [point.Weight for point in self.native_curve.Points] @property def knots(self): - if self.rhino_curve: - return [key for key, _ in groupby(self.rhino_curve.Knots)] + if self.native_curve: + return [key for key, _ in groupby(self.native_curve.Knots)] @property def knotsequence(self): - if self.rhino_curve: - return list(self.rhino_curve.Knots) + if self.native_curve: + return list(self.native_curve.Knots) @property def multiplicities(self): - if self.rhino_curve: - return [len(list(group)) for _, group in groupby(self.rhino_curve.Knots)] + if self.native_curve: + return [len(list(group)) for _, group in groupby(self.native_curve.Knots)] @property def degree(self): - if self.rhino_curve: - return self.rhino_curve.Degree + if self.native_curve: + return self.native_curve.Degree @property def order(self): - if self.rhino_curve: - return self.rhino_curve.Order + if self.native_curve: + return self.native_curve.Order @property def is_rational(self): - if self.rhino_curve: - return self.rhino_curve.IsRational + if self.native_curve: + return self.native_curve.IsRational # ============================================================================== # Constructors # ============================================================================== @classmethod - def from_parameters(cls, points, weights, knots, multiplicities, degree, is_periodic=False): - """Construct a NURBS curve from explicit curve parameters. + def from_interpolation(cls, points, precision=1e-3): + """Construct a NURBS curve by interpolating a set of points. Parameters ---------- points : list[:class:`compas.geometry.Point`] The control points. - weights : list[float] - The control point weights. - knots : list[float] - The curve knots, without duplicates. - multiplicities : list[int] - The multiplicities of the knots. - degree : int - The degree of the curve. - is_periodic : bool, optional - Flag indicating whether the curve is periodic or not. - Note that this parameters is currently not supported. + precision : float, optional + The required precision of the interpolation. + This parameter is currently not supported. Returns ------- :class:`compas_rhino.geometry.RhinoNurbsCurve` """ - curve = cls() - degree = min(degree, len(points) - 1) - curve.rhino_curve = rhino_curve_from_parameters(points, weights, knots, multiplicities, degree) - return curve + native_curve = Rhino.Geometry.NurbsCurve.CreateHSpline([point_to_rhino(point) for point in points]) + return cls.from_native(native_curve) @classmethod - def from_points(cls, points, degree=3, is_periodic=False): - """Construct a NURBS curve from control points. + def from_native(cls, native_curve): + """Construct a NURBS curve from an existing Rhino curve. Parameters ---------- - points : list[:class:`compas.geometry.Point`] - The control points. - degree : int, optional - The degree of the curve. - is_periodic : bool, optional - Flag indicating whether the curve is periodic or not. + native_curve : Rhino.Geometry.Curve Returns ------- :class:`compas_rhino.geometry.RhinoNurbsCurve` """ - curve = cls() - degree = min(degree, len(points) - 1) - rhino_curve = Rhino.Geometry.NurbsCurve.Create(is_periodic, degree, [point_to_rhino(point) for point in points]) - curve.rhino_curve = rhino_curve - return curve + return cls(native_curve) @classmethod - def from_interpolation(cls, points, precision=1e-3): - """Construct a NURBS curve by interpolating a set of points. + def from_parameters(cls, points, weights, knots, multiplicities, degree, is_periodic=False): + """Construct a NURBS curve from explicit curve parameters. Parameters ---------- points : list[:class:`compas.geometry.Point`] The control points. - precision : float, optional - The required precision of the interpolation. - This parameter is currently not supported. + weights : list[float] + The control point weights. + knots : list[float] + The curve knots, without duplicates. + multiplicities : list[int] + The multiplicities of the knots. + degree : int + The degree of the curve. + is_periodic : bool, optional + Flag indicating whether the curve is periodic or not. + Note that this parameters is currently not supported. Returns ------- :class:`compas_rhino.geometry.RhinoNurbsCurve` """ - curve = cls() - curve.rhino_curve = Rhino.Geometry.NurbsCurve.CreateHSpline([point_to_rhino(point) for point in points]) - return curve - - # @classmethod - # def from_circle(cls, circle): - # """Construct a NURBS curve from a circle. - - # Parameters - # ---------- - # circle : :class:`compas.geometry.Circle` - # A circle geometry. - - # Returns - # ------- - # :class:`compas_rhino.geometry.RhinoNurbsCurve` - - # """ - # curve = cls() - # curve.rhino_curve = Rhino.Geometry.NurbsCurve.CreateFromCircle(circle_to_rhino(circle)) - # return curve - - # @classmethod - # def from_ellipse(cls, ellipse): - # """Construct a NURBS curve from an ellipse. - - # Parameters - # ---------- - # ellipse : :class:`compas.geometry.Ellipse` - # An ellipse geometry. - - # Returns - # ------- - # :class:`compas_rhino.geometry.RhinoNurbsCurve` - - # """ - # curve = cls() - # curve.rhino_curve = Rhino.Geometry.NurbsCurve.CreateFromEllipse(ellipse_to_rhino(ellipse)) - # return curve + degree = min(degree, len(points) - 1) + native_curve = native_curve_from_parameters(points, weights, knots, multiplicities, degree) + return cls.from_native(native_curve) @classmethod - def from_line(cls, line): - """Construct a NURBS curve from a line. + def from_points(cls, points, degree=3, is_periodic=False): + """Construct a NURBS curve from control points. Parameters ---------- - line : :class:`compas.geometry.Line` - A line geometry. + points : list[:class:`compas.geometry.Point`] + The control points. + degree : int, optional + The degree of the curve. + is_periodic : bool, optional + Flag indicating whether the curve is periodic or not. Returns ------- :class:`compas_rhino.geometry.RhinoNurbsCurve` """ - curve = cls() - curve.rhino_curve = Rhino.Geometry.NurbsCurve.CreateFromLine(line_to_rhino(line)) - return curve + degree = min(degree, len(points) - 1) + native_curve = Rhino.Geometry.NurbsCurve.Create(is_periodic, degree, [point_to_rhino(point) for point in points]) + return cls.from_native(native_curve) # ============================================================================== # Conversions From bb3b72d3165ca95d68b47767d0760ca118f2823f Mon Sep 17 00:00:00 2001 From: tomvanmele Date: Thu, 4 Jul 2024 16:15:29 +0200 Subject: [PATCH 2/2] wrong type --- src/compas/geometry/curves/nurbs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compas/geometry/curves/nurbs.py b/src/compas/geometry/curves/nurbs.py index 499107e46d1..cce9186974a 100644 --- a/src/compas/geometry/curves/nurbs.py +++ b/src/compas/geometry/curves/nurbs.py @@ -107,7 +107,7 @@ def __from_data__(cls, data): ) def __new__(cls, *args, **kwargs): - if cls is Curve: + if cls is NurbsCurve: raise TypeError("Making an instance of `NurbsCurve` using `NurbsCurve()` is not allowed. Please use one of the factory methods instead (`NurbsCurve.from_...`)") return object.__new__(cls)