Repository navigation
tetwild, triwild: tagged mesh input held to a passed envelope; IO keeps data fields - #1157
Merged
Merged
Conversation
…and TriOptimizerMesh tetwild's and triwild's tagged routes (next commit) hold a mesh to a passed reference as simwild does, by the same code, so the code simwild runs moves out of simwild, unchanged: - TetOptimizerMesh, from SimWildMesh: m_has_reference, m_reference_curves, m_feature_edge_names, label_name() (now virtual), require_reference_face_labels(), init_reference_feature_tubes() and check_reference_containment(), with their helpers bit_names() and counts_by_name(). - TriOptimizerMesh, from SimWildMeshTri: m_has_reference, label_name() (virtual), require_reference_edge_labels() and check_reference_containment(). - io (tagged_mesh_data.hpp): read_labelled_envelope(), a labelled reference as a receiving mesh reads it, from simwild's read_mesh_data(); held_rows(), the reference's rows of the mesh's labels, from simwild.cpp. The checks that read simwild's members take them as arguments: the reference's labels (m_F_envelope_labels, m_E_envelope_labels) and, in 3D, the eps. simwild calls the base versions. No behaviour change: the same checks, errors and log lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…envelope is rebuilt The comment said SimWildMesh::simplify() collapses with the quality check off and then rebuilds the envelope around the result. The check is on since 16cbe2a (2026-08; EdgeCollapsing.cpp says why), and the envelope is rebuilt around the result only with envelope_reference "simplified"; with "input" it is restored around the input. Comment only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… to a passed reference
The tagged route: one tagged mesh as the input -- on the memory route a named mesh with tets
(triwild: triangles), on the file route a .msh with them -- continued from as it is, its cells
kept, so that a chain of components can go on through tetwild and triwild (tetwild ->
topological_offset -> ... -> tetwild). Skipped on it: the reading of surfaces (curves), the
simplification, the insertion (the arrangement) and the input winding numbers. Until now a tet
mesh threw on tetwild's memory route ("Expected triangle input"), and on the file route each
triangle of the .msh became one input surface, its tets and tags dropped.
- read_tagged_mesh() (tetwild, triwild) reads the mesh as simwild's read_mesh_data() reads one:
the triangle block's tags (2D: the edge block's) are the tracked faces (curves), one input per
label, each row oriented by its vertex order; in 3D the edge block's tags are feature curves;
the point block is input points. Only `tags` is read, every other field dropped, so a result goes
back in as it is. Refused: tets (2D: triangles) tagged other than ambient -- volumes and regions
are simwild's to remesh --, inverted cells, rows labelled EnvelopeSurface, and the rest listed in
read_tagged_mesh.hpp.
- TetWildMesh::init_from_tagged_mesh(), and triwild's init_mesh() on the mesh's own triangles,
initialize as after an insertion: every vertex rounded, the tagged faces (curves) tracked with
their labels and orientation, the feature curves and points tagged and anchored.
- The mesh must fill its bounding box, as the insertion's background box (the arrangement's padded
box) does: its boundary faces (2D: edges) are the wall, held by the box rule. A tracked boundary
face, the boundary of a filtered mesh, throws: the operations hold a tracked face from the cell
across it, which a boundary face has none of (measured on two filtered unit squares held by their
envelopes: a collapse left an edge 0.5 off its square). So does an untracked boundary face in no
plane (line) of the box, which nothing would hold.
- `envelope`: the reference an earlier component returned, as simwild takes it. The envelopes,
tubes and anchors are built around it at this run's eps, label by label, its labels matched to
the mesh's by name, and before the optimization the faces (edges) received must lie in it,
checked exactly by the base's checks (previous commit). It is handed on unchanged. Without it
the envelopes are built around the tagged faces as received, and those are handed on.
- Refused with a tagged mesh: any other input, input_names, input_points, tetwild's input_edges and
use_legacy_code, filter "input" (there is no input surface), and with `envelope`
use_sample_envelope or an envelope without labels.
tetwild writes no tet tags, so its output goes back into its tagged route as it is. triwild still
writes each input's region as triangle tags, which the route refuses: its own output goes back in
without them (skip_winding_number under filter "none").
Tests: tetwild and triwild continue from a tagged mesh on both routes with one result, the
reference they hand on being the rows received; tetwild takes its own output back in, fields and
all; volume and region tags, an offset's band, a filtered mesh and the inputs the route has no use
for are refused; a tagged mesh is held to a passed envelope on both routes, at a tenth of its eps
refused, as is an envelope without a label of the mesh; the wall's checks (test_tagged_input.cpp);
the Python memory route gives the file route's result.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n a filter keeps With several inputs every filter keeps the domain all of them bound. filter_inputs (default: all) names the inputs that do, by their labels (input_names, else tag_<i>, or a tagged mesh's face-tag or edge-tag labels); the others -- an open sheet through a solid, a second solid outside it -- neither cut nor add to the domain, and stay in the output as tracked faces (2D: edges), with their tags, wherever they lie. - "input": the winding number of the named inputs only (tetwild's compute_input_winding_numbers() sums theirs; triwild's filter_with_input_winding_number() keeps a face inside one of them). - "tracked": the winding number of the tracked faces carrying a named input only. A tracked face carrying a named input and another one (two solids touching face to face) throws before the filter: its orientation is one count for all its inputs, so the named inputs' own is not known there; "flood" needs no orientation. - "flood": the fill crosses the tracked faces of the other inputs. A name that is not an input throws, and so does the key with filter "none", or in tetwild with use_legacy_code under "tracked" or "flood". The field the filter reads (winding_number_input, winding_number_tracked, part) is then of the named inputs. Tests: two unit tets (2D: squares) 3 apart -- every filter keeps both by default, and with filter_inputs a only a, its volume 1/6 (area 1) exactly, on the surface and the tagged route; two unit boxes (squares) touching -- "tracked" refused, "flood" keeping a alone; an unknown name and the key with filter "none" refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nside it - A tracked face on the boundary of a tagged mesh (2D: a tracked edge) throws, in 3D and 2D. A filtered mesh's boundary is its tracked faces, and the operations hold a tracked face from the cell across it (collapse_edge_before() checks a merged tracked face in the tet beyond it), which a boundary face has none of; tetwild's and triwild's tagged routes refuse it too (previous commits). The insertion's padded box puts no tracked face there. An untracked boundary face in no plane of the box is taken as before: simwild remeshes an object's tet mesh too (held by nothing there: smooth_before() freezes only the vertices on the box's planes). - A tagged mesh with any other input throws: the route reads input[0] alone, and until now dropped the others without a word. Tests: the 2D mesh of "SimWild keeps a tagged mesh's curves on both routes" gains the two triangles below its square, so that its curve is inside it; "TetWild hands on its envelope and SimWild holds a tagged mesh to it" and its Python twin run tetwild with filter "none", a filtered mesh being refused now; a tagged mesh with a surface input is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…winding numbers are fields Mesh fields come in two kinds. Data is what a component reads: `tags`, the envelope mesh, and the named fields of io::ComponentIO::data_fields (vid, corr_input_vid, labels, is_cone, vertex_id, offset_tag, tag_0, edge_id, dofs, uv, normal), each listed with its reader. Every other field is an annotation, written for people -- tetwild's energy, triwild's quality, the inputs' wn_<name>, the offset's diagnostics: every reader now drops it, on both routes, and names the fields it dropped once per read, at info (ComponentIO::dropped_fields(), log_dropped()). A field a reader requires (c1's dofs) is still an error when missing. Until now each reader listed the fields it took and threw `Unknown mesh attribute` on any other, so a new field broke a chain until every list had it: triwild's wn_<name> (below) broke triwild -> simwild on the memory route; one commit, so no state has one without the other. - Readers: ComponentIO's raw_triangles(), triangles() and edges(); simwild's read_mesh_data() (topological_offset's and manifold_extraction's too) and its .msh route; the labelled envelope (read_labelled_envelope()); tetwild's and triwild's tagged routes, which dropped silently; isotropic_remeshing's features; prismatic_mesh; c1_simplification; tracking's transfer, whose vertex fields it warned about or, strict, refused. reader_vertex_attributes and the vertex_attributes argument of raw_triangles() and triangles() go. - topological_offset's result -- the memory meshes, the .msh, the save_vtu file -- carries the tags, the data fields and two annotations, amips and distance_over_target (DataWriter's `kept` filter); on the mesh it carried eleven annotations besides, which every reader had to list. The DEBUG_output frames carry every field, as before; the stencil file is a frame's only. Tests: a field no reader reads goes through tetwild's, triwild's and simwild's tagged routes, qslim, isotropic_remeshing without feature_fields, ComponentIO::triangles() and the Python memory route as without it; triwild's mesh, fields and all, goes into simwild with the result of its tags alone; the offset's result carries exactly the tags, the data fields and the two annotations, and a debug frame every field. Triwild: triwild kept each input's winding number only as triangle tags, one region per input named as its curve. Regions are simwild's, as volumes are (tetwild writes none), and its tagged route (previous commits) refuses them, so triwild's output could not go back into triwild. Now: - each input's winding number at each triangle's centroid is kept (FaceFinalizeAttributes, registered as tetwild's TetFinalizeAttributes) and written as a field: wn_<name> on the memory route and in the VTU (wn_<i> without input_names), wn_<i> in the .msh, as tetwild's _final.msh; - the inside bits still decide filter "input", and are then cleared: the triangles carry no tag but ambient. The curves stay the edge tags. So triwild's output goes back into its tagged route as it is. skip_winding_number leaves the fields out under filter "none". Tests: each triangle's wn_a and wn_b are 1 inside their square and 0 outside, wn_0 and wn_1 in the .msh with or without names, left out with skip_winding_number under filter "none", none on the tagged route; triwild's output, fields and all, goes back in on both routes; "TriWild writes each input's curve tags on both routes" finds no region tags. The docs examples print each input's tracked edges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
tetwild and triwild take a tagged mesh as their input and hold it to a passed envelope, so a chain of components keeps one guarantee (within eps of the first component's input) instead of one eps per stage. Built on #1156.
Commits
require_reference_face_labels,init_reference_feature_tubes,check_reference_containment, and the 2D twins) move unchanged intoTetOptimizerMesh/TriOptimizerMesh, for the tagged routes below.envelopeholds the run to a passed reference. A mesh with a volume tag throws ("use simwild").filter_inputsnames the inputs that bound the domain a filter keeps.io::ComponentIO::data_fields) and drops every other field, naming the dropped fields in the log. triwild writes each input's winding number aswn_<name>, as tetwild does. They are one commit because neither stands without the other: triwild's new field would break triwild → simwild chains.simplification_runs()states that the quality check is on, and when the envelope is rebuilt.Behaviour changes
Unknown mesh attributethrownamips,distance_over_targetKnown and left as is (TODO): simwild accepts a tagged mesh whose untracked outer boundary is in no box plane, as main does; nothing holds that boundary during smoothing (by reading, not run).
Tests
ctest 255/255 and
python/tests62 passed on the merged tree (macOS arm64, Release); each commit compiles. New cases: the tagged route on both wilds, the boundary throws, simwild's only-input throw,wn_fields on both routes, fields dropped on every reader, and the offset's trimmed output.🤖 Generated with Claude Code