feat(specklepy): curve object class (#400)

* adds curve class
This commit is contained in:
Dogukan Karatas
2025-04-11 14:09:39 +02:00
committed by GitHub
parent a7bada391b
commit f7f9f73e7b
3 changed files with 218 additions and 0 deletions
@@ -2,6 +2,7 @@ from .arc import Arc
from .box import Box
from .circle import Circle
from .control_point import ControlPoint
from .curve import Curve
from .ellipse import Ellipse
from .line import Line
from .mesh import Mesh
@@ -33,4 +34,5 @@ __all__ = [
"Polycurve",
"Spiral",
"Surface",
"Curve",
]
+58
View File
@@ -0,0 +1,58 @@
from dataclasses import dataclass
from typing import List, Optional
from specklepy.objects.base import Base
from specklepy.objects.geometry.box import Box
from specklepy.objects.geometry.polyline import Polyline
from specklepy.objects.interfaces import ICurve, IHasArea, IHasUnits
@dataclass(kw_only=True)
class Curve(
Base,
ICurve,
IHasArea,
IHasUnits,
speckle_type="Objects.Geometry.Curve",
detachable={"points", "weights", "knots", "displayValue"},
chunkable={"points": 31250, "weights": 31250, "knots": 31250},
):
"""
a NURBS curve
"""
degree: int
periodic: bool
rational: bool
points: List[float]
weights: List[float]
knots: List[float]
closed: bool
displayValue: Polyline
bbox: Optional[Box] = None
def __repr__(self) -> str:
return (
f"{self.__class__.__name__}("
f"degree: {self.degree}, "
f"periodic: {self.periodic}, "
f"rational: {self.rational}, "
f"closed: {self.closed}, "
f"units: {self.units})"
)
@property
def length(self) -> float:
return self.__dict__.get("_length", 0.0)
@length.setter
def length(self, value: float) -> None:
self.__dict__["_length"] = value
@property
def area(self) -> float:
return self.__dict__.get("_area", 0.0)
@area.setter
def area(self, value: float) -> None:
self.__dict__["_area"] = value
+158
View File
@@ -0,0 +1,158 @@
import pytest
from specklepy.core.api.operations import deserialize, serialize
from specklepy.objects.geometry import Curve, Plane, Point, Polyline, Vector
from specklepy.objects.models.units import Units
@pytest.fixture
def sample_polyline():
"""
sample polyline
"""
return Polyline(value=[0, 0, 0, 1, 0, 0, 1, 1, 0], units=Units.m)
@pytest.fixture
def sample_plane():
"""
sample plane for bbox creation
"""
origin = Point(x=0, y=0, z=0, units=Units.m)
normal = Vector(x=0, y=0, z=1, units=Units.m)
xdir = Vector(x=1, y=0, z=0, units=Units.m)
ydir = Vector(x=0, y=1, z=0, units=Units.m)
return Plane(origin=origin, normal=normal, xdir=xdir, ydir=ydir, units=Units.m)
@pytest.fixture
def sample_curve(sample_polyline):
"""
sample curve for testing
"""
return Curve(
degree=3,
periodic=False,
rational=False,
points=[0, 0, 0, 1, 1, 0, 2, 0, 0, 3, 1, 0],
weights=[1, 1, 1, 1],
knots=[0, 0, 0, 0, 1, 1, 1, 1],
closed=False,
displayValue=sample_polyline,
units=Units.m,
)
def test_curve_creation(sample_polyline):
"""
test curve initialization
"""
curve = Curve(
degree=3,
periodic=False,
rational=False,
points=[0, 0, 0, 1, 1, 0, 2, 0, 0, 3, 1, 0],
weights=[1, 1, 1, 1],
knots=[0, 0, 0, 0, 1, 1, 1, 1],
closed=False,
displayValue=sample_polyline,
units=Units.m,
)
assert curve.degree == 3
assert curve.periodic is False
assert curve.rational is False
assert curve.points == [0, 0, 0, 1, 1, 0, 2, 0, 0, 3, 1, 0]
assert curve.weights == [1, 1, 1, 1]
assert curve.knots == [0, 0, 0, 0, 1, 1, 1, 1]
assert curve.closed is False
assert curve.units == Units.m.value
assert curve.displayValue == sample_polyline
def test_length_property(sample_polyline):
"""
test the length property setter and getter
"""
curve = Curve(
degree=1,
periodic=False,
rational=False,
points=[0, 0, 0, 1, 0, 0],
weights=[1, 1],
knots=[0, 0, 1, 1],
closed=False,
displayValue=sample_polyline,
units=Units.m,
)
assert curve.length == 0.0
curve.length = 1.5
assert curve.length == 1.5
def test_area_property(sample_polyline):
"""
test the area property setter and getter
"""
polyline = Polyline(
value=[0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0], units=Units.m
)
curve = Curve(
degree=1,
periodic=False,
rational=False,
points=[0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0],
weights=[1, 1, 1, 1, 1],
knots=[0, 0, 1, 2, 3, 4, 4],
closed=True,
displayValue=polyline,
units=Units.m,
)
assert curve.area == 0.0
curve.area = 1.0
assert curve.area == 1.0
def test_curve_serialization(sample_curve):
"""
test serialization and deserialization of the curve
"""
serialized = serialize(sample_curve)
deserialized = deserialize(serialized)
assert deserialized.degree == sample_curve.degree
assert deserialized.periodic == sample_curve.periodic
assert deserialized.rational == sample_curve.rational
assert deserialized.points == sample_curve.points
assert deserialized.weights == sample_curve.weights
assert deserialized.knots == sample_curve.knots
assert deserialized.closed == sample_curve.closed
assert deserialized.units == sample_curve.units
@pytest.mark.parametrize("new_units", ["mm", "cm", "in"])
def test_curve_units(sample_polyline, new_units):
"""
test changing units of a curve
"""
curve = Curve(
degree=3,
periodic=False,
rational=False,
points=[0, 0, 0, 1, 1, 0, 2, 0, 0, 3, 1, 0],
weights=[1, 1, 1, 1],
knots=[0, 0, 0, 0, 1, 1, 1, 1],
closed=False,
displayValue=sample_polyline,
units=Units.m,
)
assert curve.units == Units.m.value
curve.units = new_units
assert curve.units == new_units