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;
194bool smooth_vertex_3d(
196 const typename Mesh::Tuple& t,
198 std::unique_ptr<polysolve::nonlinear::Solver>& solver,
201 using Tuple =
typename Mesh::Tuple;
203 const size_t vid = t.vid(m);
204 auto& VA = m.m_vertex_attribute;
205 const auto locs = m.get_one_ring_tets_for_vertex(t);
206 assert(!locs.empty());
208 double max_quality = 0.;
209 for (
const Tuple& tet : locs) {
210 max_quality = std::max(max_quality, m.cell_quality(tet.tid(m)));
211 if (m.is_inverted_f(tet)) {
214 if (counters) ++counters->already_inverted;
221 std::vector<std::array<double, 12>> assembles(locs.size());
222 for (
size_t i = 0; i < locs.size(); ++i) {
223 std::array<size_t, 4> local_verts = m.oriented_tet_vids(locs[i].tid(m));
224 local_verts = wmtk::orient_preserve_tet_reorder(local_verts, vid);
225 for (
int k = 0; k < 4; k++) {
226 for (
int j = 0; j < 3; j++) {
227 assembles[i][k * 3 + j] = VA[local_verts[k]].m_posf[j];
233 solver = create_basic_solver();
236 const double amips_w = opts.w_amips > 0 ? opts.s_amips * opts.w_amips : 1.0;
237 auto amips_energy = std::make_shared<AMIPSEnergy3D>(assembles, amips_w);
243 const std::shared_ptr<polysolve::nonlinear::Problem> extra_energy =
244 m.smoothing_extra_energy(vid);
245 std::shared_ptr<polysolve::nonlinear::Problem> base_energy = amips_energy;
247 auto sum = std::make_shared<EnergySum>();
248 if (opts.w_amips > 0) sum->add_energy(amips_energy);
249 sum->add_energy(extra_energy);
252 std::shared_ptr<polysolve::nonlinear::Problem> total_energy = base_energy;
255 VectorXd x = VA[vid].m_posf;
257 solver->minimize(*total_energy, x);
258 }
catch (
const std::exception&) {
265 const std::shared_ptr<SampleEnvelope> pull_env =
266 VA[vid].m_is_on_surface ? m.smoothing_energy_envelope(vid) :
nullptr;
268 if (pull_env && opts.smoothing_mode == SmoothVertexOptions::SmoothingMode::Projected) {
270 const Vector3d x_orig = VA[vid].m_posf;
271 total_energy = base_energy;
273 const Vector3d x_new = VA[vid].m_posf;
277 const auto worst_at = [&](
const Vector3d& p) {
279 VA[vid].m_pos = to_rational(p);
281 for (
const Tuple& loc : locs) {
282 if (m.is_inverted(loc)) {
283 return std::numeric_limits<double>::infinity();
285 mq = std::max(mq, m.get_quality(loc));
290 bool accepted =
false;
291 std::vector<Vector3d> interp, proj;
295 const Vector3d p = x_orig + std::pow(0.5, k) * (x_new - x_orig);
297 pull_env->nearest_point(p, q);
300 if (worst_at(q) < max_quality) {
314 for (
size_t k = 0; k < proj.size() && !accepted; ++k) {
315 double lo = 0.0, hi = 1.0;
319 const double mid = 0.5 * (lo + hi);
320 const Vector3d cand = interp[k] + mid * (proj[k] - interp[k]);
321 if (worst_at(cand) < max_quality) {
338 VA[vid].m_posf = x_orig;
339 VA[vid].m_pos = to_rational(x_orig);
340 if (counters) ++counters->quality;
343 }
else if (pull_env) {
344 auto envelope_energy =
345 std::make_shared<ExactDistanceEnergy3D>(pull_env, opts.s_envelope * opts.w_envelope);
348 auto warmup = std::make_shared<EnergySum>();
349 if (opts.w_amips > 0) warmup->add_energy(amips_energy, 1. / opts.w_amips);
350 if (opts.w_envelope > 0) warmup->add_energy(envelope_energy, 1. / opts.w_envelope);
351 if (extra_energy) warmup->add_energy(extra_energy);
352 total_energy = warmup;
356 auto weighted = std::make_shared<EnergySum>();
357 if (opts.w_amips > 0) weighted->add_energy(amips_energy);
358 if (opts.w_envelope > 0) weighted->add_energy(envelope_energy);
359 if (extra_energy) weighted->add_energy(extra_energy);
360 total_energy = weighted;
368 const std::shared_ptr<SampleEnvelope> check_env =
369 VA[vid].m_is_on_surface ? m.smoothing_containment_envelope(vid) :
nullptr;
371 const simplex::SimplexCollection surf = m.get_surface_faces_for_vertex(vid);
372 for (
const simplex::Face& f : surf.faces()) {
373 const std::array<Eigen::Vector3d, 3> face = {
374 {VA[f.vertices()[0]].m_posf,
375 VA[f.vertices()[1]].m_posf,
376 VA[f.vertices()[2]].m_posf}};
377 if (check_env->is_outside(face)) {
378 if (counters) ++counters->envelope;
385 VA[vid].m_pos = to_rational(VA[vid].m_posf);
387 double max_after_quality = 0.;
388 for (
const Tuple& loc : locs) {
389 if (m.is_inverted(loc)) {
390 if (counters) ++counters->inverted;
393 const size_t tid = loc.tid(m);
394 const double quality = m.get_quality(loc);
395 m.set_cell_quality(tid, quality);
396 max_after_quality = std::max(max_after_quality, quality);
400 if (max_after_quality > max_quality) {
401 if (counters) ++counters->quality;
406 if (counters) ++counters->accepted;
429bool smooth_vertex_2d(
431 const typename Mesh::Tuple& t,
432 const SmoothVertexOptions& opts,
433 std::unique_ptr<polysolve::nonlinear::Solver>& solver,
434 SmoothRejectCounters* counters =
nullptr)
436 const size_t vid = t.vid(m);
437 auto& VA = m.m_vertex_attribute;
438 auto& FA = m.m_face_attribute;
440 const std::vector<size_t>& locs = m.get_one_ring_fids_for_vertex(t);
441 assert(!locs.empty());
443 double max_quality = 0.;
444 for (
const size_t fid : locs) {
445 max_quality = std::max(max_quality, FA[fid].m_quality);
446 if (m.is_inverted_f(fid)) {
449 if (counters) ++counters->already_inverted;
455 std::vector<std::array<double, 6>> assembles;
456 assembles.reserve(locs.size());
457 for (
const size_t fid : locs) {
458 std::array<size_t, 3> vs = m.oriented_tri_vids(fid);
460 for (
size_t i = 0; i < 3; ++i) {
466 const std::array<size_t, 3> buf = vs;
468 vs[1] = buf[(v_loc + 1) % 3];
469 vs[2] = buf[(v_loc + 2) % 3];
471 std::array<double, 6> T;
472 for (
int i = 0; i < 3; i++) {
473 const Vector2d p = m.smoothing_position(vs[i]);
477 assembles.push_back(T);
481 solver = create_basic_solver();
484 const double amips_w = opts.w_amips > 0 ? opts.s_amips * opts.w_amips : 1.0;
485 auto amips_energy = std::make_shared<AMIPSEnergy2D>(assembles, amips_w);
491 const std::shared_ptr<polysolve::nonlinear::Problem> extra_energy =
492 m.smoothing_extra_energy(vid);
493 std::shared_ptr<polysolve::nonlinear::Problem> base_energy = amips_energy;
495 auto sum = std::make_shared<EnergySum>();
496 if (opts.w_amips > 0) sum->add_energy(amips_energy);
497 sum->add_energy(extra_energy);
500 std::shared_ptr<polysolve::nonlinear::Problem> total_energy = base_energy;
503 VectorXd x = m.smoothing_position(vid);
505 solver->minimize(*total_energy, x);
506 }
catch (
const std::exception&) {
510 m.set_smoothing_position(vid, Vector2d(x));
516 std::vector<Vector2d> surf_neighbors;
517 if (VA[vid].m_is_on_surface) {
518 const simplex::SimplexCollection es = m.get_surface_edges_for_vertex(vid);
519 surf_neighbors.reserve(es.edges().size());
520 for (
const simplex::Edge& e : es.edges()) {
521 const auto& evs = e.vertices();
522 surf_neighbors.push_back(m.smoothing_position(evs[0] != vid ? evs[0] : evs[1]));
524 assert(!surf_neighbors.empty());
530 const std::shared_ptr<SampleEnvelope> envelope = m.smoothing_energy_envelope(vid);
532 if (envelope && opts.smoothing_mode == SmoothVertexOptions::SmoothingMode::Projected) {
533 const Vector2d x_orig = m.smoothing_position(vid);
534 total_energy = base_energy;
536 const Vector2d x_new = m.smoothing_position(vid);
540 const auto worst_at = [&](
const Vector2d& p) {
541 m.set_smoothing_position(vid, p);
543 for (
const size_t fid : locs) {
544 if (m.is_inverted(fid)) {
545 return std::numeric_limits<double>::infinity();
547 mq = std::max(mq, m.get_quality(fid));
552 bool accepted =
false;
553 std::vector<Vector2d> interp, proj;
554 interp.reserve(opts.project_line_search_steps);
555 proj.reserve(opts.project_line_search_steps);
556 for (
int k = 0; k < opts.project_line_search_steps; ++k) {
557 const Vector2d p = x_orig + std::pow(0.5, k) * (x_new - x_orig);
559 envelope->nearest_point(p, q);
562 if (worst_at(q) < max_quality) {
575 if (!accepted && opts.project_line_search_nested_steps > 0) {
576 for (
size_t k = 0; k < proj.size() && !accepted; ++k) {
577 double lo = 0.0, hi = 1.0;
580 for (
int j = 0; j < opts.project_line_search_nested_steps; ++j) {
581 const double mid = 0.5 * (lo + hi);
582 const Vector2d cand = interp[k] + mid * (proj[k] - interp[k]);
583 if (worst_at(cand) < max_quality) {
600 m.set_smoothing_position(vid, x_orig);
601 if (counters) ++counters->quality;
604 }
else if (envelope) {
605 auto envelope_energy =
606 std::make_shared<ExactDistanceEnergy2D>(envelope, opts.s_envelope * opts.w_envelope);
608 if (opts.two_stage) {
609 auto warmup = std::make_shared<EnergySum>();
610 if (opts.w_amips > 0) warmup->add_energy(amips_energy, 1. / opts.w_amips);
611 if (opts.w_envelope > 0) warmup->add_energy(envelope_energy, 1. / opts.w_envelope);
612 if (extra_energy) warmup->add_energy(extra_energy);
613 total_energy = warmup;
617 auto weighted = std::make_shared<EnergySum>();
618 if (opts.w_amips > 0) weighted->add_energy(amips_energy);
619 if (opts.w_envelope > 0) weighted->add_energy(envelope_energy);
620 if (extra_energy) weighted->add_energy(extra_energy);
621 total_energy = weighted;
631 if (!m.smoothing_position_is_allowed(vid, m.smoothing_position(vid))) {
632 if (counters) ++counters->envelope;
638 if (
const std::shared_ptr<SampleEnvelope> hold_env = m.smoothing_containment_envelope(vid)) {
639 const Vector2d p = m.smoothing_position(vid);
640 for (
const Vector2d& q : surf_neighbors) {
641 const std::array<Eigen::Vector2d, 2> edge = {{p, q}};
642 if (hold_env->is_outside(edge)) {
643 if (counters) ++counters->envelope;
649 double max_after_quality = 0.;
650 for (
const size_t fid : locs) {
651 if (m.is_inverted(fid)) {
652 if (counters) ++counters->inverted;
655 const double q = m.get_quality(fid);
656 FA[fid].m_quality = q;
657 max_after_quality = std::max(max_after_quality, q);
660 if (opts.quality_veto && (!VA[vid].m_is_on_surface || opts.quality_veto_on_surface)) {
661 if (max_after_quality > max_quality) {
662 if (counters) ++counters->quality;
667 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:165
bool quality_veto
Definition SmoothVertex.hpp:147
bool two_stage
Definition SmoothVertex.hpp:78
SmoothingMode
Definition SmoothVertex.hpp:123