leafthrough 0.1.4 → 0.1.6

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/dist/element.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  LeafThrough
3
- } from "./chunk-GYSQO7QZ.js";
3
+ } from "./chunk-QFOGCGJJ.js";
4
4
 
5
5
  // src/element.ts
6
6
  var ATTRIBUTES = {
package/dist/engine.d.ts CHANGED
@@ -57,6 +57,8 @@ export interface Engine {
57
57
  /** Moves to a position at once, without animating. */
58
58
  jumpTo(position: number): void;
59
59
  setPageTexture(page: number, texture: THREE.Texture): void;
60
+ /** Turns the sheets' shadows on or off, both casting and receiving, without a rebuild. */
61
+ setShadows?(on: boolean): void;
60
62
  /** Releases pointer capture and cancels active gestures and hold timers. */
61
63
  cancelInput?(): void;
62
64
  dispose(): void;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { LeafThrough, type LeafThroughEventMap, type GoToOptions } from './LeafThrough.js';
2
2
  export { LeafThroughObject, type LeafThroughBounds, type LeafThroughObjectContext, type LeafThroughObjectEventMap, type LeafThroughObjectOptions, } from './LeafThroughObject.js';
3
3
  export type { Rect } from './framing.js';
4
- export { DEFAULT_PAGE_ASPECT, DEFAULT_SPREAD_QUERY, defaultPageLabel, type LeafThroughOptions, type InteractionOptions, type Layout, type LightingOptions, type MotionOptions, type PageChangeDetail, type PageDescriptor, type PageImage, type PageSource, type PaperOptions, } from './options.js';
4
+ export { DEFAULT_PAGE_ASPECT, DEFAULT_SPREAD_QUERY, defaultPageLabel, type LeafThroughOptions, type InteractionOptions, type Layout, type LightingOptions, type MotionOptions, type PageChangeDetail, type PageDescriptor, type PageImage, type PageSource, type PaperDeform, type PaperOptions, } from './options.js';
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  SpreadEngine,
11
11
  bindKeyboard,
12
12
  changedOptions,
13
+ deepEqual,
13
14
  defaultPageLabel,
14
15
  floorPoint,
15
16
  framingFor,
@@ -23,10 +24,11 @@ import {
23
24
  resolveSettings,
24
25
  snapshot,
25
26
  sourceKey
26
- } from "./chunk-GYSQO7QZ.js";
27
+ } from "./chunk-QFOGCGJJ.js";
27
28
 
28
29
  // src/LeafThroughObject.ts
29
30
  import * as THREE from "three";
31
+ var SHEET_GAP = 5e-4;
30
32
  var isClient = typeof window !== "undefined";
31
33
  var LeafThroughObject = class extends THREE.Group {
32
34
  /** The camera your scene is drawn with; pointer input is cast from it. */
@@ -71,7 +73,7 @@ var LeafThroughObject = class extends THREE.Group {
71
73
  this.renderer = renderer;
72
74
  this.camera = camera;
73
75
  this.domElement = domElement;
74
- this.options = snapshot(options);
76
+ this.options = copyOptions(options);
75
77
  const object = this;
76
78
  this.metrics = {
77
79
  get dragWidth() {
@@ -123,7 +125,7 @@ var LeafThroughObject = class extends THREE.Group {
123
125
  */
124
126
  get bounds() {
125
127
  const layout = this.layout;
126
- const aspect = this.view?.settings.pageAspect ?? resolveSettings(this.options, layout, false).pageAspect;
128
+ const aspect = this.view?.settings.pageAspect ?? this.resolve(layout).pageAspect;
127
129
  return { pages: plainFramingFor(layout, aspect).box, reach: framingFor(layout, aspect).view };
128
130
  }
129
131
  /** Turns to the next page. Returns false at the end. */
@@ -216,12 +218,20 @@ var LeafThroughObject = class extends THREE.Group {
216
218
  applyOptions(options) {
217
219
  if (this.disposed) return;
218
220
  const previous = this.options;
219
- const next = this.options = snapshot(options);
221
+ const next = this.options = copyOptions(options);
220
222
  const changed = changedOptions(previous, next);
221
223
  if (!changed.length) return;
222
224
  if (changed.includes("spreadQuery")) this.watchSpreadQuery();
225
+ const { shadows: wasShadowed, ...lighting } = previous.lighting ?? {};
226
+ const { shadows: shadowed, ...nextLighting } = next.lighting ?? {};
227
+ const liveShadows = changed.includes("lighting") && deepEqual(lighting, nextLighting);
228
+ if (liveShadows && (wasShadowed ?? true) !== (shadowed ?? true)) this.view?.engine.setShadows?.(shadowed ?? true);
223
229
  const page = this.page;
224
- let rebuild = changed.some((key) => !LIVE_OPTIONS.has(key));
230
+ let rebuild = changed.some((key) => {
231
+ if (key === "deform") return deformKey(previous) !== deformKey(next);
232
+ if (key === "lighting") return !liveShadows;
233
+ return !LIVE_OPTIONS.has(key);
234
+ });
225
235
  if (changed.includes("pages") && this.pagesKey(next) !== this.lastPagesKey) {
226
236
  this.loader?.dispose();
227
237
  this.loader = null;
@@ -247,7 +257,7 @@ var LeafThroughObject = class extends THREE.Group {
247
257
  this.riffle.stop();
248
258
  this.view?.dispose();
249
259
  const layout = this.pickLayout();
250
- const settings = resolveSettings(this.options, layout, this.motionMedia.matches);
260
+ const settings = this.resolve(layout);
251
261
  this.lastPagesKey = this.pagesKey(this.options);
252
262
  if (!this.loader) this.loadPages(page, layout);
253
263
  this.loader?.setWrap(layout === "single" && settings.loop);
@@ -267,7 +277,8 @@ var LeafThroughObject = class extends THREE.Group {
267
277
  metrics: this.metrics,
268
278
  shading: {
269
279
  spineContact: { value: new THREE.Vector3(1, 0, 0) },
270
- lightDirection: { value: new THREE.Vector3(...settings.lighting.sun.position).normalize() }
280
+ lightDirection: { value: new THREE.Vector3(...settings.lighting.sun.position).normalize() },
281
+ origin: { value: new THREE.Vector3() }
271
282
  },
272
283
  requestFrame: () => this.requestFrame()
273
284
  };
@@ -284,6 +295,27 @@ var LeafThroughObject = class extends THREE.Group {
284
295
  };
285
296
  this.requestFrame();
286
297
  }
298
+ // the settings, with this object's own defaults and its deform
299
+ resolve(layout) {
300
+ const settings = resolveSettings(this.options, layout, this.motionMedia.matches);
301
+ const paper = this.options.paper;
302
+ settings.paper.sheetGap = Math.max(0, paper?.sheetGap ?? SHEET_GAP);
303
+ settings.paper.depthBias = paper?.depthBias ?? false;
304
+ const deform = this.options.deform;
305
+ if (deform) {
306
+ const uniforms = {};
307
+ for (const name of Object.keys(deform.uniforms ?? {})) {
308
+ const options = () => this.options;
309
+ uniforms[name] = {
310
+ get value() {
311
+ return options().deform?.uniforms?.[name]?.value;
312
+ }
313
+ };
314
+ }
315
+ settings.deform = { glsl: deform.glsl, uniforms };
316
+ }
317
+ return settings;
318
+ }
287
319
  // A drag across this many pixels turns a page all the way: the magazine's drag width, at the
288
320
  // size it shows on screen now.
289
321
  axis = new THREE.Vector2();
@@ -368,6 +400,14 @@ var LeafThroughObject = class extends THREE.Group {
368
400
  this.dispatchEvent({ type: "pagechange", detail });
369
401
  }
370
402
  };
403
+ var deformKey = ({ deform }) => deform ? `${deform.glsl}
404
+ ${Object.keys(deform.uniforms ?? {}).join(",")}` : "";
405
+ function copyOptions(options) {
406
+ const copy = snapshot(options);
407
+ const uniforms = options.deform?.uniforms;
408
+ if (copy.deform && uniforms) copy.deform = { ...copy.deform, uniforms: { ...uniforms } };
409
+ return copy;
410
+ }
371
411
  export {
372
412
  DEFAULT_PAGE_ASPECT,
373
413
  DEFAULT_SPREAD_QUERY,
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 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 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 * `sun.position` (which way the paper is shaded) and `shadows` (whether sheets cast and\n * receive them) apply: the lights are your scene's.\n */\nexport type LeafThroughObjectOptions = Omit<LeafThroughOptions, 'bleed' | 'maxPixelRatio' | 'plain' | 'label' | 'formatPageLabel'>\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 = snapshot(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 ?? resolveSettings(this.options, layout, false).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 = snapshot(options))\n const changed = changedOptions(previous, next)\n if (!changed.length) return\n if (changed.includes('spreadQuery')) this.watchSpreadQuery()\n\n const page = this.page\n let rebuild = changed.some((key) => !LIVE_OPTIONS.has(key))\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 = resolveSettings(this.options, layout, this.motionMedia.matches)\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 },\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 // 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"],"mappings":"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","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 { 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":"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","names":[]}
package/dist/options.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ColorRepresentation, Texture } from 'three';
1
+ import type { ColorRepresentation, IUniform, Texture } from 'three';
2
2
  /** Anything a page can be printed from. Strings are image URLs. */
3
3
  export type PageImage = string | HTMLImageElement | HTMLCanvasElement | ImageBitmap | Texture;
4
4
  /** A page image with what's on it, for screen readers and the plain view. */
@@ -49,6 +49,35 @@ export interface PaperOptions {
49
49
  * `false`: transparent areas are unprinted paper.
50
50
  */
51
51
  cutout?: boolean;
52
+ /**
53
+ * Space between stacked sheets, in page widths. Sheets otherwise meet at the spine, where only
54
+ * `depthBias` keeps the right one in front. A booklet 120 mm wide with 0.1 mm paper is
55
+ * `0.1 / 120`. Default `0`, or `0.0005` in a `LeafThroughObject`.
56
+ */
57
+ sheetGap?: number;
58
+ /**
59
+ * Draws each sheet a little nearer the camera than the sheets under it (a polygon offset), so
60
+ * sheets that touch at the spine don't flicker through each other. It assumes the magazine is
61
+ * seen from the front, and in a scene of your own it can pull pages through what lies just in
62
+ * front of them. How far it moves a sheet depends on the camera's near and far planes. With
63
+ * a `sheetGap`, turn it off. Default `true`, or `false` in a `LeafThroughObject`.
64
+ */
65
+ depthBias?: boolean;
66
+ }
67
+ /**
68
+ * Reshapes the paper in your own GLSL, after the magazine has shaped it. Experimental: it may
69
+ * change in a patch release.
70
+ */
71
+ export interface PaperDeform {
72
+ /**
73
+ * Declares the uniforms it reads and defines `vec3 deformSheet(vec3 p, vec2 uv)`. `p` is a
74
+ * point of a sheet in the magazine's own units (a page is 1 wide, the spine runs up the y
75
+ * axis, the pages lie on z = 0 facing +z) and `uv` where it is on the sheet (x from the spine
76
+ * to the outer edge). Return where to draw it, in the same units.
77
+ */
78
+ glsl: string;
79
+ /** The uniforms `glsl` declares, by name. Change their `value` any time: they're read every frame. */
80
+ uniforms?: Record<string, IUniform>;
52
81
  }
53
82
  export interface LightingOptions {
54
83
  /** Hemisphere light. Default sky `#ffffff`, ground `#a1aeaf`, intensity `1.5` (spread) or `1.7` (single). */
@@ -177,6 +206,8 @@ export interface ResolvedSettings {
177
206
  paper: Required<Omit<PaperOptions, 'grain'>> & {
178
207
  grain: PaperOptions['grain'];
179
208
  };
209
+ /** Only a `LeafThroughObject` has one. */
210
+ deform: PaperDeform | null;
180
211
  lighting: {
181
212
  ambient: {
182
213
  sky: ColorRepresentation;
@@ -7,6 +7,8 @@ export interface SheetShading {
7
7
  spineContact: THREE.IUniform<THREE.Vector3>;
8
8
  /** Towards the sun, in the magazine's frame. */
9
9
  lightDirection: THREE.IUniform<THREE.Vector3>;
10
+ /** Where the sheets' group sits in the magazine's frame; the engine keeps it current. */
11
+ origin: THREE.IUniform<THREE.Vector3>;
10
12
  }
11
13
  export interface SheetShader {
12
14
  /** GLSL defining `vec3 sheetPosition(vec2 uv, float noise)` and its uniforms. */
@@ -23,12 +25,12 @@ export interface SheetShader {
23
25
  export type SheetMesh = THREE.Mesh<THREE.BufferGeometry, THREE.MeshStandardMaterial>;
24
26
  export declare function createSheetMesh(geometry: THREE.BufferGeometry, uniforms: SheetUniforms, shader: SheetShader, settings: ResolvedSettings): SheetMesh;
25
27
  /**
26
- * Every sheet rises from the spine at height 0, so along the spine they all but touch and
27
- * the depth buffer can't separate them: sheets further down flicker through as a light seam.
28
- * Bias each sheet's depth by its place in the stack instead: the top sheet wins, and a sheet
29
- * that's turning wins over everything.
28
+ * Without a sheet gap every sheet rises from the spine at height 0, so along the spine they
29
+ * all but touch and the depth buffer can't separate them: sheets further down flicker through
30
+ * as a light seam. Bias each sheet's depth by its place in the stack instead: the top sheet
31
+ * wins, and a sheet that's turning wins over everything. `bias` false leaves depth alone.
30
32
  */
31
- export declare function layerSheet(mesh: SheetMesh, depth: number, count: number, moving: boolean): void;
33
+ export declare function layerSheet(mesh: SheetMesh, depth: number, count: number, moving: boolean, bias: boolean): void;
32
34
  export declare function disposeSheetMesh(mesh: SheetMesh): void;
33
35
  /** A 1×1 texture of one colour, for blank pages and placeholders. */
34
36
  export declare function solidTexture(color: THREE.ColorRepresentation): THREE.DataTexture;
package/dist/react.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  LeafThrough,
4
4
  defaultPageLabel
5
- } from "./chunk-GYSQO7QZ.js";
5
+ } from "./chunk-QFOGCGJJ.js";
6
6
 
7
7
  // src/react.tsx
8
8
  import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
@@ -20,6 +20,7 @@ export declare class SingleEngine implements Engine {
20
20
  private readonly fallDelay;
21
21
  private readonly fallDuration;
22
22
  private readonly abort;
23
+ private readonly shading;
23
24
  private current;
24
25
  private turned;
25
26
  private flights;
@@ -42,6 +43,7 @@ export declare class SingleEngine implements Engine {
42
43
  private pileTop;
43
44
  setPageTexture(page: number, texture: THREE.Texture): void;
44
45
  cancelInput(): void;
46
+ setShadows(on: boolean): void;
45
47
  dispose(): void;
46
48
  private randomWobble;
47
49
  private seconds;
@@ -11,11 +11,13 @@ export interface SingleSheetUniforms extends SheetUniforms {
11
11
  uCone: THREE.IUniform<THREE.Vector3>;
12
12
  uFlipRotation: THREE.IUniform<THREE.Vector2>;
13
13
  uTrim: THREE.IUniform<THREE.Vector3>;
14
+ uStackGap: THREE.IUniform<number>;
14
15
  uFront: THREE.IUniform<THREE.Texture>;
15
16
  uBack: THREE.IUniform<THREE.Texture>;
16
17
  uPattern: THREE.IUniform<THREE.Texture>;
17
18
  uSpineContact: THREE.IUniform<THREE.Vector3>;
18
19
  uLightDirection: THREE.IUniform<THREE.Vector3>;
20
+ uSheetOrigin: THREE.IUniform<THREE.Vector3>;
19
21
  }
20
22
  export declare function createSingleSheet(geometry: THREE.BufferGeometry, front: THREE.Texture, back: THREE.Texture, pattern: THREE.Texture, settings: ResolvedSettings, shading: SheetShading): {
21
23
  mesh: SheetMesh;
@@ -38,6 +38,7 @@ export declare class SpreadEngine implements Engine {
38
38
  get activeSide(): "both" | "left" | "right";
39
39
  setPageTexture(page: number, texture: THREE.Texture): void;
40
40
  cancelInput(): void;
41
+ setShadows(on: boolean): void;
41
42
  dispose(): void;
42
43
  private randomWobble;
43
44
  private seconds;
@@ -11,11 +11,13 @@ export interface SpreadSheetUniforms extends SheetUniforms {
11
11
  uFlipRotation: THREE.IUniform<THREE.Vector2>;
12
12
  uDirection: THREE.IUniform<number>;
13
13
  uStackLift: THREE.IUniform<number>;
14
+ uStackGap: THREE.IUniform<number>;
14
15
  uFront: THREE.IUniform<THREE.Texture>;
15
16
  uBack: THREE.IUniform<THREE.Texture>;
16
17
  uPattern: THREE.IUniform<THREE.Texture>;
17
18
  uSpineContact: THREE.IUniform<THREE.Vector3>;
18
19
  uLightDirection: THREE.IUniform<THREE.Vector3>;
20
+ uSheetOrigin: THREE.IUniform<THREE.Vector3>;
19
21
  }
20
22
  export declare function createSpreadSheet(geometry: THREE.BufferGeometry, front: THREE.Texture, back: THREE.Texture, pattern: THREE.Texture, settings: ResolvedSettings, shading: SheetShading): {
21
23
  mesh: SheetMesh;
package/dist/svelte.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  LeafThrough
3
- } from "./chunk-GYSQO7QZ.js";
3
+ } from "./chunk-QFOGCGJJ.js";
4
4
 
5
5
  // src/svelte.ts
6
6
  function leafThrough(node, options) {
package/dist/vue.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  LeafThrough
3
- } from "./chunk-GYSQO7QZ.js";
3
+ } from "./chunk-QFOGCGJJ.js";
4
4
 
5
5
  // src/vue.ts
6
6
  import { defineComponent, h, onBeforeUnmount, onMounted, ref, shallowRef, watch } from "vue";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leafthrough",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
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",