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