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