Lagrange
Loading...
Searching...
No Matches
mesh_convert.impl.h
1/*
2 * Copyright 2022 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 <lagrange/mesh_convert.h>
15
16#include <lagrange/IndexedAttribute.h>
17#include <lagrange/MeshTrait.h>
18#include <lagrange/SurfaceMeshTypes.h>
19#include <lagrange/attribute_names.h>
20#include <lagrange/create_mesh.h>
21#include <lagrange/foreach_attribute.h>
22#include <lagrange/internal/fast_edge_sort.h>
23#include <lagrange/internal/get_unique_attribute_name.h>
24#include <lagrange/utils/Error.h>
25#include <lagrange/utils/assert.h>
26#include <lagrange/utils/strings.h>
27#include <lagrange/views.h>
28
29#include <type_traits>
30
31namespace lagrange {
32
33namespace mesh_convert_detail {
34
40enum Policy {
41 Copy,
42 Wrap,
44};
45
46template <Policy policy, typename Scalar, typename Index, typename InputMeshType>
47SurfaceMesh<Scalar, Index> to_surface_mesh_internal(InputMeshType&& mesh)
48{
49 using CMeshType = std::remove_reference_t<InputMeshType>;
50 using MeshType = std::decay_t<InputMeshType>;
51 static_assert(MeshTrait<MeshType>::is_mesh(), "Input type is not Mesh");
52 using AttributeArray = typename MeshType::AttributeArray;
53 using IndexArray = typename MeshType::IndexArray;
54 using MeshScalar = typename MeshType::Scalar;
55 using MeshIndex = typename MeshType::Index;
56
57 if constexpr (policy == Policy::Wrap) {
58 using VertexArray = typename MeshType::VertexArray;
59 using FacetArray = typename MeshType::FacetArray;
60 static_assert(
61 std::is_lvalue_reference_v<InputMeshType&&>,
62 "Input mesh type must be a lvalue reference when wrapping a mesh object.");
63 static_assert(VertexArray::IsRowMajor, "Input vertex array must have row major storage");
64 static_assert(FacetArray::IsRowMajor, "Input facet array must have row major storage");
65 }
66
67#define LA_X_to_surface_mesh_scalar(_, S) || std::is_same_v<Scalar, S>
68 static_assert(
69 false LA_SURFACE_MESH_SCALAR_X(to_surface_mesh_scalar, 0),
70 "Scalar type should be float or double");
71#undef LA_X_to_surface_mesh_scalar
72
73#define LA_X_to_surface_mesh_index(_, I) || std::is_same_v<Index, I>
74 static_assert(
75 false LA_SURFACE_MESH_INDEX_X(to_surface_mesh_index, 0),
76 "Index type should be uint32_t or uint64_t");
77#undef LA_X_to_surface_mesh_index
78
79 // 1st - Transfer vertices positions and facets indices
80 SurfaceMesh<Scalar, Index> new_mesh(static_cast<Index>(mesh.get_dim()));
81 if constexpr (policy == Policy::Copy) {
82 // Copy vertex/facet buffer
83 new_mesh.add_vertices(static_cast<Index>(mesh.get_num_vertices()));
84 new_mesh.add_polygons(
85 static_cast<Index>(mesh.get_num_facets()),
86 static_cast<Index>(mesh.get_vertex_per_facet()));
87 if (mesh.get_num_vertices() > 0) {
88 vertex_ref(new_mesh) = mesh.get_vertices().template cast<Scalar>();
89 }
90 if (mesh.get_num_facets() > 0) {
91 facet_ref(new_mesh) = mesh.get_facets().template cast<Index>();
92 }
93 } else if constexpr (policy == Policy::Wrap) {
94 // Wrap vertex/facet buffer, but only if types are compatible
95 static_assert(std::is_same_v<Scalar, MeshScalar>, "Mesh scalar type mismatch");
96 static_assert(std::is_same_v<Index, MeshIndex>, "Mesh index type mismatch");
97 if constexpr (std::is_const_v<CMeshType>) {
98 const auto& vertices = mesh.get_vertices();
99 const auto& facets = mesh.get_facets();
100 new_mesh.wrap_as_const_vertices(
101 {vertices.data(), safe_cast<size_t>(vertices.size())},
102 mesh.get_num_vertices());
103 new_mesh.wrap_as_const_facets(
104 {facets.data(), safe_cast<size_t>(facets.size())},
105 mesh.get_num_facets(),
106 mesh.get_vertex_per_facet());
107 } else {
108 auto& vertices = mesh.ref_vertices();
109 auto& facets = mesh.ref_facets();
110 new_mesh.wrap_as_vertices(
111 {vertices.data(), safe_cast<size_t>(vertices.size())},
112 mesh.get_num_vertices());
113 new_mesh.wrap_as_facets(
114 {facets.data(), safe_cast<size_t>(facets.size())},
115 mesh.get_num_facets(),
116 mesh.get_vertex_per_facet());
117 }
118 }
119
120 // 2nd - Transfer edge indices
121 if (mesh.is_edge_data_initialized()) {
122 new_mesh.initialize_edges(
123 static_cast<Index>(mesh.get_num_edges()),
124 [&](Index e) -> std::array<Index, 2> {
125 auto v = mesh.get_edge_vertices(static_cast<typename MeshType::Index>(e));
126 return {static_cast<Index>(v[0]), static_cast<Index>(v[1])};
127 });
128 }
129
130 // 3rd - Transfer attributes
131 auto usage_from_name = [](std::string_view name) {
134 }
135 // "uv" is for legacy code with hardcoded "uv" name.
136 if (starts_with(name, AttributeName::texcoord) || starts_with(name, "uv")) {
137 return AttributeUsage::UV;
138 }
141 }
143 };
144
145 auto transfer_attribute = [&](auto&& name, auto&& array, AttributeElement elem) {
146 std::string new_name = internal::get_unique_attribute_name(new_mesh, name);
147 decltype(auto) attr = array->template get<AttributeArray>();
148 if constexpr (policy == Policy::Copy) {
149 new_mesh.template create_attribute<MeshScalar>(
150 new_name,
151 elem,
152 usage_from_name(name),
153 attr.cols());
154 attribute_matrix_ref<MeshScalar>(new_mesh, new_name) = attr;
155 } else if constexpr (policy == Policy::Wrap) {
156 if constexpr (std::is_const_v<CMeshType>) {
157 new_mesh.template wrap_as_const_attribute<MeshScalar>(
158 new_name,
159 elem,
160 usage_from_name(name),
161 attr.cols(),
162 {attr.data(), static_cast<size_t>(attr.size())});
163
164 } else {
165 new_mesh.template wrap_as_attribute<MeshScalar>(
166 new_name,
167 elem,
168 usage_from_name(name),
169 attr.cols(),
170 {attr.data(), static_cast<size_t>(attr.size())});
171 }
172 }
173 };
174
175 // Transfer vertex attributes
176 for (const auto& name : mesh.get_vertex_attribute_names()) {
177 transfer_attribute(name, mesh.get_vertex_attribute_array(name), AttributeElement::Vertex);
178 }
179
180 // Transfer facet attributes
181 for (const auto& name : mesh.get_facet_attribute_names()) {
182 transfer_attribute(name, mesh.get_facet_attribute_array(name), AttributeElement::Facet);
183 }
184
185 // Transfer corner attributes
186 for (const auto& name : mesh.get_corner_attribute_names()) {
187 transfer_attribute(name, mesh.get_corner_attribute_array(name), AttributeElement::Corner);
188 }
189
190 // Transfer edge attributes
191 for (const auto& name : mesh.get_edge_attribute_names()) {
192 transfer_attribute(name, mesh.get_edge_attribute_array(name), AttributeElement::Edge);
193 }
194
195 // Transfer indexed attributes
196 for (const auto& name : mesh.get_indexed_attribute_names()) {
197 decltype(auto) attr = mesh.get_indexed_attribute_array(name);
198 decltype(auto) values = std::get<0>(attr)->template get<AttributeArray>();
199 decltype(auto) indices = std::get<1>(attr)->template get<IndexArray>();
200 std::string new_name = internal::get_unique_attribute_name(new_mesh, name);
201
202 if constexpr (policy == Policy::Wrap) {
203 static_assert(std::is_same_v<Index, MeshIndex>, "Mesh attribute index type mismatch");
204 if (std::is_const_v<CMeshType>) {
205 new_mesh.template wrap_as_const_indexed_attribute<MeshScalar>(
206 new_name,
207 usage_from_name(name),
208 values.rows(),
209 values.cols(),
210 {values.data(), static_cast<size_t>(values.size())},
211 {indices.data(), static_cast<size_t>(indices.size())});
212 } else {
213 new_mesh.template wrap_as_indexed_attribute<MeshScalar>(
214 new_name,
215 usage_from_name(name),
216 values.rows(),
217 values.cols(),
218 {values.data(), static_cast<size_t>(values.size())},
219 {indices.data(), static_cast<size_t>(indices.size())});
220 }
221 } else if constexpr (policy == Policy::Copy) {
222 auto id = new_mesh.template create_attribute<MeshScalar>(
223 new_name,
225 usage_from_name(name),
226 values.cols());
227 auto& new_attr = new_mesh.template ref_indexed_attribute<MeshScalar>(id);
228 new_attr.values().resize_elements(values.rows());
229 matrix_ref(new_attr.values()) = values;
230 reshaped_ref(new_attr.indices(), indices.cols()) = indices.template cast<Index>();
231 }
232 }
233
234 return new_mesh;
235}
236
237} // namespace mesh_convert_detail
238
239template <typename Scalar, typename Index, typename MeshType>
241{
242 return mesh_convert_detail::
243 to_surface_mesh_internal<mesh_convert_detail::Policy::Copy, Scalar, Index>(mesh);
244}
245
246template <typename Scalar, typename Index, typename MeshType>
248{
249 return mesh_convert_detail::
250 to_surface_mesh_internal<mesh_convert_detail::Policy::Wrap, Scalar, Index>(
251 std::forward<MeshType>(mesh));
252}
253
254template <typename MeshType, typename Scalar, typename Index>
255std::unique_ptr<MeshType> to_legacy_mesh(const SurfaceMesh<Scalar, Index>& mesh)
256{
257 static_assert(MeshTrait<MeshType>::is_mesh(), "Input type is not Mesh");
258 using VertexArray = typename MeshType::VertexArray;
259 using FacetArray = typename MeshType::FacetArray;
260 using IndexArray = typename MeshType::IndexArray;
261 using AttributeArray = typename MeshType::AttributeArray;
262 using MeshScalar = typename MeshType::Scalar;
263 using MeshIndex = typename MeshType::Index;
264
265#define LA_X_to_legacy_mesh_scalar(_, S) || std::is_same_v<Scalar, S>
266 static_assert(
267 false LA_SURFACE_MESH_SCALAR_X(to_legacy_mesh_scalar, 0),
268 "Scalar type should be float or double");
269#undef LA_X_to_legacy_mesh_scalar
270
271#define LA_X_to_legacy_mesh_index(_, I) || std::is_same_v<Index, I>
272 static_assert(
273 false LA_SURFACE_MESH_INDEX_X(to_legacy_mesh_index, 0),
274 "Index type should be uint32_t or uint64_t");
275#undef LA_X_to_legacy_mesh_index
276
277 la_runtime_assert(mesh.is_regular(), "Input polygonal mesh is not regular");
278
279 LA_IGNORE(mesh);
280 auto new_mesh = [&] {
281 VertexArray vertices;
282 FacetArray facets;
283 if (mesh.get_num_vertices() > 0) {
284 vertices = vertex_view(mesh).template cast<MeshScalar>();
285 }
286 if (mesh.get_num_facets() > 0) {
287 facets = facet_view(mesh).template cast<MeshIndex>();
288 }
289 return create_mesh<VertexArray, FacetArray>(std::move(vertices), std::move(facets));
290 }();
291
292 // If mesh contains edges, attempt to transfer them as well
293 std::vector<Index> old_edge_ids;
294 std::vector<MeshIndex> new_edge_ids;
295 if (mesh.has_edges()) {
296 logger().warn("Mesh contains edges information. A possible reordering may occur.");
297 new_mesh->initialize_edge_data();
299 mesh.get_num_edges() == static_cast<Index>(new_mesh->get_num_edges()),
300 "Number of edges do not match");
301 const auto num_vertices = static_cast<size_t>(mesh.get_num_vertices());
302 auto buffer = std::make_unique<uint8_t[]>(
303 (num_vertices + 1) * std::max(sizeof(Index), sizeof(MeshIndex)));
304 old_edge_ids = internal::fast_edge_sort(
305 mesh.get_num_edges(),
306 mesh.get_num_vertices(),
307 [&](Index e) -> std::array<Index, 2> { return mesh.get_edge_vertices(e); },
308 {reinterpret_cast<Index*>(buffer.get()), num_vertices + 1});
309 new_edge_ids = internal::fast_edge_sort(
310 new_mesh->get_num_edges(),
311 new_mesh->get_num_vertices(),
312 [&](MeshIndex e) -> std::array<MeshIndex, 2> { return new_mesh->get_edge_vertices(e); },
313 {reinterpret_cast<MeshIndex*>(buffer.get()), num_vertices + 1});
314 }
315
316 // Transfer non-indexed attributes
318 mesh,
319 [&](std::string_view name_view, auto&& attr) {
320 using AttributeType = std::decay_t<decltype(attr)>;
321 using ValueType = typename AttributeType::ValueType;
322 if (mesh.attr_name_is_reserved(name_view)) {
323 return;
324 }
325 std::string name(name_view);
326 AttributeArray vals =
328 switch (attr.get_element_type()) {
330 new_mesh->add_vertex_attribute(name);
331 new_mesh->import_vertex_attribute(name, std::move(vals));
332 break;
333 }
335 new_mesh->add_facet_attribute(name);
336 new_mesh->import_facet_attribute(name, std::move(vals));
337 break;
338 }
340 new_mesh->add_corner_attribute(name);
341 new_mesh->import_corner_attribute(name, std::move(vals));
342 break;
343 }
345 // Permute rows
346 auto old_vals = attribute_matrix_view<ValueType>(mesh, name);
347 for (Index e = 0; e < mesh.get_num_edges(); ++e) {
348 vals.row(new_edge_ids[e]) =
349 old_vals.row(old_edge_ids[e]).template cast<MeshScalar>();
350 }
351 new_mesh->add_edge_attribute(name);
352 new_mesh->import_edge_attribute(name, std::move(vals));
353 break;
354 }
356 logger().warn("Cannot transfer value attribute: {}", name);
357 break;
359 // Not possible by construction
360 break;
361 }
362 });
363
364 // Transfer indexed attributes
365 const auto nvpf = static_cast<size_t>(new_mesh->get_vertex_per_facet());
367 mesh,
368 [&](std::string_view name_view, auto&& attr) {
369 if (mesh.attr_name_is_reserved(name_view)) {
370 return;
371 }
372 std::string name(name_view);
373 IndexArray indices = reshaped_view(attr.indices(), nvpf).template cast<MeshIndex>();
374 AttributeArray values = matrix_view(attr.values()).template cast<MeshScalar>();
375 new_mesh->add_indexed_attribute(name);
376 new_mesh->import_indexed_attribute(name, std::move(values), std::move(indices));
377 });
378
379 return new_mesh;
380}
381
382} // namespace lagrange
A general purpose polygonal mesh class.
Definition SurfaceMesh.h:66
Index get_num_vertices() const
Retrieves the number of vertices.
Definition SurfaceMesh.h:680
AttributeId wrap_as_const_vertices(span< const Scalar > vertices_view, Index num_vertices)
Wraps a read-only external buffer as mesh vertices coordinates.
Definition SurfaceMesh.cpp:832
Index get_num_edges() const
Retrieves the number of edges.
Definition SurfaceMesh.h:701
AttributeId wrap_as_vertices(span< Scalar > vertices_view, Index num_vertices)
Wraps a writable external buffer as mesh vertices coordinates.
Definition SurfaceMesh.cpp:808
Index get_num_facets() const
Retrieves the number of facets.
Definition SurfaceMesh.h:687
Index get_vertex_per_facet() const
Retrieves the number of vertex per facet in a regular mesh.
Definition SurfaceMesh.cpp:2345
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:40
AttributeElement
Type of element to which the attribute is attached.
Definition AttributeFwd.h:26
@ Vector
Mesh attribute can have any number of channels (including 1 channel).
Definition AttributeFwd.h:55
@ Normal
Mesh attribute can have dim or dim + 1 channels.
Definition AttributeFwd.h:58
@ Color
Mesh attribute can have 1, 2, 3 or 4 channels.
Definition AttributeFwd.h:61
@ UV
Mesh attribute must have exactly 2 channels.
Definition AttributeFwd.h:62
@ Value
Values that are not attached to a specific element.
Definition AttributeFwd.h:42
@ Edge
Per-edge mesh attributes.
Definition AttributeFwd.h:34
@ Indexed
Indexed mesh attributes.
Definition AttributeFwd.h:45
@ Facet
Per-facet mesh attributes.
Definition AttributeFwd.h:31
@ Corner
Per-corner mesh attributes.
Definition AttributeFwd.h:37
@ Vertex
Per-vertex mesh attributes.
Definition AttributeFwd.h:28
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:208
SurfaceMesh< Scalar, Index > to_surface_mesh_wrap(MeshType &&mesh)
Wrap a legacy mesh object as a surface mesh object.
Definition mesh_convert.impl.h:247
SurfaceMesh< ToScalar, ToIndex > cast(const SurfaceMesh< FromScalar, FromIndex > &source_mesh, const AttributeFilter &convertible_attributes={}, std::vector< std::string > *converted_attributes_names=nullptr)
Cast a mesh to a mesh of different scalar and/or index type.
SurfaceMesh< Scalar, Index > to_surface_mesh_copy(const MeshType &mesh)
Convert a legacy mesh object to a surface mesh object.
Definition mesh_convert.impl.h:240
std::unique_ptr< MeshType > to_legacy_mesh(const SurfaceMesh< Scalar, Index > &mesh)
Convert a surface mesh object to a legacy mesh object.
Definition mesh_convert.impl.h:255
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
ConstRowMatrixView< ValueType > attribute_matrix_view(const SurfaceMesh< Scalar, Index > &mesh, std::string_view name)
Returns a read-only view of a mesh attribute in the form of an Eigen matrix.
Definition views.cpp:96
ConstRowMatrixView< Scalar > vertex_view(const SurfaceMesh< Scalar, Index > &mesh)
Returns a read-only view of the mesh vertices in the form of an Eigen matrix.
Definition views.cpp:156
RowMatrixView< ValueType > attribute_matrix_ref(SurfaceMesh< Scalar, Index > &mesh, std::string_view name)
Returns a writable view of a mesh attribute in the form of an Eigen matrix.
Definition views.cpp:88
RowMatrixView< Scalar > vertex_ref(SurfaceMesh< Scalar, Index > &mesh)
Returns a writable view of the mesh vertices in the form of an Eigen matrix.
Definition views.cpp:150
ConstRowMatrixView< ValueType > reshaped_view(const Attribute< ValueType > &attribute, size_t num_cols)
Returns a read-only view of a given single-channel attribute in the form of an Eigen matrix with a pr...
Definition views.cpp:71
ConstRowMatrixView< Index > facet_view(const SurfaceMesh< Scalar, Index > &mesh)
Returns a read-only view of a mesh facets in the form of an Eigen matrix.
Definition views.cpp:170
RowMatrixView< ValueType > reshaped_ref(Attribute< ValueType > &attribute, size_t num_cols)
Returns a writable view of a given single-channel attribute in the form of an Eigen matrix with a pre...
Definition views.cpp:58
RowMatrixView< Index > facet_ref(SurfaceMesh< Scalar, Index > &mesh)
Returns a writable view of a mesh facets in the form of an Eigen matrix.
Definition views.cpp:162
RowMatrixView< ValueType > matrix_ref(Attribute< ValueType > &attribute)
Returns a writable view of a given attribute in the form of an Eigen matrix.
Definition views.cpp:26
#define LA_SURFACE_MESH_INDEX_X(mode, data)
X Macro arguments to iterate over index types available for the SurfaceMesh<> class.
Definition SurfaceMeshTypes.h:71
#define LA_SURFACE_MESH_SCALAR_X(mode, data)
X Macro arguments to iterate over scalar types available for the SurfaceMesh<> class.
Definition SurfaceMeshTypes.h:91
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:174
LA_CORE_API bool starts_with(std::string_view str, std::string_view prefix)
Checks if the string begins with the given prefix.
Definition strings.cpp:38
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:50
#define LA_IGNORE(x)
Ignore x to avoid an unused variable warning.
Definition warning.h:144
std::vector< Index > fast_edge_sort(Index num_edges, Index num_vertices, Func get_edge, span< Index > vertex_to_first_edge={})
Sort an array of edges using a parallel bucket sort.
Definition fast_edge_sort.h:71
std::string get_unique_attribute_name(const SurfaceMesh< Scalar, Index > &mesh, std::string_view name)
Returns a unique attribute name by appending a suffix if necessary.
Definition get_unique_attribute_name.h:37
Main namespace for Lagrange.
auto create_mesh(const Eigen::MatrixBase< DerivedV > &vertices, const Eigen::MatrixBase< DerivedF > &facets)
This function create a new mesh given the vertex and facet arrays by copying data into the Mesh objec...
Definition create_mesh.h:39
static constexpr std::string_view normal
Normal.
Definition attribute_names.h:27
static constexpr std::string_view color
Color.
Definition attribute_names.h:32
static constexpr std::string_view texcoord
Texture coordinates.
Definition attribute_names.h:37