diff --git a/ladybug_geometry/geometry3d/cone.py b/ladybug_geometry/geometry3d/cone.py index 150227fb..058c19fc 100644 --- a/ladybug_geometry/geometry3d/cone.py +++ b/ladybug_geometry/geometry3d/cone.py @@ -1,5 +1,5 @@ # coding=utf-8 -"""Cone3D""" +"""Cone""" from __future__ import division from .pointvector import Point3D, Vector3D @@ -7,8 +7,8 @@ import math -class Cone3D(object): - """Cone3D object. +class Cone(object): + """Cone object. Args: vertex: A Point3D at the tip of the cone. @@ -30,12 +30,12 @@ class Cone3D(object): __slots__ = ('_vertex', '_axis', '_angle') def __init__(self, vertex, axis, angle): - """Initilize Cone3D. + """Initilize Cone. """ assert isinstance(vertex, Point3D), \ "Expected Point3D. Got {}.".format(type(vertex)) assert isinstance(axis, Vector3D), \ - "Expected Vector3D. Got {}.".format(type(vertex)) + "Expected Vector3D. Got {}.".format(type(axis)) assert angle > 0, 'Cone angle must be greater than 0. Got {}.'.format(angle) self._vertex = vertex self._axis = axis @@ -43,7 +43,7 @@ def __init__(self, vertex, axis, angle): @classmethod def from_dict(cls, data): - """Create a Cone3D from a dictionary. + """Create a Cone from a dictionary. Args: data: A python dictionary in the following format @@ -51,7 +51,7 @@ def from_dict(cls, data): .. code-block:: python { - "type": "Cone3D" + "type": "Cone" "vertex": (10, 0, 0), "axis": (0, 0, 1), "angle": 1.0, @@ -107,7 +107,7 @@ def move(self, moving_vec): Args: moving_vec: A Vector3D with the direction and distance to move the cone. """ - return Cone3D(self.vertex.move(moving_vec), self.axis, self.angle) + return Cone(self.vertex.move(moving_vec), self.axis, self.angle) def rotate(self, axis, angle, origin): """Rotate this cone by a certain angle around an axis and origin. @@ -119,22 +119,22 @@ def rotate(self, axis, angle, origin): Args: axis: A Vector3D axis representing the axis of rotation. angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the sphere will be rotated. + origin: A Point3D for the origin around which the cone will be rotated. """ - return Cone3D(self.vertex.rotate(axis, angle, origin), - self.axis.rotate(axis, angle), - self.angle) + return Cone(self.vertex.rotate(axis, angle, origin), + self.axis.rotate(axis, angle), + self.angle) def rotate_xy(self, angle, origin): """Get a cone that is rotated counterclockwise in the world XY plane by an angle. Args: angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the sphere will be rotated. + origin: A Point3D for the origin around which the cone will be rotated. """ - return Cone3D(self.vertex.rotate_xy(angle, origin), - self.axis.rotate_xy(angle), - self.angle) + return Cone(self.vertex.rotate_xy(angle, origin), + self.axis.rotate_xy(angle), + self.angle) def reflect(self, normal, origin): """Get a cone reflected across a plane with the input normal vector and origin. @@ -144,9 +144,9 @@ def reflect(self, normal, origin): which the arc will be reflected. THIS VECTOR MUST BE NORMALIZED. origin: A Point3D representing the origin from which to reflect. """ - return Cone3D(self.vertex.reflect(normal, origin), - self.axis.reflect(normal), - self.angle) + return Cone(self.vertex.reflect(normal, origin), + self.axis.reflect(normal), + self.angle) def scale(self, factor, origin=None): """Scale a cone by a factor from an origin point. @@ -156,9 +156,9 @@ def scale(self, factor, origin=None): origin: A Point3D representing the origin from which to scale. If None, it will be scaled from the World origin (0, 0, 0). """ - return Cone3D(self.vertex.scale(factor, origin), - self.axis * factor, - self.angle) + return Cone(self.vertex.scale(factor, origin), + self.axis * factor, + self.angle) def duplicate(self): """Get a copy of this object.""" @@ -166,11 +166,11 @@ def duplicate(self): def to_dict(self): """Get Cone as a dictionary.""" - return {'type': 'Cone3D', 'vertex': self.vertex.to_array(), + return {'type': 'Cone', 'vertex': self.vertex.to_array(), 'axis': self.axis.to_array(), 'angle': self.angle} def __copy__(self): - return Cone3D(self.vertex, self.axis, self.angle) + return Cone(self.vertex, self.axis, self.angle) def __key(self): """A tuple based on the object properties, useful for hashing.""" @@ -180,7 +180,7 @@ def __hash__(self): return hash(self.__key()) def __eq__(self, other): - return isinstance(other, Cone3D) and self.__key() == other.__key() + return isinstance(other, Cone) and self.__key() == other.__key() def __ne__(self, other): return not self.__eq__(other) @@ -190,5 +190,5 @@ def ToString(self): return self.__repr__() def __repr__(self): - return 'Cone3D (vertex {}) (axis {}) (angle {}) (height {})'.\ + return 'Cone (vertex {}) (axis {}) (angle {}) (height {})'.\ format(self.vertex, self.axis, self.angle, self.height) diff --git a/ladybug_geometry/geometry3d/cylinder.py b/ladybug_geometry/geometry3d/cylinder.py new file mode 100644 index 00000000..0be14059 --- /dev/null +++ b/ladybug_geometry/geometry3d/cylinder.py @@ -0,0 +1,208 @@ +# coding=utf-8 +"""Cylinder""" +from __future__ import division + +from .pointvector import Point3D, Vector3D + +import math + + +class Cylinder(object): + """Cylinder object. + + Args: + center: A Point3D at the center of the bottom base of the cylinder. + axis: A Vector3D representing the direction and height of the cylinder. + The vector extends from the bottom base center to the top base center. + radius: A number representing the radius of the cylinder. + + Properties: + * center + * axis + * radius + * center_end + * diameter + * height + * area + * volume + """ + __slots__ = ('_center', '_axis', '_radius') + + def __init__(self, center, axis, radius): + """Initilize Cylinder. + """ + assert isinstance(center, Point3D), \ + "Expected Point3D. Got {}.".format(type(center)) + assert isinstance(axis, Vector3D), \ + "Expected Vector3D. Got {}.".format(type(axis)) + assert radius > 0, \ + 'Cylinder radius must be greater than 0. Got {}.'.format(radius) + self._center = center + self._axis = axis + self._radius = radius + + @classmethod + def from_start_end(cls, p1, p2, radius): + """Initialize a new cylinder from start and end points. + + Args: + p1: The start point of the cylinder, represents the center of the + bottom base of the cylinder. + p2: The end point of the cylinder, represents the center of the top + base of the cylinder + radius: A number representing the radius of the cylinder. + """ + axis = p2 - p1 + return cls(p1, axis, radius) + + @classmethod + def from_dict(cls, data): + """Create a Cylinder from a dictionary. + + Args: + data: A python dictionary in the following format + + .. code-block:: python + + { + "type": "Cylinder" + "center": (10, 0, 0), + "axis": (0, 0, 1), + "radius": 1.0, + } + """ + return cls(Point3D.from_array(data['center']), + Vector3D.from_array(data['axis']), + data['radius']) + + @property + def center(self): + """Center of Cylinder.""" + return self._center + + @property + def axis(self): + """Axis of Cylinder.""" + return self._axis + + @property + def radius(self): + """Radius of Cylinder""" + return self._radius + + @property + def center_end(self): + """Center of oposite end of Cylinder.""" + return self.center + self.axis + + @property + def diameter(self): + """Diameter of Cylinder""" + return self.radius * 2 + + @property + def height(self): + """Height of Cylinder""" + return self.axis.magnitude + + @property + def area(self): + """Surface area of a Cylinder""" + return 2 * math.pi * self.radius * self.height + 2 * math.pi * self.radius ** 2 + + @property + def volume(self): + """Volume of a Cylinder""" + return math.pi * self.radius ** 2 * self.height + + def move(self, moving_vec): + """Get a Cylinder that has been moved along a vector. + + Args: + moving_vec: A Vector3D with the direction and distance to move the Cylinder. + """ + return Cylinder(self.center.move(moving_vec), self.axis, self.radius) + + def rotate(self, axis, angle, origin): + """Rotate this Cylinder by a certain angle around an axis and origin. + + Right hand rule applies: + If axis has a positive orientation, rotation will be clockwise. + If axis has a negative orientation, rotation will be counterclockwise. + + Args: + axis: A Vector3D axis representing the axis of rotation. + angle: An angle for rotation in radians. + origin: A Point3D for the origin around which the cylinder will be rotated. + """ + return Cylinder(self.center.rotate(axis, angle, origin), + self.axis.rotate(axis, angle), + self.radius) + + def rotate_xy(self, angle, origin): + """Get a Cylinder that is rotated counterclockwise in the world XY plane by an angle. + + Args: + angle: An angle for rotation in radians. + origin: A Point3D for the origin around which the cylinder will be rotated. + """ + return Cylinder(self.center.rotate_xy(angle, origin), + self.axis.rotate_xy(angle), + self.radius) + + def reflect(self, normal, origin): + """Get a Cylinder reflected across a plane with the input normal vector and origin. + + Args: + normal: A Vector3D representing the normal vector for the plane across + which the arc will be reflected. THIS VECTOR MUST BE NORMALIZED. + origin: A Point3D representing the origin from which to reflect. + """ + return Cylinder(self.center.reflect(normal, origin), + self.axis.reflect(normal), + self.radius) + + def scale(self, factor, origin=None): + """Scale a Cylinder by a factor from an origin point. + + Args: + factor: A number representing how much the Cylinder should be scaled. + origin: A Point3D representing the origin from which to scale. + If None, it will be scaled from the World origin (0, 0, 0). + """ + return Cylinder(self.center.scale(factor, origin), + self.axis * factor, + self.radius * factor) + + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__() + + def to_dict(self): + """Get Cylinder as a dictionary.""" + return {'type': 'Cylinder', 'center': self.center.to_array(), + 'axis': self.axis.to_array(), 'radius': self.radius} + + def __copy__(self): + return Cylinder(self.center, self.axis, self.radius) + + def __key(self): + """A tuple based on the object properties, useful for hashing.""" + return (self._center, self._axis, self._radius) + + def __hash__(self): + return hash(self.__key()) + + def __eq__(self, other): + return isinstance(other, Cylinder) and self.__key() == other.__key() + + def __ne__(self, other): + return not self.__eq__(other) + + def ToString(self): + """Overwrite .NET ToString.""" + return self.__repr__() + + def __repr__(self): + return 'Cylinder (center {}) (axis {}) (radius {})'.\ + format(self.center, self.axis, self.radius) diff --git a/ladybug_geometry/geometry3d/sphere.py b/ladybug_geometry/geometry3d/sphere.py index 31fd9be7..e17fb3ef 100644 --- a/ladybug_geometry/geometry3d/sphere.py +++ b/ladybug_geometry/geometry3d/sphere.py @@ -1,19 +1,19 @@ # coding=utf-8 -"""Sphere3D""" +"""Sphere""" from __future__ import division from .pointvector import Point3D from .plane import Plane from .arc import Arc3D from .line import LineSegment3D -from ..intersection3d import intersect_line3d_sphere3d, intersect_plane_sphere3d +from ..intersection3d import intersect_line3d_sphere, intersect_plane_sphere import math -class Sphere3D(object): - """Sphere3D object. +class Sphere(object): + """Sphere object. Args: center: A Point3D representing the center of the arc. @@ -32,7 +32,7 @@ class Sphere3D(object): __slots__ = ('_center', '_radius') def __init__(self, center, radius): - """Initilize Sphere3D. + """Initilize Sphere. """ assert isinstance(center, Point3D), \ "Expected Point3D. Got {}.".format(type(center)) @@ -42,7 +42,7 @@ def __init__(self, center, radius): @classmethod def from_dict(cls, data): - """Create a Sphere3D from a dictionary. + """Create a Sphere from a dictionary. Args: data: A python dictionary in the following format @@ -50,7 +50,7 @@ def from_dict(cls, data): .. code-block:: python { - "type": "Sphere3D" + "type": "Sphere" "center": (10, 0, 0), "radius": 5, } @@ -105,7 +105,7 @@ def move(self, moving_vec): Args: moving_vec: A Vector3D with the direction and distance to move the shere. """ - return Sphere3D(self.center.move(moving_vec), self.radius) + return Sphere(self.center.move(moving_vec), self.radius) def rotate(self, axis, angle, origin): """Rotate this sphere by a certain angle around an axis and origin. @@ -119,7 +119,7 @@ def rotate(self, axis, angle, origin): angle: An angle for rotation in radians. origin: A Point3D for the origin around which the sphere will be rotated. """ - return Sphere3D(self.center.rotate(axis, angle, origin), self.radius) + return Sphere(self.center.rotate(axis, angle, origin), self.radius) def rotate_xy(self, angle, origin): """Get a sphere that is rotated counterclockwise in the world XY plane by an angle. @@ -128,7 +128,7 @@ def rotate_xy(self, angle, origin): angle: An angle for rotation in radians. origin: A Point3D for the origin around which the sphere will be rotated. """ - return Sphere3D(self.center.rotate_xy(angle, origin), self.radius) + return Sphere(self.center.rotate_xy(angle, origin), self.radius) def reflect(self, normal, origin): """Get a sphere reflected across a plane with the input normal vector and origin. @@ -138,7 +138,7 @@ def reflect(self, normal, origin): which the arc will be reflected. THIS VECTOR MUST BE NORMALIZED. origin: A Point3D representing the origin from which to reflect. """ - return Sphere3D(self.center.reflect(normal, origin), self.radius) + return Sphere(self.center.reflect(normal, origin), self.radius) def scale(self, factor, origin=None): """Scale a sphere by a factor from an origin point. @@ -148,10 +148,10 @@ def scale(self, factor, origin=None): origin: A Point3D representing the origin from which to scale. If None, it will be scaled from the World origin (0, 0, 0). """ - return Sphere3D(self.center.scale(factor, origin), self.radius * factor) + return Sphere(self.center.scale(factor, origin), self.radius * factor) def intersect_plane(self, plane): - """Get the intersection of a plane with this Sphere3D object + """Get the intersection of a plane with this Sphere object Args: plane: A Plane object. @@ -160,12 +160,12 @@ def intersect_plane(self, plane): Arc3D representing a full circle if it exists. None if no full intersection exists. """ - ip = intersect_plane_sphere3d(plane, self) # ip = [center pt, vector, radius] + ip = intersect_plane_sphere(plane, self) # ip = [center pt, vector, radius] return None if ip is None or isinstance(ip, Point3D) else \ Arc3D(Plane(ip[1], ip[0]), ip[2]) def intersect_line_ray(self, line_ray): - """Get the intersection between this Sphere3D object and a Ray2D/LineSegment2D. + """Get the intersection between this Sphere object and a Ray2D/LineSegment2D. Args: line_ray: A LineSegment3D or Ray3D that will be extended infinitely @@ -176,7 +176,7 @@ def intersect_line_ray(self, line_ray): A Point if a tangent intersection exists. None if no full intersection exists. """ - il = intersect_line3d_sphere3d(line_ray, self) + il = intersect_line3d_sphere(line_ray, self) return None if il is None else \ il if isinstance(il, Point3D) else \ LineSegment3D.from_end_points(il[0], il[1]) @@ -187,12 +187,12 @@ def duplicate(self): def to_dict(self): """Get Sphere as a dictionary.""" - return {'type': 'Sphere3D', + return {'type': 'Sphere', 'center': self.center.to_array(), 'radius': self.radius} def __copy__(self): - return Sphere3D(self._center, self._radius) + return Sphere(self._center, self._radius) def __key(self): """A tuple based on the object properties, useful for hashing.""" @@ -202,8 +202,8 @@ def __hash__(self): return hash(self.__key()) def __eq__(self, other): - return isinstance(other, Sphere3D) and self.__key() == other.__key() - + return isinstance(other, Sphere) and self.__key() == other.__key() + def __ne__(self, other): return not self.__eq__(other) @@ -212,4 +212,4 @@ def ToString(self): return self.__repr__() def __repr__(self): - return 'Sphere3D (center {}) (radius {}))'.format(self.center, self.radius) + return 'Sphere (center {}) (radius {}))'.format(self.center, self.radius) diff --git a/ladybug_geometry/intersection3d.py b/ladybug_geometry/intersection3d.py index 00e8208b..c9383c31 100644 --- a/ladybug_geometry/intersection3d.py +++ b/ladybug_geometry/intersection3d.py @@ -144,12 +144,12 @@ def closest_point3d_between_line3d_plane(line_ray, plane): return None # intersection -def intersect_line3d_sphere3d(line_ray, sphere): - """Get the intersection between this Sphere3D object and a Ray2D/LineSegment2D. +def intersect_line3d_sphere(line_ray, sphere): + """Get the intersection between this Sphere object and a Ray2D/LineSegment2D. Args: line_ray: A LineSegment3D or Ray3D for intersection. - sphere: A Sphere3D to intersect. + sphere: A Sphere to intersect. Returns: Two Point3D objects if a full intersection exists. @@ -185,12 +185,12 @@ def intersect_line3d_sphere3d(line_ray, sphere): return p1, p2 -def intersect_plane_sphere3d(plane, sphere): - """Get the intersection of a plane with this Sphere3D object +def intersect_plane_sphere(plane, sphere): + """Get the intersection of a plane with this Sphere object Args: plane: A Plane object. - sphere: A Sphere3D to intersect. + sphere: A Sphere to intersect. Returns: If a full intersection exists diff --git a/tests/cone3d_test.py b/tests/cone_test.py similarity index 77% rename from tests/cone3d_test.py rename to tests/cone_test.py index ff567a3f..690612ac 100644 --- a/tests/cone3d_test.py +++ b/tests/cone_test.py @@ -2,17 +2,17 @@ import pytest from ladybug_geometry.geometry3d.pointvector import Point3D, Vector3D -from ladybug_geometry.geometry3d.cone import Cone3D +from ladybug_geometry.geometry3d.cone import Cone import math def test_cone_init(): - """Test the initalization of Cone3D objects and basic properties.""" + """Test the initalization of Cone objects and basic properties.""" vertex = Point3D(2, 0, 2) axis = Vector3D(0, 2, 2) angle = 0.7 - c = Cone3D(vertex, axis, angle) + c = Cone(vertex, axis, angle) str(c) # test the string representation of the cone assert c.vertex == Point3D(2, 0, 2) @@ -25,10 +25,10 @@ def test_cone_init(): def test_equality(): - """Test the equality of Cone3D objects.""" - cone = Cone3D(Point3D(2, 0, 2), Vector3D(0, 2, 2), 0.7) + """Test the equality of Cone objects.""" + cone = Cone(Point3D(2, 0, 2), Vector3D(0, 2, 2), 0.7) cone_dup = cone.duplicate() - cone_alt = Cone3D(Point3D(2, 0.1, 2), Vector3D(0, 2, 2), 0.7) + cone_alt = Cone(Point3D(2, 0.1, 2), Vector3D(0, 2, 2), 0.7) assert cone is cone assert cone is not cone_dup @@ -39,10 +39,10 @@ def test_equality(): def test_cone_to_from_dict(): - """Test the Cone3D to_dict and from_dict methods.""" - c = Cone3D(Point3D(4, 0.5, 2), Vector3D(1, 0, 2.5), 0.7) + """Test the Cone to_dict and from_dict methods.""" + c = Cone(Point3D(4, 0.5, 2), Vector3D(1, 0, 2.5), 0.7) d = c.to_dict() - c = Cone3D.from_dict(d) + c = Cone.from_dict(d) assert c.vertex.x == pytest.approx(4, rel=1e-3) assert c.vertex.y == pytest.approx(0.5, rel=1e-3) assert c.vertex.z == pytest.approx(2, rel=1e-3) @@ -52,8 +52,8 @@ def test_cone_to_from_dict(): def test_cone_duplicate(): - """Test the Cone3D duplicate method.""" - c = Cone3D(Point3D(0, 0.5, 2), Vector3D(1, 0.5, 2.5), 0.75) + """Test the Cone duplicate method.""" + c = Cone(Point3D(0, 0.5, 2), Vector3D(1, 0.5, 2.5), 0.75) test = c.duplicate() assert test.angle == 0.75 assert c.vertex.x == pytest.approx(0, rel=1e-3) @@ -65,8 +65,8 @@ def test_cone_duplicate(): def test_cone_rotate(): - """Test the Cone3D rotate method.""" - c = Cone3D(Point3D(2, 0, 2), Vector3D(1, 0.5, 2.5), 0.75) + """Test the Cone rotate method.""" + c = Cone(Point3D(2, 0, 2), Vector3D(1, 0.5, 2.5), 0.75) test1 = c.rotate(Vector3D(0, 0, 1), math.pi, Point3D(0, 0, 0)) assert test1.vertex.x == pytest.approx(-2, rel=1e-3) assert test1.vertex.y == pytest.approx(0, rel=1e-3) @@ -75,8 +75,8 @@ def test_cone_rotate(): def test_cone_rotate_xy(): - """Test the Cone3D rotate_xy method.""" - c = Cone3D(Point3D(1, 0, 3), Vector3D(1, 2, 0), 0.5) + """Test the Cone rotate_xy method.""" + c = Cone(Point3D(1, 0, 3), Vector3D(1, 2, 0), 0.5) test = c.rotate_xy(math.pi / 2, Point3D(0, 0, 0)) assert test.vertex.x == pytest.approx(0, rel=1e-3) assert test.vertex.y == pytest.approx(1, rel=1e-3) @@ -85,13 +85,13 @@ def test_cone_rotate_xy(): def test_cone_reflect(): - """Test the Cone3D reflect method.""" + """Test the Cone reflect method.""" origin_1 = Point3D(1, 0, 0) origin_2 = Point3D(0, 0, 2) normal_1 = Vector3D(0, 0, 1) normal_2 = Vector3D(1, 0, 0) - c = Cone3D(Point3D(0, 0, 0), Vector3D(1, 2, 0), 0.5) + c = Cone(Point3D(0, 0, 0), Vector3D(1, 2, 0), 0.5) test_1 = c.reflect(normal_1, origin_1) assert test_1.vertex.x == pytest.approx(0, rel=1e-3) assert test_1.vertex.y == pytest.approx(0, rel=1e-3) @@ -109,8 +109,8 @@ def test_cone_reflect(): def test_cone_move(): - """Test the Cone3D move method.""" - c = Cone3D(Point3D(4, 0.5, 2), Vector3D(1, 0, 2.5), 0.7) + """Test the Cone move method.""" + c = Cone(Point3D(4, 0.5, 2), Vector3D(1, 0, 2.5), 0.7) test = c.move(Vector3D(2, 3, 6.5)) assert test.vertex.x == pytest.approx(6, rel=1e-3) assert test.vertex.y == pytest.approx(3.5, rel=1e-3) @@ -121,8 +121,8 @@ def test_cone_move(): def test_cone_scale(): - """Test the Sphere3D scale method.""" - c = Cone3D(Point3D(4, 0.5, 2), Vector3D(1, 0, 2.5), 0.7) + """Test the Cone scale method.""" + c = Cone(Point3D(4, 0.5, 2), Vector3D(1, 0, 2.5), 0.7) test = c.scale(2, Point3D(0, 0, 0)) assert test.vertex.x == pytest.approx(8, rel=1e-3) assert test.vertex.y == pytest.approx(1, rel=1e-3) diff --git a/tests/cylinder_test.py b/tests/cylinder_test.py new file mode 100644 index 00000000..c730aa97 --- /dev/null +++ b/tests/cylinder_test.py @@ -0,0 +1,147 @@ +# coding=utf-8 +import pytest + +from ladybug_geometry.geometry3d.pointvector import Point3D, Vector3D +from ladybug_geometry.geometry3d.cylinder import Cylinder + +import math + + +def test_cylinder_init(): + """Test the initalization of Cylinder objects and basic properties.""" + center = Point3D(2, 0, 2) + axis = Vector3D(0, 2, 2) + radius = 0.7 + c = Cylinder(center, axis, radius) + + str(c) # test the string representation of the cylinder + assert c.center == Point3D(2, 0, 2) + assert c.axis == Vector3D(0, 2, 2) + assert c.radius == 0.7 + assert c.height == c.axis.magnitude + assert c.center_end == c.center + c.axis + assert c.diameter == c.radius * 2 + assert isinstance(c.area, float) + assert isinstance(c.volume, float) + + p1 = Point3D(1, 1, 0) + p2 = Point3D(1, 1, 5) + radius = 1.2 + c2 = Cylinder.from_start_end(p1, p2, radius) + assert c2.center == Point3D(1, 1, 0) + assert c2.axis == Vector3D(0, 0, 5) + assert c2.radius == 1.2 + assert c2.height == c2.axis.magnitude + assert c2.center_end == c2.center + c2.axis + assert c2.diameter == c2.radius * 2 + assert isinstance(c2.area, float) + assert isinstance(c2.volume, float) + + +def test_equality(): + """Test the equality of Cylinder objects.""" + cylinder = Cylinder(Point3D(2, 0, 2), Vector3D(0, 2, 2), 0.7) + cylinder_dup = cylinder.duplicate() + cylinder_alt = Cylinder(Point3D(2, 0.1, 2), Vector3D(0, 2, 2), 0.7) + + assert cylinder is cylinder + assert cylinder is not cylinder_dup + assert cylinder == cylinder_dup + assert hash(cylinder) == hash(cylinder_dup) + assert cylinder != cylinder_alt + assert hash(cylinder) != hash(cylinder_alt) + + +def test_cylinder_to_from_dict(): + """Test the Cylinder to_dict and from_dict methods.""" + c = Cylinder(Point3D(4, 0.5, 2), Vector3D(1, 0, 2.5), 0.7) + d = c.to_dict() + c = Cylinder.from_dict(d) + assert c.center.x == pytest.approx(4, rel=1e-3) + assert c.center.y == pytest.approx(0.5, rel=1e-3) + assert c.center.z == pytest.approx(2, rel=1e-3) + assert c.axis.x == pytest.approx(1, rel=1e-3) + assert c.axis.y == pytest.approx(0, rel=1e-3) + assert c.axis.z == pytest.approx(2.5, rel=1e-3) + + +def test_cylinder_duplicate(): + """Test the Cylinder duplicate method.""" + c = Cylinder(Point3D(0, 0.5, 2), Vector3D(1, 0.5, 2.5), 0.75) + test = c.duplicate() + assert test.radius == 0.75 + assert c.center.x == pytest.approx(0, rel=1e-3) + assert c.center.y == pytest.approx(0.5, rel=1e-3) + assert c.center.z == pytest.approx(2, rel=1e-3) + assert c.axis.x == pytest.approx(1, rel=1e-3) + assert c.axis.y == pytest.approx(0.5, rel=1e-3) + assert c.axis.z == pytest.approx(2.5, rel=1e-3) + + +def test_cylinder_rotate(): + """Test the Cylinder rotate method.""" + c = Cylinder(Point3D(2, 0, 2), Vector3D(1, 0.5, 2.5), 0.75) + test1 = c.rotate(Vector3D(0, 0, 1), math.pi, Point3D(0, 0, 0)) + assert test1.center.x == pytest.approx(-2, rel=1e-3) + assert test1.center.y == pytest.approx(0, rel=1e-3) + assert test1.center.z == pytest.approx(2, rel=1e-3) + assert test1.axis == Vector3D(1, 0.5, 2.5).rotate(Vector3D(0, 0, 1), math.pi) + + +def test_cylinder_rotate_xy(): + """Test the Cylinder rotate_xy method.""" + c = Cylinder(Point3D(1, 0, 3), Vector3D(1, 2, 0), 0.5) + test = c.rotate_xy(math.pi / 2, Point3D(0, 0, 0)) + assert test.center.x == pytest.approx(0, rel=1e-3) + assert test.center.y == pytest.approx(1, rel=1e-3) + assert test.center.z == pytest.approx(3, rel=1e-3) + assert test.axis == Vector3D(1, 2, 0).rotate_xy(math.pi / 2) + + +def test_cylinder_reflect(): + """Test the Cylinder reflect method.""" + origin_1 = Point3D(1, 0, 0) + origin_2 = Point3D(0, 0, 2) + normal_1 = Vector3D(0, 0, 1) + normal_2 = Vector3D(1, 0, 0) + + c = Cylinder(Point3D(0, 0, 0), Vector3D(1, 2, 0), 0.5) + test_1 = c.reflect(normal_1, origin_1) + assert test_1.center.x == pytest.approx(0, rel=1e-3) + assert test_1.center.y == pytest.approx(0, rel=1e-3) + assert test_1.center.z == pytest.approx(0, rel=1e-3) + + test_2 = c.reflect(normal_2, origin_1) + assert test_2.center.x == pytest.approx(2, rel=1e-3) + assert test_2.center.y == pytest.approx(0, rel=1e-3) + assert test_2.center.z == pytest.approx(0, rel=1e-3) + + test_3 = c.reflect(normal_1, origin_2) + assert test_3.center.x == pytest.approx(0, rel=1e-3) + assert test_3.center.y == pytest.approx(0, rel=1e-3) + assert test_3.center.z == pytest.approx(4, rel=1e-3) + + +def test_cylinder_move(): + """Test the Cylinder move method.""" + c = Cylinder(Point3D(4, 0.5, 2), Vector3D(1, 0, 2.5), 0.7) + test = c.move(Vector3D(2, 3, 6.5)) + assert test.center.x == pytest.approx(6, rel=1e-3) + assert test.center.y == pytest.approx(3.5, rel=1e-3) + assert test.center.z == pytest.approx(8.5, rel=1e-3) + assert test.axis.x == pytest.approx(1, rel=1e-3) + assert test.axis.y == pytest.approx(0, rel=1e-3) + assert test.axis.z == pytest.approx(2.5, rel=1e-3) + + +def test_cylinder_scale(): + """Test the Cylinder scale method.""" + c = Cylinder(Point3D(4, 0.5, 2), Vector3D(1, 0, 2.5), 0.7) + test = c.scale(2, Point3D(0, 0, 0)) + assert test.center.x == pytest.approx(8, rel=1e-3) + assert test.center.y == pytest.approx(1, rel=1e-3) + assert test.center.z == pytest.approx(4, rel=1e-3) + assert test.axis.x == pytest.approx(2, rel=1e-3) + assert test.axis.y == pytest.approx(0, rel=1e-3) + assert test.axis.z == pytest.approx(5, rel=1e-3) + assert test.radius == pytest.approx(1.4, rel=1e-3) diff --git a/tests/sphere3d_test.py b/tests/sphere_test.py similarity index 80% rename from tests/sphere3d_test.py rename to tests/sphere_test.py index 253b0458..2eeee3e1 100644 --- a/tests/sphere3d_test.py +++ b/tests/sphere_test.py @@ -2,7 +2,7 @@ import pytest from ladybug_geometry.geometry3d.pointvector import Point3D, Vector3D -from ladybug_geometry.geometry3d.sphere import Sphere3D +from ladybug_geometry.geometry3d.sphere import Sphere from ladybug_geometry.geometry3d.plane import Plane from ladybug_geometry.geometry3d.line import LineSegment3D from ladybug_geometry.geometry3d.arc import Arc3D @@ -11,10 +11,10 @@ def test_sphere_init(): - """Test the initalization of Sphere3D objects and basic properties.""" + """Test the initalization of Sphere objects and basic properties.""" pt = Point3D(2, 0, 2) r = 3 - sp = Sphere3D(pt, r) + sp = Sphere(pt, r) str(sp) # test the string representation of the line segment assert sp.center == Point3D(2, 0, 2) @@ -28,10 +28,10 @@ def test_sphere_init(): def test_equality(): - """Test the equality of Polygon2D objects.""" - sphere = Sphere3D(Point3D(2, 0, 2), 3) + """Test the equality of Sphere objects.""" + sphere = Sphere(Point3D(2, 0, 2), 3) sphere_dup = sphere.duplicate() - sphere_alt = Sphere3D(Point3D(2, 0.1, 2), 3) + sphere_alt = Sphere(Point3D(2, 0.1, 2), 3) assert sphere is sphere assert sphere is not sphere_dup @@ -42,18 +42,18 @@ def test_equality(): def test_sphere_to_from_dict(): - """Test the Sphere3D to_dict and from_dict methods.""" - sp = Sphere3D(Point3D(4, 0, 2), 3) + """Test the Sphere to_dict and from_dict methods.""" + sp = Sphere(Point3D(4, 0, 2), 3) d = sp.to_dict() - sp = Sphere3D.from_dict(d) + sp = Sphere.from_dict(d) assert sp.center.x == pytest.approx(4, rel=1e-3) assert sp.center.y == pytest.approx(0, rel=1e-3) assert sp.center.z == pytest.approx(2, rel=1e-3) def test_sphere_duplicate(): - """Test the Sphere3D duplicate method.""" - sp = Sphere3D(Point3D(8.5, 1.2, 2.9), 6.5) + """Test the Sphere duplicate method.""" + sp = Sphere(Point3D(8.5, 1.2, 2.9), 6.5) test = sp.duplicate() assert test.radius == 6.5 assert test.center.x == pytest.approx(8.5, rel=1e-3) @@ -62,8 +62,8 @@ def test_sphere_duplicate(): def test_sphere_rotate(): - """Test the Sphere3D rotate method.""" - sp = Sphere3D(Point3D(2, 0, 2), 3) + """Test the Sphere rotate method.""" + sp = Sphere(Point3D(2, 0, 2), 3) test1 = sp.rotate(Vector3D(0, 0, 1), math.pi, Point3D(0, 0, 0)) assert test1.center.x == pytest.approx(-2, rel=1e-3) assert test1.center.y == pytest.approx(0, rel=1e-3) @@ -76,8 +76,8 @@ def test_sphere_rotate(): def test_sphere_rotate_xy(): - """Test the Sphere3D rotate_xy method.""" - sp = Sphere3D(Point3D(4, 0, 2), 3) + """Test the Sphere rotate_xy method.""" + sp = Sphere(Point3D(4, 0, 2), 3) test = sp.rotate_xy(math.pi / 2, Point3D(0, 0, 0)) assert test.center.x == pytest.approx(0, rel=1e-3) assert test.center.y == pytest.approx(4, rel=1e-3) @@ -85,13 +85,13 @@ def test_sphere_rotate_xy(): def test_sphere_reflect(): - """Test the Sphere3D reflect method.""" + """Test the Sphere reflect method.""" origin_1 = Point3D(1, 0, 0) origin_2 = Point3D(0, 0, 2) normal_1 = Vector3D(0, 0, 1) normal_2 = Vector3D(1, 0, 0) - sp = Sphere3D(Point3D(0, 0, 0), 3) + sp = Sphere(Point3D(0, 0, 0), 3) test_1 = sp.reflect(normal_1, origin_1) assert test_1.center.x == pytest.approx(0, rel=1e-3) assert test_1.center.y == pytest.approx(0, rel=1e-3) @@ -109,8 +109,8 @@ def test_sphere_reflect(): def test_sphere_move(): - """Test the Sphere3D move method.""" - sp = Sphere3D(Point3D(2, 0, 2), 3) + """Test the Sphere move method.""" + sp = Sphere(Point3D(2, 0, 2), 3) test = sp.move(Vector3D(2, 3, 6.5)) assert test.center.x == pytest.approx(4, rel=1e-3) assert test.center.y == pytest.approx(3, rel=1e-3) @@ -118,8 +118,8 @@ def test_sphere_move(): def test_sphere_scale(): - """Test the Sphere3D scale method.""" - sp = Sphere3D(Point3D(4, 0, 2), 2.5) + """Test the Sphere scale method.""" + sp = Sphere(Point3D(4, 0, 2), 2.5) test = sp.scale(2, Point3D(0, 0, 0)) assert test.radius == 5 assert test.center.x == pytest.approx(8, rel=1e-3) @@ -128,12 +128,12 @@ def test_sphere_scale(): def test_sphere_intersection_with_line_ray(): - """Test the Sphere3D intersect_line_ray method.""" + """Test the Sphere intersect_line_ray method.""" lpt = Point3D(-2, 0, 0) vec = Vector3D(4, 0, 0) seg = LineSegment3D(lpt, vec) spt = Point3D(0, 0, 0) - sp = Sphere3D(spt, 1.5) + sp = Sphere(spt, 1.5) int1 = sp.intersect_line_ray(seg) assert isinstance(int1, LineSegment3D) @@ -147,12 +147,12 @@ def test_sphere_intersection_with_line_ray(): def test_sphere_intersection_with_plane(): - """Test the Sphere3D intersect_plane method.""" + """Test the Sphere intersect_plane method.""" ppt = Point3D(-1.5, 0, 1.46) vec = Vector3D(0.1, 0, 1) pl = Plane(vec, ppt) spt = Point3D(0, 0, 0) - sp = Sphere3D(spt, 1.5) + sp = Sphere(spt, 1.5) int1 = sp.intersect_plane(pl) assert isinstance(int1, Arc3D)