Wildmeshing Toolkit
Loading...
Searching...
No Matches
Marching.cpp
Go to the documentation of this file.
1#include "Marching.hpp"
2
3#include <deque>
4#include <optional>
5#include <wmtk/Scheduler.hpp>
13#include <wmtk/utils/Logger.hpp>
14
15namespace wmtk::components {
16
18{
19public:
22 int64_t m_val;
23
25 Mesh& m,
26 const attribute::MeshAttributeHandle& attribute,
27 PrimitiveType ptype,
28 int64_t val)
29 : m_accessor(m.create_accessor<int64_t>(attribute))
30 , m_ptype(ptype)
31 , m_val(val)
32 {}
33
35};
36
39 std::map<PrimitiveType, attribute::MeshAttributeHandle>& label_handles,
40 const std::vector<int64_t>& input_values,
41 const int64_t output_value)
42 : m_mesh(pos_handle.mesh())
43 , m_pos_handle(pos_handle)
44 , m_input_values(input_values)
45 , m_output_value(output_value)
46{
47 assert(label_handles.count(PrimitiveType::Vertex));
48
50
51 if (label_handles.count(PrimitiveType::Edge)) {
53 }
54
55 if (label_handles.count(PrimitiveType::Triangle)) {
57 }
58}
59
61{
62 using namespace operations;
63
64 auto todo_attribute = m_mesh.register_attribute_typed<int64_t>(
65 "todo_edgesplit_in_marching_component",
67 1);
68 auto splitted_edges_attribute = m_mesh.register_attribute_typed<int64_t>(
69 "splitted_edges_in_marching_component",
71 1);
72
73 assert(m_input_values.size() == 1 || m_input_values.size() == 2);
74
75 if (m_input_values.size() == 1) {
76 logger().info(
77 "Only one input value was given. Perform marching in between value {} and any other "
78 "value.",
80 }
81
84
85 std::deque<TagAttribute> filters;
86 for (size_t i = 0; i < m_filter_labels.size(); ++i) {
87 filters.emplace_back(m_mesh, m_filter_labels[i], PrimitiveType::Edge, m_filter_values[i]);
88 }
89
90 // compute the todo list for the split edge
92 wmtk::attribute::Accessor<int64_t> acc_splitted_edges =
93 m_mesh.create_accessor(splitted_edges_attribute);
94 for (const Tuple& edge : m_mesh.get_all(PrimitiveType::Edge)) {
95 bool is_of_interest = true;
96 for (const TagAttribute& filter : filters) {
97 if ((filter.m_accessor.const_scalar_attribute(edge) == filter.m_val) ==
99 is_of_interest = false;
100 break;
101 }
102 }
103
104 if (!is_of_interest) {
105 continue;
106 }
107
108 const int64_t vt0 = acc_vertex_tag.scalar_attribute(edge);
109 const int64_t vt1 =
111 if (m_input_values.size() == 2) {
112 if ((vt0 == m_input_values[0] && vt1 == m_input_values[1]) ||
113 (vt1 == m_input_values[0] && vt0 == m_input_values[1])) {
114 acc_todo.scalar_attribute(edge) = 1;
115 acc_splitted_edges.scalar_attribute(edge) = 1;
116 }
117 } else {
118 assert(m_input_values.size() == 1);
119 if ((vt0 == m_input_values[0] && vt1 != m_input_values[0]) ||
120 (vt1 == m_input_values[0] && vt0 != m_input_values[0])) {
121 acc_todo.scalar_attribute(edge) = 1;
122 acc_splitted_edges.scalar_attribute(edge) = 1;
123 }
124 }
125 }
126
127 EdgeSplit op_split(m_mesh);
128 op_split.add_invariant(std::make_shared<TodoInvariant>(m_mesh, todo_attribute));
129
130 // position
132
133 // labels
134 {
135 /**************************edge tag******************************/
136 if (m_edge_tag_handle.has_value()) {
137 auto compute_edge_label =
138 [this](
139 const Eigen::MatrixX<int64_t>& labels,
140 const std::vector<Tuple>& tuples) -> Eigen::VectorX<int64_t> {
141 assert(labels.cols() == 2);
142 assert(labels.rows() == 1);
143 assert(tuples.size() == 2);
144 if (labels(0, 0) == m_output_value && labels(0, 1) == m_output_value) {
145 return Eigen::VectorX<int64_t>::Constant(1, m_output_value);
146 }
147
148 // do not change the current value
149 auto acc = m_mesh.create_const_accessor<int64_t>(m_edge_tag_handle.value());
150 const int64_t val = acc.const_scalar_attribute(tuples[0]);
151 return Eigen::VectorX<int64_t>::Constant(1, val);
152 };
153
154 std::shared_ptr edge_tag_strategy = std::make_shared<
156 m_edge_tag_handle.value(),
158 compute_edge_label);
159
161 m_edge_tag_handle.value(),
162 SplitBasicStrategy::Copy,
163 SplitRibBasicStrategy::None);
164 op_split.add_transfer_strategy(edge_tag_strategy);
165 }
166
167 /**************************face tag******************************/
168 if (m_face_tag_handle.has_value() && m_edge_tag_handle.has_value()) {
169 auto compute_face_label =
170 [this](
171 const Eigen::MatrixX<int64_t>& labels,
172 const std::vector<Tuple>& tuples) -> Eigen::VectorX<int64_t> {
173 assert(labels.cols() == 3);
174 assert(labels.rows() == 1);
175 assert(tuples.size() == 3);
176 if (labels(0, 0) == m_output_value && labels(0, 1) == m_output_value &&
177 labels(0, 2) == m_output_value) {
178 return Eigen::VectorX<int64_t>::Constant(1, m_output_value);
179 }
180
181 // do not change the current value
182 auto acc = m_mesh.create_const_accessor<int64_t>(m_face_tag_handle.value());
183 const int64_t val = acc.const_scalar_attribute(tuples[0]);
184 return Eigen::VectorX<int64_t>::Constant(1, val);
185 };
186
187 std::shared_ptr face_tag_strategy = std::make_shared<
189 m_face_tag_handle.value(),
190 m_edge_tag_handle.value(),
191 compute_face_label);
192
194 m_face_tag_handle.value(),
195 SplitBasicStrategy::Copy,
196 SplitRibBasicStrategy::None);
197 op_split.add_transfer_strategy(face_tag_strategy);
198 }
199
200
201 auto tmp = std::make_shared<SplitNewAttributeStrategy<int64_t>>(m_vertex_tag_handle);
202 tmp->set_strategy(SplitBasicStrategy::None);
203 tmp->set_rib_strategy(
204 [this](const VectorX<int64_t>&, const VectorX<int64_t>&, const std::bitset<2>&) {
205 VectorX<int64_t> ret(1);
206 ret(0) = m_output_value;
207 return ret;
208 });
210
212 attribute::MeshAttributeHandle(m_mesh, splitted_edges_attribute),
213 SplitBasicStrategy::Copy,
214 SplitRibBasicStrategy::None);
215 }
216
217 // filters
218 for (const auto& edge_filter_handle : m_filter_labels) {
220 edge_filter_handle,
221 SplitBasicStrategy::None,
222 SplitRibBasicStrategy::None);
223 }
224 // todo_attribute
226 attribute::MeshAttributeHandle(m_mesh, todo_attribute),
227 SplitBasicStrategy::None,
228 SplitRibBasicStrategy::None);
229 // scalar field
230 if (m_scalar_field.has_value()) {
232 m_scalar_field.value(),
233 SplitBasicStrategy::None,
234 SplitRibBasicStrategy::None);
235 }
236 // pass_through
237 for (const auto& attr : m_pass_through_attributes) {
238 op_split.set_new_attribute_strategy(attr);
239 }
240
241 Scheduler scheduler;
242 while (true) {
243 const auto stats = scheduler.run_operation_on_all(op_split);
244 if (stats.number_of_successful_operations() == 0) {
245 break;
246 }
247 }
248
249 if (m_scalar_field.has_value()) {
250 // move vertices according to isovalue
251 auto sf_acc = m_mesh.create_accessor<double>(m_scalar_field.value());
252
253 auto pos_acc = m_mesh.create_accessor<double>(m_pos_handle);
254
255 auto inversion_invariant =
257
258 for (const Tuple& v_tuple : m_mesh.get_all(PrimitiveType::Vertex)) {
260
261 if (acc_vertex_tag.const_scalar_attribute(v) != m_output_value) {
262 continue;
263 }
264 // get the two input positions
265 Tuple v0;
266 Tuple v1;
267 auto incident_edges =
269 for (Tuple e : incident_edges) {
270 if (acc_splitted_edges.const_scalar_attribute(e) == 0) {
271 continue;
272 }
273 if (acc_vertex_tag.const_scalar_attribute(e) == m_output_value) {
275 }
276 if (acc_vertex_tag.const_scalar_attribute(e) == m_input_values[0]) {
277 v0 = e;
278 }
279 if (m_input_values.size() == 2) {
280 if (acc_vertex_tag.const_scalar_attribute(e) == m_input_values[1]) {
281 v1 = e;
282 }
283 } else {
284 if (acc_vertex_tag.const_scalar_attribute(e) != m_input_values[0]) {
285 v1 = e;
286 }
287 }
288 }
289#if defined(WMTK_ENABLE_HASH_UPDATE)
290 assert(m_mesh.is_valid_with_hash(v0));
291 assert(m_mesh.is_valid_with_hash(v1));
292#else
293 assert(m_mesh.is_valid(v0));
294 assert(m_mesh.is_valid(v1));
295#endif
296 const auto p0 = pos_acc.const_vector_attribute(v0);
297 const auto p1 = pos_acc.const_vector_attribute(v1);
298 const double sf0 = sf_acc.const_scalar_attribute(v0);
299 const double sf1 = sf_acc.const_scalar_attribute(v1);
300
301 auto p = pos_acc.vector_attribute(v);
302
303 const double u = (m_isovalue - sf0) / (sf1 - sf0);
304
305 const Eigen::VectorXd p_opt = (1. - u) * p0 + u * p1;
306 const Eigen::VectorXd p_mid = p;
307
308
309 const auto tets = simplex::top_dimension_cofaces_tuples(m_mesh, v);
310
311 // make sure that the current position is valid
312 if (!inversion_invariant.after({}, tets)) {
313 log_and_throw_error("Midpoint split im Marching component caused inversions.");
314 }
315
316 // sf_acc.scalar_attribute(v) = m_isovalue;
317 // continue;
318
319 // test position on the isovalue
320 p = p_opt;
321 if (u > 0 && u < 1 && inversion_invariant.after({}, tets)) {
322 sf_acc.scalar_attribute(v) = m_isovalue;
323 continue;
324 }
325
326 Eigen::VectorXd p_valid = p_mid;
327 Eigen::VectorXd p_target = p_opt;
328
329 // line search
330 bool found_valid_position = false;
331 for (size_t i = 0; i < m_linesearch_iterations; ++i) {
332 p = 0.5 * (p_valid + p_target);
333
334
335 if (inversion_invariant.after({}, tets)) {
336 p_valid = p;
337 } else {
338 p_target = p;
339 }
340 }
341
342 if (p_valid == p_mid) {
343 logger().trace(
344 "Could not find a solution in the linesearch. Using the mid point: {}",
345 p_mid.transpose());
346 }
347
348 p = p_valid;
349 {
350 // compute isovalue
351 const double l = (p1 - p0).norm();
352 const double u_0 = (p - p0).norm() / l;
353 const double u_1 = (p1 - p).norm() / l;
354 const double sf_p = u_0 * sf0 + u_1 * sf1;
355 sf_acc.scalar_attribute(v) = sf_p;
356 }
357 }
358 }
359}
360
361void Marching::add_filter(const attribute::MeshAttributeHandle& label, const int64_t value)
362{
363 assert(label.primitive_type() == PrimitiveType::Edge);
364 m_filter_labels.emplace_back(label);
365 m_filter_values.emplace_back(value);
366}
367
369{
370 m_pass_through_attributes.emplace_back(pass_through);
371}
372
373void Marching::add_pass_through(const std::vector<attribute::MeshAttributeHandle>& pass_through)
374{
377 pass_through.begin(),
378 pass_through.end());
379}
380
382 const attribute::MeshAttributeHandle& scalar_field,
383 const double isovalue)
384{
385 m_scalar_field = scalar_field;
386 m_isovalue = isovalue;
387}
388
389void Marching::set_isovalue_linesearch_iterations(const int64_t linesearch_iterations)
390{
391 m_linesearch_iterations = linesearch_iterations;
392}
393
395{
396 m_invert_filter = true;
397}
398
399
400} // namespace wmtk::components
const attribute::Accessor< T, Mesh, D > create_const_accessor(const attribute::MeshAttributeHandle &handle) const
attribute::TypedAttributeHandle< T > register_attribute_typed(const std::string &name, PrimitiveType type, int64_t size, bool replace=false, T default_value=T(0))
attribute::Accessor< T, Mesh, D > create_accessor(const attribute::MeshAttributeHandle &handle)
std::vector< Tuple > get_all(PrimitiveType type) const
Generate a vector of Tuples from global vertex/edge/triangle/tetrahedron index.
Definition Mesh.cpp:18
virtual bool is_valid(const Tuple &tuple) const
check validity of tuple including its hash
Definition Mesh.cpp:113
virtual Tuple switch_tuple(const Tuple &tuple, PrimitiveType type) const =0
switch the orientation of the Tuple of the given dimension
SchedulerStats run_operation_on_all(operations::Operation &op)
Definition Scheduler.cpp:33
The Tuple is the basic navigation tool in our mesh data structure.
Definition Tuple.hpp:19
A CachingAccessor that uses tuples for accessing attributes instead of indices.
Definition Accessor.hpp:28
T & scalar_attribute(const ArgType &t)
T const_scalar_attribute(const ArgType &t) const
Definition Accessor.hxx:109
auto as() const -> const held_handle_type< held_type_from_primitive< T >()> &
std::vector< attribute::MeshAttributeHandle > m_pass_through_attributes
Definition Marching.hpp:92
void add_filter(const attribute::MeshAttributeHandle &label, const int64_t value)
Add an edge filter to marching.
Definition Marching.cpp:361
std::vector< int64_t > m_input_values
Definition Marching.hpp:86
Marching(attribute::MeshAttributeHandle &pos_handle, std::map< PrimitiveType, attribute::MeshAttributeHandle > &label_handles, const std::vector< int64_t > &input_values, const int64_t output_value)
Initialize the marching method.
Definition Marching.cpp:37
std::optional< attribute::MeshAttributeHandle > m_face_tag_handle
Definition Marching.hpp:85
std::vector< attribute::MeshAttributeHandle > m_filter_labels
Definition Marching.hpp:89
attribute::MeshAttributeHandle m_vertex_tag_handle
Definition Marching.hpp:83
void set_isovalue_linesearch_iterations(const int64_t linesearch_iterations)
Set the number of iterations for the linesearch when trying to match the isovalue.
Definition Marching.cpp:389
attribute::MeshAttributeHandle m_pos_handle
Definition Marching.hpp:82
void invert_filter()
Invert the filter such that everything covered by the filter is ignored.
Definition Marching.cpp:394
void add_isovalue(const attribute::MeshAttributeHandle &scalar_field, const double isovalue)
Position the new vertices along an edge according to an isovalue in a scalar field.
Definition Marching.cpp:381
void process()
Perform the actual marching.
Definition Marching.cpp:60
void add_pass_through(const attribute::MeshAttributeHandle &pass_through)
Add pass through attributes.
Definition Marching.cpp:368
std::optional< attribute::MeshAttributeHandle > m_edge_tag_handle
Definition Marching.hpp:84
std::optional< attribute::MeshAttributeHandle > m_scalar_field
Definition Marching.hpp:94
std::vector< int64_t > m_filter_values
Definition Marching.hpp:90
wmtk::attribute::Accessor< int64_t > m_accessor
Definition Marching.cpp:20
TagAttribute(Mesh &m, const attribute::MeshAttributeHandle &attribute, PrimitiveType ptype, int64_t val)
Definition Marching.cpp:24
TagAttribute(TagAttribute &)=delete
void set_new_attribute_strategy(const attribute::MeshAttributeHandle &attribute, const std::shared_ptr< const operations::BaseSplitNewAttributeStrategy > &other)
Definition EdgeSplit.cpp:85
void add_invariant(std::shared_ptr< Invariant > invariant)
Definition Operation.hpp:48
void add_transfer_strategy(const std::shared_ptr< const operations::AttributeTransferStrategyBase > &other)
Definition Operation.cpp:59
void top_dimension_cofaces_tuples(const PointMesh &mesh, const Simplex &simplex, SimplexCollection &collection)
std::vector< Tuple > cofaces_single_dimension_tuples(const Mesh &mesh, const Simplex &my_simplex, PrimitiveType cofaces_type)
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:101
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:58
Vector< T, Eigen::Dynamic > VectorX
Definition Types.hpp:19