|
Wildmeshing Toolkit
|
The offset's 2D mesh, on the shared 2D optimizer. More...
#include <TopoOffsetTriMesh.h>
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< SampleEnvelope > | surface_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< 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 () |
| 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 |
| 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< 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. | |
| 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 |
| 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, 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< OptSplitCache2d > | m_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. | |
| 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 |
| 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 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< EdgeSplitCache > | edge_split_cache |
| wmtk::threading::enumerable_thread_specific< FaceSplitCache > | face_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< 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.
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.
|
overridevirtual |
Reimplemented from wmtk::TriOptimizerMesh.
|
overridevirtual |
Reimplemented from wmtk::TriOptimizerMesh.
|
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.
|
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.
| 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().
| 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.
|
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().
|
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 |
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.
| 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.
| 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.
| 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.
| 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
| 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_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_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.
| 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.
|
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 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.
| 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.
| 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().
| 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.
| 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.
| 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.
|
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.
| 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.
| 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.
| [out] | tang | unit tangent, written only on success. Sign is arbitrary; callers project onto it, which is sign-independent. |
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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 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.
|
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.
|
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.
| 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:
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.
|
inlineoverridevirtual |
Implements wmtk::TriOptimizerMesh.
|
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.
| 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.
|
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.
|
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.
|
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 |
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.
| 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.