Source code for ladybug_geometry.geometry3d.cone

# coding=utf-8
"""Cone"""
from __future__ import division

from .pointvector import Point3D, Vector3D
from .plane import Plane
from .arc import Arc3D

import math


[docs]class Cone(object): """Cone object. Args: vertex: A Point3D at the tip of the cone. axis: A Vector3D representing the direction and height of the cone. The vector extends from the vertex to the center of the base. angle: An angle in radians representing the half angle between the axis and the surface. Properties: * vertex * axis * angle * height * slant_height * radius * area * volume * min * max * base """ __slots__ = ('_vertex', '_axis', '_angle', '_base', '_min', '_max') def __init__(self, vertex, axis, angle): """Initialize Cone.""" assert isinstance(vertex, Point3D), \ "Expected Point3D. Got {}.".format(type(vertex)) assert isinstance(axis, Vector3D), \ "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 self._angle = angle self._base = None self._min = None self._max = None
[docs] @classmethod def from_dict(cls, data): """Create a Cone from a dictionary. Args: data: A python dictionary in the following format .. code-block:: python { "type": "Cone" "vertex": (10, 0, 0), "axis": (0, 0, 1), "angle": 1.0 } """ return cls(Point3D.from_array(data['vertex']), Vector3D.from_array(data['axis']), data['angle'])
@property def vertex(self): """Vertex of cone.""" return self._vertex @property def axis(self): """Axis of cone.""" return self._axis @property def angle(self): """Angle of cone""" return self._angle @property def height(self): """Height of cone""" return self.axis.magnitude @property def radius(self): """Radius of a cone""" return self.height * math.tan(self.angle) @property def slant_height(self): """Slant height of a cone""" return math.sqrt(self.radius ** 2 + self.height ** 2) @property def area(self): """Surface area of a cone""" return math.pi * self.radius ** 2 + math.pi * self.radius * self.slant_height @property def volume(self): """Volume of a cone""" return 1 / 3 * math.pi * self.radius ** 2 * self.height @property def base(self): """Get an Arc3D representing the circular base of the cone.""" if self._base is None: plane = Plane(self.axis.reverse(), self.vertex + self.axis) self._base = Arc3D(plane, self.radius) return self._base @property def min(self): """A Point3D for the minimum bounding box vertex around this geometry.""" if self._min is None: self._calculate_min_max() return self._min @property def max(self): """A Point3D for the maximum bounding box vertex around this geometry.""" if self._max is None: self._calculate_min_max() return self._max
[docs] def move(self, moving_vec): """Get a cone that has been moved along a vector. Args: moving_vec: A Vector3D with the direction and distance to move the cone. """ return Cone(self.vertex.move(moving_vec), self.axis, self.angle)
[docs] def rotate(self, axis, angle, origin): """Rotate this cone 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 cone will be rotated. """ return Cone(self.vertex.rotate(axis, angle, origin), self.axis.rotate(axis, angle), self.angle)
[docs] 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 cone will be rotated. """ return Cone(self.vertex.rotate_xy(angle, origin), self.axis.rotate_xy(angle), self.angle)
[docs] def reflect(self, normal, origin): """Get a cone 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 Cone(self.vertex.reflect(normal, origin), self.axis.reflect(normal), self.angle)
[docs] def scale(self, factor, origin=None): """Scale a cone by a factor from an origin point. Args: factor: A number representing how much the cone 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 Cone(self.vertex.scale(factor, origin), self.axis * factor, self.angle)
[docs] def duplicate(self): """Get a copy of this object.""" return self.__copy__()
[docs] def to_dict(self): """Get Cone as a dictionary.""" return { 'type': 'Cone', 'vertex': self.vertex.to_array(), 'axis': self.axis.to_array(), 'angle': self.angle }
def _calculate_min_max(self): """Calculate maximum and minimum Point3D for this object.""" base = self.base bmn, bmx, ver = base.min, base.max, self.vertex self._min = Point3D(min(bmn.x, ver.x), min(bmn.y, ver.y), min(bmn.z, ver.z)) self._max = Point3D(max(bmx.x, ver.x), max(bmx.y, ver.y), max(bmx.z, ver.z)) def __copy__(self): return Cone(self.vertex, self.axis, self.angle) def __key(self): """A tuple based on the object properties, useful for hashing.""" return (self._vertex, self._axis, self._angle) def __hash__(self): return hash(self.__key()) def __eq__(self, other): return isinstance(other, Cone) and self.__key() == other.__key() def __ne__(self, other): return not self.__eq__(other)
[docs] def ToString(self): """Overwrite .NET ToString.""" return self.__repr__()
def __repr__(self): return 'Cone (vertex {}) (axis {}) (angle {}) (height {})'.\ format(self.vertex, self.axis, self.angle, self.height)