vector.py 4.0 KB

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