10 #include <Eigen/src/Core/Matrix.h>
21 namespace minimum_obb {
28 :
R_(Eigen::Matrix3d::Identity()),
29 extent_(Eigen::Vector3d::Zero()),
30 center_(Eigen::Vector3d::Zero()) {}
72 if (hull_mesh.GetVertexPositions().NumElements() == 0) {
78 const std::vector<Eigen::Vector3d>& hull_v =
80 hull_mesh.GetVertexPositions());
81 const std::vector<Eigen::Vector3i>& hull_t =
83 hull_mesh.GetTriangleIndices());
84 int num_vertices =
static_cast<int>(hull_v.size());
85 int num_triangles =
static_cast<int>(hull_t.size());
88 hull_mesh.GetVertexPositions())
90 double min_volume = min_obb.Volume();
93 if (num_vertices <= 3 || num_triangles < 1) {
98 auto mapOBBToClosestIdentity = [&](EigenOBB obb) {
99 Eigen::Matrix3d& R = obb.R_;
100 Eigen::Vector3d& extent = obb.extent_;
101 Eigen::Vector3d col[3] = {R.col(0), R.col(1), R.col(2)};
102 Eigen::Vector3d ext = extent;
103 double best_score = -1e9;
104 Eigen::Matrix3d best_R = Eigen::Matrix3d::Identity();
105 Eigen::Vector3d best_extent{{-1, -1, -1}};
107 static const std::array<std::array<int, 3>, 6> permutations = {
116 for (
const auto& p : permutations) {
117 for (
int sign_bits = 0; sign_bits < 8; ++sign_bits) {
120 const int s0 = (sign_bits & 1) ? 1 : -1;
121 const int s1 = (sign_bits & 2) ? 1 : -1;
122 const int s2 = (sign_bits & 4) ? 1 : -1;
125 Eigen::Vector3d c0 = s0 * col[p[0]];
126 Eigen::Vector3d c1 = s1 * col[p[1]];
127 Eigen::Vector3d c2 = s2 * col[p[2]];
132 double score = c0(0) + c1(1) + c2(2);
135 if (score > best_score) {
143 best_extent(0) = ext(p[0]);
144 best_extent(1) = ext(p[1]);
145 best_extent(2) = ext(p[2]);
152 obb.extent_ = best_extent;
158 std::vector<std::vector<int>> adjacency_data;
159 adjacency_data.reserve(num_vertices);
160 adjacency_data.insert(adjacency_data.end(), num_vertices,
163 std::vector<Eigen::Vector3d> face_normals;
164 face_normals.reserve(num_triangles);
167 std::vector<std::pair<int, int>> edges;
168 edges.reserve(num_vertices * 2);
171 std::vector<std::pair<int, int>> faces_for_edge;
172 faces_for_edge.reserve(num_vertices * 2);
174 constexpr
unsigned int empty_edge =
176 std::vector<unsigned int> vertex_pairs_to_edges(num_vertices * num_vertices,
179 for (
int i = 0; i < num_triangles; ++i) {
181 int t0 = tri(0), t1 = tri(1), t2 = tri(2);
182 int v0 = t2, v1 = t0;
184 for (
int j = 0; j < 3; ++j) {
188 adjacency_data[v0].push_back(v1);
191 unsigned int& ref_idx1 =
192 vertex_pairs_to_edges[v0 * num_vertices + v1];
193 unsigned int& ref_idx2 =
194 vertex_pairs_to_edges[v1 * num_vertices + v0];
195 if (ref_idx1 == empty_edge) {
197 unsigned int new_idx =
static_cast<unsigned int>(edges.size());
200 edges.emplace_back(v0, v1);
201 faces_for_edge.emplace_back(i, -1);
204 faces_for_edge[ref_idx1].second = i;
210 auto n = (hull_v[t1] - hull_v[t0]).
cross(hull_v[t2] - hull_v[t0]);
211 face_normals.push_back(n.normalized());
219 auto isInternalEdge = [&](std::size_t iEdge) noexcept {
220 return (face_normals[faces_for_edge[iEdge].first].
dot(
221 face_normals[faces_for_edge[iEdge].second]) >
239 int edge_size = edges.size();
240 std::vector<std::vector<int>> antipodal_points_for_edge(edge_size);
241 antipodal_points_for_edge.reserve(edge_size);
243 std::vector<unsigned int> flood_fill_visited(num_vertices, 0u);
244 unsigned int flood_fill_visit_color = 1u;
246 auto markVertexVisited = [&](
int v) {
247 flood_fill_visited[v] = flood_fill_visit_color;
250 auto haveVisitedVertex = [&](
int v) ->
bool {
251 return flood_fill_visited[v] == flood_fill_visit_color;
254 auto clearGraphSearch = [&]() { ++flood_fill_visit_color; };
256 auto isVertexAntipodalToEdge =
257 [&](
int vi,
const std::vector<int>& neighbors,
258 const Eigen::Vector3d& f1a,
259 const Eigen::Vector3d& f1b) noexcept ->
bool {
260 constexpr
double epsilon = 1e-4;
261 constexpr
double degenerate_threshold = -5e-2;
266 const auto& v = hull_v[vi];
267 Eigen::Vector3d f1b_f1a = f1b - f1a;
270 for (
int neighbor_index : neighbors) {
271 const auto& neighbor = hull_v[neighbor_index];
274 Eigen::Vector3d e = neighbor - v;
277 double s = f1b_f1a.dot(e);
278 double n = f1b.dot(e);
283 }
else if (s < -epsilon) {
285 }
else if (n < -epsilon) {
291 if ((t_max - t_min) < degenerate_threshold) {
298 auto extremeVertexConvex =
299 [&](
auto&
self,
const Eigen::Vector3d& direction,
300 std::vector<unsigned int>& flood_fill_visited,
301 unsigned int flood_fill_visit_color,
302 double& most_extreme_distance,
int starting_vertex) ->
int {
304 double cur_dot = direction.dot(hull_v[starting_vertex]);
307 const int* neighbors = &adjacency_data[starting_vertex][0];
308 const int* neighbors_end =
309 neighbors + adjacency_data[starting_vertex].size();
312 flood_fill_visited[starting_vertex] = flood_fill_visit_color;
315 int second_best = -1;
316 double second_best_dot = cur_dot - 1e-3;
317 while (neighbors != neighbors_end) {
318 int n = *neighbors++;
319 if (flood_fill_visited[n] != flood_fill_visit_color) {
320 double dot = direction.dot(hull_v[n]);
325 flood_fill_visited[starting_vertex] =
326 flood_fill_visit_color;
327 neighbors = &adjacency_data[starting_vertex][0];
329 neighbors + adjacency_data[starting_vertex].size();
331 second_best_dot = cur_dot - 1e-3;
332 }
else if (
dot > second_best_dot) {
335 second_best_dot =
dot;
341 if (second_best != -1 &&
342 flood_fill_visited[second_best] != flood_fill_visit_color) {
343 double second_most_extreme =
345 int second_try =
self(
self, direction, flood_fill_visited,
346 flood_fill_visit_color, second_most_extreme,
349 if (second_most_extreme > cur_dot) {
350 most_extreme_distance = second_most_extreme;
355 most_extreme_distance = cur_dot;
356 return starting_vertex;
361 std::vector<int> spatial_face_order;
362 spatial_face_order.reserve(num_triangles);
363 std::vector<int> spatial_edge_order;
364 spatial_edge_order.reserve(edge_size);
367 std::random_device rd;
368 std::mt19937 rng(rd());
371 std::vector<unsigned int> visited_edges(edge_size, 0u);
372 std::vector<unsigned int> visited_faces(num_triangles, 0u);
374 std::vector<std::pair<int, int>> traverse_stack_edges;
375 traverse_stack_edges.reserve(edge_size);
376 traverse_stack_edges.emplace_back(0, adjacency_data[0].front());
377 while (!traverse_stack_edges.empty()) {
378 auto e = traverse_stack_edges.back();
379 traverse_stack_edges.pop_back();
383 vertex_pairs_to_edges[e.first * num_vertices + e.second];
384 if (visited_edges[edge_idx])
continue;
385 visited_edges[edge_idx] = 1;
386 auto& ff = faces_for_edge[edge_idx];
387 if (!visited_faces[ff.first]) {
388 visited_faces[ff.first] = 1;
389 spatial_face_order.push_back(ff.first);
391 if (!visited_faces[ff.second]) {
392 visited_faces[ff.second] = 1;
393 spatial_face_order.push_back(ff.second);
397 if (!isInternalEdge(edge_idx)) {
398 spatial_edge_order.push_back(edge_idx);
402 size_t size_before = traverse_stack_edges.size();
403 for (
int v1 : adjacency_data[v0]) {
404 int e1 = vertex_pairs_to_edges[v0 * num_vertices + v1];
405 if (visited_edges[e1])
continue;
411 static_cast<int>(traverse_stack_edges.size() - size_before);
412 if (n_new_edges > 0) {
413 std::uniform_int_distribution<> distr(0, n_new_edges - 1);
416 traverse_stack_edges[size_before + r]);
426 std::vector<int> traverse_stack;
433 int start_vertex = 0;
438 for (
int edge_i : spatial_edge_order) {
439 auto [face_i_a, face_i_b] = faces_for_edge[edge_i];
440 const Eigen::Vector3d& f1a = face_normals[face_i_a];
441 const Eigen::Vector3d& f1b = face_normals[face_i_b];
445 start_vertex = extremeVertexConvex(
446 extremeVertexConvex, -f1a, flood_fill_visited,
447 flood_fill_visit_color, dummy, start_vertex);
450 traverse_stack.push_back(start_vertex);
451 markVertexVisited(start_vertex);
452 while (!traverse_stack.empty()) {
453 int v = traverse_stack.back();
454 traverse_stack.pop_back();
455 const auto& neighbors = adjacency_data[v];
456 if (isVertexAntipodalToEdge(v, neighbors, f1a, f1b)) {
457 if (edges[edge_i].first == v || edges[edge_i].second == v) {
460 antipodal_points_for_edge[edge_i].push_back(v);
461 for (
size_t j = 0; j < neighbors.size(); ++j) {
462 if (!haveVisitedVertex(neighbors[j])) {
463 traverse_stack.push_back(neighbors[j]);
464 markVertexVisited(neighbors[j]);
474 if (antipodal_points_for_edge[edge_i].empty()) {
477 for (
int j = 0; j < num_vertices; ++j) {
478 if (isVertexAntipodalToEdge(j, adjacency_data[j], f1a, f1b)) {
479 antipodal_points_for_edge[edge_i].push_back(j);
486 std::vector<unsigned char> sidepodal_vertices(edge_size * num_vertices, 0);
490 std::vector<std::vector<int>> compatible_edges(edge_size);
491 compatible_edges.reserve(edge_size);
496 for (
int edge_i : spatial_edge_order) {
497 auto [face_i_a, face_i_b] = faces_for_edge[edge_i];
498 const Eigen::Vector3d& f1a = face_normals[face_i_a];
499 const Eigen::Vector3d& f1b = face_normals[face_i_b];
503 Eigen::Vector3d dead_direction = (f1a + f1b) * 0.5;
504 Eigen::Vector3d basis1, basis2;
505 double sign = std::copysign(1.0, dead_direction.z());
506 const double a = -1.0 / (sign + dead_direction.z());
507 const double b = dead_direction.x() * dead_direction.y() * a;
508 basis1 = Eigen::Vector3d(
509 1.0 + sign * dead_direction.x() * dead_direction.x() * a,
510 sign * b, -sign * dead_direction.x());
511 basis2 = Eigen::Vector3d(
512 b, sign + dead_direction.y() * dead_direction.y() * a,
513 -dead_direction.y());
516 Eigen::Vector3d dir =
517 (f1a.cross(Eigen::Vector3d(0, 1, 0))).normalized();
518 if (dir.norm() < 1e-4) {
519 dir = Eigen::Vector3d(0, 0, 1);
522 start_vertex = extremeVertexConvex(
523 extremeVertexConvex, dir, flood_fill_visited,
524 flood_fill_visit_color, dummy, start_vertex);
526 traverse_stack.push_back(start_vertex);
527 while (!traverse_stack.empty()) {
528 int v = traverse_stack.back();
529 traverse_stack.pop_back();
531 if (haveVisitedVertex(v))
continue;
532 markVertexVisited(v);
535 for (
int v_adj : adjacency_data[v]) {
536 if (haveVisitedVertex(v_adj))
continue;
537 int edge = vertex_pairs_to_edges[v * num_vertices + v_adj];
538 auto [face_i_a, face_i_b] = faces_for_edge[edge];
539 Eigen::Vector3d f1a_f1b = f1a - f1b;
540 Eigen::Vector3d f2a_f2b =
541 face_normals[face_i_a] - face_normals[face_i_b];
543 double a2 = f1b.dot(face_normals[face_i_b]);
544 double b2 = f1a_f1b.dot(face_normals[face_i_b]);
545 double c2 = f2a_f2b.dot(f1b);
546 double d2 = f1a_f1b.dot(f2a_f2b);
549 double abcd = ab + c2 + d2;
550 double min_val =
std::min({a2, ab, ac, abcd});
551 double max_val =
std::max({a2, ab, ac, abcd});
552 bool are_edges_compatible_for_obb =
553 (min_val <= 0.0 && max_val >= 0.0);
555 if (are_edges_compatible_for_obb) {
556 if (edge_i <= edge) {
557 if (!isInternalEdge(edge)) {
558 compatible_edges[edge_i].push_back(edge);
561 sidepodal_vertices[edge_i * num_vertices +
562 edges[edge].first] = 1;
563 sidepodal_vertices[edge_i * num_vertices +
564 edges[edge].second] = 1;
565 if (edge_i != edge) {
566 if (!isInternalEdge(edge)) {
567 compatible_edges[edge].push_back(edge_i);
569 sidepodal_vertices[edge * num_vertices +
570 edges[edge_i].first] = 1;
571 sidepodal_vertices[edge * num_vertices +
572 edges[edge_i].second] = 1;
575 traverse_stack.push_back(v_adj);
602 std::vector<int> traverseStackCommonSidepodals;
603 traverseStackCommonSidepodals.reserve(num_vertices);
604 for (
int edge_i : spatial_edge_order) {
605 auto [face_i_a, face_i_b] = faces_for_edge[edge_i];
606 const Eigen::Vector3d& f1a = face_normals[face_i_a];
607 const Eigen::Vector3d& f1b = face_normals[face_i_b];
609 const auto& compatible_edges_i = compatible_edges[edge_i];
610 Eigen::Vector3d baseDir = 0.5 * (f1a + f1b);
612 for (
int edge_j : compatible_edges_i) {
613 if (edge_j <= edge_i)
continue;
614 auto [faceJ_a, faceJ_b] = faces_for_edge[edge_j];
615 const Eigen::Vector3d& f2a = face_normals[faceJ_a];
616 const Eigen::Vector3d& f2b = face_normals[faceJ_b];
619 Eigen::Vector3d dead_dir = 0.5 * (f2a + f2b);
620 Eigen::Vector3d search_dir = baseDir.cross(dead_dir);
621 search_dir = search_dir.normalized();
622 if (search_dir.norm() < 1e-9) {
623 search_dir = f1a.cross(f2a);
624 search_dir = search_dir.normalized();
625 if (search_dir.norm() < 1e-9) {
627 (f1a.cross(Eigen::Vector3d(0, 1, 0))).normalized();
633 v_hint1 = extremeVertexConvex(
634 extremeVertexConvex, search_dir, flood_fill_visited,
635 flood_fill_visit_color, dummy, v_hint1);
637 v_hint2 = extremeVertexConvex(
638 extremeVertexConvex, -search_dir, flood_fill_visited,
639 flood_fill_visit_color, dummy, v_hint2);
641 int secondSearch = -1;
642 if (sidepodal_vertices[edge_j * num_vertices + v_hint1]) {
643 traverseStackCommonSidepodals.push_back(v_hint1);
645 traverse_stack.push_back(v_hint1);
647 if (sidepodal_vertices[edge_j * num_vertices + v_hint2]) {
648 traverseStackCommonSidepodals.push_back(v_hint2);
650 secondSearch = v_hint2;
655 while (!traverse_stack.empty()) {
656 int v = traverse_stack.front();
657 traverse_stack.erase(traverse_stack.begin());
658 if (haveVisitedVertex(v))
continue;
659 markVertexVisited(v);
660 const auto& neighbors = adjacency_data[v];
661 for (
int v_adj : neighbors) {
662 if (!haveVisitedVertex(v_adj) &&
663 sidepodal_vertices[edge_i * num_vertices + v_adj]) {
664 if (sidepodal_vertices[edge_j * num_vertices + v_adj]) {
665 traverse_stack.clear();
666 if (secondSearch != -1) {
667 traverse_stack.push_back(secondSearch);
669 markVertexVisited(v_adj);
671 traverseStackCommonSidepodals.push_back(v_adj);
674 traverse_stack.push_back(v_adj);
681 while (!traverseStackCommonSidepodals.empty()) {
682 int v = traverseStackCommonSidepodals.back();
683 traverseStackCommonSidepodals.pop_back();
684 if (haveVisitedVertex(v))
continue;
685 markVertexVisited(v);
686 const auto& neighbors = adjacency_data[v];
687 for (
int v_adj : neighbors) {
689 vertex_pairs_to_edges[v * num_vertices + v_adj];
690 int idx_i = edge_i * num_vertices + v_adj;
691 int idx_j = edge_j * num_vertices + v_adj;
693 if (isInternalEdge(edge_k))
continue;
695 if (sidepodal_vertices[idx_i] &&
696 sidepodal_vertices[idx_j]) {
697 if (!haveVisitedVertex(v_adj)) {
698 traverseStackCommonSidepodals.push_back(v_adj);
700 if (edge_j < edge_k) {
701 auto [faceK_a, faceK_b] = faces_for_edge[edge_k];
702 const Eigen::Vector3d& f3a = face_normals[faceK_a];
703 const Eigen::Vector3d& f3b = face_normals[faceK_b];
705 std::vector<Eigen::Vector3d> n1 = {
706 Eigen::Vector3d::Zero(),
707 Eigen::Vector3d::Zero()};
708 std::vector<Eigen::Vector3d> n2 = {
709 Eigen::Vector3d::Zero(),
710 Eigen::Vector3d::Zero()};
711 std::vector<Eigen::Vector3d> n3 = {
712 Eigen::Vector3d::Zero(),
713 Eigen::Vector3d::Zero()};
715 constexpr
double eps = 1e-4;
716 constexpr
double angle_eps = 1e-3;
722 Eigen::Vector3d a = f1b;
723 Eigen::Vector3d b = f1a - f1b;
724 Eigen::Vector3d c = f2b;
725 Eigen::Vector3d d = f2a - f2b;
726 Eigen::Vector3d e = f3b;
727 Eigen::Vector3d f = f3a - f3b;
730 double g = a.dot(c) * d.dot(e) -
732 double h = a.dot(c) * d.dot(f) -
734 double i = b.dot(c) * d.dot(e) -
736 double j = b.dot(c) * d.dot(f) -
738 double k = g * b.dot(e) - a.dot(e) * i;
739 double l = h * b.dot(e) + g * b.dot(f) -
740 a.dot(f) * i - a.dot(e) * j;
741 double m = h * b.dot(f) - a.dot(f) * j;
742 double s = l * l - 4 * m * k;
747 double t = -(g + h * v) / (i + j * v);
748 double u = -(c.dot(e) + c.dot(f) * v) /
749 (d.dot(e) + d.dot(f) * v);
754 if (v >= -eps && t >= -eps && u >= -eps &&
755 v <= 1.0 + eps && t <= 1.0 + eps &&
757 n1[0] = (a + b * t).normalized();
758 n2[0] = (c + d * u).normalized();
759 n3[0] = (e + f * v).normalized();
776 double sgn_l = l < 0 ? -1.0 : 1.0;
778 -(l + sgn_l * std::sqrt(s)) /
780 double V2 = k / (m * V1);
782 -(g + h * V1) / (i + j * V1);
784 -(g + h * V2) / (i + j * V2);
786 -(c.dot(e) + c.dot(f) * V1) /
787 (d.dot(e) + d.dot(f) * V1);
789 -(c.dot(e) + c.dot(f) * V2) /
790 (d.dot(e) + d.dot(f) * V2);
792 if (V1 >= -eps && T1 >= -eps &&
793 U1 >= -eps && V1 <= 1.0 + eps &&
797 (a + b * T1).normalized();
799 (c + d * U1).normalized();
801 (e + f * V1).normalized();
814 if (V2 >= -eps && T2 >= -eps &&
815 U2 >= -eps && V2 <= 1.0 + eps &&
819 (a + b * T2).normalized();
821 (c + d * U2).normalized();
823 (e + f * V2).normalized();
835 if (s < 1e-4 && n_solutions == 2) {
842 for (
int s = 0; s < n_solutions; ++s) {
843 const auto& hull_v_i =
844 hull_v[edges[edge_i].first];
845 const auto& hull_v_j =
846 hull_v[edges[edge_j].first];
847 const auto& hull_v_k =
848 hull_v[edges[edge_k].first];
849 const auto& n1_ = n1[s];
850 const auto& n2_ = n2[s];
851 const auto& n3_ = n3[s];
855 double max_n1 = n1_.dot(hull_v_i);
856 double max_n2 = n2_.dot(hull_v_j);
857 double max_n3 = n3_.dot(hull_v_k);
865 const auto& antipodal_i =
866 antipodal_points_for_edge[edge_i];
867 const auto& antipodal_j =
868 antipodal_points_for_edge[edge_j];
869 const auto& antipodal_k =
870 antipodal_points_for_edge[edge_k];
874 for (
int v_idx : antipodal_i) {
876 n1_.dot(hull_v[v_idx]));
878 for (
int v_idx : antipodal_j) {
880 n2_.dot(hull_v[v_idx]));
882 for (
int v_idx : antipodal_k) {
884 n3_.dot(hull_v[v_idx]));
889 double extent0 = max_n1 - min_n1;
890 double extent1 = max_n2 - min_n2;
891 double extent2 = max_n3 - min_n3;
892 double volume = extent0 * extent1 * extent2;
896 if (volume < min_volume) {
898 min_obb.R_.col(0) = n1_;
899 min_obb.R_.col(1) = n2_;
900 min_obb.R_.col(2) = n3_;
903 min_obb.extent_(0) = extent0;
904 min_obb.extent_(1) = extent1;
905 min_obb.extent_(2) = extent2;
910 (min_n1 + 0.5 * extent0) * n1_ +
911 (min_n2 + 0.5 * extent1) * n2_ +
912 (min_n3 + 0.5 * extent2) * n3_;
930 std::vector<int> antipodal_edges;
931 antipodal_edges.reserve(128);
932 std::vector<Eigen::Vector3d> antipodal_edge_normals;
933 antipodal_edge_normals.reserve(128);
936 for (
int edge_i : spatial_edge_order) {
938 auto [face_i_a, face_i_b] = faces_for_edge[edge_i];
939 const Eigen::Vector3d& f1a = face_normals[face_i_a];
940 const Eigen::Vector3d& f1b = face_normals[face_i_b];
942 antipodal_edges.clear();
943 antipodal_edge_normals.clear();
946 const auto& antipodals_for_i = antipodal_points_for_edge[edge_i];
947 for (
int antipodal_vertex : antipodals_for_i) {
948 const auto& neighbors = adjacency_data[antipodal_vertex];
949 for (
int v_adj : neighbors) {
950 if (v_adj < antipodal_vertex)
continue;
952 int edgeIndex = antipodal_vertex * num_vertices + v_adj;
953 int edge = vertex_pairs_to_edges[edgeIndex];
955 if (edge_i > edge)
continue;
956 if (isInternalEdge(edge))
continue;
958 auto [faceJ_a, faceJ_b] = faces_for_edge[edge];
959 const Eigen::Vector3d& f2a = face_normals[faceJ_a];
960 const Eigen::Vector3d& f2b = face_normals[faceJ_b];
964 bool areCompatibleOpposingEdges =
false;
965 constexpr
double tooCloseToFaceEpsilon = 1e-4;
969 A.col(1) = f1a - f1b;
970 A.col(2) = f2a - f2b;
971 Eigen::ColPivHouseholderQR<Eigen::Matrix3d> solver(A);
972 Eigen::Vector3d x = solver.solve(-f1b);
977 if (c <= 0.0 || t < 0.0 || t > 1.0) {
978 areCompatibleOpposingEdges =
false;
981 if (t < tooCloseToFaceEpsilon ||
982 t > 1.0 - tooCloseToFaceEpsilon ||
983 u < tooCloseToFaceEpsilon ||
984 u > 1.0 - tooCloseToFaceEpsilon) {
985 areCompatibleOpposingEdges =
false;
987 if (cu < 0.0 || cu > c) {
988 areCompatibleOpposingEdges =
false;
990 n = f1b + (f1a - f1b) * t;
991 areCompatibleOpposingEdges =
true;
996 if (areCompatibleOpposingEdges) {
997 antipodal_edges.push_back(edge);
998 antipodal_edge_normals.push_back(n.normalized());
1003 auto moveSign = [](
double& dst,
double& src) {
1010 const auto& compatible_edges_i = compatible_edges[edge_i];
1011 for (
int edge_j : compatible_edges_i) {
1012 for (
size_t k = 0; k < antipodal_edges.size(); ++k) {
1013 int edgeK = antipodal_edges[k];
1015 const Eigen::Vector3d& n1 = antipodal_edge_normals[k];
1016 double min_n1 = n1.dot(hull_v[edges[edgeK].first]);
1017 double max_n1 = n1.dot(hull_v[edges[edge_i].first]);
1020 auto [faceK_a, faceK_b] = faces_for_edge[edge_j];
1021 const Eigen::Vector3d& f3a = face_normals[faceK_a];
1022 const Eigen::Vector3d& f3b = face_normals[faceK_b];
1024 double num = n1.dot(f3b);
1025 double den = n1.dot(f3b - f3a);
1028 constexpr
double epsilon = 1e-4;
1029 if (den < epsilon) {
1030 num = (
std::abs(num) < 1e-4) ? 0.0 : -1.0;
1034 if (num >= den * -epsilon && num <= den * (1.0 + epsilon)) {
1035 double v = num / den;
1036 Eigen::Vector3d n3 =
1037 (f3b + (f3a - f3b) * v).normalized();
1038 Eigen::Vector3d n2 = n3.cross(n1).normalized();
1040 double max_n2, min_n2;
1042 int hint = extremeVertexConvex(
1043 extremeVertexConvex, n2, flood_fill_visited,
1044 flood_fill_visit_color, max_n2,
1045 (k == 0) ? v_hint1 : v_hint1_b);
1047 v_hint1 = v_hint1_b = hint;
1053 hint = extremeVertexConvex(
1054 extremeVertexConvex, -n2, flood_fill_visited,
1055 flood_fill_visit_color, min_n2,
1056 (k == 0) ? v_hint2 : v_hint2_b);
1058 v_hint2 = v_hint2_b = hint;
1065 double max_n3 = n3.dot(hull_v[edges[edge_j].first]);
1070 const auto& antipodals_edge =
1071 antipodal_points_for_edge[edge_j];
1072 if (antipodals_edge.size() < 20) {
1073 for (
int v_idx : antipodals_edge) {
1075 std::min(min_n3, n3.dot(hull_v[v_idx]));
1081 hint = extremeVertexConvex(
1082 extremeVertexConvex, -n3,
1083 flood_fill_visited, flood_fill_visit_color,
1084 min_n3, (k == 0) ? v_hint3 : v_hint3_b);
1087 v_hint3 = v_hint3_b = hint;
1095 double volume = (max_n1 - min_n1) * (max_n2 - min_n2) *
1097 if (volume < min_volume) {
1098 min_obb.R_.col(0) = n1;
1099 min_obb.R_.col(1) = n2;
1100 min_obb.R_.col(2) = n3;
1101 min_obb.extent_(0) = (max_n1 - min_n1);
1102 min_obb.extent_(1) = (max_n2 - min_n2);
1103 min_obb.extent_(2) = (max_n3 - min_n3);
1104 min_obb.center_ = 0.5 * ((min_n1 + max_n1) * n1 +
1105 (min_n2 + max_n2) * n2 +
1106 (min_n3 + max_n3) * n3);
1107 min_volume = volume;
1121 std::vector<int> antipodal_edges;
1122 antipodal_edges.reserve(128);
1123 std::vector<Eigen::Vector3d> antipodal_edge_normals;
1124 antipodal_edge_normals.reserve(128);
1126 for (
int face_idx : spatial_face_order) {
1127 const Eigen::Vector3d& n1 = face_normals[face_idx];
1135 const auto& tri = hull_t[face_idx];
1137 for (
int j = 0; j < 3; ++j) {
1139 int e = vertex_pairs_to_edges[v0 * num_vertices + v1];
1140 if (!isInternalEdge(e)) {
1147 if (e1 == -1)
continue;
1151 double max_n1 = n1.dot(hull_v[edges[e1].first]);
1153 const auto& antipodals = antipodal_points_for_edge[e1];
1154 if (antipodals.size() < 20) {
1156 for (
int v_idx : antipodals) {
1157 min_n1 =
std::min(min_n1, n1.dot(hull_v[v_idx]));
1161 v_hint4 = extremeVertexConvex(
1162 extremeVertexConvex, -n1, flood_fill_visited,
1163 flood_fill_visit_color, min_n1, v_hint4);
1168 const auto& compatible_edges_i = compatible_edges[e1];
1169 for (
int edge_k : compatible_edges_i) {
1170 auto [face_k_a, face_k_b] = faces_for_edge[edge_k];
1171 const Eigen::Vector3d& f3a = face_normals[face_k_a];
1172 const Eigen::Vector3d& f3b = face_normals[face_k_b];
1175 double num = n1.dot(f3b);
1176 double den = n1.dot(f3b - f3a);
1178 constexpr
double epsilon = 1e-4;
1182 v = (
std::abs(num) < epsilon) ? 0.0 : -1.0;
1185 if (v >= -epsilon && v <= 1.0 + epsilon) {
1186 Eigen::Vector3d n3 = (f3b + (f3a - f3b) * v).normalized();
1187 Eigen::Vector3d n2 = n3.cross(n1).normalized();
1189 double max_n2, min_n2;
1191 v_hint1 = extremeVertexConvex(
1192 extremeVertexConvex, n2, flood_fill_visited,
1193 flood_fill_visit_color, max_n2, v_hint1);
1195 v_hint2 = extremeVertexConvex(
1196 extremeVertexConvex, -n2, flood_fill_visited,
1197 flood_fill_visit_color, min_n2, v_hint2);
1200 double max_n3 = n3.dot(hull_v[edges[edge_k].first]);
1205 const auto& antipodals_edge =
1206 antipodal_points_for_edge[edge_k];
1207 if (antipodals_edge.size() < 20) {
1208 for (
int v_idx : antipodals_edge) {
1209 min_n3 =
std::min(min_n3, n3.dot(hull_v[v_idx]));
1213 v_hint3 = extremeVertexConvex(
1214 extremeVertexConvex, -n3, flood_fill_visited,
1215 flood_fill_visit_color, min_n3, v_hint3);
1219 double volume = (max_n1 - min_n1) * (max_n2 - min_n2) *
1221 if (volume < min_volume) {
1222 min_obb.R_.col(0) = n1;
1223 min_obb.R_.col(1) = n2;
1224 min_obb.R_.col(2) = n3;
1225 min_obb.extent_(0) = (max_n1 - min_n1);
1226 min_obb.extent_(1) = (max_n2 - min_n2);
1227 min_obb.extent_(2) = (max_n3 - min_n3);
1228 min_obb.center_ = 0.5 * ((min_n1 + max_n1) * n1 +
1229 (min_n2 + max_n2) * n2 +
1230 (min_n3 + max_n3) * n3);
1231 assert(volume > 0.0);
1232 min_volume = volume;
1240 if (min_obb.R_.col(0).cross(min_obb.R_.col(1)).dot(min_obb.R_.col(2)) <
1242 min_obb.R_.col(2) = -min_obb.R_.col(2);
1244 mapOBBToClosestIdentity(min_obb);
1251 if (
points.GetShape(0) == 0) {
1255 if (
points.GetShape(0) < 4) {
1263 if (hull_mesh.GetVertexPositions().NumElements() == 0) {
1269 const std::vector<Eigen::Vector3d>& hull_v =
1271 hull_mesh.GetVertexPositions());
1272 const std::vector<Eigen::Vector3i>& hull_t =
1274 hull_mesh.GetTriangleIndices());
1280 double min_vol = min_box.
Volume();
1282 PointCloud hull_pcd(hull_mesh.GetVertexPositions().Clone());
1283 for (
auto& tri : hull_t) {
1285 Eigen::Vector3d a = hull_v[tri(0)];
1286 Eigen::Vector3d b = hull_v[tri(1)];
1287 Eigen::Vector3d c = hull_v[tri(2)];
1288 Eigen::Vector3d u = b - a;
1289 Eigen::Vector3d v = c - a;
1290 Eigen::Vector3d w = u.cross(v);
1295 Eigen::Matrix3d m_rot;
1296 m_rot << u[0], v[0], w[0], u[1], v[1], w[1], u[2], v[2], w[2];
1301 hull_pcd.
Rotate(rot_inv, center);
1304 double volume = aabox.
Volume();
1305 if (volume < min_vol) {
1307 min_box = aabox.GetOrientedBoundingBox();
1309 min_box.
Rotate(rot, center);
1312 auto device =
points.GetDevice();
1313 auto dtype =
points.GetDtype();
#define AssertTensorShape(tensor,...)
void CopyFrom(const Tensor &other)
Copy Tensor values to current tensor from the source tensor.
Device GetDevice() const override
Tensor Reshape(const SizeVector &dst_shape) const
SizeVector GetShape() const
Tensor To(Dtype dtype, bool copy=false) const
static AxisAlignedBoundingBox CreateFromPoints(const core::Tensor &points)
OrientedBoundingBox GetOrientedBoundingBox() const
Convert to an oriented box.
double Volume() const
Returns the volume of the bounding box.
A bounding box oriented along an arbitrary frame of reference.
OrientedBoundingBox & Rotate(const core::Tensor &rotation, const utility::optional< core::Tensor > ¢er=utility::nullopt)
Rotate the oriented box by the given rotation matrix. If the rotation matrix is not orthogonal,...
void SetExtent(const core::Tensor &extent)
Set the extent of the box. If the data type of the given tensor differs from the data type of the box...
core::Tensor GetExtent() const
void SetCenter(const core::Tensor ¢er)
Set the center of the box. If the data type of the given tensor differs from the data type of the box...
void SetRotation(const core::Tensor &rotation)
Set the rotation matrix of the box. If the data type of the given tensor differs from the data type o...
double Volume() const
Returns the volume of the bounding box.
core::Tensor GetCenter() const
core::Tensor GetRotation() const
A point cloud contains a list of 3D points.
PointCloud & Rotate(const core::Tensor &R, const core::Tensor ¢er)
Rotates the PointPositions and PointNormals (if exists).
TriangleMesh ComputeConvexHull(bool joggle_inputs=false) const
core::Tensor & GetPointPositions()
Get the value of the "positions" attribute. Convenience function.
AxisAlignedBoundingBox GetAxisAlignedBoundingBox() const
Create an axis-aligned bounding box from attribute "positions".
__device__ __forceinline__ float infinity()
__host__ __device__ float3 cross(float3 a, float3 b)
__host__ __device__ float dot(float2 a, float2 b)
__host__ __device__ int2 abs(int2 v)
core::Tensor EigenMatrixToTensor(const Eigen::MatrixBase< Derived > &matrix)
Converts a eigen matrix of shape (M, N) with alignment A and type T to a tensor.
std::vector< Eigen::Vector3d > TensorToEigenVector3dVector(const core::Tensor &tensor)
Converts a tensor of shape (N, 3) to std::vector<Eigen::Vector3d>. An exception will be thrown if the...
std::vector< Eigen::Vector3i > TensorToEigenVector3iVector(const core::Tensor &tensor)
Converts a tensor of shape (N, 3) to std::vector<Eigen::Vector3i>. An exception will be thrown if the...
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > TensorToEigenMatrixXd(const core::Tensor &tensor)
Converts a 2D tensor to Eigen::MatrixXd of same shape. Regardless of the tensor dtype,...
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
::ecvOrientedBBox OrientedBoundingBox
void To(const core::Tensor &src, core::Tensor &dst, double scale, double offset)
OrientedBoundingBox ComputeMinimumOBBApprox(const core::Tensor &points, bool robust)
OrientedBoundingBox ComputeMinimumOBBJylanki(const core::Tensor &points_, bool robust)
constexpr nullopt_t nullopt
Generic file read and write utility for python interface.
Eigen::Matrix< Index, 3, 1 > Vector3i
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.