Source code for sknano.core.math.quaternion

# -*- coding: utf-8 -*-
"""
=============================================================================
Custom NumPy Quaternion class (:mod:`sknano.core.math.quaternion`)
=============================================================================

.. currentmodule:: sknano.core.math.quaternion

"""
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__docformat__ = 'restructuredtext en'

# import copy
import numbers
import warnings

import numpy as np
np.seterr(all='warn')

# from .point import Point
# from .transforms import rotate, transformation_matrix
from .vector import Vector

__all__ = ['Quaternion']


[docs]class Quaternion(np.ndarray): """Abstract object representation of a quaternion. Parameters ---------- dtype : data-type, optional copy : bool, optional Examples -------- """ __array_priority__ = 15.0 _verbosity = 0 def __new__(cls, H, dtype=None, copy=True): if isinstance(H, Quaternion): intype = H.dtype if dtype is None: dtype = intype if intype == dtype and not copy: return H return H.astype(dtype) if isinstance(H, np.ndarray): if dtype is None: intype = H.dtype else: intype = np.dtype(dtype) Hview = H.view(cls) if intype != H.dtype: return Hview.astype(intype) if copy: return Hview.copy() else: return Hview arr = np.array(H, dtype=dtype, copy=copy) if len(arr) != 4: raise ValueError("Expected array_like input: [w, x, y, z]") ret = np.ndarray.__new__(cls, arr.shape, arr.dtype, buffer=arr) return ret def __array_finalize__(self, obj): if obj is None: return None if len(self) == 4: return else: raise ValueError("Expected array_like input: [w, x, y, z]") def __str__(self): fmtstr = "Quaternion:\nw = {w!r}\nx = {x!r}\ny = {y!r}\nz = {z!r}" return fmtstr.format(**dict(w=self.w, x=self.x, y=self.y, z=self.z)) def __repr__(self): return "Quaternion({!r})".format(self.tolist())
[docs] def tolist(self): return np.around(self.__array__(), decimals=6).tolist()
def __eq__(self, other): if isinstance(other, Quaternion) and \ (self is other or np.allclose(self.__array__(), other.__array__())): return True return False def __lt__(self, other): if isinstance(other, Quaternion): return self.norm < other.norm return False def __le__(self, other): return self < other or self == other def __gt__(self, other): return not (self < other or self == other) def __ge__(self, other): return not (self < other) def __ne__(self, other): return not (self == other) def __add__(self, other): if isinstance(other, Quaternion): return super().__add__(other) if np.isscalar(other): return Quaternion.from_components(w=self.w + other, x=self.x, y=self.y, z=self.z) return NotImplemented def __mul__(self, other): if isinstance(other, Quaternion): w = self.w * other.w - self.v.dot(other.v) v = self.v.cross(other.v) + self.w * other.v + other.w * self.v return Quaternion.from_components(w=w, x=v.x, y=v.y, z=v.z) if np.isscalar(other): return Quaternion(self.__array__() * other) return NotImplemented def __truediv__(self, other): if np.isscalar(other): return Quaternion(self.__array__() / other) return NotImplemented def __floordiv__(self, other): if np.isscalar(other): return Quaternion(self.__array__() // other) return NotImplemented def __mod__(self, other): return NotImplemented def __divmod__(self, other): return NotImplemented def __pow__(self, other): return NotImplemented def __lshift__(self, other): return NotImplemented def __rshift__(self, other): return NotImplemented def __and__(self, other): return NotImplemented def __xor__(self, other): return NotImplemented def __or__(self, other): return NotImplemented __radd__ = __add__ __rmul__ = __mul__ def __rtruediv__(self, other): return NotImplemented def __rfloordiv__(self, other): return NotImplemented @classmethod
[docs] def from_components(cls, w, x, y, z): return cls([w, x, y, z])
@property def w(self): """Real component of `Quaternion`.""" return self.real @property def v(self): """Vector of imaginary components of `Quaternion`.""" return Vector(self.imag) @property def real(self): """Real part :math:`w` of `Quaternion`.""" return self[0] @property def imag(self): """:class:`~python:list` of `Quaternion` imaginary components \ :math:`x,y,z`, with imaginary part \ :math:`x\\mathbf{i}+y\\mathbf{j}+z\\mathbf{k}`.""" return self.__array__()[1:].tolist() @property def x(self): return self[1] @property def y(self): return self[2] @property def z(self): return self[3] @property def axis(self): """Rotation axis.""" return Vector([self.x, self.y, self.z]) / np.sin(np.arccos(self.w)) @property def angle(self): """Rotation angle.""" return 2 * np.arccos(self.w) @property def norm(self): return np.sqrt((self.__array__() ** 2).sum()) @property def conjugate(self): return Quaternion([self.w, -self.x, -self.y, -self.z]) @property def inverse(self): return self.conjugate / self.norm @property def unit_quaternion(self): return self / self.norm