Lagrange
All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Modules Pages
equivalence_check.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/Attribute.h>
16#include <lagrange/Logger.h>
17#include <lagrange/SurfaceMesh.h>
18#include <lagrange/foreach_attribute.h>
19#include <lagrange/map_attribute.h>
20#include <lagrange/views.h>
21
22// clang-format off
23#include <lagrange/utils/warnoff.h>
24#include <catch2/catch_test_macros.hpp>
25#include <lagrange/utils/warnon.h>
26// clang-format on
27
28namespace lagrange::testing {
29
48template <typename ValueType1, typename ValueType2, typename Scalar, typename Index>
49bool attribute_is_approx_equivalent(
52 AttributeId id1,
53 AttributeId id2)
54{
55 std::string_view name = "testing_attribute_is_approx_equivalent";
56 id1 = map_attribute(mesh1, id1, name, AttributeElement::Corner);
57 id2 = map_attribute(mesh2, id2, name, AttributeElement::Corner);
58
59 const auto& attr1 = mesh1.template get_attribute<ValueType1>(id1);
60 const auto& attr2 = mesh2.template get_attribute<ValueType2>(id2);
61
62 REQUIRE(attr1.get_usage() == attr2.get_usage());
63 REQUIRE(attr1.get_num_channels() == attr2.get_num_channels());
64 REQUIRE(attr1.get_num_elements() == attr2.get_num_elements());
65
66 auto data1 = matrix_view(attr1);
67 auto data2 = matrix_view(attr2);
68
69 constexpr Scalar rel_tol = static_cast<Scalar>(1e-6);
70 bool match = data1.template cast<Scalar>().isApprox(data2.template cast<Scalar>(), rel_tol);
71
72 mesh1.delete_attribute(name);
73 mesh2.delete_attribute(name);
74 return match;
75}
76
91template <AttributeUsage usage, typename Scalar, typename Index>
92void ensure_approx_equivalent_usage(
95{
96 seq_foreach_named_attribute_read(mesh1, [&](std::string_view name1, auto&& attr1) {
97 using ValueType1 = typename std::decay_t<decltype(attr1)>::ValueType;
98 if (attr1.get_usage() != usage) return;
99 auto id1 = mesh1.get_attribute_id(name1);
100
101 bool has_equivalent = false;
102 seq_foreach_named_attribute_read(mesh2, [&](std::string_view name2, auto&& attr2) {
103 using ValueType2 = typename std::decay_t<decltype(attr2)>::ValueType;
104 if (attr2.get_usage() != usage) return;
105 auto id2 = mesh2.get_attribute_id(name2);
106 if (attribute_is_approx_equivalent<ValueType1, ValueType2>(mesh1, mesh2, id1, id2))
107 has_equivalent = true;
108 });
109
110 REQUIRE(has_equivalent);
111 });
112}
113
133template <typename Scalar, typename Index>
134void ensure_approx_equivalent_mesh(
137{
138 // Ensure vertices are equivalent.
139 REQUIRE(testing::attribute_is_approx_equivalent<Scalar, Scalar>(
140 mesh1,
141 mesh2,
144
145 // Special attributes are compared based on usage.
146 testing::ensure_approx_equivalent_usage<AttributeUsage::Normal>(mesh1, mesh2);
147 testing::ensure_approx_equivalent_usage<AttributeUsage::UV>(mesh1, mesh2);
148 testing::ensure_approx_equivalent_usage<AttributeUsage::Color>(mesh1, mesh2);
149 testing::ensure_approx_equivalent_usage<AttributeUsage::Position>(mesh1, mesh2);
150 testing::ensure_approx_equivalent_usage<AttributeUsage::Tangent>(mesh1, mesh2);
151 testing::ensure_approx_equivalent_usage<AttributeUsage::Bitangent>(mesh1, mesh2);
152
153 // For all other attributes, compare with the attribute in the other mesh with the same name.
154 seq_foreach_named_attribute_read(mesh1, [&](std::string_view name, auto&& attr1) {
155 if (mesh1.attr_name_is_reserved(name)) return;
156 auto usage = attr1.get_usage();
157 if (usage != AttributeUsage::Scalar && usage != AttributeUsage::Vector) return;
158 auto element = attr1.get_element_type();
159
160 // We do not check edge or value attributes as there is no clear mapping of them across
161 // multiple meshes.
162 if (element == AttributeElement::Edge || element == AttributeElement::Value) return;
163
164 using ValueType1 = typename std::decay_t<decltype(attr1)>::ValueType;
165 AttributeId id1 = mesh1.get_attribute_id(name);
166 AttributeId id2 = mesh2.get_attribute_id(name);
167
168 details::internal_foreach_named_attribute<
170 details::Ordering::Sequential,
171 details::Access::Read>(
172 mesh2,
173 [&](std::string_view name2, auto&& attr2) {
174 if (mesh2.attr_name_is_reserved(name2)) return;
175
176 using ValueType2 = typename std::decay_t<decltype(attr2)>::ValueType;
177 REQUIRE(testing::attribute_is_approx_equivalent<ValueType1, ValueType2>(
178 mesh1,
179 mesh2,
180 id1,
181 id2));
182 },
183 {&id2, 1});
184 });
185}
186
187} // namespace lagrange::testing
static constexpr BitField all()
Named constructor returning a bitfield set to 1.
Definition: BitField.h:76
A general purpose polygonal mesh class.
Definition: SurfaceMesh.h:66
AttributeId attr_id_vertex_to_position() const
Attribute id for vertex -> positions.
Definition: SurfaceMesh.h:2028
void delete_attribute(std::string_view name, AttributeDeletePolicy policy=AttributeDeletePolicy::ErrorIfReserved)
Delete an attribute given by name.
Definition: SurfaceMesh.cpp:1036
AttributeId get_attribute_id(std::string_view name) const
Retrieve an attribute id given its name.
Definition: SurfaceMesh.cpp:341
static bool attr_name_is_reserved(std::string_view name)
Check whether the given name corresponds to a reserved attribute.
Definition: SurfaceMesh.cpp:1403
AttributeId map_attribute(SurfaceMesh< Scalar, Index > &mesh, AttributeId id, std::string_view new_name, AttributeElement new_element)
Map attribute values to a new attribute with a different element type.
Definition: map_attribute.cpp:241
uint32_t AttributeId
Identified to be used to access an attribute.
Definition: AttributeFwd.h:73
@ Vector
Mesh attribute can have any number of channels (including 1 channel).
@ Scalar
Mesh attribute must have exactly 1 channel.
@ Value
Values that are not attached to a specific element.
Definition: AttributeFwd.h:42
@ Edge
Per-edge mesh attributes.
Definition: AttributeFwd.h:34
@ Corner
Per-corner mesh attributes.
Definition: AttributeFwd.h:37
void seq_foreach_named_attribute_read(const SurfaceMesh< Scalar, Index > &mesh, Visitor &&vis)
Applies a function to each attribute of a mesh.
Definition: foreach_attribute.h:207
ConstRowMatrixView< ValueType > matrix_view(const Attribute< ValueType > &attribute)
Returns a read-only view of a given attribute in the form of an Eigen matrix.
Definition: views.cpp:35