Lagrange
split_triangle.h
1/*
2 * Copyright 2016 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#include <iostream>
15#include <limits>
16#include <queue>
17#include <vector>
18
19#include <lagrange/common.h>
20#include <lagrange/legacy/inline.h>
21#include <lagrange/utils/safe_cast.h>
22
23namespace lagrange {
24LAGRANGE_LEGACY_INLINE
25namespace legacy {
42template <typename VertexArray, typename Index>
43std::vector<Eigen::Matrix<Index, 3, 1>> split_triangle(
44 const VertexArray& vertices,
45 const std::vector<Index>& chain,
46 const Index v0,
47 const Index v1,
48 const Index v2)
49{
50 const Index chain_size = safe_cast<Index>(chain.size());
51 auto next = [&chain_size](Index i) { return (i + 1) % chain_size; };
52 auto prev = [&chain_size](Index i) { return (i + chain_size - 1) % chain_size; };
53 auto sq_length = [&vertices, &chain](Index vi, Index vj) {
54 return (vertices.row(chain[vi]) - vertices.row(chain[vj])).squaredNorm();
55 };
56 auto is_corner = [v0, v1, v2](Index v) { return v == v0 || v == v1 || v == v2; };
57 std::vector<Eigen::Matrix<Index, 3, 1>> facets;
58
59 const double NOT_USED = std::numeric_limits<double>::max();
60 Eigen::Matrix<Index, 3, 6, Eigen::RowMajor> candidates;
61 candidates << v0, next(v0), prev(v0), 0, 0, 0, v1, next(v1), prev(v1), 0, 0, 0, v2, next(v2),
62 prev(v2), 0, 0, 0;
63
64 Eigen::Matrix<double, 3, 2, Eigen::RowMajor> candidate_lengths;
65 candidate_lengths << sq_length(candidates(0, 1), candidates(0, 2)), NOT_USED,
66 sq_length(candidates(1, 1), candidates(1, 2)), NOT_USED,
67 sq_length(candidates(2, 1), candidates(2, 2)), NOT_USED;
68
69 auto index_comp = [&candidate_lengths](Index i, Index j) {
70 // Use greater than operator so the heap is min heap.
71 return candidate_lengths.row(i).minCoeff() > candidate_lengths.row(j).minCoeff();
72 };
73 std::priority_queue<Index, std::vector<Index>, decltype(index_comp)> Q(index_comp);
74 Q.push(0);
75 Q.push(1);
76 Q.push(2);
77
78 Eigen::Matrix<Index, Eigen::Dynamic, 3, Eigen::RowMajor> visited(chain_size, 3);
79 visited.setZero();
80 visited(v0, 0) = 1;
81 visited(v1, 1) = 1;
82 visited(v2, 2) = 1;
83
84 while (!Q.empty()) {
85 Index idx = Q.top();
86 Q.pop();
87 Index selection = 0;
88 if (candidate_lengths(idx, 0) != NOT_USED &&
89 candidate_lengths(idx, 0) <= candidate_lengths(idx, 1)) {
90 selection = 0;
91 } else if (
92 candidate_lengths(idx, 1) != NOT_USED &&
93 candidate_lengths(idx, 1) <= candidate_lengths(idx, 0)) {
94 selection = 1;
95 } else {
96 // Select neither candidate from this corner.
97 continue;
98 }
99 la_runtime_assert(candidate_lengths.row(idx).minCoeff() <= candidate_lengths.minCoeff());
100
101 Index base_v = candidates(idx, selection * 3);
102 Index right_v = candidates(idx, selection * 3 + 1);
103 Index left_v = candidates(idx, selection * 3 + 2);
104 la_runtime_assert(base_v >= 0 && base_v < chain_size);
105 la_runtime_assert(right_v >= 0 && right_v < chain_size);
106 la_runtime_assert(left_v >= 0 && left_v < chain_size);
107 la_runtime_assert(visited(base_v, idx) >= 1);
108
109 // A special case.
110 if (is_corner(base_v) && is_corner(right_v) && is_corner(left_v)) {
111 if (chain_size != 3) {
112 candidate_lengths(idx, selection) = NOT_USED;
113 Q.push(idx);
114 continue;
115 }
116 }
117
118 if (visited.row(base_v).sum() > 1 || visited(right_v, idx) > 1 ||
119 visited(left_v, idx) > 1) {
120 // This canadidate is invalid.
121 candidate_lengths(idx, selection) = NOT_USED;
122 Q.push(idx);
123 continue;
124 }
125
126 visited(base_v, idx) = 2;
127 visited(right_v, idx) = 1;
128 visited(left_v, idx) = 1;
129 facets.push_back({chain[base_v], chain[right_v], chain[left_v]});
130
131 // Update candidate from this corner.
132 if (visited.row(right_v).sum() == 1) {
133 Index right_to_right = next(right_v);
134 candidate_lengths(idx, 0) = sq_length(left_v, right_to_right);
135 candidates(idx, 0) = right_v;
136 candidates(idx, 1) = right_to_right;
137 candidates(idx, 2) = left_v;
138 } else {
139 candidate_lengths(idx, 0) = NOT_USED;
140 }
141
142 if (visited.row(left_v).sum() == 1) {
143 Index left_to_left = prev(left_v);
144 candidate_lengths(idx, 1) = sq_length(right_v, left_to_left);
145 candidates(idx, 3) = left_v;
146 candidates(idx, 4) = right_v;
147 candidates(idx, 5) = left_to_left;
148 } else {
149 candidate_lengths(idx, 1) = NOT_USED;
150 }
151 Q.push(idx);
152 }
153
154 Eigen::Matrix<Eigen::Index, Eigen::Dynamic, 1> visited_sum =
155 (visited.array() > 0).rowwise().count();
156 if ((visited_sum.array() > 1).count() == 3) {
157 Index f[3];
158 Index count = 0;
159 for (Index i = 0; i < chain_size; i++) {
160 if (visited_sum[i] > 1) {
161 la_runtime_assert(count < 3);
162 f[count] = chain[i];
163 count++;
164 }
165 }
166 la_runtime_assert(count == 3);
167 facets.push_back({f[0], f[1], f[2]});
168 }
169 return facets;
170}
171} // namespace legacy
172} // namespace lagrange
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
Main namespace for Lagrange.
Definition: AABBIGL.h:30
void split_triangle(size_t num_points, span< const Scalar > points, span< const Index > chain, span< Index > visited_buffer, std::vector< Index > &queue_buffer, const Index v0, const Index v1, const Index v2, span< Index > triangulation)
Split a triangle into smaller triangles based on the chain of splitting points on the edges.
Definition: split_triangle.cpp:27