Wildmeshing Toolkit
Loading...
Searching...
No Matches
SizingField.hpp
1#pragma once
2
3#include <wmtk/simplex/Simplex.hpp>
4
5#include <algorithm>
6#include <array>
7#include <cstddef>
8#include <queue>
9#include <unordered_set>
10#include <utility>
11#include <vector>
12
13namespace wmtk::utils {
14
15// Dimension-generic pieces of the "stuck element" sizing-field refinement shared by the
16// wild-meshing applications (tetwild, simwild, triwild). Each application keeps its own
17// refine_sizing_around_worst() driver -- they differ in which attributes hold the quality
18// and the sizing scalar, and simwild additionally caps the sizing by its per-tag sizing
19// field -- but the actual algorithms below are identical between them, so they live here.
20//
21// Everything is expressed through small callables rather than a mesh interface: the data
22// these need (quality, sizing scalar, positions) lives in the application's attribute
23// collections, not in TetMesh/TriMesh. They are templates, not std::function, because the
24// one-ring accessor is called once per vertex inside a BFS.
25
28using WorstCell = std::pair<double, size_t>;
29
42template <class IsValid, class Energy>
43std::vector<WorstCell> select_worst_cells(
44 size_t n_cells,
45 IsValid is_valid,
46 Energy energy,
47 double filter_energy,
48 int num_worst)
49{
50 // `worst` is kept sorted ascending by quality, size <= num_worst (front = smallest kept).
51 std::vector<WorstCell> worst;
52 if (num_worst > 0) {
53 worst.reserve(num_worst);
54 }
55
56 for (size_t cid = 0; cid < n_cells; ++cid) {
57 if (!is_valid(cid)) {
58 continue;
59 }
60 const double q = energy(cid);
61 if (q < filter_energy) {
62 continue;
63 }
64 if (num_worst > 0) {
65 if (static_cast<int>(worst.size()) < num_worst) {
66 worst.emplace_back(q, cid);
67 std::sort(worst.begin(), worst.end());
68 } else if (q > worst.front().first) {
69 worst.front() = {q, cid};
70 std::sort(worst.begin(), worst.end());
71 }
72 } else {
73 worst.emplace_back(q, cid);
74 }
75 }
76
77 if (num_worst <= 0) {
78 std::sort(worst.begin(), worst.end());
79 }
80 return worst;
81}
82
93template <size_t N, class Pos>
94simplex::Edge longest_edge(const std::array<size_t, N>& vs, Pos pos)
95{
96 static_assert(N >= 2, "a cell needs at least two vertices to have an edge");
97 double l2max = -1;
98 size_t ea = vs[0];
99 size_t eb = vs[1];
100 for (size_t a = 0; a < N; ++a) {
101 for (size_t b = a + 1; b < N; ++b) {
102 const double l2 = (pos(vs[a]) - pos(vs[b])).squaredNorm();
103 if (l2 > l2max) {
104 l2max = l2;
105 ea = vs[a];
106 eb = vs[b];
107 }
108 }
109 }
110 return simplex::Edge(ea, eb);
111}
112
119template <class OneRing>
120std::unordered_set<size_t>
121grow_vertex_region(const std::vector<size_t>& seeds, int n_rings, OneRing one_ring)
122{
123 std::unordered_set<size_t> region(seeds.begin(), seeds.end());
124 std::vector<size_t> frontier(region.begin(), region.end());
125
126 for (int r = 0; r < n_rings; ++r) {
127 std::vector<size_t> next;
128 for (const size_t v : frontier) {
129 for (const size_t u : one_ring(v)) {
130 if (region.insert(u).second) {
131 next.push_back(u);
132 }
133 }
134 }
135 frontier.swap(next);
136 }
137 return region;
138}
139
146template <class Sizing>
147std::vector<size_t> apply_sizing_refinement(
148 const std::unordered_set<size_t>& region,
149 double factor,
150 double floor,
151 Sizing sizing)
152{
153 std::vector<size_t> refined;
154 refined.reserve(region.size());
155 for (const size_t v : region) {
156 double& s = sizing(v);
157 const double ns = std::max(floor, s * factor);
158 if (ns < s) {
159 s = ns;
160 refined.push_back(v);
161 }
162 }
163 return refined;
164}
165
177template <class Sizing, class OneRing>
178void gradation_smooth_sizing(
179 double grade,
180 const std::vector<size_t>& seeds,
181 Sizing sizing,
182 OneRing one_ring)
183{
184 if (grade <= 1.0) {
185 return;
186 }
187
188 std::queue<size_t> q;
189 for (const size_t v : seeds) {
190 q.push(v);
191 }
192 while (!q.empty()) {
193 const size_t u = q.front();
194 q.pop();
195 const double cap = grade * sizing(u);
196 if (cap >= 1.0) {
197 continue;
198 }
199 for (const size_t w : one_ring(u)) {
200 double& sw = sizing(w);
201 if (sw > cap) {
202 sw = cap;
203 q.push(w);
204 }
205 }
206 }
207}
208
233template <class IsValid, class Quality, class CellVids, class IsSurfaceVertex>
234std::vector<size_t> active_vertices(
235 size_t n_verts,
236 size_t n_cells,
237 IsValid is_valid,
238 Quality quality,
239 CellVids cell_vids,
240 double thr,
241 IsSurfaceVertex is_surface_vertex)
242{
243 std::vector<char> seen(n_verts, 0);
244 std::vector<size_t> out;
245 for (size_t cid = 0; cid < n_cells; ++cid) {
246 if (!is_valid(cid)) {
247 continue;
248 }
249 if (quality(cid) < thr) {
250 continue;
251 }
252 for (const size_t v : cell_vids(cid)) {
253 if (!seen[v]) {
254 seen[v] = 1;
255 out.push_back(v);
256 }
257 }
258 }
259 for (size_t v = 0; v < n_verts; ++v) {
260 if (!seen[v] && is_surface_vertex(v)) {
261 seen[v] = 1;
262 out.push_back(v);
263 }
264 }
265 return out;
266}
267
268} // namespace wmtk::utils