paperlab 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -45
- package/dist/{chunk-3IMUEESH.js → chunk-4ZU5DZEF.js} +1705 -1807
- package/dist/chunk-4ZU5DZEF.js.map +1 -0
- package/dist/index.cjs +493 -748
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +419 -1874
- package/dist/index.d.ts +419 -1874
- package/dist/index.js +183 -431
- package/dist/index.js.map +1 -1
- package/dist/{slots-D0Nc_5dq.d.cts → slots-BYoLxVZ4.d.cts} +3 -656
- package/dist/{slots-D0Nc_5dq.d.ts → slots-BYoLxVZ4.d.ts} +3 -656
- package/dist/stage.cjs +3 -17
- package/dist/stage.cjs.map +1 -1
- package/dist/stage.d.cts +2 -178
- package/dist/stage.d.ts +2 -178
- package/dist/stage.js +1 -8
- package/package.json +1 -1
- package/dist/chunk-3IMUEESH.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import * as THREE from 'three';
|
|
4
|
-
import { P as PaperConfig,
|
|
5
|
-
export {
|
|
4
|
+
import { P as PaperConfig, a as PaperConfigInput, S as SheetConfig, b as StockName, C as ContentConfigInput, B as BehaviorConfigInput, D as DeformerInstanceConfigInput, c as SurfaceConfigInput, d as SceneConfigInput, e as PhysicsConfigInput, F as FieldPaperSlot, f as ContentConfig, g as PaperStatesInput, L as LightingName, h as FilmName } from './slots-BYoLxVZ4.js';
|
|
5
|
+
export { i as BackContentConfig, j as BehaviorConfig, k as ClothConfig, l as CoreStateName, m as DeformerInstanceConfig, n as PaperEdge, o as PaperStates, p as PhysicsConfig, q as SceneConfig, r as StateDef, s as StateName, t as StateTransitionConfig, u as SurfaceConfig, v as behaviorConfigSchema, w as clothConfigSchema, x as coreStateNames, y as lightingNames, z as paperConfigSchema, A as paperEdges, E as paperStatesSchema, G as physicsNames, H as stateDefSchema, I as stockNames } from './slots-BYoLxVZ4.js';
|
|
6
6
|
import { z } from 'zod';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* The interaction-state engine. A state is a set of parameter overrides on
|
|
10
|
-
* the base preset — never a separate preset
|
|
10
|
+
* the base preset — never a separate preset. The machine
|
|
11
11
|
* resolves each state to a full config (base + overrides), keeps ONE live
|
|
12
12
|
* tween target of flattened numeric leaves, and always tweens FROM CURRENT
|
|
13
13
|
* VALUES: a pointer that enters/leaves rapidly retargets the same tween
|
|
14
14
|
* values instead of stacking or snapping.
|
|
15
15
|
*
|
|
16
|
-
* Delivery split (
|
|
16
|
+
* Delivery split (GSAP owns values, useFrame owns uploads):
|
|
17
17
|
* GSAP animates the STABLE `flat` object in place; per-tick values are polled
|
|
18
18
|
* off `liveConfig` by the consumer's frame loop, never pushed through React.
|
|
19
19
|
* `onChange` fires only on STRUCTURAL boundaries (a transition's start and
|
|
@@ -22,10 +22,6 @@ import { z } from 'zod';
|
|
|
22
22
|
*/
|
|
23
23
|
/** Built-in trigger events (v1 — user-defined wiring is parked for editor v2). */
|
|
24
24
|
type StateEvent = 'enter' | 'leave' | 'down' | 'up' | 'pick' | 'place' | 'return';
|
|
25
|
-
/** The fixed v1 transition table: pointer flow + pick/drop flow. */
|
|
26
|
-
declare const stateEventTransitions: Record<string, Partial<Record<StateEvent, StateName>>>;
|
|
27
|
-
/** The base config with `states` stripped — what a state resolves against. */
|
|
28
|
-
declare function stripStates(config: PaperConfig): PaperConfig;
|
|
29
25
|
/**
|
|
30
26
|
* Resolve a state name to its full config: base + that state's overrides.
|
|
31
27
|
* States without a recorded def (e.g. an untouched 'rest') are the base.
|
|
@@ -43,7 +39,6 @@ declare function resolveStateConfig(base: PaperConfig, state: string): PaperConf
|
|
|
43
39
|
* cleanly throws here.
|
|
44
40
|
*/
|
|
45
41
|
declare function recordStateOverride(config: PaperConfig, stateName: string, patch: Record<string, unknown>): PaperConfig;
|
|
46
|
-
declare function flattenNumeric(value: unknown, prefix?: string, out?: Record<string, number>): Record<string, number>;
|
|
47
42
|
interface PaperStateMachineOptions {
|
|
48
43
|
/** Reduced motion: every transition applies instantly (duration 0). */
|
|
49
44
|
instant?: boolean;
|
|
@@ -204,8 +199,6 @@ interface PaperHandle {
|
|
|
204
199
|
/** Instant, legal return (picked → rest). */
|
|
205
200
|
returnProgrammatic(): boolean;
|
|
206
201
|
}
|
|
207
|
-
/** Resolve preset + prop overrides into a validated config. */
|
|
208
|
-
declare function resolveConfig(props: PaperMeshProps): PaperConfig;
|
|
209
202
|
/**
|
|
210
203
|
* The atom: one sheet of paper, hero-mode CPU path. The deformer stack (or
|
|
211
204
|
* the cloth sim — never both) writes geometry positions each frame. GSAP
|
|
@@ -249,26 +242,6 @@ interface PlacedPaper {
|
|
|
249
242
|
interface DropZoneProps extends DropZoneConfig {
|
|
250
243
|
onPlace?(paper: PlacedPaper, zone: string): void;
|
|
251
244
|
}
|
|
252
|
-
interface ZoneEntry extends DropZoneConfig {
|
|
253
|
-
onPlace?: DropZoneProps['onPlace'];
|
|
254
|
-
}
|
|
255
|
-
/** Shared zone state: `<DropZone>` children register; the carry loop hit-tests. */
|
|
256
|
-
declare class DropZoneRegistry {
|
|
257
|
-
private zones;
|
|
258
|
-
private hoveredId;
|
|
259
|
-
private listeners;
|
|
260
|
-
private version;
|
|
261
|
-
register(zone: ZoneEntry): () => void;
|
|
262
|
-
list(): ZoneEntry[];
|
|
263
|
-
get(id: string): ZoneEntry | undefined;
|
|
264
|
-
get hovered(): string | null;
|
|
265
|
-
setHovered(id: string | null): void;
|
|
266
|
-
subscribe: (fn: () => void) => (() => void);
|
|
267
|
-
getVersion: () => number;
|
|
268
|
-
private notify;
|
|
269
|
-
}
|
|
270
|
-
/** True when `name` matches the zone's accept globs (all zones accept by default). */
|
|
271
|
-
declare function zoneAccepts(zone: Pick<DropZoneConfig, 'accept'>, name: string): boolean;
|
|
272
245
|
/**
|
|
273
246
|
* A drop target inside a `<PaperField>`. While a paper is picked, its center
|
|
274
247
|
* is tested against the bounds each frame; hovering applies the highlight
|
|
@@ -301,7 +274,7 @@ interface DeformerContext {
|
|
|
301
274
|
* A deformer is a pure vertex-mapping function. The JS implementation is the
|
|
302
275
|
* CPU path (hero mode) and must be allocation-free in the loop: it mutates
|
|
303
276
|
* `out` in place. The mirrored GLSL implementation (GPU path, field mode)
|
|
304
|
-
* lands
|
|
277
|
+
* lands with field mode — golden-vector parity tests will enforce identical results.
|
|
305
278
|
*/
|
|
306
279
|
interface Deformer<O = Record<string, unknown>> {
|
|
307
280
|
id: string;
|
|
@@ -310,7 +283,7 @@ interface Deformer<O = Record<string, unknown>> {
|
|
|
310
283
|
optionsSchema: z.ZodType<O, z.ZodTypeDef, unknown>;
|
|
311
284
|
/** Mutate `out` (sheet-local space; flat sheet is the XY plane facing +Z). */
|
|
312
285
|
displace(out: THREE.Vector3, uv: THREE.Vector2, o: O, ctx: DeformerContext): void;
|
|
313
|
-
/** GPU path — arrives with field mode
|
|
286
|
+
/** GPU path — arrives with field mode. */
|
|
314
287
|
glsl?: {
|
|
315
288
|
chunk: string;
|
|
316
289
|
uniforms(o: O): Record<string, unknown>;
|
|
@@ -373,37 +346,6 @@ interface DeformerInstance<O = Record<string, unknown>> {
|
|
|
373
346
|
enabled?: boolean;
|
|
374
347
|
}
|
|
375
348
|
|
|
376
|
-
declare const cornerNames: readonly ["top-left", "top-right", "bottom-left", "bottom-right"];
|
|
377
|
-
declare const curlOptionsSchema: z.ZodObject<{
|
|
378
|
-
corner: z.ZodDefault<z.ZodEnum<["top-left", "top-right", "bottom-left", "bottom-right"]>>;
|
|
379
|
-
/** How far the curl has traveled from the corner, as a fraction of the diagonal. */
|
|
380
|
-
amount: z.ZodDefault<z.ZodNumber>;
|
|
381
|
-
/** Cylinder radius — curl sharpness. */
|
|
382
|
-
radius: z.ZodDefault<z.ZodNumber>;
|
|
383
|
-
/** Skew of the fold line away from the corner diagonal, degrees. */
|
|
384
|
-
skew: z.ZodDefault<z.ZodNumber>;
|
|
385
|
-
}, "strip", z.ZodTypeAny, {
|
|
386
|
-
corner: "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
387
|
-
amount: number;
|
|
388
|
-
radius: number;
|
|
389
|
-
skew: number;
|
|
390
|
-
}, {
|
|
391
|
-
corner?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | undefined;
|
|
392
|
-
amount?: number | undefined;
|
|
393
|
-
radius?: number | undefined;
|
|
394
|
-
skew?: number | undefined;
|
|
395
|
-
}>;
|
|
396
|
-
type CurlOptions = z.infer<typeof curlOptionsSchema>;
|
|
397
|
-
/**
|
|
398
|
-
* Corner-anchored cylinder wrap — the peel/dog-ear deformer, and the
|
|
399
|
-
* crown-jewel realism case: the mesh genuinely wraps the cylinder so content
|
|
400
|
-
* bends with perfect continuity and the backside becomes visible.
|
|
401
|
-
*
|
|
402
|
-
* The fold line runs perpendicular to the corner diagonal and travels inward
|
|
403
|
-
* with `amount`; everything cornerward of it wraps around the cylinder.
|
|
404
|
-
*/
|
|
405
|
-
declare const curl: Deformer<CurlOptions>;
|
|
406
|
-
|
|
407
349
|
/**
|
|
408
350
|
* Pure grid math for the `sheet` layout — a block of stamps on a shared
|
|
409
351
|
* backing. Shared by the layout's pose function, the backing silhouette
|
|
@@ -439,40 +381,6 @@ declare const sheetLayoutSchema: z.ZodObject<{
|
|
|
439
381
|
backingMargin?: number | undefined;
|
|
440
382
|
}>;
|
|
441
383
|
type SheetLayoutOptions = z.infer<typeof sheetLayoutSchema>;
|
|
442
|
-
/** Backing thickness + ε — papers float just above the backing sheet. */
|
|
443
|
-
declare const SHEET_LIFT = 0.012;
|
|
444
|
-
/**
|
|
445
|
-
* Cell footprint = the paper's own sheet dims unless the user set an
|
|
446
|
-
* explicit cellWidth/cellHeight — so `gutter` is literally the spacing
|
|
447
|
-
* between stamps, whatever preset populates the grid.
|
|
448
|
-
*/
|
|
449
|
-
declare function withSheetCellFromPaper(parsed: SheetLayoutOptions, rawOptions: Record<string, unknown> | undefined, paperDims: {
|
|
450
|
-
width: number;
|
|
451
|
-
height: number;
|
|
452
|
-
} | undefined): SheetLayoutOptions;
|
|
453
|
-
declare function sheetSlotXY(i: number, o: SheetLayoutOptions): {
|
|
454
|
-
x: number;
|
|
455
|
-
y: number;
|
|
456
|
-
};
|
|
457
|
-
/** Grid bounds + margin — the backing sheet's size. */
|
|
458
|
-
declare function sheetBackingSize(o: SheetLayoutOptions): {
|
|
459
|
-
width: number;
|
|
460
|
-
height: number;
|
|
461
|
-
};
|
|
462
|
-
type Corner = CurlOptions['corner'];
|
|
463
|
-
/**
|
|
464
|
-
* The corner facing away from the sheet's center — what a thumb would find
|
|
465
|
-
* (spec M6 §1.3). A tie (an odd grid's exact-center row/column, where the
|
|
466
|
-
* cell straddles the midline) breaks outward-and-down: strict `<` on both
|
|
467
|
-
* axes sends the center right and down, so a dead-center cell peels
|
|
468
|
-
* bottom-right — the standalone peel default.
|
|
469
|
-
*/
|
|
470
|
-
declare function outwardCorner(i: number, o: Pick<SheetLayoutOptions, 'rows' | 'columns'>): Corner;
|
|
471
|
-
/**
|
|
472
|
-
* Perforation auto-wiring on detach: edges that faced a neighboring slot tear
|
|
473
|
-
* through; edges on the sheet's outer boundary keep their clean punches.
|
|
474
|
-
*/
|
|
475
|
-
declare function tornEdgesOnDetach(i: number, o: Pick<SheetLayoutOptions, 'rows' | 'columns'>): Partial<Record<PaperEdge, 'torn' | 'intact'>>;
|
|
476
384
|
|
|
477
385
|
/** Imperative field controls for the hidden a11y keyboard flow. */
|
|
478
386
|
interface FieldA11yController {
|
|
@@ -483,26 +391,6 @@ interface FieldA11yController {
|
|
|
483
391
|
slotState(slot: number): string;
|
|
484
392
|
}
|
|
485
393
|
|
|
486
|
-
/** Carry state of the hidden keyboard mirror: which paper is aloft, which zone is focused. */
|
|
487
|
-
interface KeyboardCarry {
|
|
488
|
-
slot: number;
|
|
489
|
-
zoneIndex: number;
|
|
490
|
-
}
|
|
491
|
-
/** A keyboard step's decision: the next carry state and whether it consumed the key. */
|
|
492
|
-
interface KeyboardStepResult {
|
|
493
|
-
carry: KeyboardCarry | null;
|
|
494
|
-
handled: boolean;
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* The M6 §6 keyboard flow as a pure step (so it's testable without a DOM):
|
|
498
|
-
* given the current carry state, the focused paper `slot`, and the pressed
|
|
499
|
-
* `key`, it drives the field `controller` (pick → move between zones → place /
|
|
500
|
-
* cancel) and returns the next carry state. All side effects go through
|
|
501
|
-
* `controller`; the caller applies `carry` to its state and calls
|
|
502
|
-
* `preventDefault()` when `handled` is true.
|
|
503
|
-
*/
|
|
504
|
-
declare function fieldKeyboardStep(carry: KeyboardCarry | null, slot: number, key: string, controller: FieldA11yController): KeyboardStepResult;
|
|
505
|
-
|
|
506
394
|
interface PaperFieldMeshProps {
|
|
507
395
|
/** Per-paper slots; length sets the instance count. Slot presets override the shared one. */
|
|
508
396
|
papers?: FieldPaperSlot[];
|
|
@@ -570,152 +458,379 @@ declare const PaperFieldMesh: react.ForwardRefExoticComponent<PaperFieldMeshProp
|
|
|
570
458
|
/** `<PaperField />` owns its own Canvas; PaperFieldMesh drops into existing scenes. */
|
|
571
459
|
declare const PaperField: react.ForwardRefExoticComponent<PaperFieldProps & react.RefAttributes<THREE.Group<THREE.Object3DEventMap>>>;
|
|
572
460
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
machine: PaperStateMachine | null;
|
|
580
|
-
}
|
|
461
|
+
declare function getPreset(name: string): PaperConfig;
|
|
462
|
+
/** Register a user preset (validated). Built-in names are reserved. */
|
|
463
|
+
declare function registerPreset(name: string, input: PaperConfigInput): void;
|
|
464
|
+
declare function unregisterPreset(name: string): void;
|
|
465
|
+
declare function isBuiltinPreset(name: string): boolean;
|
|
466
|
+
declare function listPresets(): string[];
|
|
581
467
|
/**
|
|
582
|
-
*
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
468
|
+
* A collision-free preset name built from `base`: `base`, else `base 2`,
|
|
469
|
+
* `base 3`, … The disambiguating suffix always grows from the SAME base — a
|
|
470
|
+
* name derived from a synthetic base (e.g. an untitled import → "imported")
|
|
471
|
+
* must not fall back to the original when it collides. `taken` reports whether
|
|
472
|
+
* a candidate is already used (built-in or user preset).
|
|
473
|
+
*/
|
|
474
|
+
declare function uniquePresetName(base: string, taken: (name: string) => boolean): string;
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Deep-merge preset config with prop overrides (overrides win; arrays and
|
|
478
|
+
* discriminated unions replace wholesale, plain objects merge).
|
|
586
479
|
*
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
* rebase), not per frame. Per-tick numeric values are read straight off
|
|
590
|
-
* `machine.liveConfig` by the caller's frame loop — GSAP owns values, useFrame
|
|
591
|
-
* owns uploads. `machine` is returned so the caller can poll it.
|
|
480
|
+
* Lives in its own module (not serialize.ts) because the schema needs it to
|
|
481
|
+
* validate state overrides — schema → serialize would be circular.
|
|
592
482
|
*/
|
|
593
|
-
declare function
|
|
483
|
+
declare function mergeConfig<T>(base: T, override: unknown): T;
|
|
484
|
+
/**
|
|
485
|
+
* Like {@link mergeConfig}, but an explicit `undefined` DELETES its key instead
|
|
486
|
+
* of being ignored — the semantics a BASE-config write needs: structural
|
|
487
|
+
* setters can clear `behavior`/`deformers` or toggle a surface effect off.
|
|
488
|
+
* Discriminated unions (differing `type`) still replace wholesale.
|
|
489
|
+
*/
|
|
490
|
+
declare function mergeWithDeletes<T>(base: T, patch: unknown): T;
|
|
491
|
+
|
|
492
|
+
/** Parse anything preset-shaped (object or JSON string) into a full, defaulted config. */
|
|
493
|
+
declare function parsePreset(input: PaperConfigInput | string): PaperConfig;
|
|
494
|
+
/** Serialize a config to `.paper` JSON. */
|
|
495
|
+
declare function serializePreset(config: PaperConfig): string;
|
|
594
496
|
|
|
497
|
+
/** The minimal PaperConfigInput that parses back to `config`. */
|
|
498
|
+
declare function diffConfig(config: PaperConfig): PaperConfigInput;
|
|
595
499
|
/**
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
* (picked/placed), its silhouette lightens further and gains a faint
|
|
599
|
-
* adhesive-sheen gradient — the visual proof of removal. Redrawn on state
|
|
600
|
-
* change, never per-frame.
|
|
500
|
+
* A plain `<Paper />` snippet with only the non-default props — the
|
|
501
|
+
* secondary export for people who read code.
|
|
601
502
|
*/
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
}
|
|
609
|
-
/** Pure: where each slot's silhouette sits on the backing, in canvas UV. */
|
|
610
|
-
declare function silhouetteRects(o: SheetLayoutOptions, count: number): SilhouetteRect[];
|
|
611
|
-
/** Lighten a hex tint toward white by `amount` (0..1). */
|
|
612
|
-
declare function lightenHex(hex: string, amount: number): string;
|
|
613
|
-
interface BackingDrawSpec {
|
|
614
|
-
options: SheetLayoutOptions;
|
|
615
|
-
count: number;
|
|
616
|
-
tint: string;
|
|
617
|
-
/** Slot indices whose paper has been picked/placed away. */
|
|
618
|
-
removed: ReadonlySet<number>;
|
|
619
|
-
}
|
|
620
|
-
/** Draw the backing content onto a canvas (called on state change only). */
|
|
621
|
-
declare function drawBacking(canvas: HTMLCanvasElement, spec: BackingDrawSpec): void;
|
|
503
|
+
declare function buildJsxSnippet(config: PaperConfig): string;
|
|
504
|
+
|
|
505
|
+
/** One line an agent can verify against what it sees after `npm run dev`. */
|
|
506
|
+
declare function describeConfig(config: PaperConfig): string;
|
|
507
|
+
/** The self-contained integration brief — one paste into a coding agent. */
|
|
508
|
+
declare function buildAgentPayload(config: PaperConfig): string;
|
|
622
509
|
|
|
623
510
|
/**
|
|
624
|
-
*
|
|
625
|
-
*
|
|
626
|
-
*
|
|
627
|
-
* PaperLighting.tsx. Serialized into presets as `scene.lighting`.
|
|
628
|
-
*
|
|
629
|
-
* A preset is the *starting point*, not the ceiling. `lightSchema` below is
|
|
630
|
-
* the art-directable half: a handful of overrides that ride on top of a
|
|
631
|
-
* named preset, so "nave, but the sun is lower and the room is dimmer" is a
|
|
632
|
-
* thing you can say — and serialize — instead of a preset you have to fork.
|
|
511
|
+
* Field-mode export. Every preset a field references is INLINED as a const —
|
|
512
|
+
* receivers don't have the sender's local preset library. Same fixed payload
|
|
513
|
+
* anatomy as the single-paper export, same version.
|
|
633
514
|
*/
|
|
634
|
-
interface
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
515
|
+
interface FieldExportPaper {
|
|
516
|
+
/** Display name of the preset this slot uses (const naming + description). */
|
|
517
|
+
presetName: string;
|
|
518
|
+
/** The RESOLVED preset config (user presets included — nothing to fetch). */
|
|
519
|
+
preset: PaperConfig;
|
|
520
|
+
content?: ContentConfig;
|
|
521
|
+
/** Per-slot state overrides (slot 3's hover deeper than slot 4's). */
|
|
522
|
+
states?: PaperStatesInput;
|
|
523
|
+
}
|
|
524
|
+
interface FieldExportZone {
|
|
525
|
+
id: string;
|
|
526
|
+
accept?: string[];
|
|
527
|
+
bounds: {
|
|
641
528
|
position: [number, number, number];
|
|
529
|
+
size: [number, number];
|
|
642
530
|
};
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
* `exposure` picks the stop; this picks the FILM, and on a subject that is
|
|
652
|
-
* almost white the film is the louder of the two. Every preset ships
|
|
653
|
-
* `neutral` — Khronos PBR Neutral — because it is the only one of the
|
|
654
|
-
* three that keeps a clipping sheet BOTH bright and the colour it actually
|
|
655
|
-
* is. See the note on `filmNames` for what the other two do to warm light.
|
|
656
|
-
*/
|
|
657
|
-
film: FilmName;
|
|
658
|
-
shadow: {
|
|
659
|
-
mapSize: number;
|
|
660
|
-
radius: number;
|
|
661
|
-
};
|
|
662
|
-
gobo?: {
|
|
663
|
-
kind: 'blinds' | 'leaves';
|
|
664
|
-
drift: number;
|
|
665
|
-
angle: number;
|
|
666
|
-
};
|
|
667
|
-
/**
|
|
668
|
-
* Distance haze. Depth in a deep space is staged almost entirely by fog —
|
|
669
|
-
* it is what turns a row of banners into a receding colonnade instead of
|
|
670
|
-
* a flat row of rectangles.
|
|
671
|
-
*/
|
|
672
|
-
fog?: {
|
|
673
|
-
color: string;
|
|
674
|
-
near: number;
|
|
675
|
-
far: number;
|
|
531
|
+
highlight?: 'none' | 'glow' | 'outline';
|
|
532
|
+
}
|
|
533
|
+
interface FieldExportInput {
|
|
534
|
+
layout: string;
|
|
535
|
+
layoutOptions?: Record<string, unknown>;
|
|
536
|
+
motion?: {
|
|
537
|
+
driver?: 'autoplay' | 'drag' | 'none';
|
|
538
|
+
speed?: number;
|
|
676
539
|
};
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
* single biggest reason a surface reads flat. This is the same brightness
|
|
682
|
-
* with a SHAPE — an environment built from the sky below, so a sheet
|
|
683
|
-
* turned toward the bright side of the room gets more light than one
|
|
684
|
-
* turned away, and paper's sheen finally has something to reflect.
|
|
685
|
-
*/
|
|
686
|
-
studio: number;
|
|
687
|
-
/**
|
|
688
|
-
* The room, as three colours. It grades zenith → horizon → floor, carries
|
|
689
|
-
* a soft disc of the key's own colour where the key stands, and becomes
|
|
690
|
-
* both the environment map and (in stage mode) the cyclorama, so the
|
|
691
|
-
* light and the space it is in cannot disagree.
|
|
692
|
-
*/
|
|
693
|
-
sky: {
|
|
694
|
-
zenith: string;
|
|
695
|
-
horizon: string;
|
|
696
|
-
ground: string;
|
|
540
|
+
entrance?: {
|
|
541
|
+
type?: 'rise' | 'scatter' | 'none';
|
|
542
|
+
stagger?: number;
|
|
543
|
+
duration?: number;
|
|
697
544
|
};
|
|
545
|
+
papers: FieldExportPaper[];
|
|
546
|
+
/** Drop zones — exported as `<DropZone>` children with an onPlace stub. */
|
|
547
|
+
zones?: FieldExportZone[];
|
|
548
|
+
}
|
|
549
|
+
/** The layout/motion/entrance props of the export, defaults stripped. */
|
|
550
|
+
declare function diffFieldProps(input: FieldExportInput): Record<string, unknown>;
|
|
551
|
+
/** The one-line visual an agent verifies after `npm run dev`. */
|
|
552
|
+
declare function describeFieldConfig(input: FieldExportInput): string;
|
|
553
|
+
/** Component source shared by the JSX snippet and the agent payload. */
|
|
554
|
+
declare function buildFieldComponentSource(input: FieldExportInput): string;
|
|
555
|
+
/** The self-contained field integration brief — one paste into a coding agent. */
|
|
556
|
+
declare function buildFieldAgentPayload(input: FieldExportInput): string;
|
|
557
|
+
|
|
558
|
+
/** Community deformers register here; built-ins are pre-registered. */
|
|
559
|
+
declare function registerDeformer(deformer: Deformer<AnyOptions>): void;
|
|
560
|
+
declare function getDeformer(id: string): Deformer<AnyOptions>;
|
|
561
|
+
declare function listDeformers(): string[];
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* The shared fake-aerodynamics core: velocity-linked lift +
|
|
565
|
+
* curated noise, per v0.2's "reads more real than a true sim" doctrine.
|
|
566
|
+
* Pure math, no three.js, no allocation in the per-frame paths — `carry`
|
|
567
|
+
* and `flight` both source their motion here.
|
|
568
|
+
*/
|
|
569
|
+
interface AeroPose {
|
|
570
|
+
position: [number, number, number];
|
|
571
|
+
rotation: [number, number, number];
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/** A draggable 3D grab point; drags write back to behavior params. */
|
|
575
|
+
interface HandleSpec<O = AnyOptions> {
|
|
576
|
+
id: string;
|
|
577
|
+
/** UV-space (0..1) anchor of the grab point on the flat sheet. */
|
|
578
|
+
anchor(o: O, sheet: SheetDims): [number, number];
|
|
579
|
+
/** Map a pointer position in sheet-local XY to option updates. */
|
|
580
|
+
drag(local: {
|
|
581
|
+
x: number;
|
|
582
|
+
y: number;
|
|
583
|
+
}, o: O, sheet: SheetDims): Partial<O>;
|
|
698
584
|
}
|
|
699
|
-
declare const lightingPresets: Record<LightingName, LightingPreset>;
|
|
700
|
-
declare function getLightingPreset(name: LightingName): LightingPreset;
|
|
701
585
|
/**
|
|
702
|
-
*
|
|
703
|
-
*
|
|
704
|
-
*
|
|
705
|
-
*
|
|
706
|
-
*
|
|
707
|
-
* another preset keeps your intent instead of your numbers.
|
|
586
|
+
* A behavior is a named, curated bundle: a deformer stack + a parameter
|
|
587
|
+
* mapping + optional idle loop + handles for direct manipulation. Designers
|
|
588
|
+
* see 3–5 human-named params ('tightness', not 'cylinderRadius'); the stack
|
|
589
|
+
* underneath is an Advanced disclosure. New behaviors are the community
|
|
590
|
+
* on-ramp: ~50 lines over existing deformers.
|
|
708
591
|
*/
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
592
|
+
interface Behavior<O = AnyOptions> {
|
|
593
|
+
id: string;
|
|
594
|
+
label: string;
|
|
595
|
+
defaults: O;
|
|
596
|
+
optionsSchema: z.ZodType<O, z.ZodTypeDef, unknown>;
|
|
597
|
+
/** Expand human params to the underlying deformer stack. */
|
|
598
|
+
stack(o: O, sheet: SheetDims): DeformerInstance[];
|
|
599
|
+
/** Transient, time-varying option overrides (idle motion). Never persisted. */
|
|
600
|
+
loop?(o: O, t: number): Partial<O>;
|
|
712
601
|
/**
|
|
713
|
-
*
|
|
714
|
-
*
|
|
715
|
-
*
|
|
716
|
-
*
|
|
717
|
-
|
|
718
|
-
|
|
602
|
+
* Whole-sheet motion written into `pose` each frame (allocation-free),
|
|
603
|
+
* composed after any idle preset's transform. Must be a pure function of
|
|
604
|
+
* (options, t) — the field applies it per instance with a time offset, so
|
|
605
|
+
* it has to be deterministic (flight's travel-across-the-scene).
|
|
606
|
+
*/
|
|
607
|
+
transform?(o: O, t: number, pose: AeroPose): void;
|
|
608
|
+
handles?: HandleSpec<O>[];
|
|
609
|
+
/**
|
|
610
|
+
* The two or three options that ARE this behavior — the ones someone
|
|
611
|
+
* reaches for first, in the order they'd reach for them.
|
|
612
|
+
*
|
|
613
|
+
* The schema still generates a control for every option; this only says
|
|
614
|
+
* which ones get the big controls and which fold away behind "More". A
|
|
615
|
+
* behavior that nominates nothing shows all of its options flat, because
|
|
616
|
+
* the library must never hide a param it was not told to hide — silence
|
|
617
|
+
* from a community behavior is not permission to guess.
|
|
618
|
+
*
|
|
619
|
+
* Every name here has to be a field of {@link optionsSchema}; the built-in
|
|
620
|
+
* behaviors are checked for that, and for staying within three.
|
|
621
|
+
*/
|
|
622
|
+
signature?: (keyof O & string)[];
|
|
623
|
+
/** The option the transport scrubber drives. */
|
|
624
|
+
progressParam: keyof O & string;
|
|
625
|
+
/** Seconds for a full 0→1 play, and how play repeats. */
|
|
626
|
+
duration: number;
|
|
627
|
+
loopMode: 'yoyo' | 'restart';
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/** Community behaviors register here; built-ins are pre-registered. */
|
|
631
|
+
declare function registerBehavior(behavior: Behavior<AnyOptions>): void;
|
|
632
|
+
declare function getBehavior(id: string): Behavior<AnyOptions>;
|
|
633
|
+
declare function listBehaviors(): string[];
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* A layout is a pure `pose(i, n, options, phase)` function — no state, no
|
|
637
|
+
* three.js. `phase` is the motion driver's continuous offset in turns
|
|
638
|
+
* (0..1 = one full cycle); cyclic layouts use it, static ones ignore it.
|
|
639
|
+
* Community layouts are ~30 lines.
|
|
640
|
+
*
|
|
641
|
+
* Every built-in names a place paper actually sits — a fanned swatch deck, a
|
|
642
|
+
* slipped stack, a heap on a desk — because arrangement alone is what makes a
|
|
643
|
+
* field read as a photo carousel instead of as paper. The other half of that
|
|
644
|
+
* is `bias`: paper in the world does not all bend alike.
|
|
645
|
+
*/
|
|
646
|
+
interface PaperPose {
|
|
647
|
+
position: [number, number, number];
|
|
648
|
+
rotation: [number, number, number];
|
|
649
|
+
scale: number;
|
|
650
|
+
/**
|
|
651
|
+
* How strongly this sheet takes the field's deformation: 1 = exactly as the
|
|
652
|
+
* preset configures it, 0 = flat. Lets one instanced draw call curl the top
|
|
653
|
+
* of a pile while the sheets pressed underneath stay flat. Omitted = 1.
|
|
654
|
+
*/
|
|
655
|
+
bias?: number;
|
|
656
|
+
}
|
|
657
|
+
interface Layout<O = Record<string, unknown>> {
|
|
658
|
+
id: string;
|
|
659
|
+
label: string;
|
|
660
|
+
defaults: O;
|
|
661
|
+
optionsSchema: z.ZodType<O, z.ZodTypeDef, unknown>;
|
|
662
|
+
/**
|
|
663
|
+
* `sheet` is the field's paper size. Layouts that arrange by CONTACT —
|
|
664
|
+
* edges meeting, sheets resting on each other — cannot work without it,
|
|
665
|
+
* and a layout that ignores it may simply omit the parameter.
|
|
666
|
+
*/
|
|
667
|
+
pose(i: number, n: number, o: O, phase: number, sheet: SheetDims): PaperPose;
|
|
668
|
+
/**
|
|
669
|
+
* Where along a walk this layout put each paper, as normalized arc length,
|
|
670
|
+
* in the layout's own index order.
|
|
671
|
+
*
|
|
672
|
+
* Only layouts that arrange along a PATH can answer, which is why it is
|
|
673
|
+
* optional. Stage mode uses it to let a viewer step from one paper to the
|
|
674
|
+
* next: the stops have to be where the paper actually is, and the only
|
|
675
|
+
* thing that knows that is the function that placed it.
|
|
676
|
+
*/
|
|
677
|
+
walkStops?(n: number, o: O): number[];
|
|
678
|
+
}
|
|
679
|
+
declare function registerLayout(layout: Layout<AnyOptions>): void;
|
|
680
|
+
declare function getLayout(id: string): Layout<AnyOptions>;
|
|
681
|
+
declare function listLayouts(): string[];
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* A stock is a named bundle of material + geometry defaults — choosing paper
|
|
685
|
+
* at a print shop. Individual controls always override; schema-wise these are
|
|
686
|
+
* just defaults.
|
|
687
|
+
*/
|
|
688
|
+
interface Stock {
|
|
689
|
+
id: StockName;
|
|
690
|
+
label: string;
|
|
691
|
+
/** Base tint, also used as the canvas background behind content. */
|
|
692
|
+
color: string;
|
|
693
|
+
roughness: number;
|
|
694
|
+
/** 0 = opaque. Vellum is translucent. */
|
|
695
|
+
opacity: number;
|
|
696
|
+
/**
|
|
697
|
+
* How much light passes THROUGH the sheet when something is behind it,
|
|
698
|
+
* 0..1. Distinct from `opacity`: newsprint is fully opaque to look at and
|
|
699
|
+
* still glows on a lightbox. This is what makes a backlit banner read.
|
|
700
|
+
*/
|
|
701
|
+
translucency: number;
|
|
702
|
+
/** Ink multiply tint for content drawn on this stock (thermal prints grey-black). */
|
|
703
|
+
inkColor: string;
|
|
704
|
+
/** Thermal-printer banding intensity baked into the grain effect. */
|
|
705
|
+
banding: number;
|
|
706
|
+
/** Surface effects this stock ships with; explicit surface config overrides per key. */
|
|
707
|
+
defaultSurface: {
|
|
708
|
+
grain?: number;
|
|
709
|
+
aging?: number;
|
|
710
|
+
};
|
|
711
|
+
/** Reversed front-content ghost on the backside (thin stocks let ink show). */
|
|
712
|
+
showThrough: number;
|
|
713
|
+
/** Glossy near-white glue underside (stickers) — forces showThrough 0. */
|
|
714
|
+
adhesive: boolean;
|
|
715
|
+
}
|
|
716
|
+
declare const stocks: Record<StockName, Stock>;
|
|
717
|
+
declare function getStock(name: StockName): Stock;
|
|
718
|
+
|
|
719
|
+
declare const idleNames: readonly ["float", "tumble", "dangle", "taped", "breeze"];
|
|
720
|
+
type IdleName = (typeof idleNames)[number];
|
|
721
|
+
interface IdlePose {
|
|
722
|
+
/** Offsets added to the paper's base transform each frame. */
|
|
723
|
+
position: [number, number, number];
|
|
724
|
+
rotation: [number, number, number];
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Curated fake physics: hand-tuned motion presets, no simulation. Cheap
|
|
728
|
+
* enough for both hero and field modes, and — for things like the
|
|
729
|
+
* falling-leaf tumble — they read *more* real than a true sim.
|
|
730
|
+
*/
|
|
731
|
+
interface IdlePreset {
|
|
732
|
+
id: IdleName;
|
|
733
|
+
label: string;
|
|
734
|
+
/** Whole-sheet motion, written into `pose` (allocation-free). */
|
|
735
|
+
transform?(t: number, pose: IdlePose): void;
|
|
736
|
+
/** Extra deformers appended after the behavior's stack. */
|
|
737
|
+
stack?(): DeformerInstance[];
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Lighting presets: each is a key light + ambient level + contact shadow +
|
|
742
|
+
* optional gobo (a texture the key light projects — window blinds, foliage).
|
|
743
|
+
* Pure data here (testable in node); textures and R3F live in
|
|
744
|
+
* PaperLighting.tsx. Serialized into presets as `scene.lighting`.
|
|
745
|
+
*
|
|
746
|
+
* A preset is the *starting point*, not the ceiling. `lightSchema` below is
|
|
747
|
+
* the art-directable half: a handful of overrides that ride on top of a
|
|
748
|
+
* named preset, so "nave, but the sun is lower and the room is dimmer" is a
|
|
749
|
+
* thing you can say — and serialize — instead of a preset you have to fork.
|
|
750
|
+
*/
|
|
751
|
+
interface LightingPreset {
|
|
752
|
+
id: LightingName;
|
|
753
|
+
label: string;
|
|
754
|
+
ambient: number;
|
|
755
|
+
key: {
|
|
756
|
+
color: string;
|
|
757
|
+
intensity: number;
|
|
758
|
+
position: [number, number, number];
|
|
759
|
+
};
|
|
760
|
+
contactShadowOpacity: number;
|
|
761
|
+
/** Contact shadow blur — hard for noir, long and soft for golden hour. */
|
|
762
|
+
contactShadowBlur: number;
|
|
763
|
+
/** Renderer tone-mapping exposure while active. */
|
|
764
|
+
exposure: number;
|
|
765
|
+
/**
|
|
766
|
+
* The tone curve the picture is printed through.
|
|
767
|
+
*
|
|
768
|
+
* `exposure` picks the stop; this picks the FILM, and on a subject that is
|
|
769
|
+
* almost white the film is the louder of the two. Every preset ships
|
|
770
|
+
* `neutral` — Khronos PBR Neutral — because it is the only one of the
|
|
771
|
+
* three that keeps a clipping sheet BOTH bright and the colour it actually
|
|
772
|
+
* is. See the note on `filmNames` for what the other two do to warm light.
|
|
773
|
+
*/
|
|
774
|
+
film: FilmName;
|
|
775
|
+
shadow: {
|
|
776
|
+
mapSize: number;
|
|
777
|
+
radius: number;
|
|
778
|
+
};
|
|
779
|
+
gobo?: {
|
|
780
|
+
kind: 'blinds' | 'leaves';
|
|
781
|
+
drift: number;
|
|
782
|
+
angle: number;
|
|
783
|
+
};
|
|
784
|
+
/**
|
|
785
|
+
* Distance haze. Depth in a deep space is staged almost entirely by fog —
|
|
786
|
+
* it is what turns a row of banners into a receding colonnade instead of
|
|
787
|
+
* a flat row of rectangles.
|
|
788
|
+
*/
|
|
789
|
+
fog?: {
|
|
790
|
+
color: string;
|
|
791
|
+
near: number;
|
|
792
|
+
far: number;
|
|
793
|
+
};
|
|
794
|
+
/**
|
|
795
|
+
* The studio light: how strongly the room itself lights the paper.
|
|
796
|
+
*
|
|
797
|
+
* `<ambientLight>` adds brightness with no direction at all, which is the
|
|
798
|
+
* single biggest reason a surface reads flat. This is the same brightness
|
|
799
|
+
* with a SHAPE — an environment built from the sky below, so a sheet
|
|
800
|
+
* turned toward the bright side of the room gets more light than one
|
|
801
|
+
* turned away, and paper's sheen finally has something to reflect.
|
|
802
|
+
*/
|
|
803
|
+
studio: number;
|
|
804
|
+
/**
|
|
805
|
+
* The room, as three colours. It grades zenith → horizon → floor, carries
|
|
806
|
+
* a soft disc of the key's own colour where the key stands, and becomes
|
|
807
|
+
* both the environment map and (in stage mode) the cyclorama, so the
|
|
808
|
+
* light and the space it is in cannot disagree.
|
|
809
|
+
*/
|
|
810
|
+
sky: {
|
|
811
|
+
zenith: string;
|
|
812
|
+
horizon: string;
|
|
813
|
+
ground: string;
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Overrides on top of a named preset — the Blender-panel half of lighting.
|
|
818
|
+
*
|
|
819
|
+
* Every field is optional ON PURPOSE. An unset field means "whatever the
|
|
820
|
+
* preset says", so a shared link carries the two sliders you actually moved
|
|
821
|
+
* rather than a frozen copy of a rig you never touched, and re-basing onto
|
|
822
|
+
* another preset keeps your intent instead of your numbers.
|
|
823
|
+
*/
|
|
824
|
+
declare const lightSchema: z.ZodObject<{
|
|
825
|
+
/** Tone-mapping exposure — the stop the whole picture is printed at. */
|
|
826
|
+
exposure: z.ZodOptional<z.ZodNumber>;
|
|
827
|
+
/**
|
|
828
|
+
* The tone curve — the film, where `exposure` is the stop.
|
|
829
|
+
*
|
|
830
|
+
* `filmic` is ACES, which is what every preset used to be pinned to and is
|
|
831
|
+
* kept so a scene tuned against it can say so. On near-white paper it is
|
|
832
|
+
* the wrong film: it desaturates and drags bright neutrals toward
|
|
833
|
+
* yellow-green, which is the sepia cast a lit sheet used to pick up.
|
|
719
834
|
*/
|
|
720
835
|
film: z.ZodOptional<z.ZodEnum<["agx", "neutral", "filmic"]>>;
|
|
721
836
|
/** Key light strength. */
|
|
@@ -777,8 +892,6 @@ interface LightAngles {
|
|
|
777
892
|
* they have to agree.
|
|
778
893
|
*/
|
|
779
894
|
declare function lightAngles(position: readonly [number, number, number]): LightAngles;
|
|
780
|
-
/** The inverse: put a light back where those angles say it stands. */
|
|
781
|
-
declare function lightPosition(angles: LightAngles): [number, number, number];
|
|
782
895
|
/**
|
|
783
896
|
* A preset with the overrides applied — the rig everything else reads.
|
|
784
897
|
*
|
|
@@ -791,11 +904,6 @@ declare function lightPosition(angles: LightAngles): [number, number, number];
|
|
|
791
904
|
*/
|
|
792
905
|
declare function resolveLighting(base: LightingName | LightingPreset, overrides?: LightOverrides): LightingPreset;
|
|
793
906
|
|
|
794
|
-
/**
|
|
795
|
-
* Procedural gobo textures — no binary assets in the repo. White passes
|
|
796
|
-
* light, dark blocks it (SpotLight.map multiplies the beam).
|
|
797
|
-
*/
|
|
798
|
-
declare function makeGoboTexture(kind: 'blinds' | 'leaves'): THREE.CanvasTexture;
|
|
799
907
|
interface PaperLightingProps {
|
|
800
908
|
preset?: LightingName;
|
|
801
909
|
/**
|
|
@@ -845,33 +953,56 @@ declare function LightRig({ rig, children }: {
|
|
|
845
953
|
children: ReactNode;
|
|
846
954
|
}): react.JSX.Element;
|
|
847
955
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
956
|
+
interface UsePaperStatesResult {
|
|
957
|
+
/** The config to render this frame — animated when a machine is live. */
|
|
958
|
+
config: PaperConfig;
|
|
959
|
+
/** Current state name ('rest' when no machine). */
|
|
960
|
+
state: string;
|
|
961
|
+
/** Send triggers through this; null when states are absent or disabled. */
|
|
962
|
+
machine: PaperStateMachine | null;
|
|
963
|
+
}
|
|
852
964
|
/**
|
|
853
|
-
*
|
|
854
|
-
*
|
|
855
|
-
*
|
|
856
|
-
*
|
|
857
|
-
* naming an option that doesn't exist (`frequency` where wave wants
|
|
858
|
-
* `wavelength`) reached the GLSL builder as `undefined` and died there with
|
|
859
|
-
* a message about `.length`, or reached the CPU path and quietly produced
|
|
860
|
-
* NaN vertices. Parsing through each deformer's own schema turns that into
|
|
861
|
-
* the validation error it always was, and fills in defaults for whatever a
|
|
862
|
-
* hand-written preset left out.
|
|
965
|
+
* Bind a state machine to a config with `states`. The machine survives
|
|
966
|
+
* config edits via `rebase` (current state and live values are kept —
|
|
967
|
+
* flipping perforation to torn mid-pick must not snap the stamp to rest);
|
|
968
|
+
* it is only rebuilt when states are toggled on/off.
|
|
863
969
|
*
|
|
864
|
-
*
|
|
865
|
-
*
|
|
970
|
+
* `animated` holds an IMMUTABLE structural snapshot for the React tree; it
|
|
971
|
+
* updates only on structural boundaries (transition start/settle, state swap,
|
|
972
|
+
* rebase), not per frame. Per-tick numeric values are read straight off
|
|
973
|
+
* `machine.liveConfig` by the caller's frame loop — GSAP owns values, useFrame
|
|
974
|
+
* owns uploads. `machine` is returned so the caller can poll it.
|
|
866
975
|
*/
|
|
867
|
-
declare function
|
|
868
|
-
type: string;
|
|
869
|
-
options?: Record<string, unknown>;
|
|
870
|
-
enabled?: boolean;
|
|
871
|
-
}[]): DeformerInstance[];
|
|
976
|
+
declare function usePaperStates(config: PaperConfig, enabled: boolean, instant: boolean, onAction?: (event: string, state: string) => void, onStateChange?: (state: string) => void): UsePaperStatesResult;
|
|
872
977
|
|
|
873
978
|
/**
|
|
874
|
-
*
|
|
979
|
+
* Accessibility layer: reduced-motion handling, a hidden DOM mirror so the
|
|
980
|
+
* paper's content exists for screen readers and find-in-page, and a no-WebGL
|
|
981
|
+
* DOM fallback.
|
|
982
|
+
*/
|
|
983
|
+
declare function usePrefersReducedMotion(override?: boolean): boolean;
|
|
984
|
+
declare function supportsWebGL(): boolean;
|
|
985
|
+
|
|
986
|
+
/** highp float + sin/cos implementation differences across GPUs. */
|
|
987
|
+
declare const PARITY_EPSILON = 0.0005;
|
|
988
|
+
interface ParityCase {
|
|
989
|
+
name: string;
|
|
990
|
+
stack: DeformerInstance[];
|
|
991
|
+
sheet: SheetDims;
|
|
992
|
+
t: number;
|
|
993
|
+
}
|
|
994
|
+
interface ParityResult {
|
|
995
|
+
name: string;
|
|
996
|
+
maxError: number;
|
|
997
|
+
pass: boolean;
|
|
998
|
+
}
|
|
999
|
+
/** One case per deformer feature worth guarding, both easy and adversarial. */
|
|
1000
|
+
declare const parityCases: ParityCase[];
|
|
1001
|
+
/** Run every parity case. Requires a WebGL2 context with float render targets. */
|
|
1002
|
+
declare function runParityHarness(canvas?: HTMLCanvasElement): ParityResult[];
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* How finely a sheet has to be subdivided for a deformer to look like the
|
|
875
1006
|
* surface it is approximating instead of like the polygons it is made of.
|
|
876
1007
|
*
|
|
877
1008
|
* A deformed mesh is a piecewise-linear stand-in for a curved surface, and the
|
|
@@ -957,16 +1088,6 @@ declare const AUTO_CEILING = 192;
|
|
|
957
1088
|
declare const FLAT_SEGMENTS = 8;
|
|
958
1089
|
/** Smallest ladder step at or above `n`, clamped to the ceiling. */
|
|
959
1090
|
declare function quantizeSegments(n: number): number;
|
|
960
|
-
/**
|
|
961
|
-
* A resolved grid: segments along the sheet's own X and Y.
|
|
962
|
-
*
|
|
963
|
-
* Two numbers rather than one because a sheet is not subdivided by a single
|
|
964
|
-
* density. A banner 1.5 wide and 8.5 tall, draped in folds that run across
|
|
965
|
-
* its width, needs the folds resolved ACROSS and needs almost nothing down
|
|
966
|
-
* the drop — and a single number, however it is distributed, answers one of
|
|
967
|
-
* those questions by getting the other one wrong.
|
|
968
|
-
*/
|
|
969
|
-
type SegmentPair = [x: number, y: number];
|
|
970
1091
|
/**
|
|
971
1092
|
* Extent of the sheet along a direction in its own plane, degrees. For a
|
|
972
1093
|
* rectangle centered on the origin this is exactly `|w·cos| + |h·sin|` — the
|
|
@@ -991,1598 +1112,22 @@ declare function segmentsForArc(span: number, radius: number, tol?: number): num
|
|
|
991
1112
|
declare function segmentsForSine(span: number, amplitude: number, wavelength: number, tol?: number): number;
|
|
992
1113
|
|
|
993
1114
|
/**
|
|
994
|
-
*
|
|
995
|
-
* from its flat base position and flows through every enabled deformer in
|
|
996
|
-
* order. Writes positions in place and recomputes normals.
|
|
997
|
-
*/
|
|
998
|
-
declare function applyDeformerStack(geometry: THREE.BufferGeometry, basePositions: Float32Array, stack: DeformerInstance[], ctx: DeformerContext): void;
|
|
999
|
-
/** A single point through the stack — used for handle anchors and tests. */
|
|
1000
|
-
declare function displacePoint(point: THREE.Vector3, uvX: number, uvY: number, stack: DeformerInstance[], ctx: DeformerContext): THREE.Vector3;
|
|
1001
|
-
/**
|
|
1002
|
-
* The densest grid any deformer in the stack REQUIRES to work at all, per
|
|
1003
|
-
* axis — the componentwise max of every entry's floor projected onto the
|
|
1004
|
-
* sheet's own X and Y by the direction that entry curves in.
|
|
1005
|
-
*/
|
|
1006
|
-
declare function stackMinSegments(stack: DeformerInstance[], sheet: SheetDims): SegmentPair;
|
|
1007
|
-
/**
|
|
1008
|
-
* The densest grid any deformer in the stack WANTS, given the options it is
|
|
1009
|
-
* actually carrying — what `segments: 'auto'` resolves to, per axis.
|
|
1010
|
-
*
|
|
1011
|
-
* Disabled instances are skipped, exactly as `applyDeformerStack` skips them:
|
|
1012
|
-
* a deformer that is not displacing anything has no opinion about the grid.
|
|
1013
|
-
* (`stackMinSegments` does not skip them, and that difference is deliberate —
|
|
1014
|
-
* a floor is about what the stack could do, a target about what it is doing.)
|
|
1015
|
-
*
|
|
1016
|
-
* A deformer with no `autoSegments` falls back to its floor, which is the
|
|
1017
|
-
* right answer for one whose cost does not move with its options.
|
|
1018
|
-
*/
|
|
1019
|
-
declare function stackAutoSegments(stack: DeformerInstance[], sheet: SheetDims): SegmentPair;
|
|
1020
|
-
|
|
1021
|
-
declare const rollOptionsSchema: z.ZodObject<{
|
|
1022
|
-
/** Direction of rolling in the sheet plane, degrees. 0 = +x, 90 = +y. */
|
|
1023
|
-
angle: z.ZodDefault<z.ZodNumber>;
|
|
1024
|
-
/** Signed distance (along the roll direction, from sheet center) where the roll begins. */
|
|
1025
|
-
boundary: z.ZodDefault<z.ZodNumber>;
|
|
1026
|
-
/** Cylinder radius — sharpness of the roll. */
|
|
1027
|
-
radius: z.ZodDefault<z.ZodNumber>;
|
|
1028
|
-
/** Radius growth per radian so multi-turn rolls spiral instead of z-fighting. */
|
|
1029
|
-
spiral: z.ZodDefault<z.ZodNumber>;
|
|
1030
|
-
}, "strip", z.ZodTypeAny, {
|
|
1031
|
-
radius: number;
|
|
1032
|
-
angle: number;
|
|
1033
|
-
boundary: number;
|
|
1034
|
-
spiral: number;
|
|
1035
|
-
}, {
|
|
1036
|
-
radius?: number | undefined;
|
|
1037
|
-
angle?: number | undefined;
|
|
1038
|
-
boundary?: number | undefined;
|
|
1039
|
-
spiral?: number | undefined;
|
|
1040
|
-
}>;
|
|
1041
|
-
type RollOptions = z.infer<typeof rollOptionsSchema>;
|
|
1042
|
-
/**
|
|
1043
|
-
* Wrap the sheet around a virtual cylinder lying across the roll direction.
|
|
1044
|
-
* Everything past `boundary` wraps; the wrap is C¹-continuous at the
|
|
1045
|
-
* boundary and preserves arc length (content never stretches).
|
|
1046
|
-
*
|
|
1047
|
-
* Points arriving with z ≠ 0 (from earlier deformers in the stack) ride
|
|
1048
|
-
* along the rolled surface's normal, so stacks compose sanely.
|
|
1049
|
-
*/
|
|
1050
|
-
declare const roll: Deformer<RollOptions>;
|
|
1051
|
-
|
|
1052
|
-
declare const bendOptionsSchema: z.ZodObject<{
|
|
1053
|
-
/** 1/radius in world units; sign flips the arc direction. 0 = flat. */
|
|
1054
|
-
curvature: z.ZodDefault<z.ZodNumber>;
|
|
1055
|
-
/** Bend axis direction in the sheet plane, degrees. 0 bends across x. */
|
|
1056
|
-
angle: z.ZodDefault<z.ZodNumber>;
|
|
1057
|
-
}, "strip", z.ZodTypeAny, {
|
|
1058
|
-
angle: number;
|
|
1059
|
-
curvature: number;
|
|
1060
|
-
}, {
|
|
1061
|
-
angle?: number | undefined;
|
|
1062
|
-
curvature?: number | undefined;
|
|
1063
|
-
}>;
|
|
1064
|
-
type BendOptions = z.infer<typeof bendOptionsSchema>;
|
|
1065
|
-
/**
|
|
1066
|
-
* Gentle global arc around a cylinder centered on the sheet — a standing
|
|
1067
|
-
* paper's lean. Arc-length preserving, like roll, but symmetric about the
|
|
1068
|
-
* center instead of one-sided.
|
|
1069
|
-
*
|
|
1070
|
-
* Written in its cancellation-free form throughout: `r(1 − cos θ)` is
|
|
1071
|
-
* `2r·sin²(θ/2)`, and the in-plane shift goes through `sinMinusX`. Same arc,
|
|
1072
|
-
* same numbers to sixteen places — it is only the float32 half that could
|
|
1073
|
-
* tell the difference, and that is exactly the half the parity gate checks.
|
|
1074
|
-
*/
|
|
1075
|
-
declare const bend: Deformer<BendOptions>;
|
|
1076
|
-
|
|
1077
|
-
declare const foldOptionsSchema: z.ZodObject<{
|
|
1078
|
-
/** Direction of the fold travel in the sheet plane, degrees (the crease line runs perpendicular). */
|
|
1079
|
-
angle: z.ZodDefault<z.ZodNumber>;
|
|
1080
|
-
/** Signed distance of the crease line from the sheet center, along the travel direction. */
|
|
1081
|
-
offset: z.ZodDefault<z.ZodNumber>;
|
|
1082
|
-
/** How far the flap folds over, degrees. 180 = flat against the sheet. */
|
|
1083
|
-
foldAngle: z.ZodDefault<z.ZodNumber>;
|
|
1084
|
-
/** Width of the soft hinge — paper never creases to a mathematical edge. */
|
|
1085
|
-
radius: z.ZodDefault<z.ZodNumber>;
|
|
1086
|
-
}, "strip", z.ZodTypeAny, {
|
|
1087
|
-
radius: number;
|
|
1088
|
-
angle: number;
|
|
1089
|
-
offset: number;
|
|
1090
|
-
foldAngle: number;
|
|
1091
|
-
}, {
|
|
1092
|
-
radius?: number | undefined;
|
|
1093
|
-
angle?: number | undefined;
|
|
1094
|
-
offset?: number | undefined;
|
|
1095
|
-
foldAngle?: number | undefined;
|
|
1096
|
-
}>;
|
|
1097
|
-
type FoldOptions = z.infer<typeof foldOptionsSchema>;
|
|
1098
|
-
/**
|
|
1099
|
-
* Angular crease across a line: within the hinge width the sheet wraps a
|
|
1100
|
-
* small cylinder (a roll), beyond it the flap continues rigid at the full
|
|
1101
|
-
* fold angle. n folds = n instances stacked (half-fold, letter-fold,
|
|
1102
|
-
* accordion). Arc-length preserving like every Paperlab deformer.
|
|
1103
|
-
*/
|
|
1104
|
-
declare const fold: Deformer<FoldOptions>;
|
|
1105
|
-
|
|
1106
|
-
declare const waveOptionsSchema: z.ZodObject<{
|
|
1107
|
-
amplitude: z.ZodDefault<z.ZodNumber>;
|
|
1108
|
-
wavelength: z.ZodDefault<z.ZodNumber>;
|
|
1109
|
-
/** Travel speed; 0 freezes the ripple. */
|
|
1110
|
-
speed: z.ZodDefault<z.ZodNumber>;
|
|
1111
|
-
/** Travel direction in the sheet plane, degrees. */
|
|
1112
|
-
angle: z.ZodDefault<z.ZodNumber>;
|
|
1113
|
-
/** Zero the displacement at one edge (a taped/pinned edge doesn't ripple). */
|
|
1114
|
-
pinnedEdge: z.ZodDefault<z.ZodEnum<["none", "top", "bottom", "left", "right"]>>;
|
|
1115
|
-
}, "strip", z.ZodTypeAny, {
|
|
1116
|
-
angle: number;
|
|
1117
|
-
amplitude: number;
|
|
1118
|
-
wavelength: number;
|
|
1119
|
-
speed: number;
|
|
1120
|
-
pinnedEdge: "left" | "right" | "top" | "none" | "bottom";
|
|
1121
|
-
}, {
|
|
1122
|
-
angle?: number | undefined;
|
|
1123
|
-
amplitude?: number | undefined;
|
|
1124
|
-
wavelength?: number | undefined;
|
|
1125
|
-
speed?: number | undefined;
|
|
1126
|
-
pinnedEdge?: "left" | "right" | "top" | "none" | "bottom" | undefined;
|
|
1127
|
-
}>;
|
|
1128
|
-
type WaveOptions = z.infer<typeof waveOptionsSchema>;
|
|
1129
|
-
/**
|
|
1130
|
-
* Traveling sine displacement with a quieter second harmonic — idle flutter
|
|
1131
|
-
* and wind ripple. The only time-driven deformer so far: stacks containing
|
|
1132
|
-
* it re-deform every frame.
|
|
1133
|
-
*/
|
|
1134
|
-
declare const wave: Deformer<WaveOptions>;
|
|
1135
|
-
|
|
1136
|
-
declare const drapeOptionsSchema: z.ZodObject<{
|
|
1137
|
-
/** Fold depth at the free edge, world units. */
|
|
1138
|
-
amplitude: z.ZodDefault<z.ZodNumber>;
|
|
1139
|
-
/** How many folds run down the drop. */
|
|
1140
|
-
folds: z.ZodDefault<z.ZodNumber>;
|
|
1141
|
-
/**
|
|
1142
|
-
* How fast folds deepen away from the pinned edge. 1 is linear; higher
|
|
1143
|
-
* holds the top flat and gathers all the movement at the free end, which
|
|
1144
|
-
* is what a sheet hung from a rod actually does.
|
|
1145
|
-
*/
|
|
1146
|
-
falloff: z.ZodDefault<z.ZodNumber>;
|
|
1147
|
-
/** How much a second, non-harmonic fold breaks the regularity. */
|
|
1148
|
-
irregular: z.ZodDefault<z.ZodNumber>;
|
|
1149
|
-
/** How much the sheet narrows as its folds deepen. */
|
|
1150
|
-
gather: z.ZodDefault<z.ZodNumber>;
|
|
1151
|
-
pinnedEdge: z.ZodDefault<z.ZodEnum<["top", "bottom"]>>;
|
|
1152
|
-
}, "strip", z.ZodTypeAny, {
|
|
1153
|
-
amplitude: number;
|
|
1154
|
-
pinnedEdge: "top" | "bottom";
|
|
1155
|
-
folds: number;
|
|
1156
|
-
falloff: number;
|
|
1157
|
-
irregular: number;
|
|
1158
|
-
gather: number;
|
|
1159
|
-
}, {
|
|
1160
|
-
amplitude?: number | undefined;
|
|
1161
|
-
pinnedEdge?: "top" | "bottom" | undefined;
|
|
1162
|
-
folds?: number | undefined;
|
|
1163
|
-
falloff?: number | undefined;
|
|
1164
|
-
irregular?: number | undefined;
|
|
1165
|
-
gather?: number | undefined;
|
|
1166
|
-
}>;
|
|
1167
|
-
type DrapeOptions = z.infer<typeof drapeOptionsSchema>;
|
|
1168
|
-
/**
|
|
1169
|
-
* Hung paper: vertical folds running the length of the drop, shallow at the
|
|
1170
|
-
* fixed edge and deepening toward the free one.
|
|
1171
|
-
*
|
|
1172
|
-
* `wave` can put ripples on a sheet, but a traveling sine is a flag, not a
|
|
1173
|
-
* curtain — it kinks the sheet ACROSS its drop and it is uniform end to end.
|
|
1174
|
-
* Cloth hung from an edge does the opposite: the folds run WITH the drop and
|
|
1175
|
-
* they grow as they get further from whatever is holding the sheet up. Two
|
|
1176
|
-
* details do most of the work:
|
|
1177
|
-
*
|
|
1178
|
-
* - the folds are not harmonic. A pure sine reads as corrugated metal, so a
|
|
1179
|
-
* second wave at an incommensurate frequency breaks the repeat.
|
|
1180
|
-
* - gathered paper is narrower than flat paper. Pulling the surface toward
|
|
1181
|
-
* its centerline in proportion to fold depth is what stops the drape from
|
|
1182
|
-
* looking like a texture painted on a rectangle.
|
|
1183
|
-
*/
|
|
1184
|
-
declare const drape: Deformer<DrapeOptions>;
|
|
1185
|
-
|
|
1186
|
-
declare const crumpleOptionsSchema: z.ZodObject<{
|
|
1187
|
-
/** How crushed, 0..1. Peak-to-peak height, and it drives the pull too. */
|
|
1188
|
-
amount: z.ZodDefault<z.ZodNumber>;
|
|
1189
|
-
/** Facets per world unit. Higher is finer, and needs more segments to resolve. */
|
|
1190
|
-
scale: z.ZodDefault<z.ZodNumber>;
|
|
1191
|
-
/**
|
|
1192
|
-
* How much the sheet draws in on itself. Crumpled paper occupies a smaller
|
|
1193
|
-
* footprint than flat paper; without this it reads as an embossed sheet
|
|
1194
|
-
* rather than a crushed one.
|
|
1195
|
-
*/
|
|
1196
|
-
pull: z.ZodDefault<z.ZodNumber>;
|
|
1197
|
-
/** A different crush of the same paper. */
|
|
1198
|
-
seed: z.ZodDefault<z.ZodNumber>;
|
|
1199
|
-
}, "strip", z.ZodTypeAny, {
|
|
1200
|
-
amount: number;
|
|
1201
|
-
seed: number;
|
|
1202
|
-
scale: number;
|
|
1203
|
-
pull: number;
|
|
1204
|
-
}, {
|
|
1205
|
-
amount?: number | undefined;
|
|
1206
|
-
seed?: number | undefined;
|
|
1207
|
-
scale?: number | undefined;
|
|
1208
|
-
pull?: number | undefined;
|
|
1209
|
-
}>;
|
|
1210
|
-
type CrumpleOptions = z.infer<typeof crumpleOptionsSchema>;
|
|
1211
|
-
/**
|
|
1212
|
-
* Paper that has been handled.
|
|
1213
|
-
*
|
|
1214
|
-
* Six deformers and not one of them crushed a sheet — `wave` and `fold` were
|
|
1215
|
-
* the nearest and neither reads as crumpled. This is the missing primitive.
|
|
1216
|
-
*
|
|
1217
|
-
* The field is a jittered grid of cells, each pushed up or down, with the
|
|
1218
|
-
* height going to zero exactly on the boundary between them: `F2 − F1`, the
|
|
1219
|
-
* gap between the two nearest cell points, signed per cell. That vanishes on
|
|
1220
|
-
* every boundary, so the sheet stays continuous, and its gradient flips
|
|
1221
|
-
* across one — which is a crease. The result is an irregular polygonal
|
|
1222
|
-
* network of facets alternating toward and away from you, which is what a
|
|
1223
|
-
* sheet crushed in a fist actually is.
|
|
1224
|
-
*
|
|
1225
|
-
* **The normals matter more than the displacement here**, and getting there
|
|
1226
|
-
* took three tries worth recording. Three summed triangle waves: periodic,
|
|
1227
|
-
* an egg-crate. Plain distance-to-nearest (`F1`): irregular but smooth cone
|
|
1228
|
-
* tips, so it read as hammered metal. Only creases with a sign change across
|
|
1229
|
-
* them shade like paper.
|
|
1230
|
-
*
|
|
1231
|
-
* Both normal paths agree with that: the hero path averages vertex normals
|
|
1232
|
-
* over a dense grid, the field path probes two tangents a hundredth of a
|
|
1233
|
-
* sheet apart. Both need facets several segments wide.
|
|
1234
|
-
*
|
|
1235
|
-
* Cost, measured rather than assumed (`pnpm perf:field --soft`): a field of
|
|
1236
|
-
* these runs about four times longer per frame at ×20, and six at ×60, than
|
|
1237
|
-
* the same field of an undeformed preset. That ratio used to read 45%, and it
|
|
1238
|
-
* grew without this deformer getting one instruction slower — `'auto'` now
|
|
1239
|
-
* sizes the grid to the work, so the undeformed sheet it is measured against
|
|
1240
|
-
* went from 72 segments a side to 8 and got very cheap, while crumple keeps
|
|
1241
|
-
* the density its creases need. Which makes the point sharper than before:
|
|
1242
|
-
* almost none of this cost is geometry. It is the nine cell lookups per
|
|
1243
|
-
* probe, three probes deep for the normal.
|
|
1244
|
-
*/
|
|
1245
|
-
declare const crumple: Deformer<CrumpleOptions>;
|
|
1246
|
-
|
|
1247
|
-
/**
|
|
1248
|
-
* The shared fake-aerodynamics core (spec M6 §4): velocity-linked lift +
|
|
1249
|
-
* curated noise, per v0.2's "reads more real than a true sim" doctrine.
|
|
1250
|
-
* Pure math, no three.js, no allocation in the per-frame paths — `carry`
|
|
1251
|
-
* and `flight` both source their motion here.
|
|
1252
|
-
*/
|
|
1253
|
-
interface AeroPose {
|
|
1254
|
-
position: [number, number, number];
|
|
1255
|
-
rotation: [number, number, number];
|
|
1256
|
-
}
|
|
1257
|
-
/**
|
|
1258
|
-
* Critically-damped spring toward a target — the carry pin's cursor
|
|
1259
|
-
* follow. Mutates `state` in place; returns nothing.
|
|
1260
|
-
*/
|
|
1261
|
-
interface DampedValue {
|
|
1262
|
-
value: number;
|
|
1263
|
-
velocity: number;
|
|
1264
|
-
}
|
|
1265
|
-
declare function dampTo(state: DampedValue, target: number, smoothing: number, dt: number): void;
|
|
1266
|
-
/** Coherent gust factor in [1-g, 1+g] — curated, seeded, cheap. */
|
|
1267
|
-
declare function gust(t: number, seed: number, gustiness: number): number;
|
|
1268
|
-
interface FlightParams {
|
|
1269
|
-
wind: [number, number, number];
|
|
1270
|
-
gustiness: number;
|
|
1271
|
-
tumble: number;
|
|
1272
|
-
path: 'drift' | 'loop';
|
|
1273
|
-
/** Exit the scene → re-enter the opposite side (drift only). */
|
|
1274
|
-
respawn: boolean;
|
|
1275
|
-
/** Half-extent of the drift travel before respawn wraps it. */
|
|
1276
|
-
range: number;
|
|
1277
|
-
}
|
|
1278
|
-
/**
|
|
1279
|
-
* Free paper on the wind: the falling-leaf tumble core + a directional wind
|
|
1280
|
-
* vector + lift, so paper travels ACROSS, not just down. A pure function of
|
|
1281
|
-
* time and phase — instancing-safe, deterministic, loopable.
|
|
1282
|
-
*/
|
|
1283
|
-
declare function flightPose(t: number, o: FlightParams, phase: number, pose: AeroPose): void;
|
|
1284
|
-
/**
|
|
1285
|
-
* Carry flutter drive: how much a held paper ripples for a given drag speed
|
|
1286
|
-
* (world units/s). Saturates — a violent drag doesn't tear the illusion.
|
|
1287
|
-
*/
|
|
1288
|
-
declare function carryDrive(speed: number): number;
|
|
1289
|
-
|
|
1290
|
-
/** A draggable 3D grab point; drags write back to behavior params. */
|
|
1291
|
-
interface HandleSpec<O = AnyOptions> {
|
|
1292
|
-
id: string;
|
|
1293
|
-
/** UV-space (0..1) anchor of the grab point on the flat sheet. */
|
|
1294
|
-
anchor(o: O, sheet: SheetDims): [number, number];
|
|
1295
|
-
/** Map a pointer position in sheet-local XY to option updates. */
|
|
1296
|
-
drag(local: {
|
|
1297
|
-
x: number;
|
|
1298
|
-
y: number;
|
|
1299
|
-
}, o: O, sheet: SheetDims): Partial<O>;
|
|
1300
|
-
}
|
|
1301
|
-
/**
|
|
1302
|
-
* A behavior is a named, curated bundle: a deformer stack + a parameter
|
|
1303
|
-
* mapping + optional idle loop + handles for direct manipulation. Designers
|
|
1304
|
-
* see 3–5 human-named params ('tightness', not 'cylinderRadius'); the stack
|
|
1305
|
-
* underneath is an Advanced disclosure. New behaviors are the community
|
|
1306
|
-
* on-ramp: ~50 lines over existing deformers.
|
|
1307
|
-
*/
|
|
1308
|
-
interface Behavior<O = AnyOptions> {
|
|
1309
|
-
id: string;
|
|
1310
|
-
label: string;
|
|
1311
|
-
defaults: O;
|
|
1312
|
-
optionsSchema: z.ZodType<O, z.ZodTypeDef, unknown>;
|
|
1313
|
-
/** Expand human params to the underlying deformer stack. */
|
|
1314
|
-
stack(o: O, sheet: SheetDims): DeformerInstance[];
|
|
1315
|
-
/** Transient, time-varying option overrides (idle motion). Never persisted. */
|
|
1316
|
-
loop?(o: O, t: number): Partial<O>;
|
|
1317
|
-
/**
|
|
1318
|
-
* Whole-sheet motion written into `pose` each frame (allocation-free),
|
|
1319
|
-
* composed after any idle preset's transform. Must be a pure function of
|
|
1320
|
-
* (options, t) — the field applies it per instance with a time offset, so
|
|
1321
|
-
* it has to be deterministic (flight's travel-across-the-scene).
|
|
1322
|
-
*/
|
|
1323
|
-
transform?(o: O, t: number, pose: AeroPose): void;
|
|
1324
|
-
handles?: HandleSpec<O>[];
|
|
1325
|
-
/**
|
|
1326
|
-
* The two or three options that ARE this behavior — the ones someone
|
|
1327
|
-
* reaches for first, in the order they'd reach for them.
|
|
1328
|
-
*
|
|
1329
|
-
* The schema still generates a control for every option; this only says
|
|
1330
|
-
* which ones get the big controls and which fold away behind "More". A
|
|
1331
|
-
* behavior that nominates nothing shows all of its options flat, because
|
|
1332
|
-
* the library must never hide a param it was not told to hide — silence
|
|
1333
|
-
* from a community behavior is not permission to guess.
|
|
1334
|
-
*
|
|
1335
|
-
* Every name here has to be a field of {@link optionsSchema}; the built-in
|
|
1336
|
-
* behaviors are checked for that, and for staying within three.
|
|
1337
|
-
*/
|
|
1338
|
-
signature?: (keyof O & string)[];
|
|
1339
|
-
/** The option the transport scrubber drives. */
|
|
1340
|
-
progressParam: keyof O & string;
|
|
1341
|
-
/** Seconds for a full 0→1 play, and how play repeats. */
|
|
1342
|
-
duration: number;
|
|
1343
|
-
loopMode: 'yoyo' | 'restart';
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
/** Community behaviors register here; built-ins are pre-registered. */
|
|
1347
|
-
declare function registerBehavior(behavior: Behavior<AnyOptions>): void;
|
|
1348
|
-
declare function getBehavior(id: string): Behavior<AnyOptions>;
|
|
1349
|
-
declare function listBehaviors(): string[];
|
|
1350
|
-
|
|
1351
|
-
declare const peelOptionsSchema: z.ZodObject<{
|
|
1352
|
-
progress: z.ZodDefault<z.ZodNumber>;
|
|
1353
|
-
/** 'auto' resolves per slot in a `sheet` field (outward-facing corner); standalone it means bottom-right. */
|
|
1354
|
-
corner: z.ZodDefault<z.ZodEnum<["top-left", "top-right", "bottom-left", "bottom-right", "auto"]>>;
|
|
1355
|
-
/** Curl sharpness — small is a tight dog-ear, large a soft lift. */
|
|
1356
|
-
radius: z.ZodDefault<z.ZodNumber>;
|
|
1357
|
-
}, "strip", z.ZodTypeAny, {
|
|
1358
|
-
corner: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "auto";
|
|
1359
|
-
radius: number;
|
|
1360
|
-
progress: number;
|
|
1361
|
-
}, {
|
|
1362
|
-
corner?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "auto" | undefined;
|
|
1363
|
-
radius?: number | undefined;
|
|
1364
|
-
progress?: number | undefined;
|
|
1365
|
-
}>;
|
|
1366
|
-
type PeelOptions = z.infer<typeof peelOptionsSchema>;
|
|
1367
|
-
/** A corner lifts and curls back — the hero-image hover peel. */
|
|
1368
|
-
declare const peel: Behavior<PeelOptions>;
|
|
1369
|
-
|
|
1370
|
-
declare const unrollOptionsSchema: z.ZodObject<{
|
|
1371
|
-
/** 0 = fully rolled cylinder, 1 = flat sheet. */
|
|
1372
|
-
progress: z.ZodDefault<z.ZodNumber>;
|
|
1373
|
-
/** How tightly the paper is wound. */
|
|
1374
|
-
tightness: z.ZodDefault<z.ZodNumber>;
|
|
1375
|
-
/** Idle rocking of the rolled end. */
|
|
1376
|
-
sway: z.ZodDefault<z.ZodNumber>;
|
|
1377
|
-
}, "strip", z.ZodTypeAny, {
|
|
1378
|
-
progress: number;
|
|
1379
|
-
tightness: number;
|
|
1380
|
-
sway: number;
|
|
1381
|
-
}, {
|
|
1382
|
-
progress?: number | undefined;
|
|
1383
|
-
tightness?: number | undefined;
|
|
1384
|
-
sway?: number | undefined;
|
|
1385
|
-
}>;
|
|
1386
|
-
type UnrollOptions = z.infer<typeof unrollOptionsSchema>;
|
|
1387
|
-
/**
|
|
1388
|
-
* A receipt unrolls from the bottom: the sheet hangs flat from its top edge
|
|
1389
|
-
* and the remaining paper is wound in a roll at the bottom. Content bends
|
|
1390
|
-
* true around the roll — the reference-image requirement.
|
|
1391
|
-
*/
|
|
1392
|
-
declare const unroll: Behavior<UnrollOptions>;
|
|
1393
|
-
|
|
1394
|
-
declare const flipOptionsSchema: z.ZodObject<{
|
|
1395
|
-
/** 0 = flat, 1 = page fully turned over the spine. */
|
|
1396
|
-
progress: z.ZodDefault<z.ZodNumber>;
|
|
1397
|
-
/** Which edge is the spine. */
|
|
1398
|
-
spine: z.ZodDefault<z.ZodEnum<["left", "right"]>>;
|
|
1399
|
-
/** Softness of the turning curl. */
|
|
1400
|
-
radius: z.ZodDefault<z.ZodNumber>;
|
|
1401
|
-
}, "strip", z.ZodTypeAny, {
|
|
1402
|
-
radius: number;
|
|
1403
|
-
progress: number;
|
|
1404
|
-
spine: "left" | "right";
|
|
1405
|
-
}, {
|
|
1406
|
-
radius?: number | undefined;
|
|
1407
|
-
progress?: number | undefined;
|
|
1408
|
-
spine?: "left" | "right" | undefined;
|
|
1409
|
-
}>;
|
|
1410
|
-
type FlipOptions = z.infer<typeof flipOptionsSchema>;
|
|
1411
|
-
/**
|
|
1412
|
-
* A page turn: the free edge curls up and rolls over toward the spine —
|
|
1413
|
-
* the roll deformer with its boundary swept across the page.
|
|
1414
|
-
*/
|
|
1415
|
-
declare const flip: Behavior<FlipOptions>;
|
|
1416
|
-
|
|
1417
|
-
declare const letterFoldOptionsSchema: z.ZodObject<{
|
|
1418
|
-
/** 0 = flat letter, 1 = fully tri-folded. */
|
|
1419
|
-
progress: z.ZodDefault<z.ZodNumber>;
|
|
1420
|
-
/** Softness of the two creases. */
|
|
1421
|
-
crease: z.ZodDefault<z.ZodNumber>;
|
|
1422
|
-
}, "strip", z.ZodTypeAny, {
|
|
1423
|
-
progress: number;
|
|
1424
|
-
crease: number;
|
|
1425
|
-
}, {
|
|
1426
|
-
progress?: number | undefined;
|
|
1427
|
-
crease?: number | undefined;
|
|
1428
|
-
}>;
|
|
1429
|
-
type LetterFoldOptions = z.infer<typeof letterFoldOptionsSchema>;
|
|
1430
|
-
/**
|
|
1431
|
-
* The classic tri-fold: the bottom third folds up first, then the top third
|
|
1432
|
-
* folds down over it. Two fold deformers stacked — order is the physics.
|
|
1433
|
-
*/
|
|
1434
|
-
declare const letterFold: Behavior<LetterFoldOptions>;
|
|
1435
|
-
|
|
1436
|
-
declare const hangOptionsSchema: z.ZodObject<{
|
|
1437
|
-
/** Wind strength driving the ripple. */
|
|
1438
|
-
wind: z.ZodDefault<z.ZodNumber>;
|
|
1439
|
-
/** Gravity bulge of the hanging sheet. */
|
|
1440
|
-
sag: z.ZodDefault<z.ZodNumber>;
|
|
1441
|
-
}, "strip", z.ZodTypeAny, {
|
|
1442
|
-
wind: number;
|
|
1443
|
-
sag: number;
|
|
1444
|
-
}, {
|
|
1445
|
-
wind?: number | undefined;
|
|
1446
|
-
sag?: number | undefined;
|
|
1447
|
-
}>;
|
|
1448
|
-
type HangOptions = z.infer<typeof hangOptionsSchema>;
|
|
1449
|
-
/** A poster hanging from its top edge, rippling in wind. */
|
|
1450
|
-
declare const hang: Behavior<HangOptions>;
|
|
1451
|
-
|
|
1452
|
-
declare const flyOptionsSchema: z.ZodObject<{
|
|
1453
|
-
/** Ripple energy. */
|
|
1454
|
-
flutter: z.ZodDefault<z.ZodNumber>;
|
|
1455
|
-
/** Aerodynamic arc of the sheet. */
|
|
1456
|
-
curve: z.ZodDefault<z.ZodNumber>;
|
|
1457
|
-
}, "strip", z.ZodTypeAny, {
|
|
1458
|
-
flutter: number;
|
|
1459
|
-
curve: number;
|
|
1460
|
-
}, {
|
|
1461
|
-
flutter?: number | undefined;
|
|
1462
|
-
curve?: number | undefined;
|
|
1463
|
-
}>;
|
|
1464
|
-
type FlyOptions = z.infer<typeof flyOptionsSchema>;
|
|
1465
|
-
/** A note carried on air — arched and fluttering. Pair with the `tumble` idle. */
|
|
1466
|
-
declare const fly: Behavior<FlyOptions>;
|
|
1467
|
-
|
|
1468
|
-
declare const fallOptionsSchema: z.ZodObject<{
|
|
1469
|
-
/** Air resistance ripple while falling. */
|
|
1470
|
-
flutter: z.ZodDefault<z.ZodNumber>;
|
|
1471
|
-
/** A falling sheet always lifts a corner. */
|
|
1472
|
-
curl: z.ZodDefault<z.ZodNumber>;
|
|
1473
|
-
}, "strip", z.ZodTypeAny, {
|
|
1474
|
-
curl: number;
|
|
1475
|
-
flutter: number;
|
|
1476
|
-
}, {
|
|
1477
|
-
curl?: number | undefined;
|
|
1478
|
-
flutter?: number | undefined;
|
|
1479
|
-
}>;
|
|
1480
|
-
type FallOptions = z.infer<typeof fallOptionsSchema>;
|
|
1481
|
-
/** A dropped sheet — corner lifted, rippling. Pair with the `tumble` idle for the descent. */
|
|
1482
|
-
declare const fall: Behavior<FallOptions>;
|
|
1483
|
-
|
|
1484
|
-
declare const settleOptionsSchema: z.ZodObject<{
|
|
1485
|
-
/**
|
|
1486
|
-
* How long ago it landed, 0..1.
|
|
1487
|
-
*
|
|
1488
|
-
* 0 is the instant of arrival — still carrying the shape it fell in. 1 is
|
|
1489
|
-
* a sheet that has been lying there, where its own weight has flattened
|
|
1490
|
-
* out everything except what its stiffness refuses to give up.
|
|
1491
|
-
*/
|
|
1492
|
-
relax: z.ZodDefault<z.ZodNumber>;
|
|
1493
|
-
/**
|
|
1494
|
-
* How hard the paper resists lying flat, 0..1.
|
|
1495
|
-
*
|
|
1496
|
-
* This is the stock, not the pose: tissue surrenders completely, card
|
|
1497
|
-
* never does. It is the whole reason a settled sheet reads as PAPER and
|
|
1498
|
-
* not as a decal — at 0 the mesh is a rectangle painted on the floor.
|
|
1499
|
-
*/
|
|
1500
|
-
lift: z.ZodDefault<z.ZodNumber>;
|
|
1501
|
-
/** Which corner stayed up. */
|
|
1502
|
-
corner: z.ZodDefault<z.ZodEnum<["top-left", "top-right", "bottom-left", "bottom-right"]>>;
|
|
1503
|
-
/**
|
|
1504
|
-
* Slack across the middle — the low, long undulation of a sheet that is
|
|
1505
|
-
* touching a floor in two places and bridging between them.
|
|
1506
|
-
*/
|
|
1507
|
-
slack: z.ZodDefault<z.ZodNumber>;
|
|
1508
|
-
}, "strip", z.ZodTypeAny, {
|
|
1509
|
-
corner: "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
1510
|
-
relax: number;
|
|
1511
|
-
lift: number;
|
|
1512
|
-
slack: number;
|
|
1513
|
-
}, {
|
|
1514
|
-
corner?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | undefined;
|
|
1515
|
-
relax?: number | undefined;
|
|
1516
|
-
lift?: number | undefined;
|
|
1517
|
-
slack?: number | undefined;
|
|
1518
|
-
}>;
|
|
1519
|
-
type SettleOptions = z.infer<typeof settleOptionsSchema>;
|
|
1520
|
-
/**
|
|
1521
|
-
* A sheet that has landed and relaxed.
|
|
1522
|
-
*
|
|
1523
|
-
* The library could drop paper (`fall`), fly it (`fly`, `flight`), heap it
|
|
1524
|
-
* (`pile`) and catch it mid-air (`spill`) — and had no way at all to show a
|
|
1525
|
-
* sheet that has ARRIVED. Every reference installation worth copying has
|
|
1526
|
-
* paper on the floor: sheets settled on concrete after the fall, ribbons
|
|
1527
|
-
* pooling where they meet the ground. It is the most beautiful detail in the
|
|
1528
|
-
* set and it appears in it twice.
|
|
1529
|
-
*
|
|
1530
|
-
* The distinction from `fall` is not the shape, it is the CLOCK. `fall`
|
|
1531
|
-
* flutters — its wave carries `speed: 1.3`, and it is a sheet still arguing
|
|
1532
|
-
* with the air. This one is over. Everything here is static, and that is the
|
|
1533
|
-
* point: a settled sheet that ripples is a settled sheet nobody believes.
|
|
1534
|
-
*
|
|
1535
|
-
* Which is also why it composes rather than deforming: a landed sheet is a
|
|
1536
|
-
* gentle curl the stiffness held on to, plus a long slack undulation where
|
|
1537
|
-
* it bridges the floor. Both already exist, and a deformer that can be
|
|
1538
|
-
* spelled out of the ones we have does not earn a GLSL twin and a parity
|
|
1539
|
-
* case.
|
|
1540
|
-
*/
|
|
1541
|
-
declare const settle: Behavior<SettleOptions>;
|
|
1542
|
-
|
|
1543
|
-
declare const ribbonOptionsSchema: z.ZodObject<{
|
|
1544
|
-
/**
|
|
1545
|
-
* How much of the drop is lying on the floor, as a fraction of the height.
|
|
1546
|
-
*
|
|
1547
|
-
* This is the whole image. A strip that stops dead at the ground reads as
|
|
1548
|
-
* a strip that was cut to fit; one that arrives with a length to spare and
|
|
1549
|
-
* turns over reads as paper meeting a floor, which is the thing the
|
|
1550
|
-
* reference installations are actually about.
|
|
1551
|
-
*/
|
|
1552
|
-
pool: z.ZodDefault<z.ZodNumber>;
|
|
1553
|
-
/** How tightly it turns where it lands. Low is a soft slump, high is a curl. */
|
|
1554
|
-
curl: z.ZodDefault<z.ZodNumber>;
|
|
1555
|
-
/** Folds running down the length. A printed strip is never a flat plane. */
|
|
1556
|
-
drape: z.ZodDefault<z.ZodNumber>;
|
|
1557
|
-
}, "strip", z.ZodTypeAny, {
|
|
1558
|
-
curl: number;
|
|
1559
|
-
pool: number;
|
|
1560
|
-
drape: number;
|
|
1561
|
-
}, {
|
|
1562
|
-
curl?: number | undefined;
|
|
1563
|
-
pool?: number | undefined;
|
|
1564
|
-
drape?: number | undefined;
|
|
1565
|
-
}>;
|
|
1566
|
-
type RibbonOptions = z.infer<typeof ribbonOptionsSchema>;
|
|
1567
|
-
/**
|
|
1568
|
-
* A strip hung from the ceiling that reaches the floor and keeps going.
|
|
1569
|
-
*
|
|
1570
|
-
* The single most striking image in the reference set, and the reason it
|
|
1571
|
-
* took until now to build is that it needs three separate things that did
|
|
1572
|
-
* not exist a week ago: a room with a ceiling to hang from, hardware to hang
|
|
1573
|
-
* BY, and type that can be set down the length of a sheet without looking
|
|
1574
|
-
* like a caption. It is the payoff for all of them.
|
|
1115
|
+
* Line breaking, shared by every content type that sets prose.
|
|
1575
1116
|
*
|
|
1576
|
-
*
|
|
1577
|
-
*
|
|
1578
|
-
*
|
|
1579
|
-
*
|
|
1580
|
-
*/
|
|
1581
|
-
declare const ribbon: Behavior<RibbonOptions>;
|
|
1582
|
-
|
|
1583
|
-
declare const carryOptionsSchema: z.ZodObject<{
|
|
1584
|
-
/**
|
|
1585
|
-
* The grab point — where the pointer was on the paper at pick time.
|
|
1586
|
-
* 'auto' is resolved by the carry controller (usually the peeled corner:
|
|
1587
|
-
* continuity from peel → carry is the immersion moment).
|
|
1588
|
-
*/
|
|
1589
|
-
grab: z.ZodDefault<z.ZodEnum<["top-left", "top-right", "bottom-left", "bottom-right", "auto"]>>;
|
|
1590
|
-
/** From stock feel: a stamp is stiff — it flutters, it doesn't flow. */
|
|
1591
|
-
stiffness: z.ZodDefault<z.ZodNumber>;
|
|
1592
|
-
flutter: z.ZodDefault<z.ZodNumber>;
|
|
1593
|
-
/** How far the paper's yaw trails the drag direction (runtime transform). */
|
|
1594
|
-
lag: z.ZodDefault<z.ZodNumber>;
|
|
1595
|
-
/** Drag-speed drive (0..1). Written live by the carry controller. */
|
|
1596
|
-
drive: z.ZodDefault<z.ZodNumber>;
|
|
1597
|
-
}, "strip", z.ZodTypeAny, {
|
|
1598
|
-
flutter: number;
|
|
1599
|
-
grab: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "auto";
|
|
1600
|
-
stiffness: number;
|
|
1601
|
-
lag: number;
|
|
1602
|
-
drive: number;
|
|
1603
|
-
}, {
|
|
1604
|
-
flutter?: number | undefined;
|
|
1605
|
-
grab?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "auto" | undefined;
|
|
1606
|
-
stiffness?: number | undefined;
|
|
1607
|
-
lag?: number | undefined;
|
|
1608
|
-
drive?: number | undefined;
|
|
1609
|
-
}>;
|
|
1610
|
-
type CarryOptions = z.infer<typeof carryOptionsSchema>;
|
|
1611
|
-
/**
|
|
1612
|
-
* A held paper, alive from motion (spec M6 §4.1, the field/cheap path):
|
|
1613
|
-
* droop away from the grab point + drag-velocity flutter. The hero path —
|
|
1614
|
-
* cloth with a single pin following the cursor — is the existing
|
|
1615
|
-
* `physics: 'cloth'` grab; this behavior is what fields and exports run.
|
|
1616
|
-
*/
|
|
1617
|
-
declare const carry: Behavior<CarryOptions>;
|
|
1618
|
-
|
|
1619
|
-
declare const flightOptionsSchema: z.ZodObject<{
|
|
1620
|
-
/** Directional wind vector — paper travels ACROSS the scene, not just down. */
|
|
1621
|
-
wind: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
1622
|
-
gustiness: z.ZodDefault<z.ZodNumber>;
|
|
1623
|
-
tumble: z.ZodDefault<z.ZodNumber>;
|
|
1624
|
-
/** 'loop' is a seamless idle cycle; 'drift' travels along the wind. */
|
|
1625
|
-
path: z.ZodDefault<z.ZodEnum<["drift", "loop"]>>;
|
|
1626
|
-
/** Drift only: exit the scene → re-enter the opposite side. */
|
|
1627
|
-
respawn: z.ZodDefault<z.ZodBoolean>;
|
|
1628
|
-
/** Half-extent of the travel before respawn wraps it. */
|
|
1629
|
-
range: z.ZodDefault<z.ZodNumber>;
|
|
1630
|
-
}, "strip", z.ZodTypeAny, {
|
|
1631
|
-
path: "drift" | "loop";
|
|
1632
|
-
wind: [number, number, number];
|
|
1633
|
-
gustiness: number;
|
|
1634
|
-
tumble: number;
|
|
1635
|
-
respawn: boolean;
|
|
1636
|
-
range: number;
|
|
1637
|
-
}, {
|
|
1638
|
-
path?: "drift" | "loop" | undefined;
|
|
1639
|
-
wind?: [number, number, number] | undefined;
|
|
1640
|
-
gustiness?: number | undefined;
|
|
1641
|
-
tumble?: number | undefined;
|
|
1642
|
-
respawn?: boolean | undefined;
|
|
1643
|
-
range?: number | undefined;
|
|
1644
|
-
}>;
|
|
1645
|
-
type FlightOptions = z.infer<typeof flightOptionsSchema>;
|
|
1646
|
-
/**
|
|
1647
|
-
* Untethered paper carried across the scene on the wind (spec M6 §4.2) —
|
|
1648
|
-
* the falling-leaf tumble core + directional travel + lift. Transform +
|
|
1649
|
-
* deformer based, so it's instancing-safe: a `scatter` layout + `flight`
|
|
1650
|
-
* idle = papers blowing through a hero section.
|
|
1117
|
+
* It lives on its own because `paintText` and `paintCard` were about to
|
|
1118
|
+
* carry two copies of it, and two copies of a line-breaker is two answers to
|
|
1119
|
+
* "where does this wrap" — which on a sheet that CURLS is not a cosmetic
|
|
1120
|
+
* disagreement: the reader sees the break land on a fold.
|
|
1651
1121
|
*/
|
|
1652
|
-
declare const flight: Behavior<FlightOptions>;
|
|
1653
|
-
|
|
1654
|
-
declare const crumpleBehaviorOptionsSchema: z.ZodObject<{
|
|
1655
|
-
/** 0 = flat sheet, 1 = crushed. */
|
|
1656
|
-
progress: z.ZodDefault<z.ZodNumber>;
|
|
1657
|
-
/** Few big facets at 0, many small ones at 1. */
|
|
1658
|
-
coarseness: z.ZodDefault<z.ZodNumber>;
|
|
1659
|
-
/** How far the sheet curls in on itself as it crushes. */
|
|
1660
|
-
ball: z.ZodDefault<z.ZodNumber>;
|
|
1661
|
-
/** A different crush of the same paper. */
|
|
1662
|
-
seed: z.ZodDefault<z.ZodNumber>;
|
|
1663
|
-
}, "strip", z.ZodTypeAny, {
|
|
1664
|
-
progress: number;
|
|
1665
|
-
coarseness: number;
|
|
1666
|
-
ball: number;
|
|
1667
|
-
seed: number;
|
|
1668
|
-
}, {
|
|
1669
|
-
progress?: number | undefined;
|
|
1670
|
-
coarseness?: number | undefined;
|
|
1671
|
-
ball?: number | undefined;
|
|
1672
|
-
seed?: number | undefined;
|
|
1673
|
-
}>;
|
|
1674
|
-
type CrumpleBehaviorOptions = z.infer<typeof crumpleBehaviorOptionsSchema>;
|
|
1675
1122
|
/**
|
|
1676
|
-
*
|
|
1123
|
+
* Wrap a paragraph to a measure, breaking a word that cannot fit on its own.
|
|
1677
1124
|
*
|
|
1678
|
-
*
|
|
1679
|
-
*
|
|
1680
|
-
*
|
|
1681
|
-
*
|
|
1682
|
-
|
|
1683
|
-
declare const crumpleBehavior: Behavior<CrumpleBehaviorOptions>;
|
|
1684
|
-
|
|
1685
|
-
declare const idleNames: readonly ["float", "tumble", "dangle", "taped", "breeze"];
|
|
1686
|
-
type IdleName = (typeof idleNames)[number];
|
|
1687
|
-
interface IdlePose {
|
|
1688
|
-
/** Offsets added to the paper's base transform each frame. */
|
|
1689
|
-
position: [number, number, number];
|
|
1690
|
-
rotation: [number, number, number];
|
|
1691
|
-
}
|
|
1692
|
-
/**
|
|
1693
|
-
* Curated fake physics: hand-tuned motion presets, no simulation. Cheap
|
|
1694
|
-
* enough for both hero and field modes, and — for things like the
|
|
1695
|
-
* falling-leaf tumble — they read *more* real than a true sim.
|
|
1696
|
-
*/
|
|
1697
|
-
interface IdlePreset {
|
|
1698
|
-
id: IdleName;
|
|
1699
|
-
label: string;
|
|
1700
|
-
/** Whole-sheet motion, written into `pose` (allocation-free). */
|
|
1701
|
-
transform?(t: number, pose: IdlePose): void;
|
|
1702
|
-
/** Extra deformers appended after the behavior's stack. */
|
|
1703
|
-
stack?(): DeformerInstance[];
|
|
1704
|
-
}
|
|
1705
|
-
declare const idlePresets: Record<IdleName, IdlePreset>;
|
|
1706
|
-
declare function getIdlePreset(name: IdleName): IdlePreset;
|
|
1707
|
-
|
|
1708
|
-
/**
|
|
1709
|
-
* Compose a deformer stack into GLSL — the GPU twin of deformers/compose.ts.
|
|
1710
|
-
* Each stack entry gets its own uniform namespace (`uRoll0_angle`,
|
|
1711
|
-
* `uFold1_offset`, …) so the same deformer type can appear twice
|
|
1712
|
-
* (letter-fold is two folds). Golden-vector parity with the JS path is
|
|
1713
|
-
* enforced by the GPU harness test.
|
|
1714
|
-
*/
|
|
1715
|
-
interface ComposedDisplacement {
|
|
1716
|
-
/** Uniform declarations + displacement functions. */
|
|
1717
|
-
functionsSrc: string;
|
|
1718
|
-
/** `vec3 plDisplace(vec3 p, vec2 uv, float t)` applying the whole stack. */
|
|
1719
|
-
displaceSrc: string;
|
|
1720
|
-
/** Initial uniform values, keyed by their namespaced GLSL names (uSheet included). */
|
|
1721
|
-
uniforms: Record<string, number | number[]>;
|
|
1722
|
-
}
|
|
1723
|
-
/**
|
|
1724
|
-
* Just the uniform VALUES for a stack (same namespaced keys as
|
|
1725
|
-
* buildDisplacementGLSL) — cheap enough to call every frame while a behavior
|
|
1726
|
-
* animates. Structure (names) is stable as long as the stack's type order is.
|
|
1727
|
-
*/
|
|
1728
|
-
declare function stackUniformValues(stack: DeformerInstance[], sheet: SheetDims): Record<string, number | number[]>;
|
|
1729
|
-
declare function buildDisplacementGLSL(stack: DeformerInstance[], sheet: SheetDims): ComposedDisplacement;
|
|
1730
|
-
/**
|
|
1731
|
-
* The field-mode CSM vertex shader: displaced position + numerically
|
|
1732
|
-
* recomputed normal (two tangent probes — exact normals of an arbitrary
|
|
1733
|
-
* stack have no closed form).
|
|
1734
|
-
*/
|
|
1735
|
-
declare function buildFieldVertexShader(composed: ComposedDisplacement): string;
|
|
1736
|
-
/**
|
|
1737
|
-
* Field fragment shader: per-instance tile from the shared content atlas on
|
|
1738
|
-
* the FRONT face; the BACK face renders the stock with an optional reversed
|
|
1739
|
-
* show-through ghost (per-paper back textures are a hero-mode feature).
|
|
1740
|
-
*/
|
|
1741
|
-
declare function buildFieldFragmentShader(): string;
|
|
1742
|
-
|
|
1743
|
-
/**
|
|
1744
|
-
* A layout is a pure `pose(i, n, options, phase)` function — no state, no
|
|
1745
|
-
* three.js. `phase` is the motion driver's continuous offset in turns
|
|
1746
|
-
* (0..1 = one full cycle); cyclic layouts use it, static ones ignore it.
|
|
1747
|
-
* Community layouts are ~30 lines.
|
|
1748
|
-
*
|
|
1749
|
-
* Every built-in names a place paper actually sits — a fanned swatch deck, a
|
|
1750
|
-
* slipped stack, a heap on a desk — because arrangement alone is what makes a
|
|
1751
|
-
* field read as a photo carousel instead of as paper. The other half of that
|
|
1752
|
-
* is `bias`: paper in the world does not all bend alike.
|
|
1753
|
-
*/
|
|
1754
|
-
interface PaperPose {
|
|
1755
|
-
position: [number, number, number];
|
|
1756
|
-
rotation: [number, number, number];
|
|
1757
|
-
scale: number;
|
|
1758
|
-
/**
|
|
1759
|
-
* How strongly this sheet takes the field's deformation: 1 = exactly as the
|
|
1760
|
-
* preset configures it, 0 = flat. Lets one instanced draw call curl the top
|
|
1761
|
-
* of a pile while the sheets pressed underneath stay flat. Omitted = 1.
|
|
1762
|
-
*/
|
|
1763
|
-
bias?: number;
|
|
1764
|
-
}
|
|
1765
|
-
interface Layout<O = Record<string, unknown>> {
|
|
1766
|
-
id: string;
|
|
1767
|
-
label: string;
|
|
1768
|
-
defaults: O;
|
|
1769
|
-
optionsSchema: z.ZodType<O, z.ZodTypeDef, unknown>;
|
|
1770
|
-
/**
|
|
1771
|
-
* `sheet` is the field's paper size. Layouts that arrange by CONTACT —
|
|
1772
|
-
* edges meeting, sheets resting on each other — cannot work without it,
|
|
1773
|
-
* and a layout that ignores it may simply omit the parameter.
|
|
1774
|
-
*/
|
|
1775
|
-
pose(i: number, n: number, o: O, phase: number, sheet: SheetDims): PaperPose;
|
|
1776
|
-
/**
|
|
1777
|
-
* Where along a walk this layout put each paper, as normalized arc length,
|
|
1778
|
-
* in the layout's own index order.
|
|
1779
|
-
*
|
|
1780
|
-
* Only layouts that arrange along a PATH can answer, which is why it is
|
|
1781
|
-
* optional. Stage mode uses it to let a viewer step from one paper to the
|
|
1782
|
-
* next: the stops have to be where the paper actually is, and the only
|
|
1783
|
-
* thing that knows that is the function that placed it.
|
|
1784
|
-
*/
|
|
1785
|
-
walkStops?(n: number, o: O): number[];
|
|
1786
|
-
}
|
|
1787
|
-
declare const ringSchema: z.ZodObject<{
|
|
1788
|
-
radius: z.ZodDefault<z.ZodNumber>;
|
|
1789
|
-
tiltDeg: z.ZodDefault<z.ZodNumber>;
|
|
1790
|
-
}, "strip", z.ZodTypeAny, {
|
|
1791
|
-
radius: number;
|
|
1792
|
-
tiltDeg: number;
|
|
1793
|
-
}, {
|
|
1794
|
-
radius?: number | undefined;
|
|
1795
|
-
tiltDeg?: number | undefined;
|
|
1796
|
-
}>;
|
|
1797
|
-
/** Prints pegged around a circle — the one carousel worth keeping. */
|
|
1798
|
-
declare const ring: Layout<z.infer<typeof ringSchema>>;
|
|
1799
|
-
declare const fanSchema: z.ZodObject<{
|
|
1800
|
-
/** Total angular sweep from the first sheet to the last, degrees. */
|
|
1801
|
-
sweep: z.ZodDefault<z.ZodNumber>;
|
|
1802
|
-
/** Where the shared pin sits, in half-sheet-heights below center. 1 = the bottom edge. */
|
|
1803
|
-
hinge: z.ZodDefault<z.ZodNumber>;
|
|
1804
|
-
/** Thickness step so the sheets stack in order instead of z-fighting. */
|
|
1805
|
-
lift: z.ZodDefault<z.ZodNumber>;
|
|
1806
|
-
/** How much flatter the middle of the fan sits than its outer sheets. */
|
|
1807
|
-
bow: z.ZodDefault<z.ZodNumber>;
|
|
1808
|
-
}, "strip", z.ZodTypeAny, {
|
|
1809
|
-
lift: number;
|
|
1810
|
-
sweep: number;
|
|
1811
|
-
hinge: number;
|
|
1812
|
-
bow: number;
|
|
1813
|
-
}, {
|
|
1814
|
-
lift?: number | undefined;
|
|
1815
|
-
sweep?: number | undefined;
|
|
1816
|
-
hinge?: number | undefined;
|
|
1817
|
-
bow?: number | undefined;
|
|
1818
|
-
}>;
|
|
1819
|
-
/**
|
|
1820
|
-
* A Pantone deck, a paint-chip book, a hand of cards: every sheet pinned at
|
|
1821
|
-
* one shared point and swung open. The sheets nearest the outside of the
|
|
1822
|
-
* sweep carry the most curl, which is what sells the hinge as a hinge.
|
|
1823
|
-
*/
|
|
1824
|
-
declare const fan: Layout<z.infer<typeof fanSchema>>;
|
|
1825
|
-
declare const spreadSchema: z.ZodObject<{
|
|
1826
|
-
/** How far each sheet slides past the one below it. */
|
|
1827
|
-
slip: z.ZodDefault<z.ZodNumber>;
|
|
1828
|
-
/** Direction of the slide, degrees. 0 slides right, 90 slides up. */
|
|
1829
|
-
angle: z.ZodDefault<z.ZodNumber>;
|
|
1830
|
-
lift: z.ZodDefault<z.ZodNumber>;
|
|
1831
|
-
/** How much more the sheets at the far end of the slide bow. */
|
|
1832
|
-
bow: z.ZodDefault<z.ZodNumber>;
|
|
1833
|
-
/** Nothing hand-slid is perfectly square — a touch of per-sheet rotation. */
|
|
1834
|
-
drift: z.ZodDefault<z.ZodNumber>;
|
|
1835
|
-
}, "strip", z.ZodTypeAny, {
|
|
1836
|
-
drift: number;
|
|
1837
|
-
angle: number;
|
|
1838
|
-
lift: number;
|
|
1839
|
-
bow: number;
|
|
1840
|
-
slip: number;
|
|
1841
|
-
}, {
|
|
1842
|
-
drift?: number | undefined;
|
|
1843
|
-
angle?: number | undefined;
|
|
1844
|
-
lift?: number | undefined;
|
|
1845
|
-
bow?: number | undefined;
|
|
1846
|
-
slip?: number | undefined;
|
|
1847
|
-
}>;
|
|
1848
|
-
/**
|
|
1849
|
-
* A ream pushed sideways, or a deck dealt across a table: parallel sheets at
|
|
1850
|
-
* a constant offset, each one bowing a little more as it comes free of the
|
|
1851
|
-
* stack's weight.
|
|
1852
|
-
*/
|
|
1853
|
-
declare const spread: Layout<z.infer<typeof spreadSchema>>;
|
|
1854
|
-
declare const pileSchema: z.ZodObject<{
|
|
1855
|
-
/** How far sheets wander from the center of the heap. */
|
|
1856
|
-
scatter: z.ZodDefault<z.ZodNumber>;
|
|
1857
|
-
/** Widest angle a sheet sits off square, degrees. */
|
|
1858
|
-
turn: z.ZodDefault<z.ZodNumber>;
|
|
1859
|
-
lift: z.ZodDefault<z.ZodNumber>;
|
|
1860
|
-
/** How flat the sheets underneath are pressed by the ones on top. */
|
|
1861
|
-
press: z.ZodDefault<z.ZodNumber>;
|
|
1862
|
-
seed: z.ZodDefault<z.ZodNumber>;
|
|
1863
|
-
}, "strip", z.ZodTypeAny, {
|
|
1864
|
-
seed: number;
|
|
1865
|
-
lift: number;
|
|
1866
|
-
scatter: number;
|
|
1867
|
-
turn: number;
|
|
1868
|
-
press: number;
|
|
1869
|
-
}, {
|
|
1870
|
-
seed?: number | undefined;
|
|
1871
|
-
lift?: number | undefined;
|
|
1872
|
-
scatter?: number | undefined;
|
|
1873
|
-
turn?: number | undefined;
|
|
1874
|
-
press?: number | undefined;
|
|
1875
|
-
}>;
|
|
1876
|
-
/**
|
|
1877
|
-
* The heap on a desk. The physical tell no parametric curve can fake: sheets
|
|
1878
|
-
* rest ON each other, so only the top of the pile keeps its curl and
|
|
1879
|
-
* everything below is pressed flat by the weight above it.
|
|
1880
|
-
*/
|
|
1881
|
-
declare const pile: Layout<z.infer<typeof pileSchema>>;
|
|
1882
|
-
declare const wallSchema: z.ZodObject<{
|
|
1883
|
-
gapX: z.ZodDefault<z.ZodNumber>;
|
|
1884
|
-
gapY: z.ZodDefault<z.ZodNumber>;
|
|
1885
|
-
jitterAmt: z.ZodDefault<z.ZodNumber>;
|
|
1886
|
-
/** Spread of sag across the wall — no two pinned sheets hang alike. */
|
|
1887
|
-
sag: z.ZodDefault<z.ZodNumber>;
|
|
1888
|
-
}, "strip", z.ZodTypeAny, {
|
|
1889
|
-
sag: number;
|
|
1890
|
-
gapX: number;
|
|
1891
|
-
gapY: number;
|
|
1892
|
-
jitterAmt: number;
|
|
1893
|
-
}, {
|
|
1894
|
-
sag?: number | undefined;
|
|
1895
|
-
gapX?: number | undefined;
|
|
1896
|
-
gapY?: number | undefined;
|
|
1897
|
-
jitterAmt?: number | undefined;
|
|
1898
|
-
}>;
|
|
1899
|
-
/** A studio wall of pinned sheets: a grid, but nothing hangs quite square. */
|
|
1900
|
-
declare const wall: Layout<z.infer<typeof wallSchema>>;
|
|
1901
|
-
declare const spillSchema: z.ZodObject<{
|
|
1902
|
-
spreadX: z.ZodDefault<z.ZodNumber>;
|
|
1903
|
-
spreadY: z.ZodDefault<z.ZodNumber>;
|
|
1904
|
-
depth: z.ZodDefault<z.ZodNumber>;
|
|
1905
|
-
/** How far sheets pitch and roll out of the picture plane. */
|
|
1906
|
-
tumble: z.ZodDefault<z.ZodNumber>;
|
|
1907
|
-
/** Spread of bend across the sheets — a spill does not fold them alike. */
|
|
1908
|
-
vary: z.ZodDefault<z.ZodNumber>;
|
|
1909
|
-
seed: z.ZodDefault<z.ZodNumber>;
|
|
1910
|
-
}, "strip", z.ZodTypeAny, {
|
|
1911
|
-
tumble: number;
|
|
1912
|
-
seed: number;
|
|
1913
|
-
spreadX: number;
|
|
1914
|
-
spreadY: number;
|
|
1915
|
-
depth: number;
|
|
1916
|
-
vary: number;
|
|
1917
|
-
}, {
|
|
1918
|
-
tumble?: number | undefined;
|
|
1919
|
-
seed?: number | undefined;
|
|
1920
|
-
spreadX?: number | undefined;
|
|
1921
|
-
spreadY?: number | undefined;
|
|
1922
|
-
depth?: number | undefined;
|
|
1923
|
-
vary?: number | undefined;
|
|
1924
|
-
}>;
|
|
1925
|
-
/**
|
|
1926
|
-
* A dropped folder's worth of paper, mid-air — what a `pile` looks like the
|
|
1927
|
-
* moment before it settles. Loose in all three axes, and (the part that
|
|
1928
|
-
* separates it from confetti) every sheet caught at its own angle AND its
|
|
1929
|
-
* own amount of bend.
|
|
1930
|
-
*/
|
|
1931
|
-
declare const spill: Layout<z.infer<typeof spillSchema>>;
|
|
1932
|
-
declare const sweepSchema: z.ZodObject<{
|
|
1933
|
-
columns: z.ZodDefault<z.ZodNumber>;
|
|
1934
|
-
/** Breathing room around each specimen. */
|
|
1935
|
-
gap: z.ZodDefault<z.ZodNumber>;
|
|
1936
|
-
/** Deformation at the first specimen and at the last. */
|
|
1937
|
-
from: z.ZodDefault<z.ZodNumber>;
|
|
1938
|
-
to: z.ZodDefault<z.ZodNumber>;
|
|
1939
|
-
}, "strip", z.ZodTypeAny, {
|
|
1940
|
-
columns: number;
|
|
1941
|
-
gap: number;
|
|
1942
|
-
from: number;
|
|
1943
|
-
to: number;
|
|
1944
|
-
}, {
|
|
1945
|
-
columns?: number | undefined;
|
|
1946
|
-
gap?: number | undefined;
|
|
1947
|
-
from?: number | undefined;
|
|
1948
|
-
to?: number | undefined;
|
|
1949
|
-
}>;
|
|
1950
|
-
/**
|
|
1951
|
-
* A specimen chart: the same sheet mounted in a grid, its deformation ramped
|
|
1952
|
-
* across the series so one image shows a curl at ten stages instead of one.
|
|
1953
|
-
* The layout the rest of this library exists to make possible — and the one
|
|
1954
|
-
* that documents every deformer for free.
|
|
1955
|
-
*
|
|
1956
|
-
* Only as legible as the preset it charts: a sheet with no behavior or
|
|
1957
|
-
* deformers has nothing for the ramp to scale, and every specimen comes out
|
|
1958
|
-
* identical.
|
|
1959
|
-
*/
|
|
1960
|
-
declare const sweep: Layout<z.infer<typeof sweepSchema>>;
|
|
1961
|
-
declare const bookSchema: z.ZodObject<{
|
|
1962
|
-
/** How far the outermost page lifts off the block, degrees. */
|
|
1963
|
-
spread: z.ZodDefault<z.ZodNumber>;
|
|
1964
|
-
/** Fraction of the pages bound to the left. 0 = a one-sided sample book. */
|
|
1965
|
-
split: z.ZodDefault<z.ZodNumber>;
|
|
1966
|
-
/** Page thickness — the gap between pages of one block. */
|
|
1967
|
-
lift: z.ZodDefault<z.ZodNumber>;
|
|
1968
|
-
/** How much more a lifted page arcs than one lying flat in the block. */
|
|
1969
|
-
gutter: z.ZodDefault<z.ZodNumber>;
|
|
1970
|
-
}, "strip", z.ZodTypeAny, {
|
|
1971
|
-
lift: number;
|
|
1972
|
-
split: number;
|
|
1973
|
-
gutter: number;
|
|
1974
|
-
spread: number;
|
|
1975
|
-
}, {
|
|
1976
|
-
lift?: number | undefined;
|
|
1977
|
-
split?: number | undefined;
|
|
1978
|
-
gutter?: number | undefined;
|
|
1979
|
-
spread?: number | undefined;
|
|
1980
|
-
}>;
|
|
1981
|
-
/**
|
|
1982
|
-
* An open codex: pages hinged on a shared spine, each block splaying away
|
|
1983
|
-
* from the gutter. `split` slides it between the two bound forms paper takes
|
|
1984
|
-
* — 0.5 is a book lying open, 0 is a swatch deck or sample book bound down
|
|
1985
|
-
* one side. Pages lying flat in the block are pressed by the ones above;
|
|
1986
|
-
* only the lifted pages keep their arc.
|
|
1987
|
-
*/
|
|
1988
|
-
declare const book: Layout<z.infer<typeof bookSchema>>;
|
|
1989
|
-
declare const accordionSchema: z.ZodObject<{
|
|
1990
|
-
/** How far each panel tilts off the strip's line, degrees. 0 = flat, 90 = shut. */
|
|
1991
|
-
angle: z.ZodDefault<z.ZodNumber>;
|
|
1992
|
-
/** A concertina holds its creases — how much bow the panels keep. */
|
|
1993
|
-
slack: z.ZodDefault<z.ZodNumber>;
|
|
1994
|
-
}, "strip", z.ZodTypeAny, {
|
|
1995
|
-
angle: number;
|
|
1996
|
-
slack: number;
|
|
1997
|
-
}, {
|
|
1998
|
-
angle?: number | undefined;
|
|
1999
|
-
slack?: number | undefined;
|
|
2000
|
-
}>;
|
|
2001
|
-
/**
|
|
2002
|
-
* A concertina: panels alternating about creases they genuinely share, so
|
|
2003
|
-
* the sheets read as ONE folded strip rather than as N separate papers —
|
|
2004
|
-
* the only layout here where that is true. Adjacent edges are solved to
|
|
2005
|
-
* meet, which is why it needs the sheet's real width.
|
|
2006
|
-
*/
|
|
2007
|
-
declare const accordion: Layout<z.infer<typeof accordionSchema>>;
|
|
2008
|
-
declare const rackSchema: z.ZodObject<{
|
|
2009
|
-
/** Gap along the row, as a fraction of the paper's width. Under 1 they overlap. */
|
|
2010
|
-
spacing: z.ZodDefault<z.ZodNumber>;
|
|
2011
|
-
/** How far a sheet leans back off vertical, degrees. */
|
|
2012
|
-
lean: z.ZodDefault<z.ZodNumber>;
|
|
2013
|
-
/** How much that lean differs sheet to sheet — nothing propped is uniform. */
|
|
2014
|
-
vary: z.ZodDefault<z.ZodNumber>;
|
|
2015
|
-
/** Small rotations off square. */
|
|
2016
|
-
sway: z.ZodDefault<z.ZodNumber>;
|
|
2017
|
-
seed: z.ZodDefault<z.ZodNumber>;
|
|
2018
|
-
}, "strip", z.ZodTypeAny, {
|
|
2019
|
-
sway: number;
|
|
2020
|
-
seed: number;
|
|
2021
|
-
spacing: number;
|
|
2022
|
-
vary: number;
|
|
2023
|
-
lean: number;
|
|
2024
|
-
}, {
|
|
2025
|
-
sway?: number | undefined;
|
|
2026
|
-
seed?: number | undefined;
|
|
2027
|
-
spacing?: number | undefined;
|
|
2028
|
-
vary?: number | undefined;
|
|
2029
|
-
lean?: number | undefined;
|
|
2030
|
-
}>;
|
|
2031
|
-
/**
|
|
2032
|
-
* Prints stood in a row and leaning back — against a wall, in a rack, propped
|
|
2033
|
-
* along a shelf. The one arrangement here that RESTS on a surface rather than
|
|
2034
|
-
* floating: every sheet pivots on the bottom edge it actually stands on, so
|
|
2035
|
-
* the row shares a floor. The further a sheet has leaned, the more it bows
|
|
2036
|
-
* under its own weight.
|
|
2037
|
-
*
|
|
2038
|
-
* (Stacking these front-to-back the way a letter tray really holds paper is
|
|
2039
|
-
* physically honest and visually useless — the front sheet hides the rest.
|
|
2040
|
-
* A row is the arrangement you can actually see.)
|
|
2041
|
-
*/
|
|
2042
|
-
declare const rack: Layout<z.infer<typeof rackSchema>>;
|
|
2043
|
-
declare const colonnadeSchema: z.ZodObject<{
|
|
2044
|
-
/** The walk the colonnade is built along — see `stage/path`. */
|
|
2045
|
-
path: z.ZodDefault<z.ZodObject<{
|
|
2046
|
-
points: z.ZodDefault<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">>;
|
|
2047
|
-
closed: z.ZodDefault<z.ZodBoolean>;
|
|
2048
|
-
}, "strip", z.ZodTypeAny, {
|
|
2049
|
-
points: [number, number][];
|
|
2050
|
-
closed: boolean;
|
|
2051
|
-
}, {
|
|
2052
|
-
points?: [number, number][] | undefined;
|
|
2053
|
-
closed?: boolean | undefined;
|
|
2054
|
-
}>>;
|
|
2055
|
-
/** Half-width of the clear aisle: how far each banner stands off the walk line. */
|
|
2056
|
-
aisle: z.ZodDefault<z.ZodNumber>;
|
|
2057
|
-
/** How much that gap opens and closes along the walk. Nothing hung by hand is a corridor. */
|
|
2058
|
-
breathe: z.ZodDefault<z.ZodNumber>;
|
|
2059
|
-
/** Widest angle a banner turns off square to the aisle, degrees. */
|
|
2060
|
-
twist: z.ZodDefault<z.ZodNumber>;
|
|
2061
|
-
/** Fraction of the walk left clear at each end, so the figure has somewhere to enter from. */
|
|
2062
|
-
margin: z.ZodDefault<z.ZodNumber>;
|
|
2063
|
-
/** Spread of banner heights, 0..1. */
|
|
2064
|
-
rise: z.ZodDefault<z.ZodNumber>;
|
|
2065
|
-
/**
|
|
2066
|
-
* How far the banners lift off the floor, as a fraction of their height.
|
|
2067
|
-
*
|
|
2068
|
-
* NEGATIVE is allowed, and it is what pooling actually requires. At 0 a
|
|
2069
|
-
* banner's bottom EDGE sits on the floor — which is not the same as paper
|
|
2070
|
-
* pooling on it, however much the old comment here claimed otherwise. A
|
|
2071
|
-
* ribbon creases a pool-length above its bottom edge, so it has to hang
|
|
2072
|
-
* that much lower for the crease to land on the ground and the slack to
|
|
2073
|
-
* lie ON it rather than in mid-air parallel to it.
|
|
2074
|
-
*
|
|
2075
|
-
* The bound used to be 0, so the one thing this option documented itself
|
|
2076
|
-
* as doing was the one thing it could not do.
|
|
2077
|
-
*/
|
|
2078
|
-
hover: z.ZodDefault<z.ZodNumber>;
|
|
2079
|
-
/** Spread of deformation — no two lengths of hung paper drape alike. */
|
|
2080
|
-
drape: z.ZodDefault<z.ZodNumber>;
|
|
2081
|
-
seed: z.ZodDefault<z.ZodNumber>;
|
|
2082
|
-
}, "strip", z.ZodTypeAny, {
|
|
2083
|
-
path: {
|
|
2084
|
-
points: [number, number][];
|
|
2085
|
-
closed: boolean;
|
|
2086
|
-
};
|
|
2087
|
-
seed: number;
|
|
2088
|
-
drape: number;
|
|
2089
|
-
hover: number;
|
|
2090
|
-
margin: number;
|
|
2091
|
-
twist: number;
|
|
2092
|
-
aisle: number;
|
|
2093
|
-
breathe: number;
|
|
2094
|
-
rise: number;
|
|
2095
|
-
}, {
|
|
2096
|
-
path?: {
|
|
2097
|
-
points?: [number, number][] | undefined;
|
|
2098
|
-
closed?: boolean | undefined;
|
|
2099
|
-
} | undefined;
|
|
2100
|
-
seed?: number | undefined;
|
|
2101
|
-
drape?: number | undefined;
|
|
2102
|
-
hover?: number | undefined;
|
|
2103
|
-
margin?: number | undefined;
|
|
2104
|
-
twist?: number | undefined;
|
|
2105
|
-
aisle?: number | undefined;
|
|
2106
|
-
breathe?: number | undefined;
|
|
2107
|
-
rise?: number | undefined;
|
|
2108
|
-
}>;
|
|
2109
|
-
declare const colonnade: Layout<z.infer<typeof colonnadeSchema>>;
|
|
2110
|
-
/**
|
|
2111
|
-
* A block of stamps: flat rows × columns grid in register, floating a hair
|
|
2112
|
-
* above the (field-rendered) backing sheet. Standard layout contract — it
|
|
2113
|
-
* also works standalone as a plain grid; `backing`/`backingMargin` are read
|
|
2114
|
-
* by the field renderer, not by `pose`.
|
|
2115
|
-
*/
|
|
2116
|
-
declare const sheet: Layout<SheetLayoutOptions>;
|
|
2117
|
-
declare function registerLayout(layout: Layout<AnyOptions>): void;
|
|
2118
|
-
declare function getLayout(id: string): Layout<AnyOptions>;
|
|
2119
|
-
declare function listLayouts(): string[];
|
|
2120
|
-
|
|
2121
|
-
/**
|
|
2122
|
-
* Framing a field is arithmetic, not guesswork: layouts are pure `pose`
|
|
2123
|
-
* functions, so we can just ask one where all its sheets are and put the
|
|
2124
|
-
* camera where they all fit. Community layouts get framed for free.
|
|
2125
|
-
*/
|
|
2126
|
-
interface FieldBounds {
|
|
2127
|
-
center: [number, number, number];
|
|
2128
|
-
/** Half-extents — the box reaches `center ± half`. */
|
|
2129
|
-
half: [number, number, number];
|
|
2130
|
-
}
|
|
2131
|
-
/**
|
|
2132
|
-
* The box `n` sheets occupy under a layout. Each sheet is treated as a ball
|
|
2133
|
-
* of its half-diagonal, which covers every rotation the pose can apply
|
|
2134
|
-
* without having to build the pose matrices.
|
|
2135
|
-
*/
|
|
2136
|
-
declare function fieldBounds(layout: Layout<AnyOptions>, n: number, options: unknown, sheet: SheetDims): FieldBounds;
|
|
2137
|
-
/**
|
|
2138
|
-
* Where to put a perspective camera so every sheet a layout poses lands in
|
|
2139
|
-
* frame. Solved per sheet at its own depth rather than against the field's
|
|
2140
|
-
* bounding box: a `ring`'s widest sheets sit at mid-depth, and pretending
|
|
2141
|
-
* that width exists at the near face would shove the camera far enough back
|
|
2142
|
-
* to lose the gallery entirely.
|
|
2143
|
-
*/
|
|
2144
|
-
declare function fitCamera(layout: Layout<AnyOptions>, n: number, options: unknown, sheet: SheetDims, fovDeg: number, aspect: number, margin?: number): {
|
|
2145
|
-
position: [number, number, number];
|
|
2146
|
-
target: [number, number, number];
|
|
2147
|
-
};
|
|
2148
|
-
|
|
2149
|
-
/** How much of the incident key light a fully translucent sheet passes on. */
|
|
2150
|
-
declare const TRANSMISSION_GAIN = 0.5;
|
|
2151
|
-
interface TranslucencyValues {
|
|
2152
|
-
translucency: number;
|
|
2153
|
-
/** Unit world direction from the scene toward the key light. */
|
|
2154
|
-
direction: THREE.Vector3;
|
|
2155
|
-
color: THREE.Color;
|
|
2156
|
-
/** Light the room passes through the sheet from every direction at once. */
|
|
2157
|
-
ambient: number;
|
|
2158
|
-
}
|
|
2159
|
-
/**
|
|
2160
|
-
* Resolve the transmission uniforms from the paper and the scene's lighting
|
|
2161
|
-
* — the key light's own position and color, so translucency can never
|
|
2162
|
-
* disagree with the lamp casting the shadows.
|
|
2163
|
-
*
|
|
2164
|
-
* It takes a resolved rig as well as a name because a rig is no longer
|
|
2165
|
-
* always a preset: once the light can be moved, the lamp this sheet is
|
|
2166
|
-
* backlit by is the one the SCENE ended up with, not the one the paper was
|
|
2167
|
-
* authored against.
|
|
2168
|
-
*/
|
|
2169
|
-
declare function translucencyValues(translucency: number, lighting: LightingName | LightingPreset): TranslucencyValues;
|
|
2170
|
-
/** Ready-to-bind uniform objects for a shader program. */
|
|
2171
|
-
declare function translucencyUniforms(translucency: number, lighting: LightingName | LightingPreset): Record<string, {
|
|
2172
|
-
value: unknown;
|
|
2173
|
-
}>;
|
|
2174
|
-
|
|
2175
|
-
/**
|
|
2176
|
-
* A stock is a named bundle of material + geometry defaults — choosing paper
|
|
2177
|
-
* at a print shop. Individual controls always override; schema-wise these are
|
|
2178
|
-
* just defaults.
|
|
2179
|
-
*/
|
|
2180
|
-
interface Stock {
|
|
2181
|
-
id: StockName;
|
|
2182
|
-
label: string;
|
|
2183
|
-
/** Base tint, also used as the canvas background behind content. */
|
|
2184
|
-
color: string;
|
|
2185
|
-
roughness: number;
|
|
2186
|
-
/** 0 = opaque. Vellum is translucent. */
|
|
2187
|
-
opacity: number;
|
|
2188
|
-
/**
|
|
2189
|
-
* How much light passes THROUGH the sheet when something is behind it,
|
|
2190
|
-
* 0..1. Distinct from `opacity`: newsprint is fully opaque to look at and
|
|
2191
|
-
* still glows on a lightbox. This is what makes a backlit banner read.
|
|
2192
|
-
*/
|
|
2193
|
-
translucency: number;
|
|
2194
|
-
/** Ink multiply tint for content drawn on this stock (thermal prints grey-black). */
|
|
2195
|
-
inkColor: string;
|
|
2196
|
-
/** Thermal-printer banding intensity baked into the grain effect. */
|
|
2197
|
-
banding: number;
|
|
2198
|
-
/** Surface effects this stock ships with; explicit surface config overrides per key. */
|
|
2199
|
-
defaultSurface: {
|
|
2200
|
-
grain?: number;
|
|
2201
|
-
aging?: number;
|
|
2202
|
-
};
|
|
2203
|
-
/** Reversed front-content ghost on the backside (thin stocks let ink show). */
|
|
2204
|
-
showThrough: number;
|
|
2205
|
-
/** Glossy near-white glue underside (stickers) — forces showThrough 0. */
|
|
2206
|
-
adhesive: boolean;
|
|
2207
|
-
}
|
|
2208
|
-
declare const stocks: Record<StockName, Stock>;
|
|
2209
|
-
declare function getStock(name: StockName): Stock;
|
|
2210
|
-
|
|
2211
|
-
/**
|
|
2212
|
-
* Grid shape for `count` tiles of a given aspect (height / width).
|
|
2213
|
-
*
|
|
2214
|
-
* Square tiles want a square grid, but a stage banner is 5.7 times taller
|
|
2215
|
-
* than it is wide, and packing those into a square GRID makes an atlas five
|
|
2216
|
-
* times taller than it is wide — which then has to be squashed to fit the
|
|
2217
|
-
* texture budget, and the content with it. Choosing cols/rows ≈ aspect keeps
|
|
2218
|
-
* the atlas itself roughly square whatever shape the paper is.
|
|
2219
|
-
*/
|
|
2220
|
-
declare function atlasGrid(count: number, aspect?: number): {
|
|
2221
|
-
cols: number;
|
|
2222
|
-
rows: number;
|
|
2223
|
-
};
|
|
2224
|
-
interface ContentAtlas {
|
|
2225
|
-
texture: THREE.CanvasTexture;
|
|
2226
|
-
cols: number;
|
|
2227
|
-
rows: number;
|
|
2228
|
-
}
|
|
2229
|
-
declare function useContentAtlas(contents: ContentConfig[], sheet: SheetConfig, stock: Stock): ContentAtlas | null;
|
|
2230
|
-
|
|
2231
|
-
/** highp float + sin/cos implementation differences across GPUs. */
|
|
2232
|
-
declare const PARITY_EPSILON = 0.0005;
|
|
2233
|
-
interface ParityCase {
|
|
2234
|
-
name: string;
|
|
2235
|
-
stack: DeformerInstance[];
|
|
2236
|
-
sheet: SheetDims;
|
|
2237
|
-
t: number;
|
|
2238
|
-
}
|
|
2239
|
-
interface ParityResult {
|
|
2240
|
-
name: string;
|
|
2241
|
-
maxError: number;
|
|
2242
|
-
pass: boolean;
|
|
2243
|
-
}
|
|
2244
|
-
/** One case per deformer feature worth guarding, both easy and adversarial. */
|
|
2245
|
-
declare const parityCases: ParityCase[];
|
|
2246
|
-
/** Run every parity case. Requires a WebGL2 context with float render targets. */
|
|
2247
|
-
declare function runParityHarness(canvas?: HTMLCanvasElement): ParityResult[];
|
|
2248
|
-
|
|
2249
|
-
type PinMode = 'top-edge' | 'top-corners' | 'corner' | 'none';
|
|
2250
|
-
interface ClothParams {
|
|
2251
|
-
/** Bend-spring strength: 1 = crisp paper, 0 = silk. Fabric mode falls out for free. */
|
|
2252
|
-
stiffness: number;
|
|
2253
|
-
gravity: number;
|
|
2254
|
-
wind: number;
|
|
2255
|
-
/** Local-space y of the ground plane; particles settle onto it. */
|
|
2256
|
-
floor: number;
|
|
2257
|
-
}
|
|
2258
|
-
/**
|
|
2259
|
-
* Verlet mass-spring grid on the sheet's own vertices — structural + shear +
|
|
2260
|
-
* bend springs, pins as the interface, wind as a force field, fixed timestep
|
|
2261
|
-
* with substeps, sleep when kinetic energy is negligible.
|
|
2262
|
-
*
|
|
2263
|
-
* Constraint (enforced in the schema): cloth OWNS vertex positions — a paper
|
|
2264
|
-
* runs a behavior (deformer stack) OR cloth, never both. Pure JS, no three
|
|
2265
|
-
* dependency: the PaperMesh adapter copies `positions` into the geometry.
|
|
2266
|
-
*/
|
|
2267
|
-
declare class ClothSim {
|
|
2268
|
-
readonly cols: number;
|
|
2269
|
-
readonly rows: number;
|
|
2270
|
-
readonly count: number;
|
|
2271
|
-
readonly positions: Float32Array;
|
|
2272
|
-
private readonly prev;
|
|
2273
|
-
private readonly pinned;
|
|
2274
|
-
private readonly pinTargets;
|
|
2275
|
-
private readonly constraints;
|
|
2276
|
-
private params;
|
|
2277
|
-
private time;
|
|
2278
|
-
private accumulator;
|
|
2279
|
-
private stillFrames;
|
|
2280
|
-
private grabbedIndex;
|
|
2281
|
-
/** True when the sim has settled and steps are skipped. */
|
|
2282
|
-
asleep: boolean;
|
|
2283
|
-
constructor(cols: number, rows: number, width: number, height: number, pins: PinMode, params: ClothParams);
|
|
2284
|
-
setParams(params: Partial<ClothParams>): void;
|
|
2285
|
-
wake(): void;
|
|
2286
|
-
/** Nearest particle to a local-space point — the grab interface. */
|
|
2287
|
-
grabNearest(x: number, y: number, z: number): number;
|
|
2288
|
-
moveGrab(x: number, y: number, z: number): void;
|
|
2289
|
-
release(): void;
|
|
2290
|
-
step(delta: number): void;
|
|
2291
|
-
private substep;
|
|
2292
|
-
}
|
|
2293
|
-
|
|
2294
|
-
/**
|
|
2295
|
-
* Surface effects are fragment-side chunks composed into ONE shader program
|
|
2296
|
-
* per effect set (grain + deckle + aging = one program). Uniforms are
|
|
2297
|
-
* namespaced per effect; shared helpers (noise) are included once.
|
|
2298
|
-
*/
|
|
2299
|
-
interface ComposedSurface {
|
|
2300
|
-
/** Distinguishes shader *structures* — same key ⇒ same program, only uniforms change. */
|
|
2301
|
-
structureKey: string;
|
|
2302
|
-
vertexShader: string;
|
|
2303
|
-
fragmentShader: string;
|
|
2304
|
-
uniforms: Record<string, {
|
|
2305
|
-
value: unknown;
|
|
2306
|
-
}>;
|
|
2307
|
-
/** Deckle discards via alphaTest (not blending) so shadows stay correct. */
|
|
2308
|
-
alphaTest: number;
|
|
2309
|
-
}
|
|
2310
|
-
/** Which content textures exist — part of the shader structure. */
|
|
2311
|
-
interface SurfaceMaps {
|
|
2312
|
-
hasFrontMap: boolean;
|
|
2313
|
-
hasBackMap: boolean;
|
|
2314
|
-
}
|
|
2315
|
-
/**
|
|
2316
|
-
* Compose the enabled effects into one program. The shader owns the base
|
|
2317
|
-
* color entirely: the FRONT face samples the content texture, the BACK face
|
|
2318
|
-
* renders the stock (or content.back) with an optional reversed show-through
|
|
2319
|
-
* ghost — a single DoubleSide map would mirror the front content onto the
|
|
2320
|
-
* back, which real paper doesn't do.
|
|
2321
|
-
*/
|
|
2322
|
-
declare function composeSurface(surface: SurfaceConfig, stock: Stock, thickness: number, maps?: SurfaceMaps,
|
|
2323
|
-
/** World dims — perforation holes are sized in world units. */
|
|
2324
|
-
sheet?: {
|
|
2325
|
-
width: number;
|
|
2326
|
-
height: number;
|
|
2327
|
-
},
|
|
2328
|
-
/** Whose key light transmission is measured against — a preset name or the scene's resolved rig. */
|
|
2329
|
-
lighting?: LightingName | LightingPreset): ComposedSurface;
|
|
2330
|
-
|
|
2331
|
-
interface PaperMaterialProps {
|
|
2332
|
-
stock: Stock;
|
|
2333
|
-
texture: THREE.Texture | null;
|
|
2334
|
-
/** content.back rendered on the reverse side (stock color otherwise). */
|
|
2335
|
-
backTexture?: THREE.Texture | null;
|
|
2336
|
-
surface: SurfaceConfig;
|
|
2337
|
-
thickness: number;
|
|
2338
|
-
/** World dims — perforation holes are sized in world units. */
|
|
2339
|
-
sheet?: {
|
|
2340
|
-
width: number;
|
|
2341
|
-
height: number;
|
|
2342
|
-
};
|
|
2343
|
-
/**
|
|
2344
|
-
* Scene lighting — transmission is measured against its key light. A
|
|
2345
|
-
* `<LightRig>` above this material wins over it: in a stage the paper is
|
|
2346
|
-
* lit by the hall, not by the preset it was authored with.
|
|
2347
|
-
*/
|
|
2348
|
-
lighting?: LightingName;
|
|
2349
|
-
}
|
|
2350
|
-
/**
|
|
2351
|
-
* The paper's skin: MeshStandardMaterial (real lighting preserved) extended
|
|
2352
|
-
* with the composed surface-effect chunks. Content textures are sampled by
|
|
2353
|
-
* OUR fragment (not material.map) so front and back faces can differ — real
|
|
2354
|
-
* paper doesn't mirror its front through the sheet. Programs rebuild only
|
|
2355
|
-
* on structure change; value edits mutate uniforms in place.
|
|
2356
|
-
*/
|
|
2357
|
-
declare function PaperMaterial({ stock, texture, backTexture, surface, thickness, sheet, lighting, }: PaperMaterialProps): react.JSX.Element;
|
|
2358
|
-
|
|
2359
|
-
type ReceiptContent = Extract<ContentConfig, {
|
|
2360
|
-
type: 'receipt';
|
|
2361
|
-
}>;
|
|
2362
|
-
interface ReceiptTotals {
|
|
2363
|
-
subtotal: number;
|
|
2364
|
-
tax: number;
|
|
2365
|
-
total: number;
|
|
2366
|
-
}
|
|
2367
|
-
declare function receiptTotals(content: ReceiptContent): ReceiptTotals;
|
|
2368
|
-
/** Deterministic bar widths from a string — stylized Code-128 look. */
|
|
2369
|
-
declare function barcodeBars(seed: string): number[];
|
|
2370
|
-
|
|
2371
|
-
type CardContent = Extract<ContentConfig, {
|
|
2372
|
-
type: 'card';
|
|
2373
|
-
}>;
|
|
2374
|
-
|
|
2375
|
-
/**
|
|
2376
|
-
* Line breaking, shared by every content type that sets prose.
|
|
2377
|
-
*
|
|
2378
|
-
* It lives on its own because `paintText` and `paintCard` were about to
|
|
2379
|
-
* carry two copies of it, and two copies of a line-breaker is two answers to
|
|
2380
|
-
* "where does this wrap" — which on a sheet that CURLS is not a cosmetic
|
|
2381
|
-
* disagreement: the reader sees the break land on a fold.
|
|
2382
|
-
*/
|
|
2383
|
-
/**
|
|
2384
|
-
* Wrap a paragraph to a measure, breaking a word that cannot fit on its own.
|
|
2385
|
-
*
|
|
2386
|
-
* The last clause is the part the old loop got wrong. It appended a word
|
|
2387
|
-
* whenever the line was empty, on the reasonable theory that one word always
|
|
2388
|
-
* fits — but a long URL or a compound on a narrow banner does not, and it
|
|
2389
|
-
* ran off the edge of the sheet with nothing to stop it. A sheet is a
|
|
2390
|
-
* physical object: type that leaves it has left it.
|
|
1125
|
+
* The last clause is the part the old loop got wrong. It appended a word
|
|
1126
|
+
* whenever the line was empty, on the reasonable theory that one word always
|
|
1127
|
+
* fits — but a long URL or a compound on a narrow banner does not, and it
|
|
1128
|
+
* ran off the edge of the sheet with nothing to stop it. A sheet is a
|
|
1129
|
+
* physical object: type that leaves it has left it.
|
|
2391
1130
|
*/
|
|
2392
1131
|
declare function wrapLines(ctx: CanvasRenderingContext2D, text: string, maxWidth: number, font: string): string[];
|
|
2393
1132
|
|
|
2394
|
-
|
|
2395
|
-
* Deep-merge preset config with prop overrides (overrides win; arrays and
|
|
2396
|
-
* discriminated unions replace wholesale, plain objects merge).
|
|
2397
|
-
*
|
|
2398
|
-
* Lives in its own module (not serialize.ts) because the schema needs it to
|
|
2399
|
-
* validate state overrides — schema → serialize would be circular.
|
|
2400
|
-
*/
|
|
2401
|
-
declare function mergeConfig<T>(base: T, override: unknown): T;
|
|
2402
|
-
/**
|
|
2403
|
-
* Like {@link mergeConfig}, but an explicit `undefined` DELETES its key instead
|
|
2404
|
-
* of being ignored — the semantics a BASE-config write needs: structural
|
|
2405
|
-
* setters can clear `behavior`/`deformers` or toggle a surface effect off.
|
|
2406
|
-
* Discriminated unions (differing `type`) still replace wholesale.
|
|
2407
|
-
*/
|
|
2408
|
-
declare function mergeWithDeletes<T>(base: T, patch: unknown): T;
|
|
2409
|
-
|
|
2410
|
-
/** Parse anything preset-shaped (object or JSON string) into a full, defaulted config. */
|
|
2411
|
-
declare function parsePreset(input: PaperConfigInput | string): PaperConfig;
|
|
2412
|
-
/** Serialize a config to `.paper` JSON. */
|
|
2413
|
-
declare function serializePreset(config: PaperConfig): string;
|
|
2414
|
-
|
|
2415
|
-
/** The minimal PaperConfigInput that parses back to `config`. */
|
|
2416
|
-
declare function diffConfig(config: PaperConfig): PaperConfigInput;
|
|
2417
|
-
/**
|
|
2418
|
-
* A plain `<Paper />` snippet with only the non-default props — the
|
|
2419
|
-
* secondary export for people who read code.
|
|
2420
|
-
*/
|
|
2421
|
-
declare function buildJsxSnippet(config: PaperConfig): string;
|
|
2422
|
-
|
|
2423
|
-
/**
|
|
2424
|
-
* The primary export consumer is a coding agent; the human is the courier.
|
|
2425
|
-
* This template IS product surface: versioned, snapshot-tested, regenerated
|
|
2426
|
-
* from the config. Anatomy is fixed — install → inlined code → placement
|
|
2427
|
-
* contract → a verification step the agent can self-check → pre-empted
|
|
2428
|
-
* failure modes.
|
|
2429
|
-
*/
|
|
2430
|
-
/**
|
|
2431
|
-
* v2: field exports (multi-preset galleries with inlined preset consts).
|
|
2432
|
-
* v3: interaction states, sheet/backing fields, drop zones, carry/flight.
|
|
2433
|
-
*/
|
|
2434
|
-
declare const AGENT_PAYLOAD_VERSION = 3;
|
|
2435
|
-
/** One line an agent can verify against what it sees after `npm run dev`. */
|
|
2436
|
-
declare function describeConfig(config: PaperConfig): string;
|
|
2437
|
-
/** The self-contained integration brief — one paste into a coding agent. */
|
|
2438
|
-
declare function buildAgentPayload(config: PaperConfig): string;
|
|
2439
|
-
|
|
2440
|
-
/**
|
|
2441
|
-
* Field-mode export. Every preset a field references is INLINED as a const —
|
|
2442
|
-
* receivers don't have the sender's local preset library. Same fixed payload
|
|
2443
|
-
* anatomy as the single-paper export, same version.
|
|
2444
|
-
*/
|
|
2445
|
-
interface FieldExportPaper {
|
|
2446
|
-
/** Display name of the preset this slot uses (const naming + description). */
|
|
2447
|
-
presetName: string;
|
|
2448
|
-
/** The RESOLVED preset config (user presets included — nothing to fetch). */
|
|
2449
|
-
preset: PaperConfig;
|
|
2450
|
-
content?: ContentConfig;
|
|
2451
|
-
/** Per-slot state overrides (slot 3's hover deeper than slot 4's). */
|
|
2452
|
-
states?: PaperStatesInput;
|
|
2453
|
-
}
|
|
2454
|
-
interface FieldExportZone {
|
|
2455
|
-
id: string;
|
|
2456
|
-
accept?: string[];
|
|
2457
|
-
bounds: {
|
|
2458
|
-
position: [number, number, number];
|
|
2459
|
-
size: [number, number];
|
|
2460
|
-
};
|
|
2461
|
-
highlight?: 'none' | 'glow' | 'outline';
|
|
2462
|
-
}
|
|
2463
|
-
interface FieldExportInput {
|
|
2464
|
-
layout: string;
|
|
2465
|
-
layoutOptions?: Record<string, unknown>;
|
|
2466
|
-
motion?: {
|
|
2467
|
-
driver?: 'autoplay' | 'drag' | 'none';
|
|
2468
|
-
speed?: number;
|
|
2469
|
-
};
|
|
2470
|
-
entrance?: {
|
|
2471
|
-
type?: 'rise' | 'scatter' | 'none';
|
|
2472
|
-
stagger?: number;
|
|
2473
|
-
duration?: number;
|
|
2474
|
-
};
|
|
2475
|
-
papers: FieldExportPaper[];
|
|
2476
|
-
/** Drop zones — exported as `<DropZone>` children with an onPlace stub. */
|
|
2477
|
-
zones?: FieldExportZone[];
|
|
2478
|
-
}
|
|
2479
|
-
interface DistinctPreset {
|
|
2480
|
-
varName: string;
|
|
2481
|
-
presetName: string;
|
|
2482
|
-
preset: PaperConfig;
|
|
2483
|
-
}
|
|
2484
|
-
/** Distinct presets in slot order, with collision-safe const names. */
|
|
2485
|
-
declare function distinctFieldPresets(input: FieldExportInput): DistinctPreset[];
|
|
2486
|
-
/** The layout/motion/entrance props of the export, defaults stripped. */
|
|
2487
|
-
declare function diffFieldProps(input: FieldExportInput): Record<string, unknown>;
|
|
2488
|
-
/** The one-line visual an agent verifies after `npm run dev`. */
|
|
2489
|
-
declare function describeFieldConfig(input: FieldExportInput): string;
|
|
2490
|
-
/** Component source shared by the JSX snippet and the agent payload. */
|
|
2491
|
-
declare function buildFieldComponentSource(input: FieldExportInput): string;
|
|
2492
|
-
/** The self-contained field integration brief — one paste into a coding agent. */
|
|
2493
|
-
declare function buildFieldAgentPayload(input: FieldExportInput): string;
|
|
2494
|
-
|
|
2495
|
-
/**
|
|
2496
|
-
* Accessibility layer: reduced-motion handling, a hidden DOM mirror so the
|
|
2497
|
-
* paper's content exists for screen readers and find-in-page, and a no-WebGL
|
|
2498
|
-
* DOM fallback.
|
|
2499
|
-
*/
|
|
2500
|
-
declare function usePrefersReducedMotion(override?: boolean): boolean;
|
|
2501
|
-
declare function supportsWebGL(): boolean;
|
|
2502
|
-
/** Human-readable text equivalent of a paper's content. */
|
|
2503
|
-
declare function contentText(config: PaperConfig): string;
|
|
2504
|
-
/** Hidden DOM twin of the 3D paper — screen readers read it, ctrl-F finds it. */
|
|
2505
|
-
declare function PaperMirror({ config }: {
|
|
2506
|
-
config: PaperConfig;
|
|
2507
|
-
}): react.JSX.Element;
|
|
2508
|
-
/** No WebGL: the paper renders flat — content on a stock-tinted card. */
|
|
2509
|
-
declare function PaperFallback({ config }: {
|
|
2510
|
-
config: PaperConfig;
|
|
2511
|
-
}): react.JSX.Element;
|
|
2512
|
-
|
|
2513
|
-
/**
|
|
2514
|
-
* Stop-motion feel: quantize continuous time to animation "twos"
|
|
2515
|
-
* (12 steps per second, like shooting on twos at 24fps). Applies to
|
|
2516
|
-
* deformer time, idle motion, and behavior progress; the camera is
|
|
2517
|
-
* excluded by default — quantized cameras read as jank, not craft.
|
|
2518
|
-
*/
|
|
2519
|
-
declare const ON_TWOS_FPS = 12;
|
|
2520
|
-
declare function quantizeTime(t: number, fps?: number): number;
|
|
2521
|
-
/** Snap a 0..1 progress that plays over `duration` seconds to whole frames. */
|
|
2522
|
-
declare function quantizeProgress(p: number, duration: number, fps?: number): number;
|
|
2523
|
-
|
|
2524
|
-
declare function getPreset(name: string): PaperConfig;
|
|
2525
|
-
/** Register a user preset (validated). Built-in names are reserved. */
|
|
2526
|
-
declare function registerPreset(name: string, input: PaperConfigInput): void;
|
|
2527
|
-
declare function unregisterPreset(name: string): void;
|
|
2528
|
-
declare function isBuiltinPreset(name: string): boolean;
|
|
2529
|
-
declare function listPresets(): string[];
|
|
2530
|
-
/**
|
|
2531
|
-
* A collision-free preset name built from `base`: `base`, else `base 2`,
|
|
2532
|
-
* `base 3`, … The disambiguating suffix always grows from the SAME base — a
|
|
2533
|
-
* name derived from a synthetic base (e.g. an untitled import → "imported")
|
|
2534
|
-
* must not fall back to the original when it collides. `taken` reports whether
|
|
2535
|
-
* a candidate is already used (built-in or user preset).
|
|
2536
|
-
*/
|
|
2537
|
-
declare function uniquePresetName(base: string, taken: (name: string) => boolean): string;
|
|
2538
|
-
|
|
2539
|
-
/**
|
|
2540
|
-
* Resolve the subdivision grid for a sheet.
|
|
2541
|
-
*
|
|
2542
|
-
* `minSegments` is the correctness floor the active deformers require, and it
|
|
2543
|
-
* applies however `segments` is set. `autoSegments` is what those deformers
|
|
2544
|
-
* WANT for the options they are carrying, and it is what `'auto'` resolves
|
|
2545
|
-
* to — see `stackAutoSegments` and `core/tessellation.ts`.
|
|
2546
|
-
*
|
|
2547
|
-
* Both are per axis, because a demand is a demand along a DIRECTION. A
|
|
2548
|
-
* banner draped in folds across its width needs those folds resolved across
|
|
2549
|
-
* and needs almost nothing down its drop; a single number spread by aspect
|
|
2550
|
-
* ratio gives the drop the density and the folds the leftovers, which is
|
|
2551
|
-
* both the expensive answer and the wrong-looking one. A bare number is
|
|
2552
|
-
* still accepted and still means "this many, both ways".
|
|
2553
|
-
*
|
|
2554
|
-
* `'auto'` used to hand the long side a flat 72 whatever was on the sheet, so
|
|
2555
|
-
* a blank page was tessellated exactly as finely as a crumpled one and every
|
|
2556
|
-
* `minSegments` in the library was dead weight — nothing could ever raise a
|
|
2557
|
-
* grid that already started at the highest value anyone asked for. It now
|
|
2558
|
-
* sizes to the work, quantized onto a ladder so that dragging a slider does
|
|
2559
|
-
* not rebuild the mesh.
|
|
2560
|
-
*
|
|
2561
|
-
* Omitting `autoSegments` keeps the old flat 72, which is what a caller with
|
|
2562
|
-
* no deformer stack in hand should get — this helper is exported, and its
|
|
2563
|
-
* answer to an unchanged call should not have changed.
|
|
2564
|
-
*/
|
|
2565
|
-
declare function resolveSegments(sheet: SheetConfig, minSegments?: number | SegmentPair, autoSegments?: number | SegmentPair): [number, number];
|
|
2566
|
-
/**
|
|
2567
|
-
* Geometry factory. The sheet lives in its local XY plane, centered on the
|
|
2568
|
-
* origin, facing +Z. Deformers displace these vertices; the base (flat)
|
|
2569
|
-
* positions are kept by the caller for re-deformation each frame.
|
|
2570
|
-
*/
|
|
2571
|
-
declare function createSheetGeometry(sheet: SheetConfig, minSegments?: number | SegmentPair, autoSegments?: number | SegmentPair): THREE.PlaneGeometry;
|
|
2572
|
-
|
|
2573
|
-
type PaperMode = 'hero' | 'field';
|
|
2574
|
-
type PaperModeRequest = PaperMode | 'auto';
|
|
2575
|
-
interface ModeContext {
|
|
2576
|
-
interactive: boolean;
|
|
2577
|
-
physics: string;
|
|
2578
|
-
count: number;
|
|
2579
|
-
}
|
|
2580
|
-
/**
|
|
2581
|
-
* Hero mode: deformer stack runs in JS on the CPU, writing BufferGeometry
|
|
2582
|
-
* positions (correct raycasting, shadows, physics, handle drags).
|
|
2583
|
-
* Field mode: composed GLSL vertex chunks, instanced (galleries, 10+ sheets).
|
|
2584
|
-
* The same preset JSON drives both.
|
|
2585
|
-
*/
|
|
2586
|
-
declare function resolveMode(requested: PaperModeRequest, ctx: ModeContext): PaperMode;
|
|
2587
|
-
|
|
2588
|
-
export { AGENT_PAYLOAD_VERSION, AUTO_CEILING, type AeroPose, type BackingDrawSpec, type Behavior, BehaviorConfigInput, type BendOptions, type CardContent, type CarryOptions, type ClothParams, ClothSim, type ComposedDisplacement, type ComposedSurface, type ContentAtlas, ContentConfig, ContentConfigInput, type CrumpleBehaviorOptions, type CrumpleOptions, type CurlOptions, type DampedValue, type Deformer, type DeformerContext, type DeformerInstance, DeformerInstanceConfigInput, type DrapeOptions, DropZone, type DropZoneConfig, type DropZoneProps, DropZoneRegistry, FLAT_SEGMENTS, type FallOptions, type FieldA11yController, type FieldBounds, type FieldExportInput, type FieldExportPaper, type FieldExportZone, FieldPaperSlot, FilmName, type FlightOptions, type FlightParams, type FlipOptions, type FlyOptions, type FoldOptions, type HandleSpec, type HangOptions, type IdleName, type IdlePreset, type KeyboardCarry, type KeyboardStepResult, type Layout, type LetterFoldOptions, type LightAngles, type LightOverrides, type LightOverridesInput, LightRig, LightingName, type LightingPreset, ON_TWOS_FPS, PARITY_EPSILON, Paper, PaperConfig, PaperConfigInput, PaperEdge, PaperFallback, PaperField, PaperFieldMesh, type PaperFieldMeshProps, type PaperFieldProps, type PaperHandle, PaperLighting, type PaperLightingProps, PaperMaterial, type PaperMaterialProps, PaperMesh, type PaperMeshProps, PaperMirror, type PaperMode, type PaperModeRequest, type PaperPose, type PaperProps, PaperStateMachine, type PaperStateMachineOptions, PaperStatesInput, type ParityCase, type ParityResult, type PeelOptions, PhysicsConfigInput, type PinMode, type PlacedPaper, type ReceiptContent, type RibbonOptions, type RollOptions, SAG_TOL, SHEET_LIFT, SceneConfigInput, type SettleOptions, SheetConfig, type SheetDims, type SheetLayoutOptions, type StateEvent, StateName, type Stock, StockName, SurfaceConfig, SurfaceConfigInput, type SurfaceMaps, TRANSMISSION_GAIN, type TranslucencyValues, type UnrollOptions, type UsePaperStatesResult, type WaveOptions, accordion, applyDeformerStack, atlasGrid, barcodeBars, bend, bendOptionsSchema, book, buildAgentPayload, buildDisplacementGLSL, buildFieldAgentPayload, buildFieldComponentSource, buildFieldFragmentShader, buildFieldVertexShader, buildJsxSnippet, carry, carryDrive, carryOptionsSchema, colonnade, composeSurface, contentText, cornerNames, createSheetGeometry, crumple, crumpleBehavior, crumpleBehaviorOptionsSchema, crumpleOptionsSchema, curl, curlOptionsSchema, dampTo, describeConfig, describeFieldConfig, diffConfig, diffFieldProps, displacePoint, distinctFieldPresets, drape, drapeOptionsSchema, drawBacking, fall, fallOptionsSchema, fan, fieldBounds, fieldKeyboardStep, fitCamera, flattenNumeric, flight, flightOptionsSchema, flightPose, flip, flipOptionsSchema, fly, flyOptionsSchema, fold, foldOptionsSchema, getBehavior, getDeformer, getIdlePreset, getLayout, getLightingPreset, getPreset, getStock, gust, hang, hangOptionsSchema, idleNames, idlePresets, isBuiltinPreset, letterFold, letterFoldOptionsSchema, lightAngles, lightPosition, lightSchema, lightenHex, lightingPresets, listBehaviors, listDeformers, listLayouts, listPresets, makeGoboTexture, mergeConfig, mergeWithDeletes, outwardCorner, parityCases, parsePreset, peel, peelOptionsSchema, pile, quantizeProgress, quantizeSegments, quantizeTime, rack, receiptTotals, recordStateOverride, registerBehavior, registerDeformer, registerLayout, registerPreset, resolveConfig, resolveDeformerStack, resolveLighting, resolveMode, resolveSegments, resolveStateConfig, ribbon, ribbonOptionsSchema, ring, roll, rollOptionsSchema, runParityHarness, segmentsForArc, segmentsForSine, serializePreset, settle, settleOptionsSchema, sheet, sheetBackingSize, sheetLayoutSchema, sheetSlotXY, silhouetteRects, spanAlong, spill, spread, stackAutoSegments, stackMinSegments, stackUniformValues, stateEventTransitions, stocks, stripStates, supportsWebGL, sweep, tornEdgesOnDetach, translucencyUniforms, translucencyValues, uniquePresetName, unregisterPreset, unroll, unrollOptionsSchema, useContentAtlas, usePaperStates, usePrefersReducedMotion, wall, wave, waveOptionsSchema, withSheetCellFromPaper, wrapLines, zoneAccepts };
|
|
1133
|
+
export { AUTO_CEILING, type Behavior, BehaviorConfigInput, ContentConfig, ContentConfigInput, type Deformer, type DeformerContext, type DeformerInstance, DeformerInstanceConfigInput, DropZone, type DropZoneConfig, type DropZoneProps, FLAT_SEGMENTS, type FieldExportInput, type FieldExportPaper, type FieldExportZone, FieldPaperSlot, FilmName, type HandleSpec, type IdleName, type IdlePreset, type Layout, type LightAngles, type LightOverrides, type LightOverridesInput, LightRig, LightingName, type LightingPreset, PARITY_EPSILON, Paper, PaperConfig, PaperConfigInput, PaperField, PaperFieldMesh, type PaperFieldMeshProps, type PaperFieldProps, type PaperHandle, PaperLighting, type PaperLightingProps, PaperMesh, type PaperMeshProps, type PaperPose, type PaperProps, PaperStatesInput, type ParityCase, type ParityResult, PhysicsConfigInput, type PlacedPaper, SAG_TOL, SceneConfigInput, SheetConfig, type SheetDims, type SheetLayoutOptions, type StateEvent, type Stock, StockName, SurfaceConfigInput, type UsePaperStatesResult, buildAgentPayload, buildFieldAgentPayload, buildFieldComponentSource, buildJsxSnippet, describeConfig, describeFieldConfig, diffConfig, diffFieldProps, getBehavior, getDeformer, getLayout, getPreset, getStock, idleNames, isBuiltinPreset, lightAngles, lightSchema, listBehaviors, listDeformers, listLayouts, listPresets, mergeConfig, mergeWithDeletes, parityCases, parsePreset, quantizeSegments, recordStateOverride, registerBehavior, registerDeformer, registerLayout, registerPreset, resolveLighting, resolveStateConfig, runParityHarness, segmentsForArc, segmentsForSine, serializePreset, sheetLayoutSchema, spanAlong, stocks, supportsWebGL, uniquePresetName, unregisterPreset, usePaperStates, usePrefersReducedMotion, wrapLines };
|