geometry::OrientedBoundingBox::Transform() always throws, even for a rigid transform
- Dominant language
- C++
- Stars
- 14k
- Forks
- 2.6k
- Avg merge
- 5d 18h
- Merged PRs (30d)
- 6
Description
### Checklist
- [x] I have searched for [similar issues](https://github.com/isl-org/Open3D/issues). The closest one is #4875, which reports the same limitation as a question and is still open.
- [x] For Python issues, I have tested with the [latest development wheel](https://www.open3d.org/docs/latest/getting_started.html#development-version-pip). That page currently lists no wheel links, so I reproduced with 0.19.0 from PyPI and confirmed the code path is unchanged on `main` at 1a9eb990f9, see the permalink below.
- [x] I have checked the [release documentation](https://www.open3d.org/docs/release/) and the [latest documentation](https://www.open3d.org/docs/latest/) (for `main` branch).
### Describe the issue
`geometry::OrientedBoundingBox::Transform()` always raises, even for a plain rigid transform:
https://github.com/isl-org/Open3D/blob/1a9eb990f9a20936c30c428568c602bdef760744/cpp/open3d/geometry/BoundingVolume.cpp#L154-L160
An oriented bounding box is stored as a center, a rotation matrix and an extent, so it is closed under any similarity transform (rotation, uniform scale, translation): only the center and the rotation move, and the extent scales. That is exactly the case that matters in practice, where a box is carried between coordinate frames next to the point cloud it bounds:
```cpp
cloud.Transform(target_T_source);
box.Transform(target_T_source); // throws
```
Three things make the current behavior surprising:
1. Every other `Geometry3D` accepts `Transform()`, so a box cannot be moved through generic code that transforms a geometry.
2. The tensor API already implements it, as `Rotate()` followed by `Translate()`, so the legacy and tensor APIs disagree on the same operation:
https://github.com/isl-org/Open3D/blob/1a9eb990f9a20936c30c428568c602bdef760744/cpp/open3d/t/geometry/BoundingVolume.cpp#L464-L478
3. The suggested workaround ("Call Translate, Scale, and Rotate") is easy to get wrong, because `Rotate(R)` rotates about the box center by default while `Transform()` rotates about the origin. #4875 is exactly that mistake, and the usual fix people land on is to rebuild the box by hand:
```cpp
OrientedBoundingBox transformed(transform * box.center_,
transform.rotation() * box.R_, box.extent_);
```
`geometry::OrientedBoundingEllipsoid::Transform()` on `main` has the same body and the same limitation.
A general affine transform genuinely cannot be supported, since shear or non-uniform scale turns the box into a parallelepiped and the ellipsoid into a general quadric. Those cases should keep raising. The similarity case should not.
### Steps to reproduce the bug
```python
import numpy as np
import open3d as o3d
box = o3d.geometry.OrientedBoundingBox([1, 2, 3], np.eye(3), [2, 4, 6])
transformation = np.eye(4)
transformation[:3, 3] = [1, 0, 0] # translate by 1 along x
box.transform(transformation)
```
### Error message
```
Traceback (most recent call last):
File "", line 5, in
RuntimeError: [Open3D Error] (virtual open3d::geometry::OrientedBoundingBox& open3d::geometry::OrientedBoundingBox::Transform(const Matrix4d&)) /root/Open3D/cpp/open3d/geometry/BoundingVolume.cpp:58: A general transform of an OrientedBoundingBox is not implemented. Call Translate, Scale, and Rotate.
```
### Expected behavior
A similarity transform is applied to the box: `center_` is mapped by the transform, `R_` is premultiplied by its rotation part and `extent_` is multiplied by its uniform scale. The corners of the result are then the transformed corners of the original box, which is what transforming a point cloud of those corners with the same matrix gives.
A transform with shear, non-uniform scale, mirroring or a projective part keeps raising, with a message that names which part is not supported.
### Open3D, Python and System information
```markdown
- Operating system: Ubuntu 24.04 64-bit
- Python version: 3.12.3
- Open3D version: 0.19.0, and the source of `main` at 1a9eb990f9a20936c30c428568c602bdef760744
- System architecture: x86_64
- Is this a remote workstation?: no
- How did you install Open3D?: pip, and separately built from source for the C++ side
- Compiler version (if built from source): gcc 13.3
```
### Additional information
I have a fix ready and will open a PR that implements `Transform()` for similarity transforms on both `OrientedBoundingBox` and `OrientedBoundingEllipsoid`, reusing the existing `Rotate()`, `Scale()` and `Translate()` primitives, with C++ and Python unit tests for the supported and the rejected cases.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start at cpp/open3d/geometry/BoundingVolume.cpp, especially the OrientedBoundingBox::Transform() implementation linked in the issue, and compare it with the tensor BoundingVolume.cpp implementation. Check the corresponding OrientedBoundingEllipsoid path and add the mentioned C++ and Python unit tests. Done means similarity transforms work for both classes while shear, non-uniform scale, mirroring, and projective transforms still raise.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, python
- Domain
- computer-graphics
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100