Wildmeshing Toolkit
isotropic_remeshing.cpp
Go to the documentation of this file.
2 
3 #include <wmtk/EdgeMesh.hpp>
4 #include <wmtk/Scheduler.hpp>
5 #include <wmtk/TriMesh.hpp>
28 #include <wmtk/utils/Logger.hpp>
29 
30 
31 #include <Eigen/Geometry>
34 
36 // compute the length relative to the bounding box diagonal
38  const attribute::MeshAttributeHandle& position,
39  const double length_rel)
40 {
41  auto pos = position.mesh().create_const_accessor<double>(position);
42  const auto vertices = position.mesh().get_all(PrimitiveType::Vertex);
43  Eigen::AlignedBox<double, Eigen::Dynamic> bbox(pos.dimension());
44 
45 
46  for (const auto& v : vertices) {
47  bbox.extend(pos.const_vector_attribute(v));
48  }
49 
50  const double diag_length = bbox.sizes().norm();
51 
52  return length_rel * diag_length;
53 }
54 
55 
57 {
58  using namespace internal;
59 
60 
61  auto position = options.position_attribute;
62 
63  if (position.mesh().top_simplex_type() != PrimitiveType::Triangle) {
65  "isotropic remeshing works only for triangle meshes: {}",
66  primitive_type_name(position.mesh().top_simplex_type()));
67  }
68 
69  auto pass_through_attributes = options.pass_through_attributes;
70  auto other_positions = options.other_position_attributes;
71 
72  double length = options.length_abs;
73  if (options.length_abs < 0) {
74  if (options.length_rel < 0) {
75  throw std::runtime_error("Either absolute or relative length must be set!");
76  }
77  length = relative_to_absolute_length(position, options.length_rel);
78  }
79 
80  // clear attributes
81  std::vector<attribute::MeshAttributeHandle> keeps = pass_through_attributes;
82  keeps.emplace_back(position);
83  keeps.insert(keeps.end(), other_positions.begin(), other_positions.end());
84 
85  // TODO: brig me back!
86  // mesh_in->clear_attributes(keeps);
87 
88  // gather handles again as they were invalidated by clear_attributes
89  // positions = utils::get_attributes(cache, *mesh_in,
90  // options.position_attribute); assert(positions.size() == 1); position =
91  // positions.front(); pass_through_attributes = utils::get_attributes(cache,
92  // *mesh_in, options.pass_through_attributes);
93 
94  std::optional<attribute::MeshAttributeHandle> position_for_inversion =
96 
97 
98  assert(dynamic_cast<TriMesh*>(&position.mesh()) != nullptr);
99 
100  TriMesh& mesh = static_cast<TriMesh&>(position.mesh());
101 
102  const double length_min = (4. / 5.) * length;
103  const double length_max = (4. / 3.) * length;
104 
105  std::vector<attribute::MeshAttributeHandle> positions = other_positions;
106  positions.push_back(position);
107 
108  auto invariant_link_condition =
109  std::make_shared<wmtk::invariants::MultiMeshLinkConditionInvariant>(mesh);
110 
111  auto invariant_min_edge_length = std::make_shared<MinEdgeLengthInvariant>(
112  mesh,
113  position.as<double>(),
114  length_max * length_max);
115 
116  auto invariant_max_edge_length = std::make_shared<MaxEdgeLengthInvariant>(
117  mesh,
118  position.as<double>(),
119  length_min * length_min);
120 
121  auto invariant_interior_edge = std::make_shared<invariants::InvariantCollection>(mesh);
122  auto invariant_interior_vertex = std::make_shared<invariants::InvariantCollection>(mesh);
123 
124  auto set_all_invariants = [&](auto&& m) {
125  invariant_interior_edge->add(
126  std::make_shared<invariants::InteriorSimplexInvariant>(m, PrimitiveType::Edge));
127  invariant_interior_vertex->add(
128  std::make_shared<invariants::InteriorSimplexInvariant>(m, PrimitiveType::Vertex));
129  };
130  multimesh::MultiMeshVisitor visitor(set_all_invariants);
131  visitor.execute_from_root(mesh);
132 
133  auto invariant_valence_improve =
134  std::make_shared<invariants::ValenceImprovementInvariant>(mesh);
135 
136  auto invariant_mm_map = std::make_shared<MultiMeshMapValidInvariant>(mesh);
137 
138  auto update_position_func = [](const Eigen::MatrixXd& P) -> Eigen::VectorXd {
139  return P.col(0);
140  };
141  std::shared_ptr<wmtk::operations::SingleAttributeTransferStrategy<double, double>>
142  update_position;
143 
144  if (!options.other_position_attributes.empty()) {
145  update_position =
146  std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<double, double>>(
147  other_positions.front(),
148  position,
149  update_position_func);
150  }
151 
152  using namespace operations;
153 
154  assert(mesh.is_connectivity_valid());
155 
156  std::vector<std::shared_ptr<Operation>> ops;
157 
158  // split
159  wmtk::logger().debug("Configure isotropic remeshing split");
160  auto op_split = std::make_shared<EdgeSplit>(mesh);
161  op_split->add_invariant(invariant_min_edge_length);
162  if (options.lock_boundary && !options.use_for_periodic && !options.dont_disable_split) {
163  op_split->add_invariant(invariant_interior_edge);
164  }
165  for (auto& p : positions) {
166  op_split->set_new_attribute_strategy(
167  p,
168  SplitBasicStrategy::None,
169  SplitRibBasicStrategy::Mean);
170  }
171  for (const auto& attr : pass_through_attributes) {
172  op_split->set_new_attribute_strategy(attr);
173  }
174  assert(op_split->attribute_new_all_configured());
175  ops.push_back(op_split);
176 
177 
178 
180  // collapse
181  wmtk::logger().debug("Configure isotropic remeshing collapse");
182  auto op_collapse = std::make_shared<EdgeCollapse>(mesh);
183  op_collapse->add_invariant(invariant_link_condition);
184  if (position_for_inversion) {
185  op_collapse->add_invariant(std::make_shared<SimplexInversionInvariant<double>>(
186  position_for_inversion.value().mesh(),
187  position_for_inversion.value().as<double>()));
188  }
189 
190  op_collapse->add_invariant(invariant_max_edge_length);
191  op_collapse->add_invariant(invariant_mm_map);
192 
193  // hack for uv
194  if (options.fix_uv_seam) {
195  op_collapse->add_invariant(
196  std::make_shared<invariants::uvEdgeInvariant>(mesh, other_positions.front().mesh()));
197  }
198 
199  if (options.lock_boundary && !options.use_for_periodic) {
200  op_collapse->add_invariant(invariant_interior_edge);
201  // set collapse towards boundary
202  for (auto& p : positions) {
203  auto tmp = std::make_shared<CollapseNewAttributeStrategy<double>>(p);
204  tmp->set_strategy(CollapseBasicStrategy::Mean);
205  tmp->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
206  op_collapse->set_new_attribute_strategy(p, tmp);
207  }
208  } else if (options.use_for_periodic) {
209  op_collapse->add_invariant(
210  std::make_shared<invariants::FusionEdgeInvariant>(mesh, mesh.get_multi_mesh_root()));
211  for (auto& p : positions) {
212  op_collapse->set_new_attribute_strategy(p, CollapseBasicStrategy::Mean);
213  }
214  } else {
215  for (auto& p : positions) {
216  op_collapse->set_new_attribute_strategy(p, CollapseBasicStrategy::Mean);
217  }
218  }
219 
220 
221  for (const auto& attr : pass_through_attributes) {
222  op_collapse->set_new_attribute_strategy(attr);
223  }
224  assert(op_collapse->attribute_new_all_configured());
225  ops.push_back(op_collapse);
226 
227 
229  // swap
230  wmtk::logger().debug("Configure isotropic remeshing swap");
231  auto op_swap = std::make_shared<composite::TriEdgeSwap>(mesh);
232  op_swap->add_invariant(invariant_interior_edge);
233 
234  // hack for uv
235  if (options.fix_uv_seam) {
236  op_swap->add_invariant(
237  std::make_shared<invariants::uvEdgeInvariant>(mesh, other_positions.front().mesh()));
238  }
239 
240  op_swap->add_invariant(invariant_valence_improve);
241  op_swap->collapse().add_invariant(invariant_link_condition);
242  op_swap->collapse().add_invariant(invariant_mm_map);
243  for (auto& p : positions) {
244  op_swap->split().set_new_attribute_strategy(
245  p,
246  SplitBasicStrategy::None,
247  SplitRibBasicStrategy::Mean);
248  }
249  if (position_for_inversion) {
250  op_swap->collapse().add_invariant(std::make_shared<SimplexInversionInvariant<double>>(
251  position_for_inversion.value().mesh(),
252  position_for_inversion.value().as<double>()));
253  }
254 
255  for (auto& p : positions)
256  op_swap->collapse().set_new_attribute_strategy(p, CollapseBasicStrategy::CopyOther);
257  for (const auto& attr : pass_through_attributes) {
258  op_swap->split().set_new_attribute_strategy(attr);
259  op_swap->collapse().set_new_attribute_strategy(attr);
260  }
261  assert(op_swap->split().attribute_new_all_configured());
262  assert(op_swap->collapse().attribute_new_all_configured());
263  ops.push_back(op_swap);
264 
265 
267  // smooth
268  auto op_smooth = std::make_shared<AttributesUpdateWithFunction>(mesh);
269  if (position.dimension() == 3) {
270  op_smooth->set_function(VertexTangentialLaplacianSmooth(position));
271  } else {
272  op_smooth->set_function(VertexLaplacianSmooth(position));
273  }
274 
275  if (options.lock_boundary) {
276  op_smooth->add_invariant(invariant_interior_vertex);
277  }
278 
279  // hack for uv
280  if (options.fix_uv_seam) {
281  op_smooth->add_invariant(
282  std::make_shared<invariants::uvEdgeInvariant>(mesh, other_positions.front().mesh()));
283  }
284 
285  if (position_for_inversion) {
286  op_smooth->add_invariant(std::make_shared<SimplexInversionInvariant<double>>(
287  position_for_inversion.value().mesh(),
288  position_for_inversion.value().as<double>()));
289  }
290 
291  if (update_position) op_smooth->add_transfer_strategy(update_position);
292  ops.push_back(op_smooth);
293 
294 
296  Scheduler scheduler;
297  for (long i = 0; i < options.iterations; ++i) {
298  wmtk::logger().info("Iteration {}", i);
299 
300  SchedulerStats pass_stats;
301  for (size_t j = 0; j < ops.size(); ++j) {
302  const auto& op = ops[j];
303  pass_stats += scheduler.run_operation_on_all(*op);
304  }
305 
307 
308  logger().info(
309  "Executed {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, executing: {}",
310  pass_stats.number_of_performed_operations(),
311  pass_stats.number_of_successful_operations(),
312  pass_stats.number_of_failed_operations(),
313  pass_stats.collecting_time,
314  pass_stats.sorting_time,
315  pass_stats.executing_time);
316 
318  }
319 }
320 } // namespace wmtk::components::isotropic_remeshing
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
Mesh & get_multi_mesh_root()
returns a reference to the root of a multimesh tree
SchedulerStats run_operation_on_all(operations::Operation &op)
Definition: Scheduler.cpp:33
int64_t number_of_failed_operations() const
Returns the number of failed operations performed by the scheduler.
Definition: Scheduler.hpp:23
int64_t number_of_performed_operations() const
Returns the number of performed operations performed by the scheduler.
Definition: Scheduler.hpp:30
int64_t number_of_successful_operations() const
Returns the number of successful operations performed by the scheduler.
Definition: Scheduler.hpp:16
bool is_connectivity_valid() const final override
Definition: TriMesh.cpp:370
double relative_to_absolute_length(const attribute::MeshAttributeHandle &position, const double length_rel)
void isotropic_remeshing(const IsotropicRemeshingOptions &options)
void consolidate(Mesh &mesh)
Definition: consolidate.cpp:13
std::vector< Tuple > vertices(const Mesh &m, const Simplex &simplex)
std::string_view primitive_type_name(PrimitiveType t)
void log_and_throw_error(const std::string &msg)
Definition: Logger.cpp:101
spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:58
std::vector< wmtk::attribute::MeshAttributeHandle > other_position_attributes
std::optional< wmtk::attribute::MeshAttributeHandle > inversion_position_attribute
std::vector< wmtk::attribute::MeshAttributeHandle > pass_through_attributes