Skip to content

Commit

Permalink
helper classes added
Browse files Browse the repository at this point in the history
  • Loading branch information
dogukankaratas committed Jan 23, 2025
1 parent 0650210 commit 516eff4
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 61 deletions.
31 changes: 21 additions & 10 deletions src/specklepy/objects/geometry/arc.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,35 @@


@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
endPoint: Point

@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
22 changes: 14 additions & 8 deletions src/specklepy/objects/geometry/line.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,23 @@


@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)
36 changes: 19 additions & 17 deletions src/specklepy/objects/geometry/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -44,28 +46,14 @@ def vertices_count(self) -> int:
"""
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(
self.vertices)}) must be a multiple of 3"
)
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:
"""
Expand All @@ -75,6 +63,21 @@ def texture_coordinates_count(self) -> int:

@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
"""
Expand Down Expand Up @@ -102,8 +105,7 @@ def area(self) -> float:

return total_area

@property
def volume(self) -> float:
def calculate_volume(self) -> float:
"""
calculate volume of the mesh if it is closed
"""
Expand Down
9 changes: 8 additions & 1 deletion src/specklepy/objects/geometry/polyline.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


@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.
"""
Expand Down Expand Up @@ -40,6 +40,13 @@ def is_closed(self, tolerance: float = 1e-6) -> bool:

@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):
Expand Down
13 changes: 11 additions & 2 deletions src/specklepy/objects/primitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


@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

Expand All @@ -13,8 +13,17 @@ def __repr__(self) -> str:

@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
8 changes: 2 additions & 6 deletions src/specklepy/objects/tests/test_arc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions src/specklepy/objects/tests/test_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
24 changes: 7 additions & 17 deletions src/specklepy/objects/tests/test_mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)]

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}"
calculated_volume = sample_mesh.calculate_volume()
sample_mesh.volume = calculated_volume

# Verify volume is set correctly
assert sample_mesh.volume == pytest.approx(1.0)


def test_mesh_invalid_vertices():
Expand Down
2 changes: 2 additions & 0 deletions src/specklepy/objects/tests/test_polyline.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down

0 comments on commit 516eff4

Please sign in to comment.