@weasel-js/core 1.7.3 → 1.8.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/CHANGELOG.md +47 -0
- package/dist/animation/colorHelpers.js +58 -58
- package/dist/animation/colorHelpers.js.map +1 -1
- package/dist/animation/engine/axes.js +36 -0
- package/dist/animation/engine/axes.js.map +1 -0
- package/dist/animation/engine/codec.js +170 -0
- package/dist/animation/engine/codec.js.map +1 -0
- package/dist/animation/engine/integrator.js +12 -0
- package/dist/animation/engine/integrator.js.map +1 -0
- package/dist/animation/useAnimator.js +199 -157
- package/dist/animation/useAnimator.js.map +1 -1
- package/dist/{builtins-C-gRyVRx.d.ts → builtins-BW32dOXy.d.ts} +74 -8
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js +12 -12
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
- package/dist/canvas/pickWalk.js +2 -2
- package/dist/canvas/pickWalk.js.map +1 -1
- package/dist/canvas/sceneSlotWiring.js +1 -1
- package/dist/canvas/sceneSlotWiring.js.map +1 -1
- package/dist/canvas/sceneViewRender.js +1 -1
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/core/scene/effectivePose.js +1 -1
- package/dist/core/scene/effectivePose.js.map +1 -1
- package/dist/core/scene/poseOverrides.js +42 -22
- package/dist/core/scene/poseOverrides.js.map +1 -1
- package/dist/core/scene/types.js.map +1 -1
- package/dist/index.d.ts +28 -18
- package/dist/renderer/WeaselRenderer.js +4 -1
- package/dist/renderer/WeaselRenderer.js.map +1 -1
- package/dist/renderer/batchClip.js +46 -0
- package/dist/renderer/batchClip.js.map +1 -0
- package/dist/renderer/draw.js +367 -294
- package/dist/renderer/draw.js.map +1 -1
- package/dist/renderer/drawBatch.js +75 -45
- package/dist/renderer/drawBatch.js.map +1 -1
- package/dist/renderer.d.ts +1 -1
- package/dist/tools/builtin/text/useTextTool.js +8 -8
- package/dist/tools/builtin/text/useTextTool.js.map +1 -1
- package/dist/tools/prefs.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +16 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAnimator.js","names":["numericLerp","a","b","t","resolveSpringConstants","preset","o","SPRING_PRESETS","useAnimator","optsRef","useLatest","opts","animations","useRef","nextId","tickDepth","globalTimeScale","globalPaused","colorOverrides","ColorOverrideRegistry","tickSubscribers","tickAllRef","frameLoop","useVisibleRaf","useCallback","anim","frameLoopRef","mountedRef","trippedRef","tripwire","useMemo","now","staggerTimers","c","m","cb","ms","x","h","completionListeners","fireCompletion","ls","id","watchCompletion","cur","idx","hub","AnimatorEventHub","infoOf","info","reportEnd","retire","by","tickAll","finished","realDt","scale","sub","err","ensureLoop","cancelByKey","ids","key","register","seed","s","createSupervisor","onCancelCb","base","tween","easing","resolveEasing","easeOut","factoryFn","perTickInterp","lastValueEmitted","virtualNow","elapsed","nowMs","eased","value","physics","add","subtract","v","k","magnitude","kBase","restThreshold","target","velocity","lastTime","dt","displacement","springForce","dampingForce","damping","accel","mass","velRested","posRested","newTo","api","friction","handle","retired","createLoop","factory","loopOpts","createTweenLoop","tweenLoopOpts","createStagger","items","delay","staggerOpts","createTimeline","animation","path","sup","listener","rows","row","useInsertionEffect","useEffect","overrides"],"sources":["../../src/animation/useAnimator.ts"],"sourcesContent":["import { useCallback, useEffect, useInsertionEffect, useMemo, useRef } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport { useVisibleRaf } from '../scheduling/useVisibleRaf';\nimport { easeOut, SPRING_PRESETS, resolveEasing } from '@weasel-js/geom';\nimport { createLoop, createTweenLoop } from './loop';\nimport { createStagger, type StaggerTimers } from './stagger';\nimport { createTimeline } from './timeline/createTimeline';\nimport { ColorOverrideRegistry } from './colorRegistry';\nimport { AnimatorEventHub } from './observe';\nimport type { Supervisor, SupervisorOptions, WatchCompletion } from './supervisor';\nimport type {\n AnimationHandle,\n AnimationInfo,\n AnimationKind,\n Animator,\n TweenOptions,\n SpringOptions,\n DecayOptions,\n PhysicsOptions,\n PhysicsHandle,\n UseAnimatorOptions,\n LiveAnimation,\n} from './types';\nimport type { TimelineOptions } from './timeline/types';\n\ninterface ActiveAnimation {\n id: number;\n kind: AnimationKind;\n cancelKey?: string;\n label?: string;\n /** Built the first time something reports on this entry, then shared. */\n info?: AnimationInfo;\n /** 0–1 where the animation has an end to measure against. */\n progress?(virtualNow: number): number;\n /** Its `end` is reported, though the entry stays on the table until the\n * frame's ticks are done. */\n ended?: boolean;\n paused: boolean;\n timeScale: number;\n virtualNow: number;\n lastRealNow: number | null;\n /** Returns true when finished. Called once per frame with the current\n * virtual-ms timestamp and the effective scale it advanced at this frame. */\n tick(virtualNow: number, scale: number): boolean;\n /** Called when the animation is cancelled. Skips onDone. */\n onCancel?(): void;\n}\n\nconst numericLerp = (a: number, b: number, t: number): number => a + (b - a) * t;\n\nfunction resolveSpringConstants(o: { preset?: string; stiffness?: number; damping?: number; mass?: number }) {\n const preset = o.preset ? SPRING_PRESETS[o.preset as keyof typeof SPRING_PRESETS] : null;\n return {\n stiffness: o.stiffness ?? preset?.stiffness ?? 170,\n damping: o.damping ?? preset?.damping ?? 26,\n mass: o.mass ?? preset?.mass ?? 1,\n };\n}\n\n/** Create the animator for a canvas. One rAF loop drives every animation it\n * owns, and everything still running is cancelled on unmount. */\nexport function useAnimator(opts: UseAnimatorOptions = {}): Animator {\n const optsRef = useLatest(opts);\n const animations = useRef<Map<number, ActiveAnimation>>(new Map());\n const nextId = useRef(1);\n /**\n * Re-entrancy counter incremented around each animation tick in tickAll.\n * Exposed via `isTicking()`. > 0 ⇒ we're currently inside an animation's\n * onTick; an adapter wrapper (`animateOnSetPose`) seeing this flag should\n * write through to the base adapter rather than schedule a new tween.\n */\n const tickDepth = useRef(0);\n const globalTimeScale = useRef(1);\n const globalPaused = useRef(false);\n const colorOverrides = useRef<ColorOverrideRegistry>(new ColorOverrideRegistry());\n // Per-frame tick subscribers. Notified after each batch of animation\n // ticks (i.e. after `colorOverrides` has settled to its latest values\n // but before the next RAF schedules). Used by `<SceneCanvas animator>`\n // to request a redraw on every active frame.\n const tickSubscribers = useRef<Set<() => void>>(new Set());\n\n // The loop runs behind the visibility gate. `tickAll` is built inside the\n // memo below, so the frame callback reaches it through a ref, published\n // once the memo's render commits.\n const tickAllRef = useRef<((t: number) => void) | null>(null);\n const frameLoop = useVisibleRaf(\n useCallback((t: number) => { tickAllRef.current?.(t); }, []),\n {\n requestFrame: opts.requestFrame,\n cancelFrame: opts.cancelFrame,\n // An hour spent hidden is not an hour the animation ran: dropping each\n // animation's last timestamp makes the resuming frame's `realDt` zero.\n onResume: () => {\n for (const anim of animations.current.values()) anim.lastRealNow = null;\n },\n },\n );\n const frameLoopRef = useLatest(frameLoop);\n\n // Tripwire: dev-only flag that every tween/spring/decay's tick reads to\n // detect \"animation fired after the host component unmounted.\" This is a\n // symptom of cleanup not running (regression) — typically because someone\n // refactored useAnimator and forgot to wire the unmount effect. The flag\n // flips false on cleanup; ticks see it and log a one-time error so the\n // bug is loud rather than just visually-doubled animation.\n const mountedRef = useRef(true);\n const trippedRef = useRef(false);\n const tripwire = (): boolean => {\n if (mountedRef.current) return false;\n if (!trippedRef.current) {\n trippedRef.current = true;\n const isDev = typeof process !== 'undefined' ? process.env.NODE_ENV !== 'production' : true;\n if (isDev) {\n console.error(\n 'weasel useAnimator: animation tick fired after the host component unmounted. ' +\n 'This usually means cleanup logic was bypassed — check for refactors that ' +\n 'removed the unmount effect or replaced cancelAll with a custom path.',\n );\n }\n }\n return true;\n };\n\n const { api, tickAll } = useMemo(() => {\n // Default to performance.now() so the time origin matches the\n // requestAnimationFrame callback's DOMHighResTimeStamp argument.\n // Using Date.now() here would mix epoch-millis with page-relative-millis,\n // producing huge negative `elapsed` values that clamp tween `t` to 0\n // forever (never reaching completion).\n const now = (): number =>\n (optsRef.current.now ?? (typeof performance !== 'undefined' ? performance.now.bind(performance) : Date.now))();\n // Resolved timer pair, snapshotted lazily from optsRef on each call so a\n // test that updates the injection after mount still wins. Kept internal —\n // not exposed on the public Animator surface; passed directly into\n // `createStagger` at bind time below.\n const staggerTimers: StaggerTimers = {\n setTimer: (cb, ms) =>\n (optsRef.current.setTimer ?? ((c, m) => setTimeout(c, m)))(cb, ms),\n clearTimer: (h) =>\n (optsRef.current.clearTimer ??\n ((x) => clearTimeout(x as ReturnType<typeof setTimeout>)))(h),\n now,\n };\n\n // Per-id one-shot completion listeners. Used by stagger to detect when\n // each child has left the registry (natural completion OR cancel) so the\n // supervising composite can retire itself when the last child finishes.\n const completionListeners = new Map<number, (() => void)[]>();\n const fireCompletion = (id: number): void => {\n const ls = completionListeners.get(id);\n if (!ls) return;\n completionListeners.delete(id);\n for (const cb of ls) cb();\n };\n const watchCompletion: WatchCompletion = (id, cb) => {\n const ls = completionListeners.get(id) ?? [];\n ls.push(cb);\n completionListeners.set(id, ls);\n return () => {\n const cur = completionListeners.get(id);\n if (!cur) return;\n const idx = cur.indexOf(cb);\n if (idx >= 0) cur.splice(idx, 1);\n if (cur.length === 0) completionListeners.delete(id);\n };\n };\n\n const hub = new AnimatorEventHub();\n const infoOf = (a: ActiveAnimation): AnimationInfo => {\n if (!a.info) {\n const info: { -readonly [K in keyof AnimationInfo]: AnimationInfo[K] } = { id: a.id, kind: a.kind };\n if (a.cancelKey != null) info.key = a.cancelKey;\n if (a.label != null) info.label = a.label;\n a.info = info;\n }\n return a.info;\n };\n\n /** Report `id` as finished, ahead of its `onDone`, so whatever that\n * handler starts is heard after it. Nothing revives a primitive; a\n * timeline, which can be revived, is reported as it leaves the table. */\n const reportEnd = (id: number): void => {\n const a = animations.current.get(id);\n if (!a || a.ended) return;\n a.ended = true;\n if (hub.watched) hub.emit({ type: 'end', animation: infoOf(a) });\n };\n\n /** Take `id` off the table as canceled — or, given `by`, as interrupted by\n * the animation claiming its key. */\n const retire = (id: number, by?: ActiveAnimation): void => {\n const a = animations.current.get(id);\n if (!a) return;\n a.onCancel?.();\n animations.current.delete(id);\n fireCompletion(id);\n if (!hub.watched || a.ended) return;\n hub.emit(by\n ? { type: 'interrupt', animation: infoOf(a), by: infoOf(by) }\n : { type: 'cancel', animation: infoOf(a) });\n };\n\n const tickAll = (t: number): void => {\n const finished: ActiveAnimation[] = [];\n for (const anim of animations.current.values()) {\n // `t` comes from the frame clock; `lastRealNow` is seeded at register()\n // from `now()`. The two share a time origin in a browser, where the rAF\n // timestamp and `performance.now()` are both page-relative — but not\n // everywhere: jsdom starts them ~600ms apart, which made the first\n // frame's delta hugely negative and left `virtualNow` climbing back\n // toward zero for dozens of frames before a tween advanced at all.\n // Time never runs backwards, so a negative sample is never real.\n const realDt = anim.lastRealNow == null ? 0 : Math.max(0, t - anim.lastRealNow);\n anim.lastRealNow = t;\n const scale = globalPaused.current\n ? 0\n : globalTimeScale.current * (anim.paused ? 0 : anim.timeScale);\n anim.virtualNow += realDt * scale;\n // Increment tick depth around each animation's tick so re-entrant\n // calls (e.g. `decay.onTick` → `adapter.setPose` →\n // `animateOnSetPose` checking `isTicking()`) see the flag and\n // skip scheduling a new wrap-tween that would fight the caller.\n tickDepth.current += 1;\n try {\n if (anim.tick(anim.virtualNow, scale)) finished.push(anim);\n } finally {\n tickDepth.current -= 1;\n }\n }\n for (const anim of finished) {\n // Gone already if its own tick canceled it; replaced if something\n // revived it under the same id since.\n if (animations.current.get(anim.id) !== anim) continue;\n reportEnd(anim.id);\n animations.current.delete(anim.id);\n fireCompletion(anim.id);\n }\n // Notify every onTick subscriber AFTER the tick batch so they\n // see the latest `colorOverrides` / pose values. Errors in one\n // subscriber don't suppress the others.\n for (const sub of tickSubscribers.current) {\n try { sub(); } catch (err) { console.error('useAnimator: onTick subscriber threw', err); }\n }\n if (animations.current.size > 0) frameLoopRef.current.request();\n };\n\n const ensureLoop = (): void => {\n if (animations.current.size === 0) return;\n frameLoopRef.current.request();\n };\n\n const cancelByKey = (key: string, by?: ActiveAnimation): void => {\n const ids: number[] = [];\n for (const anim of animations.current.values()) {\n if (anim.cancelKey === key) ids.push(anim.id);\n }\n for (const id of ids) retire(id, by);\n };\n\n type AnimationSeed =\n Omit<ActiveAnimation, 'paused' | 'timeScale' | 'virtualNow' | 'lastRealNow'>\n & {\n /** Registers under an existing `cancelKey` without cancelling whoever\n * holds it. For an animation re-registering itself — a revived\n * timeline — which is not a new claim on the key. */\n keepExisting?: boolean;\n };\n const register = (seed: AnimationSeed): AnimationHandle => {\n const anim = seed as ActiveAnimation;\n if (seed.cancelKey != null && !seed.keepExisting) cancelByKey(seed.cancelKey, anim);\n anim.paused = false;\n anim.timeScale = 1;\n anim.virtualNow = 0;\n // Seed lastRealNow at registration so the first frame's realDt reflects\n // the gap between register() and the first RAF callback. Preserves prior\n // wall-clock-anchored start behavior for tween and gives spring/decay a\n // non-zero first dt sample matching the pre-virtual-clock code.\n anim.lastRealNow = now();\n animations.current.set(anim.id, anim);\n if (hub.watched) hub.emit({ type: 'start', animation: infoOf(anim) });\n ensureLoop();\n return {\n id: anim.id,\n cancel: () => retire(anim.id),\n pause: () => { const a = animations.current.get(anim.id); if (a) a.paused = true; },\n resume: () => { const a = animations.current.get(anim.id); if (a) a.paused = false; },\n setTimeScale: (s) => { const a = animations.current.get(anim.id); if (a) a.timeScale = s; },\n timeScale: () => animations.current.get(anim.id)?.timeScale ?? 1,\n isPaused: () => animations.current.get(anim.id)?.paused ?? false,\n };\n };\n\n // Supervisor: a registered animation whose tick is a no-op (never\n // finishes naturally) so loop/stagger can sit in the animator's id table\n // and benefit from `cancel`/`cancelKey`/`isActive`. The owning composite\n // (loop/stagger) installs an onCancel to actually tear down its children.\n const createSupervisor = ({ kind, cancelKey, label }: SupervisorOptions): Supervisor => {\n const id = nextId.current++;\n let onCancelCb: (() => void) | undefined;\n const base = register({\n id,\n kind,\n cancelKey,\n label,\n // tick is a no-op: the supervisor only ends when the owner calls\n // `cancel()` (either via animator.cancel, animator.cancelKey, or the\n // composite's natural-completion path which forwards to `cancel`).\n tick: () => false,\n onCancel: () => onCancelCb?.(),\n });\n return {\n id: base.id,\n cancel: base.cancel,\n pause: base.pause,\n resume: base.resume,\n setTimeScale: base.setTimeScale,\n timeScale: base.timeScale,\n isPaused: base.isPaused,\n setOnCancel: (cb) => { onCancelCb = cb; },\n finish: () => {\n if (!animations.current.has(id)) return;\n reportEnd(id);\n animations.current.delete(id);\n fireCompletion(id);\n },\n cancelKey,\n };\n };\n\n const tween = <T,>(o: TweenOptions<T>): AnimationHandle => {\n const id = nextId.current++;\n // start is captured in *virtual* time; since virtualNow begins at 0 on\n // registration, that's our reference. The tween's \"elapsed\" is just\n // virtualNow itself — naturally freezing when paused (virtualNow stops\n // advancing) and decoupled from wall time.\n const start = 0;\n const easing = resolveEasing(o.easing ?? easeOut);\n // Precedence: factory > per-tick > default numeric lerp. Factory is built\n // once at tween start so expensive setup (color space conversion etc.)\n // doesn't repeat per frame.\n const factoryFn = o.interpolator ? o.interpolator(o.from, o.to) : null;\n const perTickInterp =\n factoryFn\n ? null\n : o.interpolate ??\n ((a: T, b: T, t: number) => {\n if (typeof a === 'number' && typeof b === 'number') {\n return numericLerp(a as number, b as number, t) as unknown as T;\n }\n throw new Error('tween: interpolate or interpolator is required for non-numeric T');\n });\n let lastValueEmitted = false;\n return register({\n id,\n kind: 'tween',\n cancelKey: o.cancelKey,\n label: o.label,\n progress: (virtualNow) => (o.ms <= 0 ? 1 : Math.min(1, virtualNow / o.ms)),\n tick(nowMs) {\n if (tripwire()) return true;\n const elapsed = nowMs - start;\n const t = o.ms <= 0 ? 1 : Math.min(1, Math.max(0, elapsed / o.ms));\n const eased = easing(t);\n const value = factoryFn ? factoryFn(eased) : perTickInterp!(o.from, o.to, eased);\n o.onTick(value);\n if (t >= 1 && !lastValueEmitted) {\n lastValueEmitted = true;\n // That last onTick may have cancelled us — deregistering the id.\n // A cancelled tween never completes, whenever the cancel landed.\n if (animations.current.has(id)) {\n reportEnd(id);\n o.onDone?.();\n }\n return true;\n }\n return false;\n },\n });\n };\n\n const physics = <T,>(o: PhysicsOptions<T>, kind: AnimationKind = 'physics'): PhysicsHandle<T> => {\n const id = nextId.current++;\n const isNumeric = typeof o.from === 'number';\n if (!isNumeric && (!o.add || !o.subtract || !o.scale || !o.magnitude)) {\n throw new Error('physics: add/subtract/scale/magnitude are required for non-numeric T');\n }\n const add = o.add ?? ((a: T, b: T) => ((a as unknown as number) + (b as unknown as number)) as unknown as T);\n const subtract = o.subtract ?? ((a: T, b: T) => ((a as unknown as number) - (b as unknown as number)) as unknown as T);\n const scale = o.scale ?? ((v: T, k: number) => ((v as unknown as number) * k) as unknown as T);\n const magnitude = o.magnitude ?? ((v: T) => Math.abs(v as unknown as number));\n const { stiffness: kBase, damping, mass } = resolveSpringConstants(o);\n const restThreshold = o.restThreshold ?? 0.01;\n\n let target: T | null = o.to ?? null;\n let value = o.from;\n // Default to zero velocity: assumes scale(_, 0) returns the zero vector of T. Pass an explicit velocity for unusual T where this isn't true.\n let velocity: T = o.velocity ?? scale(o.from, 0);\n let lastTime: number | null = null;\n\n const baseHandle = register({\n id,\n kind,\n cancelKey: o.cancelKey,\n label: o.label,\n tick(nowMs) {\n if (tripwire()) return true;\n if (lastTime == null) {\n lastTime = nowMs;\n // Already-at-rest short-circuit: decay-mode (target == null)\n // with starting velocity below threshold should complete\n // immediately rather than emit a tick and wait a frame.\n if (target == null && magnitude(velocity) < restThreshold) {\n reportEnd(id);\n o.onDone?.();\n return true;\n }\n o.onTick(value);\n return false;\n }\n const dt = Math.min(0.064, (nowMs - lastTime) / 1000);\n lastTime = nowMs;\n // Semi-implicit Euler. When target == null, stiffness == 0 ⇒\n // no spring force; only damping acts on velocity (exponential decay).\n const ref = target ?? value;\n const displacement = subtract(value, ref);\n const springForce = scale(displacement, target == null ? 0 : -kBase);\n const dampingForce = scale(velocity, -damping);\n const accel = scale(add(springForce, dampingForce), 1 / mass);\n velocity = add(velocity, scale(accel, dt));\n value = add(value, scale(velocity, dt));\n o.onTick(value);\n const velRested = magnitude(velocity) < restThreshold;\n const posRested = target == null\n ? true\n : magnitude(subtract(value, target)) < restThreshold;\n if (velRested && posRested) {\n if (target != null) o.onTick(target);\n reportEnd(id);\n o.onDone?.();\n return true;\n }\n return false;\n },\n });\n\n const handle: PhysicsHandle<T> = {\n ...baseHandle,\n setTarget: (newTo: T | null) => { target = newTo; },\n setVelocity: (v: T) => { velocity = v; },\n };\n return handle;\n };\n\n const spring = <T,>(o: SpringOptions<T>): AnimationHandle => physics<T>(o, 'spring');\n\n const decay = <T,>(o: DecayOptions<T>): AnimationHandle => {\n const friction = o.friction ?? 0.95;\n // Per-second friction v(t) = v0 * friction^t corresponds to\n // m*dv/dt = -c*v with c = -ln(friction), m = 1, k = 0.\n const damping = -Math.log(friction);\n return physics<T>({\n from: o.from,\n to: null,\n velocity: o.velocity,\n stiffness: 0,\n damping,\n mass: 1,\n restThreshold: o.threshold ?? 0.5,\n add: o.add,\n // DecayOptions doesn't carry subtract; derive from add+scale.\n subtract: (a, b) => o.add(a, o.scale(b, -1)),\n scale: o.scale,\n magnitude: o.magnitude,\n onTick: o.onTick,\n onDone: o.onDone,\n cancelKey: o.cancelKey,\n label: o.label,\n }, 'decay');\n };\n\n const cancelAll = (): void => {\n const retired: ActiveAnimation[] = [];\n for (const a of animations.current.values()) {\n a.onCancel?.();\n retired.push(a);\n }\n animations.current.clear();\n for (const a of retired) fireCompletion(a.id);\n if (hub.watched) for (const a of retired) hub.emit({ type: 'cancel', animation: infoOf(a) });\n frameLoopRef.current.cancel();\n };\n const api: Animator = {\n tween,\n spring,\n decay,\n physics,\n cancel: (handle) => retire(handle.id),\n cancelKey: (key) => cancelByKey(key),\n cancelAll,\n isActive: (key) => {\n if (key == null) return animations.current.size > 0;\n for (const a of animations.current.values()) {\n if (a.cancelKey === key) return true;\n }\n return false;\n },\n isTicking: () => tickDepth.current > 0,\n pause: () => { globalPaused.current = true; },\n resume: () => { globalPaused.current = false; },\n isPaused: () => globalPaused.current,\n setTimeScale: (s) => { globalTimeScale.current = s; },\n timeScale: () => globalTimeScale.current,\n pauseKey: (key) => {\n for (const a of animations.current.values()) if (a.cancelKey === key) a.paused = true;\n },\n resumeKey: (key) => {\n for (const a of animations.current.values()) if (a.cancelKey === key) a.paused = false;\n },\n setTimeScaleByKey: (key, s) => {\n for (const a of animations.current.values()) if (a.cancelKey === key) a.timeScale = s;\n },\n loop: (factory, loopOpts) => createLoop(createSupervisor, factory, loopOpts),\n tweenLoop: (tweenLoopOpts) =>\n createTweenLoop(api, createSupervisor, tweenLoopOpts),\n stagger: ((items, delay, factory, staggerOpts) =>\n createStagger(\n api,\n staggerTimers,\n createSupervisor,\n watchCompletion,\n items,\n delay,\n factory as never,\n staggerOpts,\n )) as Animator['stagger'],\n timeline: (o: TimelineOptions) => {\n const id = nextId.current++;\n const info = (): AnimationInfo | undefined => {\n const a = animations.current.get(id);\n return a && infoOf(a);\n };\n return createTimeline(register, id, o, {\n get watched() { return hub.watched; },\n fire: (track, path, event, lateBy) => {\n const animation = info();\n if (animation) hub.emit({ type: 'fire', animation, track, path: path.slice(), event, lateBy });\n },\n lap: (lap) => {\n const animation = info();\n if (animation) hub.emit({ type: 'lap', animation, lap });\n },\n });\n },\n colorOverrides: colorOverrides.current,\n onTick: (cb) => {\n tickSubscribers.current.add(cb);\n return () => { tickSubscribers.current.delete(cb); };\n },\n keepAlive: () => {\n const sup = createSupervisor({ kind: 'keepAlive' });\n return () => sup.cancel();\n },\n watch: (listener) => hub.add(listener),\n live: () => {\n const rows: LiveAnimation[] = [];\n for (const a of animations.current.values()) {\n const row: LiveAnimation = {\n ...infoOf(a),\n paused: a.paused,\n timeScale: a.timeScale,\n elapsed: a.virtualNow,\n ...(a.progress ? { progress: a.progress(a.virtualNow) } : {}),\n };\n rows.push(row);\n }\n return rows;\n },\n };\n return { api, tickAll };\n }, [frameLoopRef, optsRef]);\n\n useInsertionEffect(() => {\n tickAllRef.current = tickAll;\n }, [tickAll]);\n\n // StrictMode-safe cleanup: when the component unmounts (including the\n // dev-mode double-mount that StrictMode performs), cancel every running\n // animation and stop the RAF loop. Without this, the FIRST mount's\n // animator keeps ticking with stale callbacks pointing at the unmounted\n // adapter, while the SECOND mount creates its own animator on top —\n // visible to the user as every animation playing twice.\n useEffect(() => {\n const overrides = colorOverrides.current;\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n api.cancelAll();\n overrides.clearAll();\n };\n }, [api]);\n\n return api;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useAnimator.js","names":["resolveSpringConstants","preset","o","SPRING_PRESETS","useAnimator","optsRef","useLatest","opts","animations","useRef","nextId","tickDepth","codecFrameT","globalTimeScale","globalPaused","colorOverrides","ColorOverrideRegistry","tickSubscribers","tickAllRef","frameLoop","useVisibleRaf","useCallback","t","anim","frameLoopRef","mountedRef","trippedRef","tripwire","useMemo","now","staggerTimers","c","m","cb","ms","x","h","completionListeners","fireCompletion","ls","id","watchCompletion","cur","idx","hub","AnimatorEventHub","codec","createCodec","infoOf","a","info","reportEnd","retire","by","tickAll","finished","frameDt","realDt","scale","sub","err","ensureLoop","cancelByKey","ids","key","rateOf","register","seed","s","createSupervisor","onCancelCb","base","tween","fromAxes","axesOf","toAxes","axes","easing","resolveEasing","easeOut","factoryFn","lastValueEmitted","reader","virtualNow","nowMs","value","eased","physics","add","b","subtract","v","k","magnitude","kBase","restThreshold","target","velocity","lastTime","damping","mass","kind","sameShape","baseHandle","dt","next","stepSpring","velRested","posRested","checkShape","what","newTo","api","friction","handle","retired","createLoop","factory","loopOpts","createTweenLoop","tweenLoopOpts","createStagger","items","delay","staggerOpts","createTimeline","animation","path","sup","listener","rows","row","useInsertionEffect","useEffect","overrides"],"sources":["../../src/animation/useAnimator.ts"],"sourcesContent":["import { useCallback, useEffect, useInsertionEffect, useMemo, useRef } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport { useVisibleRaf } from '../scheduling/useVisibleRaf';\nimport { easeOut, SPRING_PRESETS, resolveEasing } from '@weasel-js/geom';\nimport { createLoop, createTweenLoop } from './loop';\nimport { stepSpring } from './engine/integrator';\nimport { createCodec, type SpringReader } from './engine/codec';\nimport { axesOf } from './engine/axes';\nimport { createStagger, type StaggerTimers } from './stagger';\nimport { createTimeline } from './timeline/createTimeline';\nimport { ColorOverrideRegistry } from './colorRegistry';\nimport { AnimatorEventHub } from './observe';\nimport type { Supervisor, SupervisorOptions, WatchCompletion } from './supervisor';\nimport type {\n AnimationHandle,\n AnimationInfo,\n AnimationKind,\n Animator,\n TweenOptions,\n SpringOptions,\n DecayOptions,\n PhysicsOptions,\n PhysicsHandle,\n UseAnimatorOptions,\n LiveAnimation,\n} from './types';\nimport type { TimelineOptions } from './timeline/types';\n\ninterface ActiveAnimation {\n id: number;\n kind: AnimationKind;\n cancelKey?: string;\n label?: string;\n /** Built the first time something reports on this entry, then shared. */\n info?: AnimationInfo;\n /** 0–1 where the animation has an end to measure against. */\n progress?(virtualNow: number): number;\n /** Its `end` is reported, though the entry stays on the table until the\n * frame's ticks are done. */\n ended?: boolean;\n paused: boolean;\n timeScale: number;\n virtualNow: number;\n lastRealNow: number | null;\n /** Returns true when finished. Called once per frame with the current\n * virtual-ms timestamp and the effective scale it advanced at this frame. */\n tick(virtualNow: number, scale: number): boolean;\n /** Called when the animation is cancelled. Skips onDone. */\n onCancel?(): void;\n}\n\nfunction resolveSpringConstants(o: { preset?: string; stiffness?: number; damping?: number; mass?: number }) {\n const preset = o.preset ? SPRING_PRESETS[o.preset as keyof typeof SPRING_PRESETS] : null;\n return {\n stiffness: o.stiffness ?? preset?.stiffness ?? 170,\n damping: o.damping ?? preset?.damping ?? 26,\n mass: o.mass ?? preset?.mass ?? 1,\n };\n}\n\n/** Create the animator for a canvas. One rAF loop drives every animation it\n * owns, and everything still running is cancelled on unmount. */\nexport function useAnimator(opts: UseAnimatorOptions = {}): Animator {\n const optsRef = useLatest(opts);\n const animations = useRef<Map<number, ActiveAnimation>>(new Map());\n const nextId = useRef(1);\n /**\n * Re-entrancy counter incremented around each animation tick in tickAll.\n * Exposed via `isTicking()`. > 0 ⇒ we're currently inside an animation's\n * onTick; an adapter wrapper (`animateOnSetPose`) seeing this flag should\n * write through to the base adapter rather than schedule a new tween.\n */\n const tickDepth = useRef(0);\n /** The timestamp the codec last advanced to. Null after the page was hidden and while nothing\n * runs, so neither gap is charged to the codec; a tween starting then seeds it from `now()`. */\n const codecFrameT = useRef<number | null>(null);\n const globalTimeScale = useRef(1);\n const globalPaused = useRef(false);\n const colorOverrides = useRef<ColorOverrideRegistry>(new ColorOverrideRegistry());\n // Per-frame tick subscribers. Notified after each batch of animation\n // ticks (i.e. after `colorOverrides` has settled to its latest values\n // but before the next RAF schedules). Used by `<SceneCanvas animator>`\n // to request a redraw on every active frame.\n const tickSubscribers = useRef<Set<() => void>>(new Set());\n\n // The loop runs behind the visibility gate. `tickAll` is built inside the\n // memo below, so the frame callback reaches it through a ref, published\n // once the memo's render commits.\n const tickAllRef = useRef<((t: number) => void) | null>(null);\n const frameLoop = useVisibleRaf(\n useCallback((t: number) => { tickAllRef.current?.(t); }, []),\n {\n requestFrame: opts.requestFrame,\n cancelFrame: opts.cancelFrame,\n // An hour spent hidden is not an hour the animation ran: dropping each\n // animation's last timestamp makes the resuming frame's `realDt` zero.\n onResume: () => {\n for (const anim of animations.current.values()) anim.lastRealNow = null;\n codecFrameT.current = null;\n },\n },\n );\n const frameLoopRef = useLatest(frameLoop);\n\n // Tripwire: dev-only flag that every tween/spring/decay's tick reads to\n // detect \"animation fired after the host component unmounted.\" This is a\n // symptom of cleanup not running (regression) — typically because someone\n // refactored useAnimator and forgot to wire the unmount effect. The flag\n // flips false on cleanup; ticks see it and log a one-time error so the\n // bug is loud rather than just visually-doubled animation.\n const mountedRef = useRef(true);\n const trippedRef = useRef(false);\n const tripwire = (): boolean => {\n if (mountedRef.current) return false;\n if (!trippedRef.current) {\n trippedRef.current = true;\n const isDev = typeof process !== 'undefined' ? process.env.NODE_ENV !== 'production' : true;\n if (isDev) {\n console.error(\n 'weasel useAnimator: animation tick fired after the host component unmounted. ' +\n 'This usually means cleanup logic was bypassed — check for refactors that ' +\n 'removed the unmount effect or replaced cancelAll with a custom path.',\n );\n }\n }\n return true;\n };\n\n const { api, tickAll } = useMemo(() => {\n // Default to performance.now() so the time origin matches the\n // requestAnimationFrame callback's DOMHighResTimeStamp argument.\n // Using Date.now() here would mix epoch-millis with page-relative-millis,\n // producing huge negative `elapsed` values that clamp tween `t` to 0\n // forever (never reaching completion).\n const now = (): number =>\n (optsRef.current.now ?? (typeof performance !== 'undefined' ? performance.now.bind(performance) : Date.now))();\n // Resolved timer pair, snapshotted lazily from optsRef on each call so a\n // test that updates the injection after mount still wins. Kept internal —\n // not exposed on the public Animator surface; passed directly into\n // `createStagger` at bind time below.\n const staggerTimers: StaggerTimers = {\n setTimer: (cb, ms) =>\n (optsRef.current.setTimer ?? ((c, m) => setTimeout(c, m)))(cb, ms),\n clearTimer: (h) =>\n (optsRef.current.clearTimer ??\n ((x) => clearTimeout(x as ReturnType<typeof setTimeout>)))(h),\n now,\n };\n\n // Per-id one-shot completion listeners. Used by stagger to detect when\n // each child has left the registry (natural completion OR cancel) so the\n // supervising composite can retire itself when the last child finishes.\n const completionListeners = new Map<number, (() => void)[]>();\n const fireCompletion = (id: number): void => {\n const ls = completionListeners.get(id);\n if (!ls) return;\n completionListeners.delete(id);\n for (const cb of ls) cb();\n };\n const watchCompletion: WatchCompletion = (id, cb) => {\n const ls = completionListeners.get(id) ?? [];\n ls.push(cb);\n completionListeners.set(id, ls);\n return () => {\n const cur = completionListeners.get(id);\n if (!cur) return;\n const idx = cur.indexOf(cb);\n if (idx >= 0) cur.splice(idx, 1);\n if (cur.length === 0) completionListeners.delete(id);\n };\n };\n\n const hub = new AnimatorEventHub();\n const codec = createCodec();\n const infoOf = (a: ActiveAnimation): AnimationInfo => {\n if (!a.info) {\n const info: { -readonly [K in keyof AnimationInfo]: AnimationInfo[K] } = { id: a.id, kind: a.kind };\n if (a.cancelKey != null) info.key = a.cancelKey;\n if (a.label != null) info.label = a.label;\n a.info = info;\n }\n return a.info;\n };\n\n /** Report `id` as finished, ahead of its `onDone`, so whatever that\n * handler starts is heard after it. Nothing revives a primitive; a\n * timeline, which can be revived, is reported as it leaves the table. */\n const reportEnd = (id: number): void => {\n const a = animations.current.get(id);\n if (!a || a.ended) return;\n a.ended = true;\n if (hub.watched) hub.emit({ type: 'end', animation: infoOf(a) });\n };\n\n /** Take `id` off the table as canceled — or, given `by`, as interrupted by\n * the animation claiming its key. */\n const retire = (id: number, by?: ActiveAnimation): void => {\n const a = animations.current.get(id);\n if (!a) return;\n a.onCancel?.();\n codec.stop(id);\n animations.current.delete(id);\n fireCompletion(id);\n if (!hub.watched || a.ended) return;\n hub.emit(by\n ? { type: 'interrupt', animation: infoOf(a), by: infoOf(by) }\n : { type: 'cancel', animation: infoOf(a) });\n };\n\n const tickAll = (t: number): void => {\n const finished: ActiveAnimation[] = [];\n const frameDt = codecFrameT.current == null ? 0 : Math.max(0, t - codecFrameT.current);\n codecFrameT.current = t;\n codec.frame(frameDt * (globalPaused.current ? 0 : globalTimeScale.current));\n for (const anim of animations.current.values()) {\n // `t` comes from the frame clock; `lastRealNow` is seeded at register()\n // from `now()`. The two share a time origin in a browser, where the rAF\n // timestamp and `performance.now()` are both page-relative — but not\n // everywhere: jsdom starts them ~600ms apart, which made the first\n // frame's delta hugely negative and left `virtualNow` climbing back\n // toward zero for dozens of frames before a tween advanced at all.\n // Time never runs backwards, so a negative sample is never real.\n const realDt = anim.lastRealNow == null ? 0 : Math.max(0, t - anim.lastRealNow);\n anim.lastRealNow = t;\n const scale = globalPaused.current\n ? 0\n : globalTimeScale.current * (anim.paused ? 0 : anim.timeScale);\n anim.virtualNow += realDt * scale;\n // Increment tick depth around each animation's tick so re-entrant\n // calls (e.g. `decay.onTick` → `adapter.setPose` →\n // `animateOnSetPose` checking `isTicking()`) see the flag and\n // skip scheduling a new wrap-tween that would fight the caller.\n tickDepth.current += 1;\n try {\n if (anim.tick(anim.virtualNow, scale)) finished.push(anim);\n } finally {\n tickDepth.current -= 1;\n }\n }\n for (const anim of finished) {\n // Gone already if its own tick canceled it; replaced if something\n // revived it under the same id since.\n if (animations.current.get(anim.id) !== anim) continue;\n reportEnd(anim.id);\n animations.current.delete(anim.id);\n fireCompletion(anim.id);\n }\n // Notify every onTick subscriber AFTER the tick batch so they\n // see the latest `colorOverrides` / pose values. Errors in one\n // subscriber don't suppress the others.\n for (const sub of tickSubscribers.current) {\n try { sub(); } catch (err) { console.error('useAnimator: onTick subscriber threw', err); }\n }\n if (animations.current.size > 0) frameLoopRef.current.request();\n else codecFrameT.current = null;\n };\n\n const ensureLoop = (): void => {\n if (animations.current.size === 0) return;\n frameLoopRef.current.request();\n };\n\n const cancelByKey = (key: string, by?: ActiveAnimation): void => {\n const ids: number[] = [];\n for (const anim of animations.current.values()) {\n if (anim.cancelKey === key) ids.push(anim.id);\n }\n for (const id of ids) retire(id, by);\n };\n\n type AnimationSeed =\n Omit<ActiveAnimation, 'paused' | 'timeScale' | 'virtualNow' | 'lastRealNow'>\n & {\n /** Registers under an existing `cancelKey` without cancelling whoever\n * holds it. For an animation re-registering itself — a revived\n * timeline — which is not a new claim on the key. */\n keepExisting?: boolean;\n };\n /** Gives a codec subject the rate its own pause and time scale now ask for. */\n const rateOf = (a: ActiveAnimation): void => {\n if (codec.has(a.id)) codec.rate(a.id, a.paused ? 0 : a.timeScale);\n };\n\n const register = (seed: AnimationSeed): AnimationHandle => {\n const anim = seed as ActiveAnimation;\n if (seed.cancelKey != null && !seed.keepExisting) cancelByKey(seed.cancelKey, anim);\n anim.paused = false;\n anim.timeScale = 1;\n anim.virtualNow = 0;\n // Seed lastRealNow at registration so the first frame's realDt reflects\n // the gap between register() and the first RAF callback: it places a\n // tween's end, and gives spring/decay a non-zero first dt sample.\n anim.lastRealNow = now();\n animations.current.set(anim.id, anim);\n if (hub.watched) hub.emit({ type: 'start', animation: infoOf(anim) });\n ensureLoop();\n return {\n id: anim.id,\n cancel: () => retire(anim.id),\n pause: () => { const a = animations.current.get(anim.id); if (a) { a.paused = true; rateOf(a); } },\n resume: () => { const a = animations.current.get(anim.id); if (a) { a.paused = false; rateOf(a); } },\n setTimeScale: (s) => { const a = animations.current.get(anim.id); if (a) { a.timeScale = s; rateOf(a); } },\n timeScale: () => animations.current.get(anim.id)?.timeScale ?? 1,\n isPaused: () => animations.current.get(anim.id)?.paused ?? false,\n };\n };\n\n // Supervisor: a registered animation whose tick is a no-op (never\n // finishes naturally) so loop/stagger can sit in the animator's id table\n // and benefit from `cancel`/`cancelKey`/`isActive`. The owning composite\n // (loop/stagger) installs an onCancel to actually tear down its children.\n const createSupervisor = ({ kind, cancelKey, label }: SupervisorOptions): Supervisor => {\n const id = nextId.current++;\n let onCancelCb: (() => void) | undefined;\n const base = register({\n id,\n kind,\n cancelKey,\n label,\n // tick is a no-op: the supervisor only ends when the owner calls\n // `cancel()` (either via animator.cancel, animator.cancelKey, or the\n // composite's natural-completion path which forwards to `cancel`).\n tick: () => false,\n onCancel: () => onCancelCb?.(),\n });\n return {\n id: base.id,\n cancel: base.cancel,\n pause: base.pause,\n resume: base.resume,\n setTimeScale: base.setTimeScale,\n timeScale: base.timeScale,\n isPaused: base.isPaused,\n setOnCancel: (cb) => { onCancelCb = cb; },\n finish: () => {\n if (!animations.current.has(id)) return;\n reportEnd(id);\n animations.current.delete(id);\n fireCompletion(id);\n },\n cancelKey,\n };\n };\n\n const tween = <T,>(o: TweenOptions<T>): AnimationHandle => {\n // blits blends in a straight line, which a caller's own function may not do.\n const fromAxes = o.interpolate || o.interpolator ? null : axesOf(o.from);\n const toAxes = fromAxes && axesOf(o.to);\n const axes = fromAxes && toAxes && toAxes.shape === fromAxes.shape ? fromAxes : null;\n // Refused here rather than on a tick, where a throw would stop every other animation.\n if (!axes && !o.interpolate && !o.interpolator) {\n throw new Error('tween: interpolate or interpolator is required for non-numeric T');\n }\n const id = nextId.current++;\n const easing = resolveEasing(o.easing ?? easeOut);\n // Precedence: factory > per-tick. Factory is built once at tween start so\n // expensive setup (color space conversion etc.) doesn't repeat per frame.\n const factoryFn = o.interpolator ? o.interpolator(o.from, o.to) : null;\n let lastValueEmitted = false;\n // Seeds the codec's clock where `register` seeds the call's, so both charge the same wait.\n if (codecFrameT.current == null) codecFrameT.current = now();\n // Started ahead of `register`, whose `start` listeners may already cancel it.\n const reader = codec.tween({\n id, ms: Math.max(o.ms, 1), ease: easing,\n from: axes ? axes.to(o.from) : [0],\n to: axes ? axes.to(o.to) : [1],\n });\n return register({\n id,\n kind: 'tween',\n cancelKey: o.cancelKey,\n label: o.label,\n progress: (virtualNow) => (o.ms <= 0 ? 1 : Math.min(1, virtualNow / o.ms)),\n // `nowMs` is the call's own virtual time, so it still decides when the tween ends; the codec\n // supplies only the value in between.\n tick(nowMs) {\n if (tripwire()) { codec.stop(id); return true; }\n const t = o.ms <= 0 ? 1 : Math.min(1, nowMs / o.ms);\n let value: T;\n if (axes) {\n value = t >= 1 ? o.to : axes.from(reader.cols, reader.at);\n } else {\n const eased = t >= 1 ? easing(1) : reader.cols[reader.at]!;\n value = factoryFn ? factoryFn(eased) : o.interpolate!(o.from, o.to, eased);\n }\n o.onTick(value);\n if (t >= 1 && !lastValueEmitted) {\n lastValueEmitted = true;\n codec.stop(id);\n // That last onTick may have cancelled us — deregistering the id.\n // A cancelled tween never completes, whenever the cancel landed.\n if (animations.current.has(id)) {\n reportEnd(id);\n o.onDone?.();\n }\n return true;\n }\n return false;\n },\n });\n };\n\n const physics = <T,>(o: PhysicsOptions<T>, kind: AnimationKind = 'physics'): PhysicsHandle<T> => {\n const id = nextId.current++;\n const isNumeric = typeof o.from === 'number';\n if (!isNumeric && (!o.add || !o.subtract || !o.scale || !o.magnitude)) {\n throw new Error('physics: add/subtract/scale/magnitude are required for non-numeric T');\n }\n const add = o.add ?? ((a: T, b: T) => ((a as unknown as number) + (b as unknown as number)) as unknown as T);\n const subtract = o.subtract ?? ((a: T, b: T) => ((a as unknown as number) - (b as unknown as number)) as unknown as T);\n const scale = o.scale ?? ((v: T, k: number) => ((v as unknown as number) * k) as unknown as T);\n const magnitude = o.magnitude ?? ((v: T) => Math.abs(v as unknown as number));\n const { stiffness: kBase, damping, mass } = resolveSpringConstants(o);\n const restThreshold = o.restThreshold ?? 0.01;\n\n let target: T | null = o.to ?? null;\n let value = o.from;\n // Default to zero velocity: assumes scale(_, 0) returns the zero vector of T. Pass an explicit velocity for unusual T where this isn't true.\n let velocity: T = o.velocity ?? scale(o.from, 0);\n let lastTime: number | null = null;\n\n // blits' closed forms need positive damping and mass, and a spring with a target needs\n // stiffness; decay's handle never takes a target.\n const solvable = damping > 0 && mass > 0 && (kBase > 0 || kind === 'decay');\n const fromAxes = solvable ? axesOf(o.from) : null;\n const sameShape = (v: T | null | undefined): boolean => v == null || axesOf(v)?.shape === fromAxes!.shape;\n const axes = fromAxes && sameShape(o.to) && sameShape(o.velocity) ? fromAxes : null;\n let reader: SpringReader | null = null;\n if (axes) {\n // Seeds the codec's clock where `register` seeds the call's, so both charge the same wait.\n if (codecFrameT.current == null) codecFrameT.current = now();\n // Started ahead of `register`, whose `start` listeners may already cancel it.\n reader = codec.spring({\n id, axes: axes.count,\n from: axes.to(o.from),\n to: o.to == null ? null : axes.to(o.to),\n velocity: o.velocity == null ? new Array<number>(axes.count).fill(0) : axes.to(o.velocity),\n stiffness: kBase, damping, mass,\n });\n }\n\n const baseHandle = register({\n id,\n kind,\n cancelKey: o.cancelKey,\n label: o.label,\n tick(nowMs) {\n if (tripwire()) { codec.stop(id); return true; }\n if (lastTime == null) {\n lastTime = nowMs;\n // Already-at-rest short-circuit: decay-mode (target == null)\n // with starting velocity below threshold should complete\n // immediately rather than emit a tick and wait a frame.\n if (target == null && magnitude(velocity) < restThreshold) {\n codec.stop(id);\n reportEnd(id);\n o.onDone?.();\n return true;\n }\n // The codec's voice has run since registration, so only Euler starts its clock here.\n if (!axes) {\n o.onTick(value);\n return false;\n }\n }\n if (axes) {\n const m = reader!.motion();\n value = axes.from(m.value);\n velocity = axes.from(m.velocity);\n } else {\n // Semi-implicit Euler, for a T blits cannot move as axes, or constants its closed forms can't solve.\n const dt = Math.min(0.064, (nowMs - lastTime) / 1000);\n const next = stepSpring({ add, subtract, scale }, value, velocity, target, kBase, damping, mass, dt);\n value = next.value;\n velocity = next.velocity;\n }\n lastTime = nowMs;\n o.onTick(value);\n const velRested = magnitude(velocity) < restThreshold;\n const posRested = target == null\n ? true\n : magnitude(subtract(value, target)) < restThreshold;\n if (velRested && posRested) {\n if (target != null) o.onTick(target);\n codec.stop(id);\n reportEnd(id);\n o.onDone?.();\n return true;\n }\n return false;\n },\n });\n\n const checkShape = (v: T, what: string): void => {\n if (!sameShape(v)) throw new Error(`physics: ${what} is not the same shape as \\`from\\``);\n };\n const handle: PhysicsHandle<T> = {\n ...baseHandle,\n setTarget: (newTo: T | null) => {\n if (axes && newTo != null) checkShape(newTo, 'setTarget');\n target = newTo;\n if (axes && codec.has(id)) codec.retarget(id, newTo == null ? null : axes.to(newTo));\n },\n setVelocity: (v: T) => {\n if (axes) checkShape(v, 'setVelocity');\n velocity = v;\n if (axes && codec.has(id)) codec.push(id, axes.to(v));\n },\n };\n return handle;\n };\n\n const spring = <T,>(o: SpringOptions<T>): AnimationHandle => physics<T>(o, 'spring');\n\n const decay = <T,>(o: DecayOptions<T>): AnimationHandle => {\n const friction = o.friction ?? 0.95;\n // Per-second friction v(t) = v0 * friction^t corresponds to\n // m*dv/dt = -c*v with c = -ln(friction), m = 1, k = 0.\n const damping = -Math.log(friction);\n return physics<T>({\n from: o.from,\n to: null,\n velocity: o.velocity,\n stiffness: 0,\n damping,\n mass: 1,\n restThreshold: o.threshold ?? 0.5,\n add: o.add,\n // DecayOptions doesn't carry subtract; derive from add+scale.\n subtract: (a, b) => o.add(a, o.scale(b, -1)),\n scale: o.scale,\n magnitude: o.magnitude,\n onTick: o.onTick,\n onDone: o.onDone,\n cancelKey: o.cancelKey,\n label: o.label,\n }, 'decay');\n };\n\n const cancelAll = (): void => {\n const retired: ActiveAnimation[] = [];\n for (const a of animations.current.values()) {\n a.onCancel?.();\n codec.stop(a.id);\n retired.push(a);\n }\n animations.current.clear();\n codecFrameT.current = null;\n for (const a of retired) fireCompletion(a.id);\n if (hub.watched) for (const a of retired) hub.emit({ type: 'cancel', animation: infoOf(a) });\n frameLoopRef.current.cancel();\n };\n const api: Animator = {\n tween,\n spring,\n decay,\n physics,\n cancel: (handle) => retire(handle.id),\n cancelKey: (key) => cancelByKey(key),\n cancelAll,\n isActive: (key) => {\n if (key == null) return animations.current.size > 0;\n for (const a of animations.current.values()) {\n if (a.cancelKey === key) return true;\n }\n return false;\n },\n isTicking: () => tickDepth.current > 0,\n pause: () => { globalPaused.current = true; },\n resume: () => { globalPaused.current = false; },\n isPaused: () => globalPaused.current,\n setTimeScale: (s) => { globalTimeScale.current = s; },\n timeScale: () => globalTimeScale.current,\n pauseKey: (key) => {\n for (const a of animations.current.values()) if (a.cancelKey === key) { a.paused = true; rateOf(a); }\n },\n resumeKey: (key) => {\n for (const a of animations.current.values()) if (a.cancelKey === key) { a.paused = false; rateOf(a); }\n },\n setTimeScaleByKey: (key, s) => {\n for (const a of animations.current.values()) if (a.cancelKey === key) { a.timeScale = s; rateOf(a); }\n },\n loop: (factory, loopOpts) => createLoop(createSupervisor, factory, loopOpts),\n tweenLoop: (tweenLoopOpts) =>\n createTweenLoop(api, createSupervisor, tweenLoopOpts),\n stagger: ((items, delay, factory, staggerOpts) =>\n createStagger(\n api,\n staggerTimers,\n createSupervisor,\n watchCompletion,\n items,\n delay,\n factory as never,\n staggerOpts,\n )) as Animator['stagger'],\n timeline: (o: TimelineOptions) => {\n const id = nextId.current++;\n const info = (): AnimationInfo | undefined => {\n const a = animations.current.get(id);\n return a && infoOf(a);\n };\n return createTimeline(register, id, o, {\n get watched() { return hub.watched; },\n fire: (track, path, event, lateBy) => {\n const animation = info();\n if (animation) hub.emit({ type: 'fire', animation, track, path: path.slice(), event, lateBy });\n },\n lap: (lap) => {\n const animation = info();\n if (animation) hub.emit({ type: 'lap', animation, lap });\n },\n });\n },\n colorOverrides: colorOverrides.current,\n onTick: (cb) => {\n tickSubscribers.current.add(cb);\n return () => { tickSubscribers.current.delete(cb); };\n },\n keepAlive: () => {\n const sup = createSupervisor({ kind: 'keepAlive' });\n return () => sup.cancel();\n },\n watch: (listener) => hub.add(listener),\n live: () => {\n const rows: LiveAnimation[] = [];\n for (const a of animations.current.values()) {\n const row: LiveAnimation = {\n ...infoOf(a),\n paused: a.paused,\n timeScale: a.timeScale,\n elapsed: a.virtualNow,\n ...(a.progress ? { progress: a.progress(a.virtualNow) } : {}),\n };\n rows.push(row);\n }\n return rows;\n },\n };\n return { api, tickAll };\n }, [frameLoopRef, optsRef]);\n\n useInsertionEffect(() => {\n tickAllRef.current = tickAll;\n }, [tickAll]);\n\n // StrictMode-safe cleanup: when the component unmounts (including the\n // dev-mode double-mount that StrictMode performs), cancel every running\n // animation and stop the RAF loop. Without this, the FIRST mount's\n // animator keeps ticking with stale callbacks pointing at the unmounted\n // adapter, while the SECOND mount creates its own animator on top —\n // visible to the user as every animation playing twice.\n useEffect(() => {\n const overrides = colorOverrides.current;\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n api.cancelAll();\n overrides.clearAll();\n };\n }, [api]);\n\n return api;\n}\n"],"mappings":"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"}
|
|
@@ -462,7 +462,13 @@ interface PoseOverride<TPose> {
|
|
|
462
462
|
interface PoseOverrides<TPose> {
|
|
463
463
|
/** Store `entry` for `id` **by reference**; the caller keeps mutating it. */
|
|
464
464
|
set(id: NodeId, entry: PoseOverride<TPose>): void;
|
|
465
|
+
/** The entry `set` stored, by reference — what a writer checks to know
|
|
466
|
+
* whether its own entry is still installed. Painting reads `read`. */
|
|
465
467
|
get(id: NodeId): PoseOverride<TPose> | undefined;
|
|
468
|
+
/** What the node is drawn and picked with: every source of overrides for
|
|
469
|
+
* it folded into one, as of the last write or `commit()`. `undefined` for
|
|
470
|
+
* a node nothing overrides. The object is reused per node. */
|
|
471
|
+
read(id: NodeId): PoseOverride<TPose> | undefined;
|
|
466
472
|
has(id: NodeId): boolean;
|
|
467
473
|
/** The overridden ids, as a snapshot array. */
|
|
468
474
|
ids(): readonly NodeId[];
|
|
@@ -1356,6 +1362,31 @@ interface ShaderDrawCommand {
|
|
|
1356
1362
|
};
|
|
1357
1363
|
}
|
|
1358
1364
|
|
|
1365
|
+
/**
|
|
1366
|
+
* Clipping staged geometry to an axis-aligned rect on the CPU, so a rect clip
|
|
1367
|
+
* needs no stencil and does not break the run.
|
|
1368
|
+
*
|
|
1369
|
+
* A stencil clip is GL state, so content inside one cannot share a draw with
|
|
1370
|
+
* content outside it, and every clipped group paid a flush on the way in and
|
|
1371
|
+
* another on the way out. A rect under a transform that keeps it axis-aligned
|
|
1372
|
+
* is the common clip, and for that one the clip can ride the geometry instead:
|
|
1373
|
+
* cut each staged polygon to the rect before it joins the run.
|
|
1374
|
+
*
|
|
1375
|
+
* Exact, not approximate: every attribute a batch vertex carries is affine
|
|
1376
|
+
* across the polygon it belongs to, so interpolating along a cut edge gives
|
|
1377
|
+
* the value the rasterizer would have produced there, and the cut polygon
|
|
1378
|
+
* covers the same pixel centers the stencil would have passed.
|
|
1379
|
+
*/
|
|
1380
|
+
|
|
1381
|
+
/** In the space batch vertices are staged in: after the model transform,
|
|
1382
|
+
* before the projection. */
|
|
1383
|
+
interface ClipRect {
|
|
1384
|
+
x0: number;
|
|
1385
|
+
y0: number;
|
|
1386
|
+
x1: number;
|
|
1387
|
+
y1: number;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1359
1390
|
/**
|
|
1360
1391
|
* Growable vertex staging for a run of solid-fill geometry, image quads and
|
|
1361
1392
|
* glyphs.
|
|
@@ -1424,6 +1455,15 @@ declare class DrawBatch {
|
|
|
1424
1455
|
/** Whether the staged run is quads alone, so its indices are the canonical
|
|
1425
1456
|
* pattern and a slot already holding that pattern needs no upload. */
|
|
1426
1457
|
private pureRects;
|
|
1458
|
+
/** The rect every push is cut to, or `null`. `draw.ts` sets it as rect
|
|
1459
|
+
* clips enter and leave — see `batchClip.ts`. A push returns `false`, having
|
|
1460
|
+
* staged nothing, for geometry it cannot cut exactly; see `clipQuad`. */
|
|
1461
|
+
clip: ClipRect | null;
|
|
1462
|
+
/** Polygon scratch for `clip`: a quad cut by four edges has at most eight
|
|
1463
|
+
* corners. */
|
|
1464
|
+
private readonly polyIn;
|
|
1465
|
+
private readonly polyOut;
|
|
1466
|
+
private readonly polyTmp;
|
|
1427
1467
|
constructor(gl: WebGL2RenderingContext, prog: ShaderProgram);
|
|
1428
1468
|
get length(): number;
|
|
1429
1469
|
/** Whether staging `vertices` more would put the run past the per-flush cap. */
|
|
@@ -1433,7 +1473,7 @@ declare class DrawBatch {
|
|
|
1433
1473
|
* alpha). An affine maps a rect to a parallelogram, so two triangles still
|
|
1434
1474
|
* cover it and the batch draws at `u_model` identity.
|
|
1435
1475
|
*/
|
|
1436
|
-
pushRect(x: number, y: number, w: number, h: number, m: GlMat3, r: number, g: number, b: number, a: number):
|
|
1476
|
+
pushRect(x: number, y: number, w: number, h: number, m: GlMat3, r: number, g: number, b: number, a: number): boolean;
|
|
1437
1477
|
/**
|
|
1438
1478
|
* Append one image quad: the destination rect `(x, y, w, h)` mapped through
|
|
1439
1479
|
* `m`, sampling `(u0, v0)`-`(u1, v1)`, every corner carrying `post` as the
|
|
@@ -1447,7 +1487,7 @@ declare class DrawBatch {
|
|
|
1447
1487
|
* `slot` is the texture unit the corners sample, which `draw.ts` assigns per
|
|
1448
1488
|
* bitmap within the run.
|
|
1449
1489
|
*/
|
|
1450
|
-
pushQuad(x: number, y: number, w: number, h: number, m: GlMat3, u0: number, v0: number, u1: number, v1: number, post: number, slot: number):
|
|
1490
|
+
pushQuad(x: number, y: number, w: number, h: number, m: GlMat3, u0: number, v0: number, u1: number, v1: number, post: number, slot: number): boolean;
|
|
1451
1491
|
/**
|
|
1452
1492
|
* Append one glyph quad: the box `(x0, y0)`-`(x1, y1)` mapped through `m`,
|
|
1453
1493
|
* sampling `(u0, v0)`-`(u1, v1)` of the font atlas at `slot`, painted in
|
|
@@ -1469,7 +1509,7 @@ declare class DrawBatch {
|
|
|
1469
1509
|
* winding, which is what lets a run of mixed rects, image quads and glyphs
|
|
1470
1510
|
* keep the canonical index pattern.
|
|
1471
1511
|
*/
|
|
1472
|
-
pushGlyph(x0: number, y0: number, x1: number, y1: number, baselineY: number, tanItalic: number, m: GlMat3, u0: number, v0: number, u1: number, v1: number, r: number, g: number, b: number, a: number, slot: number, mode: number):
|
|
1512
|
+
pushGlyph(x0: number, y0: number, x1: number, y1: number, baselineY: number, tanItalic: number, m: GlMat3, u0: number, v0: number, u1: number, v1: number, r: number, g: number, b: number, a: number, slot: number, mode: number): boolean;
|
|
1473
1513
|
/**
|
|
1474
1514
|
* Append one rect filled by a gradient: the corners of `(x, y, w, h)` mapped
|
|
1475
1515
|
* through `m`, sampling the ramp atlas at `slot`.
|
|
@@ -1486,22 +1526,39 @@ declare class DrawBatch {
|
|
|
1486
1526
|
* Values outside 0..1 are the sampler's business; the atlas clamps to the
|
|
1487
1527
|
* edge texel, which is what the gradient shader's own `clamp` did.
|
|
1488
1528
|
*/
|
|
1489
|
-
pushGradientRect(x: number, y: number, w: number, h: number, m: GlMat3, uv: GradientUV, post: number, slot: number, mode: number, r: number, g: number, b: number, a: number):
|
|
1529
|
+
pushGradientRect(x: number, y: number, w: number, h: number, m: GlMat3, uv: GradientUV, post: number, slot: number, mode: number, r: number, g: number, b: number, a: number): boolean;
|
|
1490
1530
|
/** `pushMesh` for a mesh filled by a gradient — see `pushGradientRect` for
|
|
1491
1531
|
* what `uv`, `post` and `mode` carry. */
|
|
1492
|
-
pushGradientMesh(mesh: Mesh, m: GlMat3, uv: GradientUV, post: number, slot: number, mode: number, r: number, g: number, b: number, a: number):
|
|
1532
|
+
pushGradientMesh(mesh: Mesh, m: GlMat3, uv: GradientUV, post: number, slot: number, mode: number, r: number, g: number, b: number, a: number): boolean;
|
|
1493
1533
|
/**
|
|
1494
1534
|
* Append a tessellated mesh through `m`, all vertices carrying `rgba`. The
|
|
1495
1535
|
* mesh's own indices are rebased onto the staged vertices, which is why the
|
|
1496
1536
|
* index buffer is uploaded per flush rather than written once.
|
|
1497
1537
|
*/
|
|
1498
|
-
pushMesh(mesh: Mesh, m: GlMat3, r: number, g: number, b: number, a: number):
|
|
1538
|
+
pushMesh(mesh: Mesh, m: GlMat3, r: number, g: number, b: number, a: number): boolean;
|
|
1499
1539
|
/** Upload the staged geometry into the next set of buffers and bind its VAO.
|
|
1500
1540
|
* Returns the index count for the caller's `drawElements`. */
|
|
1501
1541
|
uploadAndBind(): number;
|
|
1502
1542
|
reset(): void;
|
|
1503
1543
|
dispose(): void;
|
|
1504
1544
|
private writeVertex;
|
|
1545
|
+
/**
|
|
1546
|
+
* Fit the quad just written at `v0` to `rect`: keep it if it lies inside,
|
|
1547
|
+
* drop it if it misses, and cut it if it crosses — but only if its edges
|
|
1548
|
+
* run along the axes. Cutting a slanted edge moves its endpoint off the
|
|
1549
|
+
* subpixel grid the rasterizer snaps to, and the shortened edge then covers
|
|
1550
|
+
* different samples than the whole one did; along an axis the cut edge is
|
|
1551
|
+
* the same line exactly. So a slanted quad that crosses is rolled back and
|
|
1552
|
+
* refused, for `draw.ts` to clip through the stencil instead.
|
|
1553
|
+
*
|
|
1554
|
+
* A cut axis-aligned quad is still four corners, so the run stays pure.
|
|
1555
|
+
*/
|
|
1556
|
+
private clipQuad;
|
|
1557
|
+
/** `clipQuad` for a mesh staged from `v0`. A mesh's edges are slanted in
|
|
1558
|
+
* general, so one that crosses is refused rather than cut. */
|
|
1559
|
+
private clipMesh;
|
|
1560
|
+
/** Append the `n`-corner convex polygon in `poly` as a triangle fan. */
|
|
1561
|
+
private appendFan;
|
|
1505
1562
|
/** Two triangles over the four corners just written. */
|
|
1506
1563
|
private pushQuadIndices;
|
|
1507
1564
|
/** Grow the CPU arrays so `vertices` / `indices` more fit. */
|
|
@@ -2638,7 +2695,10 @@ interface SpringOptions<T> {
|
|
|
2638
2695
|
damping?: number;
|
|
2639
2696
|
mass?: number;
|
|
2640
2697
|
interpolate?: Interpolate<T>;
|
|
2641
|
-
/** Vector helpers — required for non-numeric T.
|
|
2698
|
+
/** Vector helpers — required for non-numeric T. When `from`, `to` and `velocity` are numbers, number
|
|
2699
|
+
* arrays or plain objects of numeric fields of one shape, and stiffness, damping and mass are
|
|
2700
|
+
* positive, the motion is solved in closed form and these only decide when it has come to rest —
|
|
2701
|
+
* a `subtract` that wraps an angle won't change the path. */
|
|
2642
2702
|
add?: (a: T, b: T) => T;
|
|
2643
2703
|
subtract?: (a: T, b: T) => T;
|
|
2644
2704
|
scale?: (v: T, k: number) => T;
|
|
@@ -2665,7 +2725,10 @@ interface PhysicsOptions<T> {
|
|
|
2665
2725
|
damping?: number;
|
|
2666
2726
|
mass?: number;
|
|
2667
2727
|
restThreshold?: number;
|
|
2668
|
-
/** Vector helpers — required for non-numeric T.
|
|
2728
|
+
/** Vector helpers — required for non-numeric T. When `from`, `to` and `velocity` are numbers, number
|
|
2729
|
+
* arrays or plain objects of numeric fields of one shape, and stiffness, damping and mass are
|
|
2730
|
+
* positive, the motion is solved in closed form and these only decide when it has come to rest —
|
|
2731
|
+
* a `subtract` that wraps an angle won't change the path. */
|
|
2669
2732
|
add?: (a: T, b: T) => T;
|
|
2670
2733
|
subtract?: (a: T, b: T) => T;
|
|
2671
2734
|
scale?: (v: T, k: number) => T;
|
|
@@ -2693,6 +2756,9 @@ interface DecayOptions<T> {
|
|
|
2693
2756
|
friction?: number;
|
|
2694
2757
|
/** Velocity magnitude below which decay stops. Default 0.5. */
|
|
2695
2758
|
threshold?: number;
|
|
2759
|
+
/** When `from` and `velocity` are numbers, number arrays or plain objects of numeric fields of one
|
|
2760
|
+
* shape and `friction` is below 1, the motion is solved in closed form: `add` and `scale` go
|
|
2761
|
+
* unused and `magnitude` only decides when it has come to rest. */
|
|
2696
2762
|
add: (a: T, b: T) => T;
|
|
2697
2763
|
scale: (v: T, k: number) => T;
|
|
2698
2764
|
magnitude: (v: T) => number;
|
|
@@ -333,7 +333,7 @@ function h(e = {}) {
|
|
|
333
333
|
default: !1,
|
|
334
334
|
control: "toggle",
|
|
335
335
|
icon: "italic",
|
|
336
|
-
short: "I",
|
|
336
|
+
short: ["I"],
|
|
337
337
|
pair: "Style",
|
|
338
338
|
encoding: f
|
|
339
339
|
},
|
|
@@ -344,7 +344,7 @@ function h(e = {}) {
|
|
|
344
344
|
default: !1,
|
|
345
345
|
control: "toggle",
|
|
346
346
|
icon: "underline",
|
|
347
|
-
short: "U",
|
|
347
|
+
short: ["U"],
|
|
348
348
|
pair: "Style"
|
|
349
349
|
},
|
|
350
350
|
strikethrough: {
|
|
@@ -354,7 +354,7 @@ function h(e = {}) {
|
|
|
354
354
|
default: !1,
|
|
355
355
|
control: "toggle",
|
|
356
356
|
icon: "strikethrough",
|
|
357
|
-
short: "S",
|
|
357
|
+
short: ["S"],
|
|
358
358
|
pair: "Style"
|
|
359
359
|
},
|
|
360
360
|
overline: {
|
|
@@ -364,7 +364,7 @@ function h(e = {}) {
|
|
|
364
364
|
default: !1,
|
|
365
365
|
control: "toggle",
|
|
366
366
|
icon: "overline",
|
|
367
|
-
short: "O",
|
|
367
|
+
short: ["O"],
|
|
368
368
|
pair: "Style"
|
|
369
369
|
},
|
|
370
370
|
script: {
|
|
@@ -402,22 +402,22 @@ function h(e = {}) {
|
|
|
402
402
|
{
|
|
403
403
|
value: "none",
|
|
404
404
|
label: "None",
|
|
405
|
-
short: "–"
|
|
405
|
+
short: ["–"]
|
|
406
406
|
},
|
|
407
407
|
{
|
|
408
408
|
value: "uppercase",
|
|
409
409
|
label: "Uppercase",
|
|
410
|
-
short: "AA"
|
|
410
|
+
short: ["AA"]
|
|
411
411
|
},
|
|
412
412
|
{
|
|
413
413
|
value: "lowercase",
|
|
414
414
|
label: "Lowercase",
|
|
415
|
-
short: "aa"
|
|
415
|
+
short: ["aa"]
|
|
416
416
|
},
|
|
417
417
|
{
|
|
418
418
|
value: "capitalize",
|
|
419
419
|
label: "Capitalize",
|
|
420
|
-
short: "Aa"
|
|
420
|
+
short: ["Aa"]
|
|
421
421
|
}
|
|
422
422
|
]
|
|
423
423
|
},
|
|
@@ -430,11 +430,11 @@ function h(e = {}) {
|
|
|
430
430
|
options: [{
|
|
431
431
|
value: "normal",
|
|
432
432
|
label: "Normal",
|
|
433
|
-
short: "–"
|
|
433
|
+
short: ["–"]
|
|
434
434
|
}, {
|
|
435
435
|
value: "small-caps",
|
|
436
436
|
label: "Small caps",
|
|
437
|
-
short: "Sc"
|
|
437
|
+
short: ["Sc"]
|
|
438
438
|
}]
|
|
439
439
|
}
|
|
440
440
|
}
|
|
@@ -466,11 +466,11 @@ function h(e = {}) {
|
|
|
466
466
|
options: [{
|
|
467
467
|
value: "ltr",
|
|
468
468
|
label: "Left to right",
|
|
469
|
-
short: "LTR"
|
|
469
|
+
short: ["LTR"]
|
|
470
470
|
}, {
|
|
471
471
|
value: "rtl",
|
|
472
472
|
label: "Right to left",
|
|
473
|
-
short: "RTL"
|
|
473
|
+
short: ["RTL"]
|
|
474
474
|
}]
|
|
475
475
|
},
|
|
476
476
|
align: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaultNodeProperties.js","names":["rotationDegreesUnit","prefUnit","ANGLE_RADIANS","strokeWidthOf","w","stroke","strokeDashEncoding","strokeDashStyleOf","dash","style","dashForStrokeStyle","markerEncoding","ref","value","italicEncoding","stored","on","alignEncoding","direction","resolveAlign","edge","markerOptions","listMarkers","m","shapeSchema","opts","defaultNodeProperties","KIT_SHAPE_KINDS","name","inferredNodeProperties","inferredNodeRouting"],"sources":["../../../src/canvas/SceneCanvas/defaultNodeProperties.ts"],"sourcesContent":["import { inferredNodeRouting } from './defaultNodeRouting';\nimport { KIT_SHAPE_KINDS } from 'core/shapeKinds';\nimport { listMarkers } from 'core/strokeMarkers';\nimport { dashForStrokeStyle, strokeDashStyleOf } from '@weasel-js/paint';\nimport { resolveAlign, type TextAlign } from '@weasel-js/text';\nimport type { NodePropertiesEntry } from 'core/scene/NodeProperties';\nimport { ANGLE_RADIANS } from '@weasel-js/quantity';\nimport { prefUnit, type ToolPrefBooleanEncoding, type ToolPrefEnumEncoding, type ToolPrefGroup, type ToolPrefNumberUnit } from 'tools/prefs';\n\n/** Radians-stored / degrees-shown conversion for `pose.rotation` leaves.\n * Display rounds to 0.1° so a canonical radian value doesn't render as\n * a 15-digit float. */\nexport const rotationDegreesUnit: ToolPrefNumberUnit = prefUnit(ANGLE_RADIANS, 'deg', {\n precision: 1,\n suffix: '°',\n});\n\n/** `Stroke.width` off a partly-typed stroke object, or `undefined`. */\nfunction strokeWidthOf(stroke: Record<string, unknown> | undefined): number | { px: number } | undefined {\n const w = stroke?.width;\n if (typeof w === 'number') return w;\n if (typeof w === 'object' && w !== null && typeof (w as { px?: unknown }).px === 'number') {\n return w as { px: number };\n }\n return undefined;\n}\n\n/** `Stroke.dash` — a stored array of lengths — read and written as a named\n * style. The presets are multiples of the sibling `width`, which is why the\n * encoding is handed the whole stroke rather than the field. */\nconst strokeDashEncoding: ToolPrefEnumEncoding = {\n read: (dash, stroke) =>\n stroke === undefined\n ? undefined\n : strokeDashStyleOf(Array.isArray(dash) ? (dash as number[]) : undefined, strokeWidthOf(stroke)),\n write: (style, stroke) =>\n style === 'dashed' || style === 'dotted'\n ? dashForStrokeStyle(style, strokeWidthOf(stroke))\n // `solid` is stored as no dash at all. `custom` is reportable, not\n // authorable: it names an imported array, and the array it names is the\n // one already stored.\n : style === 'solid'\n ? undefined\n : stroke?.dash,\n};\n\n/** `Stroke.markerStart` / `markerMid` / `markerEnd` — a `MarkerRef` — read and\n * written as a bare key, with the empty string standing for no marker. */\nconst markerEncoding: ToolPrefEnumEncoding = {\n read: (ref) => {\n if (typeof ref === 'string') return ref;\n if (ref && typeof ref === 'object' && 'key' in ref) return String((ref as { key: string }).key);\n return '';\n },\n write: (value) => (value === '' ? undefined : value),\n};\n\n/** `TextStyle.fontStyle` as an Italic flag. Off removes the field rather than\n * storing `'normal'`, which is what an absent one already means. */\nconst italicEncoding: ToolPrefBooleanEncoding = {\n read: (stored) => stored === 'italic',\n write: (on) => (on ? 'italic' : undefined),\n};\n\n/** `TextStyle.align` read as the edge that paints, or as `justify`. The model\n * also holds the reading-order pair `start` / `end`, which the segments would\n * otherwise leave unlit; they read through `direction`, and a click writes the\n * edge. */\nconst alignEncoding: ToolPrefEnumEncoding = {\n read: (stored, style) => {\n if (stored === undefined || stored === 'justify') return stored;\n const direction = style?.direction === 'rtl' ? 'rtl' : 'ltr';\n return resolveAlign(stored as TextAlign, direction);\n },\n write: (edge) => edge,\n};\n\n/** Options come from the registry, so a consumer's registered entry appears in\n * the picker with no kit edit. A `select` rather than a `toggle`: nine options\n * will not fit a segmented strip. */\nconst markerOptions = () => [\n { value: '', label: 'None' },\n ...listMarkers().map((m) => ({ value: m.id, label: m.id })),\n];\n\n/** Build the standard shape schema — Layout (pose box + rotation) +\n * Appearance (fill / stroke), optionally a Text group. Matches the kit's\n * builtin-shape data template (`{ path, fill, stroke?, text? }`,\n * `useBuiltinShapeTools`). */\nfunction shapeSchema(opts: { text?: boolean } = {}): ToolPrefGroup {\n return {\n name: 'Properties',\n children: {\n layout: {\n name: 'Layout',\n children: {\n 'pose.x': { kind: 'number', name: 'X', description: 'Left edge, world units.', default: 0, pair: 'Position' },\n 'pose.y': { kind: 'number', name: 'Y', description: 'Top edge, world units.', default: 0, pair: 'Position' },\n 'pose.width': { kind: 'number', name: 'W', description: 'Width, world units.', default: 0, min: 0, pair: 'Size' },\n 'pose.height': { kind: 'number', name: 'H', description: 'Height, world units.', default: 0, min: 0, pair: 'Size' },\n 'pose.rotation': { kind: 'number', name: 'Rotation', description: 'Rotation about the box center.', default: 0, step: 1, unit: rotationDegreesUnit },\n },\n },\n // What the node *is* outranks how it is painted: a text node's copy is\n // the first thing an editor reaches for.\n ...(opts.text\n ? {\n content: {\n name: 'Content',\n children: {\n // `block`: the content is the section, so a 64px label column\n // reading \"Text\" inside a section reading \"Text\" is one word\n // twice and a narrower field for the sake of it.\n 'data.text': { kind: 'string', name: 'Text', description: 'Text content.', default: '', block: true },\n },\n },\n }\n : {}),\n appearance: {\n // Headless: `Fill` and `Stroke` head their own blocks, and an\n // `Appearance` rule over them names neither of the two paints a\n // reader is looking at.\n name: '',\n children: {\n // A `paint` leaf, not a `color` one: `data.fill` is the tagged\n // `FillStyle` union, so a color control pointed at it would read\n // `undefined` off a gradient and write a bare string over it.\n // `block` for the same reason every stroke field is: the kind bar\n // and the per-kind editor want the width a 64px label column takes.\n 'data.fill': { kind: 'paint', name: 'Fill', description: 'Fill paint.', default: { fill: 'solid', color: '#000000ff' }, alpha: true, block: true },\n // An object leaf: `data.stroke` is a whole `Stroke`, and its fields\n // belong to one value. Sibling leaves addressing into it would each\n // write one field of a value they can only half see.\n 'data.stroke': {\n kind: 'object',\n name: 'Stroke',\n description: 'Stroke paint and line geometry.',\n default: { paint: { fill: 'solid', color: '#000000ff' }, width: 1 },\n block: true,\n children: {\n // Every row is `block`: under a heading already reading STROKE,\n // a 64px label column spells the section name again and takes\n // the width the controls need. Each field leads with its own\n // glyph instead.\n // Unpaired: the paint field is a whole paint editor — a kind bar\n // over a per-kind body — and cannot share a row with a slider.\n width: { kind: 'number', name: 'Width', description: 'Stroke width, world units.', default: 1, min: 0, max: 20, step: 0.5, control: 'slider', icon: 'strokeWidth', block: true },\n paint: { kind: 'paint', name: 'Paint', description: 'Stroke paint.', default: { fill: 'solid', color: '#000000ff' }, alpha: true, block: true },\n // Three options each: a segmented control shows every one at\n // once where a select shows the current one and hides the rest\n // behind a click.\n cap: { kind: 'enum', name: 'Cap', description: 'How an open end is drawn.', default: 'butt', control: 'toggle', block: true, pair: 'Line', options: [{ value: 'butt', label: 'Butt', icon: 'capButt' }, { value: 'round', label: 'Round', icon: 'capRound' }, { value: 'square', label: 'Square', icon: 'capSquare' }] },\n join: { kind: 'enum', name: 'Join', description: 'How a corner is drawn.', default: 'miter', control: 'toggle', block: true, pair: 'Line', options: [{ value: 'miter', label: 'Miter', icon: 'joinMiter' }, { value: 'round', label: 'Round', icon: 'joinRound' }, { value: 'bevel', label: 'Bevel', icon: 'joinBevel' }] },\n align: { kind: 'enum', name: 'Align', description: 'Where the ribbon sits relative to the edge.', default: 'center', control: 'toggle', block: true, pair: 'Line', options: [{ value: 'inner', label: 'Inner', icon: 'alignInner' }, { value: 'center', label: 'Center', icon: 'alignCenter' }, { value: 'outer', label: 'Outer', icon: 'alignOuter' }] },\n // Its own row: three bars already fill the one above, and the\n // dash pattern is a property of the line rather than of how the\n // ribbon meets the geometry.\n dash: { kind: 'enum', name: 'Style', description: 'Solid, dashed or dotted. Dash lengths are multiples of the stroke width, so a style holds as the width changes.', default: 'solid', control: 'toggle', block: true, encoding: strokeDashEncoding, options: [{ value: 'solid', label: 'Solid', icon: 'dashSolid' }, { value: 'dashed', label: 'Dashed', icon: 'dashDashed' }, { value: 'dotted', label: 'Dotted', icon: 'dashDotted' }, { value: 'custom', label: 'Custom', icon: 'dashCustom', disabled: true }] },\n markerStart: { kind: 'enum', name: 'Start', description: 'Marker at the first vertex of each open subpath.', default: '', control: 'select', block: true, pair: 'Markers', encoding: markerEncoding, options: markerOptions() },\n markerMid: { kind: 'enum', name: 'Mid', description: 'Marker at every interior authored vertex.', default: '', control: 'select', block: true, pair: 'Markers', encoding: markerEncoding, options: markerOptions() },\n markerEnd: { kind: 'enum', name: 'End', description: 'Marker at the last vertex of each open subpath.', default: '', control: 'select', block: true, pair: 'Markers', encoding: markerEncoding, options: markerOptions() },\n },\n },\n },\n },\n ...(opts.text\n ? {\n text: {\n // Headless: `Character` and `Paragraph` carry the labels, and a\n // `Typography` heading over them names nothing a reader can't\n // already see.\n name: '',\n children: {\n // One object leaf, not a row of siblings addressing into it:\n // `data.style` is a `TextStyle`, and its character and\n // paragraph fields are one value that reads as two lists.\n // The groups head those lists and contribute nothing to the\n // path — a field inside one is still a field of the style.\n 'data.style': {\n kind: 'object',\n name: 'Style',\n description: 'Typography for the whole node. Its color is the node\\'s own `data.fill`, in Appearance.',\n default: {},\n block: true,\n children: {\n character: {\n name: 'Character',\n children: {\n fontFamily: { kind: 'font-family', name: 'Font', description: 'Registered font family.', default: 'sans-serif' },\n fontSize: { kind: 'number', name: 'Size', description: 'Font size, world units.', default: 16, min: 1, step: 1, pair: 'Size / weight' },\n fontWeight: { kind: 'font-weight', name: 'Weight', description: 'Font weight, from the faces the family has.', default: 400, pair: 'Size / weight' },\n // One row of segments, the strip every text editor\n // draws: the slant, the three rules, then the two\n // scripts. Italic is a flag here although the field\n // is `fontStyle`, whose only other value is upright.\n fontStyle: { kind: 'boolean', name: 'Italic', description: 'Set the text in its italic face.', default: false, control: 'toggle', icon: 'italic', short: 'I', pair: 'Style', encoding: italicEncoding },\n underline: { kind: 'boolean', name: 'Underline', description: 'Underline the text.', default: false, control: 'toggle', icon: 'underline', short: 'U', pair: 'Style' },\n strikethrough: { kind: 'boolean', name: 'Strikethrough', description: 'Strike through the text.', default: false, control: 'toggle', icon: 'strikethrough', short: 'S', pair: 'Style' },\n overline: { kind: 'boolean', name: 'Overline', description: 'Rule the text above its ascent.', default: false, control: 'toggle', icon: 'overline', short: 'O', pair: 'Style' },\n script: { kind: 'enum', name: 'Script', description: 'Set the text smaller and raised or lowered, on the line it would otherwise hold.', default: undefined, control: 'toggle', clearable: true, pair: 'Style', options: [{ value: 'super', label: 'Superscript', icon: 'superscript' }, { value: 'sub', label: 'Subscript', icon: 'subscript' }] },\n letterSpacing: { kind: 'number', name: 'Tracking', description: 'Extra advance per glyph, world units.', default: 0, step: 0.1 },\n textTransform: { kind: 'enum', name: 'Case', description: 'Draw the text in capitals, lowercase or title case. The text itself is unchanged.', default: 'none', control: 'toggle', options: [{ value: 'none', label: 'None', short: '–' }, { value: 'uppercase', label: 'Uppercase', short: 'AA' }, { value: 'lowercase', label: 'Lowercase', short: 'aa' }, { value: 'capitalize', label: 'Capitalize', short: 'Aa' }] },\n fontVariantCaps: { kind: 'enum', name: 'Small caps', description: 'Draw lowercase letters as smaller capitals. The text itself is unchanged.', default: 'normal', control: 'toggle', options: [{ value: 'normal', label: 'Normal', short: '–' }, { value: 'small-caps', label: 'Small caps', short: 'Sc' }] },\n },\n },\n paragraph: {\n name: 'Paragraph',\n children: {\n lineHeight: { kind: 'number', name: 'Leading', description: 'Line height as a multiple of font size.', default: 1.2, min: 0.5, step: 0.1 },\n wrap: { kind: 'boolean', name: 'Wrap', description: 'Break lines between words at the box width.', default: false, control: 'switch' },\n direction: { kind: 'enum', name: 'Direction', description: 'Reading direction.', default: 'ltr', control: 'toggle', options: [{ value: 'ltr', label: 'Left to right', short: 'LTR' }, { value: 'rtl', label: 'Right to left', short: 'RTL' }] },\n // Last, so the box's vertical alignment — a field of\n // the node rather than of its style — follows it.\n align: { kind: 'enum', name: 'Align', description: 'Horizontal alignment.', default: 'left', control: 'toggle', encoding: alignEncoding, options: [{ value: 'left', label: 'Left', icon: 'textAlignLeft' }, { value: 'center', label: 'Center', icon: 'textAlignCenter' }, { value: 'right', label: 'Right', icon: 'textAlignRight' }, { value: 'justify', label: 'Justify', icon: 'textAlignJustify' }] },\n },\n },\n },\n },\n 'data.verticalAlign': { kind: 'enum', name: 'Vertical', description: 'Where the lines sit in the box\\'s height.', default: 'top', control: 'toggle', options: [{ value: 'top', label: 'Top', icon: 'textAlignTop' }, { value: 'center', label: 'Middle', icon: 'textAlignMiddle' }, { value: 'bottom', label: 'Bottom', icon: 'textAlignBottom' }] },\n },\n },\n }\n : {}),\n },\n };\n}\n\n/**\n * Default properties-trait entries covering the kit's built-in shape\n * kinds (`KIT_SHAPE_KINDS`) — the sibling of `defaultNodeRouting`. Every\n * kind gets the standard box schema; `text` adds a Text group. In\n * lockstep with `KIT_SHAPE_KINDS` by construction (derived via `.map`).\n */\nexport const defaultNodeProperties: readonly NodePropertiesEntry[] =\n KIT_SHAPE_KINDS.map((name) => ({\n name,\n schema: shapeSchema({ text: name === 'text' }),\n }));\n\n/**\n * Properties-trait entries for the *inferred* routing kinds\n * (`inferredNodeRouting`: `text` / `path` / `image`) — the vocabulary\n * consumers produce when they don't tag `data.kind` (e.g. WeaselDraw).\n * In lockstep with `inferredNodeRouting` by construction.\n */\nexport const inferredNodeProperties: readonly NodePropertiesEntry[] =\n inferredNodeRouting.map((e) => ({\n name: e.name,\n schema: shapeSchema({ text: e.name === 'text' }),\n }));\n"],"mappings":";;;;;;;;AAYA,IAAaA,IAA0CC,EAASC,GAAe,OAAO;CACpF,WAAW;CACX,QAAQ;AACV,CAAC;AAGD,SAASC,EAAc,GAAkF;CACvG,IAAMC,IAAIC,GAAQ;CAElB,IADI,OAAOD,KAAM,YACb,OAAOA,KAAM,YAAYA,KAAc,OAAQA,EAAuB,MAAO,UAC/E,OAAOA;AAGX;AAKA,IAAME,IAA2C;CAC/C,OAAO,GAAM,MACXD,MAAW,KAAA,IACP,KAAA,IACAE,EAAkB,MAAM,QAAQC,CAAI,IAAKA,IAAoB,KAAA,GAAWL,EAAcE,CAAM,CAAC;CACnG,QAAQ,GAAO,MACbI,MAAU,YAAYA,MAAU,WAC5BC,EAAmBD,GAAON,EAAcE,CAAM,CAAC,IAI/CI,MAAU,UACR,KAAA,IACAJ,GAAQ;AAClB,GAIMM,IAAuC;CAC3C,OAAO,MACD,OAAOC,KAAQ,WAAiBA,IAChCA,KAAO,OAAOA,KAAQ,YAAY,SAASA,IAAY,OAAQA,EAAwB,GAAG,IACvF;CAET,QAAQ,MAAWC,MAAU,KAAK,KAAA,IAAYA;AAChD,GAIMC,IAA0C;CAC9C,OAAO,MAAWC,MAAW;CAC7B,QAAQ,MAAQC,IAAK,WAAW,KAAA;AAClC,GAMMC,IAAsC;CAC1C,OAAO,GAAQ,MAAU;EACvB,IAAIF,MAAW,KAAA,KAAaA,MAAW,WAAW,OAAOA;EACzD,IAAMG,IAAYT,GAAO,cAAc,QAAQ,QAAQ;EACvD,OAAOU,EAAaJ,GAAqBG,CAAS;CACpD;CACA,QAAQ,MAASE;AACnB,GAKMC,UAAsB,CAC1B;CAAE,OAAO;CAAI,OAAO;AAAO,GAC3B,GAAGC,EAAY,CAAC,CAAC,KAAK,OAAO;CAAE,OAAOC,EAAE;CAAI,OAAOA,EAAE;AAAG,EAAE,CAC5D;AAMA,SAASC,EAAY,IAA2B,CAAC,GAAkB;CACjE,OAAO;EACL,MAAM;EACN,UAAU;GACR,QAAQ;IACN,MAAM;IACN,UAAU;KACR,UAAU;MAAE,MAAM;MAAU,MAAM;MAAK,aAAa;MAA2B,SAAS;MAAG,MAAM;KAAW;KAC5G,UAAU;MAAE,MAAM;MAAU,MAAM;MAAK,aAAa;MAA0B,SAAS;MAAG,MAAM;KAAW;KAC3G,cAAc;MAAE,MAAM;MAAU,MAAM;MAAK,aAAa;MAAuB,SAAS;MAAG,KAAK;MAAG,MAAM;KAAO;KAChH,eAAe;MAAE,MAAM;MAAU,MAAM;MAAK,aAAa;MAAwB,SAAS;MAAG,KAAK;MAAG,MAAM;KAAO;KAClH,iBAAiB;MAAE,MAAM;MAAU,MAAM;MAAY,aAAa;MAAkC,SAAS;MAAG,MAAM;MAAG,MAAMxB;KAAoB;IACrJ;GACF;GAGA,GAAIyB,EAAK,OACL,EACE,SAAS;IACP,MAAM;IACN,UAAU,EAIR,aAAa;KAAE,MAAM;KAAU,MAAM;KAAQ,aAAa;KAAiB,SAAS;KAAI,OAAO;IAAK,EACtG;GACF,EACF,IACA,CAAC;GACL,YAAY;IAIV,MAAM;IACN,UAAU;KAMR,aAAa;MAAE,MAAM;MAAS,MAAM;MAAQ,aAAa;MAAe,SAAS;OAAE,MAAM;OAAS,OAAO;MAAY;MAAG,OAAO;MAAM,OAAO;KAAK;KAIjJ,eAAe;MACb,MAAM;MACN,MAAM;MACN,aAAa;MACb,SAAS;OAAE,OAAO;QAAE,MAAM;QAAS,OAAO;OAAY;OAAG,OAAO;MAAE;MAClE,OAAO;MACP,UAAU;OAOR,OAAO;QAAE,MAAM;QAAU,MAAM;QAAS,aAAa;QAA8B,SAAS;QAAG,KAAK;QAAG,KAAK;QAAI,MAAM;QAAK,SAAS;QAAU,MAAM;QAAe,OAAO;OAAK;OAC/K,OAAO;QAAE,MAAM;QAAS,MAAM;QAAS,aAAa;QAAiB,SAAS;SAAE,MAAM;SAAS,OAAO;QAAY;QAAG,OAAO;QAAM,OAAO;OAAK;OAI9I,KAAK;QAAE,MAAM;QAAQ,MAAM;QAAO,aAAa;QAA6B,SAAS;QAAQ,SAAS;QAAU,OAAO;QAAM,MAAM;QAAQ,SAAS;SAAC;UAAE,OAAO;UAAQ,OAAO;UAAQ,MAAM;SAAU;SAAG;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAW;SAAG;UAAE,OAAO;UAAU,OAAO;UAAU,MAAM;SAAY;QAAC;OAAE;OACvT,MAAM;QAAE,MAAM;QAAQ,MAAM;QAAQ,aAAa;QAA0B,SAAS;QAAS,SAAS;QAAU,OAAO;QAAM,MAAM;QAAQ,SAAS;SAAC;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAY;SAAG;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAY;SAAG;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAY;QAAC;OAAE;OAC1T,OAAO;QAAE,MAAM;QAAQ,MAAM;QAAS,aAAa;QAA+C,SAAS;QAAU,SAAS;QAAU,OAAO;QAAM,MAAM;QAAQ,SAAS;SAAC;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAa;SAAG;UAAE,OAAO;UAAU,OAAO;UAAU,MAAM;SAAc;SAAG;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAa;QAAC;OAAE;OAIxV,MAAM;QAAE,MAAM;QAAQ,MAAM;QAAS,aAAa;QAAmH,SAAS;QAAS,SAAS;QAAU,OAAO;QAAM,UAAUnB;QAAoB,SAAS;SAAC;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAY;SAAG;UAAE,OAAO;UAAU,OAAO;UAAU,MAAM;SAAa;SAAG;UAAE,OAAO;UAAU,OAAO;UAAU,MAAM;SAAa;SAAG;UAAE,OAAO;UAAU,OAAO;UAAU,MAAM;UAAc,UAAU;SAAK;QAAC;OAAE;OACpf,aAAa;QAAE,MAAM;QAAQ,MAAM;QAAS,aAAa;QAAoD,SAAS;QAAI,SAAS;QAAU,OAAO;QAAM,MAAM;QAAW,UAAUK;QAAgB,SAASU,EAAc;OAAE;OAC9N,WAAW;QAAE,MAAM;QAAQ,MAAM;QAAO,aAAa;QAA6C,SAAS;QAAI,SAAS;QAAU,OAAO;QAAM,MAAM;QAAW,UAAUV;QAAgB,SAASU,EAAc;OAAE;OACnN,WAAW;QAAE,MAAM;QAAQ,MAAM;QAAO,aAAa;QAAmD,SAAS;QAAI,SAAS;QAAU,OAAO;QAAM,MAAM;QAAW,UAAUV;QAAgB,SAASU,EAAc;OAAE;MAC3N;KACF;IACF;GACF;GACA,GAAII,EAAK,OACL,EACE,MAAM;IAIJ,MAAM;IACN,UAAU;KAMR,cAAc;MACZ,MAAM;MACN,MAAM;MACN,aAAa;MACb,SAAS,CAAC;MACV,OAAO;MACP,UAAU;OACR,WAAW;QACT,MAAM;QACN,UAAU;SACR,YAAY;UAAE,MAAM;UAAe,MAAM;UAAQ,aAAa;UAA2B,SAAS;SAAa;SAC/G,UAAU;UAAE,MAAM;UAAU,MAAM;UAAQ,aAAa;UAA2B,SAAS;UAAI,KAAK;UAAG,MAAM;UAAG,MAAM;SAAgB;SACtI,YAAY;UAAE,MAAM;UAAe,MAAM;UAAU,aAAa;UAA+C,SAAS;UAAK,MAAM;SAAgB;SAKnJ,WAAW;UAAE,MAAM;UAAW,MAAM;UAAU,aAAa;UAAoC,SAAS;UAAO,SAAS;UAAU,MAAM;UAAU,OAAO;UAAK,MAAM;UAAS,UAAUX;SAAe;SACtM,WAAW;UAAE,MAAM;UAAW,MAAM;UAAa,aAAa;UAAuB,SAAS;UAAO,SAAS;UAAU,MAAM;UAAa,OAAO;UAAK,MAAM;SAAQ;SACrK,eAAe;UAAE,MAAM;UAAW,MAAM;UAAiB,aAAa;UAA4B,SAAS;UAAO,SAAS;UAAU,MAAM;UAAiB,OAAO;UAAK,MAAM;SAAQ;SACtL,UAAU;UAAE,MAAM;UAAW,MAAM;UAAY,aAAa;UAAmC,SAAS;UAAO,SAAS;UAAU,MAAM;UAAY,OAAO;UAAK,MAAM;SAAQ;SAC9K,QAAQ;UAAE,MAAM;UAAQ,MAAM;UAAU,aAAa;UAAoF,SAAS,KAAA;UAAW,SAAS;UAAU,WAAW;UAAM,MAAM;UAAS,SAAS,CAAC;WAAE,OAAO;WAAS,OAAO;WAAe,MAAM;UAAc,GAAG;WAAE,OAAO;WAAO,OAAO;WAAa,MAAM;UAAY,CAAC;SAAE;SAClV,eAAe;UAAE,MAAM;UAAU,MAAM;UAAY,aAAa;UAAyC,SAAS;UAAG,MAAM;SAAI;SAC/H,eAAe;UAAE,MAAM;UAAQ,MAAM;UAAQ,aAAa;UAAqF,SAAS;UAAQ,SAAS;UAAU,SAAS;WAAC;YAAE,OAAO;YAAQ,OAAO;YAAQ,OAAO;WAAI;WAAG;YAAE,OAAO;YAAa,OAAO;YAAa,OAAO;WAAK;WAAG;YAAE,OAAO;YAAa,OAAO;YAAa,OAAO;WAAK;WAAG;YAAE,OAAO;YAAc,OAAO;YAAc,OAAO;WAAK;UAAC;SAAE;SACxZ,iBAAiB;UAAE,MAAM;UAAQ,MAAM;UAAc,aAAa;UAA6E,SAAS;UAAU,SAAS;UAAU,SAAS,CAAC;WAAE,OAAO;WAAU,OAAO;WAAU,OAAO;UAAI,GAAG;WAAE,OAAO;WAAc,OAAO;WAAc,OAAO;UAAK,CAAC;SAAE;QAC9S;OACF;OACA,WAAW;QACT,MAAM;QACN,UAAU;SACR,YAAY;UAAE,MAAM;UAAU,MAAM;UAAW,aAAa;UAA2C,SAAS;UAAK,KAAK;UAAK,MAAM;SAAI;SACzI,MAAM;UAAE,MAAM;UAAW,MAAM;UAAQ,aAAa;UAA+C,SAAS;UAAO,SAAS;SAAS;SACrI,WAAW;UAAE,MAAM;UAAQ,MAAM;UAAa,aAAa;UAAsB,SAAS;UAAO,SAAS;UAAU,SAAS,CAAC;WAAE,OAAO;WAAO,OAAO;WAAiB,OAAO;UAAM,GAAG;WAAE,OAAO;WAAO,OAAO;WAAiB,OAAO;UAAM,CAAC;SAAE;SAG9O,OAAO;UAAE,MAAM;UAAQ,MAAM;UAAS,aAAa;UAAyB,SAAS;UAAQ,SAAS;UAAU,UAAUG;UAAe,SAAS;WAAC;YAAE,OAAO;YAAQ,OAAO;YAAQ,MAAM;WAAgB;WAAG;YAAE,OAAO;YAAU,OAAO;YAAU,MAAM;WAAkB;WAAG;YAAE,OAAO;YAAS,OAAO;YAAS,MAAM;WAAiB;WAAG;YAAE,OAAO;YAAW,OAAO;YAAW,MAAM;WAAmB;UAAC;SAAE;QAC3Y;OACF;MACF;KACF;KACA,sBAAsB;MAAE,MAAM;MAAQ,MAAM;MAAY,aAAa;MAA6C,SAAS;MAAO,SAAS;MAAU,SAAS;OAAC;QAAE,OAAO;QAAO,OAAO;QAAO,MAAM;OAAe;OAAG;QAAE,OAAO;QAAU,OAAO;QAAU,MAAM;OAAkB;OAAG;QAAE,OAAO;QAAU,OAAO;QAAU,MAAM;OAAkB;MAAC;KAAE;IACrV;GACF,EACF,IACA,CAAC;EACP;CACF;AACF;AAQA,IAAaS,IACXC,EAAgB,KAAK,OAAU;CAC7B;CACA,QAAQH,EAAY,EAAE,MAAMI,MAAS,OAAO,CAAC;AAC/C,EAAE,GAQSC,IACXC,EAAoB,KAAK,OAAO;CAC9B,MAAM,EAAE;CACR,QAAQN,EAAY,EAAE,MAAM,EAAE,SAAS,OAAO,CAAC;AACjD,EAAE"}
|
|
1
|
+
{"version":3,"file":"defaultNodeProperties.js","names":["rotationDegreesUnit","prefUnit","ANGLE_RADIANS","strokeWidthOf","w","stroke","strokeDashEncoding","strokeDashStyleOf","dash","style","dashForStrokeStyle","markerEncoding","ref","value","italicEncoding","stored","on","alignEncoding","direction","resolveAlign","edge","markerOptions","listMarkers","m","shapeSchema","opts","defaultNodeProperties","KIT_SHAPE_KINDS","name","inferredNodeProperties","inferredNodeRouting"],"sources":["../../../src/canvas/SceneCanvas/defaultNodeProperties.ts"],"sourcesContent":["import { inferredNodeRouting } from './defaultNodeRouting';\nimport { KIT_SHAPE_KINDS } from 'core/shapeKinds';\nimport { listMarkers } from 'core/strokeMarkers';\nimport { dashForStrokeStyle, strokeDashStyleOf } from '@weasel-js/paint';\nimport { resolveAlign, type TextAlign } from '@weasel-js/text';\nimport type { NodePropertiesEntry } from 'core/scene/NodeProperties';\nimport { ANGLE_RADIANS } from '@weasel-js/quantity';\nimport { prefUnit, type ToolPrefBooleanEncoding, type ToolPrefEnumEncoding, type ToolPrefGroup, type ToolPrefNumberUnit } from 'tools/prefs';\n\n/** Radians-stored / degrees-shown conversion for `pose.rotation` leaves.\n * Display rounds to 0.1° so a canonical radian value doesn't render as\n * a 15-digit float. */\nexport const rotationDegreesUnit: ToolPrefNumberUnit = prefUnit(ANGLE_RADIANS, 'deg', {\n precision: 1,\n suffix: '°',\n});\n\n/** `Stroke.width` off a partly-typed stroke object, or `undefined`. */\nfunction strokeWidthOf(stroke: Record<string, unknown> | undefined): number | { px: number } | undefined {\n const w = stroke?.width;\n if (typeof w === 'number') return w;\n if (typeof w === 'object' && w !== null && typeof (w as { px?: unknown }).px === 'number') {\n return w as { px: number };\n }\n return undefined;\n}\n\n/** `Stroke.dash` — a stored array of lengths — read and written as a named\n * style. The presets are multiples of the sibling `width`, which is why the\n * encoding is handed the whole stroke rather than the field. */\nconst strokeDashEncoding: ToolPrefEnumEncoding = {\n read: (dash, stroke) =>\n stroke === undefined\n ? undefined\n : strokeDashStyleOf(Array.isArray(dash) ? (dash as number[]) : undefined, strokeWidthOf(stroke)),\n write: (style, stroke) =>\n style === 'dashed' || style === 'dotted'\n ? dashForStrokeStyle(style, strokeWidthOf(stroke))\n // `solid` is stored as no dash at all. `custom` is reportable, not\n // authorable: it names an imported array, and the array it names is the\n // one already stored.\n : style === 'solid'\n ? undefined\n : stroke?.dash,\n};\n\n/** `Stroke.markerStart` / `markerMid` / `markerEnd` — a `MarkerRef` — read and\n * written as a bare key, with the empty string standing for no marker. */\nconst markerEncoding: ToolPrefEnumEncoding = {\n read: (ref) => {\n if (typeof ref === 'string') return ref;\n if (ref && typeof ref === 'object' && 'key' in ref) return String((ref as { key: string }).key);\n return '';\n },\n write: (value) => (value === '' ? undefined : value),\n};\n\n/** `TextStyle.fontStyle` as an Italic flag. Off removes the field rather than\n * storing `'normal'`, which is what an absent one already means. */\nconst italicEncoding: ToolPrefBooleanEncoding = {\n read: (stored) => stored === 'italic',\n write: (on) => (on ? 'italic' : undefined),\n};\n\n/** `TextStyle.align` read as the edge that paints, or as `justify`. The model\n * also holds the reading-order pair `start` / `end`, which the segments would\n * otherwise leave unlit; they read through `direction`, and a click writes the\n * edge. */\nconst alignEncoding: ToolPrefEnumEncoding = {\n read: (stored, style) => {\n if (stored === undefined || stored === 'justify') return stored;\n const direction = style?.direction === 'rtl' ? 'rtl' : 'ltr';\n return resolveAlign(stored as TextAlign, direction);\n },\n write: (edge) => edge,\n};\n\n/** Options come from the registry, so a consumer's registered entry appears in\n * the picker with no kit edit. A `select` rather than a `toggle`: nine options\n * will not fit a segmented strip. */\nconst markerOptions = () => [\n { value: '', label: 'None' },\n ...listMarkers().map((m) => ({ value: m.id, label: m.id })),\n];\n\n/** Build the standard shape schema — Layout (pose box + rotation) +\n * Appearance (fill / stroke), optionally a Text group. Matches the kit's\n * builtin-shape data template (`{ path, fill, stroke?, text? }`,\n * `useBuiltinShapeTools`). */\nfunction shapeSchema(opts: { text?: boolean } = {}): ToolPrefGroup {\n return {\n name: 'Properties',\n children: {\n layout: {\n name: 'Layout',\n children: {\n 'pose.x': { kind: 'number', name: 'X', description: 'Left edge, world units.', default: 0, pair: 'Position' },\n 'pose.y': { kind: 'number', name: 'Y', description: 'Top edge, world units.', default: 0, pair: 'Position' },\n 'pose.width': { kind: 'number', name: 'W', description: 'Width, world units.', default: 0, min: 0, pair: 'Size' },\n 'pose.height': { kind: 'number', name: 'H', description: 'Height, world units.', default: 0, min: 0, pair: 'Size' },\n 'pose.rotation': { kind: 'number', name: 'Rotation', description: 'Rotation about the box center.', default: 0, step: 1, unit: rotationDegreesUnit },\n },\n },\n // What the node *is* outranks how it is painted: a text node's copy is\n // the first thing an editor reaches for.\n ...(opts.text\n ? {\n content: {\n name: 'Content',\n children: {\n // `block`: the content is the section, so a 64px label column\n // reading \"Text\" inside a section reading \"Text\" is one word\n // twice and a narrower field for the sake of it.\n 'data.text': { kind: 'string', name: 'Text', description: 'Text content.', default: '', block: true },\n },\n },\n }\n : {}),\n appearance: {\n // Headless: `Fill` and `Stroke` head their own blocks, and an\n // `Appearance` rule over them names neither of the two paints a\n // reader is looking at.\n name: '',\n children: {\n // A `paint` leaf, not a `color` one: `data.fill` is the tagged\n // `FillStyle` union, so a color control pointed at it would read\n // `undefined` off a gradient and write a bare string over it.\n // `block` for the same reason every stroke field is: the kind bar\n // and the per-kind editor want the width a 64px label column takes.\n 'data.fill': { kind: 'paint', name: 'Fill', description: 'Fill paint.', default: { fill: 'solid', color: '#000000ff' }, alpha: true, block: true },\n // An object leaf: `data.stroke` is a whole `Stroke`, and its fields\n // belong to one value. Sibling leaves addressing into it would each\n // write one field of a value they can only half see.\n 'data.stroke': {\n kind: 'object',\n name: 'Stroke',\n description: 'Stroke paint and line geometry.',\n default: { paint: { fill: 'solid', color: '#000000ff' }, width: 1 },\n block: true,\n children: {\n // Every row is `block`: under a heading already reading STROKE,\n // a 64px label column spells the section name again and takes\n // the width the controls need. Each field leads with its own\n // glyph instead.\n // Unpaired: the paint field is a whole paint editor — a kind bar\n // over a per-kind body — and cannot share a row with a slider.\n width: { kind: 'number', name: 'Width', description: 'Stroke width, world units.', default: 1, min: 0, max: 20, step: 0.5, control: 'slider', icon: 'strokeWidth', block: true },\n paint: { kind: 'paint', name: 'Paint', description: 'Stroke paint.', default: { fill: 'solid', color: '#000000ff' }, alpha: true, block: true },\n // Three options each: a segmented control shows every one at\n // once where a select shows the current one and hides the rest\n // behind a click.\n cap: { kind: 'enum', name: 'Cap', description: 'How an open end is drawn.', default: 'butt', control: 'toggle', block: true, pair: 'Line', options: [{ value: 'butt', label: 'Butt', icon: 'capButt' }, { value: 'round', label: 'Round', icon: 'capRound' }, { value: 'square', label: 'Square', icon: 'capSquare' }] },\n join: { kind: 'enum', name: 'Join', description: 'How a corner is drawn.', default: 'miter', control: 'toggle', block: true, pair: 'Line', options: [{ value: 'miter', label: 'Miter', icon: 'joinMiter' }, { value: 'round', label: 'Round', icon: 'joinRound' }, { value: 'bevel', label: 'Bevel', icon: 'joinBevel' }] },\n align: { kind: 'enum', name: 'Align', description: 'Where the ribbon sits relative to the edge.', default: 'center', control: 'toggle', block: true, pair: 'Line', options: [{ value: 'inner', label: 'Inner', icon: 'alignInner' }, { value: 'center', label: 'Center', icon: 'alignCenter' }, { value: 'outer', label: 'Outer', icon: 'alignOuter' }] },\n // Its own row: three bars already fill the one above, and the\n // dash pattern is a property of the line rather than of how the\n // ribbon meets the geometry.\n dash: { kind: 'enum', name: 'Style', description: 'Solid, dashed or dotted. Dash lengths are multiples of the stroke width, so a style holds as the width changes.', default: 'solid', control: 'toggle', block: true, encoding: strokeDashEncoding, options: [{ value: 'solid', label: 'Solid', icon: 'dashSolid' }, { value: 'dashed', label: 'Dashed', icon: 'dashDashed' }, { value: 'dotted', label: 'Dotted', icon: 'dashDotted' }, { value: 'custom', label: 'Custom', icon: 'dashCustom', disabled: true }] },\n markerStart: { kind: 'enum', name: 'Start', description: 'Marker at the first vertex of each open subpath.', default: '', control: 'select', block: true, pair: 'Markers', encoding: markerEncoding, options: markerOptions() },\n markerMid: { kind: 'enum', name: 'Mid', description: 'Marker at every interior authored vertex.', default: '', control: 'select', block: true, pair: 'Markers', encoding: markerEncoding, options: markerOptions() },\n markerEnd: { kind: 'enum', name: 'End', description: 'Marker at the last vertex of each open subpath.', default: '', control: 'select', block: true, pair: 'Markers', encoding: markerEncoding, options: markerOptions() },\n },\n },\n },\n },\n ...(opts.text\n ? {\n text: {\n // Headless: `Character` and `Paragraph` carry the labels, and a\n // `Typography` heading over them names nothing a reader can't\n // already see.\n name: '',\n children: {\n // One object leaf, not a row of siblings addressing into it:\n // `data.style` is a `TextStyle`, and its character and\n // paragraph fields are one value that reads as two lists.\n // The groups head those lists and contribute nothing to the\n // path — a field inside one is still a field of the style.\n 'data.style': {\n kind: 'object',\n name: 'Style',\n description: 'Typography for the whole node. Its color is the node\\'s own `data.fill`, in Appearance.',\n default: {},\n block: true,\n children: {\n character: {\n name: 'Character',\n children: {\n fontFamily: { kind: 'font-family', name: 'Font', description: 'Registered font family.', default: 'sans-serif' },\n fontSize: { kind: 'number', name: 'Size', description: 'Font size, world units.', default: 16, min: 1, step: 1, pair: 'Size / weight' },\n fontWeight: { kind: 'font-weight', name: 'Weight', description: 'Font weight, from the faces the family has.', default: 400, pair: 'Size / weight' },\n // One row of segments, the strip every text editor\n // draws: the slant, the three rules, then the two\n // scripts. Italic is a flag here although the field\n // is `fontStyle`, whose only other value is upright.\n fontStyle: { kind: 'boolean', name: 'Italic', description: 'Set the text in its italic face.', default: false, control: 'toggle', icon: 'italic', short: ['I'], pair: 'Style', encoding: italicEncoding },\n underline: { kind: 'boolean', name: 'Underline', description: 'Underline the text.', default: false, control: 'toggle', icon: 'underline', short: ['U'], pair: 'Style' },\n strikethrough: { kind: 'boolean', name: 'Strikethrough', description: 'Strike through the text.', default: false, control: 'toggle', icon: 'strikethrough', short: ['S'], pair: 'Style' },\n overline: { kind: 'boolean', name: 'Overline', description: 'Rule the text above its ascent.', default: false, control: 'toggle', icon: 'overline', short: ['O'], pair: 'Style' },\n script: { kind: 'enum', name: 'Script', description: 'Set the text smaller and raised or lowered, on the line it would otherwise hold.', default: undefined, control: 'toggle', clearable: true, pair: 'Style', options: [{ value: 'super', label: 'Superscript', icon: 'superscript' }, { value: 'sub', label: 'Subscript', icon: 'subscript' }] },\n letterSpacing: { kind: 'number', name: 'Tracking', description: 'Extra advance per glyph, world units.', default: 0, step: 0.1 },\n textTransform: { kind: 'enum', name: 'Case', description: 'Draw the text in capitals, lowercase or title case. The text itself is unchanged.', default: 'none', control: 'toggle', options: [{ value: 'none', label: 'None', short: ['–'] }, { value: 'uppercase', label: 'Uppercase', short: ['AA'] }, { value: 'lowercase', label: 'Lowercase', short: ['aa'] }, { value: 'capitalize', label: 'Capitalize', short: ['Aa'] }] },\n fontVariantCaps: { kind: 'enum', name: 'Small caps', description: 'Draw lowercase letters as smaller capitals. The text itself is unchanged.', default: 'normal', control: 'toggle', options: [{ value: 'normal', label: 'Normal', short: ['–'] }, { value: 'small-caps', label: 'Small caps', short: ['Sc'] }] },\n },\n },\n paragraph: {\n name: 'Paragraph',\n children: {\n lineHeight: { kind: 'number', name: 'Leading', description: 'Line height as a multiple of font size.', default: 1.2, min: 0.5, step: 0.1 },\n wrap: { kind: 'boolean', name: 'Wrap', description: 'Break lines between words at the box width.', default: false, control: 'switch' },\n direction: { kind: 'enum', name: 'Direction', description: 'Reading direction.', default: 'ltr', control: 'toggle', options: [{ value: 'ltr', label: 'Left to right', short: ['LTR'] }, { value: 'rtl', label: 'Right to left', short: ['RTL'] }] },\n // Last, so the box's vertical alignment — a field of\n // the node rather than of its style — follows it.\n align: { kind: 'enum', name: 'Align', description: 'Horizontal alignment.', default: 'left', control: 'toggle', encoding: alignEncoding, options: [{ value: 'left', label: 'Left', icon: 'textAlignLeft' }, { value: 'center', label: 'Center', icon: 'textAlignCenter' }, { value: 'right', label: 'Right', icon: 'textAlignRight' }, { value: 'justify', label: 'Justify', icon: 'textAlignJustify' }] },\n },\n },\n },\n },\n 'data.verticalAlign': { kind: 'enum', name: 'Vertical', description: 'Where the lines sit in the box\\'s height.', default: 'top', control: 'toggle', options: [{ value: 'top', label: 'Top', icon: 'textAlignTop' }, { value: 'center', label: 'Middle', icon: 'textAlignMiddle' }, { value: 'bottom', label: 'Bottom', icon: 'textAlignBottom' }] },\n },\n },\n }\n : {}),\n },\n };\n}\n\n/**\n * Default properties-trait entries covering the kit's built-in shape\n * kinds (`KIT_SHAPE_KINDS`) — the sibling of `defaultNodeRouting`. Every\n * kind gets the standard box schema; `text` adds a Text group. In\n * lockstep with `KIT_SHAPE_KINDS` by construction (derived via `.map`).\n */\nexport const defaultNodeProperties: readonly NodePropertiesEntry[] =\n KIT_SHAPE_KINDS.map((name) => ({\n name,\n schema: shapeSchema({ text: name === 'text' }),\n }));\n\n/**\n * Properties-trait entries for the *inferred* routing kinds\n * (`inferredNodeRouting`: `text` / `path` / `image`) — the vocabulary\n * consumers produce when they don't tag `data.kind` (e.g. WeaselDraw).\n * In lockstep with `inferredNodeRouting` by construction.\n */\nexport const inferredNodeProperties: readonly NodePropertiesEntry[] =\n inferredNodeRouting.map((e) => ({\n name: e.name,\n schema: shapeSchema({ text: e.name === 'text' }),\n }));\n"],"mappings":";;;;;;;;AAYA,IAAaA,IAA0CC,EAASC,GAAe,OAAO;CACpF,WAAW;CACX,QAAQ;AACV,CAAC;AAGD,SAASC,EAAc,GAAkF;CACvG,IAAMC,IAAIC,GAAQ;CAElB,IADI,OAAOD,KAAM,YACb,OAAOA,KAAM,YAAYA,KAAc,OAAQA,EAAuB,MAAO,UAC/E,OAAOA;AAGX;AAKA,IAAME,IAA2C;CAC/C,OAAO,GAAM,MACXD,MAAW,KAAA,IACP,KAAA,IACAE,EAAkB,MAAM,QAAQC,CAAI,IAAKA,IAAoB,KAAA,GAAWL,EAAcE,CAAM,CAAC;CACnG,QAAQ,GAAO,MACbI,MAAU,YAAYA,MAAU,WAC5BC,EAAmBD,GAAON,EAAcE,CAAM,CAAC,IAI/CI,MAAU,UACR,KAAA,IACAJ,GAAQ;AAClB,GAIMM,IAAuC;CAC3C,OAAO,MACD,OAAOC,KAAQ,WAAiBA,IAChCA,KAAO,OAAOA,KAAQ,YAAY,SAASA,IAAY,OAAQA,EAAwB,GAAG,IACvF;CAET,QAAQ,MAAWC,MAAU,KAAK,KAAA,IAAYA;AAChD,GAIMC,IAA0C;CAC9C,OAAO,MAAWC,MAAW;CAC7B,QAAQ,MAAQC,IAAK,WAAW,KAAA;AAClC,GAMMC,IAAsC;CAC1C,OAAO,GAAQ,MAAU;EACvB,IAAIF,MAAW,KAAA,KAAaA,MAAW,WAAW,OAAOA;EACzD,IAAMG,IAAYT,GAAO,cAAc,QAAQ,QAAQ;EACvD,OAAOU,EAAaJ,GAAqBG,CAAS;CACpD;CACA,QAAQ,MAASE;AACnB,GAKMC,UAAsB,CAC1B;CAAE,OAAO;CAAI,OAAO;AAAO,GAC3B,GAAGC,EAAY,CAAC,CAAC,KAAK,OAAO;CAAE,OAAOC,EAAE;CAAI,OAAOA,EAAE;AAAG,EAAE,CAC5D;AAMA,SAASC,EAAY,IAA2B,CAAC,GAAkB;CACjE,OAAO;EACL,MAAM;EACN,UAAU;GACR,QAAQ;IACN,MAAM;IACN,UAAU;KACR,UAAU;MAAE,MAAM;MAAU,MAAM;MAAK,aAAa;MAA2B,SAAS;MAAG,MAAM;KAAW;KAC5G,UAAU;MAAE,MAAM;MAAU,MAAM;MAAK,aAAa;MAA0B,SAAS;MAAG,MAAM;KAAW;KAC3G,cAAc;MAAE,MAAM;MAAU,MAAM;MAAK,aAAa;MAAuB,SAAS;MAAG,KAAK;MAAG,MAAM;KAAO;KAChH,eAAe;MAAE,MAAM;MAAU,MAAM;MAAK,aAAa;MAAwB,SAAS;MAAG,KAAK;MAAG,MAAM;KAAO;KAClH,iBAAiB;MAAE,MAAM;MAAU,MAAM;MAAY,aAAa;MAAkC,SAAS;MAAG,MAAM;MAAG,MAAMxB;KAAoB;IACrJ;GACF;GAGA,GAAIyB,EAAK,OACL,EACE,SAAS;IACP,MAAM;IACN,UAAU,EAIR,aAAa;KAAE,MAAM;KAAU,MAAM;KAAQ,aAAa;KAAiB,SAAS;KAAI,OAAO;IAAK,EACtG;GACF,EACF,IACA,CAAC;GACL,YAAY;IAIV,MAAM;IACN,UAAU;KAMR,aAAa;MAAE,MAAM;MAAS,MAAM;MAAQ,aAAa;MAAe,SAAS;OAAE,MAAM;OAAS,OAAO;MAAY;MAAG,OAAO;MAAM,OAAO;KAAK;KAIjJ,eAAe;MACb,MAAM;MACN,MAAM;MACN,aAAa;MACb,SAAS;OAAE,OAAO;QAAE,MAAM;QAAS,OAAO;OAAY;OAAG,OAAO;MAAE;MAClE,OAAO;MACP,UAAU;OAOR,OAAO;QAAE,MAAM;QAAU,MAAM;QAAS,aAAa;QAA8B,SAAS;QAAG,KAAK;QAAG,KAAK;QAAI,MAAM;QAAK,SAAS;QAAU,MAAM;QAAe,OAAO;OAAK;OAC/K,OAAO;QAAE,MAAM;QAAS,MAAM;QAAS,aAAa;QAAiB,SAAS;SAAE,MAAM;SAAS,OAAO;QAAY;QAAG,OAAO;QAAM,OAAO;OAAK;OAI9I,KAAK;QAAE,MAAM;QAAQ,MAAM;QAAO,aAAa;QAA6B,SAAS;QAAQ,SAAS;QAAU,OAAO;QAAM,MAAM;QAAQ,SAAS;SAAC;UAAE,OAAO;UAAQ,OAAO;UAAQ,MAAM;SAAU;SAAG;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAW;SAAG;UAAE,OAAO;UAAU,OAAO;UAAU,MAAM;SAAY;QAAC;OAAE;OACvT,MAAM;QAAE,MAAM;QAAQ,MAAM;QAAQ,aAAa;QAA0B,SAAS;QAAS,SAAS;QAAU,OAAO;QAAM,MAAM;QAAQ,SAAS;SAAC;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAY;SAAG;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAY;SAAG;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAY;QAAC;OAAE;OAC1T,OAAO;QAAE,MAAM;QAAQ,MAAM;QAAS,aAAa;QAA+C,SAAS;QAAU,SAAS;QAAU,OAAO;QAAM,MAAM;QAAQ,SAAS;SAAC;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAa;SAAG;UAAE,OAAO;UAAU,OAAO;UAAU,MAAM;SAAc;SAAG;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAa;QAAC;OAAE;OAIxV,MAAM;QAAE,MAAM;QAAQ,MAAM;QAAS,aAAa;QAAmH,SAAS;QAAS,SAAS;QAAU,OAAO;QAAM,UAAUnB;QAAoB,SAAS;SAAC;UAAE,OAAO;UAAS,OAAO;UAAS,MAAM;SAAY;SAAG;UAAE,OAAO;UAAU,OAAO;UAAU,MAAM;SAAa;SAAG;UAAE,OAAO;UAAU,OAAO;UAAU,MAAM;SAAa;SAAG;UAAE,OAAO;UAAU,OAAO;UAAU,MAAM;UAAc,UAAU;SAAK;QAAC;OAAE;OACpf,aAAa;QAAE,MAAM;QAAQ,MAAM;QAAS,aAAa;QAAoD,SAAS;QAAI,SAAS;QAAU,OAAO;QAAM,MAAM;QAAW,UAAUK;QAAgB,SAASU,EAAc;OAAE;OAC9N,WAAW;QAAE,MAAM;QAAQ,MAAM;QAAO,aAAa;QAA6C,SAAS;QAAI,SAAS;QAAU,OAAO;QAAM,MAAM;QAAW,UAAUV;QAAgB,SAASU,EAAc;OAAE;OACnN,WAAW;QAAE,MAAM;QAAQ,MAAM;QAAO,aAAa;QAAmD,SAAS;QAAI,SAAS;QAAU,OAAO;QAAM,MAAM;QAAW,UAAUV;QAAgB,SAASU,EAAc;OAAE;MAC3N;KACF;IACF;GACF;GACA,GAAII,EAAK,OACL,EACE,MAAM;IAIJ,MAAM;IACN,UAAU;KAMR,cAAc;MACZ,MAAM;MACN,MAAM;MACN,aAAa;MACb,SAAS,CAAC;MACV,OAAO;MACP,UAAU;OACR,WAAW;QACT,MAAM;QACN,UAAU;SACR,YAAY;UAAE,MAAM;UAAe,MAAM;UAAQ,aAAa;UAA2B,SAAS;SAAa;SAC/G,UAAU;UAAE,MAAM;UAAU,MAAM;UAAQ,aAAa;UAA2B,SAAS;UAAI,KAAK;UAAG,MAAM;UAAG,MAAM;SAAgB;SACtI,YAAY;UAAE,MAAM;UAAe,MAAM;UAAU,aAAa;UAA+C,SAAS;UAAK,MAAM;SAAgB;SAKnJ,WAAW;UAAE,MAAM;UAAW,MAAM;UAAU,aAAa;UAAoC,SAAS;UAAO,SAAS;UAAU,MAAM;UAAU,OAAO,CAAC,GAAG;UAAG,MAAM;UAAS,UAAUX;SAAe;SACxM,WAAW;UAAE,MAAM;UAAW,MAAM;UAAa,aAAa;UAAuB,SAAS;UAAO,SAAS;UAAU,MAAM;UAAa,OAAO,CAAC,GAAG;UAAG,MAAM;SAAQ;SACvK,eAAe;UAAE,MAAM;UAAW,MAAM;UAAiB,aAAa;UAA4B,SAAS;UAAO,SAAS;UAAU,MAAM;UAAiB,OAAO,CAAC,GAAG;UAAG,MAAM;SAAQ;SACxL,UAAU;UAAE,MAAM;UAAW,MAAM;UAAY,aAAa;UAAmC,SAAS;UAAO,SAAS;UAAU,MAAM;UAAY,OAAO,CAAC,GAAG;UAAG,MAAM;SAAQ;SAChL,QAAQ;UAAE,MAAM;UAAQ,MAAM;UAAU,aAAa;UAAoF,SAAS,KAAA;UAAW,SAAS;UAAU,WAAW;UAAM,MAAM;UAAS,SAAS,CAAC;WAAE,OAAO;WAAS,OAAO;WAAe,MAAM;UAAc,GAAG;WAAE,OAAO;WAAO,OAAO;WAAa,MAAM;UAAY,CAAC;SAAE;SAClV,eAAe;UAAE,MAAM;UAAU,MAAM;UAAY,aAAa;UAAyC,SAAS;UAAG,MAAM;SAAI;SAC/H,eAAe;UAAE,MAAM;UAAQ,MAAM;UAAQ,aAAa;UAAqF,SAAS;UAAQ,SAAS;UAAU,SAAS;WAAC;YAAE,OAAO;YAAQ,OAAO;YAAQ,OAAO,CAAC,GAAG;WAAE;WAAG;YAAE,OAAO;YAAa,OAAO;YAAa,OAAO,CAAC,IAAI;WAAE;WAAG;YAAE,OAAO;YAAa,OAAO;YAAa,OAAO,CAAC,IAAI;WAAE;WAAG;YAAE,OAAO;YAAc,OAAO;YAAc,OAAO,CAAC,IAAI;WAAE;UAAC;SAAE;SACha,iBAAiB;UAAE,MAAM;UAAQ,MAAM;UAAc,aAAa;UAA6E,SAAS;UAAU,SAAS;UAAU,SAAS,CAAC;WAAE,OAAO;WAAU,OAAO;WAAU,OAAO,CAAC,GAAG;UAAE,GAAG;WAAE,OAAO;WAAc,OAAO;WAAc,OAAO,CAAC,IAAI;UAAE,CAAC;SAAE;QAClT;OACF;OACA,WAAW;QACT,MAAM;QACN,UAAU;SACR,YAAY;UAAE,MAAM;UAAU,MAAM;UAAW,aAAa;UAA2C,SAAS;UAAK,KAAK;UAAK,MAAM;SAAI;SACzI,MAAM;UAAE,MAAM;UAAW,MAAM;UAAQ,aAAa;UAA+C,SAAS;UAAO,SAAS;SAAS;SACrI,WAAW;UAAE,MAAM;UAAQ,MAAM;UAAa,aAAa;UAAsB,SAAS;UAAO,SAAS;UAAU,SAAS,CAAC;WAAE,OAAO;WAAO,OAAO;WAAiB,OAAO,CAAC,KAAK;UAAE,GAAG;WAAE,OAAO;WAAO,OAAO;WAAiB,OAAO,CAAC,KAAK;UAAE,CAAC;SAAE;SAGlP,OAAO;UAAE,MAAM;UAAQ,MAAM;UAAS,aAAa;UAAyB,SAAS;UAAQ,SAAS;UAAU,UAAUG;UAAe,SAAS;WAAC;YAAE,OAAO;YAAQ,OAAO;YAAQ,MAAM;WAAgB;WAAG;YAAE,OAAO;YAAU,OAAO;YAAU,MAAM;WAAkB;WAAG;YAAE,OAAO;YAAS,OAAO;YAAS,MAAM;WAAiB;WAAG;YAAE,OAAO;YAAW,OAAO;YAAW,MAAM;WAAmB;UAAC;SAAE;QAC3Y;OACF;MACF;KACF;KACA,sBAAsB;MAAE,MAAM;MAAQ,MAAM;MAAY,aAAa;MAA6C,SAAS;MAAO,SAAS;MAAU,SAAS;OAAC;QAAE,OAAO;QAAO,OAAO;QAAO,MAAM;OAAe;OAAG;QAAE,OAAO;QAAU,OAAO;QAAU,MAAM;OAAkB;OAAG;QAAE,OAAO;QAAU,OAAO;QAAU,MAAM;OAAkB;MAAC;KAAE;IACrV;GACF,EACF,IACA,CAAC;EACP;CACF;AACF;AAQA,IAAaS,IACXC,EAAgB,KAAK,OAAU;CAC7B;CACA,QAAQH,EAAY,EAAE,MAAMI,MAAS,OAAO,CAAC;AAC/C,EAAE,GAQSC,IACXC,EAAoB,KAAK,OAAO;CAC9B,MAAM,EAAE;CACR,QAAQN,EAAY,EAAE,MAAM,EAAE,SAAS,OAAO,CAAC;AACjD,EAAE"}
|
package/dist/canvas/pickWalk.js
CHANGED
|
@@ -65,10 +65,10 @@ function f(e) {
|
|
|
65
65
|
function p(e, o = {}) {
|
|
66
66
|
let s = f(e.layers), l = d(e), { getPose: u, alphaOf: p, layerIsPainted: h } = o, g = null, _ = typeof e.overrides?.ids == "function";
|
|
67
67
|
if (!p && _) for (let t of e.overrides.ids()) {
|
|
68
|
-
let n = e.overrides.
|
|
68
|
+
let n = e.overrides.read(t)?.alpha;
|
|
69
69
|
n !== void 0 && n <= 0 && (g ??= /* @__PURE__ */ new Set()).add(t);
|
|
70
70
|
}
|
|
71
|
-
let v = p ?? (_ ? g ? (e) => +!g.has(e) : void 0 : (t) => e.overrides.
|
|
71
|
+
let v = p ?? (_ ? g ? (e) => +!g.has(e) : void 0 : (t) => e.overrides.read(a(t))?.alpha ?? 1), y = o.poseComposition, b = y !== void 0 && y.closure !== "identity" ? {
|
|
72
72
|
getPose: (t) => {
|
|
73
73
|
let n = e.get(a(t));
|
|
74
74
|
return n ? i(e, n) : void 0;
|