Skip to content

Commit 8f5d3b4

Browse files
authored
Fix minimal oriented bounding box of MeshBase derived classes and add new unit tests (#6898)
* Fix minimal oriented bounding box of MeshBase derived classes and add new unit tests * Update CHANGELOG
1 parent b271acb commit 8f5d3b4

4 files changed

Lines changed: 452 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
- Fix build with fmt v10.2.0 (#6783)
4141
- Fix segmentation fault (lambda reference capture) of VisualizerWithCustomAnimation::Play (PR #6804)
4242
- Add O3DVisualizer API to enable collapse control of verts in the side panel (PR #6865)
43+
- Fix minimal oriented bounding box of MeshBase derived classes and add new unit tests (PR #6898)
4344

4445
## 0.13
4546

cpp/open3d/geometry/MeshBase.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ OrientedBoundingBox MeshBase::GetOrientedBoundingBox(bool robust) const {
5151
}
5252

5353
OrientedBoundingBox MeshBase::GetMinimalOrientedBoundingBox(bool robust) const {
54-
return OrientedBoundingBox::CreateFromPoints(vertices_, robust);
54+
return OrientedBoundingBox::CreateFromPointsMinimal(vertices_, robust);
5555
}
5656

5757
MeshBase &MeshBase::Transform(const Eigen::Matrix4d &transformation) {

cpp/tests/geometry/TetraMesh.cpp

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <Eigen/Geometry>
1111

12+
#include "open3d/geometry/BoundingVolume.h"
1213
#include "open3d/geometry/PointCloud.h"
1314
#include "open3d/geometry/TriangleMesh.h"
1415
#include "tests/Tests.h"
@@ -119,6 +120,229 @@ TEST(TetraMesh, GetMaxBound) {
119120
tm.GetMaxBound());
120121
}
121122

123+
TEST(TetraMesh, GetCenter) {
124+
int size = 100;
125+
126+
Eigen::Vector3d dmin(0.0, 0.0, 0.0);
127+
Eigen::Vector3d dmax(1000.0, 1000.0, 1000.0);
128+
129+
geometry::TetraMesh tm;
130+
131+
tm.vertices_.resize(size);
132+
Rand(tm.vertices_, dmin, dmax, 0);
133+
134+
ExpectEQ(tm.GetCenter(),
135+
Eigen::Vector3d(531.137254, 535.176470, 501.882352));
136+
137+
geometry::TetraMesh tm_empty;
138+
ExpectEQ(tm_empty.GetCenter(), Eigen::Vector3d(0, 0, 0));
139+
}
140+
141+
TEST(TetraMesh, GetAxisAlignedBoundingBox) {
142+
geometry::TetraMesh tm;
143+
geometry::AxisAlignedBoundingBox aabb;
144+
145+
tm = geometry::TetraMesh();
146+
aabb = tm.GetAxisAlignedBoundingBox();
147+
EXPECT_EQ(aabb.min_bound_, Eigen::Vector3d(0, 0, 0));
148+
EXPECT_EQ(aabb.max_bound_, Eigen::Vector3d(0, 0, 0));
149+
EXPECT_EQ(aabb.color_, Eigen::Vector3d(1, 1, 1));
150+
151+
tm = geometry::TetraMesh({{0, 0, 0}}, {{0, 0, 0, 0}});
152+
aabb = tm.GetAxisAlignedBoundingBox();
153+
EXPECT_EQ(aabb.min_bound_, Eigen::Vector3d(0, 0, 0));
154+
EXPECT_EQ(aabb.max_bound_, Eigen::Vector3d(0, 0, 0));
155+
EXPECT_EQ(aabb.color_, Eigen::Vector3d(1, 1, 1));
156+
157+
tm = geometry::TetraMesh({{0, 2, 0},
158+
{1, 1, 2},
159+
{1, 0, 3},
160+
{0, 1, 4},
161+
{1, 2, 5},
162+
{1, 3, 6},
163+
{0, 0, 7},
164+
{0, 3, 8},
165+
{1, 0, 9},
166+
{0, 2, 10}},
167+
{{0, 1, 2, 3},
168+
{4, 0, 1, 2},
169+
{4, 5, 2, 3},
170+
{4, 0, 2, 3},
171+
{4, 0, 5, 3},
172+
{6, 0, 5, 3},
173+
{6, 7, 8, 5},
174+
{9, 4, 7, 1},
175+
{9, 4, 0, 1},
176+
{9, 0, 1, 3},
177+
{9, 6, 7, 8},
178+
{9, 4, 7, 5},
179+
{9, 6, 7, 5},
180+
{9, 4, 0, 5},
181+
{9, 6, 0, 5}});
182+
aabb = tm.GetAxisAlignedBoundingBox();
183+
EXPECT_EQ(aabb.min_bound_, Eigen::Vector3d(0, 0, 0));
184+
EXPECT_EQ(aabb.max_bound_, Eigen::Vector3d(1, 3, 10));
185+
EXPECT_EQ(aabb.color_, Eigen::Vector3d(1, 1, 1));
186+
}
187+
188+
TEST(TetraMesh, GetOrientedBoundingBox) {
189+
geometry::TetraMesh tm;
190+
geometry::OrientedBoundingBox obb;
191+
192+
// Empty (GetOrientedBoundingBox requires >=4 points)
193+
tm = geometry::TetraMesh();
194+
EXPECT_ANY_THROW(tm.GetOrientedBoundingBox());
195+
196+
// Point
197+
tm = geometry::TetraMesh({{0, 0, 0}}, {{0, 0, 0, 0}});
198+
EXPECT_ANY_THROW(tm.GetOrientedBoundingBox());
199+
tm = geometry::TetraMesh({{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
200+
{{0, 1, 2, 3}});
201+
EXPECT_ANY_THROW(tm.GetOrientedBoundingBox());
202+
EXPECT_NO_THROW(tm.GetOrientedBoundingBox(true));
203+
204+
// Plane
205+
tm = geometry::TetraMesh({{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}},
206+
{{0, 1, 2, 3}});
207+
EXPECT_ANY_THROW(tm.GetOrientedBoundingBox());
208+
EXPECT_NO_THROW(tm.GetOrientedBoundingBox(true));
209+
210+
// Valid 4 points
211+
tm = geometry::TetraMesh({{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {1, 1, 1}},
212+
{{0, 1, 2, 3}});
213+
tm.GetOrientedBoundingBox();
214+
215+
// 8 points with known ground truth
216+
tm = geometry::TetraMesh({{0, 0, 0},
217+
{0, 0, 1},
218+
{0, 2, 0},
219+
{0, 2, 1},
220+
{3, 0, 0},
221+
{3, 0, 1},
222+
{3, 2, 0},
223+
{3, 2, 1}},
224+
{{0, 1, 2, 3},
225+
{4, 5, 6, 7},
226+
{0, 1, 4, 5},
227+
{2, 3, 6, 7},
228+
{0, 2, 4, 6},
229+
{1, 3, 5, 7}});
230+
obb = tm.GetOrientedBoundingBox();
231+
EXPECT_EQ(obb.center_, Eigen::Vector3d(1.5, 1, 0.5));
232+
EXPECT_EQ(obb.extent_, Eigen::Vector3d(3, 2, 1));
233+
EXPECT_EQ(obb.color_, Eigen::Vector3d(1, 1, 1));
234+
EXPECT_EQ(obb.R_, Eigen::Matrix3d::Identity());
235+
ExpectEQ(Sort(obb.GetBoxPoints()),
236+
Sort(std::vector<Eigen::Vector3d>({{0, 0, 0},
237+
{0, 0, 1},
238+
{0, 2, 0},
239+
{0, 2, 1},
240+
{3, 0, 0},
241+
{3, 0, 1},
242+
{3, 2, 0},
243+
{3, 2, 1}})));
244+
245+
// Check for a bug where the OBB rotation contained a reflection for this
246+
// example.
247+
tm = geometry::TetraMesh({{0, 2, 4}, {7, 9, 1}, {5, 2, 0}, {3, 8, 7}},
248+
{{0, 1, 2, 3}});
249+
obb = tm.GetOrientedBoundingBox();
250+
EXPECT_GT(obb.R_.determinant(), 0.999);
251+
}
252+
253+
TEST(TetraMesh, GetMinimalOrientedBoundingBox) {
254+
geometry::TetraMesh tm;
255+
geometry::OrientedBoundingBox obb;
256+
257+
// Empty (GetOrientedBoundingBox requires >=4 points)
258+
tm = geometry::TetraMesh();
259+
EXPECT_ANY_THROW(tm.GetMinimalOrientedBoundingBox());
260+
261+
// Point
262+
tm = geometry::TetraMesh({{0, 0, 0}}, {{0, 0, 0, 0}});
263+
EXPECT_ANY_THROW(tm.GetMinimalOrientedBoundingBox());
264+
tm = geometry::TetraMesh({{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
265+
{{0, 1, 2, 3}});
266+
EXPECT_ANY_THROW(tm.GetMinimalOrientedBoundingBox());
267+
EXPECT_NO_THROW(tm.GetMinimalOrientedBoundingBox(true));
268+
269+
// Plane
270+
tm = geometry::TetraMesh({{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}},
271+
{{0, 1, 2, 3}});
272+
EXPECT_ANY_THROW(tm.GetMinimalOrientedBoundingBox());
273+
EXPECT_NO_THROW(tm.GetMinimalOrientedBoundingBox(true));
274+
275+
// Valid 4 points
276+
tm = geometry::TetraMesh({{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {1, 1, 1}},
277+
{{0, 1, 2, 3}});
278+
tm.GetMinimalOrientedBoundingBox();
279+
280+
// 8 points with known ground truth
281+
tm = geometry::TetraMesh({{0, 0, 0},
282+
{0, 0, 1},
283+
{0, 2, 0},
284+
{0, 2, 1},
285+
{3, 0, 0},
286+
{3, 0, 1},
287+
{3, 2, 0},
288+
{3, 2, 1}},
289+
{{0, 1, 2, 3},
290+
{4, 5, 6, 7},
291+
{0, 1, 4, 5},
292+
{2, 3, 6, 7},
293+
{0, 2, 4, 6},
294+
{1, 3, 5, 7}});
295+
obb = tm.GetMinimalOrientedBoundingBox();
296+
EXPECT_EQ(obb.center_, Eigen::Vector3d(1.5, 1, 0.5));
297+
EXPECT_EQ(obb.extent_, Eigen::Vector3d(3, 2, 1));
298+
EXPECT_EQ(obb.color_, Eigen::Vector3d(1, 1, 1));
299+
ExpectEQ(Sort(obb.GetBoxPoints()),
300+
Sort(std::vector<Eigen::Vector3d>({{0, 0, 0},
301+
{0, 0, 1},
302+
{0, 2, 0},
303+
{0, 2, 1},
304+
{3, 0, 0},
305+
{3, 0, 1},
306+
{3, 2, 0},
307+
{3, 2, 1}})));
308+
309+
// Check for a bug where the OBB rotation contained a reflection for this
310+
// example.
311+
tm = geometry::TetraMesh({{0, 2, 4}, {7, 9, 1}, {5, 2, 0}, {3, 8, 7}},
312+
{{0, 1, 2, 3}});
313+
obb = tm.GetMinimalOrientedBoundingBox();
314+
EXPECT_GT(obb.R_.determinant(), 0.999);
315+
316+
// should always be equal/smaller than axis aligned- & oriented bounding box
317+
tm = geometry::TetraMesh({{0.866, 0.474, 0.659},
318+
{0.943, 0.025, 0.789},
319+
{0.386, 0.264, 0.691},
320+
{0.938, 0.588, 0.496},
321+
{0.221, 0.116, 0.257},
322+
{0.744, 0.182, 0.052},
323+
{0.019, 0.525, 0.699},
324+
{0.722, 0.134, 0.668}},
325+
{{0, 1, 2, 3},
326+
{4, 0, 2, 3},
327+
{4, 0, 1, 2},
328+
{5, 4, 6, 3},
329+
{5, 4, 2, 3},
330+
{5, 4, 1, 2},
331+
{7, 1, 2, 6},
332+
{7, 5, 2, 6},
333+
{7, 5, 1, 2},
334+
{7, 5, 4, 6},
335+
{7, 5, 4, 1},
336+
{7, 0, 1, 6},
337+
{7, 4, 0, 6},
338+
{7, 4, 0, 1}});
339+
geometry::OrientedBoundingBox mobb = tm.GetMinimalOrientedBoundingBox();
340+
obb = tm.GetOrientedBoundingBox();
341+
geometry::AxisAlignedBoundingBox aabb = tm.GetAxisAlignedBoundingBox();
342+
EXPECT_GT(obb.Volume(), mobb.Volume());
343+
EXPECT_GT(aabb.Volume(), mobb.Volume());
344+
}
345+
122346
TEST(TetraMesh, Transform) {
123347
std::vector<Eigen::Vector3d> ref_vertices = {
124348
{1.411252, 4.274168, 3.130918}, {1.231757, 4.154505, 3.183678},

0 commit comments

Comments
 (0)