|
Wildmeshing Toolkit
|
The offset's 2D mesh, on the shared 2D optimizer. More...
#include <TopoOffsetTriMesh.h>
Classes | |
| struct | DistanceSplit |
| The band's distance error, split by whether the optimizer can do anything about it. More... | |
| struct | EdgeSamples |
| The residual sampled at points along a band edge – see offset_edge_samples(). More... | |
| struct | EdgeSnapshot2d |
| An edge's / face's shared attributes together with the offset's own label. More... | |
| struct | EdgeSplitCache |
| struct | EnergyCriterion |
| The "energy_gradient" criterion: the front is at a critical point of Phase B's energy, and every edge resolves the pull that drives it there. More... | |
| struct | FaceSnapshot2d |
| struct | FaceSplitCache |
| 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 | SmoothTrace |
| What smoothing did with each class of vertex, per pass. More... | |
| struct | TagPolyline2d |
| The per-tag boundary polyline, with the adjacency an arclength walk needs. More... | |
Public Types | |
| enum class | EdgeSplitMode { Midpoint = 0 , Optimization = 2 } |
| enum class | OptPhase { A , B , Single } |
| Which half of the alternating optimization is running. More... | |
| 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 > |
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 () |
| 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< SampleEnvelope > | containment_for (uint64_t region_mask, bool on_offset) const |
| The containment a simplex with this region mask, on/off the offset front, must satisfy – the intersection of everything that holds it, or null if nothing does. | |
| bool | project_into_containment (size_t vid, Vector2d &x) const |
Move x back inside every region tube this vertex lies on. True if it ended up inside all of them. | |
| int64_t | tangent_curve_tag (size_t vid, const Vector2d &x) const |
| Which tag's boundary curve a vertex slides along, or -1. | |
| bool | walk_along_curve (int64_t tag, const Vector2d &x, double s, Vector2d &out) const |
March s of arclength along tag tag's boundary polyline from x's foot on it. | |
| bool | curve_tangent (int64_t tag, const Vector2d &x, const Vector2d &prefer, Vector2d &tau) const |
The unit tangent of tag tag's curve at x's foot, or false if there is none. | |
| bool | phase_places_front () const |
| 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 | check_no_vertex_on_both_surfaces (const char *when) const |
| void | optimize_offset_single_phase () |
| TriWild's loop, the front placed inside its smoothing passes. | |
| double | phase_b_front_gradient_linf () |
| TopoOffsetTriMesh (Parameters &_m_offset_params, int _num_threads=0) | |
| 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 |
| Is an edge part of the substructure. | |
| 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 | init_offset_sizing_field () |
| bool | marching_split_edge_before (const Tuple &t) |
| bool | marching_split_edge_after (const Tuple &t) |
| bool | collapse_edge_before (const Tuple &t) override |
| Reject any collapse that violates the substructure link condition. | |
| bool | swap_edge_before (const Tuple &t) override |
| Reject a flip whose new edge already exists. | |
| 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. | |
| bool | edge_is_on_domain_boundary (const size_t eid) const |
| 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. | |
| 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}. | |
| 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< SampleEnvelope > | envelope_for_mask (uint64_t mask) const |
| The envelope a simplex with this boundary mask is contained in, or null. | |
| std::shared_ptr< SampleEnvelope > | surface_envelope_for_edge (const std::array< size_t, 2 > &vids) const override |
| Class-0 segments – every region boundary, the input complex and the domain wall included – carry a containment requirement; the offset boundary does not. | |
| 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< SampleEnvelope > | 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< SampleEnvelope > | smoothing_containment_envelope (const size_t vid) const override |
| ... and it is not contained by one either, except in Phase A. | |
| bool | smooth_front_vertex_phase_b (const Tuple &t) |
| Phase B placement of a front vertex: the shared smoother with the offset's options. | |
| double | front_vertex_normal_gradient (size_t vid) const |
| Vector2d | front_vertex_move_direction (size_t vid) const |
| bool | front_vertex_alignment_traps_1d_solve (size_t vid) const |
| double | front_vertex_conv_ratio (size_t vid) const |
| double | edge_conv_ratio (const Tuple &e) const |
| The edge test divided by its bar (1 = bar), per front_conv_criterion; -1 unmeasurable. | |
| 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 > | phase_b_front_objective (size_t vid, const Vector2d &x) const |
| std::shared_ptr< polysolve::nonlinear::Problem > | smoothing_extra_energy (const size_t vid) const override |
| std::shared_ptr< SampleEnvelope > | 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 > | phase_b_front_energy (size_t vid, const std::shared_ptr< const OffsetPotential2D > &pot) const |
| std::tuple< double, double > | optimization_quality_stats () override |
| The loop's convergence metric, normalized so that 1.0 means "done". | |
| double | optimization_stop_metric () const override |
| 1.0 in Phase B, where the metric is normalized; the base's stop_energy in Phase A. | |
| int | offset_residual_samples () const |
| double | offset_residual_tolerance () const |
| double | offset_gradient_tolerance () const |
| The convergence tolerance: the bound on |grad (Phi - c)^2| at a band vertex. | |
| double | gradient_reference () 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 |
| EdgeSamples | offset_edge_samples (const Tuple &e) const |
The Phi residual at offset_residual_samples interior points of band edge e. | |
| template<typename Visit > | |
| void | for_each_offset_edge_sample (const Tuple &e, Visit &&visit) const |
| Visit the same interior sample points offset_edge_samples() measures on. | |
| GradientSplit | gradient_split (bool include_edge_samples=true) const |
| EnergyCriterion | energy_criterion () |
| bool | front_vertex_touches_other (size_t vid) const |
| double | front_chord_target (size_t va, size_t vb, double len, double sag, double tube) const |
| size_t | refine_front_from_sag (const std::vector< EnergyCriterion::Refinable > &edges) |
| double | edge_interpolation_residual (const Tuple &e) const |
| The interpolation residual of front edge e, see EnergyCriterion. -1 when unmeasurable. | |
| Vector2d | offset_vertex_normal (const size_t vid) const |
| The normal at an offset vertex. Every caller that needs one goes through here, so switching the definition is a one-line edit rather than a hunt through the call sites. | |
| 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 Phase A 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 v2, double q, double ring_max) const override |
| Instrumentation only: which operation manufactures the MAX_ENERGY needles. | |
| 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 |
| bool | collapse_edge_after (const Tuple &t) override |
| A collapse is accepted by the same criterion the smoothing minimises. | |
| double | face_criterion_rel (const size_t fid) const |
| void | write_smoothing_debug_output (const std::string &path) const override |
| Put the frames beside the run's own output, and rename them into one timeline. | |
| void | append_frame_label (size_t idx, const std::string &label) const |
| 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 | pre_optimize_input_mesh () |
| TriWild over the input mesh, before any of the offset exists. | |
| void | execute_offset (const std::filesystem::path &output_file) |
| void | marching_tris () |
| execute simplistic marching tris. All edges with one vertex labelled 0 and the other 1/2 are split, always at the midpoint. | |
| 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) |
| void | write_msh_groups (const std::string &file) |
| 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 | |
| std::vector< Tuple > | get_edge_adjacent_faces (const Tuple &f) const |
| get faces (as Tuples) that are edge-adjacent to the given face (as Tuple) | |
Public Member Functions inherited from wmtk::TriOptimizerMesh | |
| 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 () |
| virtual void | update_attributes () |
| Update the attributes of the mesh after an iteration of operations. | |
| 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 | 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 | 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. | |
| 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_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 |
| 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. | |
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 | |
| 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 |
| Tuple | tuple_from_cell (size_t cid) const |
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 | |
| int | m_vtu_counter = 0 |
| 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< SampleEnvelope > > | m_tag_envelopes |
| One containment envelope per input tag, ambient included. Both phases. | |
| std::vector< Eigen::Vector2d > | m_env_polyline_V |
| std::map< int64_t, TagPolyline2d > | m_tag_polyline |
| std::map< int64_t, int > | m_tag_bit |
| std::map< uint64_t, std::shared_ptr< SampleEnvelope > > | m_isect_cache |
| std::mutex | m_isect_mutex |
| std::map< uint64_t, std::shared_ptr< SampleEnvelope > > | m_offset_isect_cache |
| Memoized "region tubes AND the offset envelope", keyed by the region mask. | |
| OptPhase | m_phase = OptPhase::A |
| Which phase is running. Read by every hook that differs between them; see OptPhase. | |
| bool | m_freeze_front = false |
| The final Phase A: front vertices are not smoothed (see smooth_before()). | |
| std::shared_ptr< SampleEnvelope > | m_offset_envelope |
| The tube the offset boundary may not leave during Phase A, of half-width offset_envelope_rel x target_distance. Rebuilt at the end of every Phase B from the boundary as that phase left it, which is what lets the boundary travel across rounds. Non-null once the offset exists; whether it constrains is containment_for()'s phase test, not the pointer. Unlike m_tag_envelopes, which must never be rebuilt. | |
| double | m_front_gradient_reference = 0. |
| 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 |
| std::vector< std::array< int, 3 > > | churn_counts |
| std::vector< std::array< int, 3 > > | op_counts |
| int | m_ab_round = 0 |
| size_t | m_debug_seq = 0 |
| Monotonic frame counter for the debug timeline. Mutable because the write hook is const. | |
| int | m_debug_pass = 0 |
| int | m_debug_last_round = -1 |
| char | m_debug_last_phase = '?' |
| double | m_gradient_reference = 0. |
| See offset_gradient_tolerance(). Nothing sets it on the single-phase path; it stays 0. | |
| bool | m_converged = false |
| bool | m_quality_converged = true |
| double | m_quality_max_amips = 0. |
| std::atomic< int > | iter_cnt_split_born {0} |
| std::atomic< int > | iter_cnt_recollapsed {0} |
| std::atomic< int > | iter_cnt_recollapsed_same_pass {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 face over tolerance. | |
| std::atomic< int > | iter_cnt_swap_offset_reject {0} |
| std::atomic< int > | iter_cnt_split_offset_before {0} |
| Splits of an offset-boundary edge: offered, accepted. | |
| std::atomic< int > | iter_cnt_split_offset {0} |
| wmtk::threading::enumerable_thread_specific< OptSplitCache2d > | m_opt_split_cache |
| SmoothTrace | m_smooth_trace |
| 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 |
| std::atomic< int > | m_placement_env_entry_outside {0} |
| std::atomic< int > | m_placement_projected {0} |
| std::atomic< int > | m_placement_tangential {0} |
| size_t | m_front_gradient_worst_vid |
| 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< SampleEnvelope > | 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::vector< char > | m_placement_pressed |
| std::atomic< size_t > | m_deg_split_created {0} |
| std::atomic< size_t > | m_deg_collapse_offered {0} |
| std::atomic< size_t > | m_deg_collapse_allowed {0} |
| std::atomic< size_t > | m_deg_collapse_by_ringmax {0} |
| std::atomic< size_t > | m_deg_collapse_by_stop {0} |
| std::atomic< size_t > | m_deg_collapse_by_unrounded {0} |
| std::array< size_t, 6 > | m_deg_prev_counts {{0, 0, 0, 0, 0, 0}} |
| Values at the previous census, so each census can report deltas rather than totals. | |
| 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 |
| 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< SampleEnvelope > | 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 |
| CoarsenStats | m_coarsen_stats |
Public Attributes inherited from wmtk::TriMesh | |
| AbstractAttributeContainer * | p_vertex_attrs = nullptr |
| AbstractAttributeContainer * | p_edge_attrs = nullptr |
| AbstractAttributeContainer * | p_face_attrs = nullptr |
| 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 | 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 Public Attributes inherited from wmtk::TriMesh | |
| static constexpr int | EDGES_PER_CELL = 3 |
Private Member Functions | |
| bool | any_tag_present (const CellTag &tag1, const CellTag &tag2) const |
| determine if any tag from tag1 is also present in tag2. | |
| 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 | |
Protected Member Functions inherited from wmtk::TriOptimizerMesh | |
| 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 | optimization_debug_checkpoint () |
| Called at every pass boundary, whether or not debug output is on. | |
| 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.
|
strong |
Which half of the alternating optimization is running.
The two criteria are optimized in turn, not jointly. The 2D counterpart of TopoOffsetTetMesh::OptPhase.
Phase A is TriWild and nothing else: same operations, gates, sizing field and stall-driven refinement, with no offset energy term, acceptance criterion or stop metric. Its one addition is m_offset_envelope.
Phase B moves the offset boundary and nothing else: smoothing passes against the offset energy, run to a fixed point, with no envelope on the offset (it is what has to travel) and no topological operations at all.
The sizing field is shared and both phases write it: Phase A through TriWild's stall refinement on element quality, Phase B through the Phi residual of the faces smoothing could not place. Single is the mode the 2D run uses: TriWild's own loop with the front placed by Phase B's objective inside the smoothing passes. It follows B wherever the smoother is concerned (which objective a front vertex gets, no offset tube while it moves) and A everywhere the loop is concerned (quality stats and the stop metric are TriWild's).
| void wmtk::components::topological_offset::TopoOffsetTriMesh::append_frame_label | ( | size_t | idx, |
| const std::string & | label | ||
| ) | const |
One line of <output>_frames.txt; truncates the file on the first frame. See write_smoothing_debug_output().
| 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 Phase A moved the offset boundary out of the tube holding it where Phase B left 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 (TagEnvelopes.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, and the pre-optimize pass holds every tag boundary as it always did), 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 and after every phase.
| 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 |
A collapse is accepted by the same criterion the smoothing minimises.
The smoother places an offset vertex by minimising w (Phi - c)^2, so every other operation must answer to that same measure or it undoes in one collapse what the smoother spent an iteration achieving. A length gate cannot express it: it asks whether an edge is short against a sizing target, a statement about the mesh, not whether the boundary is still the offset, a statement about the geometry – and only the second is what the run is for.
Reimplemented from wmtk::TriMesh.
|
overridevirtual |
Reject any collapse that violates the substructure link condition.
The base applies it 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.
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.
|
overridevirtual |
Instrumentation only: which operation manufactures the MAX_ENERGY needles.
The base leaves two doors open and this counts what goes through each.
q <= ring_max, and once a single needle sits in the ring ring_max is MAX_ENERGY – so the clause admits a collapse that produces another needle. The counters below say which clause did the admitting. 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().
| std::shared_ptr< SampleEnvelope > 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, but only in Phase A – Phase B is the pass whose job is to move the offset boundary, so there the result is the region tubes alone.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::curve_tangent | ( | int64_t | tag, |
| const Vector2d & | x, | ||
| const Vector2d & | prefer, | ||
| Vector2d & | tau | ||
| ) | const |
The unit tangent of tag tag's curve at x's foot, or false if there is none.
From nearest_point_feature()'s seg_normal, rotated a quarter turn. At a polyline vertex the tangent is two-valued, so this takes the incident segment best aligned with the descent the caller is about to attempt – the standard reading of a one-sided derivative at a kink.
| 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: execute_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 |
Is an edge part of the substructure.
| vids | The vertex IDs of the edge |
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.
| std::shared_ptr< SampleEnvelope > 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 (AMIPS over stop, residual over tolerance) on this face; >= 1 means it fails at least one. The coarsen-mode collapse accept reads it, and it is the per-face form of optimization_quality_stats()'s Phase B max.
| 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.
|
inline |
Visit the same interior sample points offset_edge_samples() measures on.
Factored out so the residual and the convergence gradient are measured on one lattice – a criterion sampled on a different set of points from the quantity the sizing field refines by is two measurements pretending to be one. The 3D twin is for_each_offset_face_sample().
| 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 sag 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). See the definition; energy_criterion() and refine_front_from_sag() both use it.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::front_vertex_alignment_traps_1d_solve | ( | size_t | vid | ) | const |
Whether the 1-D placement at vid is trapped by the alignment term: a live front edge at or past perpendicular to the field AND the alignment term's 1-D gradient opposing the placement term's along the move direction. See the definition.
| double wmtk::components::topological_offset::TopoOffsetTriMesh::front_vertex_conv_ratio | ( | size_t | vid | ) | const |
The vertex's convergence measure divided by its bar, per front_conv_criterion: 1 is the bar. See the spec entry for the three measures. Infinite when unmeasurable.
| 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, F the objective smooth_front_vertex_phase_b() minimises. +inf if unmeasurable. The pass stop and the loop's vertex test.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::front_vertex_touches_other | ( | size_t | vid | ) | const |
Whether a front vertex touches another front, the input or a region boundary through a background triangle – the topological fact behind the pressed state.
|
inline |
max |2 (Phi - c) grad Phi . n| over the initial offset-surface vertices; the scale offset_gradient_tolerance() is a fraction of. Always 0 on the single-phase path (never measured); kept for the report.
| 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_offset_sizing_field | ( | ) |
Seed the sizing field from the offset's current edge lengths (paper Sec. 5.3.3, Step 1), once, before the first operation pass. Without it the field starts at the background target length and the first collapse pass decimates the offset.
| 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.
| 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 Phase A 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, in either phase. Construction cannot produce one, so a non-zero count here is a hole opened afterwards. Called at construction and at each Phase B entry so the two can be compared.
| void wmtk::components::topological_offset::TopoOffsetTriMesh::log_stuck_refine_census | ( | double | max_metric, |
| double | filter_energy | ||
| ) |
Why Phase A is stuck: a census of the faces stuck-refine is about to chase.
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 | ( | ) |
execute simplistic marching tris. All edges with one vertex labelled 0 and the other 1/2 are split, always at the midpoint.
No target_distance enters construction at all: the paper places inserted vertices at the midpoint (Sec. 5.2) and leaves the distance to Step 3, so carrying the boundary out to the level set is entirely the optimization phase's job. Same as 3D.
| 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 pre-pass, 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 offset_residual_samples interior points of band edge e.
The criterion cannot be a vertex criterion: a boundary can have every vertex exactly on the level set while zig-zagging or cutting corners between them, which reads as converged and is not the offset. That is the gap the paper's normal-deviation criterion (Sec. 5.3.3) covered.
Sampling the edges is what the potential makes possible and a distance field did not: Phi is defined everywhere, so the offset can be measured anywhere along the band rather than only where the mesh happens to have put a vertex. The same samples feed face_criterion_rel(), so the sizing field refines a band too coarse to represent the offset instead of letting it decimate.
Samples are uniform interior points, i/(k+1) for i = 1..k, so k = 1 is the midpoint. Returns nothing for an edge with an unreachable endpoint: a segment running onto the input complex is legitimately closer than target_distance along its length.
|
inline |
The convergence tolerance: the bound on |grad (Phi - c)^2| at a band vertex.
A fraction of target_distance, which is the right unit: grad E = 2 (Phi - c) grad Phi, and grad Phi is dimensionless for a field whose value is a length, so grad E is a length.
The gradient needs no conversion of Phi's value into a length – it is the stationarity condition of the objective Phase B minimises, so it is the same test for the exact Euclidean field and for the smooth potential alike, which is what lets a reentrant input be judged by the same number as a convex one. See TopoOffsetTetMesh::offset_gradient_tolerance() for the full derivation of the slope normalization.
|
inline |
Samples per band edge; see offset_edge_samples(). 0 falls back to a vertex-only criterion, which is measurably blind to a band too coarse to be the offset.
|
inline |
The residual scale, derived from the criterion rather than configured beside it.
grad E = 2 (Phi - c) grad Phi, so on a field with unit slope at the level set the gradient bound |grad E| <= g is exactly |Phi - c| <= g/2: half the gradient tolerance, in length units. It feeds the Phase A offset envelope (offset_envelope_rel x this) and the derived min_edge_length floor, so loosening the criterion loosens the tube with it.
| Vector2d wmtk::components::topological_offset::TopoOffsetTriMesh::offset_vertex_normal | ( | const size_t | vid | ) | const |
The normal at an offset vertex. Every caller that needs one goes through here, so switching the definition is a one-line edit rather than a hunt through the call sites.
n is the unit vector from the nearest point on the input complex to the vertex – the direction the offset grew along. A property of the input geometry alone, so it does not move as the offset mesh is re-triangulated and it is defined for every band vertex whether or not it has live offset edges. Known weakness: it flips discontinuously across the medial axis, exactly where two offset fronts approach each other.
|
inlineoverridevirtual |
TriWild's bare collapse passes are off for the offset: the opening one (length gate off), the closing one, and coarsen_mesh(). The opening pass is meant to run once on an inserted input, but the A/B loop calls mesh_improvement() every round, so with no length gate the quality test alone demolishes the band interior and background each round. The sizing field cannot refuse a collapse – only the length gate can, and that pass switches it off – and the offset envelope holds the boundary, not the interior. Phase A is otherwise TriWild's mesh_improvement(), untouched.
Reimplemented from wmtk::TriOptimizerMesh.
|
overridevirtual |
The loop's convergence metric, normalized so that 1.0 means "done".
The max of the two criteria this optimization has to meet, each divided by its own target, so mesh_improvement() stops exactly when both are met:
The average returned alongside it is the same expression over the two averages, so both numbers live on the same 1.0 scale. Nothing reads the average; it is logged.
Reimplemented from wmtk::TriOptimizerMesh.
|
inlineoverridevirtual |
1.0 in Phase B, where the metric is normalized; the base's stop_energy in Phase A.
The units are part of "identical to TriWild", and getting this wrong is silent. The pair (optimization_quality_stats, optimization_stop_metric) has to be in one set of units, because refine_sizing_around_worst() derives its filter from the first and then compares that filter against a per-face score. Phase A ranks by m_face_attribute[].m_quality, which is absolute AMIPS, so its metric and its bar must be absolute too – otherwise select_worst_cells returns nothing, no sizing is refined, and the stall detector fires every iteration and does nothing.
Reimplemented from wmtk::TriOptimizerMesh.
| std::shared_ptr< polysolve::nonlinear::Problem > wmtk::components::topological_offset::TopoOffsetTriMesh::phase_b_front_energy | ( | size_t | vid, |
| const std::shared_ptr< const OffsetPotential2D > & | pot | ||
| ) | const |
The two offset terms for a front vertex, see smooth_front_vertex_phase_b(): the zeroth-order OffsetEnergy2D and the first-order AlignEnergy2D (one residual per incident live front edge). Defined in FrontSmooth2d.cpp, next to the criterion measuring the same quantities.
| double wmtk::components::topological_offset::TopoOffsetTriMesh::phase_b_front_gradient_linf | ( | ) |
Max over the front vertices Phase B places of ||grad F||, F the vertex's full Phase B objective (AMIPS + the offset terms, as the shared smoother assembles it). The pass stop.
| std::shared_ptr< polysolve::nonlinear::Problem > wmtk::components::topological_offset::TopoOffsetTriMesh::phase_b_front_objective | ( | size_t | vid, |
| const Vector2d & | x | ||
| ) | const |
The Phase B objective of front vertex vid with the vertex at x: AMIPS of its one-ring + phase_b_front_energy(). What the measure above differentiates.
|
inline |
Whether the smoother places front vertices against the offset objective: Phase B, and the single-phase mode that does the same thing inside TriWild's passes.
|
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::pre_optimize_input_mesh | ( | ) |
TriWild over the input mesh, before any of the offset exists.
Runs the shared mesh_improvement() with Phase A's own parameters and units, at a point where the only tracked surfaces are the tag-region boundaries (input complex and domain wall among them) and the only containment is their per-tag envelopes. There is no offset yet, so no offset envelope and no Phi term: this is TriWild, exactly.
Worth a pass because marching_tris() puts the offset boundary on the background triangulation's own cell boundaries: how far the constructed offset lands from the complex is a property of the input mesh, and init_offset_potential() sizes dhat from that reach. One coarse cell touching the complex inflates dhat several-fold, and a large dhat merges the level sets of features that are close together, which no later stage can undo.
The sizing field is seeded first, to target_distance on the input-complex boundary and graded outward: a band built on cells of delta scale lands near delta from the complex, which is what keeps dhat small. Unseeded, the base field is m_params.l everywhere, far coarser than the mesh around the complex, so the pass would coarsen the input instead.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::project_into_containment | ( | size_t | vid, |
| Vector2d & | x | ||
| ) | const |
Move x back inside every region tube this vertex lies on. True if it ended up inside all of them.
The projection half of the projected-gradient placement: an offset vertex a region envelope also holds takes the same unconstrained step on the same objective as any other, and this restores its validity. Refusal cannot express "go as far as you may" – a vertex whose every trial step leaves the tube never moves at all – while projecting keeps the component of the step the tube allows, which for a vertex on a boundary curve is motion along that curve.
Never asks a composite: nearest_point() and squared_distance() are non-virtual on SampleEnvelope and would bind to an IntersectionEnvelope's base subobject, whose BVH was never built (TagEnvelopes.hpp). So this walks the mask's real members from m_tag_envelopes and composes them by alternating projection onto the worst-violated one; each nearest_point() lands x on that member's curve, so the rounds converge a junction onto the intersection of its curves.
Returns false if the alternation did not converge, which is the caller's signal to keep the entry position rather than commit an invalid one.
| 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. Called when the offset is created and at the end of every Phase B.
| size_t wmtk::components::topological_offset::TopoOffsetTriMesh::refine_front_from_sag | ( | const std::vector< EnergyCriterion::Refinable > & | edges | ) |
The resolution rule: sets the target length at each refinable edge's ends from front_chord_target(), graded outward. Returns the vertices changed.
|
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.
Phase A only: mesh_improvement() is its one caller, and the driver only runs that as Phase A. Phase B's refinement question belongs to update_band_sizing_from_tolerance().
Implements wmtk::TriOptimizerMesh.
| 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< SampleEnvelope > 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.
| 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; 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, but m_pos must still be filled because the shared split's exact-midpoint fallback reads it.
|
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.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::smooth_front_vertex_phase_b | ( | const Tuple & | t | ) |
Phase B placement of a front vertex: the shared smoother with the offset's options.
The same 2-D Newton solve, line search and accept tests as any TriWild vertex; the objective carries the offset terms through smoothing_extra_energy(). No quality veto, because a front vertex has to be able to worsen its ring on its way to the level set – element shape is Phase A's job.
| 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.
|
inlineoverridevirtual |
... and it is not contained by one either, except in Phase A.
Phase A is TriWild and its smoothing minimises AMIPS alone; without a container nothing would stop it relocating the offset boundary for the sake of element shape, which Phase B would then have to undo. Phase B keeps null – that is the pass whose job is to move it.
Boundary geometry is contained in the intersection of its tags' tubes, in both phases: the caller only asks is_outside(segment), which composites answer, so unlike the pull this side may hand out an IntersectionEnvelope.
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 SampleEnvelope 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 |
Phase B's offset terms, handed to the shared smoother for a front vertex it is placing (null in Phase A and for a front vertex an input envelope also pins) – plus, under deform_others, the rest-shape AMIPS of the deformable faces in the vertex's ring, in every phase. Whichever apply are summed; null when neither does.
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. The base's hook is pure virtual, so this stays as the honest constant rather than being deleted outright.
Implements wmtk::TriOptimizerMesh.
|
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.
|
inlineoverridevirtual |
Class-0 segments – every region boundary, the input complex and the domain wall included – carry a containment requirement; the offset boundary does not.
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 offset boundary is exempt in Phase B, where it is the surface the optimization exists to move and a tube around its initial position would cap how far it can travel. In Phase A it is held by m_offset_envelope instead – a tube of one Phi tolerance around wherever Phase B last left it, rebuilt each round – which turns "do not degrade the offset" from a per-operation criterion into a geometric constraint every shared operation already honours.
Null means "no containment requirement", which the base handles by skipping the check.
Reimplemented from wmtk::TriOptimizerMesh.
|
overridevirtual |
Only to count an accepted flip. The base decides whether it is accepted; there is nothing the offset needs to do to a flip that survives swap_edge_before().
Reimplemented from wmtk::TriMesh.
|
overridevirtual |
Reject a flip whose new edge already exists.
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.
|
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).
| int64_t wmtk::components::topological_offset::TopoOffsetTriMesh::tangent_curve_tag | ( | size_t | vid, |
| const Vector2d & | x | ||
| ) | const |
Which tag's boundary curve a vertex slides along, or -1.
The bit of its mask whose curve passes closest to it. For the common multi-bit case that choice is immaterial: a mask carries a bit per tag on either side of the boundary, so an interface between two regions gives both bits and both curves contain it. Where the curves genuinely differ the line search still tests containment against every member tube, so picking the nearest chooses the parameterization, never the constraint.
|
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 |
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.
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::walk_along_curve | ( | int64_t | tag, |
| const Vector2d & | x, | ||
| double | s, | ||
| Vector2d & | out | ||
| ) | const |
March s of arclength along tag tag's boundary polyline from x's foot on it.
The reduced coordinate of the tangential placement: the constraint is eliminated rather than enforced, so every point this returns is on the curve and needs no containment test of its own. Returns false where the walk cannot continue – an open end, or a polyline vertex where three or more segments meet and the continuation is ambiguous – with out holding the furthest point reached, the end of the feasible interval.
Corners are not special-cased, deliberately. The walk crosses any corner; what stops a vertex sliding past a sharp one is the incident chord leaving its tube, which the caller's backtracking finds. That reproduces the true bound (roughly eps/sin(theta) for a turn of theta, unbounded along a straight run) with no angle threshold anywhere.
| 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.
| 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 frames beside the run's own output, and rename them into one timeline.
Every debug frame in 2D goes through here – the shared driver's per-pass frames, Phase B's, and the A/B driver's per-phase ones – so this is the one place that can give them all a common order. A bare debug_N or phase_<r><A|B> comes out as
<output>_step_<NNNNN>_r<round><A|B><pass>.vtu a pass inside a phase <output>_step_<NNNNN>_r<round><A|B>_end.vtu the frame the phase handed on
NNNNN is a single monotonic counter across the whole run, so sorting on it is run order. <pass> counts passes within the current phase and restarts whenever the round or the phase changes, so r1A3 reads as "round 1, phase A, third pass"; the restart is detected here, so no call site has to remember to reset anything. Round 0 is construction.
Renaming here rather than at the call sites is what keeps this out of the shared driver: wmtk::TriOptimizerMesh is also triwild's and simwild's, so its naming is not ours to change.
Implements wmtk::TriOptimizerMesh.
| std::vector<std::array<int, 3> > wmtk::components::topological_offset::TopoOffsetTriMesh::churn_counts |
{split-born vertices, recollapsed, recollapsed in the immediately following collapse pass} per A/B round, in step with op_counts. See VertexExtra2d::m_born_epoch.
|
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.
| std::atomic<int> wmtk::components::topological_offset::TopoOffsetTriMesh::iter_cnt_split_born {0} |
Churn: split-born vertices that a collapse later removed, and the subset removed in the same pass-pair that created them.
|
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.
| int wmtk::components::topological_offset::TopoOffsetTriMesh::m_ab_round = 0 |
The A/B round the run is in, 1-based; 0 before the loop starts. Read only by write_smoothing_debug_output(), to tag each frame with the sub-iteration it belongs to.
|
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
| bool wmtk::components::topological_offset::TopoOffsetTriMesh::m_converged = false |
The run's verdict: the front placed AND the final quality under stop_energy. Read by the report and by throw_on_nonconvergence.
| 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 |
Pass index within the current phase, and the (round, phase) it belongs to – when those change the index restarts. All three exist only to name frames.
| std::optional<EnergyCriterion> wmtk::components::topological_offset::TopoOffsetTriMesh::m_energy_verdict |
The energy criterion as measured when the A/B loop converged; the final Phase A runs after it and the verdict must not be re-measured on that mesh.
| std::vector<Eigen::Vector2d> wmtk::components::topological_offset::TopoOffsetTriMesh::m_env_polyline_V |
Shared vertex array for every TagPolyline2d: the positions as init_surfaces_and_boundaries() saw them, indexed by the mesh vid at construction. Never renumbered – the envelopes hold the same snapshot, and both describe the input, not the live mesh.
| double wmtk::components::topological_offset::TopoOffsetTriMesh::m_front_gradient_reference = 0. |
Its value on the band as constructed, measured once before round 1: the reference the Phase B pass stop is a fraction of.
|
mutable |
argmax of phase_b_front_gradient_linf()
| 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 execute_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.
| 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.
|
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 rebuilt after every Phase B. rebuild_offset_envelope() clears this and must keep doing so – a stale entry holds the previous round's offset tube and would pin the boundary to where it was two rounds ago. 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().
|
mutable |
How many Phase B offset placements found the vertex already outside its own envelope on entry. The post-step projection pulls it back in, but the invariant is 0: a nonzero count means construction or Phase A leaves offset vertices outside their region tube. A run total.
| std::vector<char> wmtk::components::topological_offset::TopoOffsetTriMesh::m_placement_pressed |
Set by the placement when a vertex's last visit stopped on QualityBound, cleared when it moved. distance_criterion() counts such a vertex as placed – its level set is unreachable by construction – and drops edges touching one from the resolution and orientation halves; update_band_sizing_from_tolerance() does not refine such edges.
|
mutable |
How many Phase B offset placements had their accepted step projected back into the vertex's region tubes – expected wherever the offset coincides with a region boundary, and not a problem: it counts constrained motion along a boundary curve. Read it against the EnvelopeBlocked count, where a projection that could not be committed lands. A run total.
|
mutable |
How many Phase B offset placements were solved tangentially – reduced to arclength along the vertex's own tag boundary curve rather than stepped freely in 2D, expected for every offset vertex a region envelope also holds. Read it against ChordBlocked, which counts the visits whose slide an incident chord leaving its tube cut off. A run total.
| 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 placed, 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: a SampleEnvelope 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::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().
| 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<SampleEnvelope> > 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<int, 3> > wmtk::components::topological_offset::TopoOffsetTriMesh::op_counts |
{splits, collapses, swaps} per A/B round – one entry per round the driver runs, including the round that converges, as deltas rather than running totals. Phase B does no topological work, so a round's entry is exactly what its Phase A did. This does not mirror optimization_metrics, which is a single whole-run summary.
| 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.