Lagrange
condense_indexed_attribute.h
1/*
2 * Copyright 2020 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 <algorithm>
15#include <numeric>
16#include <string>
17#include <vector>
18
19// clang-format off
20#include <lagrange/utils/warnoff.h>
21#include <tbb/blocked_range.h>
22#include <tbb/parallel_for.h>
23#include <tbb/parallel_sort.h>
24#include <lagrange/utils/warnon.h>
25// clang-format on
26
27#include <lagrange/Mesh.h>
28#include <lagrange/MeshTrait.h>
29#include <lagrange/legacy/inline.h>
30#include <lagrange/utils/assert.h>
31#include <lagrange/utils/range.h>
32
33namespace lagrange {
34LAGRANGE_LEGACY_INLINE
35namespace legacy {
36
41template <typename MeshType>
42void condense_indexed_attribute(
43 MeshType& mesh,
44 const std::string& attr_name,
45 const std::string& new_attr_name = "")
46{
47 static_assert(MeshTrait<MeshType>::is_mesh(), "MeshType is not a mesh");
49 mesh.has_indexed_attribute(attr_name),
50 fmt::format("Missing attribute '{}'", attr_name));
51
52 using Index = typename MeshType::Index;
53 using AttributeArray = typename MeshType::AttributeArray;
54 using IndexArray = typename MeshType::IndexArray;
55 static_assert(AttributeArray::IsRowMajor, "Attribute array must be row major");
56
57 const auto num_vertices = mesh.get_num_vertices();
58 const auto num_facets = mesh.get_num_facets();
59 const auto& facets = mesh.get_facets();
60 const auto vertex_per_facet = mesh.get_vertex_per_facet();
61
62 const auto attr = mesh.get_indexed_attribute(attr_name);
63 const auto& attr_values = std::get<0>(attr);
64 const auto& attr_indices = std::get<1>(attr);
65 const Index num_cols = safe_cast<Index>(attr_values.cols());
66
67 using IndexEntry = std::array<Index, 2>; // facet id, corner id
68
74 auto index_pair_comp = [&](const IndexEntry& id0, const IndexEntry& id1) {
75 const Index offset_0 = attr_indices(id0[0], id0[1]) * num_cols;
76 const Index offset_1 = attr_indices(id1[0], id1[1]) * num_cols;
77 const auto base_ptr = attr_values.data();
78 return std::lexicographical_compare(
79 base_ptr + offset_0,
80 base_ptr + offset_0 + num_cols,
81 base_ptr + offset_1,
82 base_ptr + offset_1 + num_cols);
83 };
84
88 auto index_pair_eq = [&](const IndexEntry& id0, const IndexEntry& id1) -> bool {
89 return attr_values.row(attr_indices(id0[0], id0[1])) ==
90 attr_values.row(attr_indices(id1[0], id1[1]));
91 };
92
93 Index count = 0;
94 std::vector<Index> condensed_attr_values_index;
95 condensed_attr_values_index.reserve(attr_values.rows());
96 AttributeArray condensed_attr_values;
97 IndexArray condensed_attr_indices(num_facets, vertex_per_facet);
98 std::vector<IndexEntry> indices(num_facets * vertex_per_facet);
99 std::vector<Index> offsets(num_vertices + 2, 0);
100
101 for (auto fi : range(num_facets)) {
102 for (auto ci : range(vertex_per_facet)) {
103 offsets[facets(fi, ci) + 2]++;
104 }
105 }
106 std::partial_sum(offsets.begin(), offsets.end(), offsets.begin());
107
108 for (auto fi : range(num_facets)) {
109 for (auto ci : range(vertex_per_facet)) {
110 auto vi = facets(fi, ci);
111 indices[offsets[vi + 1]++] = {fi, ci};
112 }
113 }
114
115 tbb::parallel_for(
116 tbb::blocked_range<Index>(0, num_vertices),
117 [&](const tbb::blocked_range<Index>& tbb_range) {
118 for (auto vi = tbb_range.begin(); vi != tbb_range.end(); vi++) {
119 tbb::parallel_sort(
120 indices.data() + offsets[vi],
121 indices.data() + offsets[vi + 1],
122 index_pair_comp);
123 }
124 });
125
126 for (auto vi : range(num_vertices)) {
127 const IndexEntry* local_indices = indices.data() + offsets[vi];
128 const Index num_adj_facets = safe_cast<Index>(offsets[vi + 1] - offsets[vi]);
129 for (Index i = 0; i < num_adj_facets; i++) {
130 const auto& curr_entry = local_indices[i];
131 condensed_attr_indices(curr_entry[0], curr_entry[1]) = count;
132
133 for (Index j = i; j < num_adj_facets; j++) {
134 const auto& next_entry = local_indices[j];
135 if (index_pair_eq(curr_entry, next_entry)) {
136 condensed_attr_indices(next_entry[0], next_entry[1]) = count;
137 i = j;
138 } else {
139 break;
140 }
141 }
142 condensed_attr_values_index.push_back(attr_indices(curr_entry[0], curr_entry[1]));
143 count++;
144 }
145 }
146
147 assert(safe_cast<Index>(condensed_attr_values_index.size()) == count);
148
149 condensed_attr_values.resize(count, num_cols);
150 for (auto i : range(count)) {
151 condensed_attr_values.row(i) = attr_values.row(condensed_attr_values_index[i]);
152 }
153
154 if (new_attr_name == "" || new_attr_name == attr_name) {
155 mesh.import_indexed_attribute(attr_name, condensed_attr_values, condensed_attr_indices);
156 } else {
157 mesh.add_indexed_attribute(new_attr_name);
158 mesh.import_indexed_attribute(new_attr_name, condensed_attr_values, condensed_attr_indices);
159 }
160}
161
162} // namespace legacy
163} // namespace lagrange
Definition: Mesh.h:48
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
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