leafthrough 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -4
- package/dist/LeafThroughObject.d.ts +10 -2
- package/dist/{chunk-GYSQO7QZ.js → chunk-QFOGCGJJ.js} +73 -24
- package/dist/chunk-QFOGCGJJ.js.map +1 -0
- package/dist/element.js +1 -1
- package/dist/engine.d.ts +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +47 -7
- package/dist/index.js.map +1 -1
- package/dist/options.d.ts +32 -1
- package/dist/paper/paperMaterial.d.ts +7 -5
- package/dist/react.js +1 -1
- package/dist/single/SingleEngine.d.ts +2 -0
- package/dist/single/singleSheet.d.ts +2 -0
- package/dist/spread/SpreadEngine.d.ts +1 -0
- package/dist/spread/spreadSheet.d.ts +2 -0
- package/dist/svelte.js +1 -1
- package/dist/vue.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-GYSQO7QZ.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/options.ts","../src/LeafThrough.ts","../src/content.ts","../src/engine.ts","../src/framing.ts","../src/navigation.ts","../src/paper/paperTextures.ts","../src/textures.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"],"sourcesContent":["import type { ColorRepresentation, IUniform, 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 * Cuts each sheet to the shape of its front page: where that image is transparent there's no\n * paper, so the sheet and its shadow take its outline (a die-cut cover, a flap). Default\n * `false`: transparent areas are unprinted paper.\n */\n cutout?: boolean\n /**\n * Space between stacked sheets, in page widths. Sheets otherwise meet at the spine, where only\n * `depthBias` keeps the right one in front. A booklet 120 mm wide with 0.1 mm paper is\n * `0.1 / 120`. Default `0`, or `0.0005` in a `LeafThroughObject`.\n */\n sheetGap?: number\n /**\n * Draws each sheet a little nearer the camera than the sheets under it (a polygon offset), so\n * sheets that touch at the spine don't flicker through each other. It assumes the magazine is\n * seen from the front, and in a scene of your own it can pull pages through what lies just in\n * front of them. How far it moves a sheet depends on the camera's near and far planes. With\n * a `sheetGap`, turn it off. Default `true`, or `false` in a `LeafThroughObject`.\n */\n depthBias?: boolean\n}\n\n/**\n * Reshapes the paper in your own GLSL, after the magazine has shaped it. Experimental: it may\n * change in a patch release.\n */\nexport interface PaperDeform {\n /**\n * Declares the uniforms it reads and defines `vec3 deformSheet(vec3 p, vec2 uv)`. `p` is a\n * point of a sheet in the magazine's own units (a page is 1 wide, the spine runs up the y\n * axis, the pages lie on z = 0 facing +z) and `uv` where it is on the sheet (x from the spine\n * to the outer edge). Return where to draw it, in the same units.\n */\n glsl: string\n /** The uniforms `glsl` declares, by name. Change their `value` any time: they're read every frame. */\n uniforms?: Record<string, IUniform>\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, the decorative motion\n * goes: hovered pages don't lift and turns don't wobble at random. Turns themselves keep their\n * speed, and press and hold still riffles. 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 /** Only a `LeafThroughObject` has one. */\n deform: PaperDeform | null\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 cutout: paper.cutout ?? false,\n sheetGap: Math.max(0, paper.sheetGap ?? 0),\n depthBias: paper.depthBias ?? true,\n },\n deform: null,\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),\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: reduced ? false : (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,\n hover: interaction.hover ?? true,\n keyboard: interaction.keyboard ?? true,\n },\n maxPixelRatio: options.maxPixelRatio ?? 3,\n }\n}\n\n/** Options that don't need a rebuild (pages only need one when their images change). */\nexport const LIVE_OPTIONS = new Set<keyof LeafThroughOptions>([\n 'onPageChange',\n 'onReady',\n 'onPageLoad',\n 'onError',\n 'formatPageLabel',\n 'label',\n 'startPage',\n 'preload',\n 'pages',\n])\n\n/** How many pages either side of the visible ones load, from the `preload` option. */\nexport function preloadRadius(preload: number | undefined) {\n const radius = preload ?? 6\n return Number.isNaN(radius) ? 6 : Math.max(0, radius)\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.\nexport function 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\nexport function 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\n/** The options that changed between two snapshots. */\nexport function changedOptions<T extends object>(previous: T, next: T): (keyof T)[] {\n const keys = new Set([...Object.keys(previous), ...Object.keys(next)]) as Set<keyof T>\n return [...keys].filter((key) => !deepEqual(previous[key], next[key]))\n}\n","import * as THREE from 'three'\nimport { PageContent, sourceKey, VISUALLY_HIDDEN } from './content.js'\nimport { rayThrough, type Engine } from './engine.js'\nimport { fit, framingFor, plainFramingFor, type Framing, type Rect } from './framing.js'\nimport { bindKeyboard, pageDetail, pagesAt, pageToPosition, Riffle } from './navigation.js'\nimport {\n changedOptions,\n DEFAULT_SPREAD_QUERY,\n defaultPageLabel,\n LIVE_OPTIONS,\n pageImage,\n preloadRadius,\n resolveSettings,\n snapshot,\n type LeafThroughOptions,\n type Layout,\n type PageChangeDetail,\n type ResolvedSettings,\n} from './options.js'\nimport { colorKey, PaperTextures } from './paper/paperTextures.js'\nimport { PlainEngine } from './plain/PlainEngine.js'\nimport { SingleEngine } from './single/SingleEngine.js'\nimport { SpreadEngine } from './spread/SpreadEngine.js'\nimport { 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\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 paper: PaperTextures\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 private readonly riffle = new Riffle()\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.paper = new PaperTextures(options.paper?.backColor ?? '#fcfcf9', {\n onGrain: () => this.remount(),\n onError: (error) => this.fail(error),\n })\n\n const signal = this.abort.signal\n this.watchSpreadQuery()\n this.motionMedia = window.matchMedia('(prefers-reduced-motion: reduce)')\n this.motionMedia.addEventListener('change', () => this.remount(), { signal })\n\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.riffle.stop()\n return this.view?.engine.flip(true) ?? false\n }\n\n /** Turns to the previous page. Returns false at the start. */\n prev(): boolean {\n this.riffle.stop()\n return this.view?.engine.flip(false) ?? false\n }\n\n /** Shows `page` (0-based), jumping there or turning the pages one by one. */\n goTo(page: number, { animate = false }: GoToOptions = {}) {\n if (!this.view || Number.isNaN(page)) return\n this.riffle.stop()\n const engine = this.view.engine\n const target = 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.start(target, this.view.settings.motion.speed)\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 changed = changedOptions(previous, next)\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_OPTIONS.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(preloadRadius(this.options.preload))\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.riffle.stop()\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.paper.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.riffle.stop()\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 origin: { value: new THREE.Vector3() },\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 blank = this.paper.blank\n const textures = this.options.pages.map((_, i) => this.loader?.textures[i] ?? blank)\n const ctx = {\n input: {\n element: this.hitArea,\n ray: (x: number, y: number, ray: THREE.Ray) => rayThrough(camera, this.canvas, x, y, ray),\n // the hit area itself covers just the pages\n covers: () => true,\n },\n settings,\n textures,\n blank: layout === 'single' ? this.paper.back(settings.paper.backColor) : blank,\n pattern: this.paper.grain(settings, renderer),\n position: this.pageToPosition(page, layout),\n metrics: this.metrics,\n shading,\n // the loop draws every frame the engine asks for while the magazine is on screen\n requestFrame: () => {},\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 // 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 loadPages(renderer: THREE.WebGLRenderer, around: number, layout: Layout) {\n this.loader?.dispose()\n const loader = new PageLoader(this.options.pages.map(pageImage), renderer, preloadRadius(this.options.preload), {\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.paper.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 return pageToPosition(page, layout, this.options.pages.length)\n }\n\n private pagesAt(position: number, layout: Layout) {\n return pagesAt(position, layout, this.options.pages.length)\n }\n\n private detail(): PageChangeDetail {\n const layout = this.view?.settings.layout ?? this.pickLayout()\n return pageDetail(this.view?.engine.position ?? 0, layout, this.options.pages.length)\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 bindKeyboard(\n this.element,\n () => this.view?.settings.interaction.keyboard,\n { next: () => this.next(), prev: () => this.prev(), first: () => this.goTo(0), last: () => this.goTo(this.pageCount - 1) },\n this.abort.signal,\n )\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.riffle.step(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\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","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 * as THREE from 'three'\nimport type { ResolvedSettings } from './options.js'\nimport type { SheetShading } from './paper/paperMaterial.js'\n\nexport interface EngineContext {\n input: EngineInput\n settings: ResolvedSettings\n /** One texture per page, in reading order. */\n textures: THREE.Texture[]\n /** Plain paper, for missing or unprinted sides. */\n blank: THREE.Texture\n pattern: THREE.Texture\n /** Where the magazine opens, in the engine's own position units. */\n position: number\n /** Kept current by the controller as the magazine resizes. */\n metrics: EngineMetrics\n /** Shared by every sheet; the engine keeps spineContact current. */\n shading: SheetShading\n /** Asks for a frame: input and timers change the pages between frames. */\n requestFrame: () => void\n}\n\n/** Where pointer input comes from, and how a point on screen reaches the magazine. */\nexport interface EngineInput {\n /** Receives pointer input. */\n element: HTMLElement\n /** Sets `ray` to the world-space ray through a point on screen; false when there's none. */\n ray(clientX: number, clientY: number, ray: THREE.Ray): boolean\n /** Whether a press at this point lands on the magazine. */\n covers(clientX: number, clientY: number): boolean\n}\n\nexport interface EngineMetrics {\n /** Pixels a drag spans per unit of `dragDistance`. */\n readonly dragWidth: number\n /** Screen displacement along the magazine’s projected x axis; defaults to horizontal. */\n dragDelta?(dx: number, dy: number): number\n}\n\nexport interface Engine {\n readonly group: THREE.Group\n /** Spread: sheets turned to the left. Single: pages turned. */\n readonly position: number\n readonly maxPosition: number\n /** Whether a page is being dragged or is still completing a turn. */\n readonly turning: boolean\n /** Which part of the pages takes input: a closed spread only turns from its cover side. */\n readonly activeSide: 'both' | 'right' | 'left'\n /**\n * The pages that can be seen right now, as a range: the ones at rest and any mid-turn. A\n * quick riffle leaves pages turning well behind the position, and they mustn't unload.\n */\n pagesInView(): { from: number; to: number }\n /** Advances the simulation; returns true when the frame needs drawing. */\n update(now: number): boolean\n /** Turns the top page one way; false when there's nothing to turn. */\n flip(forward: boolean): boolean\n /** Moves to a position at once, without animating. */\n jumpTo(position: number): void\n setPageTexture(page: number, texture: THREE.Texture): void\n /** Turns the sheets' shadows on or off, both casting and receiving, without a rebuild. */\n setShadows?(on: boolean): void\n /** Releases pointer capture and cancels active gestures and hold timers. */\n cancelInput?(): void\n dispose(): void\n}\n\nconst floor = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0)\nconst inverse = new THREE.Matrix4()\nconst localRay = new THREE.Ray()\n\n/**\n * Where a world-space ray meets the floor of `object` (its z = 0 plane), in the object's own\n * units; null when it misses. However the object is moved, turned or scaled.\n */\nexport function floorPoint(object: THREE.Object3D, ray: THREE.Ray, target: THREE.Vector3): THREE.Vector3 | null {\n object.updateWorldMatrix(true, false)\n localRay.copy(ray).applyMatrix4(inverse.copy(object.matrixWorld).invert())\n return localRay.intersectPlane(floor, target)\n}\n\nconst raycaster = new THREE.Raycaster()\nconst ndc = new THREE.Vector2()\n\n/** Sets `ray` to the ray from `camera` through a point on screen over `element`, which shows its view. */\nexport function rayThrough(camera: THREE.Camera, element: Element, clientX: number, clientY: number, ray: THREE.Ray): boolean {\n const rect = element.getBoundingClientRect()\n if (!rect.width || !rect.height) return false\n ndc.set(((clientX - rect.left) / rect.width) * 2 - 1, -((clientY - rect.top) / rect.height) * 2 + 1)\n camera.updateWorldMatrix(true, false)\n raycaster.setFromCamera(ndc, camera)\n ray.copy(raycaster.ray)\n return true\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 { Engine } from './engine.js'\nimport type { InteractionOptions, Layout, PageChangeDetail } from './options.js'\n\n// --- pages ↔ engine positions -------------------------------------------------------\n\nexport function pageToPosition(page: number, layout: Layout, count: number) {\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\nexport function 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. */\nexport function pagesAt(position: number, layout: Layout, count: number) {\n const candidates = layout === 'spread' ? [2 * position - 1, 2 * position] : [position]\n return candidates.filter((p) => p >= 0 && p < count)\n}\n\nexport function pageDetail(position: number, layout: Layout, count: number): PageChangeDetail {\n const visiblePages = pagesAt(position, layout, count)\n const page = visiblePages.length ? visiblePages[0] : Math.max(0, Math.min(positionToPage(position, layout), count - 1))\n return { page, visiblePages, pageCount: count, layout }\n}\n\n// --- an animated goTo() ----------------------------------------------------------\n\n/** Turns one page every `interval` ms until the engine reaches `target`. */\nexport class Riffle {\n private state: { target: number; interval: number; last: number } | null = null\n\n get active() {\n return this.state !== null\n }\n\n start(target: number, speed: number) {\n this.state = { target, interval: 140 / speed, last: -Infinity }\n }\n\n stop() {\n this.state = null\n }\n\n step(now: number, engine: Engine) {\n const riffle = this.state\n if (!riffle || now - riffle.last < riffle.interval) return\n if (engine.position === riffle.target) {\n this.state = null\n return\n }\n riffle.last = now\n if (!engine.flip(engine.position < riffle.target)) this.state = null\n }\n}\n\n// --- keyboard -------------------------------------------------------------------\n\nexport interface KeyboardActions {\n next(): unknown\n prev(): unknown\n first(): unknown\n last(): unknown\n}\n\n/**\n * Arrow keys, PageUp/PageDown and Home/End. With `true` they work while `element` has focus;\n * with `'global'` anywhere on the page, except in text fields.\n */\nexport function bindKeyboard(\n element: HTMLElement,\n mode: () => InteractionOptions['keyboard'] | undefined,\n actions: KeyboardActions,\n signal: AbortSignal,\n) {\n const handler = (e: KeyboardEvent) => {\n const current = mode()\n if (!current || e.defaultPrevented || e.altKey || e.ctrlKey || e.metaKey) return\n if (current === '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 !== element) return\n const keys: Record<string, () => unknown> = {\n ArrowRight: actions.next,\n PageDown: actions.next,\n ArrowLeft: actions.prev,\n PageUp: actions.prev,\n Home: actions.first,\n End: actions.last,\n }\n const action = keys[e.key]\n if (!action) return\n e.preventDefault()\n action()\n }\n element.addEventListener('keydown', handler, { signal })\n window.addEventListener(\n 'keydown',\n (e) => {\n if (mode() === 'global' && !element.contains(e.target as Node)) handler(e)\n },\n { signal },\n )\n}\n","import * as THREE from 'three'\nimport type { ResolvedSettings } from '../options.js'\nimport { loadGrain } from '../textures.js'\nimport { solidTexture } from './paperMaterial.js'\nimport { createPaperPattern } from './paperPattern.js'\n\nexport interface PaperTextureEvents {\n /** A custom grain image arrived; sheets made before it show none. */\n onGrain(): void\n onError(error: unknown): void\n}\n\n/** Plain paper and the grain, kept across rebuilds and made again only when their options change. */\nexport class PaperTextures {\n /** Plain paper, for missing pages and the spread's unprinted sides. */\n readonly blank = solidTexture('#fcfcf9')\n backBlank: { texture: THREE.DataTexture; color: string }\n private pattern: { texture: THREE.Texture; key: unknown } | null = null\n private disposed = false\n\n constructor(backColor: THREE.ColorRepresentation, private readonly events: PaperTextureEvents) {\n this.backBlank = { texture: solidTexture(backColor), color: colorKey(backColor) }\n }\n\n /** The blank backs of single-layout pages, in `paper.backColor`. */\n back(color: THREE.ColorRepresentation): THREE.Texture {\n const key = colorKey(color)\n if (key !== this.backBlank.color) {\n this.backBlank.texture.dispose()\n this.backBlank = { texture: solidTexture(color), color: key }\n }\n return this.backBlank.texture\n }\n\n grain(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.events.onGrain()\n },\n (error) => {\n if (this.pattern?.key === key && !this.disposed) this.events.onError(error)\n },\n )\n }\n this.pattern = { texture, key }\n return texture\n }\n\n dispose() {\n this.disposed = true\n this.pattern?.texture.dispose()\n this.blank.dispose()\n this.backBlank.texture.dispose()\n }\n}\n\nexport function colorKey(color: THREE.ColorRepresentation) {\n return new THREE.Color(color).getHexString()\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","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 /** Where the sheets' group sits in the magazine's frame; the engine keeps it current. */\n origin: 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 // Without a deform the shaders are just as they'd be without the option.\n const deform = settings.deform\n const deformChunk = deform\n ? `#include <common>\n uniform vec3 uSheetOrigin;\n ${deform.glsl}\n vec3 sheetDeformed(vec3 p, vec2 uv) {\n return deformSheet(p + uSheetOrigin, uv) - uSheetOrigin;\n }`\n : '#include <common>'\n const deformed = (p: string, uv: string) => (deform ? `sheetDeformed(${p}, ${uv})` : p)\n const deformUniforms = deform ? { ...deform.uniforms, uSheetOrigin: uniforms.uSheetOrigin } : {}\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, deformUniforms)\n\n program.vertexShader =\n constants + shader.vertexChunk + 'varying vec2 vSheetUv;\\nvarying vec3 vSheetPosition;\\n' + program.vertexShader\n // Normals come from the drawn (deformed) shape; the fragment shader's tests work on the\n // magazine's own shape, before any deform.\n program.vertexShader = program.vertexShader\n .replace('#include <common>', deformChunk)\n .replace(\n '#include <beginnormal_vertex>',\n `#include <beginnormal_vertex>\n vec2 sheetStep = vec2(NORMAL_EPSILON, NORMAL_EPSILON / SHEET_ASPECT);\n vec3 sheetShaped = sheetPosition(uv, ${noiseAt('x')});\n vec3 sheetP = ${deformed('sheetShaped', 'uv')};\n vec3 sheetPx = ${deformed(`sheetPosition(uv + vec2(sheetStep.x, 0.0), ${noiseAt('y')})`, 'uv + vec2(sheetStep.x, 0.0)')};\n vec3 sheetPy = ${deformed(`sheetPosition(uv + vec2(0.0, sheetStep.y), ${noiseAt('z')})`, 'uv + vec2(0.0, sheetStep.y)')};\n objectNormal = normalize(cross(sheetPx - sheetP, sheetPy - sheetP));`,\n )\n .replace(\n '#include <begin_vertex>',\n `vec3 transformed = sheetP;\n vSheetUv = uv;\n vSheetPosition = sheetShaped;`,\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 ${\n paper.cutout\n ? `// Cut to the front page: no paper where it's transparent. The edge that's left\n // keeps its own colour (page images are premultiplied, so divide the alpha out) rather\n // than fading to paper, which would leave a pale fringe.\n if (frontColor.a < 0.5) discard;\n frontColor = vec4(frontColor.rgb / frontColor.a, 1.0);\n backColor = backColor.a > 0.01 ? vec4(backColor.rgb / backColor.a, 1.0) : vec4(BLANK_PAPER, 1.0);`\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 }\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 const distanceMaterial = new THREE.MeshDistanceMaterial({ side: THREE.DoubleSide })\n // Point lights use a distance pass; directional and spot lights use a depth pass.\n // Both must draw precisely the same deformed, trimmed silhouette as the visible sheet.\n depthMaterial.onBeforeCompile = distanceMaterial.onBeforeCompile = (program) => {\n Object.assign(program.uniforms, uniforms, deformUniforms)\n program.vertexShader = constants + shader.vertexChunk + 'varying vec2 vSheetUv;\\nvarying vec3 vSheetPosition;\\n' + program.vertexShader\n program.vertexShader = program.vertexShader.replace('#include <common>', deformChunk).replace(\n '#include <begin_vertex>',\n `vSheetUv = uv;\n vSheetPosition = sheetPosition(uv, ${noiseAt('x')});\n vec3 transformed = ${deformed('vSheetPosition - vec3(SHADOW_OFFSET_X, 0.0, 0.0)', 'uv')};`,\n )\n program.fragmentShader = constants + `\n uniform sampler2D uFront;\n varying vec2 vSheetUv;\n varying vec3 vSheetPosition;\n ${shader.fragmentChunk ?? ''}\n ` + program.fragmentShader\n program.fragmentShader = program.fragmentShader.replace(\n '#include <clipping_planes_fragment>',\n `#include <clipping_planes_fragment>\n ${paper.cutout ? 'if (texture2D(uFront, vSheetUv).a < 0.5) discard;' : ''}\n ${shader.fragmentChunk ? 'if (sheetHidden(vSheetPosition)) discard;' : ''}`,\n )\n }\n\n // three.js reuses a compiled program whose cache key matches, and the default key is the\n // onBeforeCompile source, the same for every sheet. The generated code is what differs (layout,\n // page aspect, paper), so it is the key: magazines sharing a renderer each get their own.\n const cacheKey =\n constants + shader.vertexChunk + (shader.fragmentChunk ?? '') + (paper.cutout ? 'cutout' : '') + (deform?.glsl ?? '')\n material.customProgramCacheKey = () => cacheKey\n depthMaterial.customProgramCacheKey = () => cacheKey\n distanceMaterial.customProgramCacheKey = () => cacheKey\n\n return { material, depthMaterial, distanceMaterial }\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, distanceMaterial } = createPaperMaterials(uniforms, shader, settings)\n const mesh = new THREE.Mesh(geometry, material)\n mesh.customDepthMaterial = depthMaterial\n mesh.customDistanceMaterial = distanceMaterial\n mesh.castShadow = settings.lighting.shadows\n mesh.receiveShadow = settings.lighting.shadows\n mesh.frustumCulled = false\n return mesh\n}\n\n/**\n * Without a sheet gap every sheet rises from the spine at height 0, so along the spine they\n * all but touch and the depth buffer can't separate them: sheets further down flicker through\n * as a light seam. Bias each sheet's depth by its place in the stack instead: the top sheet\n * wins, and a sheet that's turning wins over everything. `bias` false leaves depth alone.\n */\nexport function layerSheet(mesh: SheetMesh, depth: number, count: number, moving: boolean, bias: boolean) {\n const material = mesh.material\n material.polygonOffset = bias\n material.polygonOffsetFactor = 0\n material.polygonOffsetUnits = bias ? (moving ? -(count + 4) : -(count - Math.min(depth, count))) : 0\n mesh.renderOrder = depth\n}\n\nexport function disposeSheetMesh(mesh: SheetMesh) {\n mesh.material.dispose()\n mesh.customDepthMaterial?.dispose()\n mesh.customDistanceMaterial?.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 { floorPoint, type Engine, type EngineContext, type EngineInput, type 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 input: EngineInput\n private readonly metrics: EngineMetrics\n private readonly requestFrame: () => void\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 private readonly shading: EngineContext['shading']\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 ray = new THREE.Ray()\n private readonly hit = 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.input = ctx.input\n this.metrics = ctx.metrics\n this.requestFrame = ctx.requestFrame\n this.shading = ctx.shading\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.wake()\n }\n\n cancelInput() {\n this.cancelHold()\n this.touch = null\n if (this.press) this.release(new PointerEvent('pointercancel', { pointerId: this.press.id }))\n this.wake()\n }\n\n setShadows(on: boolean) {\n this.settings.lighting.shadows = on\n for (const sheet of this.sheets) sheet.mesh.castShadow = sheet.mesh.receiveShadow = on\n this.wake()\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 // something changed between frames\n private wake() {\n this.dirty = true\n this.requestFrame()\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 pointAt(clientX: number, clientY: number) {\n if (!this.input.ray(clientX, clientY, this.ray)) return null\n return floorPoint(this.group, this.ray, this.hit)\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 here would pick up: right of the spine turns forward\n private sheetAt(point: { x: number } | null) {\n if (!point) return null\n const forward = point.x >= 0\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.wake()\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.wake()\n }\n\n // --- input ------------------------------------------------------------------\n\n private bindInput() {\n const el = this.input.element\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 = this.metrics.dragDelta?.(e.clientX - this.press.x, e.clientY - this.press.y) ?? 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.pointAt(e.clientX, e.clientY), false)\n this.wake()\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 if (!this.input.covers(e.clientX, e.clientY)) return\n const point = this.pointAt(e.clientX, e.clientY)\n const target = this.sheetAt(point)\n if (!target) return\n const sheet = target.sheet\n if (!this.inFlight(sheet)) sheet.wobble = this.randomWobble()\n this.grab(sheet, point, 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.wake()\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 const single = e.touches.length === 1 && this.input.covers(t.clientX, t.clientY)\n this.touch = single ? { 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.pointAt(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.input.element.releasePointerCapture(e.pointerId)\n } catch {}\n this.wake()\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 this.wake()\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 const depth = i < this.current ? this.current - 1 - i : i - this.current\n layerSheet(sheet.mesh, depth, this.count, this.busy(sheet), this.settings.paper.depthBias)\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 this.shading.origin.value.copy(this.group.position)\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 // Each sheet stands off the spine along its own normal, so the offset turns with it: by the\n // sheets under it while it's on the pile, by the read pages that went round before it once\n // it's rolled up (the older, the further out, like their rolls). A page going round moves\n // from one to the other as it turns, and the pages before it make room. Settled flat under\n // the pile they keep the height they came round at, and all go back to their place in the\n // closed magazine as it shuts.\n const under = P - 1 - sheet.index\n const before = Math.max(0, turned - 1 - sheet.index)\n const around = smooth(T)\n const offset = fallen ? before * Math.cos(spineOpen) : under + (before - under) * around\n u.uStackGap.value = this.settings.paper.sheetGap * (offset + (under - offset) * shut)\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 uniform float uStackGap; // how far the sheet stands off the spine, along its own normal\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 + uStackGap;\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 uStackGap: 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 uSheetOrigin: 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 uStackGap: { value: 0 },\n uFront: { value: front },\n uBack: { value: back },\n uPattern: { value: pattern },\n uSpineContact: shading.spineContact,\n uLightDirection: shading.lightDirection,\n uSheetOrigin: shading.origin,\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 { floorPoint, type Engine, type EngineContext, type EngineInput, type 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// How many sheets lie under this one: those after it on the right, those before it on the\n// left. A turning sheet keeps the height of the pile it leans over, so it stays above every\n// sheet there, and changes over while it stands upright, where a height change only slides\n// it along itself.\nfunction sheetsUnder(index: number, count: number, progress: number) {\n const t = clamp((progress - 0.25) / 0.5, 0, 1)\n return count - 1 - index + (2 * index - count + 1) * t * t * (3 - 2 * t)\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 input: EngineInput\n private readonly metrics: EngineMetrics\n private readonly requestFrame: () => void\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 ray = new THREE.Ray()\n private readonly hit = new THREE.Vector3()\n\n constructor(ctx: EngineContext) {\n this.settings = ctx.settings\n this.halfHeight = ctx.settings.pageAspect / 2\n this.input = ctx.input\n this.metrics = ctx.metrics\n this.requestFrame = ctx.requestFrame\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.wake()\n }\n\n cancelInput() {\n this.cancelHold()\n this.hover.active = false\n if (this.press) this.release(new PointerEvent('pointercancel', { pointerId: this.press.id }))\n this.wake()\n }\n\n setShadows(on: boolean) {\n this.settings.lighting.shadows = on\n for (const sheet of this.sheets) sheet.mesh.receiveShadow = on\n this.wake()\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 // something changed between frames\n private wake() {\n this.dirty = true\n this.requestFrame()\n }\n\n // the point on the floor under the pointer, the spine at x = 0\n private pointAt(clientX: number, clientY: number) {\n if (!this.input.ray(clientX, clientY, this.ray)) return null\n return floorPoint(this.group, this.ray, this.hit)\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 here would pick up\n private sheetAt(clientX: number, clientY: number) {\n const point = this.pointAt(clientX, clientY)\n if (!point) return null\n const forward = point.x >= 0\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 hit = this.pointAt(clientX, clientY)\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.wake()\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.wake()\n }\n\n // --- input ------------------------------------------------------------------\n\n private bindInput() {\n const el = this.input.element\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 = this.metrics.dragDelta?.(e.clientX - this.press.x, e.clientY - this.press.y) ?? 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: this.input.covers(e.clientX, e.clientY), x: e.clientX, y: e.clientY }\n }\n this.wake()\n },\n { signal },\n )\n\n el.addEventListener(\n 'pointerleave',\n () => {\n this.hover.active = false\n this.wake()\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 if (!this.input.covers(e.clientX, e.clientY)) return\n const target = this.sheetAt(e.clientX, e.clientY)\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.wake()\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.input.element.releasePointerCapture(e.pointerId)\n } catch {}\n this.wake()\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, this.hover.y) : 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, this.settings.paper.depthBias)\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.uStackGap.value = this.settings.paper.sheetGap * sheetsUnder(sheet.index, this.sheets.length, 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 const bool NESTED = ${settings.paper.depthBias ? 'false' : 'true'};\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 uniform float uStackGap;\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. Where a wrinkle dips, a\n // bigger one dips below the sheets under it, which only the depth bias hides; without it\n // the dips are the same on every sheet.\n float wrinkle = uWrinkleSide * (mix(noise, 1.0 - noise, uFlipProgress) - 0.5) * smoothstep(0.0, 0.35, x) * WRINKLE;\n wrinkle *= NESTED && wrinkle < 0.0 ? 1.0 : 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 + wrinkle + uStackGap;\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 uStackGap: 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 uSheetOrigin: 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 uStackGap: { value: 0 },\n uFront: { value: front },\n uBack: { value: back },\n uPattern: { value: pattern },\n uSpineContact: shading.spineContact,\n uLightDirection: shading.lightDirection,\n uSheetOrigin: shading.origin,\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"],"mappings":"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","names":["spread","THREE","spread","THREE","box","view","THREE","THREE","THREE","THREE","THREE","THREE","THREE","THREE","spread","THREE","THREE","NORMAL_EPSILON","vertexChunk","hash","HOLD","DRAG","CLICK","clamp","floor"]}
|