passthrough
A round-trip instrument for the reverse problem in computational geometry. Carry identity and topology across the mesh boundary so a reconstruction is constrained, its validity checkable, and its deviation measurable. The experiment found the reverse problem is two problems.
Repository · Writing: the reverse problem is two problems · Contact

Problem
Forward is solved. Take a CAD surface, an analytic description with control points and knots, run the kernel, and tessellate it into a mesh. The reverse is not. Hand that mesh to a solver that pushes on it, and turning the morphed mesh back into an analytic surface that behaves like the part is the open direction. It tends to get treated as a single difficulty: reconstruction is hard, get better at reconstruction.
The handoff a downstream CAD team actually wants is narrow. A surface changed only where the simulation changed it, unchanged everywhere else. That needs a reconstruction that is determined rather than guessed, checkable for validity, and measurable against where it should have landed.
What crosses the boundary
passthrough carries three layers on the same points. Position, which the solver may change. Identity, a stable label per vertex, which it may not. Topology, the neighbor and winding relationships, which it may not. Vertex 47 is vertex 47 wherever it moves, so correspondence is never inferred.
One contract holds it together: a solver may move positions and is forbidden to renumber or rewire. That single rule is what makes reconstruction, validity, and measurement possible at all.

The validity gate reads the returned geometry against the carried identity and produces three distinct signals.
Collision detection falls out of the carried adjacency directly. Neighbors near each other is expected, non-neighbors coincident is a defect. The topology is the reference that tells a feature from a defect.
The degraded-information ladder
The result comes from one experiment. Hold the same surface and the same loop, and remove what you carry across the boundary, one rung at a time. The point was to measure where reconstruction stays tractable and where it falls apart.
Positional drift climbs three orders of magnitude as the carry is stripped away. That climb is what carrying identity is worth. The ground is synthetic on purpose: a single surface with known ground truth, built to make the boundary measurable rather than asserted.
Reconstruct, then check
A tagged mesh goes out, an external solver morphs it, and it comes back still carrying its identity. Because correspondence is carried rather than inferred, reconstruction is a determined fit. A clean pass writes the reconstructed analytic surface (result.json, schema passthrough.surface.v1), the per-vertex deviation field (field.json), and a status (status.json). The geometry math stays in tested Python. A C# Grasshopper plugin for Rhino 8 is a construction-and-display client that reads the same file contract and rebuilds the surface natively on the canvas.
The two-boundary result
Positional drift behaved exactly as the single-problem story predicted, the clean staircase above. One number refused to follow it. Curvature deviation, how faithfully the reconstruction holds the surface’s curvature, was already saturated at the top of the ladder with full information carried, about 2.2 at the leading edge, and better parameterization did nothing for it. A coarse basis cannot hold a sharp curvature no matter how perfectly the points are placed on it. That number is reported, not tuned away.
The full round trip was validated on Windows in Rhino 8 through the Grasshopper plugin, and the native rebuild matched the Python side exactly.
So the reverse problem is two problems wearing one coat.
The honest version of the work is not “reconstruction works.” It is: here are the two boundaries, here is the one this closes, and here is the one that stays open. The fuller argument, and why the same move shows up far outside geometry, is in the companion essay, the reverse problem is two problems.
Artifacts
Repository · Companion essay · Contact
Run it
Requires Python 3.13 and uv. Rhino is optional, only for the Grasshopper side.
git clone https://github.com/barnes-ngb/passthrough
cd passthrough && uv sync
uv run python -m pytest # 123 passing
# emit the synthetic wing, then run one round trip
uv run python scripts/run_roundtrip.py emit exchange/payload.json --kind good
uv run python scripts/run_roundtrip.py run exchange/payload.json exchange/return --morph clean
Swap --morph clean for collision or fold to watch the validity gate catch an unphysical return instead of a clean one. The degraded-information ladder and its drift-versus-rung plot come from uv run python scripts/run_phase6.py.