Skip to content

No recoverable mapping from torn seam-duplicate vertices back to the consumer's pre-HEM mesh #100

Description

@csparker247

Use case

Downstream consumers do not use HalfEdgeMesh as their primary mesh type.
They keep their own mesh (the "raw" / pre-HEM mesh — their own vertex array and
face list) and wrap it in a HalfEdgeMesh only to flatten: build the HEM with
insert_vertices/insert_faces, tear it (split_path), extract components,
parameterize, and pack/merge. They then build a per-wedge UV map from the
packed result and expect to apply it back onto their pre-HEM mesh, keyed by
their own (face, corner) identities.

Summary

The round-trip is atlas corner → (F2 maps) → torn-HEM corner → pre-HEM mesh.
Three of the four hops already round-trip cleanly:

  • Face index. insert_faces inserts faces in order
    (HalfEdgeMesh.hpp:1040-1049) and split_path only tears edges (never
    re-inserts faces), so pre-HEM face i == HEM face i, carried back by F2's
    face_map/face_source.
  • Corner order. insert_face may auto-reverse a mis-wound face to stay
    manifold (HalfEdgeMesh.hpp:1706-1748), permuting a face's stored corner
    order. This is not a blocker: the documented per-wedge recipe resolves
    corners by vertex identity against the consumer's own face, never by raw
    traversal index (ChartPacking.hpp:108-123), so the reversal is absorbed,
    not inverted.
  • Non-seam vertices. insert_vertices preserves order and tearing only
    appends vertices, so a non-duplicated HEM vertex keeps its pre-HEM index;
    carried back by F2's vertex_map/vertex_source.

The one hop that breaks is seam-vertex identity. Tearing duplicates seam
vertices via insert_vertex(oldStart->pos) (HalfEdgeMesh.hpp:1445,1465),
appending a new index whose only link to its origin is a copied position.
split_path/split_edge return void and record no duplicate → original
map. So a torn-HEM corner that lands on a seam duplicate cannot be expressed
in the consumer's pre-HEM vertex namespace
— and identity-based corner
resolution (the very mechanism that absorbs the winding reversal) fails for
exactly those corners.

Why this matters

The per-wedge UV recipe in MultiChartFlatten.cpp is self-consistent within
the torn HEM's own namespace, but the consumer's goal is to land UVs on their
pre-HEM mesh. Non-seam corners already work; a seam corner silently cannot,
because the duplicate has no recoverable pre-HEM vertex identity. The same gap
blocks scattering per-vertex attributes captured on the pre-HEM mesh and
emitting output indexed by the consumer's original vertices. Surfaced during
PR #99 (multi-chart packing).

Goal

Make every torn/extracted vertex — original or seam duplicate — name its
pre-HEM (input) vertex, so the documented identity-keyed per-wedge recipe
resolves corners against the consumer's own faces for all corners, seams
included. Composed with F2's vertex_map/face_map and
vertex_source/face_source, a consumer can take any packed/merged atlas
corner and name the pre-HEM face, corner, and vertex it came from.

Out of scope

  • Removing auto-rewinding or seam duplication (both intentional).
  • Recording the insert_face corner-order permutation / a reversed flag.
    Identity-keyed corner resolution (ChartPacking.hpp:108-123) absorbs the
    reversal, so it does not need to be inverted for this use case — the
    corner order is recovered implicitly once seam-duplicate vertices carry their
    pre-HEM identity, by locating each vertex within the consumer's own face.

Candidate approaches (to decide in the track)

  1. Vertex origin tracking (primary) — store an origin (pre-HEM vertex
    index) set on construction and copied to duplicates by split_edge, so every
    vertex names its pre-HEM vertex. Survives extraction via the clone_face_
    vertex copy path. With this, corners are located by identity in the
    consumer's own face and the winding reversal needs no separate record.
  2. Returned remapssplit_edge/split_path return/accumulate
    duplicate → original pairs; lighter-weight but does not survive extraction
    without the caller threading it through, and gives no uniform "every vertex
    names its input vertex" accessor.

References

  • include/OpenABF/HalfEdgeMesh.hpp:1445,1465 (seam-vertex duplication — the gap)
  • include/OpenABF/HalfEdgeMesh.hpp:1706-1748 (winding reversal — absorbed, not a blocker)
  • include/OpenABF/ChartPacking.hpp:108-123 (identity-keyed per-wedge recipe)
  • PR feat: Multi-chart UV packing (PackCharts) #99 (multi-chart packing): MultiChartFlatten.cpp reference comment +
    PackCharts/MergeMeshes provenance maps (the downstream half of the chain).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions