Build log · a living document
Live at origami.asasdsdas.com · status: waterbomb base corrected (a composition) — layer-aware engine underway
The goal. A camera-calibration target you can print, fold, and hold: one 200 mm square, printed with a computed pattern, that folds along the classic waterbomb (balloon) base into a 50 mm cube — each of its six faces a ChArUco board. One sheet, single-sided, no glue. Beside it, a self-contained 3-D simulator that not only shows the fold but proves it is physically real: paper that never stretches, tears, or passes through itself.
A flat ChArUco board only calibrates cameras that see it face-on. A cube shows three faces to almost any viewpoint at once — far better for multi-camera rigs. And the waterbomb base already folds a square into a cube-shaped pocket, with exact geometry: the cube edge is the sheet's side over four (200 mm → 50 mm).
(a) compute which patch of the printed pattern lands on which cube face after folding, and (b) prove the fold that gets it there — a rigid-origami motion with no tearing and no stretching. Part (b) turned out to be the deep part.
Get a printable pattern and an animated fold, end to end.
Four pieces in one weekend: a pattern generator (ChArUco boards via OpenCV/ArUco), a fold engine (2-D affine transforms, facet splitting, layer stacks), a cube solver (randomized backtracking over the crease-hinge graph), and a three.js UI. First result: a sheet you could print and an animation that folded it.
Solver landing near 4.5 / 6 faces covered.
Per-step fold data was keyed by facet id and orphaned every time a crease split a facet, so later steps animated garbage. Rebuilt the per-facet animation arrays to survive splits, and chose each face's ply-stacking order to maximize printed coverage.
Side faces went from ~75% to 100% printed coverage.
Rewrote inflation as hinge-tree kinematics — but discovered the solver could return configurations with torn creases: the repair pass optimized ink coverage without ever checking that shared edges stayed joined. A torn layout had even been cached as "best."
Added a tear penalty and rejected torn trials. First real signal that looking right ≠ being physical.
The demand that reshaped the project: the paper is a rigid, inextensible sheet — no stretching, no tearing, no vertices splitting apart. Prove it, don't assume it.
The steering demand. We welded the mesh into rigid panels sharing material vertices (so tearing is structurally impossible to hide) and added three honest metrics measured at zero thickness: tear (do shared points agree?), deform (do panels keep their shape?), and motion (did the fold actually move?). Old code was tearing 1.04 units mid-collapse.
Also this stretch: an accordion-correct stacking rule, and a teach mode (GPU colour-ID picking) so the ground truth of which facet faces out could be marked by hand.
A sharp catch: facet frames had drifted out of the rotation group (SO(3)) — "a meaningless composition of vectors." An affine two-edge reconstruction was accumulating up to 16% error by the inflation step. Replaced it with an orthogonal Procrustes fit, forcing every placement to be a true rigid motion.
Orthonormality error dropped to 0.0000 at every step.
A hypothesis worth testing: some folds must transiently move away from their destination, forced by loop closure. We built a rigid-origami mechanism solver in fold-angle space (Newton projection onto the closure manifold) to look for it.
Confirmed, and stronger than predicted: the book-fold midline reopens ~halfway through the collapse before everything re-closes. The backtracks are real, discovered from the geometry rather than scripted.
New rule: every fold must be a named, immutable origami operation — valley, mountain, pleat, reverse, squash — never an ad-hoc reflection. Built foldops.js (the vocabulary) and a Fold Lab that isolates each fold on minimal paper and grades it with the metrics.
The lab immediately did its job: it flagged the inside-reverse fold as broken — it crumpled (deform 0.04). The base collapses were only ever going to be as correct as their parts.
A new tactic: stop patching a 2-D reflection engine and represent the paper the way a GPU would.
The pivot. Each facet is a triangle carrying an immutable flat shape plus an OpenGL-style model matrix; folds are world-space rotations left-multiplied onto the movers; any crease can bisect a triangle into new matrix-carrying triangles. Two properties fall out for free: rigidity is structural (a rigid matrix can't stretch) and welding is automatic (points on a hinge axis can't move, so a shared edge can't tear).
Shipped as matfold.js + the Matrix Fold Lab — 26/26 proofs green.
Ported the whole canonical vocabulary onto the matrix engine (Canonical Fold Lab). Simple folds went clean to machine precision. The keystone — the inside-reverse that used to crumple — is now modelled as a degree-4 vertex mechanism where the spine inverts through the vertex (tip = −base), traced by continuation seeded off the flat-fold singularity.
Inside- and outside-reverse rigid & welded to ~1e-14, the full lab suite green. (A "squash" listed here at the time turned out to be a mislabelled reverse — corrected Jul 22, below.)
The heart of the whole project, and it fought back. Three wrong turns taught three things:
The fix: don't start at the flat state. Seed a folded guess, project it onto the closure manifold (where the Jacobian is healthy), and continue back toward flat. A random-restart search found the branch; it collapses the square straight down to the flat right triangle.
Rigid & welded end to end — but the target was wrong. This single-vertex collapse is the vertex's BISTABLE mode (a shallow cone), not the flat base (see Jul 22). The solver was sound; the shape it chased wasn't the waterbomb.
Deployed the static site to the edge (Caddy on vps01, auto-TLS) and wired a consistent nav across every page so the simulator, both labs, the print sheet — and this log — are one traversable site.
You're reading it at origami.asasdsdas.com.
With the reverse fold and the waterbomb triangle both cracked, the plan was to reuse the same machinery for the next coupled folds — the preliminary base, the squash, the petal. Instead it found an honest boundary. Enumerating every mountain/valley pattern of the degree-8 vertex, it rigid-collapses only to the triangle: the "square" preliminary base is not a single-vertex rigid folding at all. And a squash needs the two plies of a flap to move independently — something a zero-thickness sheet simply can't express. A search of the degree-4 vertex confirmed it: only the trivial fold and the reverse, no distinct squash.
So a "squash" that had been quietly mislabelled (it was a reverse fold) is now honestly marked planned — and the real next capability is finally clear: an engine that tracks layers.
A sharp, correct call: the "waterbomb base" in the lab looked wrong — flaps splayed out, never quite flat. Two things were off. The single-vertex collapse I'd been so pleased with reaches the vertex's bistable cone, not the pressed-flat base; and the base isn't a single-vertex fold at all. The real waterbomb base is a book fold plus two reverse-folds along the diagonals — a composition, exactly what the aggregate simulator already does.
Rebuilt it that way on the matrix engine, which meant teaching foldAbout to fold a chosen subset of plies about an explicit hinge — the first real piece of the layer-aware engine. It now reaches the correct flat isosceles triangle: base = square side, height = half, four plies per half.
Rigid & welded throughout, tear ~1e-15. The lesson worth keeping: a clean metric (my "the silhouette is a triangle" check) can still measure the wrong thing. Look at the actual shape.
The matrix engine is proven and the canonical vocabulary runs on it cleanly: valley, mountain, pre-crease, pleat, crimp, and both reverse folds as single-vertex mechanisms; the waterbomb base as a composition (book fold + two reverse-folds) reaching its correct flat triangle. We've mapped where the single-vertex method stops, and taken the first step of the layer-aware engine — folding a chosen subset of plies about an explicit hinge.