@motionscript/charts 0.1.0-alpha.8 → 0.1.0-alpha.9

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 (75) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/LICENSE +201 -201
  3. package/NOTICE +49 -49
  4. package/README.md +99 -99
  5. package/dist/bar-chart/bar-chart.js.map +1 -1
  6. package/dist/bar-chart/category-axis.js.map +1 -1
  7. package/dist/bar-chart/chart-bars.js.map +1 -1
  8. package/dist/bar-chart/index.js.map +1 -1
  9. package/dist/bar-chart/shared.js.map +1 -1
  10. package/dist/browser/chunks/{chunk-XROBIRQK.js → chunk-TJNHLQGV.js} +1 -1
  11. package/dist/browser/chunks/chunk-TJNHLQGV.js.map +7 -0
  12. package/dist/browser/index.js +1 -1
  13. package/dist/browser/index.js.map +1 -1
  14. package/dist/browser/kit.js +1 -1
  15. package/dist/engine.d.ts +1 -1
  16. package/dist/engine.js +1 -1
  17. package/dist/engine.js.map +1 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/kit/cartesian.js.map +1 -1
  20. package/dist/kit/chart-border.js.map +1 -1
  21. package/dist/kit/chart-canvas.js.map +1 -1
  22. package/dist/kit/chart-legend.js.map +1 -1
  23. package/dist/kit/entries.js.map +1 -1
  24. package/dist/kit/motion.js.map +1 -1
  25. package/dist/kit/paints.js.map +1 -1
  26. package/dist/kit/shared.js.map +1 -1
  27. package/dist/kit/vertical-axis.js.map +1 -1
  28. package/dist/line-chart/chart-line.js.map +1 -1
  29. package/dist/line-chart/chart-region.js.map +1 -1
  30. package/dist/line-chart/horizontal-axis.js.map +1 -1
  31. package/dist/line-chart/index.d.ts +1 -1
  32. package/dist/line-chart/index.js +1 -1
  33. package/dist/line-chart/index.js.map +1 -1
  34. package/dist/line-chart/line-chart.js.map +1 -1
  35. package/dist/line-chart/shared.js.map +1 -1
  36. package/dist/line-chart/x-scale.js.map +1 -1
  37. package/dist/nodes.js.map +1 -1
  38. package/dist/pie-chart/index.js.map +1 -1
  39. package/dist/pie-chart/pie-chart.js.map +1 -1
  40. package/dist/pie-chart/pie-legend.js.map +1 -1
  41. package/dist/pie-chart/pie-ring.js.map +1 -1
  42. package/dist/pie-chart/shared.js.map +1 -1
  43. package/package.json +4 -4
  44. package/registry.json +23 -23
  45. package/src/bar-chart/bar-chart.ts +542 -542
  46. package/src/bar-chart/category-axis.ts +162 -162
  47. package/src/bar-chart/chart-bars.ts +287 -287
  48. package/src/bar-chart/index.ts +31 -31
  49. package/src/bar-chart/shared.ts +354 -354
  50. package/src/engine.ts +28 -28
  51. package/src/index.ts +5 -5
  52. package/src/kit/cartesian.ts +81 -81
  53. package/src/kit/chart-border.ts +50 -50
  54. package/src/kit/chart-canvas.ts +122 -122
  55. package/src/kit/chart-legend.ts +229 -229
  56. package/src/kit/entries.ts +20 -20
  57. package/src/kit/motion.ts +90 -90
  58. package/src/kit/paints.ts +15 -15
  59. package/src/kit/shared.ts +782 -782
  60. package/src/kit/vertical-axis.ts +194 -194
  61. package/src/line-chart/chart-line.ts +156 -156
  62. package/src/line-chart/chart-region.ts +90 -90
  63. package/src/line-chart/horizontal-axis.ts +151 -151
  64. package/src/line-chart/index.ts +39 -39
  65. package/src/line-chart/line-chart.ts +598 -598
  66. package/src/line-chart/shared.ts +473 -473
  67. package/src/line-chart/x-scale.ts +382 -382
  68. package/src/nodes.ts +18 -18
  69. package/src/pie-chart/index.ts +37 -37
  70. package/src/pie-chart/pie-chart.ts +296 -296
  71. package/src/pie-chart/pie-legend.ts +169 -169
  72. package/src/pie-chart/pie-ring.ts +848 -848
  73. package/src/pie-chart/shared.ts +496 -496
  74. package/dist/browser/chunks/chunk-XROBIRQK.js.map +0 -7
  75. package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"x-scale.js","sourceRoot":"","sources":["../../src/line-chart/x-scale.ts"],"names":[],"mappings":"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","sourcesContent":["/**\r\n * What a line chart's x column *means* — the one question that has to be\r\n * answered before a row can be given a position along the bottom of the plot.\r\n *\r\n * A y column is always a magnitude. An x column is one of three things, and\r\n * which one is a property of the data rather than something the author states:\r\n *\r\n * - **numeric** — `0, 5, 10` — plot the number.\r\n * - **time** — `Jan`, `2026-03`, `2026-W12`, `2026-03-14` — plot the instant,\r\n * so samples a month apart sit a month apart and a gap in the data reads as a\r\n * gap.\r\n * - **ordinal** — anything else — plot the row's position, evenly spaced.\r\n *\r\n * Before this, every x column went through `num()`, which reads a non-numeric\r\n * cell as `0`. A `Month` column of `Jan…Dec` therefore stacked all twelve\r\n * points on one x and drew a vertical line — no error, no empty chart, just a\r\n * wrong picture. That is the failure this module exists to remove: every column\r\n * now plots as *something*, and the something is the reading a person would\r\n * give it.\r\n *\r\n * The captions come back off the rows rather than out of a formatter. A tick on\r\n * a time or ordinal scale is chosen *from the data* (see {@link XScale.ticks}),\r\n * so the label it wants is the cell the author wrote — `Jan`, not `1970-01-01`,\r\n * and `2026-W12`, not the Monday it resolves to. Only a tick that is not a\r\n * sample — an author-set range, or a `zoomTo` mid-flight — needs formatting,\r\n * and that is the one case {@link formatInstant} covers.\r\n */\r\n\r\nimport type { DataRecord } from \"@motionscript/sdk\"\r\n\r\nimport {\r\n formatMarker,\r\n markerValues,\r\n num,\r\n text,\r\n type Range,\r\n} from \"../kit/shared\"\r\nimport { formatNumber } from \"@motionscript/sdk/component\"\r\n\r\n/** How an x column's cells become positions along the axis. */\r\nexport type XScaleKind = \"numeric\" | \"time\" | \"ordinal\"\r\n\r\n/**\r\n * An x column resolved into positions, captions and ticks.\r\n *\r\n * The three kinds differ in all three answers, so they are three closures built\r\n * once per (data, column) rather than a kind tag every caller switches on.\r\n */\r\nexport interface XScale {\r\n kind: XScaleKind\r\n /** Position of row `index` along the axis, in this scale's own units. */\r\n at(index: number): number\r\n /** `[min, max]` across the rows — the scale when no range is set. */\r\n extent: Range\r\n /** The caption for a tick at `value`. */\r\n label(value: number): string\r\n /** `divisions + 1` tick values spanning `range`, ends included. */\r\n ticks(range: Range, divisions: number): number[]\r\n}\r\n\r\n// ---- Time parsing --------------------------------------------------------\r\n\r\nconst DAY = 86_400_000\r\n\r\nconst MONTHS = [\r\n \"january\",\r\n \"february\",\r\n \"march\",\r\n \"april\",\r\n \"may\",\r\n \"june\",\r\n \"july\",\r\n \"august\",\r\n \"september\",\r\n \"october\",\r\n \"november\",\r\n \"december\",\r\n]\r\n\r\n/**\r\n * The year a cell that names no year is dated into.\r\n *\r\n * Any year would place `Jan…Dec` in the right order at the right spacing; a\r\n * leap year is the one that also spaces February correctly against a column\r\n * that *does* carry years, so the two read alike.\r\n */\r\nconst NOMINAL_YEAR = 2024\r\n\r\n/**\r\n * Month index for any unambiguous abbreviation of a month name — `jan`, `sept`,\r\n * `january` — or -1.\r\n *\r\n * Three letters minimum, and then a prefix match, which is what lets `sept`\r\n * through without letting `janitor` through: a longer word has to still *be*\r\n * the month.\r\n */\r\nfunction monthIndex(word: string): number {\r\n if (word.length < 3) return -1\r\n return MONTHS.findIndex((month) => month.startsWith(word))\r\n}\r\n\r\n/** The Monday of ISO week `week` in `year`, as an epoch timestamp. */\r\nfunction isoWeekStart(year: number, week: number): number {\r\n // ISO week 1 is the week containing 4 January, and weeks start on Monday.\r\n const jan4 = Date.UTC(year, 0, 4)\r\n const weekday = (new Date(jan4).getUTCDay() + 6) % 7 // Monday = 0\r\n return jan4 - weekday * DAY + (week - 1) * 7 * DAY\r\n}\r\n\r\n/**\r\n * One cell read as an instant, or `null` when it isn't one.\r\n *\r\n * Deliberately **not** `Date.parse`. That accepts `\"5\"`, `\"Q3\"` and a good deal\r\n * else besides, differs between engines on anything non-ISO, and reads a bare\r\n * `2026-03-14` as UTC but `2026/03/14` as local — three ways for a column to be\r\n * silently misread as time. Every form below is matched explicitly, and an\r\n * ambiguous all-numeric date (`03/04/2026` — March or April, depending on where\r\n * the author lives) is deliberately not one of them.\r\n */\r\nexport function parseInstant(raw: unknown): number | null {\r\n if (typeof raw !== \"string\") return null\r\n const s = raw.trim().toLowerCase()\r\n if (s === \"\") return null\r\n\r\n // 2026-03-14 · 2026/03/14 · 2026-03 · 2026/03\r\n const ymd = /^(\\d{4})[-/](\\d{1,2})(?:[-/](\\d{1,2}))?$/.exec(s)\r\n if (ymd) {\r\n const month = Number(ymd[2])\r\n const day = ymd[3] === undefined ? 1 : Number(ymd[3])\r\n if (month < 1 || month > 12 || day < 1 || day > 31) return null\r\n return Date.UTC(Number(ymd[1]), month - 1, day)\r\n }\r\n\r\n // 2026-W12 · 2026W12 · w12 · week 12\r\n const week = /^(?:(\\d{4})[-\\s]?)?w(?:ee)?k?[-\\s]?(\\d{1,2})$/.exec(s)\r\n if (week) {\r\n const n = Number(week[2])\r\n if (n < 1 || n > 53) return null\r\n return isoWeekStart(\r\n week[1] === undefined ? NOMINAL_YEAR : Number(week[1]),\r\n n\r\n )\r\n }\r\n\r\n // jan · january · jan 2026 · january-2026 · 2026 jan\r\n const named = /^(?:(\\d{4})[-\\s]+)?([a-z]+)(?:[-\\s]+(\\d{4}))?$/.exec(s)\r\n if (named) {\r\n const month = monthIndex(named[2])\r\n if (month >= 0) {\r\n const year = named[1] ?? named[3]\r\n const y = year === undefined ? NOMINAL_YEAR : Number(year)\r\n return Date.UTC(y, month, 1)\r\n }\r\n }\r\n\r\n // 14 mar 2026 · mar 14 2026 · mar 14, 2026\r\n const dayNamed =\r\n /^(?:(\\d{1,2})[-\\s]+([a-z]+)|([a-z]+)[-\\s]+(\\d{1,2}),?)[-\\s]+(\\d{4})$/.exec(\r\n s\r\n )\r\n if (dayNamed) {\r\n const month = monthIndex(dayNamed[2] ?? dayNamed[3])\r\n const day = Number(dayNamed[1] ?? dayNamed[4])\r\n if (month >= 0 && day >= 1 && day <= 31) {\r\n return Date.UTC(Number(dayNamed[5]), month, day)\r\n }\r\n }\r\n\r\n return null\r\n}\r\n\r\n/**\r\n * An instant captioned at the coarsest granularity the axis's own span can tell\r\n * apart — a decade of samples reads as years, a year of them as months.\r\n *\r\n * Only reached for a tick that is *not* one of the rows (an author-set range, a\r\n * `zoomTo` part-way through). A tick that is a sample is captioned with the\r\n * cell the author wrote; see the note at the top of this file.\r\n */\r\nexport function formatInstant(value: number, span: number): string {\r\n const date = new Date(value)\r\n const year = date.getUTCFullYear()\r\n const month = MONTHS[date.getUTCMonth()].slice(0, 3)\r\n const label = month.charAt(0).toUpperCase() + month.slice(1)\r\n if (span > 4 * 365 * DAY) return String(year)\r\n if (span > 60 * DAY) return `${label} ${year}`\r\n return `${label} ${date.getUTCDate()}`\r\n}\r\n\r\n// ---- Column reading ------------------------------------------------------\r\n\r\n/**\r\n * Whether every non-empty cell of `field` holds a number.\r\n *\r\n * The same rule `numericFields` applies, asked of one column: a single text\r\n * cell in an otherwise numeric column takes the whole column off the numeric\r\n * scale, because plotting that row at 0 is the failure this module is here to\r\n * stop.\r\n */\r\nfunction isNumericColumn(data: DataRecord[], field: string): boolean {\r\n let sawValue = false\r\n for (const row of data) {\r\n const v = row[field]\r\n if (v === undefined || v === null || v === \"\") continue\r\n if (typeof v !== \"number\") return false\r\n sawValue = true\r\n }\r\n return sawValue\r\n}\r\n\r\n/**\r\n * Every non-empty cell of `field` parsed as an instant, or `null` if any one of\r\n * them isn't one — in which case the column isn't a time column at all.\r\n *\r\n * A blank cell keeps its slot, so the returned list stays index-aligned with\r\n * the rows, and takes the nearest known instant: a hole in an x column is a row\r\n * whose date the file didn't say, and stacking it on the epoch would drag the\r\n * whole scale back fifty years to make room for it.\r\n */\r\nfunction instantColumn(data: DataRecord[], field: string): number[] | null {\r\n const parsed: (number | null)[] = []\r\n let sawValue = false\r\n for (const row of data) {\r\n const raw = row[field]\r\n if (raw === undefined || raw === null || raw === \"\") {\r\n parsed.push(null)\r\n continue\r\n }\r\n const t = parseInstant(raw)\r\n if (t === null) return null\r\n parsed.push(t)\r\n sawValue = true\r\n }\r\n if (!sawValue) return null\r\n\r\n const first = parsed.find((t): t is number => t !== null) as number\r\n let last = first\r\n return parsed.map((t) => (t === null ? last : (last = t)))\r\n}\r\n\r\n/** `[min, max]` of `values`, or `[0, 1]` when there is nothing to span. */\r\nfunction extentOf(values: number[]): Range {\r\n let lo = Infinity\r\n let hi = -Infinity\r\n for (const v of values) {\r\n if (v < lo) lo = v\r\n if (v > hi) hi = v\r\n }\r\n return lo <= hi ? [lo, hi] : [0, 1]\r\n}\r\n\r\n/**\r\n * Ticks spanning `range`, each one **a sample** — a row the author can point at\r\n * in their file — placed as near as it can be to where an evenly spaced tick\r\n * would have gone.\r\n *\r\n * Two things have to hold at once, and picking either alone gets it wrong.\r\n * Ticks must be *samples*, because a caption between two of them has nothing to\r\n * say (two-thirds of the way through March is not a month, and on a category\r\n * column it is not anything at all). And they must be *spread*, because the\r\n * labels sit side by side in a row: taking every nth sample instead puts five\r\n * dates in the two weeks a burst of rows happens to cover and prints them on\r\n * top of each other, which is what a column with a gap in it does.\r\n *\r\n * Walking the targets rather than the samples is what satisfies both. A run of\r\n * rows too close together to caption collapses to one tick — consecutive\r\n * targets pick the same sample and the duplicate is dropped — so a chart asked\r\n * for five ticks across a gappy column draws four honest ones instead of five\r\n * overlapping.\r\n */\r\nfunction sampledTicks(\r\n positions: number[],\r\n [min, max]: Range,\r\n divisions: number\r\n): number[] {\r\n const inside = positions.filter((v) => v >= min && v <= max)\r\n const unique = [...new Set(inside)].sort((a, b) => a - b)\r\n if (unique.length === 0) return markerValues([min, max], divisions)\r\n\r\n const nearest = (target: number) =>\r\n unique.reduce((best, v) =>\r\n Math.abs(v - target) < Math.abs(best - target) ? v : best\r\n )\r\n\r\n const out: number[] = []\r\n for (const target of markerValues([min, max], divisions)) {\r\n const tick = nearest(target)\r\n if (out[out.length - 1] !== tick) out.push(tick)\r\n }\r\n return out\r\n}\r\n\r\n/**\r\n * The caption a tick carries on a sample-based scale: the cell of the row it\r\n * came from, or `fallback` when the tick isn't a sample after all.\r\n */\r\nfunction labelBySample(\r\n positions: number[],\r\n captions: string[],\r\n value: number,\r\n fallback: (value: number) => string\r\n): string {\r\n const i = positions.indexOf(value)\r\n return i >= 0 ? captions[i] : fallback(value)\r\n}\r\n\r\n/** Scales already resolved, by data array, then by field and pattern. */\r\nconst scales = new WeakMap<DataRecord[], Map<string, XScale>>()\r\n\r\n/**\r\n * How `field` positions the rows: the number it holds, the instant it names, or\r\n * failing both, the row's own place in the file.\r\n *\r\n * Kept per `data` array: a chart's bound props ask a dozen times a frame, and\r\n * parsing every row's instant each time was most of a frame.\r\n */\r\nexport function resolveXScale(\r\n data: DataRecord[],\r\n field: string,\r\n pattern?: string\r\n): XScale {\r\n let byKey = scales.get(data)\r\n if (!byKey) scales.set(data, (byKey = new Map()))\r\n const key = JSON.stringify([field, pattern ?? null])\r\n let scale = byKey.get(key)\r\n if (!scale) byKey.set(key, (scale = xScaleOf(data, field, pattern)))\r\n return scale\r\n}\r\n\r\nfunction xScaleOf(\r\n data: DataRecord[],\r\n field: string,\r\n /**\r\n * The number-format pattern for a *numeric* column. Ignored by the other two\r\n * kinds, and correctly so: a tick on those is captioned with the cell the\r\n * author wrote, and `Jan` has no decimal places.\r\n */\r\n pattern?: string\r\n): XScale {\r\n if (field !== \"\" && isNumericColumn(data, field)) {\r\n const positions = data.map((row) => num(row, field))\r\n return {\r\n kind: \"numeric\",\r\n at: (i) => positions[i] ?? 0,\r\n extent: extentOf(positions),\r\n label: (value) =>\r\n pattern === undefined ? formatMarker(value) : formatNumber(value, pattern),\r\n // Round numbers, evenly spaced — a continuous axis is read by its scale\r\n // rather than by which samples happen to be on it.\r\n ticks: markerValues,\r\n }\r\n }\r\n\r\n const captions = data.map((row) => text(row, field))\r\n const instants = field === \"\" ? null : instantColumn(data, field)\r\n\r\n if (instants) {\r\n const extent = extentOf(instants)\r\n const span = extent[1] - extent[0]\r\n return {\r\n kind: \"time\",\r\n at: (i) => instants[i] ?? 0,\r\n extent,\r\n label: (value) =>\r\n labelBySample(instants, captions, value, (v) =>\r\n formatInstant(v, span)\r\n ),\r\n ticks: (range, divisions) => sampledTicks(instants, range, divisions),\r\n }\r\n }\r\n\r\n const positions = data.map((_, i) => i)\r\n return {\r\n kind: \"ordinal\",\r\n at: (i) => i,\r\n // `[0, n-1]` rather than the tidy `[0, n]`: the first and last rows sit on\r\n // the plot's edges, which is where a reader looks for them.\r\n extent: positions.length > 0 ? [0, positions.length - 1] : [0, 1],\r\n label: (value) => captions[Math.round(value)] ?? \"\",\r\n ticks: (range, divisions) => sampledTicks(positions, range, divisions),\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"x-scale.js","sourceRoot":"","sources":["../../src/line-chart/x-scale.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a line chart's x column *means* — the one question that has to be\n * answered before a row can be given a position along the bottom of the plot.\n *\n * A y column is always a magnitude. An x column is one of three things, and\n * which one is a property of the data rather than something the author states:\n *\n * - **numeric** — `0, 5, 10` — plot the number.\n * - **time** — `Jan`, `2026-03`, `2026-W12`, `2026-03-14` — plot the instant,\n * so samples a month apart sit a month apart and a gap in the data reads as a\n * gap.\n * - **ordinal** — anything else — plot the row's position, evenly spaced.\n *\n * Before this, every x column went through `num()`, which reads a non-numeric\n * cell as `0`. A `Month` column of `Jan…Dec` therefore stacked all twelve\n * points on one x and drew a vertical line — no error, no empty chart, just a\n * wrong picture. That is the failure this module exists to remove: every column\n * now plots as *something*, and the something is the reading a person would\n * give it.\n *\n * The captions come back off the rows rather than out of a formatter. A tick on\n * a time or ordinal scale is chosen *from the data* (see {@link XScale.ticks}),\n * so the label it wants is the cell the author wrote — `Jan`, not `1970-01-01`,\n * and `2026-W12`, not the Monday it resolves to. Only a tick that is not a\n * sample — an author-set range, or a `zoomTo` mid-flight — needs formatting,\n * and that is the one case {@link formatInstant} covers.\n */\n\nimport type { DataRecord } from \"@motionscript/sdk\"\n\nimport {\n formatMarker,\n markerValues,\n num,\n text,\n type Range,\n} from \"../kit/shared\"\nimport { formatNumber } from \"@motionscript/sdk/component\"\n\n/** How an x column's cells become positions along the axis. */\nexport type XScaleKind = \"numeric\" | \"time\" | \"ordinal\"\n\n/**\n * An x column resolved into positions, captions and ticks.\n *\n * The three kinds differ in all three answers, so they are three closures built\n * once per (data, column) rather than a kind tag every caller switches on.\n */\nexport interface XScale {\n kind: XScaleKind\n /** Position of row `index` along the axis, in this scale's own units. */\n at(index: number): number\n /** `[min, max]` across the rows — the scale when no range is set. */\n extent: Range\n /** The caption for a tick at `value`. */\n label(value: number): string\n /** `divisions + 1` tick values spanning `range`, ends included. */\n ticks(range: Range, divisions: number): number[]\n}\n\n// ---- Time parsing --------------------------------------------------------\n\nconst DAY = 86_400_000\n\nconst MONTHS = [\n \"january\",\n \"february\",\n \"march\",\n \"april\",\n \"may\",\n \"june\",\n \"july\",\n \"august\",\n \"september\",\n \"october\",\n \"november\",\n \"december\",\n]\n\n/**\n * The year a cell that names no year is dated into.\n *\n * Any year would place `Jan…Dec` in the right order at the right spacing; a\n * leap year is the one that also spaces February correctly against a column\n * that *does* carry years, so the two read alike.\n */\nconst NOMINAL_YEAR = 2024\n\n/**\n * Month index for any unambiguous abbreviation of a month name — `jan`, `sept`,\n * `january` — or -1.\n *\n * Three letters minimum, and then a prefix match, which is what lets `sept`\n * through without letting `janitor` through: a longer word has to still *be*\n * the month.\n */\nfunction monthIndex(word: string): number {\n if (word.length < 3) return -1\n return MONTHS.findIndex((month) => month.startsWith(word))\n}\n\n/** The Monday of ISO week `week` in `year`, as an epoch timestamp. */\nfunction isoWeekStart(year: number, week: number): number {\n // ISO week 1 is the week containing 4 January, and weeks start on Monday.\n const jan4 = Date.UTC(year, 0, 4)\n const weekday = (new Date(jan4).getUTCDay() + 6) % 7 // Monday = 0\n return jan4 - weekday * DAY + (week - 1) * 7 * DAY\n}\n\n/**\n * One cell read as an instant, or `null` when it isn't one.\n *\n * Deliberately **not** `Date.parse`. That accepts `\"5\"`, `\"Q3\"` and a good deal\n * else besides, differs between engines on anything non-ISO, and reads a bare\n * `2026-03-14` as UTC but `2026/03/14` as local — three ways for a column to be\n * silently misread as time. Every form below is matched explicitly, and an\n * ambiguous all-numeric date (`03/04/2026` — March or April, depending on where\n * the author lives) is deliberately not one of them.\n */\nexport function parseInstant(raw: unknown): number | null {\n if (typeof raw !== \"string\") return null\n const s = raw.trim().toLowerCase()\n if (s === \"\") return null\n\n // 2026-03-14 · 2026/03/14 · 2026-03 · 2026/03\n const ymd = /^(\\d{4})[-/](\\d{1,2})(?:[-/](\\d{1,2}))?$/.exec(s)\n if (ymd) {\n const month = Number(ymd[2])\n const day = ymd[3] === undefined ? 1 : Number(ymd[3])\n if (month < 1 || month > 12 || day < 1 || day > 31) return null\n return Date.UTC(Number(ymd[1]), month - 1, day)\n }\n\n // 2026-W12 · 2026W12 · w12 · week 12\n const week = /^(?:(\\d{4})[-\\s]?)?w(?:ee)?k?[-\\s]?(\\d{1,2})$/.exec(s)\n if (week) {\n const n = Number(week[2])\n if (n < 1 || n > 53) return null\n return isoWeekStart(\n week[1] === undefined ? NOMINAL_YEAR : Number(week[1]),\n n\n )\n }\n\n // jan · january · jan 2026 · january-2026 · 2026 jan\n const named = /^(?:(\\d{4})[-\\s]+)?([a-z]+)(?:[-\\s]+(\\d{4}))?$/.exec(s)\n if (named) {\n const month = monthIndex(named[2])\n if (month >= 0) {\n const year = named[1] ?? named[3]\n const y = year === undefined ? NOMINAL_YEAR : Number(year)\n return Date.UTC(y, month, 1)\n }\n }\n\n // 14 mar 2026 · mar 14 2026 · mar 14, 2026\n const dayNamed =\n /^(?:(\\d{1,2})[-\\s]+([a-z]+)|([a-z]+)[-\\s]+(\\d{1,2}),?)[-\\s]+(\\d{4})$/.exec(\n s\n )\n if (dayNamed) {\n const month = monthIndex(dayNamed[2] ?? dayNamed[3])\n const day = Number(dayNamed[1] ?? dayNamed[4])\n if (month >= 0 && day >= 1 && day <= 31) {\n return Date.UTC(Number(dayNamed[5]), month, day)\n }\n }\n\n return null\n}\n\n/**\n * An instant captioned at the coarsest granularity the axis's own span can tell\n * apart — a decade of samples reads as years, a year of them as months.\n *\n * Only reached for a tick that is *not* one of the rows (an author-set range, a\n * `zoomTo` part-way through). A tick that is a sample is captioned with the\n * cell the author wrote; see the note at the top of this file.\n */\nexport function formatInstant(value: number, span: number): string {\n const date = new Date(value)\n const year = date.getUTCFullYear()\n const month = MONTHS[date.getUTCMonth()].slice(0, 3)\n const label = month.charAt(0).toUpperCase() + month.slice(1)\n if (span > 4 * 365 * DAY) return String(year)\n if (span > 60 * DAY) return `${label} ${year}`\n return `${label} ${date.getUTCDate()}`\n}\n\n// ---- Column reading ------------------------------------------------------\n\n/**\n * Whether every non-empty cell of `field` holds a number.\n *\n * The same rule `numericFields` applies, asked of one column: a single text\n * cell in an otherwise numeric column takes the whole column off the numeric\n * scale, because plotting that row at 0 is the failure this module is here to\n * stop.\n */\nfunction isNumericColumn(data: DataRecord[], field: string): boolean {\n let sawValue = false\n for (const row of data) {\n const v = row[field]\n if (v === undefined || v === null || v === \"\") continue\n if (typeof v !== \"number\") return false\n sawValue = true\n }\n return sawValue\n}\n\n/**\n * Every non-empty cell of `field` parsed as an instant, or `null` if any one of\n * them isn't one — in which case the column isn't a time column at all.\n *\n * A blank cell keeps its slot, so the returned list stays index-aligned with\n * the rows, and takes the nearest known instant: a hole in an x column is a row\n * whose date the file didn't say, and stacking it on the epoch would drag the\n * whole scale back fifty years to make room for it.\n */\nfunction instantColumn(data: DataRecord[], field: string): number[] | null {\n const parsed: (number | null)[] = []\n let sawValue = false\n for (const row of data) {\n const raw = row[field]\n if (raw === undefined || raw === null || raw === \"\") {\n parsed.push(null)\n continue\n }\n const t = parseInstant(raw)\n if (t === null) return null\n parsed.push(t)\n sawValue = true\n }\n if (!sawValue) return null\n\n const first = parsed.find((t): t is number => t !== null) as number\n let last = first\n return parsed.map((t) => (t === null ? last : (last = t)))\n}\n\n/** `[min, max]` of `values`, or `[0, 1]` when there is nothing to span. */\nfunction extentOf(values: number[]): Range {\n let lo = Infinity\n let hi = -Infinity\n for (const v of values) {\n if (v < lo) lo = v\n if (v > hi) hi = v\n }\n return lo <= hi ? [lo, hi] : [0, 1]\n}\n\n/**\n * Ticks spanning `range`, each one **a sample** — a row the author can point at\n * in their file — placed as near as it can be to where an evenly spaced tick\n * would have gone.\n *\n * Two things have to hold at once, and picking either alone gets it wrong.\n * Ticks must be *samples*, because a caption between two of them has nothing to\n * say (two-thirds of the way through March is not a month, and on a category\n * column it is not anything at all). And they must be *spread*, because the\n * labels sit side by side in a row: taking every nth sample instead puts five\n * dates in the two weeks a burst of rows happens to cover and prints them on\n * top of each other, which is what a column with a gap in it does.\n *\n * Walking the targets rather than the samples is what satisfies both. A run of\n * rows too close together to caption collapses to one tick — consecutive\n * targets pick the same sample and the duplicate is dropped — so a chart asked\n * for five ticks across a gappy column draws four honest ones instead of five\n * overlapping.\n */\nfunction sampledTicks(\n positions: number[],\n [min, max]: Range,\n divisions: number\n): number[] {\n const inside = positions.filter((v) => v >= min && v <= max)\n const unique = [...new Set(inside)].sort((a, b) => a - b)\n if (unique.length === 0) return markerValues([min, max], divisions)\n\n const nearest = (target: number) =>\n unique.reduce((best, v) =>\n Math.abs(v - target) < Math.abs(best - target) ? v : best\n )\n\n const out: number[] = []\n for (const target of markerValues([min, max], divisions)) {\n const tick = nearest(target)\n if (out[out.length - 1] !== tick) out.push(tick)\n }\n return out\n}\n\n/**\n * The caption a tick carries on a sample-based scale: the cell of the row it\n * came from, or `fallback` when the tick isn't a sample after all.\n */\nfunction labelBySample(\n positions: number[],\n captions: string[],\n value: number,\n fallback: (value: number) => string\n): string {\n const i = positions.indexOf(value)\n return i >= 0 ? captions[i] : fallback(value)\n}\n\n/** Scales already resolved, by data array, then by field and pattern. */\nconst scales = new WeakMap<DataRecord[], Map<string, XScale>>()\n\n/**\n * How `field` positions the rows: the number it holds, the instant it names, or\n * failing both, the row's own place in the file.\n *\n * Kept per `data` array: a chart's bound props ask a dozen times a frame, and\n * parsing every row's instant each time was most of a frame.\n */\nexport function resolveXScale(\n data: DataRecord[],\n field: string,\n pattern?: string\n): XScale {\n let byKey = scales.get(data)\n if (!byKey) scales.set(data, (byKey = new Map()))\n const key = JSON.stringify([field, pattern ?? null])\n let scale = byKey.get(key)\n if (!scale) byKey.set(key, (scale = xScaleOf(data, field, pattern)))\n return scale\n}\n\nfunction xScaleOf(\n data: DataRecord[],\n field: string,\n /**\n * The number-format pattern for a *numeric* column. Ignored by the other two\n * kinds, and correctly so: a tick on those is captioned with the cell the\n * author wrote, and `Jan` has no decimal places.\n */\n pattern?: string\n): XScale {\n if (field !== \"\" && isNumericColumn(data, field)) {\n const positions = data.map((row) => num(row, field))\n return {\n kind: \"numeric\",\n at: (i) => positions[i] ?? 0,\n extent: extentOf(positions),\n label: (value) =>\n pattern === undefined ? formatMarker(value) : formatNumber(value, pattern),\n // Round numbers, evenly spaced — a continuous axis is read by its scale\n // rather than by which samples happen to be on it.\n ticks: markerValues,\n }\n }\n\n const captions = data.map((row) => text(row, field))\n const instants = field === \"\" ? null : instantColumn(data, field)\n\n if (instants) {\n const extent = extentOf(instants)\n const span = extent[1] - extent[0]\n return {\n kind: \"time\",\n at: (i) => instants[i] ?? 0,\n extent,\n label: (value) =>\n labelBySample(instants, captions, value, (v) =>\n formatInstant(v, span)\n ),\n ticks: (range, divisions) => sampledTicks(instants, range, divisions),\n }\n }\n\n const positions = data.map((_, i) => i)\n return {\n kind: \"ordinal\",\n at: (i) => i,\n // `[0, n-1]` rather than the tidy `[0, n]`: the first and last rows sit on\n // the plot's edges, which is where a reader looks for them.\n extent: positions.length > 0 ? [0, positions.length - 1] : [0, 1],\n label: (value) => captions[Math.round(value)] ?? \"\",\n ticks: (range, divisions) => sampledTicks(positions, range, divisions),\n }\n}\n"]}
package/dist/nodes.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"nodes.js","sourceRoot":"","sources":["../src/nodes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC;;;;;;;;;;;GAWG;AACH,yGAAyG;AACzG,MAAM,CAAC,MAAM,KAAK,GAA6D,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC","sourcesContent":["import { LineChart } from \"./line-chart\";\r\nimport { BarChart } from \"./bar-chart\";\r\nimport { PieChart } from \"./pie-chart\";\r\n\r\n/**\r\n * Every node type this package publishes.\r\n *\r\n * A host registers these by handing the array to an engine —\r\n * `new Engine(platform, { nodes: NODES })` — which reads each component's key.\r\n * Defining a component only *declares* that key: nothing is registered by\r\n * importing a module, so a registry holds exactly what its owner asked for.\r\n *\r\n * Listing the component **values** is also what survives bundling: this package\r\n * declares `sideEffects: false`, and a document names a node type by string, so\r\n * an import kept only for its side effect is exactly what gets shaken out.\r\n */\r\n// Typed by name, so the declaration names each component rather than spelling out its type through core.\r\nexport const NODES: (typeof LineChart | typeof BarChart | typeof PieChart)[] = [LineChart, BarChart, PieChart];\r\n"]}
1
+ {"version":3,"file":"nodes.js","sourceRoot":"","sources":["../src/nodes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC;;;;;;;;;;;GAWG;AACH,yGAAyG;AACzG,MAAM,CAAC,MAAM,KAAK,GAA6D,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC","sourcesContent":["import { LineChart } from \"./line-chart\";\nimport { BarChart } from \"./bar-chart\";\nimport { PieChart } from \"./pie-chart\";\n\n/**\n * Every node type this package publishes.\n *\n * A host registers these by handing the array to an engine —\n * `new Engine(platform, { nodes: NODES })` — which reads each component's key.\n * Defining a component only *declares* that key: nothing is registered by\n * importing a module, so a registry holds exactly what its owner asked for.\n *\n * Listing the component **values** is also what survives bundling: this package\n * declares `sideEffects: false`, and a document names a node type by string, so\n * an import kept only for its side effect is exactly what gets shaken out.\n */\n// Typed by name, so the declaration names each component rather than spelling out its type through core.\nexport const NODES: (typeof LineChart | typeof BarChart | typeof PieChart)[] = [LineChart, BarChart, PieChart];\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pie-chart/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAEvD,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEpC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,OAAO,EACP,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EAChB,SAAS,EACT,UAAU,EACV,UAAU,GACX,MAAM,UAAU,CAAA","sourcesContent":["/**\r\n * The pie chart: the one in the family that draws a mark per **row** rather\r\n * than per column.\r\n *\r\n * So it shares the legend and the theme machinery with its siblings and none of\r\n * the cartesian geometry. Set the theme's `ring.holeRadius` to 0 for a solid\r\n * pie; it defaults to a donut, because the hole is the only place a chart has\r\n * to put its conclusion.\r\n */\r\nexport { PieChart, isSliceSelected } from \"./pie-chart\"\r\nexport type { PieChartProps, PieChartValues } from \"./pie-chart\"\r\nexport { PieRing } from \"./pie-ring\"\r\nexport type { PieRingProps } from \"./pie-ring\"\r\nexport { PieLegend } from \"./pie-legend\"\r\nexport type { PieLegendProps } from \"./pie-legend\"\r\nexport {\r\n DEFAULT_PIE_CHART_THEME,\r\n lerpPieChartTheme,\r\n pieArcs,\r\n relaxLabels,\r\n resolvePieChartTheme,\r\n resolvePieSlices,\r\n ringPoint,\r\n sliceTotal,\r\n sliceValue,\r\n} from \"./shared\"\r\nexport type {\r\n CenterConfig,\r\n LabelConfig,\r\n PieArc,\r\n PieChartConfig,\r\n PieChartTheme,\r\n PieLabelMode,\r\n PieLabelPlacement,\r\n PieSlice,\r\n RingConfig,\r\n} from \"./shared\"\r\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pie-chart/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAEvD,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEpC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,OAAO,EACP,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EAChB,SAAS,EACT,UAAU,EACV,UAAU,GACX,MAAM,UAAU,CAAA","sourcesContent":["/**\n * The pie chart: the one in the family that draws a mark per **row** rather\n * than per column.\n *\n * So it shares the legend and the theme machinery with its siblings and none of\n * the cartesian geometry. Set the theme's `ring.holeRadius` to 0 for a solid\n * pie; it defaults to a donut, because the hole is the only place a chart has\n * to put its conclusion.\n */\nexport { PieChart, isSliceSelected } from \"./pie-chart\"\nexport type { PieChartProps, PieChartValues } from \"./pie-chart\"\nexport { PieRing } from \"./pie-ring\"\nexport type { PieRingProps } from \"./pie-ring\"\nexport { PieLegend } from \"./pie-legend\"\nexport type { PieLegendProps } from \"./pie-legend\"\nexport {\n DEFAULT_PIE_CHART_THEME,\n lerpPieChartTheme,\n pieArcs,\n relaxLabels,\n resolvePieChartTheme,\n resolvePieSlices,\n ringPoint,\n sliceTotal,\n sliceValue,\n} from \"./shared\"\nexport type {\n CenterConfig,\n LabelConfig,\n PieArc,\n PieChartConfig,\n PieChartTheme,\n PieLabelMode,\n PieLabelPlacement,\n PieSlice,\n RingConfig,\n} from \"./shared\"\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"pie-chart.js","sourceRoot":"","sources":["../../src/pie-chart/pie-chart.ts"],"names":[],"mappings":"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","sourcesContent":["import {\r\n Rect,\r\n Types,\r\n component2d,\r\n easeOut,\r\n type ChildView,\r\n type DataRecord,\r\n type NodeOf,\r\n // Named so this component's emitted type reaches `Node2D` through the sdk, not core.\r\n type Node2D,\r\n type Node2DProps,\r\n type Timeline,\r\n} from \"@motionscript/sdk\"\r\n\r\nimport { asParts, fresh } from \"../kit/entries\"\r\nimport { beats, type Beat } from \"../kit/motion\"\r\nimport { clampUnit } from \"../kit/shared\"\r\nimport { PieLegend } from \"./pie-legend\"\r\nimport { PieRing } from \"./pie-ring\"\r\nimport {\r\n DEFAULT_PIE_CHART_THEME,\r\n lerpPieChartTheme,\r\n resolvePieChartTheme,\r\n resolvePieSlices,\r\n type PieChartConfig,\r\n type PieChartTheme,\r\n type PieLabelMode,\r\n type PieLabelPlacement,\r\n type PieSlice,\r\n} from \"./shared\"\r\n\r\nexport interface PieChartProps extends Node2DProps {\r\n /** One row per slice — a plain record of field name to value. */\r\n data: DataRecord[]\r\n /** Key into each row for its slice caption, used on the labels and the legend. */\r\n categoryField: string\r\n /** Key into each row for its slice magnitude — the wedge's share of the ring. */\r\n valueField: string\r\n /**\r\n * The chart's visual theme — slice palette, ring geometry, label and centre\r\n * type, legend. Every branch is optional and deep-merged onto\r\n * {@link DEFAULT_PIE_CHART_THEME}.\r\n */\r\n theme?: PieChartTheme\r\n /** What the ring's labels say. */\r\n labelMode?: PieLabelMode\r\n /** Where they sit: inside their wedge, outside on a leader, or nowhere. */\r\n labelPlacement?: PieLabelPlacement\r\n /** Whether the legend column beside the ring is drawn at all. */\r\n showLegend?: boolean\r\n /** Gap between the ring and the legend, in px. */\r\n gap?: number\r\n /** Text set in the ring's hole — see `revealCenter`. */\r\n centerLabel?: string\r\n /** Smaller second line under {@link centerLabel}. */\r\n centerCaption?: string\r\n /**\r\n * Whether the hole's summary is drawn at rest. `true` (the default) is the\r\n * plain answer; a scene that wants the conclusion to land on a later beat sets\r\n * this `false` and runs `revealCenter` when it gets there.\r\n */\r\n showCenter?: boolean\r\n}\r\n\r\n/** What the selection reads: the rows, how they are cut, and the pops the commands move. */\r\nexport interface PieChartValues {\r\n readonly data: DataRecord[]\r\n readonly categoryField: string\r\n readonly valueField: string\r\n readonly theme: PieChartConfig\r\n readonly pops: number[]\r\n}\r\n\r\n/**\r\n * How `enter` divides its duration: the wedges sweeping in, then the labels and\r\n * the legend arriving together. Shares of the whole, so they sum to 1.\r\n */\r\nconst SWEEP_SHARE = 0.65\r\nconst LABEL_SHARE = 0.35\r\n\r\nconst SLICE_ARG = Types.number(0, { min: 0, step: 1 })\r\n\r\n/** The slices the ring and the legend draw, resolved from the rows and the palette. */\r\nfunction slicesOf(s: PieChartValues): PieSlice[] {\r\n return resolvePieSlices(s.data, s.categoryField, s.valueField, s.theme.sliceFills)\r\n}\r\n\r\n/** How far slice `index` is pushed out, in `[0, 1]`. */\r\nfunction popAt(s: PieChartValues, index: number): number {\r\n return clampUnit(s.pops[index] ?? 0)\r\n}\r\n\r\n/** Whether slice `index` is grown out past the ring's rim: past half way, so a slice mid-swell is not yet counted. */\r\nexport function isSliceSelected(s: PieChartValues, index: number): boolean {\r\n return popAt(s, index) > 0.5\r\n}\r\n\r\n/**\r\n * How far each slice is dimmed by the selection, one entry per slice: 1 while\r\n * nothing is out, and for the selected slice itself; falling toward the theme's\r\n * `dimOpacity` for the rest in proportion to how far the selection has grown.\r\n */\r\nfunction sliceDims(s: PieChartValues): number[] {\r\n let focus = 0\r\n for (let i = 0; i < s.data.length; i++) focus = Math.max(focus, popAt(s, i))\r\n const dim = s.theme.spotlight.dimOpacity\r\n if (focus <= 0) return s.data.map(() => 1)\r\n return s.data.map((_, i) => 1 - (1 - clampUnit(dim)) * focus * (1 - popAt(s, i)))\r\n}\r\n\r\ntype AnyTimeline = Timeline<any, any>\r\n\r\n/**\r\n * Every slice's pop eased from where it is to what `pop` asks for. A move that\r\n * changes nothing still takes its slot, writing nothing.\r\n */\r\nfunction popping(timeline: AnyTimeline, s: PieChartValues, pop: (index: number) => number) {\r\n const from = s.data.map((_, i) => popAt(s, i))\r\n const targets = s.data.map((_, i) => clampUnit(pop(i)))\r\n if (from.every((value, i) => value === targets[i])) return timeline.wait(timeline.duration)\r\n return timeline.animate((t) => ({ pops: from.map((value, i) => value + (targets[i] - value) * t) }))\r\n}\r\n\r\n/** The ring and the legend, found through the chart's children: the last row of them is the one it built. */\r\nfunction partsOf(children: readonly ChildView[]) {\r\n for (let i = children.length - 1; i >= 0; i--) {\r\n const row = children[i]\r\n const ring = Rect.is(row) ? row.children.find(PieRing.is) : undefined\r\n if (ring) return { ring, legend: row.children.find(PieLegend.is) ?? null }\r\n }\r\n return null\r\n}\r\n\r\n/**\r\n * A pie or donut chart, composed from two parts laid out by a horizontal-flow\r\n * {@link Rect} — the ring filling whatever the hugging legend leaves, both\r\n * centred:\r\n *\r\n * - {@link PieRing} — the wedges, each sized by its share of the total, with its\r\n * label inside its own band or pinned outside on a leader line, and the\r\n * summary in the hole.\r\n * - {@link PieLegend} — a column of swatch-and-caption rows naming each slice.\r\n *\r\n * **One mark per row, not per column**: a pie's palette is indexed by row and\r\n * there is nothing to hide, since dropping a slice would re-cut every other\r\n * wedge. `selectSlice` and its siblings are what a pie has instead — a slice\r\n * swells out of the ring and everything else dims behind it.\r\n *\r\n * **Resting state is the finished chart**; `enter` arms every part on its first\r\n * frame. The one exception is the hole's summary, which a scene can hold back\r\n * with `showCenter: false`, because it is usually the chart's *conclusion*.\r\n */\r\nexport const PieChart = component2d({\r\n key: \"pieChart\",\r\n state: {\r\n data: fresh<DataRecord[]>(() => []),\r\n categoryField: \"\",\r\n valueField: \"\",\r\n theme: Types.custom<PieChartTheme, PieChartConfig>(DEFAULT_PIE_CHART_THEME, { resolve: resolvePieChartTheme, lerp: lerpPieChartTheme }),\r\n labelMode: Types.custom<PieLabelMode>(\"percent\"),\r\n labelPlacement: Types.custom<PieLabelPlacement>(\"leader\"),\r\n gap: 40,\r\n centerLabel: \"\",\r\n centerCaption: \"\",\r\n /**\r\n * How far each slice is pushed out of the ring, `0`–`1` per index. Held here\r\n * rather than on the ring because two parts read it: the wedges grow out by\r\n * it, and the legend entries dim by it. Seeded flush, one to a row.\r\n */\r\n pops: fresh<number[]>(() => []),\r\n /** Read as the chart is built: the legend is a part the chart builds or doesn't. */\r\n showLegend: Types.boolean(true, { animatable: false }),\r\n /** Read as the chart is built: whether the hole's summary is drawn at rest. */\r\n showCenter: Types.boolean(true, { animatable: false }),\r\n },\r\n defaults: {\r\n // Its parts fill it; a node made of them would hug them.\r\n width: \"fill\",\r\n height: \"fill\",\r\n pops: (state) => slicesOf(state).map(() => 0),\r\n },\r\n allowedChildren: [],\r\n // The slice *structure* (one label and one legend row per row) is read once;\r\n // the slices themselves, their paint and the selection are bound, so a theme\r\n // tween re-paints and a `set` of `data` re-cuts the wedges.\r\n children: (state) => {\r\n const theme = () => state.theme\r\n const slices = () => slicesOf(state)\r\n const parts: Node2D[] = [\r\n new PieRing({\r\n width: \"fill\",\r\n height: \"fill\",\r\n slices,\r\n ring: () => theme().ring,\r\n label: () => theme().label,\r\n centerStyle: () => theme().center,\r\n labelMode: state.labelMode,\r\n labelPlacement: state.labelPlacement,\r\n pops: () => state.pops,\r\n dimOpacity: () => theme().spotlight.dimOpacity,\r\n centerLabel: state.centerLabel,\r\n centerCaption: state.centerCaption,\r\n centerReveal: state.showCenter === false ? 0 : 1,\r\n }) as unknown as Node2D,\r\n ]\r\n if (state.showLegend !== false) {\r\n parts.push(\r\n new PieLegend({\r\n slices,\r\n legend: () => theme().legend,\r\n dims: () => sliceDims(state),\r\n // The column's spacing is the theme's as the chart was built, like the\r\n // entries' type and chips.\r\n gap: state.theme.legend.itemGap / 3,\r\n padding: state.theme.legend.padding,\r\n }) as unknown as Node2D\r\n )\r\n }\r\n const row = new Rect({ flow: \"horizontal\", width: \"fill\", height: \"fill\", gap: state.gap, align: \"center\", children: parts })\r\n return asParts([row as unknown as Node2D])\r\n },\r\n commands: {\r\n /**\r\n * Animate the whole chart in: the wedges sweep in clockwise from the theme's\r\n * `startAngle`, then the labels fade up and the legend staggers in together.\r\n * Its first frame arms the wedges, the labels and the legend; the hole's\r\n * summary keeps its own signal. `duration` is the **whole** entrance.\r\n */\r\n enter: {\r\n args: {},\r\n // Held from the chart's arrival, so it never shows finished ahead of its draw.\r\n entrance: true,\r\n easing: easeOut(),\r\n run: (timeline, _state, children) => {\r\n const parts = partsOf(children)\r\n if (!parts) return timeline.wait(timeline.duration)\r\n const { ring, legend } = parts\r\n const sweep = timeline.duration * SWEEP_SHARE\r\n const labels = timeline.duration * LABEL_SHARE\r\n const at0 = (piece: Beat[\"piece\"]): Beat => ({ at: 0, duration: 0, piece })\r\n return beats(timeline, [\r\n at0(ring.to({ growth: 0, labelReveal: 0 })),\r\n ...(legend ? [at0(legend.run(\"arm\"))] : []),\r\n { at: 0, duration: sweep, piece: ring.run(\"sweep\") },\r\n { at: sweep, duration: labels, piece: ring.run(\"revealLabels\") },\r\n ...(legend ? [{ at: sweep, duration: labels, piece: legend.run(\"enter\") }] : []),\r\n ])\r\n },\r\n },\r\n /**\r\n * Fade the hole's summary up. Held out of `enter` on purpose: what sits in a\r\n * donut's hole is usually the chart's conclusion, so the scene lands it on\r\n * its own beat. Pair it with `showCenter: false`.\r\n */\r\n revealCenter: {\r\n args: {},\r\n easing: easeOut(),\r\n run: (timeline, _state, children) => partsOf(children)?.ring.run(\"revealCenter\") ?? timeline.wait(timeline.duration),\r\n },\r\n /** Grow slice `index` out past the ring's rim and dim the rest, leaving any other selected slice where it is. */\r\n selectSlice: {\r\n args: { sliceIndex: SLICE_ARG },\r\n easing: easeOut(),\r\n run: (timeline, state) => {\r\n const index = timeline.args.sliceIndex\r\n return popping(timeline, state, (i) => (i === index ? 1 : popAt(state, i)))\r\n },\r\n },\r\n /** Settle slice `index` back flush with the ring. */\r\n deselectSlice: {\r\n args: { sliceIndex: SLICE_ARG },\r\n easing: easeOut(),\r\n run: (timeline, state) => {\r\n const index = timeline.args.sliceIndex\r\n return popping(timeline, state, (i) => (i === index ? 0 : popAt(state, i)))\r\n },\r\n },\r\n /** Move the focus to slice `index` alone, settling whatever else was out. */\r\n selectOnlySlice: {\r\n args: { sliceIndex: SLICE_ARG },\r\n easing: easeOut(),\r\n run: (timeline, state) => {\r\n const index = timeline.args.sliceIndex\r\n return popping(timeline, state, (i) => (i === index ? 1 : 0))\r\n },\r\n },\r\n /** Settle every slice flush and bring the whole chart back to full. */\r\n clearSelection: {\r\n args: {},\r\n easing: easeOut(),\r\n run: (timeline, state) => popping(timeline, state, () => 0),\r\n },\r\n },\r\n})\r\n\r\nexport type PieChart = NodeOf<typeof PieChart>\r\n"]}
1
+ {"version":3,"file":"pie-chart.js","sourceRoot":"","sources":["../../src/pie-chart/pie-chart.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Rect,\n Types,\n component2d,\n easeOut,\n type ChildView,\n type DataRecord,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` through the sdk, not core.\n type Node2D,\n type Node2DProps,\n type Timeline,\n} from \"@motionscript/sdk\"\n\nimport { asParts, fresh } from \"../kit/entries\"\nimport { beats, type Beat } from \"../kit/motion\"\nimport { clampUnit } from \"../kit/shared\"\nimport { PieLegend } from \"./pie-legend\"\nimport { PieRing } from \"./pie-ring\"\nimport {\n DEFAULT_PIE_CHART_THEME,\n lerpPieChartTheme,\n resolvePieChartTheme,\n resolvePieSlices,\n type PieChartConfig,\n type PieChartTheme,\n type PieLabelMode,\n type PieLabelPlacement,\n type PieSlice,\n} from \"./shared\"\n\nexport interface PieChartProps extends Node2DProps {\n /** One row per slice — a plain record of field name to value. */\n data: DataRecord[]\n /** Key into each row for its slice caption, used on the labels and the legend. */\n categoryField: string\n /** Key into each row for its slice magnitude — the wedge's share of the ring. */\n valueField: string\n /**\n * The chart's visual theme — slice palette, ring geometry, label and centre\n * type, legend. Every branch is optional and deep-merged onto\n * {@link DEFAULT_PIE_CHART_THEME}.\n */\n theme?: PieChartTheme\n /** What the ring's labels say. */\n labelMode?: PieLabelMode\n /** Where they sit: inside their wedge, outside on a leader, or nowhere. */\n labelPlacement?: PieLabelPlacement\n /** Whether the legend column beside the ring is drawn at all. */\n showLegend?: boolean\n /** Gap between the ring and the legend, in px. */\n gap?: number\n /** Text set in the ring's hole — see `revealCenter`. */\n centerLabel?: string\n /** Smaller second line under {@link centerLabel}. */\n centerCaption?: string\n /**\n * Whether the hole's summary is drawn at rest. `true` (the default) is the\n * plain answer; a scene that wants the conclusion to land on a later beat sets\n * this `false` and runs `revealCenter` when it gets there.\n */\n showCenter?: boolean\n}\n\n/** What the selection reads: the rows, how they are cut, and the pops the commands move. */\nexport interface PieChartValues {\n readonly data: DataRecord[]\n readonly categoryField: string\n readonly valueField: string\n readonly theme: PieChartConfig\n readonly pops: number[]\n}\n\n/**\n * How `enter` divides its duration: the wedges sweeping in, then the labels and\n * the legend arriving together. Shares of the whole, so they sum to 1.\n */\nconst SWEEP_SHARE = 0.65\nconst LABEL_SHARE = 0.35\n\nconst SLICE_ARG = Types.number(0, { min: 0, step: 1 })\n\n/** The slices the ring and the legend draw, resolved from the rows and the palette. */\nfunction slicesOf(s: PieChartValues): PieSlice[] {\n return resolvePieSlices(s.data, s.categoryField, s.valueField, s.theme.sliceFills)\n}\n\n/** How far slice `index` is pushed out, in `[0, 1]`. */\nfunction popAt(s: PieChartValues, index: number): number {\n return clampUnit(s.pops[index] ?? 0)\n}\n\n/** Whether slice `index` is grown out past the ring's rim: past half way, so a slice mid-swell is not yet counted. */\nexport function isSliceSelected(s: PieChartValues, index: number): boolean {\n return popAt(s, index) > 0.5\n}\n\n/**\n * How far each slice is dimmed by the selection, one entry per slice: 1 while\n * nothing is out, and for the selected slice itself; falling toward the theme's\n * `dimOpacity` for the rest in proportion to how far the selection has grown.\n */\nfunction sliceDims(s: PieChartValues): number[] {\n let focus = 0\n for (let i = 0; i < s.data.length; i++) focus = Math.max(focus, popAt(s, i))\n const dim = s.theme.spotlight.dimOpacity\n if (focus <= 0) return s.data.map(() => 1)\n return s.data.map((_, i) => 1 - (1 - clampUnit(dim)) * focus * (1 - popAt(s, i)))\n}\n\ntype AnyTimeline = Timeline<any, any>\n\n/**\n * Every slice's pop eased from where it is to what `pop` asks for. A move that\n * changes nothing still takes its slot, writing nothing.\n */\nfunction popping(timeline: AnyTimeline, s: PieChartValues, pop: (index: number) => number) {\n const from = s.data.map((_, i) => popAt(s, i))\n const targets = s.data.map((_, i) => clampUnit(pop(i)))\n if (from.every((value, i) => value === targets[i])) return timeline.wait(timeline.duration)\n return timeline.animate((t) => ({ pops: from.map((value, i) => value + (targets[i] - value) * t) }))\n}\n\n/** The ring and the legend, found through the chart's children: the last row of them is the one it built. */\nfunction partsOf(children: readonly ChildView[]) {\n for (let i = children.length - 1; i >= 0; i--) {\n const row = children[i]\n const ring = Rect.is(row) ? row.children.find(PieRing.is) : undefined\n if (ring) return { ring, legend: row.children.find(PieLegend.is) ?? null }\n }\n return null\n}\n\n/**\n * A pie or donut chart, composed from two parts laid out by a horizontal-flow\n * {@link Rect} — the ring filling whatever the hugging legend leaves, both\n * centred:\n *\n * - {@link PieRing} — the wedges, each sized by its share of the total, with its\n * label inside its own band or pinned outside on a leader line, and the\n * summary in the hole.\n * - {@link PieLegend} — a column of swatch-and-caption rows naming each slice.\n *\n * **One mark per row, not per column**: a pie's palette is indexed by row and\n * there is nothing to hide, since dropping a slice would re-cut every other\n * wedge. `selectSlice` and its siblings are what a pie has instead — a slice\n * swells out of the ring and everything else dims behind it.\n *\n * **Resting state is the finished chart**; `enter` arms every part on its first\n * frame. The one exception is the hole's summary, which a scene can hold back\n * with `showCenter: false`, because it is usually the chart's *conclusion*.\n */\nexport const PieChart = component2d({\n key: \"pieChart\",\n state: {\n data: fresh<DataRecord[]>(() => []),\n categoryField: \"\",\n valueField: \"\",\n theme: Types.custom<PieChartTheme, PieChartConfig>(DEFAULT_PIE_CHART_THEME, { resolve: resolvePieChartTheme, lerp: lerpPieChartTheme }),\n labelMode: Types.custom<PieLabelMode>(\"percent\"),\n labelPlacement: Types.custom<PieLabelPlacement>(\"leader\"),\n gap: 40,\n centerLabel: \"\",\n centerCaption: \"\",\n /**\n * How far each slice is pushed out of the ring, `0`–`1` per index. Held here\n * rather than on the ring because two parts read it: the wedges grow out by\n * it, and the legend entries dim by it. Seeded flush, one to a row.\n */\n pops: fresh<number[]>(() => []),\n /** Read as the chart is built: the legend is a part the chart builds or doesn't. */\n showLegend: Types.boolean(true, { animatable: false }),\n /** Read as the chart is built: whether the hole's summary is drawn at rest. */\n showCenter: Types.boolean(true, { animatable: false }),\n },\n defaults: {\n // Its parts fill it; a node made of them would hug them.\n width: \"fill\",\n height: \"fill\",\n pops: (state) => slicesOf(state).map(() => 0),\n },\n allowedChildren: [],\n // The slice *structure* (one label and one legend row per row) is read once;\n // the slices themselves, their paint and the selection are bound, so a theme\n // tween re-paints and a `set` of `data` re-cuts the wedges.\n children: (state) => {\n const theme = () => state.theme\n const slices = () => slicesOf(state)\n const parts: Node2D[] = [\n new PieRing({\n width: \"fill\",\n height: \"fill\",\n slices,\n ring: () => theme().ring,\n label: () => theme().label,\n centerStyle: () => theme().center,\n labelMode: state.labelMode,\n labelPlacement: state.labelPlacement,\n pops: () => state.pops,\n dimOpacity: () => theme().spotlight.dimOpacity,\n centerLabel: state.centerLabel,\n centerCaption: state.centerCaption,\n centerReveal: state.showCenter === false ? 0 : 1,\n }) as unknown as Node2D,\n ]\n if (state.showLegend !== false) {\n parts.push(\n new PieLegend({\n slices,\n legend: () => theme().legend,\n dims: () => sliceDims(state),\n // The column's spacing is the theme's as the chart was built, like the\n // entries' type and chips.\n gap: state.theme.legend.itemGap / 3,\n padding: state.theme.legend.padding,\n }) as unknown as Node2D\n )\n }\n const row = new Rect({ flow: \"horizontal\", width: \"fill\", height: \"fill\", gap: state.gap, align: \"center\", children: parts })\n return asParts([row as unknown as Node2D])\n },\n commands: {\n /**\n * Animate the whole chart in: the wedges sweep in clockwise from the theme's\n * `startAngle`, then the labels fade up and the legend staggers in together.\n * Its first frame arms the wedges, the labels and the legend; the hole's\n * summary keeps its own signal. `duration` is the **whole** entrance.\n */\n enter: {\n args: {},\n // Held from the chart's arrival, so it never shows finished ahead of its draw.\n entrance: true,\n easing: easeOut(),\n run: (timeline, _state, children) => {\n const parts = partsOf(children)\n if (!parts) return timeline.wait(timeline.duration)\n const { ring, legend } = parts\n const sweep = timeline.duration * SWEEP_SHARE\n const labels = timeline.duration * LABEL_SHARE\n const at0 = (piece: Beat[\"piece\"]): Beat => ({ at: 0, duration: 0, piece })\n return beats(timeline, [\n at0(ring.to({ growth: 0, labelReveal: 0 })),\n ...(legend ? [at0(legend.run(\"arm\"))] : []),\n { at: 0, duration: sweep, piece: ring.run(\"sweep\") },\n { at: sweep, duration: labels, piece: ring.run(\"revealLabels\") },\n ...(legend ? [{ at: sweep, duration: labels, piece: legend.run(\"enter\") }] : []),\n ])\n },\n },\n /**\n * Fade the hole's summary up. Held out of `enter` on purpose: what sits in a\n * donut's hole is usually the chart's conclusion, so the scene lands it on\n * its own beat. Pair it with `showCenter: false`.\n */\n revealCenter: {\n args: {},\n easing: easeOut(),\n run: (timeline, _state, children) => partsOf(children)?.ring.run(\"revealCenter\") ?? timeline.wait(timeline.duration),\n },\n /** Grow slice `index` out past the ring's rim and dim the rest, leaving any other selected slice where it is. */\n selectSlice: {\n args: { sliceIndex: SLICE_ARG },\n easing: easeOut(),\n run: (timeline, state) => {\n const index = timeline.args.sliceIndex\n return popping(timeline, state, (i) => (i === index ? 1 : popAt(state, i)))\n },\n },\n /** Settle slice `index` back flush with the ring. */\n deselectSlice: {\n args: { sliceIndex: SLICE_ARG },\n easing: easeOut(),\n run: (timeline, state) => {\n const index = timeline.args.sliceIndex\n return popping(timeline, state, (i) => (i === index ? 0 : popAt(state, i)))\n },\n },\n /** Move the focus to slice `index` alone, settling whatever else was out. */\n selectOnlySlice: {\n args: { sliceIndex: SLICE_ARG },\n easing: easeOut(),\n run: (timeline, state) => {\n const index = timeline.args.sliceIndex\n return popping(timeline, state, (i) => (i === index ? 1 : 0))\n },\n },\n /** Settle every slice flush and bring the whole chart back to full. */\n clearSelection: {\n args: {},\n easing: easeOut(),\n run: (timeline, state) => popping(timeline, state, () => 0),\n },\n },\n})\n\nexport type PieChart = NodeOf<typeof PieChart>\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"pie-legend.js","sourceRoot":"","sources":["../../src/pie-chart/pie-legend.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,KAAK,EACL,WAAW,EACX,OAAO,GAqBR,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,EAAE,SAAS,EAAgB,MAAM,eAAe,CAAA;AACxG,OAAO,EACL,cAAc,EACd,SAAS,GAEV,MAAM,eAAe,CAAA;AAetB,8EAA8E;AAC9E,MAAM,KAAK,GAAG,EAAE,CAAA;AAEhB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC;IACnC,GAAG,EAAE,WAAW;IAChB,KAAK,EAAE;QACL,GAAG,KAAK,CAAC,KAAK;QACd,GAAG,IAAI,CAAC,KAAK;QACb,GAAG,MAAM,CAAC,OAAO;QACjB,MAAM,EAAE,KAAK,CAAa,GAAG,EAAE,CAAC,EAAE,CAAC;QACnC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAe,cAAc,CAAC;QAClD,IAAI,EAAE,KAAK,CAAW,GAAG,EAAE,CAAC,EAAE,CAAC;QAC/B,QAAQ,EAAE,aAAa,EAAE;KAC1B;IACD,8EAA8E;IAC9E,2EAA2E;IAC3E,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,KAAK;QACb,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC;QACxC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO;KACzC;IACD,+EAA+E;IAC/E,4EAA4E;IAC5E,4EAA4E;IAC5E,0DAA0D;IAC1D,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;QAClB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,CAAA;QACvC,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CACnC,IAAI,CAAC;YACH,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAChD,QAAQ,EAAE;gBACR,IAAI,CAAC;oBACH,IAAI,EAAE,YAAY;oBAClB,GAAG,EAAE,EAAE;oBACP,KAAK,EAAE,QAAQ;oBACf,KAAK,EAAE,KAAK;oBACZ,MAAM,EAAE,KAAK;oBACb,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,OAAO;oBACvD,CAAC,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,KAAK;oBAC/C,QAAQ,EAAE;wBACR,IAAI,CAAC;4BACH,KAAK,EAAE,MAAM,CAAC,MAAM;4BACpB,MAAM,EAAE,MAAM,CAAC,MAAM;4BACrB,YAAY,EAAE,MAAM,CAAC,YAAY;4BACjC,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,IAAI,OAAO;4BAChD,MAAM,EAAE,MAAM,CAAC,MAAM;4BACrB,MAAM,EAAE,MAAM,CAAC,MAAM;yBACtB,CAAC;wBACF,IAAI,CAAC;4BACH,GAAG,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC;4BAC7C,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,QAAQ,IAAI,EAAE;yBAChD,CAAC;qBACH;iBACF,CAAC;aACH;SACF,CAAC,CACH,CAAA;IACH,CAAC;IACD,QAAQ,EAAE;QACR,GAAG,KAAK,CAAC,QAAQ;QACjB;;;;WAIG;QACH,GAAG,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,CACvB,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACvG;;;;;;;WAOG;QACH,KAAK,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;YACzB,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAA;YACjC,IAAI,KAAK,KAAK,CAAC;gBAAE,OAAO,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;YACxD,MAAM,IAAI,GAAY,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;YAClD,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;YACzF,uEAAuE;YACvE,sDAAsD;YACtD,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YAChE,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,CAAA;YACpB,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;YAChC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;YACtH,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;QAC3D,CAAC;KACF;IACD,OAAO,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACnG,MAAM,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvF,MAAM,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;CAC7E,CAAC,CAAA","sourcesContent":["import {\r\n Flow,\r\n Paint,\r\n Rect,\r\n Shapes,\r\n Text,\r\n Types,\r\n component2d,\r\n textOps,\r\n type NodeOf,\r\n type RectProps,\r\n // Named so the column's emitted type reaches the Rect surface it spreads through the sdk, not core.\r\n type Alignment as _Alignment,\r\n type CornerRadiusResolved as _CornerRadiusResolved,\r\n type CornerStyleResolved as _CornerStyleResolved,\r\n type Fill as _Fill,\r\n type FillResolved as _FillResolved,\r\n type FlowMode as _FlowMode,\r\n type FlowResolved as _FlowResolved,\r\n type GapSize as _GapSize,\r\n type Node2D as _Node2D,\r\n type Node2DProps as _Node2DProps,\r\n type RectCornerRadius as _RectCornerRadius,\r\n type RectCornerStyle as _RectCornerStyle,\r\n type Shadow as _Shadow,\r\n type ShadowResolved as _ShadowResolved,\r\n type Stroke as _Stroke,\r\n type StrokeResolved as _StrokeResolved,\r\n type Vector2 as _Vector2,\r\n} from \"@motionscript/sdk\"\r\n\r\nimport { fresh } from \"../kit/entries\"\r\nimport { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from \"../kit/motion\"\r\nimport {\r\n DEFAULT_LEGEND,\r\n clampUnit,\r\n type LegendConfig,\r\n} from \"../kit/shared\"\r\nimport type { PieSlice } from \"./shared\"\r\n\r\nexport interface PieLegendProps extends RectProps {\r\n /** One entry per slice, in ring order. */\r\n slices: PieSlice[]\r\n /** Padding, caption style, gaps, and swatch geometry — see {@link LegendConfig}. */\r\n legend: LegendConfig\r\n /**\r\n * Per-slice opacity multiplier, by index — the same signal that dims the ring,\r\n * so an entry fades in step with the wedge it names. Missing entries read as 1.\r\n */\r\n dims: number[]\r\n}\r\n\r\n/** How far an entry starts to the right of its slot when it enters, in px. */\r\nconst SLIDE = 16\r\n\r\n/**\r\n * The legend of a {@link PieChart}: a **vertical-flow column** of entries, one per\r\n * slice, each pairing the slice's caption with its colour swatch.\r\n *\r\n * A column rather than the cartesian charts' row, because it sits *beside* the\r\n * ring rather than beneath the plot — a pie is as tall as it is wide, so the\r\n * space it leaves over is a vertical strip. It is otherwise the same entry: the\r\n * swatch carries the colour, the caption reads in the legend's neutral ink, and\r\n * both take the shared {@link LegendConfig}.\r\n *\r\n * There is no show/hide here. A pie's marks are its *rows*, and a row that\r\n * shouldn't be in the chart shouldn't be in the table — removing one would\r\n * silently re-cut every other wedge's share, which is a different chart rather\r\n * than the same chart with something hidden. Selection is what a pie has\r\n * instead, and it arrives through {@link PieLegendProps.dims}.\r\n */\r\nexport const PieLegend = component2d({\r\n key: \"pieLegend\",\r\n state: {\r\n ...Paint.state,\r\n ...Flow.state,\r\n ...Shapes.corners,\r\n slices: fresh<PieSlice[]>(() => []),\r\n legend: Types.custom<LegendConfig>(DEFAULT_LEGEND),\r\n dims: fresh<number[]>(() => []),\r\n arrivals: arrivalsEntry(),\r\n },\r\n // A column, for the reason `ChartLegend` gives for its row: a `Rect` given no\r\n // flow lays its children out `freeform`, every caption on top of the last.\r\n defaults: {\r\n flow: \"vertical\",\r\n align: \"centerLeft\",\r\n width: \"hug\",\r\n height: \"hug\",\r\n gap: (state) => state.legend.itemGap / 3,\r\n padding: (state) => state.legend.padding,\r\n },\r\n // One entry per slice, ordered top → bottom. The selection dim rides a wrapper\r\n // rather than the entry itself, so the entrance can animate the entry's own\r\n // opacity without the two fighting over one value — opacity multiplies down\r\n // the tree, which is exactly the composition wanted here.\r\n children: (state) => {\r\n const marker = state.legend.markerStyle\r\n return state.slices.map((_, index) =>\r\n Rect({\r\n width: \"hug\",\r\n height: \"hug\",\r\n flow: \"freeform\",\r\n opacity: () => clampUnit(state.dims[index] ?? 1),\r\n children: [\r\n Rect({\r\n flow: \"horizontal\",\r\n gap: 12,\r\n align: \"center\",\r\n width: \"hug\",\r\n height: \"hug\",\r\n opacity: () => arrivalOf(state.arrivals, index).opacity,\r\n x: () => arrivalOf(state.arrivals, index).shift,\r\n children: [\r\n Rect({\r\n width: marker.height,\r\n height: marker.height,\r\n cornerRadius: marker.borderRadius,\r\n fill: () => state.slices[index]?.fill ?? \"white\",\r\n stroke: marker.stroke,\r\n shadow: marker.shadow,\r\n }),\r\n Text({\r\n ...textOps.styleProps(state.legend.textStyle),\r\n text: () => state.slices[index]?.category ?? \"\",\r\n }),\r\n ],\r\n }),\r\n ],\r\n })\r\n )\r\n },\r\n commands: {\r\n ...Paint.commands,\r\n /**\r\n * Every entry at its pre-entrance state, shifted right and transparent: what\r\n * a chart shows ahead of this legend's own beat in its entrance, since\r\n * `enter` arms the entries only on its first frame.\r\n */\r\n arm: (timeline, state) =>\r\n timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], { opacity: 0, shift: SLIDE }) })),\r\n /**\r\n * Slide + fade every entry in from the right, staggered top-to-bottom. Its\r\n * first frame arms them all: shifted right and transparent.\r\n *\r\n * The timeline's duration is the **whole** entrance: the stagger is carved\r\n * out of it rather than added on top, so the command takes exactly the time\r\n * the timeline laid out for it.\r\n */\r\n enter: (timeline, state) => {\r\n const count = state.slices.length\r\n if (count === 0) return timeline.wait(timeline.duration)\r\n const from: Arrival = { opacity: 0, shift: SLIDE }\r\n const arm = timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], from) }))\r\n // Each gap is a fifth of an entry's own move, the rhythm the axes use;\r\n // solved for from the budget rather than added to it.\r\n const per = timeline.duration / (1 + Math.max(0, count - 1) / 5)\r\n const step = per / 5\r\n const ease = frameEase(timeline)\r\n const entries = state.slices.map((_, i) => ({ at: i * step, duration: per, piece: arrival(timeline, i, from, ease) }))\r\n return timeline.together([arm, beats(timeline, entries)])\r\n },\r\n },\r\n measure: (measurer, state, children) => Flow.measure(measurer, state, children, Paint.inset(state)),\r\n layout: (box, state, children) => Flow.layout(box, state, children, Paint.inset(state)),\r\n render: (ctx, state) => Paint.draw(ctx, state, Shapes.rect(ctx.size, state)),\r\n})\r\n\r\nexport type PieLegend = NodeOf<typeof PieLegend>\r\n"]}
1
+ {"version":3,"file":"pie-legend.js","sourceRoot":"","sources":["../../src/pie-chart/pie-legend.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,KAAK,EACL,WAAW,EACX,OAAO,GAqBR,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,EAAE,SAAS,EAAgB,MAAM,eAAe,CAAA;AACxG,OAAO,EACL,cAAc,EACd,SAAS,GAEV,MAAM,eAAe,CAAA;AAetB,8EAA8E;AAC9E,MAAM,KAAK,GAAG,EAAE,CAAA;AAEhB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC;IACnC,GAAG,EAAE,WAAW;IAChB,KAAK,EAAE;QACL,GAAG,KAAK,CAAC,KAAK;QACd,GAAG,IAAI,CAAC,KAAK;QACb,GAAG,MAAM,CAAC,OAAO;QACjB,MAAM,EAAE,KAAK,CAAa,GAAG,EAAE,CAAC,EAAE,CAAC;QACnC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAe,cAAc,CAAC;QAClD,IAAI,EAAE,KAAK,CAAW,GAAG,EAAE,CAAC,EAAE,CAAC;QAC/B,QAAQ,EAAE,aAAa,EAAE;KAC1B;IACD,8EAA8E;IAC9E,2EAA2E;IAC3E,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,KAAK;QACb,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC;QACxC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO;KACzC;IACD,+EAA+E;IAC/E,4EAA4E;IAC5E,4EAA4E;IAC5E,0DAA0D;IAC1D,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;QAClB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,CAAA;QACvC,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CACnC,IAAI,CAAC;YACH,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAChD,QAAQ,EAAE;gBACR,IAAI,CAAC;oBACH,IAAI,EAAE,YAAY;oBAClB,GAAG,EAAE,EAAE;oBACP,KAAK,EAAE,QAAQ;oBACf,KAAK,EAAE,KAAK;oBACZ,MAAM,EAAE,KAAK;oBACb,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,OAAO;oBACvD,CAAC,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,KAAK;oBAC/C,QAAQ,EAAE;wBACR,IAAI,CAAC;4BACH,KAAK,EAAE,MAAM,CAAC,MAAM;4BACpB,MAAM,EAAE,MAAM,CAAC,MAAM;4BACrB,YAAY,EAAE,MAAM,CAAC,YAAY;4BACjC,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,IAAI,OAAO;4BAChD,MAAM,EAAE,MAAM,CAAC,MAAM;4BACrB,MAAM,EAAE,MAAM,CAAC,MAAM;yBACtB,CAAC;wBACF,IAAI,CAAC;4BACH,GAAG,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC;4BAC7C,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,QAAQ,IAAI,EAAE;yBAChD,CAAC;qBACH;iBACF,CAAC;aACH;SACF,CAAC,CACH,CAAA;IACH,CAAC;IACD,QAAQ,EAAE;QACR,GAAG,KAAK,CAAC,QAAQ;QACjB;;;;WAIG;QACH,GAAG,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,CACvB,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACvG;;;;;;;WAOG;QACH,KAAK,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;YACzB,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAA;YACjC,IAAI,KAAK,KAAK,CAAC;gBAAE,OAAO,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;YACxD,MAAM,IAAI,GAAY,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;YAClD,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;YACzF,uEAAuE;YACvE,sDAAsD;YACtD,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YAChE,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,CAAA;YACpB,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;YAChC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;YACtH,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;QAC3D,CAAC;KACF;IACD,OAAO,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACnG,MAAM,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvF,MAAM,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;CAC7E,CAAC,CAAA","sourcesContent":["import {\n Flow,\n Paint,\n Rect,\n Shapes,\n Text,\n Types,\n component2d,\n textOps,\n type NodeOf,\n type RectProps,\n // Named so the column's emitted type reaches the Rect surface it spreads through the sdk, not core.\n type Alignment as _Alignment,\n type CornerRadiusResolved as _CornerRadiusResolved,\n type CornerStyleResolved as _CornerStyleResolved,\n type Fill as _Fill,\n type FillResolved as _FillResolved,\n type FlowMode as _FlowMode,\n type FlowResolved as _FlowResolved,\n type GapSize as _GapSize,\n type Node2D as _Node2D,\n type Node2DProps as _Node2DProps,\n type RectCornerRadius as _RectCornerRadius,\n type RectCornerStyle as _RectCornerStyle,\n type Shadow as _Shadow,\n type ShadowResolved as _ShadowResolved,\n type Stroke as _Stroke,\n type StrokeResolved as _StrokeResolved,\n type Vector2 as _Vector2,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"../kit/entries\"\nimport { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from \"../kit/motion\"\nimport {\n DEFAULT_LEGEND,\n clampUnit,\n type LegendConfig,\n} from \"../kit/shared\"\nimport type { PieSlice } from \"./shared\"\n\nexport interface PieLegendProps extends RectProps {\n /** One entry per slice, in ring order. */\n slices: PieSlice[]\n /** Padding, caption style, gaps, and swatch geometry — see {@link LegendConfig}. */\n legend: LegendConfig\n /**\n * Per-slice opacity multiplier, by index — the same signal that dims the ring,\n * so an entry fades in step with the wedge it names. Missing entries read as 1.\n */\n dims: number[]\n}\n\n/** How far an entry starts to the right of its slot when it enters, in px. */\nconst SLIDE = 16\n\n/**\n * The legend of a {@link PieChart}: a **vertical-flow column** of entries, one per\n * slice, each pairing the slice's caption with its colour swatch.\n *\n * A column rather than the cartesian charts' row, because it sits *beside* the\n * ring rather than beneath the plot — a pie is as tall as it is wide, so the\n * space it leaves over is a vertical strip. It is otherwise the same entry: the\n * swatch carries the colour, the caption reads in the legend's neutral ink, and\n * both take the shared {@link LegendConfig}.\n *\n * There is no show/hide here. A pie's marks are its *rows*, and a row that\n * shouldn't be in the chart shouldn't be in the table — removing one would\n * silently re-cut every other wedge's share, which is a different chart rather\n * than the same chart with something hidden. Selection is what a pie has\n * instead, and it arrives through {@link PieLegendProps.dims}.\n */\nexport const PieLegend = component2d({\n key: \"pieLegend\",\n state: {\n ...Paint.state,\n ...Flow.state,\n ...Shapes.corners,\n slices: fresh<PieSlice[]>(() => []),\n legend: Types.custom<LegendConfig>(DEFAULT_LEGEND),\n dims: fresh<number[]>(() => []),\n arrivals: arrivalsEntry(),\n },\n // A column, for the reason `ChartLegend` gives for its row: a `Rect` given no\n // flow lays its children out `freeform`, every caption on top of the last.\n defaults: {\n flow: \"vertical\",\n align: \"centerLeft\",\n width: \"hug\",\n height: \"hug\",\n gap: (state) => state.legend.itemGap / 3,\n padding: (state) => state.legend.padding,\n },\n // One entry per slice, ordered top → bottom. The selection dim rides a wrapper\n // rather than the entry itself, so the entrance can animate the entry's own\n // opacity without the two fighting over one value — opacity multiplies down\n // the tree, which is exactly the composition wanted here.\n children: (state) => {\n const marker = state.legend.markerStyle\n return state.slices.map((_, index) =>\n Rect({\n width: \"hug\",\n height: \"hug\",\n flow: \"freeform\",\n opacity: () => clampUnit(state.dims[index] ?? 1),\n children: [\n Rect({\n flow: \"horizontal\",\n gap: 12,\n align: \"center\",\n width: \"hug\",\n height: \"hug\",\n opacity: () => arrivalOf(state.arrivals, index).opacity,\n x: () => arrivalOf(state.arrivals, index).shift,\n children: [\n Rect({\n width: marker.height,\n height: marker.height,\n cornerRadius: marker.borderRadius,\n fill: () => state.slices[index]?.fill ?? \"white\",\n stroke: marker.stroke,\n shadow: marker.shadow,\n }),\n Text({\n ...textOps.styleProps(state.legend.textStyle),\n text: () => state.slices[index]?.category ?? \"\",\n }),\n ],\n }),\n ],\n })\n )\n },\n commands: {\n ...Paint.commands,\n /**\n * Every entry at its pre-entrance state, shifted right and transparent: what\n * a chart shows ahead of this legend's own beat in its entrance, since\n * `enter` arms the entries only on its first frame.\n */\n arm: (timeline, state) =>\n timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], { opacity: 0, shift: SLIDE }) })),\n /**\n * Slide + fade every entry in from the right, staggered top-to-bottom. Its\n * first frame arms them all: shifted right and transparent.\n *\n * The timeline's duration is the **whole** entrance: the stagger is carved\n * out of it rather than added on top, so the command takes exactly the time\n * the timeline laid out for it.\n */\n enter: (timeline, state) => {\n const count = state.slices.length\n if (count === 0) return timeline.wait(timeline.duration)\n const from: Arrival = { opacity: 0, shift: SLIDE }\n const arm = timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], from) }))\n // Each gap is a fifth of an entry's own move, the rhythm the axes use;\n // solved for from the budget rather than added to it.\n const per = timeline.duration / (1 + Math.max(0, count - 1) / 5)\n const step = per / 5\n const ease = frameEase(timeline)\n const entries = state.slices.map((_, i) => ({ at: i * step, duration: per, piece: arrival(timeline, i, from, ease) }))\n return timeline.together([arm, beats(timeline, entries)])\n },\n },\n measure: (measurer, state, children) => Flow.measure(measurer, state, children, Paint.inset(state)),\n layout: (box, state, children) => Flow.layout(box, state, children, Paint.inset(state)),\n render: (ctx, state) => Paint.draw(ctx, state, Shapes.rect(ctx.size, state)),\n})\n\nexport type PieLegend = NodeOf<typeof PieLegend>\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"pie-ring.js","sourceRoot":"","sources":["../../src/pie-chart/pie-ring.ts"],"names":[],"mappings":"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","sourcesContent":["import {\r\n Graphics2D,\r\n PathBuilder,\r\n Rect,\r\n Text,\r\n Types,\r\n component2d,\r\n fillOps,\r\n shadowOps,\r\n strokeOps,\r\n textOps,\r\n type Fill,\r\n type FillResolved,\r\n type NodeOf,\r\n type Node2D,\r\n type Node2DProps,\r\n type TextStyle,\r\n type Vector2,\r\n} from \"@motionscript/sdk\"\r\n\r\nimport { asParts, fresh } from \"../kit/entries\"\r\nimport { frameEase } from \"../kit/motion\"\r\nimport { loadPaints } from \"../kit/paints\"\r\nimport { clampUnit, formatPercent } from \"../kit/shared\"\r\nimport {\r\n DEFAULT_PIE_CHART_THEME,\r\n pieArcs,\r\n relaxLabels,\r\n ringPoint,\r\n type CenterConfig,\r\n type LabelConfig,\r\n type PieArc,\r\n type PieLabelMode,\r\n type PieLabelPlacement,\r\n type PieSlice,\r\n type RingConfig,\r\n} from \"./shared\"\r\n\r\nexport interface PieRingProps extends Node2DProps {\r\n /** One entry per slice: its `category`, `value` and `fill`. */\r\n slices: PieSlice[]\r\n /** Ring geometry and paint — gap, hole, start angle, selection growth. */\r\n ring: RingConfig\r\n /** How the labels are set, and the leader lines that point at them. */\r\n label: LabelConfig\r\n /**\r\n * How the hole's summary is set.\r\n *\r\n * Named `centerStyle` rather than `center` because every node already has a\r\n * `center` — the anchor its position is measured from.\r\n */\r\n centerStyle: CenterConfig\r\n /** What the labels say, or `\"none\"` to drop them entirely. */\r\n labelMode: PieLabelMode\r\n /** Where the labels sit: inside their wedge, outside on a leader, or nowhere. */\r\n labelPlacement: PieLabelPlacement\r\n /**\r\n * Sweep of the ring in `[0, 1]`: 0 draws nothing, 1 the full circle. The\r\n * wedges grow around clockwise from the ring's `startAngle` as this rises.\r\n */\r\n growth: number\r\n /**\r\n * Fade of the leader lines and labels in `[0, 1]`, held separate from\r\n * {@link growth} so they arrive *after* the ring has swept in.\r\n */\r\n labelReveal: number\r\n /**\r\n * How far each slice is currently pushed out of the ring, `0`–`1` per index.\r\n * Owned by the chart, so the ring and the legend read one signal.\r\n */\r\n pops: number[]\r\n /** Opacity everything *unselected* falls back to while any slice is selected. */\r\n dimOpacity: number\r\n /**\r\n * Text set in the middle of the ring's hole — the largest uninterrupted area\r\n * the chart owns, and on a donut otherwise empty. A one-line summary there\r\n * routinely does more work than the whole ring around it. Empty (the default)\r\n * leaves the hole clear; ignored when there is no hole.\r\n */\r\n centerLabel: string\r\n /** Smaller second line under {@link centerLabel}. Empty (the default) drops it. */\r\n centerCaption: string\r\n /**\r\n * Fade of the hole's summary in `[0, 1]`. Its own signal, untouched by the\r\n * ring's entrance — a hole label is usually a *conclusion*, so a scene that\r\n * wants it to land on a later beat sets this to 0 and calls\r\n * `PieChart.revealCenter` when it gets there.\r\n */\r\n centerReveal: number\r\n}\r\n\r\n/** Vertical breathing room between one label block and the next, in px. */\r\nconst LABEL_GAP = 12\r\n/** Breathing room past the outermost label, so it never touches the node's edge, in px. */\r\nconst LABEL_PAD = 14\r\n/** Rough width of one glyph as a fraction of the font size, for gutter sizing. */\r\nconst GLYPH_RATIO = 0.62\r\n/** Gap between a wedge's outer edge and where its leader lifts off, in px. */\r\nconst LEADER_LIFTOFF = 6\r\n/** How far past the ring's outer edge a leader runs before it elbows, in px. */\r\nconst LEADER_LENGTH = 34\r\n/** Horizontal run from the elbow to the label, in px. */\r\nconst LABEL_STUB = 26\r\n/** Gap between a leader's end and the label text it points at, in px. */\r\nconst TEXT_INSET = 8\r\n/**\r\n * Floor on the elbow's horizontal distance from the ring's axis, as a fraction\r\n * of the leader radius — so a label pushed clear above (or below) the ring still\r\n * elbows out sideways rather than running straight down the 12 o'clock line.\r\n */\r\nconst MIN_ELBOW = 0.18\r\n/** Segments used to tessellate a full turn of arc; a wedge takes its share. */\r\nconst ARC_STEPS = 240\r\n/** Rounding applied to the leader's elbow, in px. */\r\nconst ELBOW_RADIUS = 8\r\n/** Side padding used when nothing is leadered out — no gutter to reserve. */\r\nconst BARE_PAD = 2\r\n/** An inside label's caption size, as a fraction of its leading line. */\r\nconst INSIDE_CAPTION_RATIO = 0.44\r\n/**\r\n * Where an inside label sits on a **solid pie** (no hole to split the band\r\n * with), as a fraction of the wedge's radius — far enough out that the label\r\n * clears the point every wedge converges on.\r\n */\r\nconst PIE_LABEL_RADIUS = 0.62\r\n/** Fraction of the hole's diameter its summary is allowed to run to. */\r\nconst CENTER_LABEL_FIT = 0.78\r\n/** Fraction of the room inside a wedge that its label is allowed to run to. */\r\nconst INSIDE_LABEL_FIT = 0.9\r\n/** Below this, a wedge's bisector counts as axis-aligned and that term stops binding. */\r\nconst AXIS_EPSILON = 1e-3\r\n/** Fallback type size when a theme style leaves it unset or on autofit. */\r\nconst FALLBACK_SIZE = 26\r\n\r\n/** A style's numeric size, guarding the autofit and unset cases. */\r\nfunction sizeOf(style: TextStyle, fallback = FALLBACK_SIZE): number {\r\n const size = style.fontSize\r\n return typeof size === \"number\" ? size : fallback\r\n}\r\n\r\n/** Where one slice's label sits and the polyline that points at it. */\r\ninterface LabelPlacement {\r\n /** Index of the slice. */\r\n index: number\r\n /** Which side of the ring the label sits on: +1 right, −1 left. */\r\n side: 1 | -1\r\n /** Leader polyline: wedge edge → elbow → label. */\r\n leader: Vector2[]\r\n /** Point the label's inner edge is pinned to. */\r\n text: Vector2\r\n}\r\n\r\n/**\r\n * A ring-band wedge over `a0`→`a1` (radians, clockwise from 12 o'clock) spanning\r\n * `rInner`→`rOuter`, centred on `cx`. Ring wedges trace the outer arc, step in,\r\n * and trace the inner arc back; full pie wedges (`rInner <= 0`) close through the\r\n * centre.\r\n *\r\n * `halfGap` is **half the gap to the neighbouring slice, in px**, shaved off both\r\n * ends of the wedge. It's converted to an angle *per radius* (`halfGap / r` — a\r\n * wider angle near the hole than out at the rim), which is what keeps the channel\r\n * between two slices the same width all the way across the band. A fixed angular\r\n * gap instead reads as a thin pizza slice: right at the rim, pinched shut at the\r\n * hole. The inset is capped at the wedge's own half-span so a sliver of a slice\r\n * narrows to nothing rather than turning inside out.\r\n */\r\nfunction wedgePath(\r\n a0: number,\r\n a1: number,\r\n rInner: number,\r\n rOuter: number,\r\n halfGap: number,\r\n cx: number\r\n): PathBuilder {\r\n const path = new PathBuilder()\r\n const span = a1 - a0\r\n const steps = Math.max(\r\n 2,\r\n Math.ceil((Math.abs(span) / (Math.PI * 2)) * ARC_STEPS)\r\n )\r\n /** Angular inset at radius `r` that puts the wedge's edge `halfGap` px off the divide. */\r\n const insetAt = (r: number): number =>\r\n r <= 0 ? 0 : Math.min(halfGap / r, span / 2.2)\r\n const arcTo = (r: number, i: number): Vector2 => {\r\n const inset = insetAt(r)\r\n const from = a0 + inset\r\n const to = a1 - inset\r\n const point = ringPoint(r, from + ((to - from) * i) / steps)\r\n return { x: point.x + cx, y: point.y }\r\n }\r\n\r\n if (rInner <= 0) {\r\n // Solid pie wedge: the sides converge on the centre, so only the outer rim\r\n // carries the gap.\r\n path.moveTo(cx, 0)\r\n for (let i = 0; i <= steps; i++) {\r\n const p = arcTo(rOuter, i)\r\n path.lineTo(p.x, p.y)\r\n }\r\n path.close()\r\n return path\r\n }\r\n\r\n for (let i = 0; i <= steps; i++) {\r\n const p = arcTo(rOuter, i)\r\n if (i === 0) path.moveTo(p.x, p.y)\r\n else path.lineTo(p.x, p.y)\r\n }\r\n // Back along the inner arc — its own (wider) inset makes each side edge a\r\n // straight chord sitting `halfGap` px off the divide at both ends.\r\n for (let i = steps; i >= 0; i--) {\r\n const p = arcTo(rInner, i)\r\n path.lineTo(p.x, p.y)\r\n }\r\n path.close()\r\n return path\r\n}\r\n\r\n/** What the ring's geometry reads off its entries. */\r\ninterface RingState {\r\n readonly slices: PieSlice[]\r\n readonly ring: RingConfig\r\n readonly label: LabelConfig\r\n readonly centerStyle: CenterConfig\r\n readonly labelMode: PieLabelMode\r\n readonly labelPlacement: PieLabelPlacement\r\n readonly pops: readonly number[]\r\n readonly dimOpacity: number\r\n readonly centerLabel: string\r\n readonly centerCaption: string\r\n}\r\n\r\n/**\r\n * The ring's geometry for one box: every radius, gutter and label position,\r\n * derived from the entries and the size the ring was laid out at, and asked\r\n * afresh wherever it is read — the drawing, and the labels' bound positions\r\n * and sizes alike, so the two cannot disagree.\r\n */\r\nexport class RingGeometry {\r\n constructor(\r\n private readonly s: RingState,\r\n private readonly box: { readonly width: number; readonly height: number }\r\n ) { }\r\n\r\n /** The drawn slices' angular spans, re-read every frame so a value tween re-cuts them. */\r\n get arcs(): PieArc[] {\r\n return pieArcs(this.s.slices, this.s.ring.startAngle)\r\n }\r\n\r\n /** Whether any slice is labelled on a leader — the only thing that costs a gutter. */\r\n get hasLeaderLabels(): boolean {\r\n return this.s.labelPlacement === \"leader\" && this.s.labelMode !== \"none\" && this.arcs.length > 0\r\n }\r\n\r\n // ---- Label sizing ------------------------------------------------------\r\n\r\n /**\r\n * How wide a horizontal label centred in slice `arc`'s band can run before it\r\n * leaves the wedge, in px.\r\n *\r\n * Two things bound it, and which one binds depends entirely on where the wedge\r\n * sits. Stepping sideways from the label's centre moves you *radially* by\r\n * `dx·sin(mid)` and *tangentially* by `dx·cos(mid)`, so a wedge at 3 o'clock is\r\n * limited by the band's thickness (its label runs straight across the band)\r\n * while a wedge at 12 o'clock is limited by its own arc (its label runs along\r\n * the band, which is much roomier). Taking the smaller of the two is what stops\r\n * a label overflowing the ring on one chart and being pointlessly shrunk on the\r\n * next.\r\n *\r\n * Measured off the *unpopped* radius, so a slice growing out of the ring\r\n * doesn't resize its own text mid-animation.\r\n */\r\n insideRoom(arc: PieArc): number {\r\n const outer = this.outerRadius\r\n const hole = this.holeRadius\r\n const band = hole > 0 ? outer - hole : outer * (1 - PIE_LABEL_RADIUS) * 2\r\n const mid = hole > 0 ? (hole + outer) / 2 : outer * PIE_LABEL_RADIUS\r\n const sin = Math.abs(Math.sin(arc.mid))\r\n const cos = Math.abs(Math.cos(arc.mid))\r\n const acrossBand = sin > AXIS_EPSILON ? band / sin : Infinity\r\n const alongArc = cos > AXIS_EPSILON ? ((arc.a1 - arc.a0) * mid) / cos : Infinity\r\n return Math.max(0, Math.min(acrossBand, alongArc))\r\n }\r\n\r\n /** Widest line, in glyphs at the leading line's size, an inside label runs to. */\r\n insideGlyphs(arc: PieArc): number {\r\n const mode = this.s.labelMode\r\n const category = this.s.slices[arc.index]?.category.length ?? 0\r\n const percent = mode === \"both\" || mode === \"percent\" ? formatPercent(arc.fraction).length : 0\r\n // The caption is set at a fraction of the leading line, except when it *is*\r\n // the leading line.\r\n const caption = mode === \"category\" ? category : mode === \"both\" ? category * INSIDE_CAPTION_RATIO : 0\r\n return Math.max(1, percent, caption)\r\n }\r\n\r\n /**\r\n * Font size of an inside label's leading line: what the theme asked for,\r\n * shrunk if that wouldn't fit inside its wedge. **One size across every inside\r\n * label** — the smallest any of them needs — because labels at mismatched\r\n * sizes read as a mistake rather than as emphasis.\r\n *\r\n * So the theme's `insideStyle.fontSize` is an upper bound, not a promise: ask\r\n * for the size the chart wants and the ring keeps it legal.\r\n */\r\n get insideFont(): number {\r\n let size = sizeOf(this.s.label.insideStyle, FALLBACK_SIZE * 1.9)\r\n for (const arc of this.arcs) {\r\n const fits = (this.insideRoom(arc) * INSIDE_LABEL_FIT) / (this.insideGlyphs(arc) * GLYPH_RATIO)\r\n size = Math.min(size, fits)\r\n }\r\n return Math.max(1, size)\r\n }\r\n\r\n /**\r\n * Font size of the hole's summary: the configured size, capped so it still\r\n * fits across the hole. The cap matters because the hole's radius derives from\r\n * the box, so a chart that reflows smaller would otherwise push its summary out\r\n * through the ring.\r\n */\r\n get centerFont(): number {\r\n const { centerStyle, centerLabel, centerCaption } = this.s\r\n const wanted = sizeOf(centerStyle.labelStyle, 46)\r\n const hole = this.holeRadius\r\n if (hole <= 0) return wanted\r\n const captionRatio = sizeOf(centerStyle.captionStyle, 22) / Math.max(1, wanted)\r\n // Both lines have to clear the hole, and the caption is set at a fraction of\r\n // this size — so what binds is whichever is wider once drawn, not whichever\r\n // has more characters.\r\n const glyphs = Math.max(1, centerLabel.length, centerCaption.length * captionRatio)\r\n return Math.min(wanted, (hole * 2 * CENTER_LABEL_FIT) / (glyphs * GLYPH_RATIO))\r\n }\r\n\r\n // ---- Ring geometry, all derived from the box ---------------------------\r\n\r\n /** Rim radius of a fully selected slice as a multiple of the ring's, clamped sane. */\r\n get growFactor(): number {\r\n return Math.max(1, Math.min(1.6, this.s.ring.selectedGrow))\r\n }\r\n\r\n /** How far past the ring's rim a fully selected slice reaches, in px. */\r\n get maxPop(): number {\r\n return this.outerRadius * (this.growFactor - 1)\r\n }\r\n\r\n /** Height of one **leader** label block, in px — the unit those columns are spaced by. */\r\n get labelRowHeight(): number {\r\n // Inside labels are stacked in the band, not in a column beside the ring, so\r\n // they cost no vertical reserve.\r\n if (!this.hasLeaderLabels) return 0\r\n const caption = sizeOf(this.s.label.textStyle) * 1.25\r\n const percent = sizeOf(this.s.label.percentStyle) * 1.25\r\n if (this.s.labelMode === \"both\") return caption + percent + 2\r\n return this.s.labelMode === \"category\" ? caption : percent\r\n }\r\n\r\n /** Widest line, in glyphs, that a leader label for slice `index` can run to. */\r\n captionGlyphs(index: number): number {\r\n const mode = this.s.labelMode\r\n // \"100%\" is the widest a percentage gets; captions are their own length.\r\n const percent = mode === \"percent\" || mode === \"both\" ? 4 : 0\r\n const category = mode === \"category\" || mode === \"both\" ? (this.s.slices[index]?.category.length ?? 0) : 0\r\n return Math.max(percent, category)\r\n }\r\n\r\n /**\r\n * Horizontal room reserved on each side for the leaders and their labels, in\r\n * px — measured **per side**, from the slices that actually leader out that\r\n * way. A ring whose labels are all inside reserves nothing and draws bigger.\r\n *\r\n * Excludes the ring's own selection-growth allowance, which scales with the\r\n * radius and so is divided out in {@link outerRadius} instead.\r\n */\r\n get labelGutters(): { left: number; right: number } {\r\n const gutters = { left: BARE_PAD, right: BARE_PAD }\r\n if (!this.hasLeaderLabels) return gutters\r\n const glyph = sizeOf(this.s.label.textStyle) * GLYPH_RATIO\r\n for (const arc of this.arcs) {\r\n const room = LEADER_LENGTH + LABEL_STUB + TEXT_INSET + this.captionGlyphs(arc.index) * glyph + LABEL_PAD\r\n const key = sideOf(arc.mid) > 0 ? \"right\" : \"left\"\r\n gutters[key] = Math.max(gutters[key], room)\r\n }\r\n return gutters\r\n }\r\n\r\n /**\r\n * How far the ring's centre sits from the node's, in px. Zero while the\r\n * gutters match; when the labels all fall one way it slides the ring into the\r\n * empty half so the ring-plus-labels group stays centred in the box instead of\r\n * the ring sitting dead centre with a bald patch beside it.\r\n */\r\n get centerX(): number {\r\n const { left, right } = this.labelGutters\r\n return (left - right) / 2\r\n }\r\n\r\n /**\r\n * Radius of the ring's outer edge, fitted to whatever the label gutters leave.\r\n * A fully selected slice reaches `radius × growFactor`, so the room has to\r\n * cover that — hence the divisor, which keeps even the grown slice inside the\r\n * box (and holds the ring the same size whether or not anything is selected).\r\n */\r\n get outerRadius(): number {\r\n const { left, right } = this.labelGutters\r\n const room = Math.min((this.box.width - left - right) / 2, this.box.height / 2 - this.labelRowHeight - 4)\r\n return Math.max(0, room / this.growFactor)\r\n }\r\n\r\n /** Radius of the hole in the middle: a fraction of the outer radius, or explicit px. */\r\n get holeRadius(): number {\r\n const configured = this.s.ring.holeRadius\r\n if (configured <= 0) return 0\r\n const px = configured < 1 ? configured * this.outerRadius : configured\r\n return Math.min(px, this.outerRadius * 0.95)\r\n }\r\n\r\n /** Radius of the circle every leader elbows on, clear of even a popped-out slice. */\r\n get elbowRadius(): number {\r\n return this.outerRadius + this.maxPop + LEADER_LENGTH\r\n }\r\n\r\n /** How far slice `index` is currently pushed out, in `[0, 1]`. */\r\n popOf(index: number): number {\r\n return clampUnit(this.s.pops[index] ?? 0)\r\n }\r\n\r\n /** Radius of slice `index`'s outer edge, its selection growth included. */\r\n anchorRadius(index: number): number {\r\n return this.outerRadius + this.maxPop * this.popOf(index)\r\n }\r\n\r\n /**\r\n * Radius an inside label sits at on slice `index`: halfway across the band on a\r\n * donut, and further out than halfway on a solid pie, where \"halfway\" would\r\n * crowd the point every wedge converges on. Rides the slice's own pop, so an\r\n * inside label travels out with the wedge it belongs to.\r\n */\r\n bandMidRadius(index: number): number {\r\n const rim = this.anchorRadius(index)\r\n const hole = this.holeRadius\r\n return hole > 0 ? (hole + rim) / 2 : rim * PIE_LABEL_RADIUS\r\n }\r\n\r\n /** The point slice `index`'s inside label is centred on. */\r\n insideAnchor(index: number): Vector2 {\r\n const arc = this.arcs.find((a) => a.index === index)\r\n if (arc === undefined) return { x: this.centerX, y: 0 }\r\n const point = ringPoint(this.bandMidRadius(index), arc.mid)\r\n return { x: point.x + this.centerX, y: point.y }\r\n }\r\n\r\n /**\r\n * How selected the ring is overall, in `[0, 1]` — the most-grown slice. Rides\r\n * the same tween the pops do, so the dim below arrives with the growth rather\r\n * than needing an animation of its own.\r\n */\r\n get selectionStrength(): number {\r\n let strongest = 0\r\n for (let i = 0; i < this.s.slices.length; i++) strongest = Math.max(strongest, this.popOf(i))\r\n return strongest\r\n }\r\n\r\n /**\r\n * Opacity slice `index` (and its leader and label) is drawn at: full while\r\n * nothing is selected, falling toward `dimOpacity` in proportion to how\r\n * selected the *rest* of the ring is. A slice that's selected itself stays at\r\n * full — the two factors cancel.\r\n */\r\n sliceOpacity(index: number): number {\r\n const focus = this.selectionStrength\r\n if (focus <= 0) return 1\r\n return 1 - (1 - clampUnit(this.s.dimOpacity)) * focus * (1 - this.popOf(index))\r\n }\r\n\r\n /** Slice `index`'s share of the ring as its label reads it, off the live values. */\r\n percentText(index: number): string {\r\n const arc = this.arcs.find((a) => a.index === index)\r\n return formatPercent(arc?.fraction ?? 0)\r\n }\r\n\r\n // ---- Label placement ---------------------------------------------------\r\n\r\n /**\r\n * Where every leader label sits and how its leader gets there. Labels are split\r\n * into a right and a left column by their slice's bisector, each column is\r\n * spread so nothing overlaps (and nothing leaves the box), and each leader is\r\n * then built as wedge edge → elbow → label. The elbow rides the leader circle at\r\n * the label's *settled* y, which keeps the final run horizontal and the whole\r\n * polyline outside the ring however far the label was nudged.\r\n */\r\n placements(): LabelPlacement[] {\r\n if (!this.hasLeaderLabels) return []\r\n const arcs = this.arcs\r\n if (arcs.length === 0 || this.outerRadius <= 0) return []\r\n\r\n const radius = this.elbowRadius\r\n const rowHeight = this.labelRowHeight\r\n const top = this.box.height / 2 - rowHeight / 2\r\n const bottom = -top\r\n const cx = this.centerX\r\n\r\n interface Slot {\r\n index: number\r\n side: 1 | -1\r\n mid: number\r\n ideal: number\r\n y: number\r\n }\r\n const right: Slot[] = []\r\n const left: Slot[] = []\r\n for (const arc of arcs) {\r\n const side = sideOf(arc.mid)\r\n const ideal = ringPoint(radius, arc.mid).y\r\n ;(side > 0 ? right : left).push({ index: arc.index, side, mid: arc.mid, ideal, y: ideal })\r\n }\r\n relaxLabels(right, rowHeight + LABEL_GAP, top, bottom)\r\n relaxLabels(left, rowHeight + LABEL_GAP, top, bottom)\r\n\r\n return [...right, ...left].map((slot) => {\r\n const start = ringPoint(this.anchorRadius(slot.index) + LEADER_LIFTOFF, slot.mid)\r\n // The elbow sits where the label's y crosses the leader circle. Solving\r\n // x² + y² = radius² for x keeps it *on* that circle (never inside the\r\n // ring); a label pushed past the circle's top or bottom falls back to the\r\n // sideways floor so its leader still reads as pointing outward.\r\n const onCircle = Math.sqrt(Math.max(0, radius * radius - slot.y * slot.y))\r\n const elbowX = cx + slot.side * Math.max(onCircle, radius * MIN_ELBOW)\r\n const endX = cx + slot.side * (radius + LABEL_STUB)\r\n\r\n return {\r\n index: slot.index,\r\n side: slot.side,\r\n leader: [\r\n { x: start.x + cx, y: start.y },\r\n { x: elbowX, y: slot.y },\r\n { x: endX, y: slot.y },\r\n ],\r\n text: { x: endX + slot.side * TEXT_INSET, y: slot.y },\r\n }\r\n })\r\n }\r\n\r\n /** This frame's placement for slice `index`, or `undefined` if it isn't leadered. */\r\n placementFor(index: number): LabelPlacement | undefined {\r\n return this.placements().find((placement) => placement.index === index)\r\n }\r\n}\r\n\r\n/** Which side a leader off bisector `mid` runs to: +1 right, −1 left. */\r\nfunction sideOf(mid: number): 1 | -1 {\r\n return Math.sin(mid) >= 0 ? 1 : -1\r\n}\r\n\r\nconst NO_BOX = { width: 0, height: 0 }\r\n\r\n/**\r\n * Each slice's fill, resolved once per paint rather than every frame: the slices\r\n * are bound to the chart's rows and palette, so a theme tween hands over new\r\n * paint and the drawing must see it, but a frame that moves nothing must not\r\n * re-resolve. Keyed by the paint itself, so it is shared and needs no node.\r\n */\r\nconst resolvedFills = new WeakMap<object, FillResolved[]>()\r\n\r\nfunction resolvedFill(fill: Fill): FillResolved[] {\r\n if (typeof fill !== \"object\" || fill === null) return fillOps.resolve(fill)\r\n let resolved = resolvedFills.get(fill)\r\n if (resolved === undefined) resolvedFills.set(fill, resolved = fillOps.resolve(fill))\r\n return resolved\r\n}\r\n\r\n/** Each slice's fill, resolved. */\r\nexport function ringFills(slices: readonly PieSlice[]): FillResolved[][] {\r\n return slices.map((slice) => resolvedFill(slice.fill))\r\n}\r\n\r\n/**\r\n * The labels a ring was built with: one per *drawn* slice (a zero-valued slice\r\n * gets no wedge, so no label either), in whichever of the two forms the chart\r\n * asked for, then the hole's summary. Both are real Text stacked in a hug\r\n * vertical flow — the difference is only what they are pinned to and which line\r\n * leads. The point each is pinned to stays reactive and follows the ring as the\r\n * box resizes or its slice pops out; only the choice of form, which depends on\r\n * entries alone, is settled here.\r\n */\r\nfunction ringLabels(s: RingState & { readonly labelReveal: number; readonly centerReveal: number }) {\r\n const blocks: Node2D[] = []\r\n // A label block's parent is the ring: its box is what the ring was laid out at.\r\n const geometry = () => new RingGeometry(s, (blocks[0]?.parent as Node2D | null | undefined)?.layoutBounds ?? NO_BOX)\r\n\r\n const label = (arc: PieArc): Node2D => {\r\n // Fades in with the ring's labels, then dims along with its own slice\r\n // whenever another slice takes the focus.\r\n const opacity = () => clampUnit(s.labelReveal) * geometry().sliceOpacity(arc.index)\r\n const wantsCategory = s.labelMode === \"both\" || s.labelMode === \"category\"\r\n const wantsPercent = s.labelMode === \"both\" || s.labelMode === \"percent\"\r\n\r\n if (s.labelPlacement === \"inside\") {\r\n // The number leads and the caption sits under it: inside a wedge the\r\n // reader already knows *which* wedge they are looking at, so the magnitude\r\n // is what the label is for. Both sizes are bound rather than read once —\r\n // the fitted size derives from the ring's radius, which isn't known until\r\n // the node has been laid out.\r\n const inside: Node2D[] = []\r\n if (wantsPercent) {\r\n inside.push(new Text({\r\n ...textOps.styleProps(s.label.insideStyle),\r\n text: () => geometry().percentText(arc.index),\r\n fontSize: () => geometry().insideFont,\r\n }))\r\n }\r\n if (wantsCategory) {\r\n inside.push(new Text({\r\n ...textOps.styleProps(s.label.insideStyle),\r\n text: () => s.slices[arc.index]?.category ?? \"\",\r\n fontSize: () => (s.labelMode === \"category\" ? geometry().insideFont : geometry().insideFont * INSIDE_CAPTION_RATIO),\r\n }))\r\n }\r\n return new Rect({\r\n flow: \"vertical\",\r\n width: \"hug\",\r\n height: \"hug\",\r\n gap: 0,\r\n align: \"center\",\r\n opacity,\r\n center: () => geometry().insideAnchor(arc.index),\r\n children: inside,\r\n })\r\n }\r\n\r\n const side = sideOf(arc.mid)\r\n const at = () => geometry().placementFor(arc.index)?.text ?? { x: 0, y: 0 }\r\n const children: Node2D[] = []\r\n if (wantsCategory) {\r\n children.push(new Text({\r\n ...textOps.styleProps(s.label.textStyle),\r\n text: () => s.slices[arc.index]?.category ?? \"\",\r\n textAlign: side > 0 ? \"left\" : \"right\",\r\n }))\r\n }\r\n if (wantsPercent) {\r\n children.push(new Text({\r\n ...textOps.styleProps(s.label.percentStyle),\r\n text: () => geometry().percentText(arc.index),\r\n textAlign: side > 0 ? \"left\" : \"right\",\r\n }))\r\n }\r\n return new Rect({\r\n flow: \"vertical\",\r\n width: \"hug\",\r\n height: \"hug\",\r\n gap: 2,\r\n align: side > 0 ? \"centerLeft\" : \"centerRight\",\r\n opacity,\r\n // Exactly one of these is set; `undefined` anchors are ignored.\r\n centerLeft: side > 0 ? at : undefined,\r\n centerRight: side > 0 ? undefined : at,\r\n children,\r\n })\r\n }\r\n\r\n if (s.labelPlacement !== \"none\" && s.labelMode !== \"none\") {\r\n for (const arc of pieArcs(s.slices, s.ring.startAngle)) blocks.push(label(arc))\r\n }\r\n\r\n // The hole's summary, pinned to the ring's centre so it tracks the sideways\r\n // shift a one-sided label column puts on the ring.\r\n if (s.centerLabel !== \"\" || s.centerCaption !== \"\") {\r\n const children: Node2D[] = []\r\n if (s.centerLabel !== \"\") {\r\n children.push(new Text({\r\n ...textOps.styleProps(s.centerStyle.labelStyle),\r\n text: s.centerLabel,\r\n fontSize: () => geometry().centerFont,\r\n }))\r\n }\r\n if (s.centerCaption !== \"\") {\r\n children.push(new Text({\r\n ...textOps.styleProps(s.centerStyle.captionStyle),\r\n text: s.centerCaption,\r\n fontSize: () =>\r\n geometry().centerFont * (sizeOf(s.centerStyle.captionStyle, 22) / sizeOf(s.centerStyle.labelStyle, 46)),\r\n }))\r\n }\r\n blocks.push(new Rect({\r\n flow: \"vertical\",\r\n width: \"hug\",\r\n height: \"hug\",\r\n gap: 2,\r\n align: \"center\",\r\n opacity: () => clampUnit(s.centerReveal),\r\n center: () => ({ x: geometry().centerX, y: 0 }),\r\n children,\r\n }))\r\n }\r\n return asParts(blocks)\r\n}\r\n\r\n/**\r\n * The ring itself: one wedge per {@link PieSlice}, sized by its share of the\r\n * total, swept clockwise from the theme's `startAngle` with a `gap` of clear\r\n * space between neighbours — a *constant-width* channel, the same at the hole as\r\n * at the rim (see {@link wedgePath}). A **selected** slice reaches further out\r\n * while its hole edge stays put — so it reads as that band swelling out of the\r\n * ring rather than breaking off it — and the rest of the ring dims behind it,\r\n * leaders and labels included.\r\n *\r\n * This is the only part of the chart family that draws a path: the wedges are\r\n * filled {@link PathBuilder} polygons approximating each arc, and the leaders are\r\n * stroked polylines — both in the node's centred, y-up space, so neither depends\r\n * on SVG arc-sweep conventions. The **labels are real {@link Text} nodes**\r\n * (stacked in a hug vertical flow and pinned by their inner edge), so no\r\n * glyph-width guesswork decides where a caption starts.\r\n *\r\n * ### Label placement\r\n * Each label sits either **inside** its wedge or **outside** on a leader.\r\n *\r\n * An inside label is centred on its slice's band and leads with the number, the\r\n * caption set small underneath — inside a wedge the reader already knows which\r\n * slice they're on, so the magnitude is the thing being read. It costs no gutter\r\n * and no eye round-trip, so a ring labelled that way draws considerably bigger in\r\n * the same box.\r\n *\r\n * A leader label sits outside on the end of a polyline that lifts off its wedge,\r\n * elbows, and runs horizontally into the text. Labels are split into a left and a\r\n * right column by which way their slice's bisector points, and each column is\r\n * de-conflicted independently (see {@link relaxLabels}): a label takes the spot\r\n * nearest its wedge that still clears its neighbour, and the column is squeezed\r\n * to fit the node's height rather than spilling out of it. The elbow then rides on\r\n * a circle just outside the ring **at the label's settled y**, so the run into the\r\n * text is always horizontal and a nudged label never drags its leader back across\r\n * the ring.\r\n *\r\n * Only the sides that actually carry a leader reserve a gutter, and the ring's\r\n * centre slides into whatever the other side left over — so a ring whose slices\r\n * all leader one way doesn't leave a bald patch on the other.\r\n */\r\nexport const PieRing = component2d({\r\n key: \"pieRing\",\r\n state: {\r\n slices: fresh<PieSlice[]>(() => []),\r\n ring: Types.custom<RingConfig>(DEFAULT_PIE_CHART_THEME.ring),\r\n label: Types.custom<LabelConfig>(DEFAULT_PIE_CHART_THEME.label),\r\n centerStyle: Types.custom<CenterConfig>(DEFAULT_PIE_CHART_THEME.center),\r\n labelMode: Types.custom<PieLabelMode>(\"percent\"),\r\n labelPlacement: Types.custom<PieLabelPlacement>(\"leader\"),\r\n growth: 1,\r\n labelReveal: 1,\r\n pops: fresh<number[]>(() => []),\r\n dimOpacity: DEFAULT_PIE_CHART_THEME.spotlight.dimOpacity,\r\n centerLabel: \"\",\r\n centerCaption: \"\",\r\n centerReveal: 1,\r\n },\r\n // A plain node filled its parent; one made of its labels would hug them.\r\n defaults: { width: \"fill\", height: \"fill\" },\r\n children: (state) => ringLabels(state),\r\n // The ring's paints live nowhere a paint entry would look: one fill per slice,\r\n // the wedge outline and drop shadow of `ring`, and the leader line's own stroke.\r\n load: (loader, state) =>\r\n loadPaints(loader, {\r\n fills: ringFills(state.slices),\r\n shadows: [shadowOps.resolve(state.ring.shadow)],\r\n strokes: [strokeOps.resolve(state.ring.stroke), strokeOps.resolve(state.label.leaderStroke)],\r\n }),\r\n commands: {\r\n /**\r\n * Tween `growth` `0 → 1`, wiping the wedges in around the ring. No priming\r\n * to 0 first — the value at `t` is a function of `t`, so a repeat call\r\n * re-sweeps by definition rather than by having reset something.\r\n */\r\n sweep: (timeline) => {\r\n const ease = frameEase(timeline)\r\n return timeline.animate((t) => ({ growth: ease(t) }))\r\n },\r\n /** Tween `labelReveal` `0 → 1`, fading the leaders and captions up. */\r\n revealLabels: (timeline) => {\r\n const ease = frameEase(timeline)\r\n return timeline.animate((t) => ({ labelReveal: ease(t) }))\r\n },\r\n /**\r\n * Fade the hole's summary up. Deliberately not part of the ring's entrance: a\r\n * hole label is usually the chart's conclusion, so the scene decides when it\r\n * lands.\r\n */\r\n revealCenter: (timeline, state) => {\r\n const ease = frameEase(timeline)\r\n const from = clampUnit(state.centerReveal)\r\n return timeline.animate((t) => ({ centerReveal: from + (1 - from) * ease(t) }))\r\n },\r\n },\r\n render: (ctx, state) => {\r\n const g = new RingGeometry(state, ctx.size)\r\n const rOuter = g.outerRadius\r\n const arcs = rOuter > 0 ? g.arcs : []\r\n if (arcs.length > 0) {\r\n const sweep = clampUnit(state.growth)\r\n const revealEnd = state.ring.startAngle * (Math.PI / 180) + Math.PI * 2 * sweep\r\n const hole = g.holeRadius\r\n const halfGap = Math.max(0, state.ring.gap) / 2\r\n\r\n // One shared frame (the whole ring square, grown slices included) pinned onto\r\n // every wedge path, so `Graphics2D.path()` doesn't re-centre each wedge on its\r\n // own bbox and collapse them all onto the origin. It stays symmetric about the\r\n // *node's* origin — so path coordinates land exactly in node space — and is\r\n // grown by the ring's sideways offset rather than moved with it, which would\r\n // undo the very shift it has to contain.\r\n const cx = g.centerX\r\n const half = rOuter + g.maxPop + 1 + Math.abs(cx)\r\n const centerBounds: [number, number, number, number] = [-half, -half, half, half]\r\n // Explicit width/height alongside centerBounds: a wedge path's command list\r\n // carries no implied extent, so a slice filled with an image or a video needs\r\n // this to size its decode.\r\n const span = half * 2\r\n\r\n const fills = ringFills(state.slices)\r\n for (const arc of arcs) {\r\n // Clip the wedge to the growth-revealed arc; skip it until the sweep\r\n // reaches it, so the ring draws in as one clockwise wipe.\r\n const a1 = Math.min(arc.a1, revealEnd)\r\n if (a1 <= arc.a0) continue\r\n\r\n const path = wedgePath(arc.a0, a1, hole, g.anchorRadius(arc.index), halfGap, cx)\r\n const wedge = new Graphics2D().path(path.toPathState({ centerBounds, width: span, height: span }))\r\n wedge.shadow(state.ring.shadow)\r\n wedge.fill(fills[arc.index] ?? [])\r\n wedge.stroke(state.ring.stroke)\r\n ctx.draw(wedge.opacity(g.sliceOpacity(arc.index)))\r\n }\r\n\r\n // Leaders. Each polyline gets its own `Graphics2D`: several open polylines\r\n // chained into one command list are combined into a single surface before the\r\n // stroke, which closes each contour back on itself and paints the leaders as\r\n // thin triangles.\r\n const reveal = clampUnit(state.labelReveal)\r\n if (reveal > 0.001) {\r\n for (const placement of g.placements()) {\r\n ctx.draw(\r\n new Graphics2D()\r\n .line({ points: placement.leader, radius: ELBOW_RADIUS })\r\n .stroke(state.label.leaderStroke)\r\n .opacity(reveal * g.sliceOpacity(placement.index))\r\n )\r\n }\r\n }\r\n }\r\n ctx.children()\r\n },\r\n})\r\n\r\nexport type PieRing = NodeOf<typeof PieRing>\r\n"]}
1
+ {"version":3,"file":"pie-ring.js","sourceRoot":"","sources":["../../src/pie-chart/pie-ring.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,WAAW,EACX,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,EACT,SAAS,EACT,OAAO,GAQR,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AACxD,OAAO,EACL,uBAAuB,EACvB,OAAO,EACP,WAAW,EACX,SAAS,GAQV,MAAM,UAAU,CAAA;AAuDjB,2EAA2E;AAC3E,MAAM,SAAS,GAAG,EAAE,CAAA;AACpB,2FAA2F;AAC3F,MAAM,SAAS,GAAG,EAAE,CAAA;AACpB,kFAAkF;AAClF,MAAM,WAAW,GAAG,IAAI,CAAA;AACxB,8EAA8E;AAC9E,MAAM,cAAc,GAAG,CAAC,CAAA;AACxB,gFAAgF;AAChF,MAAM,aAAa,GAAG,EAAE,CAAA;AACxB,yDAAyD;AACzD,MAAM,UAAU,GAAG,EAAE,CAAA;AACrB,yEAAyE;AACzE,MAAM,UAAU,GAAG,CAAC,CAAA;AACpB;;;;GAIG;AACH,MAAM,SAAS,GAAG,IAAI,CAAA;AACtB,+EAA+E;AAC/E,MAAM,SAAS,GAAG,GAAG,CAAA;AACrB,qDAAqD;AACrD,MAAM,YAAY,GAAG,CAAC,CAAA;AACtB,6EAA6E;AAC7E,MAAM,QAAQ,GAAG,CAAC,CAAA;AAClB,yEAAyE;AACzE,MAAM,oBAAoB,GAAG,IAAI,CAAA;AACjC;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,IAAI,CAAA;AAC7B,wEAAwE;AACxE,MAAM,gBAAgB,GAAG,IAAI,CAAA;AAC7B,+EAA+E;AAC/E,MAAM,gBAAgB,GAAG,GAAG,CAAA;AAC5B,yFAAyF;AACzF,MAAM,YAAY,GAAG,IAAI,CAAA;AACzB,2EAA2E;AAC3E,MAAM,aAAa,GAAG,EAAE,CAAA;AAExB,oEAAoE;AACpE,SAAS,MAAM,CAAC,KAAgB,EAAE,QAAQ,GAAG,aAAa;IACxD,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAA;IAC3B,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAA;AACnD,CAAC;AAcD;;;;;;;;;;;;;GAaG;AACH,SAAS,SAAS,CAChB,EAAU,EACV,EAAU,EACV,MAAc,EACd,MAAc,EACd,OAAe,EACf,EAAU;IAEV,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAA;IAC9B,MAAM,IAAI,GAAG,EAAE,GAAG,EAAE,CAAA;IACpB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,CAAC,EACD,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CACxD,CAAA;IACD,0FAA0F;IAC1F,MAAM,OAAO,GAAG,CAAC,CAAS,EAAU,EAAE,CACpC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,EAAE,IAAI,GAAG,GAAG,CAAC,CAAA;IAChD,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,CAAS,EAAW,EAAE;QAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;QACxB,MAAM,IAAI,GAAG,EAAE,GAAG,KAAK,CAAA;QACvB,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAA;QACrB,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAA;QAC5D,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAA;IACxC,CAAC,CAAA;IAED,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAChB,2EAA2E;QAC3E,mBAAmB;QACnB,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;QAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAChC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;YAC1B,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;QACvB,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,CAAA;QACZ,OAAO,IAAI,CAAA;IACb,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;QAC1B,IAAI,CAAC,KAAK,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;;YAC7B,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5B,CAAC;IACD,0EAA0E;IAC1E,mEAAmE;IACnE,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;QAC1B,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IACvB,CAAC;IACD,IAAI,CAAC,KAAK,EAAE,CAAA;IACZ,OAAO,IAAI,CAAA;AACb,CAAC;AAgBD;;;;;GAKG;AACH,MAAM,OAAO,YAAY;IAEJ;IACA;IAFnB,YACmB,CAAY,EACZ,GAAwD;QADxD,MAAC,GAAD,CAAC,CAAW;QACZ,QAAG,GAAH,GAAG,CAAqD;IACvE,CAAC;IAEL,0FAA0F;IAC1F,IAAI,IAAI;QACN,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACvD,CAAC;IAED,sFAAsF;IACtF,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,CAAC,CAAC,cAAc,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;IAClG,CAAC;IAED,2EAA2E;IAE3E;;;;;;;;;;;;;;;OAeG;IACH,UAAU,CAAC,GAAW;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAA;QAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAA;QAC5B,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;QACzE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,gBAAgB,CAAA;QACpE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QACvC,MAAM,UAAU,GAAG,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAA;QAC7D,MAAM,QAAQ,GAAG,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAA;QAChF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAA;IACpD,CAAC;IAED,kFAAkF;IAClF,YAAY,CAAC,GAAW;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAA;QAC/D,MAAM,OAAO,GAAG,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QAC9F,4EAA4E;QAC5E,oBAAoB;QACpB,MAAM,OAAO,GAAG,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAA;QACtG,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACtC,CAAC;IAED;;;;;;;;OAQG;IACH,IAAI,UAAU;QACZ,IAAI,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,aAAa,GAAG,GAAG,CAAC,CAAA;QAChE,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,CAAA;YAC/F,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC7B,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;IAC1B,CAAC;IAED;;;;;OAKG;IACH,IAAI,UAAU;QACZ,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,CAAC,CAAA;QAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAA;QAC5B,IAAI,IAAI,IAAI,CAAC;YAAE,OAAO,MAAM,CAAA;QAC5B,MAAM,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC,YAAY,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;QAC/E,6EAA6E;QAC7E,4EAA4E;QAC5E,uBAAuB;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,GAAG,YAAY,CAAC,CAAA;QACnF,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC,CAAA;IACjF,CAAC;IAED,2EAA2E;IAE3E,sFAAsF;IACtF,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAA;IAC7D,CAAC;IAED,yEAAyE;IACzE,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,WAAW,GAAG,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAA;IACjD,CAAC;IAED,0FAA0F;IAC1F,IAAI,cAAc;QAChB,6EAA6E;QAC7E,iCAAiC;QACjC,IAAI,CAAC,IAAI,CAAC,eAAe;YAAE,OAAO,CAAC,CAAA;QACnC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,IAAI,CAAA;QACrD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,IAAI,CAAA;QACxD,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,KAAK,MAAM;YAAE,OAAO,OAAO,GAAG,OAAO,GAAG,CAAC,CAAA;QAC7D,OAAO,IAAI,CAAC,CAAC,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAA;IAC5D,CAAC;IAED,gFAAgF;IAChF,aAAa,CAAC,KAAa;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;QAC7B,yEAAyE;QACzE,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7D,MAAM,QAAQ,GAAG,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1G,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IACpC,CAAC;IAED;;;;;;;OAOG;IACH,IAAI,YAAY;QACd,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;QACnD,IAAI,CAAC,IAAI,CAAC,eAAe;YAAE,OAAO,OAAO,CAAA;QACzC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,WAAW,CAAA;QAC1D,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,aAAa,GAAG,UAAU,GAAG,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,SAAS,CAAA;YACxG,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAA;YAClD,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAA;QAC7C,CAAC;QACD,OAAO,OAAO,CAAA;IAChB,CAAC;IAED;;;;;OAKG;IACH,IAAI,OAAO;QACT,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,YAAY,CAAA;QACzC,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;IAC3B,CAAC;IAED;;;;;OAKG;IACH,IAAI,WAAW;QACb,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,YAAY,CAAA;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC,CAAA;QACzG,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,CAAA;IAC5C,CAAC;IAED,wFAAwF;IACxF,IAAI,UAAU;QACZ,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAA;QACzC,IAAI,UAAU,IAAI,CAAC;YAAE,OAAO,CAAC,CAAA;QAC7B,MAAM,EAAE,GAAG,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAA;QACtE,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;IAC9C,CAAC;IAED,qFAAqF;IACrF,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,GAAG,aAAa,CAAA;IACvD,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,KAAa;QACjB,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;IAC3C,CAAC;IAED,2EAA2E;IAC3E,YAAY,CAAC,KAAa;QACxB,OAAO,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC3D,CAAC;IAED;;;;;OAKG;IACH,aAAa,CAAC,KAAa;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAA;QAC5B,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,gBAAgB,CAAA;IAC7D,CAAC;IAED,4DAA4D;IAC5D,YAAY,CAAC,KAAa;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAA;QACpD,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;QACvD,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAA;QAC3D,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAA;IAClD,CAAC;IAED;;;;OAIG;IACH,IAAI,iBAAiB;QACnB,IAAI,SAAS,GAAG,CAAC,CAAA;QACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE;YAAE,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7F,OAAO,SAAS,CAAA;IAClB,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,KAAa;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,CAAA;QACpC,IAAI,KAAK,IAAI,CAAC;YAAE,OAAO,CAAC,CAAA;QACxB,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;IACjF,CAAC;IAED,oFAAoF;IACpF,WAAW,CAAC,KAAa;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAA;QACpD,OAAO,aAAa,CAAC,GAAG,EAAE,QAAQ,IAAI,CAAC,CAAC,CAAA;IAC1C,CAAC;IAED,2EAA2E;IAE3E;;;;;;;OAOG;IACH,UAAU;QACR,IAAI,CAAC,IAAI,CAAC,eAAe;YAAE,OAAO,EAAE,CAAA;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;QACtB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC;YAAE,OAAO,EAAE,CAAA;QAEzD,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAA;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,CAAA;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,SAAS,GAAG,CAAC,CAAA;QAC/C,MAAM,MAAM,GAAG,CAAC,GAAG,CAAA;QACnB,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAA;QASvB,MAAM,KAAK,GAAW,EAAE,CAAA;QACxB,MAAM,IAAI,GAAW,EAAE,CAAA;QACvB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAC5B,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CACzC;YAAA,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;QAC5F,CAAC;QACD,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;QACtD,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;QAErD,OAAO,CAAC,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACtC,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,cAAc,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;YACjF,wEAAwE;YACxE,sEAAsE;YACtE,0EAA0E;YAC1E,gEAAgE;YAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;YAC1E,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;YACtE,MAAM,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,UAAU,CAAC,CAAA;YAEnD,OAAO;gBACL,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM,EAAE;oBACN,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE;oBAC/B,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE;oBACxB,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE;iBACvB;gBACD,IAAI,EAAE,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,UAAU,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE;aACtD,CAAA;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,qFAAqF;IACrF,YAAY,CAAC,KAAa;QACxB,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,KAAK,KAAK,CAAC,CAAA;IACzE,CAAC;CACF;AAED,yEAAyE;AACzE,SAAS,MAAM,CAAC,GAAW;IACzB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACpC,CAAC;AAED,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAA;AAEtC;;;;;GAKG;AACH,MAAM,aAAa,GAAG,IAAI,OAAO,EAA0B,CAAA;AAE3D,SAAS,YAAY,CAAC,IAAU;IAC9B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3E,IAAI,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACtC,IAAI,QAAQ,KAAK,SAAS;QAAE,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IACrF,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,SAAS,CAAC,MAA2B;IACnD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;AACxD,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,UAAU,CAAC,CAA8E;IAChG,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,gFAAgF;IAChF,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,CAAC,EAAG,MAAM,CAAC,CAAC,CAAC,EAAE,MAAoC,EAAE,YAAY,IAAI,MAAM,CAAC,CAAA;IAEpH,MAAM,KAAK,GAAG,CAAC,GAAW,EAAU,EAAE;QACpC,sEAAsE;QACtE,0CAA0C;QAC1C,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,QAAQ,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACnF,MAAM,aAAa,GAAG,CAAC,CAAC,SAAS,KAAK,MAAM,IAAI,CAAC,CAAC,SAAS,KAAK,UAAU,CAAA;QAC1E,MAAM,YAAY,GAAG,CAAC,CAAC,SAAS,KAAK,MAAM,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,CAAA;QAExE,IAAI,CAAC,CAAC,cAAc,KAAK,QAAQ,EAAE,CAAC;YAClC,qEAAqE;YACrE,2EAA2E;YAC3E,yEAAyE;YACzE,0EAA0E;YAC1E,8BAA8B;YAC9B,MAAM,MAAM,GAAa,EAAE,CAAA;YAC3B,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;oBACnB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC;oBAC1C,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;oBAC7C,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,UAAU;iBACtC,CAAC,CAAC,CAAA;YACL,CAAC;YACD,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;oBACnB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC;oBAC1C,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,QAAQ,IAAI,EAAE;oBAC/C,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,UAAU,GAAG,oBAAoB,CAAC;iBACpH,CAAC,CAAC,CAAA;YACL,CAAC;YACD,OAAO,IAAI,IAAI,CAAC;gBACd,IAAI,EAAE,UAAU;gBAChB,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,CAAC;gBACN,KAAK,EAAE,QAAQ;gBACf,OAAO;gBACP,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC;gBAChD,QAAQ,EAAE,MAAM;aACjB,CAAC,CAAA;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC5B,MAAM,EAAE,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;QAC3E,MAAM,QAAQ,GAAa,EAAE,CAAA;QAC7B,IAAI,aAAa,EAAE,CAAC;YAClB,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;gBACrB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC;gBACxC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,QAAQ,IAAI,EAAE;gBAC/C,SAAS,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;aACvC,CAAC,CAAC,CAAA;QACL,CAAC;QACD,IAAI,YAAY,EAAE,CAAC;YACjB,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;gBACrB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC;gBAC3C,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;gBAC7C,SAAS,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;aACvC,CAAC,CAAC,CAAA;QACL,CAAC;QACD,OAAO,IAAI,IAAI,CAAC;YACd,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,KAAK;YACb,GAAG,EAAE,CAAC;YACN,KAAK,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa;YAC9C,OAAO;YACP,gEAAgE;YAChE,UAAU,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;YACrC,WAAW,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;YACtC,QAAQ;SACT,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,IAAI,CAAC,CAAC,cAAc,KAAK,MAAM,IAAI,CAAC,CAAC,SAAS,KAAK,MAAM,EAAE,CAAC;QAC1D,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;IACjF,CAAC;IAED,4EAA4E;IAC5E,mDAAmD;IACnD,IAAI,CAAC,CAAC,WAAW,KAAK,EAAE,IAAI,CAAC,CAAC,aAAa,KAAK,EAAE,EAAE,CAAC;QACnD,MAAM,QAAQ,GAAa,EAAE,CAAA;QAC7B,IAAI,CAAC,CAAC,WAAW,KAAK,EAAE,EAAE,CAAC;YACzB,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;gBACrB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC;gBAC/C,IAAI,EAAE,CAAC,CAAC,WAAW;gBACnB,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,UAAU;aACtC,CAAC,CAAC,CAAA;QACL,CAAC;QACD,IAAI,CAAC,CAAC,aAAa,KAAK,EAAE,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;gBACrB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,YAAY,CAAC;gBACjD,IAAI,EAAE,CAAC,CAAC,aAAa;gBACrB,QAAQ,EAAE,GAAG,EAAE,CACb,QAAQ,EAAE,CAAC,UAAU,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,YAAY,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;aAC1G,CAAC,CAAC,CAAA;QACL,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;YACnB,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,KAAK;YACb,GAAG,EAAE,CAAC;YACN,KAAK,EAAE,QAAQ;YACf,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC;YACxC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;YAC/C,QAAQ;SACT,CAAC,CAAC,CAAA;IACL,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAA;AACxB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,WAAW,CAAC;IACjC,GAAG,EAAE,SAAS;IACd,KAAK,EAAE;QACL,MAAM,EAAE,KAAK,CAAa,GAAG,EAAE,CAAC,EAAE,CAAC;QACnC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAa,uBAAuB,CAAC,IAAI,CAAC;QAC5D,KAAK,EAAE,KAAK,CAAC,MAAM,CAAc,uBAAuB,CAAC,KAAK,CAAC;QAC/D,WAAW,EAAE,KAAK,CAAC,MAAM,CAAe,uBAAuB,CAAC,MAAM,CAAC;QACvE,SAAS,EAAE,KAAK,CAAC,MAAM,CAAe,SAAS,CAAC;QAChD,cAAc,EAAE,KAAK,CAAC,MAAM,CAAoB,QAAQ,CAAC;QACzD,MAAM,EAAE,CAAC;QACT,WAAW,EAAE,CAAC;QACd,IAAI,EAAE,KAAK,CAAW,GAAG,EAAE,CAAC,EAAE,CAAC;QAC/B,UAAU,EAAE,uBAAuB,CAAC,SAAS,CAAC,UAAU;QACxD,WAAW,EAAE,EAAE;QACf,aAAa,EAAE,EAAE;QACjB,YAAY,EAAE,CAAC;KAChB;IACD,yEAAyE;IACzE,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE;IAC3C,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC;IACtC,+EAA+E;IAC/E,iFAAiF;IACjF,IAAI,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CACtB,UAAU,CAAC,MAAM,EAAE;QACjB,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC;QAC9B,OAAO,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/C,OAAO,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;KAC7F,CAAC;IACJ,QAAQ,EAAE;QACR;;;;WAIG;QACH,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE;YAClB,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;YAChC,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACvD,CAAC;QACD,uEAAuE;QACvE,YAAY,EAAE,CAAC,QAAQ,EAAE,EAAE;YACzB,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;YAChC,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QAC5D,CAAC;QACD;;;;WAIG;QACH,YAAY,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;YAChC,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;YAChC,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;YAC1C,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACjF,CAAC;KACF;IACD,MAAM,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;QACrB,MAAM,CAAC,GAAG,IAAI,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;QAC3C,MAAM,MAAM,GAAG,CAAC,CAAC,WAAW,CAAA;QAC5B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;QACrC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YACrC,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAA;YAC/E,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,CAAA;YACzB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAE/C,8EAA8E;YAC9E,+EAA+E;YAC/E,+EAA+E;YAC/E,4EAA4E;YAC5E,6EAA6E;YAC7E,yCAAyC;YACzC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAA;YACpB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACjD,MAAM,YAAY,GAAqC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;YACjF,4EAA4E;YAC5E,8EAA8E;YAC9E,2BAA2B;YAC3B,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAA;YAErB,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YACrC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,qEAAqE;gBACrE,0DAA0D;gBAC1D,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAA;gBACtC,IAAI,EAAE,IAAI,GAAG,CAAC,EAAE;oBAAE,SAAQ;gBAE1B,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAA;gBAChF,MAAM,KAAK,GAAG,IAAI,UAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;gBAClG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;gBAC/B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;gBAClC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;gBAC/B,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YACpD,CAAC;YAED,2EAA2E;YAC3E,8EAA8E;YAC9E,6EAA6E;YAC7E,kBAAkB;YAClB,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;YAC3C,IAAI,MAAM,GAAG,KAAK,EAAE,CAAC;gBACnB,KAAK,MAAM,SAAS,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC;oBACvC,GAAG,CAAC,IAAI,CACN,IAAI,UAAU,EAAE;yBACb,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;yBACxD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC;yBAChC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CACrD,CAAA;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QACD,GAAG,CAAC,QAAQ,EAAE,CAAA;IAChB,CAAC;CACF,CAAC,CAAA","sourcesContent":["import {\n Graphics2D,\n PathBuilder,\n Rect,\n Text,\n Types,\n component2d,\n fillOps,\n shadowOps,\n strokeOps,\n textOps,\n type Fill,\n type FillResolved,\n type NodeOf,\n type Node2D,\n type Node2DProps,\n type TextStyle,\n type Vector2,\n} from \"@motionscript/sdk\"\n\nimport { asParts, fresh } from \"../kit/entries\"\nimport { frameEase } from \"../kit/motion\"\nimport { loadPaints } from \"../kit/paints\"\nimport { clampUnit, formatPercent } from \"../kit/shared\"\nimport {\n DEFAULT_PIE_CHART_THEME,\n pieArcs,\n relaxLabels,\n ringPoint,\n type CenterConfig,\n type LabelConfig,\n type PieArc,\n type PieLabelMode,\n type PieLabelPlacement,\n type PieSlice,\n type RingConfig,\n} from \"./shared\"\n\nexport interface PieRingProps extends Node2DProps {\n /** One entry per slice: its `category`, `value` and `fill`. */\n slices: PieSlice[]\n /** Ring geometry and paint — gap, hole, start angle, selection growth. */\n ring: RingConfig\n /** How the labels are set, and the leader lines that point at them. */\n label: LabelConfig\n /**\n * How the hole's summary is set.\n *\n * Named `centerStyle` rather than `center` because every node already has a\n * `center` — the anchor its position is measured from.\n */\n centerStyle: CenterConfig\n /** What the labels say, or `\"none\"` to drop them entirely. */\n labelMode: PieLabelMode\n /** Where the labels sit: inside their wedge, outside on a leader, or nowhere. */\n labelPlacement: PieLabelPlacement\n /**\n * Sweep of the ring in `[0, 1]`: 0 draws nothing, 1 the full circle. The\n * wedges grow around clockwise from the ring's `startAngle` as this rises.\n */\n growth: number\n /**\n * Fade of the leader lines and labels in `[0, 1]`, held separate from\n * {@link growth} so they arrive *after* the ring has swept in.\n */\n labelReveal: number\n /**\n * How far each slice is currently pushed out of the ring, `0`–`1` per index.\n * Owned by the chart, so the ring and the legend read one signal.\n */\n pops: number[]\n /** Opacity everything *unselected* falls back to while any slice is selected. */\n dimOpacity: number\n /**\n * Text set in the middle of the ring's hole — the largest uninterrupted area\n * the chart owns, and on a donut otherwise empty. A one-line summary there\n * routinely does more work than the whole ring around it. Empty (the default)\n * leaves the hole clear; ignored when there is no hole.\n */\n centerLabel: string\n /** Smaller second line under {@link centerLabel}. Empty (the default) drops it. */\n centerCaption: string\n /**\n * Fade of the hole's summary in `[0, 1]`. Its own signal, untouched by the\n * ring's entrance — a hole label is usually a *conclusion*, so a scene that\n * wants it to land on a later beat sets this to 0 and calls\n * `PieChart.revealCenter` when it gets there.\n */\n centerReveal: number\n}\n\n/** Vertical breathing room between one label block and the next, in px. */\nconst LABEL_GAP = 12\n/** Breathing room past the outermost label, so it never touches the node's edge, in px. */\nconst LABEL_PAD = 14\n/** Rough width of one glyph as a fraction of the font size, for gutter sizing. */\nconst GLYPH_RATIO = 0.62\n/** Gap between a wedge's outer edge and where its leader lifts off, in px. */\nconst LEADER_LIFTOFF = 6\n/** How far past the ring's outer edge a leader runs before it elbows, in px. */\nconst LEADER_LENGTH = 34\n/** Horizontal run from the elbow to the label, in px. */\nconst LABEL_STUB = 26\n/** Gap between a leader's end and the label text it points at, in px. */\nconst TEXT_INSET = 8\n/**\n * Floor on the elbow's horizontal distance from the ring's axis, as a fraction\n * of the leader radius — so a label pushed clear above (or below) the ring still\n * elbows out sideways rather than running straight down the 12 o'clock line.\n */\nconst MIN_ELBOW = 0.18\n/** Segments used to tessellate a full turn of arc; a wedge takes its share. */\nconst ARC_STEPS = 240\n/** Rounding applied to the leader's elbow, in px. */\nconst ELBOW_RADIUS = 8\n/** Side padding used when nothing is leadered out — no gutter to reserve. */\nconst BARE_PAD = 2\n/** An inside label's caption size, as a fraction of its leading line. */\nconst INSIDE_CAPTION_RATIO = 0.44\n/**\n * Where an inside label sits on a **solid pie** (no hole to split the band\n * with), as a fraction of the wedge's radius — far enough out that the label\n * clears the point every wedge converges on.\n */\nconst PIE_LABEL_RADIUS = 0.62\n/** Fraction of the hole's diameter its summary is allowed to run to. */\nconst CENTER_LABEL_FIT = 0.78\n/** Fraction of the room inside a wedge that its label is allowed to run to. */\nconst INSIDE_LABEL_FIT = 0.9\n/** Below this, a wedge's bisector counts as axis-aligned and that term stops binding. */\nconst AXIS_EPSILON = 1e-3\n/** Fallback type size when a theme style leaves it unset or on autofit. */\nconst FALLBACK_SIZE = 26\n\n/** A style's numeric size, guarding the autofit and unset cases. */\nfunction sizeOf(style: TextStyle, fallback = FALLBACK_SIZE): number {\n const size = style.fontSize\n return typeof size === \"number\" ? size : fallback\n}\n\n/** Where one slice's label sits and the polyline that points at it. */\ninterface LabelPlacement {\n /** Index of the slice. */\n index: number\n /** Which side of the ring the label sits on: +1 right, −1 left. */\n side: 1 | -1\n /** Leader polyline: wedge edge → elbow → label. */\n leader: Vector2[]\n /** Point the label's inner edge is pinned to. */\n text: Vector2\n}\n\n/**\n * A ring-band wedge over `a0`→`a1` (radians, clockwise from 12 o'clock) spanning\n * `rInner`→`rOuter`, centred on `cx`. Ring wedges trace the outer arc, step in,\n * and trace the inner arc back; full pie wedges (`rInner <= 0`) close through the\n * centre.\n *\n * `halfGap` is **half the gap to the neighbouring slice, in px**, shaved off both\n * ends of the wedge. It's converted to an angle *per radius* (`halfGap / r` — a\n * wider angle near the hole than out at the rim), which is what keeps the channel\n * between two slices the same width all the way across the band. A fixed angular\n * gap instead reads as a thin pizza slice: right at the rim, pinched shut at the\n * hole. The inset is capped at the wedge's own half-span so a sliver of a slice\n * narrows to nothing rather than turning inside out.\n */\nfunction wedgePath(\n a0: number,\n a1: number,\n rInner: number,\n rOuter: number,\n halfGap: number,\n cx: number\n): PathBuilder {\n const path = new PathBuilder()\n const span = a1 - a0\n const steps = Math.max(\n 2,\n Math.ceil((Math.abs(span) / (Math.PI * 2)) * ARC_STEPS)\n )\n /** Angular inset at radius `r` that puts the wedge's edge `halfGap` px off the divide. */\n const insetAt = (r: number): number =>\n r <= 0 ? 0 : Math.min(halfGap / r, span / 2.2)\n const arcTo = (r: number, i: number): Vector2 => {\n const inset = insetAt(r)\n const from = a0 + inset\n const to = a1 - inset\n const point = ringPoint(r, from + ((to - from) * i) / steps)\n return { x: point.x + cx, y: point.y }\n }\n\n if (rInner <= 0) {\n // Solid pie wedge: the sides converge on the centre, so only the outer rim\n // carries the gap.\n path.moveTo(cx, 0)\n for (let i = 0; i <= steps; i++) {\n const p = arcTo(rOuter, i)\n path.lineTo(p.x, p.y)\n }\n path.close()\n return path\n }\n\n for (let i = 0; i <= steps; i++) {\n const p = arcTo(rOuter, i)\n if (i === 0) path.moveTo(p.x, p.y)\n else path.lineTo(p.x, p.y)\n }\n // Back along the inner arc — its own (wider) inset makes each side edge a\n // straight chord sitting `halfGap` px off the divide at both ends.\n for (let i = steps; i >= 0; i--) {\n const p = arcTo(rInner, i)\n path.lineTo(p.x, p.y)\n }\n path.close()\n return path\n}\n\n/** What the ring's geometry reads off its entries. */\ninterface RingState {\n readonly slices: PieSlice[]\n readonly ring: RingConfig\n readonly label: LabelConfig\n readonly centerStyle: CenterConfig\n readonly labelMode: PieLabelMode\n readonly labelPlacement: PieLabelPlacement\n readonly pops: readonly number[]\n readonly dimOpacity: number\n readonly centerLabel: string\n readonly centerCaption: string\n}\n\n/**\n * The ring's geometry for one box: every radius, gutter and label position,\n * derived from the entries and the size the ring was laid out at, and asked\n * afresh wherever it is read — the drawing, and the labels' bound positions\n * and sizes alike, so the two cannot disagree.\n */\nexport class RingGeometry {\n constructor(\n private readonly s: RingState,\n private readonly box: { readonly width: number; readonly height: number }\n ) { }\n\n /** The drawn slices' angular spans, re-read every frame so a value tween re-cuts them. */\n get arcs(): PieArc[] {\n return pieArcs(this.s.slices, this.s.ring.startAngle)\n }\n\n /** Whether any slice is labelled on a leader — the only thing that costs a gutter. */\n get hasLeaderLabels(): boolean {\n return this.s.labelPlacement === \"leader\" && this.s.labelMode !== \"none\" && this.arcs.length > 0\n }\n\n // ---- Label sizing ------------------------------------------------------\n\n /**\n * How wide a horizontal label centred in slice `arc`'s band can run before it\n * leaves the wedge, in px.\n *\n * Two things bound it, and which one binds depends entirely on where the wedge\n * sits. Stepping sideways from the label's centre moves you *radially* by\n * `dx·sin(mid)` and *tangentially* by `dx·cos(mid)`, so a wedge at 3 o'clock is\n * limited by the band's thickness (its label runs straight across the band)\n * while a wedge at 12 o'clock is limited by its own arc (its label runs along\n * the band, which is much roomier). Taking the smaller of the two is what stops\n * a label overflowing the ring on one chart and being pointlessly shrunk on the\n * next.\n *\n * Measured off the *unpopped* radius, so a slice growing out of the ring\n * doesn't resize its own text mid-animation.\n */\n insideRoom(arc: PieArc): number {\n const outer = this.outerRadius\n const hole = this.holeRadius\n const band = hole > 0 ? outer - hole : outer * (1 - PIE_LABEL_RADIUS) * 2\n const mid = hole > 0 ? (hole + outer) / 2 : outer * PIE_LABEL_RADIUS\n const sin = Math.abs(Math.sin(arc.mid))\n const cos = Math.abs(Math.cos(arc.mid))\n const acrossBand = sin > AXIS_EPSILON ? band / sin : Infinity\n const alongArc = cos > AXIS_EPSILON ? ((arc.a1 - arc.a0) * mid) / cos : Infinity\n return Math.max(0, Math.min(acrossBand, alongArc))\n }\n\n /** Widest line, in glyphs at the leading line's size, an inside label runs to. */\n insideGlyphs(arc: PieArc): number {\n const mode = this.s.labelMode\n const category = this.s.slices[arc.index]?.category.length ?? 0\n const percent = mode === \"both\" || mode === \"percent\" ? formatPercent(arc.fraction).length : 0\n // The caption is set at a fraction of the leading line, except when it *is*\n // the leading line.\n const caption = mode === \"category\" ? category : mode === \"both\" ? category * INSIDE_CAPTION_RATIO : 0\n return Math.max(1, percent, caption)\n }\n\n /**\n * Font size of an inside label's leading line: what the theme asked for,\n * shrunk if that wouldn't fit inside its wedge. **One size across every inside\n * label** — the smallest any of them needs — because labels at mismatched\n * sizes read as a mistake rather than as emphasis.\n *\n * So the theme's `insideStyle.fontSize` is an upper bound, not a promise: ask\n * for the size the chart wants and the ring keeps it legal.\n */\n get insideFont(): number {\n let size = sizeOf(this.s.label.insideStyle, FALLBACK_SIZE * 1.9)\n for (const arc of this.arcs) {\n const fits = (this.insideRoom(arc) * INSIDE_LABEL_FIT) / (this.insideGlyphs(arc) * GLYPH_RATIO)\n size = Math.min(size, fits)\n }\n return Math.max(1, size)\n }\n\n /**\n * Font size of the hole's summary: the configured size, capped so it still\n * fits across the hole. The cap matters because the hole's radius derives from\n * the box, so a chart that reflows smaller would otherwise push its summary out\n * through the ring.\n */\n get centerFont(): number {\n const { centerStyle, centerLabel, centerCaption } = this.s\n const wanted = sizeOf(centerStyle.labelStyle, 46)\n const hole = this.holeRadius\n if (hole <= 0) return wanted\n const captionRatio = sizeOf(centerStyle.captionStyle, 22) / Math.max(1, wanted)\n // Both lines have to clear the hole, and the caption is set at a fraction of\n // this size — so what binds is whichever is wider once drawn, not whichever\n // has more characters.\n const glyphs = Math.max(1, centerLabel.length, centerCaption.length * captionRatio)\n return Math.min(wanted, (hole * 2 * CENTER_LABEL_FIT) / (glyphs * GLYPH_RATIO))\n }\n\n // ---- Ring geometry, all derived from the box ---------------------------\n\n /** Rim radius of a fully selected slice as a multiple of the ring's, clamped sane. */\n get growFactor(): number {\n return Math.max(1, Math.min(1.6, this.s.ring.selectedGrow))\n }\n\n /** How far past the ring's rim a fully selected slice reaches, in px. */\n get maxPop(): number {\n return this.outerRadius * (this.growFactor - 1)\n }\n\n /** Height of one **leader** label block, in px — the unit those columns are spaced by. */\n get labelRowHeight(): number {\n // Inside labels are stacked in the band, not in a column beside the ring, so\n // they cost no vertical reserve.\n if (!this.hasLeaderLabels) return 0\n const caption = sizeOf(this.s.label.textStyle) * 1.25\n const percent = sizeOf(this.s.label.percentStyle) * 1.25\n if (this.s.labelMode === \"both\") return caption + percent + 2\n return this.s.labelMode === \"category\" ? caption : percent\n }\n\n /** Widest line, in glyphs, that a leader label for slice `index` can run to. */\n captionGlyphs(index: number): number {\n const mode = this.s.labelMode\n // \"100%\" is the widest a percentage gets; captions are their own length.\n const percent = mode === \"percent\" || mode === \"both\" ? 4 : 0\n const category = mode === \"category\" || mode === \"both\" ? (this.s.slices[index]?.category.length ?? 0) : 0\n return Math.max(percent, category)\n }\n\n /**\n * Horizontal room reserved on each side for the leaders and their labels, in\n * px — measured **per side**, from the slices that actually leader out that\n * way. A ring whose labels are all inside reserves nothing and draws bigger.\n *\n * Excludes the ring's own selection-growth allowance, which scales with the\n * radius and so is divided out in {@link outerRadius} instead.\n */\n get labelGutters(): { left: number; right: number } {\n const gutters = { left: BARE_PAD, right: BARE_PAD }\n if (!this.hasLeaderLabels) return gutters\n const glyph = sizeOf(this.s.label.textStyle) * GLYPH_RATIO\n for (const arc of this.arcs) {\n const room = LEADER_LENGTH + LABEL_STUB + TEXT_INSET + this.captionGlyphs(arc.index) * glyph + LABEL_PAD\n const key = sideOf(arc.mid) > 0 ? \"right\" : \"left\"\n gutters[key] = Math.max(gutters[key], room)\n }\n return gutters\n }\n\n /**\n * How far the ring's centre sits from the node's, in px. Zero while the\n * gutters match; when the labels all fall one way it slides the ring into the\n * empty half so the ring-plus-labels group stays centred in the box instead of\n * the ring sitting dead centre with a bald patch beside it.\n */\n get centerX(): number {\n const { left, right } = this.labelGutters\n return (left - right) / 2\n }\n\n /**\n * Radius of the ring's outer edge, fitted to whatever the label gutters leave.\n * A fully selected slice reaches `radius × growFactor`, so the room has to\n * cover that — hence the divisor, which keeps even the grown slice inside the\n * box (and holds the ring the same size whether or not anything is selected).\n */\n get outerRadius(): number {\n const { left, right } = this.labelGutters\n const room = Math.min((this.box.width - left - right) / 2, this.box.height / 2 - this.labelRowHeight - 4)\n return Math.max(0, room / this.growFactor)\n }\n\n /** Radius of the hole in the middle: a fraction of the outer radius, or explicit px. */\n get holeRadius(): number {\n const configured = this.s.ring.holeRadius\n if (configured <= 0) return 0\n const px = configured < 1 ? configured * this.outerRadius : configured\n return Math.min(px, this.outerRadius * 0.95)\n }\n\n /** Radius of the circle every leader elbows on, clear of even a popped-out slice. */\n get elbowRadius(): number {\n return this.outerRadius + this.maxPop + LEADER_LENGTH\n }\n\n /** How far slice `index` is currently pushed out, in `[0, 1]`. */\n popOf(index: number): number {\n return clampUnit(this.s.pops[index] ?? 0)\n }\n\n /** Radius of slice `index`'s outer edge, its selection growth included. */\n anchorRadius(index: number): number {\n return this.outerRadius + this.maxPop * this.popOf(index)\n }\n\n /**\n * Radius an inside label sits at on slice `index`: halfway across the band on a\n * donut, and further out than halfway on a solid pie, where \"halfway\" would\n * crowd the point every wedge converges on. Rides the slice's own pop, so an\n * inside label travels out with the wedge it belongs to.\n */\n bandMidRadius(index: number): number {\n const rim = this.anchorRadius(index)\n const hole = this.holeRadius\n return hole > 0 ? (hole + rim) / 2 : rim * PIE_LABEL_RADIUS\n }\n\n /** The point slice `index`'s inside label is centred on. */\n insideAnchor(index: number): Vector2 {\n const arc = this.arcs.find((a) => a.index === index)\n if (arc === undefined) return { x: this.centerX, y: 0 }\n const point = ringPoint(this.bandMidRadius(index), arc.mid)\n return { x: point.x + this.centerX, y: point.y }\n }\n\n /**\n * How selected the ring is overall, in `[0, 1]` — the most-grown slice. Rides\n * the same tween the pops do, so the dim below arrives with the growth rather\n * than needing an animation of its own.\n */\n get selectionStrength(): number {\n let strongest = 0\n for (let i = 0; i < this.s.slices.length; i++) strongest = Math.max(strongest, this.popOf(i))\n return strongest\n }\n\n /**\n * Opacity slice `index` (and its leader and label) is drawn at: full while\n * nothing is selected, falling toward `dimOpacity` in proportion to how\n * selected the *rest* of the ring is. A slice that's selected itself stays at\n * full — the two factors cancel.\n */\n sliceOpacity(index: number): number {\n const focus = this.selectionStrength\n if (focus <= 0) return 1\n return 1 - (1 - clampUnit(this.s.dimOpacity)) * focus * (1 - this.popOf(index))\n }\n\n /** Slice `index`'s share of the ring as its label reads it, off the live values. */\n percentText(index: number): string {\n const arc = this.arcs.find((a) => a.index === index)\n return formatPercent(arc?.fraction ?? 0)\n }\n\n // ---- Label placement ---------------------------------------------------\n\n /**\n * Where every leader label sits and how its leader gets there. Labels are split\n * into a right and a left column by their slice's bisector, each column is\n * spread so nothing overlaps (and nothing leaves the box), and each leader is\n * then built as wedge edge → elbow → label. The elbow rides the leader circle at\n * the label's *settled* y, which keeps the final run horizontal and the whole\n * polyline outside the ring however far the label was nudged.\n */\n placements(): LabelPlacement[] {\n if (!this.hasLeaderLabels) return []\n const arcs = this.arcs\n if (arcs.length === 0 || this.outerRadius <= 0) return []\n\n const radius = this.elbowRadius\n const rowHeight = this.labelRowHeight\n const top = this.box.height / 2 - rowHeight / 2\n const bottom = -top\n const cx = this.centerX\n\n interface Slot {\n index: number\n side: 1 | -1\n mid: number\n ideal: number\n y: number\n }\n const right: Slot[] = []\n const left: Slot[] = []\n for (const arc of arcs) {\n const side = sideOf(arc.mid)\n const ideal = ringPoint(radius, arc.mid).y\n ;(side > 0 ? right : left).push({ index: arc.index, side, mid: arc.mid, ideal, y: ideal })\n }\n relaxLabels(right, rowHeight + LABEL_GAP, top, bottom)\n relaxLabels(left, rowHeight + LABEL_GAP, top, bottom)\n\n return [...right, ...left].map((slot) => {\n const start = ringPoint(this.anchorRadius(slot.index) + LEADER_LIFTOFF, slot.mid)\n // The elbow sits where the label's y crosses the leader circle. Solving\n // x² + y² = radius² for x keeps it *on* that circle (never inside the\n // ring); a label pushed past the circle's top or bottom falls back to the\n // sideways floor so its leader still reads as pointing outward.\n const onCircle = Math.sqrt(Math.max(0, radius * radius - slot.y * slot.y))\n const elbowX = cx + slot.side * Math.max(onCircle, radius * MIN_ELBOW)\n const endX = cx + slot.side * (radius + LABEL_STUB)\n\n return {\n index: slot.index,\n side: slot.side,\n leader: [\n { x: start.x + cx, y: start.y },\n { x: elbowX, y: slot.y },\n { x: endX, y: slot.y },\n ],\n text: { x: endX + slot.side * TEXT_INSET, y: slot.y },\n }\n })\n }\n\n /** This frame's placement for slice `index`, or `undefined` if it isn't leadered. */\n placementFor(index: number): LabelPlacement | undefined {\n return this.placements().find((placement) => placement.index === index)\n }\n}\n\n/** Which side a leader off bisector `mid` runs to: +1 right, −1 left. */\nfunction sideOf(mid: number): 1 | -1 {\n return Math.sin(mid) >= 0 ? 1 : -1\n}\n\nconst NO_BOX = { width: 0, height: 0 }\n\n/**\n * Each slice's fill, resolved once per paint rather than every frame: the slices\n * are bound to the chart's rows and palette, so a theme tween hands over new\n * paint and the drawing must see it, but a frame that moves nothing must not\n * re-resolve. Keyed by the paint itself, so it is shared and needs no node.\n */\nconst resolvedFills = new WeakMap<object, FillResolved[]>()\n\nfunction resolvedFill(fill: Fill): FillResolved[] {\n if (typeof fill !== \"object\" || fill === null) return fillOps.resolve(fill)\n let resolved = resolvedFills.get(fill)\n if (resolved === undefined) resolvedFills.set(fill, resolved = fillOps.resolve(fill))\n return resolved\n}\n\n/** Each slice's fill, resolved. */\nexport function ringFills(slices: readonly PieSlice[]): FillResolved[][] {\n return slices.map((slice) => resolvedFill(slice.fill))\n}\n\n/**\n * The labels a ring was built with: one per *drawn* slice (a zero-valued slice\n * gets no wedge, so no label either), in whichever of the two forms the chart\n * asked for, then the hole's summary. Both are real Text stacked in a hug\n * vertical flow — the difference is only what they are pinned to and which line\n * leads. The point each is pinned to stays reactive and follows the ring as the\n * box resizes or its slice pops out; only the choice of form, which depends on\n * entries alone, is settled here.\n */\nfunction ringLabels(s: RingState & { readonly labelReveal: number; readonly centerReveal: number }) {\n const blocks: Node2D[] = []\n // A label block's parent is the ring: its box is what the ring was laid out at.\n const geometry = () => new RingGeometry(s, (blocks[0]?.parent as Node2D | null | undefined)?.layoutBounds ?? NO_BOX)\n\n const label = (arc: PieArc): Node2D => {\n // Fades in with the ring's labels, then dims along with its own slice\n // whenever another slice takes the focus.\n const opacity = () => clampUnit(s.labelReveal) * geometry().sliceOpacity(arc.index)\n const wantsCategory = s.labelMode === \"both\" || s.labelMode === \"category\"\n const wantsPercent = s.labelMode === \"both\" || s.labelMode === \"percent\"\n\n if (s.labelPlacement === \"inside\") {\n // The number leads and the caption sits under it: inside a wedge the\n // reader already knows *which* wedge they are looking at, so the magnitude\n // is what the label is for. Both sizes are bound rather than read once —\n // the fitted size derives from the ring's radius, which isn't known until\n // the node has been laid out.\n const inside: Node2D[] = []\n if (wantsPercent) {\n inside.push(new Text({\n ...textOps.styleProps(s.label.insideStyle),\n text: () => geometry().percentText(arc.index),\n fontSize: () => geometry().insideFont,\n }))\n }\n if (wantsCategory) {\n inside.push(new Text({\n ...textOps.styleProps(s.label.insideStyle),\n text: () => s.slices[arc.index]?.category ?? \"\",\n fontSize: () => (s.labelMode === \"category\" ? geometry().insideFont : geometry().insideFont * INSIDE_CAPTION_RATIO),\n }))\n }\n return new Rect({\n flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n gap: 0,\n align: \"center\",\n opacity,\n center: () => geometry().insideAnchor(arc.index),\n children: inside,\n })\n }\n\n const side = sideOf(arc.mid)\n const at = () => geometry().placementFor(arc.index)?.text ?? { x: 0, y: 0 }\n const children: Node2D[] = []\n if (wantsCategory) {\n children.push(new Text({\n ...textOps.styleProps(s.label.textStyle),\n text: () => s.slices[arc.index]?.category ?? \"\",\n textAlign: side > 0 ? \"left\" : \"right\",\n }))\n }\n if (wantsPercent) {\n children.push(new Text({\n ...textOps.styleProps(s.label.percentStyle),\n text: () => geometry().percentText(arc.index),\n textAlign: side > 0 ? \"left\" : \"right\",\n }))\n }\n return new Rect({\n flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n gap: 2,\n align: side > 0 ? \"centerLeft\" : \"centerRight\",\n opacity,\n // Exactly one of these is set; `undefined` anchors are ignored.\n centerLeft: side > 0 ? at : undefined,\n centerRight: side > 0 ? undefined : at,\n children,\n })\n }\n\n if (s.labelPlacement !== \"none\" && s.labelMode !== \"none\") {\n for (const arc of pieArcs(s.slices, s.ring.startAngle)) blocks.push(label(arc))\n }\n\n // The hole's summary, pinned to the ring's centre so it tracks the sideways\n // shift a one-sided label column puts on the ring.\n if (s.centerLabel !== \"\" || s.centerCaption !== \"\") {\n const children: Node2D[] = []\n if (s.centerLabel !== \"\") {\n children.push(new Text({\n ...textOps.styleProps(s.centerStyle.labelStyle),\n text: s.centerLabel,\n fontSize: () => geometry().centerFont,\n }))\n }\n if (s.centerCaption !== \"\") {\n children.push(new Text({\n ...textOps.styleProps(s.centerStyle.captionStyle),\n text: s.centerCaption,\n fontSize: () =>\n geometry().centerFont * (sizeOf(s.centerStyle.captionStyle, 22) / sizeOf(s.centerStyle.labelStyle, 46)),\n }))\n }\n blocks.push(new Rect({\n flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n gap: 2,\n align: \"center\",\n opacity: () => clampUnit(s.centerReveal),\n center: () => ({ x: geometry().centerX, y: 0 }),\n children,\n }))\n }\n return asParts(blocks)\n}\n\n/**\n * The ring itself: one wedge per {@link PieSlice}, sized by its share of the\n * total, swept clockwise from the theme's `startAngle` with a `gap` of clear\n * space between neighbours — a *constant-width* channel, the same at the hole as\n * at the rim (see {@link wedgePath}). A **selected** slice reaches further out\n * while its hole edge stays put — so it reads as that band swelling out of the\n * ring rather than breaking off it — and the rest of the ring dims behind it,\n * leaders and labels included.\n *\n * This is the only part of the chart family that draws a path: the wedges are\n * filled {@link PathBuilder} polygons approximating each arc, and the leaders are\n * stroked polylines — both in the node's centred, y-up space, so neither depends\n * on SVG arc-sweep conventions. The **labels are real {@link Text} nodes**\n * (stacked in a hug vertical flow and pinned by their inner edge), so no\n * glyph-width guesswork decides where a caption starts.\n *\n * ### Label placement\n * Each label sits either **inside** its wedge or **outside** on a leader.\n *\n * An inside label is centred on its slice's band and leads with the number, the\n * caption set small underneath — inside a wedge the reader already knows which\n * slice they're on, so the magnitude is the thing being read. It costs no gutter\n * and no eye round-trip, so a ring labelled that way draws considerably bigger in\n * the same box.\n *\n * A leader label sits outside on the end of a polyline that lifts off its wedge,\n * elbows, and runs horizontally into the text. Labels are split into a left and a\n * right column by which way their slice's bisector points, and each column is\n * de-conflicted independently (see {@link relaxLabels}): a label takes the spot\n * nearest its wedge that still clears its neighbour, and the column is squeezed\n * to fit the node's height rather than spilling out of it. The elbow then rides on\n * a circle just outside the ring **at the label's settled y**, so the run into the\n * text is always horizontal and a nudged label never drags its leader back across\n * the ring.\n *\n * Only the sides that actually carry a leader reserve a gutter, and the ring's\n * centre slides into whatever the other side left over — so a ring whose slices\n * all leader one way doesn't leave a bald patch on the other.\n */\nexport const PieRing = component2d({\n key: \"pieRing\",\n state: {\n slices: fresh<PieSlice[]>(() => []),\n ring: Types.custom<RingConfig>(DEFAULT_PIE_CHART_THEME.ring),\n label: Types.custom<LabelConfig>(DEFAULT_PIE_CHART_THEME.label),\n centerStyle: Types.custom<CenterConfig>(DEFAULT_PIE_CHART_THEME.center),\n labelMode: Types.custom<PieLabelMode>(\"percent\"),\n labelPlacement: Types.custom<PieLabelPlacement>(\"leader\"),\n growth: 1,\n labelReveal: 1,\n pops: fresh<number[]>(() => []),\n dimOpacity: DEFAULT_PIE_CHART_THEME.spotlight.dimOpacity,\n centerLabel: \"\",\n centerCaption: \"\",\n centerReveal: 1,\n },\n // A plain node filled its parent; one made of its labels would hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n children: (state) => ringLabels(state),\n // The ring's paints live nowhere a paint entry would look: one fill per slice,\n // the wedge outline and drop shadow of `ring`, and the leader line's own stroke.\n load: (loader, state) =>\n loadPaints(loader, {\n fills: ringFills(state.slices),\n shadows: [shadowOps.resolve(state.ring.shadow)],\n strokes: [strokeOps.resolve(state.ring.stroke), strokeOps.resolve(state.label.leaderStroke)],\n }),\n commands: {\n /**\n * Tween `growth` `0 → 1`, wiping the wedges in around the ring. No priming\n * to 0 first — the value at `t` is a function of `t`, so a repeat call\n * re-sweeps by definition rather than by having reset something.\n */\n sweep: (timeline) => {\n const ease = frameEase(timeline)\n return timeline.animate((t) => ({ growth: ease(t) }))\n },\n /** Tween `labelReveal` `0 → 1`, fading the leaders and captions up. */\n revealLabels: (timeline) => {\n const ease = frameEase(timeline)\n return timeline.animate((t) => ({ labelReveal: ease(t) }))\n },\n /**\n * Fade the hole's summary up. Deliberately not part of the ring's entrance: a\n * hole label is usually the chart's conclusion, so the scene decides when it\n * lands.\n */\n revealCenter: (timeline, state) => {\n const ease = frameEase(timeline)\n const from = clampUnit(state.centerReveal)\n return timeline.animate((t) => ({ centerReveal: from + (1 - from) * ease(t) }))\n },\n },\n render: (ctx, state) => {\n const g = new RingGeometry(state, ctx.size)\n const rOuter = g.outerRadius\n const arcs = rOuter > 0 ? g.arcs : []\n if (arcs.length > 0) {\n const sweep = clampUnit(state.growth)\n const revealEnd = state.ring.startAngle * (Math.PI / 180) + Math.PI * 2 * sweep\n const hole = g.holeRadius\n const halfGap = Math.max(0, state.ring.gap) / 2\n\n // One shared frame (the whole ring square, grown slices included) pinned onto\n // every wedge path, so `Graphics2D.path()` doesn't re-centre each wedge on its\n // own bbox and collapse them all onto the origin. It stays symmetric about the\n // *node's* origin — so path coordinates land exactly in node space — and is\n // grown by the ring's sideways offset rather than moved with it, which would\n // undo the very shift it has to contain.\n const cx = g.centerX\n const half = rOuter + g.maxPop + 1 + Math.abs(cx)\n const centerBounds: [number, number, number, number] = [-half, -half, half, half]\n // Explicit width/height alongside centerBounds: a wedge path's command list\n // carries no implied extent, so a slice filled with an image or a video needs\n // this to size its decode.\n const span = half * 2\n\n const fills = ringFills(state.slices)\n for (const arc of arcs) {\n // Clip the wedge to the growth-revealed arc; skip it until the sweep\n // reaches it, so the ring draws in as one clockwise wipe.\n const a1 = Math.min(arc.a1, revealEnd)\n if (a1 <= arc.a0) continue\n\n const path = wedgePath(arc.a0, a1, hole, g.anchorRadius(arc.index), halfGap, cx)\n const wedge = new Graphics2D().path(path.toPathState({ centerBounds, width: span, height: span }))\n wedge.shadow(state.ring.shadow)\n wedge.fill(fills[arc.index] ?? [])\n wedge.stroke(state.ring.stroke)\n ctx.draw(wedge.opacity(g.sliceOpacity(arc.index)))\n }\n\n // Leaders. Each polyline gets its own `Graphics2D`: several open polylines\n // chained into one command list are combined into a single surface before the\n // stroke, which closes each contour back on itself and paints the leaders as\n // thin triangles.\n const reveal = clampUnit(state.labelReveal)\n if (reveal > 0.001) {\n for (const placement of g.placements()) {\n ctx.draw(\n new Graphics2D()\n .line({ points: placement.leader, radius: ELBOW_RADIUS })\n .stroke(state.label.leaderStroke)\n .opacity(reveal * g.sliceOpacity(placement.index))\n )\n }\n }\n }\n ctx.children()\n },\n})\n\nexport type PieRing = NodeOf<typeof PieRing>\n"]}