Wildmeshing Toolkit
Loading...
Searching...
No Matches
TopoOffsetTriMesh.h
1#pragma once
2#include <wmtk/TriMesh.h>
3#include <wmtk/TriOptimizerMesh.h>
4#include <algorithm>
5#include <array>
6#include <atomic>
7#include <cmath>
8#include <cstdint>
9#include <functional>
10#include <map>
11#include <mutex>
12#include <set>
13#include <wmtk/optimization/EnergySum.hpp>
14#include <wmtk/optimization/solver.hpp>
15#include <wmtk/threading/enumerable_thread_specific.hpp>
16#include "OffsetPotential.hpp"
17#include "Parameters.h"
18#include "SimplicialComplexBVH.hpp"
19#include "TagEnvelopes.hpp"
20
21using CellTag = std::set<int64_t>;
22
23namespace wmtk::components::topological_offset {
24
25
26const int64_t TEMP_OFFSET_TRI_TAG = -1;
27const CellTag TEMP_OFFSET_TRI_TAG_SET{TEMP_OFFSET_TRI_TAG};
28
38{
39public:
40 int label = 0;
41 bool m_is_on_input = false; // on the input complex
42 bool m_is_on_offset = false; // on the offset boundary itself
43 bool m_is_on_region = false; // on some OTHER tag region's boundary
46 Vector2d m_turn_start = Vector2d::Zero();
47 bool m_turn_start_valid = false;
48
57 uint64_t m_boundary_mask = 0;
58
63 uint32_t m_born_epoch = 0;
64};
65
66
70{
71public:
72 int label = 0;
76 bool on_curve = false;
77};
78
79
83{
84public:
85 int label = 0;
93 bool rest_valid = false;
94 std::array<Eigen::Vector2d, 3> rest_pos;
95};
96
97
111{
112public: // mode for splitting in marching tets
113 enum class EdgeSplitMode {
114 Midpoint = 0, // construction: simplicial embedding AND marching_tris
115 SphereTrace = 1, // marching_tris under sphere_trace_initialization: sphere tracing
116 // along the edge to d(x) = target_distance, midpoint when the trace
117 // leaves the edge
118 Optimization = 2 // the optimization phase; the shared engine places the vertex
119 };
120
121public:
122 std::array<size_t, 3> m_init_counts = {{0, 0, 0}};
123 size_t m_tags_count;
127 int64_t m_curve_tag = -1;
129 MatrixXd m_curve_V;
130 MatrixXi m_curve_E;
158 std::shared_ptr<SimplicialComplexBVH> m_input_complex_bvh;
159
169 std::shared_ptr<OffsetPotential2D> m_offset_potential;
170
200 int m_n_regions = 0;
201 std::vector<std::shared_ptr<OffsetPotential2D>> m_region_potentials;
202 std::vector<int64_t> m_phi_vert_region;
203 std::vector<int64_t> m_phi_seg_region;
204 std::vector<int64_t> m_phi_face_region;
205 std::vector<int64_t> m_phi_point_region;
206 std::vector<int> m_face_region;
207 std::vector<int> m_vertex_region;
208 void init_region_potentials(double delta, double effective_factor);
211 void assign_band_regions(bool log = true);
213 void log_front_profile(size_t vid);
214 int vertex_region(const size_t vid) const
215 {
216 return vid < m_vertex_region.size() ? m_vertex_region[vid] : -1;
217 }
218 int edge_region(const size_t va, const size_t vb) const
219 {
220 const int a = vertex_region(va), b = vertex_region(vb);
221 return (a >= 0 && a == b) ? a : -1;
222 }
223 const OffsetPotential2D& potential_for_region(const int region) const
224 {
225 return (region >= 0 && size_t(region) < m_region_potentials.size())
226 ? *m_region_potentials[size_t(region)]
228 }
229 const OffsetPotential2D& potential_for(const size_t vid) const
230 {
231 return potential_for_region(vertex_region(vid));
232 }
235 std::shared_ptr<const OffsetPotential2D> potential_ptr_for(const size_t vid) const
236 {
237 const int r = vertex_region(vid);
238 return (r >= 0 && size_t(r) < m_region_potentials.size()) ? m_region_potentials[size_t(r)]
240 }
241 const OffsetPotential2D& potential_for_edge(const size_t va, const size_t vb) const
242 {
243 return potential_for_region(edge_region(va, vb));
244 }
245 const OffsetPotential2D& potential_for_face(const size_t fid) const
246 {
247 return potential_for_region(fid < m_face_region.size() ? m_face_region[fid] : -1);
248 }
249
266 std::map<int64_t, std::shared_ptr<SampleEnvelope>> m_tag_envelopes;
267
282 {
283 std::vector<Eigen::Vector2i> E;
284 std::vector<std::vector<int>> at_vertex;
285 };
289 std::vector<Eigen::Vector2d> m_env_polyline_V;
290 std::map<int64_t, TagPolyline2d> m_tag_polyline;
291
294 std::map<int64_t, int> m_tag_bit;
295
298 mutable std::map<uint64_t, std::shared_ptr<SampleEnvelope>> m_isect_cache;
299 mutable std::mutex m_isect_mutex;
300
312 mutable std::map<uint64_t, std::shared_ptr<SampleEnvelope>> m_offset_isect_cache;
313
324 std::shared_ptr<SampleEnvelope> containment_for(uint64_t region_mask, bool on_offset) const;
325
346 bool project_into_containment(size_t vid, Vector2d& x) const;
347
357 int64_t tangent_curve_tag(size_t vid, const Vector2d& x) const;
358
373 bool walk_along_curve(int64_t tag, const Vector2d& x, double s, Vector2d& out) const;
374
382 bool curve_tangent(int64_t tag, const Vector2d& x, const Vector2d& prefer, Vector2d& tau) const;
383
395 enum class OptPhase { A, B, Single };
396
399 bool phase_places_front() const { return m_phase != OptPhase::A; }
400
402 OptPhase m_phase = OptPhase::A;
403
404 // Phase B is one sweep over every vertex per pass: a front vertex is placed by
405 // smooth_offset_vertex_backtracking(), an interior one relaxed by the shared smoother, in
406 // mesh order -- see smooth_before().
407
409 bool m_freeze_front = false;
410
429 enum class EnvelopeSetup { PerTag, WallComplex };
430 EnvelopeSetup envelope_setup() const
431 {
432 return m_offset_params.deform_others ? EnvelopeSetup::WallComplex : EnvelopeSetup::PerTag;
433 }
434 static constexpr int64_t m_wall_tag = -2;
435 static constexpr int64_t m_complex_tag = -3;
437 std::string envelope_key_name(int64_t tag) const;
440 bool edge_is_complex_boundary(const Tuple& e) const;
446 void build_boundary_envelopes(const char* when, EnvelopeSetup setup);
447
448 // No Phi test gates collapse and swap: the envelope is the constraint, and the offset
449 // criterion belongs to Phase B's own placement. The coarsening bar is applied where 3D
450 // applies it -- absolute, and only in m_coarsen_mode.
451
459 std::shared_ptr<SampleEnvelope> m_offset_envelope;
460
465
468 void check_no_vertex_on_both_surfaces(const char* when) const;
469
472
479
480 EdgeSplitMode m_edge_split_mode = EdgeSplitMode::Midpoint;
481
482 // tag name maps
483 std::map<std::string, int64_t> m_tag_name_to_id;
484 std::map<int64_t, std::string> m_tag_id_to_name;
485 CellTag m_offset_output_tag_ids;
486
487 // if in 'singlebody' mode
488 bool m_singlebody = false;
489 int64_t m_single_tag;
490
491 // just for retaining in output. dont actually use
492 bool m_has_envelope = false;
493 MatrixXd m_V_envelope;
494 MatrixXi m_F_envelope;
495
507 static constexpr int INPUT_SURFACE_CLASS = 0;
508 static constexpr int OFFSET_SURFACE_CLASS = 1;
509
512
516 // m_vertex_attribute, m_edge_attribute and m_face_attribute are the base's; these three
517 // are registered alongside them in its attribute groups.
518 VertexExtraCol m_vertex_extra;
519 EdgeExtraCol m_edge_extra;
520 FaceExtraCol m_face_extra;
521
522 TopoOffsetTriMesh(Parameters& _m_offset_params, int _num_threads = 0)
523 : wmtk::TriOptimizerMesh(_m_offset_params)
524 , m_offset_params(_m_offset_params)
525 {
526 NUM_THREADS = _num_threads;
527 m_vertex_attr_group.add(&m_vertex_extra);
528 m_edge_attr_group.add(&m_edge_extra);
529 m_face_attr_group.add(&m_face_extra);
530
531 // As in 3D. The per-vertex Newton solver logs a line per smoothing attempt at info level,
532 // which is one line per vertex per pass and buries the run's own output.
533 optimization::deactivate_opt_logger();
534 }
535
536 ~TopoOffsetTriMesh() override = default;
537
544 void set_vertex_position(const size_t vid, const Vector2d& p)
545 {
546 m_vertex_attribute[vid].m_posf = p;
547 m_vertex_attribute[vid].m_pos = to_rational(p);
548 m_vertex_attribute[vid].m_is_rounded = true;
549 }
550
553 bool edge_is_offset(const size_t eid) const
554 {
555 return m_edge_attribute[eid].m_is_surface_fs &&
556 m_edge_attribute[eid].m_surface_class == OFFSET_SURFACE_CLASS;
557 }
561 bool edge_is_region(const size_t eid) const
562 {
563 return m_edge_attribute[eid].m_is_surface_fs &&
564 m_edge_attribute[eid].m_surface_class != OFFSET_SURFACE_CLASS;
565 }
573 {
574 EdgeAttributes tags;
575 EdgeExtra2d extra;
576 };
578 {
579 FaceAttributes attrs;
580 FaceExtra2d extra;
581 };
582 EdgeSnapshot2d edge_snapshot(const size_t eid) const
583 {
584 return EdgeSnapshot2d{m_edge_attribute[eid], m_edge_extra[eid]};
585 }
586 void restore_edge(const size_t eid, const EdgeSnapshot2d& s)
587 {
588 m_edge_attribute[eid] = s.tags;
589 m_edge_extra[eid] = s.extra;
590 }
591 FaceSnapshot2d face_snapshot(const size_t fid) const
592 {
593 return FaceSnapshot2d{m_face_attribute[fid], m_face_extra[fid]};
594 }
595 void restore_face(const size_t fid, const FaceSnapshot2d& s)
596 {
597 m_face_attribute[fid] = s.attrs;
598 m_face_extra[fid] = s.extra;
599 }
600
610
620 bool face_in_region(const size_t fid) const;
621
624 bool face_is_input_complex(const size_t fid) const;
625
635 bool vertex_is_on_surface(const size_t vid) const override;
636 bool edge_is_on_surface(const std::array<size_t, 2>& vids) const override;
637
639 void optimize_offset(const std::filesystem::path& output_file);
640
644 bool face_is_offset_band(const size_t fid) const;
645
646
655 std::pair<double, double> compute_distance_deviation() const;
656
659 mutable size_t m_worst_dist_vid = static_cast<size_t>(-1);
660 void log_worst_dist_vertex() const;
661
664 bool edge_is_offset_surface_live(const Tuple& e) const;
665
671 std::vector<std::array<double, 8>> optimization_metrics;
674 std::vector<std::array<int, 3>> churn_counts;
679 std::vector<std::array<int, 3>> op_counts;
682 int m_ab_round = 0;
684 mutable size_t m_debug_seq = 0;
688 mutable std::vector<std::string> m_debug_frame_labels;
689 mutable std::map<std::string, std::vector<size_t>> m_debug_pvd_series;
694 void write_debug_pvd() const;
697 mutable int m_debug_pass = 0;
698 mutable int m_debug_last_round = -1;
699 mutable char m_debug_last_phase = '?';
705 bool m_converged = false;
710 double m_quality_max_amips = 0.;
711
714 std::atomic<int> iter_cnt_split_born{0};
715 std::atomic<int> iter_cnt_recollapsed{0};
716 std::atomic<int> iter_cnt_recollapsed_same_pass{0};
717 std::atomic<int> iter_cnt_split = 0, iter_cnt_collapse = 0, iter_cnt_swap = 0;
718 std::atomic<int> iter_cnt_collapse_offset_removed{0};
721 std::atomic<int> iter_cnt_swap_offset_reject{0};
727 std::atomic<int> iter_cnt_split_offset_before{0};
728 std::atomic<int> iter_cnt_split_offset{0};
734 {
735 std::map<size_t, int> face_label;
736 size_t v1_id = 0;
737 size_t v2_id = 0;
743 uint64_t edge_bits = 0;
746 double parent_q_max = -1.;
749 double parent_flatness = 1.;
750 };
752
753 bool marching_split_edge_before(const Tuple& t);
754 bool marching_split_edge_after(const Tuple& t);
771 const Vector2d& p_in,
772 const Vector2d& p_out,
773 Vector2d& p_new,
774 size_t& steps) const;
778 size_t m_marching_root_splits = 0, m_marching_midpoint_splits = 0;
779 size_t m_marching_trace_steps = 0, m_marching_trace_steps_max = 0;
780
789 bool collapse_edge_before(const Tuple& t) override;
790
798 bool swap_edge_before(const Tuple& t) override;
799
802 bool swap_edge_after(const Tuple& t) override;
803 bool collapse_before_vertex(size_t v1, size_t v2) override;
804 void collapse_after_vertex(size_t v1, size_t v2) override;
805 void split_after_vertex(size_t v_new) override;
806
822 bool split_adjust_position(size_t v_new, const std::vector<Tuple>& children) override;
823
824 bool smooth_before(const Tuple& t) override;
825 bool smooth_after(const Tuple& t) override;
826
844 bool vertex_is_on_domain_boundary(const size_t vid) const
845 {
846 return !m_vertex_attribute[vid].on_bbox_faces.empty();
847 }
848 bool edge_is_on_domain_boundary(const size_t eid) const
849 {
850 return m_edge_attribute[eid].m_is_bbox_fs >= 0;
851 }
852
868
873
887
898 {
899 std::atomic<int> attempted{0};
900 std::atomic<int> before_bbox{0};
901 std::atomic<int> before_unrounded{0};
903 0};
906 std::atomic<int> offset_attempted{0};
907 std::atomic<int> offset_accepted{0};
908 std::atomic<int> interior_attempted{0};
909 std::atomic<int> region_attempted{0};
916 std::atomic<long long> res_before_nano{0};
917 std::atomic<long long> res_after_nano{0};
918 std::atomic<long long> res_max_before_nano{0};
919 std::atomic<long long> res_max_after_nano{0};
920
921 void reset()
922 {
923 for (std::atomic<int>* c :
924 {&attempted,
934 c->store(0);
935 }
936 for (std::atomic<long long>* c :
937 {&res_before_nano, &res_after_nano, &res_max_before_nano, &res_max_after_nano}) {
938 c->store(0);
939 }
940 }
941 };
942 SmoothTrace m_smooth_trace;
943
960 mutable std::atomic<size_t> m_needle_smooth_offered{0};
961 mutable std::atomic<size_t> m_needle_smooth_reached{0};
962 mutable std::atomic<size_t> m_needle_smooth_fixed{0};
963 mutable std::atomic<size_t> m_needle_smooth_stationary{0};
965 mutable std::atomic<size_t> m_needle_smooth_reports{0};
966
968 double ring_max_quality(size_t vid) const;
969
978 double face_flatness(size_t fid) const;
979
990 void needle_forensics() const;
991
993 void record_flatness(const char* op, double parent_flat, size_t child_fid) const;
994 mutable std::atomic<size_t> m_flat_created_split{0};
995 mutable std::atomic<size_t> m_flat_created_collapse{0};
996 mutable std::atomic<size_t> m_flat_worsened_split{0};
997 mutable std::atomic<size_t> m_flat_genesis_reports{0};
998 static constexpr double kFlatThreshold = 1e-3;
1007 double offset_edge_sag(size_t a, size_t b) const;
1010 double max_offset_edge_sag(const std::vector<std::array<size_t, 2>>& edges) const;
1017 bool ops_guard_refuses_collapse(size_t v1, size_t v2) const;
1020 std::vector<Tuple> offset_surface_edges_live_at(size_t vid) const;
1021
1041 std::vector<char> offset_surface_foldover_labels() const;
1042 void log_smooth_trace() const;
1043
1044
1054 void log_region_edge_mask_health(const std::string& when) const;
1055
1072 void audit_surface_containment(const std::string& when) const;
1073
1077 mutable std::atomic<int> m_placement_env_entry_outside{0};
1078
1083 mutable std::atomic<int> m_placement_projected{0};
1084
1089 mutable std::atomic<int> m_placement_tangential{0};
1090
1092
1100 bool vertex_is_on_region(const size_t vid) const
1101 {
1102 return m_vertex_extra[vid].m_is_on_region || !m_vertex_attribute[vid].on_bbox_faces.empty();
1103 }
1104
1107 uint64_t tag_bits(const CellTag& tags) const
1108 {
1109 uint64_t bits = 0;
1110 for (const int64_t t : tags) {
1111 const auto it = m_tag_bit.find(t);
1112 if (it != m_tag_bit.end()) bits |= (uint64_t(1) << it->second);
1113 }
1114 return bits;
1115 }
1116
1127 uint64_t vertex_boundary_mask(const size_t vid) const
1128 {
1129 return vertex_is_on_region(vid) ? m_vertex_extra[vid].m_boundary_mask : uint64_t(0);
1130 }
1131
1134 uint64_t edge_mask(const std::array<size_t, 2>& vids) const
1135 {
1136 return vertex_boundary_mask(vids[0]) & vertex_boundary_mask(vids[1]);
1137 }
1138
1151 uint64_t edge_boundary_bits(const Tuple& e) const
1152 {
1153 const std::optional<Tuple> opp = e.switch_face(*this);
1154 if (!opp) {
1155 return tag_bits(m_face_attribute[e.fid(*this)].tags); // domain wall
1156 }
1157 const auto& t0 = m_face_attribute[e.fid(*this)].tags;
1158 const auto& t1 = m_face_attribute[opp->fid(*this)].tags;
1159 CellTag diff;
1160 std::set_symmetric_difference(
1161 t0.begin(),
1162 t0.end(),
1163 t1.begin(),
1164 t1.end(),
1165 std::inserter(diff, diff.begin()));
1166 return tag_bits(diff);
1167 }
1168
1178 std::shared_ptr<SampleEnvelope> envelope_for_mask(uint64_t mask) const;
1179
1196 std::shared_ptr<SampleEnvelope> surface_envelope_for_edge(
1197 const std::array<size_t, 2>& vids) const override
1198 {
1199 // Boundary geometry first, in both phases: a segment on any tag-region boundary may not
1200 // drift out of that boundary's tube, and one on several boundaries -- a junction -- is
1201 // held in their intersection. The mask carries the input complex too, since every complex
1202 // simplex lies on tag boundaries, and E_t is built from the input mesh before construction
1203 // touches it, so the per-tag tubes hold the as-loaded geometry.
1204 //
1205 // Keyed on the vertices, not the edge: every caller is an operation asking about a segment
1206 // it is about to create or has just created, whose own edge attributes are not written
1207 // yet. The endpoints' masks are, maintained by the operations themselves (AND at a split,
1208 // OR at a collapse).
1209 uint64_t mask = edge_mask(vids);
1210 bool all_offset = true;
1211 for (const size_t v : vids) {
1212 all_offset = all_offset && m_vertex_extra[v].m_is_on_offset;
1213 }
1214
1215 // The ambiguous case: both endpoints can be on region boundaries and on the offset front
1216 // at once -- a real state, and the point of tracking the two families separately. The
1217 // endpoint-mask AND is then necessary but not sufficient for the segment lying on a shared
1218 // boundary: two vertices on different junctions can share a tag bit by coincidence and be
1219 // joined by an offset chord nowhere near that tag's curve, which a container it was never
1220 // meant to satisfy then refuses to refine. Ask the edge's own class, the only record that
1221 // distinguishes a chord from a boundary.
1222 //
1223 // Reading the slot is safe here and would not be unconditionally: the base checks a
1224 // split's two child segments before writing their attributes, so their slots still hold a
1225 // recycled edge's class. A split child never reaches this branch -- its mask and its
1226 // offset flag are mutually exclusive by construction, so one of `mask` and `all_offset` is
1227 // always empty -- and the m_is_surface_fs guard leaves both constraints standing rather
1228 // than dropping one when a slot is illegible.
1229 if (mask != 0 && all_offset) {
1230 if (const auto found = try_tuple_from_edge(vids)) {
1231 const size_t eid = std::get<1>(*found);
1232 if (m_edge_attribute[eid].m_is_surface_fs) {
1233 if (edge_is_offset(eid)) {
1234 mask = 0; // an offset edge lies on no region boundary
1235 } else {
1236 all_offset = false; // a region edge is not the offset front
1237 }
1238 }
1239 }
1240 }
1241 // Both families compose: whatever holds this segment holds it at once. Phase A holds the
1242 // offset where Phase B left it; Phase B is what moves it, so it contributes nothing there
1243 // -- and null before the offset exists at all.
1244 const std::shared_ptr<SampleEnvelope> base = containment_for(mask, all_offset);
1245 if (base || m_deform_tags.empty()) return base;
1246 // deform_others' ops-only tube: a released boundary is held by no mask -- its vertices
1247 // were freed so smoothing can carry the object -- which would leave the operations free
1248 // to decimate and reposition it. A segment the masks and the offset class do not claim,
1249 // but which lies on a released boundary by its incident faces' current tags, is held to
1250 // the tube around the boundary's current shape. A fresh split child can misread its
1251 // recycled face slots for this one check, and is at worst skipped or over-held once.
1252 if (const auto found = try_tuple_from_edge(vids)) {
1253 if (edge_borders_released_boundary(std::get<0>(*found))) return released_envelope();
1254 }
1255 return nullptr;
1256 }
1257
1270 bool smoothing_position_is_allowed(const size_t, const Vector2d&) const override
1271 {
1272 return true;
1273 }
1274
1288 std::shared_ptr<SampleEnvelope> smoothing_energy_envelope(const size_t vid) const override
1289 {
1290 if (m_vertex_extra[vid].m_is_on_offset && !vertex_is_on_region(vid)) {
1291 return nullptr;
1292 }
1293 const uint64_t mask = vertex_boundary_mask(vid);
1294 if (mask == 0) {
1295 // Reachable only for a construction artefact (a wall-chord midpoint flagged
1296 // on-surface with disjoint endpoint masks); its containment is vacuous too, so no
1297 // pull is behavior-neutral. Not an error.
1298 return nullptr;
1299 }
1300 std::shared_ptr<SampleEnvelope> best;
1301 double worst_d2 = -1.;
1302 for (const auto& [tag, env] : m_tag_envelopes) {
1303 const auto it = m_tag_bit.find(tag);
1304 if (it == m_tag_bit.end() || !(mask & (uint64_t(1) << it->second))) continue;
1305 if (!best) {
1306 best = env;
1307 if ((mask & (mask - 1)) == 0) break; // single bit: no violation contest to run
1308 worst_d2 = env->squared_distance(m_vertex_attribute[vid].m_posf);
1309 continue;
1310 }
1311 const double d2 = env->squared_distance(m_vertex_attribute[vid].m_posf);
1312 if (d2 > worst_d2) {
1313 worst_d2 = d2;
1314 best = env;
1315 }
1316 }
1317 return best;
1318 }
1319
1331 std::shared_ptr<SampleEnvelope> smoothing_containment_envelope(const size_t vid) const override
1332 {
1333 // Both families, composed -- not a choice between them. A vertex where the offset front
1334 // meets one or more region boundaries is on all of them at once, and the three cases fall
1335 // out of one expression: pure-offset (mask 0) gives the offset tube in Phase A and null in
1336 // Phase B, pure-region gives its tubes' intersection in both, and a junction of the two
1337 // gives the intersection of everything.
1338 return containment_for(vertex_boundary_mask(vid), m_vertex_extra[vid].m_is_on_offset);
1339 }
1340
1349 bool smooth_front_vertex_phase_b(const Tuple& t);
1352 double front_vertex_normal_gradient(size_t vid) const;
1355 Vector2d front_vertex_move_direction(size_t vid) const;
1359 double front_move_alignment(size_t vid) const;
1363 bool front_vertex_alignment_traps_1d_solve(size_t vid) const;
1367 double front_vertex_conv_ratio(size_t vid) const;
1395 bool front_vertex_placed(size_t vid) const;
1399 bool front_placed_by_ratio(const double ratio) const
1400 {
1401 return std::isfinite(ratio) && ratio <= 1.;
1402 }
1404 double edge_conv_ratio(const Tuple& e) const;
1407 double edge_conv_ratio(size_t a, size_t b) const;
1409 static_cast<size_t>(-1);
1411 Vector2d front_vertex_normal(size_t vid) const;
1414 std::shared_ptr<polysolve::nonlinear::Problem> phase_b_front_objective(
1415 size_t vid,
1416 const Vector2d& x) const;
1421 std::shared_ptr<polysolve::nonlinear::Problem> smoothing_extra_energy(
1422 const size_t vid) const override
1423 {
1424 std::shared_ptr<polysolve::nonlinear::Problem> front;
1425 if (phase_places_front() && m_offset_potential && m_vertex_extra[vid].m_is_on_offset &&
1426 vertex_boundary_mask(vid) == 0) {
1427 front = phase_b_front_energy(vid, potential_ptr_for(vid));
1428 }
1429 const std::shared_ptr<polysolve::nonlinear::Problem> rest = rest_energy_for_vertex(vid);
1430 if (!front) return rest;
1431 if (!rest) return front;
1432 auto sum = std::make_shared<optimization::EnergySum>();
1433 sum->add_energy(front);
1434 sum->add_energy(rest);
1435 return sum;
1436 }
1437
1438 // ------- deform_others: other input regions deform instead of being envelope-held -------
1439
1441 std::set<int64_t> m_deform_tags;
1444 std::set<int64_t> m_source_tags;
1451 mutable std::shared_ptr<SampleEnvelope> m_released_envelope;
1452 mutable std::atomic<bool> m_released_tube_dirty{false};
1453 mutable std::mutex m_released_mutex;
1456 std::shared_ptr<SampleEnvelope> released_envelope() const;
1459 bool edge_borders_released_boundary(const Tuple& e) const;
1461 bool face_is_deformable(size_t fid) const;
1467 bool m_plastic_active = false;
1468 bool face_is_plastic(size_t fid) const
1469 {
1470 // Everything outside the band: ambient, the other objects and the input complex's
1471 // interior alike -- one material. The complex's boundary is what its tube holds.
1472 return m_plastic_active && m_face_extra[fid].label != 2;
1473 }
1475 void stamp_plastic_rests();
1478 bool smooth_plastic_vertex(const Tuple& t);
1482 bool face_is_released_band(size_t fid) const;
1485 void stamp_rest_face(size_t fid);
1492 std::shared_ptr<polysolve::nonlinear::Problem> rest_energy_for_vertex(size_t vid) const;
1497 std::shared_ptr<polysolve::nonlinear::Problem> phase_b_front_energy(
1498 size_t vid,
1499 const std::shared_ptr<const OffsetPotential2D>& pot) const;
1500
1513 std::tuple<double, double> optimization_quality_stats() override;
1514
1526 double optimization_stop_metric() const override
1527 {
1528 // Single is TriWild's loop, so its stop metric is TriWild's.
1529 return m_phase != OptPhase::B ? wmtk::TriOptimizerMesh::optimization_stop_metric() : 1.;
1530 }
1531
1534 int stencil_order() const { return m_offset_params.stencil_order; }
1535
1543 {
1544 // Derived from the tolerance, not from the knob. grad E . n = 2 (Phi - c) (grad Phi . n),
1545 // so with |grad Phi| = s at the level set a bound g on the normal gradient is the length
1546 // bound |Phi - c| <= g / (2 s^2). The tolerance is rel x a measured maximum, so this must
1547 // follow it rather than reading the knob.
1548 const double s = m_offset_potential ? m_offset_potential->level_set_slope() : 1.;
1549 return std::max(0.5 * offset_gradient_tolerance() / (s * s), 1e-16);
1550 }
1551
1565 {
1566 // A fraction of a measured maximum, not of an analytic estimate. m_gradient_reference is
1567 // max |2 (Phi - c) grad Phi . n| over the offset-surface vertices as constructed, taken
1568 // once before the A/B loop starts; everything this bar serves -- the convergence criterion
1569 // and every Phase B local stop -- compares the full gradient norm at those same vertices
1570 // against it, so both sides come from one measurement pass and no per-field calibration.
1571 //
1572 // Never measured on the single-phase path, so this sits at the 1e-16 floor there; the
1573 // single-phase convergence bar uses m_front_gradient_reference instead.
1574 return std::max(m_offset_params.front_conv_frac() * m_gradient_reference, 1e-16);
1575 }
1576
1580 double gradient_reference() const { return m_gradient_reference; }
1581
1592 void check_offset_within_support(const char* when) const;
1593
1607 {
1608 double max_reachable = 0., avg_reachable = 0.;
1609 double max_pinned = 0.;
1610 size_t n_reachable = 0, n_pinned = 0;
1615 double max_at_vertex = 0., max_in_edge = 0.;
1620 size_t worst_outside_vid = static_cast<size_t>(-1);
1621 double worst_outside_dist = 0.;
1622 };
1623 DistanceSplit distance_deviation_split() const;
1624
1630 std::vector<bool> band_vertex_mask() const;
1631
1634 double max_band_vertex_distance() const;
1637 double band_vertex_distance_error(const size_t vid) const;
1638
1641 double band_vertex_residual(const size_t vid) const;
1642
1645 {
1646 double max = 0.;
1647 double sum = 0.;
1648 size_t n = 0;
1649 };
1650
1668 EdgeSamples offset_edge_samples(const Tuple& e) const;
1669
1677 template <typename Visit>
1678 void for_each_offset_edge_sample(const Tuple& e, Visit&& visit) const
1679 {
1680 const int k = m_offset_params.stencil_order;
1681 if (k <= 0) return;
1682 const Vector2d p0 = m_vertex_attribute[e.vid(*this)].m_posf;
1683 const Vector2d p1 = m_vertex_attribute[e.switch_vertex(*this).vid(*this)].m_posf;
1684 for (int i = 1; i <= k; ++i) {
1685 const double t = double(i) / double(k + 1);
1686 visit(Vector2d((1. - t) * p0 + t * p1));
1687 }
1688 }
1689
1708 {
1709 double max_reachable = 0., avg_reachable = 0.;
1710 double max_pinned = 0.;
1711 size_t n_reachable = 0, n_pinned = 0;
1716 double max_at_vertex = 0., max_in_edge = 0.;
1723 size_t n_edge_samples = 0;
1726 size_t n_skipped_inverted = 0, n_skipped_unrounded = 0;
1729 size_t worst_vid = static_cast<size_t>(-1);
1730 };
1733 GradientSplit gradient_split(bool include_edge_samples = true) const;
1734
1755 {
1756 double max_vertex = 0., max_edge = 0.;
1757 double bar = 1.;
1758 size_t n_vertices = 0, n_edges = 0, n_unmeasurable = 0;
1759 size_t worst_vid = static_cast<size_t>(-1);
1760 Vector2d worst_edge_mid = Vector2d::Zero();
1761 double worst_edge_len = 0.;
1767 size_t n_edges_over = 0, n_edges_over_placed = 0;
1768 double max_edge_placed = 0.;
1769 Vector2d worst_placed_mid = Vector2d::Zero();
1770 double tube = 0.;
1777 size_t n_at_floor = 0;
1778 double max_edge_at_floor = 0.;
1779 Vector2d worst_at_floor_mid = Vector2d::Zero();
1787 Vector2d worst_corners_at_floor_mid = Vector2d::Zero();
1789 double floor_scalar = 0.;
1790 bool floor_from_min_edge_length = false;
1791 size_t n_unplaced = 0;
1795 {
1796 size_t a, b;
1797 double sag, len;
1798 };
1799 std::vector<Refinable> refinable;
1813 bool ring_exit = false;
1814 static const char* ring_name() { return "length-weighted ring measure"; }
1815 double max_ring = 0.;
1816 size_t n_rings = 0, n_rings_unmeasurable = 0;
1817 size_t worst_ring_vid = static_cast<size_t>(-1);
1818 size_t n_rings_over = 0;
1821 std::vector<size_t> refinable_vertices;
1825 double max_ring_at_floor = 0.;
1826 Vector2d worst_ring_at_floor_pos = Vector2d::Zero();
1827 bool rings_ok() const { return max_ring <= bar; }
1832 bool vertices_ok() const { return n_unplaced == 0; }
1833 bool edges_ok() const { return max_edge <= bar; }
1857 bool converged() const
1858 {
1859 return (ring_exit ? rings_ok() : edges_ok()) && n_unmeasurable == 0;
1860 }
1866 std::string sizing_floor_fact() const;
1867 double ratio() const { return bar > 0. ? std::max(max_vertex, max_edge) / bar : 0.; }
1868 };
1869 EnergyCriterion energy_criterion();
1874 double front_chord_target(size_t va, size_t vb, double len, double sag, double tube) const;
1875
1878 size_t refine_front_from_sag(const std::vector<EnergyCriterion::Refinable>& edges);
1883 size_t refine_front_by_halving(const std::vector<EnergyCriterion::Refinable>& edges);
1888 size_t refine_front_by_halving(const std::vector<size_t>& vertices);
1889
1894 void grade_sizing(double grade, const std::vector<size_t>& seeds);
1900 size_t grade_sizing_by_distance(const std::vector<size_t>& seeds);
1901
1905 {
1908 double front_max_ratio = 0.;
1909 size_t front_worst_vid = static_cast<size_t>(-1);
1910 size_t n_front = 0;
1912 double front_max_step = 0.;
1916 size_t background_worst_vid = static_cast<size_t>(-1);
1917 size_t n_background = 0;
1918 };
1920 SmoothingProgress smoothing_progress(const std::vector<Vector2d>& before);
1926 void smooth_group_to_convergence(const char* group_name);
1929 std::optional<EnergyCriterion> m_energy_verdict;
1931 double edge_interpolation_residual(const Tuple& e) const;
1933 double edge_interpolation_residual(size_t a, size_t b) const;
1934
1947 Vector2d offset_vertex_normal(const size_t vid) const;
1948
1949
1952 void report_outside_support(const char* when, const DistanceSplit& s) const;
1960 bool band_vertex_is_reachable(const size_t vid) const
1961 {
1962 // An envelope-held offset vertex is pinned: it must stay within envelope_size of the input
1963 // region boundary AND sit on Phi = c, target_distance away. Those are not simultaneously
1964 // satisfiable, so its residual gradient is a statement about the constraint rather than
1965 // about the offset's quality, and leaving it in max_reachable makes convergence impossible
1966 // by construction. Structural, not left to whether the tolerance happens to hide it.
1967 //
1968 // Still reported, never silent: the pinned half of every band measure is logged beside the
1969 // reachable half, the same contract vertex_is_on_domain_boundary() gets below. This is a
1970 // retreat, to be reverted the moment a placement that works exists.
1971 if (m_vertex_extra[vid].m_is_on_offset && vertex_boundary_mask(vid) != 0) return false;
1972
1973 return !vertex_is_on_domain_boundary(vid);
1974 }
1975
1987 size_t refine_sizing_around_worst(double max_metric) override;
1988
2010 void log_stuck_refine_census(double max_metric, double filter_energy);
2011
2045 void log_refine_block_census(const std::string& when, double filter_energy) const;
2046
2058 bool collapse_quality_allowed(size_t v1, size_t v2, double q, double ring_max) const override;
2059
2068 bool swap_quality_allowed(const double after, const double before, const bool is_surface_flip)
2069 const override
2070 {
2071 if (!is_surface_flip) {
2072 return after < before;
2073 }
2074 return after < m_params.stop_energy;
2075 }
2076
2077 mutable std::atomic<size_t> m_deg_split_created{0};
2078 mutable std::atomic<size_t> m_deg_collapse_offered{0};
2079 mutable std::atomic<size_t> m_deg_collapse_allowed{0};
2080 mutable std::atomic<size_t> m_deg_collapse_by_ringmax{0};
2081 mutable std::atomic<size_t> m_deg_collapse_by_stop{0};
2082 mutable std::atomic<size_t> m_deg_collapse_by_unrounded{0};
2084 std::array<size_t, 6> m_deg_prev_counts{{0, 0, 0, 0, 0, 0}};
2085
2098 void report_needle(const char* op, size_t fid, double parent_q) const;
2099 static constexpr size_t kNeedleReports = 12;
2108 static constexpr double kNeedleQuality = 1e6;
2109 mutable std::atomic<size_t> m_needle_reports{0};
2110
2113 void needle_scan(const char* when) const;
2116 void collapse_pass_begin() override;
2117
2120 std::set<std::pair<long, long>> m_stuck_prev_cells;
2121 size_t m_stuck_calls = 0;
2122
2130 bool optimization_bare_coarsen_passes() const override { return false; }
2131
2141 bool collapse_edge_after(const Tuple& t) override;
2145 double face_criterion_rel(const size_t fid) const;
2155 void write_smoothing_debug_output(const std::string& path) const override
2156 {
2157 const char ph = (m_phase == OptPhase::A) ? 'A' : (m_phase == OptPhase::B ? 'B' : 'S');
2158 if (m_ab_round != m_debug_last_round || ph != m_debug_last_phase) {
2159 m_debug_last_round = m_ab_round;
2160 m_debug_last_phase = ph;
2161 m_debug_pass = 0;
2162 }
2163 // What this frame is, as a compact token: round, phase, pass within the phase, and the
2164 // operation it follows (from m_debug_pass_name -- the shared driver writes several frames
2165 // per operation group). Phase B and the single phase write one per sweep.
2166 std::string label = path;
2167 if (path.rfind("debug_", 0) == 0) {
2168 label = fmt::format(
2169 "r{}{}{}{}",
2170 m_ab_round,
2171 ph,
2172 ++m_debug_pass,
2173 m_debug_pass_name.empty() ? std::string() : "_" + m_debug_pass_name);
2174 } else if (path.rfind("phase_", 0) == 0) {
2175 label = fmt::format("r{}{}_end", m_ab_round, ph);
2176 }
2177 const_cast<TopoOffsetTriMesh*>(this)->write_debug_frame(label);
2178 }
2180 void append_frame_label(size_t idx, const std::string& label) const;
2190 void write_debug_frame(const std::string& label);
2191
2200 void init_from_image(
2201 const MatrixXd& V,
2202 const MatrixXi& F,
2203 const MatrixSi& F_tags,
2204 const MatrixXd& V_env,
2205 const MatrixXi& F_env,
2206 const std::vector<std::string>& tag_names,
2207 const std::string& curve_name = "");
2208
2212 bool ambient_assert();
2213
2217 void label_input_complex();
2218
2223 bool empty_input_complex();
2224
2234
2242 void init_offset_potential();
2243
2246 MatrixXd m_phi_V;
2247 MatrixXi m_phi_E;
2248 MatrixXi m_phi_F;
2249 std::vector<int> m_phi_P;
2250
2252 bool split_edge_before(const Tuple& t) override;
2253 bool split_edge_after(const Tuple& t) override;
2254 bool split_face_before(const Tuple& t) override;
2255 bool split_face_after(const Tuple& t) override;
2256 bool invariants(const std::vector<Tuple>& tris) override;
2258
2262 void execute_offset(const std::filesystem::path& output_file);
2263
2269 void marching_tris();
2270
2271
2273
2277 bool is_simplicially_embedded() const;
2278
2283 bool tri_is_simp_emb(const Tuple& t) const;
2284
2288 void simplicial_embedding();
2290
2295 void set_offset_tri_tags();
2296
2301 bool offset_is_manifold();
2302
2304
2315 void write_phi_grid(const std::string& path, int n) const;
2316
2317 void write_input_complex(const std::string& path);
2318 void write_vtu(const std::string& path);
2319 // void write_msh(const std::string& file);
2320 void write_msh_groups(const std::string& file);
2322
2323private:
2330 {
2331 size_t v1_id;
2332 size_t v2_id;
2333 Vector2d new_v_pos;
2334 VertexExtra2d new_v_extra;
2335
2336 // cache edge attributes
2337 EdgeSnapshot2d split_eattr;
2338 std::map<simplex::Edge, EdgeSnapshot2d> existing_eattr;
2339
2340 // cache face attributes
2341 std::map<size_t, FaceSnapshot2d> opp_v_fattr;
2342 };
2344
2346 {
2347 size_t v1_id;
2348 size_t v2_id;
2349 size_t v3_id;
2350 Vector2d new_v_pos;
2351 VertexExtra2d new_v_extra;
2352
2353 std::map<simplex::Edge, EdgeSnapshot2d> existing_eattr; // 3 orig edges
2354 FaceSnapshot2d split_fattr; // split face attributes
2355 };
2357
2358private: // helpers
2362 bool any_tag_present(const CellTag& tag1, const CellTag& tag2) const
2363 {
2364 for (const int64_t& i : tag1) {
2365 if (tag2.find(i) != tag2.end()) {
2366 return true;
2367 }
2368 }
2369 return false;
2370 }
2371
2375 void sort_edges_by_length(std::vector<simplex::Edge>& edges)
2376 {
2377 std::sort(
2378 edges.begin(),
2379 edges.end(),
2380 [this](const simplex::Edge& e1, const simplex::Edge& e2) {
2381 double len1 = (m_vertex_attribute[e1.vertices()[0]].m_posf -
2382 m_vertex_attribute[e1.vertices()[1]].m_posf)
2383 .squaredNorm();
2384 double len2 = (m_vertex_attribute[e2.vertices()[0]].m_posf -
2385 m_vertex_attribute[e2.vertices()[1]].m_posf)
2386 .squaredNorm();
2387 return len1 > len2;
2388 });
2389 }
2390
2391public: // helpers
2396 {
2397 const auto& verts = e.vertices();
2398 const auto incident = simplex_incident_triangles(e);
2399 const auto& faces = incident.faces();
2400
2401 assert(!faces.empty()); // throw error here otherwise
2402
2403 const size_t f_id = tuple_from_simplex(faces.front()).fid(*this);
2404 const Tuple t_edge = tuple_from_edge(verts[0], verts[1], f_id);
2405 return t_edge.eid(*this);
2406 }
2407
2412 {
2413 const auto& v = e.vertices();
2414 const auto faces = simplex_incident_triangles(e).faces();
2415 assert(!faces.empty());
2416 const size_t fid = tuple_from_simplex(faces.front()).fid(*this);
2417 return tuple_from_edge(v[0], v[1], fid);
2418 }
2419
2423 std::vector<Tuple> get_edge_adjacent_faces(const Tuple& f) const
2424 {
2425 std::vector<Tuple> adj_tris;
2426 auto tri_1 = f.switch_face(*this);
2427 if (tri_1) {
2428 adj_tris.push_back(tri_1.value());
2429 }
2430 auto tri_2 = f.switch_edge(*this).switch_face(*this);
2431 if (tri_2) {
2432 adj_tris.push_back(tri_2.value());
2433 }
2434 auto tri_3 = f.switch_vertex(*this).switch_edge(*this).switch_face(*this);
2435 if (tri_3) {
2436 adj_tris.push_back(tri_3.value());
2437 }
2438 return adj_tris;
2439 }
2440};
2441
2442
2443} // namespace wmtk::components::topological_offset
Whether a codimension-1 simplex is tracked surface, and which bbox side it lies on.
Definition SurfaceTagAttributes.h:15
Definition TriMesh.h:32
size_t fid(const TriMesh &) const
Definition TriMesh.h:80
Tuple switch_vertex(const TriMesh &m) const
Definition TriMesh.cpp:158
Tuple switch_edge(const TriMesh &m) const
Definition TriMesh.cpp:187
std::optional< Tuple > switch_face(const TriMesh &m) const
Definition TriMesh.cpp:214
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:2042
Tuple tuple_from_edge(size_t vid1, size_t vid2, size_t fid) const
Definition TriMesh.cpp:2202
What triwild and simwild's 2D mesh share.
Definition TriOptimizerMesh.h:39
AttributeContainerGroup m_edge_attr_group
What p_edge_attrs points at, so a derived class can register more.
Definition TriOptimizerMesh.h:111
AttributeContainerGroup m_vertex_attr_group
What p_vertex_attrs points at, so a derived class can register more.
Definition TriOptimizerMesh.h:96
AttributeContainerGroup m_face_attr_group
What p_face_attrs points at, so a derived class can register more.
Definition TriOptimizerMesh.h:122
OptimizerParameters & m_params
Definition TriOptimizerMesh.h:136
std::string m_debug_pass_name
Definition TriOptimizerMesh.h:158
Definition TopoOffsetTriMesh.h:70
bool on_curve
Definition TopoOffsetTriMesh.h:76
Definition TopoOffsetTriMesh.h:83
bool rest_valid
Definition TopoOffsetTriMesh.h:93
The offset potential: the scalar field on space whose level set the front is placed on.
Definition OffsetPotential.hpp:37
The offset's 2D mesh, on the shared 2D optimizer.
Definition TopoOffsetTriMesh.h:111
void rebuild_offset_envelope()
Definition Optimize2d.cpp:3881
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:3379
void stamp_plastic_rests()
Stamp rest := current for every plastic face; called before every operation group.
Definition Optimize2d.cpp:360
void stamp_rest_face(size_t fid)
Definition Optimize2d.cpp:351
double offset_edge_sag(size_t a, size_t b) const
Definition Optimize2d.cpp:750
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:200
int64_t tangent_curve_tag(size_t vid, const Vector2d &x) const
Which tag's boundary curve a vertex slides along, or -1.
Definition Optimize2d.cpp:507
int m_debug_pass
Definition TopoOffsetTriMesh.h:697
void label_input_complex()
label input complex simplices as per boolean expression (or single body mode)
Definition TopoOffsetTriMesh.cpp:406
double edge_interpolation_residual(const Tuple &e) const
The interpolation residual of front edge e, see EnergyCriterion. -1 when unmeasurable.
Definition Optimize2d.cpp:2508
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:544
std::string envelope_key_name(int64_t tag) const
The name a tag or pseudo-tag prints under.
Definition TopoOffsetTriMesh.cpp:210
void assign_band_regions(bool log=true)
Definition Optimize2d.cpp:2889
Parameters & m_offset_params
The base holds only wmtk::OptimizerParameters; this is the same object, typed.
Definition TopoOffsetTriMesh.h:511
double band_vertex_distance_error(const size_t vid) const
Definition Optimize2d.cpp:2301
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:2155
int stencil_order() const
Definition TopoOffsetTriMesh.h:1534
std::atomic< int > m_placement_tangential
Definition TopoOffsetTriMesh.h:1089
void log_front_profile(size_t vid)
Diagnostic: the front objective of one vertex along its normal, offset term vs total.
Definition Optimize2d.cpp:2996
void audit_surface_containment(const std::string &when) const
Which tracked edges are outside their envelope, and by how much.
Definition Optimize2d.cpp:1168
double optimization_stop_metric() const override
1.0 in Phase B, where the metric is normalized; the base's stop_energy in Phase A.
Definition TopoOffsetTriMesh.h:1526
void needle_scan(const char *when) const
Definition Optimize2d.cpp:2010
void log_stuck_refine_census(double max_metric, double filter_energy)
Why Phase A is stuck: a census of the faces stuck-refine is about to chase.
Definition Optimize2d.cpp:1728
std::shared_ptr< polysolve::nonlinear::Problem > smoothing_extra_energy(const size_t vid) const override
Definition TopoOffsetTriMesh.h:1421
bool split_face_before(const Tuple &t) override
User specified preparations and desideratas for a face split.
Definition EdgeSplittingTri.cpp:248
std::atomic< int > iter_cnt_collapse_guard_reject
Definition TopoOffsetTriMesh.h:725
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:3151
std::set< int64_t > m_source_tags
Definition TopoOffsetTriMesh.h:1444
Vector2d front_vertex_move_direction(size_t vid) const
Definition FrontSmooth2d.cpp:192
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:1281
size_t refine_front_by_halving(const std::vector< EnergyCriterion::Refinable > &edges)
Definition Optimize2d.cpp:3116
std::map< int64_t, int > m_tag_bit
Definition TopoOffsetTriMesh.h:294
bool front_vertex_alignment_traps_1d_solve(size_t vid) const
Definition FrontSmooth2d.cpp:231
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:55
std::vector< int64_t > m_phi_point_region
per m_phi_P entry: region index, -1 unknown
Definition TopoOffsetTriMesh.h:205
std::atomic< int > m_placement_env_entry_outside
Definition TopoOffsetTriMesh.h:1077
bool band_vertex_is_reachable(const size_t vid) const
Definition TopoOffsetTriMesh.h:1960
bool collapse_quality_allowed(size_t v1, size_t v2, double q, double ring_max) const override
Instrumentation only: which operation manufactures the MAX_ENERGY needles.
Definition Optimize2d.cpp:1928
size_t refine_front_from_sag(const std::vector< EnergyCriterion::Refinable > &edges)
Definition Optimize2d.cpp:3079
bool offset_is_manifold()
verify that the closed offset region (simplices labelled 1 or 2) form a manifold region....
Definition TopoOffsetTriMesh.cpp:1316
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:1957
void optimize_offset(const std::filesystem::path &output_file)
The 2D optimization phase: split / collapse / swap / smooth on the shared driver.
Definition Optimize2d.cpp:4386
void report_outside_support(const char *when, const DistanceSplit &s) const
Definition Optimize2d.cpp:3358
std::shared_ptr< SampleEnvelope > m_released_envelope
Definition TopoOffsetTriMesh.h:1451
double offset_residual_tolerance() const
Definition TopoOffsetTriMesh.h:1542
GradientSplit gradient_split(bool include_edge_samples=true) const
Definition Optimize2d.cpp:2393
std::shared_ptr< SampleEnvelope > released_envelope() const
Definition Optimize2d.cpp:3828
double m_gradient_reference
See offset_gradient_tolerance(). Nothing sets it on the single-phase path; it stays 0.
Definition TopoOffsetTriMesh.h:701
std::vector< int64_t > m_phi_vert_region
per m_phi_V row: region index
Definition TopoOffsetTriMesh.h:202
size_t m_worst_dist_vid
Definition TopoOffsetTriMesh.h:659
std::atomic< int > iter_cnt_split_offset_before
Splits of an offset-boundary edge: offered, accepted.
Definition TopoOffsetTriMesh.h:727
bool front_placed_by_ratio(const double ratio) const
Definition TopoOffsetTriMesh.h:1399
std::array< size_t, 6 > m_deg_prev_counts
Values at the previous census, so each census can report deltas rather than totals.
Definition TopoOffsetTriMesh.h:2084
size_t m_debug_seq
Monotonic frame counter for the debug timeline. Mutable because the write hook is const.
Definition TopoOffsetTriMesh.h:684
EdgeSamples offset_edge_samples(const Tuple &e) const
The Phi residual at stencil_order interior points of band edge e.
Definition Optimize2d.cpp:2317
bool empty_input_complex()
check if the input complex is empty. Only valid after calling init_from_image(...)....
Definition TopoOffsetTriMesh.cpp:588
void build_boundary_envelopes(const char *when, EnvelopeSetup setup)
Definition TopoOffsetTriMesh.cpp:230
bool smooth_plastic_vertex(const Tuple &t)
Definition Optimize2d.cpp:376
OptPhase m_phase
Which phase is running. Read by every hook that differs between them; see OptPhase.
Definition TopoOffsetTriMesh.h:402
std::shared_ptr< SimplicialComplexBVH > m_input_complex_bvh
The input complex as loaded. Built once, never rebuilt.
Definition TopoOffsetTriMesh.h:158
void init_input_complex_bvh()
Build the input complex's BVH and its smooth offset potential, from one extraction.
Definition TopoOffsetTriMesh.cpp:601
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:325
double front_chord_target(size_t va, size_t vb, double len, double sag, double tube) const
Definition Optimize2d.cpp:3036
bool phase_places_front() const
Definition TopoOffsetTriMesh.h:399
size_t edge_id_from_simplex(const simplex::Edge &e) const
get global id of edge from simplex::Edge object
Definition TopoOffsetTriMesh.h:2395
std::atomic< int > iter_cnt_collapse_offset_reject
Operations refused because they would have left an offset-boundary face over tolerance.
Definition TopoOffsetTriMesh.h:720
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:1551
std::map< uint64_t, std::shared_ptr< SampleEnvelope > > m_offset_isect_cache
Memoized "region tubes AND the offset envelope", keyed by the region mask.
Definition TopoOffsetTriMesh.h:312
bool face_is_offset_band(const size_t fid) const
Definition Optimize2d.cpp:2258
Tuple get_tuple_from_edge(const simplex::Edge &e) const
get Tuple simplex::Edge object
Definition TopoOffsetTriMesh.h:2411
std::vector< int > m_vertex_region
per vertex: region of its band faces, -1 / -2 as above
Definition TopoOffsetTriMesh.h:207
Vector2d offset_vertex_normal(const size_t vid) const
The normal at an offset vertex. Every caller that needs one goes through here, so switching the defin...
Definition Optimize2d.cpp:1135
void check_no_vertex_on_both_surfaces(const char *when) const
Definition Optimize2d.cpp:3749
void marching_tris()
Definition TopoOffsetTriMesh.cpp:1147
uint64_t tag_bits(const CellTag &tags) const
Definition TopoOffsetTriMesh.h:1107
void write_debug_pvd() const
Definition Optimize2d.cpp:4032
double gradient_reference() const
Definition TopoOffsetTriMesh.h:1580
void classify_curve_edges()
Mark the mesh edges that lie on the input's curve group (EdgeExtra2d::on_curve).
Definition TopoOffsetTriMesh.cpp:370
double face_flatness(size_t fid) const
Scale-invariant flatness: 2*area / longest_edge^2.
Definition Optimize2d.cpp:2069
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:65
void sort_edges_by_length(std::vector< simplex::Edge > &edges)
sort vector of edge simplices in place by decreasing length
Definition TopoOffsetTriMesh.h:2375
bool swap_edge_after(const Tuple &t) override
Definition Optimize2d.cpp:664
bool m_freeze_front
The final Phase A: front vertices are not smoothed (see smooth_before()).
Definition TopoOffsetTriMesh.h:409
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:1127
double front_vertex_normal_gradient(size_t vid) const
Definition Optimize2d.cpp:1151
double max_band_vertex_distance() const
Definition TopoOffsetTriMesh.cpp:952
MatrixXi m_phi_F
the complex faces, in the same vertex index space (for per-region BVHs)
Definition TopoOffsetTriMesh.h:2248
DistanceSplit residual_split() const
Definition Optimize2d.cpp:2338
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:4008
std::vector< bool > band_vertex_mask() const
Definition Optimize2d.cpp:2264
void for_each_offset_edge_sample(const Tuple &e, Visit &&visit) const
Visit the same interior sample points offset_edge_samples() measures on.
Definition TopoOffsetTriMesh.h:1678
std::set< int64_t > m_deform_tags
The released tags. Filled by release_deformable_regions(); empty = feature inactive.
Definition TopoOffsetTriMesh.h:1441
bool ops_guard_refuses_collapse(size_t v1, size_t v2) const
Definition Optimize2d.cpp:771
std::shared_ptr< SampleEnvelope > envelope_for_mask(uint64_t mask) const
The envelope a simplex with this boundary mask is contained in, or null.
Definition Optimize2d.cpp:209
bool m_converged
Definition TopoOffsetTriMesh.h:705
std::vector< std::array< int, 3 > > churn_counts
Definition TopoOffsetTriMesh.h:674
void warn_if_offset_reaches_domain_boundary() const
Warn if the offset band has grown into the domain boundary.
Definition Optimize2d.cpp:176
double edge_conv_ratio(const Tuple &e) const
The edge test divided by its bar (1 = bar), per front_conv_criterion; -1 unmeasurable.
Definition Optimize2d.cpp:2856
double offset_gradient_tolerance() const
The convergence tolerance: the bound on |grad (Phi - c)^2| at a band vertex.
Definition TopoOffsetTriMesh.h:1564
bool edge_is_offset_surface_live(const Tuple &e) const
Definition Optimize2d.cpp:3680
bool split_edge_after(const Tuple &t) override
User specified modifications and desideratas after an edge split.
Definition EdgeSplittingTri.cpp:172
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:1151
bool vertex_is_on_domain_boundary(const size_t vid) const
Identification only – no operation refuses the domain wall through these.
Definition TopoOffsetTriMesh.h:844
std::vector< std::string > m_debug_frame_labels
Definition TopoOffsetTriMesh.h:688
std::optional< EnergyCriterion > m_energy_verdict
Definition TopoOffsetTriMesh.h:1929
bool invariants(const std::vector< Tuple > &tris) override
User specified invariants that can't be violated.
Definition TopoOffsetTriMesh.cpp:1366
double face_criterion_rel(const size_t fid) const
Definition Optimize2d.cpp:3457
void execute_offset(const std::filesystem::path &output_file)
Definition TopoOffsetTriMesh.cpp:976
double phase_b_front_gradient_linf()
Definition Optimize2d.cpp:2783
std::shared_ptr< const OffsetPotential2D > potential_ptr_for(const size_t vid) const
Definition TopoOffsetTriMesh.h:235
void optimize_offset_single_phase()
TriWild's loop, the front placed inside its smoothing passes.
Definition Optimize2d.cpp:4070
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:3702
std::vector< int > m_face_region
per face: band's region, -1 none, -2 reached from two
Definition TopoOffsetTriMesh.h:206
std::set< std::pair< long, long > > m_stuck_prev_cells
Definition TopoOffsetTriMesh.h:2120
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:13
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:3353
bool project_into_containment(size_t vid, Vector2d &x) const
Move x back inside every region tube this vertex lies on. True if it ended up inside all of them.
Definition Optimize2d.cpp:278
std::shared_ptr< OffsetPotential2D > m_offset_potential
The smooth offset potential, and with it the definition of the offset itself.
Definition TopoOffsetTriMesh.h:169
MatrixXd m_curve_V
The curve group as loaded, kept because the classification below is redone on demand.
Definition TopoOffsetTriMesh.h:129
std::vector< int64_t > m_phi_seg_region
per m_phi_E row: region index, -1 unknown
Definition TopoOffsetTriMesh.h:203
int64_t m_curve_tag
Definition TopoOffsetTriMesh.h:127
static constexpr int INPUT_SURFACE_CLASS
SurfaceTagAttributes::m_surface_class: which of the two tracked surfaces an edge belongs to....
Definition TopoOffsetTriMesh.h:507
bool smooth_before(const Tuple &t) override
User specified preparations and desideratas for an edge smooth.
Definition Optimize2d.cpp:1006
MatrixXd m_phi_V
Definition TopoOffsetTriMesh.h:2246
std::shared_ptr< SampleEnvelope > surface_envelope_for_edge(const std::array< size_t, 2 > &vids) const override
Class-0 segments – every region boundary, the input complex and the domain wall included – carry a co...
Definition TopoOffsetTriMesh.h:1196
uint64_t edge_mask(const std::array< size_t, 2 > &vids) const
Definition TopoOffsetTriMesh.h:1134
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:1270
static constexpr int64_t m_wall_tag
pseudo-tag: the domain wall's tube
Definition TopoOffsetTriMesh.h:434
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:282
bool face_is_released_band(size_t fid) const
Definition Optimize2d.cpp:332
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:2081
void mark_input_complex_vertices()
Definition TopoOffsetTriMesh.cpp:339
std::shared_ptr< polysolve::nonlinear::Problem > phase_b_front_objective(size_t vid, const Vector2d &x) const
Definition FrontSmooth2d.cpp:302
double front_move_alignment(size_t vid) const
Definition FrontSmooth2d.cpp:173
bool front_vertex_placed(size_t vid) const
THE definition of "placed" for a vertex on the offset surface.
Definition Optimize2d.cpp:2849
std::atomic< size_t > m_needle_smooth_reports
Worst-case record: the best (lowest) ring max any needle-adjacent smooth achieved.
Definition TopoOffsetTriMesh.h:965
void smooth_group_to_convergence(const char *group_name)
Definition Optimize2d.cpp:3193
double m_front_gradient_reference
Definition TopoOffsetTriMesh.h:478
std::shared_ptr< SampleEnvelope > 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:249
std::vector< std::shared_ptr< OffsetPotential2D > > m_region_potentials
one per piece
Definition TopoOffsetTriMesh.h:201
std::atomic< int > iter_cnt_split_born
Definition TopoOffsetTriMesh.h:714
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:1100
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:1000
double front_vertex_conv_ratio(size_t vid) const
Definition Optimize2d.cpp:2804
bool edge_is_on_surface(const std::array< size_t, 2 > &vids) const override
Is an edge part of the substructure.
Definition Optimize2d.cpp:38
bool any_tag_present(const CellTag &tag1, const CellTag &tag2) const
determine if any tag from tag1 is also present in tag2.
Definition TopoOffsetTriMesh.h:2362
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:224
size_t refine_sizing_around_worst(double max_metric) override
TriWild's stall-driven sizing refinement, verbatim.
Definition Optimize2d.cpp:3479
std::vector< int64_t > m_phi_face_region
per m_phi_F row: region index, -1 unknown
Definition TopoOffsetTriMesh.h:204
static constexpr double kNeedleQuality
What counts as a needle for the tripwire – deliberately far below MAX_ENERGY.
Definition TopoOffsetTriMesh.h:2108
std::shared_ptr< polysolve::nonlinear::Problem > phase_b_front_energy(size_t vid, const std::shared_ptr< const OffsetPotential2D > &pot) const
Definition FrontSmooth2d.cpp:350
std::map< int64_t, std::shared_ptr< SampleEnvelope > > m_tag_envelopes
One containment envelope per input tag, ambient included. Both phases.
Definition TopoOffsetTriMesh.h:266
std::map< uint64_t, std::shared_ptr< SampleEnvelope > > m_isect_cache
Definition TopoOffsetTriMesh.h:298
bool edge_split_sphere_trace(const Vector2d &p_in, const Vector2d &p_out, Vector2d &p_new, size_t &steps) const
Construction placement under sphere_trace_initialization: sphere tracing along the edge from p_in (th...
Definition EdgeSplittingTri.cpp:138
bool optimization_bare_coarsen_passes() const override
Definition TopoOffsetTriMesh.h:2130
void label_offset_boundary()
Tag the two tracked surfaces for the optimization phase.
Definition Optimize2d.cpp:75
bool edge_borders_released_boundary(const Tuple &e) const
Definition Optimize2d.cpp:3804
bool edge_is_region(const size_t eid) const
Definition TopoOffsetTriMesh.h:561
bool curve_tangent(int64_t tag, const Vector2d &x, const Vector2d &prefer, Vector2d &tau) const
The unit tangent of tag tag's curve at x's foot, or false if there is none.
Definition Optimize2d.cpp:526
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:4000
double band_vertex_residual(const size_t vid) const
Definition Optimize2d.cpp:2310
std::vector< std::array< double, 8 > > optimization_metrics
Definition TopoOffsetTriMesh.h:671
void simplicial_embedding()
make mesh a simplicial embedding of the input complex (simplices labelled 1)
Definition TopoOffsetTriMesh.cpp:1084
bool edge_is_offset(const size_t eid) const
Definition TopoOffsetTriMesh.h:553
static constexpr int64_t m_complex_tag
Definition TopoOffsetTriMesh.h:435
std::tuple< double, double > optimization_quality_stats() override
The loop's convergence metric, normalized so that 1.0 means "done".
Definition Optimize2d.cpp:3425
bool swap_edge_before(const Tuple &t) override
Reject a flip whose new edge already exists.
Definition Optimize2d.cpp:134
bool face_is_input_complex(const size_t fid) const
Definition Optimize2d.cpp:70
double max_offset_edge_sag(const std::vector< std::array< size_t, 2 > > &edges) const
Definition Optimize2d.cpp:762
void log_region_edge_mask_health(const std::string &when) const
Are the tracked region boundaries actually contained by anything?
Definition Optimize2d.cpp:1333
size_t grade_sizing_by_distance(const std::vector< size_t > &seeds)
Definition Optimize2d.cpp:3250
bool smooth_after(const Tuple &t) override
User specified modifications and desideras after an edge smooth.
Definition Optimize2d.cpp:1038
bool collapse_edge_after(const Tuple &t) override
A collapse is accepted by the same criterion the smoothing minimises.
Definition Optimize2d.cpp:678
void needle_forensics() const
The full post-mortem on why nothing removes the flat faces.
Definition Optimize2d.cpp:2133
bool walk_along_curve(int64_t tag, const Vector2d &x, double s, Vector2d &out) const
March s of arclength along tag tag's boundary polyline from x's foot on it.
Definition Optimize2d.cpp:574
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:953
size_t m_marching_root_splits
Definition TopoOffsetTriMesh.h:778
wmtk::threading::enumerable_thread_specific< double > m_collapse_survivor_sizing
Definition TopoOffsetTriMesh.h:1003
std::vector< Tuple > get_edge_adjacent_faces(const Tuple &f) const
get faces (as Tuples) that are edge-adjacent to the given face (as Tuple)
Definition TopoOffsetTriMesh.h:2423
std::vector< std::array< int, 3 > > op_counts
Definition TopoOffsetTriMesh.h:679
std::vector< Eigen::Vector2d > m_env_polyline_V
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:959
void collapse_pass_begin() override
Definition Optimize2d.cpp:877
int m_ab_round
Definition TopoOffsetTriMesh.h:682
void init_surfaces_and_boundaries()
Classify every region boundary, build the per-tag containment envelopes, and tag the domain wall – on...
Definition TopoOffsetTriMesh.cpp:140
void release_deformable_regions()
Definition Optimize2d.cpp:438
bool m_quality_converged
Definition TopoOffsetTriMesh.h:709
bool m_plastic_active
set in optimize_offset() when deform_others
Definition TopoOffsetTriMesh.h:1467
double ring_max_quality(size_t vid) const
Max AMIPS over the faces incident to vid. -1 if it has none.
Definition Optimize2d.cpp:2059
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:1700
void grade_sizing(double grade, const std::vector< size_t > &seeds)
Definition Optimize2d.cpp:3240
EnvelopeSetup
Which boundaries the region-class envelopes hold, and how they are built.
Definition TopoOffsetTriMesh.h:429
bool ambient_assert()
ensure ambient tag does not overlap any other tags in mesh.
Definition TopoOffsetTriMesh.cpp:356
std::shared_ptr< SampleEnvelope > smoothing_containment_envelope(const size_t vid) const override
... and it is not contained by one either, except in Phase A.
Definition TopoOffsetTriMesh.h:1331
size_t m_front_gradient_worst_vid
Definition TopoOffsetTriMesh.h:1408
bool edge_is_complex_boundary(const Tuple &e) const
Definition TopoOffsetTriMesh.cpp:218
OptPhase
Which mode the hooks are running in. The 2D twin of TopoOffsetTetMesh::OptPhase.
Definition TopoOffsetTriMesh.h:395
bool is_simplicially_embedded() const
check if the input complex (simplices labelled 1) are simplicially embedded w.r.t....
Definition TopoOffsetTriMesh.cpp:1039
std::shared_ptr< SampleEnvelope > 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:1288
bool collapse_edge_before(const Tuple &t) override
Reject any collapse that violates the substructure link condition.
Definition Optimize2d.cpp:715
std::shared_ptr< polysolve::nonlinear::Problem > rest_energy_for_vertex(size_t vid) const
Definition Optimize2d.cpp:471
std::shared_ptr< SampleEnvelope > m_offset_envelope
The tube the offset boundary may not leave during Phase A, of half-width offset_envelope....
Definition TopoOffsetTriMesh.h:459
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:1058
std::vector< Tuple > offset_surface_edges_live_at(size_t vid) const
Definition Optimize2d.cpp:738
std::atomic< int > m_placement_projected
Definition TopoOffsetTriMesh.h:1083
bool swap_quality_allowed(const double after, const double before, const bool is_surface_flip) const override
Definition TopoOffsetTriMesh.h:2068
void init_offset_potential()
Build the smooth offset potential from the extraction init_input_complex_bvh() kept.
Definition TopoOffsetTriMesh.cpp:820
bool smooth_front_vertex_phase_b(const Tuple &t)
Phase B placement of a front vertex: the shared smoother with the offset's options.
Definition FrontSmooth2d.cpp:77
Per-vertex data the shared 2D optimizer knows nothing about.
Definition TopoOffsetTriMesh.h:38
uint64_t m_boundary_mask
Which tag boundaries this vertex lies on – one bit per input tag, ambient included....
Definition TopoOffsetTriMesh.h:57
uint32_t m_born_epoch
Definition TopoOffsetTriMesh.h:63
Vector2d m_turn_start
Definition TopoOffsetTriMesh.h:46
Definition Simplex.hpp:46
Definition enumerable_thread_specific.hpp:26
Definition TriOptimizerMesh.h:74
What the offset needs on top of the parameters every wmtk optimizer shares.
Definition Parameters.h:24
double front_conv_frac() const
Definition Parameters.h:81
bool deform_others
Definition Parameters.h:114
The band's distance error, split by whether the optimizer can do anything about it.
Definition TopoOffsetTriMesh.h:1607
The residual sampled at points along a band edge – see offset_edge_samples().
Definition TopoOffsetTriMesh.h:1645
An edge's / face's shared attributes together with the offset's own label.
Definition TopoOffsetTriMesh.h:573
The "energy_gradient" criterion: the front is at a critical point of Phase B's energy,...
Definition TopoOffsetTriMesh.h:1755
double max_ring_at_floor
the worst of them, as a ratio to the bar
Definition TopoOffsetTriMesh.h:1825
bool ring_exit
front_measure "vertex_ring"
Definition TopoOffsetTriMesh.h:1813
std::vector< size_t > refinable_vertices
Definition TopoOffsetTriMesh.h:1821
bool converged() const
Definition TopoOffsetTriMesh.h:1857
double max_edge
ratios to the bar (1 = bar)
Definition TopoOffsetTriMesh.h:1756
bool vertices_ok() const
Definition TopoOffsetTriMesh.h:1832
double floor_scalar
The floor, max(min_sizing_scalar, min_edge_length / l), and which of the two it is.
Definition TopoOffsetTriMesh.h:1789
std::string sizing_floor_fact() const
Definition Optimize2d.cpp:2733
double max_edge_corners_at_floor
the worst of them, as a ratio to the bar
Definition TopoOffsetTriMesh.h:1786
double max_ring
ratio to the bar (1 = bar)
Definition TopoOffsetTriMesh.h:1815
double max_edge_at_floor
the worst of them, as a ratio to the bar
Definition TopoOffsetTriMesh.h:1778
The convergence criterion's own split: ||grad (Phi - c)^2|| at band vertices – the deciding measure –...
Definition TopoOffsetTriMesh.h:1708
double max_in_edge_pinned
Definition TopoOffsetTriMesh.h:1719
size_t n_skipped_inverted
Definition TopoOffsetTriMesh.h:1726
size_t n_edge_samples
Edge-interior samples measured into max_in_edge (not part of n_reachable).
Definition TopoOffsetTriMesh.h:1723
double max_normal_aligned
max |2 (Phi - c) grad Phi . n| at band vertices, reachable AND pinned.
Definition TopoOffsetTriMesh.h:1721
What smoothing did with each class of vertex, per pass.
Definition TopoOffsetTriMesh.h:898
std::atomic< int > offset_accepted
... and the smoother kept the new position
Definition TopoOffsetTriMesh.h:907
std::atomic< int > offset_attempted
reached the smoother with the offset term
Definition TopoOffsetTriMesh.h:906
std::atomic< int > interior_attempted
reached it without one
Definition TopoOffsetTriMesh.h:908
std::atomic< int > before_phase_b_not_offset
Phase B: on an input surface, neither placed nor relaxed.
Definition TopoOffsetTriMesh.h:902
std::atomic< int > attempted
smooth_before() entered
Definition TopoOffsetTriMesh.h:899
std::atomic< int > region_attempted
Definition TopoOffsetTriMesh.h:909
std::atomic< int > before_phase_b_enveloped_background
Phase B: envelope-held.
Definition TopoOffsetTriMesh.h:904
std::atomic< long long > res_before_nano
Definition TopoOffsetTriMesh.h:916
std::atomic< int > before_unrounded
base smooth_before said no: could not round
Definition TopoOffsetTriMesh.h:901
std::atomic< int > before_bbox
base smooth_before said no: on the bounding box
Definition TopoOffsetTriMesh.h:900
std::atomic< int > before_phase_b_enveloped_offset
Phase B: on-offset AND held.
Definition TopoOffsetTriMesh.h:905
size_t n_front
front vertices the max was taken over
Definition TopoOffsetTriMesh.h:1910
size_t n_front_unmeasurable
ratio not finite: left out of the max
Definition TopoOffsetTriMesh.h:1911
The per-tag boundary polyline, with the adjacency an arclength walk needs.
Definition TopoOffsetTriMesh.h:282
std::vector< std::vector< int > > at_vertex
polyline vertex -> incident segment ids
Definition TopoOffsetTriMesh.h:284
std::vector< Eigen::Vector2i > E
segments, indexing m_env_polyline_V
Definition TopoOffsetTriMesh.h:283