Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ce1c36a51 | |||
| 6f56ecb0c0 |
@@ -49,9 +49,8 @@ class Mesh(
|
||||
|
||||
if len(self.vertices) % 3 != 0:
|
||||
raise ValueError(
|
||||
f"Invalid vertices list: length ({
|
||||
len(self.vertices)
|
||||
}) must be a multiple of 3"
|
||||
f"Invalid vertices list: length {len(self.vertices)} "
|
||||
f"must be a multiple of 3"
|
||||
)
|
||||
return len(self.vertices) // 3
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import List
|
||||
from specklepy.objects.base import Base
|
||||
from specklepy.objects.geometry.point import Point
|
||||
from specklepy.objects.interfaces import ICurve, IHasUnits
|
||||
from specklepy.objects.models.units import Units
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
@@ -13,24 +14,28 @@ class Polyline(Base, IHasUnits, ICurve, speckle_type="Objects.Geometry.Polyline"
|
||||
"""
|
||||
|
||||
value: List[float]
|
||||
closed: bool = False
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.__class__.__name__}(value: {self.value}, units: {self.units})"
|
||||
return (
|
||||
f"{self.__class__.__name__}("
|
||||
f"value: {self.value}, "
|
||||
f"closed: {self.closed}, "
|
||||
f"units: {self.units})"
|
||||
)
|
||||
|
||||
def is_closed(self, tolerance: float = 1e-6) -> bool:
|
||||
@staticmethod
|
||||
def is_closed(points: List[float], tolerance: float = 1e-6) -> bool:
|
||||
"""
|
||||
check if the polyline is closed (start point equals end point within tolerance)
|
||||
check if the polyline is closed
|
||||
"""
|
||||
if len(self.value) < 6: # need at least 2 points to be closed
|
||||
if len(points) < 6: # need at least 2 points to be closed
|
||||
return False
|
||||
|
||||
# compare first and last points
|
||||
start = Point(
|
||||
x=self.value[0], y=self.value[1], z=self.value[2], units=self.units
|
||||
)
|
||||
end = Point(
|
||||
x=self.value[-3], y=self.value[-2], z=self.value[-1], units=self.units
|
||||
)
|
||||
start = Point(x=points[0], y=points[1], z=points[2], units=Units.m)
|
||||
end = Point(x=points[-3], y=points[-2], z=points[-1], units=Units.m)
|
||||
|
||||
return start.distance_to(end) <= tolerance
|
||||
|
||||
@property
|
||||
@@ -46,7 +51,7 @@ class Polyline(Base, IHasUnits, ICurve, speckle_type="Objects.Geometry.Polyline"
|
||||
total_length = 0.0
|
||||
for i in range(len(points) - 1):
|
||||
total_length += points[i].distance_to(points[i + 1])
|
||||
if self.is_closed() and points:
|
||||
if self.closed and points:
|
||||
total_length += points[-1].distance_to(points[0])
|
||||
return total_length
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ def test_polyline_creation(open_square_coords):
|
||||
polyline = Polyline(value=open_square_coords, units=Units.m)
|
||||
assert polyline.value == open_square_coords
|
||||
assert polyline.units == Units.m.value
|
||||
assert polyline.closed is False
|
||||
|
||||
|
||||
def test_polyline_domain(sample_polyline):
|
||||
@@ -63,23 +64,31 @@ def test_polyline_domain(sample_polyline):
|
||||
|
||||
|
||||
def test_polyline_is_closed(open_square_coords, closed_square_coords):
|
||||
open_poly = Polyline(value=open_square_coords, units=Units.m)
|
||||
closed_poly = Polyline(value=closed_square_coords, units=Units.m)
|
||||
# Test the static method
|
||||
assert not Polyline.is_closed(open_square_coords)
|
||||
assert Polyline.is_closed(closed_square_coords)
|
||||
|
||||
assert not open_poly.is_closed()
|
||||
assert closed_poly.is_closed()
|
||||
# Test with closed flag
|
||||
open_poly = Polyline(value=open_square_coords, units=Units.m)
|
||||
closed_poly = Polyline(value=closed_square_coords, units=Units.m, closed=True)
|
||||
assert not open_poly.closed
|
||||
assert closed_poly.closed
|
||||
|
||||
|
||||
def test_polyline_is_closed_with_tolerance(open_square_coords):
|
||||
almost_closed = open_square_coords + [
|
||||
0.0,
|
||||
0.0,
|
||||
0.001,
|
||||
] # last point slightly above start
|
||||
poly = Polyline(value=almost_closed, units=Units.m)
|
||||
0.001, # last point slightly above start
|
||||
]
|
||||
# Test static method with tolerance
|
||||
assert not Polyline.is_closed(almost_closed, tolerance=1e-6)
|
||||
assert Polyline.is_closed(almost_closed, tolerance=0.01)
|
||||
|
||||
assert not poly.is_closed(tolerance=1e-6)
|
||||
assert poly.is_closed(tolerance=0.01)
|
||||
# Also test with instance
|
||||
poly = Polyline(value=almost_closed, units=Units.m)
|
||||
# poly.closed should reflect what was passed in construction, not computed
|
||||
assert not poly.closed
|
||||
|
||||
|
||||
def test_polyline_length_open(sample_polyline):
|
||||
@@ -88,14 +97,13 @@ def test_polyline_length_open(sample_polyline):
|
||||
|
||||
|
||||
def test_polyline_length_closed(closed_square_coords):
|
||||
polyline = Polyline(value=closed_square_coords, units=Units.m)
|
||||
polyline = Polyline(value=closed_square_coords, units=Units.m, closed=True)
|
||||
polyline.length = polyline.calculate_length()
|
||||
assert polyline.length == 4.0
|
||||
|
||||
|
||||
def test_polyline_get_points(sample_polyline):
|
||||
points = sample_polyline.get_points()
|
||||
|
||||
assert len(points) == 4
|
||||
assert all(isinstance(p, Point) for p in points)
|
||||
assert all(p.units == Units.m.value for p in points)
|
||||
@@ -125,16 +133,25 @@ def test_polyline_invalid_coordinates():
|
||||
def test_polyline_units(open_square_coords):
|
||||
polyline = Polyline(value=open_square_coords, units=Units.m)
|
||||
assert polyline.units == Units.m.value
|
||||
|
||||
polyline.units = "mm"
|
||||
assert polyline.units == "mm"
|
||||
|
||||
|
||||
def test_polyline_closed_flag(open_square_coords, closed_square_coords):
|
||||
# Test default value
|
||||
poly1 = Polyline(value=open_square_coords, units=Units.m)
|
||||
assert poly1.closed is False
|
||||
|
||||
# Test explicit value
|
||||
poly2 = Polyline(value=closed_square_coords, units=Units.m, closed=True)
|
||||
assert poly2.closed is True
|
||||
|
||||
|
||||
def test_polyline_serialization(sample_polyline):
|
||||
serialized = serialize(sample_polyline)
|
||||
deserialized = deserialize(serialized)
|
||||
|
||||
assert deserialized.value == sample_polyline.value
|
||||
assert deserialized.units == sample_polyline.units
|
||||
assert deserialized.domain.start == sample_polyline.domain.start
|
||||
assert deserialized.domain.end == sample_polyline.domain.end
|
||||
assert deserialized.closed == sample_polyline.closed
|
||||
|
||||
Reference in New Issue
Block a user