Lagrange
Loading...
Searching...
No Matches
Edge.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 <array>
15#include <exception>
16#include <functional>
17#include <memory>
18#include <unordered_map>
19#include <unordered_set>
20
21#include <lagrange/common.h>
22#include <lagrange/utils/assert.h>
23#include <lagrange/utils/safe_cast.h>
24#include <lagrange/utils/warning.h>
25
26namespace lagrange {
27
28template <typename Index>
29class EdgeType
30{
31private:
32 Index m_v1;
33 Index m_v2;
34
35public:
36 EdgeType(Index v1, Index v2)
37 : m_v1(v1)
38 , m_v2(v2)
39 {}
40 EdgeType(const EdgeType<Index>& obj)
41 : m_v1(obj.v1())
42 , m_v2(obj.v2())
43 {}
44 EdgeType(EdgeType<Index>&& obj) noexcept
45 : m_v1(obj.v1())
46 , m_v2(obj.v2())
47 {}
48
49 // Invalid() can be useful for EdgeType temporary variables
50 static EdgeType Invalid() { return EdgeType(invalid<Index>(), invalid<Index>()); }
51
52 // Alternate constructor for backward compatibility,
53 // allowing `EdgeType e = {0, 1}`
54 EdgeType(const std::array<Index, 2>& arr)
55 : m_v1(arr[0])
56 , m_v2(arr[1])
57 {}
58
59 inline Index v1() const { return m_v1; }
60 inline Index v2() const { return m_v2; }
61 inline bool is_valid() const { return m_v1 != invalid<Index>() && m_v2 != invalid<Index>(); }
62
63 EdgeType<Index>& operator=(const EdgeType<Index>& other)
64 {
65 this->m_v1 = other.m_v1;
66 this->m_v2 = other.m_v2;
67 return *this;
68 }
69 EdgeType<Index>& operator=(EdgeType<Index>&& other) noexcept
70 {
71 this->m_v1 = other.m_v1;
72 this->m_v2 = other.m_v2;
73 return *this;
74 }
75
76 ~EdgeType() noexcept = default;
77
78 bool operator==(const EdgeType<Index>& rhs) const
79 {
80 return (m_v1 == rhs.m_v1 && m_v2 == rhs.m_v2) || (m_v1 == rhs.m_v2 && m_v2 == rhs.m_v1);
81 }
82 bool operator!=(const EdgeType<Index>& rhs) const { return !(*this == rhs); }
83
84 Index operator[](const Index i) const
85 {
86 if (i == 0) return m_v1;
87 if (i == 1) return m_v2;
88 la_runtime_assert(false, "Bad Index: " + std::to_string(i));
89 return m_v1;
90 }
91
92 // Note: strict order operator< is NOT implemented, because:
93 // - order of edges is not well defined
94 // - by not implementing it, defining a std::set<EdgeType> will
95 // result in a compile error, and you should *not* use
96 // std::set<EdgeType>. Use std::unordered_set<EdgeType> instead.
97
98 // allows: for (Index v : edge) { ... }
99 class iterator
100 {
101 public:
102 // Types needed to make a compatible iterator.
103 using iterator_category = std::input_iterator_tag;
104 using value_type = EdgeType<Index>;
105 using difference_type = std::ptrdiff_t;
106 using pointer = EdgeType<Index>*;
107 using reference = EdgeType<Index>&;
108
109 private:
110 int m_i;
111 const EdgeType<Index>& m_edge;
112
113 public:
114 iterator(const EdgeType<Index>& edge, int i)
115 : m_i(i)
116 , m_edge(edge)
117 {}
118 bool operator!=(const iterator& rhs) const
119 {
120 return m_edge != rhs.m_edge || m_i != rhs.m_i;
121 }
122 iterator& operator++()
123 {
124 ++m_i;
125 return *this;
126 }
127 Index operator*() const { return m_edge[m_i]; }
128 };
129 iterator begin() const { return iterator(*this, 0); }
130 iterator end() const { return iterator(*this, 2); }
131
132
133 bool has_shared_vertex(const EdgeType<Index>& other) const
134 {
135 return m_v1 == other.m_v1 || m_v1 == other.m_v2 || m_v2 == other.m_v1 || m_v2 == other.m_v2;
136 }
137 Index get_shared_vertex(const EdgeType<Index>& other) const
138 {
139 la_runtime_assert(*this != other, "get_shared_vertex() failed due to identical edges");
140 if (m_v1 == other.m_v1) return m_v1;
141 if (m_v1 == other.m_v2) return m_v1;
142 if (m_v2 == other.m_v1) return m_v2;
143 if (m_v2 == other.m_v2) return m_v2;
144 return invalid<Index>();
145 }
146 Index get_other_vertex(Index v) const
147 {
148 la_runtime_assert(m_v1 == v || m_v2 == v);
149 if (m_v1 == v) return m_v2;
150 return m_v1;
151 }
152};
153
154template <typename Index, typename T>
155using EdgeMap = std::unordered_map<EdgeType<Index>, T>;
156
157template <typename Index>
158using EdgeSet = std::unordered_set<EdgeType<Index>>;
159
160template <typename MeshType>
161using EdgeFacetMap =
162 std::unordered_map<EdgeType<typename MeshType::Index>, std::vector<typename MeshType::Index>>;
163
164// Computes a mapping from each edge to its adjacent facets.
165// Only considers the sub-mesh defined by 'active_facets'
166template <typename MeshType>
167EdgeFacetMap<MeshType> compute_edge_facet_map_in_active_facets(
168 const MeshType& mesh,
169 const std::unordered_set<typename MeshType::Index>& active_facets)
170{
171 using Index = typename MeshType::Index;
172 std::unordered_map<EdgeType<Index>, std::vector<Index>> edge_facet_map;
173 const Index num_facets = mesh.get_num_facets();
174 const Index vertex_per_facet = mesh.get_vertex_per_facet();
175 const typename MeshType::FacetArray& facets = mesh.get_facets();
176 edge_facet_map.reserve(active_facets.size() * vertex_per_facet);
177 for (Index i = 0; i < num_facets; ++i) {
178 if (active_facets.find(i) == active_facets.end()) continue;
179 for (Index j = 0; j < vertex_per_facet; ++j) {
180 const Index v1 = facets(i, j);
181 const Index v2 = facets(i, (j + 1) % vertex_per_facet);
182 const EdgeType<Index> edge(v1, v2);
183 auto it = edge_facet_map.find(edge);
184 if (it == edge_facet_map.end()) {
185 // GCC 13.1-13.3 -Warray-bounds false positive with -fsanitize=undefined
186 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109727
188 edge_facet_map[edge] = {i};
189 LA_IGNORE_ARRAY_BOUNDS_END
190 } else {
191 it->second.push_back(i);
192 }
193 }
194 }
195 return edge_facet_map;
196}
197
198// Computes a mapping from each edge to its adjacent facets.
199// Only considers the sub-mesh defined by 'active_vertices'
200template <typename MeshType>
201EdgeFacetMap<MeshType> compute_edge_facet_map_in_active_vertices(
202 const MeshType& mesh,
203 const std::unordered_set<typename MeshType::Index>& active_vertices)
204{
205 using Index = typename MeshType::Index;
206
207 std::unordered_set<Index> active_facets;
208
209 if (mesh.is_connectivity_initialized()) {
210 // this path is faster but requires connectivity initialized
211 for (Index v : active_vertices) {
212 for (Index f : mesh.get_facets_adjacent_to_vertex(v)) {
213 active_facets.insert(f);
214 }
215 }
216 } else {
217 const Index num_facets = mesh.get_num_facets();
218 const Index vertex_per_facet = mesh.get_vertex_per_facet();
219 const typename MeshType::FacetArray& facets = mesh.get_facets();
220 for (Index i = 0; i < num_facets; ++i) {
221 if (active_facets.find(i) != active_facets.end()) continue; // already there
222 for (Index j = 0; j < vertex_per_facet; ++j) {
223 if (active_vertices.find(facets(i, j)) != active_vertices.end()) {
224 active_facets.insert(i);
225 break;
226 }
227 }
228 }
229 }
230 return compute_edge_facet_map_in_active_facets(mesh, active_facets);
231}
232} // namespace lagrange
233
234namespace std {
235template <typename Index>
236struct hash<lagrange::EdgeType<Index>>
237{
238 std::size_t operator()(const lagrange::EdgeType<Index>& key) const
239 {
240 return lagrange::safe_cast<std::size_t>(key.v1() + key.v2());
241 }
242};
243} // namespace std
Definition Edge.h:100
Definition Edge.h:30
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:175
constexpr T invalid()
You can use invalid<T>() to get a value that can represent "invalid" values, such as invalid indices ...
Definition invalid.h:40
constexpr auto safe_cast(SourceType value) -> std::enable_if_t<!std::is_same< SourceType, TargetType >::value, TargetType >
Perform safe cast from SourceType to TargetType, where "safe" means:
Definition safe_cast.h:51
#define LA_IGNORE_ARRAY_BOUNDS_BEGIN
Ignore warning "out of bounds subscripts or offsets into arrays" This is used to bypass the following...
Definition warning.h:161
Main namespace for Lagrange.