1from numbers import Number 2import math 3import operator 4import warnings 5 6 7__all__ = ["Vector"] 8 9 10class Vector(tuple): 11 """A math-like vector. 12 13 Represents an n-dimensional numeric vector. ``Vector`` objects support 14 vector addition and subtraction, scalar multiplication and division, 15 negation, rounding, and comparison tests. 16 """ 17 18 __slots__ = () 19 20 def __new__(cls, values, keep=False): 21 if keep is not False: 22 warnings.warn( 23 "the 'keep' argument has been deprecated", 24 DeprecationWarning, 25 ) 26 if type(values) == Vector: 27 # No need to create a new object 28 return values 29 return super().__new__(cls, values) 30 31 def __repr__(self): 32 return f"{self.__class__.__name__}({super().__repr__()})" 33 34 def _vectorOp(self, other, op): 35 if isinstance(other, Vector): 36 assert len(self) == len(other) 37 return self.__class__(op(a, b) for a, b in zip(self, other)) 38 if isinstance(other, Number): 39 return self.__class__(op(v, other) for v in self) 40 raise NotImplementedError() 41 42 def _scalarOp(self, other, op): 43 if isinstance(other, Number): 44 return self.__class__(op(v, other) for v in self) 45 raise NotImplementedError() 46 47 def _unaryOp(self, op): 48 return self.__class__(op(v) for v in self) 49 50 def __add__(self, other): 51 return self._vectorOp(other, operator.add) 52 53 __radd__ = __add__ 54 55 def __sub__(self, other): 56 return self._vectorOp(other, operator.sub) 57 58 def __rsub__(self, other): 59 return self._vectorOp(other, _operator_rsub) 60 61 def __mul__(self, other): 62 return self._scalarOp(other, operator.mul) 63 64 __rmul__ = __mul__ 65 66 def __truediv__(self, other): 67 return self._scalarOp(other, operator.truediv) 68 69 def __rtruediv__(self, other): 70 return self._scalarOp(other, _operator_rtruediv) 71 72 def __pos__(self): 73 return self._unaryOp(operator.pos) 74 75 def __neg__(self): 76 return self._unaryOp(operator.neg) 77 78 def __round__(self, *, round=round): 79 return self._unaryOp(round) 80 81 def __eq__(self, other): 82 if isinstance(other, list): 83 # bw compat Vector([1, 2, 3]) == [1, 2, 3] 84 other = tuple(other) 85 return super().__eq__(other) 86 87 def __ne__(self, other): 88 return not self.__eq__(other) 89 90 def __bool__(self): 91 return any(self) 92 93 __nonzero__ = __bool__ 94 95 def __abs__(self): 96 return math.sqrt(sum(x * x for x in self)) 97 98 def length(self): 99 """Return the length of the vector. Equivalent to abs(vector).""" 100 return abs(self) 101 102 def normalized(self): 103 """Return the normalized vector of the vector.""" 104 return self / abs(self) 105 106 def dot(self, other): 107 """Performs vector dot product, returning the sum of 108 ``a[0] * b[0], a[1] * b[1], ...``""" 109 assert len(self) == len(other) 110 return sum(a * b for a, b in zip(self, other)) 111 112 # Deprecated methods/properties 113 114 def toInt(self): 115 warnings.warn( 116 "the 'toInt' method has been deprecated, use round(vector) instead", 117 DeprecationWarning, 118 ) 119 return self.__round__() 120 121 @property 122 def values(self): 123 warnings.warn( 124 "the 'values' attribute has been deprecated, use " 125 "the vector object itself instead", 126 DeprecationWarning, 127 ) 128 return list(self) 129 130 @values.setter 131 def values(self, values): 132 raise AttributeError( 133 "can't set attribute, the 'values' attribute has been deprecated", 134 ) 135 136 def isclose(self, other: "Vector", **kwargs) -> bool: 137 """Return True if the vector is close to another Vector.""" 138 assert len(self) == len(other) 139 return all(math.isclose(a, b, **kwargs) for a, b in zip(self, other)) 140 141 142def _operator_rsub(a, b): 143 return operator.sub(b, a) 144 145 146def _operator_rtruediv(a, b): 147 return operator.truediv(b, a) 148