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