postext-folio 0.7.1 → 0.8.1
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/README.md +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/loupe.d.ts +46 -0
- package/dist/loupe.d.ts.map +1 -0
- package/dist/loupe.js +220 -0
- package/dist/loupe.js.map +1 -0
- package/dist/pageFlip.d.ts +29 -0
- package/dist/pageFlip.d.ts.map +1 -1
- package/dist/pageFlip.js +112 -2
- package/dist/pageFlip.js.map +1 -1
- package/dist/postext.d.ts.map +1 -1
- package/dist/postext.js +122 -1
- package/dist/postext.js.map +1 -1
- package/dist/styles.d.ts +1 -1
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +15 -1
- package/dist/styles.js.map +1 -1
- package/dist/viewer.d.ts +39 -3
- package/dist/viewer.d.ts.map +1 -1
- package/dist/viewer.js +205 -13
- package/dist/viewer.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -120,9 +120,9 @@ book.goToPage(2);
|
|
|
120
120
|
<table>
|
|
121
121
|
<thead><tr><th>Export</th><th>What it does</th></tr></thead>
|
|
122
122
|
<tbody>
|
|
123
|
-
<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>interaction</code> (what the left button does: <code>'hand'</code> turns pages, <code>'orbit'</code> turns the view, <code>'select'</code> leaves the pointer to the host), <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>covers</code>, <code>spineImage</code>, <code>textureBaseUrl</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>
|
|
123
|
+
<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>interaction</code> (what the left button does: <code>'hand'</code> turns pages, <code>'orbit'</code> turns the view, <code>'select'</code> leaves the pointer to the host, <code>'magnify'</code> holds a magnifying glass over the book: wheel, + and − zoom, Esc puts it away; the pointer selects too, <code>pageAt</code> giving the point under the glass's centre), <code>magnifier</code> (<code>{ zoom, diameter }</code>), <code>detail</code> (<code>{ paint(index, deviceWidth), release() }</code>: sharper paintings for the glass; <code>createFolioFromDocument</code> supplies its own), <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>covers</code>, <code>spineImage</code>, <code>textureBaseUrl</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>
|
|
124
124
|
<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>, <code>videos</code>, <code>videoUrl</code>, <code>onVideo</code>, and on the viewer <code>setDocument(doc)</code>, <code>toggleVideoAt(point)</code> and <code>stopVideo()</code> (stops every video on the pages).</td></tr>
|
|
125
|
-
<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>resetView()</code> (eases the view back to the settings' <code>tilt</code> and <code>yaw</code> after the reader orbited it with a right-drag), <code>getView()</code> (the view as seen now, <code>{ tilt, yaw }</code> in degrees, to store as the settings), <code>setInteraction(mode)</code>, <code>pageAt(event)</code> (the page under a pointer and where on it, <code>{ page, x, y }</code>, on the book as seen), <code>pointOnScreen(point)</code> (the reverse, for drawing a caret or a selection), <code>refreshPage(src)</code>, <code>setPageVideo(video)</code> (draws a playing <code><video></code> on a page, at a place given in page fractions), <code>setPageVideos(videos)</code> (several at once, in place of those shown before), <code>onPageClick</code> / <code>isPageAction</code> options (a click on a page that acts instead of turning it), <code>pageSize</code>, <code>state</code> (<code>{ spread, pages }</code>), <code>element</code>, <code>dispose()</code>.</td></tr>
|
|
125
|
+
<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>resetView()</code> (eases the view back to the settings' <code>tilt</code> and <code>yaw</code> after the reader orbited it with a right-drag), <code>getView()</code> (the view as seen now, <code>{ tilt, yaw }</code> in degrees, to store as the settings), <code>setInteraction(mode)</code>, <code>setMagnification(zoom)</code> (null for the default), <code>pageAt(event)</code> (the page under a pointer and where on it, <code>{ page, x, y }</code>, on the book as seen), <code>pointOnScreen(point)</code> (the reverse, for drawing a caret or a selection), <code>refreshPage(src)</code>, <code>setPageVideo(video)</code> (draws a playing <code><video></code> on a page, at a place given in page fractions), <code>setPageVideos(videos)</code> (several at once, in place of those shown before), <code>onPageClick</code> / <code>isPageAction</code> options (a click on a page that acts instead of turning it), <code>pageSize</code>, <code>state</code> (<code>{ spread, pages }</code>), <code>element</code>, <code>dispose()</code>.</td></tr>
|
|
126
126
|
<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>
|
|
127
127
|
<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>
|
|
128
128
|
<tr><td><code>pageVideoSpots(page, doc)</code>, <code>attachVideoSource(el, url, opts)</code></td><td>Where each video lies on a page as bound (turned, unmirrored, cropped as printed), and a source for a <code><video></code>: a file as it is, an HLS stream natively or through hls.js.</td></tr>
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { MAX_PAGE_VIDEOS } from "./videoAtlas";
|
|
|
5
5
|
export { spreadsOf, spreadOfPage } from "./spreads";
|
|
6
6
|
export type { Spread } from "./spreads";
|
|
7
7
|
export { createFolio } from "./viewer";
|
|
8
|
-
export type { FolioAppearance, FolioInteraction, FolioLabels, FolioOptions, FolioPage, FolioPagePoint, FolioPageVideo, FolioState, FolioViewer } from "./viewer";
|
|
8
|
+
export type { FolioAppearance, FolioInteraction, FolioLabels, FolioMagnifier, FolioOptions, FolioPageDetail, FolioPage, FolioPagePoint, FolioPageVideo, FolioState, FolioViewer } from "./viewer";
|
|
9
9
|
export type { FlipAppearance } from "./pageFlip";
|
|
10
10
|
export { paperSpec, type PaperSpec } from "./paper";
|
|
11
11
|
export { loadDeskMaps, type DeskMaps } from "./deskTextures";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACpD,YAAY,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACpD,YAAY,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAClM,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACtE,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/loupe.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { PerspectiveCamera, Scene, type WebGLRenderer } from "three";
|
|
2
|
+
/** A magnifying glass held over the book (#527): where its centre is on
|
|
3
|
+
* the canvas (device px from the top left), the radius of its glass and
|
|
4
|
+
* the width of its rim (device px), and how much it magnifies at its
|
|
5
|
+
* centre. */
|
|
6
|
+
export interface LoupeView {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
radius: number;
|
|
10
|
+
rim: number;
|
|
11
|
+
zoom: number;
|
|
12
|
+
}
|
|
13
|
+
/** The share of the lens's field its centre takes: the sample radius at a
|
|
14
|
+
* point r (0 … 1) of the glass is r (A + (1 − A) r²), so the centre
|
|
15
|
+
* magnifies 1 / A times more than a flat lens of the same field and the
|
|
16
|
+
* rim gathers the field round it in, curving it as a real glass does. */
|
|
17
|
+
export declare const LOUPE_A = 0.58;
|
|
18
|
+
/** The magnifying glass: its picture of the book and the glass drawn over
|
|
19
|
+
* the screen. */
|
|
20
|
+
export declare class Loupe {
|
|
21
|
+
private target;
|
|
22
|
+
private camera;
|
|
23
|
+
private overlay;
|
|
24
|
+
private overlayCamera;
|
|
25
|
+
private mesh;
|
|
26
|
+
constructor();
|
|
27
|
+
/** How far from its centre (device px) the field the glass shows
|
|
28
|
+
* reaches on the screen. */
|
|
29
|
+
static field(view: LoupeView): number;
|
|
30
|
+
/**
|
|
31
|
+
* Draws the lens's picture: the book from the eye (`camera`, the main
|
|
32
|
+
* view) through the lens's field only, a square around the lens's centre
|
|
33
|
+
* as wide as the field, into a picture with about one texel for each
|
|
34
|
+
* screen pixel at the glass's centre (more towards the rim, which
|
|
35
|
+
* gathers the field in: no multisampling, which would cost more than the
|
|
36
|
+
* book's own frame). `before` and `after` run round the
|
|
37
|
+
* draw (the host swaps in its sharper paintings and turns off what reads
|
|
38
|
+
* the main view's screen).
|
|
39
|
+
*/
|
|
40
|
+
capture(renderer: WebGLRenderer, scene: Scene, camera: PerspectiveCamera, view: LoupeView, before: () => void, after: () => void): void;
|
|
41
|
+
/** Draws the glass and its rim over the screen (`width` × `height`
|
|
42
|
+
* device px), its picture as `capture` drew it last. */
|
|
43
|
+
draw(renderer: WebGLRenderer, width: number, height: number, view: LoupeView): void;
|
|
44
|
+
dispose(): void;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=loupe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loupe.d.ts","sourceRoot":"","sources":["../src/loupe.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,iBAAiB,EAEjB,KAAK,EAKL,KAAK,aAAa,EACnB,MAAM,OAAO,CAAC;AAEf;;;cAGc;AACd,MAAM,WAAW,SAAS;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;0EAG0E;AAC1E,eAAO,MAAM,OAAO,OAAO,CAAC;AAiG5B;kBACkB;AAClB,qBAAa,KAAK;IAChB,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,IAAI,CAAsC;;IA2BlD;iCAC6B;IAC7B,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM;IAIrC;;;;;;;;;OASG;IACH,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE,KAAK,EAAE,MAAM,IAAI;IA+BhI;6DACyD;IACzD,IAAI,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS;IA+B5E,OAAO;CAMR"}
|
package/dist/loupe.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { HalfFloatType, LinearFilter, Mesh, NormalBlending, OrthographicCamera, PerspectiveCamera, PlaneGeometry, Scene, ShaderMaterial, Vector2, WebGLRenderTarget, } from "three";
|
|
2
|
+
/** The share of the lens's field its centre takes: the sample radius at a
|
|
3
|
+
* point r (0 … 1) of the glass is r (A + (1 − A) r²), so the centre
|
|
4
|
+
* magnifies 1 / A times more than a flat lens of the same field and the
|
|
5
|
+
* rim gathers the field round it in, curving it as a real glass does. */
|
|
6
|
+
export const LOUPE_A = 0.58;
|
|
7
|
+
/** The biggest picture the lens is drawn into (device px a side). */
|
|
8
|
+
const MAX_SIDE = 2048;
|
|
9
|
+
const VERTEX = /* glsl */ `
|
|
10
|
+
uniform vec2 uCenter;
|
|
11
|
+
varying vec2 vPos;
|
|
12
|
+
void main() {
|
|
13
|
+
vec4 world = modelMatrix * vec4(position, 1.0);
|
|
14
|
+
vPos = world.xy - uCenter;
|
|
15
|
+
gl_Position = projectionMatrix * viewMatrix * world;
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
/**
|
|
19
|
+
* The glass and its rim, over the book as the screen shows it. The glass
|
|
20
|
+
* shows the lens's own picture of the book (drawn from the reader's eye
|
|
21
|
+
* through the lens's field, in linear light), refracted: magnified most at
|
|
22
|
+
* the centre, curving in towards the rim, the colours parting a little
|
|
23
|
+
* there; its edge darkens where the light bends away, and it catches a
|
|
24
|
+
* soft window and a bright arc on the side the light comes from. The rim
|
|
25
|
+
* is black plastic with a rounded section, lit by the same light, and
|
|
26
|
+
* casts a soft shadow on the book. Toned for the screen as the book is.
|
|
27
|
+
*/
|
|
28
|
+
const FRAGMENT = /* glsl */ `
|
|
29
|
+
uniform sampler2D uScene;
|
|
30
|
+
uniform float uGlass;
|
|
31
|
+
uniform float uRim;
|
|
32
|
+
uniform float uShadow;
|
|
33
|
+
uniform vec2 uShadowOffset;
|
|
34
|
+
uniform float uA;
|
|
35
|
+
varying vec2 vPos;
|
|
36
|
+
|
|
37
|
+
vec4 over(vec4 src, vec4 dst) { return src + dst * (1.0 - src.a); }
|
|
38
|
+
|
|
39
|
+
void main() {
|
|
40
|
+
float d = length(vPos);
|
|
41
|
+
float R = uGlass;
|
|
42
|
+
float outer = R + uRim;
|
|
43
|
+
vec2 dir = d > 0.0 ? vPos / d : vec2(0.0);
|
|
44
|
+
vec2 light = normalize(vec2(-0.55, 0.65));
|
|
45
|
+
float lit = dot(dir, light);
|
|
46
|
+
|
|
47
|
+
// The rim's shadow on the page (premultiplied, as everything here).
|
|
48
|
+
float sd = length(vPos - uShadowOffset);
|
|
49
|
+
vec4 color = vec4(0.0, 0.0, 0.0, 0.42 * (1.0 - smoothstep(outer - 0.6 * uShadow, outer + uShadow, sd)));
|
|
50
|
+
// A thin dark edge round the rim, where it meets the page.
|
|
51
|
+
color = over(vec4(0.0, 0.0, 0.0, 0.35 * (1.0 - smoothstep(outer, outer + 2.0, d))), color);
|
|
52
|
+
|
|
53
|
+
// The rim: black plastic, a rounded section a little flatter on top.
|
|
54
|
+
if (d < outer + 1.0) {
|
|
55
|
+
float t = clamp((d - R) / uRim, 0.0, 1.0);
|
|
56
|
+
float c = 2.0 * t - 1.0;
|
|
57
|
+
vec3 n = normalize(vec3(dir * c * 1.15, sqrt(max(0.0, 1.0 - c * c)) + 0.2));
|
|
58
|
+
vec3 V = vec3(0.0, 0.0, 1.0);
|
|
59
|
+
vec3 L = normalize(vec3(light * 0.75, 0.66));
|
|
60
|
+
vec3 L2 = normalize(vec3(-light * 0.7, 0.7));
|
|
61
|
+
float diff = max(dot(n, L), 0.0);
|
|
62
|
+
float nh = max(dot(n, normalize(L + V)), 0.0);
|
|
63
|
+
float nh2 = max(dot(n, normalize(L2 + V)), 0.0);
|
|
64
|
+
vec3 plastic = vec3(0.008) + vec3(0.02) * diff + vec3(0.9) * pow(nh, 90.0) + vec3(0.06) * pow(nh, 10.0) + vec3(0.1) * pow(nh2, 50.0);
|
|
65
|
+
// The groove the glass sits in.
|
|
66
|
+
plastic *= mix(0.25, 1.0, smoothstep(0.0, 0.14, t));
|
|
67
|
+
float a = 1.0 - smoothstep(outer - 1.0, outer + 0.5, d);
|
|
68
|
+
color = over(vec4(plastic * a, a), color);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// The glass.
|
|
72
|
+
if (d < R + 1.0) {
|
|
73
|
+
float r = clamp(d / R, 0.0, 1.0);
|
|
74
|
+
float s = r * (uA + (1.0 - uA) * r * r);
|
|
75
|
+
float ca = 0.012 * r * r * r;
|
|
76
|
+
vec2 at = dir * 0.5;
|
|
77
|
+
vec4 g = texture2D(uScene, 0.5 + at * s);
|
|
78
|
+
g.r = texture2D(uScene, 0.5 + at * s * (1.0 + ca)).r;
|
|
79
|
+
g.b = texture2D(uScene, 0.5 + at * s * (1.0 - ca)).b;
|
|
80
|
+
// Darker towards the edge, where the light bends away, and in the
|
|
81
|
+
// rim's shadow on the side away from the light.
|
|
82
|
+
g.rgb *= mix(1.0, 0.7, smoothstep(0.8, 1.0, r));
|
|
83
|
+
g.rgb *= 1.0 - 0.3 * smoothstep(0.9, 1.0, r) * (0.5 - 0.5 * lit);
|
|
84
|
+
// What the glass mirrors: a soft window and an arc on its lit edge.
|
|
85
|
+
vec2 q = (vPos / R - vec2(-0.36, 0.42)) * vec2(2.3, 3.4);
|
|
86
|
+
float spot = 0.07 * exp(-dot(q, q));
|
|
87
|
+
float arc = 0.4 * smoothstep(0.85, 0.96, r) * (1.0 - smoothstep(0.96, 1.0, r)) * max(0.0, lit);
|
|
88
|
+
vec4 glass = vec4(g.rgb + vec3(spot + arc), clamp(g.a + spot + arc, 0.0, 1.0));
|
|
89
|
+
float a = 1.0 - smoothstep(R - 1.0, R + 0.5, d);
|
|
90
|
+
color = mix(color, glass, a);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (color.a <= 0.0) discard;
|
|
94
|
+
gl_FragColor = vec4(color.rgb / color.a, color.a);
|
|
95
|
+
#include <tonemapping_fragment>
|
|
96
|
+
#include <colorspace_fragment>
|
|
97
|
+
gl_FragColor.rgb *= gl_FragColor.a;
|
|
98
|
+
}
|
|
99
|
+
`;
|
|
100
|
+
/** The magnifying glass: its picture of the book and the glass drawn over
|
|
101
|
+
* the screen. */
|
|
102
|
+
export class Loupe {
|
|
103
|
+
target = null;
|
|
104
|
+
camera = new PerspectiveCamera();
|
|
105
|
+
overlay = new Scene();
|
|
106
|
+
overlayCamera = new OrthographicCamera(0, 1, 1, 0, -1, 1);
|
|
107
|
+
mesh;
|
|
108
|
+
constructor() {
|
|
109
|
+
const material = new ShaderMaterial({
|
|
110
|
+
vertexShader: VERTEX,
|
|
111
|
+
fragmentShader: FRAGMENT,
|
|
112
|
+
uniforms: {
|
|
113
|
+
uScene: { value: null },
|
|
114
|
+
uCenter: { value: new Vector2() },
|
|
115
|
+
uGlass: { value: 1 },
|
|
116
|
+
uRim: { value: 1 },
|
|
117
|
+
uShadow: { value: 1 },
|
|
118
|
+
uShadowOffset: { value: new Vector2() },
|
|
119
|
+
uA: { value: LOUPE_A },
|
|
120
|
+
},
|
|
121
|
+
transparent: true,
|
|
122
|
+
premultipliedAlpha: true,
|
|
123
|
+
blending: NormalBlending,
|
|
124
|
+
depthTest: false,
|
|
125
|
+
depthWrite: false,
|
|
126
|
+
toneMapped: true,
|
|
127
|
+
});
|
|
128
|
+
this.mesh = new Mesh(new PlaneGeometry(1, 1), material);
|
|
129
|
+
this.mesh.frustumCulled = false;
|
|
130
|
+
this.overlay.add(this.mesh);
|
|
131
|
+
}
|
|
132
|
+
/** How far from its centre (device px) the field the glass shows
|
|
133
|
+
* reaches on the screen. */
|
|
134
|
+
static field(view) {
|
|
135
|
+
return view.radius / (LOUPE_A * Math.max(1, view.zoom));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Draws the lens's picture: the book from the eye (`camera`, the main
|
|
139
|
+
* view) through the lens's field only, a square around the lens's centre
|
|
140
|
+
* as wide as the field, into a picture with about one texel for each
|
|
141
|
+
* screen pixel at the glass's centre (more towards the rim, which
|
|
142
|
+
* gathers the field in: no multisampling, which would cost more than the
|
|
143
|
+
* book's own frame). `before` and `after` run round the
|
|
144
|
+
* draw (the host swaps in its sharper paintings and turns off what reads
|
|
145
|
+
* the main view's screen).
|
|
146
|
+
*/
|
|
147
|
+
capture(renderer, scene, camera, view, before, after) {
|
|
148
|
+
const max = Math.min(MAX_SIDE, renderer.capabilities.maxRenderbufferSize ?? MAX_SIDE);
|
|
149
|
+
const side = Math.max(16, Math.min(max, Math.ceil((2 * view.radius) / LOUPE_A)));
|
|
150
|
+
let target = this.target;
|
|
151
|
+
if (!target) {
|
|
152
|
+
target = new WebGLRenderTarget(side, side, { type: HalfFloatType, minFilter: LinearFilter, magFilter: LinearFilter, generateMipmaps: false });
|
|
153
|
+
this.target = target;
|
|
154
|
+
}
|
|
155
|
+
else if (target.width !== side)
|
|
156
|
+
target.setSize(side, side);
|
|
157
|
+
const field = Loupe.field(view);
|
|
158
|
+
const cam = this.camera;
|
|
159
|
+
cam.copy(camera);
|
|
160
|
+
const v = camera.view;
|
|
161
|
+
const full = v ? { w: v.fullWidth, h: v.fullHeight, x: v.offsetX, y: v.offsetY } : { w: 1, h: 1, x: 0, y: 0 };
|
|
162
|
+
cam.setViewOffset(full.w, full.h, full.x + view.x - field, full.y + view.y - field, 2 * field, 2 * field);
|
|
163
|
+
cam.updateProjectionMatrix();
|
|
164
|
+
cam.updateMatrixWorld();
|
|
165
|
+
const shadows = renderer.shadowMap.autoUpdate;
|
|
166
|
+
// The key's shadow map as the main view drew it.
|
|
167
|
+
renderer.shadowMap.autoUpdate = false;
|
|
168
|
+
before();
|
|
169
|
+
try {
|
|
170
|
+
renderer.setRenderTarget(target);
|
|
171
|
+
renderer.clear();
|
|
172
|
+
renderer.render(scene, cam);
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
renderer.setRenderTarget(null);
|
|
176
|
+
renderer.shadowMap.autoUpdate = shadows;
|
|
177
|
+
after();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/** Draws the glass and its rim over the screen (`width` × `height`
|
|
181
|
+
* device px), its picture as `capture` drew it last. */
|
|
182
|
+
draw(renderer, width, height, view) {
|
|
183
|
+
if (!this.target)
|
|
184
|
+
return;
|
|
185
|
+
const cam = this.overlayCamera;
|
|
186
|
+
if (cam.right !== width || cam.top !== height) {
|
|
187
|
+
cam.right = width;
|
|
188
|
+
cam.top = height;
|
|
189
|
+
cam.updateProjectionMatrix();
|
|
190
|
+
}
|
|
191
|
+
const u = this.mesh.material.uniforms;
|
|
192
|
+
const shadow = Math.max(6, view.rim * 1.6);
|
|
193
|
+
const offset = new Vector2(view.rim * 0.35, -view.rim * 0.55);
|
|
194
|
+
const reach = view.radius + view.rim + shadow + offset.length() + 2;
|
|
195
|
+
// The overlay's y runs up from the bottom of the screen.
|
|
196
|
+
const cx = view.x;
|
|
197
|
+
const cy = height - view.y;
|
|
198
|
+
this.mesh.position.set(cx, cy, 0);
|
|
199
|
+
this.mesh.scale.set(2 * reach, 2 * reach, 1);
|
|
200
|
+
this.mesh.updateMatrixWorld();
|
|
201
|
+
u.uScene.value = this.target.texture;
|
|
202
|
+
u.uCenter.value.set(cx, cy);
|
|
203
|
+
u.uGlass.value = view.radius;
|
|
204
|
+
u.uRim.value = view.rim;
|
|
205
|
+
u.uShadow.value = shadow;
|
|
206
|
+
u.uShadowOffset.value.copy(offset);
|
|
207
|
+
const clear = renderer.autoClear;
|
|
208
|
+
renderer.autoClear = false;
|
|
209
|
+
renderer.setRenderTarget(null);
|
|
210
|
+
renderer.render(this.overlay, cam);
|
|
211
|
+
renderer.autoClear = clear;
|
|
212
|
+
}
|
|
213
|
+
dispose() {
|
|
214
|
+
this.target?.dispose();
|
|
215
|
+
this.target = null;
|
|
216
|
+
this.mesh.geometry.dispose();
|
|
217
|
+
this.mesh.material.dispose();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=loupe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loupe.js","sourceRoot":"","sources":["../src/loupe.ts"],"names":[],"mappings":"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"}
|
package/dist/pageFlip.d.ts
CHANGED
|
@@ -261,6 +261,12 @@ export declare class PageFlipper {
|
|
|
261
261
|
* tilted by `pitch` (radians from straight above); null: the
|
|
262
262
|
* settings' `yaw` and `tilt`. */
|
|
263
263
|
private orbit;
|
|
264
|
+
/** The magnifying glass over the book (#527), in CSS px (client
|
|
265
|
+
* coordinates for its centre), and the sharper paintings it shows the
|
|
266
|
+
* pages in: page source → its painting and texture. */
|
|
267
|
+
private lens;
|
|
268
|
+
private loupe;
|
|
269
|
+
private details;
|
|
264
270
|
private paper;
|
|
265
271
|
constructor(canvas: HTMLCanvasElement, spread: HTMLElement, book: SpreadSrc[], at: number, onSettle: (index: number) => void,
|
|
266
272
|
/** The reader's hand turned the book to another spread. */
|
|
@@ -470,6 +476,29 @@ export declare class PageFlipper {
|
|
|
470
476
|
* speed, never jumping. */
|
|
471
477
|
release(click?: boolean): void;
|
|
472
478
|
private draw;
|
|
479
|
+
/** Holds a magnifying glass over the book: its centre (client px), the
|
|
480
|
+
* radius of its glass and the width of its rim (CSS px), and how much
|
|
481
|
+
* it magnifies at its centre; null puts it away. */
|
|
482
|
+
setLens(lens: {
|
|
483
|
+
x: number;
|
|
484
|
+
y: number;
|
|
485
|
+
radius: number;
|
|
486
|
+
rim: number;
|
|
487
|
+
zoom: number;
|
|
488
|
+
} | null): void;
|
|
489
|
+
/** The largest texture the book can show (a painting for the glass is
|
|
490
|
+
* kept within it). */
|
|
491
|
+
get maxTextureSize(): number;
|
|
492
|
+
/** The sharper paintings the glass shows the pages in: page source (as
|
|
493
|
+
* given in the book) → its painting; pages left out are shown as
|
|
494
|
+
* painted, and their sharper textures are freed. */
|
|
495
|
+
setDetail(details: ReadonlyMap<PageSource, PageSource>): void;
|
|
496
|
+
/** Draws the glass over the frame just drawn: the book once more
|
|
497
|
+
* through its field, the pages in their sharper paintings, then the
|
|
498
|
+
* glass and its rim. What reads the main view's screen (the light
|
|
499
|
+
* through a thin leaf, a gloss page's mirror) is left out of its
|
|
500
|
+
* picture. */
|
|
501
|
+
private drawLoupe;
|
|
473
502
|
/**
|
|
474
503
|
* Thin paper lets light through (#484). While translucent leaves are in
|
|
475
504
|
* the air, the scene is drawn once more without them (at half the
|
package/dist/pageFlip.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pageFlip.d.ts","sourceRoot":"","sources":["../src/pageFlip.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,cAAc,EAoBd,KAAK,EAmBN,MAAM,OAAO,CAAC;AAKf,OAAO,EAAsB,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAA4B,MAAM,SAAS,CAAC;AAChH,OAAO,EAAoE,KAAK,OAAO,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"pageFlip.d.ts","sourceRoot":"","sources":["../src/pageFlip.ts"],"names":[],"mappings":"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"}
|
package/dist/pageFlip.js
CHANGED
|
@@ -10,6 +10,7 @@ import { pagePaper, paperSpec } from "./paper";
|
|
|
10
10
|
import { loadDeskMaps } from "./deskTextures";
|
|
11
11
|
import { ATLAS_MAX, MAX_PAGE_VIDEOS, VideoAtlas } from "./videoAtlas";
|
|
12
12
|
import { cachedDesk, cachedRelief } from "./procedural";
|
|
13
|
+
import { Loupe } from "./loupe";
|
|
13
14
|
/** Columns and rows of a leaf's mesh: fine enough for a tight roll. */
|
|
14
15
|
const NX = 96;
|
|
15
16
|
/** How far a leaf's folded paper stays over its own paper beneath it. */
|
|
@@ -1021,6 +1022,12 @@ export class PageFlipper {
|
|
|
1021
1022
|
* tilted by `pitch` (radians from straight above); null: the
|
|
1022
1023
|
* settings' `yaw` and `tilt`. */
|
|
1023
1024
|
orbit = { yaw: null, pitch: null };
|
|
1025
|
+
/** The magnifying glass over the book (#527), in CSS px (client
|
|
1026
|
+
* coordinates for its centre), and the sharper paintings it shows the
|
|
1027
|
+
* pages in: page source → its painting and texture. */
|
|
1028
|
+
lens = null;
|
|
1029
|
+
loupe = null;
|
|
1030
|
+
details = new Map();
|
|
1024
1031
|
paper = [1, 1, 1];
|
|
1025
1032
|
constructor(canvas, spread, book, at, onSettle,
|
|
1026
1033
|
/** The reader's hand turned the book to another spread. */
|
|
@@ -1940,9 +1947,19 @@ export class PageFlipper {
|
|
|
1940
1947
|
if (!src || typeof src === "string")
|
|
1941
1948
|
return;
|
|
1942
1949
|
const tex = this.ready.get(src);
|
|
1943
|
-
|
|
1950
|
+
// A sharper painting for the glass too (its decorations drawn again,
|
|
1951
|
+
// #543).
|
|
1952
|
+
let detail = false;
|
|
1953
|
+
for (const d of this.details.values()) {
|
|
1954
|
+
if (d.src !== src || !d.tex)
|
|
1955
|
+
continue;
|
|
1956
|
+
d.tex.needsUpdate = true;
|
|
1957
|
+
detail = true;
|
|
1958
|
+
}
|
|
1959
|
+
if (!tex && !detail)
|
|
1944
1960
|
return;
|
|
1945
|
-
tex
|
|
1961
|
+
if (tex)
|
|
1962
|
+
tex.needsUpdate = true;
|
|
1946
1963
|
this.redraw();
|
|
1947
1964
|
}
|
|
1948
1965
|
/** Starts loading the pages of these spreads as textures. */
|
|
@@ -2665,6 +2682,95 @@ export class PageFlipper {
|
|
|
2665
2682
|
this.mirror(air.length > 0 || this.block !== null);
|
|
2666
2683
|
this.behind(air.map((a) => a.mesh));
|
|
2667
2684
|
this.composer.render();
|
|
2685
|
+
this.drawLoupe();
|
|
2686
|
+
}
|
|
2687
|
+
// ── The magnifying glass (#527) ──
|
|
2688
|
+
/** Holds a magnifying glass over the book: its centre (client px), the
|
|
2689
|
+
* radius of its glass and the width of its rim (CSS px), and how much
|
|
2690
|
+
* it magnifies at its centre; null puts it away. */
|
|
2691
|
+
setLens(lens) {
|
|
2692
|
+
if (!lens && !this.lens)
|
|
2693
|
+
return;
|
|
2694
|
+
this.lens = lens;
|
|
2695
|
+
if (!this.raf)
|
|
2696
|
+
this.redraw();
|
|
2697
|
+
}
|
|
2698
|
+
/** The largest texture the book can show (a painting for the glass is
|
|
2699
|
+
* kept within it). */
|
|
2700
|
+
get maxTextureSize() {
|
|
2701
|
+
return this.renderer.capabilities.maxTextureSize ?? 4096;
|
|
2702
|
+
}
|
|
2703
|
+
/** The sharper paintings the glass shows the pages in: page source (as
|
|
2704
|
+
* given in the book) → its painting; pages left out are shown as
|
|
2705
|
+
* painted, and their sharper textures are freed. */
|
|
2706
|
+
setDetail(details) {
|
|
2707
|
+
for (const [page, d] of this.details) {
|
|
2708
|
+
if (details.get(page) === d.src)
|
|
2709
|
+
continue;
|
|
2710
|
+
d.tex?.dispose();
|
|
2711
|
+
this.details.delete(page);
|
|
2712
|
+
}
|
|
2713
|
+
for (const [page, src] of details) {
|
|
2714
|
+
if (!page || !src || this.details.has(page))
|
|
2715
|
+
continue;
|
|
2716
|
+
const entry = { src, tex: null };
|
|
2717
|
+
this.details.set(page, entry);
|
|
2718
|
+
const prepare = (tex) => {
|
|
2719
|
+
tex.colorSpace = SRGBColorSpace;
|
|
2720
|
+
tex.minFilter = LinearMipmapLinearFilter;
|
|
2721
|
+
tex.anisotropy = this.renderer.capabilities.getMaxAnisotropy();
|
|
2722
|
+
if (this.details.get(page) !== entry)
|
|
2723
|
+
return tex.dispose();
|
|
2724
|
+
entry.tex = tex;
|
|
2725
|
+
this.redraw();
|
|
2726
|
+
};
|
|
2727
|
+
if (typeof src === "string")
|
|
2728
|
+
void this.loader.loadAsync(src).then(prepare, () => undefined);
|
|
2729
|
+
else {
|
|
2730
|
+
const decoded = src instanceof HTMLCanvasElement ? Promise.resolve() : src.decode();
|
|
2731
|
+
void decoded.then(() => {
|
|
2732
|
+
const tex = new Texture(src);
|
|
2733
|
+
tex.needsUpdate = true;
|
|
2734
|
+
prepare(tex);
|
|
2735
|
+
}, () => undefined);
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
/** Draws the glass over the frame just drawn: the book once more
|
|
2740
|
+
* through its field, the pages in their sharper paintings, then the
|
|
2741
|
+
* glass and its rim. What reads the main view's screen (the light
|
|
2742
|
+
* through a thin leaf, a gloss page's mirror) is left out of its
|
|
2743
|
+
* picture. */
|
|
2744
|
+
drawLoupe() {
|
|
2745
|
+
const lens = this.lens;
|
|
2746
|
+
if (!lens)
|
|
2747
|
+
return;
|
|
2748
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
2749
|
+
const k = this.dpr;
|
|
2750
|
+
const view = { x: (lens.x - rect.left) * k, y: (lens.y - rect.top) * k, radius: lens.radius * k, rim: lens.rim * k, zoom: lens.zoom };
|
|
2751
|
+
this.loupe ??= new Loupe();
|
|
2752
|
+
const undo = [];
|
|
2753
|
+
const swap = (slot, value) => {
|
|
2754
|
+
const was = slot.value;
|
|
2755
|
+
slot.value = value;
|
|
2756
|
+
undo.push(() => (slot.value = was));
|
|
2757
|
+
};
|
|
2758
|
+
this.loupe.capture(this.renderer, this.scene, this.camera, view, () => {
|
|
2759
|
+
swap(behindUniforms.uBehindOn, 0);
|
|
2760
|
+
for (const mesh of this.pageMeshes()) {
|
|
2761
|
+
const u = mesh.material.userData.uniforms;
|
|
2762
|
+
swap(u.uReflectionOn, 0);
|
|
2763
|
+
for (const face of ["Front", "Back"]) {
|
|
2764
|
+
const tex = this.details.get(mesh.userData[face])?.tex;
|
|
2765
|
+
if (tex && u[`uHas${face}`].value === 1)
|
|
2766
|
+
swap(u[`u${face}`], tex);
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
}, () => {
|
|
2770
|
+
for (const f of undo.reverse())
|
|
2771
|
+
f();
|
|
2772
|
+
});
|
|
2773
|
+
this.loupe.draw(this.renderer, this.buffer.x, this.buffer.y, view);
|
|
2668
2774
|
}
|
|
2669
2775
|
/**
|
|
2670
2776
|
* Thin paper lets light through (#484). While translucent leaves are in
|
|
@@ -2860,6 +2966,10 @@ export class PageFlipper {
|
|
|
2860
2966
|
tex.dispose();
|
|
2861
2967
|
this.shadowTarget?.dispose();
|
|
2862
2968
|
this.composer.dispose();
|
|
2969
|
+
this.loupe?.dispose();
|
|
2970
|
+
for (const d of this.details.values())
|
|
2971
|
+
d.tex?.dispose();
|
|
2972
|
+
this.details.clear();
|
|
2863
2973
|
this.mirrors.left?.dispose();
|
|
2864
2974
|
this.mirrors.right?.dispose();
|
|
2865
2975
|
this.behindTarget?.depthTexture?.dispose();
|