feat(fold): fold scope — flap-typed moving, up to, @fold #67

Merged
toph merged 6 commits from feat/fold-scope into main 2026-07-05 12:11:57 +00:00
Owner

Implements the fold-scope design (ADR 0016): every fold has axis / anchor / scope / direction.

  • moving is now flap-typed: a point (the flap carrying it), a line (usually a multi-match, per ADR 0016), or #(…) incidence constraints. Map folds keep their implied anchor.
  • up to <flap> scopes the fold to a contiguous some-layers range [demaine2007, §14] — the outer prefix of the stack over the crease region from anchor through target, computed as a closure in Fold_state.select_scope. Non-moving faces stay whole and uncut.
  • @fold <crease> moving … [up to …] folds along existing material; new segments join the same bundle (ADR 0014). Bare bind + @fold … up to composes into crease-all-fold-some: non-moving layers keep their flat U mark.

One amendment to the design surfaced during verification: the outer-prefix rule alone admits folds that tear the paper — a moving flap still hinged off-axis to a stationary one. Renders of the first drafted examples exposed it (bowtie faces in the FOLD output). The fold now also enforces hinge-closure — every mover↔stayer crease must lie on the fold axis — and errors with a tear message; design doc and spec §4.6 carry the rule now. The all-layers default cannot tear, so existing behavior is untouched.

Spec §4.6 is rewritten around the four-ingredient model, grammar and Landed list bumped to v0.18-dev. All pre-existing goldens byte-identical.

Examples

Each example: the scoped program on the left, the same program with the up to clause dropped (all-layers status quo) on the right. Crease patterns show which layers got creased; folded views show the resulting stack outline.

up to the anchor — fold only the top flap (fold-top-flap.bel)

paper square
--l = through .a .d
--h = @map .d onto .a       ; halve — hinge at the top
.p = cross --l --h
@map .d onto .p up to .d    ; fold ONLY the flap carrying .d
up to .d (scoped) without up to (all-layers)
crease pattern scoped CP all-layers CP
folded scoped folded all-layers folded

The scoped fold creases only the top layer's paper (material y=3/4); all-layers also scores the bottom layer (y=1/4, third CP line). Folded: the scoped bottom layer stays put, keeping the full half-height outline; all-layers tucks everything up into a quarter-height strip. The fold axis runs parallel to the --h hinge — an axis crossing the hinge of a partially-moving flap is the new tear error.

up to a range — fold the top two of four (fold-top-two.bel)

paper square
--l = through .a .d
--bot = through .a .b
--v = @map .b onto .a
--h = @map .d onto .a       ; four layers, corners stacked at the origin
.p = cross --l --h
.q = cross --v --bot
@through .p .q moving .d up to .c   ; anchor .d (top), through .c (second)
moving .d up to .c (top two) without up to (all four)
crease pattern scoped CP all-layers CP
folded scoped folded all-layers folded

CP: roof (two quadrants creased) vs diamond (all four). Folded: the scoped fold keeps the full quarter outline — the two lower layers still fill the corner under the moved triangles; all-layers folds the corner away entirely. select_scope walks the stack over the crease region from the anchor flap (.d, top) through the target (.c, second layer) inclusive. A line-construction fold like @through has no implied anchor, so moving is required.

Crease all layers, @fold some (crease-all-fold-some.bel)

paper square
--l = through .a .d
--h = @map .d onto .a
.p = cross --l --h
--m = map .a onto .p        ; bare bind: scores BOTH layers flat (two U segments)
@fold --m moving .d up to .d  ; fold only the top flap along it
up to .d (scoped) without up to (all-layers)
crease pattern scoped CP all-layers CP
folded scoped folded all-layers folded

This was the motivating composition. The two crease patterns are geometrically identical--m scored both layers either way, which is the point: the difference is the assignment. Scoped, only the top flap's segment upgrades to a live fold; the bottom layer's segment stays a flat U mark (U count 1 vs 0 in the FOLD output). The folded views make it visible: scoped keeps the bottom layer down (full outline), all-layers folds both up (strip).

Precrease → @fold (fold-along.bel)

paper square
--d = map .b onto .a   ; precrease
@fold --d moving .b    ; fold along the material crease itself
crease pattern folded
@fold --d CP folded

Replaces the axiom re-statement idiom (precrease-fold.bel restates @map .b onto .a): @fold references the material crease, the U upgrades to V (#27), and any new segments cut by the fold would join --d's bundle (ADR 0014). Byte-equal faces/assignments to the re-statement version, minus the duplicated construction.

Implements the [fold-scope design](https://git.toph.so/toph/beloch/src/branch/main/docs/superpowers/specs/2026-07-05-fold-scope-design.md) ([ADR 0016](https://git.toph.so/toph/beloch/src/branch/main/decisions/0016-typed-operands-bundle-values-singleton-slots.md)): every fold has axis / anchor / scope / direction. - `moving` is now flap-typed: a point (the flap carrying it), a line (usually a multi-match, per ADR 0016), or `#(…)` incidence constraints. Map folds keep their implied anchor. - `up to <flap>` scopes the fold to a contiguous some-layers range [demaine2007, §14] — the outer prefix of the stack over the crease region from anchor through target, computed as a closure in `Fold_state.select_scope`. Non-moving faces stay whole and uncut. - `@fold <crease> moving … [up to …]` folds along existing material; new segments join the same bundle (ADR 0014). Bare bind + `@fold … up to` composes into crease-all-fold-some: non-moving layers keep their flat `U` mark. One amendment to the design surfaced during verification: the outer-prefix rule alone admits folds that *tear* the paper — a moving flap still hinged off-axis to a stationary one. Renders of the first drafted examples exposed it (bowtie faces in the FOLD output). The fold now also enforces hinge-closure — every mover↔stayer crease must lie on the fold axis — and errors with a tear message; design doc and spec §4.6 carry the rule now. The all-layers default cannot tear, so existing behavior is untouched. Spec §4.6 is rewritten around the four-ingredient model, grammar and Landed list bumped to v0.18-dev. All pre-existing goldens byte-identical. ## Examples Each example: the scoped program on the left, the same program with the `up to` clause dropped (all-layers status quo) on the right. Crease patterns show *which layers* got creased; folded views show the resulting stack outline. ### `up to` the anchor — fold only the top flap ([`fold-top-flap.bel`](https://git.toph.so/toph/beloch/src/branch/feat/fold-scope/examples/fold-top-flap.bel)) ``` paper square --l = through .a .d --h = @map .d onto .a ; halve — hinge at the top .p = cross --l --h @map .d onto .p up to .d ; fold ONLY the flap carrying .d ``` | | `up to .d` (scoped) | without `up to` (all-layers) | | --- | --- | --- | | crease pattern | ![scoped CP](https://git.toph.so/attachments/3f1f4802-7777-4df3-a9c2-bde536b4508e) | ![all-layers CP](https://git.toph.so/attachments/470f4ede-ee1b-4163-815c-11c586d311a8) | | folded | ![scoped folded](https://git.toph.so/attachments/2b7b6b74-e4fc-4e4a-9522-7ae6ce9c6db9) | ![all-layers folded](https://git.toph.so/attachments/4f128196-691b-4656-a0f6-00478363f009) | The scoped fold creases only the top layer's paper (material `y=3/4`); all-layers also scores the bottom layer (`y=1/4`, third CP line). Folded: the scoped bottom layer stays put, keeping the full half-height outline; all-layers tucks everything up into a quarter-height strip. The fold axis runs parallel to the `--h` hinge — an axis *crossing* the hinge of a partially-moving flap is the new tear error. ### `up to` a range — fold the top two of four ([`fold-top-two.bel`](https://git.toph.so/toph/beloch/src/branch/feat/fold-scope/examples/fold-top-two.bel)) ``` paper square --l = through .a .d --bot = through .a .b --v = @map .b onto .a --h = @map .d onto .a ; four layers, corners stacked at the origin .p = cross --l --h .q = cross --v --bot @through .p .q moving .d up to .c ; anchor .d (top), through .c (second) ``` | | `moving .d up to .c` (top two) | without `up to` (all four) | | --- | --- | --- | | crease pattern | ![scoped CP](https://git.toph.so/attachments/343df8a0-de3a-468f-ab74-7255c0580612) | ![all-layers CP](https://git.toph.so/attachments/0acb4958-0e6a-40fe-9949-a67741c75384) | | folded | ![scoped folded](https://git.toph.so/attachments/7dd887d0-b05b-440f-9440-c24fabf01be7) | ![all-layers folded](https://git.toph.so/attachments/bb1359ca-86a2-4b50-a36a-5fd563546a14) | CP: roof (two quadrants creased) vs diamond (all four). Folded: the scoped fold keeps the full quarter outline — the two lower layers still fill the corner under the moved triangles; all-layers folds the corner away entirely. `select_scope` walks the stack over the crease region from the anchor flap (`.d`, top) through the target (`.c`, second layer) inclusive. A line-construction fold like `@through` has no implied anchor, so `moving` is required. ### Crease all layers, `@fold` some ([`crease-all-fold-some.bel`](https://git.toph.so/toph/beloch/src/branch/feat/fold-scope/examples/crease-all-fold-some.bel)) ``` paper square --l = through .a .d --h = @map .d onto .a .p = cross --l --h --m = map .a onto .p ; bare bind: scores BOTH layers flat (two U segments) @fold --m moving .d up to .d ; fold only the top flap along it ``` | | `up to .d` (scoped) | without `up to` (all-layers) | | --- | --- | --- | | crease pattern | ![scoped CP](https://git.toph.so/attachments/ec7a053b-f6f7-4a3d-abfd-4eb8fce21e07) | ![all-layers CP](https://git.toph.so/attachments/2d124f4a-05a0-4677-b91d-ea2bc6567a0f) | | folded | ![scoped folded](https://git.toph.so/attachments/48d5308e-f715-47a9-83b3-820636ccc8e7) | ![all-layers folded](https://git.toph.so/attachments/f9eeabfb-ea5a-4615-a5e2-7c2990c76c06) | This was the motivating composition. The two crease patterns are geometrically *identical* — `--m` scored both layers either way, which is the point: the difference is the assignment. Scoped, only the top flap's segment upgrades to a live fold; the bottom layer's segment stays a flat `U` mark (`U` count 1 vs 0 in the FOLD output). The folded views make it visible: scoped keeps the bottom layer down (full outline), all-layers folds both up (strip). ### Precrease → `@fold` ([`fold-along.bel`](https://git.toph.so/toph/beloch/src/branch/feat/fold-scope/examples/fold-along.bel)) ``` paper square --d = map .b onto .a ; precrease @fold --d moving .b ; fold along the material crease itself ``` | | crease pattern | folded | | --- | --- | --- | | `@fold --d` | ![CP](https://git.toph.so/attachments/574c57f6-47e3-4f2e-8768-d50c4b2c198b) | ![folded](https://git.toph.so/attachments/925d36a2-e8f2-4d0f-a402-ab688bacecc8) | Replaces the axiom re-statement idiom (`precrease-fold.bel` restates `@map .b onto .a`): `@fold` references the material crease, the `U` upgrades to `V` (#27), and any new segments cut by the fold would join `--d`'s bundle (ADR 0014). Byte-equal faces/assignments to the re-statement version, minus the duplicated construction.
toph merged commit bc8862a86d into main 2026-07-05 12:11:57 +00:00
toph deleted branch feat/fold-scope 2026-07-05 12:11:58 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
toph/beloch!67
No description provided.