FOLD import/export + open in Origami Simulator - #13
Merged
Conversation
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).
There was a problem hiding this comment.
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.foldfor FOLD serialization (graph_to_fold/fold_to_graph) plus.foldload/save. - Add
pleat.origami_simulatorto open/embed Origami Simulator using animportFoldpostMessage handshake. - Refactor
pleat/io.pyintopleat/io/{heg,circlepack,fold}.py, updateG.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.
… 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 creasepatterns:
edges_assignment(M/V/B),edges_foldAngle,faces_vertices.pleat/io.pyis promoted to a subpackagepleat/io/{heg,circlepack,fold}.py(mechanical split; existing
pleat.io.*imports unchanged)..heg, not replacing it:.hegstays the generalhalf-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 underJupyter / Lab / VS Code, or opens the system browser from a plain script.
new_tab=Trueforces the browser;height=resizes. Alsocp.origami_simulator().origami_simulator_button(cp)— a button that embeds the simulator inlineon 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
postMessageimportFoldhandshake — verified end-to-end in a real browser(OS imports and folds the pattern). The empty
?model=query suppresses OS'sdefault 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) andtests/test_origami_simulator.py(launcher HTML + public surface). Full suite: 626 passed, 1 skipped.
🤖 Generated with Claude Code