|
Wildmeshing Toolkit
|
The offset's 2D mesh, on the shared 2D optimizer. More...
#include <TopoOffsetTriMesh.h>
Classes | |
| struct | CollapseCells |
| struct | DistanceSplit |
| The band's distance error, split by whether the optimizer can do anything about it. More... | |
| struct | EdgeSamples |
| struct | EdgeSnapshot2d |
| An edge's / face's shared attributes together with the offset's own label. More... | |
| struct | EdgeSplitCache |
| struct | EnergyCriterion |
| The front's measures against the one bar and the refinable chords. The 2D twin of TopoOffsetTetMesh::EnergyCriterion, front chords in place of offset faces. More... | |
| struct | FaceSnapshot2d |
| struct | FaceSplitCache |
| struct | FrontSolveRecord |
| DEBUG_output only: every front solve since the last debug frame, so the frames show per vertex what m_newton_front counts per pass (frame fields front_newton_iters and front_newton_status, see write_vtu()). Appended by smooth_front_vertex(), emptied by write_debug_frame() once the frame is written. As in 3D. More... | |
| struct | GradientSplit |
| The convergence criterion's own split: ||grad (Phi - c)^2|| at band vertices – the deciding measure – plus the edge-interior chord diagnostic and the normal-aligned reference quantity. More... | |
| struct | OptSplitCache2d |
| struct | SmoothingProgress |
| struct | SmoothTrace |
| What smoothing did with each class of vertex, per pass. Same fields as 3D. More... | |
Public Types | |
| enum class | EdgeSplitMode { Midpoint = 0 , SphereTrace = 1 , Optimization = 2 } |
| enum class | EnvelopeSetup { PerTag , WallComplex } |
| Which boundaries the region-class envelopes hold, and how they are built. More... | |
| using | VertexExtraCol = wmtk::AttributeCollection< VertexExtra2d > |
| using | EdgeExtraCol = wmtk::AttributeCollection< EdgeExtra2d > |
| using | FaceExtraCol = wmtk::AttributeCollection< FaceExtra2d > |
Public Types inherited from wmtk::TriOptimizerMesh | |
| using | EdgeAttributes = wmtk::SurfaceTagAttributes |
| using | VertAttCol = AttributeCollection< VertexAttributes > |
| using | EdgeAttCol = AttributeCollection< EdgeAttributes > |
| using | FaceAttCol = AttributeCollection< FaceAttributes > |
| using | CurveSources = std::map< std::array< size_t, 2 >, std::vector< uint64_t > > |
| using | FeaturePointAttCol = AttributeCollection< FeaturePointAttributes > |
Public Types inherited from wmtk::TriMesh | |
| template<typename T > | |
| using | vector = std::vector< T > |
| using | VertexMutex = wmtk::threading::VertexMutex |
Public Member Functions | |
| void | classify_curve_edges () |
| Mark the mesh edges that lie on the input's curve group (EdgeExtra2d::on_curve). | |
| void | init_region_potentials (double delta, double effective_factor) |
| void | assign_band_regions (bool log=true) |
| void | log_front_profile (size_t vid) |
| Diagnostic: the front objective of one vertex along its normal, offset term vs total. | |
| int | vertex_region (const size_t vid) const |
| int | edge_region (const size_t va, const size_t vb) const |
| const OffsetPotential2D & | potential_for_region (const int region) const |
| const OffsetPotential2D & | potential_for (const size_t vid) const |
| std::shared_ptr< const OffsetPotential2D > | potential_ptr_for (const size_t vid) const |
| const OffsetPotential2D & | potential_for_edge (const size_t va, const size_t vb) const |
| const OffsetPotential2D & | potential_for_face (const size_t fid) const |
| 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 intersection of everything that holds it, or null if nothing does. | |
| EnvelopeSetup | envelope_setup () const |
| std::string | envelope_key_name (int64_t tag) const |
| The name a tag or pseudo-tag prints under. | |
| bool | edge_is_complex_boundary (const Tuple &e) const |
| void | build_boundary_envelopes (const char *when, EnvelopeSetup setup) |
| void | rebuild_offset_envelope () |
| void | refresh_released_envelope () |
| void | check_no_vertex_on_both_surfaces (const char *when) const |
| void | optimize_offset_loop (bool final_stage=true, bool refine=true, const std::string &label=std::string()) |
| double | front_gradient_linf () |
| TopoOffsetTriMesh (Parameters &_m_offset_params, int _num_threads=0) | |
| double | tri_energy (size_t fid) const |
| THE per-cell energy, read from the mesh as it is – labels, neighbours and positions, nothing hypothetical. The 2D twin of TopoOffsetTetMesh::tet_energy(). | |
| double | tri_energy (size_t fid, double amips) const |
| tri_energy() with the face's AMIPS given, as the shared smoother has just computed it. | |
| double | smoothing_cell_energy (const size_t fid, const double quality) const override |
| double | weighted_amips (const double amips) const |
| 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. | |
| double | offset_term_weight () const |
| void | set_vertex_position (const size_t vid, const Vector2d &p) |
| Place a vertex, keeping its exact and rounded coordinates in step. | |
| bool | edge_is_offset (const size_t eid) const |
| bool | edge_is_region (const size_t eid) const |
| EdgeSnapshot2d | edge_snapshot (const size_t eid) const |
| void | restore_edge (const size_t eid, const EdgeSnapshot2d &s) |
| FaceSnapshot2d | face_snapshot (const size_t fid) const |
| void | restore_face (const size_t fid, const FaceSnapshot2d &s) |
| void | label_offset_boundary () |
| Tag the two tracked surfaces for the optimization phase. | |
| bool | face_in_region (const size_t fid) const |
Whether face fid belongs to the closed offset region, read from its label. | |
| bool | face_is_input_complex (const size_t fid) const |
| bool | vertex_is_on_surface (const size_t vid) const override |
| The substructure the link condition is evaluated against, derived not cached. | |
| bool | edge_is_on_surface (const std::array< size_t, 2 > &vids) const override |
| void | optimize_offset (const std::filesystem::path &output_file) |
| The 2D optimization phase: split / collapse / swap / smooth on the shared driver. | |
| bool | face_is_offset_band (const size_t fid) const |
| std::pair< double, double > | compute_distance_deviation () const |
| How far the offset boundary is from where it should be: {max, avg} over vertices. | |
| void | log_worst_dist_vertex () const |
| bool | edge_is_offset_surface_live (const Tuple &e) const |
| void | write_debug_pvd () const |
| bool | marching_split_edge_before (const Tuple &t) |
| bool | marching_split_edge_after (const Tuple &t) |
| 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 input complex, label != 0) towards p_out (the background endpoint) for the point where d(x) = D, D = m_construction_distance and d(x) the distance to the input complex through m_input_complex_bvh. From t = 0 the trace evaluates d at the current point and steps forward by D - d, the largest step that cannot cross the level set (d is 1-Lipschitz); it stops when |d - D| <= sphere_trace_target_rel_tol x D and returns true with p_new there. It returns false, p_new untouched, as soon as the current point reaches or passes p_out (t >= L: the level set is not on the edge) or would move behind p_in (d(p_in) already beyond the target); the caller then places the plain midpoint. Every step taken is longer than the tolerance, so the trace ends within L / (tol x D) steps; steps returns how many it took. No snapping away from the endpoints: a point found arbitrarily close to p_out is used as is. Same as 3D. | |
| bool | collapse_edge_before (const Tuple &t) override |
| Reject any collapse that violates the substructure link condition, and remember the survivor's sizing for sizing_collapse_min = false. | |
| 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 committed, then the base's after-hook, the coarsening bar, the sizing restore and the rest re-stamp. A refusal returns false, which the engine rolls back. | |
| bool | swap_edge_before (const Tuple &t) override |
| Reject a flip whose new edge already exists, and cache the energy rule's before-half. | |
| bool | swap_edge_after (const Tuple &t) override |
| bool | collapse_before_vertex (size_t v1, size_t v2) override |
| void | collapse_after_vertex (size_t v1, size_t v2) override |
| void | split_after_vertex (size_t v_new) override |
| 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. | |
| bool | smooth_before (const Tuple &t) override |
| User specified preparations and desideratas for an edge smooth. | |
| bool | smooth_after (const Tuple &t) override |
| User specified modifications and desideras after an edge smooth. | |
| bool | vertex_is_on_domain_boundary (const size_t vid) const |
| Identification only – no operation refuses the domain wall through these. | |
| void | init_surfaces_and_boundaries () |
| Classify every region boundary, build the per-tag containment envelopes, and tag the domain wall – once, from the input mesh, before offset construction runs. | |
| void | mark_input_complex_vertices () |
| void | warn_if_offset_reaches_domain_boundary () const |
| Warn if the offset band has grown into the domain boundary. | |
| polysolve::nonlinear::Solver & | smoothing_solver () |
| bool | smooth_vertex_2d_counted (const Tuple &t, const optimization::SmoothVertexOptions &opts, optimization::NewtonCounters &newton, bool *solved=nullptr) |
| 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). | |
| bool | is_repulsion_vertex (const size_t vid) const |
| double | repulsion_term_at (const size_t vid) const |
| double | repulsion_cell_term (const std::array< size_t, 3 > &vids) const |
| size_t | repulsion_cells_at (const size_t vid) const |
| std::shared_ptr< OffsetEnergy2D > | repulsion_energy (const size_t vid) const |
| O(v) for vid's smoothing objective, charged once per carrying face. | |
| bool | repulsion_embedding_kept (const std::vector< size_t > &fids) const |
| bool | simplex_in_input_complex (size_t a, size_t b) const |
| std::vector< double > | front_ring_measures () const |
| The exit test's ring measure of every vertex (energy_criterion()), chord for chord. | |
| double | ring_measure_at (size_t vid) const |
| One vertex's ring measure, from its own live front chords (NaN if it has none). | |
| void | crossing_snapshot (const std::string &pass, bool compare, bool match_positions) |
| void | update_attributes () override |
| double | ring_max_quality (size_t vid) const |
Max AMIPS over the faces incident to vid. -1 if it has none. | |
| double | face_flatness (size_t fid) const |
| Scale-invariant flatness: 2*area / longest_edge^2. | |
| void | needle_forensics () const |
| The full post-mortem on why nothing removes the flat faces. | |
| 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}. | |
| std::vector< Tuple > | offset_surface_edges_live_at (size_t vid) const |
| std::vector< std::array< size_t, 2 > > | offset_surface_edges () const |
| 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. | |
| 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 boundary iff some incident edge has the band on one side and a non-complex face on the other. Called from the hooks where an operation can change the answer – the collapse (v2 and the edge's link) and the split (the new vertex). Reads labels, never the flag it is writing. Leaves m_vertex_attribute[vid].m_is_on_surface alone (the base's union over every tracked surface). As in 3D. | |
| std::pair< size_t, size_t > | offset_membership_mismatches () const |
| void | check_offset_membership (const char *when) const |
| 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; see write_vtu(). Costs one pass over the live offset edges, no field evaluation. | |
| void | log_smooth_trace () const |
| void | log_region_edge_mask_health (const std::string &when) const |
| Are the tracked region boundaries actually contained by anything? | |
| void | audit_surface_containment (const std::string &when) const |
| Which tracked edges are outside their envelope, and by how much. | |
| bool | vertex_is_on_region (const size_t vid) const |
| Is this vertex on a region boundary – a tag boundary, or the domain wall. | |
| uint64_t | tag_bits (const CellTag &tags) const |
| 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 at all. | |
| uint64_t | edge_mask (const std::array< size_t, 2 > &vids) const |
| 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 symmetric difference init_surfaces_and_boundaries() classified by. | |
| std::shared_ptr< Envelope > | envelope_for_mask (uint64_t mask) const |
| The envelope a simplex with this boundary mask is contained in, or null. | |
| 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 containment requirement, and so does the offset boundary in the frozen-front final pass: m_offset_envelope holds it where the loop left it (see containment_for()). | |
| 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 deletion 3D made to its lower-strata point refusal. | |
| 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. | |
| std::shared_ptr< Envelope > | smoothing_containment_envelope (const size_t vid) const |
| std::shared_ptr< Envelope > | smoothing_containment_envelope (const size_t vid, const std::array< size_t, 2 > &, const EdgeAttributes &) const override |
| bool | smooth_front_vertex (const Tuple &t) |
| Placement of a front vertex: the shared smoother with the offset's options. See FrontSmooth2d.cpp. | |
| bool | smooth_repulsion_vertex (const Tuple &t) |
| A repulsion vertex in the passes and rounds before the march (repulsion_smoothing()): the shared smoother at the front's options, against the sum of the per-tri energy over its ring – w AMIPS, and O(v) once for every ring face that carries it (repulsion_energy(), repulsion_cell_term()); under offset_front_smooth_veto, the max of the per-tri energy over its ring may not rise. See FrontSmooth2d.cpp. | |
| bool | smooth_nonfront_vertex (const Tuple &t) |
| Every vertex off the front (and off the plastic medium): the shared smoother with TriWild's options (TriOptimizerMesh::smooth_after()), except that it minimises the per-cell energy – smoothing_extra_energy(), w AMIPS over the ring, the smoother adding no AMIPS term of its own – and the veto compares the max of tri_energy() over the ring, under the same key (offset_smooth_veto) and on the same vertices as the engine's AMIPS veto it replaces. See Optimize2d.cpp. | |
| std::shared_ptr< polysolve::nonlinear::Problem > | amips_energy (size_t vid) const |
| double | front_vertex_normal_gradient (size_t vid) const |
| Vector2d | front_vertex_move_direction (size_t vid) const |
| double | front_move_alignment (size_t vid) const |
| double | front_vertex_conv_ratio (size_t vid) const |
| 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_vertex_conv_ratio(): finite and within the one bar. Every caller that asks "is the placement of this front vertex done" – energy_criterion()'s vertex count and the placed split of the chords over the bar, and the adaptive-smoothing stop – goes through here. Unmeasurable (a non-finite ratio) is NOT placed. | |
| 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_conv_frac() – the squared chord measure, in units of the tolerance, 1 at the bar. The 2D twin of TopoOffsetTetMesh::face_offset_term(). | |
| double | edge_offset_term (const OffsetPotential2D &pot, const Vector2d &pa, const Vector2d &pb) const |
| Vector2d | front_vertex_normal (size_t vid) const |
| The field's outward unit direction at front vertex vid (zero where grad Phi vanishes). | |
| std::shared_ptr< polysolve::nonlinear::Problem > | front_objective (size_t vid) const |
| bool | vertex_carries_offset_term (const size_t vid) const |
| double | smoother_amips_weight (const size_t vid) const |
| std::shared_ptr< polysolve::nonlinear::Problem > | smoothing_extra_energy (const size_t vid) const override |
| std::shared_ptr< Envelope > | released_envelope () const |
| bool | edge_borders_released_boundary (const Tuple &e) const |
| bool | face_is_deformable (size_t fid) const |
| Under deform_others the same set as face_is_plastic(): every face outside the band. | |
| bool | face_is_plastic (size_t fid) const |
| void | stamp_plastic_rests () |
| Stamp rest := current for every plastic face; called before every operation group. | |
| bool | smooth_plastic_vertex (const Tuple &t) |
| bool | face_is_released_band (size_t fid) const |
| void | stamp_rest_face (size_t fid) |
| void | release_deformable_regions () |
| std::shared_ptr< polysolve::nonlinear::Problem > | rest_energy_for_vertex (size_t vid) const |
| std::shared_ptr< polysolve::nonlinear::Problem > | front_energy (size_t vid, const std::shared_ptr< const OffsetPotential2D > &pot) const |
| int | stencil_order () const |
| Samples per front chord; see for_each_edge_sample(). | |
| int | stencil_points_per_edge () const |
| void | check_offset_within_support (const char *when) const |
| Stop the run if any reachable band vertex has left the potential's support. | |
| DistanceSplit | distance_deviation_split () const |
| DistanceSplit | residual_split () const |
| std::vector< bool > | band_vertex_mask () const |
| double | max_band_vertex_distance () const |
| double | band_vertex_distance_error (const size_t vid) const |
| double | band_vertex_residual (const size_t vid) const |
| template<typename Visit > | |
| 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, wa, wb) with the barycentric weights that built it. The 2D twin of TopoOffsetTetMesh::for_each_face_sample(), the same rule one dimension down: order 0 is the two ends alone; order k >= 1 is the vertices of the chord cut into n = 2^(k-1) equal pieces plus the midpoint of each piece, so the counts are 2, 3, 5, 9, 17 for k = 0..4 (stencil_points_per_edge()). The ends are in it on purpose: the quantity sampled is a distance to the level set, which at an end is that vertex's own placement error (see edge_offset_term()). | |
| template<typename Visit > | |
| 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. | |
| EdgeSamples | offset_edge_samples (const Tuple &e) const |
| GradientSplit | gradient_split (bool include_edge_samples=true) const |
| EnergyCriterion | energy_criterion () |
| double | front_chord_target (size_t va, size_t vb, double len, double sag, double tube) const |
| size_t | refine_front_by_halving (const std::vector< EnergyCriterion::Refinable > &edges) |
| size_t | refine_front_by_halving (const std::vector< size_t > &vertices) |
| void | grade_sizing (double grade, const std::vector< size_t > &seeds) |
| size_t | grade_sizing_by_distance (const std::vector< size_t > &seeds) |
| SmoothingProgress | smoothing_progress (const std::vector< Vector2d > &before) |
Measure a pass: before holds every live vertex's position before it, indexed by vid. | |
| void | smooth_group_to_convergence (const char *group_name) |
| void | report_outside_support (const char *when, const DistanceSplit &s) const |
| bool | band_vertex_is_reachable (const size_t vid) const |
| size_t | refine_sizing_around_worst (double max_metric) override |
| TriWild's stall-driven sizing refinement, verbatim. | |
| 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. | |
| 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. | |
| 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. | |
| 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. | |
| void | report_needle (const char *op, size_t fid, double parent_q) const |
| Where the first needles come from – a tripwire, not a census. | |
| void | needle_scan (const char *when) const |
| void | collapse_pass_begin () override |
| bool | optimization_bare_coarsen_passes () const override |
| double | face_criterion_rel (const size_t fid) const |
| 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()), labelled "r<turn><tag><pass>_<op>" / "r<turn><tag>_end", tag S in the loop and F in the final pass; led by m_frame_prefix (i during init_optimize). Same scheme as 3D. | |
| void | optimization_debug_checkpoint () override |
| Called by the engine at every smoothing pass boundary (and only there in this component's loop): the per-pass smoothing accounting the 3D engine prints through its log_smoothing_pass_accounting() hook, which the 2D engine does not have, then the debug frame the base default writes. Writes the log and the frame, never the mesh. | |
| void | log_smoothing_pass_accounting () |
| 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. | |
| 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, and one "NNNNN<tab>label" line in <output>_frames.txt. Every debug frame the run writes – the input as loaded, the construction stages, and the optimization's own frames through write_smoothing_debug_output() – goes through this sequence, so the numbers are consecutive and the .txt says what each one is. The only debug files outside it are the ones that are not this mesh: <output>_input_complex.vtu and the phi grid. | |
| 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 | |
| bool | ambient_assert () |
| ensure ambient tag does not overlap any other tags in mesh. | |
| void | label_input_complex () |
| label input complex simplices as per boolean expression (or single body mode) | |
| bool | empty_input_complex () |
| check if the input complex is empty. Only valid after calling init_from_image(...). Checks if any vertices (therefore any simplices) are labelled 1, if not returns true | |
| void | init_input_complex_bvh () |
| Build the input complex's BVH and its smooth offset potential, from one extraction. | |
| void | init_offset_potential () |
| Build the smooth offset potential from the extraction init_input_complex_bvh() kept. | |
| bool | split_edge_before (const Tuple &t) override |
| User specified preparations and desideratas for an edge split. | |
| bool | split_edge_after (const Tuple &t) override |
| User specified modifications and desideratas after an edge split. | |
| bool | split_face_before (const Tuple &t) override |
| User specified preparations and desideratas for a face split. | |
| bool | split_face_after (const Tuple &t) override |
| User specified modifications and desideratas after a face split. | |
| bool | invariants (const std::vector< Tuple > &tris) override |
| User specified invariants that can't be violated. | |
| void | construct_offset (const std::filesystem::path &output_file) |
| void | marching_tris () |
| void | repulsion_smoothing () |
| void | relabel_input_complex () |
| size_t | flood_fill () |
| 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). | |
| bool | is_simplicially_embedded () const |
| check if the input complex (simplices labelled 1) are simplicially embedded w.r.t. the entire mesh | |
| 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) | |
| void | simplicial_embedding () |
| make mesh a simplicial embedding of the input complex (simplices labelled 1) | |
| void | set_offset_tri_tags () |
| update 'tags' data for triangles in the offset region (tris labelled 2) based on the given offset tag values in m_offset_params.offset_tag_value | |
| bool | offset_is_manifold () |
| verify that the closed offset region (simplices labelled 1 or 2) form a manifold region. This should be true for any offset. This function is for verification | |
| 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. | |
| void | write_input_complex (const std::string &path) |
| void | write_vtu (const std::string &path, MeshMap *output=nullptr) |
| size_t | edge_id_from_simplex (const simplex::Edge &e) const |
| get global id of edge from simplex::Edge object | |
| Tuple | get_tuple_from_edge (const simplex::Edge &e) const |
| get Tuple simplex::Edge object | |
Public Member Functions inherited from wmtk::TriOptimizerMesh | |
| std::vector< std::array< size_t, 2 > > | oriented_tracked_edges () const |
| std::vector< std::pair< size_t, int > > | tracked_curve_boundary () const |
| Vertices where the oriented tracked curves have a boundary (in minus out != 0), each with that sum. Empty for closed input curves; the operations preserve it. | |
| TriOptimizerMesh (OptimizerParameters ¶ms) | |
| size_t | get_partition_id (const Tuple &loc) const |
| void | partition_mesh () |
| void | partition_mesh_morton () |
| double | get_length2 (const Tuple &l) const |
| bool | is_inverted (const std::array< size_t, 3 > &vs) const |
| Orientation check, exact for the coordinates the vertices actually carry. | |
| bool | is_inverted (const Tuple &loc) const |
| bool | is_inverted (const size_t fid) const |
| bool | is_inverted_f (const Tuple &loc) const |
| Inversion check using only the double positions. | |
| bool | is_inverted_f (const size_t fid) const |
| double | get_quality (const std::array< size_t, 3 > &vs) const |
| double | get_quality (const Tuple &loc) const |
| double | get_quality (const size_t fid) const |
| std::tuple< double, double > | get_max_avg_energy () |
| void | mesh_improvement (int max_its=80) |
| std::tuple< double, double > | local_operations (const std::array< int, 4 > &ops, bool collapse_limit_length=true) |
| bool | storage_grows_on_demand () const override |
| bool | split_edge_is_due (const Tuple &e) const |
| bool | is_force_split_edge (const size_t v1, const size_t v2) const |
| void | split_all_edges () |
| bool | split_edge_before (const Tuple &t) override |
| User specified preparations and desideratas for an edge split. | |
| bool | split_edge_after (const Tuple &loc) override |
| User specified modifications and desideratas after an edge split. | |
| void | collapse_all_edges (bool is_limit_length=true) |
| bool | collapse_edge_before (const Tuple &t) override |
| User specified preparations and desideratas for an edge collapse including the link check as collapse prerequisite. | |
| bool | collapse_edge_after (const Tuple &t) override |
| User specified modifications and desideratas after an edge collapse. | |
| size_t | coarsen_mesh () |
| Coarsen the mesh without letting the max energy rise. | |
| bool | coarsen_collapse_edge (const Tuple &e, std::vector< Tuple > &new_tris) |
| One collapse under the coarsening rules, outside a coarsening pass. | |
| size_t | swap_all_edges () |
| Run TriWild's quality-improving interior edge-flip pass. | |
| double | swap_weight (const Tuple &t) const |
| bool | swap_edge_before (const Tuple &t) override |
| User specified preparations and desideratas for an edge swap including 1.can't swap on boundary edge. 2. when swap edge between v1, v2, there can't exist edges between the two opposite vertices v3, v4. | |
| bool | swap_edge_after (const Tuple &t) override |
| User specified modifications and desideras after an edge swap. | |
| void | smooth_all_vertices (size_t n_iters=1) |
| Run TriWild's vertex-smoothing pass. | |
| bool | use_colored_smoothing () const |
| Whether this pass smooths by color class (see OptimizerParameters::colored_smoothing). | |
| void | smooth_vertices_colored (const std::vector< std::pair< std::string, Tuple > > &ops) |
One smoothing sweep over ops (all "vertex_smooth"), one color class at a time. | |
| bool | smooth_before (const Tuple &t) override |
The smoothing hooks. A derived class overriding them – or invariants(), which a smooth also runs – must read every attribute outside the smoothed vertex's star (other vertices, and the edges and triangles not incident to the vertex) through const access, e.g. std::as_const(m_vertex_attribute)[u]. | |
| bool | smooth_after (const Tuple &t) override |
| User specified modifications and desideras after an edge smooth. | |
| Vector2d | smoothing_position (size_t vid) const |
| void | set_smoothing_position (size_t vid, const Vector2d &p) |
| double | active_quality_threshold () const |
| virtual std::vector< size_t > | active_vertices () const |
| virtual double | quality_rel (const size_t fid) const |
| A face's quality relative to the quality it is required to reach; <= 1 means it meets it. | |
| bool | round (const Tuple &v) |
| Round a vertex position to floating point, if that inverts no incident face. | |
| bool | is_edge_on_surface (const Tuple &loc) const |
| bool | is_tracked_edge (const Tuple &e) const override |
| std::vector< size_t > | tracked_feature_points () const override |
| void | record_curve_history (const std::string &metadata, const CurveSources &sources={}) |
| std::optional< history::CurveHistory > | finish_curve_history (const history::CurveHistory *input=nullptr) |
| Check the recorded curves against the tracked edges, stop recording, log what the history holds and return it, for the application to save (io::ComponentIO::save_history). None if not recording. | |
| bool | is_edge_on_surface (const std::array< size_t, 2 > &vids) const |
| bool | is_edge_on_bbox (const Tuple &loc) const |
| bool | is_edge_on_bbox (const std::array< size_t, 2 > &vids) const |
| bool | vertex_is_on_surface (const size_t vid) const override |
| Is a vertex part of the substructure. | |
| bool | edge_is_on_surface (const std::array< size_t, 2 > &vids) const override |
| Is an edge part of the substructure. | |
| bool | surface_segment_is_outside (const size_t a, const size_t b, const EdgeAttributes &attr) const |
| std::vector< std::array< size_t, 2 > > | get_edges_by_condition (std::function< bool(const EdgeAttributes &)> cond) const |
| void | gradation_smooth_sizing (double grade, const std::vector< size_t > &seeds) |
| Monotone (only-decreasing) gradation smoothing of the sizing field. | |
| void | throw_if_in_operation_pass (const char *call) const |
| void | enable_feature_point_tracking (const bool preserve) |
Turn on feature-point tracking, and the policy when preserve. | |
| bool | collapse_breaks_feature_point (const size_t v1_id, const size_t v2_id) const |
| bool | feature_point_ball_contains (const size_t vid, const Vector2d &p) const |
May vertex vid sit at p as far as its anchor goes? | |
| std::pair< size_t, size_t > | feature_retention (double *worst_ratio=nullptr) const |
| {feature points still represented within m_feature_eps, total feature points}. | |
Public Member Functions inherited from wmtk::TriMesh | |
| void | init (size_t n_vertices, const std::vector< std::array< size_t, 3 > > &tris) |
| void | init (const MatrixXi &F) |
| Generate the connectivity of the mesh from an IGL-style F matrix. | |
| std::vector< Tuple > | get_vertices () const |
| std::vector< Tuple > | get_edges () const |
| std::vector< Tuple > | get_faces () const |
| Tuple | tuple_from_edge (size_t vid1, size_t vid2, size_t fid) const |
| Tuple | tuple_from_vids (size_t vid0, size_t vid1, size_t vid2) const |
| simplex::Vertex | simplex_from_vertex (const Tuple &t) const |
| simplex::Edge | simplex_from_edge (const Tuple &t) const |
| simplex::Face | simplex_from_face (const Tuple &t) const |
| simplex::Face | simplex_from_face (const size_t fid) const |
| Tuple | tuple_from_simplex (const simplex::Face &s) const |
| simplex::SimplexCollection | simplex_incident_triangles (const simplex::Vertex &v) const |
| simplex::SimplexCollection | simplex_incident_triangles (const simplex::Edge &e) const |
| simplex::SimplexCollection | simplex_link_vertices (const simplex::Vertex &v) const |
| simplex::SimplexCollection | simplex_link_vertices (const simplex::Edge &e) const |
| simplex::SimplexCollection | simplex_link_edges (const simplex::Vertex &v) const |
| void | set_preallocation_factor (double factor) |
| Preallocation factor: init/consolidate reserve capacity = max(floor, ceil(factor * live_count)) so operations can grab fresh slots without resizing the storage. When a pass exhausts the reserved capacity the affected operations fail (retried later after a consolidate). Values < 1 are clamped to 1. | |
| double | preallocation_factor () const |
| bool | slots_exhausted () const |
| void | clear_slots_exhausted () |
| size_t | request_tri_slots (size_t n) |
| size_t | request_vert_slots (size_t n) |
| size_t | tri_capacity () const |
| get the current largest global fid | |
| size_t | vert_capacity () const |
| get the current largest global vid | |
| void | consolidate_mesh () |
| removing the elements that are removed | |
| std::shared_ptr< history::Recorder > | start_history (std::function< Eigen::Vector3d(size_t)> position) |
| Record every operation this mesh commits from now on (see wmtk/history). | |
| void | stop_history () |
| Stop recording. The recorder keeps the edits it has. | |
| std::shared_ptr< history::TrackedCurveRecorder > | start_curve_history (std::function< Eigen::Vector3d(size_t)> position) |
| Record every change to the tracked curves from now on (see wmtk/history/TrackedRecorder.hpp). | |
| void | stop_curve_history () |
| Stop recording curves. The recorder keeps what it has. | |
| size_t | resync_curve_history (const std::vector< std::array< size_t, 2 > > &edges) |
| std::vector< std::array< size_t, 2 > > | tracked_edges () const |
| Every tracked edge, each with its smaller vertex first. | |
| void | remove_tris_by_ids (const std::vector< size_t > &fids) |
| Mark the given triangles, and any vertex left without an incident triangle, as removed. | |
| Tuple | switch_vertex (const Tuple &t) const |
| a duplicate of Tuple::switch_vertex funciton | |
| Tuple | switch_edge (const Tuple &t) const |
| a duplicate of Tuple::switch_edge funciton | |
| std::optional< Tuple > | switch_face (const Tuple &t) const |
| a duplicate of Tuple::switch_face funciton | |
| bool | check_link_condition (const Tuple &t) const |
| prerequisite for collapse | |
| void | set_use_link_condition (bool use_it) |
| Should collapse_edge_before enforce the link condition? | |
| bool | use_link_condition () const |
| bool | check_mesh_connectivity_validity () const |
| verify the connectivity validity of the mesh | |
| bool | check_edge_manifold () const |
| verify the edge manifoldness of the mesh | |
| size_t | edge_valence (const TriMesh::Tuple &t) const |
| Number of triangles incident to the edge the Tuple points at. | |
| bool | is_boundary_edge (const TriMesh::Tuple &t) const |
| Does exactly one triangle share this edge? | |
| bool | is_manifold_edge (const TriMesh::Tuple &t) const |
| Do exactly two triangles share this edge? | |
| size_t | vertex_component_count (const size_t vid) const |
| Number of edge-connected components in the fan of a vertex. | |
| size_t | vertex_component_count (const TriMesh::Tuple &t) const |
| bool | is_manifold_vertex (const size_t vid) const |
| std::optional< Tuple > | switch_component (const TriMesh::Tuple &t) const |
| Jump to the next edge-connected component of the fan of the Tuple's vertex. | |
| bool | is_boundary_vertex (const TriMesh::Tuple &t) const |
| check if the vertex that's represented by a Tuple is at the boundary of the mesh | |
| bool | split_edge (const Tuple &t, std::vector< Tuple > &new_t) |
| virtual bool | collapse_edge (const Tuple &t, std::vector< Tuple > &new_t) |
| void | collapse_edge_conn (const Tuple &loc0, std::vector< Tuple > &new_tris, Tuple &return_t, size_t &new_vid, std::vector< std::pair< size_t, TriangleConnectivity > > &old_tris, std::vector< std::pair< size_t, VertexConnectivity > > &old_vertices, std::vector< std::pair< size_t, size_t > > &same_edge_vid_fid, std::vector< size_t > &n12_intersect_fids) |
| void | collapse_edge_rollback (size_t &new_vid, std::vector< std::pair< size_t, TriangleConnectivity > > &old_tris, std::vector< std::pair< size_t, VertexConnectivity > > &old_vertices, std::vector< std::pair< size_t, size_t > > &same_edge_vid_fid, std::vector< size_t > &n12_intersect_fids) |
| bool | swap_edge (const Tuple &t, std::vector< Tuple > &new_t) |
| bool | smooth_vertex (const Tuple &t) |
| bool | split_face (const Tuple &t, std::vector< Tuple > &new_t) |
| Split a face in 3 faces. | |
| size_t | get_valence_for_vertex (const Tuple &t) const |
| Count the number of the one ring tris for a vertex. | |
| size_t | vertex_valence (const size_t vid) const |
| Number of triangles incident to a vertex, by id. | |
| std::vector< Tuple > | get_one_ring_tris_for_vertex (const Tuple &t) const |
| Get the one ring tris for a vertex. | |
| const std::vector< size_t > & | get_one_ring_fids_for_vertex (const Tuple &t) const |
| const std::vector< size_t > & | get_one_ring_fids_for_vertex (const size_t vid) const |
| 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. | |
| void | get_one_ring_vids_for_vertex_duplicate (const size_t &t, std::vector< size_t > &one_ring) const |
| std::vector< size_t > | get_incident_fids_for_edge (const Tuple &t) const |
| std::vector< size_t > | get_incident_fids_for_edge (const size_t vid0, const size_t vid1) const |
| std::vector< Tuple > | get_one_ring_edges_for_vertex (const Tuple &t) const |
Get all edges that are incident to the vertex of Tuple t. | |
| std::vector< Tuple > | get_one_ring_edges_for_vertex (const size_t vid) const |
| std::array< Tuple, 3 > | oriented_tri_vertices (const Tuple &t) const |
| Get the incident vertices for a triangle. | |
| std::array< size_t, 3 > | oriented_tri_vids (const Tuple &t) const |
| Get the incident vertices for a triangle. | |
| std::array< size_t, 3 > | oriented_tri_vids (const size_t i) const |
| std::array< Tuple, 2 > | get_edge_vertices (const Tuple &t) const |
| std::array< size_t, 2 > | get_edge_vids (const Tuple &t) const |
| Tuple | tuple_from_tri (size_t fid) const |
| Tuple | tuple_from_vertex (size_t vid) const |
| Tuple | tuple_from_edge (size_t fid, size_t local_eid) const |
| std::tuple< Tuple, size_t > | tuple_from_edge (const std::array< size_t, 2 > &vids) const |
| 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. | |
| void | start_protect_attributes () |
| Start the phase where the attributes that will be modified can be recorded. | |
| void | release_protect_attributes () |
| End the modification phase. | |
| void | rollback_protected_attributes () |
| rollback the attributes that are modified if any condition failed | |
| int | release_vertex_mutex_in_stack () |
| int | release_vertex_mutex_to (size_t mark) |
Release the mutexes taken since the release stack held mark entries. | |
| bool | try_set_vertex_mutex_n_ring (const Tuple &v, int threadid, int n) |
Lock every vertex within graph distance n of v, the seed included. | |
| bool | try_set_vertex_mutex_n_ring (size_t vid, int threadid, int n) |
| bool | try_set_edge_mutex_n_ring (const Tuple &e, int threadid, int n) |
| try_set_vertex_mutex_n_ring seeded from both ends of an edge. | |
| bool | try_set_face_mutex_one_ring (const Tuple &f, int threadid) |
| try lock the one-ring neighboring triangles' incident vertices. | |
| void | for_each_face (const std::function< void(const Tuple &)> &) |
| perform the given function for each face | |
| void | for_each_edge (const std::function< void(const Tuple &)> &) |
| perform the given function for each edge | |
| void | for_each_vertex (const std::function< void(const Tuple &)> &) |
| perform the given function for each vertex | |
| simplex::SimplexCollection | get_surface_edges_for_vertex (const size_t vid) const |
| Get all edges on the surface that are incident to vid. | |
| size_t | get_order_of_edge (const std::array< size_t, 2 > &vids) const |
| Compute the order of an edge. | |
| size_t | get_order_of_vertex (const size_t vid) const |
| Get the order of a vertex. | |
| bool | substructure_link_condition (const Tuple &e_tuple) const |
| Link condition that also considers substructures. | |
| size_t | tri_storage_capacity () const |
| size_t | vert_storage_capacity () const |
| bool | try_set_vertex_mutex_two_ring (const Tuple &v, int threadid) |
| Lock v's one-ring and, partially, its two-ring. See the note above. | |
| bool | try_set_edge_mutex_two_ring (const Tuple &e, int threadid) |
| Lock the edge's one-ring and, partially, its two-ring. See the note above. | |
| bool | try_set_vertex_mutex_one_ring (const Tuple &v, int threadid) |
| Lock v and its one-ring. Complete, unlike the two-ring pair. | |
| size_t | cell_capacity () const |
| size_t | cell_storage_capacity () const |
| Tuple | tuple_from_cell (size_t cid) const |
| size_t | cell_slot_bound (const Tuple &t) const |
| void | reserve_free_slots (size_t cells, size_t verts) |
Public Member Functions inherited from wmtk::RationalPositions | |
| size_t | round_all_vertices () |
| Try to round every un-rounded vertex; returns the number reclaimed. | |
| bool | round_and_check_all_rounded () |
| Run the sweep, then report whether the mesh is now fully rounded. | |
Public Attributes | |
| std::array< size_t, 3 > | m_init_counts = {{0, 0, 0}} |
| size_t | m_tags_count |
| int64_t | m_curve_tag = -1 |
| MatrixXd | m_curve_V |
| The curve group as loaded, kept because the classification below is redone on demand. | |
| MatrixXi | m_curve_E |
| std::shared_ptr< SimplicialComplexBVH > | m_input_complex_bvh |
| The input complex as loaded. Built once, never rebuilt. | |
| std::shared_ptr< OffsetPotential2D > | m_offset_potential |
| The smooth offset potential, and with it the definition of the offset itself. | |
| int | m_n_regions = 0 |
| One field per connected piece of the input complex, and which one each band vertex is placed on. | |
| std::vector< std::shared_ptr< OffsetPotential2D > > | m_region_potentials |
| one per piece | |
| std::vector< int64_t > | m_phi_vert_region |
| per m_phi_V row: region index | |
| std::vector< int64_t > | m_phi_seg_region |
| per m_phi_E row: region index, -1 unknown | |
| std::vector< int64_t > | m_phi_face_region |
| per m_phi_F row: region index, -1 unknown | |
| std::vector< int64_t > | m_phi_point_region |
| per m_phi_P entry: region index, -1 unknown | |
| std::vector< int > | m_face_region |
| per face: band's region, -1 none, -2 reached from two | |
| std::vector< int > | m_vertex_region |
| per vertex: region of its band faces, -1 / -2 as above | |
| std::map< int64_t, std::shared_ptr< Envelope > > | m_tag_envelopes |
| One containment envelope per input tag, ambient included. Both phases. | |
| std::map< int64_t, int > | m_tag_bit |
| std::map< uint64_t, std::shared_ptr< Envelope > > | m_isect_cache |
| std::mutex | m_isect_mutex |
| std::map< uint64_t, std::shared_ptr< Envelope > > | m_offset_isect_cache |
| Memoized "region tubes AND the offset envelope", keyed by the region mask. | |
| bool | m_freeze_front = false |
| The final pass: front vertices are not smoothed (see smooth_before()). | |
| 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_envelope, built from the boundary as the loop left it just before that pass (containment_for() holds the final pass's operations to it). Null until then: the loop holds the offset boundary to no envelope. Unlike m_tag_envelopes, which must never be rebuilt. As in 3D. | |
| bool | m_init_optimize = false |
| std::string | m_frame_prefix |
| Leads every debug frame label: i during the init_optimize loop, empty otherwise. | |
| double | m_front_gradient_reference = 0. |
| Its value on the band as constructed, measured once before turn 1. | |
| EdgeSplitMode | m_edge_split_mode = EdgeSplitMode::Midpoint |
| std::map< std::string, int64_t > | m_tag_name_to_id |
| std::map< int64_t, std::string > | m_tag_id_to_name |
| CellTag | m_offset_output_tag_ids |
| bool | m_singlebody = false |
| int64_t | m_single_tag |
| bool | m_has_envelope = false |
| MatrixXd | m_V_envelope |
| MatrixXi | m_F_envelope |
| Parameters & | m_offset_params |
| The base holds only wmtk::OptimizerParameters; this is the same object, typed. | |
| VertexExtraCol | m_vertex_extra |
| EdgeExtraCol | m_edge_extra |
| FaceExtraCol | m_face_extra |
| size_t | m_worst_dist_vid = static_cast<size_t>(-1) |
| std::vector< std::array< double, 8 > > | optimization_metrics |
| int | m_round = 0 |
| size_t | m_debug_seq = 0 |
| Monotonic frame counter for the debug timeline. Mutable because the write hook is const. | |
| std::vector< std::string > | m_debug_frame_labels |
| std::map< std::string, std::vector< size_t > > | m_debug_pvd_series |
| int | m_debug_pass = 0 |
| int | m_debug_last_round = -1 |
| char | m_debug_last_tag = '?' |
| std::string | m_debug_last_prefix = "?" |
| bool | m_converged = false |
| bool | m_quality_converged = true |
| double | m_quality_max_amips = 0. |
| std::atomic< int > | iter_cnt_split = 0 |
| std::atomic< int > | iter_cnt_collapse = 0 |
| std::atomic< int > | iter_cnt_swap = 0 |
| std::atomic< int > | iter_cnt_collapse_offset_removed {0} |
| std::atomic< int > | iter_cnt_collapse_offset_reject {0} |
| Operations refused because they would have left an offset-boundary chord over tolerance. | |
| std::atomic< int > | iter_cnt_swap_offset_reject {0} |
| std::atomic< int > | iter_cnt_collapse_energy_reject {0} |
| std::atomic< int > | iter_cnt_swap_energy_reject {0} |
| std::atomic< int > | iter_cnt_split_offset_before {0} |
| std::atomic< int > | iter_cnt_split_offset {0} |
| wmtk::threading::enumerable_thread_specific< OptSplitCache2d > | m_opt_split_cache |
| size_t | m_marching_root_splits = 0 |
| size_t | m_marching_midpoint_splits = 0 |
| size_t | m_marching_trace_steps = 0 |
| size_t | m_marching_trace_steps_max = 0 |
| double | m_construction_distance = 0. |
| SmoothTrace | m_smooth_trace |
| optimization::NewtonCounters | m_newton |
| optimization::NewtonCounters | m_newton_front |
| std::vector< FrontSolveRecord > | m_front_solve_log |
| std::mutex | m_front_solve_log_mutex |
| std::atomic< size_t > | m_front_veto_asked {0} |
| std::atomic< size_t > | m_front_veto_fired {0} |
| std::shared_ptr< const OffsetPotential2D > | m_repulsion_potential |
| optimization::NewtonCounters | m_newton_repulsion |
| std::atomic< size_t > | m_repulsion_veto_asked {0} |
| std::atomic< size_t > | m_repulsion_veto_fired {0} |
| std::shared_ptr< OffsetEnergy2D > | m_repulsion_term |
| std::atomic< size_t > | m_repulsion_embed_refused {0} |
| std::array< std::atomic< size_t >, kGradBins > | m_front_grad_abs {} |
| std::array< std::atomic< size_t >, kGradBins > | m_front_grad_rel {} |
| optimization::NewtonCounters | m_newton_plastic |
| std::vector< double > | m_cross_ring |
| std::vector< Vector2d > | m_cross_pos |
| bool | m_cross_valid = false |
| std::atomic< size_t > | m_cross_own {0} |
| std::atomic< size_t > | m_cross_neighbour {0} |
| 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. | |
| std::atomic< size_t > | m_needle_smooth_offered {0} |
| std::atomic< size_t > | m_needle_smooth_reached {0} |
| std::atomic< size_t > | m_needle_smooth_fixed {0} |
| std::atomic< size_t > | m_needle_smooth_stationary {0} |
| std::atomic< size_t > | m_needle_smooth_reports {0} |
| Worst-case record: the best (lowest) ring max any needle-adjacent smooth achieved. | |
| std::atomic< size_t > | m_flat_created_split {0} |
| std::atomic< size_t > | m_flat_created_collapse {0} |
| std::atomic< size_t > | m_flat_worsened_split {0} |
| std::atomic< size_t > | m_flat_genesis_reports {0} |
| wmtk::threading::enumerable_thread_specific< double > | m_collapse_parent_flatness |
| The flattest face in the collapse's ring before it ran, for record_flatness(). | |
| wmtk::threading::enumerable_thread_specific< double > | m_collapse_survivor_sizing |
| wmtk::threading::enumerable_thread_specific< double > | m_collapse_energy_before |
| wmtk::threading::enumerable_thread_specific< CollapseCells > | m_collapse_cells |
| 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 whose offset membership a collapse can change. | |
| wmtk::threading::enumerable_thread_specific< double > | m_swap_energy_before |
| std::set< int64_t > | m_deform_tags |
| The released tags. Filled by release_deformable_regions(); empty = feature inactive. | |
| std::set< int64_t > | m_source_tags |
| std::shared_ptr< Envelope > | m_released_envelope |
| std::atomic< bool > | m_released_tube_dirty {false} |
| std::mutex | m_released_mutex |
| bool | m_plastic_active = false |
| set in optimize_offset() when deform_others | |
| std::optional< EnergyCriterion > | m_energy_verdict |
| std::atomic< size_t > | m_deg_split_created {0} |
| size_t | m_deg_prev_split_created = 0 |
| std::atomic< size_t > | m_needle_reports {0} |
| std::set< std::pair< long, long > > | m_stuck_prev_cells |
| size_t | m_stuck_calls = 0 |
| MatrixXd | m_phi_V |
| MatrixXi | m_phi_E |
| MatrixXi | m_phi_F |
| the complex faces, in the same vertex index space (for per-region BVHs) | |
| std::vector< int > | m_phi_P |
Public Attributes inherited from wmtk::TriOptimizerMesh | |
| VertAttCol | m_vertex_attribute |
| EdgeAttCol | m_edge_attribute |
| bool | m_tracks_orientation = false |
| The application set EdgeAttributes::m_orientation at insertion, so the tracked curves carry the input's direction and the operations keep it. See TetOptimizerMesh::m_tracks_orientation; this is the same thing one dimension down. | |
| FaceAttCol | m_face_attribute |
| AttributeContainerGroup | m_vertex_attr_group |
| What p_vertex_attrs points at, so a derived class can register more. | |
| AttributeContainerGroup | m_edge_attr_group |
| What p_edge_attrs points at, so a derived class can register more. | |
| AttributeContainerGroup | m_face_attr_group |
| What p_face_attrs points at, so a derived class can register more. | |
| OptimizerParameters & | m_params |
| std::shared_ptr< Envelope > | m_envelope |
| double | m_envelope_eps = -1 |
| double | m_s_amips = -1 |
| double | m_s_envelope = -1 |
| wmtk::threading::enumerable_thread_specific< std::unique_ptr< polysolve::nonlinear::Solver > > | m_solver |
| optimization::SmoothRejectCounters | m_smooth_rejects |
| Why smoothing attempts were refused, reported once per pass. | |
| bool | m_collapse_limit_length = true |
| int | m_debug_print_counter = 0 |
| std::string | m_debug_pass_name |
| size_t | m_tags_count = 0 |
| std::map< int64_t, std::string > | m_tag_id_to_name |
| std::map< std::string, int64_t > | m_tag_name_to_id |
| int | m_iterations_used = 0 |
| Shared TriWild/SimWild outer optimization schedule. | |
| std::set< simplex::Edge > | m_force_split_edges |
| size_t | m_force_split_count = 0 |
| std::unique_ptr< std::atomic< int >[]> | m_high_valence_claim |
| size_t | m_high_valence_claim_size = 0 |
| std::atomic< size_t > | m_high_valence_rejects = 0 |
| size_t | m_split_order_waits = 0 |
| The current split pass's ExecutePass::waits() and wait_defects(). | |
| size_t | m_split_order_wait_defects = 0 |
| CoarsenStats | m_coarsen_stats |
| FeaturePointAttCol | m_feature_point_attribute |
| Registered with m_vertex_attr_group only once enable_feature_point_tracking() ran. | |
| std::vector< Vector2d > | m_feature_points |
| double | m_feature_eps = -1 |
| bool | m_track_feature_points = false |
| Whether the shared operations maintain the ids (enable_feature_point_tracking()). | |
| bool | m_preserve_feature_points = false |
Whether the collapse and smoothing policy is on (its preserve argument). | |
| std::atomic< size_t > | m_feature_rejects = 0 |
| bool | m_in_operation_pass = false |
| True while a pass of the shared operations runs. | |
Public Attributes inherited from wmtk::TriMesh | |
| AbstractAttributeContainer * | p_vertex_attrs = nullptr |
| AbstractAttributeContainer * | p_edge_attrs = nullptr |
| AbstractAttributeContainer * | p_face_attrs = nullptr |
| bool | m_check_smoothing_stays_in_star = false |
| While true, smooth_vertex() checks that the smooth recorded nothing outside the vertex's star – the vertex itself and the edges, faces and triangles incident to it – and throws if it did. | |
| wmtk::threading::enumerable_thread_specific< std::vector< size_t > > | mutex_release_stack |
| int | NUM_THREADS = 0 |
Static Public Attributes | |
| static constexpr int64_t | m_wall_tag = -2 |
| pseudo-tag: the domain wall's tube | |
| static constexpr int64_t | m_complex_tag = -3 |
| static constexpr int | INPUT_SURFACE_CLASS = 0 |
| SurfaceTagAttributes::m_surface_class: which of the two tracked surfaces an edge belongs to. Same scheme as 3D. | |
| static constexpr int | OFFSET_SURFACE_CLASS = 1 |
| static constexpr double | kSmoothRelGradNormTol = 1e-6 |
| static constexpr int | kGradBins = 22 |
| static constexpr double | kFlatThreshold = 1e-3 |
| static constexpr size_t | kNeedleReports = 12 |
| static constexpr double | kNeedleQuality = 1e6 |
| What counts as a needle for the tripwire – deliberately far below MAX_ENERGY. | |
Static Public Attributes inherited from wmtk::TriOptimizerMesh | |
| static constexpr double | MAX_ENERGY = 1e50 |
| The sentinel get_quality returns for a face AMIPS2D cannot score. | |
| static constexpr size_t | NO_FEATURE_POINT = std::numeric_limits<size_t>::max() |
| A vertex that stands for no feature point. See FeaturePointAttributes. | |
Static Public Attributes inherited from wmtk::TriMesh | |
| static constexpr int | EDGES_PER_CELL = 3 |
Private Member Functions | |
| void | sort_edges_by_length (std::vector< simplex::Edge > &edges) |
| sort vector of edge simplices in place by decreasing length | |
Private Attributes | |
| wmtk::threading::enumerable_thread_specific< EdgeSplitCache > | edge_split_cache |
| wmtk::threading::enumerable_thread_specific< FaceSplitCache > | face_split_cache |
Additional Inherited Members | |
Static Public Member Functions inherited from wmtk::TriMesh | |
| static bool | dry_run () |
| Whether operations on this thread are dry runs (see wmtk/OperationDryRun.hpp). | |
| static SlotUsage | slot_usage_of_this_thread () |
Protected Member Functions inherited from wmtk::TriOptimizerMesh | |
| void | on_slot_storage_grown () override |
| virtual std::tuple< double, double > | optimization_quality_stats () |
| virtual double | optimization_stop_metric () const |
| virtual bool | optimization_stop_at_float () const |
| virtual bool | optimization_stalled (double prev, double cur) |
Whether an iteration that moved the metric from prev to cur is stalled, and the sizing refinement should therefore fire. | |
| virtual void | collapse_pass_end (size_t) |
| std::vector< size_t > | all_vertex_ids () const override |
| Every live vertex, in the mesh's own iteration order. | |
| bool | vertex_is_rounded (const size_t vid) const override |
| Whether this vertex's double position is currently trusted. | |
| bool | round_vertex (const size_t vid) override |
Protected Member Functions inherited from wmtk::TriMesh | |
| void | vertex_fan_components (size_t vid, std::vector< size_t > &component_of, std::vector< size_t > &representatives) const |
| void | resize_mutex (size_t v) |
Protected Attributes inherited from wmtk::TriOptimizerMesh | |
| uint32_t | m_op_epoch = 0 |
| wmtk::threading::enumerable_thread_specific< SplitInfoCache > | split_cache |
| wmtk::threading::enumerable_thread_specific< CollapseInfoCache > | collapse_cache |
| bool | m_coarsen_mode = false |
| Set for the duration of coarsen_mesh(); read-only while a pass is running. | |
Protected Attributes inherited from wmtk::RationalPositions | |
| std::atomic< bool > | m_all_rounded = false |
| True when every vertex is known to be rounded. | |
The offset's 2D mesh, on the shared 2D optimizer.
Mirrors TopoOffsetTetMesh: the construction phase is entirely its own, and the optimization phase that follows is wmtk::TriOptimizerMesh's.
Two surfaces are tracked. Every tag-region boundary (input complex and domain wall included) keeps the primary class 0 and is held in its tags' envelopes, as triwild holds its input; the offset boundary is OFFSET_SURFACE_CLASS, in 2D exactly the edges across which the incident face labels differ, so label_offset_boundary() derives it rather than storing it. Class-0 edges may move within their tubes; only the offset one is driven toward target_distance.
|
strong |
Which boundaries the region-class envelopes hold, and how they are built.
PerTag (deform_others false): one exact tube per input tag around that tag's boundary segments, the domain wall in the tags of its wall faces. A vertex carries the bit of every tube it lies on and is contained in their intersection, so every region boundary – the input complex and the wall included – is held.
WallComplex (deform_others true): exactly two tubes, the domain wall and the boundary of the input complex (any dimension, any manifoldness: it is a set of segments), under the pseudo-tags m_wall_tag / m_complex_tag. Every other region boundary carries no bit and is held by nothing; the medium around it is plastic, see face_is_plastic().
Either way build_boundary_envelopes() derives the masks and tubes from the mesh as it stands when called: at load (PerTag, before the complex is labelled), when deform_others switches the setup at construction, and fresh at the start of the final pass. The offset tube is separate and unchanged.
| std::shared_ptr< polysolve::nonlinear::Problem > wmtk::components::topological_offset::TopoOffsetTriMesh::amips_energy | ( | size_t | vid | ) | const |
w AMIPS over vid's one-ring, the per-cell energy's AMIPS part in the smoother's form (optimization::AMIPSEnergy2D, the engine's own quality), w = offset_amips_weight. The 3D twin is amips3_energy(), AMIPS^3 being the 3D engine's quality.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::assign_band_regions | ( | bool | log = true | ) |
Rebuild m_*_region from the mesh. log false suppresses the per-turn "[regions]" line: write_vtu() re-derives the map for its frame diagnostics and puts the old one back.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::audit_surface_containment | ( | const std::string & | when | ) | const |
Which tracked edges are outside their envelope, and by how much.
The shared pass driver's sanity_checks() reports "Edge [a, b] is outside!" but not which envelope refused it, and the answer forks the diagnosis: offset-class (mask 0) means the final pass moved the offset boundary out of the tube holding it; region-class (mask != 0) means a tag-region boundary has drifted off the input partition, refused by that tag's tube or by the intersection of several at a junction.
Reports per-endpoint distance to each real member tube. A multi-bit mask dispatches an IntersectionEnvelope, which must never be asked squared_distance (CompositeEnvelopes.hpp: its BVH is null), so the members are walked individually instead of querying the composite.
Call it at construction as well as inside the loop: an edge already outside before any operation runs is a construction defect, a different bug. Diagnostic only.
< furthest ALONG-SEGMENT distance to a real member tube
< furthest ENDPOINT distance – 0 means both ends are inside
< where along the segment worst_d sits; ~0.5 means a chord bulge
| double wmtk::components::topological_offset::TopoOffsetTriMesh::band_vertex_distance_error | ( | const size_t | vid | ) | const |
|dist(vid, input complex) - target_distance|. Diagnostic: the Euclidean offset, which the level set only coincides with away from reentrant features.
|
inline |
Whether vid is a band vertex the optimizer could still place at target_distance.
Only the domain boundary disqualifies one. m_is_on_input must not: the flag is over-broad – splits propagate it and collapses OR it onto survivors – so a vertex carrying it may sit a full target_distance from the complex, which is exactly where the offset wants it. check_no_vertex_on_both_surfaces() already throws on the genuinely contradictory case, so any vertex reaching here with the flag is placeable. Same rule as 3D.
| std::vector< bool > wmtk::components::topological_offset::TopoOffsetTriMesh::band_vertex_mask | ( | ) | const |
Which vertices lie on the band's outer surface – the one that is supposed to sit at target_distance. Shared by every measurement so they all agree on what "the band" is.
| double wmtk::components::topological_offset::TopoOffsetTriMesh::band_vertex_residual | ( | const size_t | vid | ) | const |
How far vid is from the level set Phi = c, as a length. This is what the loop converges on and what the sizing field refines by.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::build_boundary_envelopes | ( | const char * | when, |
| EnvelopeSetup | setup | ||
| ) |
Rebuild every region-class tube and every vertex's boundary mask from the current mesh under setup. PerTag at load (the complex is not labelled yet), WallComplex when deform_others switches it at construction, envelope_setup() fresh at the final pass. The tracked-edge flags are left alone: they are the topology the operations maintain. when labels the log line.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::check_no_vertex_on_both_surfaces | ( | const char * | when | ) | const |
Hard error if any vertex is on both the input complex and the offset boundary – a state no placement satisfies. Called at construction.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::check_offset_membership | ( | const char * | when | ) | const |
Log offset_membership_mismatches() and throw when it is not {0, 0}. perform_sanity_checks only.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::check_offset_within_support | ( | const char * | when | ) | const |
Stop the run if any reachable band vertex has left the potential's support.
Beyond dhat, Phi is identically zero with a zero gradient: the vertex is given no direction back, its residual saturates instead of growing, and the sizing field refines around a vertex nothing can move. There is no recovery from that state and no honest report of it either, so it is a hard error – the answer to it firing is a larger offset_dhat_factor.
Called once per optimization iteration, and once on the band as constructed.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::classify_curve_edges | ( | ) |
Mark the mesh edges that lie on the input's curve group (EdgeExtra2d::on_curve).
Geometric, against the curve's own tube (the same eps the tag envelopes use), because triwild writes its curves with their own vertices and there is no index to match on.
Called whenever the complex is labelled, not once at load: the flag is a property of an edge and nothing propagates it through split and collapse, so an operation pass shreds it. Re-deriving it is exact and costs one tube query per edge.
|
overridevirtual |
Reimplemented from wmtk::TriOptimizerMesh.
|
overridevirtual |
Reimplemented from wmtk::TriOptimizerMesh.
|
overridevirtual |
THE ENERGY RULE for a collapse (see tri_energy()), on the real mesh once the connectivity is committed, then the base's after-hook, the coarsening bar, the sizing restore and the rest re-stamp. A refusal returns false, which the engine rolls back.
The 3D twin applies the rule in collapse_after_connectivity(), a hook the 2D engine does not have; here it runs as the first thing in the after-hook, BEFORE TriOptimizerMesh::collapse_edge_after(), whose last step (collapse_after_vertex()) counts the collapse as done. A collapse moves no vertex, so the energy is read from the mesh as it now is.
Reimplemented from wmtk::TriMesh.
|
overridevirtual |
Reject any collapse that violates the substructure link condition, and remember the survivor's sizing for sizing_collapse_min = false.
The base applies the link condition only when both endpoints already sit on a tracked surface or the bbox, which is the right rule for tetwild and simwild but not here: the offset region is a thin band, and a collapse with only one endpoint on the boundary can still pinch its two sides together and make the region non-manifold. The offset asks unconditionally. The energy rule's before-half is taken in collapse_before_vertex(), which the base calls before its scoring loop. As in 3D.
Reimplemented from wmtk::TriMesh.
|
overridevirtual |
Diagnostic only: the base offers no per-iteration hook except this one, so the needle population scan rides on it. Calls nothing else – the base default is empty.
Reimplemented from wmtk::TriOptimizerMesh.
|
inlineoverridevirtual |
The energy rule's EARLY half, on the engine's lower bound.
The engine scores each reshaped face before the collapse exists and hands its AMIPS in as q. tri_energy() of that face is weighted_amips(q) plus a non-negative chord term, so q alone above the before-maximum (collapse_before_vertex()) already decides: the rule that collapse_edge_after() applies on the real faces would refuse too. Refusing here costs nothing; refusing there costs the collapse and its rollback. Same rule, same counter, applied as soon as it can be. The engine's own ring_max (AMIPS over v1's ring) is not read; TriWild's exemption for an unrounded v1 is kept. Off with offset_collapse_veto. As in 3D.
Reimplemented from wmtk::TriOptimizerMesh.
| std::pair< double, double > wmtk::components::topological_offset::TopoOffsetTriMesh::compute_distance_deviation | ( | ) | const |
How far the offset boundary is from where it should be: {max, avg} over vertices.
The absolute error |dist(v, input complex) - target_distance| over the offset-boundary vertices only. The max is what the optimization converges against – the offset is only as good as its worst-placed vertex, and an average hides a stretch far off the target. Mirrors TopoOffsetTetMesh::compute_distance_deviation().
| void wmtk::components::topological_offset::TopoOffsetTriMesh::construct_offset | ( | const std::filesystem::path & | output_file | ) |
Construction, start to finish, on the input mesh as given: the simplicial embedding, repulsion_smoothing() when asked for, marching_tris() and the offset tagging. The optimization is optimize_offset(), which the driver calls afterwards.
| std::shared_ptr< Envelope > wmtk::components::topological_offset::TopoOffsetTriMesh::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 intersection of everything that holds it, or null if nothing does.
The single place the two containment families are composed. region_mask dispatches through envelope_for_mask() (itself an intersection when the mask is multi-bit, which is what pins a junction to the junction); on_offset adds m_offset_envelope, in the frozen-front final pass only. Every operation's containment check reaches this through surface_envelope_for_edge(), so in the final pass split, collapse and swap hold the offset boundary to its tube; in the loop they do not. The smoother asks with on_offset false (smoothing_containment_envelope()): placing the front is what moves the offset boundary, and a tube around where it currently sits would cap how far it can travel. As in 3D.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::crossing_snapshot | ( | const std::string & | pass, |
| bool | compare, | ||
| bool | match_positions | ||
| ) |
Take the snapshot; with compare, log the crossings against the previous one first. match_positions: an id counts as the same vertex only at the same position (operation passes, which move no vertex; a storage retry inside one renumbers).
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::edge_borders_released_boundary | ( | const Tuple & | e | ) | const |
Whether this edge lies on a released region's boundary, by the incident faces' current tag symmetric difference – the same test the release freed vertices by.
|
inline |
Diagnostic only: which tag boundaries the incident faces say this edge lies on right now – the same symmetric difference init_surfaces_and_boundaries() classified by.
Nothing dispatches or propagates from this. It is only trustworthy while the face tags are still the input's own: construct_offset() replaces the tags of every face the band grows through, after which this is empty across every region edge the band swallowed, so deriving split masks from it mints uncontained region vertices (log_region_edge_mask_health counts the divergence). New vertices take the endpoints' mask AND behind the parent edge's class gate instead – 3D's rule at both of its split sites – which is what stops a chord, not being a region-class edge, from over-claiming a tube a full target_distance away.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::edge_is_complex_boundary | ( | const Tuple & | e | ) | const |
Whether this edge lies on the boundary of the input complex: exactly one incident face carries label 1, or the edge itself does while neither face does (a curve or edge piece).
|
inline |
Whether edge eid is on the offset boundary / carries input geometry / bounds some other tag region.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::edge_is_offset_surface_live | ( | const Tuple & | e | ) | const |
Whether this edge is on the band's outer surface, recomputed from the tags on every call – the live counterpart of edge_is_offset(), for use inside the operation passes.
|
overridevirtual |
The 2D optimization phase. The operations themselves – split, collapse, the quality-driven edge flip, and the driver that sequences them – are wmtk::TriOptimizerMesh's. What is here is only what the offset knows: where its two tracked surfaces are, and how a vertex on the offset boundary is allowed to move.
Reimplemented from wmtk::TriMesh.
|
inline |
... and whether it bounds a region – any tracked edge that is not the offset boundary. The input complex is included, and deliberately: both are held by the same per-tag envelopes and neither is what the optimization moves. Same shape as 3D's face_is_region().
|
inline |
A segment lies on a boundary only if both ends do: the AND of its endpoints' masks. The 2D twin of face_mask(), which ANDs three.
| double wmtk::components::topological_offset::TopoOffsetTriMesh::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_conv_frac() – the squared chord measure, in units of the tolerance, 1 at the bar. The 2D twin of TopoOffsetTetMesh::face_offset_term().
Every reader of the chord measure goes through it: the logs print its root, energy_criterion()'s exit, refinement and ring measures, the debug frames' front_err_ratio and front_ring_ratio, and the per-cell energy tri_energy(), which adds it to the band face of every live front chord. -1 when unmeasurable (a sample whose relative_residual() is not finite, which the euclidean field never has), +inf when the bar is not positive.
The (a, b) form reads the field of the chord (potential_for_edge()); the energy passes its band face's field and its ends sorted, so a chord's term does not depend on which face or which operation asks for it.
| std::shared_ptr< Envelope > wmtk::components::topological_offset::TopoOffsetTriMesh::envelope_for_mask | ( | uint64_t | mask | ) | const |
The envelope a simplex with this boundary mask is contained in, or null.
Zero bits: no boundary, no container. One bit: that tag's own envelope. Several bits: a memoized IntersectionEnvelope over the members – inside means inside every tube, which pins junction geometry to the junction. Containment-only for the multi-bit case: the composite implements just the virtual is_outside queries, so it must never be returned from smoothing_energy_envelope(), whose pull calls the non-virtual nearest_point.
| double wmtk::components::topological_offset::TopoOffsetTriMesh::face_criterion_rel | ( | const size_t | fid | ) | const |
Max of the two normalized criteria on this face: AMIPS over stop_energy, and on each live chord it carries the root of edge_offset_term() (the chord's RMS relative error over its stencil in units of the bar; +inf if unmeasurable). > 1 means it fails at least one. The coarsen-mode collapse accept reads it. As in 3D.
| double wmtk::components::topological_offset::TopoOffsetTriMesh::face_flatness | ( | size_t | fid | ) | const |
Scale-invariant flatness: 2*area / longest_edge^2.
~0.433 for an equilateral triangle, -> 0 as the three vertices become collinear, and independent of size. AMIPS saturates at the MAX_ENERGY sentinel while this keeps resolving, which is what the genesis tracking needs: "this face got flatter" is a statement AMIPS cannot make once it is unscoreable.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::face_in_region | ( | const size_t | fid | ) | const |
Whether face fid belongs to the closed offset region, read from its label.
The region is the offset band (label 2) plus the input complex it wraps (label 1), both set at construction from geometry rather than tags, and every operation carries the label onto the faces it creates, so this is exact. Tags cannot express the distinction: nothing stops the band's output tag already appearing elsewhere in the input mesh, and such a face would read as offset band, so the offset energy would drag an unrelated region to the level set.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::face_is_input_complex | ( | const size_t | fid | ) | const |
Whether face fid is part of the input complex (as opposed to the offset band). Label 1, assigned by label_input_complex() from the user's selection expression.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::face_is_offset_band | ( | const size_t | fid | ) | const |
Whether this face carries one of the offset output tags, i.e. is inside the offset band. Read from the tags, which every shared operation maintains, rather than from the face label, which is only refreshed once per optimization iteration.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::face_is_released_band | ( | size_t | fid | ) | const |
A band cell that is a released object's material: every non-output tag released, at least one present. Read by the front placement objective and the rest stamping only – see the definition for why the band's interior smoothing is left equilateral.
| size_t wmtk::components::topological_offset::TopoOffsetTriMesh::flood_fill | ( | ) |
Label connected simplicial complex components (simplices labelled 1 or 2) and return their number. The driver compares the count before and after construction. As in 3D.
|
inline |
The stencil_order stencil a chord is sampled on, handed to visit one point at a time as (point, wa, wb) with the barycentric weights that built it. The 2D twin of TopoOffsetTetMesh::for_each_face_sample(), the same rule one dimension down: order 0 is the two ends alone; order k >= 1 is the vertices of the chord cut into n = 2^(k-1) equal pieces plus the midpoint of each piece, so the counts are 2, 3, 5, 9, 17 for k = 0..4 (stencil_points_per_edge()). The ends are in it on purpose: the quantity sampled is a distance to the level set, which at an end is that vertex's own placement error (see edge_offset_term()).
The weights are handed out for the front smoother: with the moving vertex x as p0, a sample is wa*x + wb*p1, so it moves by wa per unit of x; StencilEnergy2D keeps the weights fixed while the points slide with x (stencil_edge_at() in FrontSmooth2d.cpp).
Takes positions rather than a Tuple: edge_offset_term() reads a chord's ends in sorted order (see tri_energy()).
| double wmtk::components::topological_offset::TopoOffsetTriMesh::front_chord_target | ( | size_t | va, |
| size_t | vb, | ||
| double | len, | ||
| double | sag, | ||
| double | tube | ||
| ) | const |
The edge length that would bring a front chord's error under the tube: 3/4 L (tube / sag)^(1/p) capped at L/2, with the exponent p measured from how the level set turns across the chord (2 where it is smooth, 1 where the chord straddles a kink). Reached from ONE place: the refinable / at-floor test in energy_criterion(). Same formula as 3D.
| std::shared_ptr< polysolve::nonlinear::Problem > wmtk::components::topological_offset::TopoOffsetTriMesh::front_energy | ( | size_t | vid, |
| const std::shared_ptr< const OffsetPotential2D > & | pot | ||
| ) | const |
The offset term for a front vertex, at offset_term_weight(): StencilEnergy2D over its incident live front chords, whose value is sum_e O(e), the terms the per-cell energy carries. Defined in FrontSmooth2d.cpp.
| double wmtk::components::topological_offset::TopoOffsetTriMesh::front_gradient_linf | ( | ) |
Max over the front vertices of ||grad F . n||, F the vertex's front objective (front_objective()). Logged as the loop's gradient reference.
| double wmtk::components::topological_offset::TopoOffsetTriMesh::front_move_alignment | ( | size_t | vid | ) | const |
|cos| between front_vertex_move_direction() and the field normal: 1 means the convergence test's 1-D step is the step toward the level set, 0 means it measures a direction that cannot reduce the distance. Debug-frame diagnostic; see write_vtu().
| std::shared_ptr< polysolve::nonlinear::Problem > wmtk::components::topological_offset::TopoOffsetTriMesh::front_objective | ( | size_t | vid | ) | const |
The objective of front vertex vid, as the smoother assembles it for a front vertex it places against the offset term (smoothing_extra_energy()): w AMIPS over the one-ring (amips_energy()), the rest-shape AMIPS of its plastic faces (rest_energy_for_vertex(), null without the plastic medium), and front_energy(). What front_vertex_normal_gradient() differentiates. As in 3D, where the AMIPS part is cubed.
| double wmtk::components::topological_offset::TopoOffsetTriMesh::front_vertex_conv_ratio | ( | size_t | vid | ) | const |
The vertex measure over the one bar: |relative_residual(x)| / front_conv_frac(), the chord term's order-0 stencil at one end. Infinite when unmeasurable. As in 3D.
| Vector2d wmtk::components::topological_offset::TopoOffsetTriMesh::front_vertex_move_direction | ( | size_t | vid | ) | const |
The line a front vertex is placed along: the field normal, or the boundary tangent where an input envelope holds it. See the definition.
| double wmtk::components::topological_offset::TopoOffsetTriMesh::front_vertex_normal_gradient | ( | size_t | vid | ) | const |
||grad F|| at front vertex vid along its move direction, F the objective smooth_front_vertex() minimises. +inf if unmeasurable.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::grade_sizing | ( | double | grade, |
| const std::vector< size_t > & | seeds | ||
| ) |
Spread the refinement just made at seeds to the vertices around them, the way sizing_gradation_mode says: "ring" is the base gradation_smooth_sizing(grade, seeds), "distance" is grade_sizing_by_distance(seeds) and ignores grade. Every place the offset lowers the field goes through here.
| size_t wmtk::components::topological_offset::TopoOffsetTriMesh::grade_sizing_by_distance | ( | const std::vector< size_t > & | seeds | ) |
TetWild's gradation, ported from tetwild::TetWild::adjust_sizing_field: a breadth-first walk out of the seeds over mesh neighbours; every vertex reached within R = 1.8 l of its nearest seed has its scalar multiplied by 0.5 + 0.5 dist / R, the walk stops at vertices farther than R, and the result is floored at the sizing floor. The seeds themselves keep the scalar the caller gave them. Returns the number of vertices lowered.
| TopoOffsetTriMesh::GradientSplit wmtk::components::topological_offset::TopoOffsetTriMesh::gradient_split | ( | bool | include_edge_samples = true | ) | const |
| include_edge_samples | false skips the edge-interior half (the expensive one). Every convergence decision passes true. |
| void wmtk::components::topological_offset::TopoOffsetTriMesh::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
| V | V by 2 vertex matrix |
| F | #F by 3 face matrix |
| F_tags | #F by physical groups tag matrix |
| V_env | V_env by 2 EnvelopeSurface vertex matrix |
| F_env | F_env by 2 EnvelopeSurface edge matrix |
| void wmtk::components::topological_offset::TopoOffsetTriMesh::init_input_complex_bvh | ( | ) |
Build the input complex's BVH and its smooth offset potential, from one extraction.
Must be called after init_from_image(...) and label_input_complex(). The potential is built from the boundary of the complex rather than its interior: Phi's 2D primitives are segments and points, so a solid input region enters as its outline. Outside the region – the only place an offset exists – the two descriptions agree exactly.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::init_offset_potential | ( | ) |
Build the smooth offset potential from the extraction init_input_complex_bvh() kept.
Separate from that call only because it needs target_distance and offset_dhat_factor, which a caller wanting nothing but the distance field has no reason to have set. The geometry is still extracted exactly once, so the potential and the BVH cannot describe different inputs.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::init_surfaces_and_boundaries | ( | ) |
Classify every region boundary, build the per-tag containment envelopes, and tag the domain wall – once, from the input mesh, before offset construction runs.
The 2D twin of TopoOffsetTetMesh::init_surfaces_and_boundaries(), called from the same place for the same reason: the band's tags replace a face's own rather than joining them, so an envelope built afterwards would be a tube around a curve truncated at the band.
A region boundary is an edge whose two incident faces carry different tag sets; it enters the bucket of every tag on exactly one side (the symmetric difference). An edge with only one incident face is the domain wall and enters its single face's tags' buckets, which is how ambient's envelope comes to hold the box. Requires the face tags to be set, which init_from_image() does just above the call.
|
overridevirtual |
User specified invariants that can't be violated.
| std::vector<Tuple> | a vector of Tuples that are concerned in a given operation |
Reimplemented from wmtk::TriMesh.
|
inline |
While the repulsion runs: an outer end of a marched edge – off the complex, with a neighbour on it – that no envelope holds. Asked of the mesh every time, so it follows the repulsion rounds' operations. As in 3D.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::label_offset_boundary | ( | ) |
Tag the two tracked surfaces for the optimization phase.
The offset boundary has no stored definition in 2D – it is exactly the edges across which the incident face labels differ, so it falls out of the labelling and is recomputed here once. An edge with one incident face is on the domain boundary and is tagged bbox instead, which is what stops the box from collapsing.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::log_refine_block_census | ( | const std::string & | when, |
| double | filter_energy | ||
| ) | const |
For every element above filter_energy, why its edges cannot be split.
log_stuck_refine_census() answers "what are the bad elements"; this answers "what is stopping the mesh from fixing them", the question that matters when the final pass refines somewhere else instead. Attributes each of a bad face's three edges to the first gate that refuses it, in the order the code applies them (TriOptimizerMeshSplit.cpp):
short length^2 < splitting_l2 * mean(sizing)^2 – never even offered to the queue. The remedy is the sizing field, not the split. valence a link vertex is over split_high_valence_threshold. Reported as a ceiling: the real gate is one such split per vertex per pass, which a static probe cannot see, so this counts vertices that could be refused, not that were. contain the dispatched envelope refuses one of the two halves, and the column says which envelope. free nothing blocks it – so a face all of whose edges are free is starved by no gate, and the stall is elsewhere.
Two shortcuts, both stated so the output is not over-read:
Each bad face is also located: centroid, distance to the input complex, and Phi/c there, which separates a collided corridor between two fronts from somewhere in the background.
Diagnostic only: reads the mesh, writes only the log.
< a face's BEST edge – its actual prospect
< which envelope did the refusing
One exemplar per face-level verdict: the worst-quality face that got it.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::log_region_edge_mask_health | ( | const std::string & | when | ) | const |
Are the tracked region boundaries actually contained by anything?
A class-0 edge is dispatched to an envelope by its boundary mask, the symmetric difference of its two faces' tags. An edge whose faces carry the same tags has an empty difference, so envelope_for_mask() gives it nullptr: tracked as a region boundary and held by nothing. Construction cannot produce one, so a non-zero count here is a hole opened afterwards. Called at construction.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::log_smoothing_pass_accounting | ( | ) |
The per-pass smoothing lines: the Newton counters, the front veto, the gradient bins and, under DEBUG_crossings, the crossings. The 3D twin overrides the engine hook of this name.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::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.
Runs from refine_sizing_around_worst(), which only fires once max energy has stalled. The question is whether refinement is even the right response, so it separates four things the single MAX_ENERGY sentinel fuses:
Plus where they are: class distribution, connected clusters, and how much the set overlaps the previous call's (quantised on a grid, because fids are recycled and cannot be compared across passes). A high overlap with a low cluster count says the pass is chasing the same few spots forever; a scattered, changing set says something is manufacturing new degeneracies as fast as they are refined.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::marching_tris | ( | ) |
Marching triangles: every edge with one endpoint in the input complex (label 1/2) and the other in the background (label 0) is split – where d(x) = target_distance along the edge, or under construction_mode's fallback at half the maximum marchable distance or at the midpoint (see edge_split_sphere_trace()) – and afterwards every background triangle still touching a complex frontier vertex (the split-off halves) becomes the band (label 2).
| void wmtk::components::topological_offset::TopoOffsetTriMesh::mark_input_complex_vertices | ( | ) |
Set VertexExtra2d::m_is_on_input from the construction labels, once label_input_complex() has evaluated the selection. Separate from init_surfaces_and_boundaries(), which runs earlier and can only see tag boundaries. The 3D twin is mark_input_complex_vertices().
| double wmtk::components::topological_offset::TopoOffsetTriMesh::max_band_vertex_distance | ( | ) | const |
The furthest any offset-boundary vertex sits from the input complex, by BVH. 0 when no offset exists yet. Sizes dhat in init_offset_potential(); the 3D twin has the same name.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::needle_forensics | ( | ) | const |
The full post-mortem on why nothing removes the flat faces.
For the worst faces by flatness, reports per edge every gate that decides whether an operation may touch it: length against the collapse gate (4/5 l s-bar) and the split gate (4/3 l s-bar), whether it is force-split queued, is_edge_on_surface (swap_weight returns lowest() for a surface edge, so it is never swapped) and swap_weight itself. Plus a scan for coincident vertices, with whether each pair shares an edge – a pair that does not is geometry no local operation can reach.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::needle_scan | ( | const char * | when | ) | const |
Population scan at a named moment, for the points no operation hook covers – after the after construction, at each collapse pass. Reports the count and the worst few.
| TopoOffsetTriMesh::EdgeSamples wmtk::components::topological_offset::TopoOffsetTriMesh::offset_edge_samples | ( | const Tuple & | e | ) | const |
The Phi residual at the stencil_order stencil's points of front chord e. Returns nothing for a chord with an unreachable end. The 3D twin is offset_face_samples().
| std::pair< size_t, size_t > wmtk::components::topological_offset::TopoOffsetTriMesh::offset_membership_mismatches | ( | ) | const |
perform_sanity_checks: how many vertices carry m_is_on_offset without a live offset edge, and how many are the other way round. Whole-mesh; zero is the invariant.
| std::vector< std::array< size_t, 2 > > wmtk::components::topological_offset::TopoOffsetTriMesh::offset_surface_edges | ( | ) | const |
Every live offset-boundary edge as a sorted vertex pair. The 3D twin is offset_surface_faces().
| std::vector< TopoOffsetTriMesh::Tuple > wmtk::components::topological_offset::TopoOffsetTriMesh::offset_surface_edges_live_at | ( | size_t | vid | ) | const |
The live offset-boundary edges incident to vid, deduplicated. The 3D twin is offset_surface_faces_live_at().
| std::vector< char > wmtk::components::topological_offset::TopoOffsetTriMesh::offset_surface_foldover_labels | ( | ) | const |
Per-vertex 0/1: has the offset curve folded back on itself at this vertex? Debug frame diagnostic; see write_vtu(). Costs one pass over the live offset edges, no field evaluation.
A vertex on the offset curve carries two live offset edges. Measured through either side, the angle between them is 180 degrees where the curve is straight and 360 where the two edges lie on top of each other with that side pinched to nothing. Over FOLDOVER_OUTER_ANGLE_DEG through EITHER side is the fold, and the vertex gets 1.
Which side is pinched is deliberately not determined; see the 3D twin, where the measured folds pinch the background rather than the band. Since the two sides sum to 360, the test is simply that the unsigned angle is under 360 minus the threshold. Vertices without exactly two live offset edges are left 0, as are degenerate edges: this is a diagnostic, and a number it cannot measure is not a fold.
The 3D twin is TopoOffsetTetMesh::offset_surface_foldover_labels(), which asks the same question of an offset-surface EDGE's two faces and marks that edge's two endpoints.
|
inline |
1 / front_conv_frac()^2: the factor that turns a squared relative error into a squared error in units of the tolerance. The weight of the front smoother's offset terms, and the scale of edge_offset_term(). As in 3D.
|
inlineoverridevirtual |
TriWild's bare collapse passes are off for the offset, as TetWild's are in 3D: with no length gate the quality test alone demolishes the band, and the sizing field cannot refuse a collapse.
Reimplemented from wmtk::TriOptimizerMesh.
|
overridevirtual |
Called by the engine at every smoothing pass boundary (and only there in this component's loop): the per-pass smoothing accounting the 3D engine prints through its log_smoothing_pass_accounting() hook, which the 2D engine does not have, then the debug frame the base default writes. Writes the log and the frame, never the mesh.
Reimplemented from wmtk::TriOptimizerMesh.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::optimize_offset_loop | ( | bool | final_stage = true, |
| bool | refine = true, |
||
| const std::string & | label = std::string() |
||
| ) |
TriWild's loop, the front placed inside its smoothing passes. final_stage false is the init_optimize loop: it returns on convergence without the frozen-front final pass and without deciding the verdict. refine false skips the halving. label names the loop in its log. As in 3D.
|
inline |
The same selection as potential_for(), as the pointer the energies take a share of. Null only when m_offset_potential is, which the front placement paths test for.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::rebuild_offset_envelope | ( | ) |
Rebuild m_offset_envelope from the current offset-boundary segments, and drop the intersections memoized against the old one; also refresh_released_envelope().
| size_t wmtk::components::topological_offset::TopoOffsetTriMesh::refine_front_by_halving | ( | const std::vector< EnergyCriterion::Refinable > & | edges | ) |
THE refinement: halve the sizing scalar at the ends of every refinable chord, once per vertex per call, floored at max(min_sizing_scalar, min_edge_length / l), then graded outward. Returns the number of vertices lowered.
| size_t wmtk::components::topological_offset::TopoOffsetTriMesh::refine_front_by_halving | ( | const std::vector< size_t > & | vertices | ) |
The same halving at the listed vertices themselves: each lowered once per call, floored, then graded. The edge form above is this on its chords' ends, in the order given; front_measure "vertex_ring" calls it directly with the vertices whose ring measure is over the bar. The 3D twin has the same pair.
|
overridevirtual |
TriWild's stall-driven sizing refinement, verbatim.
Structurally TriWildMesh::refine_sizing_around_worst, down to the shared helpers in wmtk/utils/SizingField.hpp and every stuck_refine_* parameter: rank faces by AMIPS, force-split the worst ones' longest edges, grow the region by rings, lower the per-vertex sizing scalar, grade it outward.
Final pass only: mesh_improvement() is its one caller, and the loop only runs that as the frozen-front final pass.
Implements wmtk::TriOptimizerMesh.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::refresh_released_envelope | ( | ) |
Rebuild deform_others' released-boundary tube now, between passes (see released_envelope(), which never rebuilds mid-operation).
| void wmtk::components::topological_offset::TopoOffsetTriMesh::relabel_input_complex | ( | ) |
Every construction label (vertex, edge, face) back to 0, then label_input_complex(): the input complex from the region tags, which the operations carry.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::release_deformable_regions | ( | ) |
Drop the released tags' envelopes and stamp every deformable face's rest. Called once from optimize_offset() when deform_others is set; see the FaceExtra2d::rest_valid doc for the tracking contract.
| std::shared_ptr< Envelope > wmtk::components::topological_offset::TopoOffsetTriMesh::released_envelope | ( | ) | const |
The current released-boundary tube, rebuilt first if dirty. Null when nothing is released or no released-boundary segment exists.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::report_needle | ( | const char * | op, |
| size_t | fid, | ||
| double | parent_q | ||
| ) | const |
Where the first needles come from – a tripwire, not a census.
The census counts the population once it exists and the attribution counters say which operation touches them; neither says how the first one is born. This logs the first kNeedleReports needle faces any operation hook sees, with what tells a creation from a copy: the operation, the parent quality where there is one, both the float and the exact orientation, full-precision coordinates, and each vertex's flags, birth epoch and rounding.
Deliberately capped – once the force-split loop engages there are thousands per pass, and it is the first few that carry the information.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::report_outside_support | ( | const char * | when, |
| const DistanceSplit & | s | ||
| ) | const |
Turn a residual_split()'s outside-support tally into the hard error. Separate from check_offset_within_support() so the per-round check can reuse a split it already has.
|
inline |
THE REPULSION PART OF THE PER-TRI ENERGY, before the march (tri_energy()): a face carries O(v) of each of its corners v that is an outer end within it – off the complex, held by no envelope, in a face that has a complex vertex, i.e. joined to the complex by a marched edge of this face. These faces are the band the march will make, so this is the counterpart of a band face carrying its front chord's term. Reads the face's own three vertices only. 0 while the repulsion does not run. As in 3D.
|
inline |
How many faces of vid's ring carry its term (repulsion_cell_term()): the smoother's objective is the ring's sum of the per-tri energy, so it charges O(v) that many times.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::repulsion_embedding_kept | ( | const std::vector< size_t > & | fids | ) | const |
repulsion_rounds: whether the complex is still simplicially embedded around the faces fids an operation just made – no face off the complex has complex vertices spanning an edge off the complex (tri_is_simp_emb()'s test, decided from the faces' labels, which the operations carry, instead of the edge labels, which they do not). A collapse or a swap that breaks it is refused. Defined in TopoOffsetTriMesh.cpp. As in 3D.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::repulsion_smoothing | ( | ) |
repulsion_smoothing_passes and repulsion_rounds (see the spec): between the simplicial embedding and marching_tris(), smoothing passes and then rounds of the loop's operations that push the outer ends of the marched edges out to 2 x target_distance, stopping once every outer end is beyond target_distance + front_conv. As in 3D.
| TopoOffsetTriMesh::DistanceSplit wmtk::components::topological_offset::TopoOffsetTriMesh::residual_split | ( | ) | const |
The same split over the quantity the loop converges on: the Phi residual, as a length. Reported beside the Euclidean one so the two offsets can always be compared.
| std::shared_ptr< polysolve::nonlinear::Problem > wmtk::components::topological_offset::TopoOffsetTriMesh::rest_energy_for_vertex | ( | size_t | vid | ) | const |
The rest-shape AMIPS over the deformable faces of vid's one-ring, weighted like the shared smoother weights its AMIPS term at vid (smoother_amips_weight()); null when the ring has none.
|
inline |
Place a vertex, keeping its exact and rounded coordinates in step.
As in 3D: the offset works in doubles, so every vertex it places is rounded and its exact position is its double one (set_pos_to_posf).
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::simplex_in_input_complex | ( | size_t | a, |
| size_t | b | ||
| ) | const |
Whether the edge (a, b) is in the input complex, from the faces' labels: an edge of a face of the complex, or, for offset_in, a domain-boundary edge of a body face. Single-body offset_in or offset_out only, which repulsion_smoothing() checks. The 3D twin also takes a face.
|
overridevirtual |
User specified modifications and desideras after an edge smooth.
| the | edge Tuple to be smoothed |
Reimplemented from wmtk::TriMesh.
|
overridevirtual |
User specified preparations and desideratas for an edge smooth.
| the | edge Tuple to be smoothed |
Reimplemented from wmtk::TriMesh.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::smooth_group_to_convergence | ( | const char * | group_name | ) |
The interleaved smoothing of one operation group under adaptive_smoothing: one pass at a time through local_operations({0,0,0,1}), each followed by smoothing_progress(), until the front has converged (max ratio <= 1) or stalled (max ratio fell by less than adaptive_smoothing_stall_rel) AND the background has settled (max step <= adaptive_smoothing_step_rel x its target edge), or adaptive_smoothing_max_passes.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::smooth_plastic_vertex | ( | const Tuple & | t | ) |
The plastic vertex's smoothing: rest-shape AMIPS over its ring, nothing else – no equilateral term, no quality veto, exact inversion as the only accept test.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::smooth_vertex_2d_counted | ( | const Tuple & | t, |
| const optimization::SmoothVertexOptions & | opts, | ||
| optimization::NewtonCounters & | newton, | ||
| bool * | solved = nullptr |
||
| ) |
One smooth_vertex_2d() call with its solve recorded in newton: the 3D engine's smooth_vertex_3d() takes a NewtonCounters and the 2D one does not, so the component records the solve itself. A solve runs exactly when the ring is not already inverted in floats (smooth_vertex_2d()'s only refusal before solving; two_stage is off on every component path), so solved reports that, and the solver still holds its state.
|
inline |
The AMIPS weight the rest-shape term uses at vid: offset_amips_weight for a vertex placed against the offset term, the engine's w_amips factor otherwise. As in 3D.
|
inlineoverridevirtual |
The shared smoother's per-face comparison – its projected step for a vertex held on a surface, and its quality veto – is the per-cell energy, so every smoothing test judges a move by the energy the smoother minimises (smoothing_extra_energy()). As in 3D.
Reimplemented from wmtk::TriOptimizerMesh.
|
inline |
... and it is not contained by the offset tube either: placing the front is what moves the offset boundary, so the smoother asks for the region tubes alone. The operations hold the offset boundary to its tube through surface_envelope_for_edge() in the final pass; see containment_for(). As in 3D.
|
inlineoverridevirtual |
The smoother holds every tracked segment at the vertex to that one envelope, whatever the segment: the per-vertex answer above, not the base's per-segment one.
Reimplemented from wmtk::TriOptimizerMesh.
|
inlineoverridevirtual |
The offset boundary is the one tracked surface with no envelope, in either role.
It is the surface the optimization exists to move: a tube around wherever construction left it would cap how far it can ever travel toward the level set. What holds it is the offset term in the objective, not a container.
The pull must be a real envelope, never a composite: this hook's consumers call the non-virtual Envelope queries – nearest_point and the ExactDistanceEnergy2D trio – which on a composite would bind to the base's null BVH. So a junction vertex (several mask bits) is pulled toward its most-violated member tube instead, one real envelope per attempt, while the containment intersection below enforces the full constraint.
Reimplemented from wmtk::TriOptimizerMesh.
|
inlineoverridevirtual |
THE smoothing objective at vid, for every vertex the shared smoother places: the per-cell energy tri_energy() = w AMIPS + O summed over vid's ring, up to the terms that do not move with vid. w AMIPS over the ring (amips_energy()) for every vertex – the smoother adds no AMIPS term of its own, every caller passing opts.w_amips 0 – plus the offset terms of the front chords at vid when it is a front vertex the loop places (front_energy(); null in the final pass and for a front vertex an input envelope also pins), or, in the passes before the march, a repulsion vertex's term once per ring face that carries it (repulsion_energy(), repulsion_cell_term()). Under deform_others the rest-shape AMIPS of the deformable faces in the ring is added. As in 3D.
Reimplemented from wmtk::TriOptimizerMesh.
|
inlineoverridevirtual |
No per-vertex positional constraint. The per-tag envelopes close that hole structurally – the same deletion 3D made to its lower-strata point refusal.
An isolated point of the complex only ever arises where two or more selected tags meet (the boolean selection can only label an isolated simplex whose face star is tag-heterogeneous), so the edges radiating from it are tag boundaries and its boundary mask carries several bits. smoothing_containment_envelope() therefore hands the smoother an IntersectionEnvelope – within eps of every curve it lies on – which pins it to the junction, and the pull toward the most-violated member drags it back if it strays. Kept as the honest constant: the base's default asks the feature-point anchors, which this mesh never turns on.
Reimplemented from wmtk::TriOptimizerMesh.
| polysolve::nonlinear::Solver & wmtk::components::topological_offset::TopoOffsetTriMesh::smoothing_solver | ( | ) |
The thread's shared solver, created with the engine's parameters if needed, with kSmoothRelGradNormTol applied. Every smoothing path of this component takes it from here.
|
overridevirtual |
Carry each parent's region label onto the two children it became.
Bookkeeping, not positioning, and it lives here because of when the base calls the hooks: the containment check inside split_edge_after() runs on both new segments and so reaches surface_envelope_for_edge() and the endpoints' boundary masks. split_after_vertex() runs after that check; this hook is the last one the base offers before it.
Getting it wrong is silent in the dangerous direction: children still holding whatever occupied their recycled fid slots classify as "not a region boundary", which yields a null envelope, and a null envelope makes surface_segment_is_outside() return false – containment skipped rather than failed, so the offset polyline decays unchecked.
Position is left entirely to the base; this always returns its result unchanged.
Reimplemented from wmtk::TriOptimizerMesh.
|
overridevirtual |
Reimplemented from wmtk::TriOptimizerMesh.
|
overridevirtual |
User specified modifications and desideratas after an edge split.
| the | edge Tuple to be split |
Reimplemented from wmtk::TriMesh.
|
overridevirtual |
User specified preparations and desideratas for an edge split.
| the | edge Tuple to be split |
Reimplemented from wmtk::TriMesh.
|
overridevirtual |
User specified modifications and desideratas after a face split.
| the | face Tuple to be split |
Reimplemented from wmtk::TriMesh.
|
overridevirtual |
User specified preparations and desideratas for a face split.
| the | face Tuple to be split |
Reimplemented from wmtk::TriMesh.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::stamp_rest_face | ( | size_t | fid | ) |
Stamp rest := the face's current corner positions (oriented order). No-op for non-deformable faces.
|
inline |
How many points for_each_edge_sample() visits at the configured order: 2 at order 0, and 2n + 1 with n = 2^(k-1) above it, i.e. 3, 5, 9, 17, ... Kept in step with for_each_edge_sample() by the unit test stencil-order-point-counts-2d. The 3D twin is stencil_points_per_face().
|
inlineoverridevirtual |
Class-0 segments – every region boundary, the input complex and the domain wall included – carry a containment requirement, and so does the offset boundary in the frozen-front final pass: m_offset_envelope holds it where the loop left it (see containment_for()).
The envelope holds the other tag regions where they are, and the input complex too. That half is exactly TriWild's input envelope: the complex may be split, collapsed and smoothed, and this is what bounds how far the result may drift from the geometry as loaded. The 2D twin of TopoOffsetTetMesh::surface_envelope_for_face().
Null means "no containment requirement", which the base handles by skipping the check.
Reimplemented from wmtk::TriOptimizerMesh.
|
overridevirtual |
THE ENERGY RULE for a swap (see tri_energy()): the max of tri_energy() over the two faces the flip made must be STRICTLY below the max over the two it replaced (m_swap_energy_before). The 3D twin is swap_after_cells().
Reimplemented from wmtk::TriMesh.
|
overridevirtual |
Reject a flip whose new edge already exists, and cache the energy rule's before-half.
Flipping (a,b) to (c,d) when c and d are already joined creates a second edge between the same pair. Across a thin offset band that is how the two sides get stitched together and the region stops being manifold. The base refuses tracked-surface edges but not this.
Reimplemented from wmtk::TriMesh.
|
inlineoverridevirtual |
The energy rule's EARLY half for a swap, on the engine's lower bound.
after is the largest AMIPS over the two new faces. tri_energy() of a face is weighted_amips() of its AMIPS plus a non-negative chord term, so after not strictly below the before-maximum (swap_edge_before()) already decides what swap_edge_after() would decide on the real faces: refused. The engine's before (stored AMIPS of the old faces) is not read. Off with offset_swap_veto. A 2D swap never flips the offset boundary (the engine refuses every tracked edge), so is_surface_flip is always false. As in 3D.
Reimplemented from wmtk::TriOptimizerMesh.
|
inline |
The three helpers of the per-tag envelope dispatch. tag_bits() and edge_mask() are trivial; envelope_for_mask() is out of line (it builds IntersectionEnvelopes lazily).
| double wmtk::components::topological_offset::TopoOffsetTriMesh::tri_energy | ( | size_t | fid | ) | const |
THE per-cell energy, read from the mesh as it is – labels, neighbours and positions, nothing hypothetical. The 2D twin of TopoOffsetTetMesh::tet_energy().
E(t) = w A(t) + [t is band] * sum over the live front chords e of t of O(e) O(e) = (1/N) sum_i (relative_residual(q_i) / front_conv_frac())^2
A(t) is the base's TriOptimizerMesh::get_quality(), the AMIPS2D the engine stores as the face quality (3D stores AMIPS^3 there, which is why its energy reads AMIPS^3); its MAX_ENERGY (unscoreable) passes through unchanged, and w is offset_amips_weight (weighted_amips()). q_i are the N points of e's stencil_order stencil (for_each_edge_sample()) on the field of e's band face (potential_for_face()), e's ends sorted so that a chord's term does not depend on which face or which operation reads it. O is edge_offset_term(), the squared chord measure in units of the tolerance, 1 at the bar. With no field yet (m_offset_potential null) E = w A.
An unmeasurable chord makes the face MAX_ENERGY, the engine's own "unscoreable". THE BAND SIDE CARRIES THE CHORD: only a band face (face_is_offset_band()) adds terms, and a live front chord has exactly one band side (edge_is_offset_surface_live()), so every front chord is counted once.
WHERE IT IS COMPARED: the collapse rule (collapse_before_vertex() / collapse_edge_after(), early half collapse_quality_allowed()), the swap rule (swap_edge_before() / swap_edge_after(), early half swap_quality_allowed()), the smoother's projected step and veto (smoothing_cell_energy()) and the front and interior vetoes. The engine's stored face quality stays AMIPS2D and every engine diagnostic reads it unchanged.
|
overridevirtual |
The engine calls this at the start of local_operations() and after each operation pass that ran; DEBUG_crossings hooks the operation passes here. Log-only.
Reimplemented from wmtk::TriOptimizerMesh.
|
inline |
The tag boundaries this vertex lies on – the raw mask gated on the vertex still being region geometry at all.
The gate is not redundant, it is what keeps the mask honest. m_boundary_mask propagates by a bare AND of a split's endpoints, which over-claims: an edge whose two ends happen to share a bit hands that bit to its midpoint even when the edge is a chord through the interior, and the offset front is built by splitting precisely such edges. So the mask says which boundaries and vertex_is_on_region() says whether the vertex is on one at all.
|
inline |
Whether the smoother places vid against the offset term: a front vertex, outside the frozen-front final pass, that no input envelope also pins.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::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.
< the edge's other end
|
inline |
Identification only – no operation refuses the domain wall through these.
The wall is a tracked region boundary like every other one: init_surfaces_and_boundaries() tags its edges m_is_surface_fs, masks its vertices with ambient's bit and puts its segments in ambient's envelope, so refinement, coarsening, flips and smoothing are governed by the same containment, merge rules and link conditions that govern the input complex. As in 3D, the hooks carry no categorical wall refusal of their own.
What still reads these two:
|
inline |
Is this vertex on a region boundary – a tag boundary, or the domain wall.
Derived, not stored, exactly as in 3D. Both halves are already maintained: m_is_on_region by the split/collapse hooks, on_bbox_faces by set_intersection of the split endpoints and by the collapse rule that a wall vertex may only merge into one at least as constrained.
|
overridevirtual |
The substructure the link condition is evaluated against, derived not cached.
substructure_link_condition() is only as good as these answers. Cached edge tags are refreshed once per iteration, which is too coarse: the split pass creates edges the tagging never classified, so the collapse pass that follows would evaluate against a substructure that no longer describes the mesh – which is why split and collapse tear the region together while each is safe alone. Computing from the face labels on demand cannot go stale.
Reimplemented from wmtk::TriMesh.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::warn_if_offset_reaches_domain_boundary | ( | ) | const |
Warn if the offset band has grown into the domain boundary.
When target_distance exceeds the clearance between the input complex and the bounding box, construction runs out of room and the band's outer boundary becomes the box itself.
Two things then go wrong invisibly: those vertices are on the bbox and cannot be moved, so the target distance is unreachable there, and compute_distance_deviation() cannot even see them – it skips edges with no opposite face, which is what a band edge on the domain boundary is, so the clipped stretch enters neither max_dist_err nor avg_dist_err. The run then looks like a near-miss and is a structural failure, hence a warning, not a debug line.
|
inline |
The AMIPS part of the energy: offset_amips_weight times AMIPS, the MAX_ENERGY sentinel (unscoreable) passed through unscaled so that it stays the largest value anywhere.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::write_debug_pvd | ( | ) | const |
DEBUG_output: rewrite <output>{_main,_off,_surf,_edge,_front}.pvd, a ParaView time series over the debug frames. Needed because ParaView only groups a file series when the index is immediately before the extension, which is false for every companion (<output>_NNNNN_off.vtu). Called after every frame, so a killed run still opens.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::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.
The offset is a level set of a field defined everywhere and the output mesh only samples that field along one curve, so a result that looks wrong cannot be diagnosed from the mesh alone. This writes the field itself: Phi (clamped, since it diverges on the input complex), the residual as a length, and the exact Euclidean distance beside it, all as vertex fields on a triangulated grid so a viewer can draw the isoline Phi = c directly.
| n | samples per side; 0 or 1 writes nothing. |
|
inlineoverridevirtual |
Put the optimization's frames on the run's single debug timeline (see write_debug_frame()), labelled "r<turn><tag><pass>_<op>" / "r<turn><tag>_end", tag S in the loop and F in the final pass; led by m_frame_prefix (i during init_optimize). Same scheme as 3D.
Implements wmtk::TriOptimizerMesh.
|
staticconstexpr |
SurfaceTagAttributes::m_surface_class: which of the two tracked surfaces an edge belongs to. Same scheme as 3D.
OFFSET is the surface the optimization places at target_distance. Everything else – the input complex, another body's outline, an overlap seam, the domain wall – keeps the primary class 0 and is envelope-checked by the shared operations exactly as in triwild and simwild. The distinction has to exist: filing a region boundary under OFFSET drives placement at vertices nowhere near target_distance and leaves the sizing field refining there forever. Class 0 is not split further – the boundary mask says which tubes hold a simplex, per tag.
|
mutable |
The energy rules (see tri_energy()): collapses refused for raising the max energy over the survivor's ring, swaps for not strictly lowering it over the faces they make.
| std::atomic<int> wmtk::components::topological_offset::TopoOffsetTriMesh::iter_cnt_split_offset_before {0} |
Splits of an offset-boundary edge: offered, accepted. Real attempts only: a screened pass's dry runs are not counted.
|
staticconstexpr |
Diagnostic: where the front solves stop. Per pass, histograms of the final gradient norm of each front solve (log10 bins, -14..+7) and of its ratio to the solve's first gradient norm, read from polysolve's Criteria after the solve. As in 3D.
|
staticconstexpr |
What counts as a needle for the tripwire – deliberately far below MAX_ENERGY.
A healthy triangle is O(2); 1e6 is far outside anything the optimizer should tolerate and far below the sentinel, so the creation event is caught while its parent is still scoreable and can be quoted. A >= MAX_ENERGY test misses parents that are already catastrophically flat, which is where the collinearity actually originates.
|
staticconstexpr |
The Newton stopping rule every smoother of this component runs with, set on the thread's shared solver (the engine's m_solver slot) by smoothing_solver() at the start of every visit: relative gradient tolerance 1e-6 beside the engine's absolute 1e-10. See the 3D twin for the measurement behind it.
|
mutable |
The link of the collapsed edge, captured in collapse_before_vertex(): the only vertices besides v2 whose offset membership a collapse can change.
The chords that DIE are the ones carrying both endpoints – in 2D only the edge itself, and the faces (v1, v2, w) for w in the link take their two chords (v1, w) and (v2, w) down to one – so only v2 and those w can lose their last front chord; nothing but v2 can gain, since edges only ever move from v1 to v2. Captured before the collapse because the edge is gone by collapse_after_vertex(), which is where the refresh runs. As in 3D.
|
mutable |
The collapse energy rule's before-half: the max of tri_energy() over the one-rings of v1 and v2, cached by collapse_before_vertex() and compared by collapse_edge_after().
|
mutable |
The collapse survivor's own sizing scalar, recorded in collapse_edge_before() and put back in collapse_edge_after() when sizing_collapse_min is false; see that key.
|
staticconstexpr |
pseudo-tag: the input complex boundary
| double wmtk::components::topological_offset::TopoOffsetTriMesh::m_construction_distance = 0. |
The distance the marching's sphere trace aims for: target_distance, or half the maximum marchable distance under construction_mode "max_marchable_fallback" (see the marching).
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::m_converged = false |
The run's verdict: the front resolved (EnergyCriterion::converged(): every front chord's measure within the bar, nothing unmeasurable) AND the final quality under stop_energy. Read by the report and by throw_on_nonconvergence.
| std::atomic<size_t> wmtk::components::topological_offset::TopoOffsetTriMesh::m_cross_own {0} |
Per smoothing pass: accepted front moves that took the moved vertex itself (own), or one of its front neighbours (neighbour), from a ring measure <= 1 to > 1.
| std::vector<double> wmtk::components::topological_offset::TopoOffsetTriMesh::m_cross_ring |
DEBUG_crossings (log-only). The ring measure of every front vertex as the last pass left it (NaN where a vertex has none), the positions it was taken at, and whether it is current (consolidation renumbers vertices, so the loop clears it). A pass's line counts the front vertices whose ring measure went from <= 1 to > 1 (up), back (down), new vertices already over (a split's, or an old id at a new position after a storage retry), and vertices over the bar that the pass removed. As in 3D.
| int64_t wmtk::components::topological_offset::TopoOffsetTriMesh::m_curve_tag = -1 |
Tag id of the input's curve group (the .msh line elements), or -1. An open curve has no face set whose boundary it is, so it is selectable only through this tag: offset_selection naming it makes the curve the complex and the band grows on both of its sides.
|
mutable |
DEBUG_output: the label of each debug frame, indexed by its sequence number, and, per companion suffix, the frame indices that actually produced one. Both exist only to write the ParaView collections – see write_debug_pvd(). Same in 3D.
|
mutable |
Pass index within the current turn, and the (turn, loop-or-final-pass tag, prefix) it belongs to – when those change the index restarts. All exist only to name frames.
| std::optional<EnergyCriterion> wmtk::components::topological_offset::TopoOffsetTriMesh::m_energy_verdict |
The energy criterion as measured when the loop converged; the final pass runs after it and the verdict must not be re-measured on that mesh.
| std::atomic<size_t> wmtk::components::topological_offset::TopoOffsetTriMesh::m_front_veto_asked {0} |
The front veto (smooth_front_vertex()): moves whose Newton solve succeeded and reached the veto, and how many it refused for raising the ring's max tri_energy. Reported and reset per pass beside the Newton counters.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::m_init_optimize = false |
init_optimize: optimize_offset() opens with a stencil_order loop without refinement. Set by marching_tris(), only when the target is beyond the maximum marchable distance.
| std::shared_ptr<SimplicialComplexBVH> wmtk::components::topological_offset::TopoOffsetTriMesh::m_input_complex_bvh |
The input complex as loaded. Built once, never rebuilt.
It answers the Euclidean distance to the input, a diagnostic rather than the definition of the offset – see m_offset_potential, which is what the optimization is driven by. init_input_complex_bvh() has one call site, before construct_offset() runs, so this holds the original geometry however the elements representing the complex are later remeshed.
That invariant is load-bearing: rebuilding from the live mesh would redefine the offset distance in terms of a surface the optimizer had just moved, and the convergence criterion would be measuring the mesh against itself.
The only structure over the input complex. For offset_field "euclidean" the potential shares this very object as its query engine, which is why it is a shared_ptr. Containment is not its job – the per-tag region envelopes (m_tag_envelopes) hold the complex in place.
|
mutable |
Memoized IntersectionEnvelope per multi-bit mask. Lazily built under the mutex because the queries that need them run concurrently under kPartition.
| size_t wmtk::components::topological_offset::TopoOffsetTriMesh::m_marching_root_splits = 0 |
marching_tris() tallies for the construction log: edges placed on the level set / at the midpoint because the trace left the edge, and the trace steps (total, max). Reset at the start of marching_tris().
| int wmtk::components::topological_offset::TopoOffsetTriMesh::m_n_regions = 0 |
One field per connected piece of the input complex, and which one each band vertex is placed on.
m_offset_potential above is built over the whole selected complex: the sum of every piece's barrier for the smooth potential, the distance to the nearest piece for the Euclidean field. Neither is the field a front should be placed on where two pieces are close – the sum has no level set at all across a narrow gap, so both fronts are pushed through the background strip until inversion.
A region is a connected piece, not a tag: one tag covering two pieces that never touch would make them share a field and bring that bridging back. Pieces are the connected components of the captured complex under vertex connectivity (two pieces meeting at a point share an offset there, so they share a field), computed once in init_input_complex_bvh() so the numbering is fixed for the whole run. simplicial_embedding() is what makes this correspond to the band: no background triangle can touch two disjoint pieces, so the band's connected components are the disjoint offsets.
A band grown from one piece is placed on that piece's field alone: Phi_A = c for band A, Phi_B = c for band B. Where the two would overlap, each front is pulled outward by its own field and held by the strip's quality bar – a symmetric local minimum with a thin gap of background between the fronts, which is the topological offset.
The map from band to region is assign_band_regions(): a flood fill over the band faces, seeded from every band face with a complex vertex, whose piece is read off the captured complex geometrically. A face reachable from two regions and a vertex on faces of two regions read -2 and fall back to the union field. m_offset_potential is kept for everything that is not per-vertex: the support (dhat), the viewer's grid, the report. connected pieces of the input complex; one field each
|
mutable |
Why smoothing does not lift a sliver's apex off its opposite edge.
Interleaved smoothing is on by default, so every needle-adjacent vertex is visited after every topological pass. These counters say what happens when it is:
offered minus reached is the search failing outright; reached minus fixed is a move being made that does not repair the sliver. The two have different causes and the fix for one is not the fix for the other, which is why they are counted separately.
| optimization::NewtonCounters wmtk::components::topological_offset::TopoOffsetTriMesh::m_newton |
How the solves this class makes itself ended, per smoothing pass. Background: every smooth_nonfront_vertex() solve – the 3D engine counts these itself (its m_newton), the 2D engine does not, so the component keeps the counter. Front: every front placement. Plastic: the rest-shape solve of smooth_plastic_vertex(). Logged and reset by log_smoothing_pass_accounting().
One difference from 3D, read before comparing the two logs: the 2D engine's smoother (optimization::smooth_vertex_2d) catches a solver throw internally and reports nothing, so the threw count is always 0 here; a solve that threw shows only through its stop status (LineSearchFailed, the throw polysolve raises).
| optimization::NewtonCounters wmtk::components::topological_offset::TopoOffsetTriMesh::m_newton_repulsion |
Per repulsion pass or round, as m_newton_front and the front veto counters are per loop pass.
|
mutable |
Memoized "region tubes AND the offset envelope", keyed by the region mask.
A simplex can be on both, so this is always their intersection, never an either/or.
Separate from m_isect_cache because the members differ in lifetime: the tag envelopes live for the whole run, m_offset_envelope is built for the final pass. rebuild_offset_envelope() clears this and must keep doing so. Guarded by m_isect_mutex.
| std::shared_ptr<OffsetPotential2D> wmtk::components::topological_offset::TopoOffsetTriMesh::m_offset_potential |
The smooth offset potential, and with it the definition of the offset itself.
The offset boundary is the level set Phi = c. Built from the same extraction as m_input_complex_bvh, in the same call, so the two describe the same geometry and the same never-rebuilt rule applies. See OffsetPotential for what Phi is.
shared_ptr because OffsetEnergy2D holds one per smoothing call.
| MatrixXd wmtk::components::topological_offset::TopoOffsetTriMesh::m_phi_V |
The complex as the potential sees it: vertices, its boundary segments, and its isolated points. Filled by init_input_complex_bvh(), consumed by init_offset_potential().
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::m_plastic_active = false |
set in optimize_offset() when deform_others
Plastic medium: under deform_others every background face – ambient and the other objects alike – is plastic, its rest shape re-stamped before every operation group, so smoothing resists only the increment since the group started and the medium flows instead of behaving as an elastic solid glued to the walls. The band (label 2) and the complex (label 1) are not plastic; element quality in the medium is the operation passes' job.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::m_quality_converged = true |
The finishing-pass half of the verdict: max AMIPS < stop_energy once the front is resolved, after the final pass when one ran. True when no pass was needed; false when the pass ended still over. m_quality_max_amips is the value it was judged on.
|
mutable |
The released boundaries' ops-only tube: an Envelope around the current deformed boundaries, consulted only by surface_envelope_for_edge() – the dispatch every operation containment check goes through and no smoothing path does – so operations preserve the current shape through remeshing while smoothing stays free to carry the object. Rebuilt lazily by released_envelope() when m_released_tube_dirty says a smoothing accept may have moved the boundary.
| std::shared_ptr<const OffsetPotential2D> wmtk::components::topological_offset::TopoOffsetTriMesh::m_repulsion_potential |
The repulsion passes and rounds (repulsion_smoothing()), set only while they run: the Euclidean field at level 2 x target_distance. Null otherwise, so every other path is untouched.
| std::shared_ptr<OffsetEnergy2D> wmtk::components::topological_offset::TopoOffsetTriMesh::m_repulsion_term |
O(v) = (max(0, 2 delta - d(v)) / front_conv)^2: the Euclidean residual against level 2 delta is (d - 2 delta) / (2 delta), so the weight (2 delta / front_conv)^2 = 4 offset_term_weight() puts it in units of the tolerance, as the front's terms are. One object for the whole repulsion (value() changes nothing in it, so threads share it); set with m_repulsion_potential.
| int wmtk::components::topological_offset::TopoOffsetTriMesh::m_round = 0 |
The turn the run is in, 1-based; 0 before the loop starts. Read only by write_smoothing_debug_output(), to tag each frame with the turn it belongs to.
| std::set<int64_t> wmtk::components::topological_offset::TopoOffsetTriMesh::m_source_tags |
The source tags (offset_selection's tags_involved), stored at release so the ops-only tube's edge classification applies the same never-freed rule the release did.
| std::set<std::pair<long, long> > wmtk::components::topological_offset::TopoOffsetTriMesh::m_stuck_prev_cells |
Quantised centroids of the MAX_ENERGY faces at the previous stuck-refine, for the overlap line above. Diagnostic only; nothing reads it but log_stuck_refine_census().
|
mutable |
The swap energy rule's before-half: the max of tri_energy() over the two faces the flip replaces, cached by swap_edge_before() and compared by swap_edge_after() and, early, by swap_quality_allowed(). The 3D twin is SwapEnergyBefore, which also carries the band cells beyond a 3-2 flip's faces; a 2D flip replaces two faces with two over the same quad, every quad edge keeping its outer face, so there is no "outside" to add.
| std::map<int64_t, int> wmtk::components::topological_offset::TopoOffsetTriMesh::m_tag_bit |
Input tag id -> bit position in VertexExtra2d::m_boundary_mask. Assigned in init_from_image() once the tag maps are complete; at most 64 input tags.
| std::map<int64_t, std::shared_ptr<Envelope> > wmtk::components::topological_offset::TopoOffsetTriMesh::m_tag_envelopes |
One containment envelope per input tag, ambient included. Both phases.
E_t is a tube of half-width m_envelope_eps around region t's boundary segments as the input mesh carried them, built in init_surfaces_and_boundaries() before offset construction: the band's tags replace a face's own, so an envelope built later would be a tube around a curve truncated at the band. A simplex on several boundaries is held by the intersection of its tags' tubes (envelope_for_mask()), which pins junction points to the junction itself.
m_envelope (the base's pointer) survives as a UnionEnvelope over these members, purely so the shared engine's direct uses of it – the collapse_edge_before point check and the "segment does not exist yet" fallback here – keep union semantics.
Interior edges of a region are not held by these: identical tag sets on both sides land in no bucket, so a filled complex's interior is free to optimise.
|
mutable |
The vertex compute_distance_deviation() last found the max at, and a dump of everything that could be stopping it from moving. Diagnostic only.
| std::vector<std::array<double, 8> > wmtk::components::topological_offset::TopoOffsetTriMesh::optimization_metrics |
{max_dist_err, avg_dist_err, max_phi_residual, avg_phi_residual, max_grad, avg_grad, max_grad_at_vertex, max_grad_in_edge}. max_grad is the convergence criterion – the full placement-gradient norm at band vertices – so max_grad_at_vertex repeats it and max_grad_in_edge is the chord diagnostic; the rest are diagnostics. One entry for the whole run, as in 3D.