Skip to content

FOLD import/export + open in Origami Simulator - #13

Merged
imagirom merged 18 commits into
mainfrom
worktree-fold-origami-simulator
Jul 20, 2026
Merged

FOLD import/export + open in Origami Simulator#13
imagirom merged 18 commits into
mainfrom
worktree-fold-origami-simulator

Conversation

@imagirom

Copy link
Copy Markdown
Owner

Summary

Adds FOLD (v1.2) crease-pattern import/export and lets a crease pattern be
opened in Origami Simulator from a notebook,
a script, or the online docs.

FOLD format (pleat.io.fold)

  • graph_to_fold / fold_to_graph, save_fold / load_fold — Euclidean crease
    patterns: edges_assignment (M/V/B), edges_foldAngle, faces_vertices.
  • pleat/io.py is promoted to a subpackage pleat/io/{heg,circlepack,fold}.py
    (mechanical split; existing pleat.io.* imports unchanged).
  • FOLD is added alongside .heg, not replacing it: .heg stays the general
    half-edge serializer for the geometry-rich graphs/ corpus (hyperbolic/spherical),
    which FOLD can't represent. Details in the design spec.

Origami Simulator (pleat.origami_simulator)

A distinct feature from the FOLD format, in its own module. Two entry points:

  • origami_simulator(cp) — embeds the simulator inline in the cell under
    Jupyter / Lab / VS Code, or opens the system browser from a plain script.
    new_tab=True forces the browser; height= resizes. Also cp.origami_simulator().
  • origami_simulator_button(cp) — a button that embeds the simulator inline
    on click (no popup, so it works in VS Code; lazy, so it's light in the docs).

Mechanism: a self-contained page embeds OS in an iframe and hands it the FOLD over
OS's postMessage importFold handshake — verified end-to-end in a real browser
(OS imports and folds the pattern). The empty ?model= query suppresses OS's
default demo to avoid a load race.

Docs

New "FOLD & Origami Simulator" section in the Saving and Exporting notebook, with
a live "Open in Origami Simulator" button that works in the built site.

Tests

tests/test_fold.py (FOLD round-trip + validity) and tests/test_origami_simulator.py
(launcher HTML + public surface). Full suite: 626 passed, 1 skipped.

🤖 Generated with Claude Code

imagirom added 15 commits July 18, 2026 00:41
Split pleat/io.py into pleat/io/{heg,circlepack}.py with an __init__ that
re-exports the existing public names unchanged. Mechanical move, no behavior
change; tests/test_io.py passes as-is. Also adds the FOLD/Origami-Simulator
design spec and implementation plan under docs/superpowers/.
pleat/io/fold.py adds graph_to_fold / fold_to_graph / save_fold / load_fold
(Euclidean crease patterns: M/V/B assignments, fold angles, faces), plus a
click-free Origami Simulator launcher: open_in_origami_simulator writes a
self-contained page that embeds OS in an iframe and hands it the FOLD via the
postMessage handshake; origami_simulator_button renders an inline button for
browser Jupyter and the online docs. Re-exported from pleat.io and pleat;
EuclideanPositionHEG gains an open_in_origami_simulator() method.

Verified end-to-end in a real browser: OS imports and folds the pattern.
Add a FOLD & Origami Simulator section to the Saving and Exporting notebook
(save_fold + an origami_simulator_button that folds the pattern live in the
online docs), and flip the intro note that said FOLD was unsupported.
OS async-loads its default demo (waterbomb) at init unless a ?model= query is
present; that load could finish after our importFold and clobber the pattern.
Opening OS with an empty ?model= (as erikdemaine.org's maze does) makes it skip
the default entirely, so our pattern is the only thing loaded.
…cell

Renders OS live in notebook output via an iframe whose srcdoc carries the full
handshake page, so the import is parent-to-child within the iframe (no popup/
opener link for a sandbox to sever). Complements open_in_origami_simulator
(real browser) and origami_simulator_button (static docs).
… display

- Move the enlarge button to the bottom right.
- Replace the 'open in full tab' popup with the Fullscreen API: window.open is
  blocked inside a sandboxed VS Code webview (the button did nothing there),
  whereas requestFullscreen works. Enlarges the already-loaded OS in place.
- origami_simulator_iframe now displays via IPython.display and returns None, so
  it works off the last line of a cell and can be called multiple times to show
  several simulators. Split out _origami_simulator_iframe_html for testing.
…ify API

OS is a distinct feature from the FOLD format, so move the launcher out of
pleat/io/fold.py into its own pleat/origami_simulator.py (it depends on the FOLD
serializer). pleat.io.fold is now pure FOLD import/export.

Simplify the OS surface to two entry points:
- origami_simulator(cp): inline iframe in Jupyter/Lab/VS Code, or opens the
  system browser from a script (new_tab=True forces the browser); displays via a
  raw mimebundle so it works off any line, multiple times per cell, and without
  IPython's HTML-iframe warning.
- origami_simulator_button(cp): a button that embeds the simulator inline on
  click (no popup -> works in VS Code; lazy -> good for the static docs).

Drop origami_simulator_html / open_in_origami_simulator / origami_simulator_iframe
from the public API; rename the graph method to G.origami_simulator(). Update the
Saving_and_Exporting notebook to the new API.
…etricHEG

- G.save(path) now routes by extension: .heg / .fold (data serialization) in
  addition to .svg / .png / no-ext (rendered image, unchanged). Kwargs go to the
  matching backend. Docstring lists the endings up front and in the path arg.
- Move G.origami_simulator() up from EuclideanPositionHEG to GeometricHEG so any
  geometric graph has it (fixes AttributeError on plain GeometricHEG crease
  patterns).
- graph_to_fold raises ValueError on non-Euclidean geometry (hyperbolic/spherical
  can't be represented in FOLD), so .fold export and .origami_simulator() fail
  with a clear message rather than emitting garbage coordinates.
…+fold)

- No-extension G.save(path) now writes path.svg, path.png, path.heg, and
  path.fold (the .fold skipped for non-Euclidean graphs). overwrite defaults to
  True for the data files; other kwargs are render style.
- save_results: the CP and CP_for_origami_simulator panels inherit this (they now
  also get .heg + .fold for reload / interchange), while the folded views and the
  backlit composite render straight to svg+png (they're pictures, not patterns).
- graph_to_fold guards on real 2D positions (rejects complex hyperbolic / 3D
  spherical) instead of the geometry attribute, which from_tiles doesn't set.
- save_fold builds the FOLD dict before opening the file, so a rejected export
  leaves no empty .fold behind.
origami_simulator_button gains a 'title' kwarg for the label. FoldResult.show()
displays a launch button for the (OS-optimised) crease pattern, gated on the new
show_origami_simulator_button flag (which the initial version didn't actually
check).
…d side up

Origami Simulator's importFold treats a face's CCW side as the back (white) and
shows it up; pleat's vertex_iter is CCW, so tessellations loaded white-side-up.
Emit faces clockwise instead (fold_to_graph reverses back, preserving pleat's own
winding on round-trip). Verified in-browser: the coloured side now faces up and
the folded geometry is unchanged -- mountain/valley is driven by edges_foldAngle,
independent of winding (OS's own SVG path reverses faces the same way).
graph_to_dict float()-ed every scalar attribute, but np.isscalar is True for
strings, so a hex colour_key like '#cc2222' raised ValueError. Now strings (and
bytes/bool) pass through as-is. Surfaced by save_results writing the CP as .heg
(the no-extension bundle); before, save_results only rendered images.
fold_to_graph now sets edge color_key from the restored M/V assignment (so a
loaded FOLD renders with mountain/valley colours), and shrink_rotate_pattern uses
the shared color_creases() instead of an inline loop. Drops the now-unused
BORDER / *_COLOR imports in the pipeline.
…ulator

Saving_and_Exporting: use a real shrink-rotate CP as the example, document that
G.save('out') now writes svg+png+heg+fold, list .fold in the intro. index: add a
'Open in origami simulator' cell. Alternating_Flagstones: minor updates.
Process artifacts, not user-facing docs; the code is the deliverable. Archived
outside the repo under .claude/superpowers/ (gitignored).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class FOLD v1.2 import/export for Euclidean crease patterns and introduces an Origami Simulator launcher (inline iframe in notebooks + browser fallback), while refactoring pleat.io into a subpackage and updating docs/tests to cover the new surfaces.

Changes:

  • Add pleat.io.fold for FOLD serialization (graph_to_fold/fold_to_graph) plus .fold load/save.
  • Add pleat.origami_simulator to open/embed Origami Simulator using an importFold postMessage handshake.
  • Refactor pleat/io.py into pleat/io/{heg,circlepack,fold}.py, update G.save(...) dispatching, and extend docs/tests accordingly.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/test_origami_simulator.py Adds tests for launcher HTML generation and public API surface.
tests/test_io.py Adds regression test for .heg round-trip of string and array attributes.
tests/test_fold.py Adds FOLD validity + round-trip tests and bundle save behavior tests.
tests/test_circle_packing.py Updates imports to new pleat.io.circlepack module path.
pleat/shrink_rotate/pipeline.py Replaces manual crease coloring with color_creases(...).
pleat/overlap.py Adds optional Origami Simulator button in results display; adjusts save behavior for folded render outputs.
pleat/origami_simulator.py New module: generates HTML/iframe/button to load crease patterns into Origami Simulator.
pleat/io/heg.py New module: .heg YAML serialization (and fixes attribute scalar handling).
pleat/io/fold.py New module: FOLD v1.2 serialization + .fold file I/O + crease coloring on import.
pleat/io/circlepack.py Extracted CirclePack .p I/O into its own module; fixes relative imports.
pleat/io/init.py Re-exports the public I/O API from the new subpackage modules.
pleat/half.py Extends save() to dispatch by extension and adds origami_simulator() convenience method.
pleat/init.py Imports pleat.origami_simulator module for accessibility without top-level re-exporting functions.
docs/superpowers/specs/2026-07-17-fold-origami-simulator-design.md Adds design spec for FOLD + Origami Simulator integration.
docs/superpowers/plans/2026-07-18-fold-origami-simulator.md Adds detailed implementation plan document.
docs/notebooks/Saving_and_Exporting.ipynb Updates docs to mention .fold and demonstrates new export paths.
docs/notebooks/Alternating_Flagstones.ipynb Adds Origami Simulator buttons and tweaks subdivision example.
docs/index.ipynb Adds a short “open in origami simulator” usage snippet.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pleat/origami_simulator.py
Comment thread pleat/origami_simulator.py Outdated
Comment thread pleat/overlap.py Outdated
Comment thread pleat/io/heg.py Outdated
Comment thread docs/notebooks/Saving_and_Exporting.ipynb Outdated
Comment thread docs/notebooks/Alternating_Flagstones.ipynb Outdated
imagirom added 2 commits July 20, 2026 15:06
… checks

Address PR review feedback:
- origami_simulator: the postMessage listener now only answers messages whose
  origin is origamisimulator.org (browser-set, unforgeable) and posts the FOLD
  back to that origin instead of '*', so another frame can't spoof 'ready' and
  siphon the pattern. Verified in-browser that import still works.
- origami_simulator: write the temp HTML as UTF-8 (it contains '->' etc.), so it
  can't raise UnicodeEncodeError on non-UTF-8 default encodings (Windows).
- Add utils.in_notebook() helper; FoldResult.show() only shows the OS button when
  in a notebook, so show() still works in scripts / minimal installs without
  IPython. origami_simulator reuses the same helper.
- heg.save_graph raises FileExistsError (not assert, which -O strips) like
  save_fold; update the test accordingly.
@imagirom
imagirom merged commit 931426f into main Jul 20, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants