diff --git a/CHANGELOG.md b/CHANGELOG.md index 10afa7a6905..a73a050f2b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added implementation of `compute_vertices`, `compute_edges`, `compute_faces` to `compas.geometry.Cylinder`. * Added implementation of `compute_vertices`, `compute_edges`, `compute_faces` to `compas.geometry.Sphere`. * Added implementation of `compute_vertices`, `compute_edges`, `compute_faces` to `compas.geometry.Torus`. +* Added `compas.geometry.vector.__radd__`. +* Added `compas.geometry.vector.__rsub__`. +* Added `compas.geometry.vector.__rmul__`. +* Added `compas.geometry.vector.__rtruediv__`. ### Changed @@ -28,6 +32,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Changed check for empty vertices and faces to use `is None` to add support for `numpy` arrays. * Changed order of `u` and `v` of `compas.geometry.SphericalSurface` to the match the excpected parametrisation. * Changed `compas.geometry.Shape.to_vertices_and_faces` to use `Shape.vertices` and `Shape.faces` or `Shape.triangles`. +* Updated `compas.geometry.vector.__mul__` to allow element-wise multiplication with another vector. +* Updated `compas.geometry.vector.__truediv__` to allow element-wise division with another vector. ### Removed diff --git a/src/compas/geometry/vector.py b/src/compas/geometry/vector.py index 8e1b5702257..7ccf054431f 100644 --- a/src/compas/geometry/vector.py +++ b/src/compas/geometry/vector.py @@ -148,11 +148,25 @@ def __add__(self, other): def __sub__(self, other): return Vector(self.x - other[0], self.y - other[1], self.z - other[2]) - def __mul__(self, n): - return Vector(self.x * n, self.y * n, self.z * n) - - def __truediv__(self, n): - return Vector(self.x / n, self.y / n, self.z / n) + def __mul__(self, other): + if isinstance(other, (int, float)): + return Vector(self.x * other, self.y * other, self.z * other) + + try: + other = Vector(*other) + return Vector(self.x * other.x, self.y * other.y, self.z * other.z) + except TypeError: + raise TypeError("Cannot cast {} {} to Vector".format(other, type(other))) + + def __truediv__(self, other): + if isinstance(other, (int, float)): + return Vector(self.x / other, self.y / other, self.z / other) + + try: + other = Vector(*other) + return Vector(self.x / other.x, self.y / other.y, self.z / other.z) + except TypeError: + raise TypeError("Cannot cast {} {} to Vector".format(other, type(other))) def __pow__(self, n): return Vector(self.x**n, self.y**n, self.z**n) @@ -190,6 +204,26 @@ def __ipow__(self, n): self.z **= n return self + def __rmul__(self, n): + return self.__mul__(n) + + def __radd__(self, other): + return self.__add__(other) + + def __rsub__(self, other): + try: + other = Vector(*other) + return other - self + except TypeError: + raise TypeError("Cannot cast {} {} to Vector".format(other, type(other))) + + def __rtruediv__(self, other): + try: + other = Vector(*other) + return other / self + except TypeError: + raise TypeError("Cannot cast {} {} to Vector".format(other, type(other))) + # ========================================================================== # Properties # ========================================================================== diff --git a/tests/compas/geometry/test_vector.py b/tests/compas/geometry/test_vector.py index ba76c1e42ee..5294775665e 100644 --- a/tests/compas/geometry/test_vector.py +++ b/tests/compas/geometry/test_vector.py @@ -47,11 +47,42 @@ def test_vector2(x, y): def test_vector_operators(): a = Vector(random(), random(), random()) b = Vector(random(), random(), random()) - assert a + b == [a.x + b.x, a.y + b.y, a.z + b.z] - assert a - b == [a.x - b.x, a.y - b.y, a.z - b.z] + c = [random(), random(), random()] + assert a * 2 == [a.x * 2, a.y * 2, a.z * 2] assert a / 2 == [a.x / 2, a.y / 2, a.z / 2] assert a**3 == [a.x**3, a.y**3, a.z**3] + assert 2 * a == [2 * a.x, 2 * a.y, 2 * a.z] + + assert a + b == [a.x + b.x, a.y + b.y, a.z + b.z] + assert a - b == [a.x - b.x, a.y - b.y, a.z - b.z] + assert a * b == [a.x * b.x, a.y * b.y, a.z * b.z] + assert a / b == [a.x / b.x, a.y / b.y, a.z / b.z] + + assert b + a == [a.x + b.x, a.y + b.y, a.z + b.z] + assert b - a == [b.x - a.x, b.y - a.y, b.z - a.z] + assert b * a == [a.x * b.x, a.y * b.y, a.z * b.z] + assert b / a == [b.x / a.x, b.y / a.y, b.z / a.z] + + assert a * c == [a.x * c[0], a.y * c[1], a.z * c[2]] + assert c * a == [a.x * c[0], a.y * c[1], a.z * c[2]] + assert a + c == [a.x + c[0], a.y + c[1], a.z + c[2]] + assert a - c == [a.x - c[0], a.y - c[1], a.z - c[2]] + + assert c * a == [a.x * c[0], a.y * c[1], a.z * c[2]] + assert c / a == [c[0] / a.x, c[1] / a.y, c[2] / a.z] + assert c + a == [a.x + c[0], a.y + c[1], a.z + c[2]] + assert c - a == [c[0] - a.x, c[1] - a.y, c[2] - a.z] + + with pytest.raises(TypeError) as exc_info: + a / "wrong type" + if not compas.IPY: + assert str(exc_info.value) == "Cannot cast wrong type to Vector" + + with pytest.raises(TypeError) as exc_info: + a * "wrong type" + if not compas.IPY: + assert str(exc_info.value) == "Cannot cast wrong type to Vector" def test_vector_equality():