procedural-paper 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +205 -2
- package/THIRD_PARTY_NOTICES.md +51 -0
- package/dist/cards.d.ts +42 -0
- package/dist/color.d.ts +5 -0
- package/dist/composition/assets.d.ts +7 -0
- package/dist/composition/cords.d.ts +43 -0
- package/dist/composition/filters.d.ts +1 -0
- package/dist/composition/fit.d.ts +58 -0
- package/dist/composition/index.d.ts +27 -0
- package/dist/composition/index.js +1247 -0
- package/dist/composition/index.js.map +1 -0
- package/dist/composition/model.d.ts +72 -0
- package/dist/composition/motion-spec.d.ts +98 -0
- package/dist/composition/motion.d.ts +36 -0
- package/dist/composition/motion.js +291 -0
- package/dist/composition/motion.js.map +1 -0
- package/dist/composition/nodes.d.ts +7 -0
- package/dist/composition/pen.d.ts +6 -0
- package/dist/composition/photos.d.ts +41 -0
- package/dist/composition/props.d.ts +32 -0
- package/dist/composition/react.d.ts +10 -0
- package/dist/composition/react.js +22 -0
- package/dist/composition/react.js.map +1 -0
- package/dist/composition/renderer.d.ts +77 -0
- package/dist/composition/rng.d.ts +11 -0
- package/dist/composition/scene.d.ts +77 -0
- package/dist/composition/unrotate.d.ts +3 -0
- package/dist/composition.css +78 -0
- package/dist/cpu/compose.d.ts +7 -0
- package/dist/cpu/field.d.ts +10 -0
- package/dist/cpu/noise.d.ts +7 -0
- package/dist/cpu/overlays.d.ts +12 -0
- package/dist/cpu/renderer.d.ts +16 -0
- package/dist/cpu/strands.d.ts +33 -0
- package/dist/dom.d.ts +110 -0
- package/dist/dom.js +65 -0
- package/dist/dom.js.map +1 -0
- package/dist/export.d.ts +20 -0
- package/dist/gl/renderer.d.ts +61 -0
- package/dist/gl/shaders.d.ts +10 -0
- package/dist/gl/sheet-shader.d.ts +1 -0
- package/dist/hash.d.ts +12 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +2415 -0
- package/dist/index.js.map +1 -0
- package/dist/params.d.ts +169 -0
- package/dist/presets.d.ts +11 -0
- package/dist/react.d.ts +20 -0
- package/dist/react.js +50 -0
- package/dist/react.js.map +1 -0
- package/dist/resolve.d.ts +125 -0
- package/dist/sheet.d.ts +167 -0
- package/dist/stocks.d.ts +12 -0
- package/dist/styles.css +13 -0
- package/licenses/Apache-2.0.txt +201 -0
- package/package.json +116 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maks Surguy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,206 @@
|
|
|
1
|
-
#
|
|
1
|
+
# procedural-paper API
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
MIT © 2026 Maks Surguy. [Source](https://github.com/msurguy/procedural-paper) · [Demo](https://msurguy.github.io/procedural-paper/)
|
|
4
|
+
|
|
5
|
+
Seamless procedural paper textures and 25 paper UI components, generated from
|
|
6
|
+
code without source images.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install procedural-paper
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Entry points
|
|
13
|
+
|
|
14
|
+
| Import | Purpose |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `procedural-paper` | Dependency-free texture/sheet rendering, stock recipes, card presets |
|
|
17
|
+
| `procedural-paper/dom` | `createPaperSheet`, `createPaperCard`, shared `renderPaperLayers` compositor |
|
|
18
|
+
| `procedural-paper/react` | `PaperSheet`, `PaperCard`; requires React 18.2+ or 19 |
|
|
19
|
+
| `procedural-paper/styles.css` | Required, namespaced component styling |
|
|
20
|
+
|
|
21
|
+
Build output is ESM with TypeScript declarations. CPU texture generation works in
|
|
22
|
+
Node without a DOM. DOM helpers must be called in the browser. React components
|
|
23
|
+
are SSR-safe and hydrate a plain paper container before rendering textures.
|
|
24
|
+
|
|
25
|
+
## Textures
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { generatePaper, encodePNG } from 'procedural-paper';
|
|
29
|
+
const paper = await generatePaper({
|
|
30
|
+
preset: 'watercolor-cold-press', width: 1024, height: 768, seed: 7,
|
|
31
|
+
outputs: ['color', 'albedo', 'overlay', 'height', 'normal'],
|
|
32
|
+
params: { color: { base: '#f4efe4' } },
|
|
33
|
+
backend: 'auto', // or 'webgl2' / 'cpu'
|
|
34
|
+
});
|
|
35
|
+
const png = await encodePNG(paper.maps.color!);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`presets`, `paramSchema`, `buildParams`, `mergeParams`, `getParam` and `setParam`
|
|
39
|
+
provide discovery and editing. `toBlob` and `toCanvas` provide browser outputs.
|
|
40
|
+
Reuse `PaperRenderer` for repeated work and call `dispose()` when finished.
|
|
41
|
+
`preview()` and `renderSheet()` require WebGL2; CPU rendering covers repeating maps.
|
|
42
|
+
|
|
43
|
+
## Vanilla components
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { createPaperCard, createPaperSheet } from 'procedural-paper/dom';
|
|
47
|
+
import 'procedural-paper/styles.css';
|
|
48
|
+
const card = createPaperCard({ preset: 'polaroid', seed: 4 });
|
|
49
|
+
const photo = document.createElement('img');
|
|
50
|
+
photo.src = '/my-photo.jpg'; photo.alt = 'A mountain lake';
|
|
51
|
+
card.slots.photo.append(photo);
|
|
52
|
+
card.slots.content.textContent = 'Summer';
|
|
53
|
+
document.body.append(card.element);
|
|
54
|
+
card.update({ seed: 5 }); // preserves content nodes and listeners
|
|
55
|
+
// card.dispose(); // removes element and releases its owned renderer
|
|
56
|
+
|
|
57
|
+
const sheet = createPaperSheet({ width: 240, height: 320, preset: 'kraft',
|
|
58
|
+
sheet: { shape: { edges: { left: 'torn' } } } });
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Card options: `preset`, optional `width`, `height`, `seed`, paper `params`, sheet
|
|
62
|
+
`sheet` overrides, and optional borrowed `renderer`. Sheet options require
|
|
63
|
+
`width` and `height`; their `preset` is a texture preset instead of a card ID.
|
|
64
|
+
Dimensions are explicit CSS pixels, not automatically measured from children.
|
|
65
|
+
Pixel ratio defaults to device pixel ratio capped at 2; override with `sheet.pixelRatio`.
|
|
66
|
+
Card geometry and content regions scale with width/height. Hole radii and edge
|
|
67
|
+
texture detail remain in CSS pixels. `params` merges into the stock recipe;
|
|
68
|
+
`sheet` replaces top-level preset sheet groups. `update` shallow-merges options;
|
|
69
|
+
pass `undefined` to clear an optional override. `sheet.seed` overrides the outline seed.
|
|
70
|
+
|
|
71
|
+
`slots` contains live DOM containers for all regions. Unused regions are empty;
|
|
72
|
+
when switching preset, former slots are hidden and retained for reuse. Caller
|
|
73
|
+
content is never interpreted as HTML strings. Text remains selectable and controls
|
|
74
|
+
remain interactive; canvases and shading do not intercept pointer events.
|
|
75
|
+
Consumers own their content's event handlers and any object URLs they create.
|
|
76
|
+
A borrowed renderer is never disposed by a component.
|
|
77
|
+
|
|
78
|
+
If the GPU is unavailable, components retain readable content on a solid paper
|
|
79
|
+
background. Rendering failures emit `papererror` on the vanilla element. Invalid
|
|
80
|
+
preset IDs or non-positive/non-finite dimensions throw before rendering.
|
|
81
|
+
|
|
82
|
+
## React
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import { PaperCard, PaperSheet } from 'procedural-paper/react';
|
|
86
|
+
import 'procedural-paper/styles.css';
|
|
87
|
+
export function Example() {
|
|
88
|
+
return <PaperCard preset="polaroid" seed={4}
|
|
89
|
+
slots={{ photo: <img src="/my-photo.jpg" alt="A mountain lake" /> }}>
|
|
90
|
+
Summer
|
|
91
|
+
</PaperCard>;
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Props mirror vanilla options plus `children`, `slots`, `className`, `style`, and
|
|
96
|
+
`aria-label`. `children` fills `content`; `slots` supplies named React regions.
|
|
97
|
+
React owns the children throughout updates. Memoize parameter/sheet objects and
|
|
98
|
+
borrow a renderer when rendering many components. Effects clean up on unmount
|
|
99
|
+
and support Strict Mode. The wrapper is marked as a client entry point for React
|
|
100
|
+
server-component frameworks; CSS should be imported at the application's entry.
|
|
101
|
+
|
|
102
|
+
## Card presets
|
|
103
|
+
|
|
104
|
+
`cardPresetIds` and `cardPresets` enumerate all 25 styles. `getCardPreset(id)`
|
|
105
|
+
returns an independent copy. `resolveCard(options)` returns paper parameters,
|
|
106
|
+
sheet settings and named content regions without accessing the DOM.
|
|
107
|
+
|
|
108
|
+
| IDs | Regions in addition to `content` |
|
|
109
|
+
| --- | --- |
|
|
110
|
+
| sticky, notebook, napkin, scrap, graph, biz, quote, tag, receipt | none |
|
|
111
|
+
| index | title |
|
|
112
|
+
| ticket | stub |
|
|
113
|
+
| memo | signature |
|
|
114
|
+
| watercolor, clipping, stamp, tracing, tapelabel, print | none |
|
|
115
|
+
| polaroid, photonote, vintage, tear | photo |
|
|
116
|
+
| album | photo1, photo2, caption2 |
|
|
117
|
+
| booth | photo1, photo2, photo3, photo4 |
|
|
118
|
+
| film | photo1, photo2, photo3 |
|
|
119
|
+
|
|
120
|
+
`paperStocks` and `stockParams(key)` expose the desk's paper recipes. Presets
|
|
121
|
+
include outlines, relief, photo regions and ruled/grid decorations. Supply your
|
|
122
|
+
own content and images. The library doesn't bundle photos, fonts, sample prose,
|
|
123
|
+
photo grading, text fitting, attachments, physics, or handwriting animations.
|
|
124
|
+
|
|
125
|
+
## Low-level sheets
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { PaperRenderer, buildParams } from 'procedural-paper';
|
|
129
|
+
const renderer = new PaperRenderer();
|
|
130
|
+
const result = renderer.renderSheet(buildParams('kraft'), {
|
|
131
|
+
width: 240, height: 320, pixelRatio: 2,
|
|
132
|
+
shape: { edges: { left: 'torn' } },
|
|
133
|
+
}, ['base', 'shade', 'shadow']);
|
|
134
|
+
renderer.dispose();
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Place live content between base and shade; shade uses `mix-blend-mode: hard-light`.
|
|
138
|
+
`renderPaperLayers(renderer, params, sheet)` performs the browser canvas conversion
|
|
139
|
+
and returns canvases plus mask CSS. Its output includes the transparent margin;
|
|
140
|
+
the helper creates no object URLs. This is also how the desk integrates its own
|
|
141
|
+
interaction layers with the library.
|
|
142
|
+
|
|
143
|
+
## Export an exact desk component
|
|
144
|
+
|
|
145
|
+
Select a rendered card in the **Desk** tab, then choose **Export React** or
|
|
146
|
+
**Export Vanilla**. The dialog previews that card independently, including its
|
|
147
|
+
paper, editable text, photographs, tape, pins, cords, shadows and content motion.
|
|
148
|
+
Choose a file to copy, or download the ZIP to get the complete example.
|
|
149
|
+
|
|
150
|
+
The ZIP contains both framework examples, `composition.json`, a static asset
|
|
151
|
+
resolver, selected local photographs and fonts, optional pen data, credits and
|
|
152
|
+
licenses, and a small Vite application. Run `npm install` then `npm run dev` in
|
|
153
|
+
that folder. Assets work without Google
|
|
154
|
+
Fonts or other external services; installing dependencies still requires an
|
|
155
|
+
available package source.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import { createPaperComposition } from 'procedural-paper/composition';
|
|
159
|
+
import 'procedural-paper/composition.css';
|
|
160
|
+
import { resolveAsset } from './assets';
|
|
161
|
+
import data from './composition.json';
|
|
162
|
+
import type { PaperComposition } from 'procedural-paper/composition';
|
|
163
|
+
|
|
164
|
+
const paper = createPaperComposition({
|
|
165
|
+
composition: data as unknown as PaperComposition,
|
|
166
|
+
resolveAsset,
|
|
167
|
+
fit: 'contain',
|
|
168
|
+
});
|
|
169
|
+
document.querySelector('#app')!.append(paper.element);
|
|
170
|
+
await paper.ready;
|
|
171
|
+
// paper.play(), paper.pause(), paper.replay(), paper.seek(0.5)
|
|
172
|
+
// paper.dispose() when removing the component.
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
React uses `PaperComposition` from `procedural-paper/composition/react`, with the
|
|
176
|
+
same options and an optional ref exposing playback controls. It cleans up on
|
|
177
|
+
unmount and reports asynchronous failures through `onError`. `onReady` signals
|
|
178
|
+
completion. The vanilla handle supports `update(options)` and `ready`.
|
|
179
|
+
|
|
180
|
+
Composition entry points are optional and do not add their renderers or motion
|
|
181
|
+
libraries to the base texture API. Install `@chenglou/pretext@0.0.9` for composition
|
|
182
|
+
text fitting, `brushmark@0.1.2`, and `tegaki@1.1.0` when bundling the composition
|
|
183
|
+
entry. Motion and pen engines load lazily at runtime; bundlers still need these
|
|
184
|
+
peers available to resolve their chunks. React itself remains an optional peer dependency.
|
|
185
|
+
|
|
186
|
+
The version 1 composition model records local dimensions, material parameters,
|
|
187
|
+
lighting, texture scale, rotation, stable content node IDs, fitting inputs,
|
|
188
|
+
attachment geometry, photo grading, asset metadata and resolved motion steps.
|
|
189
|
+
The desk and exported components share the same rendering algorithms. Texture
|
|
190
|
+
images are generated; desk background scans are never part of a component export.
|
|
191
|
+
The runtime renders at fixed dimensions and can scale the entire result to its
|
|
192
|
+
container with `fit: 'contain'`.
|
|
193
|
+
|
|
194
|
+
Use `setCompositionText(composition, nodeId, newText)` to create an edited copy.
|
|
195
|
+
Fitted text is measured again and animation references within the changed block
|
|
196
|
+
are moved to that block. For richer changes, edit the node tree directly and keep
|
|
197
|
+
its animation references valid. To change photos, update their asset records,
|
|
198
|
+
resolver URLs and photo specifications. Exported JSON is configuration for your
|
|
199
|
+
own application; it is not a general HTML import format.
|
|
200
|
+
|
|
201
|
+
The asset resolver must return a URL for every declared asset. Missing assets
|
|
202
|
+
reject `ready` instead of silently substituting photos. WebGL2 is required for
|
|
203
|
+
full visual fidelity; the runtime marks a readable material/content fallback with
|
|
204
|
+
`data-rendered="fallback"` and `rendered === false`. Reduced-motion preferences
|
|
205
|
+
finish content immediately. Dragging, peeling, selection highlights and the desk
|
|
206
|
+
background belong to the demo and are excluded from component exports.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
Original project code is MIT © 2026 Maks Surguy. Third-party materials retain
|
|
4
|
+
their own licenses; the MIT license does not replace those terms.
|
|
5
|
+
|
|
6
|
+
## Paper Shaders
|
|
7
|
+
|
|
8
|
+
The paper overlay algorithms acknowledge the paper-texture shader in
|
|
9
|
+
[Paper Shaders](https://github.com/paper-design/shaders), by Paper Design,
|
|
10
|
+
licensed under Apache-2.0. This project's implementation reworks the overlays
|
|
11
|
+
for periodic tiling and matching CPU/GPU rendering. Attribution appears in
|
|
12
|
+
`src/params.ts` and `src/cpu/overlays.ts` in the library source. The upstream
|
|
13
|
+
license is included in `licenses/Apache-2.0.txt` in both repository and package.
|
|
14
|
+
|
|
15
|
+
## Demo-only materials (not included in the npm library)
|
|
16
|
+
|
|
17
|
+
- Desk scan derivatives: Poly Haven CC0. Full author, source and modification
|
|
18
|
+
records: `apps/demo/public/textures/README.md`.
|
|
19
|
+
- Sample photo derivatives: StockSnap CC0, found through Openverse. Full author
|
|
20
|
+
and source records: `apps/demo/public/photos/README.md`.
|
|
21
|
+
- Hershey stroke font data: acknowledgements and redistribution conditions in
|
|
22
|
+
each `apps/demo/src/desk/pen-fonts/hershey-*/LICENSE.txt`.
|
|
23
|
+
- EMS fonts: Sheldon B. Michaels; SVG conversion by Windell H. Oskay. Derived
|
|
24
|
+
from Allura (Rob Leuschke), Covered By Your Grace (Kimberly Geswein), and
|
|
25
|
+
Source Sans (Paul D. Hunt / Adobe). Attribution and full OFL text accompany
|
|
26
|
+
each font under `apps/demo/src/desk/pen-fonts/ems-*`. Tegaki generates pen
|
|
27
|
+
outlines and the generation script merges overlapping strokes.
|
|
28
|
+
- Self-hosted Google Fonts retain their upstream licenses. The npm library
|
|
29
|
+
does not bundle font binaries; composition consumers supply their own asset URLs.
|
|
30
|
+
- Brushmark, Tegaki and Pretext are demo dependencies and optional library
|
|
31
|
+
composition peers. The texture core has no runtime dependencies.
|
|
32
|
+
Their upstream licenses remain with their distributions. A demo build copies
|
|
33
|
+
available package license files and bundled font notices into `licenses/`.
|
|
34
|
+
|
|
35
|
+
Only the documented CC0 derivatives belong in the demo's image directories.
|
|
36
|
+
Private scans, reference photographs, and original source collections must not
|
|
37
|
+
be committed. The release check enforces the current image allowlist.
|
|
38
|
+
|
|
39
|
+
## Desk example exports
|
|
40
|
+
|
|
41
|
+
The demo self-hosts its Google Fonts faces in `apps/demo/public/fonts`, with
|
|
42
|
+
per-family license files. `apps/demo/src/desk/font-assets.json` records their
|
|
43
|
+
upstream URLs and SHA-256 hashes. `apps/demo/scripts/vendor-fonts.mjs` refreshes
|
|
44
|
+
this manifest deliberately; it is not part of normal builds. Component ZIPs copy
|
|
45
|
+
only the selected assets and their license/credit files. The npm library does
|
|
46
|
+
not bundle these font binaries or demo photos.
|
|
47
|
+
|
|
48
|
+
Optional composition peers: @chenglou/pretext (text layout), brushmark (content
|
|
49
|
+
motion), and tegaki (pen strokes). Existing pen font license files accompany
|
|
50
|
+
exports containing those fonts. Generated example code is MIT; the included
|
|
51
|
+
media and font licenses retain their own terms.
|
package/dist/cards.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type DeepPartial, type PaperParams } from './params';
|
|
2
|
+
import type { SheetSpec } from './sheet';
|
|
3
|
+
export declare const cardPresetIds: readonly ["sticky", "index", "notebook", "napkin", "scrap", "graph", "biz", "quote", "tag", "receipt", "ticket", "memo", "watercolor", "clipping", "stamp", "tracing", "tapelabel", "polaroid", "photonote", "print", "vintage", "tear", "album", "booth", "film"];
|
|
4
|
+
export type CardPresetId = typeof cardPresetIds[number];
|
|
5
|
+
/** Region coordinates are CSS pixels, relative to the sheet's top-left corner. */
|
|
6
|
+
export interface CardRegion {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
}
|
|
12
|
+
export interface CardPreset {
|
|
13
|
+
id: CardPresetId;
|
|
14
|
+
label: string;
|
|
15
|
+
stock: string;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
sheet: Omit<SheetSpec, 'width' | 'height' | 'pixelRatio'>;
|
|
19
|
+
regions: Record<string, CardRegion>;
|
|
20
|
+
ruling?: 'index' | 'notebook' | 'grid';
|
|
21
|
+
}
|
|
22
|
+
/** Canonical paper styles from Paper Desk, without demo content or attachments. */
|
|
23
|
+
export declare const cardPresets: readonly CardPreset[];
|
|
24
|
+
export interface CardOptions {
|
|
25
|
+
preset: CardPresetId;
|
|
26
|
+
width?: number;
|
|
27
|
+
height?: number;
|
|
28
|
+
seed?: number;
|
|
29
|
+
params?: DeepPartial<PaperParams>;
|
|
30
|
+
/** Top-level sheet groups replace their preset counterparts. */
|
|
31
|
+
sheet?: Omit<SheetSpec, 'width' | 'height'>;
|
|
32
|
+
}
|
|
33
|
+
export declare function getCardPreset(id: CardPresetId): CardPreset;
|
|
34
|
+
/** Scale the preset's geometry and regions to explicit CSS dimensions. */
|
|
35
|
+
export declare function resolveCard(options: CardOptions): {
|
|
36
|
+
preset: CardPreset;
|
|
37
|
+
regions: {
|
|
38
|
+
[k: string]: CardRegion;
|
|
39
|
+
};
|
|
40
|
+
params: PaperParams;
|
|
41
|
+
sheet: SheetSpec;
|
|
42
|
+
};
|
package/dist/color.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type RGB = [number, number, number];
|
|
2
|
+
/** Parse `#rgb`, `#rrggbb` (or `rgb(r,g,b)`) into 0..1 sRGB components. */
|
|
3
|
+
export declare function parseColor(input: string): RGB;
|
|
4
|
+
export declare function toHex([r, g, b]: RGB): string;
|
|
5
|
+
export declare function luminance([r, g, b]: RGB): number;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AssetResolver, CompositionAsset } from './model';
|
|
2
|
+
import type { PenContext } from './pen';
|
|
3
|
+
export declare function loadCompositionAssets(assets: CompositionAsset[], resolve: AssetResolver, pen: boolean): Promise<{
|
|
4
|
+
urls: Map<string, string>;
|
|
5
|
+
penContext: PenContext | undefined;
|
|
6
|
+
dispose: () => void;
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Rng } from './rng';
|
|
2
|
+
import type { Sprite } from './props';
|
|
3
|
+
export type CordKind = 'jute' | 'hemp' | 'baker' | 'cotton' | 'satin' | 'grosgrain' | 'wire';
|
|
4
|
+
type P3 = [number, number, number];
|
|
5
|
+
export interface Polyline {
|
|
6
|
+
pts: P3[];
|
|
7
|
+
/** Per point: true if that point lies under the sheet. */
|
|
8
|
+
under: boolean[];
|
|
9
|
+
/** Free ends fray (twine) or get a cut (ribbon). */
|
|
10
|
+
freeStart: boolean;
|
|
11
|
+
freeEnd: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface CordSpec {
|
|
14
|
+
kind: CordKind;
|
|
15
|
+
width: number;
|
|
16
|
+
colors: [string, string];
|
|
17
|
+
lines: Polyline[];
|
|
18
|
+
/** Knots: x, y, radius (lying on top). */
|
|
19
|
+
knots: [number, number, number][];
|
|
20
|
+
seed: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function createCordRenderer(): {
|
|
23
|
+
renderCord: (spec: CordSpec, part: "front" | "back", dpr: number, azimuth: number, elevation: number) => Sprite | null;
|
|
24
|
+
dispose(): void;
|
|
25
|
+
};
|
|
26
|
+
export declare const renderCord: ReturnType<typeof createCordRenderer>['renderCord'];
|
|
27
|
+
export declare const CORD_STYLES: {
|
|
28
|
+
kind: CordKind;
|
|
29
|
+
colors: [string, string];
|
|
30
|
+
width: number;
|
|
31
|
+
weight: number;
|
|
32
|
+
label: string;
|
|
33
|
+
}[];
|
|
34
|
+
/**
|
|
35
|
+
* A cord tied through a tag's hole at (cx, cy), radius hr, tag top edge at y = 0.
|
|
36
|
+
* Twine and wire: lark's head (two strands over the edge, bight behind the hole,
|
|
37
|
+
* doubled tails). Ribbon: threaded once, knotted just past the edge, two V-cut tails.
|
|
38
|
+
*/
|
|
39
|
+
export declare function tagCord(rng: Rng, cx: number, cy: number, hr: number, tagW: number): {
|
|
40
|
+
spec: CordSpec;
|
|
41
|
+
label: string;
|
|
42
|
+
};
|
|
43
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const filterMarkup = " <svg width=\"0\" height=\"0\" style=\"position: absolute\" aria-hidden=\"true\">\n <filter id=\"f-ink\" x=\"-5%\" y=\"-5%\" width=\"110%\" height=\"110%\">\n <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.8\" numOctaves=\"2\" seed=\"4\" result=\"n\" />\n <feDisplacementMap in=\"SourceGraphic\" in2=\"n\" scale=\"1.4\" xChannelSelector=\"R\" yChannelSelector=\"G\" />\n </filter>\n <filter id=\"f-pencil\" x=\"-5%\" y=\"-5%\" width=\"110%\" height=\"110%\">\n <feTurbulence type=\"fractalNoise\" baseFrequency=\"1.3\" numOctaves=\"2\" seed=\"9\" result=\"n\" />\n <feColorMatrix in=\"n\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2.4 0 0 0 -0.55\" result=\"m\" />\n <feComposite in=\"SourceGraphic\" in2=\"m\" operator=\"in\" result=\"g\" />\n <feDisplacementMap in=\"g\" in2=\"n\" scale=\"1\" xChannelSelector=\"G\" yChannelSelector=\"B\" />\n </filter>\n <filter id=\"f-stamp\" x=\"-5%\" y=\"-10%\" width=\"110%\" height=\"120%\">\n <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.55\" numOctaves=\"3\" seed=\"2\" result=\"n\" />\n <feColorMatrix in=\"n\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.2 0 0 0 -0.75\" result=\"m\" />\n <feComposite in=\"SourceGraphic\" in2=\"m\" operator=\"in\" result=\"g\" />\n <feDisplacementMap in=\"g\" in2=\"n\" scale=\"1.6\" xChannelSelector=\"R\" yChannelSelector=\"G\" />\n </filter>\n <filter id=\"f-wash\" x=\"-10%\" y=\"-10%\" width=\"120%\" height=\"120%\">\n <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.035\" numOctaves=\"3\" seed=\"7\" result=\"n\" />\n <feDisplacementMap in=\"SourceGraphic\" in2=\"n\" scale=\"16\" xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"d\" />\n <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.9\" numOctaves=\"2\" seed=\"5\" result=\"g\" />\n <feColorMatrix in=\"g\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.2 0 0 0 0.25\" result=\"ga\" />\n <feComposite in=\"d\" in2=\"ga\" operator=\"in\" />\n </filter>\n <filter id=\"f-stain\" x=\"-10%\" y=\"-10%\" width=\"120%\" height=\"120%\">\n <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.06\" numOctaves=\"3\" seed=\"11\" result=\"n\" />\n <feDisplacementMap in=\"SourceGraphic\" in2=\"n\" scale=\"7\" xChannelSelector=\"R\" yChannelSelector=\"G\" />\n </filter>\n </svg>";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface Font {
|
|
2
|
+
family: string;
|
|
3
|
+
size: number;
|
|
4
|
+
kind: 'hand' | 'mono';
|
|
5
|
+
marker?: boolean;
|
|
6
|
+
weight?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface FitBlock {
|
|
9
|
+
/** Plain text used for measuring (and rendering, unless `html` is given). */
|
|
10
|
+
text: string;
|
|
11
|
+
/** Rendered markup when it differs from `text` (e.g. typewriter spans). */
|
|
12
|
+
html?: string;
|
|
13
|
+
/** Size relative to the block's base size (em). */
|
|
14
|
+
scale?: number;
|
|
15
|
+
/** Extra space above the block (em of its own size). */
|
|
16
|
+
gap?: number;
|
|
17
|
+
/** Letter-spacing in em. */
|
|
18
|
+
spacing?: number;
|
|
19
|
+
cls?: string;
|
|
20
|
+
/** Font weight for this block (defaults to the spec's). */
|
|
21
|
+
weight?: string;
|
|
22
|
+
/** Only the first block is kept... unless this is set (titles, signatures stay). */
|
|
23
|
+
keep?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface FitSpec {
|
|
26
|
+
font: Font;
|
|
27
|
+
weight?: string;
|
|
28
|
+
/** Box in px. */
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
/** Largest / smallest font size in px (final, after the font's own size factor). */
|
|
32
|
+
max: number;
|
|
33
|
+
min: number;
|
|
34
|
+
/** Line height as a multiple of the font size... */
|
|
35
|
+
lineHeight?: number;
|
|
36
|
+
/** ...or a fixed pitch in px (ruled paper). */
|
|
37
|
+
pitch?: number;
|
|
38
|
+
whiteSpace?: 'normal' | 'pre-wrap';
|
|
39
|
+
/** Drop trailing blocks when even `min` doesn't fit (default true). */
|
|
40
|
+
drop?: boolean;
|
|
41
|
+
/** Never wrap past this many lines (labels, one-liners). */
|
|
42
|
+
maxLines?: number;
|
|
43
|
+
/** Vertical writing mode: width/height describe the inline/block axes. */
|
|
44
|
+
vertical?: boolean;
|
|
45
|
+
blocks: FitBlock[];
|
|
46
|
+
}
|
|
47
|
+
export interface FitResult {
|
|
48
|
+
size: number;
|
|
49
|
+
kept: number;
|
|
50
|
+
lines: number;
|
|
51
|
+
}
|
|
52
|
+
export declare function fitText(spec: FitSpec): Promise<FitResult>;
|
|
53
|
+
export declare const fitSpecs: WeakMap<HTMLElement, FitSpec>;
|
|
54
|
+
export declare function blocksHTML(blocks: FitBlock[]): string;
|
|
55
|
+
/** Register `el` to be fitted before its card is shown; renders at max size for now. */
|
|
56
|
+
export declare function fitLater(el: HTMLElement, spec: FitSpec): void;
|
|
57
|
+
/** Fit every registered text block inside `root`. */
|
|
58
|
+
export declare function runFits(root: HTMLElement): Promise<void>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PaperRenderer } from '../index';
|
|
2
|
+
import { type AssetResolver, type PaperComposition } from './model';
|
|
3
|
+
import type { CardPlan } from './scene';
|
|
4
|
+
export * from './model';
|
|
5
|
+
export type { CompositionNode } from './model';
|
|
6
|
+
export interface CompositionOptions {
|
|
7
|
+
composition: PaperComposition;
|
|
8
|
+
resolveAsset: AssetResolver;
|
|
9
|
+
autoplay?: boolean;
|
|
10
|
+
fit?: 'natural' | 'contain';
|
|
11
|
+
renderer?: PaperRenderer;
|
|
12
|
+
}
|
|
13
|
+
export interface CompositionHandle {
|
|
14
|
+
element: HTMLDivElement;
|
|
15
|
+
readonly ready: Promise<void>;
|
|
16
|
+
readonly rendered: boolean;
|
|
17
|
+
update(options: Partial<CompositionOptions>): Promise<void>;
|
|
18
|
+
play(): void;
|
|
19
|
+
pause(): void;
|
|
20
|
+
replay(): void;
|
|
21
|
+
seek(progress: number): void;
|
|
22
|
+
dispose(): void;
|
|
23
|
+
}
|
|
24
|
+
export declare function hydrateComposition(composition: PaperComposition, scope: string): CardPlan;
|
|
25
|
+
/** Use the same SVG filter definitions, with instance-local IDs. */
|
|
26
|
+
export declare function mountCompositionFilters(host: HTMLElement, scope: string): void;
|
|
27
|
+
export declare function createPaperComposition(initial: CompositionOptions): CompositionHandle;
|