leafthrough 0.1.1 → 0.1.2
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 +1 -1
- package/dist/{chunk-DQL4R32K.js → chunk-GJRP6UAH.js} +2 -2
- package/dist/chunk-GJRP6UAH.js.map +1 -0
- package/dist/element.js +1 -1
- package/dist/index.js +1 -1
- package/dist/options.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/svelte.js +1 -1
- package/dist/vue.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-DQL4R32K.js.map +0 -1
package/README.md
CHANGED
|
@@ -165,7 +165,7 @@ The magazine fills the element you give it and keeps its proportions, centred.
|
|
|
165
165
|
| `lighting` | `LightingOptions` | | See below. |
|
|
166
166
|
| `motion` | `MotionOptions` | | See below. |
|
|
167
167
|
| `interaction` | `InteractionOptions` | | See below. |
|
|
168
|
-
| `maxPixelRatio` | `number` | `
|
|
168
|
+
| `maxPixelRatio` | `number` | `3` | The canvas is also capped at about 15 megapixels. |
|
|
169
169
|
| `preload` | `number` | `6` | Pages either side of the visible ones (mid-turn and rolled-up ones included) to load ahead. Pages twice as far away unload. `Infinity` keeps all. |
|
|
170
170
|
| `plain` | `boolean` | `false` | Flat pages without 3D. Used by itself when the browser can't run WebGL. |
|
|
171
171
|
| `label` | `string` | `'Magazine'` | Accessible name. |
|
|
@@ -66,7 +66,7 @@ function resolveSettings(options, layout, prefersReducedMotion) {
|
|
|
66
66
|
hover: interaction.hover ?? true,
|
|
67
67
|
keyboard: interaction.keyboard ?? true
|
|
68
68
|
},
|
|
69
|
-
maxPixelRatio: options.maxPixelRatio ??
|
|
69
|
+
maxPixelRatio: options.maxPixelRatio ?? 3
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -2975,4 +2975,4 @@ export {
|
|
|
2975
2975
|
defaultPageLabel,
|
|
2976
2976
|
LeafThrough
|
|
2977
2977
|
};
|
|
2978
|
-
//# sourceMappingURL=chunk-
|
|
2978
|
+
//# sourceMappingURL=chunk-GJRP6UAH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/options.ts","../src/LeafThrough.ts","../src/content.ts","../src/framing.ts","../src/paper/paperMaterial.ts","../src/paper/paperPattern.ts","../src/plain/PlainEngine.ts","../src/single/SingleEngine.ts","../src/single/singleSheet.ts","../src/spread/SpreadEngine.ts","../src/spread/spreadSheet.ts","../src/textures.ts"],"sourcesContent":["import type { ColorRepresentation, Texture } from 'three'\n\n/** Anything a page can be printed from. Strings are image URLs. */\nexport type PageImage = string | HTMLImageElement | HTMLCanvasElement | ImageBitmap | Texture\n\n/** A page image with what's on it, for screen readers and the plain view. */\nexport interface PageDescriptor {\n src: PageImage\n /** A short description of the page, like an image's alt text. */\n alt?: string\n /** The page's text, or a transcript of it. Blank lines separate paragraphs. */\n text?: string\n}\n\n/** A page: its image, or a descriptor with the image and what's on it. */\nexport type PageSource = PageImage | PageDescriptor\n\n/**\n * `spread`: an open magazine, two pages side by side, sheets turning over the spine.\n * `single`: one page at a time, read pages rolling up behind the magazine.\n */\nexport type Layout = 'spread' | 'single'\n\nexport interface PaperOptions {\n /**\n * Paper fibre texture. `true` uses the built-in one, `false` turns it off, or pass a\n * tiling greyscale image (or its URL). Only the brightest specks show. Default `true`.\n */\n grain?: boolean | string | HTMLImageElement | HTMLCanvasElement\n /** How many times the grain tiles across a page. Default `1.46`. */\n grainScale?: number\n /** How much the grain darkens the paper, 0–1. Default `0.17`. */\n grainStrength?: number\n /** How much of the other side's print shows through, 0–1. Default `0.04` (spread), `0.3` (single). */\n showThrough?: number\n /** How much glossier ink is than bare paper, 0–1. Default `0.33`. */\n inkGloss?: number\n /** Default `0.5`. */\n roughness?: number\n /** A touch of metalness gives the paper its sheen. Default `0.17`. */\n metalness?: number\n /**\n * Colour of unprinted paper: the blank back of single-layout pages, and what shows through\n * transparent parts of page images. Default `#fcfcf9`.\n */\n backColor?: ColorRepresentation\n /** Scales the waviness of resting sheets (spread). `0` lays them flat. Default `1`. */\n wrinkles?: number\n /** Scales how much resting pages bulge up off the spine. Default `1`. */\n bulge?: number\n}\n\nexport interface LightingOptions {\n /** Hemisphere light. Default sky `#ffffff`, ground `#a1aeaf`, intensity `1.5` (spread) or `1.7` (single). */\n ambient?: { sky?: ColorRepresentation; ground?: ColorRepresentation; intensity?: number }\n /** The one directional light. `position` is in page widths, +z towards the viewer. */\n sun?: { color?: ColorRepresentation; intensity?: number; position?: [number, number, number] }\n /** Default `true`. */\n shadows?: boolean\n /** Shadow map resolution. Default `2048`. */\n shadowMapSize?: number\n /** Opacity of the shadow the magazine casts on the surface under it (spread). `0` hides it. Default `0.15`. */\n floorShadow?: number\n}\n\nexport interface MotionOptions {\n /** Multiplies the speed of every page turn. Default `1`. */\n speed?: number\n /** Share of the canvas width a drag must travel to turn a page all the way. Default `0.8` (spread), `1.2` (single). */\n dragDistance?: number\n /** Share of a turn a released drag must have made to complete it. Default `0.2` (spread), `0.1` (single). */\n commitThreshold?: number\n /** Scales how much a turning sheet bends. Default `1`. */\n curl?: number\n /** Largest tilt of the fold, in degrees, from where the page is held. Default `45` (spread), `35` (single). */\n maxTilt?: number\n /** Small random differences between turns, so no two look the same. Default `true`. */\n jitter?: boolean\n /** How far a hovered sheet lifts, as a share of a turn (spread). `0` disables. Default `0.03`. */\n hoverLift?: number\n /**\n * `'auto'` follows the user's `prefers-reduced-motion`. When reduced, turns are quicker and\n * hovering doesn't lift pages. Default `'auto'`.\n */\n reducedMotion?: 'auto' | boolean\n}\n\nexport interface InteractionOptions {\n /** Drag pages to turn them. Default `true`. */\n drag?: boolean\n /** Click or tap a page to turn it. Default `true`. */\n click?: boolean\n /** Press and hold to keep turning pages. Default `true`. */\n hold?: boolean\n /** Hovering lifts the top sheet (spread). Default `true`. */\n hover?: boolean\n /**\n * Arrow keys, PageUp/PageDown, Home/End. `true` listens while the magazine has focus,\n * `'global'` listens on the whole window. Default `true`.\n */\n keyboard?: boolean | 'global'\n}\n\nexport interface PageChangeDetail {\n /** First visible page, 0-based. */\n page: number\n /** Every page currently showing, 0-based. */\n visiblePages: number[]\n pageCount: number\n layout: Layout\n}\n\nexport interface LeafThroughOptions {\n /** The pages in reading order, front cover first. */\n pages: PageSource[]\n /** Page to open at, 0-based. Default `0`. Later changes are ignored; use `goTo()`. */\n startPage?: number\n /** `'auto'` picks `spread` when `spreadQuery` matches, `single` otherwise. Default `'auto'`. */\n layout?: 'auto' | Layout\n /** Media query for the spread layout. Default `(min-width: 768px) and (orientation: landscape)`. */\n spreadQuery?: string\n /** Page height divided by width. Default `1.377`. */\n pageAspect?: number\n /** Single layout: after the last page, close and start again from the cover. Default `true`. */\n loop?: boolean\n /**\n * `true` fits the magazine to the container and lets turning pages and shadows spill\n * outside it, up to the edges of the document (the container must not clip).\n * `false` keeps everything inside the container, which makes the pages smaller.\n * Default `true`.\n */\n bleed?: boolean\n paper?: PaperOptions\n lighting?: LightingOptions\n motion?: MotionOptions\n interaction?: InteractionOptions\n /** Upper limit for the canvas pixel ratio. `3` keeps pages sharp on 3× phone screens. Default `3`. */\n maxPixelRatio?: number\n /**\n * How many pages either side of the visible ones to load ahead. Visible means on screen:\n * pages mid-turn count, and in `single` so do read pages still rolled up behind the magazine.\n * Pages more than twice as far away are unloaded, which frees their memory. `Infinity` loads\n * and keeps every page. Default `6`.\n */\n preload?: number\n /**\n * Shows the pages as flat images, without 3D. The magazine switches to this by itself when\n * the browser can't run WebGL. Default `false`.\n */\n plain?: boolean\n /** Accessible name. Default `'Magazine'`. */\n label?: string\n /** Text announced to screen readers when pages change. */\n formatPageLabel?: (detail: PageChangeDetail) => string\n\n onPageChange?: (detail: PageChangeDetail) => void\n onReady?: () => void\n /** A page's image has loaded. Fires again if the page is unloaded and loads later. */\n onPageLoad?: (page: number) => void\n onError?: (error: unknown, page?: number) => void\n}\n\nexport const DEFAULT_SPREAD_QUERY = '(min-width: 768px) and (orientation: landscape)'\nexport const DEFAULT_PAGE_ASPECT = 1.377\n\nexport function defaultPageLabel({ visiblePages, pageCount }: PageChangeDetail): string {\n if (!visiblePages.length) return `Closed, ${pageCount} pages`\n const [first, last] = [visiblePages[0] + 1, visiblePages[visiblePages.length - 1] + 1]\n return first === last ? `Page ${first} of ${pageCount}` : `Pages ${first}–${last} of ${pageCount}`\n}\n\nexport function isPageDescriptor(page: PageSource): page is PageDescriptor {\n return typeof page === 'object' && page !== null && Object.getPrototypeOf(page) === Object.prototype && 'src' in page\n}\n\nexport const pageImage = (page: PageSource): PageImage => (isPageDescriptor(page) ? page.src : page)\n\n/** Everything an engine needs, with layout defaults filled in. */\nexport interface ResolvedSettings {\n layout: Layout\n pageAspect: number\n loop: boolean\n bleed: boolean\n paper: Required<Omit<PaperOptions, 'grain'>> & { grain: PaperOptions['grain'] }\n lighting: {\n ambient: { sky: ColorRepresentation; ground: ColorRepresentation; intensity: number }\n sun: { color: ColorRepresentation; intensity: number; position: [number, number, number] }\n shadows: boolean\n shadowMapSize: number\n floorShadow: number\n }\n motion: Required<Omit<MotionOptions, 'reducedMotion'>> & { reduced: boolean }\n interaction: Required<InteractionOptions>\n maxPixelRatio: number\n}\n\nexport function resolveSettings(options: LeafThroughOptions, layout: Layout, prefersReducedMotion: boolean): ResolvedSettings {\n const spread = layout === 'spread'\n const paper = options.paper ?? {}\n const lighting = options.lighting ?? {}\n const motion = options.motion ?? {}\n const interaction = options.interaction ?? {}\n const reduced = motion.reducedMotion === 'auto' || motion.reducedMotion === undefined ? prefersReducedMotion : motion.reducedMotion\n\n return {\n layout,\n pageAspect: options.pageAspect ?? DEFAULT_PAGE_ASPECT,\n loop: options.loop ?? true,\n bleed: options.bleed ?? true,\n paper: {\n grain: paper.grain ?? true,\n grainScale: paper.grainScale ?? 1.46,\n grainStrength: paper.grainStrength ?? 0.17,\n showThrough: paper.showThrough ?? (spread ? 0.04 : 0.3),\n inkGloss: paper.inkGloss ?? 0.33,\n roughness: paper.roughness ?? 0.5,\n metalness: paper.metalness ?? 0.17,\n backColor: paper.backColor ?? '#fcfcf9',\n wrinkles: paper.wrinkles ?? 1,\n bulge: paper.bulge ?? 1,\n },\n lighting: {\n ambient: {\n sky: lighting.ambient?.sky ?? '#ffffff',\n ground: lighting.ambient?.ground ?? '#a1aeaf',\n intensity: lighting.ambient?.intensity ?? (spread ? 1.5 : 1.7),\n },\n sun: {\n color: lighting.sun?.color ?? '#ffffff',\n intensity: lighting.sun?.intensity ?? 2,\n position: lighting.sun?.position ?? (spread ? [-3.5, 1.3, 4.1] : [0.689, -1.15, 2.15]),\n },\n shadows: lighting.shadows ?? true,\n shadowMapSize: lighting.shadowMapSize ?? 2048,\n floorShadow: lighting.floorShadow ?? 0.15,\n },\n motion: {\n speed: Math.max(0.05, (motion.speed ?? 1) * (reduced ? 2.5 : 1)),\n dragDistance: motion.dragDistance ?? (spread ? 0.8 : 1.2),\n commitThreshold: motion.commitThreshold ?? (spread ? 0.2 : 0.1),\n curl: motion.curl ?? 1,\n maxTilt: motion.maxTilt ?? (spread ? 45 : 35),\n jitter: motion.jitter ?? true,\n hoverLift: reduced ? 0 : (motion.hoverLift ?? 0.03),\n reduced,\n },\n interaction: {\n drag: interaction.drag ?? true,\n click: interaction.click ?? true,\n hold: (interaction.hold ?? true) && !reduced,\n hover: interaction.hover ?? true,\n keyboard: interaction.keyboard ?? true,\n },\n maxPixelRatio: options.maxPixelRatio ?? 3,\n }\n}\n","import * as THREE from 'three'\nimport { PageContent, sourceKey, VISUALLY_HIDDEN } from './content.js'\nimport type { Engine } from './engine.js'\nimport { fit, framingFor, plainFramingFor, type Framing, type Rect } from './framing.js'\nimport {\n DEFAULT_SPREAD_QUERY,\n defaultPageLabel,\n pageImage,\n resolveSettings,\n type LeafThroughOptions,\n type Layout,\n type PageChangeDetail,\n type ResolvedSettings,\n} from './options.js'\nimport { solidTexture } from './paper/paperMaterial.js'\nimport { createPaperPattern } from './paper/paperPattern.js'\nimport { PlainEngine } from './plain/PlainEngine.js'\nimport { SingleEngine } from './single/SingleEngine.js'\nimport { SpreadEngine } from './spread/SpreadEngine.js'\nimport { loadGrain, PageLoader } from './textures.js'\n\nexport interface LeafThroughEventMap {\n pagechange: CustomEvent<PageChangeDetail>\n ready: CustomEvent<void>\n pageload: CustomEvent<{ page: number }>\n error: CustomEvent<{ error: unknown; page?: number }>\n}\n\nexport interface GoToOptions {\n /** Turn the pages one by one instead of jumping. Default `false`. */\n animate?: boolean\n}\n\n// options that don't need a rebuild (pages only need one when their images change)\nconst LIVE_KEYS = new Set<keyof LeafThroughOptions>([\n 'onPageChange',\n 'onReady',\n 'onPageLoad',\n 'onError',\n 'formatPageLabel',\n 'label',\n 'startPage',\n 'preload',\n 'pages',\n])\nconst DEFAULT_PRELOAD = 6\n\nconst isClient = typeof window !== 'undefined'\n\n/**\n * A 3D magazine rendered into `container`. Size the container with CSS; the magazine\n * fits inside it (a container without a height takes its height from its width).\n *\n * ```ts\n * const magazine = new LeafThrough(document.querySelector('#magazine'), {\n * pages: ['/pages/01.jpg', '/pages/02.jpg', '/pages/03.jpg'],\n * })\n * magazine.addEventListener('pagechange', (e) => console.log(e.detail.page))\n * ```\n */\nexport class LeafThrough extends EventTarget {\n readonly container: HTMLElement\n /** The element the magazine renders into; focusable, labelled, and styleable via `[data-leafthrough]`. */\n readonly element: HTMLDivElement\n\n private options: LeafThroughOptions\n private readonly canvas: HTMLCanvasElement\n private readonly hitArea: HTMLDivElement\n private readonly live: HTMLDivElement\n private readonly content: PageContent\n // null when the browser can't run WebGL; the magazine then shows plain pages\n private readonly renderer: THREE.WebGLRenderer | null\n private frameRequest = 0\n private readonly blank: THREE.Texture\n private readonly backBlank: { texture: THREE.Texture; color: string }\n private pattern: { texture: THREE.Texture; key: unknown } | null = null\n private loader: PageLoader | null = null\n\n private view: {\n engine: Engine\n // null in the plain view\n scene: THREE.Scene | null\n camera: THREE.PerspectiveCamera | null\n framing: Framing\n settings: ResolvedSettings\n dispose: () => void\n } | 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 resizeObserver: ResizeObserver\n private readonly metrics = { dragWidth: 1 }\n private hitBox: Rect | null = null\n private hitSide: 'both' | 'right' | 'left' = 'both'\n private readonly intersection: IntersectionObserver\n private visible = true\n private needsRender = true\n private running = false\n // an animated goTo(): turn one page every `interval` ms until `target`\n private riffle: { target: number; interval: number; last: number } | null = null\n private lastPagesKey = ''\n private ready = false\n private disposed = false\n\n constructor(container: HTMLElement, options: LeafThroughOptions) {\n super()\n if (!isClient) throw new Error('LeafThrough needs a browser; create it on the client.')\n this.container = container\n this.options = snapshot(options)\n\n const element = (this.element = document.createElement('div'))\n element.dataset.leafthrough = ''\n element.dataset.state = 'loading'\n element.tabIndex = 0\n element.setAttribute('role', 'region')\n element.setAttribute('aria-roledescription', 'magazine')\n element.style.cssText = 'position:relative;width:100%;height:100%;outline-offset:4px;touch-action:pan-y pinch-zoom;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none'\n\n this.canvas = document.createElement('canvas')\n this.canvas.style.cssText = 'position:absolute;display:block;pointer-events:none;opacity:0;transition:opacity .4s'\n this.canvas.setAttribute('aria-hidden', 'true')\n this.hitArea = document.createElement('div')\n this.hitArea.dataset.leafthroughHitArea = ''\n this.hitArea.style.cssText = 'position:absolute;cursor:grab;touch-action:pan-y pinch-zoom'\n this.live = document.createElement('div')\n this.live.setAttribute('aria-live', 'polite')\n this.live.style.cssText = VISUALLY_HIDDEN\n this.content = new PageContent({\n onLoad: (page) => !this.disposed && this.pageLoaded(page),\n onError: (page, error) => !this.disposed && this.fail(error, page),\n onShown: () => {\n if (!this.plain) return\n this.ready = true\n this.needsRender = true\n },\n })\n element.append(this.canvas, this.content.element, this.hitArea, this.live)\n\n this.renderer = createRenderer(this.canvas)\n container.append(element)\n\n this.blank = solidTexture('#fcfcf9')\n const backColor = options.paper?.backColor ?? '#fcfcf9'\n this.backBlank = { texture: solidTexture(backColor), color: colorKey(backColor) }\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\n this.resizeObserver = new ResizeObserver(() => this.layoutBox())\n this.resizeObserver.observe(container)\n this.intersection = new IntersectionObserver(\n (entries) => {\n this.visible = entries[entries.length - 1].isIntersecting\n this.syncLoop()\n },\n { rootMargin: '50% 0px' },\n )\n this.intersection.observe(element)\n document.addEventListener('visibilitychange', () => this.syncLoop(), { signal })\n // the canvas is clipped to the document, which can move without the container resizing\n window.addEventListener('resize', () => this.layoutBox(), { signal })\n window.addEventListener('online', () => this.retry(), { signal })\n\n this.canvas.addEventListener('webglcontextlost', (e) => e.preventDefault(), { signal })\n this.canvas.addEventListener('webglcontextrestored', () => (this.needsRender = true), { signal })\n this.bindKeyboard()\n\n this.applyLabel()\n this.remount(options.startPage ?? 0)\n this.syncLoop()\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 showing flat pages: the `plain` option is on, or the browser can't run WebGL. */\n get plain(): boolean {\n return !this.renderer || !!this.options.plain\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 /** Turns to the next page. Returns false at the end. */\n next(): boolean {\n this.stopRiffle()\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.stopRiffle()\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.stopRiffle()\n const engine = this.view.engine\n const target = this.pageToPosition(page, this.view.settings.layout)\n if (!animate) {\n engine.jumpTo(target)\n return\n }\n if (engine.position === target) return\n this.riffle = { target, interval: 140 / this.view.settings.motion.speed, last: -Infinity }\n this.needsRender = true\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 if (this.plain) this.content.refresh()\n }\n\n /** Changes some options and keeps the rest. Structural changes rebuild the scene and keep the current page. */\n update(options: Partial<LeafThroughOptions>) {\n this.applyOptions({ ...this.options, ...options })\n }\n\n /**\n * Replaces every option at once: anything left out goes back to its default. Bindings\n * use this, since props describe the whole state. Structural changes rebuild the scene\n * and keep the current page.\n */\n setOptions(options: LeafThroughOptions) {\n this.applyOptions(options)\n }\n\n private applyOptions(options: LeafThroughOptions) {\n if (this.disposed) return\n const previous = this.options\n const next = (this.options = snapshot(options))\n\n const keys = new Set([...Object.keys(previous), ...Object.keys(next)]) as Set<keyof LeafThroughOptions>\n const changed = [...keys].filter((key) => !deepEqual(previous[key], next[key]))\n if (!changed.length) return\n if (changed.includes('label') || changed.includes('formatPageLabel')) this.applyLabel()\n if (changed.includes('spreadQuery')) this.watchSpreadQuery()\n\n const page = this.page\n let rebuild = changed.some((key) => !LIVE_KEYS.has(key))\n // new images load from scratch; a change to only alt or text just updates the content\n if (changed.includes('pages') && this.pagesKey(next) !== this.lastPagesKey) {\n this.loader?.dispose()\n this.loader = null\n this.resetReady()\n rebuild = true\n }\n if (changed.includes('preload')) this.loader?.setRadius(this.preloadRadius())\n if (rebuild) this.remount(page)\n }\n\n addEventListener<K extends keyof LeafThroughEventMap>(\n type: K,\n listener: (event: LeafThroughEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ): void\n addEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions): void\n addEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions) {\n super.addEventListener(type, listener, options)\n }\n\n removeEventListener<K extends keyof LeafThroughEventMap>(\n type: K,\n listener: (event: LeafThroughEventMap[K]) => void,\n options?: boolean | EventListenerOptions,\n ): void\n removeEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions): void\n removeEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions) {\n super.removeEventListener(type, listener, options)\n }\n\n dispose() {\n if (this.disposed) return\n this.disposed = true\n this.stopRiffle()\n this.setLoop(null)\n this.abort.abort()\n this.spreadMedia?.abort.abort()\n this.resizeObserver.disconnect()\n this.intersection.disconnect()\n this.view?.dispose()\n this.view = null\n this.loader?.dispose()\n this.pattern?.texture.dispose()\n this.blank.dispose()\n this.backBlank.texture.dispose()\n this.renderer?.dispose()\n // free the GL context now; browsers only allow a handful at once\n this.renderer?.forceContextLoss()\n this.element.remove()\n }\n\n // --- layout and scene -------------------------------------------------------------\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.stopRiffle()\n const wasPlain = this.view?.engine instanceof PlainEngine\n this.view?.dispose()\n\n const layout = this.pickLayout()\n const settings = resolveSettings(this.options, layout, this.motionMedia.matches)\n this.element.dataset.layout = layout\n this.lastPagesKey = this.pagesKey(this.options)\n this.view = this.renderer && !this.options.plain\n ? this.createScene(this.renderer, page, layout, settings)\n : this.createPlain(page, layout, settings, wasPlain)\n this.layoutBox()\n this.needsRender = true\n }\n\n private createPlain(page: number, layout: Layout, settings: ResolvedSettings, wasPlain: boolean) {\n // the plain view draws no textures\n this.loader?.dispose()\n this.loader = null\n if (!wasPlain) this.resetReady()\n this.element.dataset.plain = ''\n this.canvas.style.display = 'none'\n const { drag, click } = settings.interaction\n this.hitArea.style.cursor = drag || click ? 'pointer' : 'default'\n const engine = new PlainEngine({\n hitArea: this.hitArea,\n settings,\n pageCount: this.options.pages.length,\n position: this.pageToPosition(page, layout),\n })\n return {\n engine,\n scene: null,\n camera: null,\n framing: plainFramingFor(layout, settings.pageAspect),\n settings,\n dispose: () => engine.dispose(),\n }\n }\n\n private createScene(renderer: THREE.WebGLRenderer, page: number, layout: Layout, settings: ResolvedSettings) {\n delete this.element.dataset.plain\n this.canvas.style.display = 'block'\n this.content.place(null)\n if (!this.loader) this.loadPages(renderer, page, layout)\n this.loader?.setWrap(layout === 'single' && settings.loop)\n const framing = framingFor(layout, settings.pageAspect)\n const { drag, click, hold } = settings.interaction\n this.hitArea.style.cursor = drag ? 'grab' : click || hold ? 'pointer' : 'default'\n\n const scene = new THREE.Scene()\n // the frustum is set up in layoutBox(), once we know which part of the view is visible\n // Clip planes hug what a page can reach (about a page width towards the camera, a little\n // behind the floor): the tighter they are, the finer the depth buffer near the spine.\n const camera = new THREE.PerspectiveCamera(40, 1, Math.max(0.1, framing.distance - 1.8), framing.distance + 2)\n camera.position.set(framing.eye.x, framing.eye.y, framing.distance)\n camera.lookAt(framing.eye.x, framing.eye.y, 0)\n\n const { ambient, sun: sunOptions, shadows } = settings.lighting\n // the floor is the XY plane and +Z points at the viewer; the hemisphere keeps its\n // default +Y \"up\", so page faces get an even mix of sky and ground\n scene.add(new THREE.HemisphereLight(ambient.sky, ambient.ground, ambient.intensity))\n const sun = new THREE.DirectionalLight(sunOptions.color, sunOptions.intensity)\n sun.position.set(...sunOptions.position)\n sun.castShadow = shadows\n if (layout === 'single') sun.shadow.intensity = 0.4\n sun.shadow.mapSize.set(settings.lighting.shadowMapSize, settings.lighting.shadowMapSize)\n Object.assign(sun.shadow.camera, framing.shadow, { near: 0.5, far: 12 })\n sun.shadow.camera.updateProjectionMatrix()\n sun.shadow.bias = -0.001\n scene.add(sun, sun.target)\n const shading = {\n spineContact: { value: new THREE.Vector3(1, 0, 0) },\n lightDirection: { value: sun.position.clone().sub(sun.target.position).normalize() },\n }\n\n let floor: THREE.Mesh<THREE.PlaneGeometry, THREE.ShadowMaterial> | null = null\n if (layout === 'spread' && shadows && settings.lighting.floorShadow > 0) {\n floor = new THREE.Mesh(new THREE.PlaneGeometry(8, 8), new THREE.ShadowMaterial({ opacity: settings.lighting.floorShadow }))\n floor.position.z = -1e-5\n floor.receiveShadow = true\n scene.add(floor)\n }\n\n const pages = this.options.pages\n const textures = pages.map((_, i) => this.loader?.textures[i] ?? this.blank)\n const backColor = colorKey(settings.paper.backColor)\n if (backColor !== this.backBlank.color) {\n this.backBlank.texture.dispose()\n this.backBlank.texture = solidTexture(settings.paper.backColor)\n this.backBlank.color = backColor\n }\n const ctx = {\n camera,\n canvas: this.canvas,\n hitArea: this.hitArea,\n settings,\n textures,\n blank: layout === 'single' ? this.backBlank.texture : this.blank,\n pattern: this.patternFor(settings, renderer),\n position: this.pageToPosition(page, layout),\n metrics: this.metrics,\n shading,\n }\n const engine: Engine = layout === 'spread' ? new SpreadEngine(ctx) : new SingleEngine(ctx)\n scene.add(engine.group)\n\n return {\n engine,\n scene,\n camera,\n framing,\n settings,\n dispose: () => {\n engine.dispose()\n sun.shadow.dispose()\n floor?.geometry.dispose()\n floor?.material.dispose()\n },\n }\n }\n\n private patternFor(settings: ResolvedSettings, renderer: THREE.WebGLRenderer): THREE.Texture {\n const grain = settings.paper.grain\n const key = grain === true || grain === undefined ? 'builtin' : grain === false ? 'none' : grain\n if (this.pattern?.key === key) return this.pattern.texture\n this.pattern?.texture.dispose()\n let texture: THREE.Texture\n if (key === 'builtin') texture = createPaperPattern(renderer)\n else if (key === 'none') texture = solidTexture('#000000')\n else {\n // until the custom grain arrives, use none\n texture = solidTexture('#000000')\n loadGrain(key as string | HTMLImageElement | HTMLCanvasElement).then(\n (loaded) => {\n if (this.pattern?.key !== key || this.disposed) return loaded.dispose()\n this.pattern.texture.dispose()\n this.pattern.texture = loaded\n this.remount()\n },\n (error) => {\n if (this.pattern?.key === key && !this.disposed) this.fail(error)\n },\n )\n }\n this.pattern = { texture, key }\n return texture\n }\n\n // sizes the canvas and hit area to the container\n private layoutBox() {\n if (!this.view) return\n const { element } = this\n const { framing, settings, camera } = this.view\n element.style.height = '100%'\n const width = element.clientWidth\n const height = element.clientHeight\n if (!width) return\n const placed = fit(framing, width, height, settings.bleed)\n if (!height) element.style.height = `${Math.round(placed.height)}px`\n this.metrics.dragWidth = framing.dragWidth * placed.scale\n this.hitBox = placed.box\n this.placeHitArea()\n this.needsRender = true\n if (!camera || !this.renderer) {\n // the plain view: the pages are DOM, laid out over the box\n this.content.place(placed.box)\n return\n }\n\n // A bleeding view can reach past the edges of the page; past the sides that would\n // add a horizontal scrollbar, so the canvas stops at the document's edges and the\n // camera renders just that part of the view. The edges are measured in the element's own\n // pixels, which a CSS transform on the container (say, a scale) makes differ from the screen's.\n const root = element.getBoundingClientRect()\n const scaleX = root.width / element.offsetWidth || 1\n const scaleY = root.height / element.offsetHeight || 1\n const pageLeft = root.left + window.scrollX\n const pageTop = root.top + window.scrollY\n const documentWidth = document.documentElement.clientWidth\n const left = Math.max(placed.view.x, -pageLeft / scaleX)\n const right = Math.min(placed.view.x + placed.view.width, (documentWidth - pageLeft) / scaleX)\n const top = Math.max(placed.view.y, -pageTop / scaleY)\n const bottom = placed.view.y + placed.view.height\n const canvas = { x: left, y: top, width: Math.max(1, right - left), height: Math.max(1, bottom - top) }\n\n const px = (n: number) => `${Math.round(n * 100) / 100}px`\n Object.assign(this.canvas.style, {\n left: px(canvas.x),\n top: px(canvas.y),\n width: px(canvas.width),\n height: px(canvas.height),\n })\n\n const maxRatio = Math.min(devicePixelRatio, settings.maxPixelRatio)\n const budget = Math.sqrt(15e6 / Math.max(1, canvas.width * canvas.height))\n this.renderer.setPixelRatio(Math.min(maxRatio, budget))\n this.renderer.setSize(Math.max(1, Math.round(canvas.width)), Math.max(1, Math.round(canvas.height)), false)\n\n // A frustum centred on the eye that covers the whole view, then cropped to the canvas.\n const { eye, view, distance } = framing\n const halfWidth = Math.max(eye.x - view.x, view.x + view.width - eye.x)\n const halfHeight = Math.max(eye.y - view.y, view.y + view.height - eye.y)\n const scale = placed.scale\n camera.fov = (2 * Math.atan(halfHeight / distance) * 180) / Math.PI\n camera.aspect = halfWidth / halfHeight\n // where the frustum's top-left corner (eye.x - halfWidth, eye.y + halfHeight) lands, in px\n const frustumLeft = placed.view.x + (eye.x - halfWidth - view.x) * scale\n const frustumTop = placed.view.y + (view.y + view.height - (eye.y + halfHeight)) * scale\n camera.setViewOffset(\n 2 * halfWidth * scale,\n 2 * halfHeight * scale,\n canvas.x - frustumLeft,\n canvas.y - frustumTop,\n canvas.width,\n canvas.height,\n )\n camera.updateProjectionMatrix()\n }\n\n // --- pages ---------------------------------------------------------------------\n\n // what the pages show; their alt and text don't count\n private pagesKey(options: LeafThroughOptions) {\n return options.pages.map((p) => sourceKey(pageImage(p))).join('\\n')\n }\n\n private preloadRadius() {\n const preload = this.options.preload ?? DEFAULT_PRELOAD\n return Number.isNaN(preload) ? DEFAULT_PRELOAD : Math.max(0, preload)\n }\n\n private loadPages(renderer: THREE.WebGLRenderer, around: number, layout: Layout) {\n this.loader?.dispose()\n const loader = new PageLoader(this.options.pages.map(pageImage), renderer, this.preloadRadius(), {\n onLoad: (page, texture) => {\n if (this.loader !== loader) return\n this.view?.engine.setPageTexture(page, texture)\n this.needsRender = true\n this.pageLoaded(page)\n },\n onUnload: (page) => {\n if (this.loader !== loader) return\n this.view?.engine.setPageTexture(page, this.blank)\n this.needsRender = true\n },\n onError: (page, error) => this.fail(error, page),\n })\n this.loader = loader\n this.inView = ''\n this.resetReady()\n // the pages around the start, so the first turn either way has its next page ready\n const first = [around - 1, around, around + 1, around + 2]\n const visible = this.pagesAt(this.pageToPosition(around, layout), layout)\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.needsRender = true\n })\n }\n\n // loads around what's on screen, pages mid-turn included, the page being read first\n private inView = ''\n private followReader(engine: Engine, layout: Layout) {\n if (!this.loader) return\n const { from, to } = engine.pagesInView()\n const around = this.pagesAt(engine.position, layout)[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 resetReady() {\n this.ready = false\n this.element.dataset.state = 'loading'\n }\n\n private pageLoaded(page: number) {\n this.options.onPageLoad?.(page)\n this.dispatchEvent(new CustomEvent('pageload', { detail: { page } }))\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(new CustomEvent('error', { detail: { error, page } }))\n }\n\n // --- pages ↔ engine positions -------------------------------------------------------\n\n private pageToPosition(page: number, layout: Layout) {\n const count = this.options.pages.length\n // NaN (say, from a start-page attribute that isn't a number) means the first page\n const p = THREE.MathUtils.clamp(Math.round(page) || 0, 0, Math.max(count - 1, 0))\n return layout === 'spread' ? Math.ceil(p / 2) : p\n }\n\n private positionToPage(position: number, layout: Layout) {\n return layout === 'spread' ? Math.max(0, 2 * position - 1) : position\n }\n\n // the pages showing at an engine position\n private pagesAt(position: number, layout: Layout) {\n const candidates = layout === 'spread' ? [2 * position - 1, 2 * position] : [position]\n return candidates.filter((p) => p >= 0 && p < this.options.pages.length)\n }\n\n private detail(): PageChangeDetail {\n const count = this.options.pages.length\n const layout = this.view?.settings.layout ?? this.pickLayout()\n const position = this.view?.engine.position ?? 0\n const visiblePages = this.pagesAt(position, layout)\n const page = visiblePages.length ? visiblePages[0] : Math.max(0, Math.min(this.positionToPage(position, layout), count - 1))\n return { page, visiblePages, pageCount: count, layout }\n }\n\n private lastDetailKey = ''\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 this.live.textContent = (this.options.formatPageLabel ?? defaultPageLabel)(detail)\n if (first) return\n this.options.onPageChange?.(detail)\n this.dispatchEvent(new CustomEvent('pagechange', { detail }))\n }\n\n // the visible pages' content, for screen readers and the plain view\n private syncContent(view: NonNullable<typeof this.view>) {\n const { visiblePages, pageCount, layout } = this.detail()\n const format = this.options.formatPageLabel ?? defaultPageLabel\n this.content.show({\n pages: this.options.pages,\n visiblePages,\n layout,\n plain: this.plain,\n label: (page) => format({ page, visiblePages: [page], pageCount, layout }),\n paper: `#${colorKey(view.settings.paper.backColor)}`,\n })\n }\n\n private applyLabel() {\n this.element.setAttribute('aria-label', this.options.label ?? 'Magazine')\n this.lastDetailKey = ''\n }\n\n // --- input and loop -----------------------------------------------------------------\n\n private bindKeyboard() {\n const handler = (e: KeyboardEvent) => {\n const mode = this.view?.settings.interaction.keyboard\n if (!mode || e.defaultPrevented || e.altKey || e.ctrlKey || e.metaKey) return\n if (mode === 'global') {\n const target = e.target as HTMLElement | null\n if (target?.closest?.('input, textarea, select, [contenteditable=\"\"], [contenteditable=\"true\"]')) return\n } else if (e.currentTarget !== this.element) return\n const last = this.pageCount - 1\n const actions: Record<string, () => unknown> = {\n ArrowRight: () => this.next(),\n PageDown: () => this.next(),\n ArrowLeft: () => this.prev(),\n PageUp: () => this.prev(),\n Home: () => this.goTo(0),\n End: () => this.goTo(last),\n }\n const action = actions[e.key]\n if (!action) return\n e.preventDefault()\n action()\n }\n this.element.addEventListener('keydown', handler, { signal: this.abort.signal })\n window.addEventListener(\n 'keydown',\n (e) => {\n if (this.view?.settings.interaction.keyboard === 'global' && !this.element.contains(e.target as Node)) handler(e)\n },\n { signal: this.abort.signal },\n )\n }\n\n private stopRiffle() {\n this.riffle = null\n }\n\n private stepRiffle(now: number, engine: Engine) {\n const riffle = this.riffle\n if (!riffle || now - riffle.last < riffle.interval) return\n if (engine.position === riffle.target) {\n this.riffle = null\n return\n }\n riffle.last = now\n if (!engine.flip(engine.position < riffle.target)) this.riffle = null\n }\n\n // only animate while the magazine is on screen and the tab is visible\n private syncLoop() {\n const run = this.visible && !document.hidden && !this.disposed\n if (run === this.running) return\n this.running = run\n this.setLoop(run ? (now) => this.frame(now) : null)\n if (run) this.needsRender = true\n }\n\n // the renderer's animation loop, or plain requestAnimationFrame without one\n private setLoop(callback: ((now: number) => void) | null) {\n if (this.renderer) return this.renderer.setAnimationLoop(callback)\n cancelAnimationFrame(this.frameRequest)\n if (!callback) return\n const tick = (now: number) => {\n this.frameRequest = requestAnimationFrame(tick)\n callback(now)\n }\n this.frameRequest = requestAnimationFrame(tick)\n }\n\n // The hit area covers the pages; a closed magazine only takes input on its cover side.\n // This is the only place that positions it.\n private placeHitArea() {\n const box = this.hitBox\n const side = this.view?.engine.activeSide ?? 'both'\n if (!box) return\n this.hitSide = side\n const half = side === 'both' ? 1 : 0.5\n const px = (n: number) => `${Math.round(n * 100) / 100}px`\n Object.assign(this.hitArea.style, {\n left: px(side === 'right' ? box.x + box.width / 2 : box.x),\n top: px(box.y),\n width: px(box.width * half),\n height: px(box.height),\n })\n }\n\n private frame(now: number) {\n const view = this.view\n if (!view) return\n this.stepRiffle(now, view.engine)\n const moved = view.engine.update(now)\n this.followReader(view.engine, view.settings.layout)\n if (view.engine.activeSide !== this.hitSide) this.placeHitArea()\n if (moved || this.needsRender) {\n if (this.renderer && view.scene && view.camera) {\n this.renderer.shadowMap.needsUpdate = true\n this.renderer.render(view.scene, view.camera)\n }\n this.needsRender = false\n if (this.ready && this.element.dataset.state !== 'ready') {\n this.element.dataset.state = 'ready'\n this.canvas.style.opacity = '1'\n this.options.onReady?.()\n this.dispatchEvent(new CustomEvent('ready'))\n }\n }\n this.emitPageChange()\n this.syncContent(view)\n }\n}\n\n// --- helpers ---------------------------------------------------------------------\n\nfunction colorKey(color: THREE.ColorRepresentation) {\n return new THREE.Color(color).getHexString()\n}\n\n// null when the browser can't run WebGL\nfunction createRenderer(canvas: HTMLCanvasElement) {\n let renderer: THREE.WebGLRenderer\n try {\n renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true })\n } catch {\n return null\n }\n renderer.outputColorSpace = THREE.SRGBColorSpace\n renderer.shadowMap.enabled = true\n renderer.shadowMap.type = THREE.PCFShadowMap\n // shadows only re-render on frames where something moved\n renderer.shadowMap.autoUpdate = false\n return renderer\n}\n\n// Copies the plain objects and arrays, keeping everything else (textures, elements) as is.\n// Comparing against a copy sees changes made in place, such as Vue's reactive objects.\nfunction snapshot<T>(value: T): T {\n if (typeof value !== 'object' || value === null) return value\n if (Array.isArray(value)) return value.map(snapshot) as T\n if (Object.getPrototypeOf(value) !== Object.prototype) return value\n return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, snapshot(item)])) as T\n}\n\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false\n if (Array.isArray(a) !== Array.isArray(b)) return false\n const protoA = Object.getPrototypeOf(a)\n if (protoA !== Object.prototype && protoA !== Array.prototype) return false\n if (protoA !== Object.getPrototypeOf(b)) return false\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n if (keysA.length !== keysB.length) return false\n return keysA.every((key) => deepEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key]))\n}\n","import * as THREE from 'three'\nimport type { Rect } from './framing.js'\nimport { isPageDescriptor, pageImage, type Layout, type PageDescriptor, type PageImage, type PageSource } from './options.js'\n\n// plain view: page images kept after they leave the screen, so turning back doesn't load them again\nconst KEPT_IMAGES = 8\n\nexport const VISUALLY_HIDDEN =\n 'position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap'\n\nexport interface ContentState {\n pages: PageSource[]\n visiblePages: number[]\n layout: Layout\n /** Shows the page images (the plain view) instead of only describing them. */\n plain: boolean\n /** Accessible name of one page. */\n label: (page: number) => string\n /** Paper colour behind transparent page images, as a CSS colour. */\n paper: string\n}\n\nexport interface ContentEvents {\n /** Plain view: a page image loaded (once per image; images shown recently are reused). */\n onLoad(page: number): void\n onError(page: number, error: unknown): void\n /** Plain view: every visible page has loaded or failed. */\n onShown(): void\n}\n\n/**\n * What's on the visible pages, as DOM. In 3D it's hidden and describes the pages for screen\n * readers (the canvas itself is hidden from them). In the plain view it shows the pages too.\n */\nexport class PageContent {\n readonly element = document.createElement('div')\n private key = ''\n private shown = 0\n // plain view: recently shown page images by source and alt, oldest first\n private readonly images = new Map<string, HTMLElement>()\n\n constructor(private readonly events: ContentEvents) {\n this.element.dataset.leafthroughContent = ''\n this.element.style.cssText = VISUALLY_HIDDEN\n }\n\n /** Brings the DOM up to date; cheap when nothing changed. */\n show(state: ContentState) {\n const { pages, visiblePages, layout, plain } = state\n const key = [\n plain,\n layout,\n state.paper,\n ...visiblePages.map((i) => {\n const page = pages[i]\n const info: Partial<PageDescriptor> = isPageDescriptor(page) ? page : {}\n return [i, sourceKey(pageImage(page)), info.alt, info.text, state.label(i)].join('\\u0000')\n }),\n ].join('\\u0001')\n if (key === this.key) return\n this.key = key\n\n const token = ++this.shown\n const used = new Set<HTMLElement>()\n let pending = 0\n const settle = () => {\n if (token === this.shown && --pending === 0) this.events.onShown()\n }\n const figures = visiblePages.flatMap((i) => {\n const page = pages[i]\n const { alt, text } = isPageDescriptor(page) ? page : { alt: undefined, text: undefined }\n // in 3D, a page with nothing to say needs nothing here; the live region gives its number\n if (!plain && !alt && !text) return []\n const figure = document.createElement('figure')\n figure.dataset.page = String(i)\n figure.setAttribute('aria-label', state.label(i))\n figure.style.cssText = 'margin:0'\n if (plain) {\n // a spread's cover sits alone on the right, then pages pair up as left 1, right 2, …\n const spread = layout === 'spread'\n const x = spread && i % 2 === 0 ? '50%' : '0'\n figure.style.cssText =\n `margin:0;position:absolute;top:0;left:${x};width:${spread ? '50%' : '100%'};height:100%;` +\n `background:${state.paper};box-shadow:0 1px 6px rgba(0,0,0,.18)`\n const image = this.imageFor(i, pageImage(page), alt, used)\n if (image instanceof HTMLImageElement && !image.complete) {\n pending++\n image.addEventListener('load', settle, { once: true })\n image.addEventListener('error', settle, { once: true })\n }\n figure.append(image)\n } else if (alt) {\n const image = document.createElement('span')\n image.setAttribute('role', 'img')\n image.setAttribute('aria-label', alt)\n figure.append(image)\n }\n if (text) figure.append(paragraphs(text, plain))\n return [figure]\n })\n this.element.replaceChildren(...figures)\n if (!pending) queueMicrotask(() => token === this.shown && this.events.onShown())\n }\n\n /** Rebuilds the DOM on the next show(), loading the images again. */\n refresh() {\n this.key = ''\n this.images.clear()\n }\n\n // a page image for the plain view, reused if it was shown recently (and isn't already in\n // `used`, as when two visible pages share an image)\n private imageFor(page: number, source: PageImage, alt = '', used: Set<HTMLElement>) {\n const key = `${sourceKey(source)}\\u0000${alt}`\n let image = this.images.get(key)\n if (image && !used.has(image)) this.images.delete(key)\n else {\n const created = (image = displayImage(source, alt))\n if (created instanceof HTMLImageElement && !created.complete) {\n created.addEventListener('load', () => this.events.onLoad(page), { once: true })\n created.addEventListener('error', () => this.events.onError(page, new Error(`Failed to load page image: ${created.src}`)), {\n once: true,\n })\n } else queueMicrotask(() => this.events.onLoad(page))\n }\n used.add(image)\n this.images.set(key, image)\n if (this.images.size > KEPT_IMAGES) this.images.delete(this.images.keys().next().value!)\n return image\n }\n\n /** Plain view: where the pages go, in px relative to the root. `null` hides the content again. */\n place(box: Rect | null) {\n if (!box) {\n this.element.style.cssText = VISUALLY_HIDDEN\n return\n }\n const px = (n: number) => `${Math.round(n * 100) / 100}px`\n this.element.style.cssText = `position:absolute;left:${px(box.x)};top:${px(box.y)};width:${px(box.width)};height:${px(box.height)};pointer-events:none`\n }\n}\n\nconst ids = new WeakMap<object, number>()\nlet nextId = 1\n/** Identifies a page image: URLs by value, everything else by identity. */\nexport function sourceKey(source: PageImage) {\n if (typeof source === 'string') return source\n let id = ids.get(source)\n if (!id) ids.set(source, (id = nextId++))\n return `#${id}`\n}\n\n// the page's text, read by screen readers; hidden in the plain view, where the image shows it\nfunction paragraphs(text: string, hidden: boolean) {\n const block = document.createElement('div')\n if (hidden) block.style.cssText = VISUALLY_HIDDEN\n for (const paragraph of text.split(/\\n\\s*\\n/)) {\n const p = document.createElement('p')\n p.textContent = paragraph.trim()\n block.append(p)\n }\n return block\n}\n\n// an element showing the page image as it is now\nfunction displayImage(source: PageImage, alt = ''): HTMLElement {\n const url = typeof source === 'string' ? source : source instanceof HTMLImageElement ? source.currentSrc || source.src : null\n const drawable = source instanceof THREE.Texture ? source.image : source\n const style = 'display:block;width:100%;height:100%'\n if (url === null && drawable instanceof HTMLImageElement) return displayImage(drawable, alt)\n if (url !== null) {\n const img = document.createElement('img')\n img.decoding = 'async'\n img.alt = alt\n img.draggable = false\n img.style.cssText = style\n img.src = url\n return img\n }\n if (typeof HTMLCanvasElement !== 'undefined' && (drawable instanceof HTMLCanvasElement || drawable instanceof ImageBitmap)) {\n // a copy, so the original stays where it is\n const canvas = document.createElement('canvas')\n canvas.width = drawable.width\n canvas.height = drawable.height\n canvas.getContext('2d')?.drawImage(drawable, 0, 0)\n return describe(canvas, alt, style)\n }\n // nothing drawable (a data texture, say): plain paper\n return describe(document.createElement('div'), alt, style)\n}\n\n// labels an element the way alt labels an image: without alt, it's decoration\nfunction describe(element: HTMLElement, alt: string, style: string) {\n element.style.cssText = style\n if (alt) {\n element.setAttribute('role', 'img')\n element.setAttribute('aria-label', alt)\n } else element.setAttribute('aria-hidden', 'true')\n return element\n}\n","import type { Layout } from './options.js'\n\nexport interface Rect {\n x: number\n y: number\n width: number\n height: number\n}\n\n/** What the camera looks at, in world units (a page is 1 wide, the spine is at x = 0). */\nexport interface Framing {\n /** Fits the element when bleeding: the pages, plus any room the layout always needs. Takes input. */\n box: Rect\n /** Everything a turning page can reach. The canvas covers this (clipped to the document). */\n view: Rect\n /** Camera position over the floor; the view needn't be centred on it. */\n eye: { x: number; y: number }\n distance: number\n /** World width that a drag of `dragDistance` × this turns a page all the way over. */\n dragWidth: number\n /** Directional shadow frustum, already scaled to the page aspect. */\n shadow: { left: number; right: number; top: number; bottom: number }\n}\n\nconst REFERENCE_ASPECT = 1.377\n// the open spread's canvas reaches this far past the pages on every side\nconst SPREAD_OVERSCAN = 0.27975\n\nexport function framingFor(layout: Layout, aspect: number): Framing {\n const k = aspect / REFERENCE_ASPECT\n if (layout === 'spread') {\n const box = { x: -1, y: -aspect / 2, width: 2, height: aspect }\n const view = {\n x: box.x - SPREAD_OVERSCAN * box.width,\n y: box.y - SPREAD_OVERSCAN * box.height,\n width: box.width * (1 + 2 * SPREAD_OVERSCAN),\n height: box.height * (1 + 2 * SPREAD_OVERSCAN),\n }\n const distance = view.height / 2 / Math.tan((20 * Math.PI) / 180) // 40° fov\n return {\n box,\n view,\n eye: { x: 0, y: 0 },\n distance,\n dragWidth: view.width,\n shadow: { left: -0.8, right: 1, top: 0.85 * k, bottom: -1.05 * k },\n }\n }\n\n // One page, with a strip left of the spine for turning back and room above and below.\n // Mid-turn a page rolls round the spine as far as x ≈ -1.06 (measured with the fold\n // fully tilted), so the view reaches past that.\n const box = { x: -0.34, y: -1.2 * k, width: 1.56, height: 2.4 * k }\n const view = { x: -1.15, y: box.y, width: box.x + box.width + 1.15, height: box.height }\n return {\n box,\n view,\n eye: { x: box.x + box.width / 2, y: 0 },\n distance: 3.1 * k,\n dragWidth: box.width,\n shadow: { left: -1.1, right: 1.1, top: 0.8 * k, bottom: -0.8 * k },\n }\n}\n\n/** The plain view: just the pages, centred, with nothing around them. */\nexport function plainFramingFor(layout: Layout, aspect: number): Framing {\n const box = layout === 'spread' ? { x: -1, y: -aspect / 2, width: 2, height: aspect } : { x: 0, y: -aspect / 2, width: 1, height: aspect }\n return {\n box,\n view: box,\n eye: { x: box.x + box.width / 2, y: 0 },\n distance: 1,\n dragWidth: box.width,\n shadow: { left: 0, right: 0, top: 0, bottom: 0 },\n }\n}\n\nexport interface Fit {\n /** Pixels per world unit. */\n scale: number\n /** Where the whole view lands, in px relative to the root's top-left corner. */\n view: Rect\n /** Where the box lands, in px relative to the root. */\n box: Rect\n /** Height the root needs when its container has no height of its own. */\n height: number\n}\n\n/**\n * Fits the framing into an element. With `bleed`, the box fills the element and the rest\n * of the view spills past it; without, the whole view stays inside. An element with no\n * height (`height` 0) takes its height from its width.\n */\nexport function fit(framing: Framing, width: number, height: number, bleed: boolean): Fit {\n const fitted = bleed ? framing.box : framing.view\n const scale = height > 0 ? Math.min(width / fitted.width, height / fitted.height) : width / fitted.width\n const boxHeight = height > 0 ? height : fitted.height * scale\n\n // centre the fitted rect, then place everything else around it (world y points up)\n const left = (width - fitted.width * scale) / 2 - fitted.x * scale\n const top = (boxHeight - fitted.height * scale) / 2 + (fitted.y + fitted.height) * scale\n const toPx = (r: Rect): Rect => ({\n x: left + r.x * scale,\n y: top - (r.y + r.height) * scale,\n width: r.width * scale,\n height: r.height * scale,\n })\n return { scale, view: toPx(framing.view), box: toPx(framing.box), height: boxHeight }\n}\n","import * as THREE from 'three'\nimport type { ResolvedSettings } from '../options.js'\n\nexport type SheetUniforms = Record<string, THREE.IUniform>\n\n/** Uniforms every sheet in a scene shares. */\nexport interface SheetShading {\n /** Direction across of the turning sheet's root (x, z) and whether one is turning. */\n spineContact: THREE.IUniform<THREE.Vector3>\n /** Towards the sun, in the magazine's frame. */\n lightDirection: THREE.IUniform<THREE.Vector3>\n}\n\nexport interface SheetShader {\n /** GLSL defining `vec3 sheetPosition(vec2 uv, float noise)` and its uniforms. */\n vertexChunk: string\n /** GLSL defining `bool sheetHidden(vec3 p)` and its uniforms: true where not to draw. */\n fragmentChunk?: string\n /** Finite difference step for normals, in page widths. */\n epsilon: number\n /** Read per-vertex noise from the `aNoise` attribute (declared by the chunk). */\n noise?: boolean\n /** Nudges the shadow caster sideways. */\n shadowOffsetX: number\n}\n\nconst glslFloat = (n: number) => (Number.isInteger(n) ? `${n}.0` : `${n}`)\n\n// Both layouts draw sheets the same way: a double-sided MeshStandardMaterial whose vertex\n// shader moves each vertex with the layout's sheetPosition(), normals from finite\n// differences, and the same printed-paper look in the fragment shader.\nfunction createPaperMaterials(uniforms: SheetUniforms, shader: SheetShader, settings: ResolvedSettings) {\n const { paper, pageAspect } = settings\n // in the working (linear) colour space, like the sampled page images\n const blank = new THREE.Color(paper.backColor)\n const constants = /* glsl */ `\n const float SHEET_ASPECT = ${glslFloat(pageAspect)};\n const float NORMAL_EPSILON = ${glslFloat(shader.epsilon)};\n const float PATTERN_SIZE = ${glslFloat(paper.grainScale)};\n const float PATTERN_DARKEN = ${glslFloat(paper.grain === false ? 0 : paper.grainStrength)};\n const float PATTERN_ROUGHNESS = 0.33;\n const float SHOW_THROUGH = ${glslFloat(paper.showThrough)};\n const float INK_GLOSS = ${glslFloat(paper.inkGloss)};\n const float SHADOW_OFFSET_X = ${glslFloat(shader.shadowOffsetX)};\n const float CONTACT_REACH = 0.04;\n const vec3 BLANK_PAPER = vec3(${glslFloat(blank.r)}, ${glslFloat(blank.g)}, ${glslFloat(blank.b)});\n `\n const noiseAt = (component: string) => (shader.noise ? `aNoise.${component}` : '0.0')\n\n const material = new THREE.MeshStandardMaterial({\n side: THREE.DoubleSide,\n metalness: paper.metalness,\n roughness: paper.roughness,\n })\n\n material.onBeforeCompile = (program) => {\n Object.assign(program.uniforms, uniforms)\n\n program.vertexShader =\n constants + shader.vertexChunk + 'varying vec2 vSheetUv;\\nvarying vec3 vSheetPosition;\\n' + program.vertexShader\n program.vertexShader = program.vertexShader\n .replace(\n '#include <beginnormal_vertex>',\n `#include <beginnormal_vertex>\n vec2 sheetStep = vec2(NORMAL_EPSILON, NORMAL_EPSILON / SHEET_ASPECT);\n vec3 sheetP = sheetPosition(uv, ${noiseAt('x')});\n vec3 sheetPx = sheetPosition(uv + vec2(sheetStep.x, 0.0), ${noiseAt('y')});\n vec3 sheetPy = sheetPosition(uv + vec2(0.0, sheetStep.y), ${noiseAt('z')});\n objectNormal = normalize(cross(sheetPx - sheetP, sheetPy - sheetP));`,\n )\n .replace(\n '#include <begin_vertex>',\n `vec3 transformed = sheetP;\n vSheetUv = uv;\n vSheetPosition = sheetP;`,\n )\n\n program.fragmentShader =\n constants +\n `uniform sampler2D uFront;\n uniform sampler2D uBack;\n uniform sampler2D uPattern;\n uniform vec3 uSpineContact;\n uniform vec3 uLightDirection;\n varying vec2 vSheetUv;\n varying vec3 vSheetPosition;\n ${shader.fragmentChunk ?? ''}\n\n // Right where a turning sheet leaves the spine it all but touches the pages under it, and\n // the shadow map's depth bias can't tell them apart: a thin strip next to the spine stays\n // lit, which reads as a light seam. There the sheet is a plane through the spine\n // (uSpineContact.xy is its direction across, z says whether a sheet is turning), so test\n // the way to the light against that plane directly.\n float sheetContactShadow(vec3 p, float intensity) {\n if (uSpineContact.z < 0.5) return 1.0;\n vec3 across = vec3(uSpineContact.x, 0.0, uSpineContact.y);\n vec3 normal = vec3(-uSpineContact.y, 0.0, uSpineContact.x);\n float facing = dot(normal, uLightDirection);\n if (abs(facing) < 1e-4) return 1.0;\n float t = -dot(normal, p) / facing;\n if (t <= 1e-4) return 1.0;\n vec3 hit = p + t * uLightDirection;\n float along = dot(hit, across);\n if (along <= 0.0 || along > CONTACT_REACH || abs(hit.y) > SHEET_ASPECT * 0.5) return 1.0;\n return 1.0 - intensity;\n }\n ` +\n program.fragmentShader\n program.fragmentShader = program.fragmentShader\n .replace(\n '#include <clipping_planes_fragment>',\n `#include <clipping_planes_fragment>\n ${shader.fragmentChunk ? 'if (sheetHidden(vSheetPosition)) discard;' : ''}`,\n )\n // light whichever side faces the camera\n .replace(\n '#include <normal_fragment_begin>',\n `vec3 normal = normalize(vNormal);\n float faceDirection = normal.z >= 0.0 ? 1.0 : -1.0;\n normal *= faceDirection;\n vec3 nonPerturbedNormal = normal;`,\n )\n .replace(\n '#include <map_fragment>',\n `vec4 frontColor = texture2D(uFront, vSheetUv);\n vec4 backColor = texture2D(uBack, vec2(1.0 - vSheetUv.x, vSheetUv.y));\n // Transparent artwork is unprinted paper, not a hole in the sheet. Page images are\n // premultiplied, so this is \"over\" compositing onto the paper colour.\n frontColor = vec4(frontColor.rgb + BLANK_PAPER * (1.0 - frontColor.a), 1.0);\n backColor = vec4(backColor.rgb + BLANK_PAPER * (1.0 - backColor.a), 1.0);\n vec4 faceColor = gl_FrontFacing ? frontColor : backColor;\n vec4 otherColor = gl_FrontFacing ? backColor : frontColor;\n // thin paper lets some of the other side through\n vec4 sheetColor = faceColor;\n sheetColor.rgb *= mix(vec3(1.0), otherColor.rgb, SHOW_THROUGH);\n diffuseColor *= sheetColor;\n float inkAmount = 1.0 - dot(faceColor.rgb, vec3(0.299, 0.587, 0.114));`,\n )\n // directional shadows: whichever is darker of the shadow map and the contact test\n .replace('#include <lights_fragment_begin>', withContactShadow(THREE.ShaderChunk.lights_fragment_begin))\n .replace(\n '#include <roughnessmap_fragment>',\n `#include <roughnessmap_fragment>\n // ink sits glossier than bare paper; fibres are rougher\n roughnessFactor *= 1.0 - inkAmount * INK_GLOSS;\n float pattern = texture2D(uPattern, vSheetUv * PATTERN_SIZE * vec2(1.0, SHEET_ASPECT)).r;\n pattern = 2.0 * pow(pattern, 7.0);\n roughnessFactor = clamp(mix(roughnessFactor, 1.0, clamp(pattern * PATTERN_ROUGHNESS, 0.0, 1.0)), 0.0, 1.0);`,\n )\n .replace(\n '#include <opaque_fragment>',\n `#include <opaque_fragment>\n gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.0), pattern * PATTERN_DARKEN);`,\n )\n }\n\n const depthMaterial = new THREE.MeshDepthMaterial({\n depthPacking: THREE.RGBADepthPacking,\n side: THREE.DoubleSide,\n })\n depthMaterial.onBeforeCompile = (program) => {\n Object.assign(program.uniforms, uniforms)\n program.vertexShader = constants + shader.vertexChunk + program.vertexShader\n program.vertexShader = program.vertexShader.replace(\n '#include <begin_vertex>',\n `vec3 transformed = sheetPosition(uv, ${noiseAt('x')}) - vec3(SHADOW_OFFSET_X, 0.0, 0.0);`,\n )\n }\n\n return { material, depthMaterial }\n}\n\nconst DIRECTIONAL_SHADOW =\n 'getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowIntensity, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] )'\n\n// If a future three.js words this differently, sheets just go without the contact shadow.\nfunction withContactShadow(chunk: string) {\n if (!chunk.includes(DIRECTIONAL_SHADOW)) return chunk\n return chunk.replace(\n DIRECTIONAL_SHADOW,\n `min( ${DIRECTIONAL_SHADOW}, sheetContactShadow( vSheetPosition, directionalLightShadow.shadowIntensity ) )`,\n )\n}\n\nexport type SheetMesh = THREE.Mesh<THREE.BufferGeometry, THREE.MeshStandardMaterial>\n\nexport function createSheetMesh(\n geometry: THREE.BufferGeometry,\n uniforms: SheetUniforms,\n shader: SheetShader,\n settings: ResolvedSettings,\n): SheetMesh {\n const { material, depthMaterial } = createPaperMaterials(uniforms, shader, settings)\n const mesh = new THREE.Mesh(geometry, material)\n mesh.customDepthMaterial = depthMaterial\n mesh.castShadow = settings.lighting.shadows\n mesh.receiveShadow = settings.lighting.shadows\n mesh.frustumCulled = false\n return mesh\n}\n\n/**\n * Every sheet rises from the spine at height 0, so along the spine they all but touch and\n * the depth buffer can't separate them: sheets further down flicker through as a light seam.\n * Bias each sheet's depth by its place in the stack instead: the top sheet wins, and a sheet\n * that's turning wins over everything.\n */\nexport function layerSheet(mesh: SheetMesh, depth: number, count: number, moving: boolean) {\n const material = mesh.material\n material.polygonOffset = true\n material.polygonOffsetFactor = 0\n material.polygonOffsetUnits = moving ? -(count + 4) : -(count - Math.min(depth, count))\n mesh.renderOrder = depth\n}\n\nexport function disposeSheetMesh(mesh: SheetMesh) {\n mesh.material.dispose()\n mesh.customDepthMaterial?.dispose()\n}\n\n/** A 1×1 texture of one colour, for blank pages and placeholders. */\nexport function solidTexture(color: THREE.ColorRepresentation): THREE.DataTexture {\n const c = new THREE.Color(color)\n const srgb = c.clone().convertLinearToSRGB()\n const texture = new THREE.DataTexture(\n new Uint8Array([Math.round(srgb.r * 255), Math.round(srgb.g * 255), Math.round(srgb.b * 255), 255]),\n 1,\n 1,\n )\n texture.colorSpace = THREE.SRGBColorSpace\n texture.needsUpdate = true\n return texture\n}\n","import * as THREE from 'three'\n\n// A tiling paper grain, modelled on a scan of fine paper: nearly all of it is pixel-sized\n// speckle, each speck a little like its neighbours and a little unlike the ones two along,\n// with only a trace of cloudiness. The sheet shader only cares about the brightest bits\n// (pow 7), which come out as faint darker flecks and rougher spots.\nconst SIZE = 384\n// filters white noise into the speckle; one quadrant, [y][x], mirrored for the others\nconst KERNEL = [\n [1, 0.108, -0.049, 0.022],\n [0.131, 0.016, -0.014, 0.008],\n [-0.039, -0.018, 0, 0.009],\n [0.021, 0.005, 0.004, 0.009],\n]\n// the cloudiness, as a share of the speckle's spread\nconst CLOUD = 0.07\nconst CLOUD_PERIOD = 32\n// how bright the grain is: [share of pixels darker, level]\nconst LEVELS = [\n [0, 0.537],\n [0.001, 0.624],\n [0.01, 0.675],\n [0.05, 0.714],\n [0.1, 0.733],\n [0.25, 0.761],\n [0.5, 0.788],\n [0.75, 0.816],\n [0.9, 0.835],\n [0.95, 0.847],\n [0.99, 0.867],\n [0.999, 0.89],\n [1, 0.914],\n]\n\nfunction periodicNoise(period: number, random: () => number) {\n const grid = new Float32Array(period * period)\n for (let i = 0; i < grid.length; i++) grid[i] = random()\n const at = (x: number, y: number) => grid[((y % period) + period) % period * period + (((x % period) + period) % period)]\n return (x: number, y: number) => {\n const xi = Math.floor(x)\n const yi = Math.floor(y)\n const fx = x - xi\n const fy = y - yi\n const sx = fx * fx * (3 - 2 * fx)\n const sy = fy * fy * (3 - 2 * fy)\n const a = at(xi, yi) + (at(xi + 1, yi) - at(xi, yi)) * sx\n const b = at(xi, yi + 1) + (at(xi + 1, yi + 1) - at(xi, yi + 1)) * sx\n return a + (b - a) * sy\n }\n}\n\nfunction spread(values: Float32Array) {\n let mean = 0\n for (const v of values) mean += v\n mean /= values.length\n let variance = 0\n for (const v of values) variance += (v - mean) ** 2\n return Math.sqrt(variance / values.length) || 1\n}\n\nexport function createPaperPattern(renderer: THREE.WebGLRenderer): THREE.CanvasTexture {\n let s = 7\n const random = () => ((s = (s * 1664525 + 1013904223) >>> 0) / 4294967296)\n\n const noise = new Float32Array(SIZE * SIZE)\n for (let i = 0; i < noise.length; i++) {\n noise[i] = Math.sqrt(-2 * Math.log(1 - random())) * Math.cos(2 * Math.PI * random())\n }\n const at = (x: number, y: number) => noise[((y + SIZE) % SIZE) * SIZE + ((x + SIZE) % SIZE)]\n\n // speckle, wrapped at the edges so the tile stays seamless\n const reach = KERNEL.length - 1\n const grain = new Float32Array(SIZE * SIZE)\n for (let y = 0; y < SIZE; y++) {\n for (let x = 0; x < SIZE; x++) {\n let v = 0\n for (let dy = -reach; dy <= reach; dy++) {\n const row = KERNEL[Math.abs(dy)]\n for (let dx = -reach; dx <= reach; dx++) v += row[Math.abs(dx)] * at(x + dx, y + dy)\n }\n grain[y * SIZE + x] = v\n }\n }\n\n const cloudNoise = periodicNoise(CLOUD_PERIOD, random)\n const cloud = new Float32Array(SIZE * SIZE)\n for (let y = 0; y < SIZE; y++) {\n for (let x = 0; x < SIZE; x++) cloud[y * SIZE + x] = cloudNoise((x / SIZE) * CLOUD_PERIOD, (y / SIZE) * CLOUD_PERIOD)\n }\n const cloudScale = (CLOUD * spread(grain)) / spread(cloud)\n for (let i = 0; i < grain.length; i++) grain[i] += cloud[i] * cloudScale\n\n // keep the pattern, take the brightness from LEVELS\n const order = Array.from(grain.keys()).sort((a, b) => grain[a] - grain[b])\n const canvas = document.createElement('canvas')\n canvas.width = canvas.height = SIZE\n const ctx = canvas.getContext('2d')!\n const image = ctx.createImageData(SIZE, SIZE)\n let level = 1\n order.forEach((pixel, rank) => {\n const share = rank / (order.length - 1)\n while (LEVELS[level][0] < share) level++\n const [fromShare, from] = LEVELS[level - 1]\n const [toShare, to] = LEVELS[level]\n const g = Math.round(255 * (from + ((to - from) * (share - fromShare)) / (toShare - fromShare)))\n const o = pixel * 4\n image.data[o] = image.data[o + 1] = image.data[o + 2] = g\n image.data[o + 3] = 255\n })\n ctx.putImageData(image, 0, 0)\n\n const texture = new THREE.CanvasTexture(canvas)\n texture.wrapS = texture.wrapT = THREE.RepeatWrapping\n texture.anisotropy = renderer.capabilities.getMaxAnisotropy()\n return texture\n}\n","import * as THREE from 'three'\nimport type { Engine } from '../engine.js'\nimport type { ResolvedSettings } from '../options.js'\n\n// a horizontal swipe longer than this turns the page; anything shorter is a click\nconst SWIPE_PX = 30\n\nexport interface PlainEngineContext {\n /** Receives pointer input; covers the pages. */\n hitArea: HTMLElement\n settings: ResolvedSettings\n pageCount: number\n /** Where the magazine opens, in the engine's own position units. */\n position: number\n}\n\n/**\n * The plain view's engine: nothing moves, the pages just change. Positions mean what they\n * do in the 3D engines (spread: sheets turned to the left; single: pages turned). The\n * controller shows the pages themselves.\n */\nexport class PlainEngine implements Engine {\n readonly group = new THREE.Group()\n readonly maxPosition: number\n private readonly spread: boolean\n private readonly settings: ResolvedSettings\n private readonly hitArea: HTMLElement\n private readonly abort = new AbortController()\n private current: number\n private dirty = true\n // the pointer that started the gesture; other pointers are ignored until it lifts\n private press: { id: number; x: number; y: number } | null = null\n\n constructor(ctx: PlainEngineContext) {\n this.settings = ctx.settings\n this.hitArea = ctx.hitArea\n this.spread = ctx.settings.layout === 'spread'\n this.maxPosition = this.spread ? Math.ceil(ctx.pageCount / 2) : ctx.pageCount\n this.current = this.clamp(ctx.position)\n this.bindInput()\n }\n\n get position() {\n return this.current\n }\n\n get turning() {\n return false\n }\n\n pagesInView() {\n return this.spread ? { from: Math.max(2 * this.current - 1, 0), to: 2 * this.current } : { from: this.current, to: this.current }\n }\n\n // closed, only the cover side can be clicked\n get activeSide() {\n if (!this.spread) return 'both'\n if (this.current === 0) return 'right'\n if (this.current === this.maxPosition) return 'left'\n return 'both'\n }\n\n update() {\n const moved = this.dirty\n this.dirty = false\n return moved\n }\n\n flip(forward: boolean) {\n if (!forward) {\n if (this.current <= 0) return false\n this.current--\n } else if (this.spread) {\n if (this.current >= this.maxPosition) return false\n this.current++\n } else if (this.current < this.maxPosition - 1) {\n this.current++\n } else if (this.settings.loop && this.current > 0) {\n // the last page turns back to the cover\n this.current = 0\n } else return false\n this.dirty = true\n return true\n }\n\n jumpTo(position: number) {\n this.current = this.clamp(position)\n this.dirty = true\n }\n\n setPageTexture() {}\n\n dispose() {\n this.abort.abort()\n }\n\n private clamp(position: number) {\n const last = this.spread ? this.maxPosition : this.maxPosition - 1\n return Math.min(Math.max(Math.round(position), 0), Math.max(last, 0))\n }\n\n // a click on the left half goes back, on the right half forward; a swipe goes its way\n private bindInput() {\n const el = this.hitArea\n const signal = this.abort.signal\n const { interaction } = this.settings\n el.addEventListener(\n 'pointerdown',\n (e) => {\n if (this.press && !e.isPrimary) return\n if (e.button !== 0 || !(interaction.drag || interaction.click)) return\n this.press = { id: e.pointerId, x: e.clientX, y: e.clientY }\n },\n { signal },\n )\n el.addEventListener(\n 'pointerup',\n (e) => {\n const press = this.press\n if (!press || e.pointerId !== press.id) return\n this.press = null\n const dx = e.clientX - press.x\n if (Math.abs(dx) > SWIPE_PX) {\n if (interaction.drag) this.flip(dx < 0)\n return\n }\n if (!interaction.click) return\n const rect = el.getBoundingClientRect()\n // a closed spread's hit area is just its cover, so the side decides\n const side = this.activeSide\n this.flip(side === 'both' ? e.clientX >= rect.left + rect.width / 2 : side === 'right')\n },\n { signal },\n )\n const cancel = (e: PointerEvent) => {\n if (this.press?.id === e.pointerId) this.press = null\n }\n el.addEventListener('pointercancel', cancel, { signal })\n }\n}\n","import * as THREE from 'three'\nimport type { Engine, EngineContext, EngineMetrics } from '../engine.js'\nimport type { ResolvedSettings } from '../options.js'\nimport { disposeSheetMesh, layerSheet, type SheetMesh } from '../paper/paperMaterial.js'\nimport { createSingleGeometry, createSingleSheet, type SingleSheetUniforms } from './singleSheet.js'\n\n// One page in view. Turned pages swing around the spine and roll up around a cone behind\n// the magazine; the unread pile bulges up off the spine. After the last page everything\n// settles flat again and the magazine starts over.\nconst HOLD = {\n startTime: 0.22,\n firstFlipTime: 1.4,\n fastestFlipTime: 1,\n speedUpSheets: 7,\n firstGapShare: 0.16,\n fastestGapShare: 0.1,\n moveSlopPx: 40,\n}\nconst DRAG = {\n progressSmoothTime: 0.9,\n clickSlopPx: 5,\n claimSteepness: 1.6, // a touch swipe this much flatter than vertical turns pages instead of scrolling\n claimAfterPx: 4,\n fallTime: 1.3,\n fallTimeExponent: 0.5,\n}\nconst CLICK = { flipTime: 1.2, settlePower: 0.8 }\nconst GRAB = { smoothTime: 0.05 }\nconst WOBBLE = { tiltDeg: 8, flipTimeMin: 0.95, flipTimeMax: 1.08 }\nconst ROLL = {\n spineOpenStartDeg: 100, // how far read pages swing round before they start rolling\n spineOpenEndDeg: 190,\n spineOpenPower: 4,\n coneEvenPower: 6,\n lastPageEven: 0.98, // how close to even the roll gets by the last page, before the power\n coverTopRadius: 0.04,\n backTopRadius: 0.18,\n coverBottomRadius: 0.01,\n backBottomRadius: 0.05,\n radiusSpacingClump: 7.5,\n radiusSpacingSeed: 41.5,\n fallDelayPages: 15, // read pages this far back settle flat behind everything (fewer in short magazines)\n fallDurationPages: 3,\n}\nconst SHAPE = { midLeadPower: 2, midBow: 2, midBowFlatFrom: 0.3, midBowFlatTo: 0.8, midRollScale: 2.5 }\nconst PILE = {\n liftHeight: 0.08,\n liftPeakX: 0.4,\n liftEdgeDrop: 0.5,\n liftShutShare: 0.67,\n liftDepthFalloff: 0.5,\n coneTaper: -0.6,\n renderDepth: 3,\n}\nconst BOOK = { pushBack: 0.05, pushRight: 0.02 }\nconst CATCH_UP = { clump: 1.6, seed: 41 }\n\nconst TAU = Math.PI * 2\nconst clamp = THREE.MathUtils.clamp\nconst smooth = (x: number) => {\n const t = clamp(x, 0, 1)\n return t * t * (3 - 2 * t)\n}\nconst hash = (a: number, b: number) => {\n const s = 43758.5453 * Math.sin(12.9898 * a + 78.233 * b)\n return s - Math.floor(s)\n}\n\ninterface Wobble {\n tiltDeg: number\n flipTime: number\n}\ninterface Sheet {\n index: number\n mesh: SheetMesh\n uniforms: SingleSheetUniforms\n progress: number\n grabX: number\n grabXTarget: number\n grabY: number\n grabYTarget: number\n wobble: Wobble\n}\ninterface Flight {\n sheet: Sheet\n from: number\n to: number\n start: number\n duration: number\n launch?: number\n}\ninterface Drag {\n sheet: Sheet\n forward: boolean\n base: number\n progress: number\n rectWidth: number\n speed: number\n}\ninterface Hold {\n forward: boolean\n x: number\n y: number\n count: number\n fired: boolean\n timer: ReturnType<typeof setTimeout>\n}\n\n/**\n * One page at a time: turned pages swing round the spine and roll up behind the magazine.\n */\nexport class SingleEngine implements Engine {\n readonly group = new THREE.Group() // the book, nudged back and right as you read\n readonly maxPosition: number\n\n private readonly settings: ResolvedSettings\n private readonly halfHeight: number\n private readonly liftHeight: number\n private readonly camera: THREE.PerspectiveCamera\n private readonly canvas: HTMLCanvasElement\n private readonly hitArea: HTMLElement\n private readonly metrics: EngineMetrics\n private readonly geometry: THREE.BufferGeometry\n private readonly sheets: Sheet[]\n private readonly count: number\n private readonly radiusSpacing: number[]\n private readonly catchUp: number[]\n private readonly fallDelay: number\n private readonly fallDuration: number\n private readonly abort = new AbortController()\n\n private current: number\n private turned = 0 // sum of every sheet's progress, as of the last update\n private flights: Flight[] = []\n private drag: Drag | null = null\n // the pointer that started the gesture; other pointers are ignored until it lifts\n private press: { id: number; x: number; y: number } | null = null\n private hold: Hold | null = null\n private touch: { x: number; y: number; claimed: boolean } | null = null\n private dirty = true\n private last = 0\n\n private readonly raycaster = new THREE.Raycaster()\n private readonly ndc = new THREE.Vector2()\n private readonly floor = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0)\n private readonly hit = new THREE.Vector3()\n private readonly spine = new THREE.Vector3()\n\n constructor(ctx: EngineContext) {\n this.settings = ctx.settings\n this.halfHeight = ctx.settings.pageAspect / 2\n this.liftHeight = PILE.liftHeight * ctx.settings.paper.bulge\n this.camera = ctx.camera\n this.canvas = ctx.canvas\n this.hitArea = ctx.hitArea\n this.metrics = ctx.metrics\n this.count = ctx.textures.length\n this.maxPosition = this.count\n this.current = clamp(Math.round(ctx.position), 0, Math.max(this.count - 1, 0)) // pages turned\n\n this.geometry = createSingleGeometry(ctx.settings.pageAspect)\n this.sheets = ctx.textures.map((front, index) => {\n const { mesh, uniforms } = createSingleSheet(this.geometry, front, ctx.blank, ctx.pattern, ctx.settings, ctx.shading)\n this.group.add(mesh)\n return {\n index,\n mesh,\n uniforms,\n progress: index < this.current ? 1 : 0,\n grabX: 1,\n grabXTarget: 1,\n grabY: 0,\n grabYTarget: 0,\n wobble: { tiltDeg: 0, flipTime: 1 },\n }\n })\n\n // Uneven spacing between rolled pages (some gaps clump together) and an uneven pace\n // for how the roll catches up, both seeded so they look the same every time.\n const P = this.count\n const gaps: number[] = []\n for (let i = 0; i < P - 1; i++) gaps.push(Math.pow(hash(i + 1, ROLL.radiusSpacingSeed), ROLL.radiusSpacingClump))\n const gapSum = gaps.reduce((a, b) => a + b, 0)\n this.radiusSpacing = [0]\n let acc = 0\n for (const g of gaps) {\n acc += 0.1 / gaps.length + 0.9 * (gapSum > 0 ? g / gapSum : 1 / gaps.length)\n this.radiusSpacing.push(acc)\n }\n const steps: number[] = []\n for (let i = 0; i < P; i++) steps.push(Math.pow(hash(i + 1, CATCH_UP.seed + 0.5), CATCH_UP.clump))\n const stepSum = steps.reduce((a, b) => a + b, 0)\n this.catchUp = [0]\n acc = 0\n for (const s of steps) {\n acc += stepSum > 0 ? (s / stepSum) * P : 1\n this.catchUp.push(acc)\n }\n\n // Read pages settle under the pile some way behind. A short magazine settles them sooner,\n // so the pages under the cover are in place before the last page turns and the magazine\n // closes without the pile jumping up.\n const settleBy = P - 2 - PILE.renderDepth\n this.fallDuration = clamp(settleBy / 2, 1, ROLL.fallDurationPages)\n this.fallDelay = clamp(settleBy - this.fallDuration, 0, ROLL.fallDelayPages)\n\n this.bindInput()\n }\n\n get position() {\n return this.current\n }\n\n get turning() {\n return this.flights.length > 0 || this.drag !== null\n }\n\n readonly activeSide = 'both'\n\n pagesInView() {\n // read pages stay on the roll, their corners showing, until they settle under the pile\n const settled = Math.floor(this.paced(this.turned) - 1 - this.fallDelay - this.fallDuration)\n let from = clamp(settled + 1, 0, Math.max(this.current - 1, 0))\n let to = Math.max(Math.min(this.current, this.count - 1), this.pileTop()?.index ?? 0)\n const turning = this.flights.map((flight) => flight.sheet)\n if (this.drag) turning.push(this.drag.sheet)\n for (const sheet of turning) {\n from = Math.min(from, sheet.index)\n to = Math.max(to, sheet.index)\n }\n return { from, to: Math.max(from, to) }\n }\n\n // the page on top of the pile, the one a turning page reveals\n private pileTop() {\n return this.sheets.find((sheet) => sheet.index >= this.current && !this.busy(sheet))\n }\n\n setPageTexture(page: number, texture: THREE.Texture) {\n const sheet = this.sheets[page]\n if (!sheet) return\n sheet.uniforms.uFront.value = texture\n this.dirty = true\n }\n\n dispose() {\n this.cancelHold()\n this.abort.abort()\n for (const sheet of this.sheets) disposeSheetMesh(sheet.mesh)\n this.geometry.dispose()\n }\n\n // --- helpers ----------------------------------------------------------------\n\n private randomWobble(): Wobble {\n if (!this.settings.motion.jitter) return { tiltDeg: 0, flipTime: 1 }\n return {\n tiltDeg: THREE.MathUtils.lerp(-WOBBLE.tiltDeg, WOBBLE.tiltDeg, Math.random()),\n flipTime: THREE.MathUtils.lerp(WOBBLE.flipTimeMin, WOBBLE.flipTimeMax, Math.random()),\n }\n }\n\n private seconds(time: number) {\n return time / this.settings.motion.speed\n }\n\n // pile lift for a sheet this many pages below the top\n private pileHeight(depth: number) {\n return (this.liftHeight / (1 + depth * PILE.liftDepthFalloff)) * (1 - 0.002 * depth)\n }\n\n // A bulged page is longer than it is wide; squeeze it so its arc stays one page long.\n private pileReach(height: number) {\n if (height <= 1e-6) return 1\n const curve = (x: number) => {\n const t = 1 - Math.min(x / PILE.liftPeakX, 1)\n return height * Math.sqrt(1 - t * t) * (1 - PILE.liftEdgeDrop * smooth((x - PILE.liftPeakX) / (1 - PILE.liftPeakX)))\n }\n let length = 0\n for (let i = 0; i < 6; i++) {\n const a = (i / 6) ** 2\n const b = ((i + 1) / 6) ** 2\n length += Math.hypot(b - a, curve(b) - curve(a))\n }\n return Math.max(2 - length, 0.5)\n }\n\n // turned pages, reshaped by the uneven catch-up pace\n private paced(turned: number) {\n const i = Math.min(Math.floor(turned), this.count)\n if (i >= this.count) return this.count\n return this.catchUp[i] + (this.catchUp[i + 1] - this.catchUp[i]) * (turned - i)\n }\n\n // 0..1 while the last page is turning\n private closing(turned: number) {\n return clamp(turned - (this.count - 1), 0, 1)\n }\n\n private shutting(turned: number) {\n return smooth((this.closing(turned) - 0.6) / 0.4)\n }\n\n private inFlight(sheet: Sheet) {\n return this.flights.some((f) => f.sheet === sheet)\n }\n\n private busy(sheet: Sheet) {\n return this.inFlight(sheet) || this.drag?.sheet === sheet\n }\n\n // floor point under the pointer, relative to the book\n private floorPoint(clientX: number, clientY: number) {\n const rect = this.canvas.getBoundingClientRect()\n this.ndc.set(((clientX - rect.left) / rect.width) * 2 - 1, -((clientY - rect.top) / rect.height) * 2 + 1)\n this.raycaster.setFromCamera(this.ndc, this.camera)\n this.floor.constant = -this.group.position.z\n const hit = this.raycaster.ray.intersectPlane(this.floor, this.hit)\n return hit ? { x: hit.x - this.group.position.x, y: hit.y - this.group.position.y } : null\n }\n\n // where on the page the hand is, steering how the page tilts as it turns\n private grab(sheet: Sheet, point: { x: number; y: number } | null, snap: boolean) {\n if (!point) return\n sheet.grabXTarget = Math.min(Math.abs(point.x), 1)\n sheet.grabYTarget = clamp(point.y / this.halfHeight, -1, 1)\n if (snap) {\n sheet.grabX = sheet.grabXTarget\n sheet.grabY = sheet.grabYTarget\n }\n }\n\n // the last page only turns when the magazine loops back to the cover\n private canTurn(forward: boolean) {\n if (!forward) return this.current > 0\n return this.current < (this.settings.loop ? this.count : this.count - 1)\n }\n\n // which page a press at this x would pick up: right of the spine turns forward\n private sheetAt(clientX: number) {\n const rect = this.canvas.getBoundingClientRect()\n const spineX = this.spine.copy(this.group.position).project(this.camera).x\n const forward = clientX >= rect.left + (0.5 * spineX + 0.5) * rect.width\n if (!this.canTurn(forward)) return null\n return { sheet: this.sheets[forward ? this.current : this.current - 1], forward }\n }\n\n // moves the turned count when a page is sent one way or the other\n private commit(forward: boolean, toProgress: number) {\n if (forward && toProgress === 1) this.current++\n else if (!forward && toProgress === 0) this.current--\n this.dirty = true\n }\n\n private fly(sheet: Sheet, forward: boolean, toProgress: number, flipTime = CLICK.flipTime) {\n this.commit(forward, toProgress)\n const from = sheet.progress\n this.flights = this.flights.filter((f) => f.sheet !== sheet)\n this.flights.push({\n sheet,\n from,\n to: toProgress,\n start: performance.now(),\n duration: 1000 * this.seconds(flipTime) * sheet.wobble.flipTime * Math.max(0.2, Math.abs(toProgress - from)),\n })\n }\n\n flip(forward: boolean) {\n if (!this.canTurn(forward)) return false\n const sheet = this.sheets[forward ? this.current : this.current - 1]\n if (this.drag?.sheet === sheet) return false\n if (!this.inFlight(sheet)) sheet.wobble = this.randomWobble()\n this.fly(sheet, forward, forward ? 1 : 0)\n return true\n }\n\n jumpTo(position: number) {\n this.cancelHold()\n this.drag = null\n this.flights = []\n this.current = clamp(Math.round(position), 0, Math.max(this.count - 1, 0))\n for (const sheet of this.sheets) sheet.progress = sheet.index < this.current ? 1 : 0\n this.dirty = true\n }\n\n // --- input ------------------------------------------------------------------\n\n private bindInput() {\n const el = this.hitArea\n const signal = this.abort.signal\n const { interaction } = this.settings\n\n el.addEventListener(\n 'pointermove',\n (e) => {\n if (this.press && e.pointerId !== this.press.id) return\n if (this.hold && this.moved(e, this.hold.fired ? HOLD.moveSlopPx : undefined)) this.cancelHold()\n if (!this.drag || !this.press || !interaction.drag) return\n const dx = e.clientX - this.press.x\n this.drag.progress = clamp(this.drag.base - dx / this.drag.rectWidth / this.settings.motion.dragDistance, 0, 1)\n this.grab(this.drag.sheet, this.floorPoint(e.clientX, e.clientY), false)\n this.dirty = true\n },\n { signal },\n )\n\n el.addEventListener(\n 'pointerdown',\n (e) => {\n // a second finger doesn't take over; a new primary pointer means the old one is gone\n if (this.press && !e.isPrimary) return\n if (e.button !== 0 || !(interaction.drag || interaction.click || interaction.hold)) return\n const target = this.sheetAt(e.clientX)\n if (!target) return\n const sheet = target.sheet\n if (!this.inFlight(sheet)) sheet.wobble = this.randomWobble()\n this.grab(sheet, this.floorPoint(e.clientX, e.clientY), true)\n this.flights = this.flights.filter((f) => f.sheet !== sheet)\n this.press = { id: e.pointerId, x: e.clientX, y: e.clientY }\n this.drag = {\n sheet,\n forward: target.forward,\n base: sheet.progress,\n progress: sheet.progress,\n rectWidth: this.metrics.dragWidth,\n speed: 0,\n }\n this.cancelHold()\n if (interaction.hold) {\n this.hold = {\n forward: target.forward,\n x: e.clientX,\n y: e.clientY,\n count: 0,\n fired: false,\n timer: setTimeout(() => this.holdTick(), 1000 * HOLD.startTime),\n }\n }\n try {\n el.setPointerCapture(e.pointerId)\n } catch {}\n this.dirty = true\n },\n { signal },\n )\n\n el.addEventListener('pointerup', (e) => this.release(e), { signal })\n el.addEventListener('pointercancel', (e) => this.release(e), { signal })\n el.addEventListener('lostpointercapture', (e) => this.release(e), { signal })\n\n if (!interaction.drag) return\n // let vertical swipes scroll the page, take over the flatter ones\n el.addEventListener(\n 'touchstart',\n (e) => {\n const t = e.touches[0]\n this.touch = e.touches.length === 1 ? { x: t.clientX, y: t.clientY, claimed: false } : null\n },\n { passive: true, signal },\n )\n el.addEventListener(\n 'touchmove',\n (e) => {\n if (!this.touch || e.touches.length !== 1) return\n if (this.touch.claimed) {\n if (e.cancelable) e.preventDefault()\n return\n }\n if (!e.cancelable) {\n this.touch = null\n return\n }\n const t = e.touches[0]\n const dx = Math.abs(t.clientX - this.touch.x)\n const dy = Math.abs(t.clientY - this.touch.y)\n if (dx >= DRAG.claimAfterPx && dy < dx * DRAG.claimSteepness) {\n this.touch.claimed = true\n e.preventDefault()\n }\n },\n { passive: false, signal },\n )\n }\n\n private moved(e: PointerEvent, slop = DRAG.clickSlopPx) {\n return this.press !== null && (e.clientX - this.press.x) ** 2 + (e.clientY - this.press.y) ** 2 > slop * slop\n }\n\n private cancelHold() {\n if (this.hold) clearTimeout(this.hold.timer)\n this.hold = null\n }\n\n // press and hold keeps turning pages, a little faster each time\n private holdTick() {\n const hold = this.hold\n if (!hold) return\n if (!hold.fired) {\n hold.fired = true\n this.drag = null\n }\n if (!this.canTurn(hold.forward)) return this.cancelHold()\n const sheet = this.sheets[hold.forward ? this.current : this.current - 1]\n const ramp = (from: number, to: number) => from + (to - from) * Math.min(hold.count / HOLD.speedUpSheets, 1)\n if (!this.inFlight(sheet)) sheet.wobble = this.randomWobble()\n this.grab(sheet, this.floorPoint(hold.x, hold.y), true)\n const flipTime = ramp(HOLD.firstFlipTime, HOLD.fastestFlipTime)\n this.fly(sheet, hold.forward, hold.forward ? 1 : 0, flipTime)\n hold.count++\n hold.timer = setTimeout(\n () => this.holdTick(),\n 1000 * this.seconds(flipTime) * ramp(HOLD.firstGapShare, HOLD.fastestGapShare),\n )\n }\n\n // pointercancel and lostpointercapture put the page back; only pointerup completes a turn\n private release(e: PointerEvent) {\n if (!this.press || e.pointerId !== this.press.id) return\n const cancelled = e.type !== 'pointerup'\n const wasMoved = this.moved(e)\n this.cancelHold()\n const drag = this.drag\n this.drag = null\n this.press = null\n try {\n this.hitArea.releasePointerCapture(e.pointerId)\n } catch {}\n if (!drag) return\n const { interaction } = this.settings\n\n // a click turns the page\n if (!wasMoved || !interaction.drag) {\n if (!cancelled && interaction.click) this.fly(drag.sheet, drag.forward, drag.forward ? 1 : 0)\n else this.settleBack(drag)\n return\n }\n\n let to: number\n if (cancelled) to = drag.forward ? 0 : 1\n else {\n const travelled = drag.forward ? drag.progress - drag.base : drag.base - drag.progress\n to = drag.forward === travelled >= this.settings.motion.commitThreshold ? 1 : 0\n }\n this.commit(drag.forward, to)\n\n // fall the rest of the way, launched with the speed the hand left it with\n const sheet = drag.sheet\n const speed = to === 1 ? drag.speed : -drag.speed\n const remaining = Math.max(Math.abs(to - sheet.progress), 0.001)\n const duration = this.seconds(DRAG.fallTime) * sheet.wobble.flipTime * Math.pow(remaining, DRAG.fallTimeExponent)\n this.flights = this.flights.filter((f) => f.sheet !== sheet)\n this.flights.push({\n sheet,\n from: sheet.progress,\n to,\n start: performance.now(),\n duration: 1000 * duration,\n launch: clamp((speed * duration) / remaining, 0, 2),\n })\n }\n\n // a press that didn't turn anything: put the page back where it was\n private settleBack(drag: Drag) {\n const to = drag.forward ? 0 : 1\n if (drag.sheet.progress === to) return\n this.flights.push({ sheet: drag.sheet, from: drag.sheet.progress, to, start: performance.now(), duration: 300 })\n }\n\n // --- simulation ---------------------------------------------------------------\n\n update(now: number) {\n for (let i = this.flights.length - 1; i >= 0; i--) {\n const f = this.flights[i]\n const t = clamp((now - f.start) / f.duration, 0, 1)\n let k: number\n if (f.launch === undefined) {\n k = 0.5 * (1 - Math.cos((1 - Math.pow(1 - t, CLICK.settlePower)) * Math.PI))\n } else {\n // hermite: leaves with the hand's speed, lands at rest\n const t2 = t * t\n const t3 = t2 * t\n k = (t3 - 2 * t2 + t) * f.launch + (3 * t2 - 2 * t3)\n }\n f.sheet.progress = f.from + (f.to - f.from) * k\n if (now >= f.start + f.duration) {\n this.flights.splice(i, 1)\n this.dirty = true\n }\n }\n\n // read to the end: everything has settled flat, start over from the cover\n if (this.count && this.current >= this.count && !this.flights.length && !this.drag) {\n this.current = 0\n for (const sheet of this.sheets) sheet.progress = 0\n this.dirty = true\n }\n\n const dt = this.last ? Math.min((now - this.last) / 1000, 0.1) : 1 / 60\n this.last = now\n\n if (this.drag) {\n const sheet = this.drag.sheet\n const step = (this.drag.progress - sheet.progress) * (1 - Math.pow(0.01, dt / DRAG.progressSmoothTime))\n sheet.progress += step\n this.drag.speed = dt > 0 ? step / dt : 0\n }\n\n let moving = this.flights.length > 0 || this.drag !== null\n const grabStep = 1 - Math.pow(0.001, dt / GRAB.smoothTime)\n for (const sheet of this.sheets) {\n for (const key of ['grabX', 'grabY'] as const) {\n const target = key === 'grabX' ? sheet.grabXTarget : sheet.grabYTarget\n const d = target - sheet[key]\n if (Math.abs(d) < 1e-4) sheet[key] = target\n else {\n sheet[key] += d * grabStep\n moving = true\n }\n }\n if (!this.busy(sheet)) {\n const rest = sheet.index < this.current ? 1 : 0\n if (sheet.progress !== rest) {\n sheet.progress = rest\n this.dirty = true\n }\n }\n }\n\n if (!moving && !this.dirty) return false\n\n let turned = 0\n for (const sheet of this.sheets) turned += sheet.progress\n this.turned = turned\n const edge = this.pileEdge(turned)\n const top = this.pileTop()\n for (const sheet of this.sheets) {\n this.applyUniforms(sheet, turned, edge)\n sheet.uniforms.uTrim.value.set(sheet === top || this.busy(sheet) ? 0 : 1, edge.bottom, edge.top)\n }\n\n const closing = this.closing(turned) > 0\n // Pages turning back can run well ahead of the position, so the pile is drawn down from its\n // top, the page they're settling onto, rather than from the position.\n const pileFrom = Math.max(this.current, top?.index ?? this.current)\n for (const sheet of this.sheets) {\n const i = sheet.index\n sheet.mesh.visible = closing || i <= pileFrom + PILE.renderDepth || this.busy(sheet)\n layerSheet(sheet.mesh, i < this.current ? this.current - 1 - i : i - this.current, this.count, this.busy(sheet))\n }\n\n // the book eases back and to the right as the read pages pile up behind it\n const read = smooth(this.count > 1 ? this.paced(turned) / (this.count - 1) : 0) * (1 - this.shutting(turned))\n this.group.position.x = BOOK.pushRight * read\n this.group.position.z = -BOOK.pushBack * read\n\n this.dirty = false\n return true\n }\n\n // How far the top of the pile reaches at its bottom and top edges. Pages under it would\n // reach further, being flatter, and show a sliver of whatever is printed there; instead\n // they stop at its edge, and whatever still gets past is trimmed in the shader.\n private pileEdge(turned: number) {\n const share = PILE.liftShutShare + (1 - PILE.liftShutShare) * (this.count ? this.paced(turned) / this.count : 0)\n const topShare = share + (PILE.liftShutShare - share) * this.shutting(turned)\n const height = this.pileHeight(0) * Math.max(topShare, 0.001)\n return {\n bottom: this.pileReach(height * (1 - 0.5 * PILE.coneTaper)),\n top: this.pileReach(height * (1 + 0.5 * PILE.coneTaper)),\n }\n }\n\n private applyUniforms(sheet: Sheet, turned: number, edge: { bottom: number; top: number }) {\n const u = sheet.uniforms\n const P = this.count\n const T = sheet.progress\n const paced = this.paced(turned)\n // 1 on the last page, whatever the page count, so the roll is open and even by then\n const lastPage = this.catchUp[Math.max(P - 1, 0)]\n const through = lastPage > 0 ? paced / lastPage : 1\n\n // how far read pages swing round before rolling; it opens up as you read on\n const openFraction = Math.min(through, 1)\n const spineOpen =\n ((ROLL.spineOpenStartDeg +\n (ROLL.spineOpenEndDeg - ROLL.spineOpenStartDeg) * Math.pow(openFraction, ROLL.spineOpenPower)) *\n Math.PI) /\n 180\n const shut = this.shutting(turned)\n const closed = this.closing(turned) >= 1 ? 1 : 0\n const fall = Math.max(\n smooth((paced - (sheet.index + 1) - this.fallDelay) / this.fallDuration),\n shut,\n )\n\n // cone radii: the further back in the roll, the wider; it evens out as the roll fills\n const spacing = this.radiusSpacing[sheet.index] ?? 0\n const topR = ROLL.coverTopRadius + (ROLL.backTopRadius - ROLL.coverTopRadius) * spacing\n const bottomR = ROLL.coverBottomRadius + (ROLL.backBottomRadius - ROLL.coverBottomRadius) * spacing\n const even = Math.pow(Math.min(through * ROLL.lastPageEven, 1), ROLL.coneEvenPower)\n const midR = (topR + bottomR) / 2\n const radii = {\n top: (topR + (midR - topR) * even) * (1 - fall),\n bottom: (bottomR + (midR - bottomR) * even) * (1 - fall),\n }\n\n // pile bulge\n const fallen = fall >= 1\n const readShare = P ? paced / P : 0\n const depth = fallen ? Math.max(0, P - turned) + sheet.index : Math.max(0, sheet.index - turned)\n const presence = fallen\n ? Math.max(smooth(paced - (sheet.index + 1) - this.fallDelay - this.fallDuration), closed)\n : (1 - T) * (1 - shut)\n const share = PILE.liftShutShare + (1 - PILE.liftShutShare) * readShare\n const open = this.pileHeight(depth) * (fallen ? PILE.liftShutShare : share)\n const lift = (open + (this.pileHeight(sheet.index) * PILE.liftShutShare - open) * shut) * presence\n u.uPileLift.value.set(PILE.liftPeakX, lift, PILE.liftEdgeDrop)\n const topShare = share + (PILE.liftShutShare - share) * shut\n const reference = Math.max(this.pileHeight(0) * Math.max(topShare, 0.001), 1e-6)\n u.uConeTaper.value = (PILE.coneTaper * lift) / reference\n\n // hold a page high and to the side and it turns tilted\n const reachFade = 1 - 0.5 * Math.max(sheet.grabY, 0) * sheet.grabX\n const tiltDeg = sheet.grabY * (0.5 + 0.5 * sheet.grabX) * this.settings.motion.maxTilt + sheet.wobble.tiltDeg\n\n if (fallen) {\n u.uCone.value.set(0, 1, 0)\n u.uTailBend.value = 0\n const angle = TAU * T\n u.uFlipRotation.value.set(Math.cos(angle), Math.sin(angle))\n } else {\n this.shapeTurn(sheet, T, spineOpen, radii, (tiltDeg * reachFade * Math.PI) / 180)\n }\n\n const height = u.uPileLift.value.y\n const taper = u.uConeTaper.value\n let bottomReach = this.pileReach(height * (1 - 0.5 * taper))\n let topReach = this.pileReach(height * (1 + 0.5 * taper))\n if (!this.busy(sheet) && (fallen || sheet.index >= this.current)) {\n bottomReach = Math.min(bottomReach, edge.bottom)\n topReach = Math.min(topReach, edge.top)\n }\n u.uPileReach.value.set(bottomReach, topReach)\n }\n\n // a turning page: swing round the spine up to the open angle, then roll round the cone\n private shapeTurn(sheet: Sheet, T: number, spineOpen: number, radii: { top: number; bottom: number }, tilt: number) {\n const u = sheet.uniforms\n const half = this.halfHeight\n const aspect = this.settings.pageAspect\n const curl = this.settings.motion.curl\n const lead = TAU * (1 - Math.pow(1 - T, SHAPE.midLeadPower))\n const even = TAU * T\n const swing = Math.min(lead, spineOpen)\n const wrap = lead - swing\n const wrapShare = wrap / Math.max(TAU - spineOpen, 0.001)\n // start the roll loose and tighten it as the page goes round\n const rollScale = 1 + (SHAPE.midRollScale - 1) * (1 - smooth(wrapShare))\n const topRadius = Math.max(radii.top * rollScale, 1e-4)\n const spread = clamp((radii.bottom * rollScale - topRadius) / aspect, -0.999, 0.999)\n\n // length of page taken up by the roll at height y\n const rolled = (y: number) => {\n const r = Math.max(1e-5, topRadius + (half - y) * spread)\n const a = clamp(spread * wrap, -1.5, 1.5)\n return Math.abs(spread) < 1e-4 ? r * wrap : (r * Math.tan(a)) / spread\n }\n // the free edge lines up with the roll as the page goes round\n const angle = tilt + (Math.atan2(rolled(-half) - rolled(half), aspect) - tilt) * smooth(T)\n const cos = Math.cos(angle)\n const sin = Math.sin(angle)\n const curlStart = Math.max(rolled(half) * cos + half * sin, rolled(-half) * cos - half * sin)\n const curlLength = cos + half * Math.abs(sin) - curlStart\n\n // the pile bulge lets go as the page leaves it\n const letGo = 1 - smooth(lead / spineOpen)\n u.uPileLift.value.y *= letGo\n u.uConeTaper.value *= letGo\n u.uCone.value.set(spread, topRadius, wrap)\n u.uTailCurl.value.set(cos, sin, curlStart, Math.max(curlLength, 0.001))\n\n // mid-turn the free end bows back behind the roll\n const flat = 1 - smooth((T - SHAPE.midBowFlatFrom) / (SHAPE.midBowFlatTo - SHAPE.midBowFlatFrom))\n u.uTailBend.value = Math.max(SHAPE.midBow * curl * flat * Math.min(even - lead, 0), -lead)\n u.uFlipRotation.value.set(Math.cos(swing), Math.sin(swing))\n }\n}\n","import * as THREE from 'three'\nimport type { ResolvedSettings } from '../options.js'\nimport { createSheetMesh, type SheetMesh, type SheetShading, type SheetUniforms } from '../paper/paperMaterial.js'\n\n// Single-layout sheet: one page per sheet, printed on the front only. Pages that have been\n// read don't land on a left stack; they swing around the spine and roll up around a cone\n// behind the magazine, like a magazine folded back on itself.\nconst NORMAL_EPSILON = 0.02\n\nconst vertexChunk = /* glsl */ `\n uniform vec3 uPileLift; // x: where the bulge peaks, y: its height, z: how much it drops at the edge\n uniform float uConeTaper; // bulge difference between top and bottom of the pile\n uniform vec2 uPileReach; // horizontal squeeze at the top/bottom so the bulged page keeps its length\n uniform vec4 uTailCurl; // fold direction (cos, sin), where the curl starts, curl length\n uniform float uTailBend; // how far the free end bows back\n uniform vec3 uCone; // radius change per unit of y, radius at the top, how far to wrap\n uniform vec2 uFlipRotation;\n\n float sheetPileLift(float x) {\n float t = 1.0 - min(x / uPileLift.x, 1.0);\n float fall = uPileLift.z * smoothstep(uPileLift.x, 1.0, x);\n return uPileLift.y * sqrt(1.0 - t * t) * (1.0 - fall);\n }\n\n // Roll a flat point around a cone whose axis is the spine. The radius grows from the\n // top of the page to the bottom (spread), so pages roll up tighter at one end.\n vec3 sheetConeWrap(float x, float y, float spread, float topRadius, float maxWrap) {\n float apexY = SHEET_ASPECT * 0.5;\n float rowRadius = max(1e-5, topRadius + (apexY - y) * spread);\n float cosCone = sqrt(max(0.0, 1.0 - spread * spread));\n\n float slant = x * spread / rowRadius;\n float lean = abs(slant) < 1e-3 ? 1.0 - slant * slant / 3.0 : atan(slant) / slant;\n float wrap = (x / rowRadius) * lean;\n\n if (wrap <= maxWrap) {\n float reach = length(vec2(rowRadius, x * spread));\n return vec3(\n reach * sin(wrap),\n y + spread * (reach * (1.0 - cos(wrap)) - x * x / (rowRadius + reach)),\n reach * cosCone * (1.0 - cos(wrap))\n );\n }\n\n // past the wrap angle the sheet leaves the cone and carries on straight\n float fan = spread * maxWrap;\n float fanSin = abs(fan) < 1e-3 ? 1.0 - fan * fan / 6.0 : sin(fan) / fan;\n float fanVers = abs(fan) < 1e-3 ? fan * (0.5 - fan * fan / 24.0) : (1.0 - cos(fan)) / fan;\n\n float along = x * spread * sin(fan) + rowRadius * cos(fan);\n float past = x * cos(fan) - rowRadius * maxWrap * fanSin;\n float lift = along * (1.0 - cos(maxWrap)) + past * sin(maxWrap);\n\n return vec3(\n along * sin(maxWrap) + past * cos(maxWrap),\n y + rowRadius * maxWrap * fanVers - x * sin(fan) + spread * lift,\n cosCone * lift\n );\n }\n\n vec3 sheetShape(float x, float y) {\n return sheetConeWrap(x, y, uCone.x, uCone.y, uCone.z);\n }\n\n // the unread pile bulges up off the spine\n vec3 sheetPile(float u, float y) {\n float row = y / SHEET_ASPECT;\n float taper = 1.0 + uConeTaper * row;\n float x = u * mix(uPileReach.x, uPileReach.y, row + 0.5);\n return vec3(x, y, sheetPileLift(x) * taper);\n }\n\n // the free end of a turning page bows back past the roll\n vec3 sheetTailCurl(vec2 p) {\n float cosTilt = uTailCurl.x;\n float sinTilt = uTailCurl.y;\n float along = p.x * cosTilt + p.y * sinTilt;\n float across = -p.x * sinTilt + p.y * cosTilt;\n float beyond = max(0.0, along - uTailCurl.z);\n float angle = beyond / uTailCurl.w * uTailBend;\n float sinc = abs(angle) < 1e-4 ? 1.0 : sin(angle) / angle;\n float versine = abs(angle) < 1e-4 ? 0.0 : (1.0 - cos(angle)) / angle;\n float curledAlong = along - beyond + beyond * sinc;\n return vec3(\n curledAlong * cosTilt - across * sinTilt,\n curledAlong * sinTilt + across * cosTilt,\n beyond * versine\n );\n }\n\n vec3 sheetPosition(vec2 uv, float noise) {\n vec3 pile = sheetPile(uv.x, (uv.y - 0.5) * SHEET_ASPECT);\n vec3 curled = sheetTailCurl(pile.xy);\n vec3 shape = sheetShape(curled.x, curled.y);\n // the curl lifts off the cone surface, along its normal\n if (curled.z != 0.0) {\n vec3 alongX = sheetShape(curled.x + 1e-3, curled.y) - shape;\n vec3 alongY = sheetShape(curled.x, curled.y + 1e-3) - shape;\n shape += normalize(cross(alongX, alongY)) * curled.z;\n }\n\n // spin about the spine\n float z = shape.z + pile.z;\n return vec3(\n shape.x * uFlipRotation.x - z * uFlipRotation.y,\n shape.y,\n shape.x * uFlipRotation.y + z * uFlipRotation.x\n );\n }\n`\n\nconst fragmentChunk = /* glsl */ `\n uniform vec3 uTrim; // x: whether to trim, y/z: the top page's right edge at its bottom/top\n\n // Right of the spine, a page under the top one only shows where the top page is: whatever\n // reaches past its right or bottom edge is trimmed away.\n bool sheetHidden(vec3 p) {\n if (uTrim.x < 0.5 || p.x <= 0.0) return false;\n float row = clamp(p.y / SHEET_ASPECT + 0.5, 0.0, 1.0);\n return p.x > mix(uTrim.y, uTrim.z, row) || p.y < -0.5 * SHEET_ASPECT;\n }\n`\n\nexport function createSingleGeometry(aspect: number): THREE.BufferGeometry {\n return new THREE.PlaneGeometry(1, aspect, 64, Math.round(64 * aspect))\n}\n\nexport interface SingleSheetUniforms extends SheetUniforms {\n uPileLift: THREE.IUniform<THREE.Vector3>\n uPileReach: THREE.IUniform<THREE.Vector2>\n uConeTaper: THREE.IUniform<number>\n uTailCurl: THREE.IUniform<THREE.Vector4>\n uTailBend: THREE.IUniform<number>\n uCone: THREE.IUniform<THREE.Vector3>\n uFlipRotation: THREE.IUniform<THREE.Vector2>\n uTrim: THREE.IUniform<THREE.Vector3>\n uFront: THREE.IUniform<THREE.Texture>\n uBack: THREE.IUniform<THREE.Texture>\n uPattern: THREE.IUniform<THREE.Texture>\n uSpineContact: THREE.IUniform<THREE.Vector3>\n uLightDirection: THREE.IUniform<THREE.Vector3>\n}\n\nexport function createSingleSheet(\n geometry: THREE.BufferGeometry,\n front: THREE.Texture,\n back: THREE.Texture,\n pattern: THREE.Texture,\n settings: ResolvedSettings,\n shading: SheetShading,\n): { mesh: SheetMesh; uniforms: SingleSheetUniforms } {\n const uniforms: SingleSheetUniforms = {\n uPileLift: { value: new THREE.Vector3(1, 0, 0) },\n uPileReach: { value: new THREE.Vector2(1, 1) },\n uConeTaper: { value: 0 },\n uTailCurl: { value: new THREE.Vector4(1, 0, 1, 1) },\n uTailBend: { value: 0 },\n uCone: { value: new THREE.Vector3(0, 1, 0) },\n uFlipRotation: { value: new THREE.Vector2(1, 0) },\n uTrim: { value: new THREE.Vector3(0, 1, 1) },\n uFront: { value: front },\n uBack: { value: back },\n uPattern: { value: pattern },\n uSpineContact: shading.spineContact,\n uLightDirection: shading.lightDirection,\n }\n const mesh = createSheetMesh(\n geometry,\n uniforms,\n { vertexChunk, fragmentChunk, epsilon: NORMAL_EPSILON, shadowOffsetX: -0.004 },\n settings,\n )\n return { mesh, uniforms }\n}\n","import * as THREE from 'three'\nimport type { Engine, EngineContext, EngineMetrics } from '../engine.js'\nimport type { ResolvedSettings } from '../options.js'\nimport { disposeSheetMesh, layerSheet, type SheetMesh } from '../paper/paperMaterial.js'\nimport { createSpreadGeometry, createSpreadSheet, type SpreadSheetUniforms } from './spreadSheet.js'\n\nconst CURVE = {\n arc: 0.88, // share of a half turn the sheet bends by mid-flip\n arcJitter: 0.2,\n angleJitter: 0.3,\n tiltJitterDeg: 6,\n cornerRollMax: 2.3,\n directionSmoothTime: 0.4,\n smoothTime: 0.5,\n settleEpsilon: 1e-4,\n}\nconst HOVER = { smoothTime: 2, gapToSheetAbove: 0.1 }\nconst HOLD = {\n startTime: 0.22,\n firstFlipTime: 1.2,\n fastestFlipTime: 0.5,\n speedUpSheets: 20,\n firstGapShare: 0.15,\n fastestGapShare: 0.05,\n moveSlopPx: 40,\n}\nconst DRAG = { progressSmoothTime: 0.9, fallTime: 1, fallTimeExponent: 0.5, landingSpeed: 0.5, clickSlopPx: 5 }\nconst CLICK = { flipTime: 0.85, followArcGainMin: 1, followArcGainMax: 1.08 }\n\nconst clamp = THREE.MathUtils.clamp\nconst approach = (smoothTime: number, dt: number) => 1 - Math.pow(0.001, dt / smoothTime)\n\ninterface Curve {\n arc: number\n angleDeg: number\n}\ninterface Wobble {\n arc: number\n angle: number\n tilt: number\n}\ninterface Sheet {\n index: number\n mesh: SheetMesh\n uniforms: SpreadSheetUniforms\n progress: number\n direction: number\n directionSmooth: number\n curve: Curve\n curveTarget: Curve\n wobble: Wobble\n liftBase: number\n liftSpan: number\n}\ninterface Flight {\n index: number\n from: number\n to: number\n direction: number\n start: number\n duration: number\n launch?: number\n}\ninterface Drag {\n index: number\n forward: boolean\n base: number\n progress: number\n rectWidth: number\n targetCurve: Curve\n speed: number\n}\ninterface Hold {\n forward: boolean\n x: number\n y: number\n count: number\n fired: boolean\n timer: ReturnType<typeof setTimeout>\n}\n\n// The last sheets of each stack sit lowest. Sheets on top bulge and wrinkle the most.\nfunction stackLift(index: number, count: number, progress: number) {\n if (count <= 1) return 1\n return 0.31 + (0.69 * (index + (count - 1 - 2 * index) * (1 - progress))) / (count - 1)\n}\n\n/**\n * The open magazine: sheets rotate over the spine, bending as they go. Horizontal drag\n * drives the turn; where the hand is around the spine tilts the fold.\n */\nexport class SpreadEngine implements Engine {\n readonly group = new THREE.Group()\n readonly maxPosition: number\n\n private readonly settings: ResolvedSettings\n private readonly halfHeight: number\n private readonly camera: THREE.PerspectiveCamera\n private readonly canvas: HTMLCanvasElement\n private readonly hitArea: HTMLElement\n private readonly metrics: EngineMetrics\n private readonly geometry: THREE.BufferGeometry\n private readonly sheets: Sheet[]\n private readonly abort = new AbortController()\n private readonly shading: EngineContext['shading']\n\n private current: number\n private flights: Flight[] = []\n private drag: Drag | null = null\n // the pointer that started the gesture; other pointers are ignored until it lifts\n private press: { id: number; x: number; y: number } | null = null\n private hold: Hold | null = null\n private hover = { active: false, x: 0, y: 0 }\n private hovered = -1\n private leader = -1 // the sheet most recently sent flying\n private dirty = true\n private last = 0\n\n private readonly raycaster = new THREE.Raycaster()\n private readonly ndc = new THREE.Vector2()\n private readonly floor = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0)\n private readonly hit = new THREE.Vector3()\n private readonly spine = new THREE.Vector3()\n\n constructor(ctx: EngineContext) {\n this.settings = ctx.settings\n this.halfHeight = ctx.settings.pageAspect / 2\n this.camera = ctx.camera\n this.canvas = ctx.canvas\n this.hitArea = ctx.hitArea\n this.metrics = ctx.metrics\n this.shading = ctx.shading\n\n const count = Math.ceil(ctx.textures.length / 2)\n this.maxPosition = count\n this.current = clamp(Math.round(ctx.position), 0, count)\n this.geometry = createSpreadGeometry(ctx.settings.pageAspect)\n this.sheets = []\n for (let i = 0; i < count; i++) {\n const front = ctx.textures[2 * i]\n const back = ctx.textures[2 * i + 1] ?? ctx.blank\n const { mesh, uniforms } = createSpreadSheet(this.geometry, front, back, ctx.pattern, ctx.settings, ctx.shading)\n this.sheets.push({\n index: i,\n mesh,\n uniforms,\n progress: i < this.current ? 1 : 0,\n direction: 1,\n directionSmooth: 1,\n curve: { arc: 0, angleDeg: 0 },\n curveTarget: { arc: 0, angleDeg: 0 },\n wobble: { arc: 0.5, angle: 0.5, tilt: 0 },\n liftBase: stackLift(i, count, 0),\n liftSpan: stackLift(i, count, 1) - stackLift(i, count, 0),\n })\n this.group.add(mesh)\n }\n this.bindInput()\n }\n\n get position() {\n return this.current\n }\n\n get turning() {\n return this.flights.length > 0 || this.drag !== null\n }\n\n pagesInView() {\n // sheet i carries pages 2i (front) and 2i + 1 (back)\n let from = Math.max(2 * this.current - 1, 0)\n let to = 2 * this.current\n for (const sheet of this.sheets) {\n if (sheet.progress <= 0 || sheet.progress >= 1) continue\n from = Math.min(from, 2 * sheet.index)\n to = Math.max(to, 2 * sheet.index + 1)\n }\n return { from, to }\n }\n\n // closed, only the cover side can be grabbed\n get activeSide() {\n const busy = this.flights.length > 0 || this.drag !== null || this.hold !== null\n if (busy) return 'both'\n if (this.current === 0) return 'right'\n if (this.current === this.sheets.length) return 'left'\n return 'both'\n }\n\n setPageTexture(page: number, texture: THREE.Texture) {\n const sheet = this.sheets[page >> 1]\n if (!sheet) return\n if (page % 2 === 0) sheet.uniforms.uFront.value = texture\n else sheet.uniforms.uBack.value = texture\n this.dirty = true\n }\n\n dispose() {\n this.cancelHold()\n this.abort.abort()\n for (const sheet of this.sheets) disposeSheetMesh(sheet.mesh)\n this.geometry.dispose()\n }\n\n // --- helpers ----------------------------------------------------------------\n\n private randomWobble(): Wobble {\n if (!this.settings.motion.jitter) return { arc: 0.5, angle: 0.5, tilt: 0 }\n return { arc: Math.random(), angle: Math.random(), tilt: 2 * Math.random() - 1 }\n }\n\n private seconds(time: number) {\n return time / this.settings.motion.speed\n }\n\n // how far a sheet may bend before its far corner rolls over itself\n private arcLimit(angleDeg: number) {\n const t = (Math.abs(angleDeg) * Math.PI) / 180\n const reach = Math.cos(t) / (1 - this.halfHeight * Math.sin(t))\n return CURVE.cornerRollMax / (Math.PI * reach)\n }\n\n private restingArc(wobble: Wobble, angleDeg: number) {\n return Math.min(CURVE.arc * this.settings.motion.curl * (1 - wobble.arc * CURVE.arcJitter), this.arcLimit(angleDeg))\n }\n\n private inFlight(index: number) {\n return this.flights.some((f) => f.index === index)\n }\n\n // which sheet a press at this x would pick up\n private sheetAt(clientX: number) {\n const rect = this.canvas.getBoundingClientRect()\n const spineX = this.spine.set(0, 0, 0).project(this.camera).x\n const forward = clientX >= rect.left + (0.5 * spineX + 0.5) * rect.width\n if (forward ? this.current >= this.sheets.length : this.current <= 0) return null\n return { index: forward ? this.current : this.current - 1, forward }\n }\n\n // Where the hand is around the spine shapes the bend: near the top or bottom the fold\n // tilts, and further from the spine it tilts more.\n private curveFor(clientX: number, clientY: number, index: number): Curve {\n const wobble = this.sheets[index].wobble\n const maxTilt = this.settings.motion.maxTilt\n const rect = this.canvas.getBoundingClientRect()\n this.ndc.set(((clientX - rect.left) / rect.width) * 2 - 1, -((clientY - rect.top) / rect.height) * 2 + 1)\n this.raycaster.setFromCamera(this.ndc, this.camera)\n const hit = this.raycaster.ray.intersectPlane(this.floor, this.hit)\n\n let angleDeg = 0\n if (hit) {\n let deg = (Math.atan2(hit.y, hit.x) * 180) / Math.PI\n if (deg > 90) deg = 180 - deg\n if (deg < -90) deg = -180 - deg\n const jitter = 1 - wobble.angle * CURVE.angleJitter\n angleDeg = clamp(\n -(deg / 90) * maxTilt * Math.hypot(hit.x, hit.y) * jitter + wobble.tilt * CURVE.tiltJitterDeg,\n -maxTilt,\n maxTilt,\n )\n }\n\n // a sheet following one that's still in the air keeps to the inside of its curve\n const leader = this.sheets[this.leader]\n const following =\n leader &&\n this.leader !== index &&\n this.inFlight(this.leader) &&\n !(leader.direction > 0 ? leader.progress > 0.5 : leader.progress < 0.5)\n if (!following) return { arc: this.restingArc(wobble, angleDeg), angleDeg }\n\n const lead = leader.curveTarget\n angleDeg = clamp(angleDeg, Math.min(0, lead.angleDeg), Math.max(0, lead.angleDeg))\n const gain = THREE.MathUtils.lerp(CLICK.followArcGainMin, CLICK.followArcGainMax, wobble.arc)\n return { arc: clamp(lead.arc * gain, lead.arc, this.arcLimit(angleDeg)), angleDeg }\n }\n\n private setCurve(sheet: Sheet, curve: Curve, snap: boolean) {\n sheet.curveTarget.arc = curve.arc\n sheet.curveTarget.angleDeg = curve.angleDeg\n if (snap) {\n sheet.curve.arc = curve.arc\n sheet.curve.angleDeg = curve.angleDeg\n }\n }\n\n private commit(index: number, toProgress: number) {\n this.leader = index\n this.sheets[index].wobble = this.randomWobble()\n this.current = toProgress === 1 ? index + 1 : index\n this.dirty = true\n }\n\n private fly(index: number, forward: boolean, toProgress: number, flipTime = CLICK.flipTime) {\n this.commit(index, toProgress)\n const from = this.sheets[index].progress\n this.flights = this.flights.filter((f) => f.index !== index)\n this.flights.push({\n index,\n from,\n to: toProgress,\n direction: forward ? 1 : -1,\n start: performance.now(),\n duration: 1000 * this.seconds(flipTime) * Math.max(0.2, Math.abs(toProgress - from)),\n })\n }\n\n flip(forward: boolean) {\n if (forward ? this.current >= this.sheets.length : this.current <= 0) return false\n const index = forward ? this.current : this.current - 1\n if (this.drag?.index === index) return false\n const sheet = this.sheets[index]\n if (!this.inFlight(index)) {\n sheet.wobble = this.randomWobble()\n const angleDeg = sheet.wobble.tilt * CURVE.tiltJitterDeg\n this.setCurve(sheet, { arc: this.restingArc(sheet.wobble, angleDeg), angleDeg }, true)\n }\n sheet.direction = forward ? 1 : -1\n this.fly(index, forward, forward ? 1 : 0)\n return true\n }\n\n jumpTo(position: number) {\n this.cancelHold()\n this.drag = null\n this.flights = []\n this.current = clamp(Math.round(position), 0, this.sheets.length)\n for (const sheet of this.sheets) {\n sheet.progress = sheet.index < this.current ? 1 : 0\n sheet.direction = sheet.directionSmooth = 1\n }\n this.dirty = true\n }\n\n // --- input ------------------------------------------------------------------\n\n private bindInput() {\n const el = this.hitArea\n const signal = this.abort.signal\n const { interaction } = this.settings\n\n el.addEventListener(\n 'pointermove',\n (e) => {\n if (this.press && e.pointerId !== this.press.id) return\n if (this.hold && this.moved(e, this.hold.fired ? HOLD.moveSlopPx : undefined)) this.cancelHold()\n if (this.drag && this.press) {\n if (!interaction.drag) return\n const dx = e.clientX - this.press.x\n this.drag.progress = clamp(this.drag.base - dx / this.drag.rectWidth / this.settings.motion.dragDistance, 0, 1)\n this.drag.targetCurve = this.curveFor(e.clientX, e.clientY, this.drag.index)\n } else if (e.pointerType === 'mouse') {\n this.hover = { active: true, x: e.clientX, y: e.clientY }\n }\n this.dirty = true\n },\n { signal },\n )\n\n el.addEventListener(\n 'pointerleave',\n () => {\n this.hover.active = false\n this.dirty = true\n },\n { signal },\n )\n\n el.addEventListener(\n 'pointerdown',\n (e) => {\n // a second finger doesn't take over; a new primary pointer means the old one is gone\n if (this.press && !e.isPrimary) return\n if (e.button !== 0 || !(interaction.drag || interaction.click || interaction.hold)) return\n const target = this.sheetAt(e.clientX)\n if (!target) return\n const sheet = this.sheets[target.index]\n if (!this.inFlight(target.index)) this.setCurve(sheet, this.curveFor(e.clientX, e.clientY, target.index), true)\n this.flights = this.flights.filter((f) => f.index !== target.index)\n this.press = { id: e.pointerId, x: e.clientX, y: e.clientY }\n this.drag = {\n index: target.index,\n forward: target.forward,\n base: sheet.progress,\n progress: sheet.progress,\n rectWidth: this.metrics.dragWidth,\n targetCurve: { ...sheet.curveTarget },\n speed: 0,\n }\n sheet.direction = target.forward ? 1 : -1\n this.cancelHold()\n if (interaction.hold) {\n this.hold = {\n forward: target.forward,\n x: e.clientX,\n y: e.clientY,\n count: 0,\n fired: false,\n timer: setTimeout(() => this.holdTick(), 1000 * HOLD.startTime),\n }\n }\n try {\n el.setPointerCapture(e.pointerId)\n } catch {}\n this.dirty = true\n },\n { signal },\n )\n\n el.addEventListener('pointerup', (e) => this.release(e), { signal })\n el.addEventListener('pointercancel', (e) => this.release(e), { signal })\n el.addEventListener('lostpointercapture', (e) => this.release(e), { signal })\n }\n\n private moved(e: PointerEvent, slop = DRAG.clickSlopPx) {\n return this.press !== null && (e.clientX - this.press.x) ** 2 + (e.clientY - this.press.y) ** 2 > slop * slop\n }\n\n private cancelHold() {\n if (this.hold) clearTimeout(this.hold.timer)\n this.hold = null\n }\n\n // press and hold keeps turning pages, faster and faster\n private holdTick() {\n const hold = this.hold\n if (!hold) return\n if (!hold.fired) {\n hold.fired = true\n this.drag = null\n }\n if (hold.forward ? this.current >= this.sheets.length : this.current <= 0) return this.cancelHold()\n const ramp = (from: number, to: number) => from + (to - from) * Math.min(hold.count / HOLD.speedUpSheets, 1)\n const index = hold.forward ? this.current : this.current - 1\n const sheet = this.sheets[index]\n this.setCurve(sheet, hold.count === 0 ? sheet.curveTarget : this.curveFor(hold.x, hold.y, index), true)\n sheet.direction = hold.forward ? 1 : -1\n const flipTime = ramp(HOLD.firstFlipTime, HOLD.fastestFlipTime)\n this.fly(index, hold.forward, hold.forward ? 1 : 0, flipTime)\n hold.count++\n hold.timer = setTimeout(\n () => this.holdTick(),\n 1000 * this.seconds(flipTime) * ramp(HOLD.firstGapShare, HOLD.fastestGapShare),\n )\n }\n\n // pointercancel and lostpointercapture put the page back; only pointerup completes a turn\n private release(e: PointerEvent) {\n if (!this.press || e.pointerId !== this.press.id) return\n const cancelled = e.type !== 'pointerup'\n const wasMoved = this.moved(e)\n this.cancelHold()\n const drag = this.drag\n this.drag = null\n this.press = null\n try {\n this.hitArea.releasePointerCapture(e.pointerId)\n } catch {}\n if (!drag) return\n const { interaction } = this.settings\n\n // a click turns the page\n if (!wasMoved || !interaction.drag) {\n if (!cancelled && interaction.click) this.fly(drag.index, drag.forward, drag.forward ? 1 : 0)\n return\n }\n\n const travelled = drag.forward ? drag.progress - drag.base : drag.base - drag.progress\n const committed = !cancelled && travelled >= this.settings.motion.commitThreshold\n const to = drag.forward === committed ? 1 : 0\n this.commit(drag.index, to)\n\n // fall the rest of the way, launched with the speed the hand left it with\n const sheet = this.sheets[drag.index]\n const speed = to === 1 ? drag.speed : -drag.speed\n const remaining = Math.max(Math.abs(to - sheet.progress), 0.001)\n const duration = this.seconds(DRAG.fallTime) * Math.pow(remaining, DRAG.fallTimeExponent)\n this.flights = this.flights.filter((f) => f.index !== drag.index)\n this.flights.push({\n index: drag.index,\n from: sheet.progress,\n to,\n direction: drag.forward ? 1 : -1,\n start: performance.now(),\n duration: 1000 * duration,\n launch: clamp((speed * duration) / remaining, 0, 2),\n })\n }\n\n // --- simulation ---------------------------------------------------------------\n\n update(now: number) {\n for (let i = this.flights.length - 1; i >= 0; i--) {\n const f = this.flights[i]\n const sheet = this.sheets[f.index]\n const t = clamp((now - f.start) / f.duration, 0, 1)\n let k: number\n if (f.launch === undefined) {\n k = 0.5 * (1 - Math.cos(t * Math.PI))\n } else {\n // hermite: leaves with the hand's speed, lands softly\n const t2 = t * t\n const t3 = t2 * t\n k = (t3 - 2 * t2 + t) * f.launch + (3 * t2 - 2 * t3) + (t3 - t2) * DRAG.landingSpeed\n }\n sheet.progress = f.from + (f.to - f.from) * k\n sheet.direction = f.direction\n if (now >= f.start + f.duration) {\n this.flights.splice(i, 1)\n this.dirty = true\n }\n }\n\n const dt = this.last ? Math.min((now - this.last) / 1000, 0.1) : 1 / 60\n this.last = now\n\n let moving = this.flights.length > 0 || this.drag !== null\n\n if (this.drag) {\n const sheet = this.sheets[this.drag.index]\n const step = (this.drag.progress - sheet.progress) * (1 - Math.pow(0.01, dt / DRAG.progressSmoothTime))\n sheet.progress += step\n this.drag.speed = dt > 0 ? step / dt : 0\n this.setCurve(sheet, this.drag.targetCurve, false)\n }\n\n // hovering lifts the top sheet a touch\n const { hoverLift } = this.settings.motion\n const previous = this.hovered\n const canHover = this.settings.interaction.hover && hoverLift > 0\n const target = canHover && this.hover.active && !this.drag && !this.hold ? this.sheetAt(this.hover.x) : null\n this.hovered = target && !this.inFlight(target.index) ? target.index : -1\n if (this.hovered !== -1) {\n const sheet = this.sheets[this.hovered]\n this.setCurve(sheet, this.curveFor(this.hover.x, this.hover.y, this.hovered), this.hovered !== previous)\n }\n\n const toward = approach(HOVER.smoothTime, dt)\n const directionStep = approach(CURVE.directionSmoothTime, dt)\n const curveStep = approach(CURVE.smoothTime, dt)\n\n for (const sheet of this.sheets) {\n const i = sheet.index\n if (!this.inFlight(i) && this.drag?.index !== i) {\n const rest = i >= this.current ? 0 : 1\n const goal = i === this.hovered ? Math.abs(rest - hoverLift) : rest\n const diff = goal - sheet.progress\n if (Math.abs(diff) < CURVE.settleEpsilon) {\n if (sheet.progress !== goal) {\n sheet.progress = goal\n this.dirty = true\n }\n } else {\n sheet.progress += diff * toward\n moving = true\n }\n\n // never get ahead of the sheet lying on top of this one\n const above = this.sheets[rest === 0 ? i - 1 : i + 1]\n if (above) {\n const bounded =\n rest === 0\n ? Math.min(sheet.progress, Math.max(0, above.progress - HOVER.gapToSheetAbove))\n : Math.max(sheet.progress, Math.min(1, above.progress + HOVER.gapToSheetAbove))\n if (bounded !== sheet.progress) {\n sheet.progress = bounded\n moving = true\n }\n }\n sheet.direction = rest === 1 && sheet.progress !== rest ? -1 : 1\n sheet.directionSmooth = sheet.direction\n }\n\n const dd = sheet.direction - sheet.directionSmooth\n if (Math.abs(dd) < CURVE.settleEpsilon) {\n if (sheet.directionSmooth !== sheet.direction) {\n sheet.directionSmooth = sheet.direction\n this.dirty = true\n }\n } else {\n sheet.directionSmooth += dd * directionStep\n moving = true\n }\n\n for (const key of ['arc', 'angleDeg'] as const) {\n const d = sheet.curveTarget[key] - sheet.curve[key]\n if (Math.abs(d) < CURVE.settleEpsilon) sheet.curve[key] = sheet.curveTarget[key]\n else {\n sheet.curve[key] += d * curveStep\n moving = true\n }\n }\n\n this.applyUniforms(sheet)\n }\n\n if (!moving && !this.dirty) return false\n // the turning sheet standing most upright at the spine casts the contact shadow\n let contact: Sheet | null = null\n let upright = 0\n for (const sheet of this.sheets) {\n const i = sheet.index\n const depth = i >= this.current ? i - this.current : this.current - 1 - i\n const turning = this.inFlight(i) || this.drag?.index === i\n sheet.mesh.castShadow = this.settings.lighting.shadows && (depth < 3 || turning)\n layerSheet(sheet.mesh, depth, this.sheets.length, turning)\n const lift = Math.abs(sheet.uniforms.uFlipRotation.value.y)\n if (turning && lift > upright) {\n upright = lift\n contact = sheet\n }\n }\n const rotation = contact?.uniforms.uFlipRotation.value\n if (rotation) this.shading.spineContact.value.set(rotation.x, rotation.y, 1)\n else this.shading.spineContact.value.set(1, 0, 0)\n this.dirty = false\n return true\n }\n\n private applyUniforms(sheet: Sheet) {\n const u = sheet.uniforms\n const half = this.halfHeight\n const turn = sheet.progress * Math.PI\n const bend = Math.sin(turn) * Math.PI * sheet.curve.arc\n const tilt = (sheet.curve.angleDeg * Math.PI) / 180\n const cos = Math.cos(tilt)\n const sin = Math.sin(tilt)\n // the bend line always passes through a corner of the spine, so the spine never folds\n const curlLength = 1 - half * Math.sin(Math.abs(tilt))\n const curlStart = 1 - curlLength\n const reach = Math.max(0, cos + sin * half * Math.sign(sin) - curlStart)\n const tipAngle = (reach / curlLength) * bend\n const tipLift = reach * (Math.abs(tipAngle) < 1e-4 ? 0 : (1 - Math.cos(tipAngle)) / tipAngle)\n // turn a little further to make up for the trailing bend\n const rotation = turn + 0.5 * sheet.directionSmooth * tipLift\n\n u.uFlipProgress.value = sheet.progress\n u.uWrinkleSide.value = -Math.cos(turn)\n u.uDirection.value = sheet.directionSmooth\n u.uStackLift.value = sheet.liftBase + sheet.liftSpan * sheet.progress\n u.uBendAngle.value = bend\n u.uFold.value.set(cos, sin)\n u.uCurl.value.set(curlStart, curlLength)\n u.uFlipRotation.value.set(Math.cos(rotation), Math.sin(rotation))\n }\n}\n","import * as THREE from 'three'\nimport type { ResolvedSettings } from '../options.js'\nimport { createSheetMesh, type SheetMesh, type SheetShading, type SheetUniforms } from '../paper/paperMaterial.js'\n\n// A spread sheet is a 1 × pageAspect plane. Its flat coordinates come from the uv: x runs\n// from the spine (0) to the outer edge (1), y is centred on the spine's midpoint.\nconst NORMAL_EPSILON = 0.03\nconst WRINKLE_SCALE = 1.2\nconst WRINKLE_SEED = 46\n\nconst vertexChunk = (settings: ResolvedSettings) => /* glsl */ `\n // resting bulge across the page: up off the spine, a slight dip, up again, down at the edge\n const float LIFT_MAX_X = 0.14;\n const float LIFT_MAX_Z = 0.04;\n const float LIFT_DIP_X = 0.58;\n const float LIFT_DIP_Z = 0.035;\n const float LIFT_MID_X = 0.72;\n const float LIFT_MID_Z = 0.04;\n const float LIFT_EDGE_Z = 0.03;\n\n const float BULGE = ${settings.paper.bulge.toFixed(4)};\n const float WRINKLE = ${(0.13 * settings.paper.wrinkles).toFixed(4)};\n\n uniform float uFlipProgress;\n uniform float uWrinkleSide;\n uniform float uBendAngle;\n uniform vec2 uFold;\n uniform vec2 uCurl;\n uniform vec2 uFlipRotation;\n uniform float uDirection;\n uniform float uStackLift;\n\n attribute vec3 aNoise;\n\n float sheetRestingLift(float x) {\n if (x <= LIFT_MAX_X) return LIFT_MAX_Z * sin(x / LIFT_MAX_X * 1.57079633);\n if (x <= LIFT_DIP_X) return mix(LIFT_MAX_Z, LIFT_DIP_Z, smoothstep(LIFT_MAX_X, LIFT_DIP_X, x));\n if (x <= LIFT_MID_X) return mix(LIFT_DIP_Z, LIFT_MID_Z, smoothstep(LIFT_DIP_X, LIFT_MID_X, x));\n float t = (x - LIFT_MID_X) / (1.0 - LIFT_MID_X);\n return mix(LIFT_MID_Z, LIFT_EDGE_Z, 1.0 - cos(t * 1.57079633));\n }\n\n vec3 sheetPosition(vec2 uv, float noise) {\n float x = uv.x;\n float y = (uv.y - 0.5) * SHEET_ASPECT;\n\n // Every sheet shares the same wrinkles and bulge, scaled by its place in the stack,\n // so stacked sheets nest inside each other instead of crossing.\n float wrinkle = (mix(noise, 1.0 - noise, uFlipProgress) - 0.5) * smoothstep(0.0, 0.35, x) * WRINKLE * uStackLift;\n float lift = sheetRestingLift(x) * BULGE * uStackLift;\n\n // bend with constant curvature past the fold line, trailing against the turn\n float u = x * uFold.x + y * uFold.y;\n float v = -x * uFold.y + y * uFold.x;\n float beyond = max(0.0, u - uCurl.x);\n float angle = beyond / uCurl.y * uBendAngle;\n float sinc = abs(angle) < 1e-4 ? 1.0 : sin(angle) / angle;\n float versine = abs(angle) < 1e-4 ? 0.0 : (1.0 - cos(angle)) / angle;\n float bentU = u - beyond + beyond * sinc;\n float bentZ = -uDirection * beyond * versine;\n\n vec3 p = vec3(bentU * uFold.x - v * uFold.y, bentU * uFold.y + v * uFold.x, bentZ);\n\n // turn about the spine\n p = vec3(p.x * uFlipRotation.x - p.z * uFlipRotation.y, p.y, p.x * uFlipRotation.y + p.z * uFlipRotation.x);\n\n p.z += lift + uWrinkleSide * wrinkle;\n return p;\n }\n`\n\n// fbm value noise, sampled per vertex once (and at the two neighbours used for normals)\nfunction hash(x: number, y: number) {\n const s = Math.sin(x * 127.1 + y * 311.7) * 43758.5453\n return s - Math.floor(s)\n}\n\nfunction valueNoise(x: number, y: number) {\n const xi = Math.floor(x)\n const yi = Math.floor(y)\n const fx = x - xi\n const fy = y - yi\n const sx = fx * fx * (3 - 2 * fx)\n const sy = fy * fy * (3 - 2 * fy)\n const a = hash(xi, yi) + (hash(xi + 1, yi) - hash(xi, yi)) * sx\n const b = hash(xi, yi + 1) + (hash(xi + 1, yi + 1) - hash(xi, yi + 1)) * sx\n return a + (b - a) * sy\n}\n\nfunction wrinkleNoise(u: number, v: number, aspect: number) {\n let x = u / WRINKLE_SCALE + WRINKLE_SEED * 13.37\n let y = ((v - 0.5) * aspect) / WRINKLE_SCALE + WRINKLE_SEED * 7.77\n let value = 0\n let amplitude = 0.5\n for (let octave = 0; octave < 3; octave++) {\n value += amplitude * valueNoise(x, y)\n amplitude *= 0.5\n x *= 2\n y *= 2\n }\n return value\n}\n\nexport function createSpreadGeometry(aspect: number): THREE.BufferGeometry {\n const geometry = new THREE.PlaneGeometry(1, aspect, 64, Math.round(64 * aspect))\n const uv = geometry.attributes.uv\n const noise = new Float32Array(uv.count * 3)\n for (let i = 0; i < uv.count; i++) {\n const u = uv.getX(i)\n const v = uv.getY(i)\n noise[i * 3] = wrinkleNoise(u, v, aspect)\n noise[i * 3 + 1] = wrinkleNoise(u + NORMAL_EPSILON, v, aspect)\n noise[i * 3 + 2] = wrinkleNoise(u, v + NORMAL_EPSILON / aspect, aspect)\n }\n geometry.setAttribute('aNoise', new THREE.BufferAttribute(noise, 3))\n return geometry\n}\n\nexport interface SpreadSheetUniforms extends SheetUniforms {\n uFlipProgress: THREE.IUniform<number>\n uWrinkleSide: THREE.IUniform<number>\n uBendAngle: THREE.IUniform<number>\n uFold: THREE.IUniform<THREE.Vector2>\n uCurl: THREE.IUniform<THREE.Vector2>\n uFlipRotation: THREE.IUniform<THREE.Vector2>\n uDirection: THREE.IUniform<number>\n uStackLift: THREE.IUniform<number>\n uFront: THREE.IUniform<THREE.Texture>\n uBack: THREE.IUniform<THREE.Texture>\n uPattern: THREE.IUniform<THREE.Texture>\n uSpineContact: THREE.IUniform<THREE.Vector3>\n uLightDirection: THREE.IUniform<THREE.Vector3>\n}\n\nexport function createSpreadSheet(\n geometry: THREE.BufferGeometry,\n front: THREE.Texture,\n back: THREE.Texture,\n pattern: THREE.Texture,\n settings: ResolvedSettings,\n shading: SheetShading,\n): { mesh: SheetMesh; uniforms: SpreadSheetUniforms } {\n const uniforms: SpreadSheetUniforms = {\n uFlipProgress: { value: 0 },\n uWrinkleSide: { value: -1 },\n uBendAngle: { value: 0 },\n uFold: { value: new THREE.Vector2(1, 0) },\n uCurl: { value: new THREE.Vector2(0, 1) },\n uFlipRotation: { value: new THREE.Vector2(1, 0) },\n uDirection: { value: 1 },\n uStackLift: { value: 1 },\n uFront: { value: front },\n uBack: { value: back },\n uPattern: { value: pattern },\n uSpineContact: shading.spineContact,\n uLightDirection: shading.lightDirection,\n }\n const mesh = createSheetMesh(\n geometry,\n uniforms,\n { vertexChunk: vertexChunk(settings), epsilon: NORMAL_EPSILON, noise: true, shadowOffsetX: 0.004 },\n settings,\n )\n return { mesh, uniforms }\n}\n","import * as THREE from 'three'\nimport type { PageImage } from './options.js'\n\n// requests in flight at once, so the pages nearest the reader aren't starved\nconst CONCURRENCY = 4\n\nexport interface PageLoaderEvents {\n onLoad(page: number, texture: THREE.Texture): void\n /** The page's texture was freed; show plain paper until it loads again. */\n onUnload(page: number): void\n onError(page: number, error: unknown): void\n}\n\n/**\n * Loads page images into textures, nearest the visible pages first. Pages within `radius`\n * of the visible ones load; pages more than twice as far unload. Pages show plain paper\n * until their image arrives.\n */\nexport class PageLoader {\n /** Loaded textures by page; missing pages are still loading, unloaded or failed. */\n readonly textures: (THREE.Texture | undefined)[]\n private readonly owned = new Set<THREE.Texture>()\n private readonly inFlight = new Map<number, AbortController>()\n private readonly failed = new Set<number>()\n // `first` pages load before the rest; the rest go nearest first\n private first: number[] = []\n // the pages in view, and the one being read, which loads first among them\n private from = 0\n private to = 0\n private around = 0\n // whether the magazine loops, putting the cover right after the last page\n private wrap = false\n private waiting = new Set<number>()\n private settleFirst: () => void = () => {}\n private disposed = false\n\n constructor(\n private readonly sources: PageImage[],\n private readonly renderer: THREE.WebGLRenderer,\n private radius: number,\n private readonly events: PageLoaderEvents,\n ) {\n this.textures = new Array(sources.length)\n }\n\n /**\n * Starts loading around the visible pages `from`–`to`. Resolves once the `first` pages\n * within reach are in (or failed).\n */\n start(from: number, to: number, first: number[]): Promise<void> {\n this.from = from\n this.to = to\n this.first = first.filter((i) => i >= 0 && i < this.sources.length && this.distance(i) <= this.radius)\n this.waiting = new Set(this.first)\n const ready = new Promise<void>((resolve) => (this.settleFirst = resolve))\n this.focus(from, to)\n if (!this.waiting.size) this.settleFirst()\n return ready\n }\n\n /**\n * Follows the reader: loads around the pages in view (`from`–`to`), the page being read\n * (`around`) first, and unloads pages left far behind.\n */\n focus(from: number, to = from, around = from) {\n if (this.disposed) return\n this.from = from\n this.to = to\n this.around = around\n const keep = 2 * this.radius\n for (const [i, request] of this.inFlight) {\n if (this.distance(i) <= keep) continue\n request.abort()\n // frees its slot now, for the pages that are wanted\n this.inFlight.delete(i)\n }\n this.textures.forEach((texture, i) => {\n if (!texture || this.distance(i) <= keep || !this.owned.has(texture)) return\n this.textures[i] = undefined\n this.events.onUnload(i)\n this.owned.delete(texture)\n texture.dispose()\n })\n this.pump()\n }\n\n setWrap(wrap: boolean) {\n if (wrap === this.wrap) return\n this.wrap = wrap\n this.focus(this.from, this.to, this.around)\n }\n\n setRadius(radius: number) {\n this.radius = radius\n this.focus(this.from, this.to, this.around)\n }\n\n /** Loads the pages that failed again. */\n retry() {\n this.failed.clear()\n this.pump()\n }\n\n dispose() {\n this.disposed = true\n for (const request of this.inFlight.values()) request.abort()\n for (const texture of this.owned) texture.dispose()\n this.owned.clear()\n }\n\n // pages away from the visible ones\n private distance(page: number) {\n const straight = page < this.from ? this.from - page : page > this.to ? page - this.to : 0\n if (!this.wrap) return straight\n const count = this.sources.length\n return Math.min(straight, page + count - this.to, this.from + count - page)\n }\n\n // starts the most wanted pages while there's room\n private pump() {\n if (this.disposed || this.inFlight.size >= CONCURRENCY) return\n const wanted = (i: number) =>\n !this.textures[i] && !this.inFlight.has(i) && !this.failed.has(i) && this.distance(i) <= this.radius\n const queue = [\n ...this.first.filter((i) => this.waiting.has(i)),\n ...this.sources\n .map((_, i) => i)\n .sort((a, b) => this.distance(a) - this.distance(b) || Math.abs(a - this.around) - Math.abs(b - this.around)),\n ]\n for (const page of queue) {\n if (this.inFlight.size >= CONCURRENCY) return\n if (wanted(page)) this.load(page)\n }\n }\n\n private load(page: number) {\n const request = new AbortController()\n this.inFlight.set(page, request)\n this.createTexture(this.sources[page], request.signal)\n .then((texture) => {\n if (this.disposed || request.signal.aborted) {\n if (this.owned.delete(texture)) texture.dispose()\n return\n }\n texture.colorSpace = THREE.SRGBColorSpace\n // premultiplied, so filtering at the edge of transparent artwork doesn't pull in the\n // colour of texels nobody sees (the shader lays the page over the paper colour)\n texture.premultiplyAlpha = true\n texture.anisotropy = this.renderer.capabilities.getMaxAnisotropy()\n texture.needsUpdate = true\n this.renderer.initTexture(texture)\n this.textures[page] = texture\n this.events.onLoad(page, texture)\n })\n .catch((error) => {\n if (this.disposed || request.signal.aborted) return\n this.failed.add(page)\n this.events.onError(page, error)\n })\n .finally(() => {\n if (this.inFlight.get(page) === request) this.inFlight.delete(page)\n // an aborted page counts too: it's no longer needed to call the magazine ready\n if (this.waiting.delete(page) && !this.waiting.size) this.settleFirst()\n this.pump()\n })\n }\n\n private async createTexture(source: PageImage, signal: AbortSignal): Promise<THREE.Texture> {\n if (source instanceof THREE.Texture) return source\n const own = <T extends THREE.Texture>(texture: T) => (this.owned.add(texture), texture)\n if (typeof source === 'string') return own(new THREE.Texture(await loadImage(source, signal)))\n if (typeof HTMLCanvasElement !== 'undefined' && source instanceof HTMLCanvasElement) return own(new THREE.CanvasTexture(source))\n if (typeof HTMLImageElement !== 'undefined' && source instanceof HTMLImageElement) {\n if (!source.complete || !source.naturalWidth) await source.decode()\n return own(new THREE.Texture(source))\n }\n // ImageBitmaps ignore flipY, so redraw them the right way up\n const canvas = document.createElement('canvas')\n canvas.width = source.width\n canvas.height = source.height\n canvas.getContext('2d')!.drawImage(source, 0, 0)\n return own(new THREE.CanvasTexture(canvas))\n }\n}\n\nfunction loadImage(url: string, signal?: AbortSignal): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.decoding = 'async'\n const onAbort = () => {\n done()\n // stops the download\n img.src = ''\n reject(new DOMException('Page no longer needed', 'AbortError'))\n }\n const done = () => {\n img.onload = img.onerror = null\n signal?.removeEventListener('abort', onAbort)\n }\n signal?.addEventListener('abort', onAbort, { once: true })\n img.onload = () => {\n done()\n img.decode().then(() => resolve(img), () => resolve(img))\n }\n img.onerror = () => {\n done()\n reject(new Error(`Failed to load page image: ${url}`))\n }\n img.src = url\n })\n}\n\n/** Loads the paper grain option into a tiling texture. */\nexport async function loadGrain(source: string | HTMLImageElement | HTMLCanvasElement): Promise<THREE.Texture> {\n let texture: THREE.Texture\n if (typeof source === 'string') texture = new THREE.Texture(await loadImage(source))\n else if (source instanceof HTMLCanvasElement) texture = new THREE.CanvasTexture(source)\n else {\n if (!source.complete || !source.naturalWidth) await source.decode()\n texture = new THREE.Texture(source)\n }\n texture.wrapS = texture.wrapT = THREE.RepeatWrapping\n texture.needsUpdate = true\n return texture\n}\n"],"mappings":"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","names":["spread","THREE","spread","box","view","THREE","THREE","THREE","THREE","THREE","spread","THREE","THREE","NORMAL_EPSILON","vertexChunk","hash","HOLD","DRAG","CLICK","clamp","THREE"]}
|
package/dist/element.js
CHANGED