leafthrough 0.1.6 → 0.1.7
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 +16 -6
- package/dist/{chunk-QFOGCGJJ.js → chunk-KCIOFDSW.js} +189 -66
- package/dist/chunk-KCIOFDSW.js.map +1 -0
- package/dist/element.js +1 -1
- package/dist/engine.d.ts +3 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/paper/paperPattern.d.ts +6 -0
- package/dist/react.js +1 -1
- package/dist/shared.d.ts +19 -0
- package/dist/single/SingleEngine.d.ts +2 -0
- package/dist/single/singleSheet.d.ts +6 -0
- package/dist/spread/SpreadEngine.d.ts +1 -0
- package/dist/spread/spreadSheet.d.ts +6 -0
- package/dist/svelte.js +1 -1
- package/dist/textures.d.ts +5 -0
- package/dist/vue.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-QFOGCGJJ.js.map +0 -1
package/dist/element.js
CHANGED
package/dist/engine.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import type { ResolvedSettings } from './options.js';
|
|
3
3
|
import type { SheetShading } from './paper/paperMaterial.js';
|
|
4
|
+
import type { SharedResources } from './shared.js';
|
|
4
5
|
export interface EngineContext {
|
|
5
6
|
input: EngineInput;
|
|
6
7
|
settings: ResolvedSettings;
|
|
@@ -9,6 +10,8 @@ export interface EngineContext {
|
|
|
9
10
|
/** Plain paper, for missing or unprinted sides. */
|
|
10
11
|
blank: THREE.Texture;
|
|
11
12
|
pattern: THREE.Texture;
|
|
13
|
+
/** What the magazines on this renderer share, such as the sheet geometry. */
|
|
14
|
+
shared: SharedResources;
|
|
12
15
|
/** Where the magazine opens, in the engine's own position units. */
|
|
13
16
|
position: number;
|
|
14
17
|
/** Kept current by the controller as the magazine resizes. */
|
package/dist/index.js
CHANGED
|
@@ -22,9 +22,10 @@ import {
|
|
|
22
22
|
preloadRadius,
|
|
23
23
|
rayThrough,
|
|
24
24
|
resolveSettings,
|
|
25
|
+
sharedResources,
|
|
25
26
|
snapshot,
|
|
26
27
|
sourceKey
|
|
27
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-KCIOFDSW.js";
|
|
28
29
|
|
|
29
30
|
// src/LeafThroughObject.ts
|
|
30
31
|
import * as THREE from "three";
|
|
@@ -273,6 +274,7 @@ var LeafThroughObject = class extends THREE.Group {
|
|
|
273
274
|
textures: this.options.pages.map((_, i) => this.loader?.textures[i] ?? blank),
|
|
274
275
|
blank: layout === "single" ? this.paper.back(settings.paper.backColor) : blank,
|
|
275
276
|
pattern: this.paper.grain(settings, this.renderer),
|
|
277
|
+
shared: sharedResources(this.renderer),
|
|
276
278
|
position: pageToPosition(page, layout, this.pageCount),
|
|
277
279
|
metrics: this.metrics,
|
|
278
280
|
shading: {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/LeafThroughObject.ts"],"sourcesContent":["import * as THREE from 'three'\nimport { sourceKey } from './content.js'\nimport { floorPoint, rayThrough, type Engine } from './engine.js'\nimport { framingFor, plainFramingFor, type Framing, type Rect } from './framing.js'\nimport type { GoToOptions } from './LeafThrough.js'\nimport { bindKeyboard, pageDetail, pagesAt, pageToPosition, Riffle } from './navigation.js'\nimport {\n changedOptions,\n deepEqual,\n DEFAULT_SPREAD_QUERY,\n LIVE_OPTIONS,\n pageImage,\n preloadRadius,\n resolveSettings,\n snapshot,\n type LeafThroughOptions,\n type Layout,\n type PageChangeDetail,\n type PaperDeform,\n type ResolvedSettings,\n} from './options.js'\nimport { PaperTextures } from './paper/paperTextures.js'\nimport { SingleEngine } from './single/SingleEngine.js'\nimport { SpreadEngine } from './spread/SpreadEngine.js'\nimport { PageLoader } from './textures.js'\n\n/**\n * The magazine's options, less those about its own canvas and DOM. Of `lighting`, only\n * `shadows` (whether sheets cast and receive them) and `sun.position` apply: the lights are\n * your scene's. Point `sun.position` towards your shadow-casting directional light, in the\n * magazine's own frame; it places the shadow a turning sheet casts right by the spine.\n * `lighting.shadows` changes without a rebuild.\n *\n * Its sheets stand apart by default (`paper.sheetGap` `0.0005`, about 0.1 mm paper on a\n * 210 mm page) and `paper.depthBias` is off, so they sort by real depth from any side.\n */\nexport type LeafThroughObjectOptions = Omit<LeafThroughOptions, 'bleed' | 'maxPixelRatio' | 'plain' | 'label' | 'formatPageLabel'> & {\n /** Reshapes the paper in your own GLSL. Experimental. */\n deform?: PaperDeform\n}\n\nconst SHEET_GAP = 0.0005\n\n/** What the magazine draws with, from your scene. */\nexport interface LeafThroughObjectContext {\n /** The renderer drawing your scene. Pages load into it. */\n renderer: THREE.WebGLRenderer\n /** The camera your scene is drawn with. Pointer input is cast from it; change it later through `camera`. */\n camera: THREE.Camera\n /**\n * Takes pointer input, and keys with `interaction.keyboard: true` while it has focus. It must\n * show the camera's whole view. Default `renderer.domElement`.\n */\n domElement?: HTMLElement\n}\n\nexport interface LeafThroughObjectEventMap extends THREE.Object3DEventMap {\n pagechange: { detail: PageChangeDetail }\n ready: {}\n pageload: { detail: { page: number } }\n error: { detail: { error: unknown; page?: number } }\n /** Something changed between frames: draw one, and keep drawing while `tick()` returns true. */\n change: {}\n}\n\n/** Where the magazine lies in its own units. */\nexport interface LeafThroughBounds {\n /** The pages at rest: what to frame. */\n pages: Rect\n /** Everything a turning page can sweep through. */\n reach: Rect\n}\n\nconst isClient = typeof window !== 'undefined'\n\n/**\n * A 3D magazine to add to your own three.js scene. It draws nothing itself: call `tick()`\n * every frame you render, and render again whenever it fires `change`.\n *\n * In its own units a page is 1 wide and `pageAspect` tall, the spine runs up the y axis at\n * x = 0, and the pages lie on the z = 0 plane facing +z. Move, turn and scale it like any\n * object. Pointer input is cast through `camera` onto the pages, wherever they are.\n *\n * ```ts\n * const magazine = new LeafThroughObject({ renderer, camera }, { pages, layout: 'spread' })\n * scene.add(magazine)\n * renderer.setAnimationLoop((now) => {\n * magazine.tick(now)\n * renderer.render(scene, camera)\n * })\n * ```\n */\nexport class LeafThroughObject extends THREE.Group<LeafThroughObjectEventMap> {\n /** The camera your scene is drawn with; pointer input is cast from it. */\n camera: THREE.Camera\n readonly domElement: HTMLElement\n private inputEnabled = true\n\n /**\n * Whether it takes pointer and key input. Turn it off while it's tucked away or moving,\n * and on again; the pages stay as they are. Turning it off lets go of a page being held.\n * Default `true`.\n */\n get enabled() {\n return this.inputEnabled\n }\n set enabled(value: boolean) {\n if (this.inputEnabled && !value) this.view?.engine.cancelInput?.()\n this.inputEnabled = value\n }\n\n private readonly renderer: THREE.WebGLRenderer\n private options: LeafThroughObjectOptions\n private readonly paper: PaperTextures\n private loader: PageLoader | null = null\n private view: { engine: Engine; settings: ResolvedSettings; framing: Framing; dispose: () => void } | null = null\n\n private spreadMedia: { list: MediaQueryList; abort: AbortController } | null = null\n private readonly motionMedia: MediaQueryList\n private readonly abort = new AbortController()\n private readonly riffle = new Riffle()\n private readonly metrics: { readonly dragWidth: number; dragDelta(dx: number, dy: number): number }\n private readonly ray = new THREE.Ray()\n private readonly point = new THREE.Vector3()\n private readonly ends = [new THREE.Vector3(), new THREE.Vector3()]\n private needsRender = true\n private lastPagesKey = ''\n private lastDetailKey = ''\n private inView = ''\n private ready = false\n private readyFired = false\n private disposed = false\n\n constructor({ renderer, camera, domElement = renderer.domElement }: LeafThroughObjectContext, options: LeafThroughObjectOptions) {\n super()\n if (!isClient) throw new Error('LeafThrough needs a browser; create it on the client.')\n this.renderer = renderer\n this.camera = camera\n this.domElement = domElement\n this.options = copyOptions(options)\n const object = this\n this.metrics = {\n get dragWidth() {\n return object.dragWidth()\n },\n dragDelta(dx, dy) {\n const axis = object.pageAxis()\n return (dx * axis.x + dy * axis.y) / (axis.length() || 1)\n },\n }\n\n this.paper = new PaperTextures(options.paper?.backColor ?? '#fcfcf9', {\n onGrain: () => this.remount(),\n onError: (error) => this.fail(error),\n })\n\n const signal = this.abort.signal\n this.watchSpreadQuery()\n this.motionMedia = window.matchMedia('(prefers-reduced-motion: reduce)')\n this.motionMedia.addEventListener('change', () => this.remount(), { signal })\n window.addEventListener('online', () => this.retry(), { signal })\n bindKeyboard(\n domElement,\n () => (this.enabled && this.shown() ? this.view?.settings.interaction.keyboard : false),\n { next: () => this.next(), prev: () => this.prev(), first: () => this.goTo(0), last: () => this.goTo(this.pageCount - 1) },\n signal,\n )\n\n this.remount(options.startPage ?? 0)\n }\n\n // --- public API -----------------------------------------------------------------\n\n /** First visible page, 0-based. */\n get page(): number {\n return this.detail().page\n }\n\n get pageCount(): number {\n return this.options.pages.length\n }\n\n get layout(): Layout {\n return this.view?.settings.layout ?? this.pickLayout()\n }\n\n get visiblePages(): number[] {\n return this.detail().visiblePages\n }\n\n /** True while a page is being dragged or is completing its turn. */\n get turning(): boolean {\n return this.view?.engine.turning ?? false\n }\n\n /**\n * Where the magazine lies in its own units: frame `pages` with your camera. Changes with\n * the layout, which `'auto'` can switch as the window resizes.\n */\n get bounds(): LeafThroughBounds {\n const layout = this.layout\n const aspect = this.view?.settings.pageAspect ?? this.resolve(layout).pageAspect\n return { pages: plainFramingFor(layout, aspect).box, reach: framingFor(layout, aspect).view }\n }\n\n /** Turns to the next page. Returns false at the end. */\n next(): boolean {\n this.riffle.stop()\n return this.view?.engine.flip(true) ?? false\n }\n\n /** Turns to the previous page. Returns false at the start. */\n prev(): boolean {\n this.riffle.stop()\n return this.view?.engine.flip(false) ?? false\n }\n\n /** Shows `page` (0-based), jumping there or turning the pages one by one. */\n goTo(page: number, { animate = false }: GoToOptions = {}) {\n if (!this.view || Number.isNaN(page)) return\n this.riffle.stop()\n const engine = this.view.engine\n const target = pageToPosition(page, this.view.settings.layout, this.pageCount)\n if (!animate) {\n engine.jumpTo(target)\n return\n }\n if (engine.position === target) return\n this.riffle.start(target, this.view.settings.motion.speed)\n this.requestFrame()\n }\n\n /** Loads the pages that failed again. Also happens by itself when the browser comes back online. */\n retry() {\n this.loader?.retry()\n }\n\n /** Changes some options and keeps the rest. Structural changes rebuild the pages and keep the current page. */\n update(options: Partial<LeafThroughObjectOptions>) {\n this.applyOptions({ ...this.options, ...options })\n }\n\n /** Replaces every option at once: anything left out goes back to its default. */\n setOptions(options: LeafThroughObjectOptions) {\n this.applyOptions(options)\n }\n\n /**\n * Whether a press at this point on screen lands on the magazine, which then takes it. Check\n * it in your own pointer handlers to leave such presses alone. False while it's hidden or\n * not `enabled`.\n */\n covers(clientX: number, clientY: number): boolean {\n const view = this.view\n if (!view || !this.enabled || !this.shown()) return false\n if (!rayThrough(this.camera, this.domElement, clientX, clientY, this.ray)) return false\n const point = floorPoint(view.engine.group, this.ray, this.point)\n if (!point) return false\n // a closed spread only turns from its cover side\n const box = view.framing.box\n const side = view.engine.activeSide\n const left = side === 'right' ? box.x + box.width / 2 : box.x\n const right = side === 'left' ? box.x + box.width / 2 : box.x + box.width\n return point.x >= left && point.x <= right && point.y >= box.y && point.y <= box.y + box.height\n }\n\n /**\n * Advances the pages to `now` (a `requestAnimationFrame` timestamp). Call it every frame\n * before rendering. Returns true while the pages are moving and need another frame.\n */\n tick(now = performance.now()): boolean {\n const view = this.view\n if (!view) return false\n this.riffle.step(now, view.engine)\n const moved = view.engine.update(now)\n this.followReader(view.engine, view.settings.layout)\n const draw = moved || this.needsRender\n this.needsRender = false\n if (this.ready && !this.readyFired) {\n this.readyFired = true\n this.options.onReady?.()\n this.dispatchEvent({ type: 'ready' })\n }\n this.emitPageChange()\n return draw || this.riffle.active\n }\n\n /** Frees the pages, textures and listeners, and takes the magazine out of its parent. */\n dispose() {\n if (this.disposed) return\n this.disposed = true\n this.riffle.stop()\n this.abort.abort()\n this.spreadMedia?.abort.abort()\n this.view?.dispose()\n this.view = null\n this.loader?.dispose()\n this.paper.dispose()\n this.removeFromParent()\n }\n\n // --- options and layout -----------------------------------------------------------\n\n private applyOptions(options: LeafThroughObjectOptions) {\n if (this.disposed) return\n const previous = this.options\n const next = (this.options = copyOptions(options))\n const changed = changedOptions(previous, next)\n if (!changed.length) return\n if (changed.includes('spreadQuery')) this.watchSpreadQuery()\n\n // shadows turn on and off in place; the rest of lighting rebuilds\n const { shadows: wasShadowed, ...lighting } = previous.lighting ?? {}\n const { shadows: shadowed, ...nextLighting } = next.lighting ?? {}\n const liveShadows = changed.includes('lighting') && deepEqual(lighting, nextLighting)\n if (liveShadows && (wasShadowed ?? true) !== (shadowed ?? true)) this.view?.engine.setShadows?.(shadowed ?? true)\n\n const page = this.page\n let rebuild = changed.some((key) => {\n if (key === 'deform') return deformKey(previous) !== deformKey(next)\n if (key === 'lighting') return !liveShadows\n return !LIVE_OPTIONS.has(key)\n })\n if (changed.includes('pages') && this.pagesKey(next) !== this.lastPagesKey) {\n this.loader?.dispose()\n this.loader = null\n rebuild = true\n }\n if (changed.includes('preload')) this.loader?.setRadius(preloadRadius(this.options.preload))\n if (rebuild) this.remount(page)\n }\n\n private pickLayout(): Layout {\n const layout = this.options.layout ?? 'auto'\n if (layout !== 'auto') return layout\n return this.spreadMedia?.list.matches ? 'spread' : 'single'\n }\n\n private watchSpreadQuery() {\n this.spreadMedia?.abort.abort()\n const list = window.matchMedia(this.options.spreadQuery ?? DEFAULT_SPREAD_QUERY)\n const abort = new AbortController()\n list.addEventListener('change', () => this.remount(), { signal: abort.signal })\n this.spreadMedia = { list, abort }\n }\n\n private remount(page = this.page) {\n if (this.disposed) return\n this.riffle.stop()\n this.view?.dispose()\n\n const layout = this.pickLayout()\n const settings = this.resolve(layout)\n this.lastPagesKey = this.pagesKey(this.options)\n if (!this.loader) this.loadPages(page, layout)\n this.loader?.setWrap(layout === 'single' && settings.loop)\n\n const domElement = this.domElement\n const blank = this.paper.blank\n const ctx = {\n input: {\n element: domElement,\n ray: (x: number, y: number, ray: THREE.Ray) => rayThrough(this.camera, domElement, x, y, ray),\n covers: (x: number, y: number) => this.covers(x, y),\n },\n settings,\n textures: this.options.pages.map((_, i) => this.loader?.textures[i] ?? blank),\n blank: layout === 'single' ? this.paper.back(settings.paper.backColor) : blank,\n pattern: this.paper.grain(settings, this.renderer),\n position: pageToPosition(page, layout, this.pageCount),\n metrics: this.metrics,\n shading: {\n spineContact: { value: new THREE.Vector3(1, 0, 0) },\n lightDirection: { value: new THREE.Vector3(...settings.lighting.sun.position).normalize() },\n origin: { value: new THREE.Vector3() },\n },\n requestFrame: () => this.requestFrame(),\n }\n const engine: Engine = layout === 'spread' ? new SpreadEngine(ctx) : new SingleEngine(ctx)\n this.add(engine.group)\n this.view = {\n engine,\n settings,\n framing: framingFor(layout, settings.pageAspect),\n dispose: () => {\n this.remove(engine.group)\n engine.dispose()\n },\n }\n this.requestFrame()\n }\n\n // the settings, with this object's own defaults and its deform\n private resolve(layout: Layout): ResolvedSettings {\n const settings = resolveSettings(this.options, layout, this.motionMedia.matches)\n const paper = this.options.paper\n settings.paper.sheetGap = Math.max(0, paper?.sheetGap ?? SHEET_GAP)\n settings.paper.depthBias = paper?.depthBias ?? false\n const deform = this.options.deform\n if (deform) {\n // The shaders read each uniform through the options as they are now, so a value set on\n // the caller's own object, or an object swapped in by update(), shows on the next frame.\n const uniforms: Record<string, THREE.IUniform> = {}\n for (const name of Object.keys(deform.uniforms ?? {})) {\n const options = () => this.options\n uniforms[name] = {\n get value() {\n return options().deform?.uniforms?.[name]?.value\n },\n }\n }\n settings.deform = { glsl: deform.glsl, uniforms }\n }\n return settings\n }\n\n // A drag across this many pixels turns a page all the way: the magazine's drag width, at the\n // size it shows on screen now.\n private readonly axis = new THREE.Vector2()\n\n private pageAxis() {\n const group = this.view?.engine.group ?? this\n group.updateWorldMatrix(true, false)\n this.camera.updateWorldMatrix(true, false)\n const rect = this.domElement.getBoundingClientRect()\n const [spine, edge] = this.ends.map((end, x) => end.set(x, 0, 0).applyMatrix4(group.matrixWorld).project(this.camera))\n return this.axis.set(((edge.x - spine.x) * rect.width) / 2, -((edge.y - spine.y) * rect.height) / 2)\n }\n\n private dragWidth() {\n return Math.max(1, (this.view?.framing.dragWidth ?? 1) * this.pageAxis().length())\n }\n\n // whether this and every parent are visible\n private shown() {\n for (let object: THREE.Object3D | null = this; object; object = object.parent) if (!object.visible) return false\n return true\n }\n\n private requestFrame() {\n this.needsRender = true\n this.dispatchEvent({ type: 'change' })\n }\n\n // --- pages ---------------------------------------------------------------------\n\n // what the pages show; their alt and text don't count\n private pagesKey(options: LeafThroughObjectOptions) {\n return options.pages.map((p) => sourceKey(pageImage(p))).join('\\n')\n }\n\n private loadPages(around: number, layout: Layout) {\n this.loader?.dispose()\n const loader = new PageLoader(this.options.pages.map(pageImage), this.renderer, preloadRadius(this.options.preload), {\n onLoad: (page, texture) => {\n if (this.loader !== loader) return\n this.view?.engine.setPageTexture(page, texture)\n this.options.onPageLoad?.(page)\n this.dispatchEvent({ type: 'pageload', detail: { page } })\n },\n onUnload: (page) => {\n if (this.loader !== loader) return\n this.view?.engine.setPageTexture(page, this.paper.blank)\n },\n onError: (page, error) => this.fail(error, page),\n })\n this.loader = loader\n this.inView = ''\n this.ready = this.readyFired = false\n // the pages around the start, so the first turn either way has its next page ready\n const count = this.pageCount\n const first = [around - 1, around, around + 1, around + 2]\n const visible = pagesAt(pageToPosition(around, layout, count), layout, count)\n loader.start(visible[0] ?? around, visible[visible.length - 1] ?? around, first).then(() => {\n if (this.loader !== loader || this.disposed) return\n this.ready = true\n this.requestFrame()\n })\n }\n\n // loads around what's on screen, pages mid-turn included, the page being read first\n private followReader(engine: Engine, layout: Layout) {\n if (!this.loader) return\n const { from, to } = engine.pagesInView()\n const around = pagesAt(engine.position, layout, this.pageCount)[0] ?? to\n const key = `${from}:${to}:${around}`\n if (key === this.inView) return\n this.inView = key\n this.loader.focus(from, to, around)\n }\n\n private fail(error: unknown, page?: number) {\n if (this.options.onError) this.options.onError(error, page)\n else console.warn('[leafthrough]', error)\n this.dispatchEvent({ type: 'error', detail: { error, page } })\n }\n\n private detail(): PageChangeDetail {\n return pageDetail(this.view?.engine.position ?? 0, this.layout, this.pageCount)\n }\n\n private emitPageChange() {\n const detail = this.detail()\n const key = `${detail.layout}:${detail.visiblePages.join(',')}/${detail.pageCount}`\n if (key === this.lastDetailKey) return\n const first = !this.lastDetailKey\n this.lastDetailKey = key\n if (first) return\n this.options.onPageChange?.(detail)\n this.dispatchEvent({ type: 'pagechange', detail })\n }\n}\n\n// What a deform compiles to: new uniform values, or new objects under the same names, don't rebuild.\nconst deformKey = ({ deform }: LeafThroughObjectOptions) =>\n deform ? `${deform.glsl}\\n${Object.keys(deform.uniforms ?? {}).join(',')}` : ''\n\n// Like snapshot(), but deform's uniforms stay the caller's own objects, whose values the shaders read.\nfunction copyOptions(options: LeafThroughObjectOptions): LeafThroughObjectOptions {\n const copy = snapshot(options)\n const uniforms = options.deform?.uniforms\n if (copy.deform && uniforms) copy.deform = { ...copy.deform, uniforms: { ...uniforms } }\n return copy\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,YAAY,WAAW;AAyCvB,IAAM,YAAY;AAgClB,IAAM,WAAW,OAAO,WAAW;AAmB5B,IAAM,oBAAN,cAAsC,YAAiC;AAAA;AAAA,EAE5E;AAAA,EACS;AAAA,EACD,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOvB,IAAI,UAAU;AACZ,WAAO,KAAK;AAAA,EACd;AAAA,EACA,IAAI,QAAQ,OAAgB;AAC1B,QAAI,KAAK,gBAAgB,CAAC,MAAO,MAAK,MAAM,OAAO,cAAc;AACjE,SAAK,eAAe;AAAA,EACtB;AAAA,EAEiB;AAAA,EACT;AAAA,EACS;AAAA,EACT,SAA4B;AAAA,EAC5B,OAAqG;AAAA,EAErG,cAAuE;AAAA,EAC9D;AAAA,EACA,QAAQ,IAAI,gBAAgB;AAAA,EAC5B,SAAS,IAAI,OAAO;AAAA,EACpB;AAAA,EACA,MAAM,IAAU,UAAI;AAAA,EACpB,QAAQ,IAAU,cAAQ;AAAA,EAC1B,OAAO,CAAC,IAAU,cAAQ,GAAG,IAAU,cAAQ,CAAC;AAAA,EACzD,cAAc;AAAA,EACd,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,WAAW;AAAA,EAEnB,YAAY,EAAE,UAAU,QAAQ,aAAa,SAAS,WAAW,GAA6B,SAAmC;AAC/H,UAAM;AACN,QAAI,CAAC,SAAU,OAAM,IAAI,MAAM,uDAAuD;AACtF,SAAK,WAAW;AAChB,SAAK,SAAS;AACd,SAAK,aAAa;AAClB,SAAK,UAAU,YAAY,OAAO;AAClC,UAAM,SAAS;AACf,SAAK,UAAU;AAAA,MACb,IAAI,YAAY;AACd,eAAO,OAAO,UAAU;AAAA,MAC1B;AAAA,MACA,UAAU,IAAI,IAAI;AAChB,cAAM,OAAO,OAAO,SAAS;AAC7B,gBAAQ,KAAK,KAAK,IAAI,KAAK,KAAK,MAAM,KAAK,OAAO,KAAK;AAAA,MACzD;AAAA,IACF;AAEA,SAAK,QAAQ,IAAI,cAAc,QAAQ,OAAO,aAAa,WAAW;AAAA,MACpE,SAAS,MAAM,KAAK,QAAQ;AAAA,MAC5B,SAAS,CAAC,UAAU,KAAK,KAAK,KAAK;AAAA,IACrC,CAAC;AAED,UAAM,SAAS,KAAK,MAAM;AAC1B,SAAK,iBAAiB;AACtB,SAAK,cAAc,OAAO,WAAW,kCAAkC;AACvE,SAAK,YAAY,iBAAiB,UAAU,MAAM,KAAK,QAAQ,GAAG,EAAE,OAAO,CAAC;AAC5E,WAAO,iBAAiB,UAAU,MAAM,KAAK,MAAM,GAAG,EAAE,OAAO,CAAC;AAChE;AAAA,MACE;AAAA,MACA,MAAO,KAAK,WAAW,KAAK,MAAM,IAAI,KAAK,MAAM,SAAS,YAAY,WAAW;AAAA,MACjF,EAAE,MAAM,MAAM,KAAK,KAAK,GAAG,MAAM,MAAM,KAAK,KAAK,GAAG,OAAO,MAAM,KAAK,KAAK,CAAC,GAAG,MAAM,MAAM,KAAK,KAAK,KAAK,YAAY,CAAC,EAAE;AAAA,MACzH;AAAA,IACF;AAEA,SAAK,QAAQ,QAAQ,aAAa,CAAC;AAAA,EACrC;AAAA;AAAA;AAAA,EAKA,IAAI,OAAe;AACjB,WAAO,KAAK,OAAO,EAAE;AAAA,EACvB;AAAA,EAEA,IAAI,YAAoB;AACtB,WAAO,KAAK,QAAQ,MAAM;AAAA,EAC5B;AAAA,EAEA,IAAI,SAAiB;AACnB,WAAO,KAAK,MAAM,SAAS,UAAU,KAAK,WAAW;AAAA,EACvD;AAAA,EAEA,IAAI,eAAyB;AAC3B,WAAO,KAAK,OAAO,EAAE;AAAA,EACvB;AAAA;AAAA,EAGA,IAAI,UAAmB;AACrB,WAAO,KAAK,MAAM,OAAO,WAAW;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,SAA4B;AAC9B,UAAM,SAAS,KAAK;AACpB,UAAM,SAAS,KAAK,MAAM,SAAS,cAAc,KAAK,QAAQ,MAAM,EAAE;AACtE,WAAO,EAAE,OAAO,gBAAgB,QAAQ,MAAM,EAAE,KAAK,OAAO,WAAW,QAAQ,MAAM,EAAE,KAAK;AAAA,EAC9F;AAAA;AAAA,EAGA,OAAgB;AACd,SAAK,OAAO,KAAK;AACjB,WAAO,KAAK,MAAM,OAAO,KAAK,IAAI,KAAK;AAAA,EACzC;AAAA;AAAA,EAGA,OAAgB;AACd,SAAK,OAAO,KAAK;AACjB,WAAO,KAAK,MAAM,OAAO,KAAK,KAAK,KAAK;AAAA,EAC1C;AAAA;AAAA,EAGA,KAAK,MAAc,EAAE,UAAU,MAAM,IAAiB,CAAC,GAAG;AACxD,QAAI,CAAC,KAAK,QAAQ,OAAO,MAAM,IAAI,EAAG;AACtC,SAAK,OAAO,KAAK;AACjB,UAAM,SAAS,KAAK,KAAK;AACzB,UAAM,SAAS,eAAe,MAAM,KAAK,KAAK,SAAS,QAAQ,KAAK,SAAS;AAC7E,QAAI,CAAC,SAAS;AACZ,aAAO,OAAO,MAAM;AACpB;AAAA,IACF;AACA,QAAI,OAAO,aAAa,OAAQ;AAChC,SAAK,OAAO,MAAM,QAAQ,KAAK,KAAK,SAAS,OAAO,KAAK;AACzD,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA,EAGA,QAAQ;AACN,SAAK,QAAQ,MAAM;AAAA,EACrB;AAAA;AAAA,EAGA,OAAO,SAA4C;AACjD,SAAK,aAAa,EAAE,GAAG,KAAK,SAAS,GAAG,QAAQ,CAAC;AAAA,EACnD;AAAA;AAAA,EAGA,WAAW,SAAmC;AAC5C,SAAK,aAAa,OAAO;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,SAAiB,SAA0B;AAChD,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,QAAQ,CAAC,KAAK,WAAW,CAAC,KAAK,MAAM,EAAG,QAAO;AACpD,QAAI,CAAC,WAAW,KAAK,QAAQ,KAAK,YAAY,SAAS,SAAS,KAAK,GAAG,EAAG,QAAO;AAClF,UAAM,QAAQ,WAAW,KAAK,OAAO,OAAO,KAAK,KAAK,KAAK,KAAK;AAChE,QAAI,CAAC,MAAO,QAAO;AAEnB,UAAM,MAAM,KAAK,QAAQ;AACzB,UAAM,OAAO,KAAK,OAAO;AACzB,UAAM,OAAO,SAAS,UAAU,IAAI,IAAI,IAAI,QAAQ,IAAI,IAAI;AAC5D,UAAM,QAAQ,SAAS,SAAS,IAAI,IAAI,IAAI,QAAQ,IAAI,IAAI,IAAI,IAAI;AACpE,WAAO,MAAM,KAAK,QAAQ,MAAM,KAAK,SAAS,MAAM,KAAK,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI;AAAA,EAC3F;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK,MAAM,YAAY,IAAI,GAAY;AACrC,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,KAAM,QAAO;AAClB,SAAK,OAAO,KAAK,KAAK,KAAK,MAAM;AACjC,UAAM,QAAQ,KAAK,OAAO,OAAO,GAAG;AACpC,SAAK,aAAa,KAAK,QAAQ,KAAK,SAAS,MAAM;AACnD,UAAM,OAAO,SAAS,KAAK;AAC3B,SAAK,cAAc;AACnB,QAAI,KAAK,SAAS,CAAC,KAAK,YAAY;AAClC,WAAK,aAAa;AAClB,WAAK,QAAQ,UAAU;AACvB,WAAK,cAAc,EAAE,MAAM,QAAQ,CAAC;AAAA,IACtC;AACA,SAAK,eAAe;AACpB,WAAO,QAAQ,KAAK,OAAO;AAAA,EAC7B;AAAA;AAAA,EAGA,UAAU;AACR,QAAI,KAAK,SAAU;AACnB,SAAK,WAAW;AAChB,SAAK,OAAO,KAAK;AACjB,SAAK,MAAM,MAAM;AACjB,SAAK,aAAa,MAAM,MAAM;AAC9B,SAAK,MAAM,QAAQ;AACnB,SAAK,OAAO;AACZ,SAAK,QAAQ,QAAQ;AACrB,SAAK,MAAM,QAAQ;AACnB,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA,EAIQ,aAAa,SAAmC;AACtD,QAAI,KAAK,SAAU;AACnB,UAAM,WAAW,KAAK;AACtB,UAAM,OAAQ,KAAK,UAAU,YAAY,OAAO;AAChD,UAAM,UAAU,eAAe,UAAU,IAAI;AAC7C,QAAI,CAAC,QAAQ,OAAQ;AACrB,QAAI,QAAQ,SAAS,aAAa,EAAG,MAAK,iBAAiB;AAG3D,UAAM,EAAE,SAAS,aAAa,GAAG,SAAS,IAAI,SAAS,YAAY,CAAC;AACpE,UAAM,EAAE,SAAS,UAAU,GAAG,aAAa,IAAI,KAAK,YAAY,CAAC;AACjE,UAAM,cAAc,QAAQ,SAAS,UAAU,KAAK,UAAU,UAAU,YAAY;AACpF,QAAI,gBAAgB,eAAe,WAAW,YAAY,MAAO,MAAK,MAAM,OAAO,aAAa,YAAY,IAAI;AAEhH,UAAM,OAAO,KAAK;AAClB,QAAI,UAAU,QAAQ,KAAK,CAAC,QAAQ;AAClC,UAAI,QAAQ,SAAU,QAAO,UAAU,QAAQ,MAAM,UAAU,IAAI;AACnE,UAAI,QAAQ,WAAY,QAAO,CAAC;AAChC,aAAO,CAAC,aAAa,IAAI,GAAG;AAAA,IAC9B,CAAC;AACD,QAAI,QAAQ,SAAS,OAAO,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,cAAc;AAC1E,WAAK,QAAQ,QAAQ;AACrB,WAAK,SAAS;AACd,gBAAU;AAAA,IACZ;AACA,QAAI,QAAQ,SAAS,SAAS,EAAG,MAAK,QAAQ,UAAU,cAAc,KAAK,QAAQ,OAAO,CAAC;AAC3F,QAAI,QAAS,MAAK,QAAQ,IAAI;AAAA,EAChC;AAAA,EAEQ,aAAqB;AAC3B,UAAM,SAAS,KAAK,QAAQ,UAAU;AACtC,QAAI,WAAW,OAAQ,QAAO;AAC9B,WAAO,KAAK,aAAa,KAAK,UAAU,WAAW;AAAA,EACrD;AAAA,EAEQ,mBAAmB;AACzB,SAAK,aAAa,MAAM,MAAM;AAC9B,UAAM,OAAO,OAAO,WAAW,KAAK,QAAQ,eAAe,oBAAoB;AAC/E,UAAM,QAAQ,IAAI,gBAAgB;AAClC,SAAK,iBAAiB,UAAU,MAAM,KAAK,QAAQ,GAAG,EAAE,QAAQ,MAAM,OAAO,CAAC;AAC9E,SAAK,cAAc,EAAE,MAAM,MAAM;AAAA,EACnC;AAAA,EAEQ,QAAQ,OAAO,KAAK,MAAM;AAChC,QAAI,KAAK,SAAU;AACnB,SAAK,OAAO,KAAK;AACjB,SAAK,MAAM,QAAQ;AAEnB,UAAM,SAAS,KAAK,WAAW;AAC/B,UAAM,WAAW,KAAK,QAAQ,MAAM;AACpC,SAAK,eAAe,KAAK,SAAS,KAAK,OAAO;AAC9C,QAAI,CAAC,KAAK,OAAQ,MAAK,UAAU,MAAM,MAAM;AAC7C,SAAK,QAAQ,QAAQ,WAAW,YAAY,SAAS,IAAI;AAEzD,UAAM,aAAa,KAAK;AACxB,UAAM,QAAQ,KAAK,MAAM;AACzB,UAAM,MAAM;AAAA,MACV,OAAO;AAAA,QACL,SAAS;AAAA,QACT,KAAK,CAAC,GAAW,GAAW,QAAmB,WAAW,KAAK,QAAQ,YAAY,GAAG,GAAG,GAAG;AAAA,QAC5F,QAAQ,CAAC,GAAW,MAAc,KAAK,OAAO,GAAG,CAAC;AAAA,MACpD;AAAA,MACA;AAAA,MACA,UAAU,KAAK,QAAQ,MAAM,IAAI,CAAC,GAAG,MAAM,KAAK,QAAQ,SAAS,CAAC,KAAK,KAAK;AAAA,MAC5E,OAAO,WAAW,WAAW,KAAK,MAAM,KAAK,SAAS,MAAM,SAAS,IAAI;AAAA,MACzE,SAAS,KAAK,MAAM,MAAM,UAAU,KAAK,QAAQ;AAAA,MACjD,UAAU,eAAe,MAAM,QAAQ,KAAK,SAAS;AAAA,MACrD,SAAS,KAAK;AAAA,MACd,SAAS;AAAA,QACP,cAAc,EAAE,OAAO,IAAU,cAAQ,GAAG,GAAG,CAAC,EAAE;AAAA,QAClD,gBAAgB,EAAE,OAAO,IAAU,cAAQ,GAAG,SAAS,SAAS,IAAI,QAAQ,EAAE,UAAU,EAAE;AAAA,QAC1F,QAAQ,EAAE,OAAO,IAAU,cAAQ,EAAE;AAAA,MACvC;AAAA,MACA,cAAc,MAAM,KAAK,aAAa;AAAA,IACxC;AACA,UAAM,SAAiB,WAAW,WAAW,IAAI,aAAa,GAAG,IAAI,IAAI,aAAa,GAAG;AACzF,SAAK,IAAI,OAAO,KAAK;AACrB,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA,MACA,SAAS,WAAW,QAAQ,SAAS,UAAU;AAAA,MAC/C,SAAS,MAAM;AACb,aAAK,OAAO,OAAO,KAAK;AACxB,eAAO,QAAQ;AAAA,MACjB;AAAA,IACF;AACA,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA,EAGQ,QAAQ,QAAkC;AAChD,UAAM,WAAW,gBAAgB,KAAK,SAAS,QAAQ,KAAK,YAAY,OAAO;AAC/E,UAAM,QAAQ,KAAK,QAAQ;AAC3B,aAAS,MAAM,WAAW,KAAK,IAAI,GAAG,OAAO,YAAY,SAAS;AAClE,aAAS,MAAM,YAAY,OAAO,aAAa;AAC/C,UAAM,SAAS,KAAK,QAAQ;AAC5B,QAAI,QAAQ;AAGV,YAAM,WAA2C,CAAC;AAClD,iBAAW,QAAQ,OAAO,KAAK,OAAO,YAAY,CAAC,CAAC,GAAG;AACrD,cAAM,UAAU,MAAM,KAAK;AAC3B,iBAAS,IAAI,IAAI;AAAA,UACf,IAAI,QAAQ;AACV,mBAAO,QAAQ,EAAE,QAAQ,WAAW,IAAI,GAAG;AAAA,UAC7C;AAAA,QACF;AAAA,MACF;AACA,eAAS,SAAS,EAAE,MAAM,OAAO,MAAM,SAAS;AAAA,IAClD;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAIiB,OAAO,IAAU,cAAQ;AAAA,EAElC,WAAW;AACjB,UAAM,QAAQ,KAAK,MAAM,OAAO,SAAS;AACzC,UAAM,kBAAkB,MAAM,KAAK;AACnC,SAAK,OAAO,kBAAkB,MAAM,KAAK;AACzC,UAAM,OAAO,KAAK,WAAW,sBAAsB;AACnD,UAAM,CAAC,OAAO,IAAI,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK,MAAM,IAAI,IAAI,GAAG,GAAG,CAAC,EAAE,aAAa,MAAM,WAAW,EAAE,QAAQ,KAAK,MAAM,CAAC;AACrH,WAAO,KAAK,KAAK,KAAM,KAAK,IAAI,MAAM,KAAK,KAAK,QAAS,GAAG,GAAG,KAAK,IAAI,MAAM,KAAK,KAAK,UAAU,CAAC;AAAA,EACrG;AAAA,EAEQ,YAAY;AAClB,WAAO,KAAK,IAAI,IAAI,KAAK,MAAM,QAAQ,aAAa,KAAK,KAAK,SAAS,EAAE,OAAO,CAAC;AAAA,EACnF;AAAA;AAAA,EAGQ,QAAQ;AACd,aAAS,SAAgC,MAAM,QAAQ,SAAS,OAAO,OAAQ,KAAI,CAAC,OAAO,QAAS,QAAO;AAC3G,WAAO;AAAA,EACT;AAAA,EAEQ,eAAe;AACrB,SAAK,cAAc;AACnB,SAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAAA,EACvC;AAAA;AAAA;AAAA,EAKQ,SAAS,SAAmC;AAClD,WAAO,QAAQ,MAAM,IAAI,CAAC,MAAM,UAAU,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI;AAAA,EACpE;AAAA,EAEQ,UAAU,QAAgB,QAAgB;AAChD,SAAK,QAAQ,QAAQ;AACrB,UAAM,SAAS,IAAI,WAAW,KAAK,QAAQ,MAAM,IAAI,SAAS,GAAG,KAAK,UAAU,cAAc,KAAK,QAAQ,OAAO,GAAG;AAAA,MACnH,QAAQ,CAAC,MAAM,YAAY;AACzB,YAAI,KAAK,WAAW,OAAQ;AAC5B,aAAK,MAAM,OAAO,eAAe,MAAM,OAAO;AAC9C,aAAK,QAAQ,aAAa,IAAI;AAC9B,aAAK,cAAc,EAAE,MAAM,YAAY,QAAQ,EAAE,KAAK,EAAE,CAAC;AAAA,MAC3D;AAAA,MACA,UAAU,CAAC,SAAS;AAClB,YAAI,KAAK,WAAW,OAAQ;AAC5B,aAAK,MAAM,OAAO,eAAe,MAAM,KAAK,MAAM,KAAK;AAAA,MACzD;AAAA,MACA,SAAS,CAAC,MAAM,UAAU,KAAK,KAAK,OAAO,IAAI;AAAA,IACjD,CAAC;AACD,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,QAAQ,KAAK,aAAa;AAE/B,UAAM,QAAQ,KAAK;AACnB,UAAM,QAAQ,CAAC,SAAS,GAAG,QAAQ,SAAS,GAAG,SAAS,CAAC;AACzD,UAAM,UAAU,QAAQ,eAAe,QAAQ,QAAQ,KAAK,GAAG,QAAQ,KAAK;AAC5E,WAAO,MAAM,QAAQ,CAAC,KAAK,QAAQ,QAAQ,QAAQ,SAAS,CAAC,KAAK,QAAQ,KAAK,EAAE,KAAK,MAAM;AAC1F,UAAI,KAAK,WAAW,UAAU,KAAK,SAAU;AAC7C,WAAK,QAAQ;AACb,WAAK,aAAa;AAAA,IACpB,CAAC;AAAA,EACH;AAAA;AAAA,EAGQ,aAAa,QAAgB,QAAgB;AACnD,QAAI,CAAC,KAAK,OAAQ;AAClB,UAAM,EAAE,MAAM,GAAG,IAAI,OAAO,YAAY;AACxC,UAAM,SAAS,QAAQ,OAAO,UAAU,QAAQ,KAAK,SAAS,EAAE,CAAC,KAAK;AACtE,UAAM,MAAM,GAAG,IAAI,IAAI,EAAE,IAAI,MAAM;AACnC,QAAI,QAAQ,KAAK,OAAQ;AACzB,SAAK,SAAS;AACd,SAAK,OAAO,MAAM,MAAM,IAAI,MAAM;AAAA,EACpC;AAAA,EAEQ,KAAK,OAAgB,MAAe;AAC1C,QAAI,KAAK,QAAQ,QAAS,MAAK,QAAQ,QAAQ,OAAO,IAAI;AAAA,QACrD,SAAQ,KAAK,iBAAiB,KAAK;AACxC,SAAK,cAAc,EAAE,MAAM,SAAS,QAAQ,EAAE,OAAO,KAAK,EAAE,CAAC;AAAA,EAC/D;AAAA,EAEQ,SAA2B;AACjC,WAAO,WAAW,KAAK,MAAM,OAAO,YAAY,GAAG,KAAK,QAAQ,KAAK,SAAS;AAAA,EAChF;AAAA,EAEQ,iBAAiB;AACvB,UAAM,SAAS,KAAK,OAAO;AAC3B,UAAM,MAAM,GAAG,OAAO,MAAM,IAAI,OAAO,aAAa,KAAK,GAAG,CAAC,IAAI,OAAO,SAAS;AACjF,QAAI,QAAQ,KAAK,cAAe;AAChC,UAAM,QAAQ,CAAC,KAAK;AACpB,SAAK,gBAAgB;AACrB,QAAI,MAAO;AACX,SAAK,QAAQ,eAAe,MAAM;AAClC,SAAK,cAAc,EAAE,MAAM,cAAc,OAAO,CAAC;AAAA,EACnD;AACF;AAGA,IAAM,YAAY,CAAC,EAAE,OAAO,MAC1B,SAAS,GAAG,OAAO,IAAI;AAAA,EAAK,OAAO,KAAK,OAAO,YAAY,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,KAAK;AAG/E,SAAS,YAAY,SAA6D;AAChF,QAAM,OAAO,SAAS,OAAO;AAC7B,QAAM,WAAW,QAAQ,QAAQ;AACjC,MAAI,KAAK,UAAU,SAAU,MAAK,SAAS,EAAE,GAAG,KAAK,QAAQ,UAAU,EAAE,GAAG,SAAS,EAAE;AACvF,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/LeafThroughObject.ts"],"sourcesContent":["import * as THREE from 'three'\nimport { sourceKey } from './content.js'\nimport { floorPoint, rayThrough, type Engine } from './engine.js'\nimport { framingFor, plainFramingFor, type Framing, type Rect } from './framing.js'\nimport type { GoToOptions } from './LeafThrough.js'\nimport { bindKeyboard, pageDetail, pagesAt, pageToPosition, Riffle } from './navigation.js'\nimport {\n changedOptions,\n deepEqual,\n DEFAULT_SPREAD_QUERY,\n LIVE_OPTIONS,\n pageImage,\n preloadRadius,\n resolveSettings,\n snapshot,\n type LeafThroughOptions,\n type Layout,\n type PageChangeDetail,\n type PaperDeform,\n type ResolvedSettings,\n} from './options.js'\nimport { PaperTextures } from './paper/paperTextures.js'\nimport { sharedResources } from './shared.js'\nimport { SingleEngine } from './single/SingleEngine.js'\nimport { SpreadEngine } from './spread/SpreadEngine.js'\nimport { PageLoader } from './textures.js'\n\n/**\n * The magazine's options, less those about its own canvas and DOM. Of `lighting`, only\n * `shadows` (whether sheets cast and receive them) and `sun.position` apply: the lights are\n * your scene's. Point `sun.position` towards your shadow-casting directional light, in the\n * magazine's own frame; it places the shadow a turning sheet casts right by the spine.\n * `lighting.shadows` changes without a rebuild.\n *\n * Its sheets stand apart by default (`paper.sheetGap` `0.0005`, about 0.1 mm paper on a\n * 210 mm page) and `paper.depthBias` is off, so they sort by real depth from any side.\n */\nexport type LeafThroughObjectOptions = Omit<LeafThroughOptions, 'bleed' | 'maxPixelRatio' | 'plain' | 'label' | 'formatPageLabel'> & {\n /** Reshapes the paper in your own GLSL. Experimental. */\n deform?: PaperDeform\n}\n\nconst SHEET_GAP = 0.0005\n\n/** What the magazine draws with, from your scene. */\nexport interface LeafThroughObjectContext {\n /** The renderer drawing your scene. Pages load into it. */\n renderer: THREE.WebGLRenderer\n /** The camera your scene is drawn with. Pointer input is cast from it; change it later through `camera`. */\n camera: THREE.Camera\n /**\n * Takes pointer input, and keys with `interaction.keyboard: true` while it has focus. It must\n * show the camera's whole view. Default `renderer.domElement`.\n */\n domElement?: HTMLElement\n}\n\nexport interface LeafThroughObjectEventMap extends THREE.Object3DEventMap {\n pagechange: { detail: PageChangeDetail }\n ready: {}\n pageload: { detail: { page: number } }\n error: { detail: { error: unknown; page?: number } }\n /** Something changed between frames: draw one, and keep drawing while `tick()` returns true. */\n change: {}\n}\n\n/** Where the magazine lies in its own units. */\nexport interface LeafThroughBounds {\n /** The pages at rest: what to frame. */\n pages: Rect\n /** Everything a turning page can sweep through. */\n reach: Rect\n}\n\nconst isClient = typeof window !== 'undefined'\n\n/**\n * A 3D magazine to add to your own three.js scene. It draws nothing itself: call `tick()`\n * every frame you render, and render again whenever it fires `change`.\n *\n * In its own units a page is 1 wide and `pageAspect` tall, the spine runs up the y axis at\n * x = 0, and the pages lie on the z = 0 plane facing +z. Move, turn and scale it like any\n * object. Pointer input is cast through `camera` onto the pages, wherever they are.\n *\n * ```ts\n * const magazine = new LeafThroughObject({ renderer, camera }, { pages, layout: 'spread' })\n * scene.add(magazine)\n * renderer.setAnimationLoop((now) => {\n * magazine.tick(now)\n * renderer.render(scene, camera)\n * })\n * ```\n */\nexport class LeafThroughObject extends THREE.Group<LeafThroughObjectEventMap> {\n /** The camera your scene is drawn with; pointer input is cast from it. */\n camera: THREE.Camera\n readonly domElement: HTMLElement\n private inputEnabled = true\n\n /**\n * Whether it takes pointer and key input. Turn it off while it's tucked away or moving,\n * and on again; the pages stay as they are. Turning it off lets go of a page being held.\n * Default `true`.\n */\n get enabled() {\n return this.inputEnabled\n }\n set enabled(value: boolean) {\n if (this.inputEnabled && !value) this.view?.engine.cancelInput?.()\n this.inputEnabled = value\n }\n\n private readonly renderer: THREE.WebGLRenderer\n private options: LeafThroughObjectOptions\n private readonly paper: PaperTextures\n private loader: PageLoader | null = null\n private view: { engine: Engine; settings: ResolvedSettings; framing: Framing; dispose: () => void } | null = null\n\n private spreadMedia: { list: MediaQueryList; abort: AbortController } | null = null\n private readonly motionMedia: MediaQueryList\n private readonly abort = new AbortController()\n private readonly riffle = new Riffle()\n private readonly metrics: { readonly dragWidth: number; dragDelta(dx: number, dy: number): number }\n private readonly ray = new THREE.Ray()\n private readonly point = new THREE.Vector3()\n private readonly ends = [new THREE.Vector3(), new THREE.Vector3()]\n private needsRender = true\n private lastPagesKey = ''\n private lastDetailKey = ''\n private inView = ''\n private ready = false\n private readyFired = false\n private disposed = false\n\n constructor({ renderer, camera, domElement = renderer.domElement }: LeafThroughObjectContext, options: LeafThroughObjectOptions) {\n super()\n if (!isClient) throw new Error('LeafThrough needs a browser; create it on the client.')\n this.renderer = renderer\n this.camera = camera\n this.domElement = domElement\n this.options = copyOptions(options)\n const object = this\n this.metrics = {\n get dragWidth() {\n return object.dragWidth()\n },\n dragDelta(dx, dy) {\n const axis = object.pageAxis()\n return (dx * axis.x + dy * axis.y) / (axis.length() || 1)\n },\n }\n\n this.paper = new PaperTextures(options.paper?.backColor ?? '#fcfcf9', {\n onGrain: () => this.remount(),\n onError: (error) => this.fail(error),\n })\n\n const signal = this.abort.signal\n this.watchSpreadQuery()\n this.motionMedia = window.matchMedia('(prefers-reduced-motion: reduce)')\n this.motionMedia.addEventListener('change', () => this.remount(), { signal })\n window.addEventListener('online', () => this.retry(), { signal })\n bindKeyboard(\n domElement,\n () => (this.enabled && this.shown() ? this.view?.settings.interaction.keyboard : false),\n { next: () => this.next(), prev: () => this.prev(), first: () => this.goTo(0), last: () => this.goTo(this.pageCount - 1) },\n signal,\n )\n\n this.remount(options.startPage ?? 0)\n }\n\n // --- public API -----------------------------------------------------------------\n\n /** First visible page, 0-based. */\n get page(): number {\n return this.detail().page\n }\n\n get pageCount(): number {\n return this.options.pages.length\n }\n\n get layout(): Layout {\n return this.view?.settings.layout ?? this.pickLayout()\n }\n\n get visiblePages(): number[] {\n return this.detail().visiblePages\n }\n\n /** True while a page is being dragged or is completing its turn. */\n get turning(): boolean {\n return this.view?.engine.turning ?? false\n }\n\n /**\n * Where the magazine lies in its own units: frame `pages` with your camera. Changes with\n * the layout, which `'auto'` can switch as the window resizes.\n */\n get bounds(): LeafThroughBounds {\n const layout = this.layout\n const aspect = this.view?.settings.pageAspect ?? this.resolve(layout).pageAspect\n return { pages: plainFramingFor(layout, aspect).box, reach: framingFor(layout, aspect).view }\n }\n\n /** Turns to the next page. Returns false at the end. */\n next(): boolean {\n this.riffle.stop()\n return this.view?.engine.flip(true) ?? false\n }\n\n /** Turns to the previous page. Returns false at the start. */\n prev(): boolean {\n this.riffle.stop()\n return this.view?.engine.flip(false) ?? false\n }\n\n /** Shows `page` (0-based), jumping there or turning the pages one by one. */\n goTo(page: number, { animate = false }: GoToOptions = {}) {\n if (!this.view || Number.isNaN(page)) return\n this.riffle.stop()\n const engine = this.view.engine\n const target = pageToPosition(page, this.view.settings.layout, this.pageCount)\n if (!animate) {\n engine.jumpTo(target)\n return\n }\n if (engine.position === target) return\n this.riffle.start(target, this.view.settings.motion.speed)\n this.requestFrame()\n }\n\n /** Loads the pages that failed again. Also happens by itself when the browser comes back online. */\n retry() {\n this.loader?.retry()\n }\n\n /** Changes some options and keeps the rest. Structural changes rebuild the pages and keep the current page. */\n update(options: Partial<LeafThroughObjectOptions>) {\n this.applyOptions({ ...this.options, ...options })\n }\n\n /** Replaces every option at once: anything left out goes back to its default. */\n setOptions(options: LeafThroughObjectOptions) {\n this.applyOptions(options)\n }\n\n /**\n * Whether a press at this point on screen lands on the magazine, which then takes it. Check\n * it in your own pointer handlers to leave such presses alone. False while it's hidden or\n * not `enabled`.\n */\n covers(clientX: number, clientY: number): boolean {\n const view = this.view\n if (!view || !this.enabled || !this.shown()) return false\n if (!rayThrough(this.camera, this.domElement, clientX, clientY, this.ray)) return false\n const point = floorPoint(view.engine.group, this.ray, this.point)\n if (!point) return false\n // a closed spread only turns from its cover side\n const box = view.framing.box\n const side = view.engine.activeSide\n const left = side === 'right' ? box.x + box.width / 2 : box.x\n const right = side === 'left' ? box.x + box.width / 2 : box.x + box.width\n return point.x >= left && point.x <= right && point.y >= box.y && point.y <= box.y + box.height\n }\n\n /**\n * Advances the pages to `now` (a `requestAnimationFrame` timestamp). Call it every frame\n * before rendering. Returns true while the pages are moving and need another frame.\n */\n tick(now = performance.now()): boolean {\n const view = this.view\n if (!view) return false\n this.riffle.step(now, view.engine)\n const moved = view.engine.update(now)\n this.followReader(view.engine, view.settings.layout)\n const draw = moved || this.needsRender\n this.needsRender = false\n if (this.ready && !this.readyFired) {\n this.readyFired = true\n this.options.onReady?.()\n this.dispatchEvent({ type: 'ready' })\n }\n this.emitPageChange()\n return draw || this.riffle.active\n }\n\n /** Frees the pages, textures and listeners, and takes the magazine out of its parent. */\n dispose() {\n if (this.disposed) return\n this.disposed = true\n this.riffle.stop()\n this.abort.abort()\n this.spreadMedia?.abort.abort()\n this.view?.dispose()\n this.view = null\n this.loader?.dispose()\n this.paper.dispose()\n this.removeFromParent()\n }\n\n // --- options and layout -----------------------------------------------------------\n\n private applyOptions(options: LeafThroughObjectOptions) {\n if (this.disposed) return\n const previous = this.options\n const next = (this.options = copyOptions(options))\n const changed = changedOptions(previous, next)\n if (!changed.length) return\n if (changed.includes('spreadQuery')) this.watchSpreadQuery()\n\n // shadows turn on and off in place; the rest of lighting rebuilds\n const { shadows: wasShadowed, ...lighting } = previous.lighting ?? {}\n const { shadows: shadowed, ...nextLighting } = next.lighting ?? {}\n const liveShadows = changed.includes('lighting') && deepEqual(lighting, nextLighting)\n if (liveShadows && (wasShadowed ?? true) !== (shadowed ?? true)) this.view?.engine.setShadows?.(shadowed ?? true)\n\n const page = this.page\n let rebuild = changed.some((key) => {\n if (key === 'deform') return deformKey(previous) !== deformKey(next)\n if (key === 'lighting') return !liveShadows\n return !LIVE_OPTIONS.has(key)\n })\n if (changed.includes('pages') && this.pagesKey(next) !== this.lastPagesKey) {\n this.loader?.dispose()\n this.loader = null\n rebuild = true\n }\n if (changed.includes('preload')) this.loader?.setRadius(preloadRadius(this.options.preload))\n if (rebuild) this.remount(page)\n }\n\n private pickLayout(): Layout {\n const layout = this.options.layout ?? 'auto'\n if (layout !== 'auto') return layout\n return this.spreadMedia?.list.matches ? 'spread' : 'single'\n }\n\n private watchSpreadQuery() {\n this.spreadMedia?.abort.abort()\n const list = window.matchMedia(this.options.spreadQuery ?? DEFAULT_SPREAD_QUERY)\n const abort = new AbortController()\n list.addEventListener('change', () => this.remount(), { signal: abort.signal })\n this.spreadMedia = { list, abort }\n }\n\n private remount(page = this.page) {\n if (this.disposed) return\n this.riffle.stop()\n this.view?.dispose()\n\n const layout = this.pickLayout()\n const settings = this.resolve(layout)\n this.lastPagesKey = this.pagesKey(this.options)\n if (!this.loader) this.loadPages(page, layout)\n this.loader?.setWrap(layout === 'single' && settings.loop)\n\n const domElement = this.domElement\n const blank = this.paper.blank\n const ctx = {\n input: {\n element: domElement,\n ray: (x: number, y: number, ray: THREE.Ray) => rayThrough(this.camera, domElement, x, y, ray),\n covers: (x: number, y: number) => this.covers(x, y),\n },\n settings,\n textures: this.options.pages.map((_, i) => this.loader?.textures[i] ?? blank),\n blank: layout === 'single' ? this.paper.back(settings.paper.backColor) : blank,\n pattern: this.paper.grain(settings, this.renderer),\n shared: sharedResources(this.renderer),\n position: pageToPosition(page, layout, this.pageCount),\n metrics: this.metrics,\n shading: {\n spineContact: { value: new THREE.Vector3(1, 0, 0) },\n lightDirection: { value: new THREE.Vector3(...settings.lighting.sun.position).normalize() },\n origin: { value: new THREE.Vector3() },\n },\n requestFrame: () => this.requestFrame(),\n }\n const engine: Engine = layout === 'spread' ? new SpreadEngine(ctx) : new SingleEngine(ctx)\n this.add(engine.group)\n this.view = {\n engine,\n settings,\n framing: framingFor(layout, settings.pageAspect),\n dispose: () => {\n this.remove(engine.group)\n engine.dispose()\n },\n }\n this.requestFrame()\n }\n\n // the settings, with this object's own defaults and its deform\n private resolve(layout: Layout): ResolvedSettings {\n const settings = resolveSettings(this.options, layout, this.motionMedia.matches)\n const paper = this.options.paper\n settings.paper.sheetGap = Math.max(0, paper?.sheetGap ?? SHEET_GAP)\n settings.paper.depthBias = paper?.depthBias ?? false\n const deform = this.options.deform\n if (deform) {\n // The shaders read each uniform through the options as they are now, so a value set on\n // the caller's own object, or an object swapped in by update(), shows on the next frame.\n const uniforms: Record<string, THREE.IUniform> = {}\n for (const name of Object.keys(deform.uniforms ?? {})) {\n const options = () => this.options\n uniforms[name] = {\n get value() {\n return options().deform?.uniforms?.[name]?.value\n },\n }\n }\n settings.deform = { glsl: deform.glsl, uniforms }\n }\n return settings\n }\n\n // A drag across this many pixels turns a page all the way: the magazine's drag width, at the\n // size it shows on screen now.\n private readonly axis = new THREE.Vector2()\n\n private pageAxis() {\n const group = this.view?.engine.group ?? this\n group.updateWorldMatrix(true, false)\n this.camera.updateWorldMatrix(true, false)\n const rect = this.domElement.getBoundingClientRect()\n const [spine, edge] = this.ends.map((end, x) => end.set(x, 0, 0).applyMatrix4(group.matrixWorld).project(this.camera))\n return this.axis.set(((edge.x - spine.x) * rect.width) / 2, -((edge.y - spine.y) * rect.height) / 2)\n }\n\n private dragWidth() {\n return Math.max(1, (this.view?.framing.dragWidth ?? 1) * this.pageAxis().length())\n }\n\n // whether this and every parent are visible\n private shown() {\n for (let object: THREE.Object3D | null = this; object; object = object.parent) if (!object.visible) return false\n return true\n }\n\n private requestFrame() {\n this.needsRender = true\n this.dispatchEvent({ type: 'change' })\n }\n\n // --- pages ---------------------------------------------------------------------\n\n // what the pages show; their alt and text don't count\n private pagesKey(options: LeafThroughObjectOptions) {\n return options.pages.map((p) => sourceKey(pageImage(p))).join('\\n')\n }\n\n private loadPages(around: number, layout: Layout) {\n this.loader?.dispose()\n const loader = new PageLoader(this.options.pages.map(pageImage), this.renderer, preloadRadius(this.options.preload), {\n onLoad: (page, texture) => {\n if (this.loader !== loader) return\n this.view?.engine.setPageTexture(page, texture)\n this.options.onPageLoad?.(page)\n this.dispatchEvent({ type: 'pageload', detail: { page } })\n },\n onUnload: (page) => {\n if (this.loader !== loader) return\n this.view?.engine.setPageTexture(page, this.paper.blank)\n },\n onError: (page, error) => this.fail(error, page),\n })\n this.loader = loader\n this.inView = ''\n this.ready = this.readyFired = false\n // the pages around the start, so the first turn either way has its next page ready\n const count = this.pageCount\n const first = [around - 1, around, around + 1, around + 2]\n const visible = pagesAt(pageToPosition(around, layout, count), layout, count)\n loader.start(visible[0] ?? around, visible[visible.length - 1] ?? around, first).then(() => {\n if (this.loader !== loader || this.disposed) return\n this.ready = true\n this.requestFrame()\n })\n }\n\n // loads around what's on screen, pages mid-turn included, the page being read first\n private followReader(engine: Engine, layout: Layout) {\n if (!this.loader) return\n const { from, to } = engine.pagesInView()\n const around = pagesAt(engine.position, layout, this.pageCount)[0] ?? to\n const key = `${from}:${to}:${around}`\n if (key === this.inView) return\n this.inView = key\n this.loader.focus(from, to, around)\n }\n\n private fail(error: unknown, page?: number) {\n if (this.options.onError) this.options.onError(error, page)\n else console.warn('[leafthrough]', error)\n this.dispatchEvent({ type: 'error', detail: { error, page } })\n }\n\n private detail(): PageChangeDetail {\n return pageDetail(this.view?.engine.position ?? 0, this.layout, this.pageCount)\n }\n\n private emitPageChange() {\n const detail = this.detail()\n const key = `${detail.layout}:${detail.visiblePages.join(',')}/${detail.pageCount}`\n if (key === this.lastDetailKey) return\n const first = !this.lastDetailKey\n this.lastDetailKey = key\n if (first) return\n this.options.onPageChange?.(detail)\n this.dispatchEvent({ type: 'pagechange', detail })\n }\n}\n\n// What a deform compiles to: new uniform values, or new objects under the same names, don't rebuild.\nconst deformKey = ({ deform }: LeafThroughObjectOptions) =>\n deform ? `${deform.glsl}\\n${Object.keys(deform.uniforms ?? {}).join(',')}` : ''\n\n// Like snapshot(), but deform's uniforms stay the caller's own objects, whose values the shaders read.\nfunction copyOptions(options: LeafThroughObjectOptions): LeafThroughObjectOptions {\n const copy = snapshot(options)\n const uniforms = options.deform?.uniforms\n if (copy.deform && uniforms) copy.deform = { ...copy.deform, uniforms: { ...uniforms } }\n return copy\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
|
+
/** The grain as a canvas: drawn the first time, the same one from then on. */
|
|
3
|
+
export declare function paperPatternCanvas(): HTMLCanvasElement;
|
|
4
|
+
/**
|
|
5
|
+
* A texture of the grain for `renderer`. The magazines it draws share one, through
|
|
6
|
+
* `sharedResources`, freed with the last of them.
|
|
7
|
+
*/
|
|
2
8
|
export declare function createPaperPattern(renderer: THREE.WebGLRenderer): THREE.CanvasTexture;
|
package/dist/react.js
CHANGED
package/dist/shared.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type * as THREE from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* Things every magazine drawn by one renderer can share: made the first time one asks, handed
|
|
4
|
+
* to the rest, and freed with the last one still using them. They're kept per renderer because
|
|
5
|
+
* three.js keeps a GPU copy of a texture or geometry, and a dispose listener on it, for each
|
|
6
|
+
* renderer that draws it; shared across renderers, it would keep hold of every renderer that
|
|
7
|
+
* ever drew it until the last magazine anywhere let it go.
|
|
8
|
+
*/
|
|
9
|
+
export declare class SharedResources {
|
|
10
|
+
private readonly entries;
|
|
11
|
+
/** The resource under `key`, made by `make` for the first user. Pair every call with `release`. */
|
|
12
|
+
acquire<T extends {
|
|
13
|
+
dispose(): void;
|
|
14
|
+
}>(key: string, make: () => T): T;
|
|
15
|
+
/** Lets go of `key`; the last user's release frees it. */
|
|
16
|
+
release(key: string): void;
|
|
17
|
+
}
|
|
18
|
+
/** What the magazines drawn by `renderer` share. */
|
|
19
|
+
export declare function sharedResources(renderer: THREE.WebGLRenderer): SharedResources;
|
|
@@ -13,6 +13,7 @@ export declare class SingleEngine implements Engine {
|
|
|
13
13
|
private readonly metrics;
|
|
14
14
|
private readonly requestFrame;
|
|
15
15
|
private readonly geometry;
|
|
16
|
+
private readonly releaseGeometry;
|
|
16
17
|
private readonly sheets;
|
|
17
18
|
private readonly count;
|
|
18
19
|
private readonly radiusSpacing;
|
|
@@ -53,6 +54,7 @@ export declare class SingleEngine implements Engine {
|
|
|
53
54
|
private paced;
|
|
54
55
|
private closing;
|
|
55
56
|
private shutting;
|
|
57
|
+
private fallOf;
|
|
56
58
|
private inFlight;
|
|
57
59
|
private busy;
|
|
58
60
|
private pointAt;
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import type { ResolvedSettings } from '../options.js';
|
|
3
|
+
import type { SharedResources } from '../shared.js';
|
|
3
4
|
import { type SheetMesh, type SheetShading, type SheetUniforms } from '../paper/paperMaterial.js';
|
|
4
5
|
export declare function createSingleGeometry(aspect: number): THREE.BufferGeometry;
|
|
6
|
+
/** The sheet geometry for `aspect`, shared by the magazines a renderer draws; `release` it when done. */
|
|
7
|
+
export declare function sharedSingleGeometry(shared: SharedResources, aspect: number): {
|
|
8
|
+
geometry: THREE.BufferGeometry<THREE.NormalBufferAttributes, THREE.BufferGeometryEventMap>;
|
|
9
|
+
release: () => void;
|
|
10
|
+
};
|
|
5
11
|
export interface SingleSheetUniforms extends SheetUniforms {
|
|
6
12
|
uPileLift: THREE.IUniform<THREE.Vector3>;
|
|
7
13
|
uPileReach: THREE.IUniform<THREE.Vector2>;
|
|
@@ -13,6 +13,7 @@ export declare class SpreadEngine implements Engine {
|
|
|
13
13
|
private readonly metrics;
|
|
14
14
|
private readonly requestFrame;
|
|
15
15
|
private readonly geometry;
|
|
16
|
+
private readonly releaseGeometry;
|
|
16
17
|
private readonly sheets;
|
|
17
18
|
private readonly abort;
|
|
18
19
|
private readonly shading;
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import type { ResolvedSettings } from '../options.js';
|
|
3
|
+
import type { SharedResources } from '../shared.js';
|
|
3
4
|
import { type SheetMesh, type SheetShading, type SheetUniforms } from '../paper/paperMaterial.js';
|
|
4
5
|
export declare function createSpreadGeometry(aspect: number): THREE.BufferGeometry;
|
|
6
|
+
/** The sheet geometry for `aspect`, shared by the magazines a renderer draws; `release` it when done. */
|
|
7
|
+
export declare function sharedSpreadGeometry(shared: SharedResources, aspect: number): {
|
|
8
|
+
geometry: THREE.BufferGeometry<THREE.NormalBufferAttributes, THREE.BufferGeometryEventMap>;
|
|
9
|
+
release: () => void;
|
|
10
|
+
};
|
|
5
11
|
export interface SpreadSheetUniforms extends SheetUniforms {
|
|
6
12
|
uFlipProgress: THREE.IUniform<number>;
|
|
7
13
|
uWrinkleSide: THREE.IUniform<number>;
|
package/dist/svelte.js
CHANGED
package/dist/textures.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export declare class PageLoader {
|
|
|
21
21
|
private readonly owned;
|
|
22
22
|
private readonly inFlight;
|
|
23
23
|
private readonly failed;
|
|
24
|
+
private readonly pending;
|
|
25
|
+
private cancelUploads;
|
|
24
26
|
private first;
|
|
25
27
|
private from;
|
|
26
28
|
private to;
|
|
@@ -48,6 +50,9 @@ export declare class PageLoader {
|
|
|
48
50
|
private distance;
|
|
49
51
|
private pump;
|
|
50
52
|
private load;
|
|
53
|
+
private show;
|
|
54
|
+
private scheduleUploads;
|
|
55
|
+
private uploadSome;
|
|
51
56
|
private createTexture;
|
|
52
57
|
}
|
|
53
58
|
/** Loads the paper grain option into a tiling texture. */
|
package/dist/vue.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leafthrough",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "A 3D magazine you can flip through, with paper that bends, rolls and casts shadows. Built on three.js, with React, Vue, Svelte and Web Component bindings.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"magazine",
|