Wildmeshing Toolkit
Loading...
Searching...
No Matches
TopoOffsetTriMesh.h
1#pragma once
2
3#include <wmtk/TriMesh.h>
4#include <wmtk/TriOptimizerMesh.h>
5#include <algorithm>
6#include <array>
7#include <atomic>
8#include <cmath>
9#include <cstdint>
10#include <functional>
11#include <map>
12#include <mutex>
13#include <set>
14#include <wmtk/MeshData.hpp>
15#include <wmtk/envelope/CompositeEnvelopes.hpp>
16#include <wmtk/optimization/EnergySum.hpp>
17#include <wmtk/optimization/solver.hpp>
18#include <wmtk/threading/enumerable_thread_specific.hpp>
19#include "OffsetPotential.hpp"
20#include "Parameters.h"
21#include "SimplicialComplexBVH.hpp"
22
23using CellTag = std::set<int64_t>;
24
25namespace wmtk::components::topological_offset {
26
27
28const int64_t TEMP_OFFSET_TRI_TAG = -1;
29const CellTag TEMP_OFFSET_TRI_TAG_SET{TEMP_OFFSET_TRI_TAG};
30
40{
41public:
42 int label = 0;
43 bool m_is_on_input = false; // on the input complex
44 bool m_is_on_offset = false; // on the offset boundary itself
45 bool m_is_on_region = false; // on some OTHER tag region's boundary
46
55 uint64_t m_boundary_mask = 0;
56
61 int64_t m_corr_input_vid = -1;
62
66 uint32_t m_born_epoch = 0;
67};
68
69
73{
74public:
75 int label = 0;
79 bool on_curve = false;
80};
81
82
86{
87public:
88 int label = 0;
96 bool rest_valid = false;
97 std::array<Eigen::Vector2d, 3> rest_pos;
98};
99
100
114{
115public: // mode for splitting in marching tets
116 enum class EdgeSplitMode {
117 Midpoint = 0, // construction: simplicial embedding AND marching_tris
118 SphereTrace = 1, // marching_tris (construction_mode): sphere tracing along the edge to
119 // d(x) = m_construction_distance, midpoint when the trace leaves the
120 // edge
121 Optimization = 2 // the optimization phase; the shared engine places the vertex
122 };
123
124public:
125 std::array<size_t, 3> m_init_counts = {{0, 0, 0}};
126 size_t m_tags_count;
130 int64_t m_curve_tag = -1;
132 MatrixXd m_curve_V;
133 MatrixXi m_curve_E;
161 std::shared_ptr<SimplicialComplexBVH> m_input_complex_bvh;
162
172 std::shared_ptr<OffsetPotential2D> m_offset_potential;
173
203 int m_n_regions = 0;
204 std::vector<std::shared_ptr<OffsetPotential2D>> m_region_potentials;
205 std::vector<int64_t> m_phi_vert_region;
206 std::vector<int64_t> m_phi_seg_region;
207 std::vector<int64_t> m_phi_face_region;
208 std::vector<int64_t> m_phi_point_region;
209 std::vector<int> m_face_region;
210 std::vector<int> m_vertex_region;
211 void init_region_potentials(double delta, double effective_factor);
214 void assign_band_regions(bool log = true);
216 void log_front_profile(size_t vid);
217 int vertex_region(const size_t vid) const
218 {
219 return vid < m_vertex_region.size() ? m_vertex_region[vid] : -1;
220 }
221 int edge_region(const size_t va, const size_t vb) const
222 {
223 const int a = vertex_region(va), b = vertex_region(vb);
224 return (a >= 0 && a == b) ? a : -1;
225 }
226 const OffsetPotential2D& potential_for_region(const int region) const
227 {
228 return (region >= 0 && size_t(region) < m_region_potentials.size())
229 ? *m_region_potentials[size_t(region)]
231 }
232 const OffsetPotential2D& potential_for(const size_t vid) const
233 {
234 return potential_for_region(vertex_region(vid));
235 }
238 std::shared_ptr<const OffsetPotential2D> potential_ptr_for(const size_t vid) const
239 {
240 const int r = vertex_region(vid);
241 return (r >= 0 && size_t(r) < m_region_potentials.size()) ? m_region_potentials[size_t(r)]
243 }
244 const OffsetPotential2D& potential_for_edge(const size_t va, const size_t vb) const
245 {
246 return potential_for_region(edge_region(va, vb));
247 }
248 const OffsetPotential2D& potential_for_face(const size_t fid) const
249 {
250 return potential_for_region(fid < m_face_region.size() ? m_face_region[fid] : -1);
251 }
252
269 std::map<int64_t, std::shared_ptr<Envelope>> m_tag_envelopes;
270
273 std::map<int64_t, int> m_tag_bit;
274
277 mutable std::map<uint64_t, std::shared_ptr<Envelope>> m_isect_cache;
278 mutable std::mutex m_isect_mutex;
279
289 mutable std::map<uint64_t, std::shared_ptr<Envelope>> m_offset_isect_cache;
290
304 std::shared_ptr<Envelope> containment_for(uint64_t region_mask, bool on_offset) const;
305
307 bool m_freeze_front = false;
308
327 enum class EnvelopeSetup { PerTag, WallComplex };
328 EnvelopeSetup envelope_setup() const
329 {
330 // deform_others was removed on the 3D-only branch spenbert/offsets-cleanup (2026-10-13);
331 // its default, false, is hard-wired here until the 2D port.
332 return /* deform_others */ false ? EnvelopeSetup::WallComplex : EnvelopeSetup::PerTag;
333 }
334 static constexpr int64_t m_wall_tag = -2;
335 static constexpr int64_t m_complex_tag = -3;
337 std::string envelope_key_name(int64_t tag) const;
340 bool edge_is_complex_boundary(const Tuple& e) const;
346 void build_boundary_envelopes(const char* when, EnvelopeSetup setup);
347
355 std::shared_ptr<Envelope> m_offset_envelope;
356
363
366 void check_no_vertex_on_both_surfaces(const char* when) const;
367
373 bool final_stage = true,
374 bool refine = true,
375 const std::string& label = std::string());
376
379 bool m_init_optimize = false;
381 std::string m_frame_prefix;
382
385 double front_gradient_linf();
388
389 EdgeSplitMode m_edge_split_mode = EdgeSplitMode::Midpoint;
390
391 // tag name maps
392 std::map<std::string, int64_t> m_tag_name_to_id;
393 std::map<int64_t, std::string> m_tag_id_to_name;
394 CellTag m_offset_output_tag_ids;
395
396 // if in 'singlebody' mode
397 bool m_singlebody = false;
398 int64_t m_single_tag;
399
400 // just for retaining in output. dont actually use
401 bool m_has_envelope = false;
402 MatrixXd m_V_envelope;
403 MatrixXi m_F_envelope;
404
416 static constexpr int INPUT_SURFACE_CLASS = 0;
417 static constexpr int OFFSET_SURFACE_CLASS = 1;
418
421
425 // m_vertex_attribute, m_edge_attribute and m_face_attribute are the base's; these three
426 // are registered alongside them in its attribute groups.
427 VertexExtraCol m_vertex_extra;
428 EdgeExtraCol m_edge_extra;
429 FaceExtraCol m_face_extra;
430
431 TopoOffsetTriMesh(Parameters& _m_offset_params, int _num_threads = 0)
432 : wmtk::TriOptimizerMesh(_m_offset_params)
433 , m_offset_params(_m_offset_params)
434 {
435 NUM_THREADS = _num_threads;
436 m_vertex_attr_group.add(&m_vertex_extra);
437 m_edge_attr_group.add(&m_edge_extra);
438 m_face_attr_group.add(&m_face_extra);
439
440 // As in 3D. The per-vertex Newton solver logs a line per smoothing attempt at info level,
441 // which is one line per vertex per pass and buries the run's own output.
442 optimization::deactivate_opt_logger();
443 }
444
445 ~TopoOffsetTriMesh() override = default;
446
474 double tri_energy(size_t fid) const;
476 double tri_energy(size_t fid, double amips) const;
480 double smoothing_cell_energy(const size_t fid, const double quality) const override
481 {
482 return tri_energy(fid, quality);
483 }
486 double weighted_amips(const double amips) const
487 {
488 return amips >= MAX_ENERGY ? amips : m_offset_params.offset_amips_weight * amips;
489 }
491 double max_tri_energy(const std::vector<size_t>& fids) const;
495 double offset_term_weight() const
496 {
497 const double f = m_offset_params.front_conv_frac();
498 return 1. / (f * f);
499 }
500
507 void set_vertex_position(const size_t vid, const Vector2d& p)
508 {
509 m_vertex_attribute[vid].m_posf = p;
510 m_vertex_attribute[vid].set_pos_to_posf();
511 m_vertex_attribute[vid].m_is_rounded = true;
512 }
513
516 bool edge_is_offset(const size_t eid) const
517 {
518 return m_edge_attribute[eid].m_is_surface_fs &&
519 m_edge_attribute[eid].m_surface_class == OFFSET_SURFACE_CLASS;
520 }
524 bool edge_is_region(const size_t eid) const
525 {
526 return m_edge_attribute[eid].m_is_surface_fs &&
527 m_edge_attribute[eid].m_surface_class != OFFSET_SURFACE_CLASS;
528 }
536 {
537 EdgeAttributes tags;
538 EdgeExtra2d extra;
539 };
541 {
542 FaceAttributes attrs;
543 FaceExtra2d extra;
544 };
545 EdgeSnapshot2d edge_snapshot(const size_t eid) const
546 {
547 return EdgeSnapshot2d{m_edge_attribute[eid], m_edge_extra[eid]};
548 }
549 void restore_edge(const size_t eid, const EdgeSnapshot2d& s)
550 {
551 m_edge_attribute[eid] = s.tags;
552 m_edge_extra[eid] = s.extra;
553 }
554 FaceSnapshot2d face_snapshot(const size_t fid) const
555 {
556 return FaceSnapshot2d{m_face_attribute[fid], m_face_extra[fid]};
557 }
558 void restore_face(const size_t fid, const FaceSnapshot2d& s)
559 {
560 m_face_attribute[fid] = s.attrs;
561 m_face_extra[fid] = s.extra;
562 }
563
573
583 bool face_in_region(const size_t fid) const;
584
587 bool face_is_input_complex(const size_t fid) const;
588
598 bool vertex_is_on_surface(const size_t vid) const override;
599 bool edge_is_on_surface(const std::array<size_t, 2>& vids) const override;
600
602 void optimize_offset(const std::filesystem::path& output_file);
603
607 bool face_is_offset_band(const size_t fid) const;
608
609
618 std::pair<double, double> compute_distance_deviation() const;
619
622 mutable size_t m_worst_dist_vid = static_cast<size_t>(-1);
623 void log_worst_dist_vertex() const;
624
627 bool edge_is_offset_surface_live(const Tuple& e) const;
628
634 std::vector<std::array<double, 8>> optimization_metrics;
637 int m_round = 0;
639 mutable size_t m_debug_seq = 0;
643 mutable std::vector<std::string> m_debug_frame_labels;
644 mutable std::map<std::string, std::vector<size_t>> m_debug_pvd_series;
649 void write_debug_pvd() const;
652 mutable int m_debug_pass = 0;
653 mutable int m_debug_last_round = -1;
654 mutable char m_debug_last_tag = '?';
655 mutable std::string m_debug_last_prefix = "?";
659 bool m_converged = false;
664 double m_quality_max_amips = 0.;
665
666 std::atomic<int> iter_cnt_split = 0, iter_cnt_collapse = 0, iter_cnt_swap = 0;
667 std::atomic<int> iter_cnt_collapse_offset_removed{0};
670 std::atomic<int> iter_cnt_swap_offset_reject{0};
673 mutable std::atomic<int> iter_cnt_collapse_energy_reject{0}; // both halves count here
674 mutable std::atomic<int> iter_cnt_swap_energy_reject{0};
677 std::atomic<int> iter_cnt_split_offset_before{0};
678 std::atomic<int> iter_cnt_split_offset{0};
684 {
685 std::map<size_t, int> face_label;
686 size_t v1_id = 0;
687 size_t v2_id = 0;
693 uint64_t edge_bits = 0;
696 double parent_q_max = -1.;
699 double parent_flatness = 1.;
700 };
702
703 bool marching_split_edge_before(const Tuple& t);
704 bool marching_split_edge_after(const Tuple& t);
721 const Vector2d& p_in,
722 const Vector2d& p_out,
723 Vector2d& p_new,
724 size_t& steps) const;
728 size_t m_marching_root_splits = 0, m_marching_midpoint_splits = 0;
729 size_t m_marching_trace_steps = 0, m_marching_trace_steps_max = 0;
733
745 bool collapse_edge_before(const Tuple& t) override;
757 bool collapse_edge_after(const Tuple& t) override;
758
767 bool swap_edge_before(const Tuple& t) override;
768
772 bool swap_edge_after(const Tuple& t) override;
773 bool collapse_before_vertex(size_t v1, size_t v2) override;
774 void collapse_after_vertex(size_t v1, size_t v2) override;
775 void split_after_vertex(size_t v_new) override;
776
792 bool split_adjust_position(size_t v_new, const std::vector<Tuple>& children) override;
793
794 bool smooth_before(const Tuple& t) override;
795 bool smooth_after(const Tuple& t) override;
796
814 bool vertex_is_on_domain_boundary(const size_t vid) const
815 {
816 return !m_vertex_attribute[vid].on_bbox_faces.empty();
817 }
818
834
839
853
863 {
864 std::atomic<int> attempted{0};
865 std::atomic<int> before_bbox{0};
866 std::atomic<int> before_unrounded{0};
867 std::atomic<int> offset_attempted{0};
868 std::atomic<int> offset_accepted{0};
869 std::atomic<int> interior_attempted{0};
870 std::atomic<int> region_attempted{0};
871
872 void reset()
873 {
874 for (std::atomic<int>* c :
875 {&attempted,
882 c->store(0);
883 }
884 }
885 };
886 SmoothTrace m_smooth_trace;
887
899 optimization::NewtonCounters m_newton_front;
907 {
908 size_t vid;
909 int iterations;
910 int status;
911 };
912 std::vector<FrontSolveRecord> m_front_solve_log;
913 std::mutex m_front_solve_log_mutex;
918 static constexpr double kSmoothRelGradNormTol = 1e-6;
921 polysolve::nonlinear::Solver& smoothing_solver();
928 const Tuple& t,
931 bool* solved = nullptr);
935 std::atomic<size_t> m_front_veto_asked{0}, m_front_veto_fired{0};
939 std::shared_ptr<const OffsetPotential2D> m_repulsion_potential;
943 std::atomic<size_t> m_repulsion_veto_asked{0}, m_repulsion_veto_fired{0};
945 bool is_marched_edge(const size_t a, const size_t b) const
946 {
947 return (m_vertex_extra[a].label == 0) != (m_vertex_extra[b].label == 0);
948 }
952 bool is_repulsion_vertex(const size_t vid) const
953 {
954 if (!m_repulsion_potential || m_vertex_extra[vid].label != 0 ||
955 vertex_boundary_mask(vid) != 0) {
956 return false;
957 }
958 for (const size_t u : get_one_ring_vids_for_vertex_duplicate(vid)) {
959 if (m_vertex_extra[u].label != 0) return true;
960 }
961 return false;
962 }
968 std::shared_ptr<OffsetEnergy2D> m_repulsion_term;
969 double repulsion_term_at(const size_t vid) const
970 {
971 Eigen::VectorXd x = m_vertex_attribute[vid].m_posf;
972 return m_repulsion_term->value(x);
973 }
980 double repulsion_cell_term(const std::array<size_t, 3>& vids) const
981 {
982 if (!m_repulsion_potential) return 0.;
983 bool has_complex = false;
984 for (const size_t v : vids) has_complex = has_complex || m_vertex_extra[v].label != 0;
985 if (!has_complex) return 0.;
986 double e = 0.;
987 for (const size_t v : vids) {
988 if (m_vertex_extra[v].label == 0 && vertex_boundary_mask(v) == 0) {
989 e += repulsion_term_at(v);
990 }
991 }
992 return e;
993 }
996 size_t repulsion_cells_at(const size_t vid) const
997 {
998 size_t n = 0;
999 for (const size_t fid : get_one_ring_fids_for_vertex(vid)) {
1000 for (const size_t u : oriented_tri_vids(fid)) {
1001 if (m_vertex_extra[u].label != 0) {
1002 ++n;
1003 break;
1004 }
1005 }
1006 }
1007 return n;
1008 }
1010 std::shared_ptr<OffsetEnergy2D> repulsion_energy(const size_t vid) const
1011 {
1012 return std::make_shared<OffsetEnergy2D>(
1014 double(repulsion_cells_at(vid)) * 4. * offset_term_weight(),
1015 true,
1016 true,
1017 /*one_sided=*/true);
1018 }
1024 bool repulsion_embedding_kept(const std::vector<size_t>& fids) const;
1029 bool simplex_in_input_complex(size_t a, size_t b) const;
1030 std::atomic<size_t> m_repulsion_embed_refused{0};
1034 static constexpr int kGradBins = 22;
1035 std::array<std::atomic<size_t>, kGradBins> m_front_grad_abs{}, m_front_grad_rel{};
1036 optimization::NewtonCounters m_newton_plastic;
1037
1044 std::vector<double> m_cross_ring;
1045 std::vector<Vector2d> m_cross_pos;
1046 bool m_cross_valid = false;
1049 std::atomic<size_t> m_cross_own{0}, m_cross_neighbour{0};
1051 std::vector<double> front_ring_measures() const;
1053 double ring_measure_at(size_t vid) const;
1057 void crossing_snapshot(const std::string& pass, bool compare, bool match_positions);
1060 void update_attributes() override;
1061
1078 mutable std::atomic<size_t> m_needle_smooth_offered{0};
1079 mutable std::atomic<size_t> m_needle_smooth_reached{0};
1080 mutable std::atomic<size_t> m_needle_smooth_fixed{0};
1081 mutable std::atomic<size_t> m_needle_smooth_stationary{0};
1083 mutable std::atomic<size_t> m_needle_smooth_reports{0};
1084
1086 double ring_max_quality(size_t vid) const;
1087
1096 double face_flatness(size_t fid) const;
1097
1108 void needle_forensics() const;
1109
1111 void record_flatness(const char* op, double parent_flat, size_t child_fid) const;
1112 mutable std::atomic<size_t> m_flat_created_split{0};
1113 mutable std::atomic<size_t> m_flat_created_collapse{0};
1114 mutable std::atomic<size_t> m_flat_worsened_split{0};
1115 mutable std::atomic<size_t> m_flat_genesis_reports{0};
1116 static constexpr double kFlatThreshold = 1e-3;
1132 {
1133 double ring1_max = 0.;
1134 std::vector<std::pair<size_t, double>> v2_only;
1135 };
1156 std::vector<Tuple> offset_surface_edges_live_at(size_t vid) const;
1159 std::vector<std::array<size_t, 2>> offset_surface_edges() const;
1161 bool vertex_has_live_offset_edge(size_t vid) const;
1170 void refresh_offset_membership(size_t vid);
1173 std::pair<size_t, size_t> offset_membership_mismatches() const;
1176 void check_offset_membership(const char* when) const;
1177
1197 std::vector<char> offset_surface_foldover_labels() const;
1198 void log_smooth_trace() const;
1199
1200
1210 void log_region_edge_mask_health(const std::string& when) const;
1211
1228 void audit_surface_containment(const std::string& when) const;
1229
1231
1239 bool vertex_is_on_region(const size_t vid) const
1240 {
1241 return m_vertex_extra[vid].m_is_on_region || !m_vertex_attribute[vid].on_bbox_faces.empty();
1242 }
1243
1246 uint64_t tag_bits(const CellTag& tags) const
1247 {
1248 uint64_t bits = 0;
1249 for (const int64_t t : tags) {
1250 const auto it = m_tag_bit.find(t);
1251 if (it != m_tag_bit.end()) bits |= (uint64_t(1) << it->second);
1252 }
1253 return bits;
1254 }
1255
1266 uint64_t vertex_boundary_mask(const size_t vid) const
1267 {
1268 return vertex_is_on_region(vid) ? m_vertex_extra[vid].m_boundary_mask : uint64_t(0);
1269 }
1270
1273 uint64_t edge_mask(const std::array<size_t, 2>& vids) const
1274 {
1275 return vertex_boundary_mask(vids[0]) & vertex_boundary_mask(vids[1]);
1276 }
1277
1290 uint64_t edge_boundary_bits(const Tuple& e) const
1291 {
1292 const std::optional<Tuple> opp = e.switch_face(*this);
1293 if (!opp) {
1294 return tag_bits(m_face_attribute[e.fid(*this)].tags); // domain wall
1295 }
1296 const auto& t0 = m_face_attribute[e.fid(*this)].tags;
1297 const auto& t1 = m_face_attribute[opp->fid(*this)].tags;
1298 CellTag diff;
1299 std::set_symmetric_difference(
1300 t0.begin(),
1301 t0.end(),
1302 t1.begin(),
1303 t1.end(),
1304 std::inserter(diff, diff.begin()));
1305 return tag_bits(diff);
1306 }
1307
1317 std::shared_ptr<Envelope> envelope_for_mask(uint64_t mask) const;
1318
1332 std::shared_ptr<Envelope> surface_envelope_for_edge(
1333 const std::array<size_t, 2>& vids,
1334 const EdgeAttributes& /*attr*/) const override
1335 {
1336 // Boundary geometry first: a segment on any tag-region boundary may not
1337 // drift out of that boundary's tube, and one on several boundaries -- a junction -- is
1338 // held in their intersection. The mask carries the input complex too, since every complex
1339 // simplex lies on tag boundaries, and E_t is built from the input mesh before construction
1340 // touches it, so the per-tag tubes hold the as-loaded geometry.
1341 //
1342 // Keyed on the vertices, not the edge: every caller is an operation asking about a segment
1343 // it is about to create or has just created, whose own edge attributes are not written
1344 // yet. The endpoints' masks are, maintained by the operations themselves (AND at a split,
1345 // OR at a collapse).
1346 uint64_t mask = edge_mask(vids);
1347 bool all_offset = true;
1348 for (const size_t v : vids) {
1349 all_offset = all_offset && m_vertex_extra[v].m_is_on_offset;
1350 }
1351
1352 // The ambiguous case: both endpoints can be on region boundaries and on the offset front
1353 // at once -- a real state, and the point of tracking the two families separately. The
1354 // endpoint-mask AND is then necessary but not sufficient for the segment lying on a shared
1355 // boundary: two vertices on different junctions can share a tag bit by coincidence and be
1356 // joined by an offset chord nowhere near that tag's curve, which a container it was never
1357 // meant to satisfy then refuses to refine. Ask the edge's own class, the only record that
1358 // distinguishes a chord from a boundary.
1359 //
1360 // Reading the slot is safe here and would not be unconditionally: the base checks a
1361 // split's two child segments before writing their attributes, so their slots still hold a
1362 // recycled edge's class. A split child never reaches this branch -- its mask and its
1363 // offset flag are mutually exclusive by construction, so one of `mask` and `all_offset` is
1364 // always empty -- and the m_is_surface_fs guard leaves both constraints standing rather
1365 // than dropping one when a slot is illegible.
1366 if (mask != 0 && all_offset) {
1367 if (const auto found = try_tuple_from_edge(vids)) {
1368 const size_t eid = std::get<1>(*found);
1369 if (m_edge_attribute[eid].m_is_surface_fs) {
1370 if (edge_is_offset(eid)) {
1371 mask = 0; // an offset edge lies on no region boundary
1372 } else {
1373 all_offset = false; // a region edge is not the offset front
1374 }
1375 }
1376 }
1377 }
1378 // Both families compose: whatever holds this segment holds it at once. The offset side
1379 // holds only in the frozen-front final pass (see containment_for()); in the loop the
1380 // front is what the optimization moves, so it contributes nothing there.
1381 const std::shared_ptr<Envelope> base = containment_for(mask, all_offset);
1382 if (base || m_deform_tags.empty()) return base;
1383 // deform_others' ops-only tube: a released boundary is held by no mask -- its vertices
1384 // were freed so smoothing can carry the object -- which would leave the operations free
1385 // to decimate and reposition it. A segment the masks and the offset class do not claim,
1386 // but which lies on a released boundary by its incident faces' current tags, is held to
1387 // the tube around the boundary's current shape. A fresh split child can misread its
1388 // recycled face slots for this one check, and is at worst skipped or over-held once.
1389 if (const auto found = try_tuple_from_edge(vids)) {
1390 if (edge_borders_released_boundary(std::get<0>(*found))) return released_envelope();
1391 }
1392 return nullptr;
1393 }
1394
1407 bool smoothing_position_is_allowed(const size_t, const Vector2d&) const override
1408 {
1409 return true;
1410 }
1411
1425 std::shared_ptr<Envelope> smoothing_energy_envelope(const size_t vid) const override
1426 {
1427 if (m_vertex_extra[vid].m_is_on_offset && !vertex_is_on_region(vid)) {
1428 return nullptr;
1429 }
1430 const uint64_t mask = vertex_boundary_mask(vid);
1431 if (mask == 0) {
1432 // Reachable only for a construction artefact (a wall-chord midpoint flagged
1433 // on-surface with disjoint endpoint masks); its containment is vacuous too, so no
1434 // pull is behavior-neutral. Not an error.
1435 return nullptr;
1436 }
1437 std::shared_ptr<Envelope> best;
1438 double worst_d2 = -1.;
1439 for (const auto& [tag, env] : m_tag_envelopes) {
1440 const auto it = m_tag_bit.find(tag);
1441 if (it == m_tag_bit.end() || !(mask & (uint64_t(1) << it->second))) continue;
1442 if (!best) {
1443 best = env;
1444 if ((mask & (mask - 1)) == 0) break; // single bit: no violation contest to run
1445 worst_d2 = env->squared_distance(m_vertex_attribute[vid].m_posf);
1446 continue;
1447 }
1448 const double d2 = env->squared_distance(m_vertex_attribute[vid].m_posf);
1449 if (d2 > worst_d2) {
1450 worst_d2 = d2;
1451 best = env;
1452 }
1453 }
1454 return best;
1455 }
1456
1461 std::shared_ptr<Envelope> smoothing_containment_envelope(const size_t vid) const
1462 {
1463 return containment_for(vertex_boundary_mask(vid), /*on_offset=*/false);
1464 }
1467 std::shared_ptr<Envelope> smoothing_containment_envelope(
1468 const size_t vid,
1469 const std::array<size_t, 2>& /*edge*/,
1470 const EdgeAttributes& /*attr*/) const override
1471 {
1473 }
1474
1479 bool smooth_front_vertex(const Tuple& t);
1487 bool smooth_repulsion_vertex(const Tuple& t);
1496 bool smooth_nonfront_vertex(const Tuple& t);
1500 std::shared_ptr<polysolve::nonlinear::Problem> amips_energy(size_t vid) const;
1503 double front_vertex_normal_gradient(size_t vid) const;
1506 Vector2d front_vertex_move_direction(size_t vid) const;
1510 double front_move_alignment(size_t vid) const;
1513 double front_vertex_conv_ratio(size_t vid) const;
1521 bool front_placed_by_ratio(const double ratio) const
1522 {
1523 return std::isfinite(ratio) && ratio <= 1.;
1524 }
1540 double edge_offset_term(size_t a, size_t b) const;
1541 double edge_offset_term(const OffsetPotential2D& pot, const Vector2d& pa, const Vector2d& pb)
1542 const;
1544 Vector2d front_vertex_normal(size_t vid) const;
1550 std::shared_ptr<polysolve::nonlinear::Problem> front_objective(size_t vid) const;
1553 bool vertex_carries_offset_term(const size_t vid) const
1554 {
1555 return !m_freeze_front && m_offset_potential && m_vertex_extra[vid].m_is_on_offset &&
1556 vertex_boundary_mask(vid) == 0;
1557 }
1560 double smoother_amips_weight(const size_t vid) const
1561 {
1563 return m_params.w_amips > 0 ? m_s_amips * m_params.w_amips : 1.0;
1564 }
1574 std::shared_ptr<polysolve::nonlinear::Problem> smoothing_extra_energy(
1575 const size_t vid) const override
1576 {
1577 auto sum = std::make_shared<optimization::EnergySum>();
1578 sum->add_energy(amips_energy(vid));
1579 // Before the march (repulsion_smoothing()) there is no front and nothing is plastic.
1580 if (is_repulsion_vertex(vid)) {
1581 sum->add_energy(repulsion_energy(vid));
1582 return sum;
1583 }
1584 if (vertex_carries_offset_term(vid)) {
1585 sum->add_energy(front_energy(vid, potential_ptr_for(vid)));
1586 }
1587 if (const auto rest = rest_energy_for_vertex(vid)) sum->add_energy(rest);
1588 return sum;
1589 }
1590
1591 // ------- deform_others: other input regions deform instead of being envelope-held -------
1592
1594 std::set<int64_t> m_deform_tags;
1597 std::set<int64_t> m_source_tags;
1604 mutable std::shared_ptr<Envelope> m_released_envelope;
1605 mutable std::atomic<bool> m_released_tube_dirty{false};
1606 mutable std::mutex m_released_mutex;
1609 std::shared_ptr<Envelope> released_envelope() const;
1612 bool edge_borders_released_boundary(const Tuple& e) const;
1614 bool face_is_deformable(size_t fid) const;
1620 bool m_plastic_active = false;
1621 bool face_is_plastic(size_t fid) const
1622 {
1623 // Everything outside the band: ambient, the other objects and the input complex's
1624 // interior alike -- one material. The complex's boundary is what its tube holds.
1625 return m_plastic_active && m_face_extra[fid].label != 2;
1626 }
1628 void stamp_plastic_rests();
1631 bool smooth_plastic_vertex(const Tuple& t);
1635 bool face_is_released_band(size_t fid) const;
1638 void stamp_rest_face(size_t fid);
1646 std::shared_ptr<polysolve::nonlinear::Problem> rest_energy_for_vertex(size_t vid) const;
1650 std::shared_ptr<polysolve::nonlinear::Problem> front_energy(
1651 size_t vid,
1652 const std::shared_ptr<const OffsetPotential2D>& pot) const;
1653
1655 int stencil_order() const { return m_offset_params.stencil_order; }
1661 {
1662 const int k = m_offset_params.stencil_order;
1663 if (k < 0) return 0;
1664 if (k == 0) return 2;
1665 return 2 * (1 << (k - 1)) + 1;
1666 }
1667
1678 void check_offset_within_support(const char* when) const;
1679
1693 {
1694 double max_reachable = 0., avg_reachable = 0.;
1695 double max_pinned = 0.;
1696 size_t n_reachable = 0, n_pinned = 0;
1701 double max_at_vertex = 0., max_in_edge = 0.;
1706 size_t worst_outside_vid = static_cast<size_t>(-1);
1707 double worst_outside_dist = 0.;
1708 };
1709 DistanceSplit distance_deviation_split() const;
1710
1716 std::vector<bool> band_vertex_mask() const;
1717
1720 double max_band_vertex_distance() const;
1723 double band_vertex_distance_error(const size_t vid) const;
1724
1727 double band_vertex_residual(const size_t vid) const;
1728
1732 {
1733 double max = 0.;
1734 double sum = 0.;
1735 size_t n = 0;
1736 };
1737
1755 template <typename Visit>
1756 void for_each_edge_sample(const Vector2d& p0, const Vector2d& p1, Visit&& visit) const
1757 {
1758 const int k = m_offset_params.stencil_order;
1759 if (k < 0) return;
1760
1761 const auto emit = [&](const double wa, const double wb) {
1762 visit(Vector2d(wa * p0 + wb * p1), wa, wb);
1763 };
1764 // Order 0 is the two ENDS alone, each end's own placement error.
1765 if (k == 0) {
1766 emit(1., 0.);
1767 emit(0., 1.);
1768 return;
1769 }
1770 // Order k >= 1: the vertices of the chord cut into n = 2^(k-1) pieces, then the midpoint
1771 // of each piece -- 2n + 1 points: 3, 5, 9, 17, ...
1772 const int n = 1 << (k - 1);
1773 const double dn = double(n);
1774 for (int i = n; i >= 0; --i) {
1775 emit(double(i) / dn, double(n - i) / dn);
1776 }
1777 const double d2n = 2. * dn;
1778 for (int i = n - 1; i >= 0; --i) {
1779 emit((2. * i + 1.) / d2n, (2. * (n - 1 - i) + 1.) / d2n);
1780 }
1781 }
1782
1784 template <typename Visit>
1785 void for_each_offset_edge_sample(const Tuple& e, Visit&& visit) const
1786 {
1788 m_vertex_attribute[e.vid(*this)].m_posf,
1789 m_vertex_attribute[e.switch_vertex(*this).vid(*this)].m_posf,
1790 std::forward<Visit>(visit));
1791 }
1792
1795 EdgeSamples offset_edge_samples(const Tuple& e) const;
1796
1814 {
1815 double max_reachable = 0., avg_reachable = 0.;
1816 double max_pinned = 0.;
1817 size_t n_reachable = 0, n_pinned = 0;
1822 double max_at_vertex = 0., max_in_edge = 0.;
1824 size_t n_edge_samples = 0;
1827 size_t n_skipped_inverted = 0, n_skipped_unrounded = 0;
1828 };
1831 GradientSplit gradient_split(bool include_edge_samples = true) const;
1832
1838 {
1839 double max_vertex = 0., max_edge = 0.;
1843 double sum_vertex = 0., sum_edge = 0.;
1844 double bar = 1.;
1845 size_t n_vertices = 0, n_edges = 0, n_unmeasurable = 0;
1846 size_t worst_vid = static_cast<size_t>(-1);
1849 size_t n_edges_over = 0, n_edges_over_placed = 0;
1850 double max_edge_placed = 0.;
1851 Vector2d worst_placed_mid = Vector2d::Zero();
1852 double tube = 0.;
1858 size_t n_at_floor = 0;
1859 double max_edge_at_floor = 0.;
1860 Vector2d worst_at_floor_mid = Vector2d::Zero();
1868 Vector2d worst_corners_at_floor_mid = Vector2d::Zero();
1870 double floor_scalar = 0.;
1871 bool floor_from_min_edge_length = false;
1872 size_t n_unplaced = 0;
1876 {
1877 size_t a, b;
1878 double measure, len;
1879 };
1880 std::vector<Refinable> refinable;
1893 bool ring_exit = false;
1894 static const char* ring_name() { return "ring measure"; }
1895 double max_ring = 0., sum_ring = 0.;
1896 size_t n_rings = 0, n_rings_unmeasurable = 0;
1897 size_t worst_ring_vid = static_cast<size_t>(-1);
1898 size_t n_rings_over = 0;
1901 std::vector<size_t> refinable_vertices;
1905 double max_ring_at_floor = 0.;
1906 Vector2d worst_ring_at_floor_pos = Vector2d::Zero();
1907 bool rings_ok() const { return max_ring <= bar; }
1908 double avg_ring() const { return n_rings ? sum_ring / double(n_rings) : 0.; }
1913 bool vertices_ok() const { return n_unplaced == 0; }
1914 bool edges_ok() const { return max_edge <= bar; }
1921 bool converged() const
1922 {
1923 return (ring_exit ? rings_ok() : edges_ok()) && n_unmeasurable == 0;
1924 }
1930 std::string sizing_floor_fact() const;
1931 double ratio() const { return bar > 0. ? std::max(max_vertex, max_edge) / bar : 0.; }
1933 double avg_vertex() const { return n_vertices ? sum_vertex / double(n_vertices) : 0.; }
1934 double avg_edge() const { return n_edges ? sum_edge / double(n_edges) : 0.; }
1935 };
1936 EnergyCriterion energy_criterion();
1942 double front_chord_target(size_t va, size_t vb, double len, double sag, double tube) const;
1943
1947 size_t refine_front_by_halving(const std::vector<EnergyCriterion::Refinable>& edges);
1952 size_t refine_front_by_halving(const std::vector<size_t>& vertices);
1953
1958 void grade_sizing(double grade, const std::vector<size_t>& seeds);
1964 size_t grade_sizing_by_distance(const std::vector<size_t>& seeds);
1965
1969 {
1971 double front_max_ratio = 0.;
1972 size_t front_worst_vid = static_cast<size_t>(-1);
1973 size_t n_front = 0;
1975 double front_max_step = 0.;
1979 size_t background_worst_vid = static_cast<size_t>(-1);
1980 size_t n_background = 0;
1981 };
1983 SmoothingProgress smoothing_progress(const std::vector<Vector2d>& before);
1989 void smooth_group_to_convergence(const char* group_name);
1992 std::optional<EnergyCriterion> m_energy_verdict;
1993
1994
1997 void report_outside_support(const char* when, const DistanceSplit& s) const;
2005 bool band_vertex_is_reachable(const size_t vid) const
2006 {
2007 // An envelope-held offset vertex is pinned: it must stay within envelope_size of the input
2008 // region boundary AND sit on Phi = c, target_distance away. Those are not simultaneously
2009 // satisfiable, so its residual gradient is a statement about the constraint rather than
2010 // about the offset's quality, and leaving it in max_reachable makes convergence impossible
2011 // by construction. Structural, not left to whether the tolerance happens to hide it.
2012 //
2013 // Still reported, never silent: the pinned half of every band measure is logged beside the
2014 // reachable half, the same contract vertex_is_on_domain_boundary() gets below. This is a
2015 // retreat, to be reverted the moment a placement that works exists.
2016 if (m_vertex_extra[vid].m_is_on_offset && vertex_boundary_mask(vid) != 0) return false;
2017
2018 return !vertex_is_on_domain_boundary(vid);
2019 }
2020
2032 size_t refine_sizing_around_worst(double max_metric) override;
2033
2055 void log_stuck_refine_census(double max_metric, double filter_energy);
2056
2090 void log_refine_block_census(const std::string& when, double filter_energy) const;
2091
2104 bool collapse_quality_allowed(size_t v1, size_t /*v2*/, double q, double /*ring_max*/)
2105 const override
2106 {
2107 if (!m_offset_params.offset_collapse_veto || !m_vertex_attribute.at(v1).m_is_rounded) {
2108 return true;
2109 }
2110 if (weighted_amips(q) <= m_collapse_energy_before.local()) return true;
2112 return false;
2113 }
2114
2125 bool swap_quality_allowed(double after, double /*before*/, bool) const override
2126 {
2127 if (!m_offset_params.offset_swap_veto) return true;
2128 if (weighted_amips(after) < m_swap_energy_before.local()) return true;
2129 ++iter_cnt_swap_energy_reject;
2130 return false;
2131 }
2132
2133 mutable std::atomic<size_t> m_deg_split_created{0};
2134 size_t m_deg_prev_split_created = 0;
2135
2148 void report_needle(const char* op, size_t fid, double parent_q) const;
2149 static constexpr size_t kNeedleReports = 12;
2158 static constexpr double kNeedleQuality = 1e6;
2159 mutable std::atomic<size_t> m_needle_reports{0};
2160
2163 void needle_scan(const char* when) const;
2166 void collapse_pass_begin() override;
2167
2170 std::set<std::pair<long, long>> m_stuck_prev_cells;
2171 size_t m_stuck_calls = 0;
2172
2176 bool optimization_bare_coarsen_passes() const override { return false; }
2177
2182 double face_criterion_rel(const size_t fid) const;
2189 void write_smoothing_debug_output(const std::string& path) const override
2190 {
2191 const char ph = m_freeze_front ? 'F' : 'S'; // the final pass, or the loop
2192 if (m_round != m_debug_last_round || ph != m_debug_last_tag ||
2193 m_frame_prefix != m_debug_last_prefix) {
2194 m_debug_last_round = m_round;
2195 m_debug_last_tag = ph;
2196 m_debug_last_prefix = m_frame_prefix;
2197 m_debug_pass = 0;
2198 }
2199 std::string label = path;
2200 // The prefix leads, since the loop after the init_optimize loop restarts at turn 1.
2201 const std::string& g = m_frame_prefix;
2202 if (path.rfind("debug_", 0) == 0) {
2203 label = fmt::format(
2204 "{}r{}{}{}{}",
2205 g,
2206 m_round,
2207 ph,
2208 ++m_debug_pass,
2209 m_debug_pass_name.empty() ? std::string() : "_" + m_debug_pass_name);
2210 } else if (path.rfind("end_", 0) == 0) {
2211 label = fmt::format("{}r{}{}_end", g, m_round, ph);
2212 }
2213 const_cast<TopoOffsetTriMesh*>(this)->write_debug_frame(label);
2214 }
2221 void optimization_debug_checkpoint() override;
2226 void append_frame_label(size_t idx, const std::string& label) const;
2236 void write_debug_frame(const std::string& label);
2237
2246 void init_from_image(
2247 const MatrixXd& V,
2248 const MatrixXi& F,
2249 const MatrixSi& F_tags,
2250 const MatrixXd& V_env,
2251 const MatrixXi& F_env,
2252 const std::vector<std::string>& tag_names,
2253 const std::string& curve_name = "");
2254
2258 bool ambient_assert();
2259
2263 void label_input_complex();
2264
2269 bool empty_input_complex();
2270
2280
2288 void init_offset_potential();
2289
2292 MatrixXd m_phi_V;
2293 MatrixXi m_phi_E;
2294 MatrixXi m_phi_F;
2295 std::vector<int> m_phi_P;
2296
2298 bool split_edge_before(const Tuple& t) override;
2299 bool split_edge_after(const Tuple& t) override;
2300 bool split_face_before(const Tuple& t) override;
2301 bool split_face_after(const Tuple& t) override;
2302 bool invariants(const std::vector<Tuple>& tris) override;
2304
2308 void construct_offset(const std::filesystem::path& output_file);
2309
2316 void marching_tris();
2317
2322 void repulsion_smoothing();
2325 void relabel_input_complex();
2326
2329 size_t flood_fill();
2331 std::vector<size_t> connected_components_helper(const size_t v_id) const
2332 {
2333 std::vector<size_t> onering_v_ids = get_one_ring_vids_for_vertex_duplicate(v_id);
2334 wmtk::vector_unique(onering_v_ids);
2335 std::vector<size_t> ret_v_ids;
2336 for (const size_t other_v_id : onering_v_ids) {
2337 if (other_v_id == v_id) continue;
2338 const size_t e_id = std::get<1>(tuple_from_edge({{v_id, other_v_id}}));
2339 if (m_edge_extra[e_id].label != 0) { // edge labelled 1 or 2
2340 ret_v_ids.push_back(other_v_id);
2341 }
2342 }
2343 return ret_v_ids;
2344 }
2345
2346
2348
2352 bool is_simplicially_embedded() const;
2353
2358 bool tri_is_simp_emb(const Tuple& t) const;
2359
2363 void simplicial_embedding();
2365
2370 void set_offset_tri_tags();
2371
2376 bool offset_is_manifold();
2377
2379
2390 void write_phi_grid(const std::string& path, int n) const;
2391
2392 void write_input_complex(const std::string& path);
2393 void write_vtu(const std::string& path, MeshMap* output = nullptr);
2394 // void write_msh(const std::string& file);
2396
2397private:
2404 {
2405 size_t v1_id;
2406 size_t v2_id;
2407 Vector2d new_v_pos;
2408 VertexExtra2d new_v_extra;
2409
2410 // cache edge attributes
2411 EdgeSnapshot2d split_eattr;
2412 std::map<simplex::Edge, EdgeSnapshot2d> existing_eattr;
2413
2414 // cache face attributes
2415 std::map<size_t, FaceSnapshot2d> opp_v_fattr;
2416 };
2418
2420 {
2421 size_t v1_id;
2422 size_t v2_id;
2423 size_t v3_id;
2424 Vector2d new_v_pos;
2425 VertexExtra2d new_v_extra;
2426
2427 std::map<simplex::Edge, EdgeSnapshot2d> existing_eattr; // 3 orig edges
2428 FaceSnapshot2d split_fattr; // split face attributes
2429 };
2431
2432private: // helpers
2436 void sort_edges_by_length(std::vector<simplex::Edge>& edges)
2437 {
2438 std::sort(
2439 edges.begin(),
2440 edges.end(),
2441 [this](const simplex::Edge& e1, const simplex::Edge& e2) {
2442 double len1 = (m_vertex_attribute[e1.vertices()[0]].m_posf -
2443 m_vertex_attribute[e1.vertices()[1]].m_posf)
2444 .squaredNorm();
2445 double len2 = (m_vertex_attribute[e2.vertices()[0]].m_posf -
2446 m_vertex_attribute[e2.vertices()[1]].m_posf)
2447 .squaredNorm();
2448 return len1 > len2;
2449 });
2450 }
2451
2452public: // helpers
2457 {
2458 const auto& verts = e.vertices();
2459 const auto incident = simplex_incident_triangles(e);
2460 const auto& faces = incident.faces();
2461
2462 assert(!faces.empty()); // throw error here otherwise
2463
2464 const size_t f_id = tuple_from_simplex(faces.front()).fid(*this);
2465 const Tuple t_edge = tuple_from_edge(verts[0], verts[1], f_id);
2466 return t_edge.eid(*this);
2467 }
2468
2473 {
2474 const auto& v = e.vertices();
2475 const auto faces = simplex_incident_triangles(e).faces();
2476 assert(!faces.empty());
2477 const size_t fid = tuple_from_simplex(faces.front()).fid(*this);
2478 return tuple_from_edge(v[0], v[1], fid);
2479 }
2480};
2481
2482
2483} // namespace wmtk::components::topological_offset
Definition SurfaceTagAttributes.h:24
Definition TriMesh.h:36
size_t fid(const TriMesh &) const
Definition TriMesh.h:84
std::array< size_t, 3 > oriented_tri_vids(const Tuple &t) const
Get the incident vertices for a triangle.
Definition TriMesh.cpp:2286
std::vector< size_t > get_one_ring_vids_for_vertex_duplicate(const size_t &t) const
Get the vids of the incident one ring tris for a vertex.
Definition TriMesh.cpp:2137
std::optional< std::tuple< Tuple, size_t > > try_tuple_from_edge(const std::array< size_t, 2 > &vids) const
tuple_from_edge for callers where a missing edge is an answer, not a bug.
Definition TriMesh.cpp:2324
Tuple tuple_from_edge(size_t vid1, size_t vid2, size_t fid) const
Definition TriMesh.cpp:2484
What triwild and simwild's 2D mesh share.
Definition TriOptimizerMesh.h:42
AttributeContainerGroup m_edge_attr_group
What p_edge_attrs points at, so a derived class can register more.
Definition TriOptimizerMesh.h:150
AttributeContainerGroup m_vertex_attr_group
What p_vertex_attrs points at, so a derived class can register more.
Definition TriOptimizerMesh.h:135
static constexpr double MAX_ENERGY
The sentinel get_quality returns for a face AMIPS2D cannot score.
Definition TriOptimizerMesh.h:171
AttributeContainerGroup m_face_attr_group
What p_face_attrs points at, so a derived class can register more.
Definition TriOptimizerMesh.h:161
OptimizerParameters & m_params
Definition TriOptimizerMesh.h:175
std::string m_debug_pass_name
Definition TriOptimizerMesh.h:197
double m_s_amips
Definition TriOptimizerMesh.h:182
Definition TopoOffsetTriMesh.h:73
bool on_curve
Definition TopoOffsetTriMesh.h:79
Definition TopoOffsetTriMesh.h:86
bool rest_valid
Definition TopoOffsetTriMesh.h:96
The offset potential: the scalar field on space whose level set the front is placed on.
Definition OffsetPotential.hpp:42
The offset's 2D mesh, on the shared 2D optimizer.
Definition TopoOffsetTriMesh.h:114
bool is_repulsion_vertex(const size_t vid) const
Definition TopoOffsetTriMesh.h:952
void refresh_offset_membership(size_t vid)
Re-derive m_is_on_offset for one vertex from the face labels, exactly: a vertex is on the offset boun...
Definition Optimize2d.cpp:3607
void rebuild_offset_envelope()
Definition Optimize2d.cpp:3840
std::pair< double, double > compute_distance_deviation() const
How far the offset boundary is from where it should be: {max, avg} over vertices.
Definition Optimize2d.cpp:3273
void stamp_plastic_rests()
Stamp rest := current for every plastic face; called before every operation group.
Definition Optimize2d.cpp:304
void stamp_rest_face(size_t fid)
Definition Optimize2d.cpp:295
int m_n_regions
One field per connected piece of the input complex, and which one each band vertex is placed on.
Definition TopoOffsetTriMesh.h:203
size_t flood_fill()
Definition TopoOffsetTriMesh.cpp:998
std::vector< size_t > connected_components_helper(const size_t v_id) const
All one-ring vertices through input / offset edges (labelled 1 or 2).
Definition TopoOffsetTriMesh.h:2331
int m_debug_pass
Definition TopoOffsetTriMesh.h:652
void label_input_complex()
label input complex simplices as per boolean expression (or single body mode)
Definition TopoOffsetTriMesh.cpp:403
void set_vertex_position(const size_t vid, const Vector2d &p)
Place a vertex, keeping its exact and rounded coordinates in step.
Definition TopoOffsetTriMesh.h:507
std::string envelope_key_name(int64_t tag) const
The name a tag or pseudo-tag prints under.
Definition TopoOffsetTriMesh.cpp:211
void assign_band_regions(bool log=true)
Definition Optimize2d.cpp:2824
Parameters & m_offset_params
The base holds only wmtk::OptimizerParameters; this is the same object, typed.
Definition TopoOffsetTriMesh.h:420
void optimize_offset_loop(bool final_stage=true, bool refine=true, const std::string &label=std::string())
Definition Optimize2d.cpp:4016
double band_vertex_distance_error(const size_t vid) const
Definition Optimize2d.cpp:2290
void write_smoothing_debug_output(const std::string &path) const override
Put the optimization's frames on the run's single debug timeline (see write_debug_frame()),...
Definition TopoOffsetTriMesh.h:2189
bool swap_quality_allowed(double after, double, bool) const override
The energy rule's EARLY half for a swap, on the engine's lower bound.
Definition TopoOffsetTriMesh.h:2125
std::atomic< size_t > m_front_veto_asked
Definition TopoOffsetTriMesh.h:935
polysolve::nonlinear::Solver & smoothing_solver()
Definition Optimize2d.cpp:869
int stencil_order() const
Samples per front chord; see for_each_edge_sample().
Definition TopoOffsetTriMesh.h:1655
double front_gradient_linf()
Definition Optimize2d.cpp:2703
void repulsion_smoothing()
Definition TopoOffsetTriMesh.cpp:1036
void log_front_profile(size_t vid)
Diagnostic: the front objective of one vertex along its normal, offset term vs total.
Definition Optimize2d.cpp:2931
void audit_surface_containment(const std::string &when) const
Which tracked edges are outside their envelope, and by how much.
Definition Optimize2d.cpp:1232
std::shared_ptr< OffsetEnergy2D > m_repulsion_term
Definition TopoOffsetTriMesh.h:968
void needle_scan(const char *when) const
Definition Optimize2d.cpp:1999
void log_stuck_refine_census(double max_metric, double filter_energy)
Why the final pass is stuck: a census of the faces stuck-refine is about to chase.
Definition Optimize2d.cpp:1758
std::shared_ptr< polysolve::nonlinear::Problem > smoothing_extra_energy(const size_t vid) const override
Definition TopoOffsetTriMesh.h:1574
bool split_face_before(const Tuple &t) override
User specified preparations and desideratas for a face split.
Definition EdgeSplittingTri.cpp:270
SmoothingProgress smoothing_progress(const std::vector< Vector2d > &before)
Measure a pass: before holds every live vertex's position before it, indexed by vid.
Definition Optimize2d.cpp:3047
std::set< int64_t > m_source_tags
Definition TopoOffsetTriMesh.h:1597
Vector2d front_vertex_move_direction(size_t vid) const
Definition FrontSmooth2d.cpp:196
std::vector< std::array< size_t, 2 > > offset_surface_edges() const
Definition Optimize2d.cpp:3556
void set_offset_tri_tags()
update 'tags' data for triangles in the offset region (tris labelled 2) based on the given offset tag...
Definition TopoOffsetTriMesh.cpp:1579
size_t refine_front_by_halving(const std::vector< EnergyCriterion::Refinable > &edges)
Definition Optimize2d.cpp:3012
std::map< int64_t, int > m_tag_bit
Definition TopoOffsetTriMesh.h:273
std::pair< size_t, size_t > offset_membership_mismatches() const
Definition Optimize2d.cpp:3612
bool vertex_is_on_surface(const size_t vid) const override
The substructure the link condition is evaluated against, derived not cached.
Definition Optimize2d.cpp:45
std::vector< int64_t > m_phi_point_region
per m_phi_P entry: region index, -1 unknown
Definition TopoOffsetTriMesh.h:208
bool band_vertex_is_reachable(const size_t vid) const
Definition TopoOffsetTriMesh.h:2005
wmtk::threading::enumerable_thread_specific< double > m_swap_energy_before
Definition TopoOffsetTriMesh.h:1153
bool offset_is_manifold()
verify that the closed offset region (simplices labelled 1 or 2) form a manifold region....
Definition TopoOffsetTriMesh.cpp:1614
void report_needle(const char *op, size_t fid, double parent_q) const
Where the first needles come from – a tripwire, not a census.
Definition Optimize2d.cpp:1946
void optimize_offset(const std::filesystem::path &output_file)
The 2D optimization phase: split / collapse / swap / smooth on the shared driver.
Definition Optimize2d.cpp:4384
void report_outside_support(const char *when, const DistanceSplit &s) const
Definition Optimize2d.cpp:3252
GradientSplit gradient_split(bool include_edge_samples=true) const
Definition Optimize2d.cpp:2377
std::vector< int64_t > m_phi_vert_region
per m_phi_V row: region index
Definition TopoOffsetTriMesh.h:205
size_t m_worst_dist_vid
Definition TopoOffsetTriMesh.h:622
std::atomic< int > iter_cnt_split_offset_before
Definition TopoOffsetTriMesh.h:677
bool front_placed_by_ratio(const double ratio) const
THE definition of "placed" for a vertex on the offset boundary, applied to its already-measured front...
Definition TopoOffsetTriMesh.h:1521
double max_tri_energy(const std::vector< size_t > &fids) const
Max of tri_energy() over fids (0 for none): the number every rule above compares.
Definition Optimize2d.cpp:2817
size_t m_debug_seq
Monotonic frame counter for the debug timeline. Mutable because the write hook is const.
Definition TopoOffsetTriMesh.h:639
std::shared_ptr< Envelope > smoothing_containment_envelope(const size_t vid, const std::array< size_t, 2 > &, const EdgeAttributes &) const override
Definition TopoOffsetTriMesh.h:1467
bool m_init_optimize
Definition TopoOffsetTriMesh.h:379
std::shared_ptr< Envelope > m_offset_envelope
The tube the offset boundary may not leave during the frozen-front final pass, of half-width offset_e...
Definition TopoOffsetTriMesh.h:355
void construct_offset(const std::filesystem::path &output_file)
Definition TopoOffsetTriMesh.cpp:958
double offset_term_weight() const
Definition TopoOffsetTriMesh.h:495
bool collapse_quality_allowed(size_t v1, size_t, double q, double) const override
The energy rule's EARLY half, on the engine's lower bound.
Definition TopoOffsetTriMesh.h:2104
wmtk::threading::enumerable_thread_specific< double > m_collapse_energy_before
Definition TopoOffsetTriMesh.h:1124
EdgeSamples offset_edge_samples(const Tuple &e) const
Definition Optimize2d.cpp:2306
bool empty_input_complex()
check if the input complex is empty. Only valid after calling init_from_image(...)....
Definition TopoOffsetTriMesh.cpp:585
std::shared_ptr< Envelope > released_envelope() const
Definition Optimize2d.cpp:3779
std::shared_ptr< Envelope > m_released_envelope
Definition TopoOffsetTriMesh.h:1604
void build_boundary_envelopes(const char *when, EnvelopeSetup setup)
Definition TopoOffsetTriMesh.cpp:231
bool smooth_plastic_vertex(const Tuple &t)
Definition Optimize2d.cpp:320
std::shared_ptr< SimplicialComplexBVH > m_input_complex_bvh
The input complex as loaded. Built once, never rebuilt.
Definition TopoOffsetTriMesh.h:161
void init_input_complex_bvh()
Build the input complex's BVH and its smooth offset potential, from one extraction.
Definition TopoOffsetTriMesh.cpp:598
bool face_is_deformable(size_t fid) const
Under deform_others the same set as face_is_plastic(): every face outside the band.
Definition Optimize2d.cpp:269
double front_chord_target(size_t va, size_t vb, double len, double sag, double tube) const
Definition Optimize2d.cpp:2969
bool smooth_repulsion_vertex(const Tuple &t)
A repulsion vertex in the passes and rounds before the march (repulsion_smoothing()): the shared smoo...
Definition FrontSmooth2d.cpp:142
size_t edge_id_from_simplex(const simplex::Edge &e) const
get global id of edge from simplex::Edge object
Definition TopoOffsetTriMesh.h:2456
std::atomic< int > iter_cnt_collapse_offset_reject
Operations refused because they would have left an offset-boundary chord over tolerance.
Definition TopoOffsetTriMesh.h:669
void log_refine_block_census(const std::string &when, double filter_energy) const
For every element above filter_energy, why its edges cannot be split.
Definition Optimize2d.cpp:1580
bool face_is_offset_band(const size_t fid) const
Definition Optimize2d.cpp:2247
double tri_energy(size_t fid) const
THE per-cell energy, read from the mesh as it is – labels, neighbours and positions,...
Definition Optimize2d.cpp:2783
Tuple get_tuple_from_edge(const simplex::Edge &e) const
get Tuple simplex::Edge object
Definition TopoOffsetTriMesh.h:2472
std::vector< int > m_vertex_region
per vertex: region of its band faces, -1 / -2 as above
Definition TopoOffsetTriMesh.h:210
void check_no_vertex_on_both_surfaces(const char *when) const
Definition Optimize2d.cpp:3701
void marching_tris()
Definition TopoOffsetTriMesh.cpp:1407
uint64_t tag_bits(const CellTag &tags) const
Definition TopoOffsetTriMesh.h:1246
void write_debug_pvd() const
Definition Optimize2d.cpp:3977
size_t repulsion_cells_at(const size_t vid) const
Definition TopoOffsetTriMesh.h:996
void optimization_debug_checkpoint() override
Called by the engine at every smoothing pass boundary (and only there in this component's loop): the ...
Definition Optimize2d.cpp:1223
double smoothing_cell_energy(const size_t fid, const double quality) const override
Definition TopoOffsetTriMesh.h:480
void classify_curve_edges()
Mark the mesh edges that lie on the input's curve group (EdgeExtra2d::on_curve).
Definition TopoOffsetTriMesh.cpp:367
double face_flatness(size_t fid) const
Scale-invariant flatness: 2*area / longest_edge^2.
Definition Optimize2d.cpp:2058
static constexpr double kSmoothRelGradNormTol
Definition TopoOffsetTriMesh.h:918
bool repulsion_embedding_kept(const std::vector< size_t > &fids) const
Definition TopoOffsetTriMesh.cpp:1279
bool face_in_region(const size_t fid) const
Whether face fid belongs to the closed offset region, read from its label.
Definition Optimize2d.cpp:55
void sort_edges_by_length(std::vector< simplex::Edge > &edges)
sort vector of edge simplices in place by decreasing length
Definition TopoOffsetTriMesh.h:2436
bool swap_edge_after(const Tuple &t) override
Definition Optimize2d.cpp:451
bool m_freeze_front
The final pass: front vertices are not smoothed (see smooth_before()).
Definition TopoOffsetTriMesh.h:307
uint64_t vertex_boundary_mask(const size_t vid) const
The tag boundaries this vertex lies on – the raw mask gated on the vertex still being region geometry...
Definition TopoOffsetTriMesh.h:1266
double front_vertex_normal_gradient(size_t vid) const
Definition Optimize2d.cpp:1049
double m_construction_distance
Definition TopoOffsetTriMesh.h:732
double max_band_vertex_distance() const
Definition TopoOffsetTriMesh.cpp:934
MatrixXi m_phi_F
the complex faces, in the same vertex index space (for per-region BVHs)
Definition TopoOffsetTriMesh.h:2294
DistanceSplit residual_split() const
Definition Optimize2d.cpp:2322
void write_debug_frame(const std::string &label)
One frame of the run's single debug timeline: <output>_NNNNN.vtu with the next sequence number,...
Definition Optimize2d.cpp:3951
std::vector< bool > band_vertex_mask() const
Definition Optimize2d.cpp:2253
optimization::NewtonCounters m_newton
Definition TopoOffsetTriMesh.h:898
void for_each_offset_edge_sample(const Tuple &e, Visit &&visit) const
The same lattice over a chord the mesh carries. Visitor signature as above.
Definition TopoOffsetTriMesh.h:1785
std::set< int64_t > m_deform_tags
The released tags. Filled by release_deformable_regions(); empty = feature inactive.
Definition TopoOffsetTriMesh.h:1594
bool m_converged
Definition TopoOffsetTriMesh.h:659
void refresh_released_envelope()
Definition Optimize2d.cpp:3832
double weighted_amips(const double amips) const
Definition TopoOffsetTriMesh.h:486
void warn_if_offset_reaches_domain_boundary() const
Warn if the offset band has grown into the domain boundary.
Definition Optimize2d.cpp:165
bool edge_is_offset_surface_live(const Tuple &e) const
Definition Optimize2d.cpp:3535
bool split_edge_after(const Tuple &t) override
User specified modifications and desideratas after an edge split.
Definition EdgeSplittingTri.cpp:189
uint64_t edge_boundary_bits(const Tuple &e) const
Diagnostic only: which tag boundaries the incident faces say this edge lies on right now – the same s...
Definition TopoOffsetTriMesh.h:1290
bool vertex_is_on_domain_boundary(const size_t vid) const
Identification only – no operation refuses the domain wall through these.
Definition TopoOffsetTriMesh.h:814
std::vector< std::string > m_debug_frame_labels
Definition TopoOffsetTriMesh.h:643
int m_round
Definition TopoOffsetTriMesh.h:637
std::optional< EnergyCriterion > m_energy_verdict
Definition TopoOffsetTriMesh.h:1992
bool invariants(const std::vector< Tuple > &tris) override
User specified invariants that can't be violated.
Definition TopoOffsetTriMesh.cpp:1664
double face_criterion_rel(const size_t fid) const
Definition Optimize2d.cpp:3319
void check_offset_membership(const char *when) const
Definition Optimize2d.cpp:3625
std::shared_ptr< const OffsetPotential2D > potential_ptr_for(const size_t vid) const
Definition TopoOffsetTriMesh.h:238
std::vector< char > offset_surface_foldover_labels() const
Per-vertex 0/1: has the offset curve folded back on itself at this vertex? Debug frame diagnostic; se...
Definition Optimize2d.cpp:3654
std::vector< double > front_ring_measures() const
The exit test's ring measure of every vertex (energy_criterion()), chord for chord.
Definition Optimize2d.cpp:1065
std::vector< int > m_face_region
per face: band's region, -1 none, -2 reached from two
Definition TopoOffsetTriMesh.h:209
std::set< std::pair< long, long > > m_stuck_prev_cells
Definition TopoOffsetTriMesh.h:2170
std::shared_ptr< Envelope > envelope_for_mask(uint64_t mask) const
The envelope a simplex with this boundary mask is contained in, or null.
Definition Optimize2d.cpp:198
bool vertex_has_live_offset_edge(size_t vid) const
Whether ANY live offset-boundary edge is incident to vid; reads only vid's own faces.
Definition Optimize2d.cpp:3568
void init_from_image(const MatrixXd &V, const MatrixXi &F, const MatrixSi &F_tags, const MatrixXd &V_env, const MatrixXi &F_env, const std::vector< std::string > &tag_names, const std::string &curve_name="")
initialize TriMesh from vertex, face, tag data
Definition TopoOffsetTriMesh.cpp:14
void check_offset_within_support(const char *when) const
Stop the run if any reachable band vertex has left the potential's support.
Definition Optimize2d.cpp:3247
std::shared_ptr< OffsetPotential2D > m_offset_potential
The smooth offset potential, and with it the definition of the offset itself.
Definition TopoOffsetTriMesh.h:172
MatrixXd m_curve_V
The curve group as loaded, kept because the classification below is redone on demand.
Definition TopoOffsetTriMesh.h:132
std::vector< int64_t > m_phi_seg_region
per m_phi_E row: region index, -1 unknown
Definition TopoOffsetTriMesh.h:206
std::shared_ptr< polysolve::nonlinear::Problem > amips_energy(size_t vid) const
Definition Optimize2d.cpp:910
std::shared_ptr< Envelope > surface_envelope_for_edge(const std::array< size_t, 2 > &vids, const EdgeAttributes &) const override
Class-0 segments – every region boundary, the input complex and the domain wall included – carry a co...
Definition TopoOffsetTriMesh.h:1332
int64_t m_curve_tag
Definition TopoOffsetTriMesh.h:130
static constexpr int INPUT_SURFACE_CLASS
SurfaceTagAttributes::m_surface_class: which of the two tracked surfaces an edge belongs to....
Definition TopoOffsetTriMesh.h:416
bool smooth_before(const Tuple &t) override
User specified preparations and desideratas for an edge smooth.
Definition Optimize2d.cpp:839
MatrixXd m_phi_V
Definition TopoOffsetTriMesh.h:2292
uint64_t edge_mask(const std::array< size_t, 2 > &vids) const
Definition TopoOffsetTriMesh.h:1273
std::shared_ptr< polysolve::nonlinear::Problem > front_objective(size_t vid) const
Definition FrontSmooth2d.cpp:235
static constexpr int kGradBins
Definition TopoOffsetTriMesh.h:1034
bool vertex_carries_offset_term(const size_t vid) const
Definition TopoOffsetTriMesh.h:1553
bool smoothing_position_is_allowed(const size_t, const Vector2d &) const override
No per-vertex positional constraint. The per-tag envelopes close that hole structurally – the same de...
Definition TopoOffsetTriMesh.h:1407
static constexpr int64_t m_wall_tag
pseudo-tag: the domain wall's tube
Definition TopoOffsetTriMesh.h:334
bool split_edge_before(const Tuple &t) override
User specified preparations and desideratas for an edge split.
Definition EdgeSplittingTri.cpp:9
bool split_face_after(const Tuple &t) override
User specified modifications and desideratas after a face split.
Definition EdgeSplittingTri.cpp:304
bool face_is_released_band(size_t fid) const
Definition Optimize2d.cpp:276
void record_flatness(const char *op, double parent_flat, size_t child_fid) const
Genesis: flatness transitions recorded at the operation hooks. {op, parent, child}.
Definition Optimize2d.cpp:2070
void mark_input_complex_vertices()
Definition TopoOffsetTriMesh.cpp:336
double front_move_alignment(size_t vid) const
Definition FrontSmooth2d.cpp:177
std::atomic< size_t > m_needle_smooth_reports
Worst-case record: the best (lowest) ring max any needle-adjacent smooth achieved.
Definition TopoOffsetTriMesh.h:1083
void smooth_group_to_convergence(const char *group_name)
Definition Optimize2d.cpp:3085
double m_front_gradient_reference
Its value on the band as constructed, measured once before turn 1.
Definition TopoOffsetTriMesh.h:387
std::vector< std::shared_ptr< OffsetPotential2D > > m_region_potentials
one per piece
Definition TopoOffsetTriMesh.h:204
bool vertex_is_on_region(const size_t vid) const
Is this vertex on a region boundary – a tag boundary, or the domain wall.
Definition TopoOffsetTriMesh.h:1239
void relabel_input_complex()
Definition TopoOffsetTriMesh.cpp:1026
wmtk::threading::enumerable_thread_specific< double > m_collapse_parent_flatness
The flattest face in the collapse's ring before it ran, for record_flatness().
Definition TopoOffsetTriMesh.h:1118
optimization::NewtonCounters m_newton_repulsion
Definition TopoOffsetTriMesh.h:942
void for_each_edge_sample(const Vector2d &p0, const Vector2d &p1, Visit &&visit) const
The stencil_order stencil a chord is sampled on, handed to visit one point at a time as (point,...
Definition TopoOffsetTriMesh.h:1756
double front_vertex_conv_ratio(size_t vid) const
Definition Optimize2d.cpp:2717
bool edge_is_on_surface(const std::array< size_t, 2 > &vids) const override
Definition Optimize2d.cpp:28
std::shared_ptr< Envelope > smoothing_containment_envelope(const size_t vid) const
Definition TopoOffsetTriMesh.h:1461
Vector2d front_vertex_normal(size_t vid) const
The field's outward unit direction at front vertex vid (zero where grad Phi vanishes).
Definition FrontSmooth2d.cpp:228
size_t refine_sizing_around_worst(double max_metric) override
TriWild's stall-driven sizing refinement, verbatim.
Definition Optimize2d.cpp:3337
std::vector< int64_t > m_phi_face_region
per m_phi_F row: region index, -1 unknown
Definition TopoOffsetTriMesh.h:207
static constexpr double kNeedleQuality
What counts as a needle for the tripwire – deliberately far below MAX_ENERGY.
Definition TopoOffsetTriMesh.h:2158
bool smooth_front_vertex(const Tuple &t)
Placement of a front vertex: the shared smoother with the offset's options. See FrontSmooth2d....
Definition FrontSmooth2d.cpp:54
bool is_marched_edge(const size_t a, const size_t b) const
The edges marching_tris() splits: exactly one end in the input complex (label != 0).
Definition TopoOffsetTriMesh.h:945
bool edge_split_sphere_trace(const Vector2d &p_in, const Vector2d &p_out, Vector2d &p_new, size_t &steps) const
Construction placement in the marching: sphere tracing along the edge from p_in (the endpoint in the ...
Definition EdgeSplittingTri.cpp:154
bool smooth_nonfront_vertex(const Tuple &t)
Every vertex off the front (and off the plastic medium): the shared smoother with TriWild's options (...
Definition Optimize2d.cpp:934
bool optimization_bare_coarsen_passes() const override
Definition TopoOffsetTriMesh.h:2176
void label_offset_boundary()
Tag the two tracked surfaces for the optimization phase.
Definition Optimize2d.cpp:65
void update_attributes() override
Definition Optimize2d.cpp:1170
double repulsion_cell_term(const std::array< size_t, 3 > &vids) const
Definition TopoOffsetTriMesh.h:980
bool edge_borders_released_boundary(const Tuple &e) const
Definition Optimize2d.cpp:3755
std::atomic< size_t > m_cross_own
Definition TopoOffsetTriMesh.h:1049
bool edge_is_region(const size_t eid) const
Definition TopoOffsetTriMesh.h:524
std::map< int64_t, std::shared_ptr< Envelope > > m_tag_envelopes
One containment envelope per input tag, ambient included. Both phases.
Definition TopoOffsetTriMesh.h:269
void append_frame_label(size_t idx, const std::string &label) const
One line of <output>_frames.txt; truncates the file on the first frame.
Definition Optimize2d.cpp:3943
std::shared_ptr< polysolve::nonlinear::Problem > front_energy(size_t vid, const std::shared_ptr< const OffsetPotential2D > &pot) const
Definition FrontSmooth2d.cpp:249
double band_vertex_residual(const size_t vid) const
Definition Optimize2d.cpp:2299
std::vector< std::array< double, 8 > > optimization_metrics
Definition TopoOffsetTriMesh.h:634
void simplicial_embedding()
make mesh a simplicial embedding of the input complex (simplices labelled 1)
Definition TopoOffsetTriMesh.cpp:1342
std::atomic< int > iter_cnt_collapse_energy_reject
Definition TopoOffsetTriMesh.h:673
bool edge_is_offset(const size_t eid) const
Definition TopoOffsetTriMesh.h:516
int stencil_points_per_edge() const
Definition TopoOffsetTriMesh.h:1660
static constexpr int64_t m_complex_tag
Definition TopoOffsetTriMesh.h:335
double ring_measure_at(size_t vid) const
One vertex's ring measure, from its own live front chords (NaN if it has none).
Definition Optimize2d.cpp:1095
bool swap_edge_before(const Tuple &t) override
Reject a flip whose new edge already exists, and cache the energy rule's before-half.
Definition Optimize2d.cpp:124
bool face_is_input_complex(const size_t fid) const
Definition Optimize2d.cpp:60
void log_region_edge_mask_health(const std::string &when) const
Are the tracked region boundaries actually contained by anything?
Definition Optimize2d.cpp:1397
std::shared_ptr< OffsetEnergy2D > repulsion_energy(const size_t vid) const
O(v) for vid's smoothing objective, charged once per carrying face.
Definition TopoOffsetTriMesh.h:1010
size_t grade_sizing_by_distance(const std::vector< size_t > &seeds)
Definition Optimize2d.cpp:3144
bool smooth_after(const Tuple &t) override
User specified modifications and desideras after an edge smooth.
Definition Optimize2d.cpp:967
bool collapse_edge_after(const Tuple &t) override
THE ENERGY RULE for a collapse (see tri_energy()), on the real mesh once the connectivity is committe...
Definition Optimize2d.cpp:482
wmtk::threading::enumerable_thread_specific< std::vector< size_t > > m_collapse_edge_link
The link of the collapsed edge, captured in collapse_before_vertex(): the only vertices besides v2 wh...
Definition TopoOffsetTriMesh.h:1147
void needle_forensics() const
The full post-mortem on why nothing removes the flat faces.
Definition Optimize2d.cpp:2122
std::map< uint64_t, std::shared_ptr< Envelope > > m_isect_cache
Definition TopoOffsetTriMesh.h:277
bool split_adjust_position(size_t v_new, const std::vector< Tuple > &children) override
Carry each parent's region label onto the two children it became.
Definition Optimize2d.cpp:786
size_t m_marching_root_splits
Definition TopoOffsetTriMesh.h:728
wmtk::threading::enumerable_thread_specific< double > m_collapse_survivor_sizing
Definition TopoOffsetTriMesh.h:1121
std::shared_ptr< Envelope > containment_for(uint64_t region_mask, bool on_offset) const
The containment a simplex with this region mask, on/off the offset front, must satisfy – the intersec...
Definition Optimize2d.cpp:238
std::map< uint64_t, std::shared_ptr< Envelope > > m_offset_isect_cache
Memoized "region tubes AND the offset envelope", keyed by the region mask.
Definition TopoOffsetTriMesh.h:289
wmtk::threading::enumerable_thread_specific< std::pair< double, Vector2d > > m_needle_pre
Why smoothing does not lift a sliver's apex off its opposite edge.
Definition TopoOffsetTriMesh.h:1077
void collapse_pass_begin() override
Definition Optimize2d.cpp:702
std::shared_ptr< const OffsetPotential2D > m_repulsion_potential
Definition TopoOffsetTriMesh.h:939
void init_surfaces_and_boundaries()
Classify every region boundary, build the per-tag containment envelopes, and tag the domain wall – on...
Definition TopoOffsetTriMesh.cpp:141
std::vector< double > m_cross_ring
Definition TopoOffsetTriMesh.h:1044
void release_deformable_regions()
Definition Optimize2d.cpp:382
bool m_quality_converged
Definition TopoOffsetTriMesh.h:663
void crossing_snapshot(const std::string &pass, bool compare, bool match_positions)
Definition Optimize2d.cpp:1113
bool m_plastic_active
set in optimize_offset() when deform_others
Definition TopoOffsetTriMesh.h:1620
double ring_max_quality(size_t vid) const
Max AMIPS over the faces incident to vid. -1 if it has none.
Definition Optimize2d.cpp:2048
void write_phi_grid(const std::string &path, int n) const
Sample the smooth offset potential on a dense grid and write it as <path>_phi.vtu.
Definition TopoOffsetTriMesh.cpp:2032
void grade_sizing(double grade, const std::vector< size_t > &seeds)
Definition Optimize2d.cpp:3134
EnvelopeSetup
Which boundaries the region-class envelopes hold, and how they are built.
Definition TopoOffsetTriMesh.h:327
bool ambient_assert()
ensure ambient tag does not overlap any other tags in mesh.
Definition TopoOffsetTriMesh.cpp:353
double smoother_amips_weight(const size_t vid) const
Definition TopoOffsetTriMesh.h:1560
bool simplex_in_input_complex(size_t a, size_t b) const
Definition TopoOffsetTriMesh.cpp:1266
bool smooth_vertex_2d_counted(const Tuple &t, const optimization::SmoothVertexOptions &opts, optimization::NewtonCounters &newton, bool *solved=nullptr)
Definition Optimize2d.cpp:886
bool edge_is_complex_boundary(const Tuple &e) const
Definition TopoOffsetTriMesh.cpp:219
bool is_simplicially_embedded() const
check if the input complex (simplices labelled 1) are simplicially embedded w.r.t....
Definition TopoOffsetTriMesh.cpp:1297
bool collapse_edge_before(const Tuple &t) override
Reject any collapse that violates the substructure link condition, and remember the survivor's sizing...
Definition Optimize2d.cpp:567
std::shared_ptr< polysolve::nonlinear::Problem > rest_energy_for_vertex(size_t vid) const
Definition Optimize2d.cpp:415
std::string m_frame_prefix
Leads every debug frame label: i during the init_optimize loop, empty otherwise.
Definition TopoOffsetTriMesh.h:381
void log_smoothing_pass_accounting()
Definition Optimize2d.cpp:1182
bool tri_is_simp_emb(const Tuple &t) const
check if a triangle satisfies simpicial embedding criteria w.r.t. input complex (simplices labelled 1...
Definition TopoOffsetTriMesh.cpp:1316
std::vector< Tuple > offset_surface_edges_live_at(size_t vid) const
Definition Optimize2d.cpp:590
std::shared_ptr< Envelope > smoothing_energy_envelope(const size_t vid) const override
The offset boundary is the one tracked surface with no envelope, in either role.
Definition TopoOffsetTriMesh.h:1425
double edge_offset_term(size_t a, size_t b) const
THE ONE CHORD FUNCTION: the mean over the chord's stencil of r^2, r = relative_residual(q) / front_co...
Definition Optimize2d.cpp:2775
void init_offset_potential()
Build the smooth offset potential from the extraction init_input_complex_bvh() kept.
Definition TopoOffsetTriMesh.cpp:817
Per-vertex data the shared 2D optimizer knows nothing about.
Definition TopoOffsetTriMesh.h:40
int64_t m_corr_input_vid
Definition TopoOffsetTriMesh.h:61
uint64_t m_boundary_mask
Which tag boundaries this vertex lies on – one bit per input tag, ambient included....
Definition TopoOffsetTriMesh.h:55
uint32_t m_born_epoch
Definition TopoOffsetTriMesh.h:66
Definition Simplex.hpp:46
Definition enumerable_thread_specific.hpp:37
double w_amips
Definition OptimizerParameters.h:172
Definition TriOptimizerMesh.h:95
bool m_is_rounded
Definition TriOptimizerMesh.h:52
What the offset needs on top of the parameters every wmtk optimizer shares.
Definition Parameters.h:24
double offset_amips_weight
Definition Parameters.h:91
double front_conv_frac() const
Definition Parameters.h:78
The band's distance error, split by whether the optimizer can do anything about it.
Definition TopoOffsetTriMesh.h:1693
An edge's / face's shared attributes together with the offset's own label.
Definition TopoOffsetTriMesh.h:536
The front's measures against the one bar and the refinable chords. The 2D twin of TopoOffsetTetMesh::...
Definition TopoOffsetTriMesh.h:1838
double max_ring_at_floor
the worst of them, as a ratio to the bar
Definition TopoOffsetTriMesh.h:1905
bool ring_exit
front_measure "vertex_ring"
Definition TopoOffsetTriMesh.h:1893
std::vector< size_t > refinable_vertices
Definition TopoOffsetTriMesh.h:1901
bool converged() const
Definition TopoOffsetTriMesh.h:1921
double sum_ring
ratios to the bar (1 = bar)
Definition TopoOffsetTriMesh.h:1895
bool vertices_ok() const
Definition TopoOffsetTriMesh.h:1913
double floor_scalar
The floor, max(min_sizing_scalar, min_edge_length / l), and which of the two it is.
Definition TopoOffsetTriMesh.h:1870
std::string sizing_floor_fact() const
Definition Optimize2d.cpp:2653
double max_edge_corners_at_floor
the worst of them, as a ratio to the bar
Definition TopoOffsetTriMesh.h:1867
double avg_vertex() const
Means over the measurable front vertices / front chords; 0 when there are none.
Definition TopoOffsetTriMesh.h:1933
double max_edge_at_floor
the worst of them, as a ratio to the bar
Definition TopoOffsetTriMesh.h:1859
DEBUG_output only: every front solve since the last debug frame, so the frames show per vertex what m...
Definition TopoOffsetTriMesh.h:907
int status
polysolve's stop status + 1: NewtonCounters::status_name()'s numbering
Definition TopoOffsetTriMesh.h:910
The convergence criterion's own split: ||grad (Phi - c)^2|| at band vertices – the deciding measure –...
Definition TopoOffsetTriMesh.h:1814
size_t n_skipped_inverted
Definition TopoOffsetTriMesh.h:1827
size_t n_edge_samples
Edge-interior samples measured into max_in_edge (not part of n_reachable).
Definition TopoOffsetTriMesh.h:1824
What smoothing did with each class of vertex, per pass. Same fields as 3D.
Definition TopoOffsetTriMesh.h:863
std::atomic< int > offset_accepted
... and the smoother kept the new position
Definition TopoOffsetTriMesh.h:868
std::atomic< int > offset_attempted
reached the smoother with the offset term
Definition TopoOffsetTriMesh.h:867
std::atomic< int > interior_attempted
reached it without one
Definition TopoOffsetTriMesh.h:869
std::atomic< int > attempted
smooth_before() entered
Definition TopoOffsetTriMesh.h:864
std::atomic< int > region_attempted
... of which sat on another region's boundary
Definition TopoOffsetTriMesh.h:870
std::atomic< int > before_unrounded
base smooth_before said no: could not round
Definition TopoOffsetTriMesh.h:866
std::atomic< int > before_bbox
base smooth_before said no: on the bounding box
Definition TopoOffsetTriMesh.h:865
double front_max_ratio
Max front_vertex_conv_ratio over the measurable front vertices (1 = the bar).
Definition TopoOffsetTriMesh.h:1971
size_t n_front
front vertices the max was taken over
Definition TopoOffsetTriMesh.h:1973
size_t n_front_unmeasurable
ratio not finite: left out of the max
Definition TopoOffsetTriMesh.h:1974
How the vertex solves of a smoothing pass ended: Newton iterations taken and the status polysolve sto...
Definition SmoothVertex.hpp:76
Weights and policy for smooth_vertex_3d.
Definition SmoothVertex.hpp:173