paperlab 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stage/PaperStage.tsx","../src/stage/camera.ts","../src/stage/Figure.tsx","../src/stage/RiggedFigure.tsx","../src/stage/gait.ts","../src/stage/Surround.tsx","../src/stage/Room.tsx","../src/stage/Suspension.tsx","../src/stage/Grade.tsx","../src/stage/schema.ts","../src/stage/navigate.ts","../src/stage/useWalk.ts","../src/stage/quality.ts","../src/stage/walks.ts","../src/stage/presets.ts","../src/stage/export.ts"],"sourcesContent":["import * as THREE from 'three'\nimport { Canvas, useFrame, useThree } from '@react-three/fiber'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { z } from 'zod'\nimport { usePrefersReducedMotion } from '../a11y'\nimport type { ContentConfigInput, PaperConfigInput } from '../config/schema'\nimport { PaperFieldMesh } from '../PaperField'\nimport { resolveConfig } from '../PaperMesh'\nimport type { FieldPaperSlot } from '../field/slots'\nimport { getLayout } from '../field/layouts'\nimport { PaperLighting } from '../scene/PaperLighting'\nimport { resolveLighting } from '../scene/lighting'\nimport { LightRig } from '../scene/rig'\nimport { getWalkPath } from './path'\nimport { stageCamera, walkPoint } from './camera'\nimport { Figure } from './Figure'\nimport { Source, Surround } from './Surround'\nimport { Ceiling, Columns, Doorway, Floor } from './Room'\nimport { Suspension } from './Suspension'\nimport { Grade } from './Grade'\nimport { stageSchema, type StageConfig, type StageConfigInput } from './schema'\nimport { stageMotionSchema, type StageMotionInput } from './navigate'\nimport { useWalk } from './useWalk'\nimport {\n FIRST_WINDOW,\n INITIAL_TIER,\n SETTLE_FRAMES,\n STEADY_WINDOW,\n qualityFor,\n qualityTiers,\n settleTier,\n type QualityName,\n type QualityTier,\n} from './quality'\n\n/**\n * Stage mode: paper as architecture, with a figure walking through it.\n *\n * The one guarantee worth stating — every part of the scene reads the SAME\n * walk. The layout arranges along it, the figure follows it, the camera is\n * stationed on it, and the source stands at the end of it. Handing those\n * four their own copies of a path is the failure this component exists to\n * prevent: a colonnade whose aisle the figure does not walk down is not a\n * near-miss, it is a completely different picture.\n */\n\n/** A banner: tall, translucent, with folds running the length of its drop. */\nconst BANNER: PaperConfigInput = {\n sheet: { width: 1.5, height: 8.5, segments: 'auto' },\n stock: 'vellum',\n surface: { grain: 0.22 },\n deformers: [{ type: 'drape', options: { amplitude: 0.16, folds: 3, falloff: 1.7, gather: 0.28 } }],\n}\n\nexport interface PaperStageSceneProps {\n /** Walk, shot, figure, lighting — see `stageSchema`. */\n stage?: StageConfigInput\n /** Any layout, but `colonnade` is the one built to arrange along a walk. */\n layout?: string\n layoutOptions?: Record<string, unknown>\n /** Per-banner slots, exactly as in field mode. */\n papers?: FieldPaperSlot[]\n images?: string[]\n /**\n * Words on the banners. A string is split across them a line at a time; an\n * array is used as given. This is the whole point of the mode — a space\n * built out of something the viewer wrote.\n */\n text?: string | string[]\n /** Shared preset behind every banner. */\n preset?: string | PaperConfigInput\n /** How many banners, when none of `papers` / `images` / `text` says. */\n count?: number\n /**\n * How far along the walk the figure is, 0..1. Bind it to scroll and the\n * page scrolls the walk. Omit and `motion` decides who drives.\n *\n * Supplying it makes the stage a CONTROLLED component and outranks\n * `motion` entirely — a driver and a page both writing the same number is\n * a fight, not a feature.\n */\n progress?: number\n /**\n * Who drives the walk when `progress` does not: `drag` hands it to the\n * viewer (pointer, wheel, arrow keys, clicking a paper), `autoplay` to the\n * clock, `none` to nobody. Same contract as a field's `motion`.\n */\n motion?: StageMotionInput\n /** Fires when the viewer moves to a paper — by clicking it, or by stepping onto it. */\n onVisit?(paper: number): void\n /**\n * The live position on the walk, 0..1, every frame it changes — whoever is\n * driving. Mirror it into an uncontrolled input to show a scrubber that\n * follows the walk without re-rendering the scene sixty times a second;\n * `<PaperMesh>`'s `onProgress` is the same affordance for a behavior.\n */\n onProgress?(walk: number): void\n reducedMotion?: boolean\n /**\n * How much the render is allowed to cost. `auto` (the default) starts in\n * the middle and adapts to whatever the machine turns out to manage — this\n * scene runs on hardware nobody developing it owns. Not part of the stage\n * config: quality describes the DEVICE, not the artwork, so it must never\n * travel in a preset or a shared link.\n */\n quality?: QualityName\n /**\n * Fires when `auto` moves the tier. Useful for showing the viewer what\n * they are getting, and for measuring what real machines settle on.\n */\n onQualityChange?(tier: QualityTier): void\n}\n\nexport interface PaperStageProps extends PaperStageSceneProps {\n children?: React.ReactNode\n className?: string\n style?: React.CSSProperties\n}\n\n/**\n * Split a paragraph across banners, and stack each banner's share DOWN its\n * drop rather than across its width.\n *\n * A banner is roughly six times taller than it is wide, so a line of prose\n * set across it wraps to nothing and leaves the other 90% of the paper\n * blank. Every reference image runs its text as a vertical column, which is\n * both what the shape wants and what makes the paper read as printed rather\n * than as a rectangle with a caption.\n */\nexport function splitAcrossBanners(text: string, banners: number): string[] {\n const words = text.split(/\\s+/).filter(Boolean)\n if (words.length === 0 || banners <= 0) return []\n // Deal the words out rather than slicing at a fixed stride. `ceil` and a\n // stride left the remainder on the floor: twenty words across twelve\n // banners chunked by two produced TEN columns, and the last two banners\n // hung blank in a stage that had asked for twelve. Dealing gives every\n // banner a share and puts the odd words at the front, where a column one\n // word longer than its neighbour reads as prose rather than as a mistake.\n const each = Math.floor(words.length / banners)\n const extra = words.length % banners\n const out: string[] = []\n let at = 0\n for (let i = 0; i < banners && at < words.length; i++) {\n const take = each + (i < extra ? 1 : 0)\n if (take === 0) break\n out.push(words.slice(at, at + take).join('\\n'))\n at += take\n }\n return out\n}\n\n/**\n * Set a single word down the drop, one letter to a line.\n *\n * This is what the reference installations do, and it is what the stage was\n * accidentally almost doing. Every built-in stage has FEWER words than\n * banners — a nave of eighteen carries fifteen — so nearly every column is\n * one word, and a one-word column asked for 150px type that no banner is\n * wide enough to hold. `wrapLines` then broke it wherever the measure ran\n * out, which turned \"carried\" into `ca / rr / ie / d`: vertical, full-drop,\n * and unreadable as a word, because the break points were an accident of\n * arithmetic rather than a decision.\n *\n * Breaking on purpose fixes both halves. One letter a line is legible as\n * vertical setting, and it is the line COUNT that then sizes the type, so a\n * long word gets small letters and a short word gets big ones — which is\n * what makes a rank of banners look set rather than scaled.\n */\nexport function letterColumn(word: string): string {\n return [...word].join('\\n')\n}\n\n/**\n * Type size for a banner carrying `lines` stacked lines — chosen to FILL the\n * drop, because a column of type that stops a third of the way down reads as\n * a mistake rather than as a design.\n *\n * The banner's texture is 1024px on its long edge and the column is set at\n * 1.25 line-height inside a 6% margin, so `lines × size × 1.25 ≈ 900` is the\n * size that lands the last line at the bottom of the paper. Clamped at both\n * ends: two words on an eight-metre drop should be enormous, but not so\n * enormous they crop, and a dense column still has to stay legible.\n *\n * **The drop is only half the constraint, and leaving the other half out was\n * a real bug.** A banner is also NARROW, and the width was never consulted.\n * On the ribbon stage — a 1.05 × 9 strip, so about 105px of measure once the\n * margins are off — a two-word column asked for 150px type, every word came\n * out wider than the sheet, `wrapLines` broke each one to a letter a line,\n * and the column then overran the drop and was silently clipped. The frame\n * showed one enormous letter per strip. So `measure` caps the size at\n * something the longest word can actually sit on.\n *\n * The character estimate is exactly that — an estimate. Real advance widths\n * need a canvas, which is not available where this is decided, so 0.62em is\n * used as a deliberately generous average for a mixed-case serif: erring\n * high makes the type a little small, and erring low brings back the letter\n * a line. `wrapLines` is still the backstop for a word no size can fit.\n */\nexport function bannerTextSize(lines: number, longestWord = 0, measure = Number.POSITIVE_INFINITY): number {\n const byDrop = 720 / Math.max(lines, 1)\n const byMeasure = longestWord > 0 ? measure / (longestWord * 0.62) : Number.POSITIVE_INFINITY\n // Floor, not round: rounding UP is how a size that was computed to fit\n // stops fitting, and half a pixel of type is worth nobody's attention.\n return Math.floor(Math.min(150, Math.max(26, Math.min(byDrop, byMeasure))))\n}\n\n/** The inset the banner column is set inside, on both the sizer and the painter. */\nconst PADDING = 0.06\n\n/**\n * The usable width of a banner's texture, in the units `content.size` is in.\n *\n * Both numbers here are facts about how content is painted, not choices:\n * the canvas is `LONG_EDGE` on its long side, and `paintText` insets by\n * `padding` of the SHORT side. Stated once so the type sizer and the painter\n * cannot drift apart about how much room there is.\n */\nexport function bannerMeasure(sheet: { width: number; height: number }, padding = PADDING): number {\n const long = Math.max(sheet.width, sheet.height)\n if (!(long > 0)) return Number.POSITIVE_INFINITY\n return (sheet.width / long) * 1024 * (1 - padding * 2)\n}\n\n/**\n * The walk drives the camera; nothing else is allowed to move it.\n *\n * Including the viewer. Dragging moves you ALONG the walk — it does not orbit\n * and it does not look around, because a camera the viewer can aim is a\n * camera that can be aimed at the back of the room, and this mode is a\n * composed shot rather than a scene you inspect.\n */\nfunction ShotRig({\n stage,\n paperHeight,\n walk,\n}: {\n stage: StageConfig\n paperHeight: number\n /** Normalized position on the walk, live — the scene's one clock. */\n walk: React.RefObject<number>\n}) {\n const camera = useThree((s) => s.camera)\n const path = useMemo(() => getWalkPath(stage.path), [stage.path])\n const scale = useMemo(\n () => ({ figure: stage.figure.height, paper: paperHeight }),\n [stage.figure.height, paperHeight],\n )\n\n useFrame(() => {\n const { position, target } = stageCamera(path, walk.current * path.length, scale, stage.shot)\n camera.position.set(position[0], position[1], position[2])\n camera.lookAt(target[0], target[1], target[2])\n })\n return null\n}\n\n/**\n * Watches the real frame rate and moves the tier.\n *\n * Deliberately hysteretic and slow: the two thresholds are far apart and\n * there is a settling period after every change, because a monitor that\n * reacts fast oscillates — dropping quality raises the frame rate, which\n * immediately argues for raising quality again, and the scene visibly\n * pumps. A machine that cannot hold the floor should sink once and stay.\n *\n * \"And stay\" is now enforced rather than hoped for. **A tier that has\n * already failed is never offered again**, because the thresholds alone\n * cannot prevent the pump: promotion needs 55 fps and demotion needs 26, so\n * any machine where the next tier up costs more than about 2.1× the current\n * one can satisfy both forever, rising until it stalls and sinking until it\n * is comfortable. That ratio is not hypothetical — `high` runs 2.1× the cost\n * of `medium` on a software rasterizer, which is exactly the machine this\n * watcher exists for. One latch, and the ladder can only ever settle.\n */\nfunction QualityWatch({ tier, onChange }: { tier: QualityTier; onChange: (tier: QualityTier) => void }) {\n const samples = useRef<number[]>([])\n const settle = useRef(SETTLE_FRAMES)\n // The first verdict comes quickly; later ones are measured carefully.\n const window = useRef(FIRST_WINDOW)\n /** The lowest tier that has already proved too expensive here. */\n const failed = useRef<QualityTier | null>(null)\n\n const settled = useCallback((next: QualityTier) => {\n samples.current = []\n settle.current = SETTLE_FRAMES\n window.current = STEADY_WINDOW\n return next\n }, [])\n\n useFrame((_, delta) => {\n if (settle.current > 0) {\n settle.current -= 1\n return\n }\n // A tab returning from the background delivers one enormous delta;\n // it says nothing about the hardware.\n if (delta > 0.5) return\n samples.current.push(delta)\n if (samples.current.length < window.current) return\n\n const sorted = [...samples.current].sort((a, b) => a - b)\n const median = sorted[Math.floor(sorted.length / 2)]!\n const fps = 1 / median\n samples.current = []\n // Even if the tier does not move, stop judging on the short window.\n window.current = STEADY_WINDOW\n\n const verdict = settleTier(tier, fps, failed.current)\n failed.current = verdict.failed\n if (verdict.tier !== tier) onChange(settled(verdict.tier))\n })\n return null\n}\n\nexport function PaperStageScene({\n stage: stageInput,\n quality = 'auto',\n onQualityChange,\n layout = 'colonnade',\n layoutOptions,\n papers,\n images,\n text,\n preset,\n count = 22,\n progress,\n motion,\n onVisit,\n onProgress,\n reducedMotion,\n}: PaperStageSceneProps) {\n const still = usePrefersReducedMotion(reducedMotion)\n // `auto` starts mid and is stepped by the frame-rate watcher below.\n const [tier, setTier] = useState<QualityTier>(quality === 'auto' ? INITIAL_TIER : (quality as QualityTier))\n useEffect(() => {\n if (quality !== 'auto') setTier(quality as QualityTier)\n }, [quality])\n /**\n * Report the tier when the TIER moves — never because the consumer\n * re-rendered.\n *\n * Held in a ref rather than named as a dependency, because the natural way\n * to write this prop is an inline arrow, and an inline arrow is a new\n * function on every render of the page above. Depending on it turned a\n * notification into a pump: report → consumer stores the tier → consumer\n * re-renders → new callback identity → report again, forever. The editor\n * spent every frame in stage mode servicing that loop, which is what made\n * the whole app feel frozen the moment you touched anything.\n */\n const reportQuality = useRef(onQualityChange)\n useEffect(() => {\n reportQuality.current = onQualityChange\n })\n useEffect(() => {\n reportQuality.current?.(tier)\n }, [tier])\n const settings = quality === 'auto' ? qualityTiers[tier] : qualityFor(quality)\n\n /**\n * Serialized deps, the way `PaperFieldMesh` already does it.\n *\n * `stage` arrives from an editor or a page as a fresh object literal every\n * render, so keying on its identity re-ran a full `stageSchema.parse` and a\n * walk resample for a value that had not changed. Harmless once, and it was\n * the reason each iteration of the quality-report pump above cost as much as\n * it did. What the scene depends on is the stage's SHAPE, which is what this\n * key is.\n */\n const stageKey = JSON.stringify(stageInput ?? {})\n // biome-ignore lint/correctness/useExhaustiveDependencies: Serialized deps — `stage` arrives as a fresh object literal every render.\n const stage = useMemo(() => stageSchema.parse(stageInput ?? {}), [stageKey])\n const path = useMemo(() => getWalkPath(stage.path), [stage.path])\n\n /**\n * The rig, resolved ONCE and handed to everything that has to agree with\n * it — the lamps, the environment, the cyclorama, and the transmission\n * through every banner. The room's own colours override the preset's,\n * because in this mode the sky is not a backdrop the light happens to sit\n * in front of: it IS the light, so a stage whose source is warm cannot\n * have a cold room.\n */\n const rig = useMemo(() => {\n const resolved = resolveLighting(stage.lighting, stage.light)\n return {\n ...resolved,\n sky: { zenith: stage.source.zenith, horizon: stage.source.color, ground: stage.ground.color },\n }\n }, [stage.lighting, stage.light, stage.source.zenith, stage.source.color, stage.ground.color])\n // The shot frames the ARCHITECTURE, so it has to know how tall the paper\n // is — read from the preset in play rather than assumed.\n // Both dimensions, resolved once. The suspension needs the width to size a\n // clip off the sheet rather than in world units — a clip that is 4cm\n // whatever it is clipped to looks like a clip on exactly one sheet size.\n const sheetDims = useMemo(() => {\n const { width, height } = resolveConfig({ preset: preset ?? BANNER }).sheet\n return { width, height }\n }, [preset])\n const paperHeight = sheetDims.height\n const paperWidth = sheetDims.width\n\n const paper = preset ?? BANNER\n\n // The walk reaches the layout too. A layout that arranges along a path and\n // a figure that walks a different one is the one bug this whole component\n // is arranged to make impossible, so the stage's path always wins.\n const resolvedLayoutOptions = useMemo(() => {\n const schema = getLayout(layout).optionsSchema\n const takesPath = schema instanceof z.ZodObject && 'path' in schema.shape\n return takesPath ? { ...layoutOptions, path: stage.path } : layoutOptions\n }, [layout, layoutOptions, stage.path])\n\n const slots = useMemo<FieldPaperSlot[] | undefined>(() => {\n if (papers) return papers\n if (images) return undefined\n if (text !== undefined) {\n const split = Array.isArray(text) ? text : splitAcrossBanners(text, count)\n // A whole-rank decision, not a per-banner one: if there is at most one\n // word for every banner, the stage is set vertically. Mixing the two\n // would put one banner's letters next to another's words at a single\n // shared size, and one of the two would always be wrong.\n const vertical = split.every((c) => !c.includes('\\n'))\n const columns = vertical ? split.map(letterColumn) : split\n const longest = columns.reduce((n, c) => Math.max(n, c.split('\\n').length), 1)\n // The longest WORD, not the longest line: lines are already one word\n // each, and it is the word that has to fit across the strip.\n const longestWord = columns.reduce(\n (n, c) => c.split('\\n').reduce((m, w) => Math.max(m, w.length), n),\n 1,\n )\n const size = bannerTextSize(longest, longestWord, bannerMeasure(sheetDims, PADDING))\n return columns.map((column) => ({\n content: {\n type: 'text',\n text: column,\n size,\n align: 'center',\n // Centred down the drop as well as across it. One size is shared\n // by the whole rank — that is what makes it read as set rather\n // than scaled — so a short word necessarily leaves slack, and the\n // slack belongs at both ends. Hung from the top instead, \"the\"\n // reads as a caption that ran out while \"remembers\" fills its\n // banner, and the rank looks broken rather than composed.\n valign: 'center',\n color: '#241f1a',\n lineHeight: 1.25,\n font: 'Georgia, \"Times New Roman\", serif',\n weight: 400,\n padding: PADDING,\n } satisfies ContentConfigInput,\n }))\n }\n return Array.from({ length: count }, () => ({}))\n // `sheetDims` belongs here: the type size is capped by how wide the\n // banner is, so a preset that changes the sheet has to re-set the type.\n }, [papers, images, text, count, sheetDims])\n\n const drive = stageMotionSchema.parse(motion ?? {})\n\n /**\n * Where the papers stand along the walk, so stepping lands ON them.\n *\n * Only a layout that arranges along a path can answer; anything else gets\n * an even spread, which is still somewhere to stop and is better than an\n * arrow key that does nothing.\n */\n const slotCount = slots?.length ?? images?.length ?? count\n const stops = useMemo(() => {\n const spec = getLayout(layout)\n // Parsed, not passed raw: `walkStops` reads options the caller may never\n // have named, and an undefined margin puts every stop at NaN.\n const placed = spec.walkStops?.(slotCount, spec.optionsSchema.parse(resolvedLayoutOptions ?? {}))\n if (placed && placed.length > 0) return placed\n return Array.from({ length: slotCount }, (_, i) => (slotCount > 1 ? i / (slotCount - 1) : 0.5))\n }, [layout, resolvedLayoutOptions, slotCount])\n\n const walk = useWalk({\n path,\n motion: drive,\n progress,\n figureSpeed: stage.figure.speed,\n stops,\n reduced: still,\n onProgress,\n })\n\n // One radius for the room: the sky, the floor and the far clip all measure\n // from it, and they have to agree or the horizon tears.\n const surroundRadius = useMemo(\n () => Math.max(path.length * 1.6, paperHeight * 9),\n [path.length, paperHeight],\n )\n\n // The source stands past the end of the walk, facing back down it.\n const source = useMemo(() => {\n const [x, z] = walkPoint(path, path.length + stage.source.beyond)\n const [tx, tz] = path.tangentAt(1)\n const size = paperHeight * stage.source.spread\n return { position: [x, size * 0.35, z] as const, yaw: Math.atan2(-tx, -tz), size }\n }, [path, stage.source.beyond, stage.source.spread, paperHeight])\n\n return (\n <LightRig rig={rig}>\n <ShotRig stage={stage} paperHeight={paperHeight} walk={walk.walk} />\n <PaperLighting\n rig={rig}\n floor={0}\n scale={60}\n reducedMotion={reducedMotion}\n shadowMapSize={settings.shadowMapSize}\n contactShadow={settings.contactShadow}\n environment={settings.environment}\n />\n {quality === 'auto' && <QualityWatch tier={tier} onChange={setTier} />}\n\n {stage.source.surround && settings.surround && <Surround radius={surroundRadius} sky={rig.sky} />}\n\n {stage.source.enabled && (\n <Source size={source.size} position={source.position} yaw={source.yaw} color={rig.sky.horizon} />\n )}\n\n {/* A square of side s has corners at s·0.707 — keep them inside the\n surround, or the floor punches out through the sky. */}\n {stage.ground.enabled && (\n <Floor size={surroundRadius * 1.3} color={stage.ground.color} slab={stage.ground.slab} />\n )}\n\n {/* The wall the source shines through. Before the floor's own draw is\n irrelevant, but before the banners matters: it is the far surface\n they are seen against. */}\n {stage.room.enabled && stage.room.doorway.enabled && stage.source.enabled && (\n <Doorway\n position={source.position}\n yaw={source.yaw}\n size={source.size}\n opening={stage.room.doorway.opening}\n color={stage.room.doorway.color}\n extent={surroundRadius * 0.9}\n />\n )}\n\n {/* Columns. The scale cue that is not a person — see stageColumnsSchema. */}\n {stage.room.enabled && stage.room.columns.enabled && (\n <Columns\n path={path}\n ceiling={paperHeight * stage.room.height}\n spacing={stage.room.columns.spacing}\n width={stage.room.columns.width}\n offset={stage.room.columns.offset}\n color={stage.room.columns.color}\n />\n )}\n\n {/* Hardware. Drawn before the lid so it is inside the room, and it\n anchors at the ceiling whether or not the ceiling is drawn — a\n thread that stops in mid-air is worse than no thread. */}\n {stage.suspension.type !== 'none' && (\n <Suspension\n layout={layout}\n layoutOptions={resolvedLayoutOptions ?? {}}\n // The slot list IS the population — same number the field draws, so\n // threads and banners cannot disagree about how many there are.\n count={slots?.length ?? count}\n sheet={{ width: paperWidth, height: paperHeight }}\n paperHeight={paperHeight}\n ceiling={paperHeight * stage.room.height}\n color={stage.suspension.color}\n type={stage.suspension.type}\n hardware={stage.suspension.hardware}\n />\n )}\n\n {/* The lid. It gives the haze a far surface to settle on — fog against\n an open sky has none, which is why the top of frame used to grade to\n nothing — and it puts a plane above the walk for the source to spill\n onto, which is how every reference installation reads as interior. */}\n {stage.room.enabled && (\n <Ceiling\n size={surroundRadius * 1.3}\n height={paperHeight * stage.room.height}\n color={stage.room.color}\n />\n )}\n\n <PaperFieldMesh\n preset={paper}\n // Subdivision is the biggest single cost in this scene and the tier's\n // one geometry lever. It is a CEILING on what `'auto'` may ask for,\n // not a replacement for it — overwriting `segments` with a number was\n // this component's own bug: a number applies to BOTH axes, and a\n // deformer's floor then raised it straight back, so every tier drew\n // the identical 48 × 48 banner and the knob did nothing at all.\n segmentCeiling={settings.segments}\n papers={slots}\n images={images}\n layout={layout}\n layoutOptions={resolvedLayoutOptions}\n // The field never drives itself here: the WALK is the motion, and a\n // field turning under a camera that is also travelling is two\n // animations of the same thing disagreeing.\n motion={{ driver: 'none' }}\n entrance={{ type: 'none' }}\n reducedMotion={reducedMotion}\n onSelect={\n drive.driver === 'drag' && progress === undefined\n ? (paperIndex) => {\n // A drag that happens to end over a banner is not a click on it.\n if (walk.dragged.current) return\n walk.travelTo(stops[paperIndex] ?? 0)\n onVisit?.(paperIndex)\n }\n : undefined\n }\n />\n\n {stage.showFigure && (\n <Figure\n path={stage.path}\n figure={stage.figure}\n // The same ref the camera reads. The figure and the shot are the\n // two things that must never disagree about where along the walk\n // we are, so they are given one number rather than two formulas.\n distanceRef={walk.walk}\n walkLength={path.length}\n frozen={reducedMotion}\n />\n )}\n\n {/*\n Last, and outside everything else, because it is not part of the\n scene — it is what happens to the frame after the scene is drawn.\n `settings.grade` is the tier's switch: the bottom tier skips the\n whole composer rather than running it cheaply.\n */}\n {settings.grade && <Grade grade={stage.grade} film={rig.film} />}\n </LightRig>\n )\n}\n\n/** `<PaperStage />` owns its Canvas; `<PaperStageScene />` drops into an existing one. */\nexport function PaperStage({ children, className, style, ...sceneProps }: PaperStageProps) {\n // Fragment cost scales with the SQUARE of pixel ratio, and this scene is\n // fragment-heavy. The canvas is created once, so the cap is taken from the\n // tier the scene starts at rather than followed live.\n const dpr = qualityFor(sceneProps.quality ?? 'auto').dpr\n return (\n <div className={className} style={{ width: '100%', height: '100%', ...style }}>\n <Canvas\n shadows\n dpr={[1, dpr]}\n camera={{ fov: 38, near: 0.05, far: 400 }}\n onCreated={({ scene }) => {\n // Tone mapping is NOT set here. It is part of the lighting rig —\n // `light.film`, resolved with everything else — so that a stage\n // and a lone <Paper> under the same preset are printed on the same\n // film. Pinning it on the canvas meant stage mode silently\n // overrode whatever the rig asked for.\n scene.background = new THREE.Color('#0c0a0b')\n }}\n >\n <PaperStageScene {...sceneProps} />\n {children}\n </Canvas>\n </div>\n )\n}\n","import { z } from 'zod'\nimport type { Ground, WalkPath } from './path'\n\n/**\n * Where to put the camera on a walk.\n *\n * `fitCamera` in field/framing.ts solves a different problem — get every\n * sheet inside the frustum — and solving it here would produce the neutral\n * three-quarter product shot that stage mode exists to avoid. These are\n * SHOTS: a camera stationed relative to the walking figure, framing the\n * space rather than the objects, with the vanishing point doing the work.\n */\n\nexport const shotNames = ['follow', 'lead', 'low', 'wide'] as const\nexport type ShotName = (typeof shotNames)[number]\n\nexport const shotSchema = z.object({\n shot: z.enum(shotNames).default('follow'),\n /**\n * How far the camera stands off the figure ALONG the walk, world units.\n * `wide` reads it as how far back it stands; how far it steps aside is\n * derived from the paper, since that is what it has to clear.\n */\n distance: z.number().min(0.2).max(40).default(4.5),\n /** Multiplier on the shot's natural camera height. 1 is as designed. */\n height: z.number().min(0).max(6).default(1),\n /** How far up the walk the camera looks past the figure, world units. */\n lookAhead: z.number().min(0).max(40).default(7),\n /** Sideways step off the walk line, world units. Positive is the walker's left. */\n offset: z.number().min(-20).max(20).default(0),\n})\n\nexport type ShotOptions = z.infer<typeof shotSchema>\n\nexport interface StageShot {\n position: [x: number, y: number, z: number]\n target: [x: number, y: number, z: number]\n}\n\n/**\n * What each shot is FOR. A stage has two subjects at very different scales —\n * a body about 1.75 units tall and paper five times that — and a camera that\n * only knows about the body frames the body, which is how a colonnade of\n * printed banners ends up showing its bottom third and the tops of some\n * letterforms. Camera height stays a body measurement (eye level is eye\n * level); where it AIMS is a blend, and the paper carries most of it.\n */\nexport interface StageScale {\n /** Standing height of the figure. */\n figure: number\n /** Height of the tallest paper on the stage. */\n paper: number\n}\n\n/** Camera height per shot, as a multiple of the figure's own height. */\nconst EYE: Record<ShotName, number> = {\n follow: 0.95,\n lead: 0.95,\n // Down near the floor, where the banners tower — the worm's-eye of the\n // reference frames, and the cheapest way to make paper read as architecture.\n low: 0.12,\n wide: 1.1,\n}\n\n/** Where each shot aims: a blend of the figure's height and the paper's. */\nconst AIM: Record<ShotName, StageScale> = {\n // Chest height on the figure, a third of the way up the paper — enough\n // tilt that a printed banner reads, not so much that the floor is lost.\n follow: { figure: 0.62, paper: 0.3 },\n // Framing the figure itself, so the paper only lifts the aim a little.\n lead: { figure: 0.62, paper: 0.1 },\n // Up the banners. The figure is incidental to this shot.\n low: { figure: 0, paper: 0.62 },\n wide: { figure: 0.62, paper: 0.2 },\n}\n\n/**\n * A point measured in DISTANCE along the walk rather than normalized `s`,\n * extrapolating straight past either end of an open path. Without that, a\n * following camera at the start of a walk would clamp onto the figure's own\n * feet instead of standing back off it.\n */\n/** How far `wide` stands off the walk line, as a multiple of the paper's height. */\nconst WIDE_STANDOFF = 1.5\n\n/** A tall banner runs about five times the height of the person beside it. */\nexport const DEFAULT_PAPER_RATIO = 4.9\n\nfunction resolveScale(scale: StageScale | number): StageScale {\n if (typeof scale === 'number') return { figure: scale, paper: scale * DEFAULT_PAPER_RATIO }\n return scale\n}\n\nexport function walkPoint(path: WalkPath, distance: number): Ground {\n if (path.length === 0) return path.pointAt(0)\n if (path.closed) return path.pointAt(distance / path.length)\n if (distance < 0) {\n const [x, z] = path.pointAt(0)\n const [tx, tz] = path.tangentAt(0)\n return [x + tx * distance, z + tz * distance]\n }\n if (distance > path.length) {\n const over = distance - path.length\n const [x, z] = path.pointAt(1)\n const [tx, tz] = path.tangentAt(1)\n return [x + tx * over, z + tz * over]\n }\n return path.pointAt(distance / path.length)\n}\n\n/** Normal at a distance along the walk, extrapolation included. */\nfunction walkNormal(path: WalkPath, distance: number): Ground {\n if (path.length === 0) return path.normalAt(0)\n if (path.closed) return path.normalAt(distance / path.length)\n return path.normalAt(Math.min(Math.max(distance, 0), path.length) / path.length)\n}\n\n/**\n * Station the camera for a figure that has walked `walked` units along the\n * path. Every height is expressed as a multiple of something in the frame —\n * the body, or the paper — so a shot holds its composition when the stage is\n * rescaled rather than needing to be re-tuned.\n *\n * `scale` accepts a bare number for the figure's height, in which case the\n * paper is assumed to be the banner-ish proportion of it.\n */\nexport function stageCamera(\n path: WalkPath,\n walked: number,\n scale: StageScale | number,\n options: ShotOptions,\n): StageShot {\n const { figure, paper } = resolveScale(scale)\n const eye = figure * EYE[options.shot] * options.height\n const aim = figure * AIM[options.shot].figure + paper * AIM[options.shot].paper\n\n // Where the camera stands along the walk, and where it points.\n let station: number\n let mark: number\n if (options.shot === 'lead') {\n // Ahead of the figure, walking backward in front of it.\n station = walked + options.distance\n mark = walked\n } else if (options.shot === 'wide') {\n // An establishing shot: back along the walk AND out to one side.\n station = walked - options.distance\n mark = walked\n } else {\n station = walked - options.distance\n mark = walked + options.lookAhead\n }\n\n const [sx, sz] = walkPoint(path, station)\n const [mx, mz] = walkPoint(path, mark)\n const [nx, nz] = walkNormal(path, station)\n // How far `wide` steps aside cannot come from `distance`: an aisle is only\n // a few units across, so any sane walk-distance drops the camera inside\n // the colonnade looking at the back of one banner. It has to clear the\n // paper, so it is the paper that sets it.\n const step = options.offset + (options.shot === 'wide' ? paper * WIDE_STANDOFF : 0)\n\n return {\n position: [sx + nx * step, eye, sz + nz * step],\n target: [mx, aim, mz],\n }\n}\n","import * as THREE from 'three'\nimport { Suspense, useEffect, useMemo, useRef, type RefObject } from 'react'\nimport { useFrame } from '@react-three/fiber'\nimport { RiggedFigure } from './RiggedFigure'\nimport { usePrefersReducedMotion } from '../a11y'\nimport { getWalkPath, walkPathSchema, type WalkPathOptions } from './path'\nimport { PROPORTIONS, figureSchema, placeFigure, type FigureOptions } from './gait'\n\n/**\n * The walking silhouette. Capsules, no rig, unlit near-black — see\n * `figure.ts` for why crude is the correct amount of fidelity here.\n *\n * `distance` is the whole interaction model. Leave it off and the figure\n * walks on the clock; drive it from scroll and the page scrolls the walk,\n * which is the same scene doing duty as both a shareable loop and a\n * scroll-driven hero.\n */\n\nexport interface FigureProps {\n /** The walk to follow. Defaults to the straight walk away from camera. */\n path?: WalkPathOptions\n /** Height, pace, stride, color — see `figureSchema`. */\n figure?: Partial<FigureOptions>\n /**\n * Distance walked in world units. Omit to advance on the clock at the\n * figure's own speed; supply it to drive the walk from scroll or a timeline.\n */\n distance?: number\n /**\n * The scene's live walk, normalized, shared by reference — and the length\n * to measure it against. Outranks `distance`.\n *\n * A ref rather than a prop because the viewer can now drive the walk, and\n * that number changes every frame: re-rendering a loaded skeleton sixty\n * times a second to tell it a float is the most expensive way to say\n * anything in React. `<PaperStage>` hands the SAME ref to the camera, which\n * is what stops the figure and the shot from disagreeing about where along\n * the walk we are.\n */\n distanceRef?: RefObject<number>\n walkLength?: number\n /** Freeze the gait (also forced by `prefers-reduced-motion`). */\n frozen?: boolean\n}\n\n/** A limb segment hanging from its joint: a capsule whose top end is the pivot. */\nfunction Segment({ length, radius, material }: { length: number; radius: number; material: THREE.Material }) {\n // CapsuleGeometry's `length` is the cylinder between the caps, so the\n // rounded ends have to come out of the segment's own length.\n const shaft = Math.max(length - radius * 2, 0.001)\n return (\n <mesh position={[0, -length / 2, 0]} material={material} castShadow>\n <capsuleGeometry args={[radius, shaft, 4, 10]} />\n </mesh>\n )\n}\n\nexport function Figure({ path, figure, distance, distanceRef, walkLength, frozen }: FigureProps) {\n const reducedMotion = usePrefersReducedMotion()\n const still = frozen ?? reducedMotion\n\n const options = useMemo(() => figureSchema.parse(figure ?? {}), [figure])\n const walk = useMemo(() => getWalkPath(walkPathSchema.parse(path ?? {})), [path])\n\n const root = useRef<THREE.Group>(null)\n // Shared with the rigged figure, which scrubs its clip off the same number\n // the capsules pose from — so swapping a model in cannot desynchronise the\n // walk from the ground it covers.\n const walkedRef = useRef(0)\n const hips = useRef<THREE.Group>(null)\n const chest = useRef<THREE.Group>(null)\n const legL = useRef<THREE.Group>(null)\n const legR = useRef<THREE.Group>(null)\n const kneeL = useRef<THREE.Group>(null)\n const kneeR = useRef<THREE.Group>(null)\n const armL = useRef<THREE.Group>(null)\n const armR = useRef<THREE.Group>(null)\n const elbowL = useRef<THREE.Group>(null)\n const elbowR = useRef<THREE.Group>(null)\n\n // One material for the whole body: a silhouette is a single shape, and an\n // unlit one survives a scene lit from behind, where a shaded figure would\n // dissolve into the very haze it has to stand against.\n const material = useMemo(\n () => new THREE.MeshBasicMaterial({ color: options.color, toneMapped: false }),\n [options.color],\n )\n useEffect(() => () => material.dispose(), [material])\n\n const h = options.height\n const p = PROPORTIONS\n const torso = (p.shoulder - p.hip) * h\n\n useFrame((state) => {\n // A frozen figure still stands wherever `distance` puts it — it stops\n // stepping, it does not teleport to the start of the walk.\n const walked =\n distanceRef !== undefined\n ? distanceRef.current * (walkLength ?? walk.length)\n : (distance ?? (still ? 0 : state.clock.elapsedTime * options.speed))\n walkedRef.current = walked\n const { position, yaw, pose } = placeFigure(walk, walked, options)\n\n root.current?.position.set(position[0], position[1], position[2])\n if (root.current) root.current.rotation.y = yaw\n\n // The pelvis carries the legs, so its turn and its tilt travel down them —\n // which is the point: that is how a step gets longer than the leg is.\n if (hips.current) {\n hips.current.position.y = p.hip * h + (still ? 0 : pose.bob)\n hips.current.rotation.y = still ? 0 : pose.pelvis\n hips.current.rotation.z = still ? 0 : pose.hipDrop\n }\n // The trunk's rotations are absolute — given against the direction of\n // travel, not against the pelvis — so the nested group applies the\n // difference. Do this wrong and the counter-rotation reads as the chest\n // going along with the hips at half strength, which looks like nothing.\n //\n // The lean lives here rather than on the hips: a body leans from the\n // waist, and hanging it off the pelvis tips the legs with it.\n if (chest.current) {\n chest.current.rotation.x = pose.lean\n chest.current.rotation.y = still ? 0 : pose.chest - pose.pelvis\n chest.current.rotation.z = still ? 0 : pose.sway - pose.hipDrop\n }\n // Limbs hang downward, so a forward swing is a NEGATIVE rotation about X.\n if (legL.current) legL.current.rotation.x = still ? 0 : -pose.leftThigh\n if (legR.current) legR.current.rotation.x = still ? 0 : -pose.rightThigh\n if (kneeL.current) kneeL.current.rotation.x = still ? 0 : -pose.leftKnee\n if (kneeR.current) kneeR.current.rotation.x = still ? 0 : -pose.rightKnee\n if (armL.current) armL.current.rotation.x = still ? 0 : -pose.leftArm\n if (armR.current) armR.current.rotation.x = still ? 0 : -pose.rightArm\n if (elbowL.current) elbowL.current.rotation.x = still ? 0 : -pose.leftElbow\n if (elbowR.current) elbowR.current.rotation.x = still ? 0 : -pose.rightElbow\n })\n\n const capsules = (\n <group ref={hips}>\n {[-1, 1].map((side) => {\n const leg = side < 0 ? legL : legR\n const knee = side < 0 ? kneeL : kneeR\n return (\n <group key={`leg${side}`} ref={leg} position={[(side * p.hipWidth * h) / 2, 0, 0]}>\n <Segment length={p.thigh * h} radius={p.limbRadius * h} material={material} />\n <group ref={knee} position={[0, -p.thigh * h, 0]}>\n <Segment length={p.shin * h} radius={p.limbRadius * h * 0.9} material={material} />\n </group>\n </group>\n )\n })}\n\n {/* Everything above the waist turns, leans and sways as one piece. */}\n <group ref={chest}>\n <mesh position={[0, torso / 2, 0]} material={material} castShadow>\n <capsuleGeometry args={[(p.torsoWidth * h) / 2, torso * 0.72, 4, 12]} />\n </mesh>\n <mesh position={[0, (p.headCenter - p.hip) * h, 0]} material={material} castShadow>\n <sphereGeometry args={[p.headRadius * h, 14, 12]} />\n </mesh>\n\n {[-1, 1].map((side) => (\n <group\n key={`arm${side}`}\n ref={side < 0 ? armL : armR}\n position={[(side * p.torsoWidth * h) / 2, torso, 0]}\n >\n <Segment length={p.upperArm * h} radius={p.limbRadius * h * 0.8} material={material} />\n <group ref={side < 0 ? elbowL : elbowR} position={[0, -p.upperArm * h, 0]}>\n <Segment length={p.foreArm * h} radius={p.limbRadius * h * 0.72} material={material} />\n </group>\n </group>\n ))}\n </group>\n </group>\n )\n\n return (\n <group ref={root}>\n {options.model ? (\n // The capsules are both the fallback and the thing being replaced, so\n // a model that is still downloading shows a walking figure rather than\n // a hole, and one that never arrives leaves the stage as it was.\n <Suspense fallback={capsules}>\n <RiggedFigure\n url={options.model}\n options={options}\n distance={walkedRef}\n frozen={still}\n fallback={capsules}\n />\n </Suspense>\n ) : (\n capsules\n )}\n </group>\n )\n}\n","import { useGLTF } from '@react-three/drei'\nimport { useFrame } from '@react-three/fiber'\nimport { Component, type ReactNode, type RefObject, useEffect, useMemo } from 'react'\nimport * as THREE from 'three'\nimport { clone as cloneSkeleton } from 'three/examples/jsm/utils/SkeletonUtils.js'\nimport { clipTimeFor, type FigureOptions, isRunning, pickClip, pickStillClip } from './gait'\n\n/**\n * The figure as somebody else's rig, when `figure.model` names one.\n *\n * The asset is never part of the library — it is a URL the app hosts, and\n * nothing about it ships in the npm tarball. What the library contributes is\n * the part that is actually hard: **the clip is scrubbed by distance walked,\n * not played on a clock.** A mixer running on its own time skates the feet\n * the instant the figure's pace disagrees with the animator's, and a\n * scroll-driven walk makes them disagree constantly. See `clipTimeFor`.\n *\n * Two deliberate choices worth knowing before supplying a detailed model:\n *\n * - **`figure.finish` decides whether you see it.** At `silhouette` the whole\n * rig is one flat unlit colour, which is what this mode was built around:\n * the figure gives the banners a scale reference and never competes with\n * them. At `shaded` it keeps its own materials and takes the scene's light\n * — in a backlit hall that means a rim down one edge and the studio light\n * filling the other, which is the only setting where bringing a good model\n * buys anything.\n * - **It is scaled to `figure.height`,** measured off its own bounding box,\n * so an asset authored in centimetres and one authored in metres both come\n * out the right size against the paper.\n */\n\n/** Falls back to the capsules rather than emptying the stage. */\nclass ModelBoundary extends Component<{ fallback: ReactNode; children: ReactNode }, { failed: boolean }> {\n state = { failed: false }\n\n static getDerivedStateFromError() {\n return { failed: true }\n }\n\n componentDidCatch(error: unknown) {\n // A missing or malformed model is the app's to fix, and silence would\n // leave it looking like `figure.model` had simply done nothing.\n console.warn('[paperlab] figure.model failed to load — using the capsule figure.', error)\n }\n\n render() {\n return this.state.failed ? this.props.fallback : this.props.children\n }\n}\n\nexport interface RiggedFigureProps {\n url: string\n options: FigureOptions\n /**\n * Ground covered, world units, shared by reference with the parent rather\n * than passed as a prop — it changes every frame, and re-rendering a loaded\n * skeleton sixty times a second to tell it a number is not worth it.\n */\n distance: RefObject<number>\n frozen: boolean\n}\n\nfunction Rigged({ url, options, distance, frozen }: RiggedFigureProps) {\n const gltf = useGLTF(url)\n\n // Clone through SkeletonUtils, not Object3D.clone: a plain clone of a\n // skinned mesh keeps pointing at the ORIGINAL skeleton, so two figures on\n // one URL would drive each other. useGLTF caches per URL, which is exactly\n // the case that would hit it.\n const scene = useMemo(() => cloneSkeleton(gltf.scene), [gltf.scene])\n\n const silhouette = options.finish === 'silhouette'\n const material = useMemo(\n () => (silhouette ? new THREE.MeshBasicMaterial({ color: options.color, toneMapped: false }) : null),\n [silhouette, options.color],\n )\n useEffect(() => () => material?.dispose(), [material])\n\n // Silhouette the whole rig — or leave its own materials on it — and size it\n // off its own bounds so the asset's authored units stop mattering.\n const scale = useMemo(() => {\n scene.traverse((child: THREE.Object3D) => {\n const mesh = child as THREE.Mesh\n if (!mesh.isMesh) return\n // Stashed on the way past, because switching back to `shaded` has to\n // put the asset's own materials back and by then we have overwritten\n // the only reference to them this clone had.\n mesh.userData.plAuthored ??= mesh.material\n mesh.material = material ?? (mesh.userData.plAuthored as THREE.Material)\n mesh.castShadow = true\n // A shaded figure standing on the floor of a lit hall catches the\n // banners' shadows; a flat silhouette has nothing to catch them with.\n mesh.receiveShadow = !silhouette\n })\n // updateMatrixWorld first, and it is load-bearing: a freshly cloned scene\n // has stale world matrices, so Box3 measures the root's untransformed\n // geometry, reports a model far smaller than it is, and the scale that\n // falls out of it is correspondingly enormous.\n scene.updateMatrixWorld(true)\n const box = new THREE.Box3().setFromObject(scene)\n const height = box.max.y - box.min.y\n return height > 0 ? options.height / height : 1\n }, [scene, material, silhouette, options.height])\n\n const mixer = useMemo(() => new THREE.AnimationMixer(scene), [scene])\n\n // A figure that is not walking should be STANDING, not holding frame 0 of\n // a stride — see `pickStillClip`.\n const clip = useMemo(() => {\n const names = gltf.animations.map((a) => a.name)\n const wanted = frozen ? pickStillClip(names) : pickClip(names, isRunning(options))\n return gltf.animations.find((a) => a.name === wanted) ?? gltf.animations[0]\n }, [gltf.animations, options, frozen])\n\n useEffect(() => {\n if (!clip) return\n mixer.clipAction(clip).play()\n return () => {\n mixer.stopAllAction()\n mixer.uncacheClip(clip)\n }\n }, [mixer, clip])\n\n useFrame(() => {\n if (!clip) return\n // setTime rather than update(delta): the playhead is a pure function of\n // ground covered, so the same distance gives the same pose however the\n // frame rate wobbled on the way there.\n mixer.setTime(frozen ? 0 : clipTimeFor(distance.current, options, clip.duration))\n })\n\n return <primitive object={scene} scale={scale} />\n}\n\n/**\n * The rig, with the capsules standing by for every way a URL can let you\n * down: a 404, a file that is not a glTF, a rig with no clips.\n */\nexport function RiggedFigure({ fallback, ...props }: RiggedFigureProps & { fallback: ReactNode }) {\n return (\n <ModelBoundary fallback={fallback}>\n <Rigged {...props} />\n </ModelBoundary>\n )\n}\n","import { z } from 'zod'\nimport type { WalkPath } from './path'\n\n/**\n * The figure — a silhouette walking the path. It exists for one reason:\n * SCALE. A banner is just a rectangle until there is a body beside it, and\n * every reference image for this mode is carried by one small dark shape\n * that never competes for attention.\n *\n * Which is also why the model is deliberately crude. At the size the figure\n * reads on screen it is forty pixels of unlit black; fidelity buys nothing,\n * and the CONTACT SHADOW does the work of selling the floor. So: procedural\n * gait, capsule limbs, no rig, no asset, no download. Pure math here (it\n * tests in node); the meshes live in Figure.tsx.\n */\n\nexport const figureSchema = z.object({\n /** Standing height in world units — the scale reference the whole stage is read against. */\n height: z.number().min(0.5).max(4).default(1.75),\n /** World units per second along the walk. A relaxed indoor pace is ~1.2. */\n speed: z.number().min(0).max(4).default(1.2),\n /** Stride length as a fraction of height — how far one step carries. */\n stride: z.number().min(0.1).max(1).default(0.42),\n /** Arm swing, 0..1. Drop it toward 0 for hands-in-pockets stillness. */\n swing: z.number().min(0).max(1).default(1),\n /** Silhouette color. Near-black by default: it should read as an absence, not an object. */\n color: z.string().default('#0a0a0c'),\n /**\n * How the figure takes light.\n *\n * `silhouette` is the flat unlit shape this mode was built around: the\n * nave is lit from behind, and a shape that reads as an absence never\n * competes with the paper for attention.\n *\n * `shaded` hands the figure to the rig instead — its own materials, lit\n * by the key and the studio light, so a backlit hall gives it a rim down\n * one edge and the room fills the other. It costs nothing extra and it is\n * the reason to bring a good model: at `silhouette` any two rigs with the\n * same outline are the same picture. Ignored by the capsule figure, which\n * has no materials worth lighting.\n */\n finish: z.enum(['silhouette', 'shaded']).default('shaded'),\n /**\n * Walk or run. `'auto'` decides from `speed` and leg length, at the point\n * people actually break into a run — see `isRunning`.\n */\n gait: z.enum(['auto', 'walk', 'run']).default('auto'),\n /**\n * URL of a rigged glTF/GLB to use instead of the capsules. Serializes as a\n * string, so a `.paper` carrying one stays a `.paper` — but the asset is\n * NOT part of the library and never ships in the npm tarball; the app hosts\n * it. Anything that fails to load falls back to the capsule figure rather\n * than emptying the stage.\n */\n model: z.string().optional(),\n})\n\nexport type FigureOptions = z.infer<typeof figureSchema>\n\n/**\n * Segment lengths as fractions of standing height, roughly canonical human\n * proportions. Shared by the gait, the renderer, and anything that needs to\n * know how tall the hips are.\n */\nexport const PROPORTIONS = {\n hip: 0.53,\n shoulder: 0.82,\n headRadius: 0.045,\n headCenter: 0.935,\n thigh: 0.245,\n shin: 0.235,\n upperArm: 0.185,\n foreArm: 0.165,\n torsoWidth: 0.19,\n hipWidth: 0.095,\n limbRadius: 0.028,\n} as const\n\n/**\n * Every amplitude below is a walk/run pair, because a run is not a fast walk\n * — it is a different gait with a flight phase, and half of these roughly\n * double across the transition.\n */\ninterface Amplitudes {\n thigh: number\n arm: number\n knee: number\n elbow: number\n /** Pelvis rotation about the vertical, carrying the swing hip forward. */\n pelvis: number\n /** Trunk rotation about the vertical, against the pelvis. */\n chest: number\n /** Lateral trunk lean toward the stance foot. */\n sway: number\n /** Pelvic obliquity — the swing-side hip drops. */\n hipDrop: number\n lean: number\n}\n\n/** Walking: ~24° thigh, ~29° arm, ~63° knee, 4°/8° pelvis/chest, ~3° sway. */\nconst WALK: Amplitudes = {\n thigh: 0.42,\n arm: 0.5,\n knee: 1.1,\n elbow: 0.38,\n pelvis: 0.07,\n chest: 0.14,\n sway: 0.05,\n hipDrop: 0.07,\n lean: 0.045,\n}\n\n/** Running: longer swing, a heel that folds toward the seat, elbows at ~85°. */\nconst RUN: Amplitudes = {\n thigh: 0.7,\n arm: 0.95,\n knee: 1.9,\n elbow: 1.5,\n pelvis: 0.14,\n chest: 0.26,\n sway: 0.08,\n hipDrop: 0.1,\n lean: 0.16,\n}\n\n/** Walking hip drop through double support, as a fraction of height. */\nconst BOB = 0.016\n/** Running: how far the stance leg compresses under the body at midstance. */\nconst RUN_COMPRESS = 0.035\n/** Running: how far the flight phase lifts it above standing. */\nconst RUN_LIFT = 0.03\n/** A running step covers this much more ground than the authored stride. */\nconst RUN_STRIDE = 1.7\n/** The speed a walk's lean reaches full, world units per second. */\nconst LEAN_FULL_SPEED = 1.2\n\nconst GRAVITY = 9.81\n/**\n * Froude number at which people stop walking and start running. A walk vaults\n * over a straight stance leg, so it is capped by the point where the body\n * would need more centripetal force than gravity can supply — `v²/gL ≈ 0.5`,\n * which for a 1.75 m figure lands at about 2.1 units/second. This is why the\n * transition is derived rather than a magic number, and why it moves with\n * height: a child breaks into a run at a slower speed than an adult does.\n */\nconst FROUDE_RUN = 0.5\n\nconst TAU = Math.PI * 2\n\n/**\n * One instant of the gait. Angles are radians about the figure's X axis;\n * positive swings a limb FORWARD, along the direction of travel.\n */\nexport interface FigurePose {\n /** Where in the two-step cycle, 0..1. */\n phase: number\n /** Whether this is a run — a gait with a flight phase — rather than a walk. */\n running: boolean\n /**\n * Vertical offset of the hips from standing, world units. A walk only ever\n * drops (≤ 0): it vaults over a straight stance leg, so standing height is\n * the ceiling. A run goes BOTH ways — the stance leg compresses under the\n * body at midstance and the flight phase lifts it clear of the ground.\n */\n bob: number\n /** Forward lean of the torso. */\n lean: number\n\n // ── the trunk ──────────────────────────────────────────────────────────\n // Rotations about the vertical, given relative to the direction of travel\n // rather than to each other. A nested rig applies the difference; the\n // absolute form is what makes \"these two oppose\" legible and testable.\n /** Pelvis about the vertical. Positive carries the LEFT hip forward. */\n pelvis: number\n /** Upper trunk about the vertical, always opposing `pelvis`. */\n chest: number\n /**\n * Lateral trunk lean, radians about the travel axis. Positive tips the\n * trunk toward the figure's left (−X). It leans toward whichever foot is\n * carrying the weight, so it cycles twice per stride, not once.\n */\n sway: number\n /** Pelvic obliquity. Positive drops the LEFT hip, which happens as it swings. */\n hipDrop: number\n\n // ── the limbs ──────────────────────────────────────────────────────────\n leftThigh: number\n rightThigh: number\n /** Knee flex, relative to the thigh. Always ≤ 0 — a knee folds backward only. */\n leftKnee: number\n rightKnee: number\n leftArm: number\n rightArm: number\n /** Elbow flex, relative to the upper arm. Always ≥ 0 — an elbow folds forward only. */\n leftElbow: number\n rightElbow: number\n}\n\n/**\n * Whether these options describe a run.\n *\n * `'auto'` uses the Froude number, `v²/gL` against the leg length, so the\n * transition sits where a real one does and moves with the figure's size\n * instead of being a constant somebody picked.\n */\nexport function isRunning(o: FigureOptions): boolean {\n if (o.gait !== 'auto') return o.gait === 'run'\n const legLength = PROPORTIONS.hip * o.height\n if (legLength <= 0) return false\n return (o.speed * o.speed) / (GRAVITY * legLength) > FROUDE_RUN\n}\n\n/**\n * Length of one full two-step cycle, in world units.\n *\n * A run covers more ground per step than the authored stride — that is most\n * of what running IS — so the cycle stretches rather than the cadence going\n * silly at speed.\n */\nexport function cycleLength(o: FigureOptions): number {\n const stride = o.stride * (isRunning(o) ? RUN_STRIDE : 1)\n return stride * o.height * 2\n}\n\n/**\n * The gait at a given distance walked. Driven by DISTANCE, not by time, so\n * the feet cannot skate: however the figure is paced — clock, scroll, or a\n * scrubbed timeline — a step always covers a step's worth of ground.\n */\nexport function figureGait(distance: number, o: FigureOptions): FigurePose {\n const running = isRunning(o)\n const a = running ? RUN : WALK\n const cycle = cycleLength(o)\n const phase = cycle > 0 ? (((distance / cycle) % 1) + 1) % 1 : 0\n const w = phase * TAU\n\n const leftThigh = a.thigh * Math.sin(w)\n const rightThigh = a.thigh * Math.sin(w + Math.PI)\n\n // The knee folds through the swing — the leg is travelling forward and has\n // to clear the floor — and stays straight through the stance, where it is\n // carrying weight. Peak flex sits at mid-swing, three quarters of a cycle\n // after the leg is furthest forward.\n const flex = (at: number) => -a.knee * Math.max(0, Math.cos(w - at)) ** 1.5\n const leftKnee = flex((7 * Math.PI) / 4)\n const rightKnee = flex((3 * Math.PI) / 4)\n\n // Arms oppose legs — the counter-rotation that stops a walk reading as a shamble.\n const leftArm = -a.arm * o.swing * Math.sin(w)\n const rightArm = -a.arm * o.swing * Math.sin(w + Math.PI)\n\n // An elbow carries a standing bend and tightens as that arm drives forward;\n // running holds it near a right angle throughout. Scaled by `swing` with\n // the shoulder, so hands-in-pockets means straight arms rather than bent\n // ones frozen mid-drive.\n const bend = (forwardness: number) => a.elbow * o.swing * (0.7 + 0.3 * Math.max(0, forwardness))\n const leftElbow = bend(-Math.sin(w))\n const rightElbow = bend(-Math.sin(w + Math.PI))\n\n // The trunk. The pelvis turns to carry the swing-side hip forward, which is\n // what lets a step be longer than the leg; the chest turns against it,\n // cancelling most of that angular momentum so the head travels straight.\n // Take this pair out and a walk reads as a shamble however good the legs\n // are — it is the single most recognisable thing about human gait.\n const pelvis = a.pelvis * Math.sin(w)\n const chest = -a.chest * Math.sin(w)\n\n // Weight has to sit over the foot carrying it, so the trunk leans toward the\n // stance leg — twice per cycle, since there are two stances in a stride. At\n // w = 0 the right leg is under the body, so the lean is toward +X, which is\n // a NEGATIVE sway by the sign convention above.\n const sway = -a.sway * Math.cos(w)\n // Meanwhile the unweighted hip drops away, once per leg: at w = 0 the left\n // leg is mid-swing, so the left hip is the one falling.\n const hipDrop = a.hipDrop * Math.cos(w)\n\n // A walk vaults over a straight stance leg: highest at midstance, dropping\n // through double support, never above standing. A run inverts that — the\n // leg is a spring that compresses under the body at midstance and throws it\n // clear of the ground in between, so the same curve turns upside down and\n // crosses zero. That inversion is the difference you actually see.\n const midstance = Math.abs(Math.cos(w))\n const bob = running\n ? o.height * (RUN_LIFT * (1 - midstance) - RUN_COMPRESS * midstance)\n : -BOB * o.height * (1 - midstance)\n\n return {\n phase,\n running,\n bob,\n lean: running ? a.lean : a.lean * Math.min(o.speed / LEAN_FULL_SPEED, 1),\n pelvis,\n chest,\n sway,\n hipDrop,\n leftThigh,\n rightThigh,\n leftKnee,\n rightKnee,\n leftArm,\n rightArm,\n leftElbow,\n rightElbow,\n }\n}\n\n/**\n * Which of a model's animation clips to play, by name.\n *\n * Exporters name clips every which way — `Walk`, `walk_01`,\n * `Armature|Running` — so this matches loosely rather than exactly, prefers\n * the gait actually being performed, and will take the other gait over\n * nothing. A model with a single unnamed clip still animates.\n */\nexport function pickClip(names: readonly string[], running: boolean): string | undefined {\n return matchClip(names, running ? RUN_CLIP : WALK_CLIP, running ? WALK_CLIP : RUN_CLIP)\n}\n\n/**\n * The clip a figure that is not going anywhere should be in.\n *\n * A rig frozen on frame 0 of its walk stands with one leg out, which reads\n * as a person paused mid-step rather than a person standing — and reduced\n * motion, which is what freezes it, is exactly when nobody gets to see the\n * next frame explain it. Anything named idle or standing beats that; if the\n * asset carries neither, frame 0 of the walk is still the fallback.\n */\nexport function pickStillClip(names: readonly string[]): string | undefined {\n return matchClip(names, IDLE_CLIP, WALK_CLIP)\n}\n\nconst WALK_CLIP = /walk/i\nconst RUN_CLIP = /run|jog|sprint/i\nconst IDLE_CLIP = /idle|stand/i\n\n/**\n * The shortest matching name wins, which is not arbitrary: `Man_Run` and\n * `Man_RunningJump` both contain \"run\", and the clip that IS the thing\n * carries the least name around it. Taking the first match instead meant a\n * pack that happened to list the jump first put the figure into it for the\n * length of the walk — the sort of thing that reads as a physics bug for an\n * hour before anyone thinks to print the clip name.\n */\nfunction matchClip(names: readonly string[], wanted: RegExp, fallback: RegExp): string | undefined {\n const best = (re: RegExp) => names.filter((n) => re.test(n)).sort((a, b) => a.length - b.length)[0]\n return best(wanted) ?? best(fallback) ?? names[0]\n}\n\n/**\n * Where to sit the playhead of a walk clip, given ground covered.\n *\n * This is the whole reason a rigged figure is worth having here rather than a\n * mixer and a clock: **the clip is scrubbed by DISTANCE, exactly as the\n * procedural gait is.** Play a walk cycle on its own timeline and the feet\n * skate the moment the figure's pace disagrees with the animator's; map one\n * gait cycle onto one clip instead, and a step always covers a step.\n *\n * It assumes the clip holds one full two-step cycle, which is the convention\n * for every walk cycle anyone ships. `stride` is the knob that syncs a\n * particular asset: set it to the stride the animator built, and the contact\n * points line up.\n */\nexport function clipTimeFor(distance: number, o: FigureOptions, clipDuration: number): number {\n if (!(clipDuration > 0)) return 0\n const cycle = cycleLength(o)\n if (!(cycle > 0)) return 0\n const phase = (((distance / cycle) % 1) + 1) % 1\n return phase * clipDuration\n}\n\n/** Where the figure stands, which way it faces, and what its limbs are doing. */\nexport interface FigurePlacement {\n /** Ground contact point — the renderer lifts the body off it. */\n position: [x: number, y: number, z: number]\n /** Facing, radians about Y. The figure model faces +Z at yaw 0, as sheets do. */\n yaw: number\n pose: FigurePose\n /** Normalized arc length along the walk, after wrapping or clamping. */\n s: number\n}\n\n/**\n * Put the figure on the walk at a given distance travelled. An open path\n * clamps at its end (the figure arrives and stands); a closed one wraps\n * forever, which is what a looping shot wants.\n */\nexport function placeFigure(path: WalkPath, distance: number, o: FigureOptions): FigurePlacement {\n const raw = path.length > 0 ? distance / path.length : 0\n const s = path.closed ? ((raw % 1) + 1) % 1 : Math.min(Math.max(raw, 0), 1)\n const [x, z] = path.pointAt(s)\n const [tx, tz] = path.tangentAt(s)\n // An open path holds the last pose once the walk runs out, rather than\n // marching the figure on through the far wall.\n const travelled = path.closed ? distance : Math.min(distance, path.length)\n return {\n position: [x, 0, z],\n yaw: Math.atan2(tx, tz),\n pose: figureGait(travelled, o),\n s,\n }\n}\n","import * as THREE from 'three'\nimport { useEffect, useMemo } from 'react'\nimport { cssColorOr } from '../scene/color'\n\n/**\n * The cyclorama: an inverted sphere graded from the source colour at the\n * horizon to near-dark overhead.\n *\n * A single bright plane at the end of the walk is enough for a shot pointed\n * down that walk, and nothing at all for one pointed across it — `wide`\n * framed the figure against an unlit void. A room has walls in every\n * direction, and grading them toward the light is what puts the haze and the\n * distance on the same side of the frame as the source.\n */\n\n/**\n * Procedural, so the repo carries no binary and the grade stays editable.\n *\n * Three stops, not two. The grade used to run zenith → horizon and stop\n * there, which put the brightest colour in the room on the floor line and\n * below it — so the space had no bottom and the ground plane sat on a band\n * of light instead of in a room. Below the horizon it now falls to the\n * floor's own colour, and the picture gains a lower half.\n */\nexport function makeSkyTexture(sky: SkyColors): THREE.CanvasTexture {\n const canvas = document.createElement('canvas')\n canvas.width = 4\n canvas.height = 256\n const ctx = canvas.getContext('2d')!\n const grade = ctx.createLinearGradient(0, 0, 0, canvas.height)\n // These three are typed into text fields, so mid-keystroke they are not\n // colours yet — and `addColorStop` throws on what it cannot parse, from\n // inside a render. See `cssColorOr`.\n const zenith = cssColorOr(sky.zenith, '#241c17')\n const horizon = cssColorOr(sky.horizon, '#fff4e2')\n const ground = cssColorOr(sky.ground, '#141210')\n // Canvas row 0 is the top of the sphere. The grade has to travel most of\n // the way down: held flat until near the horizon it reads as a dark lid\n // over a bright slot, which is the black void this is here to remove.\n grade.addColorStop(0, zenith)\n grade.addColorStop(0.3, zenith)\n grade.addColorStop(0.62, horizon)\n grade.addColorStop(0.7, horizon)\n grade.addColorStop(1, ground)\n ctx.fillStyle = grade\n ctx.fillRect(0, 0, canvas.width, canvas.height)\n const texture = new THREE.CanvasTexture(canvas)\n texture.colorSpace = THREE.SRGBColorSpace\n return texture\n}\n\n/** Zenith, horizon and floor — the same three the studio light is built from. */\nexport interface SkyColors {\n zenith: string\n horizon: string\n ground: string\n}\n\n/**\n * The source itself: bright at the centre, falling to nothing at the edges.\n *\n * A flat rectangle of light has a BORDER, and the moment a shot is not\n * pointed straight down the walk that border draws a hard diagonal across\n * the sky. Fading it out is what lets a finite plane read as an opening\n * rather than as a panel hung in the room.\n */\nexport function makeGlowTexture(color: string): THREE.CanvasTexture {\n const size = 256\n const canvas = document.createElement('canvas')\n canvas.width = canvas.height = size\n const ctx = canvas.getContext('2d')!\n const glow = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2)\n const c = new THREE.Color(color)\n const rgb = `${(c.r * 255) | 0}, ${(c.g * 255) | 0}, ${(c.b * 255) | 0}`\n // Same colour throughout — only the alpha falls, so the fade never tints.\n //\n // A held core and then a long tail. The core has to stay — it is the one\n // thing in frame brighter than the paper, and a falloff that starts at the\n // centre gives a soft warm haze with nothing to walk toward. What changed\n // is the tail: dropping from full to nothing over the last 45% put a\n // visible RIM on the plane, a disc of light with an edge hanging in the\n // room like a moon, and light does not have an edge.\n for (const [stop, alpha] of [\n [0, 1],\n [0.5, 1],\n [0.62, 0.66],\n [0.74, 0.34],\n [0.86, 0.11],\n [0.94, 0.03],\n [1, 0],\n ] as const) {\n glow.addColorStop(stop, `rgba(${rgb}, ${alpha})`)\n }\n ctx.fillStyle = glow\n ctx.fillRect(0, 0, size, size)\n const texture = new THREE.CanvasTexture(canvas)\n texture.colorSpace = THREE.SRGBColorSpace\n return texture\n}\n\n/**\n * The source's default burn, in linear light.\n *\n * Chosen by matching: at 3.4 the tone-mapped source lands on the same read\n * as the un-mapped plane it replaces, so a stage that never mounts a\n * composer looks the way it always did, and one that does gets a source\n * that blooms instead of a rectangle that clips.\n */\nexport const SOURCE_INTENSITY = 3.4\n\nexport function Source({\n size,\n position,\n yaw,\n color,\n intensity = SOURCE_INTENSITY,\n}: {\n size: number\n position: readonly [number, number, number]\n yaw: number\n color: string\n /**\n * How many times brighter than white the source burns, in linear light.\n *\n * This used to be `toneMapped: false` — the source wrote its colour\n * straight to the frame and no curve ever touched it. That is a workaround\n * for not having a post chain, and it stops working the moment there is\n * one: a composer tone-maps the whole framebuffer at the end, so a\n * material that opted out of the renderer's curve is not exempt from the\n * composer's, and the source came out crushed to a flat grey panel — the\n * one thing in the scene that must never look like a panel.\n *\n * Authoring it as a genuine HDR emitter is both the fix and the more\n * honest description: light IS brighter than white, that is what makes it\n * light, and a tone curve rolling off a value above 1.0 is exactly what\n * gives a source its falloff instead of an edge. It is also the only thing\n * bloom can key off, since a threshold near 1.0 means \"brighter than\n * paper\" and paper is the brightest thing here that is not the source.\n */\n intensity?: number\n}) {\n const texture = useMemo(() => makeGlowTexture(color), [color])\n useEffect(() => () => texture.dispose(), [texture])\n return (\n <mesh position={position as unknown as THREE.Vector3} rotation={[0, yaw, 0]}>\n <planeGeometry args={[size * 2.4, size * 1.8]} />\n <meshBasicMaterial\n map={texture}\n transparent\n // `color` multiplies the map, and a THREE.Color is not clamped to 1,\n // so this is how a basic material carries HDR.\n color={new THREE.Color(intensity, intensity, intensity)}\n // It is light, not an object: it must not occlude or fog. It IS\n // tone-mapped now — see `intensity`.\n depthWrite={false}\n fog={false}\n />\n </mesh>\n )\n}\n\nexport function Surround({ radius, sky }: { radius: number; sky: SkyColors }) {\n // Destructured so the memo depends on the three colours rather than on the\n // identity of the object carrying them — the rig is rebuilt whenever any\n // light slider moves, and repainting the dome for a change in exposure is\n // a canvas and a texture upload for nothing.\n const { zenith, horizon, ground } = sky\n const texture = useMemo(() => makeSkyTexture({ zenith, horizon, ground }), [zenith, horizon, ground])\n useEffect(() => () => texture.dispose(), [texture])\n\n return (\n <mesh>\n <sphereGeometry args={[radius, 32, 24]} />\n {/* Unlit and unfogged — it IS the distance, so haze must not stack on it. */}\n <meshBasicMaterial map={texture} side={THREE.BackSide} fog={false} />\n </mesh>\n )\n}\n","import * as THREE from 'three'\nimport { useEffect, useMemo, useRef } from 'react'\nimport type { WalkPath } from './path'\n\n/**\n * The architecture: a ceiling, and a floor with seams in it.\n *\n * Stage mode was a void with a horizon — a graded dome, a flat plane, and a\n * bright rectangle at the end. Nothing in it had a knowable size, which is\n * why the walking figure was carrying the entire scale burden on its own and\n * why removing it left the hall reading as an abstraction rather than a room.\n *\n * The fix is not a bigger light or more banners. It is putting objects in\n * frame whose size the viewer already knows. A concrete floor is poured in\n * slabs, and a slab is about two and a half metres; a ceiling is about three\n * above your head. Give a picture those two and it stops being a gradient\n * and starts being somewhere — and both are flat surfaces under good light,\n * which is the one thing a renderer never gets wrong.\n */\n\n/**\n * The floor, drawn as poured slabs.\n *\n * A repeating texture rather than geometry: the seams have to run to the\n * horizon, and a mesh dense enough to carry them that far would be paying\n * vertices for something a wrapped texture does for free.\n */\nexport function makeFloorTexture(color: string, repeats: number): THREE.CanvasTexture {\n const size = 256\n const canvas = document.createElement('canvas')\n canvas.width = canvas.height = size\n const ctx = canvas.getContext('2d')!\n ctx.fillStyle = color\n ctx.fillRect(0, 0, size, size)\n\n // The seam is a shadow in a gap, not a drawn line — so it is darker than\n // the floor by a little, twice, with the softer pass wider. A hairline of\n // pure black reads as ink on concrete rather than as a joint between two\n // pours.\n const base = new THREE.Color(color)\n const dark = base.clone().multiplyScalar(0.55)\n const softer = base.clone().multiplyScalar(0.78)\n const css = (c: THREE.Color) => `rgb(${(c.r * 255) | 0}, ${(c.g * 255) | 0}, ${(c.b * 255) | 0})`\n\n ctx.strokeStyle = css(softer)\n ctx.lineWidth = 5\n ctx.strokeRect(0, 0, size, size)\n ctx.strokeStyle = css(dark)\n ctx.lineWidth = 1.5\n ctx.strokeRect(0, 0, size, size)\n\n const texture = new THREE.CanvasTexture(canvas)\n texture.colorSpace = THREE.SRGBColorSpace\n texture.wrapS = texture.wrapT = THREE.RepeatWrapping\n texture.repeat.set(repeats, repeats)\n texture.anisotropy = 8\n return texture\n}\n\nexport function Floor({\n size,\n color,\n slab,\n}: {\n size: number\n color: string\n /** World units across one poured slab. 0 draws an unseamed floor. */\n slab: number\n}) {\n const repeats = slab > 0 ? Math.max(1, Math.round(size / slab)) : 0\n const texture = useMemo(() => (repeats > 0 ? makeFloorTexture(color, repeats) : null), [color, repeats])\n useEffect(() => () => texture?.dispose(), [texture])\n\n return (\n <mesh rotation={[-Math.PI / 2, 0, 0]} receiveShadow>\n <planeGeometry args={[size, size]} />\n {/* `map` tints itself by `color`, so the base colour is already in the\n canvas and the material's own colour stays white. */}\n <meshStandardMaterial map={texture} color={texture ? '#ffffff' : color} roughness={1} />\n </mesh>\n )\n}\n\n/**\n * The lid.\n *\n * Two things it fixes, and the second is the one worth having. It gives the\n * haze somewhere to END — fog against an open sky has no far surface to\n * settle on, which is why the top of frame graded to nothing. And it puts a\n * horizontal plane above the walk for the source to spill onto, which is how\n * every one of the reference installations reads as interior: you can see\n * the light landing on the ceiling.\n */\nexport function Ceiling({ size, height, color }: { size: number; height: number; color: string }) {\n return (\n <mesh position={[0, height, 0]} rotation={[Math.PI / 2, 0, 0]} receiveShadow>\n <planeGeometry args={[size, size]} />\n <meshStandardMaterial color={color} roughness={1} side={THREE.FrontSide} />\n </mesh>\n )\n}\n\n/**\n * Columns down the walk, each with a base plate and a capital.\n *\n * The one piece of architecture that stands IN the room rather than bounding\n * it. A ceiling and floor seams say where the space stops; a column says how\n * big it is, because you already know how wide a column is and how far apart\n * they get built. That is the reading the walking figure used to give, from\n * an object a renderer cannot get wrong.\n *\n * Square piers, not turned columns. Flat surfaces under good light is the\n * whole brief, a box has six of them, and at the distances this scene works\n * at a fluted shaft would cost geometry to deliver a silhouette nobody can\n * resolve. The base plate is the part that matters most: it is the only\n * element in the scene that puts a hard horizontal edge at a KNOWN height\n * off the floor, which is what makes the floor read as a floor.\n *\n * Three instanced meshes — shaft, base, capital — so a hundred-metre\n * colonnade is three draw calls whatever its length.\n */\nexport function Columns({\n path,\n ceiling,\n spacing,\n width,\n offset,\n color,\n}: {\n path: WalkPath\n ceiling: number\n spacing: number\n width: number\n offset: number\n color: string\n}) {\n const placements = useMemo(() => {\n if (!(spacing > 0) || !(path.length > 0)) return []\n // Bays measured along the walk's arc length, so a bent or spiral path\n // gets evenly-spaced columns rather than evenly-spaced parameter values.\n const bays = Math.max(1, Math.round(path.length / spacing))\n const out: { position: [number, number, number]; yaw: number }[] = []\n for (let i = 0; i <= bays; i++) {\n const s = i / bays\n const [px, pz] = path.pointAt(s)\n const [nx, nz] = path.normalAt(s)\n const [tx, tz] = path.tangentAt(s)\n // Square to the walk, so a colonnade on a bend still reads as built\n // rather than as scattered.\n const yaw = Math.atan2(tx, tz)\n for (const side of [-1, 1] as const) {\n out.push({ position: [px + nx * side * offset, 0, pz + nz * side * offset], yaw })\n }\n }\n return out\n }, [path, spacing, offset])\n\n const shaft = useRef<THREE.InstancedMesh>(null)\n const base = useRef<THREE.InstancedMesh>(null)\n const capital = useRef<THREE.InstancedMesh>(null)\n\n // A base plate is a slab a little wider than the shaft and about a hand\n // deep. Those proportions are what make it read as a plinth rather than as\n // a step, and they are the reason the column carries scale at all.\n const plate = width * 1.45\n const plateHeight = width * 0.24\n\n useEffect(() => {\n const m = new THREE.Matrix4()\n const q = new THREE.Quaternion()\n const scale = new THREE.Vector3(1, 1, 1)\n const put = (mesh: THREE.InstancedMesh | null, y: number) => {\n if (!mesh) return\n placements.forEach((p, i) => {\n q.setFromEuler(new THREE.Euler(0, p.yaw, 0))\n m.compose(new THREE.Vector3(p.position[0], y, p.position[2]), q, scale)\n mesh.setMatrixAt(i, m)\n })\n mesh.instanceMatrix.needsUpdate = true\n mesh.count = placements.length\n }\n put(shaft.current, ceiling / 2)\n put(base.current, plateHeight / 2)\n put(capital.current, ceiling - plateHeight / 2)\n }, [placements, ceiling, plateHeight])\n\n if (placements.length === 0) return null\n const n = placements.length\n\n return (\n <group>\n <instancedMesh ref={shaft} args={[undefined, undefined, n]} castShadow receiveShadow>\n <boxGeometry args={[width, ceiling, width]} />\n <meshStandardMaterial color={color} roughness={0.92} />\n </instancedMesh>\n <instancedMesh ref={base} args={[undefined, undefined, n]} castShadow receiveShadow>\n <boxGeometry args={[plate, plateHeight, plate]} />\n <meshStandardMaterial color={color} roughness={0.92} />\n </instancedMesh>\n <instancedMesh ref={capital} args={[undefined, undefined, n]} castShadow receiveShadow>\n <boxGeometry args={[plate, plateHeight, plate]} />\n <meshStandardMaterial color={color} roughness={0.92} />\n </instancedMesh>\n </group>\n )\n}\n\n/**\n * The end wall, with a hole in it for the source.\n *\n * A bright rectangle in a void reads as light but not as light *from*\n * anywhere. Put a wall around it and the same rectangle is a doorway: the\n * walk now resolves toward an opening in a surface, the surface meets the\n * floor and the ceiling, and the room finally has the corner it never had.\n *\n * Built as one shape with one hole rather than four quads around a gap,\n * because four quads have three seams that have to be kept in register with\n * the source every time either of them is tuned, and a hole cannot drift.\n */\n/** How far the wall stands off the source plane, in world units. */\nconst NUDGE = 0.08\n\nexport function Doorway({\n position,\n yaw,\n size,\n opening,\n color,\n extent,\n}: {\n position: readonly [number, number, number]\n yaw: number\n /** The source's own half-size, as `Source` uses it. */\n size: number\n opening: number\n color: string\n /** How far the wall runs — far enough to leave the frame. */\n extent: number\n}) {\n const geometry = useMemo(() => {\n // `Source` draws a plane of (size × 2.4, size × 1.8) centred on its\n // position, so the opening is that, scaled — matched here rather than\n // guessed, since a doorway a little smaller than its light is a bright\n // line around a door and a little larger is a shadow gap.\n const w = size * 2.4 * opening\n const h = size * 1.8 * opening\n // The outer contour runs well BELOW the floor, not down to it. A hole\n // has to sit strictly inside its shape — the opening's sill is below the\n // floor line so that a doorway reads as reaching the ground rather than\n // as a window, and a hole poking out through the bottom edge does not\n // get cut at all: the triangulator drops it and the wall comes back\n // solid, which is exactly what it did. Everything under the floor is\n // covered by the floor.\n const shape = new THREE.Shape()\n shape.moveTo(-extent, -extent)\n shape.lineTo(extent, -extent)\n shape.lineTo(extent, extent)\n shape.lineTo(-extent, extent)\n shape.closePath()\n const hole = new THREE.Path()\n hole.moveTo(-w / 2, -h / 2)\n hole.lineTo(w / 2, -h / 2)\n hole.lineTo(w / 2, h / 2)\n hole.lineTo(-w / 2, h / 2)\n hole.closePath()\n shape.holes.push(hole)\n return new THREE.ShapeGeometry(shape)\n }, [size, opening, extent])\n\n useEffect(() => () => geometry.dispose(), [geometry])\n\n // A hair in front of the source, toward the walk. Coplanar they fight for\n // the same pixels and the winner is whichever the depth buffer rounded up\n // that frame, which across a whole wall is a moiré of stripes — and it is\n // the brightest part of the frame, so there is nowhere for it to hide.\n // The source's own normal, since it is the plane being cleared.\n const stood = useMemo<[number, number, number]>(\n () => [position[0] + Math.sin(yaw) * NUDGE, position[1], position[2] + Math.cos(yaw) * NUDGE],\n [position, yaw],\n )\n\n return (\n <mesh geometry={geometry} position={stood} rotation={[0, yaw, 0]} receiveShadow>\n <meshStandardMaterial color={color} roughness={1} side={THREE.DoubleSide} />\n </mesh>\n )\n}\n","import * as THREE from 'three'\nimport { useEffect, useMemo, useRef } from 'react'\nimport { getLayout, type PaperPose } from '../field/layouts'\n\n/**\n * What holds the paper up.\n *\n * Every paper installation shows its hardware — monofilament from a ceiling\n * grid, steel wire, bulldog clips, a rod — and in the scattered-sheet pieces\n * the threads are half the composition. Stage mode's banners hung from\n * nothing at all, which is a larger realism gap than any shader in the\n * backlog and closes for a few thin lines of geometry: a hung thing that\n * shows what suspends it stops reading as a rectangle that happens to float.\n *\n * Both parts are ONE draw call each. The threads are a single `LineSegments`\n * buffer rather than N line meshes, and the clips are an `InstancedMesh`,\n * because a field of forty banners is drawn in one call and it would be\n * absurd for the string holding them up to cost eighty more.\n */\n\nconst euler = (pose: PaperPose) => new THREE.Euler(pose.rotation[0], pose.rotation[1], pose.rotation[2])\n\n/**\n * How long a sheet's rod is. A little wider than the paper, because a dowel\n * cut flush with the sheet reads as part of the sheet.\n */\nexport function rodLength(sheet: { width: number }, pose: PaperPose): number {\n return sheet.width * pose.scale * 1.22\n}\n\n/** Where a pose's top edge is, in world space. Exported to be tested. */\nexport function topOfSheet(pose: PaperPose, paperHeight: number): THREE.Vector3 {\n const half = (paperHeight * pose.scale) / 2\n // The sheet's local +Y, rotated the way the pose rotates it. A banner\n // twisted on its vertical axis still hangs from its own top edge, not from\n // a point directly above its centre.\n const up = new THREE.Vector3(0, half, 0).applyEuler(\n new THREE.Euler(pose.rotation[0], pose.rotation[1], pose.rotation[2]),\n )\n return new THREE.Vector3(...pose.position).add(up)\n}\n\nexport function Suspension({\n layout,\n layoutOptions,\n count,\n sheet,\n paperHeight,\n ceiling,\n color,\n type,\n hardware,\n}: {\n layout: string\n layoutOptions: Record<string, unknown>\n count: number\n sheet: { width: number; height: number }\n paperHeight: number\n /** Height the threads anchor at — the ceiling, or the top of the room. */\n ceiling: number\n color: string\n /** `thread` hangs each sheet on a line; `rod` hangs it on a dowel. */\n type: 'thread' | 'rod'\n hardware: 'none' | 'clip' | 'peg'\n}) {\n const poses = useMemo(() => {\n const entry = getLayout(layout)\n if (!entry) return []\n const options = entry.optionsSchema.parse(layoutOptions)\n // `phase: 0` — suspension is hardware, not motion. It hangs where the\n // layout's resting pose puts it and does not animate with the field.\n return Array.from({ length: count }, (_, i) => entry.pose(i, count, options, 0, sheet))\n }, [layout, layoutOptions, count, sheet])\n\n const geometry = useMemo(() => {\n const points: number[] = []\n const end = new THREE.Vector3()\n for (const pose of poses) {\n const top = topOfSheet(pose, paperHeight)\n // Straight up to the ceiling. A thread under tension is a straight\n // line, and the moment it is drawn with any sag it reads as rope.\n if (top.y >= ceiling) continue\n if (type === 'rod') {\n // A rod hung from one line in the middle would tip, and the eye\n // knows it. Two lines, to the rod's own ends.\n const half = rodLength(sheet, pose) / 2\n for (const side of [-1, 1] as const) {\n end\n .set(side * half, 0, 0)\n .applyEuler(euler(pose))\n .add(top)\n points.push(end.x, ceiling, end.z, end.x, end.y, end.z)\n }\n } else {\n points.push(top.x, ceiling, top.z, top.x, top.y, top.z)\n }\n }\n const g = new THREE.BufferGeometry()\n g.setAttribute('position', new THREE.Float32BufferAttribute(points, 3))\n return g\n }, [poses, paperHeight, ceiling, type, sheet])\n\n useEffect(() => () => geometry.dispose(), [geometry])\n\n const clipRef = useRef<THREE.InstancedMesh>(null)\n const rodRef = useRef<THREE.InstancedMesh>(null)\n\n // Modelled lying down once, rather than rotated in an `onUpdate` — that\n // callback runs again on every re-render, and a rotation applied to the\n // same geometry twice is a rod pointing somewhere new each time.\n const rodGeometry = useMemo(() => {\n const r = sheet.width * 0.018\n const g = new THREE.CylinderGeometry(r, r, sheet.width * 1.22, 8)\n g.rotateZ(Math.PI / 2)\n return g\n }, [sheet.width])\n useEffect(() => () => rodGeometry.dispose(), [rodGeometry])\n useEffect(() => {\n const m = new THREE.Matrix4()\n const q = new THREE.Quaternion()\n const at = new THREE.Vector3()\n const size = new THREE.Vector3()\n const place = (mesh: THREE.InstancedMesh | null, lift: number) => {\n if (!mesh) return\n poses.forEach((pose, i) => {\n const top = topOfSheet(pose, paperHeight)\n q.setFromEuler(euler(pose))\n at.set(top.x, top.y + lift, top.z)\n // Hardware scales with the sheet it holds. A layout that shrinks the\n // banners at the far end of a walk and leaves their clips full size\n // has just told the viewer how far away they are not.\n size.setScalar(pose.scale)\n m.compose(at, q, size)\n mesh.setMatrixAt(i, m)\n })\n mesh.instanceMatrix.needsUpdate = true\n mesh.count = poses.length\n }\n place(clipRef.current, 0)\n // The rod sits ON the top edge rather than through it — paper hangs from\n // a rod, it is not skewered by one.\n place(rodRef.current, sheet.height * 0.004)\n }, [poses, paperHeight, sheet.height])\n\n if (poses.length === 0) return null\n\n return (\n <group>\n {/*\n `toneMapped` is left ON, unlike the source: a thread is an object in\n the room and has to sit in the same grade as everything else. It is\n also deliberately not shadow-casting — a shadow map at this scale\n renders a monofilament as a black bar across the floor, which is far\n more visible than the thread itself and completely wrong.\n */}\n <lineSegments geometry={geometry}>\n <lineBasicMaterial color={color} transparent opacity={0.42} />\n </lineSegments>\n\n {type === 'rod' && (\n <instancedMesh ref={rodRef} args={[undefined, undefined, Math.max(poses.length, 1)]} castShadow>\n {/* Along the sheet's own width, so a twisted banner's rod is\n twisted with it. `rotation` on the geometry rather than on\n every instance: the dowel is modelled lying down once. */}\n <primitive object={rodGeometry} attach=\"geometry\" />\n <meshStandardMaterial color={color} roughness={0.7} metalness={0.15} />\n </instancedMesh>\n )}\n\n {hardware !== 'none' && (\n <instancedMesh ref={clipRef} args={[undefined, undefined, Math.max(poses.length, 1)]} castShadow>\n {/* Sized off the sheet, not in world units, so hardware on a\n postage stamp and hardware on an eight-metre banner both read as\n hardware. A clip is wide and shallow — it grips the edge. A peg\n is narrow and deep — it grips down the face. That difference is\n the whole silhouette, and the silhouette is all that survives\n the distance this scene works at. */}\n {hardware === 'peg' ? (\n <boxGeometry args={[sheet.width * 0.055, sheet.height * 0.038, sheet.width * 0.05]} />\n ) : (\n <boxGeometry args={[sheet.width * 0.13, sheet.height * 0.016, sheet.width * 0.05]} />\n )}\n <meshStandardMaterial\n color={color}\n roughness={hardware === 'peg' ? 0.85 : 0.45}\n metalness={hardware === 'peg' ? 0 : 0.6}\n />\n </instancedMesh>\n )}\n </group>\n )\n}\n","import {\n EffectComposer,\n Bloom,\n DepthOfField,\n Vignette,\n Noise,\n ToneMapping,\n} from '@react-three/postprocessing'\nimport { BlendFunction, ToneMappingMode } from 'postprocessing'\nimport type { FilmName } from '../config/schema'\nimport type { StageGradeConfig } from './schema'\n\n/**\n * The print pass: tone curve, bloom, vignette, grain.\n *\n * Kept in its own module for one reason — it is the ONLY file in the library\n * that imports `@react-three/postprocessing`, and it is reached only from\n * `<PaperStage>`. Adding an import here is fine; importing this from\n * anywhere outside stage mode is not.\n *\n * **The composer takes the tone curve away from the renderer, so this file\n * has to give it back.** `<EffectComposer>` sets `gl.toneMapping =\n * NoToneMapping` for as long as it is mounted — it has to, because tone\n * mapping belongs at the END of a post chain rather than at the end of the\n * scene pass, and a frame mapped twice is wrong twice. What that means here\n * is that mounting a composer without a `<ToneMapping>` effect silently\n * throws away `light.film` entirely: the stage's own grade would have been\n * the one thing capable of un-doing the AgX curve everything else reads.\n *\n * Why the rest of it is needed, in the order it shows up in a frame:\n *\n * - **Bloom.** The source at the end of the walk is a `meshBasicMaterial`\n * with `toneMapped: false`, deliberately, because it is light rather than\n * an object. Nothing rolls it off, so without bloom it clips to a flat\n * shape with a boundary — a lit panel hanging in the room. `Surround`\n * already spends a seven-stop alpha ramp fighting that in geometry, which\n * is the wrong layer to fight it in.\n * - **Vignette.** A frame with no edge reads as a viewport.\n * - **Grain.** The one texture the render and the subject have in common.\n */\n\n/** The rig's film, as a postprocessing mode. Mirrors `toneMappings` in PaperLighting. */\nconst toneMappingModes: Record<FilmName, ToneMappingMode> = {\n agx: ToneMappingMode.AGX,\n neutral: ToneMappingMode.NEUTRAL,\n filmic: ToneMappingMode.ACES_FILMIC,\n}\n\nexport function Grade({ grade, film }: { grade: StageGradeConfig; film: FilmName }) {\n const bloom = grade.bloom > 0\n const depth = grade.depth > 0\n const vignette = grade.vignette > 0\n const grain = grade.grain > 0\n\n // A composer is a full-screen render target and a second pass over every\n // pixel. A stage graded to nothing should not pay for one — and crucially,\n // must not MOUNT one, because an empty composer would still take the tone\n // curve off the renderer and hand back nothing.\n if (!bloom && !depth && !vignette && !grain) return null\n\n return (\n <EffectComposer>\n {/*\n Order is the whole correctness argument here.\n\n Bloom reads the scene while it is still HDR — that is what lets a\n threshold near 1.0 mean \"brighter than paper\" rather than \"brighter\n than whatever the curve happened to flatten paper to\". Tone mapping\n then lands the result in display range, and vignette and grain come\n after it because both are darkroom moves on a finished print, not\n light in the room.\n */}\n {bloom ? (\n <Bloom\n intensity={grade.bloom}\n luminanceThreshold={grade.threshold}\n luminanceSmoothing={0.22}\n mipmapBlur\n />\n ) : null}\n {/*\n Depth goes with bloom on the HDR side, before the curve, because a\n blur of tone-mapped pixels averages DISPLAY values and a blur of\n scene values averages light. Only the second one puts a bright\n highlight's glow into the soft region, which is the entire reason a\n real lens's out-of-focus areas look the way they do.\n\n `focusDistance` is normalized against the camera's far plane, and the\n stage's camera stands ON the walk looking down it — so the focal\n plane sits a little ahead of the viewer and both ends fall away.\n */}\n {depth ? (\n <DepthOfField\n // Normalized against the camera's far plane, which this scene sets\n // to 400 — so 0.008 is roughly three units out, which is where the\n // banner you are standing in front of actually is. The first pass\n // at this focused eight units away and put the focal plane in the\n // empty air past the paper, so nothing in frame was sharp.\n focusDistance={0.008}\n focalLength={0.02 + grade.depth * 0.04}\n bokehScale={grade.depth * 2.5}\n />\n ) : null}\n <ToneMapping mode={toneMappingModes[film]} />\n {vignette ? <Vignette offset={0.32} darkness={grade.vignette} /> : null}\n {/*\n OVERLAY rather than NORMAL: grain added flat lifts the blacks and\n turns a dark hall grey. Overlay leaves them where they are and puts\n the texture into the midtones, which is where film grain lives.\n */}\n {grain ? <Noise opacity={grade.grain} blendFunction={BlendFunction.OVERLAY} /> : null}\n </EffectComposer>\n )\n}\n","import { z } from 'zod'\nimport { lightingNames } from '../config/schema'\nimport { lightSchema } from '../scene/lighting'\nimport { walkPathSchema } from './path'\nimport { shotSchema } from './camera'\nimport { figureSchema } from './gait'\n\n/**\n * A stage serializes like everything else here: one object that fully\n * describes the walk, who walks it, where the camera stands, and how the\n * space is lit. Same rule as the paper schema — a feature that can't\n * serialize into this waits.\n */\n\nexport const stageSourceSchema = z.object({\n /** The bright void the walk resolves toward. Without it the vanishing point is a hole. */\n enabled: z.boolean().default(true),\n color: z.string().default('#fff4e2'),\n /** How far past the end of the walk it stands, world units. */\n beyond: z.number().min(0).max(80).default(10),\n /**\n * A cyclorama around the whole stage, graded from the source colour at the\n * horizon to near-dark overhead. The source plane only faces down the walk,\n * so without this every shot that isn't axial — `wide` especially — looks\n * out at a black void where the room should be.\n */\n surround: z.boolean().default(true),\n /** Colour overhead. The horizon takes the source's own colour. */\n zenith: z.string().default('#241c17'),\n /**\n * Size, as a multiple of the PAPER height.\n *\n * It is an OPENING, not a wall. At 5 the plane was 100 units across and\n * filled the entire frame behind the colonnade, so the hall had no dark\n * end to resolve toward and the whole picture sat at one value. Sized to\n * roughly the height of the paper it stands behind, it reads as the way\n * out — which is what the figure is walking toward.\n */\n spread: z.number().min(0.2).max(60).default(2),\n})\n\nexport const stageGroundSchema = z.object({\n /** The floor. Without something to catch the shadows there is no ground and no scale. */\n enabled: z.boolean().default(true),\n /**\n * Lifted off near-black (`#0e0b09`). A floor dark enough to disappear\n * cannot show its own seams, and the seams are the scale cue — the hall\n * kept its contrast against the source and gained a surface you can read\n * the size of the room from.\n */\n color: z.string().default('#241e19'),\n /**\n * Width of one poured slab, in world units. 0 leaves the floor unseamed.\n *\n * The cheapest scale cue there is, and the one this scene most lacked. A\n * concrete floor is poured in bays of roughly two and a half metres, and a\n * viewer knows that without being told — so a floor with seams in it\n * states the size of the room, while a floor without them is a gradient\n * that happens to be horizontal.\n */\n slab: z.number().min(0).max(20).default(2.4),\n})\n\n/**\n * The room the walk is in.\n *\n * Stage mode was a void with a horizon: a graded dome, a flat plane, and a\n * bright rectangle at the end, none of it a knowable size. That is why the\n * walking figure was carrying the whole scale burden by itself — and why\n * simply removing the figure would have left an abstraction rather than a\n * hall. Architecture is the better answer: objects whose size the viewer\n * already knows, made of flat surfaces under good light, which is the one\n * thing a renderer never gets wrong.\n */\n/**\n * What holds the paper up.\n *\n * Every paper installation shows its hardware — monofilament from a ceiling\n * grid, steel wire, bulldog clips, a rod — and in the scattered-sheet pieces\n * the threads are half the composition. Stage mode's banners hung from\n * nothing at all, which is a bigger realism gap than any shader in the\n * backlog and closes for a few thin lines of geometry.\n */\nexport const stageSuspensionSchema = z.object({\n /**\n * What carries the load.\n *\n * `thread` is monofilament to the ceiling — one straight line per sheet.\n * `rod` is a dowel across each sheet's top edge, hung from the ceiling at\n * both ends, which is a different image entirely: a rank on threads reads\n * as sheets floating in a row, and a rank on rods reads as sheets that\n * were HUNG, by someone, on something. `none` is for a stage where the\n * paper is meant to be impossible.\n */\n type: z.enum(['none', 'thread', 'rod']).default('thread'),\n color: z.string().default('#9c948a'),\n /**\n * What grips the sheet.\n *\n * A clip is wide and shallow — the bulldog clip of a gallery. A peg is\n * narrow and deep, and grips DOWN the face of the sheet rather than\n * across its edge: the domestic one, a line of paper on a washing line.\n * They are told apart by silhouette at any distance, which is the only\n * thing that survives being one instanced box at the top of an\n * eight-metre banner.\n *\n * This replaced a `clips: boolean`. Two of the four pieces of hardware the\n * plan named — pegs, and a rod — had no way to be asked for, and a boolean\n * cannot grow a third answer.\n */\n hardware: z.enum(['none', 'clip', 'peg']).default('clip'),\n})\n\n/**\n * Columns down the walk — the scale cue that is not a person.\n *\n * Retiring the walking figure rested on the argument that **architecture is\n * a better scale cue than a human mesh, and it is the thing renderers never\n * fail at**. A ceiling and floor seams carried that alone, and they are both\n * boundaries: they tell you where the room stops, not how big it is. A\n * column STANDS in the room. It has a knowable width, it is a known distance\n * from the next one, and its base plate meets the floor at a height your eye\n * already knows — which is exactly the reading a figure was there to give.\n *\n * Off by default: a colonnade of columns is a strong compositional claim,\n * and a stage that did not ask for one should not grow one.\n */\nexport const stageColumnsSchema = z.object({\n enabled: z.boolean().default(false),\n /** Centres this far apart along the walk. Roughly a bay. */\n spacing: z.number().min(1).max(24).default(7),\n /** Shaft width. The number doing the work — a column is a known size. */\n width: z.number().min(0.1).max(3).default(0.44),\n /**\n * How far off the walk's centreline each rank stands.\n *\n * Outside the banners, always. Columns are the room; the paper is the\n * subject, and a column standing between the viewer and a banner has\n * swapped the two over. Default clears a `colonnade`'s widest sensible\n * aisle with room to spare.\n */\n offset: z.number().min(0.5).max(24).default(6.6),\n /**\n * Stone, and darker than paper on purpose.\n *\n * The brightest thing in any of these frames has to be the light, and the\n * second brightest has to be the paper. A column the same value as a\n * banner does not read as architecture behind the subject; it reads as\n * more banners, and the eye stops being able to tell what the room is made\n * of from what is hanging in it.\n */\n color: z.string().default('#5c554d'),\n})\n\n/**\n * The end wall, with the source shining through an opening in it.\n *\n * Without it the source is a bright rectangle hanging in a void: it reads as\n * light, but not as light coming from anywhere. A wall around it turns the\n * same rectangle into a doorway — and gives the room the two things it had\n * no way to show, a surface at the end of the walk and a corner where that\n * surface meets the floor and the ceiling.\n *\n * Off by default for the same reason as the columns, and because it changes\n * what the brightest part of every frame is standing in.\n */\nexport const stageDoorwaySchema = z.object({\n enabled: z.boolean().default(false),\n /** Opening size, as a multiple of the source's own. 1 frames it exactly. */\n opening: z.number().min(0.2).max(3).default(1.05),\n color: z.string().default('#171310'),\n})\n\nexport const stageRoomSchema = z.object({\n enabled: z.boolean().default(true),\n /**\n * Ceiling height, as a multiple of the paper's own height.\n *\n * Relative rather than absolute because the banners ARE the architecture\n * here: a hall whose ceiling sits just above its hangings reads as built\n * for them, and one at a fixed world height reads as whatever the paper\n * happened to be scaled to that day.\n */\n height: z.number().min(1).max(6).default(2.2),\n color: z.string().default('#171310'),\n /** Columns flanking the walk — see `stageColumnsSchema`. */\n columns: stageColumnsSchema.default({}),\n /** A wall at the end of the walk with the source in it. */\n doorway: stageDoorwaySchema.default({}),\n})\n\n/**\n * The print: what happens to the frame after the scene is drawn.\n *\n * This lives on the stage rather than on the lighting rig, even though it\n * belongs to the same family as `exposure` and `film`, because the rig is\n * read by `<Paper>` too and `<Paper>` has no composer. A grade in the rig\n * would be a promise one of the two modes could not keep.\n *\n * Every value defaults to a real look rather than to zero — a stage that\n * asks for nothing should still be graded, and `grade: { bloom: 0 }` is how\n * you say you want it raw.\n */\nexport const stageGradeSchema = z.object({\n /**\n * How far light bleeds past what is emitting it.\n *\n * This is the one that matters most in a backlit hall, because the source\n * plane is drawn with `toneMapped: false` — it is light, not an object, so\n * no tone curve ever rolls it off. Bloom is the only thing that gives it\n * an edge that behaves like light instead of like a lit rectangle.\n */\n bloom: z.number().min(0).max(3).default(0.45),\n /**\n * How bright a pixel has to be before it blooms at all, in LINEAR light.\n *\n * Above 1.0 is not only legal, it is the useful range — and that is the\n * whole reason the bound is 4 rather than 1. Bloom reads the scene before\n * the tone curve, while values are still unbounded, so \"1.0\" means \"as\n * bright as white\" rather than \"as bright as the brightest pixel on\n * screen\". Lit near-white paper sits close to 1.0 all by itself; the\n * source burns at `SOURCE_INTENSITY`, several times that. A threshold\n * under 1 therefore blooms the PAPER, which fogs the hall and costs the\n * sheets their edges — the exact failure this default is set to avoid.\n */\n threshold: z.number().min(0).max(4).default(1.6),\n /**\n * Depth falloff — how much the near and far ends of the walk go soft.\n *\n * **Defaults to 0, and that is a considered default rather than a stub.**\n * Depth in this scene is already staged by haze, which is how a real hall\n * does it and which costs one fragment instruction; optical blur is a\n * second full-screen pass with a circle-of-confusion buffer behind it, and\n * it is the effect most likely to read as a video game rather than as a\n * photograph. Every paper installation worth copying is shot deep — an\n * f/11 room where the sheets at the far end are as sharp as the ones you\n * can touch.\n *\n * It is here because a shallow frame is a legitimate look and the schema\n * is the only place a look is allowed to live. Turn it up for a close shot\n * on one banner; leave it alone for a hall.\n */\n depth: z.number().min(0).max(1).default(0),\n /** How far the corners fall off. A frame with no edge reads as a viewport rather than a photograph. */\n vignette: z.number().min(0).max(1).default(0.34),\n /**\n * Film grain.\n *\n * Worth more here than in most scenes: grain is the one texture shared\n * between the render and the thing being rendered. Keep it under ~0.05 —\n * past that it stops reading as stock and starts reading as noise.\n */\n grain: z.number().min(0).max(0.5).default(0.022),\n})\n\nexport const stageSchema = z.object({\n path: walkPathSchema.default({}),\n shot: shotSchema.default({}),\n figure: figureSchema.default({}),\n /** Stage mode is built for `nave`; the others are all front-lit. */\n lighting: z.enum(lightingNames).default('nave'),\n /**\n * The light, by hand: exposure, key, direction, height, ambient, studio,\n * haze. Overrides on `lighting` rather than a replacement for it, so a\n * shared stage carries the sliders that were moved and nothing else.\n */\n light: lightSchema.default({}),\n /**\n * OFF by default now.\n *\n * The figure existed to say \"this is a room at gallery scale\", which is a\n * real job and the right instinct. A rendered human is simply the most\n * expensive and least reliable way to do it: it is the one thing in frame\n * every viewer appraises, and a low-polygon one reads as an asset-store\n * placeholder no matter how good the hall around it is.\n *\n * `stageRoomSchema` does the job instead, with objects whose size the\n * viewer already knows. And the deciding argument is that the stage is\n * NAVIGABLE — drag, wheel, arrow-step, click-to-approach — so there is\n * already a person in the hall and it is the viewer. A second one walking\n * the same aisle on its own clock competes for that role.\n *\n * Still one flag away for anyone who wants it.\n */\n showFigure: z.boolean().default(false),\n source: stageSourceSchema.default({}),\n ground: stageGroundSchema.default({}),\n /** Ceiling and the architecture around the walk — see `stageRoomSchema`. */\n room: stageRoomSchema.default({}),\n /** Thread and clips — see `stageSuspensionSchema`. */\n suspension: stageSuspensionSchema.default({}),\n /**\n * The print — bloom, vignette, grain.\n *\n * Needs `@react-three/postprocessing` and `postprocessing`. They are\n * declared OPTIONAL peers, which means `<Paper>` never pulls them in and a\n * bundle that only imports `<Paper>` never contains them — not that a\n * stage renders without them. A bundler asked to resolve `<PaperStage>`\n * without them installed fails at build time, and that is the intended\n * behaviour: a stage silently losing its grade would be worse than a\n * missing-module error that names the package.\n */\n grade: stageGradeSchema.default({}),\n})\n\nexport type StageConfig = z.infer<typeof stageSchema>\nexport type StageConfigInput = z.input<typeof stageSchema>\nexport type StageGradeConfig = z.infer<typeof stageGradeSchema>\n","import { z } from 'zod'\n\n/**\n * Moving through a stage.\n *\n * The scene was a picture you watched: the camera is stationed on the walk,\n * the walk is driven by `progress`, and if nobody supplied one it ran on a\n * clock. There was nothing to touch. This is the other half — the viewer\n * drives the walk themselves, by dragging, by wheel, by arrow key, or by\n * clicking the paper they want to stand in front of.\n *\n * It drives ONE number: distance walked. Everything else in the scene is\n * already derived from that — the figure, the camera, the light at the end —\n * so navigation cannot pull the parts of the scene away from each other,\n * which is the failure this whole component is arranged to prevent. In\n * particular the camera is still not something anything else may move: there\n * is no orbit here, and dragging does not look around. It walks.\n *\n * Pure math lives here so it tests in node; the listeners are in `useWalk`.\n */\n\nexport const stageMotionSchema = z.object({\n /**\n * Who drives the walk. Same three names as a field's, and they mean the\n * same things — a stage and a field are the same contract seen from two\n * distances.\n *\n * - `drag` — the viewer. Pointer, wheel, arrow keys, or a click on a paper.\n * It DRIFTS on the clock until the first time they touch it, and then it\n * is theirs for good. That is one behaviour rather than two drivers, and\n * it is the default because the alternatives are each half wrong: a stage\n * that only autoplays cannot be touched, and one that only waits opens as\n * a still photograph of itself.\n * - `autoplay` — the clock, and only the clock. It never hands over.\n * - `none` — nothing. The walk stands wherever it was left.\n *\n * An explicit `progress` prop outranks all three: a stage bound to page\n * scroll is a controlled component, and a driver fighting the page for the\n * same number is the bug you would spend an afternoon on.\n */\n driver: z.enum(['autoplay', 'drag', 'none']).default('drag'),\n /** Multiplier on the pace: the figure's walking speed for `autoplay`, the hand for `drag`. */\n speed: z.number().min(0).max(6).default(1),\n /**\n * Whether the walk takes the WHEEL and the TOUCH away from the page.\n *\n * True for a stage that fills the screen — it is the page, so there is\n * nothing to take it from. False for one sitting in a column of prose,\n * where capturing them means a reader who scrolls past it has their scroll\n * eaten and a reader on a phone has their finger trapped. Dragging with a\n * mouse and stepping with the arrow keys work either way, because neither\n * is a gesture the page also wants.\n *\n * Even when captured, the wheel is handed BACK at the ends of an open\n * walk: scrolling past the last banner should carry on down the page\n * rather than press silently into a wall.\n */\n capture: z.boolean().default(true),\n})\n\nexport type StageMotion = z.infer<typeof stageMotionSchema>\nexport type StageMotionInput = z.input<typeof stageMotionSchema>\n\n/**\n * How far a drag of one pixel carries you, as a fraction of the whole walk.\n *\n * Set from the gesture rather than from the world: a full-height drag on a\n * laptop is roughly 800px, and it should cover a good stretch of the hall\n * without throwing you to the far end — a fifth of it. Scaling by the walk's\n * LENGTH instead would make a long walk feel like treacle and a short one\n * uncontrollable, since the hand doing the dragging is the same size either\n * way.\n */\nconst WALK_PER_PIXEL = 0.2 / 800\n\n/** A wheel notch is ~100 deltaY; make one notch a comfortable pace. */\nconst WALK_PER_WHEEL = 0.2 / 1400\n\n/**\n * How fast a flick dies, as a time constant in seconds. Long enough that a\n * throw coasts and reads as weight, short enough that letting go never feels\n * like losing the wheel.\n */\nconst COAST_TAU = 0.32\n\n/** Below this, in walks per second, coasting has stopped. */\nconst COAST_FLOOR = 0.0015\n\n/**\n * Distance covered by a drag, in normalized walk. Dragging UP goes forward,\n * as pushing a page up does.\n *\n * Vertical only, and it takes only the axis it uses: a diagonal drag reading\n * off both would do something neither axis promised, and every scroll\n * convention this borrows from — the page, the reel, the deck — is\n * one-dimensional.\n */\nexport function dragWalk(dy: number, speed: number): number {\n return -dy * WALK_PER_PIXEL * speed\n}\n\n/** Distance covered by a wheel event. Wheel down goes forward, as on a page. */\nexport function wheelWalk(deltaY: number, speed: number): number {\n return deltaY * WALK_PER_WHEEL * speed\n}\n\n/** A flick's remaining velocity after `dt`, or exactly zero once it is spent. */\nexport function coast(velocity: number, dt: number): number {\n const next = velocity * Math.exp(-dt / COAST_TAU)\n return Math.abs(next) < COAST_FLOOR ? 0 : next\n}\n\n/**\n * Keep a position on the walk.\n *\n * A closed walk wraps, because it has no ends. An open one CLAMPS — dragging\n * past the last banner into unlit nothing is not a place the stage has\n * anything to show, and the camera extrapolates straight past the end of the\n * path rather than stopping, so without this the viewer can pull themselves\n * out of the room entirely.\n */\nexport function holdOnWalk(walk: number, closed: boolean): number {\n if (!closed) return Math.min(1, Math.max(0, walk))\n return ((walk % 1) + 1) % 1\n}\n\n/**\n * The stop before or after where you are.\n *\n * Ties are broken FORWARD deliberately: standing exactly on a stop and\n * pressing \"next\" has to move you, and floating point means \"exactly\" is a\n * question of the last bit. The epsilon is what stops an arrow key from\n * landing you back where you started.\n */\nexport function nextStop(stops: readonly number[], from: number, direction: 1 | -1, closed = false): number {\n if (stops.length === 0) return from\n const sorted = [...stops].sort((a, b) => a - b)\n const EPS = 1e-4\n const found =\n direction > 0 ? sorted.find((s) => s > from + EPS) : [...sorted].reverse().find((s) => s < from - EPS)\n if (found !== undefined) return found\n // Off the end: a closed walk comes round, an open one stays at the last one\n // it has rather than snapping to the other end of the room.\n if (closed) return direction > 0 ? sorted[0]! : sorted[sorted.length - 1]!\n return direction > 0 ? sorted[sorted.length - 1]! : sorted[0]!\n}\n\n/** The stop nearest a position — where a release settles when snapping. */\nexport function nearestStop(stops: readonly number[], from: number): number {\n if (stops.length === 0) return from\n return stops.reduce((best, s) => (Math.abs(s - from) < Math.abs(best - from) ? s : best), stops[0]!)\n}\n\n/** How long a step between stops takes, seconds. */\nexport const TRAVEL_SECONDS = 0.75\n\n/**\n * Ease for a step: fast out of the old stop, gentle into the new one.\n *\n * A step between two banners is a camera move, and a camera move that starts\n * and ends abruptly reads as a cut that failed. This is the standard\n * smootherstep — zero velocity AND zero acceleration at both ends, so the\n * move has no visible seam at either.\n */\nexport function travelEase(t: number): number {\n const x = Math.min(1, Math.max(0, t))\n return x * x * x * (x * (x * 6 - 15) + 10)\n}\n\n/**\n * Interpolate along the walk the short way round.\n *\n * On a closed walk the two stops either side of the seam are ADJACENT, and\n * lerping their numbers takes the long way through the whole room. Stepping\n * from the last banner to the first has to be one step, not eighteen.\n */\nexport function travelBetween(from: number, to: number, t: number, closed: boolean): number {\n let delta = to - from\n if (closed) {\n if (delta > 0.5) delta -= 1\n if (delta < -0.5) delta += 1\n }\n return holdOnWalk(from + delta * t, closed)\n}\n","import { useCallback, useEffect, useMemo, useRef } from 'react'\nimport { useFrame, useThree } from '@react-three/fiber'\nimport type { WalkPath } from './path'\nimport {\n coast,\n dragWalk,\n holdOnWalk,\n nextStop,\n travelBetween,\n travelEase,\n TRAVEL_SECONDS,\n wheelWalk,\n type StageMotion,\n} from './navigate'\n\n/**\n * The walk, and whoever is driving it.\n *\n * One ref, updated once a frame, read by the camera and by the figure — the\n * scene's single source of \"how far along are we\". Handing those two their\n * own copies is the same failure as handing them their own paths.\n *\n * A ref rather than state on purpose: this number changes sixty times a\n * second, and re-rendering a scene graph to tell it a float is the most\n * expensive way to say anything in React.\n */\n\nexport interface WalkDrive {\n /** Distance walked, in normalized walk (0..1), live. */\n walk: React.RefObject<number>\n /** Step to a specific point on the walk, eased. Used by the arrow keys and by clicking a paper. */\n travelTo(target: number): void\n /** Step to the stop before or after wherever we are. */\n step(direction: 1 | -1): void\n /**\n * Whether the gesture that just ended was a DRAG rather than a click.\n *\n * Letting go of a drag over a paper fires a click on it, and travelling to\n * whatever happened to be under the cursor when you stopped pulling is not\n * something anyone asked for. A few pixels of slop, because a real click\n * always moves the mouse a little.\n */\n dragged: React.RefObject<boolean>\n}\n\nexport interface UseWalkOptions {\n path: WalkPath\n motion: StageMotion\n /** Controlled position. When set, the viewer is not driving and nothing here listens. */\n progress?: number\n /** Pace for `autoplay`, world units per second. */\n figureSpeed: number\n /** Where the papers stand, normalized. Empty means nothing to step between. */\n stops: readonly number[]\n /** Freeze, and land steps instantly rather than gliding. */\n reduced: boolean\n /** Reports the live position, every frame it changes. */\n onProgress?(walk: number): void\n}\n\nexport function useWalk({\n path,\n motion,\n progress,\n figureSpeed,\n stops,\n reduced,\n onProgress,\n}: UseWalkOptions): WalkDrive {\n const gl = useThree((s) => s.gl)\n const walk = useRef(progress ?? 0)\n const velocity = useRef(0)\n const dragged = useRef(false)\n const travel = useRef<{ from: number; to: number; t: number } | null>(null)\n const controlled = progress !== undefined\n const interactive = !controlled && motion.driver === 'drag'\n /**\n * Whether the viewer has taken the walk over. Until they do, `drag` drifts\n * on the clock — so the stage is moving when it opens AND is yours the\n * moment you touch it. A ref, not state: taking over must not re-render a\n * scene graph mid-gesture.\n */\n const engaged = useRef(false)\n\n const travelTo = useCallback(\n (target: number) => {\n engaged.current = true\n velocity.current = 0\n const to = holdOnWalk(target, path.closed)\n if (reduced) {\n // Reduced motion means do not animate it. A jump is the honest\n // answer; easing it more slowly would be MORE motion, not less.\n walk.current = to\n travel.current = null\n return\n }\n travel.current = { from: walk.current, to, t: 0 }\n },\n [path.closed, reduced],\n )\n\n const step = useCallback(\n (direction: 1 | -1) => {\n // Steps go from where a travel is HEADED, not from where the camera has\n // got to — otherwise holding an arrow key down crawls, because every\n // press re-measures from a position still halfway to the last target.\n const from = travel.current?.to ?? walk.current\n travelTo(nextStop(stops, from, direction, path.closed))\n },\n [stops, path.closed, travelTo],\n )\n\n // Keep the controlled value in the same ref everything else reads, so there\n // is exactly one answer to \"how far along are we\" in either mode.\n useEffect(() => {\n if (controlled) {\n walk.current = progress\n travel.current = null\n velocity.current = 0\n }\n }, [controlled, progress])\n\n const canvas = gl.domElement\n\n useEffect(() => {\n if (!interactive) return\n\n // The canvas has to be reachable by keyboard before it can be driven by\n // one, and it has to say what it is once it is focusable.\n const hadTabIndex = canvas.hasAttribute('tabindex')\n if (!hadTabIndex) canvas.tabIndex = 0\n const hadRole = canvas.getAttribute('role')\n const hadLabel = canvas.getAttribute('aria-label')\n if (!hadRole) canvas.setAttribute('role', 'application')\n if (!hadLabel) {\n canvas.setAttribute(\n 'aria-label',\n 'A walk through hanging paper. Drag or use the arrow keys to move along it.',\n )\n }\n // Touch-dragging a canvas scrolls the page under it otherwise, and the\n // gesture we want IS a vertical drag, so the two collide directly. Only\n // when this stage is allowed to take it: a card in a column of prose\n // that traps a reader's finger is a worse bug than one that cannot be\n // swiped.\n const hadTouch = canvas.style.touchAction\n if (motion.capture) canvas.style.touchAction = 'none'\n\n let pointer: number | null = null\n let lastY = 0\n let lastAt = 0\n let startY = 0\n /** Past this many pixels the gesture is a drag and cannot also be a click. */\n const SLOP = 5\n\n const down = (event: PointerEvent) => {\n if (!event.isPrimary) return\n engaged.current = true\n pointer = event.pointerId\n dragged.current = false\n startY = event.clientY\n lastY = event.clientY\n lastAt = event.timeStamp\n velocity.current = 0\n travel.current = null\n canvas.setPointerCapture(event.pointerId)\n canvas.style.cursor = 'grabbing'\n }\n\n const move = (event: PointerEvent) => {\n if (pointer !== event.pointerId) return\n const dy = event.clientY - lastY\n if (Math.abs(event.clientY - startY) > SLOP) dragged.current = true\n const dt = Math.max((event.timeStamp - lastAt) / 1000, 1 / 240)\n const moved = dragWalk(dy, motion.speed)\n walk.current = holdOnWalk(walk.current + moved, path.closed)\n // Velocity from the LAST move rather than the whole gesture: a flick is\n // the speed of the hand as it let go, not its average since it landed.\n velocity.current = moved / dt\n lastY = event.clientY\n lastAt = event.timeStamp\n }\n\n const up = (event: PointerEvent) => {\n if (pointer !== event.pointerId) return\n pointer = null\n canvas.style.cursor = 'grab'\n // A gesture that ended still is a release, not a throw. Without this,\n // a slow drag that paused before letting go kept creeping.\n if (event.timeStamp - lastAt > 90) velocity.current = 0\n }\n\n const wheel = (event: WheelEvent) => {\n const lines = event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? 400 : 1\n const moved = wheelWalk(event.deltaY * lines, motion.speed)\n // At the end of an open walk, hand the wheel back rather than pressing\n // into a wall — the page carries on scrolling, which is what a reader\n // who has reached the last banner is asking for.\n if (!path.closed && ((walk.current >= 1 && moved > 0) || (walk.current <= 0 && moved < 0))) return\n engaged.current = true\n travel.current = null\n velocity.current = 0\n walk.current = holdOnWalk(walk.current + moved, path.closed)\n event.preventDefault()\n }\n\n const key = (event: KeyboardEvent) => {\n const forward = event.key === 'ArrowRight' || event.key === 'ArrowDown' || event.key === 'PageDown'\n const back = event.key === 'ArrowLeft' || event.key === 'ArrowUp' || event.key === 'PageUp'\n if (forward || back) step(forward ? 1 : -1)\n else if (event.key === 'Home') travelTo(stops[0] ?? 0)\n else if (event.key === 'End') travelTo(stops[stops.length - 1] ?? 1)\n else return\n engaged.current = true\n event.preventDefault()\n }\n\n canvas.style.cursor = 'grab'\n canvas.addEventListener('pointerdown', down)\n canvas.addEventListener('pointermove', move)\n canvas.addEventListener('pointerup', up)\n canvas.addEventListener('pointercancel', up)\n // Not passive: the whole point is to take the wheel off the page.\n if (motion.capture) canvas.addEventListener('wheel', wheel, { passive: false })\n canvas.addEventListener('keydown', key)\n\n return () => {\n canvas.removeEventListener('pointerdown', down)\n canvas.removeEventListener('pointermove', move)\n canvas.removeEventListener('pointerup', up)\n canvas.removeEventListener('pointercancel', up)\n canvas.removeEventListener('wheel', wheel)\n canvas.removeEventListener('keydown', key)\n canvas.style.cursor = ''\n canvas.style.touchAction = hadTouch\n if (!hadTabIndex) canvas.removeAttribute('tabindex')\n if (!hadRole) canvas.removeAttribute('role')\n if (!hadLabel) canvas.removeAttribute('aria-label')\n }\n }, [canvas, interactive, motion.speed, motion.capture, path.closed, step, travelTo, stops])\n\n const reported = useRef(-1)\n useFrame((_, delta) => {\n // Reported even while controlled: a consumer mirroring the position into\n // a scrubber wants it whoever is driving.\n if (onProgress && Math.abs(walk.current - reported.current) > 1e-4) {\n reported.current = walk.current\n onProgress(walk.current)\n }\n if (controlled) return\n const dt = Math.min(delta, 0.1)\n\n if (travel.current) {\n travel.current.t += dt / TRAVEL_SECONDS\n const { from, to, t } = travel.current\n walk.current = travelBetween(from, to, travelEase(t), path.closed)\n if (t >= 1) travel.current = null\n return\n }\n\n // `drag` drifts until it is taken over; `autoplay` never hands over.\n const drifting = motion.driver === 'autoplay' || (interactive && !engaged.current)\n if (drifting && !reduced) {\n // Wrapped, not extrapolated. An open walk used to run past its own end\n // for as long as the tab was open, which is a camera stationed in the\n // dark past the last banner — the playground had to keep its own clock\n // and its own `% 1` to avoid it.\n const perSecond = path.length > 0 ? (figureSpeed * motion.speed) / path.length : 0\n walk.current = (((walk.current + perSecond * dt) % 1) + 1) % 1\n return\n }\n\n if (velocity.current !== 0) {\n walk.current = holdOnWalk(walk.current + velocity.current * dt, path.closed)\n velocity.current = coast(velocity.current, dt)\n // Clamped against a wall, a flick has nowhere to go and should stop\n // rather than press silently into the end for another second.\n if (!path.closed && (walk.current <= 0 || walk.current >= 1)) velocity.current = 0\n }\n })\n\n return useMemo(() => ({ walk, travelTo, step, dragged }), [travelTo, step])\n}\n","/**\n * Render quality tiers.\n *\n * A stage is the heaviest thing this library draws — tens of thousands of\n * subdivided vertices, a shadow pass, a translucent fragment shader and a\n * full-screen backdrop — and it has to run on machines nobody developing it\n * owns. Quality is deliberately NOT part of `stageSchema`: it describes the\n * device, not the artwork, so it must never travel in a preset or a shared\n * link. Two people opening the same link should see the same scene at\n * whatever fidelity their hardware can hold.\n *\n * The five knobs, in the order they actually cost:\n *\n * - `segments` — a CEILING on what `segments: 'auto'` may ask for along the\n * direction a banner's folds run. Quadratic in principle, though a\n * deformer's own floor holds the bottom: `drape` states it needs 48 across\n * its folds, so `low` cannot take the banners below that and should not.\n *\n * It used to be written straight over the sheet's `segments` as a number,\n * and that made it **do nothing at all**. A number applies to BOTH axes,\n * the field caps it at 48 on the way down, and the deformer floor raised\n * it back to 48 on the way up — so every tier drew the identical 48 × 48\n * banner. Measured before the fix: 143,644 triangles at `medium` whatever\n * the tier said. Worth remembering as a shape of bug — a knob nobody had\n * measured, in the file that exists to describe what things cost.\n * - `shadowMapSize` — the shadow pass re-renders the scene's geometry. 0\n * turns shadows off, which on a weak machine is the difference between\n * moving and not.\n * - `dpr` — fragment cost scales with the square of it, and this scene is\n * fragment-heavy (translucency, fog, a full-screen backdrop).\n * - `environment` — the studio light. One prefiltered cube built once, then\n * a texture read per fragment for every lit surface in the scene. Measured\n * at a third of the frame at `medium` (51 ms → 33 ms with it off), which\n * makes it the most expensive single thing here after the geometry, so the\n * bottom tier falls back to the flat ambient it replaced.\n * - `surround` — one more full-screen draw; cheap, but free to drop.\n */\n\nexport const qualityNames = ['auto', 'low', 'medium', 'high'] as const\nexport type QualityName = (typeof qualityNames)[number]\nexport type QualityTier = Exclude<QualityName, 'auto'>\n\nexport interface QualitySettings {\n /** Cap on device pixel ratio. */\n dpr: number\n /** Shadow map resolution. 0 turns the shadow pass off entirely. */\n shadowMapSize: number\n /** Subdivisions along a sheet's long edge. */\n segments: number\n /** Draw the cyclorama behind everything. */\n surround: boolean\n /** Soft contact shadow under the scene — its own render pass. */\n contactShadow: boolean\n /** Light surfaces with the room (an environment map) as well as with the lamp. */\n environment: boolean\n /**\n * Run the print pass — bloom, vignette, grain.\n *\n * A composer is a render target plus a second walk over every pixel, and\n * bloom's mipmap chain is several more — all of it pure fragment work,\n * which is the most expensive kind on exactly the machines that have the\n * least of it.\n *\n * **`high` only, and that is measured rather than cautious.** Switched on\n * at `medium` the software floor went 51.0 ms → 92.2 ms a frame, 20 fps to\n * 11, while `low` (which never had it) stayed put at 26.1 → 28.4 ms — so\n * the control says the ~40 ms is the grade and not the weather. `medium`\n * is the tier `auto` STARTS at, so paying that there pushes weak machines\n * down to `low`, where they lose the environment light and the shadow map\n * to buy a bloom. `high` is only ever reached by a machine that measured\n * 55 fps to get there, and it is where `contactShadow` already lives for\n * the same reason.\n */\n grade: boolean\n}\n\nexport const qualityTiers: Record<QualityTier, QualitySettings> = {\n /**\n * Anything with a GPU — and measured to mean it, since `auto` only arrives\n * here after holding 55 fps. `segments: 128` is where the banners' folds\n * actually resolve: the drape asks for 133 across and spent every previous\n * version of this file getting 72, which is the difference between paper\n * that bends and paper with facets. Free on hardware — an M4 Pro holds 120\n * banners at 16 megapixels on the panel's own clock — and unreachable on\n * anything that cannot, because the ladder never promotes a machine there.\n */\n high: {\n dpr: 2,\n shadowMapSize: 2048,\n segments: 128,\n surround: true,\n contactShadow: true,\n environment: true,\n grade: true,\n },\n /** The default worth aiming at: an integrated laptop GPU from the last few years. */\n medium: {\n dpr: 1.5,\n shadowMapSize: 1024,\n segments: 48,\n surround: true,\n contactShadow: false,\n environment: true,\n grade: false,\n },\n /**\n * Old integrated graphics, a throttled phone, a software rasterizer. The\n * scene still READS — banners, figure, backlight, walk — it just stops\n * paying for the parts nobody would miss at this framerate.\n */\n low: {\n dpr: 1,\n shadowMapSize: 0,\n segments: 28,\n surround: true,\n contactShadow: false,\n environment: false,\n grade: false,\n },\n}\n\n/** The tier to start `auto` from before anything has been measured. */\nexport const INITIAL_TIER: QualityTier = 'medium'\n\n/**\n * Frames the watcher averages before it will move a tier.\n *\n * The FIRST judgement is deliberately made on far less evidence than the\n * rest. A machine that cannot hold the opening scene should not have to\n * stutter through a hundred frames — several seconds, at the frame rate\n * that is the whole problem — before anything is done about it. After that\n * first correction the window widens, because by then the cost is to be\n * measured carefully rather than reacted to.\n */\nexport const FIRST_WINDOW = 20\nexport const STEADY_WINDOW = 60\n/** Frames ignored after a change, while new programs and shadow maps land. */\nexport const SETTLE_FRAMES = 45\n\nexport const TIER_ORDER: QualityTier[] = ['low', 'medium', 'high']\n\nexport function qualityFor(name: QualityName): QualitySettings {\n return qualityTiers[name === 'auto' ? INITIAL_TIER : name]\n}\n\n/** One step better, or the same tier if already at the top. */\nexport function tierUp(tier: QualityTier): QualityTier {\n return TIER_ORDER[Math.min(TIER_ORDER.indexOf(tier) + 1, TIER_ORDER.length - 1)]!\n}\n\n/** One step worse, or the same tier if already at the bottom. */\nexport function tierDown(tier: QualityTier): QualityTier {\n return TIER_ORDER[Math.max(TIER_ORDER.indexOf(tier) - 1, 0)]!\n}\n\n/** Below this, step down. Above the upper one, step up. */\nexport const FLOOR_FPS = 26\nexport const CEILING_FPS = 55\n\nexport interface TierVerdict {\n /** Where to go. Equal to `tier` when nothing should move. */\n tier: QualityTier\n /** The lowest tier now known to be too expensive here — carry it forward. */\n failed: QualityTier | null\n}\n\n/**\n * One verdict from one frame-rate reading: the whole of `auto`'s policy,\n * pulled out of the component so it can be tested rather than watched.\n *\n * `failed` is what keeps the ladder from pumping. The two thresholds cannot\n * do it alone: promotion asks for 55 fps and demotion fires below 26, so any\n * machine where the next tier up costs more than ~2.1× the current one\n * satisfies both conditions forever — rising until it stalls, sinking until\n * it is comfortable, and visibly changing the picture every few seconds. That\n * ratio is real: `high` measures 2.1× `medium` on a software rasterizer,\n * which is precisely the hardware this exists for. So a tier that has once\n * failed is never offered again, and the scene can only ever settle.\n */\nexport function settleTier(tier: QualityTier, fps: number, failed: QualityTier | null): TierVerdict {\n if (fps < FLOOR_FPS) {\n const next = tierDown(tier)\n // What we were just running is what could not be held.\n return next === tier ? { tier, failed } : { tier: next, failed: tier }\n }\n if (fps > CEILING_FPS) {\n const next = tierUp(tier)\n if (next !== tier && next !== failed) return { tier: next, failed }\n }\n return { tier, failed }\n}\n","import type { WalkPathOptions } from './path'\n\n/**\n * Named walks. A path is a list of control points, which is the right thing\n * to serialize and the wrong thing to put in front of someone — nobody wants\n * to type coordinates to find out what a curved colonnade looks like. These\n * are the shapes worth starting from; every one resolves to ordinary points,\n * so editing on from here stays possible.\n */\n\nexport const walkNames = ['straight', 'bend', 'ess', 'ring', 'spiral'] as const\nexport type WalkName = (typeof walkNames)[number]\n\nexport const walks: Record<WalkName, WalkPathOptions> = {\n /** Straight down the nave, away from the camera. The reference shot. */\n straight: {\n points: [\n [0, 16],\n [0, -20],\n ],\n closed: false,\n },\n /** One long curve, so the far end of the colonnade stays hidden until you reach it. */\n bend: {\n points: [\n [-2, 16],\n [0, 6],\n [5, -3],\n [12, -10],\n ],\n closed: false,\n },\n /** Two opposed curves — the walk turns twice and the banners turn with it. */\n ess: {\n points: [\n [6, 17],\n [-3, 7],\n [3, -5],\n [-6, -17],\n ],\n closed: false,\n },\n /** A closed loop: the only walk `phase` can slide, and the only endless one. */\n ring: {\n points: [\n [11, 0],\n [0, 11],\n [-11, 0],\n [0, -11],\n ],\n closed: true,\n },\n /** Inward and tightening — the space closes as the figure goes deeper. */\n spiral: {\n points: [\n [14, 2],\n [2, 13],\n [-11, 1],\n [-1, -9],\n [7, -2],\n [1, 4],\n ],\n closed: false,\n },\n}\n\nexport function getWalk(name: WalkName): WalkPathOptions {\n return walks[name]\n}\n","import type { PaperConfigInput } from '../config/schema'\nimport type { StageConfigInput } from './schema'\nimport { walks } from './walks'\n\n/**\n * Named stages. A mode with no presets asks its visitor to invent a space\n * out of eleven sliders before it will show them anything — and stage mode\n * takes about fifteen seconds to understand once you have seen one, which\n * means the presets ARE the explanation.\n *\n * Each names somewhere paper is actually hung at architectural scale, the\n * same rule the layouts follow.\n */\n\nexport interface StagePreset {\n id: string\n label: string\n /** One line, shown under the name. What you are about to look at. */\n description: string\n stage: StageConfigInput\n layout: string\n layoutOptions?: Record<string, unknown>\n /** The paper itself — banners differ per stage more than anything else. */\n paper?: PaperConfigInput\n count: number\n text?: string\n}\n\n/** A banner: tall, translucent, folds running the length of its drop. */\nconst banner = (width: number, height: number, drape: Record<string, unknown> = {}): PaperConfigInput => ({\n sheet: { width, height, segments: 'auto' },\n stock: 'vellum',\n surface: { grain: 0.22 },\n deformers: [\n { type: 'drape', options: { amplitude: 0.16, folds: 3, falloff: 1.7, gather: 0.28, ...drape } },\n ],\n})\n\nexport const stagePresets: Record<string, StagePreset> = {\n nave: {\n id: 'nave',\n label: 'Nave',\n description: 'A straight aisle of hanging banners, lit from the far end.',\n stage: {\n path: walks.straight,\n shot: { shot: 'follow', distance: 5, lookAhead: 12, offset: 1.5 },\n lighting: 'nave',\n // The hall this stage is named for. Columns give it the one thing a\n // ceiling and floor seams cannot: an object of known size standing IN\n // the room rather than bounding it.\n room: { columns: { enabled: true } },\n },\n layout: 'colonnade',\n layoutOptions: { aisle: 2.6, twist: 22, drape: 0.6, rise: 0.3 },\n paper: banner(1.5, 8.5),\n count: 18,\n text: 'the paper remembers every hand that folded it and every room it was carried through',\n },\n procession: {\n id: 'procession',\n label: 'Procession',\n description: 'The walk turns twice, so the far end stays hidden until you reach it.',\n stage: {\n path: walks.ess,\n shot: { shot: 'low', distance: 4, lookAhead: 9, offset: 1.1 },\n lighting: 'nave',\n figure: { speed: 1.05 },\n },\n layout: 'colonnade',\n layoutOptions: { aisle: 2.2, twist: 34, breathe: 0.45, drape: 0.7 },\n paper: banner(1.3, 9.5, { folds: 4, amplitude: 0.2 }),\n count: 28,\n text: 'every letter you did not send is still folded somewhere in the dark waiting to be read aloud',\n },\n cloister: {\n id: 'cloister',\n label: 'Cloister',\n description: 'A closed loop. The figure walks it forever and the banners drift past.',\n stage: {\n path: walks.ring,\n shot: { shot: 'follow', distance: 4.5, lookAhead: 8, offset: 1.2 },\n lighting: 'nave',\n // Pegs, not clips. A cloister is a walk you repeat, and the sheets are\n // the same words coming back — hung the way you hang washing, not the\n // way a gallery mounts a print. The silhouette is the whole difference\n // at this distance: a peg grips DOWN the face where a clip grips\n // across the edge.\n suspension: { hardware: 'peg' },\n },\n layout: 'colonnade',\n layoutOptions: { aisle: 2.4, twist: 18, rise: 0.22 },\n paper: banner(1.6, 7.5),\n count: 24,\n text: 'around and around and the same words come back changed',\n },\n threshold: {\n id: 'threshold',\n label: 'Threshold',\n description: 'A few enormous sheets, wide enough apart to walk between and read.',\n stage: {\n // Its own short walk. A colonnade spreads over the WHOLE path whatever\n // it is populating, so ten banners on the default 36-unit walk stand\n // seven apart and the shot looks down an empty corridor.\n path: {\n points: [\n [0, 9],\n [0, -11],\n ],\n closed: false,\n },\n // The aisle has to stay inside the frustum at the distance the shot\n // stands: paper half a frame-width off the walk line is paper you\n // never see. `lead` fails here for the same reason and worse.\n shot: { shot: 'follow', distance: 6.5, lookAhead: 9, offset: 0.9 },\n lighting: 'nave',\n figure: { speed: 0.85 },\n /**\n * The one room in the set with a COLOUR in it.\n *\n * Every other stage is a warm neutral corridor, and white paper against\n * warm neutral is white paper against nothing — the sheets and the room\n * sit at the same temperature and the picture flattens. Against a\n * saturated ground the paper sings, which is why the installations\n * worth copying are shot in rooms painted terracotta and washed with\n * gels rather than in white boxes.\n *\n * `source.color` is the horizon and `ground.color` the floor of the\n * same three-stop sky that builds the environment map, so the light\n * bouncing onto the sheets is the room's own colour and cannot\n * disagree with the walls the viewer can see.\n */\n source: { spread: 1.1, color: '#ffd7a8', zenith: '#3d1c12' },\n ground: { color: '#6b2f1d' },\n // The stage named for a doorway now has one. Without the wall the\n // source is a bright rectangle floating in a coloured void — it reads\n // as light, but not as light coming from anywhere. With it, the walk\n // resolves toward an opening in a surface, and the room gets the\n // corner it never had. The wall takes the terracotta so the opening is\n // the only bright thing in the frame that is not paper.\n room: { doorway: { enabled: true, color: '#4a2013' } },\n },\n layout: 'colonnade',\n layoutOptions: { aisle: 2.4, twist: 14, breathe: 0.18, margin: 0.12, rise: 0.2 },\n paper: banner(2.6, 10, { folds: 2, amplitude: 0.24, falloff: 2 }),\n count: 10,\n text: 'stand closer and read what it cost to write this down',\n },\n /**\n * The one stage that is not a colonnade of banners.\n *\n * A ribbon reaches the floor and keeps going, and everything built in the\n * last four phases exists so that this reads: a room with a ceiling to\n * hang from, hardware to hang by, type that can be set down a length\n * without looking like a caption, and a `roll` that begins at the floor\n * line rather than at the sheet's centre.\n */\n ribbon: {\n id: 'ribbon',\n label: 'Ribbon',\n description: 'Printed strips falling the full drop of the room, pooling where they land.',\n stage: {\n path: walks.straight,\n // Close. Ribbons are a curtain you part rather than a hall you walk\n // down, so the camera stands nearer and looks less far ahead than any\n // other stage in the set.\n // Lower than the other stages. Pooled paper lies FLAT, so from\n // standing height it foreshortens to a sliver; the shot has to get\n // down toward the floor for the thing this stage is about to read.\n shot: { shot: 'follow', distance: 4.6, height: 2.3, lookAhead: 3.6, offset: 0.34 },\n lighting: 'nave',\n // A low ceiling: the strips ARE the height of the room, so a lid far\n // above them would leave metres of empty air and make the drop read as\n // short. This is the stage the room proportion matters most on.\n room: { height: 1.12 },\n source: { spread: 1.3 },\n suspension: { hardware: 'clip' },\n },\n layout: 'colonnade',\n // Packed tighter than the banner stages, barely twisted, and hung at a\n // steady height — a rank of strips reads by its rhythm, and jitter that\n // flatters a colonnade of banners just makes this look untidy.\n // `hover` is NEGATIVE by exactly the pool fraction, and that is the whole\n // trick. A colonnade hangs a sheet with its BOTTOM edge on the floor, but\n // a ribbon's crease sits a pool-length above its bottom edge — so at\n // hover 0 the pooled length lies flat in mid-air, parallel to a ground it\n // never touches. Dropping the strip by the same fraction puts the crease\n // on the floor and the pool ON it.\n layoutOptions: {\n aisle: 1.75,\n twist: 5,\n breathe: 0.1,\n margin: 0.06,\n rise: 0.06,\n drape: 0.2,\n hover: -0.22,\n },\n paper: {\n sheet: { width: 1.05, height: 9, segments: 'auto' },\n stock: 'printer',\n surface: { grain: 0.2 },\n behavior: { type: 'ribbon', pool: 0.22, curl: 0.34, drape: 0.6 },\n },\n // Eight, not twelve, and the reason is the type rather than the room.\n // A strip 1.05 wide holds about 105px of measure, which caps the type at\n // 26px, which means a column needs roughly twenty-six words to reach the\n // bottom of a nine-metre drop. Twelve strips wanted three hundred words;\n // eight want two hundred, which is a passage rather than an essay. Fewer\n // and longer is also what the reference installations look like.\n count: 8,\n // Long, because the whole point of this stage is type running the length\n // of the paper. It shipped with twenty words across twelve banners — two\n // words a strip — which set as a caption at the top of nine metres of\n // blank paper. Every word here is kept to seven letters or fewer: the\n // measure is narrow, and one long word shrinks the type on every banner\n // in the room, because a rank of banners is set at one size or it reads\n // as a mistake.\n text:\n 'the paper kept going long after the floor ran out from under it and nobody moved to pick it up ' +\n 'we let it lie there the way you let a letter lie it had come down from a height no one could name ' +\n 'and it held the shape of the fall in its folds someone inked it once and you can still read the ' +\n 'last of it where the light gets in a room is only a room until you hang a thing in it then it is ' +\n 'a place you walk across slowly the strips move when the door opens and settle again before you ' +\n 'reach them paper holds what it was rolled around it holds being flat too and it will go back to ' +\n 'flat if you leave it alone long enough but not today today it lies in a curve at the foot of the ' +\n 'wall and the curve is the whole point the floor was never meant to hold this much so the paper ' +\n 'takes over where the floor gives up it pools the way water would if water could be inked we came ' +\n 'to look at the light we stayed for the paper on the ground',\n },\n archive: {\n id: 'archive',\n label: 'Archive',\n description: 'Narrow strips packed tight — a corridor of records you edge through.',\n stage: {\n path: walks.bend,\n // Far enough back that the figure reads as small; a `low` camera\n // three units behind a body is all body.\n shot: { shot: 'low', distance: 8, lookAhead: 13, offset: 0.5 },\n lighting: 'nave',\n ground: { color: '#0b0908' },\n // Its banners are eleven units tall and `spread` is a multiple of that,\n // so the default opening would be fifty units across — a wall, on a\n // walk whose whole point is that it is narrow.\n source: { spread: 1.1 },\n // Records on a rail. Forty-four strips each on their own invisible\n // thread read as forty-four accidents; on rods they read as a system\n // somebody filed them into, which is what the stage is called.\n suspension: { type: 'rod' },\n },\n layout: 'colonnade',\n layoutOptions: { aisle: 1.7, twist: 44, breathe: 0.5, drape: 0.75, rise: 0.4 },\n paper: banner(0.85, 11, { folds: 2, amplitude: 0.12 }),\n count: 44,\n text: 'catalogued indexed cross referenced filed and never once opened by anyone at all',\n },\n}\n\nexport function getStagePreset(id: string): StagePreset {\n const preset = stagePresets[id]\n if (!preset) {\n throw new Error(\n `[paperlab] Unknown stage preset \"${id}\". Available: ${Object.keys(stagePresets).join(', ')}`,\n )\n }\n return preset\n}\n\nexport function listStagePresets(): string[] {\n return Object.keys(stagePresets)\n}\n","import { AGENT_PAYLOAD_VERSION } from '../config/agent-payload'\nimport { diffConfig } from '../config/diff'\nimport { paperConfigSchema, type PaperConfigInput } from '../config/schema'\nimport { getLayout } from '../field/layouts'\nimport { stageSchema, type StageConfig, type StageConfigInput } from './schema'\nimport { walkNames, walks } from './walks'\nimport type { WalkName } from './walks'\n\n/**\n * Stage-mode export. Same anatomy and version as the paper and field\n * exports, with one addition that matters more than the rest: the scroll\n * variant. `progress` is the whole interaction model of a stage, and a\n * scroll-driven hero is what most people opening this menu actually want —\n * so the export writes the pinning and the scroll math, which is the part\n * that is fiddly to get right and boring to write.\n */\n\nexport interface StageExportInput {\n stage: StageConfigInput\n layout: string\n layoutOptions?: Record<string, unknown>\n /** The banner itself — dims, stock, drape. Omitted uses the built-in banner. */\n paper?: PaperConfigInput\n /** The words the space is built from. Omitted renders blank banners. */\n text?: string\n count?: number\n /** Bind the walk to page scroll, pinned, rather than to the clock. */\n scroll?: boolean\n /** Exported component name. */\n componentName?: string\n}\n\n/** How many viewport-heights of scroll the walk is spread over. */\nconst SCROLL_HEIGHTS = 4\n\n/** Which named walk these points are, if any — the export reads better for it. */\nexport function walkNameFor(path: StageConfig['path']): WalkName | undefined {\n const key = JSON.stringify({ points: path.points, closed: path.closed })\n return walkNames.find(\n (name) => JSON.stringify({ points: walks[name].points, closed: walks[name].closed }) === key,\n )\n}\n\n/** Deep-strip anything that already equals the schema default. */\nfunction stripDefaults(value: unknown, defaults: unknown): unknown {\n if (Array.isArray(value) || Array.isArray(defaults)) {\n return JSON.stringify(value) === JSON.stringify(defaults) ? undefined : value\n }\n if (value && defaults && typeof value === 'object' && typeof defaults === 'object') {\n const out: Record<string, unknown> = {}\n for (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n const kept = stripDefaults(child, (defaults as Record<string, unknown>)[key])\n if (kept !== undefined) out[key] = kept\n }\n return Object.keys(out).length > 0 ? out : undefined\n }\n return value === defaults ? undefined : value\n}\n\n/** The stage config with defaults removed — what actually needs writing down. */\nexport function diffStage(stage: StageConfigInput): Record<string, unknown> {\n const resolved = stageSchema.parse(stage)\n const defaults = stageSchema.parse({})\n const diff = (stripDefaults(resolved, defaults) as Record<string, unknown>) ?? {}\n // A path is atomic. Field-by-field stripping would export a curved walk's\n // points while dropping `closed: false` for matching the default — which\n // parses correctly today and quietly breaks the moment someone edits those\n // points into a loop. Emit the whole walk or none of it.\n if (diff.path !== undefined) diff.path = resolved.path\n return diff\n}\n\n/**\n * JSON.stringify, except an array of plain numbers stays on one line. A walk\n * is a list of coordinate pairs, and the default pretty-printer spreads each\n * `[6, 17]` over four lines — twenty lines of punctuation for one gentle\n * curve. Exported code is a product surface; it should read like something a\n * person wrote.\n */\nexport function stringifyStage(value: unknown, indent = 0): string {\n const pad = ' '.repeat(indent)\n const inner = ' '.repeat(indent + 1)\n if (Array.isArray(value)) {\n if (value.length === 0) return '[]'\n if (value.every((v) => typeof v === 'number')) return `[${value.join(', ')}]`\n const items = value.map((v) => `${inner}${stringifyStage(v, indent + 1)}`)\n return `[\\n${items.join(',\\n')}\\n${pad}]`\n }\n if (value && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n if (entries.length === 0) return '{}'\n const items = entries.map(([k, v]) => `${inner}${JSON.stringify(k)}: ${stringifyStage(v, indent + 1)}`)\n return `{\\n${items.join(',\\n')}\\n${pad}}`\n }\n return JSON.stringify(value)\n}\n\nconst SHOT_PHRASES: Record<string, string> = {\n follow: 'from behind and a little above them, looking up the walk',\n lead: 'from in front, walking backward as they come on',\n low: 'from down at floor level, looking up the banners',\n wide: 'from off to one side, level with them',\n}\n\n/** The one-line visual an agent verifies after `npm run dev`. */\nexport function describeStage(input: StageExportInput): string {\n const stage = stageSchema.parse(input.stage)\n const count = input.count ?? 22\n const walk = walkNameFor(stage.path)\n const parts: string[] = []\n\n const shape =\n walk === 'straight' || walk === undefined\n ? 'a straight walk'\n : walk === 'ring'\n ? 'a closed loop of a walk'\n : `an \"${walk}\" walk that curves as it goes`\n parts.push(`${count} tall paper banners standing along ${shape}`)\n\n if (input.text?.trim()) {\n parts.push('each printed with a column of your text running down it')\n }\n // The shot is named whether or not anyone is walking. It used to ride\n // along inside the figure's clause, so turning the figure off silently\n // took the CAMERA out of the description too — and the camera is what the\n // reader is actually looking through.\n parts.push(`seen ${SHOT_PHRASES[stage.shot.shot]}`)\n if (stage.showFigure) {\n parts.push('a small dark figure walking between them')\n }\n if (stage.room.enabled) {\n parts.push('a ceiling overhead and seams in the poured floor, so the hall has a knowable size')\n }\n parts.push(\n stage.lighting === 'nave'\n ? 'the whole space dim and lit from behind, so the paper glows and the far end of the walk is a bright void'\n : `lit with the \"${stage.lighting}\" preset`,\n )\n // The description is what someone checks the render against, so a light\n // that was moved by hand has to appear in it — otherwise the sentence\n // describes the preset and the screen shows something else.\n const moved = Object.entries(stage.light)\n .filter(([, value]) => value !== undefined)\n .map(([key]) => key)\n if (moved.length > 0) parts.push(`with its ${moved.join(', ')} set by hand`)\n if (input.scroll) parts.push('and scrolling the page walks the figure deeper into it')\n return parts.join(', ')\n}\n\nfunction propLines(input: StageExportInput, indent: string): string {\n const lines: string[] = []\n if (input.paper) lines.push(`${indent}preset={banner}`)\n if (input.text?.trim()) lines.push(`${indent}text={text}`)\n if (input.count !== undefined) lines.push(`${indent}count={${input.count}}`)\n if (input.layout !== 'colonnade') lines.push(`${indent}layout=\"${input.layout}\"`)\n const layoutOptions = input.layoutOptions ?? {}\n const layoutDefaults = getLayout(input.layout).defaults as Record<string, unknown>\n const changed: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(layoutOptions)) {\n if (JSON.stringify(value) !== JSON.stringify(layoutDefaults[key])) changed[key] = value\n }\n if (Object.keys(changed).length > 0) {\n lines.push(`${indent}layoutOptions={${stringifyStage(changed).replace(/\\n\\s*/g, ' ')}}`)\n }\n lines.push(`${indent}stage={stage}`)\n return lines.join('\\n')\n}\n\n/** Component source shared by the JSX snippet and the agent payload. */\nexport function buildStageComponentSource(input: StageExportInput): string {\n const name = input.componentName ?? 'PaperNave'\n const stage = diffStage(input.stage)\n const stageConst = `const stage = ${stringifyStage(stage)} satisfies StageConfigInput`\n // The banner is inlined for the same reason field presets are: the receiver\n // does not have the sender's preset library.\n const bannerConst = input.paper\n ? `\\n\\nconst banner = ${stringifyStage(diffConfig(paperConfigSchema.parse(input.paper)))} satisfies PaperConfigInput`\n : ''\n const textConst = input.text?.trim() ? `\\n\\nconst text = ${JSON.stringify(input.text)}` : ''\n\n if (!input.scroll) {\n return `import { PaperStage, type StageConfigInput } from 'paperlab/stage'${input.paper ? \"\\nimport type { PaperConfigInput } from 'paperlab'\" : ''}\n\n${stageConst}${bannerConst}${textConst}\n\nexport function ${name}() {\n return (\n <PaperStage\n${propLines(input, ' ')}\n />\n )\n}`\n }\n\n return `import { useEffect, useRef, useState } from 'react'\nimport { PaperStage, type StageConfigInput } from 'paperlab/stage'${input.paper ? \"\\nimport type { PaperConfigInput } from 'paperlab'\" : ''}\n\n${stageConst}${bannerConst}${textConst}\n\nexport function ${name}() {\n const ref = useRef<HTMLDivElement>(null)\n const [progress, setProgress] = useState(0)\n\n // Scroll the section, walk the figure. The stage is pinned for the height\n // of the section, so the page scrolling past it IS the walk.\n useEffect(() => {\n const el = ref.current\n if (!el) return\n const onScroll = () => {\n const { top, height } = el.getBoundingClientRect()\n const travel = Math.max(height - window.innerHeight, 1)\n setProgress(Math.min(Math.max(-top / travel, 0), 1))\n }\n onScroll()\n window.addEventListener('scroll', onScroll, { passive: true })\n window.addEventListener('resize', onScroll)\n return () => {\n window.removeEventListener('scroll', onScroll)\n window.removeEventListener('resize', onScroll)\n }\n }, [])\n\n return (\n <div ref={ref} style={{ height: '${SCROLL_HEIGHTS * 100}vh' }}>\n <div style={{ position: 'sticky', top: 0, height: '100vh' }}>\n <PaperStage\n${propLines(input, ' ')}\n progress={progress}\n />\n </div>\n </div>\n )\n}`\n}\n\n/** The self-contained stage integration brief — one paste into a coding agent. */\nexport function buildStageAgentPayload(input: StageExportInput): string {\n const name = input.componentName ?? 'PaperNave'\n const sizing = input.scroll\n ? `4. Sizing: the component brings its own height — it reserves ${SCROLL_HEIGHTS} viewport\n heights of scroll and pins the canvas inside that. Drop it into the page\n flow as a section; do NOT wrap it in a fixed-height container.`\n : `4. Sizing: the component fills its parent container. Place it where I ask;\n give the parent an explicit height.`\n\n return `Integrate a Paperlab stage — paper as architecture, with a figure walking through it — into this project. (paperlab agent-payload v${AGENT_PAYLOAD_VERSION})\n\n1. Install the dependencies:\n\n npm i paperlab three @react-three/fiber gsap @react-three/postprocessing postprocessing\n\n The last two are only needed by stage mode — <Paper> and <PaperField> do\n not use them — but <PaperStage> imports the print pass (bloom, tone curve,\n vignette, grain), so a stage will not build without them.\n\n2. Create the component below as \\`components/${name}.tsx\\` (or the project's\n component convention). It is self-contained — it owns its own <Canvas>,\n its own camera and its own lighting:\n\n\\`\\`\\`tsx\n${buildStageComponentSource(input)}\n\\`\\`\\`\n\n3. Placement: this is a full-bleed scene, not an inline element. Give it the\n full width of the viewport.\n\n${sizing}\n\n5. Verify: run the dev server. You should see ${describeStage(input)}.\n If the canvas is blank, the container almost certainly has no height — give\n it one (this is the classic React Three Fiber integration bug, not a\n paperlab bug).\n\nConstraints: don't modify the stage values; the camera is driven by the\nstage's own shot, so don't add OrbitControls; three >= 0.160 and React 19 are\nrequired; the component needs no props.`\n}\n"],"mappings":"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","names":["THREE","useFrame","useThree","useCallback","useEffect","useMemo","useRef","z","z","THREE","useEffect","useMemo","useFrame","z","jsx","useMemo","useEffect","useFrame","THREE","useEffect","useMemo","jsx","jsxs","useMemo","useEffect","THREE","useEffect","useMemo","useRef","jsx","jsxs","THREE","useEffect","useMemo","useRef","jsx","jsxs","useMemo","useEffect","useRef","jsx","jsxs","z","z","z","useEffect","useMemo","useRef","useFrame","useRef","useEffect","useFrame","useMemo","jsx","jsxs","useThree","useMemo","useFrame","useRef","useCallback","useEffect","z"]}