helper classes added
This commit is contained in:
@@ -8,8 +8,7 @@ from specklepy.objects.interfaces import ICurve, IHasUnits
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class Arc(Base, IHasUnits, ICurve, speckle_type="Objects.Geometry.Arc"):
|
||||
|
||||
class Arc(Base, IHasUnits, ICurve, speckle_type="Objects.Geometry.Arc", serialize_ignore={"radius", "length"}):
|
||||
plane: Plane
|
||||
startPoint: Point
|
||||
midPoint: Point
|
||||
@@ -17,15 +16,27 @@ class Arc(Base, IHasUnits, ICurve, speckle_type="Objects.Geometry.Arc"):
|
||||
|
||||
@property
|
||||
def radius(self) -> float:
|
||||
return self.startPoint.distance_to(self.plane.origin)
|
||||
return self.__dict__.get('_radius')
|
||||
|
||||
@property
|
||||
def measure(self) -> float:
|
||||
start_to_mid = self.startPoint.distance_to(self.midPoint)
|
||||
mid_to_end = self.midPoint.distance_to(self.endPoint)
|
||||
r = self.radius
|
||||
return (2 * math.asin(start_to_mid / (2 * r))) + (2 * math.asin(mid_to_end / (2 * r)))
|
||||
@radius.setter
|
||||
def radius(self, value: float) -> None:
|
||||
self.__dict__['_radius'] = value
|
||||
|
||||
@property
|
||||
def length(self) -> float:
|
||||
return self.radius * self.measure
|
||||
return self.__dict__.get('_length')
|
||||
|
||||
@length.setter
|
||||
def length(self, value: float) -> None:
|
||||
self.__dict__['_length'] = value
|
||||
|
||||
def calculate_radius(self) -> float:
|
||||
return self.startPoint.distance_to(self.plane.origin)
|
||||
|
||||
def calculate_length(self) -> float:
|
||||
start_to_mid = self.startPoint.distance_to(self.midPoint)
|
||||
mid_to_end = self.midPoint.distance_to(self.endPoint)
|
||||
r = self.calculate_radius()
|
||||
angle = (2 * math.asin(start_to_mid / (2 * r))) + \
|
||||
(2 * math.asin(mid_to_end / (2 * r)))
|
||||
return r * angle
|
||||
|
||||
@@ -6,17 +6,23 @@ from specklepy.objects.interfaces import ICurve, IHasUnits
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class Line(Base, IHasUnits, ICurve, speckle_type="Objects.Geometry.Line"):
|
||||
"""
|
||||
a line defined by two points in 3D space
|
||||
"""
|
||||
|
||||
class Line(
|
||||
Base,
|
||||
IHasUnits,
|
||||
ICurve,
|
||||
speckle_type="Objects.Geometry.Line",
|
||||
serialize_ignore={"length"}
|
||||
):
|
||||
start: Point
|
||||
end: Point
|
||||
|
||||
@property
|
||||
def length(self) -> float:
|
||||
"""
|
||||
calculate the length of the line using Point's distance_to method
|
||||
"""
|
||||
return self.__dict__.get('_length')
|
||||
|
||||
@length.setter
|
||||
def length(self, value: float) -> None:
|
||||
self.__dict__['_length'] = value
|
||||
|
||||
def calculate_length(self) -> float:
|
||||
return self.start.distance_to(self.end)
|
||||
|
||||
@@ -20,6 +20,8 @@ class Mesh(
|
||||
"colors": 62500,
|
||||
"textureCoordinates": 31250,
|
||||
},
|
||||
serialize_ignore={"area", "volume",
|
||||
"vertices_count", "texture_coordinates_count"},
|
||||
):
|
||||
"""
|
||||
a 3D mesh consisting of vertices and faces with optional colors and texture coordinates
|
||||
@@ -44,8 +46,7 @@ class Mesh(
|
||||
"""
|
||||
get the number of vertices in the mesh
|
||||
"""
|
||||
if not self.vertices:
|
||||
return 0
|
||||
|
||||
if len(self.vertices) % 3 != 0:
|
||||
raise ValueError(
|
||||
f"Invalid vertices list: length ({len(
|
||||
@@ -53,19 +54,6 @@ class Mesh(
|
||||
)
|
||||
return len(self.vertices) // 3
|
||||
|
||||
@property
|
||||
def faces_count(self) -> int:
|
||||
"""
|
||||
get the number of faces in the mesh
|
||||
"""
|
||||
count = 0
|
||||
i = 0
|
||||
while i < len(self.faces):
|
||||
n = self.faces[i]
|
||||
count += 1
|
||||
i += n + 1
|
||||
return count
|
||||
|
||||
@property
|
||||
def texture_coordinates_count(self) -> int:
|
||||
"""
|
||||
@@ -75,6 +63,21 @@ class Mesh(
|
||||
|
||||
@property
|
||||
def area(self) -> float:
|
||||
return self.__dict__.get('_area')
|
||||
|
||||
@area.setter
|
||||
def area(self, value: float) -> None:
|
||||
self.__dict__['_area'] = value
|
||||
|
||||
@property
|
||||
def volume(self) -> float:
|
||||
return self.__dict__.get('_volume')
|
||||
|
||||
@volume.setter
|
||||
def volume(self, value: float) -> None:
|
||||
self.__dict__['_volume'] = value
|
||||
|
||||
def calculate_area(self) -> float:
|
||||
"""
|
||||
calculate total surface area of the mesh
|
||||
"""
|
||||
@@ -102,8 +105,7 @@ class Mesh(
|
||||
|
||||
return total_area
|
||||
|
||||
@property
|
||||
def volume(self) -> float:
|
||||
def calculate_volume(self) -> float:
|
||||
"""
|
||||
calculate volume of the mesh if it is closed
|
||||
"""
|
||||
|
||||
@@ -7,7 +7,7 @@ from specklepy.objects.interfaces import ICurve, IHasUnits
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class Polyline(Base, IHasUnits, ICurve, speckle_type="Objects.Geometry.Polyline"):
|
||||
class Polyline(Base, IHasUnits, ICurve, speckle_type="Objects.Geometry.Polyline", serialize_ignore={"length"}):
|
||||
"""
|
||||
a polyline curve, defined by a set of vertices.
|
||||
"""
|
||||
@@ -40,6 +40,13 @@ class Polyline(Base, IHasUnits, ICurve, speckle_type="Objects.Geometry.Polyline"
|
||||
|
||||
@property
|
||||
def length(self) -> float:
|
||||
return self.__dict__.get('_length')
|
||||
|
||||
@length.setter
|
||||
def length(self, value: float) -> None:
|
||||
self.__dict__['_length'] = value
|
||||
|
||||
def calculate_length(self) -> float:
|
||||
points = self.get_points()
|
||||
total_length = 0.0
|
||||
for i in range(len(points) - 1):
|
||||
|
||||
@@ -4,7 +4,7 @@ from specklepy.objects.base import Base
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class Interval(Base, speckle_type="Objects.Primitive.Interval"):
|
||||
class Interval(Base, speckle_type="Objects.Primitive.Interval", serialize_ignore={"length"}):
|
||||
start: float = 0.0
|
||||
end: float = 0.0
|
||||
|
||||
@@ -13,8 +13,17 @@ class Interval(Base, speckle_type="Objects.Primitive.Interval"):
|
||||
|
||||
@property
|
||||
def length(self) -> float:
|
||||
return self.__dict__.get('_length')
|
||||
|
||||
@length.setter
|
||||
def length(self, value: float) -> None:
|
||||
self.__dict__['_length'] = value
|
||||
|
||||
def calculate_length(self) -> float:
|
||||
return abs(self.end - self.start)
|
||||
|
||||
@classmethod
|
||||
def unit_interval(cls) -> "Interval":
|
||||
return cls(start=0, end=1)
|
||||
interval = cls(start=0, end=1)
|
||||
interval.length = interval.calculate_length()
|
||||
return interval
|
||||
|
||||
@@ -71,19 +71,15 @@ def test_arc_domain(sample_arc):
|
||||
|
||||
|
||||
def test_arc_radius(sample_arc):
|
||||
sample_arc.radius = sample_arc.calculate_radius()
|
||||
assert sample_arc.radius == pytest.approx(1.0)
|
||||
|
||||
|
||||
def test_arc_length(sample_arc):
|
||||
# for a semicircle, length should be pi * radius
|
||||
sample_arc.length = sample_arc.calculate_length()
|
||||
assert sample_arc.length == pytest.approx(math.pi)
|
||||
|
||||
|
||||
def test_arc_measure(sample_arc):
|
||||
# for a semicircle, measure should be pi radians
|
||||
assert sample_arc.measure == pytest.approx(math.pi)
|
||||
|
||||
|
||||
def test_arc_units(sample_points, sample_plane):
|
||||
start, mid, end = sample_points
|
||||
arc = Arc(
|
||||
|
||||
@@ -42,6 +42,7 @@ def test_line_domain(sample_line):
|
||||
|
||||
def test_line_length(sample_line):
|
||||
|
||||
sample_line.length = sample_line.calculate_length()
|
||||
assert sample_line.length == 5.0
|
||||
|
||||
|
||||
|
||||
@@ -96,10 +96,6 @@ def test_mesh_vertex_count(sample_mesh):
|
||||
assert sample_mesh.vertices_count == 8 # cube has 8 vertices
|
||||
|
||||
|
||||
def test_mesh_face_count(sample_mesh):
|
||||
assert sample_mesh.faces_count == 6 # cube has 6 faces
|
||||
|
||||
|
||||
def test_mesh_texture_coordinates_count(full_mesh):
|
||||
assert full_mesh.texture_coordinates_count == 8 # one UV per vertex
|
||||
|
||||
@@ -147,24 +143,18 @@ def test_mesh_is_closed(sample_mesh):
|
||||
|
||||
def test_mesh_area(sample_mesh):
|
||||
|
||||
calculated_area = sample_mesh.calculate_area()
|
||||
sample_mesh.area = calculated_area
|
||||
assert sample_mesh.area == pytest.approx(6.0)
|
||||
|
||||
|
||||
def test_mesh_volume(sample_mesh):
|
||||
|
||||
points = sample_mesh.get_points()
|
||||
min_coords = [min(p.x for p in points), min(
|
||||
p.y for p in points), min(p.z for p in points)]
|
||||
max_coords = [max(p.x for p in points), max(
|
||||
p.y for p in points), max(p.z for p in points)]
|
||||
calculated_volume = sample_mesh.calculate_volume()
|
||||
sample_mesh.volume = calculated_volume
|
||||
|
||||
assert all(
|
||||
minimum == -0.5 for minimum in min_coords), "Cube minimum should be at -0.5"
|
||||
assert all(
|
||||
maximum == 0.5 for maximum in max_coords), "Cube maximum should be at 0.5"
|
||||
|
||||
volume = sample_mesh.volume
|
||||
assert volume == pytest.approx(1.0), f"Expected volume 1.0, got {volume}"
|
||||
# Verify volume is set correctly
|
||||
assert sample_mesh.volume == pytest.approx(1.0)
|
||||
|
||||
|
||||
def test_mesh_invalid_vertices():
|
||||
|
||||
@@ -65,11 +65,13 @@ def test_polyline_is_closed_with_tolerance(open_square_coords):
|
||||
|
||||
|
||||
def test_polyline_length_open(sample_polyline):
|
||||
sample_polyline.length = sample_polyline.calculate_length()
|
||||
assert sample_polyline.length == 3.0
|
||||
|
||||
|
||||
def test_polyline_length_closed(closed_square_coords):
|
||||
polyline = Polyline(value=closed_square_coords, units=Units.m)
|
||||
polyline.length = polyline.calculate_length()
|
||||
assert polyline.length == 4.0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user