Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions research/penrose/10-standalone-libraries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Penrose — Standalone libraries: survey and structure

Research note, 2026-07-04. Idea parked, not started. The question: extract the
engine under `src/app/x/penrose/explore/lib` into standalone libraries for
TypeScript, Python, and Go. This note records the ecosystem survey and the
structure we would use, so the decision is already made when we pick it up.

## What exists today (surveyed 2026-07-04)

Every implementation we found answers "give me polygons to draw." None treats
the tiling as an addressable mathematical object.

**TypeScript / JavaScript.** No serious published library.

- [patterncollider](https://github.com/aatishb/patterncollider) — the best of
them, a real de Bruijn multigrid, but a Vue app, not a package.
- Demo repos: [apaleyes/penrose-tiling](https://github.com/apaleyes/penrose-tiling)
(triangle subdivision), [guinetik/penrose-js](https://github.com/guinetik/penrose-js),
[rictorlome/penrose](https://github.com/rictorlome/penrose) (p5.js),
[penrose-ii](https://fanyangxyz.github.io/penrose-ii/) (pentagrid demo).
- Bare `penrose` on npm is the CMU diagramming language
([@penrose/core](https://www.npmjs.com/package/@penrose/core)); a tiling
package must use a scope or another name.

**Python.** The most mature ecosystem, still generation-only.

- [pynrose](https://pypi.org/project/pynrose/) — published package, de Bruijn
method, P3, SVG out. Closest prior art; no addressing.
- [xnx/penrose](https://github.com/xnx/penrose) — subdivision SVG generator.
- [joshcol9232/tiling](https://github.com/joshcol9232/tiling) (`dualgrid`) —
de Bruijn in arbitrary dimensions, STL export.
- [pywonderland](https://github.com/neozhaoliang/pywonderland) — reference
implementations, educational gallery, not a library.
- Adjacent: [gglouser/cut-and-project-tiling](https://github.com/gglouser/cut-and-project-tiling)
(Rust/WASM, arbitrary cutting planes).

**Go.** Green field. Nothing library-shaped found; toys and an esolang
([Painrose](https://github.com/mousetail/Painrose)).

## The gap our engine fills

1. **Exact ℤ⁵ addressing.** Tile identity `[n; j, k]`, stable coordinates, a
wire codec. Nobody ships coordinates as API.
2. **O(visible) viewport enumeration.** The fixed-gamma pentagrid enumerator,
verified key-for-key against the cut-and-project oracle, so unbounded
panning is a library feature.
3. **The matching rule as computed, tested data.** Arc decoration with
shared-edge continuity tests; unique patch decoration by propagation.
4. **The substitution ↔ pentagrid bridge.** The fold (D'Andrea Thm 5.16, see
`09-the-fold.md`) as working code. Not available anywhere as a library.

## Structure, decided in advance

One monorepo. **Go gets the repo root**, because Go is the only ecosystem
where identity = repository layout (a module in a subdirectory carries a
`go/vX.Y.Z` tag prefix forever; npm and PyPI publish from anywhere).

```
github.com/funcimp/penrose (or n2p5/penrose; org undecided)
├── go.mod module github.com/funcimp/penrose
├── *.go the Go implementation
├── spec/ spec.md + golden test vectors (JSON)
├── ts/ npm: @funcimp/penrose (scope sidesteps the CMU collision)
└── python/ PyPI: needs a distinct name (pynrose is taken)
```

**The spec vectors are the real product of the monorepo.** Golden JSON
generated from the tested TS engine: viewport → expected face keys, codec
round-trips, arc crossing points, oracle patches. Each port's test suite
consumes the same files, so "port to Python" becomes make-the-vectors-pass,
not re-derive-the-math. Version the vectors; lockstep the packages' minor
versions to the vector version so parity is checkable.

**Org and import-path stability.** If the repo ever moves, Go consumers break.
Either pick the org to keep (funcimp leans right: the research and citations
live here), or decouple with a vanity import path (`func.lol/penrose` via a
`go-import` meta tag served from the site) at the cost of a little
infrastructure.

**API surface (instinct, to be designed twice).** Four seams and nothing else
exported: enumerate (viewport → faces), address (face ↔ `[n; j, k]` ↔ wire
codec), decorate (matching-rule arcs), deflate/inflate. The `explore/lib`
modules are already pure functions with zero runtime dependencies, so the TS
extraction is mostly moving files and writing the README.
Loading