3#include <wmtk/optimization/AMIPSEnergy.hpp>
4#include <wmtk/optimization/EnergySum.hpp>
5#include <wmtk/optimization/EnvelopeEnergy.hpp>
6#include <wmtk/optimization/solver.hpp>
8#include <wmtk/Types.hpp>
9#include <wmtk/envelope/Envelope.hpp>
10#include <wmtk/simplex/SimplexCollection.hpp>
11#include <wmtk/utils/Logger.hpp>
12#include <wmtk/utils/TetraQualityUtils.hpp>
20namespace wmtk::optimization {
36 std::atomic<size_t> accepted{0};
47 std::string to_string()
const
50 "accepted {} | rejected: pre-inverted {}, envelope {}, inverted {}, quality {}",
71 double w_amips = 1e-4;
72 double w_envelope = 1.0 - 1e-4;
74 double s_envelope = 1.0;
171bool smooth_vertex_3d(
173 const typename Mesh::Tuple& t,
175 std::unique_ptr<polysolve::nonlinear::Solver>& solver,
178 using Tuple =
typename Mesh::Tuple;
180 const size_t vid = t.vid(m);
181 auto& VA = m.m_vertex_attribute;
182 const auto locs = m.get_one_ring_tets_for_vertex(t);
183 assert(!locs.empty());
185 double max_quality = 0.;
186 for (
const Tuple& tet : locs) {
187 max_quality = std::max(max_quality, m.cell_quality(tet.tid(m)));
188 if (m.is_inverted_f(tet)) {
191 if (counters) ++counters->already_inverted;
198 std::vector<std::array<double, 12>> assembles(locs.size());
199 for (
size_t i = 0; i < locs.size(); ++i) {
200 std::array<size_t, 4> local_verts = m.oriented_tet_vids(locs[i].tid(m));
201 local_verts = wmtk::orient_preserve_tet_reorder(local_verts, vid);
202 for (
int k = 0; k < 4; k++) {
203 for (
int j = 0; j < 3; j++) {
204 assembles[i][k * 3 + j] = VA[local_verts[k]].m_posf[j];
210 solver = create_basic_solver();
213 const double amips_w = opts.w_amips > 0 ? opts.s_amips * opts.w_amips : 1.0;
214 auto amips_energy = std::make_shared<AMIPSEnergy3D>(assembles, amips_w);
215 std::shared_ptr<polysolve::nonlinear::Problem> total_energy = amips_energy;
218 VectorXd x = VA[vid].m_posf;
220 solver->minimize(*total_energy, x);
221 }
catch (
const std::exception&) {
228 const std::shared_ptr<SampleEnvelope> pull_env =
229 VA[vid].m_is_on_surface ? m.smoothing_energy_envelope(vid) :
nullptr;
231 if (pull_env && opts.smoothing_mode == SmoothVertexOptions::SmoothingMode::Projected) {
233 const Vector3d x_orig = VA[vid].m_posf;
234 total_energy = amips_energy;
236 const Vector3d x_new = VA[vid].m_posf;
240 const auto worst_at = [&](
const Vector3d& p) {
242 VA[vid].m_pos = to_rational(p);
244 for (
const Tuple& loc : locs) {
245 if (m.is_inverted(loc)) {
246 return std::numeric_limits<double>::infinity();
248 mq = std::max(mq, m.get_quality(loc));
253 bool accepted =
false;
254 std::vector<Vector3d> interp, proj;
258 const Vector3d p = x_orig + std::pow(0.5, k) * (x_new - x_orig);
260 pull_env->nearest_point(p, q);
263 if (worst_at(q) < max_quality) {
277 for (
size_t k = 0; k < proj.size() && !accepted; ++k) {
278 double lo = 0.0, hi = 1.0;
282 const double mid = 0.5 * (lo + hi);
283 const Vector3d cand = interp[k] + mid * (proj[k] - interp[k]);
284 if (worst_at(cand) < max_quality) {
301 VA[vid].m_posf = x_orig;
302 VA[vid].m_pos = to_rational(x_orig);
303 if (counters) ++counters->quality;
306 }
else if (pull_env) {
307 auto envelope_energy =
308 std::make_shared<ExactDistanceEnergy3D>(pull_env, opts.s_envelope * opts.w_envelope);
311 auto warmup = std::make_shared<EnergySum>();
312 if (opts.w_amips > 0) warmup->add_energy(amips_energy, 1. / opts.w_amips);
313 if (opts.w_envelope > 0) warmup->add_energy(envelope_energy, 1. / opts.w_envelope);
314 total_energy = warmup;
318 auto weighted = std::make_shared<EnergySum>();
319 if (opts.w_amips > 0) weighted->add_energy(amips_energy);
320 if (opts.w_envelope > 0) weighted->add_energy(envelope_energy);
321 total_energy = weighted;
329 const std::shared_ptr<SampleEnvelope> check_env =
330 VA[vid].m_is_on_surface ? m.smoothing_containment_envelope(vid) :
nullptr;
332 const simplex::SimplexCollection surf = m.get_surface_faces_for_vertex(vid);
333 for (
const simplex::Face& f : surf.faces()) {
334 const std::array<Eigen::Vector3d, 3> face = {
335 {VA[f.vertices()[0]].m_posf,
336 VA[f.vertices()[1]].m_posf,
337 VA[f.vertices()[2]].m_posf}};
338 if (check_env->is_outside(face)) {
339 if (counters) ++counters->envelope;
346 VA[vid].m_pos = to_rational(VA[vid].m_posf);
348 double max_after_quality = 0.;
349 for (
const Tuple& loc : locs) {
350 if (m.is_inverted(loc)) {
351 if (counters) ++counters->inverted;
354 const size_t tid = loc.tid(m);
355 const double quality = m.get_quality(loc);
356 m.set_cell_quality(tid, quality);
357 max_after_quality = std::max(max_after_quality, quality);
361 if (max_after_quality > max_quality) {
362 if (counters) ++counters->quality;
367 if (counters) ++counters->accepted;
390bool smooth_vertex_2d(
392 const typename Mesh::Tuple& t,
393 const SmoothVertexOptions& opts,
394 std::unique_ptr<polysolve::nonlinear::Solver>& solver,
395 SmoothRejectCounters* counters =
nullptr)
397 const size_t vid = t.vid(m);
398 auto& VA = m.m_vertex_attribute;
399 auto& FA = m.m_face_attribute;
401 const std::vector<size_t>& locs = m.get_one_ring_fids_for_vertex(t);
402 assert(!locs.empty());
404 double max_quality = 0.;
405 for (
const size_t fid : locs) {
406 max_quality = std::max(max_quality, FA[fid].m_quality);
407 if (m.is_inverted_f(fid)) {
410 if (counters) ++counters->already_inverted;
416 std::vector<std::array<double, 6>> assembles;
417 assembles.reserve(locs.size());
418 for (
const size_t fid : locs) {
419 std::array<size_t, 3> vs = m.oriented_tri_vids(fid);
421 for (
size_t i = 0; i < 3; ++i) {
427 const std::array<size_t, 3> buf = vs;
429 vs[1] = buf[(v_loc + 1) % 3];
430 vs[2] = buf[(v_loc + 2) % 3];
432 std::array<double, 6> T;
433 for (
int i = 0; i < 3; i++) {
434 const Vector2d p = m.smoothing_position(vs[i]);
438 assembles.push_back(T);
442 solver = create_basic_solver();
445 const double amips_w = opts.w_amips > 0 ? opts.s_amips * opts.w_amips : 1.0;
446 auto amips_energy = std::make_shared<AMIPSEnergy2D>(assembles, amips_w);
447 std::shared_ptr<polysolve::nonlinear::Problem> total_energy = amips_energy;
450 VectorXd x = m.smoothing_position(vid);
452 solver->minimize(*total_energy, x);
453 }
catch (
const std::exception&) {
457 m.set_smoothing_position(vid, Vector2d(x));
463 std::vector<Vector2d> surf_neighbors;
464 if (VA[vid].m_is_on_surface) {
465 const simplex::SimplexCollection es = m.get_surface_edges_for_vertex(vid);
466 surf_neighbors.reserve(es.edges().size());
467 for (
const simplex::Edge& e : es.edges()) {
468 const auto& evs = e.vertices();
469 surf_neighbors.push_back(m.smoothing_position(evs[0] != vid ? evs[0] : evs[1]));
471 assert(!surf_neighbors.empty());
474 const std::shared_ptr<SampleEnvelope> envelope =
475 VA[vid].m_is_on_surface ? m.m_envelope :
nullptr;
477 if (envelope && opts.smoothing_mode == SmoothVertexOptions::SmoothingMode::Projected) {
478 const Vector2d x_orig = m.smoothing_position(vid);
479 total_energy = amips_energy;
481 const Vector2d x_new = m.smoothing_position(vid);
485 const auto worst_at = [&](
const Vector2d& p) {
486 m.set_smoothing_position(vid, p);
488 for (
const size_t fid : locs) {
489 if (m.is_inverted(fid)) {
490 return std::numeric_limits<double>::infinity();
492 mq = std::max(mq, m.get_quality(fid));
497 bool accepted =
false;
498 std::vector<Vector2d> interp, proj;
499 interp.reserve(opts.project_line_search_steps);
500 proj.reserve(opts.project_line_search_steps);
501 for (
int k = 0; k < opts.project_line_search_steps; ++k) {
502 const Vector2d p = x_orig + std::pow(0.5, k) * (x_new - x_orig);
504 envelope->nearest_point(p, q);
507 if (worst_at(q) < max_quality) {
520 if (!accepted && opts.project_line_search_nested_steps > 0) {
521 for (
size_t k = 0; k < proj.size() && !accepted; ++k) {
522 double lo = 0.0, hi = 1.0;
525 for (
int j = 0; j < opts.project_line_search_nested_steps; ++j) {
526 const double mid = 0.5 * (lo + hi);
527 const Vector2d cand = interp[k] + mid * (proj[k] - interp[k]);
528 if (worst_at(cand) < max_quality) {
545 m.set_smoothing_position(vid, x_orig);
546 if (counters) ++counters->quality;
549 }
else if (envelope) {
550 auto envelope_energy =
551 std::make_shared<ExactDistanceEnergy2D>(envelope, opts.s_envelope * opts.w_envelope);
553 if (opts.two_stage) {
554 auto warmup = std::make_shared<EnergySum>();
555 if (opts.w_amips > 0) warmup->add_energy(amips_energy, 1. / opts.w_amips);
556 if (opts.w_envelope > 0) warmup->add_energy(envelope_energy, 1. / opts.w_envelope);
557 total_energy = warmup;
561 auto weighted = std::make_shared<EnergySum>();
562 if (opts.w_amips > 0) weighted->add_energy(amips_energy);
563 if (opts.w_envelope > 0) weighted->add_energy(envelope_energy);
564 total_energy = weighted;
574 if (!m.smoothing_position_is_allowed(vid, m.smoothing_position(vid))) {
575 if (counters) ++counters->envelope;
581 const Vector2d p = m.smoothing_position(vid);
582 for (
const Vector2d& q : surf_neighbors) {
583 const std::array<Eigen::Vector2d, 2> edge = {{p, q}};
584 if (envelope->is_outside(edge)) {
585 if (counters) ++counters->envelope;
591 double max_after_quality = 0.;
592 for (
const size_t fid : locs) {
593 if (m.is_inverted(fid)) {
594 if (counters) ++counters->inverted;
597 const double q = m.get_quality(fid);
598 FA[fid].m_quality = q;
599 max_after_quality = std::max(max_after_quality, q);
602 if (!VA[vid].m_is_on_surface || opts.quality_veto_on_surface) {
603 if (max_after_quality > max_quality) {
604 if (counters) ++counters->quality;
609 if (counters) ++counters->accepted;
Why a smoothing attempt was refused, counted per pass.
Definition SmoothVertex.hpp:31
std::atomic< size_t > envelope
a surface triangle left the envelope
Definition SmoothVertex.hpp:33
std::atomic< size_t > already_inverted
incident tet inverted in floats on entry
Definition SmoothVertex.hpp:32
std::atomic< size_t > quality
the move made the worst incident tet worse
Definition SmoothVertex.hpp:35
std::atomic< size_t > inverted
the move inverted a tet (exact predicate)
Definition SmoothVertex.hpp:34
Weights and policy for smooth_vertex_3d.
Definition SmoothVertex.hpp:70
bool quality_veto_on_surface
Definition SmoothVertex.hpp:107
int project_line_search_steps
Definition SmoothVertex.hpp:128
int project_line_search_nested_steps
Definition SmoothVertex.hpp:145
bool two_stage
Definition SmoothVertex.hpp:78
SmoothingMode
Definition SmoothVertex.hpp:123