# -*- coding: utf-8 -*-
"""Vector Module"""
import math
# import numpy as np
from .util import unify_types
from .constant import get_eps,get_sig_figures
[docs]class Vector(object):
"""Vector Class"""
[docs] @classmethod
def zero(cls):
"""Returns the zero vector (0 | 0 | 0)"""
return cls(0, 0, 0)
[docs] @classmethod
def x_unit_vector(cls):
"""Returns the unit vector (1 | 0 | 0)"""
return cls(1, 0, 0)
[docs] @classmethod
def y_unit_vector(cls):
"""Returns the unit vector (0 | 1 | 0)"""
return cls(0, 1, 0)
[docs] @classmethod
def z_unit_vector(cls):
"""Returns the unit vector (0 | 0 | 1)"""
return cls(0, 0, 1)
def __init__(self, *args):
"""Vector(x, y, z)
Vector([x, y, z]):
A vector with coordinates (x | y | z)
Vector(P1, P2):
A vector going from point P1 to P2.
"""
if len(args) == 3:
# Initialising with 3 coordinates
self._v = list(args)
elif len(args) == 2:
# Initialising from point A to point B
A, B = args
self._v = [
B.x - A.x,
B.y - A.y,
B.z - A.z,
]
elif len(args) == 1:
# Initialising with an array of coordinates
self._v = list(args[0])
else:
raise TypeError("Vector() takes one, two or three parameters, "
"not {}".format(len(args)))
self._v = unify_types(self._v)
def __hash__(self):
"""return the hash of a vector"""
return hash(("Vector",
round(self._v[0],get_sig_figures()),
round(self._v[1],get_sig_figures()),
round(self._v[2],get_sig_figures()),
round(self._v[0],get_sig_figures()) * round(self._v[1],get_sig_figures()),
round(self._v[1],get_sig_figures()) * round(self._v[2],get_sig_figures()),
round(self._v[2],get_sig_figures()) * round(self._v[0],get_sig_figures()),
))
def __repr__(self):
return "Vector({}, {}, {})".format(*self._v)
def __eq__(self, other):
return abs(self._v[0] - other._v[0]) < get_eps() and abs(self._v[1] - other._v[1]) < get_eps() and abs(self._v[2] - other._v[2]) < get_eps()
def __add__(self, other):
return Vector(x+y for x, y in zip(self, other))
def __sub__(self, other):
return Vector([x-y for x, y in zip(self, other)])
def __mul__(self, other):
if isinstance(other, Vector):
return sum(x*y for x, y in zip(self, other))
return Vector([x*other for x in self._v])
def __rmul__(self, other):
return self * other
def __neg__(self):
return self * -1
def __getitem__(self, item):
return self._v[item]
def __setitem__(self, item, value):
self._v[item] = value
# def tonumpy(self):
# return np.array(self._v)
[docs] def cross(self, other):
r"""Calculates the cross product of two vectors, defined as
_ _ / x2y3 - x3y2 \
x × y = | x3y1 - x1y3 |
\ x1y2 - x2y1 /
The cross product is orthogonal to both vectors and its length
is the area of the parallelogram given by x and y.
"""
a, b = self._v, other._v
return Vector(
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0]
)
[docs] def length(self):
"""Returns |v|, the length of the vector."""
return (self * self) ** 0.5
__abs__ = length
[docs] def parallel(self, other):
"""Returns true if both vectors are parallel."""
from .solver import solve
if self == Vector.zero() or other == Vector.zero():
return True
if self == other:
return True
return abs(abs(self * other) - self.length() * other.length()) < get_eps() * self.length()
[docs] def orthogonal(self, other):
"""Returns true if the two vectors are orthogonal"""
return abs(self * other) < get_eps()
[docs] def angle(self, other):
"""Returns the angle (in radians) enclosed by both vectors."""
return math.acos((self * other) / (self.length() * other.length()))
[docs] def normalized(self):
"""Return the normalized version of the vector, that is a vector
pointing in the same direction but with length 1.
"""
# Division is not defined, so we have to multiply by 1/|v|
return float(1 / self.length()) * self
unit = normalized
x_unit_vector = Vector.x_unit_vector
y_unit_vector = Vector.y_unit_vector
z_unit_vector = Vector.z_unit_vector
__all__ = ("Vector","x_unit_vector","y_unit_vector","z_unit_vector")