Repository navigation
Envelope IO: components write and read what they hold as tags - #1156
Merged
Merged
Conversation
… tags it writes and reads Tags are the one currency between components: what a component holds -- each input's tracked faces or curves, the feature curves -- it writes as tags, on both routes, and a reader turns them back into tracked simplices with their input sets. Until now several were lost on the way. - tetwild writes oriented face tags, one per input (input_names, else tag_<i>; io::input_labels(), io::tag_labels() moved out of collect_tags()), as triangle blocks of the memory route's mesh and as physical groups of _final.msh; no volumetric tags (the winding numbers wn_<i> stay fields). With several inputs each tracked face's inputs now come from the arrangement whether or not per_input_envelopes is on, since they are the tags; before, without it, every face was input 0. - tetwild and simwild 3D write their feature curves, the tet edges tiling each input_edges file, as an edge block, feature_<i> per file (io::collect_feature_edge_tags(), utils::feature_input_name()); the order2 kind is not written, a reader derives it again. - triwild writes each input's curves as edge tags named as its triangle tag, oriented as the input. - Readers (read_mesh_data(), read_image_msh()): a 2D edge block is tracked curves with their input sets and orientation, its EnvelopeSurface rows (or a block without tags) the envelope, as before; every other label used to make the whole block the envelope, which no data model relies on (the one 2D edge group in data2 is EnvelopeSurface). A 3D edge block is feature curves, held as input_edges are. The file route keeps corr_input_vid (and vid), as the memory route always did, so a chain of offsets keeps its correspondence through files. - topological_offset and manifold_extraction refuse an input with feature curves instead of dropping them silently: they hold none yet. - tetwild's memory mesh can no longer be written to .vtu: the VTU writer refuses labelled fields, as it already did for simwild's and triwild's meshes; the quickstart example writes .msh. Tests: TetWild writes each input's face tags and its feature curves on both routes; TriWild writes each input's curve tags on both routes; tagged meshes read an edge block as tagged curves (2D) and as feature curves (3D); SimWild keeps a tagged mesh's curves on both routes; tagged mesh files carry an earlier offset's correspondence. Two tests that assumed tetwild writes no triangles of its own match faces by corners instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the envelope _same_as_file() asserted that a .msh output has no block of the envelope's cell type (edges in 2D, triangles in 3D). That stood for "the file holds no envelope" while the envelope was the only such block a component wrote. triwild now writes each input's curves as an edge block of tags, and a reader takes only that block's EnvelopeSurface rows (or a block without tags) as an envelope; the check now asks exactly that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t one's reference A pipeline (tetwild -> simwild remeshing -> ...) kept its guarantee only against the previous stage's output: each stage built its envelopes around the mesh it received, so the bound against the original input grew by one eps per stage. Now the first component's reference -- the geometry its envelopes were built around, the input or the simplified input per envelope_reference -- is passed on with its labels, and a receiver holds to it, so the bound stays one eps of the input. - The envelope is a mesh of the reference, labelled as the tags are: triangles (2D: segments) with one tags column per input, named as the output mesh names that input; the feature curves' segments as an edge block (feature_<i>); the input points as an untagged point block. No eps. - Producers (tetwild, triwild, simwild) return it as outputs["envelope"] on the memory route and write it as <output>_envelope.msh on the file route (its own vertices, and label names that would collide with the mesh's tags in one file). - The `envelope` key is a mesh name on the memory route and a path on the file route (new; it was ignored there). simwild remeshing given one holds each face to its labels' reference envelopes, its feature edges to tubes around the reference's curves, with its own eps; a label of the mesh the envelope lacks throws, naming it. - The containment check, at the start: every held face and feature edge received must lie inside the reference's envelopes of its labels at the receiver's eps, exactly; otherwise it throws with the label, the count outside and the eps (the sender held them to a wider envelope). With a reference simwild's old sanity check, which silently rebuilt the envelope from the mesh, is skipped. Order-2 tubes and curve-end anchors are built from the reference but not checked: the engine holds an edge the reference lacks around itself, which the mesh cannot tell apart from a wider sender. - A receiver hands on the envelope it was given, unchanged. topological_offset and manifold_extraction read it on both routes and hand it on; neither holds to it yet. - simwild sets its feature-curve names before the initialization, so the checks name a label by its name. Tests: tetwild -> simwild and triwild -> simwild chains on both routes (labels and blocks of the envelope, the output within eps of the original input, both routes alike, the envelope handed on unchanged, the throws at a smaller eps and on a missing face or feature label); the Python chain on both routes with a relative envelope path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the input, as the union is A tagged mesh input given no envelope has its envelope built from the mesh's tracked faces as read (init_surfaces_and_boundaries()), which cleared the rows' labels. With no row labels the engine's init_input_envelopes() builds each label's envelope from the mesh's faces as they are when it is called; simplify() calls it again after collapsing, so under envelope_reference "input" the union was rebuilt around the input while every per-label envelope was rebuilt around the simplified faces. The rows now keep their labels (each face's input set as read), so every per-label envelope is built from the input after the simplification too. Both places that build the envelope from the mesh (no envelope given, and the sanity check's rebuild) do so. The simplify spec doc said a tet mesh input is never simplified; in 3D a tagged mesh is, under remeshing with preserve_topology. Test simwild-simplification-label-envelopes-around-the-input: a tagged slab whose two-label interface has a spike of 2 eps the simplification removes; after simplify() the spike's tip, an input vertex, lies in the union and in both labels' envelopes, on both routes (no envelope given, and the sanity check's rebuild). Before this commit both labels' envelopes miss it (4 failed checks, the tip 2.0 eps from the simplified surface); after it they contain it. simwild and component IO suites pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 5, 2026
udaykusupati
added a commit
that referenced
this pull request
Nov 5, 2026
…ope it hands on Since #1156 simwild hands on the envelope it held to, its edges labelled by their inputs (m_E_envelope_edge_sets), and the 3D simplification refreshes those labels when it rebuilds the envelope around the simplified surface (m_F_envelope_face_sets). The 2D twin rebuilt the envelope without them, so under envelope_reference "simplified" the envelope handed on carried no labels at all: a 64-edge straight interface simplified to 23 edges and came out unlabelled; now 23 edges, 23 label rows (tag_0). 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.
Follows #1155 (envelope reference).
What this does
Tags become the one way components hand each other what they hold. Each component writes its tracked faces, tracked curves and feature curves as tags, on both routes (memory and file). A reader turns the tags back into tracked simplices with their input sets. Before this PR, several of these were lost between components.
input_names, elsetag_<i>), as triangle blocks of the memory mesh and as physical groups of_final.msh. The winding numberswn_<i>stay fields. No volume tags.input_edgesfile) as an edge block,feature_<i>per file. Theorder2kind is not written; a reader derives it again.read_mesh_data(),read_image_msh())EnvelopeSurfacerows (or a block without tags) are the envelope, as before.input_edgesare.corr_input_vid(andvid), as the memory route always did. A chain of offsets keeps its correspondence through files.Envelope passing
A pipeline (tetwild → simwild remeshing → …) used to keep its guarantee only against the previous stage's output: each stage built its envelopes around the mesh it received, so the bound against the original input grew by one eps per stage. Now the first component's reference (the input, or the simplified input, per
envelope_reference) is passed on with its labels, and a receiver holds to it. The bound stays one eps of the input.outputs["envelope"], now labelled<output>_envelope.msh(new)envelopekey of a receiverBehaviour changes
per_input_envelopesoff: each tracked face's input set now comes from the arrangement, because the face tags need it. Before, every face was input 0.EnvelopeSurface: these are now tracked curves. Before, any such label made the whole block the envelope. No data model relies on the old behaviour: the only 2D edge group in data2 isEnvelopeSurface(models/topo_annots_groups.msh)..vtu: the VTU writer refuses labelled fields, as it already did for simwild's and triwild's meshes. The quickstart example now writes.msh. Making the VTU writer skip tag blocks is a follow-up.Tests
Verification
Run on this branch, whose base has the same tree as
mainat ab4fe46, with the envelope passing:wmtk_tests,wmtk_component_io_tests, tetwild, simwild, triwild, tracking, topological_offset, shortest_edge_collapse, manifold_extraction.python/tests: 61 passed, 4 skipped (the PolyFEM tests, which needPOLYFEM_BIN), including the envelope chain on both routes. One test asserted that triwild's file holds no edge block, which stood for "the file holds no envelope"; it now checks for noEnvelopeSurfacerows.tests/python: 110 passed, including the 62 docs-example subtests. One collection error appears on both this branch andmain: pytest collects the helpertested_elsewhere()as a test.🤖 Generated with Claude Code