-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
454 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
Oops, something went wrong.