@pixerate/flair 0.23.0 → 0.25.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +124 -6
  3. package/dist/catalog/index.d.ts +2 -2
  4. package/dist/catalog/index.js +2 -2
  5. package/dist/chunk-DBTLCXKY.js +332 -0
  6. package/dist/chunk-DBTLCXKY.js.map +1 -0
  7. package/dist/{chunk-VP6IEWIK.js → chunk-H6L67KHI.js} +1 -1
  8. package/dist/chunk-H6VDFBIO.js +498 -0
  9. package/dist/chunk-H6VDFBIO.js.map +1 -0
  10. package/dist/chunk-JL6TI6II.js +724 -0
  11. package/dist/chunk-JL6TI6II.js.map +1 -0
  12. package/dist/{chunk-NGKNI3QX.js → chunk-O44ZJTGG.js} +5 -485
  13. package/dist/chunk-O44ZJTGG.js.map +1 -0
  14. package/dist/chunk-OKYMKSXK.js +414 -0
  15. package/dist/chunk-OKYMKSXK.js.map +1 -0
  16. package/dist/{chunk-OHW7UG6D.js → chunk-ORAFCHX3.js} +26 -2
  17. package/dist/{chunk-OHW7UG6D.js.map → chunk-ORAFCHX3.js.map} +1 -1
  18. package/dist/{chunk-4F4KL4KU.js → chunk-PYDN5G2P.js} +769 -11
  19. package/dist/chunk-PYDN5G2P.js.map +1 -0
  20. package/dist/{chunk-ARKC7LDW.js → chunk-RDU63CDO.js} +2 -331
  21. package/dist/chunk-RDU63CDO.js.map +1 -0
  22. package/dist/chunk-S2RGHHKZ.js +902 -0
  23. package/dist/chunk-S2RGHHKZ.js.map +1 -0
  24. package/dist/{chunk-LVPIIWPP.js → chunk-STKEKXCK.js} +18 -8
  25. package/dist/chunk-STKEKXCK.js.map +1 -0
  26. package/dist/chunk-YBGRXVAE.js +407 -0
  27. package/dist/chunk-YBGRXVAE.js.map +1 -0
  28. package/dist/cli.js +68 -5
  29. package/dist/cli.js.map +1 -1
  30. package/dist/editor/index.d.ts +2 -2
  31. package/dist/editor/index.js +14 -12
  32. package/dist/editor/index.js.map +1 -1
  33. package/dist/effect-26CPF5HS.js +47 -0
  34. package/dist/embed/index.d.ts +215 -0
  35. package/dist/embed/index.js +21 -0
  36. package/dist/embed/index.js.map +1 -0
  37. package/dist/frames-VEcqa40g.d.ts +161 -0
  38. package/dist/index.d.ts +6 -163
  39. package/dist/index.js +86 -78
  40. package/dist/index.js.map +1 -1
  41. package/dist/node.d.ts +167 -7
  42. package/dist/node.js +25 -3
  43. package/dist/react/index.d.ts +33 -2
  44. package/dist/react/index.js +54 -9
  45. package/dist/react/index.js.map +1 -1
  46. package/dist/recorder-CDPPTJMG.js +31 -0
  47. package/dist/recorder-CDPPTJMG.js.map +1 -0
  48. package/dist/{renderer-C2P1qGzP.d.ts → renderer-teaa98RE.d.ts} +1 -1
  49. package/dist/svelte/index.d.ts +33 -4
  50. package/dist/svelte/index.js +51 -9
  51. package/dist/svelte/index.js.map +1 -1
  52. package/dist/{types-DLkbOX8d.d.ts → types-DUOMxZaq.d.ts} +1 -1
  53. package/dist/vector/index.d.ts +52 -1
  54. package/dist/vector/index.js +8 -4
  55. package/package.json +5 -1
  56. package/dist/chunk-4F4KL4KU.js.map +0 -1
  57. package/dist/chunk-ARKC7LDW.js.map +0 -1
  58. package/dist/chunk-LVPIIWPP.js.map +0 -1
  59. package/dist/chunk-NGKNI3QX.js.map +0 -1
  60. package/dist/chunk-UP6QQVRS.js +0 -331
  61. package/dist/chunk-UP6QQVRS.js.map +0 -1
  62. package/dist/recorder-HKFFHQES.js +0 -10
  63. /package/dist/{chunk-VP6IEWIK.js.map → chunk-H6L67KHI.js.map} +0 -0
  64. /package/dist/{recorder-HKFFHQES.js.map → effect-26CPF5HS.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/export/effect.ts"],"sourcesContent":["/**\n * Renders a catalog effect, applied to a subject, to a file in Node: video\n * (MP4, WebM, MOV), GIF, PNG frames, or (vector effects) an animated SVG.\n */\n\nimport { spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport {\n EFFECT_CATEGORIES,\n EFFECT_INPUTS,\n EFFECT_INTERACTIONS,\n effectControls,\n effectDefaults,\n effectInputs,\n getEffect,\n renderVectorEffect,\n type EffectCategory,\n type EffectDefinition,\n type EffectInput,\n type EffectInteraction,\n type VectorEffect,\n} from '../catalog/index.js';\nimport { subjectValues, type EffectSubject } from '../embed/index.js';\nimport { checkControlValue, parseColor, type ControlDef, type ControlValue } from '../shader/controls.js';\nimport { wrapSource } from '../shader/program.js';\nimport { svgAnimatedMarkup } from '../vector/animated.js';\nimport type { SvgMotion } from '../vector/motion.js';\nimport { svgFrameMarkup, svgViewBox } from '../vector/static.js';\nimport { svgDuration } from '../vector/svg.js';\nimport {\n ffmpegPath,\n frameStates,\n imageDataUrl,\n pageControls,\n planOutput,\n runRender,\n type FrameState,\n type PointerMode,\n} from './recorder.js';\n\n/**\n * What an effect is applied to, in Node:\n * - `{ image }`: a picture's file path or URL (or a `data:` URL).\n * - `{ svg }`: an SVG's file path, URL, or markup (`\"<svg …>\"`).\n * - `{ text }`: words, for a vector effect's text, or drawn with `textImage` for a shader's text or mask.\n * - `{ path }`: SVG path data (`d`), for a shape control.\n * - `{ video }`: a video file's path, decoded to frames with ffmpeg and shown one per frame.\n */\nexport type NodeSubject =\n | { image: string }\n | { svg: string }\n | { text: string; font?: string; weight?: number | string }\n | { path: string }\n | { video: string };\n\nexport interface EffectExportOptions {\n /** What to apply it to; several fill several controls in order (a transition's from and to). */\n subject?: NodeSubject | readonly NodeSubject[];\n /** A preset by name, under `values`. */\n preset?: string;\n /** Control values over the defaults (and the subject). An image control also takes a file path. */\n values?: Record<string, ControlValue>;\n /** .mp4, .webm, .mov, .gif, .svg (vector effects), or PNG frames (a folder, or `out_%04d.png`). */\n outputPath: string;\n /** Pixels (default: 1280 × 720 for effects that show a picture, 720 × 720 for other shaders, a vector's own shape 720 px on its long edge). */\n width?: number;\n height?: number;\n fps?: number;\n /**\n * Seconds. Default: a shader's loop (when it loops in 12 s or less), a\n * video subject's length (up to 10 s), else 3; a vector effect's one\n * play-through (with its trigger acted out).\n */\n duration?: number;\n /** Keep transparency (WebM, MOV, GIF, PNG and SVG; MP4 is always opaque). */\n alpha?: boolean;\n /** CSS colour under transparent pixels without `alpha` (default black for shaders, white for vector effects). */\n background?: string;\n /**\n * The pointer, for effects that answer it: `auto` (the default for them),\n * a scripted pointer that hovers, wanders and clicks; `none` leaves it\n * still. Vector effects with a trigger have it acted out (clicked through\n * their steps, or hovered and let go).\n */\n pointer?: PointerMode;\n onProgress?: (frame: number, total: number) => void;\n}\n\nconst isUrl = (s: string) => /^(https?|data):/i.test(s);\n\n/** An SVG subject as markup: given markup stays, a URL is fetched, a path read. */\nasync function svgMarkup(svg: string): Promise<string> {\n const text = svg.trim();\n if (text.startsWith('<')) return text;\n if (/^data:image\\/svg\\+xml/i.test(text)) {\n const comma = text.indexOf(',');\n const meta = text.slice(0, comma);\n const body = text.slice(comma + 1);\n return /;base64/i.test(meta) ? Buffer.from(body, 'base64').toString('utf8') : decodeURIComponent(body);\n }\n if (/^https?:/i.test(text)) {\n const res = await fetch(text);\n if (!res.ok) throw new Error(`Could not download the SVG at ${text} (HTTP ${res.status}).`);\n return res.text();\n }\n const file = path.resolve(text);\n if (!fs.existsSync(file)) throw new Error(`There is no SVG at ${file}; give a file path, a URL, or markup starting with \"<svg\".`);\n return fs.readFileSync(file, 'utf8');\n}\n\n/** What a Node subject becomes: control values, and video files by image control. */\nexport interface NodeSubjectValues {\n values: Record<string, ControlValue>;\n /** Image control → video file. */\n video: Record<string, string>;\n}\n\n/**\n * The control values that put Node subjects in `effect` (see\n * `subjectValues` in `@pixerate/flair/embed`, which picks the controls):\n * image files and URLs become `data:` URLs, SVG files and URLs are read as\n * markup, and a video is kept as its file for its control, to be decoded to\n * frames. Throws a sentence when the effect takes no such thing.\n */\nexport async function nodeSubjectValues(effect: EffectDefinition, subject: NodeSubject | readonly NodeSubject[]): Promise<NodeSubjectValues> {\n const list = (Array.isArray(subject) ? subject : [subject]) as readonly NodeSubject[];\n const browser: EffectSubject[] = [];\n const videoFiles: string[] = [];\n for (const s of list) {\n if ('image' in s) browser.push({ image: await imageDataUrl(s.image) });\n else if ('svg' in s) browser.push({ svg: await svgMarkup(s.svg) });\n else if ('video' in s) {\n const file = path.resolve(s.video);\n if (!isUrl(s.video) && !fs.existsSync(file)) throw new Error(`There is no video at ${file}.`);\n videoFiles.push(isUrl(s.video) ? s.video : file);\n // A stand-in: subjectValues only needs to know it is a video to pick its control.\n browser.push({ video: { readyState: 0, currentTime: 0 } as unknown as HTMLVideoElement });\n } else browser.push(s);\n }\n const { values, live } = subjectValues(effect, browser);\n const video: Record<string, string> = {};\n Object.keys(live).forEach((name, i) => (video[name] = videoFiles[i]!));\n return { values, video };\n}\n\n/** The effect for an id (or the definition itself); a sentence when there is none. */\nexport function resolveEffect(effect: string | EffectDefinition): EffectDefinition {\n if (typeof effect !== 'string') return effect;\n const def = getEffect(effect);\n if (!def) throw new Error(`There is no effect \"${effect}\" in the catalog; \"flair effects\" (or findEffects()) lists them.`);\n return def;\n}\n\n/**\n * Checks control values against an effect's controls: unknown names, and\n * values a control cannot take, are sentences. Image controls take file\n * paths too (read into `data:` URLs).\n */\nexport async function checkEffectValues(effect: EffectDefinition, values: Record<string, ControlValue>): Promise<Record<string, ControlValue>> {\n const controls = new Map(effectControls(effect).map((c) => [c.name, c]));\n const out: Record<string, ControlValue> = {};\n for (const [name, raw] of Object.entries(values)) {\n const control = controls.get(name);\n if (!control) throw new Error(`\"${effect.id}\" has no control \"${name}\"; it has ${[...controls.keys()].join(', ') || 'none'}.`);\n if (control.type === 'sampler2D' && typeof raw === 'string') {\n out[name] = await imageDataUrl(raw);\n continue;\n }\n if (effect.kind === 'vector' && control.color && typeof raw === 'string' && raw.trim().toLowerCase() === 'currentcolor') {\n out[name] = raw;\n continue;\n }\n const check = checkControlValue(control, raw);\n if (!check.ok) throw new Error(check.message);\n out[name] = check.value;\n }\n return out;\n}\n\n/** How a vector effect plays over time in a recording: the frame at each ms, and its loop. */\nexport interface VectorTimeline {\n /** One play-through, ms. */\n length: number;\n /** Step start times (ms) for the whole loop, for an animated SVG. */\n steps: number[];\n /** The picture's time and steps at `ms` into the recording. */\n at(ms: number): { time: number; steps: number[] };\n}\n\n/** Ms an effect rests before its trigger is acted out. */\nconst REST = 300;\n\n/**\n * A vector effect's timeline: a loop plays and rests `hold` ms; a hover or\n * press trigger plays to the end, rests, and plays back; a click trigger is\n * clicked through each of its steps (or, with none, clicked again after\n * each play-through).\n */\nexport function vectorTimeline(effect: Pick<VectorEffect, 'hold' | 'trigger'>, motion: SvgMotion): VectorTimeline {\n const hold = effect.hold ?? 600;\n const d = svgDuration(motion);\n if (effect.trigger === 'hover' || effect.trigger === 'press') {\n const length = REST + d + hold + d;\n return {\n length,\n steps: [],\n at(ms) {\n const p = ms % length;\n const time = p < REST ? 0 : p < REST + d ? p - REST : p < REST + d + hold ? d : Math.max(0, d - (p - REST - d - hold));\n return { time, steps: [] };\n },\n };\n }\n if (effect.trigger === 'click') {\n const anims = motion.animate ?? [];\n const last = anims.reduce((n, a) => Math.max(n, a.step ?? 0), 0);\n if (last === 0) {\n const length = REST + d + hold;\n return { length, steps: [], at: (ms) => ({ time: Math.max(0, (ms % length) - REST), steps: [] }) };\n }\n // One click per step, each when the step before has played and rested.\n const clicks: number[] = [];\n let at = REST;\n for (let step = 1; step <= last; step++) {\n clicks.push(at);\n const ends = anims.filter((a) => (a.step ?? 0) === step).map((a) => (a.delay ?? 0) + (a.ms ?? 800));\n at += Math.max(0, ...ends) + hold;\n }\n const length = at;\n return {\n length,\n steps: clicks,\n at(ms) {\n const p = ms % length;\n return { time: p, steps: clicks.filter((c) => c <= p) };\n },\n };\n }\n const length = d + hold;\n return { length, steps: [], at: (ms) => ({ time: ms % length, steps: [] }) };\n}\n\nconst even = (n: number) => Math.max(2, Math.round(n / 2) * 2);\n\n/** Default pixel size for an effect (see {@link EffectExportOptions.width}). */\nexport function defaultSize(effect: EffectDefinition, motion?: SvgMotion): { width: number; height: number } {\n if (effect.kind === 'vector') {\n const box = svgViewBox((motion ?? effect.motion).svg);\n const aspect = box && box[2] > 0 && box[3] > 0 ? box[2] / box[3] : 1;\n return aspect >= 1 ? { width: 720, height: even(720 / aspect) } : { width: even(720 * aspect), height: 720 };\n }\n return effectInputs(effect).images.length ? { width: 1280, height: 720 } : { width: 720, height: 720 };\n}\n\nfunction run(bin: string, args: string[]): Promise<{ code: number; stderr: string }> {\n return new Promise((resolve, reject) => {\n const proc = spawn(bin, args, { stdio: ['ignore', 'ignore', 'pipe'] });\n let stderr = '';\n proc.stderr!.on('data', (d) => (stderr += d.toString()));\n proc.on('error', (err: NodeJS.ErrnoException) =>\n reject(err.code === 'ENOENT' ? new Error('A video subject needs ffmpeg to decode it, and it is not installed (or not on the PATH); install it, or set FFMPEG_PATH.') : err),\n );\n proc.on('close', (code) => resolve({ code: code ?? 1, stderr }));\n });\n}\n\n/** A video's length in seconds, from ffmpeg's report (undefined when it does not say). */\nexport async function videoSeconds(file: string): Promise<number | undefined> {\n const { stderr } = await run(ffmpegPath(), ['-hide_banner', '-i', file]);\n const m = /Duration:\\s*(\\d+):(\\d+):(\\d+(?:\\.\\d+)?)/.exec(stderr);\n return m ? Number(m[1]) * 3600 + Number(m[2]) * 60 + Number(m[3]) : undefined;\n}\n\n/**\n * Decodes `frames` frames of a video at `fps` into PNGs in `dir` (looping a\n * short video), at most 2048 px on the long edge. Returns their paths.\n */\nexport async function extractVideoFrames(file: string, fps: number, frames: number, dir: string): Promise<string[]> {\n fs.mkdirSync(dir, { recursive: true });\n const { code, stderr } = await run(ffmpegPath(), [\n '-hide_banner', '-loglevel', 'error', '-y',\n '-stream_loop', '-1', '-i', file,\n '-vf', `fps=${fps},scale='if(gte(iw,ih),min(2048,iw),-2)':'if(gte(iw,ih),-2,min(2048,ih))'`,\n '-frames:v', String(frames), '-start_number', '0',\n path.join(dir, '%06d.png'),\n ]);\n const files = fs.readdirSync(dir).filter((f) => f.endsWith('.png')).sort().map((f) => path.join(dir, f));\n if (code !== 0 || !files.length) throw new Error(`ffmpeg could not decode the video at ${file}: ${stderr.trim() || `exit ${code}`}`);\n return files;\n}\n\n/**\n * Renders a catalog effect (by id or definition), applied to `subject`, to\n * a file: a shader through WebGL2 in headless Chrome, a vector effect by\n * drawing each frame's SVG there, or (to `.svg`) a vector effect as one\n * animated SVG with no browser at all. MP4, WebM, MOV and video subjects\n * need ffmpeg. Throws a sentence when the effect is unknown, takes no such\n * subject, or cannot be written in that format.\n *\n * ```ts\n * await exportEffect(\"image-duotone\", { subject: { image: \"team.jpg\" }, preset: \"Spotify\", outputPath: \"team.mp4\" });\n * await exportEffect(\"click-ripple\", { alpha: true, outputPath: \"ripple.webm\" });\n * await exportEffect(\"typewriter\", { subject: { text: \"Hello\" }, outputPath: \"hello.gif\" });\n * await exportEffect(\"toggle-switch\", { outputPath: \"toggle.svg\" });\n * ```\n */\nexport async function exportEffect(effect: string | EffectDefinition, options: EffectExportOptions): Promise<string> {\n const def = resolveEffect(effect);\n if (options.preset && !def.presets?.some((p) => p.name === options.preset)) {\n const names = def.presets?.map((p) => `\"${p.name}\"`).join(', ');\n throw new Error(`\"${def.id}\" has no preset \"${options.preset}\"; ${names ? `its presets are ${names}` : 'it has no presets'}.`);\n }\n const fromSubject = options.subject ? await nodeSubjectValues(def, options.subject) : { values: {}, video: {} };\n const explicit = await checkEffectValues(def, options.values ?? {});\n const values = { ...effectDefaults(def, options.preset ? { preset: options.preset } : {}), ...fromSubject.values, ...explicit };\n const fps = options.fps ?? 30;\n const background = options.background ?? (def.kind === 'vector' ? '#ffffff' : '#000000');\n\n if (def.kind === 'vector') {\n const motion = renderVectorEffect(def, values);\n const timeline = vectorTimeline(def, motion);\n const duration = options.duration ?? timeline.length / 1000;\n const frames = Math.max(1, Math.round(duration * fps));\n const plan = planOutput(options.outputPath, { alpha: options.alpha, frames });\n if (plan.format === 'svg') {\n const markup = svgAnimatedMarkup(motion, {\n ...(def.hold !== undefined ? { hold: def.hold } : {}),\n ...(timeline.steps.length ? { steps: timeline.steps, duration: timeline.length } : {}),\n ...(options.duration !== undefined ? { duration: options.duration * 1000 } : {}),\n fps,\n });\n if (!markup) throw new Error(`\"${def.id}\" has no <svg> to write.`);\n // A file gets a size of its own (it fills its container otherwise); a background only when asked for.\n const size = defaultSize(def, motion);\n const w = Math.round(options.width ?? size.width);\n const h = Math.round(options.height ?? (options.width ? (options.width * size.height) / size.width : size.height));\n const style = !plan.alpha && options.background ? ` style=\"background:${options.background.replace(/[\"<>&]/g, '')}\"` : '';\n const out = markup.replace(/^<svg\\b[^>]*>/, (open) =>\n open.replace(/\\swidth=\"100%\"/, ` width=\"${w}\"`).replace(/\\sheight=\"100%\"/, ` height=\"${h}\"`).replace(/>$/, `${style}>`),\n );\n fs.mkdirSync(path.dirname(plan.path), { recursive: true });\n fs.writeFileSync(plan.path, out, 'utf8');\n options.onProgress?.(1, 1);\n return plan.path;\n }\n const size = defaultSize(def, motion);\n const width = Math.max(1, Math.round(options.width ?? size.width));\n const height = Math.max(1, Math.round(options.height ?? (options.width ? (options.width * size.height) / size.width : size.height)));\n const base = frameStates(frames, fps, width, height, 'none');\n const pointer = options.pointer ?? 'auto';\n const state = (i: number): FrameState => {\n const ms = (i / fps) * 1000;\n const { time, steps } = pointer === 'none' && def.trigger ? { time: 0, steps: [] } : timeline.at(ms);\n return { ...base[i]!, svg: svgFrameMarkup(motion, { at: time, steps })! };\n };\n return runRender({\n page: { width, height, frames, alpha: plan.alpha, background, send: plan.format === 'gif' ? 'rgba' : 'png' },\n state,\n plan,\n fps,\n ...(options.onProgress ? { onProgress: options.onProgress } : {}),\n });\n }\n\n // A shader.\n const videoEntries = Object.entries(fromSubject.video);\n let duration = options.duration;\n if (duration === undefined && videoEntries.length) {\n const seconds = await videoSeconds(videoEntries[0]![1]);\n duration = seconds ? Math.min(10, seconds) : undefined;\n }\n duration ??= def.loop && def.loop <= 12 ? def.loop : 3;\n const frames = Math.max(1, Math.round(duration * fps));\n const plan = planOutput(options.outputPath, { alpha: options.alpha, frames });\n if (plan.format === 'svg') {\n throw new Error(`\"${def.id}\" is a shader effect, which draws pixels; write .mp4, .webm, .mov, .gif or .png frames (only vector effects become SVG).`);\n }\n const size = defaultSize(def);\n const width = Math.max(1, Math.round(options.width ?? size.width));\n const height = Math.max(1, Math.round(options.height ?? (options.width ? (options.width * size.height) / size.width : size.height)));\n const interactive = effectInputs(def).interactions.length > 0;\n const states = frameStates(frames, fps, width, height, options.pointer ?? (interactive ? 'auto' : 'none'));\n\n const tmp = videoEntries.length ? fs.mkdtempSync(path.join(os.tmpdir(), 'flair-video-')) : undefined;\n try {\n const videoFrames: Record<string, string[]> = {};\n for (const [name, file] of videoEntries) videoFrames[name] = await extractVideoFrames(file, fps, frames, path.join(tmp!, name));\n const state = (i: number): FrameState => {\n if (!videoEntries.length) return states[i]!;\n const video = Object.fromEntries(Object.entries(videoFrames).map(([name, files]) => [name, files[Math.min(i, files.length - 1)]!]));\n return { ...states[i]!, video };\n };\n return await runRender({\n page: {\n width,\n height,\n frames,\n alpha: plan.alpha,\n background,\n send: plan.format === 'gif' ? 'rgba' : 'png',\n shader: {\n fragment: wrapSource(def.source),\n controls: await pageControls(def.source, values),\n opaque: !plan.alpha && !def.transparent,\n },\n },\n state,\n plan,\n fps,\n ...(options.onProgress ? { onProgress: options.onProgress } : {}),\n });\n } finally {\n if (tmp) fs.rmSync(tmp, { recursive: true, force: true });\n }\n}\n\n// ==========================================\n// Command-line helpers\n// ==========================================\n\n/** `\"1280x720\"` → `{ width: 1280, height: 720 }`; a sentence for anything else. */\nexport function parseSize(size: string): { width: number; height: number } {\n const m = /^\\s*(\\d+)\\s*[x×X]\\s*(\\d+)\\s*$/.exec(size);\n if (!m || Number(m[1]) < 1 || Number(m[2]) < 1) throw new Error(`--size takes width x height in pixels, like 1280x720, not \"${size}\".`);\n return { width: Number(m[1]), height: Number(m[2]) };\n}\n\n/**\n * A `--set` value read for its control: numbers for `float` and `int`,\n * `true`/`false` (or 1/0) for `bool`, a colour (`#77C16A`, `rgb(…)`) or\n * numbers separated by commas or spaces for vectors, and the text itself\n * for images (a path or URL) and text.\n */\nexport function parseSetValue(control: ControlDef, raw: string): ControlValue {\n const text = raw.trim();\n switch (control.type) {\n case 'float':\n case 'int': {\n const n = Number(text);\n if (text === '' || !Number.isFinite(n)) throw new Error(`${control.name} takes a number, not \"${raw}\".`);\n return n;\n }\n case 'bool': {\n if (/^(true|1|yes|on)$/i.test(text)) return true;\n if (/^(false|0|no|off)$/i.test(text)) return false;\n throw new Error(`${control.name} takes true or false, not \"${raw}\".`);\n }\n case 'sampler2D':\n case 'text':\n return raw;\n default: {\n if (control.color && (parseColor(text) || /^currentcolor$/i.test(text)) && !/^[-\\d.]+([\\s,]+[-\\d.]+)*$/.test(text)) return text;\n const parts = text.split(/[\\s,]+/).filter(Boolean).map(Number);\n if (!parts.length || parts.some((n) => !Number.isFinite(n))) {\n throw new Error(`${control.name} takes ${control.type === 'vec2' ? 2 : control.type === 'vec3' ? 3 : 4} numbers separated by commas${control.color ? ', or a colour like \"#77C16A\"' : ''}, not \"${raw}\".`);\n }\n return parts;\n }\n }\n}\n\n/** `--set name=value` flags as control values for an effect; sentences for unknown names and bad values. */\nexport function parseSets(effect: EffectDefinition, sets: readonly string[]): Record<string, ControlValue> {\n const controls = new Map(effectControls(effect).map((c) => [c.name, c]));\n const out: Record<string, ControlValue> = {};\n for (const set of sets) {\n const eq = set.indexOf('=');\n if (eq <= 0) throw new Error(`--set takes name=value, like --set uSpeed=2, not \"${set}\".`);\n const name = set.slice(0, eq).trim();\n const control = controls.get(name);\n if (!control) throw new Error(`\"${effect.id}\" has no control \"${name}\"; it has ${[...controls.keys()].join(', ') || 'none'}.`);\n out[name] = parseSetValue(control, set.slice(eq + 1));\n }\n return out;\n}\n\n/** The render command's subject flags. */\nexport interface SubjectFlags {\n image?: readonly string[];\n svg?: readonly string[];\n text?: readonly string[];\n path?: readonly string[];\n video?: readonly string[];\n font?: string;\n weight?: string;\n}\n\n/** Subjects from the render command's flags, in the order images, SVGs, videos, texts, paths. */\nexport function subjectsFromFlags(flags: SubjectFlags): NodeSubject[] {\n const weight = flags.weight === undefined ? undefined : /^\\d+$/.test(flags.weight) ? Number(flags.weight) : flags.weight;\n return [\n ...(flags.image ?? []).map((image) => ({ image })),\n ...(flags.svg ?? []).map((svg) => ({ svg })),\n ...(flags.video ?? []).map((video) => ({ video })),\n ...(flags.text ?? []).map((text) => ({ text, ...(flags.font ? { font: flags.font } : {}), ...(weight !== undefined ? { weight } : {}) })),\n ...(flags.path ?? []).map((p) => ({ path: p })),\n ];\n}\n\n/** Comma-separated (or repeated) flag values checked against a list; a sentence for the rest. */\nexport function parseChoices<T extends string>(flag: string, values: readonly string[] | undefined, allowed: readonly T[]): T[] | undefined {\n if (!values?.length) return undefined;\n const list = values.flatMap((v) => v.split(',')).map((v) => v.trim()).filter(Boolean);\n const bad = list.filter((v) => !(allowed as readonly string[]).includes(v));\n if (bad.length) throw new Error(`${flag} takes ${allowed.join(', ')}; not ${bad.map((b) => `\"${b}\"`).join(', ')}.`);\n return list as T[];\n}\n\n/** The effects command's filters, checked. */\nexport function effectQueryFromFlags(flags: { accepts?: readonly string[]; interaction?: readonly string[]; category?: string; kind?: string; search?: string; transparent?: boolean }) {\n const accepts = parseChoices<EffectInput>('--accepts', flags.accepts, EFFECT_INPUTS);\n const interaction = parseChoices<EffectInteraction>('--interaction', flags.interaction, EFFECT_INTERACTIONS);\n if (flags.category && !(EFFECT_CATEGORIES as readonly string[]).includes(flags.category)) {\n throw new Error(`--category takes one of ${EFFECT_CATEGORIES.join(', ')}; not \"${flags.category}\".`);\n }\n if (flags.kind && flags.kind !== 'shader' && flags.kind !== 'vector') throw new Error(`--kind takes shader or vector; not \"${flags.kind}\".`);\n return {\n ...(accepts ? { accepts } : {}),\n ...(interaction ? { interaction } : {}),\n ...(flags.category ? { category: flags.category as EffectCategory } : {}),\n ...(flags.kind ? { kind: flags.kind as 'shader' | 'vector' } : {}),\n ...(flags.search ? { text: flags.search } : {}),\n ...(flags.transparent !== undefined ? { transparent: flags.transparent } : {}),\n };\n}\n\n/** One line about an effect for the effects command: id, kind, category, and what it takes. */\nexport function describeEffect(effect: EffectDefinition): string {\n const inputs = effectInputs(effect);\n const parts = [\n `${effect.kind}/${effect.category}`,\n `takes ${inputs.accepts.join(', ') || 'nothing'}`,\n ...(inputs.video ? [`video: ${inputs.video}`] : []),\n ...(inputs.interactions.length ? [`answers ${inputs.interactions.join(', ')}`] : []),\n ...(inputs.transparent ? ['overlay'] : []),\n ...(effect.presets?.length ? [`presets: ${effect.presets.map((p) => p.name).join(', ')}`] : []),\n ];\n return `${effect.id.padEnd(24)} ${parts.join(' · ')}`;\n}\n"],"mappings":"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","names":["length","duration","frames","plan","size","width","height"]}
@@ -328,9 +328,33 @@ function morphPaths(from, to, options = {}) {
328
328
  const [a, b] = morphSequence([from, to], options);
329
329
  return [a, b];
330
330
  }
331
+ function pathPointAt(d, at) {
332
+ let rings;
333
+ try {
334
+ rings = flatten(d);
335
+ } catch {
336
+ return void 0;
337
+ }
338
+ const lines = [];
339
+ for (const ring of rings) {
340
+ const pts = ring.closed ? [...ring.points, ring.points[0]] : ring.points;
341
+ for (let i = 1; i < pts.length; i++)
342
+ lines.push([pts[i - 1], pts[i], Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1])]);
343
+ }
344
+ const first = rings[0]?.points[0];
345
+ if (!first) return void 0;
346
+ let left = Math.min(Math.max(at, 0), 1) * lines.reduce((sum, l) => sum + l[2], 0);
347
+ for (const [a, b, length] of lines) {
348
+ if (left <= length && length > 0) return [a[0] + (b[0] - a[0]) * left / length, a[1] + (b[1] - a[1]) * left / length];
349
+ left -= length;
350
+ }
351
+ const last = lines[lines.length - 1];
352
+ return last ? [last[1][0], last[1][1]] : [first[0], first[1]];
353
+ }
331
354
 
332
355
  export {
333
356
  morphSequence,
334
- morphPaths
357
+ morphPaths,
358
+ pathPointAt
335
359
  };
336
- //# sourceMappingURL=chunk-OHW7UG6D.js.map
360
+ //# sourceMappingURL=chunk-ORAFCHX3.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/vector/morph.ts"],"sourcesContent":["/**\n * Shape morphing: any SVG paths turned into paths with the same structure, so\n * an `attrs` change on `d` (see `SvgAnimation`) blends one into the other\n * point by point. Pure functions of their arguments, with no DOM.\n */\n\ntype Point = [number, number];\n\n/** One piece of a path: its outline as points, and whether it closes. */\ninterface Ring {\n points: Point[];\n closed: boolean;\n}\n\nexport interface MorphOptions {\n /** Points per piece of the shape (default 72): more follows curves more closely. */\n points?: number;\n /** Decimals in the numbers written (default 2). */\n decimals?: number;\n}\n\n/** Segments a curve is split into before resampling. */\nconst CURVE_STEPS = 24;\n\nconst TOKEN = /[MmLlHhVvCcSsQqTtAaZz]|-?(?:\\d+\\.?\\d*|\\.\\d+)(?:e[-+]?\\d+)?/gi;\n\n/** Arguments each command takes (repeated commands take them again). */\nconst ARGS: Record<string, number> = { m: 2, l: 2, h: 1, v: 1, c: 6, s: 4, q: 4, t: 2, a: 7, z: 0 };\n\n/**\n * The pieces of a path's `d` as outlines: lines kept, curves and arcs split\n * into short lines. Throws on a command it does not know.\n */\nfunction flatten(d: string): Ring[] {\n const stray = d.replace(TOKEN, \"\").replace(/[\\s,]+/g, \"\");\n if (stray) throw new Error(`\"${stray[0]}\" is not a path command`);\n const tokens = d.match(TOKEN) ?? [];\n const rings: Ring[] = [];\n // Typed by a cast: the helpers below assign it, which narrowing would miss.\n let ring = null as Ring | null;\n let x = 0;\n let y = 0;\n let startX = 0;\n let startY = 0;\n // The last control point, for S and T.\n let cx = 0;\n let cy = 0;\n let prev = \"\";\n let i = 0;\n let command = \"\";\n const add = (px: number, py: number) => ring!.points.push([px, py]);\n const begin = (px: number, py: number) => {\n if (ring && ring.points.length) rings.push(ring);\n ring = { points: [[px, py]], closed: false };\n };\n while (i < tokens.length) {\n if (/[a-z]/i.test(tokens[i]!)) command = tokens[i++]!;\n else if (!command) throw new Error(`\"${tokens[i]}\" needs a command (M, L, C, …) before it`);\n const lower = command.toLowerCase();\n if (!(lower in ARGS)) throw new Error(`Unknown path command \"${command}\"`);\n const rel = command !== command.toUpperCase();\n const n = ARGS[lower]!;\n const args = tokens.slice(i, i + n).map(Number);\n if (args.length < n || args.some((v) => Number.isNaN(v))) break;\n i += n;\n const ox = rel ? x : 0;\n const oy = rel ? y : 0;\n if (lower === \"z\") {\n if (ring) {\n ring.closed = true;\n rings.push(ring);\n ring = null;\n }\n x = startX;\n y = startY;\n prev = \"z\";\n // A Z takes no arguments: wait for the next command.\n command = \"\";\n continue;\n }\n if (!ring) begin(x, y);\n switch (lower) {\n case \"m\":\n x = ox + args[0]!;\n y = oy + args[1]!;\n startX = x;\n startY = y;\n begin(x, y);\n // Pairs after a moveto are linetos.\n command = rel ? \"l\" : \"L\";\n break;\n case \"l\":\n x = ox + args[0]!;\n y = oy + args[1]!;\n add(x, y);\n break;\n case \"h\":\n x = ox + args[0]!;\n add(x, y);\n break;\n case \"v\":\n y = (rel ? y : 0) + args[0]!;\n add(x, y);\n break;\n case \"c\":\n case \"s\": {\n let x1: number;\n let y1: number;\n let rest: number[];\n if (lower === \"c\") {\n x1 = ox + args[0]!;\n y1 = oy + args[1]!;\n rest = args.slice(2);\n } else {\n const smooth = prev === \"c\" || prev === \"s\";\n x1 = smooth ? 2 * x - cx : x;\n y1 = smooth ? 2 * y - cy : y;\n rest = args;\n }\n const x2 = ox + rest[0]!;\n const y2 = oy + rest[1]!;\n const ex = ox + rest[2]!;\n const ey = oy + rest[3]!;\n for (let k = 1; k <= CURVE_STEPS; k++) {\n const t = k / CURVE_STEPS;\n const u = 1 - t;\n add(\n u * u * u * x + 3 * u * u * t * x1 + 3 * u * t * t * x2 + t * t * t * ex,\n u * u * u * y + 3 * u * u * t * y1 + 3 * u * t * t * y2 + t * t * t * ey,\n );\n }\n cx = x2;\n cy = y2;\n x = ex;\n y = ey;\n break;\n }\n case \"q\":\n case \"t\": {\n let x1: number;\n let y1: number;\n let ex: number;\n let ey: number;\n if (lower === \"q\") {\n x1 = ox + args[0]!;\n y1 = oy + args[1]!;\n ex = ox + args[2]!;\n ey = oy + args[3]!;\n } else {\n const smooth = prev === \"q\" || prev === \"t\";\n x1 = smooth ? 2 * x - cx : x;\n y1 = smooth ? 2 * y - cy : y;\n ex = ox + args[0]!;\n ey = oy + args[1]!;\n }\n for (let k = 1; k <= CURVE_STEPS; k++) {\n const t = k / CURVE_STEPS;\n const u = 1 - t;\n add(u * u * x + 2 * u * t * x1 + t * t * ex, u * u * y + 2 * u * t * y1 + t * t * ey);\n }\n cx = x1;\n cy = y1;\n x = ex;\n y = ey;\n break;\n }\n case \"a\": {\n const ex = ox + args[5]!;\n const ey = oy + args[6]!;\n for (const p of arcPoints(x, y, args[0]!, args[1]!, args[2]!, args[3]! !== 0, args[4]! !== 0, ex, ey)) add(p[0], p[1]);\n x = ex;\n y = ey;\n break;\n }\n }\n prev = lower;\n }\n if (ring && ring.points.length) rings.push(ring);\n return rings\n .map((r) => {\n // A ring that ends where it starts is closed, Z or not.\n const [a, b] = [r.points[0]!, r.points[r.points.length - 1]!];\n const closes = r.points.length > 2 && Math.hypot(a[0] - b[0], a[1] - b[1]) < 1e-6;\n return { points: closes ? r.points.slice(0, -1) : r.points, closed: r.closed || closes };\n })\n .filter((r) => r.points.length > 1);\n}\n\n/** Points along an SVG arc (endpoint form), not including its start. */\nfunction arcPoints(\n x1: number,\n y1: number,\n rx: number,\n ry: number,\n angle: number,\n large: boolean,\n sweep: boolean,\n x2: number,\n y2: number,\n): Point[] {\n rx = Math.abs(rx);\n ry = Math.abs(ry);\n if (!rx || !ry || (x1 === x2 && y1 === y2)) return [[x2, y2]];\n const phi = (angle * Math.PI) / 180;\n const cos = Math.cos(phi);\n const sin = Math.sin(phi);\n const dx = (x1 - x2) / 2;\n const dy = (y1 - y2) / 2;\n const x1p = cos * dx + sin * dy;\n const y1p = -sin * dx + cos * dy;\n // Radii too small to reach are scaled up, as SVG says.\n const lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry);\n if (lambda > 1) {\n rx *= Math.sqrt(lambda);\n ry *= Math.sqrt(lambda);\n }\n const num = rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p;\n const den = rx * rx * y1p * y1p + ry * ry * x1p * x1p;\n const k = (large === sweep ? -1 : 1) * Math.sqrt(Math.max(0, num / den));\n const cxp = (k * rx * y1p) / ry;\n const cyp = (-k * ry * x1p) / rx;\n const cx = cos * cxp - sin * cyp + (x1 + x2) / 2;\n const cy = sin * cxp + cos * cyp + (y1 + y2) / 2;\n const theta = (ux: number, uy: number, vx: number, vy: number) =>\n Math.atan2(ux * vy - uy * vx, ux * vx + uy * vy);\n const start = theta(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);\n let delta = theta((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry);\n if (!sweep && delta > 0) delta -= 2 * Math.PI;\n if (sweep && delta < 0) delta += 2 * Math.PI;\n const steps = Math.max(2, Math.ceil((Math.abs(delta) / (Math.PI / 2)) * (CURVE_STEPS / 2)));\n const out: Point[] = [];\n for (let s = 1; s <= steps; s++) {\n const t = start + (delta * s) / steps;\n const px = rx * Math.cos(t);\n const py = ry * Math.sin(t);\n out.push([cos * px - sin * py + cx, sin * px + cos * py + cy]);\n }\n out[out.length - 1] = [x2, y2];\n return out;\n}\n\n/**\n * `n` points evenly spaced along a ring. A closed ring goes round once; an\n * open one (a stroke) is resampled as closed by going there and back, so it\n * can turn into a closed shape.\n */\nfunction resample(ring: Ring, n: number): Point[] {\n const pts = ring.closed ? [...ring.points, ring.points[0]!] : [...ring.points, ...ring.points.slice(0, -1).reverse()];\n const lengths = [0];\n for (let i = 1; i < pts.length; i++)\n lengths.push(lengths[i - 1]! + Math.hypot(pts[i]![0] - pts[i - 1]![0], pts[i]![1] - pts[i - 1]![1]));\n const total = lengths[lengths.length - 1]!;\n if (total === 0) return Array.from({ length: n }, () => [pts[0]![0], pts[0]![1]] as Point);\n const out: Point[] = [];\n let j = 1;\n for (let k = 0; k < n; k++) {\n const at = (k / n) * total;\n while (j < lengths.length - 1 && lengths[j]! < at) j++;\n const seg = lengths[j]! - lengths[j - 1]!;\n const t = seg > 0 ? (at - lengths[j - 1]!) / seg : 0;\n const a = pts[j - 1]!;\n const b = pts[j]!;\n out.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]);\n }\n return out;\n}\n\nconst signedArea = (pts: Point[]) => {\n let sum = 0;\n for (let i = 0; i < pts.length; i++) {\n const a = pts[i]!;\n const b = pts[(i + 1) % pts.length]!;\n sum += a[0] * b[1] - b[0] * a[1];\n }\n return sum / 2;\n};\n\nconst centroid = (pts: Point[]): Point => {\n let x = 0;\n let y = 0;\n for (const p of pts) {\n x += p[0];\n y += p[1];\n }\n return [x / pts.length, y / pts.length];\n};\n\n/** How big a piece is: its area, or for a stroke its length times a little. */\nfunction size(ring: Ring, pts: Point[]): number {\n const area = Math.abs(signedArea(pts));\n if (ring.closed && area > 0) return area;\n let length = 0;\n for (let i = 1; i < ring.points.length; i++)\n length += Math.hypot(ring.points[i]![0] - ring.points[i - 1]![0], ring.points[i]![1] - ring.points[i - 1]![1]);\n return length;\n}\n\ninterface Piece {\n pts: Point[];\n centre: Point;\n size: number;\n}\n\n/**\n * Pairs the pieces of one shape with the slots of the shape before it,\n * nearest and most alike first: a slot left over is empty (a piece that\n * shrinks away), and a piece left over takes an empty slot (it grows).\n */\nfunction assign(before: (Piece | null)[], pieces: Piece[], scale: number): (Piece | null)[] {\n const out: (Piece | null)[] = before.map(() => null);\n const pairs: { slot: number; piece: number; cost: number }[] = [];\n before.forEach((slot, s) => {\n if (!slot) return;\n pieces.forEach((p, i) => {\n const distance = Math.hypot(slot.centre[0] - p.centre[0], slot.centre[1] - p.centre[1]) / scale;\n const likeness = Math.abs(Math.log((slot.size + 1e-9) / (p.size + 1e-9)));\n pairs.push({ slot: s, piece: i, cost: distance + 0.5 * likeness });\n });\n });\n pairs.sort((a, b) => a.cost - b.cost);\n const used = new Set<number>();\n for (const { slot, piece } of pairs) {\n if (out[slot] || used.has(piece)) continue;\n out[slot] = pieces[piece]!;\n used.add(piece);\n }\n let free = 0;\n pieces.forEach((p, i) => {\n if (used.has(i)) return;\n while (out[free] || before[free]) free++;\n out[free] = p;\n });\n return out;\n}\n\n/** `pts` turned and started so it lines up with `ref` point by point (the shortest moves). */\nfunction align(ref: Point[], pts: Point[]): Point[] {\n const n = pts.length;\n let best = pts;\n let bestCost = Infinity;\n for (const candidate of [pts, [...pts].reverse()]) {\n for (let shift = 0; shift < n; shift++) {\n let cost = 0;\n for (let k = 0; k < n && cost < bestCost; k++) {\n const a = ref[k]!;\n const b = candidate[(k + shift) % n]!;\n cost += (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2;\n }\n if (cost < bestCost) {\n bestCost = cost;\n best = candidate.slice(shift).concat(candidate.slice(0, shift));\n }\n }\n }\n return best;\n}\n\n/**\n * Paths with one structure for a sequence of shapes, so each blends into the\n * next (and the last into the first) with `attrs: { d: [a, b] }`. Any path\n * commands work, curves and arcs included, and shapes can have different\n * numbers of pieces (an icon's parts, a letter's holes): pieces are paired by\n * position and size, and one without a partner grows from or shrinks to a\n * point. Draw the results with `fill-rule=\"evenodd\"`, since a piece's\n * direction can change. Throws a sentence when a path cannot be read.\n */\nexport function morphSequence(paths: readonly string[], options: MorphOptions = {}): string[] {\n const n = Math.max(8, Math.round(options.points ?? 72));\n const decimals = options.decimals ?? 2;\n const shapes = paths.map((d, i) => {\n let rings: Ring[];\n try {\n rings = flatten(d);\n } catch (e) {\n throw new Error(`Path ${i + 1} cannot be read: ${(e as Error).message}`);\n }\n if (!rings.length) throw new Error(`Path ${i + 1} draws nothing: give it at least two points`);\n return rings.map((r): Piece => {\n const pts = resample(r, n);\n return { pts, centre: centroid(pts), size: size(r, pts) };\n });\n });\n if (!shapes.length) return [];\n const slots = Math.max(...shapes.map((s) => s.length));\n // How far apart counts as far, from the shapes' extent.\n const xs = shapes.flat().flatMap((p) => p.pts.map((q) => q[0]));\n const ys = shapes.flat().flatMap((p) => p.pts.map((q) => q[1]));\n const scale = Math.max(1e-6, Math.max(...xs) - Math.min(...xs), Math.max(...ys) - Math.min(...ys));\n // Biggest pieces first, so the main outlines keep the first slots.\n const first = [...shapes[0]!].sort((a, b) => b.size - a.size);\n const assigned: (Piece | null)[][] = [[...first, ...Array<null>(slots - first.length).fill(null)]];\n for (let i = 1; i < shapes.length; i++) assigned.push(assign(assigned[i - 1]!, shapes[i]!, scale));\n // An empty slot is a point: where the piece in that slot is in the nearest shape that has one.\n const resolved = assigned.map((shape, i) =>\n shape.map((piece, s): Point[] => {\n if (piece) return piece.pts;\n for (let k = 1; k < shapes.length; k++) {\n const near = assigned[(i + k) % shapes.length]![s] ?? assigned[(i - k + shapes.length) % shapes.length]![s];\n if (near) return Array.from({ length: n }, () => [near.centre[0], near.centre[1]] as Point);\n }\n return Array.from({ length: n }, () => [0, 0] as Point);\n }),\n );\n for (let i = 1; i < resolved.length; i++)\n resolved[i] = resolved[i]!.map((pts, s) => align(resolved[i - 1]![s]!, pts));\n const num = (v: number) => {\n const r = Number(v.toFixed(decimals));\n return String(Object.is(r, -0) ? 0 : r);\n };\n return resolved.map((shape) =>\n shape.map((pts) => `M${pts.map((p) => `${num(p[0])} ${num(p[1])}`).join(\" L\")} Z`).join(\" \"),\n );\n}\n\n/**\n * Two paths turned into paths with one structure, so `attrs: { d: [from, to] }`\n * blends one into the other (see {@link morphSequence}).\n */\nexport function morphPaths(from: string, to: string, options: MorphOptions = {}): [string, string] {\n const [a, b] = morphSequence([from, to], options);\n return [a!, b!];\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/vector/morph.ts"],"sourcesContent":["/**\n * Shape morphing: any SVG paths turned into paths with the same structure, so\n * an `attrs` change on `d` (see `SvgAnimation`) blends one into the other\n * point by point. Pure functions of their arguments, with no DOM.\n */\n\ntype Point = [number, number];\n\n/** One piece of a path: its outline as points, and whether it closes. */\ninterface Ring {\n points: Point[];\n closed: boolean;\n}\n\nexport interface MorphOptions {\n /** Points per piece of the shape (default 72): more follows curves more closely. */\n points?: number;\n /** Decimals in the numbers written (default 2). */\n decimals?: number;\n}\n\n/** Segments a curve is split into before resampling. */\nconst CURVE_STEPS = 24;\n\nconst TOKEN = /[MmLlHhVvCcSsQqTtAaZz]|-?(?:\\d+\\.?\\d*|\\.\\d+)(?:e[-+]?\\d+)?/gi;\n\n/** Arguments each command takes (repeated commands take them again). */\nconst ARGS: Record<string, number> = { m: 2, l: 2, h: 1, v: 1, c: 6, s: 4, q: 4, t: 2, a: 7, z: 0 };\n\n/**\n * The pieces of a path's `d` as outlines: lines kept, curves and arcs split\n * into short lines. Throws on a command it does not know.\n */\nfunction flatten(d: string): Ring[] {\n const stray = d.replace(TOKEN, \"\").replace(/[\\s,]+/g, \"\");\n if (stray) throw new Error(`\"${stray[0]}\" is not a path command`);\n const tokens = d.match(TOKEN) ?? [];\n const rings: Ring[] = [];\n // Typed by a cast: the helpers below assign it, which narrowing would miss.\n let ring = null as Ring | null;\n let x = 0;\n let y = 0;\n let startX = 0;\n let startY = 0;\n // The last control point, for S and T.\n let cx = 0;\n let cy = 0;\n let prev = \"\";\n let i = 0;\n let command = \"\";\n const add = (px: number, py: number) => ring!.points.push([px, py]);\n const begin = (px: number, py: number) => {\n if (ring && ring.points.length) rings.push(ring);\n ring = { points: [[px, py]], closed: false };\n };\n while (i < tokens.length) {\n if (/[a-z]/i.test(tokens[i]!)) command = tokens[i++]!;\n else if (!command) throw new Error(`\"${tokens[i]}\" needs a command (M, L, C, …) before it`);\n const lower = command.toLowerCase();\n if (!(lower in ARGS)) throw new Error(`Unknown path command \"${command}\"`);\n const rel = command !== command.toUpperCase();\n const n = ARGS[lower]!;\n const args = tokens.slice(i, i + n).map(Number);\n if (args.length < n || args.some((v) => Number.isNaN(v))) break;\n i += n;\n const ox = rel ? x : 0;\n const oy = rel ? y : 0;\n if (lower === \"z\") {\n if (ring) {\n ring.closed = true;\n rings.push(ring);\n ring = null;\n }\n x = startX;\n y = startY;\n prev = \"z\";\n // A Z takes no arguments: wait for the next command.\n command = \"\";\n continue;\n }\n if (!ring) begin(x, y);\n switch (lower) {\n case \"m\":\n x = ox + args[0]!;\n y = oy + args[1]!;\n startX = x;\n startY = y;\n begin(x, y);\n // Pairs after a moveto are linetos.\n command = rel ? \"l\" : \"L\";\n break;\n case \"l\":\n x = ox + args[0]!;\n y = oy + args[1]!;\n add(x, y);\n break;\n case \"h\":\n x = ox + args[0]!;\n add(x, y);\n break;\n case \"v\":\n y = (rel ? y : 0) + args[0]!;\n add(x, y);\n break;\n case \"c\":\n case \"s\": {\n let x1: number;\n let y1: number;\n let rest: number[];\n if (lower === \"c\") {\n x1 = ox + args[0]!;\n y1 = oy + args[1]!;\n rest = args.slice(2);\n } else {\n const smooth = prev === \"c\" || prev === \"s\";\n x1 = smooth ? 2 * x - cx : x;\n y1 = smooth ? 2 * y - cy : y;\n rest = args;\n }\n const x2 = ox + rest[0]!;\n const y2 = oy + rest[1]!;\n const ex = ox + rest[2]!;\n const ey = oy + rest[3]!;\n for (let k = 1; k <= CURVE_STEPS; k++) {\n const t = k / CURVE_STEPS;\n const u = 1 - t;\n add(\n u * u * u * x + 3 * u * u * t * x1 + 3 * u * t * t * x2 + t * t * t * ex,\n u * u * u * y + 3 * u * u * t * y1 + 3 * u * t * t * y2 + t * t * t * ey,\n );\n }\n cx = x2;\n cy = y2;\n x = ex;\n y = ey;\n break;\n }\n case \"q\":\n case \"t\": {\n let x1: number;\n let y1: number;\n let ex: number;\n let ey: number;\n if (lower === \"q\") {\n x1 = ox + args[0]!;\n y1 = oy + args[1]!;\n ex = ox + args[2]!;\n ey = oy + args[3]!;\n } else {\n const smooth = prev === \"q\" || prev === \"t\";\n x1 = smooth ? 2 * x - cx : x;\n y1 = smooth ? 2 * y - cy : y;\n ex = ox + args[0]!;\n ey = oy + args[1]!;\n }\n for (let k = 1; k <= CURVE_STEPS; k++) {\n const t = k / CURVE_STEPS;\n const u = 1 - t;\n add(u * u * x + 2 * u * t * x1 + t * t * ex, u * u * y + 2 * u * t * y1 + t * t * ey);\n }\n cx = x1;\n cy = y1;\n x = ex;\n y = ey;\n break;\n }\n case \"a\": {\n const ex = ox + args[5]!;\n const ey = oy + args[6]!;\n for (const p of arcPoints(x, y, args[0]!, args[1]!, args[2]!, args[3]! !== 0, args[4]! !== 0, ex, ey)) add(p[0], p[1]);\n x = ex;\n y = ey;\n break;\n }\n }\n prev = lower;\n }\n if (ring && ring.points.length) rings.push(ring);\n return rings\n .map((r) => {\n // A ring that ends where it starts is closed, Z or not.\n const [a, b] = [r.points[0]!, r.points[r.points.length - 1]!];\n const closes = r.points.length > 2 && Math.hypot(a[0] - b[0], a[1] - b[1]) < 1e-6;\n return { points: closes ? r.points.slice(0, -1) : r.points, closed: r.closed || closes };\n })\n .filter((r) => r.points.length > 1);\n}\n\n/** Points along an SVG arc (endpoint form), not including its start. */\nfunction arcPoints(\n x1: number,\n y1: number,\n rx: number,\n ry: number,\n angle: number,\n large: boolean,\n sweep: boolean,\n x2: number,\n y2: number,\n): Point[] {\n rx = Math.abs(rx);\n ry = Math.abs(ry);\n if (!rx || !ry || (x1 === x2 && y1 === y2)) return [[x2, y2]];\n const phi = (angle * Math.PI) / 180;\n const cos = Math.cos(phi);\n const sin = Math.sin(phi);\n const dx = (x1 - x2) / 2;\n const dy = (y1 - y2) / 2;\n const x1p = cos * dx + sin * dy;\n const y1p = -sin * dx + cos * dy;\n // Radii too small to reach are scaled up, as SVG says.\n const lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry);\n if (lambda > 1) {\n rx *= Math.sqrt(lambda);\n ry *= Math.sqrt(lambda);\n }\n const num = rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p;\n const den = rx * rx * y1p * y1p + ry * ry * x1p * x1p;\n const k = (large === sweep ? -1 : 1) * Math.sqrt(Math.max(0, num / den));\n const cxp = (k * rx * y1p) / ry;\n const cyp = (-k * ry * x1p) / rx;\n const cx = cos * cxp - sin * cyp + (x1 + x2) / 2;\n const cy = sin * cxp + cos * cyp + (y1 + y2) / 2;\n const theta = (ux: number, uy: number, vx: number, vy: number) =>\n Math.atan2(ux * vy - uy * vx, ux * vx + uy * vy);\n const start = theta(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);\n let delta = theta((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry);\n if (!sweep && delta > 0) delta -= 2 * Math.PI;\n if (sweep && delta < 0) delta += 2 * Math.PI;\n const steps = Math.max(2, Math.ceil((Math.abs(delta) / (Math.PI / 2)) * (CURVE_STEPS / 2)));\n const out: Point[] = [];\n for (let s = 1; s <= steps; s++) {\n const t = start + (delta * s) / steps;\n const px = rx * Math.cos(t);\n const py = ry * Math.sin(t);\n out.push([cos * px - sin * py + cx, sin * px + cos * py + cy]);\n }\n out[out.length - 1] = [x2, y2];\n return out;\n}\n\n/**\n * `n` points evenly spaced along a ring. A closed ring goes round once; an\n * open one (a stroke) is resampled as closed by going there and back, so it\n * can turn into a closed shape.\n */\nfunction resample(ring: Ring, n: number): Point[] {\n const pts = ring.closed ? [...ring.points, ring.points[0]!] : [...ring.points, ...ring.points.slice(0, -1).reverse()];\n const lengths = [0];\n for (let i = 1; i < pts.length; i++)\n lengths.push(lengths[i - 1]! + Math.hypot(pts[i]![0] - pts[i - 1]![0], pts[i]![1] - pts[i - 1]![1]));\n const total = lengths[lengths.length - 1]!;\n if (total === 0) return Array.from({ length: n }, () => [pts[0]![0], pts[0]![1]] as Point);\n const out: Point[] = [];\n let j = 1;\n for (let k = 0; k < n; k++) {\n const at = (k / n) * total;\n while (j < lengths.length - 1 && lengths[j]! < at) j++;\n const seg = lengths[j]! - lengths[j - 1]!;\n const t = seg > 0 ? (at - lengths[j - 1]!) / seg : 0;\n const a = pts[j - 1]!;\n const b = pts[j]!;\n out.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]);\n }\n return out;\n}\n\nconst signedArea = (pts: Point[]) => {\n let sum = 0;\n for (let i = 0; i < pts.length; i++) {\n const a = pts[i]!;\n const b = pts[(i + 1) % pts.length]!;\n sum += a[0] * b[1] - b[0] * a[1];\n }\n return sum / 2;\n};\n\nconst centroid = (pts: Point[]): Point => {\n let x = 0;\n let y = 0;\n for (const p of pts) {\n x += p[0];\n y += p[1];\n }\n return [x / pts.length, y / pts.length];\n};\n\n/** How big a piece is: its area, or for a stroke its length times a little. */\nfunction size(ring: Ring, pts: Point[]): number {\n const area = Math.abs(signedArea(pts));\n if (ring.closed && area > 0) return area;\n let length = 0;\n for (let i = 1; i < ring.points.length; i++)\n length += Math.hypot(ring.points[i]![0] - ring.points[i - 1]![0], ring.points[i]![1] - ring.points[i - 1]![1]);\n return length;\n}\n\ninterface Piece {\n pts: Point[];\n centre: Point;\n size: number;\n}\n\n/**\n * Pairs the pieces of one shape with the slots of the shape before it,\n * nearest and most alike first: a slot left over is empty (a piece that\n * shrinks away), and a piece left over takes an empty slot (it grows).\n */\nfunction assign(before: (Piece | null)[], pieces: Piece[], scale: number): (Piece | null)[] {\n const out: (Piece | null)[] = before.map(() => null);\n const pairs: { slot: number; piece: number; cost: number }[] = [];\n before.forEach((slot, s) => {\n if (!slot) return;\n pieces.forEach((p, i) => {\n const distance = Math.hypot(slot.centre[0] - p.centre[0], slot.centre[1] - p.centre[1]) / scale;\n const likeness = Math.abs(Math.log((slot.size + 1e-9) / (p.size + 1e-9)));\n pairs.push({ slot: s, piece: i, cost: distance + 0.5 * likeness });\n });\n });\n pairs.sort((a, b) => a.cost - b.cost);\n const used = new Set<number>();\n for (const { slot, piece } of pairs) {\n if (out[slot] || used.has(piece)) continue;\n out[slot] = pieces[piece]!;\n used.add(piece);\n }\n let free = 0;\n pieces.forEach((p, i) => {\n if (used.has(i)) return;\n while (out[free] || before[free]) free++;\n out[free] = p;\n });\n return out;\n}\n\n/** `pts` turned and started so it lines up with `ref` point by point (the shortest moves). */\nfunction align(ref: Point[], pts: Point[]): Point[] {\n const n = pts.length;\n let best = pts;\n let bestCost = Infinity;\n for (const candidate of [pts, [...pts].reverse()]) {\n for (let shift = 0; shift < n; shift++) {\n let cost = 0;\n for (let k = 0; k < n && cost < bestCost; k++) {\n const a = ref[k]!;\n const b = candidate[(k + shift) % n]!;\n cost += (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2;\n }\n if (cost < bestCost) {\n bestCost = cost;\n best = candidate.slice(shift).concat(candidate.slice(0, shift));\n }\n }\n }\n return best;\n}\n\n/**\n * Paths with one structure for a sequence of shapes, so each blends into the\n * next (and the last into the first) with `attrs: { d: [a, b] }`. Any path\n * commands work, curves and arcs included, and shapes can have different\n * numbers of pieces (an icon's parts, a letter's holes): pieces are paired by\n * position and size, and one without a partner grows from or shrinks to a\n * point. Draw the results with `fill-rule=\"evenodd\"`, since a piece's\n * direction can change. Throws a sentence when a path cannot be read.\n */\nexport function morphSequence(paths: readonly string[], options: MorphOptions = {}): string[] {\n const n = Math.max(8, Math.round(options.points ?? 72));\n const decimals = options.decimals ?? 2;\n const shapes = paths.map((d, i) => {\n let rings: Ring[];\n try {\n rings = flatten(d);\n } catch (e) {\n throw new Error(`Path ${i + 1} cannot be read: ${(e as Error).message}`);\n }\n if (!rings.length) throw new Error(`Path ${i + 1} draws nothing: give it at least two points`);\n return rings.map((r): Piece => {\n const pts = resample(r, n);\n return { pts, centre: centroid(pts), size: size(r, pts) };\n });\n });\n if (!shapes.length) return [];\n const slots = Math.max(...shapes.map((s) => s.length));\n // How far apart counts as far, from the shapes' extent.\n const xs = shapes.flat().flatMap((p) => p.pts.map((q) => q[0]));\n const ys = shapes.flat().flatMap((p) => p.pts.map((q) => q[1]));\n const scale = Math.max(1e-6, Math.max(...xs) - Math.min(...xs), Math.max(...ys) - Math.min(...ys));\n // Biggest pieces first, so the main outlines keep the first slots.\n const first = [...shapes[0]!].sort((a, b) => b.size - a.size);\n const assigned: (Piece | null)[][] = [[...first, ...Array<null>(slots - first.length).fill(null)]];\n for (let i = 1; i < shapes.length; i++) assigned.push(assign(assigned[i - 1]!, shapes[i]!, scale));\n // An empty slot is a point: where the piece in that slot is in the nearest shape that has one.\n const resolved = assigned.map((shape, i) =>\n shape.map((piece, s): Point[] => {\n if (piece) return piece.pts;\n for (let k = 1; k < shapes.length; k++) {\n const near = assigned[(i + k) % shapes.length]![s] ?? assigned[(i - k + shapes.length) % shapes.length]![s];\n if (near) return Array.from({ length: n }, () => [near.centre[0], near.centre[1]] as Point);\n }\n return Array.from({ length: n }, () => [0, 0] as Point);\n }),\n );\n for (let i = 1; i < resolved.length; i++)\n resolved[i] = resolved[i]!.map((pts, s) => align(resolved[i - 1]![s]!, pts));\n const num = (v: number) => {\n const r = Number(v.toFixed(decimals));\n return String(Object.is(r, -0) ? 0 : r);\n };\n return resolved.map((shape) =>\n shape.map((pts) => `M${pts.map((p) => `${num(p[0])} ${num(p[1])}`).join(\" L\")} Z`).join(\" \"),\n );\n}\n\n/**\n * Two paths turned into paths with one structure, so `attrs: { d: [from, to] }`\n * blends one into the other (see {@link morphSequence}).\n */\nexport function morphPaths(from: string, to: string, options: MorphOptions = {}): [string, string] {\n const [a, b] = morphSequence([from, to], options);\n return [a!, b!];\n}\n\n/**\n * @internal The point `at` (0–1) of the way along a path's `d` by length, as\n * a browser's `getPointAtLength` finds it (moves between pieces not counted),\n * with curves split into short lines. Undefined when `d` does not parse.\n */\nexport function pathPointAt(d: string, at: number): [number, number] | undefined {\n let rings: Ring[];\n try {\n rings = flatten(d);\n } catch {\n return undefined;\n }\n const lines: [Point, Point, number][] = [];\n for (const ring of rings) {\n const pts = ring.closed ? [...ring.points, ring.points[0]!] : ring.points;\n for (let i = 1; i < pts.length; i++)\n lines.push([pts[i - 1]!, pts[i]!, Math.hypot(pts[i]![0] - pts[i - 1]![0], pts[i]![1] - pts[i - 1]![1])]);\n }\n const first = rings[0]?.points[0];\n if (!first) return undefined;\n let left = Math.min(Math.max(at, 0), 1) * lines.reduce((sum, l) => sum + l[2], 0);\n for (const [a, b, length] of lines) {\n if (left <= length && length > 0) return [a[0] + ((b[0] - a[0]) * left) / length, a[1] + ((b[1] - a[1]) * left) / length];\n left -= length;\n }\n const last = lines[lines.length - 1];\n return last ? [last[1][0], last[1][1]] : [first[0], first[1]];\n}\n"],"mappings":"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","names":[]}