Lagrange
All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Modules Pages
check_mesh.h
1/*
2 * Copyright 2023 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
13#pragma once
14
15#include <lagrange/SurfaceMesh.h>
16#include <lagrange/foreach_attribute.h>
17#include <lagrange/utils/invalid.h>
18#include <lagrange/utils/safe_cast.h>
19
20// clang-format off
21#include <lagrange/utils/warnoff.h>
22#include <catch2/catch_test_macros.hpp>
23#include <catch2/matchers/catch_matchers_contains.hpp>
24#include <lagrange/utils/warnon.h>
25// clang-format on
26
27#include <algorithm>
28#include <array>
29#include <set>
30#include <type_traits>
31#include <unordered_set>
32#include <vector>
33
34namespace lagrange::testing {
35
36// Check whether the function f : X --> Y restricted to the elements that maps onto Y, is
37// surjective. Index.e. each element of [first, last) has an antecedent through f.
38template <typename Index>
39bool is_restriction_surjective(lagrange::span<const Index> func, Index first, Index last)
40{
41 using namespace lagrange;
42 std::vector<Index> inv(last - first, invalid<Index>());
43 for (Index x = 0; x < Index(func.size()); ++x) {
44 const Index y = func[x];
45 if (y >= first && y < last) {
46 inv[y - first] = x;
47 }
48 }
49 // No element in the range [first, last) has no predecessor through the function
50 return std::none_of(inv.begin(), inv.end(), [&](Index y) { return y == invalid<Index>(); });
51}
52
53// Check whether the function f : X --> Y restricted to the elements that maps onto Y, is injective.
54// Index.e. any two elements that maps onto [first, last) map to different values in [first, last).
55template <typename Index>
56bool is_restriction_injective(lagrange::span<const Index> func, Index first, Index last)
57{
58 using namespace lagrange;
59 std::vector<Index> inv(last - first, invalid<Index>());
60 for (Index x = 0; x < Index(func.size()); ++x) {
61 const Index y = func[x];
62 if (y >= first && y < last) {
63 // Just found another x' that maps to the same value y
64 if (inv[y - first] != invalid<Index>() && inv[y - first] != x) {
65 return false;
66 }
67 inv[y - first] = x;
68 }
69 }
70 return true;
71}
72
73// Check whether elements map within [first, last)
74template <typename Index>
75bool is_in_range(lagrange::span<const Index> func, Index first, Index last)
76{
77 return std::all_of(func.begin(), func.end(), [&](Index y) { return first <= y && y < last; });
78}
79
80template <typename Index>
81bool is_in_range_or_invalid(lagrange::span<const Index> func, Index first, Index last)
82{
83 return std::all_of(func.begin(), func.end(), [&](Index y) {
84 return (first <= y && y < last) || y == lagrange::invalid<Index>();
85 });
86}
87
88template <typename Index>
89bool is_surjective(lagrange::span<const Index> func, Index first, Index last)
90{
91 return is_in_range(func, first, last) && is_restriction_surjective(func, first, last);
92}
93
94template <typename Index>
95bool is_injective(lagrange::span<const Index> func, Index first, Index last)
96{
97 return is_in_range(func, first, last) && is_restriction_injective(func, first, last);
98}
99
100template <typename MeshType>
101void check_mesh(const MeshType& mesh)
102{
103 using namespace lagrange;
104 using Index = typename MeshType::Index;
105 const Index nv = mesh.get_num_vertices();
106 const Index nf = mesh.get_num_facets();
107 const Index nc = mesh.get_num_corners();
108 const Index ne = mesh.get_num_edges();
109
110 // Ensure that (V, F) is well-formed
111 for (Index f = 0; f < nf; ++f) {
112 const Index c0 = mesh.get_facet_corner_begin(f);
113 const Index c1 = mesh.get_facet_corner_end(f);
114 REQUIRE(c0 < c1);
115 for (Index c = c0; c < c1; ++c) {
116 const Index v = mesh.get_corner_vertex(c);
117 REQUIRE(mesh.get_corner_facet(c) == f);
118 REQUIRE((v >= 0 && v < nv));
119 }
120 }
121
122 // Ensure that each attribute has the correct number of elements
123 seq_foreach_attribute_read<AttributeElement::Vertex>(mesh, [&](auto&& attr) {
124 REQUIRE(attr.get_num_elements() == nv);
125 });
126 seq_foreach_attribute_read<AttributeElement::Facet>(mesh, [&](auto&& attr) {
127 REQUIRE(attr.get_num_elements() == nf);
128 });
129 seq_foreach_attribute_read<AttributeElement::Corner>(mesh, [&](auto&& attr) {
130 REQUIRE(attr.get_num_elements() == nc);
131 });
132
133 // Ensure that each element index is in range (or an invalid index)
134 seq_foreach_attribute_read(mesh, [&](auto&& attr) {
135 using AttributeType = std::decay_t<decltype(attr)>;
136 using ValueType = typename AttributeType::ValueType;
137 Index n = 0;
138 auto usage = attr.get_usage();
139 if (usage == AttributeUsage::VertexIndex) {
140 n = nv;
141 } else if (usage == AttributeUsage::FacetIndex) {
142 n = nf;
143 } else if (usage == AttributeUsage::CornerIndex) {
144 n = nc;
145 } else if (usage == AttributeUsage::EdgeIndex) {
146 n = ne;
147 } else {
148 return;
149 }
150 REQUIRE(std::is_same_v<ValueType, Index>);
151 if constexpr (std::is_same_v<ValueType, Index>) {
152 if constexpr (AttributeType::IsIndexed) {
153 REQUIRE(is_in_range_or_invalid<ValueType>(attr.values().get_all(), 0, n));
154 } else {
155 REQUIRE(is_in_range_or_invalid<ValueType>(attr.get_all(), 0, n));
156 }
157 } else {
158 LA_IGNORE(n);
159 }
160 });
161
162 // Ensure that only hybrid meshes have c <--> f attributes
163 if (mesh.is_hybrid()) {
166 } else {
167 REQUIRE(mesh.is_regular());
170 }
171
172 // Ensure that edge and connectivity information is well-formed
173 if (mesh.has_edges()) {
174 // Check that all facet edges have a single corresponding edge in the global indexing
175 auto c2e = mesh.template get_attribute<Index>(mesh.attr_id_corner_to_edge()).get_all();
176 auto e2c =
177 mesh.template get_attribute<Index>(mesh.attr_id_edge_to_first_corner()).get_all();
178 auto v2c =
179 mesh.template get_attribute<Index>(mesh.attr_id_vertex_to_first_corner()).get_all();
180 auto next_around_edge =
181 mesh.template get_attribute<Index>(mesh.attr_id_next_corner_around_edge()).get_all();
182 auto next_around_vertex =
183 mesh.template get_attribute<Index>(mesh.attr_id_next_corner_around_vertex()).get_all();
184 REQUIRE(is_surjective<Index>(c2e, 0, ne));
185 REQUIRE(is_injective<Index>(e2c, 0, nc));
186 REQUIRE(is_in_range_or_invalid<Index>(v2c, 0, nc));
187 REQUIRE(is_restriction_injective<Index>(
188 v2c,
189 0,
190 nc)); // may have isolated vertices that map to invalid<>()
191
192 // Make sure that e2c contains the name number of edges as the mesh
193 std::set<std::pair<Index, Index>> edges;
194 for (Index f = 0; f < nf; ++f) {
195 for (Index lv0 = 0, s = mesh.get_facet_size(f); lv0 < s; ++lv0) {
196 const Index lv1 = (lv0 + 1) % s;
197 const Index v0 = mesh.get_facet_vertex(f, lv0);
198 const Index v1 = mesh.get_facet_vertex(f, lv1);
199 edges.emplace(std::min(v0, v1), std::max(v0, v1));
200 }
201 }
202 std::vector<std::array<Index, 2>> mesh_edges;
203 for (Index e = 0; e < ne; ++e) {
204 auto v = mesh.get_edge_vertices(e);
205 mesh_edges.push_back({v[0], v[1]});
206 }
207 REQUIRE(edges.size() == e2c.size());
208 // Make sure we don't have edges that are not in the mesh?
209 edges.clear();
210 for (auto c : e2c) {
211 const Index f = mesh.get_corner_facet(c);
212 const Index first_corner = mesh.get_facet_corner_begin(f);
213 const Index s = mesh.get_facet_size(f);
214 const Index lv0 = c - first_corner;
215 const Index lv1 = (lv0 + 1) % s;
216 const Index v0 = mesh.get_facet_vertex(f, lv0);
217 const Index v1 = mesh.get_facet_vertex(f, lv1);
218 edges.emplace(std::min(v0, v1), std::max(v0, v1));
219 }
220 REQUIRE(edges.size() == e2c.size());
221 // Make sure that every corner points to an edge and back to the same vertex or the other
222 // end vertex of the edge.
223 for (Index f = 0; f < nf; ++f) {
224 const Index first_corner = mesh.get_facet_corner_begin(f);
225 for (Index lv0 = 0, s = mesh.get_facet_size(f); lv0 < s; ++lv0) {
226 const Index v0 = mesh.get_facet_vertex(f, lv0);
227 const Index c = first_corner + lv0;
228 const Index e = c2e[c];
229 const Index c_other = e2c[e];
230 const Index v_other = mesh.get_corner_vertex(c_other);
231
232 // v0 and v_other should be the end points of an edge.
233 REQUIRE_THAT(mesh_edges[e], Catch::Matchers::Contains(v0));
234 REQUIRE_THAT(mesh_edges[e], Catch::Matchers::Contains(v_other));
235 }
236 }
237 // Check that for every vertex / every edge, the "chain" of corners around it touches
238 // all the incident corners exactly once (no duplicate).
239 std::vector<std::unordered_set<Index>> corners_around_vertex(nv);
240 std::vector<std::unordered_set<Index>> corners_around_edge(ne);
241 std::vector<std::unordered_set<Index>> facets_around_vertex(nv);
242 std::vector<std::unordered_set<Index>> facets_around_edge(ne);
243 for (Index f = 0; f < nf; ++f) {
244 const Index first_corner = mesh.get_facet_corner_begin(f);
245 for (Index lv = 0, s = mesh.get_facet_size(f); lv < s; ++lv) {
246 const Index v = mesh.get_facet_vertex(f, lv);
247 const Index c = first_corner + lv;
248 const Index e = c2e[c];
249 REQUIRE(mesh.get_edge(f, lv) == e);
250 REQUIRE(mesh.get_corner_edge(c) == e);
251 REQUIRE(corners_around_vertex[v].count(c) == 0);
252 REQUIRE(corners_around_edge[e].count(c) == 0);
253 corners_around_vertex[v].insert(c);
254 corners_around_edge[e].insert(c);
255 facets_around_vertex[v].insert(f);
256 facets_around_edge[e].insert(f);
257 }
258 }
259 std::unordered_set<Index> corners_around;
260 std::unordered_set<Index> facets_around;
261 for (Index v = 0; v < nv; ++v) {
262 corners_around.clear();
263 facets_around.clear();
264 const Index c0 = v2c[v];
265 REQUIRE(mesh.get_first_corner_around_vertex(v) == c0);
266 REQUIRE(mesh.get_one_corner_around_vertex(v) == c0);
267 for (Index ci = c0; ci != invalid<Index>(); ci = next_around_vertex[ci]) {
268 REQUIRE(mesh.get_next_corner_around_vertex(ci) == next_around_vertex[ci]);
269 REQUIRE(corners_around_vertex[v].count(ci));
270 REQUIRE(corners_around.count(ci) == 0);
271 corners_around.insert(ci);
272 }
273 mesh.foreach_corner_around_vertex(v, [&](Index c) {
274 REQUIRE(corners_around.count(c));
275 });
276 mesh.foreach_facet_around_vertex(v, [&](Index f) {
277 REQUIRE(facets_around_vertex[v].count(f));
278 facets_around.insert(f);
279 });
280 REQUIRE(corners_around.size() == corners_around_vertex[v].size());
281 REQUIRE(facets_around.size() == facets_around_vertex[v].size());
282 REQUIRE(
283 corners_around.size() ==
284 safe_cast<size_t>(mesh.count_num_corners_around_vertex(v)));
285 }
286 for (Index e = 0; e < ne; ++e) {
287 corners_around.clear();
288 facets_around.clear();
289 const Index c0 = e2c[e];
290 REQUIRE(mesh.get_first_corner_around_edge(e) == c0);
291 REQUIRE(mesh.get_one_corner_around_edge(e) == c0);
292 for (Index ci = c0; ci != invalid<Index>(); ci = next_around_edge[ci]) {
293 REQUIRE(mesh.get_next_corner_around_edge(ci) == next_around_edge[ci]);
294 REQUIRE(corners_around_edge[e].count(ci));
295 REQUIRE(corners_around.count(ci) == 0);
296 corners_around.insert(ci);
297 }
298 mesh.foreach_corner_around_edge(e, [&](Index c) { REQUIRE(corners_around.count(c)); });
299 Index first_facet = invalid<Index>();
300 mesh.foreach_facet_around_edge(e, [&](Index f) {
301 REQUIRE(facets_around_edge[e].count(f));
302 facets_around.insert(f);
303 if (first_facet == invalid<Index>()) {
304 first_facet = f;
305 }
306 });
307 REQUIRE(mesh.get_one_facet_around_edge(e) == first_facet);
308 REQUIRE(corners_around.size() == corners_around_edge[e].size());
309 REQUIRE(facets_around.size() == facets_around_edge[e].size());
310 REQUIRE(
311 corners_around.size() == safe_cast<size_t>(mesh.count_num_corners_around_edge(e)));
312 if (mesh.is_boundary_edge(e)) {
313 REQUIRE(corners_around.size() == 1);
314 }
315 }
316 }
317}
318
319} // namespace lagrange::testing
Definition: Mesh.h:48
void foreach_facet_around_edge(Index e, function_ref< void(Index)> func) const
Applies a function to each facet around a prescribed edge.
Definition: SurfaceMesh.cpp:2664
bool is_boundary_edge(Index e) const
Determines whether the specified edge e is a boundary edge.
Definition: SurfaceMesh.cpp:2615
Index get_facet_corner_end(Index f) const
Index past the last corner around the facet.
Definition: SurfaceMesh.cpp:2151
AttributeId attr_id_corner_to_edge() const
Attribute id for corner -> edge indices.
Definition: SurfaceMesh.h:2070
Index count_num_corners_around_edge(Index e) const
Count the number of corners incident to a given edge.
Definition: SurfaceMesh.cpp:2586
Index get_num_vertices() const
Retrieves the number of vertices.
Definition: SurfaceMesh.h:671
Index get_corner_edge(Index c) const
Gets the edge index corresponding to a corner index.
Definition: SurfaceMesh.cpp:2443
AttributeId attr_id_facet_to_first_corner() const
Attribute id for facet -> first corner index.
Definition: SurfaceMesh.h:2053
std::array< Index, 2 > get_edge_vertices(Index e) const
Retrieve edge endpoints.
Definition: SurfaceMesh.cpp:2457
Index get_edge(Index f, Index lv) const
Gets the edge index corresponding to (f, lv) – (f, lv+1).
Definition: SurfaceMesh.cpp:2435
Index get_next_corner_around_edge(Index c) const
Gets the next corner around the edge associated to a corner.
Definition: SurfaceMesh.cpp:2514
void foreach_facet_around_vertex(Index v, function_ref< void(Index)> func) const
Applies a function to each facet around a prescribed vertex.
Definition: SurfaceMesh.cpp:2641
Index count_num_corners_around_vertex(Index v) const
Count the number of corners incident to a given vertex.
Definition: SurfaceMesh.cpp:2532
Index get_one_corner_around_vertex(Index v) const
Get the index of one corner around a given vertex.
Definition: SurfaceMesh.cpp:2609
Index get_facet_corner_begin(Index f) const
First corner around the facet.
Definition: SurfaceMesh.cpp:2139
AttributeId attr_id_vertex_to_first_corner() const
Attribute id for vertex -> first corner index.
Definition: SurfaceMesh.h:2097
Index get_one_facet_around_edge(Index e) const
Get the index of one facet around a given edge.
Definition: SurfaceMesh.cpp:2595
Index get_first_corner_around_edge(Index e) const
Get the index of the first corner around a given edge.
Definition: SurfaceMesh.cpp:2508
bool is_regular() const
Whether the mesh must only contains facets of equal sizes.
Definition: SurfaceMesh.cpp:2104
AttributeId attr_id_next_corner_around_edge() const
Attribute id for corner -> next corner around edge.
Definition: SurfaceMesh.h:2087
Index get_first_corner_around_vertex(Index v) const
Get the index of the first corner around a given vertex.
Definition: SurfaceMesh.cpp:2520
Index get_next_corner_around_vertex(Index c) const
Gets the next corner around the vertex associated to a corner.
Definition: SurfaceMesh.cpp:2526
Index get_facet_vertex(Index f, Index lv) const
Index of a vertex given from a facet + local index.
Definition: SurfaceMesh.h:732
Index get_corner_vertex(Index c) const
Retrieves the index of a vertex given its corner index.
Definition: SurfaceMesh.cpp:2165
void foreach_corner_around_vertex(Index v, function_ref< void(Index)> func) const
Applies a function to each corner around a prescribed vertex.
Definition: SurfaceMesh.cpp:2686
Index get_corner_facet(Index c) const
Retrieves the index of a facet given its corner index.
Definition: SurfaceMesh.cpp:2171
bool has_edges() const
Determines if the attributes associated to mesh edges and connectivity have been initialized.
Definition: SurfaceMesh.h:2159
bool is_hybrid() const
Whether the mesh may contain facets of different sizes.
Definition: SurfaceMesh.h:649
Index get_num_edges() const
Retrieves the number of edges.
Definition: SurfaceMesh.h:692
Index get_one_corner_around_edge(Index e) const
Get the index of one corner around a given edge.
Definition: SurfaceMesh.cpp:2603
Index get_num_facets() const
Retrieves the number of facets.
Definition: SurfaceMesh.h:678
AttributeId attr_id_edge_to_first_corner() const
Attribute id for edge -> first corner index.
Definition: SurfaceMesh.h:2077
AttributeId attr_id_next_corner_around_vertex() const
Attribute id for corner -> next corner around vertex.
Definition: SurfaceMesh.h:2107
AttributeId attr_id_corner_to_facet() const
Attribute id for corner -> facet index.
Definition: SurfaceMesh.h:2063
void foreach_corner_around_edge(Index e, function_ref< void(Index)> func) const
Applies a function to each corner around a prescribed edge.
Definition: SurfaceMesh.cpp:2700
Index get_facet_size(Index f) const
Number of vertices in the facet.
Definition: SurfaceMesh.h:719
Index get_num_corners() const
Retrieves the number of corners.
Definition: SurfaceMesh.h:685
constexpr AttributeId invalid_attribute_id()
Invalid attribute id.
Definition: AttributeFwd.h:76
@ CornerIndex
Single channel integer attribute indexing a mesh corner.
@ VertexIndex
Single channel integer attribute indexing a mesh vertex.
@ EdgeIndex
Single channel integer attribute indexing a mesh edge.
@ FacetIndex
Single channel integer attribute indexing a mesh facet.
void seq_foreach_attribute_read(const SurfaceMesh< Scalar, Index > &mesh, Visitor &&vis)
Applies a function to each attribute of a mesh.
Definition: foreach_attribute.h:233
::nonstd::span< T, Extent > span
A bounds-safe view for sequences of objects.
Definition: span.h:27
#define LA_IGNORE(x)
Ignore x to avoid an unused variable warning.
Definition: warning.h:144
Main namespace for Lagrange.
Definition: AABBIGL.h:30