|
Wildmeshing Toolkit
|
The offset's tet mesh, on the shared 3D optimizer. More...
#include <TopoOffsetTetMesh.h>
Classes | |
| struct | DistanceSplit |
| The offset surface's residual. Every band vertex and every face sample counts toward the max and the average, pinned vertices included. The reachable/pinned split (see band_vertex_is_reachable()) is attribution: a max driven by a pinned vertex calls for a different construction, not more optimization. More... | |
| struct | EdgeSplitCache |
| struct | FaceSamples |
| struct | FaceSnapshot |
| A face's shared surface tags together with the offset's own label. More... | |
| struct | FaceSplitCache |
| struct | GradientSplit |
| The band's placement gradient: |grad (Phi(x) - c)^2| over the offset surface – at every band vertex AND at interior samples of every band face. More... | |
| struct | MoveStats |
| How far each class of vertex actually gets during smoothing. More... | |
| struct | OffsetCollapseRefusals |
| struct | OptSplitCache |
| struct | SmoothTrace |
| Why smoothing refused a vertex, one counter per site where it can say no. More... | |
| struct | TetSplitCache |
| struct | WallMoveStats |
| Which of two mechanisms lets a wall vertex end up off its wall. More... | |
Public Types | |
| enum class | OptPhase { A , B } |
| Which half of the alternating optimization is running. More... | |
| enum class | PhaseBSub { Offset , Background } |
| Phase B's two sub-sweeps, run in this order within every smoothing pass. More... | |
| enum class | EdgeSplitMode { Midpoint = 0 , Optimization = 5 } |
| enum class | VClass { Bbox = 0 , Offset = 1 , Input = 2 , Interior = 3 , Count = 4 } |
| Which of four DISJOINT classes a vertex is accounted to when measuring movement. More... | |
| using | VertexExtraCol = wmtk::AttributeCollection< VertexExtra > |
| using | EdgeAttCol = wmtk::AttributeCollection< EdgeAttributes > |
| using | FaceExtraCol = wmtk::AttributeCollection< FaceExtra > |
| using | TetAttCol = wmtk::AttributeCollection< TetAttributes > |
Public Types inherited from wmtk::TetOptimizerMesh | |
| using | FaceAttributes = wmtk::SurfaceTagAttributes |
| using | VertAttCol = AttributeCollection< VertexAttributes > |
| using | FaceAttCol = AttributeCollection< FaceAttributes > |
| using | SurfaceTopoSignature = wmtk::utils::SurfaceTopoSignature |
Public Types inherited from wmtk::TetMesh | |
| template<typename T > | |
| using | vector = std::vector< T > |
| using | VertexMutex = wmtk::threading::VertexMutex |
Public Member Functions | |
| void | rebuild_offset_envelope () |
| void | check_no_vertex_on_both_surfaces (const char *when) const |
| void | optimize_offset_alternating () |
| size_t | phase_b_smooth () |
| bool | smooth_interior_vertex_phase_b (const Tuple &t) |
| Phase B's placement of a background vertex: Newton on the one-ring AMIPS energy, to this vertex's own minimum. | |
| double | phase_b_band_gradient_linf () |
| L-inf over offset vertices of the gradient of each vertex's own placement objective – the energy Phase B's Newton solves minimize. Exactly 0 at the Gauss-Seidel fixed point whatever the residual, so it separates "placement finished" from "placement blocked": a refused move contributes zero displacement but full gradient. Vertices with a float-inverted or unrounded incident tet are skipped, as the smoother skips them. | |
| size_t | update_band_sizing_from_tolerance () |
| Per-vertex band sizing update, run after Phase B. Returns the number changed. | |
| FaceSnapshot | face_snapshot (const size_t fid) const |
| void | restore_face (const size_t fid, const FaceSnapshot &s) |
| void | set_vertex_position (const size_t vid, const Vector3d &p) |
| Place a vertex, keeping its exact and rounded coordinates in step. | |
| bool | cell_in_region (const size_t tid) const |
Whether tet tid belongs to the closed offset region, read from its TAGS. | |
| bool | face_is_offset (const size_t fid) const |
Whether face fid is on the offset boundary (as opposed to the input complex). | |
| bool | face_is_region (const size_t fid) const |
Whether face fid carries input-complex geometry. | |
| TopoOffsetTetMesh (Parameters &_m_offset_params, int _num_threads=0) | |
| double | cell_quality (const size_t tid) const override |
The quality of cell tid, and how to write it. | |
| void | set_cell_quality (const size_t tid, const double q) override |
| void | log_quality_spike (size_t tid, double before, double after, const char *kind) const |
| Report one tet crossing into absurd quality: its geometry, and why it is degenerate. | |
| bool | vertex_is_on_region (const size_t vid) const |
| Is this vertex on a region boundary – a tag boundary, or the domain wall. | |
| uint64_t | tag_bits (const CellTag &tags) const |
| uint64_t | vertex_boundary_mask (const size_t vid) const |
| The tag boundaries this vertex lies on – the raw mask, gated on the vertex still being region geometry at all. | |
| uint64_t | face_mask (const std::array< size_t, 3 > &vids) const |
| A face lies on a boundary only if all of it does: the AND of its corners' masks. | |
| std::shared_ptr< SampleEnvelope > | envelope_for_mask (uint64_t mask) const |
| The envelope a simplex with this boundary mask is contained in, or null. | |
| std::shared_ptr< SampleEnvelope > | surface_envelope_for_face (const std::array< size_t, 3 > &vids) const override |
Envelope the tracked-surface triangle vids must stay inside. | |
| bool | allow_surface_swap () const override |
| bool | check_surface_topology () const override |
| std::shared_ptr< SampleEnvelope > | smoothing_energy_envelope (const size_t vid) const override |
| The offset surface is the one tracked surface with NO envelope, in either role. | |
| std::shared_ptr< SampleEnvelope > | smoothing_containment_envelope (const size_t vid) const override |
| ... and it is not CONTAINED by one either. | |
| void | write_optimization_debug_output (const std::string &path) override |
| size_t | refine_sizing_around_worst (double) override |
| Phase A's stall-driven sizing refinement – TetWild's, on element quality. | |
| std::tuple< double, double > | optimization_quality_stats () override |
| (max, avg) Phi residual over the reachable offset surface, in units of its tolerance; the engine's stop metric is therefore 1.0. | |
| double | optimization_stop_metric () const override |
| double | offset_residual_tolerance () const |
| double | offset_gradient_tolerance () const |
| The convergence tolerance: the bound on |grad (Phi - c)^2| at a band vertex. | |
| void | check_offset_within_support (const char *when) const |
| Stop the run if any reachable offset-surface vertex has left the potential's support. | |
| double | band_vertex_residual (const size_t vid) const |
| bool | band_vertex_is_reachable (const size_t vid) const |
| std::vector< bool > | band_vertex_mask () const |
| double | max_band_vertex_distance () const |
| void | write_phi_grid (const std::string &path, int n) const |
| template<typename Visit > | |
| void | for_each_offset_face_sample (const Tuple &f, Visit &&visit) const |
The interior lattice a face is sampled on, handed to visit one point at a time. | |
| FaceSamples | offset_face_samples (const Tuple &f) const |
The Phi residual at offset_residual_samples interior points of offset face f. | |
| double | face_criterion_rel (const Tuple &f) const |
| double | cell_quality_rel (const size_t tid) const |
| double | amips_rel_at_face (const Tuple &f) const |
| ... and the worst of the (up to two) cells a face separates. | |
| DistanceSplit | residual_split () const |
| GradientSplit | gradient_split (bool include_face_samples=true) const |
| void | report_outside_support (const char *when, const DistanceSplit &s) const |
| bool | swap_before_interior (const std::vector< size_t > &tids) override |
| Which tag the tets a swap creates should carry. | |
| bool | swap_before_surface (const std::vector< size_t > &tids, size_t a, size_t b, size_t c, size_t d) override |
| bool | swap_after_cells (const std::vector< size_t > &tids, bool is_surface_flip) override |
| Propagate application data to the cells made by a successful topological swap. | |
| bool | collapse_edge_before (const Tuple &t) override |
| Collapse policy that is the offset's own. | |
| bool | collapse_before_vertex (size_t v1, size_t v2, double edge_length) override |
| bool | collapse_after_connectivity (size_t v1, size_t v2, const std::vector< std::array< size_t, 2 > > &boundary_edges) override |
| bool | collapse_is_order_2_edge (const std::array< size_t, 2 > &e) override |
| void | collapse_after_vertex (size_t v1, size_t v2) override |
| bool | split_before_cells (const Tuple &edge, const std::vector< Tuple > &parents) override |
| Split policy that is the offset's own. | |
| bool | split_after_cells (size_t v1, size_t v2, size_t v_new, const std::vector< Tuple > &children) override |
| Restore application cell data on the children made by a split. | |
| bool | split_adjust_position (size_t v_new, const std::vector< Tuple > &children) override |
| void | split_after_vertex (size_t v_new, bool is_edge_open_boundary) override |
| Application metadata not represented by the shared vertex attributes. | |
| bool | is_open_boundary_edge (const Tuple &e) override |
| void | init_from_image (const MatrixXd &V, const MatrixXi &T, const MatrixSi &T_tags, const MatrixXd &V_env, const MatrixXi F_env, const std::vector< std::string > &tag_names) |
| initialize TetMesh from vertex, tet, and tag data | |
| void | init_surfaces_and_boundaries () |
| bool | is_edge_on_region (const Tuple &loc) |
| bool | is_edge_on_offset (const Tuple &loc) |
| bool | ambient_assert () |
| check that the ambient tag does not overlap with any other tags | |
| void | label_input_complex () |
| label input simplicial complex simplices, as defined in m_offset_params.offset_selection | |
| void | mark_input_complex_vertices () |
| bool | empty_input_complex () |
| check if the input complex is empty. Only valid after calling init_from_image(...). Checks if any vertices (therefore any simplices) are labelled 1, if not returns true | |
| void | init_input_complex_bvh () |
| Build the input complex's BVH and keep the extraction the potential needs. | |
| void | init_offset_potential () |
| Build the smooth offset potential from the extraction init_input_complex_bvh() kept. | |
| size_t | flood_fill () |
| label connected simplicial complex components (simplices labelled 1 or 2) | |
| std::vector< std::array< size_t, 3 > > | get_faces_by_condition (std::function< bool(const FaceAttributes &)> cond) const |
| bool | split_edge_before (const Tuple &t) override |
| bool | split_edge_after (const Tuple &t) override |
| This function computes the attributes for the added simplices. User specified modifications and desideratas for after an edge split. | |
| bool | marching_split_edge_before (const Tuple &t) |
| bool | marching_split_edge_after (const Tuple &t) |
| bool | split_face_before (const Tuple &t) override |
| User specified preparations and desideratas for a face split before changing the connectivity. | |
| bool | split_face_after (const Tuple &t) override |
| Compute the attributes for the added simplices. | |
| bool | split_tet_before (const Tuple &t) override |
| User specified preparations and desideratas for a tet split before changing the connectivity. | |
| bool | split_tet_after (const Tuple &t) override |
| Compute the attributes for the added simplices. | |
| bool | invariants (const std::vector< Tuple > &tets) override |
| bool | smooth_before (const Tuple &t) override |
| bool | smooth_after (const Tuple &t) override |
| Every vertex goes through the shared smoother. Nothing is dispatched anywhere else. | |
| bool | smooth_offset_vertex_backtracking (const Tuple &t) |
| Phase B's placement of an offset-surface vertex: the offset energy alone, then backtrack into the one-ring if the solved point inverts something. | |
| void | execute_offset (const std::filesystem::path &output_file) |
| main function from which all others are called | |
| void | optimize_offset (const std::filesystem::path &output_file) |
| optimize the offset | |
| bool | is_offset_face (const Tuple &f) const |
| true if face f has exactly one incident tet labelled 2 (offset), i.e. it lies on the boundary between the offset region and the rest of the mesh | |
| bool | is_offset_face (const size_t fid) const |
| std::vector< Tuple > | get_offset_surface_faces_for_vertex (const Tuple &t) const |
| offset-surface faces (see is_offset_face()) incident to vertex t | |
| void | log_smooth_trace () const |
| VClass | vertex_class (const size_t vid) const |
| double | wall_offplane_deviation (const size_t vid) const |
| void | log_vertex_movement (const char *when) const |
| Per-class smoothing movement, plus the direct bounding-box invariant check. | |
| void | init_offset_sizing_field () |
| Seed the sizing field from the offset surface's current edge lengths, once, before the first pass. Paper Sec. 5.3.3 Step 1: "initialized with the current length of each edge.". | |
| std::pair< double, double > | compute_distance_deviation () const |
| How far the offset surface is from where it should be: {max, avg} over its vertices. | |
| bool | face_is_offset_surface_live (const Tuple &f) const |
| The band's outer surface, recomputed live rather than read from the cached class. | |
| void | diag_offset_bands (const char *tag) const |
| bool | cell_is_offset_band (const size_t tid) const |
Whether tet tid is part of the offset BAND (as opposed to the input complex it wraps). | |
| bool | cell_is_input_complex (const size_t tid) const |
Whether tet tid is part of the INPUT complex the band wraps. | |
| void | warn_if_offset_reaches_domain_boundary () const |
| Warn if conservative growth ran into the bounding box. | |
| void | log_worst_dist_vertex () const |
| void | log_worst_tet (const char *when) const |
| Diagnostic: dump the single worst-quality tet and everything that could be pinning it, so a Phase A that will not converge says which element and why rather than only a max. | |
| void | marching_tets () |
| execute simplistic marching tets. All edges with one vertex labelled 0 and the other 1/2 are split, at the midpoint. | |
| bool | is_simplicially_embedded () const |
| check if the input complex (simplices labelled 1) are simplicially embedded w.r.t. the entire mesh | |
| bool | tet_is_simp_emb (const Tuple &t) const |
| check if a tet satisfies simpicial embedding criteria w.r.t. input complex (simplices labelled 1) | |
| void | simplicial_embedding () |
| make mesh a simplicial embedding of the input complex (simplices labelled 1) | |
| void | set_offset_tet_tags () |
| update 'tags' data for tets in the offset region (tets 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) |
| bool | is_order_2_edge (const Tuple &e) const |
| bool | is_order_2_edge (const std::array< size_t, 2 > &e) const |
| bool | vertex_is_on_surface (const size_t vid) const override |
| Is a vertex part of the substructure. | |
| bool | face_is_on_surface (const size_t fid) const override |
| Is a face part of the substructure. | |
| size_t | get_order_of_vertex (const size_t vid) const override |
| Get the order of a vertex. | |
| void | init_vertex_order () |
| Compute the vertex order for every vertex. | |
| void | compute_vertex_partition () |
| assign each vertex a partition id (by spatial Morton order) for the parallel ExecutePass policies (kPartition/kColor) used by smooth_all_vertices()/ collapse_all_edges(). A no-op if NUM_THREADS == 0. | |
| size_t | get_partition_id (const Tuple &loc) const |
| std::vector< Tuple > | get_face_adjacent_tets (const Tuple &t) const |
| get tets (as Tuples) that are face-adjacent to the given tet (as Tuple) | |
| std::vector< size_t > | connected_components_helper (const size_t &v_id) |
| get all one-ring vertices through input simplices (labelled 1) | |
| void | reset_connected_components () |
| reset connected component assignments. | |
Public Member Functions inherited from wmtk::TetOptimizerMesh | |
| bool | is_force_split_edge (const size_t v1, const size_t v2) const |
| TetOptimizerMesh (OptimizerParameters ¶ms, std::shared_ptr< SampleEnvelope > env) | |
| virtual double | quality_rel (const size_t tid) const |
| A cell's quality relative to the quality it is required to reach; <= 1 means it meets it. | |
| void | compute_vertex_partition () |
| void | compute_vertex_partition_morton () |
| size_t | get_partition_id (const Tuple &loc) const |
| double | get_length2 (const Tuple &l) const |
| bool | is_inverted (const std::array< size_t, 4 > &vs) const |
| bool | is_inverted (const Tuple &loc) const |
| bool | is_inverted_f (const Tuple &loc) const |
| Inversion check using only the double positions. | |
| double | get_quality (const std::array< size_t, 4 > &vs) const |
| double | get_quality (const Tuple &loc) const |
| std::tuple< double, double > | get_max_avg_energy () |
| virtual void | update_attributes () |
| Update the attributes of the mesh after an iteration of operations. | |
| void | mesh_improvement (int max_its=80) |
| std::tuple< double, double > | local_operations (const std::array< int, 4 > &ops, bool collapse_limit_length=true) |
| bool | round (const Tuple &v) |
| Round a vertex position to floating point, if that inverts no incident tet. | |
| bool | is_edge_on_surface (const Tuple &loc) |
| bool | is_edge_on_bbox (const Tuple &loc) |
| int | edge_incident_surface_face_count (const Tuple &e) |
How many of the faces incident to edge e are on the tracked surface. | |
| bool | surface_triangle_is_outside (const size_t a, const size_t b, const size_t c) const |
| std::vector< std::array< size_t, 3 > > | get_faces_by_condition (std::function< bool(const FaceAttributes &)> cond) const |
| void | output_faces (std::string file, std::function< bool(const FaceAttributes &)> cond) |
| void | gradation_smooth_sizing (double grade, const std::vector< size_t > &seeds) |
| Grade the refined sizing region into its surroundings (monotone, only lowers). | |
| double | active_quality_threshold () const |
| Cell-quality threshold above which a tet is "active" (worth operating on) for the skip-good-regions filter. | |
| virtual std::vector< size_t > | active_vertices () const |
| void | split_all_edges () |
| void | collapse_all_edges (bool is_limit_length=true) |
| bool | collapse_edge_after (const Tuple &t) override |
| User specified modifications and desideratas for 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_tets) |
| One collapse under the coarsening rules, outside a coarsening pass. | |
| size_t | swap_all_edges_32 () |
| bool | swap_edge_before (const Tuple &t) override |
| User specified preparations and desideratas for an 3-2 edge swap before changing the conenctivity. | |
| bool | swap_edge_after (const Tuple &t) override |
| User specified modifications and desideratas for after a 3-2 edge swap. | |
| size_t | swap_all_edges_44 () |
| bool | swap_edge_44_before (const Tuple &t) override |
| User specified preparations and desideratas for an 4-4 edge swap before changing the connectivity. | |
| bool | swap_edge_44_accept_case (const std::array< size_t, 2 > &new_edge) override |
| Filter which of the 4-4 orientations may be chosen. | |
| bool | swap_edge_44_after (const Tuple &t) override |
| User specified modifications and desideratas for after a 4-4 edge swap. | |
| size_t | swap_all_edges_56 () |
| bool | swap_edge_56_before (const Tuple &t) override |
| User specified preparations and desideratas for a 5-6 edge swap before changing the connectivity. | |
| bool | swap_edge_56_accept_case (const std::array< size_t, 3 > &new_face) override |
| Filter which of the 5-6 orientations may be chosen. | |
| bool | swap_edge_56_after (const Tuple &t) override |
| User specified modifications and desideratas for after a 5-6 edge swap. | |
| size_t | swap_all_faces () |
| bool | swap_face_before (const Tuple &t) override |
| User specified preparations and desideratas for an 2-3 face swap befroe changing the geometry. | |
| bool | swap_face_after (const Tuple &t) override |
| User specified modifications and desideratas for after a 2-3 face swap. | |
| size_t | swap_all_edges_all () |
| bool | prepare_surface_flip (const Tuple &t, const std::vector< size_t > &incident_tets) |
| SurfaceTopoSignature | surface_topology_signature () const |
| void | warn_if_surface_topology_changed (const SurfaceTopoSignature &before, const char *where) const |
| double | swap_edge_44_energy (const std::vector< std::array< size_t, 4 > > &tets, const int op_case) override |
| User specified energy to decide which of the 4 possible orientations should be chosen. | |
| double | swap_edge_56_energy (const std::vector< std::array< size_t, 4 > > &tets, const int op_case) override |
| User specified energy to decide which of the 5 possible orientations should be chosen. | |
| virtual std::shared_ptr< polysolve::nonlinear::Problem > | smoothing_extra_energy (const size_t) const |
| An extra term the application adds to this vertex's smoothing objective, or null. | |
| void | smooth_all_vertices (const size_t n_iters=1) |
Public Member Functions inherited from wmtk::TetMesh | |
| size_t | vert_capacity () const |
| get the current largest global vid | |
| size_t | tet_capacity () const |
| get the current largest global tid | |
| size_t | vertex_size () const |
| get the number of unremoved verticies | |
| size_t | tet_size () const |
| get the number of unremoved tets | |
| void | init (size_t n_vertices, const std::vector< std::array< size_t, 4 > > &tets) |
| void | init_with_isolated_vertices (size_t n_vertices, const std::vector< std::array< size_t, 4 > > &tets) |
| void | init (const MatrixXi &T) |
| Generate the connectivity of the mesh from an IGL-style T matrix. | |
| bool | split_edge (const Tuple &t, std::vector< Tuple > &new_tets) |
| virtual bool | collapse_edge (const Tuple &t, std::vector< Tuple > &new_tets) |
| bool | link_condition (const Tuple &t) |
| bool | collapse_edge_conn (const Tuple &loc0, size_t &v1_id, Tuple &new_loc, std::map< size_t, wmtk::TetMesh::VertexConnectivity > &rollback_vert_conn, std::vector< size_t > &n1_t_ids_copy, std::vector< size_t > &new_tet_id, std::vector< TetrahedronConnectivity > &old_tets) |
| bool | collapse_edge_check_topology (const std::vector< size_t > &new_tet_id) |
| Check topology after collapse connectivity change. This is a sanity check and should not be necessary. | |
| void | collapse_edge_rollback (size_t &v1_id, std::map< size_t, wmtk::TetMesh::VertexConnectivity > &rollback_vert_conn, std::vector< size_t > &n1_t_ids, std::vector< size_t > &new_tet_id, std::vector< TetrahedronConnectivity > &old_tets) |
| bool | swap_edge_56 (const Tuple &t, std::vector< Tuple > &new_tets) |
| bool | swap_edge_44 (const Tuple &t, std::vector< Tuple > &new_tets) |
| bool | swap_edge (const Tuple &t, std::vector< Tuple > &new_tets) |
| 3-2 edge swap | |
| bool | swap_face (const Tuple &t, std::vector< Tuple > &new_tets) |
| 2-3 face swap | |
| bool | smooth_vertex (const Tuple &t) |
| bool | split_tet (const Tuple &t, std::vector< Tuple > &new_tets) |
| Split a tet in 4 tets. | |
| bool | split_face (const Tuple &t, std::vector< Tuple > &new_tets) |
| Split a face in 3 faces. | |
| void | triangle_insertion (const std::vector< Tuple > &intersected_tets, const std::vector< Tuple > &intersected_edges, std::vector< size_t > &new_edge_vids, std::vector< size_t > &new_center_vids, std::vector< std::array< size_t, 4 > > ¢er_split_tets) |
| Insert a triangle into a tetmesh, with known intersection information. | |
| bool | insert_point (const Tuple &t, std::vector< Tuple > &new_tets) |
| Insert a point into a tetmesh inside a tet. In general position, this split a tet into 4. In face position, split two tets. In edge position, In point position, do nothing. | |
| virtual bool | insert_point_before (const Tuple &t) |
| virtual bool | insert_point_after (std::vector< Tuple > &new_tets) |
| void | consolidate_mesh () |
| cleans up the deleted vertices or tetrahedra, fixes the corresponding indices, and reset the version number. WARNING: it invalidates all tuples! | |
| std::vector< Tuple > | get_edges () const |
| std::vector< Tuple > | get_faces () const |
| std::vector< Tuple > | get_vertices () const |
| std::vector< Tuple > | get_tets () const |
| virtual void | for_each_face (const std::function< void(const TetMesh::Tuple &)> &) |
| looping through all the unique edges and perform the given function | |
| 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 (and are retried later after a consolidate). Tune per application (e.g. from JSON); values < 1 are clamped to 1. | |
| double | preallocation_factor () const |
| bool | slots_exhausted () const |
| void | clear_slots_exhausted () |
| size_t | request_tet_slots (size_t n) |
| size_t | request_vert_slots (size_t n) |
| void | ensure_free_tet_capacity (size_t extra) |
| void | ensure_free_vert_capacity (size_t extra) |
| Tuple | tuple_from_edge (size_t tid, int local_eid) const |
| get a Tuple from global tetra index and local edge index (from 0-5). | |
| Tuple | tuple_from_edge (const std::array< size_t, 2 > &vids) const |
| get a Tuple from global vids of the 2 end of an edge | |
| Tuple | tuple_from_face (size_t tid, int local_fid) const |
| get a Tuple from global tetra index and local face index (from 0-3). | |
| std::tuple< Tuple, size_t > | tuple_from_face (const std::array< size_t, 3 > &vids) const |
| get a Tuple and the global face index from global vertex index of the face. | |
| std::optional< std::tuple< Tuple, size_t > > | try_tuple_from_face (const std::array< size_t, 3 > &vids) const |
| tuple_from_face for callers where a missing face is an answer, not a bug. | |
| size_t | lowest_common_tet (size_t v0_id, size_t v1_id, size_t v2_id) const |
| Lowest tet id incident to all three vertices, or size_t(-1) if there is none. | |
| size_t | vertex_valence (const size_t vid) const |
| Number of tets incident to a vertex, in O(1). | |
| std::tuple< Tuple, size_t > | tuple_from_face (const simplex::Face &f) const |
| Tuple | tuple_from_vertex (size_t vid) const |
| get a Tuple from global vertex index | |
| Tuple | tuple_from_tet (size_t tid) const |
| get a Tuple from global tetra index | |
| Tuple | tuple_from_vids (size_t vid0, size_t vid1, size_t vid2, size_t vid3) const |
| Get a Tuple from global vertex IDs. | |
| simplex::Tet | simplex_from_tet (const Tuple &t) const |
| simplex::Tet | simplex_from_tet (const size_t tid) const |
| simplex::Face | simplex_from_face (const Tuple &t) const |
| simplex::Edge | simplex_from_edge (const Tuple &t) const |
| Tuple | switch_vertex (const Tuple &t) const |
| wrapper function from Tuple::switch_vertex | |
| Tuple | switch_edge (const Tuple &t) const |
| wrapper function from Tuple::switch_edge | |
| Tuple | switch_face (const Tuple &t) const |
| wrapper function from Tuple::switch_face | |
| std::optional< Tuple > | switch_tetrahedron (const Tuple &t) const |
| wrapper function from Tuple::switch_tetrahedron | |
| std::vector< Tuple > | get_one_ring_tets_for_vertex (const Tuple &t) const |
| Get the one ring tets for a vertex. | |
| const std::vector< size_t > & | get_one_ring_tids_for_vertex (const Tuple &t) const |
| Get the one ring tids for vertex. | |
| const std::vector< size_t > & | get_one_ring_tids_for_vertex (const size_t vid) const |
| std::vector< Tuple > | get_one_ring_vertices_for_vertex (const Tuple &t) const |
| Get the one ring vertices for a vertex. | |
| std::vector< size_t > | get_one_ring_vids_for_vertex (size_t vid, std::vector< size_t > &cache) |
| Get the one ring vids for vertex. | |
| std::vector< size_t > | get_one_ring_vids_for_vertex (size_t vid) const |
| Get the one ring vids for vertex. | |
| std::vector< size_t > | get_one_ring_vids_for_vertex_adj (size_t vid) const |
| Duplicate of the function TetMesh::get_one_ring_vids_for_vertex. | |
| std::vector< size_t > | get_one_ring_vids_for_vertex_adj (size_t vid, std::vector< size_t > &cache) |
| Duplicate of the function TetMesh::get_one_ring_vids_for_vertex. | |
| std::vector< Tuple > | get_incident_tets_for_edge (const Tuple &t) const |
| Get the incident tets for edge. | |
| std::vector< Tuple > | get_incident_tets_for_edge (const size_t vid0, const size_t vid1) const |
| std::vector< size_t > | get_incident_tids_for_edge (const Tuple &t) const |
| std::vector< size_t > | get_incident_tids_for_edge (const size_t vid0, const size_t vid1) const |
| std::vector< Tuple > | get_one_ring_tets_for_edge (const Tuple &t) const |
| Get the one ring tets for edge. | |
| std::vector< std::array< size_t, 3 > > | vertex_adjacent_boundary_faces (const Tuple &t) const |
| std::array< Tuple, 4 > | oriented_tet_vertices (const Tuple &t) const |
| std::array< size_t, 4 > | oriented_tet_vids (const Tuple &t) const |
| std::array< size_t, 4 > | oriented_tet_vids (const size_t tid) const |
| std::array< Tuple, 3 > | get_face_vertices (const Tuple &t) const |
| Get the 3 vertices of a face represented by Tuple. | |
| std::array< size_t, 3 > | get_face_vids (const Tuple &t) const |
| std::array< Tuple, 6 > | tet_edges (const Tuple &t) const |
| get the 6 edges of a tet represented by Tuples | |
| void | check_tuple_validity (const Tuple &t) const |
| bool | check_mesh_connectivity_validity () const |
| checks the validity of the connectivity of the mesh. Including the validity of each Tuple | |
| void | remove_tets_by_ids (const std::vector< size_t > &tids) |
| remove the tetrahedrons in the mesh that have given tet ids | |
| void | start_protect_attributes () |
| void | release_protect_attributes () |
| void | rollback_protected_attributes () |
| 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_n_ring (size_t v1, size_t v2, size_t v3, int threadid, int n) |
| try_set_vertex_mutex_n_ring seeded from the three vertices of a face. | |
| void | for_each_edge (const std::function< void(const TetMesh::Tuple &)> &) |
| perform the given function for each edge | |
| void | for_each_vertex (const std::function< void(const TetMesh::Tuple &)> &) |
| perform the given function for each vertex | |
| void | for_each_tetra (const std::function< void(const TetMesh::Tuple &)> &) |
| perform the given function for each tet | |
| simplex::SimplexCollection | get_surface_faces_for_vertex (const size_t vid) const |
| Get all faces on the surface that are incident to vid. | |
| simplex::SimplexCollection | get_surface_faces_for_edge (const std::array< size_t, 2 > &vids) const |
| Get all faces on the surface that are incident to the edge. | |
| size_t | get_num_surface_faces_for_edge (const std::array< size_t, 2 > &vids) const |
| Get the number of surface faces incident to the edge. | |
| size_t | compute_vertex_order (const size_t vid) const |
| Compute the vertex order for a single vertex. | |
| size_t | get_order_of_edge (const std::array< size_t, 2 > &vids) const |
| Compute the order of an edge. | |
| bool | substructure_link_condition (const Tuple &e_tuple) const |
| Link condition that also considers substructures. | |
| size_t | tet_storage_capacity () const |
| size_t | vert_storage_capacity () const |
| bool | try_set_vertex_mutex_two_ring (const Tuple &v, int threadid) |
| Lock v's one-ring and, partially, its two-ring. See the note above. | |
| bool | try_set_vertex_mutex_two_ring_vid (const Tuple &v, int threadid) |
| try_set_vertex_mutex_two_ring reached through vids rather than Tuples. | |
| bool | try_set_vertex_mutex_two_ring_vid (size_t v, int threadid) |
| try_set_vertex_mutex_two_ring reached through vids rather than Tuples. | |
| bool | try_set_edge_mutex_two_ring (const Tuple &e, int threadid=0) |
| Lock the edge's one-ring and, partially, its two-ring. See the note above. | |
| bool | try_set_face_mutex_two_ring (const Tuple &f, int threadid=0) |
| Lock the face's one-ring and, partially, its two-ring. See the note above. | |
| bool | try_set_face_mutex_two_ring (const Tuple &v1, const Tuple &v2, const Tuple &v3, int threadid=0) |
| Lock the face's one-ring and, partially, its two-ring. See the note above. | |
| bool | try_set_face_mutex_two_ring (size_t v1, size_t v2, size_t v3, int threadid=0) |
| Lock the face's one-ring and, partially, its two-ring. See the note above. | |
| bool | try_set_vertex_mutex_one_ring (const Tuple &v, int threadid=0) |
| Lock v and its one-ring. Complete, unlike the two-ring family. | |
| 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. | |
Static Public Member Functions | |
| static double | spike_amips (const double q) |
| static const char * | vclass_name (VClass c) |
| static double | mean_ratio_metric (const Vector3d &p0, const Vector3d &p1, const Vector3d &p2) |
| unsigned mean ratio metric of a triangle: 2*sqrt(3)*area / (sum of squared edge lengths). 1 for equilateral, -> 0 as the triangle degenerates. | |
Public Attributes | |
| OptPhase | m_phase = OptPhase::A |
| Which phase is running. Read by every hook that differs between them; see OptPhase. | |
| PhaseBSub | m_phase_b_sub = PhaseBSub::Offset |
| std::shared_ptr< SampleEnvelope > | m_offset_envelope |
| The tube the offset surface may not leave during Phase A. Null in Phase B. | |
| int | m_vtu_counter = 0 |
| std::array< size_t, 4 > | m_init_counts = {{0, 0, 0, 0}} |
| size_t | m_tags_count |
| SimplicialComplexBVH | m_input_complex_bvh |
| std::shared_ptr< OffsetPotential3D > | m_offset_potential |
| The smooth offset potential Phi of the input complex, as loaded. | |
| std::shared_ptr< SampleEnvelope > | m_input_complex_envelope |
| The exact-kind envelope of the input complex, built only for offset_field "euclidean". Null otherwise. | |
| std::map< int64_t, std::shared_ptr< SampleEnvelope > > | m_tag_envelopes |
| One containment envelope per input tag, ambient included. Both phases. | |
| std::map< int64_t, int > | m_tag_bit |
| std::map< uint64_t, std::shared_ptr< SampleEnvelope > > | m_isect_cache |
| std::mutex | m_isect_mutex |
| 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 |
| std::vector< Vector3d > | m_V_envelope |
| std::vector< Vector3i > | m_F_envelope |
| double | m_envelope_eps = -1 |
| Parameters & | m_offset_params |
| VertexExtraCol | m_vertex_extra |
| FaceExtraCol | m_face_extra |
| EdgeAttCol | m_edge_attribute |
| TetAttCol | m_tet_attribute |
| std::atomic< int > | cnt_split = 0 |
| std::atomic< int > | cnt_collapse = 0 |
| bool | m_spike_track = true |
| Diagnostic: whether set_cell_quality reports tets crossing into absurd quality. | |
| double | m_spike_threshold = 1e5 |
| AMIPS above which a tet counts as absurd rather than merely bad. | |
| double | m_spike_ordinary = 100. |
| AMIPS below which a tet counts as ordinary, so ordinary -> absurd is one operation's doing. | |
| double | m_spike_record = 0. |
| Largest AMIPS seen so far; a new maximum is logged, which bounds the output. | |
| std::atomic< int > | m_spike_dumps {0} |
| std::atomic< int > | m_spike_genesis_dumps {0} |
| std::atomic< int > | m_spike_count {0} |
| std::atomic< int > | m_spike_genesis {0} |
| int | m_spike_dump_budget = 24 |
| OffsetCollapseRefusals | m_offset_collapse_refusals |
| MatrixXd | m_phi_V |
| MatrixXi | m_phi_E |
| MatrixXi | m_phi_F |
| std::vector< int > | m_phi_P |
| SmoothTrace | m_smooth_trace |
| std::atomic< int > | m_phase_b_constrained {0} |
| wmtk::threading::enumerable_thread_specific< std::unique_ptr< polysolve::nonlinear::Solver > > | m_phase_b_solver |
| std::array< MoveStats, size_t(VClass::Count)> | m_move_stats |
| WallMoveStats | m_wall_moves |
| size_t | m_worst_dist_vid = static_cast<size_t>(-1) |
| std::vector< std::array< double, 8 > > | optimization_metrics |
| std::vector< std::array< int, 3 > > | churn_counts |
| std::vector< std::array< int, 3 > > | op_counts |
| bool | m_converged = false |
| Whether the optimization met both convergence criteria before the iteration cap. | |
| std::atomic< int > | iter_cnt_split_born {0} |
| std::atomic< int > | iter_cnt_recollapsed {0} |
| std::atomic< int > | iter_cnt_recollapsed_same_pass {0} |
| std::atomic< int > | iter_cnt_split {0} |
| std::atomic< int > | iter_cnt_collapse {0} |
| std::atomic< int > | iter_cnt_swap {0} |
| std::atomic< int > | iter_cnt_collapse_offset_removed {0} |
| std::atomic< int > | iter_cnt_collapse_offset_reject {0} |
| Operations refused because they would have left an offset face over tolerance. | |
| std::atomic< int > | iter_cnt_swap_offset_reject {0} |
| std::atomic< int > | iter_cnt_split_offset_before {0} |
| Splits of an OFFSET-surface edge: reached split_edge_after, accepted, refused there. | |
| std::atomic< int > | iter_cnt_split_offset_endpoints {0} |
| std::atomic< int > | iter_cnt_split_offset_base_reject {0} |
| std::atomic< int > | iter_cnt_split_offset_tried {0} |
| std::atomic< int > | iter_cnt_split_offset {0} |
| std::atomic< int > | iter_cnt_split_offset_reject {0} |
Public Attributes inherited from wmtk::TetOptimizerMesh | |
| VertAttCol | m_vertex_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_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 |
| Surface envelope: what a surface vertex is pulled toward and checked against. | |
| double | m_s_amips = 1. |
| double | m_s_envelope = -1. |
| double | time_env = 0.0 |
| igl::Timer | isout_timer |
| wmtk::threading::enumerable_thread_specific< std::unique_ptr< polysolve::nonlinear::Solver > > | m_solver |
| Per-thread Newton solver for smoothing; created on first use. | |
| optimization::SmoothRejectCounters | m_smooth_rejects |
| Why smoothing attempts were refused, reported once per pass. | |
| bool | m_collapse_limit_length = true |
| std::set< simplex::Edge > | m_force_split_edges |
| size_t | m_force_split_count = 0 |
| Force-splits taken in the current split pass. Diagnostic only. | |
| std::unique_ptr< std::atomic< int >[]> | m_high_valence_claim |
| Per-pass claims for the shared high-valence split gate. | |
| size_t | m_high_valence_claim_size = 0 |
| std::atomic< size_t > | m_high_valence_rejects = 0 |
| int | m_iterations_used = 0 |
| Shared TetWild/SimWild outer optimization schedule. | |
| int | m_debug_print_counter = 0 |
| CoarsenStats | m_coarsen_stats |
| std::atomic< int > | cnt_swap = 0 |
| std::atomic< int > | cnt_surface_swap = 0 |
| std::atomic< int > | cnt_surface_swap_32 = 0 |
| std::atomic< int > | cnt_surface_swap_44 = 0 |
| std::atomic< int > | cnt_surface_swap_56 = 0 |
Public Attributes inherited from wmtk::TetMesh | |
| AbstractAttributeContainer * | p_vertex_attrs = nullptr |
| AbstractAttributeContainer * | p_edge_attrs = nullptr |
| AbstractAttributeContainer * | p_face_attrs = nullptr |
| AbstractAttributeContainer * | p_tet_attrs = nullptr |
| bool | m_collapse_check_link_condition = true |
| bool | m_collapse_check_topology = false |
| bool | m_collapse_check_manifold = true |
| wmtk::threading::enumerable_thread_specific< std::vector< size_t > > | mutex_release_stack |
| wmtk::threading::enumerable_thread_specific< std::vector< size_t > > | get_one_ring_cache |
| int | NUM_THREADS = 0 |
Static Public Attributes | |
| static constexpr int | OFFSET_SURFACE_CLASS = 1 |
| SurfaceTagAttributes::m_surface_class for the offset boundary. | |
Static Public Attributes inherited from wmtk::TetOptimizerMesh | |
| static constexpr double | MAX_ENERGY = 1e50 |
| The sentinel get_quality returns for an element AMIPS cannot score. | |
Static Public Attributes inherited from wmtk::TetMesh | |
| static constexpr int | EDGES_PER_CELL = 6 |
| static constexpr int | FACES_PER_CELL = 4 |
Private Member Functions | |
| bool | swap_capture_tag (const std::vector< size_t > &tids) |
| bool | any_tag_present (const CellTag &tag1, const CellTag &tag2) |
| determine if any tag from tag1 is also present in tag2. | |
| void | sort_edges_by_length (std::vector< simplex::Edge > &edges) |
| sort edge simplices in place by decreasing edge length | |
Private Attributes | |
| wmtk::threading::enumerable_thread_specific< OptSplitCache > | m_opt_split_cache |
| wmtk::threading::enumerable_thread_specific< double > | m_collapse_offset_rel_before |
| wmtk::threading::enumerable_thread_specific< double > | m_swap_offset_rel_before |
| wmtk::threading::enumerable_thread_specific< CellTag > | m_swap_tag |
The tag swap_after_cells writes onto the tets the swap created, chosen in before. | |
| wmtk::threading::enumerable_thread_specific< int > | m_swap_label |
| wmtk::threading::enumerable_thread_specific< EdgeSplitCache > | edge_split_cache |
| wmtk::threading::enumerable_thread_specific< FaceSplitCache > | face_split_cache |
| wmtk::threading::enumerable_thread_specific< TetSplitCache > | tet_split_cache |
Additional Inherited Members | |
Protected Member Functions inherited from wmtk::TetOptimizerMesh | |
| 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 |
| virtual bool | optimization_bare_coarsen_passes () const |
| Whether the loop opens with an UNLIMITED-LENGTH collapse pass. | |
| virtual void | optimization_sanity_checks_extra () |
| virtual bool | optimization_stop_at_float () const |
| virtual bool | optimization_stalled (double prev, double cur) |
Whether an iteration that moved the metric from prev to cur is stalled, and the sizing refinement should therefore fire. | |
| virtual bool | collapse_quality_allowed (size_t v1, double quality, double ring_max) const |
Protected Member Functions inherited from wmtk::TetMesh | |
| virtual bool | triangle_insertion_before (const std::vector< Tuple > &faces) |
| virtual bool | triangle_insertion_after (const std::vector< std::vector< Tuple > > &) |
| void | resize_vertex_mutex (size_t v) |
Protected Attributes inherited from wmtk::TetOptimizerMesh | |
| uint32_t | m_op_epoch = 0 |
| wmtk::threading::enumerable_thread_specific< SwapInfoCache > | swap_cache |
| 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 tet mesh, on the shared 3D optimizer.
Construction – marching tets, simplicial embedding, growing the offset region – uses TetMesh's splits directly; the optimization after it is wmtk::TetOptimizerMesh's, with the offset supplying only policy through the hooks. Two surfaces are tracked: the input complex (class 0, envelope-held) and the offset boundary (OFFSET_SURFACE_CLASS, free to move, defined by tet labels rather than input geometry). The base's m_is_on_surface is their union, and that union is what an operation must not tear.
|
strong |
Which half of the alternating optimization is running.
The two criteria are optimized in turn, never jointly: driven from one loop they compete, split refining the band and collapse removing on quality grounds what it made.
Phase A is TetWild and nothing else – same operations, gates, sizing field and stall-driven refinement, with no offset energy term, acceptance criterion or stop metric. Its one addition is m_offset_envelope, holding the offset surface within one Phi tolerance of wherever Phase B last placed it, so "do not degrade the offset" is a geometric constraint every operation already honours rather than a per-operation criterion.
Phase B moves the offset surface and nothing else: smoothing against the offset energy to a fixed point, no envelope on the offset (it is what has to travel), no topological operations. Both phases write the shared sizing field – A on element quality, B on the Phi residual of faces smoothing could not place.
|
strong |
Phase B's two sub-sweeps, run in this order within every smoothing pass.
Offset: place every offset-surface vertex on the level set, by the 1-D minimization of (Phi - c)^2 along grad Phi, backtracked into the one-ring if the minimum lies outside it. Background: relax the interior by AMIPS, under the surface those placements just defined.
The sweeps must stay separate and in this order, so a pass means "place, then relieve what the placement cost". Interleaving would make each vertex's one-ring a moving target for its neighbours, so the backtracking a placement hits would depend on visit order rather than on geometry. smooth_before() reads this and refuses the other class outright, so the two sweeps share the executor, counters and accept gates and differ only in who they admit.
|
strong |
Which of four DISJOINT classes a vertex is accounted to when measuring movement.
Bbox first, deliberately: a vertex may be on the bounding box and on a tracked surface at once, and the bbox freeze in smooth_before() overrides everything else, so for "does it actually move" the bbox decides. Offset before input is academic – a vertex on both is a construction defect check_no_vertex_on_both_surfaces() throws on.
|
inlineoverridevirtual |
Surface edges may be flipped, as a topology-preserving diagonal flip. Both tracked surfaces need it: the offset boundary is re-triangulated constantly, and refusing its diagonals leaves badly-shaped triangles the sizing field then chases.
Implements wmtk::TetOptimizerMesh.
|
inlineprivate |
determine if any tag from tag1 is also present in tag2.
|
inline |
Whether vid is an offset-surface vertex the optimizer could still place on the level set. Only the domain boundary disqualifies one: conservative growth ran out of room there, so the vertex is on the box because the offset could not extend past it, and no placement changes that. m_is_on_region must not disqualify it – the flag is over-broad (splits propagate it, collapses OR it onto survivors), so a vertex carrying it may sit exactly where the offset wants it, and check_no_vertex_on_both_surfaces() already throws on the genuinely contradictory case.
| std::vector< bool > wmtk::components::topological_offset::TopoOffsetTetMesh::band_vertex_mask | ( | ) | const |
Which vertices lie on the offset surface. Shared by every measurement so they all agree on what "the band" is.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::band_vertex_residual | ( | const size_t | vid | ) | const |
How far vid is from the level set Phi = c, as a length. This is what the loop converges on and what the sizing field refines by.
|
inline |
Whether tet tid belongs to the closed offset region, read from its TAGS.
The 3D counterpart of TopoOffsetTriMesh::face_in_region. The region is the offset band plus the input complex it wraps, both named by tags, and tags are what the shared operations propagate. Must not be read off the construction label instead: that is derived state, stale the moment a split or swap creates a cell it was never written for.
|
inlineoverridevirtual |
The quality of cell tid, and how to write it.
The cell attribute types differ between the applications, so the base reaches the one field it shares through these. Both are AMIPS^3; see MAX_ENERGY. Topological operations and smoothing use the accessors so the shared algorithms remain independent of each application's cell-attribute type.
Implements wmtk::TetOptimizerMesh.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::cell_quality_rel | ( | const size_t | tid | ) | const |
AMIPS of a cell over stop_energy – the 3D twin of TriOptimizerMesh::quality_rel(), so both dimensions express "how bad is this element" on the same 1.0 scale.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::check_no_vertex_on_both_surfaces | ( | const char * | when | ) | const |
Hard error if any vertex is on BOTH the input complex and the offset surface – a state no placement satisfies. Called at construction and after every phase; see the definition for why a collapse can create it out of two individually fine vertices.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::check_offset_within_support | ( | const char * | when | ) | const |
Stop the run if any reachable offset-surface vertex has left the potential's support.
Beyond dhat, Phi is identically zero with a zero gradient: the vertex is given no direction back, its residual saturates instead of growing, and the sizing field refines around a vertex nothing can move. There is no recovery from that state and no honest report of it, so it is a hard error, not a warning; the answer to it firing is a larger offset_dhat_factor. Called once per optimization iteration and once on the band as built.
|
inlineoverridevirtual |
Implements wmtk::TetOptimizerMesh.
|
overridevirtual |
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Non-const: an override may cache what it measured before the collapse so its after counterpart can tell a regression from a defect that was already there.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Collapse policy that is the offset's own.
collapse_before_vertex refuses to move a vertex off the surface it belongs to (the base knows only the union of the two), to lower the order of a boundary vertex, or to collapse across a feature of the input complex; it also records how badly aligned the offset surface already was, so its after counterpart can tell a regression from a pre-existing defect.
Reimplemented from wmtk::TetOptimizerMesh.
|
inlineoverridevirtual |
Reimplemented from wmtk::TetOptimizerMesh.
| std::pair< double, double > wmtk::components::topological_offset::TopoOffsetTetMesh::compute_distance_deviation | ( | ) | const |
How far the offset surface is from where it should be: {max, avg} over its vertices.
The absolute error |dist(v, input complex) - target_distance|, over the band's outer surface only – a band cell meeting a plain-background cell, not the input complex it wraps, whose interface sits at distance 0 by construction.
A face with no opposite tet is on the domain boundary and must be counted, not skipped: those vertices are pinned on the box and can never be fixed, so dropping them under-reports a bbox-clipped offset by a large factor.
| std::shared_ptr< SampleEnvelope > wmtk::components::topological_offset::TopoOffsetTetMesh::envelope_for_mask | ( | uint64_t | mask | ) | const |
The envelope a simplex with this boundary mask is contained in, or null.
Zero bits: no boundary, no container. One bit: that tag's own envelope. Several bits: a memoized IntersectionEnvelope over the members – inside means inside every tube, which pins junction geometry to the junction. The multi-bit composite is containment-only: it implements just the virtual is_outside queries, so it must never be returned from smoothing_energy_envelope(), whose caller uses the non-virtual nearest_point.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::face_criterion_rel | ( | const Tuple & | f | ) | const |
The per-face score the collapse and swap acceptance gates gate on (Collapse.cpp, Swap.cpp); >= 1 means the face fails the criterion. Also the per-face form of optimization_quality_stats(). It ranks nothing for refinement – that is update_band_sizing_from_tolerance(), which measures the criterion directly.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::face_is_offset_surface_live | ( | const Tuple & | f | ) | const |
The band's outer surface, recomputed live rather than read from the cached class.
A band cell meeting a cell that is neither band nor input complex. Must be live: the operations that ask run between one labelling pass and the next, so a face a split just created carries whatever cached class its recycled slot happened to hold. Returns true for a band face on the domain boundary; see compute_distance_deviation() for why.
|
overridevirtual |
Is a face part of the substructure.
| fid | Face ID |
Reimplemented from wmtk::TetOptimizerMesh.
|
inline |
The interior lattice a face is sampled on, handed to visit one point at a time.
One definition of "inside this face", because two things are measured across a face: the Phi residual (the diagnostic) and the placement gradient (the criterion). They must share a lattice, or a run that fails the criterion in a face is not explicable by the residual.
Interior lattice points of the barycentric subdivision at denominator n = k + 2: every (i, j, l) with i + j + l = n and each >= 1, so nothing lands on an edge or a vertex, which are measured separately and would otherwise be double-counted. n = k + 2 rather than 2D's k + 1 because a triangle has no interior lattice point at denominator 2; k = 1 is then the centroid, where a triangle inscribed in the offset is furthest from it.
|
overridevirtual |
Get the order of a vertex.
The order of a vertex in a TetMesh is as follows: 0: vertex is not on the surface 1: vertex is on the surface 2: vertex is on the surface boundary or a non-manifold edge 3: vertex is at the boundary of a non-manifold edge or a non-manifold vertex
Computing the vertex order is expensive. It is recommended to store the vertex order as a vertex attribute and override this method.
| vid | Vertex ID |
Reimplemented from wmtk::TetOptimizerMesh.
| TopoOffsetTetMesh::GradientSplit wmtk::components::topological_offset::TopoOffsetTetMesh::gradient_split | ( | bool | include_face_samples = true | ) | const |
| include_face_samples | false measures vertices only – what Phase B's stop test wants, since a face sample is not a variable its sweeps can move. The convergence test always passes true. |
| void wmtk::components::topological_offset::TopoOffsetTetMesh::init_from_image | ( | const MatrixXd & | V, |
| const MatrixXi & | T, | ||
| const MatrixSi & | T_tags, | ||
| const MatrixXd & | V_env, | ||
| const MatrixXi | F_env, | ||
| const std::vector< std::string > & | tag_names | ||
| ) |
initialize TetMesh from vertex, tet, and tag data
| V | #V by 3 vertex matrix |
| T | #T by 4 tet matrix |
| T_tags | #T by #tags tag matrix |
| V_env | V_env by 3 EnvelopeSurface vertices |
| F_env | F_env by 3 EnvelopeSurface faces |
| void wmtk::components::topological_offset::TopoOffsetTetMesh::init_input_complex_bvh | ( | ) |
Build the input complex's BVH and keep the extraction the potential needs.
Must be called after init_from_image(...). One extraction feeds both, so the exact distance field and the smooth potential can never describe different geometry.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::init_offset_potential | ( | ) |
Build the smooth offset potential from the extraction init_input_complex_bvh() kept.
Separate from the BVH init because it needs target_distance and offset_dhat_factor, which a caller that only wants the distance field has no reason to have set.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::init_offset_sizing_field | ( | ) |
Seed the sizing field from the offset surface's current edge lengths, once, before the first pass. Paper Sec. 5.3.3 Step 1: "initialized with the current length of each edge.".
Required: the field otherwise starts at the base target, far coarser than the offset surface, so every offset edge is a collapse candidate on pass one and the surface is decimated before any metric is computed.
|
overridevirtual |
TODO: Check this individually in each operation
Reimplemented from wmtk::TetOptimizerMesh.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::is_edge_on_region | ( | const Tuple & | loc | ) |
Whether edge loc lies on the INPUT complex. The base's is_edge_on_region() asks about the union of the two tracked surfaces, and is_edge_on_bbox() is the base's.
|
inlineoverridevirtual |
The offset's surface can end on a non-manifold or boundary edge of the input complex, which the base must not flip or split across.
Reimplemented from wmtk::TetOptimizerMesh.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::log_quality_spike | ( | size_t | tid, |
| double | before, | ||
| double | after, | ||
| const char * | kind | ||
| ) | const |
Report one tet crossing into absurd quality: its geometry, and why it is degenerate.
Prints the four vertices with the two things that separate the plausible causes – their rounding state (an unrounded vertex is exact-rational, and its float image can make a valid tet look degenerate, which is why is_inverted_f and is_inverted are separate predicates) and the float volume against the edge lengths, a genuine sliver against a coordinate blow-up.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::log_worst_tet | ( | const char * | when | ) | const |
Diagnostic: dump the single worst-quality tet and everything that could be pinning it, so a Phase A that will not converge says which element and why rather than only a max.
Reports, for the max-cbrt(cell_quality) tet: its volume and inversion state, its region label, and per vertex the flags that decide whether any operation may touch it – on the input complex, on the offset surface, on the bounding box, rounded, order and sizing scalar. Then each of the six edges against the two length gates, so it is visible whether split or collapse would even consider them: split needs len^2 >= splitting_l2 * sbar^2, and the coarsening pass stops at len^2 <= collapsing_l2 unless coarsen_unbounded. The point is to separate "no operation is attempted here" from "operations are attempted and refused", which the aggregate counters cannot distinguish.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::marching_split_edge_after | ( | const Tuple & | t | ) |
check inversion & rounding
| void wmtk::components::topological_offset::TopoOffsetTetMesh::marching_tets | ( | ) |
execute simplistic marching tets. All edges with one vertex labelled 0 and the other 1/2 are split, at the midpoint.
Do not re-add a distance-field placement that root-finds each band-boundary edge onto d(x) = delta at insertion time; measured worse – see git history of this file. The paper places inserted vertices at the midpoint (Sec. 5.2) and leaves the distance entirely to the Step-3 optimization, which has error feedback and the shared accept checks.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::mark_input_complex_vertices | ( | ) |
Set m_is_on_region from the input-complex labels. Called at the end of label_input_complex(), which is the earliest point at which the complex is known.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::max_band_vertex_distance | ( | ) | const |
The furthest any offset-surface vertex sits from the input complex, by BVH. 0 when no offset exists yet. Sizes dhat in init_offset_potential().
| TopoOffsetTetMesh::FaceSamples wmtk::components::topological_offset::TopoOffsetTetMesh::offset_face_samples | ( | const Tuple & | f | ) | const |
The Phi residual at offset_residual_samples interior points of offset face f.
The criterion cannot be a vertex criterion: a surface can have every vertex exactly on the level set while its triangles cut across it, which reads as converged and is not the offset – the same gap the paper's normal-deviation criterion (Sec. 5.3.3) covered. Sampling the face interior is what the potential makes possible: Phi is defined everywhere, so the offset can be measured anywhere on the surface rather than only where the mesh happens to have put a vertex. The same samples feed face_criterion_rel(), so the sizing field refines a surface too coarse to represent the offset instead of letting it decimate.
Returns nothing for a face with an unreachable corner: a triangle running onto the input complex is legitimately closer than target_distance across part of itself.
|
inline |
The convergence tolerance: the bound on |grad (Phi - c)^2| at a band vertex.
A fraction of target_distance, the right unit: grad E = 2 (Phi - c) grad Phi, and grad Phi is dimensionless for a field whose value is a length, so grad E is a length. Stated on the gradient rather than the residual because it needs no conversion from field value to length: it is the stationarity condition of the objective Phase B minimises, so it is the same test for every potential, which lets a concave input be judged by the same number as a convex one. Where |grad Phi| departs from 1 – a medial axis, a sharp feature, competing primitives – the two bounds are different tests and neither implies the other.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::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.
|
inline |
The residual scale, derived from the criterion rather than configured beside it: grad E = 2 (Phi - c) grad Phi, so on a unit-slope field the bound |grad E| <= g is |Phi - c| <= g/2. It must stay derived – it also sizes the Phase A offset envelope (offset_envelope_rel x this) and the min_edge_length floor, and as its own knob it would not follow front_conv_rel, holding the offset surface far tighter than the run is judged by.
|
overridevirtual |
(max, avg) Phi residual over the reachable offset surface, in units of its tolerance; the engine's stop metric is therefore 1.0.
Reimplemented from wmtk::TetOptimizerMesh.
|
inlineoverridevirtual |
Phase A is TetWild, so it stops where TetWild stops: absolute AMIPS against stop_energy, the base's default. Phase B's metric is the Phi residual normalized by its own tolerance, so 1.0 means converged. Both must agree with optimization_quality_stats() above: a metric in one unit tested against a bar in another stalls the phase.
Reimplemented from wmtk::TetOptimizerMesh.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::optimize_offset | ( | const std::filesystem::path & | output_file | ) |
optimize the offset
All label attributes are ignored from here on out. All relevant information is stored in tags and in the "on_surface" and "on_offset" attributes.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::optimize_offset_alternating | ( | ) |
The A/B driver: Phase A (TetWild + offset envelope), Phase B (smoothing to a fixed point, then refine where Phi is stuck), repeated until both criteria are inside tolerance or max_rounds is reached. Replaces the single mesh_improvement() call.
| size_t wmtk::components::topological_offset::TopoOffsetTetMesh::phase_b_smooth | ( | ) |
Phase B's smoothing loop. Each pass sweeps every vertex once: offset vertices are placed on the level set by smooth_offset_vertex_backtracking(), background vertices minimize their one-ring AMIPS by smooth_interior_vertex_phase_b(). Returns the number of passes; the natural exit is a pass in which no offset vertex was backtracked by its one-ring (m_phase_b_constrained == 0), i.e. every local minimum is interior. See the definition for the other exits.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::rebuild_offset_envelope | ( | ) |
Build m_offset_envelope from the current offset-surface triangles. Called at the start of each Phase A.
|
overridevirtual |
Phase A's stall-driven sizing refinement – TetWild's, on element quality.
mesh_improvement() fires this when optimization_quality_stats()'s max stalls, and only runs inside Phase A, so this only ever runs there; it ratchets the sizing down around the worst tets by AMIPS, as TetWildMesh does. It does not refine for the offset – that is update_band_sizing_from_tolerance(). Both write the same per-vertex scalar.
Implements wmtk::TetOptimizerMesh.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::report_outside_support | ( | const char * | when, |
| const DistanceSplit & | s | ||
| ) | const |
Turn a residual_split()'s outside-support tally into the hard error. Separate from check_offset_within_support() so the per-iteration check can reuse a split it already has.
|
inlineoverridevirtual |
Implements wmtk::TetOptimizerMesh.
|
inline |
Place a vertex, keeping its exact and rounded coordinates in step.
The offset works in doubles throughout, so every vertex it places is rounded and its rational position is the exact value of the double it carries. m_pos must still be filled: the shared split falls back to the exact midpoint when the double one would invert a tet, and every quality and orientation test around an unrounded vertex reads its neighbours' m_pos. A stale m_pos would silently feed those the wrong point.
|
overridevirtual |
Every vertex goes through the shared smoother. Nothing is dispatched anywhere else.
The offset is a term smooth_offset_vertex_backtracking() drives to zero directly, so the one surface the component exists to place goes through the same line search, exact inversion test and accept checks as every other vertex. See OffsetPotential.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
The base rounds and refuses bbox vertices; this drops the bbox refusal (the wall is a region boundary held in an envelope, not frozen) and, in Phase B, refuses every vertex that is not offset-only – that phase places the offset surface and moves nothing else.
Reimplemented from wmtk::TetOptimizerMesh.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::smooth_interior_vertex_phase_b | ( | const Tuple & | t | ) |
Phase B's placement of a background vertex: Newton on the one-ring AMIPS energy, to this vertex's own minimum.
The counterpart of smooth_offset_vertex_backtracking() for vertices that carry no surface. Solving the interior alongside the surface opens one-rings that would otherwise force the offset root find to backtrack – which is the pass loop's exit criterion.
Reuses the shared smoother (optimization::smooth_vertex_3d) – pure AMIPS, no envelope terms, exact inversion accept, quality veto – but with Phase B's own solver, which stops at vertex_grad_tol_rel of the visit's initial gradient instead of a fixed iteration budget.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::smooth_offset_vertex_backtracking | ( | const Tuple & | t | ) |
Phase B's placement of an offset-surface vertex: the offset energy alone, then backtrack into the one-ring if the solved point inverts something.
The only smoothing Phase B does – smooth_before() refuses every other vertex there, so no AMIPS term is assembled anywhere in the phase. Do not route it through the shared smoother; measured worse – see git history of this file. That objective is w_amips * AMIPS plus the offset term, so the vertex rests where the two gradients cancel rather than on the level set, and AMIPS being dimensionless its positional gradient scales as 1/h, which makes the resting point worse as the mesh refines.
A 1-D root find along the normal, Phi(x) = c, never a 3-D minimisation of the offset energy: that energy's Hessian is rank one, so the level set's tangent plane is unconstrained and the vertex slides. Element shape is left entirely to split/collapse/swap. No incident tet may invert – a constraint on the segment to the target, not a reason to discard it: bisect for the furthest point along it that keeps every tet valid.
< damping increases per iteration before giving up
< mu/s^2 at entry: effectively undamped Gauss-Newton
|
inlineoverridevirtual |
... and it is not CONTAINED by one either.
The base's default answers m_envelope for every vertex the union flag m_is_on_surface is set on, which here covers both tracked surfaces. m_envelope is built from the input complex's boundary, so answering it for an offset-surface vertex would demand the offset stay within eps of the input – a tube of the wrong radius around the wrong surface, which refuses every move the offset term asks for.
The input complex falls through to the base and is contained by m_envelope in every phase. That is the whole constraint on it: it is smoothed like any other TetWild surface vertex and this tube is the tolerance it may drift within.
The offset surface is the exception only in Phase A, where m_offset_envelope contains it like any other tracked surface: Phase A minimises AMIPS alone, so without a container nothing stops it relocating the offset surface for element shape, which Phase B would then have to undo. Phase B answers null, being the pass whose job is to move the surface.
Reimplemented from wmtk::TetOptimizerMesh.
|
inlineoverridevirtual |
The offset surface is the one tracked surface with NO envelope, in either role.
It is the surface the optimization exists to move: a tube around wherever conservative growth left it would cap how far it can travel toward the level set. What holds it is the offset term in the objective, not a container.
The pull must be a real envelope, never a composite: this hook's consumers call the non-virtual SampleEnvelope queries (nearest_point, and the ExactDistanceEnergy3D trio), which on a composite bind to the base's null BVH. So a junction vertex is pulled toward its most-violated member tube instead – one real envelope per smoothing attempt, alternating toward the junction across passes – while smoothing_containment_envelope() enforces the full constraint.
Implements wmtk::TetOptimizerMesh.
|
inlinestatic |
cell_quality is AMIPS^3; everything else reports its cube root, so convert once here. A non-finite quality is worse than any finite one, not silently zero.
|
overridevirtual |
Writes the new vertex's tracked-surface membership before the shared split's containment check reads it; returns the base's positioning verdict unchanged. See the definition.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Restore application cell data on the children made by a split.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Application metadata not represented by the shared vertex attributes.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Split policy that is the offset's own.
The shared split places the vertex, keeps the quality and the shared attributes and checks containment; what it cannot know is which region tag the two child tets inherit, and which of the two tracked surfaces the new vertex joined.
The shared split places the new vertex, keeps quality and shared attributes, and checks envelope containment. These three hooks add what only the offset knows: which region tag each child tet inherits, and which tracked surfaces the new vertex joined.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
This function computes the attributes for the added simplices. User specified modifications and desideratas for after an edge split.
| the | edge Tuple to be split |
check inversion & rounding
update quality
containment: the new surface triangles must stay inside the envelope
update vertex attribute
update face attribute
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
overriden splits/invariants Dispatch: the optimization phase runs the shared split, everything else the marching-tets one below.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Compute the attributes for the added simplices.
User specified modifications and desideratas for after a face split
| t | The face tuple to be split. |
Reimplemented from wmtk::TetMesh.
|
overridevirtual |
User specified preparations and desideratas for a face split before changing the connectivity.
| t | The face tuple to be split. |
Reimplemented from wmtk::TetMesh.
|
overridevirtual |
Compute the attributes for the added simplices.
User specified modifications and desideratas for after a tet split
| t | The tet tuple to be split. |
Reimplemented from wmtk::TetMesh.
|
overridevirtual |
User specified preparations and desideratas for a tet split before changing the connectivity.
| t | The tet tuple to be split. |
Reimplemented from wmtk::TetMesh.
|
inlineoverridevirtual |
Envelope the tracked-surface triangle vids must stay inside.
Null means this triangle carries no containment requirement, and every containment check on it is skipped. Both applications that track a single surface keep one envelope for it, so the default answers with that; an application tracking more than one surface (see SurfaceTagAttributes::m_surface_class) overrides this to answer per class – the offset boundary, for instance, is free to move and is checked against nothing.
Keyed on the vertex ids rather than the fid because every caller is a topological operation asking about the triangles it is ABOUT to create or has just created, where only the vids are known for certain.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Propagate application data to the cells made by a successful topological swap.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Which tag the tets a swap creates should carry.
A swap must not move the boundary between differently tagged regions, since that boundary is the offset. Around an interior edge every incident tet already shares a tag, so this is a cheap safety net there; on a surface flip the ring genuinely spans two and the majority tag wins. Refuses the swap when three or more tags meet: there is then no single answer and any choice would relabel a tet.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Reimplemented from wmtk::TetOptimizerMesh.
|
private |
The swap operations themselves are wmtk::TetOptimizerMesh's. What is left here is the offset's own: keeping the region tags and construction labels consistent across a swap.
|
inline |
The three helpers of the per-tag envelope dispatch. tag_bits() and face_mask() are trivial; envelope_for_mask() is out of line (it builds IntersectionEnvelopes lazily).
| size_t wmtk::components::topological_offset::TopoOffsetTetMesh::update_band_sizing_from_tolerance | ( | ) |
Per-vertex band sizing update, run after Phase B. Returns the number changed.
Refine-only, and only on pure chord error. "In tolerance" is the convergence criterion itself – |grad (Phi - c)^2| <= offset_gradient_tolerance() – measured at band vertices and at face-interior samples on the lattice for_each_offset_face_sample() defines, so this responds to the quantity that decides the run rather than to a proxy for it.
This is the only thing that refines the sizing field for the offset. Phase A keeps TetWild's quality-driven stall response (refine_sizing_around_worst), which ranks elements by AMIPS; the two write the same field and accumulate.
Clamped below by max(min_sizing_scalar, min_edge_length / l) – the band's floor, not stuck_refine_min_scalar – and the changed vertices seed gradation_smooth_sizing(), so a re-sized patch does not sit against an untouched one.
|
inline |
The tag boundaries this vertex lies on – the raw mask, gated on the vertex still being region geometry at all.
The gate is required, not redundant. m_boundary_mask propagates by a bare AND of a split's endpoints, so an edge whose two ends share a bit hands that bit to its midpoint even when the edge is a chord through the interior – and the front is built by splitting precisely such edges. vertex_is_on_region() does not over-claim that way, so the mask says which boundaries and the gate says whether the vertex is on one at all; the mask only ever narrows an answer the flags already allow.
|
inline |
Is this vertex on a region boundary – a tag boundary, or the domain wall.
Derived, never stored. Both halves are already maintained exactly: m_is_on_region by is_edge_on_region() at each split and an OR at each collapse, on_bbox_faces by the intersection of the split endpoints and by the collapse rule that a wall vertex may only merge into one at least as constrained. A stored flag would be a fourth thing to keep in step with those, carrying nothing they do not.
|
overridevirtual |
Is a vertex part of the substructure.
| vid | Vertex ID |
Reimplemented from wmtk::TetOptimizerMesh.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::wall_offplane_deviation | ( | const size_t | vid | ) | const |
How far vid sits from every bounding-box plane it claims membership of. Zero when it is where its on_bbox_faces say it is; empty membership gives zero.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::warn_if_offset_reaches_domain_boundary | ( | ) | const |
Warn if conservative growth ran into the bounding box.
A band face on the domain boundary means target_distance exceeded the clearance between the input complex and the box, so the offset is clipped there. No operation can move those vertices, making the target distance unreachable by construction – worth saying out loud rather than leaving as a mysterious plateau in max_dist_err.
|
inlineoverridevirtual |
The driver hands a bare debug_N; put the frames beside the run's own output rather than in whatever directory it was launched from, which is what lets the viewer find them as a timeline – it globs *debug_*.vtu in the output directory and orders them by counter.
Implements wmtk::TetOptimizerMesh.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::write_phi_grid | ( | const std::string & | path, |
| int | n | ||
| ) | const |
Sample the potential on the plane through the box centre normal to its shortest extent and write it as <path>_phi.vtu, n x n samples. See phi_grid_resolution; 0 disables.
| std::vector<std::array<int, 3> > wmtk::components::topological_offset::TopoOffsetTetMesh::churn_counts |
{split-born vertices, recollapsed, recollapsed in the immediately following collapse pass} per A/B round, in step with op_counts. See VertexExtra::m_born_epoch.
| std::atomic<int> wmtk::components::topological_offset::TopoOffsetTetMesh::iter_cnt_split_born {0} |
Per-iteration operation counters, reset before each iteration's operation passes. Churn: split-born vertices a collapse later removed, and the subset removed in the same pass-pair that created them. The rest survived at least into a later iteration.
|
private |
The worst offset-face criterion around the operation, captured before it runs – the bar its after hook compares against, mirroring how the AMIPS gates use cache.max_energy.
| std::shared_ptr<SampleEnvelope> wmtk::components::topological_offset::TopoOffsetTetMesh::m_input_complex_envelope |
The exact-kind envelope of the input complex, built only for offset_field "euclidean". Null otherwise.
Not a containment envelope and never used as one: no operation tests against it. It exists because nearest_point_feature() – the foot point plus the feature kind the exact distance derivatives case on – is only answered by the exact path. Built from the same extraction as m_input_complex_bvh, so it describes the same geometry.
|
mutable |
Memoized IntersectionEnvelope per multi-bit mask. Lazily built under the mutex because the queries that need them run concurrently under kPartition.
| std::shared_ptr<SampleEnvelope> wmtk::components::topological_offset::TopoOffsetTetMesh::m_offset_envelope |
The tube the offset surface may not leave during Phase A. Null in Phase B.
Rebuilt at the start of every Phase A from the offset surface as Phase B just left it, with eps = offset_envelope_rel x the Phi tolerance. Rebuilding is what lets the surface travel across rounds: each Phase A pins it near its current position, each Phase B is free to move it. m_envelope, the input complex's, is built once and never rebuilt – that geometry is what the offset distance is measured against and may not drift at all.
| Parameters& wmtk::components::topological_offset::TopoOffsetTetMesh::m_offset_params |
The base holds only wmtk::OptimizerParameters; this is the same object, typed, for the offset-only fields.
| std::shared_ptr<OffsetPotential3D> wmtk::components::topological_offset::TopoOffsetTetMesh::m_offset_potential |
The smooth offset potential Phi of the input complex, as loaded.
The offset surface is the level set Phi = c. Built from the same extraction as m_input_complex_bvh, in the same call, so the two describe the same geometry, and like the BVH built once and never rebuilt: every offset quantity is measured against the input as given, not against whatever the mesh has drifted to.
|
mutable |
Offset placements this Phase B pass that could not reach their unconstrained minimum: the root find entered its one-ring bisection, was refused outright by inversion, or found the ring already float-inverted on entry. Reset by phase_b_smooth() before each pass; a pass ending with this at zero is the loop's natural exit, since the potential is a frozen field and placements couple only through the ring constraint.
|
mutable |
Per-thread Newton solver for Phase B's interior AMIPS solves. Separate from the base's m_solver because it carries a different stopping rule: polysolve's rel_grad_norm_tol set to vertex_grad_tol_rel with a deep iteration budget, against the base's fixed shallow budget with no tolerance. Created on first use.
| MatrixXd wmtk::components::topological_offset::TopoOffsetTetMesh::m_phi_V |
The input complex as Phi's primitives: the BOUNDARY triangles of the label-1 tet region plus the complex's isolated triangles, every edge of those triangles plus the complex's wires, and its isolated points. Filled by init_input_complex_bvh(), consumed by init_offset_potential().
|
private |
The construction label shared by every cell of the swap's ring, captured alongside the tag. Required: without it a swap leaves its new cells holding whatever label occupied the recycled tet slot.
| std::map<int64_t, int> wmtk::components::topological_offset::TopoOffsetTetMesh::m_tag_bit |
Input tag id -> bit position in VertexExtra::m_boundary_mask. Assigned in init_from_image() once the tag maps are complete; at most 64 input tags.
| std::map<int64_t, std::shared_ptr<SampleEnvelope> > wmtk::components::topological_offset::TopoOffsetTetMesh::m_tag_envelopes |
One containment envelope per input tag, ambient included. Both phases.
E_t is a tube of half-width m_envelope_eps around the boundary faces of region t as the input mesh carried them, built in init_surfaces_and_boundaries() before offset construction, so the band's later tag rewriting never enters them. A simplex on several boundaries is constrained by the intersection of its tags' tubes (envelope_for_mask()), which pins junction curves and points to the junction itself. Every simplex of the input complex lies on tag boundaries (see label_input_complex()), so these tubes hold all of it.
m_envelope (the base's pointer) is a UnionEnvelope over these members, so the shared engine's one direct use – the collapse_edge_before point check – keeps union semantics.
Interior vertices of the complex are not held by these: a face interior to a region has identical tag sets on both sides and lands in no bucket, so a tet-filled complex's interior is free to optimise.
|
mutable |
The vertex compute_distance_deviation() last found the max at, and a dump of everything that could be stopping it from moving. Diagnostic only.
|
staticconstexpr |
SurfaceTagAttributes::m_surface_class for the offset boundary.
The input complex keeps the primary class 0, so a face carrying input geometry is envelope-checked by the shared operations exactly as in tetwild and simwild.
| std::vector<std::array<int, 3> > wmtk::components::topological_offset::TopoOffsetTetMesh::op_counts |
{splits, collapses, swaps} per A/B round – one entry per round the driver runs, the converging one included, as deltas rather than running totals. Phase B does no topological work, so a round's entry is exactly what its Phase A did. Does not mirror optimization_metrics, which is a single whole-run summary.
| std::vector<std::array<double, 8> > wmtk::components::topological_offset::TopoOffsetTetMesh::optimization_metrics |
{max_dist_err, avg_dist_err, max_phi_residual, avg_phi_residual, max_grad, avg_grad} per optimization iteration. Only the last pair is the convergence criterion; the rest are diagnostics answering different questions – the Euclidean error says how far the smoothed offset ended up from the exact one, and the Phi residual still ranks the sizing field.