postext-folio 0.1.2 → 0.2.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.
Files changed (42) hide show
  1. package/README.md +59 -4
  2. package/dist/bookGeometry.d.ts +77 -0
  3. package/dist/bookGeometry.d.ts.map +1 -0
  4. package/dist/bookGeometry.js +204 -0
  5. package/dist/bookGeometry.js.map +1 -0
  6. package/dist/deskTextures.d.ts +24 -0
  7. package/dist/deskTextures.d.ts.map +1 -0
  8. package/dist/deskTextures.js +88 -0
  9. package/dist/deskTextures.js.map +1 -0
  10. package/dist/environments.d.ts +27 -0
  11. package/dist/environments.d.ts.map +1 -0
  12. package/dist/environments.js +90 -0
  13. package/dist/environments.js.map +1 -0
  14. package/dist/index.d.ts +5 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +3 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/pageFlip.d.ts +160 -26
  19. package/dist/pageFlip.d.ts.map +1 -1
  20. package/dist/pageFlip.js +1195 -342
  21. package/dist/pageFlip.js.map +1 -1
  22. package/dist/pageFlipFlat.d.ts +131 -0
  23. package/dist/pageFlipFlat.d.ts.map +1 -0
  24. package/dist/pageFlipFlat.js +870 -0
  25. package/dist/pageFlipFlat.js.map +1 -0
  26. package/dist/paper.d.ts +40 -0
  27. package/dist/paper.d.ts.map +1 -0
  28. package/dist/paper.js +65 -0
  29. package/dist/paper.js.map +1 -0
  30. package/dist/postext.d.ts +12 -2
  31. package/dist/postext.d.ts.map +1 -1
  32. package/dist/postext.js +167 -6
  33. package/dist/postext.js.map +1 -1
  34. package/dist/procedural.d.ts +35 -0
  35. package/dist/procedural.d.ts.map +1 -0
  36. package/dist/procedural.js +390 -0
  37. package/dist/procedural.js.map +1 -0
  38. package/dist/viewer.d.ts +41 -1
  39. package/dist/viewer.d.ts.map +1 -1
  40. package/dist/viewer.js +133 -16
  41. package/dist/viewer.js.map +1 -1
  42. package/package.json +3 -3
package/README.md CHANGED
@@ -45,6 +45,43 @@ Pages are painted with `renderPageToCanvas` at exactly the device pixels of a pa
45
45
 
46
46
  The container sets the size: the book fills it, with the ‹ › buttons and the page count in the margins. Give it a height. A resize paints the pages again at the new size.
47
47
 
48
+ ## The book on the desk
49
+
50
+ The book lies open on a desk, seen from in front and a little above (the foot of the pages comes closer), lit by an environment that glossy paper reflects as a leaf curls, and by a key light that casts the shadows. The pages already read and those still to come form two blocks of real thickness: leaf count × the paper's caliper, with the book's other chapters counted in (`extraPages`). They are never painted, only counted. The open pages curve down into the gutter, and the darker gutter comes from the occlusion of that shape, not from a painted gradient. Each leaf bends according to its paper's stiffness: bible paper rolls tight, card turns in a wide arc, and board turns as a rigid plate on its hinge.
51
+
52
+ All of this is set with postext's `folio` config, read from `doc.config.folio` or passed as `appearance`:
53
+
54
+ ```ts
55
+ const book = createFolioFromDocument(container, doc, {
56
+ appearance: {
57
+ folio: {
58
+ tilt: 22, // degrees from straight above, 0 … 40
59
+ paper: { type: 'bookWove', grammage: 80, texture: 'laid' },
60
+ binding: { type: 'hardcover', coverColor: { hex: '#5a1f1f', model: 'hex' } },
61
+ surface: { type: 'walnut' },
62
+ lighting: { environment: 'lamp', intensity: 1 },
63
+ },
64
+ },
65
+ });
66
+ book.setAppearance({ folio: { lighting: { environment: 'daylight' } } });
67
+ ```
68
+
69
+ <table>
70
+ <thead><tr><th>Setting</th><th>Values</th></tr></thead>
71
+ <tbody>
72
+ <tr><td><code>paper.type</code></td><td><code>uncoated</code> (woodfree offset, 90 g/m²), <code>bookWove</code> (bulky cream, 80 g/m²), <code>coatedMatte</code>, <code>coatedSilk</code>, <code>coatedGloss</code> (115 g/m²), <code>bible</code> (40 g/m²), <code>newsprint</code>, <code>cardStock</code> (250 g/m²), <code>board</code> (rigid, about 2 mm). Each sets the defaults below.</td></tr>
73
+ <tr><td><code>paper.grammage</code>, <code>paper.bulk</code></td><td>g/m² and cm³/g; caliper µm = grammage × bulk. Thickness, stiffness (∝ caliper^⅔ for the roll) and opacity follow.</td></tr>
74
+ <tr><td><code>paper.finish</code></td><td><code>uncoated</code> | <code>matte</code> | <code>silk</code> | <code>gloss</code>: roughness and clear coat.</td></tr>
75
+ <tr><td><code>paper.texture</code>, <code>paper.textureStrength</code></td><td><code>smooth</code> | <code>vellum</code> | <code>wove</code> | <code>laid</code> | <code>linen</code> | <code>felt</code>, as a normal map at physical scale; strength 0 … 2.</td></tr>
76
+ <tr><td><code>paper.shade</code>, <code>paper.showThrough</code></td><td>The stock's colour (pages are printed on it); the reverse page showing faintly through thin paper.</td></tr>
77
+ <tr><td><code>binding.type</code></td><td><code>hardcover</code> (boards with squares) | <code>paperback</code> (opens less flat) | <code>sewn</code> | <code>layflat</code>; plus <code>coverMaterial</code> (cloth, paper, leather) and <code>coverColor</code>.</td></tr>
78
+ <tr><td><code>surface.type</code></td><td><code>oak</code> | <code>walnut</code> | <code>linen</code> | <code>felt</code> | <code>leather</code> | <code>marble</code> | <code>plain</code> | <code>none</code> (the host's background, with the book's shadow); <code>color</code> tints it.</td></tr>
79
+ <tr><td><code>lighting</code></td><td><code>environment</code>: <code>studio</code> | <code>daylight</code> | <code>lamp</code> | <code>overcast</code> | <code>night</code>; <code>intensity</code> 0.25 … 2; <code>shadows</code>.</td></tr>
80
+ </tbody>
81
+ </table>
82
+
83
+ A `:::paper{type=coatedGloss}` run in the markdown (a plate section, say) stamps its pages with their own stock; the viewer gives those leaves that paper's look, thickness and stiffness. With `createFolio`, a page may carry its own `paper` the same way: `{ src, paper: { type: 'coatedGloss' } }`.
84
+
48
85
  ## Page images
49
86
 
50
87
  `createFolio` takes any pages: image URLs, `<canvas>` or `<img>` elements, `""` for a blank page.
@@ -65,10 +102,11 @@ book.goToPage(2);
65
102
  <table>
66
103
  <thead><tr><th>Export</th><th>What it does</th></tr></thead>
67
104
  <tbody>
68
- <tr><td><code>createFolio(container, options)</code></td><td>The viewer over any pages. Options: <code>pages</code>, <code>firstPageRecto</code> (default true), <code>binding</code> (<code>'left'</code> | <code>'right'</code>), <code>at</code> (page to open on), <code>mode</code> (<code>'auto'</code> | <code>'single'</code> | <code>'double'</code>; auto shows one page at a time below 560 px), <code>paper</code> (blank-page colour), <code>animate</code>, <code>controls</code>, <code>showCount</code> (false keeps the count for screen readers only), <code>labels</code>, <code>onChange</code>, <code>onTarget</code>, <code>onLayout</code>. One page at a time the book is WebGL too: the spine runs along the page's inner edge and the leaf turns over it; a drag towards the spine turns forward, a swipe away from it goes back, a tap turns.</td></tr>
105
+ <tr><td><code>createFolio(container, options)</code></td><td>The viewer over any pages. Options: <code>pages</code>, <code>firstPageRecto</code> (default true), <code>binding</code> (<code>'left'</code> | <code>'right'</code>), <code>at</code> (page to open on), <code>mode</code> (<code>'auto'</code> | <code>'single'</code> | <code>'double'</code>; auto shows one page at a time below 560 px), <code>paper</code> (blank-page colour), <code>animate</code>, <code>controls</code>, <code>showCount</code> (false keeps the count for screen readers only), <code>labels</code>, <code>appearance</code> (<code>folio</code>, <code>pageWidthMm</code>, <code>extraPages</code>), <code>onChange</code>, <code>onTarget</code>, <code>onLayout</code>. One page at a time the book is WebGL too: the spine runs along the page's inner edge and the leaf turns over it; a drag towards the spine turns forward, a swipe away from it goes back, a tap turns.</td></tr>
69
106
  <tr><td><code>createFolioFromDocument(container, doc, options)</code></td><td>The same over a postext <code>VDTDocument</code>, painted lazily. Spreads, binding and paper come from the document. Adds <code>scale</code>, <code>window</code>, <code>singleInk</code>, <code>pageNegative</code>, <code>alt</code>, and <code>setDocument(doc)</code> on the viewer.</td></tr>
70
- <tr><td>viewer</td><td><code>goToPage(i, { instant })</code>, <code>goToSpread(i, { instant })</code> (instant opens there without turning), <code>next()</code>, <code>prev()</code>, <code>setPages(pages, opts)</code>, <code>setLabels(labels)</code>, <code>state</code> (<code>{ spread, pages }</code>), <code>element</code>, <code>dispose()</code>.</td></tr>
71
- <tr><td><code>PageFlipper</code>, <code>canFlip()</code></td><td>The three.js engine alone, for a host that lays out its own spread DOM (as the postext Cookbook does): a canvas over the spread, <code>go(i)</code>, <code>grab</code>/<code>drag</code>/<code>release</code> for the hand, <code>setBook</code>, <code>clear</code>, <code>dispose</code>; <code>{ persistent: true }</code> draws the book at rest too, <code>turnAt</code> sets how far a leaf let go must be to turn over.</td></tr>
107
+ <tr><td>viewer</td><td><code>goToPage(i, { instant })</code>, <code>goToSpread(i, { instant })</code> (instant opens there without turning), <code>next()</code>, <code>prev()</code>, <code>setPages(pages, opts)</code>, <code>setLabels(labels)</code>, <code>setAppearance(appearance)</code>, <code>state</code> (<code>{ spread, pages }</code>), <code>element</code>, <code>dispose()</code>.</td></tr>
108
+ <tr><td><code>PageFlipper</code>, <code>canFlip()</code></td><td>The three.js engine alone: a canvas over a host's spread, <code>go(i)</code>, <code>grab</code>/<code>drag</code>/<code>release</code> for the hand (ray-cast onto the tilted pages), <code>hit</code>, <code>setBook</code>, <code>setAppearance</code>, <code>clear</code>, <code>dispose</code>; <code>{ persistent: true }</code> draws the book at rest too, <code>turnAt</code> sets how far a leaf let go must be to turn over.</td></tr>
109
+ <tr><td><code>FlatPageFlipper</code></td><td>The flat page-turn that predates the 3D book, for a host whose DOM spread shows the pages at rest and must match the turning leaf pixel for pixel (the postext Cookbook's light table).</td></tr>
72
110
  <tr><td><code>spreadsOf(count, firstPageRecto)</code></td><td>The <code>[verso, recto]</code> pairs of a book.</td></tr>
73
111
  <tr><td><code>FOLIO_CSS</code></td><td>The viewer's styles (injected once on first use). Restyle with <code>--postext-folio-accent</code> and <code>--postext-folio-nav-border</code> on the container.</td></tr>
74
112
  </tbody>
@@ -76,6 +114,23 @@ book.goToPage(2);
76
114
 
77
115
  The viewer takes the keyboard when focused: ←/→ (mirrored for a right-bound book), Page Up/Down, Home and End. Its labels default to English; pass `labels` (`region`, `prev`, `next`, `count`) to translate them.
78
116
 
117
+ ## Desk textures
118
+
119
+ The photographed desk surfaces served from postext.dev (`/folio/textures/`, with a `manifest.json` and a `CREDITS` file) are CC0 texture sets, resized and re-encoded for the web. No attribution is required; thanks to their authors:
120
+
121
+ <table>
122
+ <thead><tr><th>Desk</th><th>Set</th><th>Author</th><th>Source</th></tr></thead>
123
+ <tbody>
124
+ <tr><td>oak</td><td>Oak Veneer 01</td><td>Jenelle van Heerden</td><td><a href="https://polyhaven.com/a/oak_veneer_01">Poly Haven</a></td></tr>
125
+ <tr><td>walnut</td><td>Wood 051</td><td>Lennart Demes</td><td><a href="https://ambientcg.com/view?id=Wood051">ambientCG</a></td></tr>
126
+ <tr><td>linen</td><td>Fabric 036</td><td>Lennart Demes</td><td><a href="https://ambientcg.com/view?id=Fabric036">ambientCG</a></td></tr>
127
+ <tr><td>felt</td><td>Fabric 034</td><td>Lennart Demes</td><td><a href="https://ambientcg.com/view?id=Fabric034">ambientCG</a></td></tr>
128
+ <tr><td>leather</td><td>Brown Leather</td><td>Rob Tuytel</td><td><a href="https://polyhaven.com/a/brown_leather">Poly Haven</a></td></tr>
129
+ <tr><td>marble</td><td>Marble 021</td><td>Lennart Demes</td><td><a href="https://ambientcg.com/view?id=Marble021">ambientCG</a></td></tr>
130
+ <tr><td>plain</td><td>Plastic 013 A</td><td>Lennart Demes</td><td><a href="https://ambientcg.com/view?id=Plastic013A">ambientCG</a></td></tr>
131
+ </tbody>
132
+ </table>
133
+
79
134
  ## License
80
135
 
81
- MIT
136
+ MIT (code). The desk textures above are CC0.
@@ -0,0 +1,77 @@
1
+ import { BufferGeometry } from "three";
2
+ export type BindingKind = "hardcover" | "paperback" | "sewn" | "layflat" | "saddleStitch";
3
+ /** How each binding opens. `gutter`: the width over which the pages bend
4
+ * into the spine (a fraction of the page width, plus `perThickness` × the
5
+ * block's thickness); `spine`: how near the top of the thicker stack the
6
+ * top leaves meet (1: at its top); `power`: how steeply they drop at the end;
7
+ * `dipMm`: how far below that a thin book's pages still sink; the case's
8
+ * boards and their squares (the margin they stand out round the pages). */
9
+ export declare const BINDINGS: Record<BindingKind, {
10
+ gutter: number;
11
+ perThickness: number;
12
+ spine: number;
13
+ power: number;
14
+ dipMm: number;
15
+ boardMm: number;
16
+ squareMm: number;
17
+ jointMm: number;
18
+ }>;
19
+ /** A side's top surface: `x(s)`, `z(s)` for arc length `s` from the spine
20
+ * (a page keeps its width as it bends), and the slope. */
21
+ export interface Profile {
22
+ /** Thickness of the stack (book units). */
23
+ t: number;
24
+ /** Height of the block's base (the top of the cover under it). */
25
+ zb: number;
26
+ /** Height the top leaf meets the spine at, above zb. */
27
+ meet: number;
28
+ /** Width of the bend. */
29
+ g: number;
30
+ power: number;
31
+ /** Arc-length table: x and z every `step` of s. */
32
+ xs: Float32Array;
33
+ zs: Float32Array;
34
+ step: number;
35
+ }
36
+ /**
37
+ * The open book's two surfaces. Every leaf is sewn at the spine, so the top
38
+ * leaves of both stacks meet there, at a height between the two stacks
39
+ * (lower for a binding that opens less freely, minus a dip so even a thin
40
+ * book sinks into its gutter); away from the spine each lies on its
41
+ * stack. A stack's lower leaves follow the same curve scaled down to the
42
+ * cover, so the block's head and tail show the fan of leaves.
43
+ */
44
+ export declare function profiles(binding: BindingKind, W: number, pxPerMm: number, tLeft: number, tRight: number,
45
+ /** `flat`: a side whose top leaf is rigid (a board) lies flat, with no
46
+ * gutter; `noCase`: the boards are leaves of the stacks (the document's
47
+ * own cover pages), so the block rests on the desk. */
48
+ { flatLeft, flatRight, noCase }?: {
49
+ flatLeft?: boolean;
50
+ flatRight?: boolean;
51
+ noCase?: boolean;
52
+ }): {
53
+ left: Profile;
54
+ right: Profile;
55
+ board: number;
56
+ };
57
+ /** Where arc length `s` of a profile lies: [x, z, dz/dx]. */
58
+ export declare function along(p: Profile, s: number): [number, number, number];
59
+ /** A lower leaf (fraction `lambda` of the way up the stack) at arc
60
+ * length s, approximately: the top curve scaled down to the base. */
61
+ export declare function alongLayer(p: Profile, s: number, lambda: number): [number, number];
62
+ /**
63
+ * Ambient light reaching each point of the open pages, from the shape of
64
+ * the book's cross-section: the fraction of the sky (cosine weighted) the
65
+ * other page and the gutter leave open. This is the darkening along the
66
+ * gutter, found from the geometry rather than painted. `n` samples across
67
+ * one page, from the spine out.
68
+ */
69
+ export declare function gutterOcclusion(own: Profile, other: Profile, W: number, n?: number): Float32Array;
70
+ /**
71
+ * The page block's visible sides for one stack: the head and tail (the
72
+ * fan of leaves between the cover and the top leaf) and the fore-edge.
73
+ * Each vertex carries `layer`: the leaf it lies on, counted up from the
74
+ * cover, so the shader draws the edges of the leaves.
75
+ */
76
+ export declare function stackGeometry(p: Profile, side: 1 | -1, W: number, H: number, leaves: number): BufferGeometry;
77
+ //# sourceMappingURL=bookGeometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bookGeometry.d.ts","sourceRoot":"","sources":["../src/bookGeometry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,cAAc,EAAE,MAAM,OAAO,CAAC;AAMxD,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,cAAc,CAAC;AAE1F;;;;;4EAK4E;AAC5E,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAQnL,CAAC;AAEF;2DAC2D;AAC3D,MAAM,WAAW,OAAO;IACtB,2CAA2C;IAC3C,CAAC,EAAE,MAAM,CAAC;IACV,kEAAkE;IAClE,EAAE,EAAE,MAAM,CAAC;IACX,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,EAAE,EAAE,YAAY,CAAC;IACjB,EAAE,EAAE,YAAY,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAaD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,WAAW,EACpB,CAAC,EAAE,MAAM,EACT,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM;AACd;;wDAEwD;AACxD,EAAE,QAAgB,EAAE,SAAiB,EAAE,MAAc,EAAE,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAC1H;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA6ClD;AAED,6DAA6D;AAC7D,wBAAgB,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAQrE;AAED;sEACsE;AACtE,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAGlF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,SAAK,GAAG,YAAY,CA8B7F;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,CA+D5G"}
@@ -0,0 +1,204 @@
1
+ import { BufferAttribute, BufferGeometry } from "three";
2
+ /** How each binding opens. `gutter`: the width over which the pages bend
3
+ * into the spine (a fraction of the page width, plus `perThickness` × the
4
+ * block's thickness); `spine`: how near the top of the thicker stack the
5
+ * top leaves meet (1: at its top); `power`: how steeply they drop at the end;
6
+ * `dipMm`: how far below that a thin book's pages still sink; the case's
7
+ * boards and their squares (the margin they stand out round the pages). */
8
+ export const BINDINGS = {
9
+ hardcover: { gutter: 0.06, perThickness: 0.9, spine: 1, power: 2.4, dipMm: 3, boardMm: 2.6, squareMm: 3, jointMm: 7 },
10
+ sewn: { gutter: 0.07, perThickness: 1.0, spine: 0.95, power: 2.5, dipMm: 3.5, boardMm: 0.35, squareMm: 0, jointMm: 0 },
11
+ paperback: { gutter: 0.1, perThickness: 1.2, spine: 0.85, power: 3, dipMm: 5, boardMm: 0.35, squareMm: 0, jointMm: 0 },
12
+ layflat: { gutter: 0.025, perThickness: 0.4, spine: 1, power: 2, dipMm: 0.2, boardMm: 2.2, squareMm: 3, jointMm: 4 },
13
+ // Folded sheets stapled through the fold: the centre spread opens flat,
14
+ // the cover is a sheet like the others.
15
+ saddleStitch: { gutter: 0.05, perThickness: 1.4, spine: 0.9, power: 2.2, dipMm: 1.5, boardMm: 0.25, squareMm: 0, jointMm: 0 },
16
+ };
17
+ const SAMPLES = 256;
18
+ /** The top leaf of a stack `t` thick at height above the base, at x. */
19
+ function heightAt(p, x, lambda = 1) {
20
+ // A block's top is flat and its leaves roll over a rounded shoulder into
21
+ // the binding (a quarter superellipse: level where it leaves the top,
22
+ // steep at the spine), not a long slope across the page.
23
+ const shape = x < p.g ? Math.pow(1 - Math.pow(1 - x / p.g, p.power), 1 / p.power) : 1;
24
+ return lambda * (p.meet + (p.t - p.meet) * shape);
25
+ }
26
+ /**
27
+ * The open book's two surfaces. Every leaf is sewn at the spine, so the top
28
+ * leaves of both stacks meet there, at a height between the two stacks
29
+ * (lower for a binding that opens less freely, minus a dip so even a thin
30
+ * book sinks into its gutter); away from the spine each lies on its
31
+ * stack. A stack's lower leaves follow the same curve scaled down to the
32
+ * cover, so the block's head and tail show the fan of leaves.
33
+ */
34
+ export function profiles(binding, W, pxPerMm, tLeft, tRight,
35
+ /** `flat`: a side whose top leaf is rigid (a board) lies flat, with no
36
+ * gutter; `noCase`: the boards are leaves of the stacks (the document's
37
+ * own cover pages), so the block rests on the desk. */
38
+ { flatLeft = false, flatRight = false, noCase = false } = {}) {
39
+ const b = BINDINGS[binding];
40
+ const zb = noCase ? 0 : b.boardMm * pxPerMm;
41
+ // Where the two top leaves meet at the spine. They are sewn next to each
42
+ // other on the block's back, near the top of the thicker stack: a book
43
+ // opened at its first page keeps its block flat and only rolls the top
44
+ // leaf a little into the gutter. The valley deepens as pages pile up on
45
+ // the other side and pull the spine down (by the binding's own dip and
46
+ // a share of the thinner stack).
47
+ const low = Math.min(tLeft, tRight);
48
+ const high = Math.max(tLeft, tRight);
49
+ const dip = (b.dipMm * pxPerMm + 0.35 * low) * (binding === "layflat" ? 0.1 : 1);
50
+ const meet = Math.max(0, low + (high - low) * b.spine - dip);
51
+ const make = (t, flat) => {
52
+ // The shoulder is as wide as the leaves have to drop (or rise) to the
53
+ // binding, plus the binding's own bend.
54
+ const side = Math.min(0.3 * W, b.gutter * W * 0.6 + 1.2 * Math.abs(t - meet));
55
+ const p = { t, meet: flat ? t : meet, g: Math.max(side, 0.02 * W), power: b.power };
56
+ // Arc length along x, then x and z at even steps of arc length.
57
+ const fine = SAMPLES * 4;
58
+ const dx = (1.2 * W) / fine;
59
+ const sx = [0];
60
+ const sz = [heightAt(p, 0)];
61
+ const ss = [0];
62
+ for (let i = 1; i <= fine; i++) {
63
+ const x = i * dx;
64
+ const z = heightAt(p, x);
65
+ ss.push(ss[i - 1] + Math.hypot(dx, z - sz[i - 1]));
66
+ sx.push(x);
67
+ sz.push(z);
68
+ }
69
+ const step = (1.1 * W) / SAMPLES;
70
+ const xs = new Float32Array(SAMPLES + 1);
71
+ const zs = new Float32Array(SAMPLES + 1);
72
+ let j = 0;
73
+ for (let i = 0; i <= SAMPLES; i++) {
74
+ const s = i * step;
75
+ while (j < fine - 1 && ss[j + 1] < s)
76
+ j++;
77
+ const f = Math.min(1, Math.max(0, (s - ss[j]) / (ss[j + 1] - ss[j] || 1)));
78
+ xs[i] = sx[j] + (sx[j + 1] - sx[j]) * f;
79
+ zs[i] = zb + sz[j] + (sz[j + 1] - sz[j]) * f;
80
+ }
81
+ return { ...p, zb, xs, zs, step };
82
+ };
83
+ return { left: make(tLeft, flatLeft), right: make(tRight, flatRight), board: zb };
84
+ }
85
+ /** Where arc length `s` of a profile lies: [x, z, dz/dx]. */
86
+ export function along(p, s) {
87
+ const f = Math.max(0, s) / p.step;
88
+ const i = Math.min(p.xs.length - 2, Math.floor(f));
89
+ const t = Math.min(1.5, f - i);
90
+ const x = p.xs[i] + (p.xs[i + 1] - p.xs[i]) * t;
91
+ const z = p.zs[i] + (p.zs[i + 1] - p.zs[i]) * t;
92
+ const slope = (p.zs[i + 1] - p.zs[i]) / (p.xs[i + 1] - p.xs[i] || 1e-6);
93
+ return [x, z, slope];
94
+ }
95
+ /** A lower leaf (fraction `lambda` of the way up the stack) at arc
96
+ * length s, approximately: the top curve scaled down to the base. */
97
+ export function alongLayer(p, s, lambda) {
98
+ const [x] = along(p, s);
99
+ return [x, p.zb + heightAt(p, x, lambda)];
100
+ }
101
+ /**
102
+ * Ambient light reaching each point of the open pages, from the shape of
103
+ * the book's cross-section: the fraction of the sky (cosine weighted) the
104
+ * other page and the gutter leave open. This is the darkening along the
105
+ * gutter, found from the geometry rather than painted. `n` samples across
106
+ * one page, from the spine out.
107
+ */
108
+ export function gutterOcclusion(own, other, W, n = 64) {
109
+ const out = new Float32Array(n + 1);
110
+ // Occluders: the other page's surface (at negative x) and this page's own
111
+ // surface between the point and the spine.
112
+ const pts = [];
113
+ for (let i = 0; i <= 96; i++) {
114
+ const [x, z] = along(other, (i / 96) * W);
115
+ pts.push([-x, z]);
116
+ }
117
+ const ownPts = [];
118
+ for (let i = 0; i <= 96; i++) {
119
+ const [x, z] = along(own, (i / 96) * W);
120
+ ownPts.push([x, z]);
121
+ }
122
+ for (let k = 0; k <= n; k++) {
123
+ const [x, z, slope] = along(own, (k / n) * W);
124
+ const tau = Math.atan(slope);
125
+ // Highest elevation (angle from +x) of an occluder towards the spine.
126
+ let spine = 0;
127
+ for (const [px, pz] of pts)
128
+ spine = Math.max(spine, Math.atan2(pz - z, x - px));
129
+ for (const [px, pz] of ownPts)
130
+ if (px < x - 1e-3)
131
+ spine = Math.max(spine, Math.atan2(pz - z, x - px));
132
+ // Towards the fore-edge the page's own surface, if it rises.
133
+ let fore = tau;
134
+ for (const [px, pz] of ownPts)
135
+ if (px > x + 1e-3)
136
+ fore = Math.max(fore, Math.atan2(pz - z, px - x));
137
+ const nAngle = tau + Math.PI / 2;
138
+ const a = Math.max(tau, fore);
139
+ const b = Math.min(tau + Math.PI, Math.PI - spine);
140
+ out[k] = b > a ? Math.max(0, (Math.sin(b - nAngle) - Math.sin(a - nAngle)) / 2) : 0;
141
+ }
142
+ return out;
143
+ }
144
+ /**
145
+ * The page block's visible sides for one stack: the head and tail (the
146
+ * fan of leaves between the cover and the top leaf) and the fore-edge.
147
+ * Each vertex carries `layer`: the leaf it lies on, counted up from the
148
+ * cover, so the shader draws the edges of the leaves.
149
+ */
150
+ export function stackGeometry(p, side, W, H, leaves) {
151
+ const NS = 48;
152
+ const NL = Math.max(2, Math.min(24, leaves + 1));
153
+ const pos = [];
154
+ const nrm = [];
155
+ const layer = [];
156
+ const lam = [];
157
+ const idx = [];
158
+ const quadGrid = (cols, rows, at, normal, flip) => {
159
+ const base = pos.length / 3;
160
+ for (let r = 0; r <= rows; r++)
161
+ for (let c = 0; c <= cols; c++) {
162
+ const [x, y, z, l] = at(c, r);
163
+ pos.push(x, y, z);
164
+ nrm.push(...normal);
165
+ layer.push(l);
166
+ lam.push(leaves > 0 ? l / leaves : r / Math.max(1, rows));
167
+ }
168
+ for (let r = 0; r < rows; r++)
169
+ for (let c = 0; c < cols; c++) {
170
+ const a = base + r * (cols + 1) + c;
171
+ const b = a + 1;
172
+ const d = a + cols + 1;
173
+ const e = d + 1;
174
+ if (flip)
175
+ idx.push(a, d, b, b, d, e);
176
+ else
177
+ idx.push(a, b, d, b, e, d);
178
+ }
179
+ };
180
+ for (const end of [1, -1]) {
181
+ // Head (y = H/2, facing +y) and tail (facing −y).
182
+ quadGrid(NS, NL - 1, (c, r) => {
183
+ const lambda = r / (NL - 1);
184
+ const [x, z] = alongLayer(p, (c / NS) * W, lambda);
185
+ return [side * x, (end * H) / 2, z, lambda * leaves];
186
+ }, [0, end, 0], (end > 0) !== (side > 0));
187
+ }
188
+ // The fore-edge: each leaf ends where its arc length runs out.
189
+ quadGrid(1, NL - 1, (c, r) => {
190
+ const lambda = r / (NL - 1);
191
+ const [x, z] = alongLayer(p, W, lambda);
192
+ return [side * x, (c ? -1 : 1) * (H / 2), z, lambda * leaves];
193
+ }, [side, 0, 0], side < 0);
194
+ const g = new BufferGeometry();
195
+ g.setAttribute("position", new BufferAttribute(new Float32Array(pos), 3));
196
+ g.setAttribute("normal", new BufferAttribute(new Float32Array(nrm), 3));
197
+ g.setAttribute("layer", new BufferAttribute(new Float32Array(layer), 1));
198
+ // How far up the block (0 at its base, 1 at its top), for the bands a
199
+ // board takes in it.
200
+ g.setAttribute("lambda", new BufferAttribute(new Float32Array(lam), 1));
201
+ g.setIndex(idx);
202
+ return g;
203
+ }
204
+ //# sourceMappingURL=bookGeometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bookGeometry.js","sourceRoot":"","sources":["../src/bookGeometry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { type Texture } from "three";
2
+ import type { DeskKind } from "./procedural";
3
+ export type { DeskKind };
4
+ /** A desk surface's image maps and the width of one tile (mm). The colour
5
+ * map is sRGB, the others linear; all repeat and carry mipmaps. `tint`
6
+ * is the manifest's suggested base colour (to multiply), when it has one. */
7
+ export interface DeskMaps {
8
+ color: Texture;
9
+ normal: Texture;
10
+ roughness: Texture;
11
+ ao?: Texture;
12
+ tileMm: number;
13
+ tint?: string;
14
+ }
15
+ /**
16
+ * The image maps of a desk surface, from `${baseUrl}/manifest.json` and the
17
+ * files it lists (paths relative to `baseUrl`). The manifest is fetched once
18
+ * per `baseUrl` and the maps once per `baseUrl` + kind; every caller of the
19
+ * same pair shares the same textures, so do not dispose them per mesh.
20
+ * Anisotropy is left to the caller (it needs the renderer). Resolves null on
21
+ * any failure, after disposing whatever maps had loaded.
22
+ */
23
+ export declare function loadDeskMaps(kind: DeskKind, baseUrl: string): Promise<DeskMaps | null>;
24
+ //# sourceMappingURL=deskTextures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deskTextures.d.ts","sourceRoot":"","sources":["../src/deskTextures.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,OAAO,EACb,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,YAAY,EAAE,QAAQ,EAAE,CAAC;AAQzB;;8EAE8E;AAC9E,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AA4ED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAqBtF"}
@@ -0,0 +1,88 @@
1
+ import { LinearFilter, LinearMipmapLinearFilter, NoColorSpace, RepeatWrapping, SRGBColorSpace, TextureLoader, } from "three";
2
+ const manifests = new Map();
3
+ const deskMaps = new Map();
4
+ const trimSlash = (url) => url.replace(/\/+$/, "");
5
+ /** Remember a promise, but forget it if it settles on null so a later call
6
+ * tries again (a dropped connection should not cost the session its desk). */
7
+ function cached(cache, key, make) {
8
+ let p = cache.get(key);
9
+ if (!p) {
10
+ p = make().catch(() => null);
11
+ cache.set(key, p);
12
+ p.then((v) => {
13
+ if (v === null && cache.get(key) === p)
14
+ cache.delete(key);
15
+ });
16
+ }
17
+ return p;
18
+ }
19
+ function isManifest(json) {
20
+ return typeof json === "object" && json !== null && typeof json.kinds === "object" && json.kinds !== null;
21
+ }
22
+ function loadManifest(base) {
23
+ return cached(manifests, base, async () => {
24
+ const res = await fetch(`${base}/manifest.json`);
25
+ if (!res.ok)
26
+ return null;
27
+ const json = await res.json();
28
+ return isManifest(json) ? json : null;
29
+ });
30
+ }
31
+ function loadTexture(loader, url, srgb) {
32
+ return new Promise((resolve, reject) => {
33
+ loader.load(url, (tex) => {
34
+ tex.colorSpace = srgb ? SRGBColorSpace : NoColorSpace;
35
+ tex.wrapS = tex.wrapT = RepeatWrapping;
36
+ tex.generateMipmaps = true;
37
+ tex.minFilter = LinearMipmapLinearFilter;
38
+ tex.magFilter = LinearFilter;
39
+ tex.needsUpdate = true;
40
+ resolve(tex);
41
+ }, undefined, reject);
42
+ });
43
+ }
44
+ function validEntry(entry) {
45
+ return (!!entry &&
46
+ typeof entry.files === "object" &&
47
+ entry.files !== null &&
48
+ typeof entry.files.color === "string" &&
49
+ typeof entry.files.normal === "string" &&
50
+ typeof entry.files.roughness === "string" &&
51
+ typeof entry.tileMm === "number" &&
52
+ entry.tileMm > 0);
53
+ }
54
+ /**
55
+ * The image maps of a desk surface, from `${baseUrl}/manifest.json` and the
56
+ * files it lists (paths relative to `baseUrl`). The manifest is fetched once
57
+ * per `baseUrl` and the maps once per `baseUrl` + kind; every caller of the
58
+ * same pair shares the same textures, so do not dispose them per mesh.
59
+ * Anisotropy is left to the caller (it needs the renderer). Resolves null on
60
+ * any failure, after disposing whatever maps had loaded.
61
+ */
62
+ export function loadDeskMaps(kind, baseUrl) {
63
+ const base = trimSlash(baseUrl);
64
+ return cached(deskMaps, `${base}\n${kind}`, async () => {
65
+ const manifest = await loadManifest(base);
66
+ const entry = manifest?.kinds[kind];
67
+ if (!validEntry(entry))
68
+ return null;
69
+ const loader = new TextureLoader();
70
+ const { files } = entry;
71
+ const urls = [files.color, files.normal, files.roughness, ...(typeof files.ao === "string" ? [files.ao] : [])];
72
+ const settled = await Promise.allSettled(urls.map((f, i) => loadTexture(loader, `${base}/${f}`, i === 0)));
73
+ const loaded = settled.flatMap((s) => (s.status === "fulfilled" ? [s.value] : []));
74
+ if (loaded.length !== urls.length) {
75
+ for (const t of loaded)
76
+ t.dispose();
77
+ return null;
78
+ }
79
+ const [color, normal, roughness, ao] = loaded;
80
+ const maps = { color, normal, roughness, tileMm: entry.tileMm };
81
+ if (ao)
82
+ maps.ao = ao;
83
+ if (typeof entry.tint === "string")
84
+ maps.tint = entry.tint;
85
+ return maps;
86
+ });
87
+ }
88
+ //# sourceMappingURL=deskTextures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deskTextures.js","sourceRoot":"","sources":["../src/deskTextures.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { Color, Scene, Vector3 } from "three";
2
+ export type EnvironmentKind = "studio" | "daylight" | "lamp" | "overcast" | "night";
3
+ /** A lighting environment: a small scene of emitters the reflections and
4
+ * the soft (image-based) light are taken from, and the key light that
5
+ * casts the shadows, placed where the scene's brightest emitter is. */
6
+ export interface Environment {
7
+ scene: Scene;
8
+ /** Towards the key light (book coordinates: x right, y to the head of
9
+ * the page, z up from the desk). */
10
+ key: Vector3;
11
+ keyColor: Color;
12
+ /** The key light's strength. */
13
+ keyIntensity: number;
14
+ /** How soft the key light's shadows are (its apparent size, 0 … 1). */
15
+ softness: number;
16
+ /** Exposure, so paper at rest reads as its own colour. */
17
+ exposure: number;
18
+ }
19
+ /**
20
+ * The environments: a photographer's studio (a large softbox above left,
21
+ * a fill on the right, a neutral grey room), daylight from a tall window
22
+ * on the left, a warm reading lamp in a dim room, an even overcast sky,
23
+ * and night (a lamp, a faint cool window). Coordinates are in book units
24
+ * where the book is about 2 across, centred at the origin on the desk.
25
+ */
26
+ export declare function environment(kind: EnvironmentKind): Environment;
27
+ //# sourceMappingURL=environments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environments.d.ts","sourceRoot":"","sources":["../src/environments.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,EAIL,KAAK,EAEL,OAAO,EAER,MAAM,OAAO,CAAC;AAEf,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC;AAEpF;;wEAEwE;AACxE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,KAAK,CAAC;IACb;yCACqC;IACrC,GAAG,EAAE,OAAO,CAAC;IACb,QAAQ,EAAE,KAAK,CAAC;IAChB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;CAClB;AA+BD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,CAwE9D"}