Source code for xdesign.geometry.point

"""Define zero dimensional geometric entities."""

import logging
from math import sqrt
from numbers import Number  # TODO: Use duck typing instead of type checking
import numpy as np

from xdesign.geometry.entity import *

logger = logging.getLogger(__name__)

__author__ = "Daniel Ching, Doga Gursoy"
__copyright__ = "Copyright (c) 2016, UChicago Argonne, LLC."
__docformat__ = 'restructuredtext en'
__all__ = [
    'Point',
    'calc_standard',
    'dim',
    'rotated',
    'distance',
    'norm',
]


[docs]def dim(self): """Return the dimensionality of the ambient space.""" return self.shape[-1]
[docs]def rotated(self, theta, center=None, axis=None): """Rotates theta radians around an axis.""" if center is None: center = np.zeros(dim(self)) if axis is not None: raise NotImplementedError( "Rotation about axis besides [0 0 1] are" " not implemented." ) self = self.copy() # shift rotation center to origin self -= center # do rotation R = np.eye(dim(self)) R[0, 0] = np.cos(theta) R[0, 1] = -np.sin(theta) R[1, 0] = np.sin(theta) R[1, 1] = np.cos(theta) self = np.dot(R, self) # shift rotation center back self += center return self
[docs]def distance(self, other): """Return the closest distance this and the other.""" d = self - other return np.sqrt(d.dot(d))
[docs]def norm(self): """Euclidian (L2) norm of the vector.""" # See http://stackoverflow.com/a/23576322 for a discussion of the # quickest way to calculate the norm of a vector. return np.sqrt(self.dot(self))
[docs]def calc_standard(A): """Return the standard equation for the row-wise points in A. The coefficents (c_{0}*x + ... = c_{1}) describe the hyper-plane defined by the row-wise N-dimensional points A. Parameters ---------- A : :py:class:`np.array` (..., N, N) Each row is an N-dimensional point on the plane. Return ------ c0 : :py:class:`np.array` (..., N) The first N coefficients for the hyper-plane c1 : :py:class:`np.array` (..., 1) The last coefficient for the hyper-plane """ b = np.ones(A.shape[0:-1]) x1 = np.atleast_1d(b[..., 0]) try: x = np.linalg.solve(A, b) except np.linalg.LinAlgError as e: if str(e) != 'Singular matrix': raise else: a = A[..., 1, 1] - A[..., 0, 1] b = A[..., 0, 0] - A[..., 1, 0] x1 = np.atleast_1d(a * A[..., 0, 0] + b * A[..., 0, 1]) x = np.stack([a, b], axis=-1) return x, x1
[docs]class Point(Entity): """Define a point in ND cartesian space. Parameters ---------- x : :class:`.ndarray`, :class:`.list` ND coordinates of the point. Raises ------ TypeError If x is not a list or ndarray. """ def __init__(self, x): if not isinstance(x, (list, np.ndarray)): raise TypeError("x must be list, or array of coordinates.") super(Point, self).__init__() self._x = np.array(x, dtype=float, ndmin=1) self._x = np.ravel(self._x) self._dim = self._x.size def __repr__(self): """Return a string representation for easier debugging.""" return "Point([%s" % ', '.join([repr(n) for n in self._x]) + "])" @property def x(self): """Dimension 0 of the point.""" return self._x[0] @property def y(self): """Dimension 1 of the point.""" return self._x[1] @property def z(self): """Dimension 2 of the point.""" return self._x[2] @property def norm(self): """Euclidian (L2) norm of the vector to the point.""" # See http://stackoverflow.com/a/23576322 for a discussion of the # quickest way to calculate the norm of a vector. return sqrt(self._x.dot(self._x))
[docs] def translate(self, vector): """Translate the point along the given vector.""" if not isinstance(vector, (list, np.ndarray)): raise TypeError("vector must be arraylike.") self._x += vector
[docs] def rotate(self, theta, point=None, axis=None): """Rotates theta radians around an axis.""" if not isinstance(theta, Number): raise TypeError("theta must be scalar.") if point is None: center = np.zeros(self.dim) elif isinstance(point, Point): center = point._x else: raise TypeError("center of rotation must be Point.") if axis is not None: raise NotImplementedError( "Rotation about axis besides [0 0 1] are" " not implemented." ) # shift rotation center to origin self._x -= center # do rotation R = np.eye(self.dim) R[0, 0] = np.cos(theta) R[0, 1] = -np.sin(theta) R[1, 0] = np.sin(theta) R[1, 1] = np.cos(theta) self._x = np.dot(R, self._x) # shift rotation center back self._x += center
[docs] def scale(self, vector): """Scale the ambient space in each dimension according to vector. Scaling is centered on the origin. """ if not isinstance(vector, (List, np.ndarray)): raise TypeError("vector must be arraylike.") self._x *= vector
[docs] def contains(self, other): """Return wether the other is within the bounds of the Point. Points can only contain other Points. """ if isinstance(other, Point): return self == point if isinstance(other, np.ndarray): return np.all(self._x == other, axis=1) else: # points can only contain points return False
[docs] def collision(self, other): """Return True if this Point collides with another entity.""" if isinstance(other, Point): return self == point else: raise NotImplementedError
[docs] def distance(self, other): """Return the closest distance this and the other.""" if not isinstance(other, Point): return other.distance(self) d = self._x - other._x return sqrt(d.dot(d))
# OVERLOADS def __eq__(self, point): if not isinstance(point, Point): raise TypeError("Points can only equal to other points.") return np.array_equal(self._x, point._x) def __add__(self, point): """Addition.""" if not isinstance(point, Point): raise TypeError("Points can only add to other points.") return Point(self._x + point._x) def __sub__(self, point): """Subtraction.""" if not isinstance(point, Point): raise TypeError("Points can only subtract from other points.") return Point(self._x - point._x) def __mul__(self, c): """Scalar, vector multiplication.""" if not isinstance(c, Number): raise TypeError("Points can only multiply scalars.") return Point(self._x * c) def __truediv__(self, c): """Scalar, vector division.""" if not isinstance(c, Number): raise TypeError("Points can only divide scalars.") return Point(self._x / c) def __hash__(self): return hash(self._x[:])