compas-dev / compas

Core packages of the COMPAS framework.
https://compas.dev/compas/
MIT License
313 stars 108 forks source link

LinAlgError on oriented_bounding_box_numpy with some inputs #1127

Open beneltayar opened 1 year ago

beneltayar commented 1 year ago

There are some valid inputs that cause oriented_bounding_box_numpy to throw an numpy.linalg.LinAlgError

To Reproduce

import numpy as np

from compas.geometry import oriented_bounding_box_numpy

oriented_bounding_box_numpy(np.array([
    [198.71435547, -18.45817566, 61.03982544],
    [198.78266907, -18.38731384, 62.90990448],
    [198.78266907, -18.38731384, 61.03982544],
    [199.78454590, -17.34801292, 62.90990448],
    [199.26994324, -17.88183594, 75.31147766],
    [199.78454590, -17.34801292, 64.32066345],
    [199.78454590, -17.34801292, 62.90989685],
]))

Output

Traceback (most recent call last):
  File "C:\Users\benel\AppData\Roaming\JetBrains\PyCharm2022.3\scratches\scratch.py", line 8, in <module>
    oriented_bounding_box_numpy(np.array([
  File "C:\git\buildots\pycode\venv39\lib\site-packages\compas\geometry\bbox\bbox_numpy.py", line 133, in oriented_bounding_box_numpy
    rst = world_to_local_coordinates_numpy(frame, xyz)
  File "C:\git\buildots\pycode\venv39\lib\site-packages\compas\geometry\transformations\transformations_numpy.py", line 141, in world_to_local_coordinates_numpy
    rst = solve(uvw, xyz)
  File "C:\git\buildots\pycode\venv39\lib\site-packages\scipy\linalg\_basic.py", line 223, in solve
    _solve_check(n, info)
  File "C:\git\buildots\pycode\venv39\lib\site-packages\scipy\linalg\_basic.py", line 29, in _solve_check
    raise LinAlgError('Matrix is singular.')
numpy.linalg.LinAlgError: Matrix is singular.

Expected behavior Getting an oriented bounding box of this specific set of points

Desktop:

tomvanmele commented 1 year ago

hi,

that is indeed a bug. when possible, the code tries to use the convex hull of the points to build the oriented box, but it doesn't check the validity of the simplices of the hull during that process, which it should.

i will have a look at how this can be done more robustly. in the meantime, you could use the function the algorithm normally falls back to if the convex hull algorithm fails. it is more robust but doesn't always produce the smallest oriented box for the input.

from compas.geometry import Point, Box
from compas.geometry.bbox.bbox_numpy import oabb_numpy
from compas_view2.app import App

points = [
    [198.71435547, -18.45817566, 61.03982544],
    [198.78266907, -18.38731384, 62.90990448],
    [198.78266907, -18.38731384, 61.03982544],
    [199.78454590, -17.34801292, 62.90990448],
    [199.26994324, -17.88183594, 75.31147766],
    [199.78454590, -17.34801292, 64.32066345],
    [199.78454590, -17.34801292, 62.90989685],
]

box = oabb_numpy(points)
box = Box.from_bounding_box(box)

viewer = App()

for point in points:
    viewer.add(Point(*point))

viewer.add(box)

viewer.run()
Screenshot 2023-03-15 at 18 18 30
tomvanmele commented 11 months ago

@beneltayar i think this is now resolved. would you mind having a look?