Wildmeshing Toolkit
Loading...
Searching...
No Matches
Classes | Public Types | Public Member Functions | Public Attributes | Static Public Attributes | Private Member Functions | Private Attributes | List of all members
wmtk::components::topological_offset::TopoOffsetTriMesh Class Reference

The offset's 2D mesh, on the shared 2D optimizer. More...

#include <TopoOffsetTriMesh.h>

Inheritance diagram for wmtk::components::topological_offset::TopoOffsetTriMesh:
wmtk::TriOptimizerMesh wmtk::TriMesh wmtk::RationalPositions wmtk::components::manifold_extraction::ManExtractTriMesh

Classes

struct  EdgeSnapshot2d
 An edge's / face's shared attributes together with the offset's own label. More...
 
struct  EdgeSplitCache
 
struct  FaceSnapshot2d
 
struct  FaceSplitCache
 
struct  OptSplitCache2d
 
struct  SmoothTrace
 Why smoothing refused a vertex, one counter per site where it can say no. More...
 

Public Types

enum class  EdgeSplitMode { Midpoint = 0 , Initial = 1 , Optimization = 2 }
 
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

 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_boundary (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.
 
bool project_offset_vertex (const Tuple &t)
 
bool region_boundary_tangent (const Tuple &t, Vector2d &tang) const
 The direction the REGION curve runs through this vertex, if it has one.
 
double minimize_distance_along_tangent (const size_t vid, const Vector2d &p0, const Vector2d &tang, const double cap, const std::function< bool()> &rejected)
 Put a vertex at the distance-error minimum along its region tangent.
 
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
 
bool edge_is_region_boundary_live (const Tuple &t) const
 Live region-boundary test for an edge that currently exists in the mesh.
 
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
 
std::pair< double, double > compute_normal_deviation () const
 How far the offset boundary faces from where it should be: {max, avg} in DEGREES.
 
double edge_normal_deviation (const Tuple &e) const
 Paper Definition 5 for a 1-simplex: max angle between the offset normal at the edge's center and the offset normal at p_i = 0.1*p_c + 0.9*p_v, in degrees.
 
bool offset_field_normal (const Vector2d &p, Vector2d &n) const
 
bool edge_is_offset_surface_live (const Tuple &e) const
 
double max_offset_normal_deviation_at_vertex (const size_t vid) 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
 
bool collapse_edge_after (const Tuple &t) 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 edge_is_input (const size_t eid) const
 
bool vertex_is_frozen (const size_t vid) const
 The two simplex sets the optimization may not touch at all.
 
bool edge_is_frozen (const size_t eid) const
 
void init_region_boundary_envelope ()
 Build the containment envelope over every tag-region boundary, once, before the optimization starts.
 
void init_region_boundary_envelope_from_input ()
 Build the same envelope from the INPUT mesh, before the offset exists.
 
void warn_if_offset_reaches_domain_boundary () const
 Warn if the offset band has grown into the domain boundary.
 
bool region_boundary_is_outside_envelope (const size_t vid) const
 
void log_smooth_trace () const
 
std::shared_ptr< SampleEnvelopesurface_envelope_for_edge (const std::array< size_t, 2 > &vids) const override
 Only REGION_SURFACE_CLASS segments carry a containment requirement.
 
bool smoothing_position_is_allowed (size_t vid, const Vector2d &) const override
 
size_t refine_sizing_around_worst (double) override
 Sizing refinement over the offset polyline.
 
size_t update_sizing_field ()
 
void write_smoothing_debug_output (const std::string &path) const override
 
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)
 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 ()
 initialize BVH for input complex. Must be called after init_from_image(...)
 
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 execute_offset (const std::filesystem::path &output_file)
 entry point for offset procedure
 
void marching_tris ()
 execute simplistic marching tris. All edges with one vertex labelled 0 and the other 1/2 are split, at the midpoint (m_edge_split_mode=Midpoint) or at the hacky initialization offset (m_edge_split_mode=Initial).
 
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)
 
bool tag_tri_consistent_topology (size_t f_id, int64_t tag) const
 check if removing the given face from the given tag set would retain its topology
 
bool offset_tri_consistent_topology (const size_t f_id) const
 check if adding a triangle to the offset region does not change the topology of the offset. Returns true if topology would not be changed
 
bool tri_is_in_offset_conservative (const size_t f_id, const double threshold_r) const
 check if a triangle is inside the offset (implicitly defined via BVH distance field to input complex) via conservative circle subdivision estimation
 
void grow_offset_conservative ()
 grow offset region conservatively using conservative checks while ensuring consistent topology
 
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_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< Tupleget_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 &params)
 
size_t get_partition_id (const Tuple &loc) const
 
void partition_mesh ()
 
void partition_mesh_morton ()
 
double get_length2 (const Tuple &l) const
 
bool is_inverted (const std::array< size_t, 3 > &vs) const
 Orientation check, exact for the coordinates the vertices actually carry.
 
bool is_inverted (const Tuple &loc) const
 
bool is_inverted (const size_t fid) const
 
bool is_inverted_f (const Tuple &loc) const
 Inversion check using only the double positions.
 
bool is_inverted_f (const size_t fid) const
 
double get_quality (const std::array< size_t, 3 > &vs) const
 
double get_quality (const Tuple &loc) const
 
double get_quality (const size_t fid) const
 
std::tuple< double, double > get_max_avg_energy ()
 
void mesh_improvement (int max_its=80)
 
std::tuple< double, double > local_operations (const std::array< int, 4 > &ops, bool collapse_limit_length=true)
 
bool 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< Tupleget_vertices () const
 
std::vector< Tupleget_edges () const
 
std::vector< Tupleget_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
 
long request_tri_slots (size_t n)
 
long 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< Tupleswitch_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< Tupleswitch_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< Tupleget_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< Tupleget_one_ring_edges_for_vertex (const Tuple &t) const
 Get all edges that are incident to the vertex of Tuple t.
 
std::vector< Tupleget_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.
 
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
 
SimplicialComplexBVH m_input_complex_bvh
 
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
 
Parametersm_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, 4 > > optimization_metrics
 
std::vector< std::array< int, 3 > > op_counts
 
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_nd_reject = 0
 
std::atomic< int > iter_cnt_swap_nd_reject = 0
 
wmtk::threading::enumerable_thread_specific< double > m_collapse_nd_before
 
wmtk::threading::enumerable_thread_specific< double > m_swap_nd_before
 
wmtk::threading::enumerable_thread_specific< std::array< size_t, 4 > > m_swap_verts
 
wmtk::threading::enumerable_thread_specific< OptSplitCache2dm_opt_split_cache
 
SmoothTrace m_smooth_trace
 
- 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.
 
OptimizerParametersm_params
 
std::shared_ptr< SampleEnvelopem_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
 
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::Edgem_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
AbstractAttributeContainerp_vertex_attrs = nullptr
 
AbstractAttributeContainerp_edge_attrs = nullptr
 
AbstractAttributeContainerp_face_attrs = nullptr
 
wmtk::threading::enumerable_thread_specific< std::vector< size_t > > mutex_release_stack
 
int NUM_THREADS = 0
 

Static Public Attributes

static constexpr int INPUT_SURFACE_CLASS = 0
 SurfaceTagAttributes::m_surface_class: which of the three tracked surfaces an edge belongs to.
 
static constexpr int OFFSET_SURFACE_CLASS = 1
 
static constexpr int REGION_SURFACE_CLASS = 2
 
- 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< EdgeSplitCacheedge_split_cache
 
wmtk::threading::enumerable_thread_specific< FaceSplitCacheface_split_cache
 

Additional Inherited Members

- Protected Member Functions inherited from wmtk::TriOptimizerMesh
virtual std::tuple< double, double > optimization_quality_stats ()
 
virtual double optimization_stop_metric () const
 
virtual bool optimization_stop_at_float () const
 
virtual void collapse_pass_begin ()
 
virtual void collapse_pass_end (size_t)
 
virtual bool collapse_quality_allowed (size_t v1, size_t, double q, double ring_max) const
 
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
wmtk::threading::enumerable_thread_specific< SplitInfoCachesplit_cache
 
wmtk::threading::enumerable_thread_specific< CollapseInfoCachecollapse_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.
 

Detailed Description

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.

Three surfaces are tracked, each its own m_surface_class. The input complex is INPUT_SURFACE_CLASS and is frozen outright. The offset boundary is OFFSET_SURFACE_CLASS: in 2D it is exactly the set of edges across which the incident FACE LABELS differ – there is no stored definition of it, it falls out of the labelling, which is why label_offset_boundary() recomputes it once at the top of the optimization. Every other tag boundary is REGION_SURFACE_CLASS, tracked so the shared swap will not flip across it and silently move a region, but otherwise an ordinary part of the background mesh. The latter two may move, within m_envelope; only the offset one is driven toward target_distance.

Member Function Documentation

◆ collapse_after_vertex()

void wmtk::components::topological_offset::TopoOffsetTriMesh::collapse_after_vertex ( size_t  v1,
size_t  v2 
)
overridevirtual

Reimplemented from wmtk::TriOptimizerMesh.

◆ collapse_before_vertex()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::collapse_before_vertex ( size_t  v1,
size_t  v2 
)
overridevirtual

Reimplemented from wmtk::TriOptimizerMesh.

◆ collapse_edge_after()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::collapse_edge_after ( const Tuple t)
overridevirtual

The offset's normal-deviation guard on collapse (paper Sec. 5.3.3, Step 2). Needs to be the bool-returning hook rather than collapse_after_vertex(), which cannot reject.

Reimplemented from wmtk::TriMesh.

◆ collapse_edge_before()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::collapse_edge_before ( const Tuple t)
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. It is not enough here: the offset region is a thin band, and a collapse with only one endpoint on the boundary can still pinch the two sides of that band together, which is precisely what makes the region stop being manifold. The offset asks unconditionally.

Reimplemented from wmtk::TriMesh.

◆ compute_distance_deviation()

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|, taken 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, so an average that looks fine can still hide a stretch of boundary sitting far off the target. Mirrors the 3D TopoOffsetTetMesh::compute_distance_deviation().

◆ compute_normal_deviation()

std::pair< double, double > wmtk::components::topological_offset::TopoOffsetTriMesh::compute_normal_deviation ( ) const

How far the offset boundary faces from where it should be: {max, avg} in DEGREES.

The angle between an offset-boundary edge's own normal and the direction the distance field says it should be facing – the unit vector from the nearest point on the input complex out to a sample on the edge. Measured over the same band-outer-surface edges compute_distance_deviation() measures vertices over, and reported alongside it.

Distance error alone does not pin down the offset: a boundary can have every vertex at exactly target_distance while zig-zagging between them, which reads as converged but is not the offset. The normal deviation is what sees that, which is why the paper's own Termination criterion (Sec. 5.3.3) tests sigma_max alongside the distance errors.

◆ edge_is_input()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::edge_is_input ( const size_t  eid) const
inline

Note the test is == INPUT, not != OFFSET: a region boundary is neither, and calling one "input" would freeze it outright via edge_is_frozen().

◆ edge_is_offset()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::edge_is_offset ( const size_t  eid) const
inline

Whether edge eid is on the offset boundary / carries input geometry / bounds some other tag region.

◆ edge_is_offset_surface_live()

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.

◆ edge_is_on_surface()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::edge_is_on_surface ( const std::array< size_t, 2 > &  vids) const
overridevirtual

Is an edge part of the substructure.

Parameters
vidsThe vertex IDs of the edge

Reimplemented from wmtk::TriMesh.

◆ edge_is_region_boundary_live()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::edge_is_region_boundary_live ( const Tuple t) const
inline

Live region-boundary test for an edge that currently exists in the mesh.

Unlike edge_is_region_boundary(), which reads m_edge_attribute[].m_surface_class – a snapshot label_offset_boundary() only refreshes once per optimization ITERATION – this recomputes the same rule label_offset_boundary() applies, straight from the current face tags, every time it is called.

This is what surface_envelope_for_edge() and region_boundary_is_outside_envelope() must use, not the cached class: split, collapse and swap all run between one relabelling and the next, and collapse in particular re-points a vertex's neighbours onto brand-new edge slots whose cached m_surface_class was never set (it defaults to 0, i.e. INPUT/untracked). Querying the cache on such a segment silently reports "not region", the containment check short-circuits to "not outside" without ever comparing a position, and a genuinely bulging collapse is accepted. That is why sanity-check violations on this mesh appeared immediately after the first collapse pass and nowhere before it.

◆ edge_normal_deviation()

double wmtk::components::topological_offset::TopoOffsetTriMesh::edge_normal_deviation ( const Tuple e) const

Paper Definition 5 for a 1-simplex: max angle between the offset normal at the edge's center and the offset normal at p_i = 0.1*p_c + 0.9*p_v, in degrees.

Note this is NOT what the 3D branch's face_normal_deviation() computes – that compares the element's own geometric normal against the field. Both terms here are field normals, which is what makes the quantity vanish under refinement and therefore something the sizing field can actually drive to zero.

◆ face_in_region()

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 are set at construction – the complex by label_input_complex(), the band by conservative growth – from geometry, not from tags, and every operation now carries the label onto the faces it creates, so this is exact.

It used to read the TAGS, which cannot express the distinction the label exists for: the band is named by the output tag, and nothing stops that tag already appearing somewhere else in the input mesh. Every such face would then read as offset band, its boundary would classify as OFFSET rather than REGION, and project_offset_vertex() would drag an unrelated region toward target_distance.

◆ face_is_input_complex()

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.

◆ face_is_offset_band()

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.

◆ init_from_image()

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 
)

initialize TriMesh from vertex, face, tag data

Parameters
V#V by 2 vertex matrix
F#F by 3 face matrix
F_tags#F by physical groups tag matrix
V_envV_env by 2 EnvelopeSurface vertex matrix
F_envF_env by 2 EnvelopeSurface edge matrix

◆ init_offset_sizing_field()

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.

◆ init_region_boundary_envelope()

void wmtk::components::topological_offset::TopoOffsetTriMesh::init_region_boundary_envelope ( )

Build the containment envelope over every tag-region boundary, once, before the optimization starts.

A region boundary is an edge whose two incident faces carry different tag sets – the offset band against the background mesh, the input complex against everything else, and any other tagged region in the file. Exactly the rule SimWildMeshTri uses to build its own 2D envelope, and the same SampleEnvelope 2D edge overload does the work, so nothing about the envelope itself is reimplemented here.

◆ init_region_boundary_envelope_from_input()

void wmtk::components::topological_offset::TopoOffsetTriMesh::init_region_boundary_envelope_from_input ( )

Build the same envelope from the INPUT mesh, before the offset exists.

The band's tags REPLACE the ones already on a face rather than joining them (m_face_attribute[f].tags = new_tag, everything but protected_tags dropped). So a region the band grows through loses its own tag there, and its boundary curve is cut off at whatever contour conservative growth happened to stop on. Built afterwards, the envelope is a tube around that truncated curve, and its end cap pins the triple junction where the region boundary meets the offset – a vertex that should be free to slide along its region's curve until it reaches target_distance, held instead at a position that is an artefact of relative_ball_threshold.

Built here, the tube follows the region's ORIGINAL, untruncated curve. That relaxes the constraint in exactly one place – along the stretch the band later swallows – and nowhere else, because a wider segment set can only ever make containment easier. No vertex moves during offset construction (marching splits place the new vertex on the edge being split), so the shared stretch of curve is geometrically identical either way.

Requires only label_input_complex() to have run, for face_is_input_complex(). Inert until optimize_offset(): nothing between here and there consults m_envelope, because offset construction runs TriMesh::split_edge directly rather than the shared operations.

◆ invariants()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::invariants ( const std::vector< Tuple > &  )
overridevirtual

User specified invariants that can't be violated.

Parameters
std::vector<Tuple>a vector of Tuples that are concerned in a given operation
Returns
true if the invairnats are not violated

Reimplemented from wmtk::TriMesh.

◆ label_offset_boundary()

void wmtk::components::topological_offset::TopoOffsetTriMesh::label_offset_boundary ( )

Tag the two tracked surfaces for the optimization phase.

The offset boundary in 2D has no stored definition – it is exactly the set of edges across which the incident FACE LABELS differ, so it falls out of the labelling and is recomputed here once. An edge with only one incident face is on the domain boundary and is tagged bbox instead, which is what stops the box from collapsing.

◆ marching_tris()

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, at the midpoint (m_edge_split_mode=Midpoint) or at the hacky initialization offset (m_edge_split_mode=Initial).

There is no longer a distance-field mode here. Splitting the marched edge at the root of d(l) - target_distance is not what the paper does and it is not what places the offset: conservative growth decides where the boundary goes, and the optimization phase moves it from there. See the note on the removal in .claude/CLAUDE.md.

◆ max_offset_normal_deviation_at_vertex()

double wmtk::components::topological_offset::TopoOffsetTriMesh::max_offset_normal_deviation_at_vertex ( const size_t  vid) const

Worst normal deviation over the offset-surface edges incident to vid, in degrees. The 2D counterpart of 3D's max_offset_surface_normal_deviation_at_vertex().

◆ minimize_distance_along_tangent()

double wmtk::components::topological_offset::TopoOffsetTriMesh::minimize_distance_along_tangent ( const size_t  vid,
const Vector2d &  p0,
const Vector2d &  tang,
const double  cap,
const std::function< bool()> &  rejected 
)

Put a vertex at the distance-error minimum along its region tangent.

A vertex on a region boundary is effectively constrained to that curve – the envelope admits motion along it and almost none across it – so the right thing to solve is the 1D problem ON the curve: minimise |dist(p, input complex) - target_distance| over p = p0 + s*tang. What this replaces is a single step of length (target - p0).tang, i.e. the desired move PROJECTED onto the tangent, which fixes a direction and then takes an arbitrary length in it.

The measurement that motivated this, over the 122 slide events of the dragon rectangle at target_distance_rel 5e-3: the projected step was never the best point on its own line. 108 of 122 undershot and 14 overshot, and the best feasible point on the line had a median of 52x lower error (worst case 36295x). The envelope was not what stopped them – zero of the 122 were blocked before the projected step, and 113 were still feasible at 20x it. The vertices were under-stepped, not locked, which is also why splitting their incident region edges would not have helped: a split adds samples to the curve, it does not enlarge the set of positions the envelope admits.

Bracket first, then minimise. rejected is the caller's feasibility predicate (envelope plus inversion, evaluated at whatever position the vertex currently holds); the feasible extent is found by geometric expansion out to cap and bisection of the first refusal, separately in each direction, then golden-section search runs on the bracket with infeasible probes scored as infinite. cap bounds how far along the curve a single pass may travel.

Leaves the vertex at the minimiser and returns its signed offset s along tang.

◆ offset_field_normal()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::offset_field_normal ( const Vector2d &  p,
Vector2d &  n 
) const

The offset normal at an arbitrary point: the unit vector from the nearest point on the input complex out to p. False when p sits on the complex, where it is undefined.

◆ project_offset_vertex()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::project_offset_vertex ( const Tuple t)

Move an offset-boundary vertex back onto the target distance from the input complex, blended with the Laplacian of its offset-boundary neighbours.

◆ refine_sizing_around_worst()

size_t wmtk::components::topological_offset::TopoOffsetTriMesh::refine_sizing_around_worst ( double  )
inlineoverridevirtual

Sizing refinement over the offset polyline.

The 3D field is driven by the mean ratio of the offset TRIANGULATION; a polyline has no such metric, so this uses the quantity that actually matters in 2D – how far each offset-boundary vertex sits from the target distance. A vertex whose error exceeds sizing_mrm_threshold of the target gets a shorter target edge length, so the next split pass resolves that stretch more finely.

Implements wmtk::TriOptimizerMesh.

◆ region_boundary_is_outside_envelope()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::region_boundary_is_outside_envelope ( const size_t  vid) const

Whether any tracked-surface segment at this vertex has left the envelope. The shared operations run their own containment checks through surface_envelope_for_edge(); this is for project_offset_vertex(), which places a vertex itself and never reaches them.

◆ region_boundary_tangent()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::region_boundary_tangent ( const Tuple t,
Vector2d &  tang 
) const

The direction the REGION curve runs through this vertex, if it has one.

Only region-class edges count – the offset boundary is exempt from the envelope, so its direction is not a wall to slide along. Two incident region edges give the chord between the two neighbours (the discrete tangent of a curve passing through); one gives that edge's own direction (a curve terminating here, e.g. where a region outline meets the offset). Zero or three or more is not a curve – returns false, and the caller keeps whatever the scaled search gave it.

Parameters
[out]tangunit tangent, written only on success. Sign is arbitrary; callers project onto it, which is sign-independent.

◆ set_vertex_position()

void wmtk::components::topological_offset::TopoOffsetTriMesh::set_vertex_position ( const size_t  vid,
const Vector2d &  p 
)
inline

Place a vertex, keeping its exact and rounded coordinates in step.

As in 3D: the offset works in doubles (its construction is driven by a BVH distance field), so every vertex it places is rounded, but m_pos must still be filled because the shared split's exact-midpoint fallback reads it.

◆ smooth_after()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::smooth_after ( const Tuple t)
overridevirtual

User specified modifications and desideras after an edge smooth.

Parameters
theedge Tuple to be smoothed
Returns
true if the modifications succeed

Reimplemented from wmtk::TriMesh.

◆ smooth_before()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::smooth_before ( const Tuple t)
overridevirtual

User specified preparations and desideratas for an edge smooth.

Parameters
theedge Tuple to be smoothed
Returns
true if the preparation succeed

Reimplemented from wmtk::TriMesh.

◆ smoothing_position_is_allowed()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::smoothing_position_is_allowed ( size_t  vid,
const Vector2d &   
) const
inlineoverridevirtual

An offset-boundary vertex is placed by project_offset_vertex(); the base's smoother is only allowed to move genuinely interior ones, and the input complex never moves.

Implements wmtk::TriOptimizerMesh.

◆ split_adjust_position()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::split_adjust_position ( size_t  v_new,
const std::vector< Tuple > &  children 
)
overridevirtual

Carry each parent's region label onto the two children it became.

This is bookkeeping, not positioning, and split_after_vertex() would be the natural home for it. It lives here because of WHEN the base calls the two. Everything downstream of the label reads it during the same split: the containment check inside the base's split_edge_after() runs surface_segment_is_outside() on both new segments, which reaches surface_envelope_for_edge() -> edge_is_region_boundary_live() -> face_is_offset_band(), and that is a label test now. 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. Measured on the dragon rectangle, that decayed the offset polyline 429 -> 86 edges and took max_dist_err from 0.000215 to 0.000302.

Position is left entirely to the base; this always returns its result unchanged.

Reimplemented from wmtk::TriOptimizerMesh.

◆ split_after_vertex()

void wmtk::components::topological_offset::TopoOffsetTriMesh::split_after_vertex ( size_t  v_new)
overridevirtual

Reimplemented from wmtk::TriOptimizerMesh.

◆ split_edge_after()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::split_edge_after ( const Tuple t)
overridevirtual

User specified modifications and desideratas after an edge split.

Parameters
theedge Tuple to be split
Returns
true if the modifications succeed

Reimplemented from wmtk::TriMesh.

◆ split_edge_before()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::split_edge_before ( const Tuple t)
overridevirtual

User specified preparations and desideratas for an edge split.

Parameters
theedge Tuple to be split
Returns
true if the preparation succeed

Reimplemented from wmtk::TriMesh.

◆ split_face_after()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::split_face_after ( const Tuple t)
overridevirtual

User specified modifications and desideratas after a face split.

Parameters
theface Tuple to be split
Returns
true if the modifications succeed

Reimplemented from wmtk::TriMesh.

◆ split_face_before()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::split_face_before ( const Tuple t)
overridevirtual

User specified preparations and desideratas for a face split.

Parameters
theface Tuple to be split
Returns
true if the preparation succeed

Reimplemented from wmtk::TriMesh.

◆ surface_envelope_for_edge()

std::shared_ptr< SampleEnvelope > wmtk::components::topological_offset::TopoOffsetTriMesh::surface_envelope_for_edge ( const std::array< size_t, 2 > &  vids) const
inlineoverridevirtual

Only REGION_SURFACE_CLASS segments carry a containment requirement.

The envelope's whole job is to hold the other tag regions where they are. The offset boundary must be exempt: it is the surface the optimization exists to move, and containing it inside a tube around its initial position caps how far it can ever travel toward target_distance. The input complex and the domain boundary are exempt for the opposite reason – they are frozen outright, so no operation ever offers them a new position.

Null means "no containment requirement", which the base handles by skipping the check.

Reimplemented from wmtk::TriOptimizerMesh.

◆ swap_edge_after()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::swap_edge_after ( const Tuple t)
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.

◆ swap_edge_before()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::swap_edge_before ( const Tuple t)
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 of vertices. Across a thin offset band that is exactly how the two sides of the band get stitched together, and the region stops being manifold. The base refuses tracked-surface edges but has no reason to check this.

Reimplemented from wmtk::TriMesh.

◆ vertex_is_frozen()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::vertex_is_frozen ( const size_t  vid) const
inline

The two simplex sets the optimization may not touch at all.

The input simplicial complex is the geometry the offset is measured against, and the domain boundary is the box the background mesh lives in. Neither is something to be improved: an operation that splits, collapses or moves any of their simplices changes the reference the whole result is defined against. This is categorical, not a tolerance – unlike the tag-region boundaries, which may move within m_envelope.

◆ vertex_is_on_surface()

bool wmtk::components::topological_offset::TopoOffsetTriMesh::vertex_is_on_surface ( const size_t  vid) const
overridevirtual

The substructure the link condition is evaluated against, DERIVED not cached.

substructure_link_condition() asks these, and it is only as good as the answers. The cached edge tags are refreshed once per iteration, which is too coarse: the split pass runs first and creates edges the tagging never classified, so the collapse pass that follows evaluates the condition against a substructure that no longer describes the mesh – which is exactly why split and collapse tear the region together while each is safe alone.

Computing them from the face labels on demand costs a face lookup and cannot go stale.

Reimplemented from wmtk::TriMesh.

◆ warn_if_offset_reaches_domain_boundary()

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, conservative growth runs out of room and the band's outer surface becomes the box itself. Two things then go wrong at once, and neither is visible in the report:

  • Those vertices are ON the bbox, which is categorically frozen, so no split, collapse, swap or smooth may move them. The target distance is unreachable there by construction.
  • compute_distance_deviation() cannot even see them. It walks edges and skips any with no opposite face, which is exactly what a band edge lying on the domain boundary is, so the clipped stretch enters neither max_dist_err nor avg_dist_err.

Measured on the dragon rectangle at target_distance 0.1 (domain 1.07 x 0.98): 101 band vertices pinned on the box, true error there up to 0.0516 – 52% of target_distance – while the report showed max_dist_err 0.00477, under-reporting by 11x. The run looks like a near-miss and is actually a structural failure, which is why this is a warning and not a debug line.

◆ write_smoothing_debug_output()

void wmtk::components::topological_offset::TopoOffsetTriMesh::write_smoothing_debug_output ( const std::string &  path) const
inlineoverridevirtual

Member Data Documentation

◆ INPUT_SURFACE_CLASS

constexpr int wmtk::components::topological_offset::TopoOffsetTriMesh::INPUT_SURFACE_CLASS = 0
staticconstexpr

SurfaceTagAttributes::m_surface_class: which of the three tracked surfaces an edge belongs to.

These have to be distinct classes, not one lumped "not the input complex". OFFSET is the surface the optimization is trying to place at target_distance; REGION is every other tag boundary in the file – another body's outline, an overlap seam – which is tracked for a completely different reason (the shared swap copies one incident face's tags onto both faces it creates, so an unguarded flip across a tag boundary silently moves that region), and which has no business being pushed toward the offset distance. Filing both under OFFSET is what sent project_offset_vertex() at vertices sitting ~40x target_distance from the input complex, and what left update_sizing_field() refining around them forever.

◆ iter_cnt_collapse_nd_reject

std::atomic<int> wmtk::components::topological_offset::TopoOffsetTriMesh::iter_cnt_collapse_nd_reject = 0

Collapses and swaps refused by the normal-deviation guards this iteration – reported, not stored, because they say whether the guards are what is holding the offset's resolution up.

◆ m_collapse_nd_before

wmtk::threading::enumerable_thread_specific<double> wmtk::components::topological_offset::TopoOffsetTriMesh::m_collapse_nd_before
mutable

The offset surface's worst normal deviation around the collapsing edge, captured in collapse_edge_before() for collapse_edge_after() to compare against. Thread-local: the collapse pass runs in parallel, exactly as the 3D m_collapse_nd_before it mirrors.

◆ m_swap_nd_before

wmtk::threading::enumerable_thread_specific<double> wmtk::components::topological_offset::TopoOffsetTriMesh::m_swap_nd_before
mutable

The same for swap, plus the four vertices of the two incident triangles: after the flip the tuple names the NEW edge, so the two apexes would otherwise be unreachable.

◆ m_worst_dist_vid

size_t wmtk::components::topological_offset::TopoOffsetTriMesh::m_worst_dist_vid = static_cast<size_t>(-1)
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.

◆ op_counts

std::vector<std::array<int, 3> > wmtk::components::topological_offset::TopoOffsetTriMesh::op_counts

Accepted operations per optimization iteration: {splits, collapses, swaps}, one entry per iteration, in step with optimization_metrics. The counters are reset at the top of each iteration and read after that iteration's operation passes, so each entry is that iteration's own count, not a running total. Mirrors the 3D counters of the same names.

◆ optimization_metrics

std::vector<std::array<double, 4> > wmtk::components::topological_offset::TopoOffsetTriMesh::optimization_metrics

One {max_dist_err, avg_dist_err, max_norm_dev, avg_norm_dev} entry per optimization iteration actually run – shorter than optimization_iterations when the loop converged and stopped early.


The documentation for this class was generated from the following files: