Lagrange
combine_mesh_list.h
1/*
2 * Copyright 2019 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 <Eigen/Dense>
15#include <exception>
16
17#include <lagrange/Logger.h>
18#include <lagrange/Mesh.h>
19#include <lagrange/MeshTrait.h>
20#include <lagrange/common.h>
21#include <lagrange/create_mesh.h>
22#include <lagrange/legacy/inline.h>
23#include <lagrange/utils/range.h>
24
25namespace lagrange {
26LAGRANGE_LEGACY_INLINE
27namespace legacy {
28
29namespace combine_mesh_list_internal {
30
31template <typename MeshTypePtr, typename MeshType2>
32void combine_all_vertex_attributes(
33 const std::vector<MeshTypePtr>& mesh_list,
34 MeshType2& combined_mesh);
35
36template <typename MeshTypePtr, typename MeshType2>
37void combine_all_facet_attributes(
38 const std::vector<MeshTypePtr>& mesh_list,
39 MeshType2& combined_mesh);
40
41template <typename MeshTypePtr, typename MeshType2>
42void combine_all_corner_attributes(
43 const std::vector<MeshTypePtr>& mesh_list,
44 MeshType2& combined_mesh);
45
46template <typename MeshTypePtr, typename MeshType2>
47void combine_all_edge_attributes(
48 const std::vector<MeshTypePtr>& mesh_list,
49 MeshType2& combined_mesh);
50
51template <typename MeshTypePtr, typename MeshType2>
52void combine_all_edge_attributes(
53 const std::vector<MeshTypePtr>& mesh_list,
54 MeshType2& combined_mesh);
55
56template <typename MeshTypePtr, typename MeshType2>
57void combine_all_indexed_attributes(
58 const std::vector<MeshTypePtr>& mesh_list,
59 MeshType2& combined_mesh);
60
61} // namespace combine_mesh_list_internal
62
71template <typename MeshTypePtr>
72auto combine_mesh_list(const std::vector<MeshTypePtr>& mesh_list, bool preserve_attributes = false)
73 -> std::unique_ptr<std::decay_t<typename std::pointer_traits<MeshTypePtr>::element_type>>
74{
75 static_assert(MeshTrait<MeshTypePtr>::is_mesh_ptr(), "Input type is not Mesh smart ptr");
76 using MeshType = typename std::decay_t<typename std::pointer_traits<MeshTypePtr>::element_type>;
77 using Index = typename MeshType::Index;
78 using Vtype = typename MeshType::VertexArray;
79 using Ftype = typename MeshType::FacetArray;
80
81 if (mesh_list.size() == 0) return nullptr;
82
83 {
84 // Remove null meshes.
85 auto is_valid = [](const MeshTypePtr& ptr) { return ptr != nullptr; };
86 if (!std::all_of(mesh_list.begin(), mesh_list.end(), is_valid)) {
87 std::vector<const MeshType*> valid_mesh_list;
88 valid_mesh_list.reserve(mesh_list.size());
89 for (auto& ptr : mesh_list) {
90 if (ptr == nullptr) continue;
91 valid_mesh_list.push_back(&*ptr);
92 }
93 return combine_mesh_list(valid_mesh_list, preserve_attributes);
94 }
95 }
96
97 const auto& front_mesh = mesh_list.front();
98 const Index dim = front_mesh->get_dim();
99 const Index vertex_per_facet = front_mesh->get_vertex_per_facet();
100 Index total_vertices(0), total_facets(0);
101
102 for (const auto& mesh : mesh_list) {
103 total_vertices += mesh->get_num_vertices();
104 total_facets += mesh->get_num_facets();
105 }
106
107 Vtype V(total_vertices, dim);
108 Ftype F(total_facets, vertex_per_facet);
109 Index curr_v_index(0), curr_f_index(0);
110
111 for (const auto& mesh : mesh_list) {
112 assert(mesh->get_dim() == dim);
113 assert(mesh->get_vertex_per_facet() == vertex_per_facet);
114
115 V.middleRows(curr_v_index, mesh->get_num_vertices()) = mesh->get_vertices();
116 F.middleRows(curr_f_index, mesh->get_num_facets()) =
117 mesh->get_facets().array() + curr_v_index;
118
119 curr_v_index += mesh->get_num_vertices();
120 curr_f_index += mesh->get_num_facets();
121 }
122
123 auto combined_mesh = lagrange::create_mesh(std::move(V), std::move(F));
124
125 if (preserve_attributes) {
126 using namespace combine_mesh_list_internal;
127 combine_all_vertex_attributes(mesh_list, *combined_mesh);
128 combine_all_facet_attributes(mesh_list, *combined_mesh);
129 combine_all_corner_attributes(mesh_list, *combined_mesh);
130 combine_all_edge_attributes(mesh_list, *combined_mesh);
131 combine_all_indexed_attributes(mesh_list, *combined_mesh);
132 }
133
134 return combined_mesh;
135}
136
137namespace combine_mesh_list_internal {
138
139template <typename MeshTypePtr, typename MeshType2>
140void combine_all_vertex_attributes(
141 const std::vector<MeshTypePtr>& mesh_list,
142 MeshType2& combined_mesh)
143{
144 using MeshType = typename std::pointer_traits<MeshTypePtr>::element_type;
145 using Index = typename MeshType::Index;
146 using AttributeArray = typename MeshType::AttributeArray;
147
148 const auto& front_mesh = mesh_list.front();
149 const auto total_num_vertices = combined_mesh.get_num_vertices();
150
151 for (const auto& attr_name : front_mesh->get_vertex_attribute_names()) {
152 bool can_merge = true;
153 for (const auto& mesh : mesh_list) {
154 if (!mesh->has_vertex_attribute(attr_name)) {
155 logger().warn("Cannot combine attribute \"{}\"", attr_name);
156 can_merge = false;
157 break;
158 }
159 }
160 if (!can_merge) continue;
161
162 const auto attribute_dim = front_mesh->get_vertex_attribute(attr_name).cols();
163 AttributeArray attr(total_num_vertices, attribute_dim);
164
165 Index curr_row = 0;
166 for (const auto& mesh : mesh_list) {
167 attr.block(curr_row, 0, mesh->get_num_vertices(), attribute_dim) =
168 mesh->get_vertex_attribute(attr_name);
169 curr_row += mesh->get_num_vertices();
170 }
171
172 combined_mesh.add_vertex_attribute(attr_name);
173 combined_mesh.import_vertex_attribute(attr_name, attr);
174 }
175}
176
177template <typename MeshTypePtr, typename MeshType2>
178void combine_all_facet_attributes(
179 const std::vector<MeshTypePtr>& mesh_list,
180 MeshType2& combined_mesh)
181{
182 using MeshType = typename std::pointer_traits<MeshTypePtr>::element_type;
183 using Index = typename MeshType::Index;
184 using AttributeArray = typename MeshType::AttributeArray;
185
186 const auto& front_mesh = mesh_list.front();
187 const auto total_num_facets = combined_mesh.get_num_facets();
188
189 for (const auto& attr_name : front_mesh->get_facet_attribute_names()) {
190 bool can_merge = true;
191 for (const auto& mesh : mesh_list) {
192 if (!mesh->has_facet_attribute(attr_name)) {
193 can_merge = false;
194 logger().warn("Cannot combine facet attribute \"{}\"", attr_name);
195 break;
196 }
197 }
198 if (!can_merge) continue;
199
200 const auto attribute_dim = front_mesh->get_facet_attribute(attr_name).cols();
201 AttributeArray attr(total_num_facets, attribute_dim);
202
203 Index curr_row = 0;
204 for (const auto& mesh : mesh_list) {
205 attr.block(curr_row, 0, mesh->get_num_facets(), attribute_dim) =
206 mesh->get_facet_attribute(attr_name);
207 curr_row += mesh->get_num_facets();
208 }
209
210 combined_mesh.add_facet_attribute(attr_name);
211 combined_mesh.import_facet_attribute(attr_name, attr);
212 }
213}
214
215template <typename MeshTypePtr, typename MeshType2>
216void combine_all_corner_attributes(
217 const std::vector<MeshTypePtr>& mesh_list,
218 MeshType2& combined_mesh)
219{
220 using MeshType = typename std::pointer_traits<MeshTypePtr>::element_type;
221 using Index = typename MeshType::Index;
222 using AttributeArray = typename MeshType::AttributeArray;
223
224 const auto& front_mesh = mesh_list.front();
225 const auto total_num_facets = combined_mesh.get_num_facets();
226 const auto vertex_per_facet = combined_mesh.get_vertex_per_facet();
227
228 for (const auto& attr_name : front_mesh->get_corner_attribute_names()) {
229 bool can_merge = true;
230 for (const auto& mesh : mesh_list) {
231 if (!mesh->has_corner_attribute(attr_name)) {
232 logger().warn("Cannot combine corner attribute \"{}\"", attr_name);
233 can_merge = false;
234 break;
235 }
236 }
237 if (!can_merge) continue;
238
239 const auto attribute_dim = front_mesh->get_corner_attribute(attr_name).cols();
240 AttributeArray attr(total_num_facets * vertex_per_facet, attribute_dim);
241
242 Index curr_row = 0;
243 for (const auto& mesh : mesh_list) {
244 const auto num_facets = mesh->get_num_facets();
245 attr.block(curr_row, 0, num_facets * vertex_per_facet, attribute_dim) =
246 mesh->get_corner_attribute(attr_name);
247 curr_row += num_facets * vertex_per_facet;
248 }
249
250 combined_mesh.add_corner_attribute(attr_name);
251 combined_mesh.import_corner_attribute(attr_name, attr);
252 }
253}
254
255template <typename MeshTypePtr, typename MeshType2>
256void combine_all_edge_attributes(
257 const std::vector<MeshTypePtr>& mesh_list,
258 MeshType2& combined_mesh)
259{
260 using MeshType = typename std::pointer_traits<MeshTypePtr>::element_type;
261 using Index = typename MeshType::Index;
262 using AttributeArray = typename MeshType::AttributeArray;
263
264 const auto& front_mesh = mesh_list.front();
265
266 // All meshes must have edge initialized for this function to work.
267 for (const auto& mesh : mesh_list) {
268 if (!mesh->is_edge_data_initialized()) return;
269 }
270
271 combined_mesh.initialize_edge_data();
272
273 const auto total_num_edges = combined_mesh.get_num_edges();
274
275 for (const auto& attr_name : front_mesh->get_edge_attribute_names()) {
276 bool can_merge = true;
277 for (const auto& mesh : mesh_list) {
278 if (!mesh->has_edge_attribute(attr_name)) {
279 can_merge = false;
280 logger().warn("Cannot combine edge attribute \"{}\"", attr_name);
281 break;
282 }
283 }
284 if (!can_merge) continue;
285
286 const auto attribute_dim = front_mesh->get_edge_attribute(attr_name).cols();
287 AttributeArray attr(total_num_edges, attribute_dim);
288
289 Index vertex_offset = 0;
290 Index facet_offset = 0;
291 Index edge_offset = 0;
292 for (const auto& mesh : mesh_list) {
293 const auto& per_mesh_attr = mesh->get_edge_attribute(attr_name);
294 for (auto old_e : range(mesh->get_num_edges())) {
295 const auto& c = mesh->get_one_corner_around_edge(old_e);
296 const Index f = c / mesh->get_vertex_per_facet();
297 const Index lv = c % mesh->get_vertex_per_facet();
298 la_debug_assert(mesh->get_edge(f, lv) == old_e);
299 const auto new_e = combined_mesh.get_edge(f + facet_offset, lv);
300 attr.row(new_e) = per_mesh_attr.row(old_e);
301
302 // sanity check
303 auto old_v = mesh->get_edge_vertices(old_e);
304 auto new_v = combined_mesh.get_edge_vertices(new_e);
305 std::sort(old_v.begin(), old_v.end());
306 std::sort(new_v.begin(), new_v.end());
307 la_debug_assert(new_v[0] == old_v[0] + vertex_offset);
308 la_debug_assert(new_v[1] == old_v[1] + vertex_offset);
309 }
310 vertex_offset += mesh->get_num_vertices();
311 facet_offset += mesh->get_num_facets();
312 edge_offset += mesh->get_num_edges();
313 }
314
315 combined_mesh.add_edge_attribute(attr_name);
316 combined_mesh.import_edge_attribute(attr_name, attr);
317 }
318}
319
320template <typename MeshTypePtr, typename MeshType2>
321void combine_all_indexed_attributes(
322 const std::vector<MeshTypePtr>& mesh_list,
323 MeshType2& combined_mesh)
324{
325 using MeshType = typename std::pointer_traits<MeshTypePtr>::element_type;
326 using Scalar = typename MeshType::Scalar;
327 using Index = typename MeshType::Index;
328 using AttributeArray = typename MeshType::AttributeArray;
329 using IndexArray = typename MeshType::IndexArray;
330
331 const auto& front_mesh = mesh_list.front();
332
333 for (const auto& attr_name : front_mesh->get_indexed_attribute_names()) {
334 bool can_merge = true;
335 auto ref_attr = front_mesh->get_indexed_attribute_array(attr_name);
336 const auto& ref_values = *std::get<0>(ref_attr);
337 const auto& ref_indices = *std::get<1>(ref_attr);
338
339 if (ref_values.get_scalar_type() != experimental::ScalarToEnum_v<Scalar>) {
340 std::string expected_type = experimental::ScalarToEnum<Scalar>::name;
341 std::string current_type = experimental::enum_to_name(ref_values.get_scalar_type());
342 logger().warn(
343 "Cannot combined indexed attribute ({}) with custom Scalar type \"{}\". "
344 "Expecting \"{}\".",
345 attr_name,
346 current_type,
347 expected_type);
348 continue;
349 }
350 if (ref_indices.get_scalar_type() != experimental::ScalarToEnum_v<Index>) {
351 std::string expected_type = experimental::ScalarToEnum<Index>::name;
352 std::string current_type = experimental::enum_to_name(ref_indices.get_scalar_type());
353 logger().warn(
354 "Cannot combined indexed attribute ({}) with custom Index type \"{}\". "
355 "Expecting \"{}\".",
356 attr_name,
357 current_type,
358 expected_type);
359 continue;
360 }
361
362 Index combined_num_values = 0;
363 Index combined_num_indices = 0;
364
365 for (const auto& mesh : mesh_list) {
366 if (!mesh->has_indexed_attribute(attr_name)) {
367 can_merge = false;
368 logger().warn("Cannot combine indexed attribute \"{}\"", attr_name);
369 break;
370 }
371 auto attr = mesh->get_indexed_attribute_array(attr_name);
372 const auto& values = *std::get<0>(attr);
373 const auto& indices = *std::get<1>(attr);
374
375 if (values.get_scalar_type() != ref_values.get_scalar_type()) {
376 can_merge = false;
377 logger().warn("Cannot combine indexed attribute because value type mismatch.");
378 break;
379 }
380 if (indices.get_scalar_type() != ref_indices.get_scalar_type()) {
381 can_merge = false;
382 logger().warn("Cannot combine indexed attribute because index type mismatch.");
383 break;
384 }
385
386 combined_num_values += safe_cast<Index>(values.rows());
387 combined_num_indices += safe_cast<Index>(indices.rows());
388 }
389 if (!can_merge) continue;
390
391 AttributeArray combined_values(combined_num_values, ref_values.cols());
392 IndexArray combined_indices(combined_num_indices, ref_indices.cols());
393
394 Index curr_value_row = 0;
395 Index curr_index_row = 0;
396 for (const auto& mesh : mesh_list) {
397 auto attr = mesh->get_indexed_attribute_array(attr_name);
398 const auto& values = *std::get<0>(attr);
399 const auto& indices = *std::get<1>(attr);
400
401 combined_values.block(curr_value_row, 0, values.rows(), values.cols()) =
402 values.template view<AttributeArray>();
403 combined_indices.block(curr_index_row, 0, indices.rows(), indices.cols()) =
404 indices.template view<IndexArray>().array() + curr_value_row;
405
406 curr_value_row += safe_cast<Index>(values.rows());
407 curr_index_row += safe_cast<Index>(indices.rows());
408 }
409
410 combined_mesh.add_indexed_attribute(attr_name);
411 combined_mesh.import_indexed_attribute(
412 attr_name,
413 std::move(combined_values),
414 std::move(combined_indices));
415 }
416}
417
418} // namespace combine_mesh_list_internal
419
420} // namespace legacy
421} // namespace lagrange
Definition: Mesh.h:48
void initialize_edge_data()
Edge data initialization.
Definition: Mesh.h:633
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:40
#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
Main namespace for Lagrange.
Definition: AABBIGL.h:30
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