Skip to content

Commit

Permalink
Merge pull request #1420 from compas-dev/fix-comparisons
Browse files Browse the repository at this point in the history
Fix comparisons point and frame
  • Loading branch information
tomvanmele authored Jan 14, 2025
2 parents 7bbe6ac + 61ae095 commit 645dc74
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* Fixed `NotImplementedError` when calling `compas_rhino.conversions.surface_to_compas` on NURBS Surface.
* Fixed `NotImplementedError` when calling `compas_rhino.conversions.surface_to_compas` on Surface.
* Changed point comparison (`compas.geometry.Point.__eq__`) to use `TOL.is_allclose` instead of raw coordinate comparison.
* Changed vector comparison (`compas.geometry.Vector.__eq__`) to use `TOL.is_allclose` instead of raw coordinate comparison.
* Fixed bug in frame comparison (`compas.geometry.Frame.__eq__`).
* Fixed bug in `compas.geometry.oriented_bounding_box_numpy`.

### Removed

Expand Down
12 changes: 6 additions & 6 deletions src/compas/geometry/bbox_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,15 @@ def oriented_bounding_box_numpy(points, tol=None):
# also compute the axis aligned bounding box
# and compare the areas of the two

rect1, area1 = minimum_area_rectangle_xy(points, return_size=True)

points = world_to_local_coordinates_numpy(frame, points)
rect1, area1 = minimum_area_rectangle_xy(points, return_size=True)
rect2 = bounding_box(points)[:4]
area2 = (rect2[1][0] - rect2[0][0]) * (rect2[3][1] - rect2[0][1])

if area1 < area2:
rect = [[pt[0], pt[1], 0.0] for pt in rect1]
bbox = rect + rect
bbox = local_to_world_coordinates_numpy(frame, rect)
bbox = vstack((bbox, bbox)).tolist()
else:
rect = [[pt[0], pt[1], 0.0] for pt in rect2]
bbox = local_to_world_coordinates_numpy(frame, rect)
Expand Down Expand Up @@ -260,11 +260,12 @@ def minimum_area_rectangle_xy(points, return_size=False):
p0 = points[simplex[0]]
p1 = points[simplex[1]]

vn = xy - p0

# s direction
s = p1 - p0
sl = sum(s**2) ** 0.5
su = s / sl
vn = xy - p0
sc = (sum(vn * s, axis=1) / sl).reshape((-1, 1))
scmax = argmax(sc)
scmin = argmin(sc)
Expand All @@ -277,7 +278,6 @@ def minimum_area_rectangle_xy(points, return_size=False):
t = array([-s[1], s[0]])
tl = sum(t**2) ** 0.5
tu = t / tl
vn = xy - p0
tc = (sum(vn * t, axis=1) / tl).reshape((-1, 1))
tcmax = argmax(tc)
tcmin = argmin(tc)
Expand All @@ -287,7 +287,7 @@ def minimum_area_rectangle_xy(points, return_size=False):
h = tc[tcmax] - tc[tcmin]
a = w * h

# box corners
# other box corners
if dot(t, mean - p0) < 0:
b3 = b0 - h * tu
b2 = b1 - h * tu
Expand Down
4 changes: 2 additions & 2 deletions src/compas/geometry/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,10 @@ def __setitem__(self, key, value):
def __iter__(self):
return iter([self.point, self.xaxis, self.yaxis])

def __eq__(self, other, tol=None):
def __eq__(self, other):
if not hasattr(other, "__iter__") or not hasattr(other, "__len__") or len(self) != len(other):
return False
return TOL.is_allclose(self, other, atol=tol)
return self.point == other[0] and self.xaxis == other[1] and self.yaxis == other[2]

# ==========================================================================
# Properties
Expand Down
2 changes: 1 addition & 1 deletion src/compas/geometry/point.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def __iter__(self):
return iter([self.x, self.y, self.z])

def __eq__(self, other):
return self.x == other[0] and self.y == other[1] and self.z == other[2]
return TOL.is_allclose(self, other)

def __add__(self, other):
return Point(self.x + other[0], self.y + other[1], self.z + other[2])
Expand Down
3 changes: 2 additions & 1 deletion src/compas/geometry/shapes/box.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from compas.geometry import Frame
from compas.geometry import Line
from compas.geometry import Point # noqa: F401
from compas.geometry import Transformation
from compas.geometry import Vector
from compas.geometry import centroid_points
Expand Down Expand Up @@ -478,7 +479,7 @@ def from_points(cls, points): # type: (...) -> Box
# Discretisation
# ==========================================================================

def compute_vertices(self): # type: () -> list[list[float]]
def compute_vertices(self): # type: () -> list[Point]
"""Compute the vertices of the discrete representation of the box."""
point = self.frame.point
xaxis = self.frame.xaxis
Expand Down
2 changes: 1 addition & 1 deletion src/compas/geometry/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def __iter__(self):
return iter([self.x, self.y, self.z])

def __eq__(self, other):
return self.x == other[0] and self.y == other[1] and self.z == other[2]
return TOL.is_allclose(self, other)

def __add__(self, other):
return Vector(self.x + other[0], self.y + other[1], self.z + other[2])
Expand Down
25 changes: 25 additions & 0 deletions tests/compas/geometry/test_bbox.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import pytest
import os
import compas
from random import random
from compas.tolerance import TOL
from compas.geometry import Box
from compas.geometry import bounding_box
from compas.geometry import bounding_box_xy

Expand Down Expand Up @@ -146,3 +148,26 @@ def test_oriented_bounding_box_numpy_from_fixtures():
results = oriented_bounding_box_numpy(coords)
for result, expected_values in zip(results, expected):
assert TOL.is_allclose(result, expected_values)


def test_oriented_bounding_box_numpy_flat():
if compas.IPY:
return

from compas.geometry import oriented_bounding_box_numpy

points = [[10 * random(), 10 * random(), 0] for i in range(100)]
box = Box.from_bounding_box(oriented_bounding_box_numpy(points))

for point in points:
assert box.contains_point(point)

assert not box.contains_point([10 * random(), 10 * random(), 1])

points = [[10 * random(), 10 * random(), 10] for i in range(100)]
box = Box.from_bounding_box(oriented_bounding_box_numpy(points))

for point in points:
assert box.contains_point(point)

assert not box.contains_point([10 * random(), 10 * random(), 11])
32 changes: 32 additions & 0 deletions tests/compas/geometry/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,35 @@ def test_frame_inverted():
assert TOL.is_close(other.xaxis.dot([1, 0, 0]), 1.0)
assert TOL.is_close(other.yaxis.dot([0, -1, 0]), 1.0)
assert TOL.is_close(other.zaxis.dot([0, 0, -1]), 1.0)


def test_frame_comparison_relative():
a = Frame(Point(random(), random(), random()))

b = a.copy()
b.point.x += 0.1 * TOL.relative * b.point.x
assert a == b

c = a.copy()
c.point.x += TOL.relative * c.point.x
assert a == c

d = a.copy()
d.point.x += 10.0 * TOL.relative * d.point.x
assert a != d


def test_frame_comparison_absolute():
a = Frame(Point(0, 0, 0))

b = a.copy()
b.point.x += 0.1 * TOL.absolute
assert a == b

c = a.copy()
c.point.x += TOL.absolute
assert a == c

d = a.copy()
d.point.x += 10.0 * TOL.absolute
assert a != d
21 changes: 21 additions & 0 deletions tests/compas/geometry/test_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import compas
from random import random
from compas.geometry import Point
from compas.tolerance import TOL


@pytest.mark.parametrize(
Expand Down Expand Up @@ -64,6 +65,26 @@ def test_point_equality():
assert not (p1 == p3)


def test_point_comparison_relative():
a = Point(random(), random(), random())
b = Point(a.x + a.x * TOL.relative * 0.1, a.y + a.y * TOL.relative * 0.1, a.z + a.z * TOL.relative * 0.1)
c = Point(a.x + a.x * TOL.relative, a.y + a.y * TOL.relative, a.z + a.z * TOL.relative)
d = Point(a.x + a.x * TOL.relative * 10.0, a.y + a.y * TOL.relative * 10.0, a.z + a.z * TOL.relative * 10.0)
assert a == b
assert a == c
assert a != d


def test_point_comparison_absolute():
a = Point(0, 0, 0)
b = Point(a.x + TOL.absolute * 0.1, a.y + TOL.absolute * 0.1, a.z + TOL.absolute * 0.1)
c = Point(a.x + TOL.absolute, a.y + TOL.absolute, a.z + TOL.absolute)
d = Point(a.x + TOL.absolute * 10.0, a.y + TOL.absolute * 10.0, a.z + TOL.absolute * 10.0)
assert a == b
assert a == c
assert a != d


def test_point_inplace_operators():
pass

Expand Down
21 changes: 21 additions & 0 deletions tests/compas/geometry/test_vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import compas
from random import random
from compas.geometry import Vector
from compas.tolerance import TOL


@pytest.mark.parametrize(
Expand Down Expand Up @@ -95,6 +96,26 @@ def test_vector_equality():
assert not (p1 == p3)


def test_vector_comparison_relative():
a = Vector(random(), random(), random())
b = Vector(a.x + a.x * TOL.relative * 0.1, a.y + a.y * TOL.relative * 0.1, a.z + a.z * TOL.relative * 0.1)
c = Vector(a.x + a.x * TOL.relative, a.y + a.y * TOL.relative, a.z + a.z * TOL.relative)
d = Vector(a.x + a.x * TOL.relative * 10.0, a.y + a.y * TOL.relative * 10.0, a.z + a.z * TOL.relative * 10.0)
assert a == b
assert a == c
assert a != d


def test_vector_comparison_absolute():
a = Vector(0, 0, 0)
b = Vector(a.x + TOL.absolute * 0.1, a.y + TOL.absolute * 0.1, a.z + TOL.absolute * 0.1)
c = Vector(a.x + TOL.absolute, a.y + TOL.absolute, a.z + TOL.absolute)
d = Vector(a.x + TOL.absolute * 10.0, a.y + TOL.absolute * 10.0, a.z + TOL.absolute * 10.0)
assert a == b
assert a == c
assert a != d


def test_vector_inplace_operators():
pass

Expand Down

0 comments on commit 645dc74

Please sign in to comment.