Lagrange
chain_edges.h
1/*
2 * Copyright 2018 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 <list>
15#include <unordered_set>
16#include <utility>
17#include <vector>
18
19#include <lagrange/Edge.h>
20#include <lagrange/common.h>
21#include <lagrange/legacy/inline.h>
22#include <lagrange/utils/range.h>
23#include <lagrange/utils/warning.h>
24
25namespace lagrange {
26LAGRANGE_LEGACY_INLINE
27namespace legacy {
44template <typename Index, typename Allocator = std::list<EdgeType<Index>>>
45std::vector<std::list<Index>> chain_edges(const Allocator& edges, bool close_loop = false)
46{
47 using EdgeIterator = typename Allocator::const_iterator;
48 using VertexEdgeAdjList = std::unordered_map<Index, std::vector<EdgeIterator>>;
49
50 using Chain = std::list<Index>;
51 std::vector<Chain> chains;
52 VertexEdgeAdjList next;
53 VertexEdgeAdjList prev;
54 size_t num_edges = std::distance(edges.begin(), edges.end());
55 next.reserve(num_edges);
56 prev.reserve(num_edges);
57 std::vector<bool> visited(num_edges, false);
58
59 auto insert_or_append = [&](VertexEdgeAdjList& adj_list, Index key, EdgeIterator value) {
60 auto itr = adj_list.find(key);
61 if (itr == adj_list.end()) {
62 adj_list.insert({key, {std::move(value)}});
63 } else {
64 itr->second.push_back(std::move(value));
65 }
66 };
67
68 for (auto itr = edges.begin(); itr != edges.end(); ++itr) {
69 const auto& e = *itr;
70 insert_or_append(next, e[0], itr);
71 insert_or_append(prev, e[1], itr);
72 }
73
78 auto grow_chain_forward = [&](Chain& chain) -> void {
79 // Extend chain from the end.
80 assert(chain.size() > 0);
81 while (true) {
82 const auto curr_v = chain.back();
83 const auto itr = next.find(curr_v);
84 if (itr != next.end()) {
85 const auto& next_edges = itr->second;
86 if (next_edges.size() != 1) break;
87
88 auto e_itr = next_edges.front();
89 auto eid = std::distance(edges.begin(), e_itr);
90 if (visited[eid]) break;
91
92 const auto& e = *e_itr;
93 la_debug_assert(e[0] == curr_v);
94 chain.push_back(e[1]);
95 visited[eid] = true;
96 } else {
97 break;
98 }
99 }
100 };
101
106 auto grow_chain_backward = [&](Chain& chain) -> void {
107 // Extend chain from the beginning.
108 assert(chain.size() > 0);
109 while (true) {
110 const auto curr_v = chain.front();
111 const auto itr = prev.find(curr_v);
112 if (itr != prev.end()) {
113 const auto& prev_edges = itr->second;
114 if (prev_edges.size() != 1) break;
115
116 auto e_itr = prev_edges.front();
117 auto eid = std::distance(edges.begin(), e_itr);
118 if (visited[eid]) break;
119
120 const auto& e = *e_itr;
121 la_debug_assert(e[1] == curr_v);
122 chain.push_front(e[0]);
123 visited[eid] = true;
124 } else {
125 break;
126 }
127 }
128 };
129
130 size_t eid = 0;
131 for (auto itr = edges.begin(); itr != edges.end(); ++itr, eid++) {
132 if (visited[eid]) continue;
133
134 const auto& e = *itr;
135 Chain chain;
136 chain.push_back(e[0]);
137 chain.push_back(e[1]);
138 visited[eid] = true;
139
140 grow_chain_forward(chain);
141 grow_chain_backward(chain);
142
143 if (!close_loop && chain.back() == chain.front()) {
144 chain.pop_back();
145 }
146 chains.push_back(std::move(chain));
147 }
148 return chains;
149}
150
151namespace _detail {
152
153// Eigen integer matrix with 2 columns, one edge per row
154template <typename EdgeArray>
155size_t get_num_edges(const EdgeArray& edges)
156{
157 return static_cast<size_t>(edges.rows());
158}
159
160// std::vector of edges
161template <typename EdgeType>
162size_t get_num_edges(const std::vector<EdgeType>& edges)
163{
164 return edges.size();
165}
166
167// Eigen integer matrix with 2 columns, one edge per row
168template <typename VertexIndex, typename EdgeArray, typename EdgeIndex>
169std::pair<VertexIndex, VertexIndex> get_edge_endpoints(EdgeArray& edges, EdgeIndex edge_index)
170{
171 return std::make_pair(
172 static_cast<VertexIndex>(edges(static_cast<Eigen::Index>(edge_index), 0)),
173 static_cast<VertexIndex>(edges(static_cast<Eigen::Index>(edge_index), 1)));
174}
175
176// std::vector of edges
177template <typename VertexIndex, typename EdgeType, typename EdgeIndex>
178std::pair<VertexIndex, VertexIndex> get_edge_endpoints(
179 const std::vector<EdgeType>& edges,
180 EdgeIndex edge_index)
181{
182 const auto& e = edges[static_cast<size_t>(edge_index)];
183 return std::make_pair(static_cast<VertexIndex>(e[0]), static_cast<VertexIndex>(e[1]));
184}
185
186} // namespace _detail
187
201template <typename Index, typename Allocator = std::vector<EdgeType<Index>>>
202std::vector<std::vector<Index>> chain_undirected_edges(
203 const Allocator& edges,
204 bool close_loop = false)
205{
206 using VertexEdgeAdjList = std::unordered_map<Index, std::vector<Index>>;
207
208 using Chain = std::vector<Index>;
209 std::vector<Chain> chains;
210 VertexEdgeAdjList adj_list;
211 size_t num_edges = _detail::get_num_edges(edges);
212 adj_list.reserve(num_edges);
213 std::vector<bool> visited(num_edges, false);
214
215 auto add_adj_connection = [&](Index ei) {
216 auto v = _detail::get_edge_endpoints<Index>(edges, ei);
217
218 auto itr = adj_list.find(v.first);
219 if (itr != adj_list.end()) {
220 itr->second.push_back(ei);
221 } else {
222 adj_list.insert({v.first, {ei}});
223 }
224
225 itr = adj_list.find(v.second);
226 if (itr != adj_list.end()) {
227 itr->second.push_back(ei);
228 } else {
229 adj_list.insert({v.second, {ei}});
230 }
231 };
232
234 for (auto ei : range(num_edges)) {
235 add_adj_connection(ei);
236 }
237 LA_IGNORE_RANGE_LOOP_ANALYSIS_END
238
239 auto grow_chain_forward = [&](Chain& chain) {
240 Index curr_v = chain.back();
241
242 while (true) {
243 const auto& adj_edges = adj_list[curr_v];
244 if (adj_edges.size() != 2) break;
245
246 bool updated = false;
247 for (auto ei : adj_edges) {
248 if (visited[ei]) continue;
249
250 auto v = _detail::get_edge_endpoints<Index>(edges, ei);
251 if (v.first == curr_v) {
252 chain.push_back(v.second);
253 } else {
254 la_debug_assert(v.second == curr_v);
255 chain.push_back(v.first);
256 }
257 curr_v = chain.back();
258 visited[ei] = true;
259 updated = true;
260 }
261 if (!updated) break;
262 }
263 };
264
265 auto grow_chain_backward = [&](Chain& chain) {
266 std::reverse(chain.begin(), chain.end());
267 grow_chain_forward(chain);
268 std::reverse(chain.begin(), chain.end());
269 };
270
271 for (auto ei : range(num_edges)) {
272 if (visited[ei]) continue;
273
274 auto v = _detail::get_edge_endpoints<Index>(edges, ei);
275 visited[ei] = true;
276 Chain chain;
277 chain.reserve(num_edges);
278 chain.push_back(v.first);
279 chain.push_back(v.second);
280 grow_chain_forward(chain);
281 grow_chain_backward(chain);
282
283 if (!close_loop && chain.front() == chain.back()) {
284 chain.pop_back();
285 }
286 chains.push_back(std::move(chain));
287 }
288
289 return chains;
290}
291
292} // namespace legacy
293} // namespace lagrange
@ VertexIndex
Single channel integer attribute indexing a mesh vertex.
#define la_debug_assert(...)
Debug assertion check.
Definition: assert.h:189
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition: range.h:176
#define LA_IGNORE_RANGE_LOOP_ANALYSIS_BEGIN
Ignore range loop variable may be a copy.
Definition: warning.h:148
ChainEdgesResult< Index > chain_undirected_edges(const span< const Index > edges, const ChainEdgesOptions &options={})
Chain a set of undirected edges into loops and chains.
Definition: chain_edges.cpp:248
Main namespace for Lagrange.
Definition: AABBIGL.h:30