diff --git a/src/specklepy/objects/geometry/arc.py b/src/specklepy/objects/geometry/arc.py index dd513f3..2835a7a 100644 --- a/src/specklepy/objects/geometry/arc.py +++ b/src/specklepy/objects/geometry/arc.py @@ -1,12 +1,17 @@ from dataclasses import dataclass, field -from typing import List, Tuple +from typing import List, Tuple, Any import math from specklepy.objects.base import Base from specklepy.objects.geometry.point import Point from specklepy.objects.geometry.plane import Plane +from specklepy.objects.geometry.vector import Vector from specklepy.objects.interfaces import ICurve, IHasUnits, ITransformable from specklepy.objects.primitive import Interval +from specklepy.objects.models.units import ( + get_encoding_from_units, + get_units_from_encoding +) @dataclass(kw_only=True) @@ -54,40 +59,80 @@ class Arc(Base, IHasUnits, ICurve, ITransformable, speckle_type="Objects.Geometr ) return True, transformed - def to_list(self) -> List[float]: - from specklepy.objects.models.units import get_encoding_from_units - + def to_list(self) -> List[Any]: + """ + returns a serializable list of format: + [total_length, speckle_type, units_encoding, + radius, measure, + domain_start, domain_end, + plane_origin_x, plane_origin_y, plane_origin_z, + plane_normal_x, plane_normal_y, plane_normal_z, + plane_xdir_x, plane_xdir_y, plane_xdir_z, + plane_ydir_x, plane_ydir_y, plane_ydir_z, + start_x, start_y, start_z, + mid_x, mid_y, mid_z, + end_x, end_y, end_z] + """ result = [] - result.append(self.radius) - result.append(0) - result.append(0) - result.append(self.measure) - result.append(self.domain.start) - result.append(self.domain.end) - result.extend(self.plane.to_list()) - result.extend(self.startPoint.to_list()) - result.extend(self.midPoint.to_list()) - result.extend(self.endPoint.to_list()) - result.append(get_encoding_from_units(self.units)) + result.extend([self.radius, self.measure]) + result.extend([self.domain.start, self.domain.end]) + # skip length, type, units from Plane + result.extend(self.plane.to_list()[3:]) + # skip length, type, units from Point + result.extend(self.startPoint.to_list()[3:]) + # skip length, type, units from Point + result.extend(self.midPoint.to_list()[3:]) + # skip length, type, units from Point + result.extend(self.endPoint.to_list()[3:]) - result.insert(0, 1) - result.insert(0, len(result)) + # add header information + result.insert(0, get_encoding_from_units(self.units)) + result.insert(0, self.speckle_type) + result.insert(0, len(result) + 1) return result @classmethod - def from_list(cls, coords: List[float]) -> "Arc": - from specklepy.objects.models.units import get_units_from_encoding + def from_list(cls, coords: List[Any]) -> "Arc": + """ + creates an Arc from a list of format: + [total_length, speckle_type, units_encoding, + radius, measure, + domain_start, domain_end, + plane_origin_x, plane_origin_y, plane_origin_z, + plane_normal_x, plane_normal_y, plane_normal_z, + plane_xdir_x, plane_xdir_y, plane_xdir_z, + plane_ydir_x, plane_ydir_y, plane_ydir_z, + start_x, start_y, start_z, + mid_x, mid_y, mid_z, + end_x, end_y, end_z] + """ + units = get_units_from_encoding(coords[2]) - units = get_units_from_encoding(int(coords[-1])) + domain = Interval(start=coords[5], end=coords[6]) - arc = cls( - domain=Interval(start=coords[6], end=coords[7]), - units=units, - plane=Plane.from_list(coords[8:21]), - startPoint=Point.from_list(coords[21:24], units), - midPoint=Point.from_list(coords[24:27], units), - endPoint=Point.from_list(coords[27:30], units) + # extract plane components + plane = Plane( + origin=Point(x=coords[7], y=coords[8], z=coords[9], units=units), + normal=Vector(x=coords[10], y=coords[11], + z=coords[12], units=units), + xdir=Vector(x=coords[13], y=coords[14], z=coords[15], units=units), + ydir=Vector(x=coords[16], y=coords[17], z=coords[18], units=units), + units=units ) - arc.plane.units = arc.units - return arc + # extract points + start_point = Point( + x=coords[19], y=coords[20], z=coords[21], units=units) + mid_point = Point(x=coords[22], y=coords[23], + z=coords[24], units=units) + end_point = Point(x=coords[25], y=coords[26], + z=coords[27], units=units) + + return cls( + plane=plane, + startPoint=start_point, + midPoint=mid_point, + endPoint=end_point, + domain=domain, + units=units + ) diff --git a/src/specklepy/objects/geometry/line.py b/src/specklepy/objects/geometry/line.py index 342c90c..09a3d89 100644 --- a/src/specklepy/objects/geometry/line.py +++ b/src/specklepy/objects/geometry/line.py @@ -1,11 +1,14 @@ from dataclasses import dataclass, field -from typing import List, Tuple +from typing import List, Tuple, Any 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 from specklepy.objects.primitive import Interval +from specklepy.objects.models.units import ( + Units, + get_encoding_from_units +) @dataclass(kw_only=True) @@ -29,23 +32,53 @@ class Line(Base, IHasUnits, ICurve, speckle_type="Objects.Geometry.Line"): def _domain(self) -> Interval: return self.domain - def to_list(self) -> List[float]: + def to_list(self) -> List[Any]: + """ + returns a serializable list of format: + [total_length, speckle_type, units_encoding, + start_x, start_y, start_z, + end_x, end_y, end_z, + domain_start, domain_end] + """ result = [] - result.extend(self.start.to_list()) - result.extend(self.end.to_list()) + # skip length, type, units from Point + result.extend(self.start.to_list()[3:]) + # skip length, type, units from Point + result.extend(self.end.to_list()[3:]) result.extend([self.domain.start, self.domain.end]) + + # add header information + result.insert(0, get_encoding_from_units(self.units)) + result.insert(0, self.speckle_type) + result.insert(0, len(result) + 1) # +1 for the length we're adding return result @classmethod - def from_list(cls, coords: List[float], units: str | Units) -> "Line": - if len(coords) < 6: - raise ValueError( - "Line from coordinate array requires 6 coordinates.") - - start = Point(x=coords[0], y=coords[1], z=coords[2], units=units) - end = Point(x=coords[3], y=coords[4], z=coords[5], units=units) - - return cls(start=start, end=end, units=units) + def from_list(cls, coords: List[Any], units: str | Units) -> "Line": + """ + creates a Line from a list of format: + [total_length, speckle_type, units_encoding, + start_x, start_y, start_z, + end_x, end_y, end_z, + domain_start, domain_end] + """ + start = Point( + x=coords[3], + y=coords[4], + z=coords[5], + units=units + ) + end = Point( + x=coords[6], + y=coords[7], + z=coords[8], + units=units + ) + domain = Interval( + start=coords[9], + end=coords[10] + ) + return cls(start=start, end=end, domain=domain, units=units) @classmethod def from_coords( diff --git a/src/specklepy/objects/geometry/mesh.py b/src/specklepy/objects/geometry/mesh.py index 9a58672..0d797de 100644 --- a/src/specklepy/objects/geometry/mesh.py +++ b/src/specklepy/objects/geometry/mesh.py @@ -1,10 +1,16 @@ from dataclasses import dataclass, field -from typing import List, Tuple +from typing import List, Tuple, Any from specklepy.objects.base import Base from specklepy.objects.geometry.point import Point from specklepy.objects.interfaces import IHasArea, IHasVolume, IHasUnits, ITransformable -from specklepy.objects.models.units import Units, get_scale_factor, get_units_from_string +from specklepy.objects.models.units import ( + Units, + get_scale_factor, + get_units_from_string, + get_units_from_encoding, + get_encoding_from_units +) @dataclass(kw_only=True) @@ -185,3 +191,95 @@ class Mesh( i += vertex_count + 1 return all(count == 2 for count in edge_counts.values()) + + def to_list(self) -> List[Any]: + """Returns a serializable list of format: + [total_length, speckle_type, units_encoding, + vertices_count, v1x, v1y, v1z, v2x, v2y, v2z, ..., + faces_count, f1, f2, f3, ..., + colors_count, c1, c2, c3, ..., + texture_coords_count, t1u, t1v, t2u, t2v, ..., + area, volume]""" + result = [] + + # Add vertices + result.append(len(self.vertices)) + result.extend(self.vertices) + + # Add faces + result.append(len(self.faces)) + result.extend(self.faces) + + # Add colors (if any) + result.append(len(self.colors)) + if self.colors: + result.extend(self.colors) + + # Add texture coordinates (if any) + result.append(len(self.textureCoordinates)) + if self.textureCoordinates: + result.extend(self.textureCoordinates) + + # Add area and volume + result.extend([self.area, self.volume]) + + # Add header information + result.insert(0, get_encoding_from_units(self.units)) + result.insert(0, self.speckle_type) + result.insert(0, len(result) + 1) + return result + + @classmethod + def from_list(cls, coords: List[Any]) -> "Mesh": + """Creates a Mesh from a list of format: + [total_length, speckle_type, units_encoding, + vertices_count, v1x, v1y, v1z, v2x, v2y, v2z, ..., + faces_count, f1, f2, f3, ..., + colors_count, c1, c2, c3, ..., + texture_coords_count, t1u, t1v, t2u, t2v, ..., + area, volume]""" + units = get_units_from_encoding(coords[2]) + + index = 3 + + # Extract vertices + vertices_count = int(coords[index]) + index += 1 + vertices = coords[index:index + vertices_count] + index += vertices_count + + # Extract faces + faces_count = int(coords[index]) + index += 1 + faces = [int(f) for f in coords[index:index + faces_count]] + index += faces_count + + # Extract colors + colors_count = int(coords[index]) + index += 1 + colors = [] + if colors_count > 0: + colors = [int(c) for c in coords[index:index + colors_count]] + index += colors_count + + # Extract texture coordinates + texture_coords_count = int(coords[index]) + index += 1 + texture_coords = [] + if texture_coords_count > 0: + texture_coords = coords[index:index + texture_coords_count] + index += texture_coords_count + + # Extract area and volume + area = coords[index] + volume = coords[index + 1] + + return cls( + vertices=vertices, + faces=faces, + colors=colors, + textureCoordinates=texture_coords, + area=area, + volume=volume, + units=units + ) diff --git a/src/specklepy/objects/geometry/plane.py b/src/specklepy/objects/geometry/plane.py index 4920934..d6e6a7a 100644 --- a/src/specklepy/objects/geometry/plane.py +++ b/src/specklepy/objects/geometry/plane.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import List, Tuple +from typing import List, Tuple, Any from specklepy.objects.base import Base from specklepy.objects.geometry.point import Point @@ -32,34 +32,67 @@ class Plane(Base, ITransformable, IHasUnits, speckle_type="Objects.Geometry.Plan f"units: {self.units})" ) - def to_list(self) -> List[float]: - """ - returns the values of this Plane as a list of numbers + def to_list(self) -> List[Any]: """ + returns a serializable list of format: + [total_length, speckle_type, units_encoding, + origin_x, origin_y, origin_z, + normal_x, normal_y, normal_z, + xdir_x, xdir_y, xdir_z, + ydir_x, ydir_y, ydir_z] + """ result = [] - result.extend(self.origin.to_list()) - result.extend(self.normal.to_list()) - result.extend(self.xdir.to_list()) - result.extend(self.ydir.to_list()) - result.append(get_encoding_from_units(self.units)) + # skip length, type, units from Point + result.extend(self.origin.to_list()[3:]) + # skip length, type, units from Vector + result.extend(self.normal.to_list()[3:]) + # skip length, type, units from Vector + result.extend(self.xdir.to_list()[3:]) + # skip length, type, units from Vector + result.extend(self.ydir.to_list()[3:]) + + # add header information + result.insert(0, get_encoding_from_units(self.units)) + result.insert(0, self.speckle_type) + result.insert(0, len(result) + 1) return result @classmethod - def from_list(cls, coords: List[float]) -> "Plane": + def from_list(cls, coords: List[Any]) -> "Plane": """ - creates a new Plane based on a list of values + creates a Plane from a list of format: + [total_length, speckle_type, units_encoding, + origin_x, origin_y, origin_z, + normal_x, normal_y, normal_z, + xdir_x, xdir_y, xdir_z, + ydir_x, ydir_y, ydir_z] """ - units = get_units_from_encoding(int(coords[-1])) + units = get_units_from_encoding(coords[2]) - plane = cls( - origin=Point(x=coords[0], y=coords[1], z=coords[2], units=units), - normal=Vector(x=coords[3], y=coords[4], z=coords[5], units=units), - xdir=Vector(x=coords[6], y=coords[7], z=coords[8], units=units), - ydir=Vector(x=coords[9], y=coords[10], z=coords[11], units=units), - units=units, + origin = Point( + x=coords[3], y=coords[4], z=coords[5], + units=units + ) + normal = Vector( + x=coords[6], y=coords[7], z=coords[8], + units=units + ) + xdir = Vector( + x=coords[9], y=coords[10], z=coords[11], + units=units + ) + ydir = Vector( + x=coords[12], y=coords[13], z=coords[14], + units=units ) - return plane + return cls( + origin=origin, + normal=normal, + xdir=xdir, + ydir=ydir, + units=units + ) def transform_to(self, transform) -> Tuple[bool, Base]: """ diff --git a/src/specklepy/objects/geometry/point.py b/src/specklepy/objects/geometry/point.py index 8fc14ce..851e456 100644 --- a/src/specklepy/objects/geometry/point.py +++ b/src/specklepy/objects/geometry/point.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import List, Tuple +from typing import List, Tuple, Any from specklepy.objects.base import Base from specklepy.objects.interfaces import IHasUnits, ITransformable @@ -7,6 +7,7 @@ from specklepy.objects.models.units import ( Units, get_scale_factor, get_units_from_string, + get_encoding_from_units ) @@ -23,12 +24,25 @@ class Point(Base, IHasUnits, ITransformable, speckle_type="Objects.Geometry.Poin def __repr__(self) -> str: return f"{self.__class__.__name__}(x: {self.x}, y: {self.y}, z: {self.z}, units: {self.units})" - def to_list(self) -> List[float]: - return [self.x, self.y, self.z] + def to_list(self) -> List[Any]: + """ + returns a serializable list of format: + [total_length, speckle_type, units_encoding, x, y, z] + """ + result = [self.x, self.y, self.z] + result.insert(0, get_encoding_from_units(self.units)) + result.insert(0, self.speckle_type) + result.insert(0, len(result) + 1) # +1 for the length we're adding + return result @classmethod - def from_list(cls, coords: List[float], units: str | Units) -> "Point": - return cls(x=coords[0], y=coords[1], z=coords[2], units=units) + def from_list(cls, coords: List[Any], units: str | Units) -> "Point": + """ + creates a Point from a list of format: + [total_length, speckle_type, units_encoding, x, y, z] + """ + x, y, z = coords[3:6] # geometric data starts at index 3 + return cls(x=x, y=y, z=z, units=units) @classmethod def from_coords(cls, x: float, y: float, z: float, units: str | Units) -> "Point": diff --git a/src/specklepy/objects/geometry/polyline.py b/src/specklepy/objects/geometry/polyline.py index c859c38..323a2fb 100644 --- a/src/specklepy/objects/geometry/polyline.py +++ b/src/specklepy/objects/geometry/polyline.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import List, Tuple +from typing import List, Tuple, Any from specklepy.objects.base import Base from specklepy.objects.geometry.point import Point @@ -59,33 +59,47 @@ class Polyline(Base, IHasUnits, ICurve, ITransformable, speckle_type="Objects.Ge ) return points - def to_list(self) -> List[float]: - """ - returns the values of this Polyline as a list of numbers + def to_list(self) -> List[Any]: """ + returns a serializable list of format: + [total_length, speckle_type, units_encoding, + is_closed, + domain_start, domain_end, + coords_count, + x1, y1, z1, x2, y2, z2, ...] + """ result = [] - result.append(len(self.value) + 6) # total list length - # type indicator for polyline ?? not sure about this - result.append("Objects.Geometry.Polyline") - result.append(1 if self.closed else 0) - result.append(self.domain.start) - result.append(self.domain.end) - result.append(len(self.value)) - result.extend(self.value) - result.append(get_encoding_from_units(self.units)) + result.append(1 if self.closed else 0) # convert bool to int + result.extend([self.domain.start, self.domain.end]) + result.append(len(self.value)) # number of coordinates + result.extend(self.value) # all vertex coordinates + + # add header information + result.insert(0, get_encoding_from_units(self.units)) + result.insert(0, self.speckle_type) + result.insert(0, len(result) + 1) return result @classmethod - def from_list(cls, coords: List[float], units: str | Units) -> "Polyline": + def from_list(cls, coords: List[Any], units: str | Units) -> "Polyline": """ - creates a new Polyline based on a list of coordinates - """ - point_count = int(coords[5]) + creates a Polyline from a list of format: + [total_length, speckle_type, units_encoding, + is_closed, + domain_start, domain_end, + coords_count, + x1, y1, z1, x2, y2, z2, ...] + """ + is_closed = bool(coords[3]) + domain = Interval(start=coords[4], end=coords[5]) + coords_count = int(coords[6]) + vertex_coords = coords[7:7 + coords_count] + return cls( - closed=(int(coords[2]) == 1), - domain=Interval(start=coords[3], end=coords[4]), - value=coords[6: 6 + point_count], - units=units, + value=vertex_coords, + closed=is_closed, + domain=domain, + units=units ) def transform_to(self, transform) -> Tuple[bool, "Polyline"]: diff --git a/src/specklepy/objects/geometry/vector.py b/src/specklepy/objects/geometry/vector.py index ab0bfab..2452e20 100644 --- a/src/specklepy/objects/geometry/vector.py +++ b/src/specklepy/objects/geometry/vector.py @@ -1,9 +1,12 @@ from dataclasses import dataclass -from typing import List +from typing import List, Any from specklepy.objects.base import Base from specklepy.objects.interfaces import IHasUnits, ITransformable -from specklepy.objects.models.units import Units +from specklepy.objects.models.units import ( + Units, + get_encoding_from_units +) @dataclass(kw_only=True) @@ -23,12 +26,25 @@ class Vector(Base, IHasUnits, ITransformable, speckle_type="Objects.Geometry.Vec def length(self) -> float: return (self.x ** 2 + self.y ** 2 + self.z ** 2) ** 0.5 - def to_list(self) -> List[float]: - return [self.x, self.y, self.z] + def to_list(self) -> List[Any]: + """ + returns a serializable list of format: + [total_length, speckle_type, units_encoding, x, y, z] + """ + result = [self.x, self.y, self.z] + result.insert(0, get_encoding_from_units(self.units)) + result.insert(0, self.speckle_type) + result.insert(0, len(result) + 1) # +1 for the length we're adding + return result @classmethod - def from_list(cls, coords: List[float], units: str | Units) -> "Vector": - return cls(x=coords[0], y=coords[1], z=coords[2], units=units) + def from_list(cls, coords: List[Any], units: str | Units) -> "Vector": + """ + creates a Vector from a list of format: + [total_length, speckle_type, units_encoding, x, y, z] + """ + x, y, z = coords[3:6] # geometric data starts at index 3 + return cls(x=x, y=y, z=z, units=units) def transform_to(self, transform): m = transform.matrix diff --git a/src/specklepy/objects/tests/test_arc.py b/src/specklepy/objects/tests/test_arc.py index f94dffc..21b7b57 100644 --- a/src/specklepy/objects/tests/test_arc.py +++ b/src/specklepy/objects/tests/test_arc.py @@ -87,3 +87,65 @@ def test_arc_domain(sample_arc): assert sample_arc.domain.start == 0.0 assert sample_arc.domain.end == 1.0 assert sample_arc._domain == sample_arc.domain + + +def test_arc_to_list(): + plane = Plane( + origin=Point(x=0.0, y=0.0, z=0.0, units="m"), + normal=Vector(x=0.0, y=0.0, z=1.0, units="m"), + xdir=Vector(x=1.0, y=0.0, z=0.0, units="m"), + ydir=Vector(x=0.0, y=1.0, z=0.0, units="m"), + units="m" + ) + arc = Arc( + plane=plane, + startPoint=Point(x=1.0, y=0.0, z=0.0, units="m"), + midPoint=Point(x=0.7071, y=0.7071, z=0.0, units="m"), + endPoint=Point(x=0.0, y=1.0, z=0.0, units="m"), + domain=Interval(start=0.0, end=1.0), + units="m" + ) + + coords = arc.to_list() + assert len(coords) == 28 # total_length, type, units_encoding + data + assert coords[0] == 28 # total length + assert coords[1] == "Objects.Geometry.Arc" # speckle type + assert coords[5:7] == [0.0, 1.0] # domain + # Check plane coordinates + assert coords[7:10] == [0.0, 0.0, 0.0] # plane origin + assert coords[10:13] == [0.0, 0.0, 1.0] # plane normal + assert coords[13:16] == [1.0, 0.0, 0.0] # plane xdir + assert coords[16:19] == [0.0, 1.0, 0.0] # plane ydir + # Check point coordinates + assert coords[19:22] == [1.0, 0.0, 0.0] # start point + assert coords[22:25] == [0.7071, 0.7071, 0.0] # mid point + assert coords[25:28] == [0.0, 1.0, 0.0] # end point + + +def test_arc_from_list(): + coords = [ + 28, "Objects.Geometry.Arc", 3, # header + 1.0, 1.5708, # radius, measure + 0.0, 1.0, # domain + 0.0, 0.0, 0.0, # plane origin + 0.0, 0.0, 1.0, # plane normal + 1.0, 0.0, 0.0, # plane xdir + 0.0, 1.0, 0.0, # plane ydir + 1.0, 0.0, 0.0, # start point + 0.7071, 0.7071, 0.0, # mid point + 0.0, 1.0, 0.0 # end point + ] + + arc = Arc.from_list(coords) + assert arc.units == "m" + assert arc.domain.start == 0.0 + assert arc.domain.end == 1.0 + assert arc.startPoint.x == 1.0 + assert arc.startPoint.y == 0.0 + assert arc.startPoint.z == 0.0 + assert abs(arc.midPoint.x - 0.7071) < 0.0001 + assert abs(arc.midPoint.y - 0.7071) < 0.0001 + assert arc.midPoint.z == 0.0 + assert arc.endPoint.x == 0.0 + assert arc.endPoint.y == 1.0 + assert arc.endPoint.z == 0.0 diff --git a/src/specklepy/objects/tests/test_line.py b/src/specklepy/objects/tests/test_line.py index a3deefe..c27e343 100644 --- a/src/specklepy/objects/tests/test_line.py +++ b/src/specklepy/objects/tests/test_line.py @@ -49,11 +49,18 @@ def test_line_serialization(sample_line): def test_line_from_list(): - coords = [0.0, 0.0, 0.0, 3.0, 4.0, 0.0] - line = Line.from_list(coords, Units.m) - assert line.start.x == 0.0 - assert line.end.x == 3.0 - assert line.units == Units.m.value + coords = [11, "Objects.Geometry.Line", 3, + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 0.0, 1.0] + line = Line.from_list(coords, "m") + assert line.start.x == 1.0 + assert line.start.y == 2.0 + assert line.start.z == 3.0 + assert line.end.x == 4.0 + assert line.end.y == 5.0 + assert line.end.z == 6.0 + assert line.domain.start == 0.0 + assert line.domain.end == 1.0 + assert line.units == "m" def test_line_from_coords(): diff --git a/src/specklepy/objects/tests/test_mesh.py b/src/specklepy/objects/tests/test_mesh.py index ad30119..b050543 100644 --- a/src/specklepy/objects/tests/test_mesh.py +++ b/src/specklepy/objects/tests/test_mesh.py @@ -134,3 +134,79 @@ def test_mesh_convert_units(sample_mesh): assert sample_mesh.area == 1.0 * (1000 ** 2) assert sample_mesh.volume == 0.0 + + +def test_mesh_to_list(): + mesh = Mesh( + vertices=[0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0], + faces=[3, 0, 1, 2], + colors=[255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255], + textureCoordinates=[0.0, 0.0, 1.0, 0.0, 0.0, 1.0], + units="m", + area=0.5, + volume=0.0 + ) + + coords = mesh.to_list() + assert len(coords) > 3 # Should have at least header info + assert coords[0] > 3 # total length + assert coords[1] == "Objects.Geometry.Mesh" # speckle type + + index = 3 + vertices_count = coords[index] + assert vertices_count == 9 # 3 vertices * 3 coordinates + index += 1 + assert coords[index:index + vertices_count] == [0.0, + 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0] + + index += vertices_count + faces_count = coords[index] + assert faces_count == 4 # 1 face with 3 vertices + count + index += 1 + assert coords[index:index + faces_count] == [3, 0, 1, 2] + + index += faces_count + colors_count = coords[index] + assert colors_count == 12 # 3 vertices * 4 rgba values + index += 1 + assert coords[index:index + colors_count] == [255, + 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255] + + index += colors_count + texture_coords_count = coords[index] + assert texture_coords_count == 6 # 3 vertices * 2 uv coordinates + index += 1 + assert coords[index:index + texture_coords_count] == [0.0, + 0.0, 1.0, 0.0, 0.0, 1.0] + + index += texture_coords_count + assert coords[index] == 0.5 # area + assert coords[index + 1] == 0.0 # volume + + +def test_mesh_from_list(): + coords = [ + 26, "Objects.Geometry.Mesh", 3, # header + 9, # vertices count + 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, # vertices + 4, # faces count + 3, 0, 1, 2, # faces + 12, # colors count + 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, # colors + 6, # texture coordinates count + 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, # texture coordinates + 0.5, 0.0 # area, volume + ] + + mesh = Mesh.from_list(coords) + assert mesh.units == "m" + assert len(mesh.vertices) == 9 + assert mesh.vertices == [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0] + assert len(mesh.faces) == 4 + assert mesh.faces == [3, 0, 1, 2] + assert len(mesh.colors) == 12 + assert mesh.colors == [255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255] + assert len(mesh.textureCoordinates) == 6 + assert mesh.textureCoordinates == [0.0, 0.0, 1.0, 0.0, 0.0, 1.0] + assert mesh.area == 0.5 + assert mesh.volume == 0.0 diff --git a/src/specklepy/objects/tests/test_plane.py b/src/specklepy/objects/tests/test_plane.py index eb1689f..2649de3 100644 --- a/src/specklepy/objects/tests/test_plane.py +++ b/src/specklepy/objects/tests/test_plane.py @@ -50,22 +50,40 @@ def test_plane_serialization(sample_plane): assert deserialized.units == sample_plane.units -def test_plane_to_list(sample_plane): - coords = sample_plane.to_list() - assert len(coords) == 13 +def test_plane_to_list(): + plane = Plane( + origin=Point(x=1.0, y=2.0, z=3.0, units="m"), + normal=Vector(x=0.0, y=0.0, z=1.0, units="m"), + xdir=Vector(x=1.0, y=0.0, z=0.0, units="m"), + ydir=Vector(x=0.0, y=1.0, z=0.0, units="m"), + units="m" + ) + coords = plane.to_list() + # total_length, type, units_encoding + 12 coordinates + assert len(coords) == 15 + assert coords[3:6] == [1.0, 2.0, 3.0] # origin + assert coords[6:9] == [0.0, 0.0, 1.0] # normal + assert coords[9:12] == [1.0, 0.0, 0.0] # xdir + assert coords[12:15] == [0.0, 1.0, 0.0] # ydir def test_plane_from_list(): - coords = [ - 0.0, 0.0, 0.0, # origin - 0.0, 0.0, 1.0, # normal - 1.0, 0.0, 0.0, # xdir - 0.0, 1.0, 0.0, # ydir - 1 # units encoding (1 = mm) - ] + coords = [15, "Objects.Geometry.Plane", 3, # header + 1.0, 2.0, 3.0, # origin + 0.0, 0.0, 1.0, # normal + 1.0, 0.0, 0.0, # xdir + 0.0, 1.0, 0.0] # ydir plane = Plane.from_list(coords) - assert plane.origin.x == 0.0 + assert plane.origin.x == 1.0 + assert plane.origin.y == 2.0 + assert plane.origin.z == 3.0 + assert plane.normal.x == 0.0 + assert plane.normal.y == 0.0 assert plane.normal.z == 1.0 assert plane.xdir.x == 1.0 + assert plane.xdir.y == 0.0 + assert plane.xdir.z == 0.0 + assert plane.ydir.x == 0.0 assert plane.ydir.y == 1.0 - assert plane.units == "mm" + assert plane.ydir.z == 0.0 + assert plane.units == "m" diff --git a/src/specklepy/objects/tests/test_point.py b/src/specklepy/objects/tests/test_point.py index cb054df..4476df8 100644 --- a/src/specklepy/objects/tests/test_point.py +++ b/src/specklepy/objects/tests/test_point.py @@ -51,3 +51,22 @@ def test_point_serialization(): assert deserialized.y == p1.y assert deserialized.z == p1.z assert deserialized.units == p1.units + + +def test_point_to_list(): + point = Point(x=1.0, y=2.0, z=3.0, units="m") + coords = point.to_list() + # total_length, type, units_encoding + 3 coordinates + assert len(coords) == 6 + assert coords[0] == 6 # total length + assert coords[1] == "Objects.Geometry.Point" # speckle type + assert coords[3:] == [1.0, 2.0, 3.0] # coordinates + + +def test_point_from_list(): + coords = [6, "Objects.Geometry.Point", 3, 1.0, 2.0, 3.0] + point = Point.from_list(coords, "m") + assert point.x == 1.0 + assert point.y == 2.0 + assert point.z == 3.0 + assert point.units == "m" diff --git a/src/specklepy/objects/tests/test_polyline.py b/src/specklepy/objects/tests/test_polyline.py index 8f849b0..b408039 100644 --- a/src/specklepy/objects/tests/test_polyline.py +++ b/src/specklepy/objects/tests/test_polyline.py @@ -99,32 +99,29 @@ def test_polyline_serialization(sample_polyline): assert deserialized.domain.end == sample_polyline.domain.end -def test_polyline_to_list(sample_polyline): - result = sample_polyline.to_list() - - assert isinstance(result, list) - assert result[2] == 1 - assert result[3] == 0.0 - assert result[4] == 1.0 - assert result[5] == len(sample_polyline.value) +def test_polyline_to_list(): + polyline = Polyline( + value=[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], + closed=True, + domain=Interval(start=0.0, end=1.0), + units="m" + ) + coords = polyline.to_list() + assert coords[3] == 1 # is_closed (True = 1) + assert coords[4:6] == [0.0, 1.0] # domain + assert coords[6] == 6 # coords_count + assert coords[7:] == [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] # coordinates def test_polyline_from_list(): - input_list = [ - 18, "Objects.Geometry.Polyline", - 1, - 0.0, 1.0, - 12, - 0.0, 0.0, 0.0, - 1.0, 0.0, 0.0, - 1.0, 1.0, 0.0, - 0.0, 1.0, 0.0 - ] - - polyline = Polyline.from_list(input_list, Units.m) - + coords = [11, "Objects.Geometry.Polyline", 3, # header + 1, # closed + 0.0, 1.0, # domain + 6, # coords_count + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0] # coordinates + polyline = Polyline.from_list(coords, "m") assert polyline.closed is True - assert len(polyline.value) == 12 - assert polyline.units == Units.m.value assert polyline.domain.start == 0.0 assert polyline.domain.end == 1.0 + assert polyline.value == [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] + assert polyline.units == "m" diff --git a/src/specklepy/objects/tests/test_vector.py b/src/specklepy/objects/tests/test_vector.py index 066390d..49476be 100644 --- a/src/specklepy/objects/tests/test_vector.py +++ b/src/specklepy/objects/tests/test_vector.py @@ -56,15 +56,16 @@ def test_vector_serialization(sample_vectors): def test_vector_from_list(): - coords = [1.0, 2.0, 3.0] - v = Vector.from_list(coords, Units.m) - assert v.x == 1.0 - assert v.y == 2.0 - assert v.z == 3.0 - assert v.units == Units.m.value + coords = [6, "Objects.Geometry.Vector", 3, 1.0, 2.0, 3.0] + vector = Vector.from_list(coords, "m") + assert vector.x == 1.0 + assert vector.y == 2.0 + assert vector.z == 3.0 + assert vector.units == "m" -def test_vector_to_list(sample_vectors): - v1, _ = sample_vectors - coords = v1.to_list() - assert coords == [1.0, 2.0, 3.0] +def test_vector_to_list(): + vector = Vector(x=1.0, y=2.0, z=3.0, units="m") + coords = vector.to_list() + assert len(coords) == 6 + assert coords[3:] == [1.0, 2.0, 3.0]