From c7f958c5798361020ad9e3798e2f44d99fa11a8f Mon Sep 17 00:00:00 2001 From: Licini Date: Mon, 29 Apr 2024 17:30:47 +0200 Subject: [PATCH 1/5] Vector multiplications --- CHANGELOG.md | 1 + src/compas/geometry/vector.py | 23 ++++++++++++++++++----- tests/compas/geometry/test_vector.py | 4 ++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afce9e69104..acc07f5a49b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Changed use of `compas.geometry.allclose` to `compas.tolerance.TOL.is_allclose`. * Changed use of `compas.geometry.close` to `compas.tolerance.TOL.is_close`. * Changed imports of itertools to `compas.itertools` instead of `compas.utilities`. +* Updated `compas.geometry.vector` with `__rmul__` and allow element-wise multiplication and division with another vector. ### Removed diff --git a/src/compas/geometry/vector.py b/src/compas/geometry/vector.py index 8e1b5702257..3f440752265 100644 --- a/src/compas/geometry/vector.py +++ b/src/compas/geometry/vector.py @@ -148,11 +148,21 @@ 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) + elif isinstance(other, Vector): + return Vector(self.x * other[0], self.y * other[1], self.z * other[2]) + else: + raise TypeError("Multiplication of Vector with unsupported type: {}".format(type(other))) + + def __truediv__(self, other): + if isinstance(other, (int, float)): + return Vector(self.x / other, self.y / other, self.z / other) + elif isinstance(other, Vector): + return Vector(self.x / other[0], self.y / other[1], self.z / other[2]) + else: + raise TypeError("Division of Vector with unsupported type: {}".format(type(other))) def __pow__(self, n): return Vector(self.x**n, self.y**n, self.z**n) @@ -190,6 +200,9 @@ def __ipow__(self, n): self.z **= n return self + def __rmul__(self, n): + return self.__mul__(n) + # ========================================================================== # Properties # ========================================================================== diff --git a/tests/compas/geometry/test_vector.py b/tests/compas/geometry/test_vector.py index ba76c1e42ee..647531a2495 100644 --- a/tests/compas/geometry/test_vector.py +++ b/tests/compas/geometry/test_vector.py @@ -52,6 +52,10 @@ def test_vector_operators(): 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 b * a == [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] def test_vector_equality(): From 640393e0b2f1acd041aee6733d4b24e92c25511c Mon Sep 17 00:00:00 2001 From: Licini Date: Fri, 10 May 2024 17:58:29 +0200 Subject: [PATCH 2/5] try casting, add more tests --- src/compas/geometry/vector.py | 33 +++++++++++++++++++++++----- tests/compas/geometry/test_vector.py | 31 +++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/compas/geometry/vector.py b/src/compas/geometry/vector.py index 3f440752265..d52566b7293 100644 --- a/src/compas/geometry/vector.py +++ b/src/compas/geometry/vector.py @@ -151,18 +151,22 @@ def __sub__(self, other): def __mul__(self, other): if isinstance(other, (int, float)): return Vector(self.x * other, self.y * other, self.z * other) - elif isinstance(other, Vector): - return Vector(self.x * other[0], self.y * other[1], self.z * other[2]) else: - raise TypeError("Multiplication of Vector with unsupported type: {}".format(type(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) - elif isinstance(other, Vector): - return Vector(self.x / other[0], self.y / other[1], self.z / other[2]) else: - raise TypeError("Division of Vector with unsupported type: {}".format(type(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) @@ -203,6 +207,23 @@ def __ipow__(self, n): 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 647531a2495..c45b50dc502 100644 --- a/tests/compas/geometry/test_vector.py +++ b/tests/compas/geometry/test_vector.py @@ -47,16 +47,41 @@ 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 b * a == [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" + assert str(exc_info.value) == "Cannot cast wrong type to Vector" + + with pytest.raises(TypeError) as exc_info: + a * "wrong type" + assert str(exc_info.value) == "Cannot cast wrong type to Vector" + def test_vector_equality(): p1 = Vector(1, 1, 1) From 443483d95fab409cfa48cbcc3661b67f4116dd7e Mon Sep 17 00:00:00 2001 From: Li Date: Mon, 13 May 2024 15:14:14 +0200 Subject: [PATCH 3/5] ironpython --- tests/compas/geometry/test_vector.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/compas/geometry/test_vector.py b/tests/compas/geometry/test_vector.py index c45b50dc502..5294775665e 100644 --- a/tests/compas/geometry/test_vector.py +++ b/tests/compas/geometry/test_vector.py @@ -76,11 +76,13 @@ def test_vector_operators(): with pytest.raises(TypeError) as exc_info: a / "wrong type" - assert str(exc_info.value) == "Cannot cast wrong type to Vector" + 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" - assert str(exc_info.value) == "Cannot cast wrong type to Vector" + if not compas.IPY: + assert str(exc_info.value) == "Cannot cast wrong type to Vector" def test_vector_equality(): From 2cae3900c15eefb66e4379e2e64b509d19e85240 Mon Sep 17 00:00:00 2001 From: Licini Date: Fri, 7 Jun 2024 12:31:54 +0200 Subject: [PATCH 4/5] update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) 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 From 70acd71845f9543f1820d8586cb12e636badba24 Mon Sep 17 00:00:00 2001 From: Licini Date: Fri, 7 Jun 2024 18:51:49 +0200 Subject: [PATCH 5/5] remove else --- src/compas/geometry/vector.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/compas/geometry/vector.py b/src/compas/geometry/vector.py index d52566b7293..7ccf054431f 100644 --- a/src/compas/geometry/vector.py +++ b/src/compas/geometry/vector.py @@ -151,22 +151,22 @@ def __sub__(self, other): def __mul__(self, other): if isinstance(other, (int, float)): return Vector(self.x * other, self.y * other, self.z * other) - else: - 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))) + + 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) - else: - 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))) + + 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)