Wildmeshing Toolkit
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 
15 namespace wmtk::components {
16 
18 {
19 public:
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)) {
52  m_edge_tag_handle = label_handles[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.",
79  m_input_values[0]);
80  }
81 
82  wmtk::attribute::Accessor<int64_t> acc_vertex_tag =
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)) {
259  const simplex::Simplex v(m_mesh, PrimitiveType::Vertex, v_tuple);
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 
361 void 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 
373 void 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 
389 void 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
attribute::TypedAttributeHandle< T > register_attribute_typed(const std::string &name, PrimitiveType type, int64_t size, bool replace=false, T default_value=T(0))
const attribute::Accessor< T, Mesh, D > create_const_accessor(const attribute::MeshAttributeHandle &handle) const
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:112
virtual Tuple switch_tuple(const Tuple &tuple, PrimitiveType type) const =0
switch the orientation of the Tuple of the given dimension
attribute::Accessor< T, Mesh, D > create_accessor(const attribute::MeshAttributeHandle &handle)
SchedulerStats run_operation_on_all(operations::Operation &op)
Definition: Scheduler.cpp:33
T & scalar_attribute(const ArgType &t)
T const_scalar_attribute(const ArgType &t) const
Definition: Accessor.hxx:112
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