@motionscript/charts 0.1.1-alpha.1 → 0.1.1-alpha.2

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 (81) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +19 -8
  3. package/dist/bar-chart/bar-chart.d.ts +97 -65
  4. package/dist/bar-chart/bar-chart.d.ts.map +1 -1
  5. package/dist/bar-chart/bar-chart.js +9 -5
  6. package/dist/bar-chart/bar-chart.js.map +1 -1
  7. package/dist/bar-chart/chart-bars.d.ts +29 -5
  8. package/dist/bar-chart/chart-bars.d.ts.map +1 -1
  9. package/dist/bar-chart/chart-bars.js +33 -8
  10. package/dist/bar-chart/chart-bars.js.map +1 -1
  11. package/dist/bar-chart/shared.d.ts +9 -4
  12. package/dist/bar-chart/shared.d.ts.map +1 -1
  13. package/dist/bar-chart/shared.js +10 -5
  14. package/dist/bar-chart/shared.js.map +1 -1
  15. package/dist/browser/chunks/chunk-7XCRX6GE.js +2 -0
  16. package/dist/browser/chunks/chunk-7XCRX6GE.js.map +7 -0
  17. package/dist/browser/index.js +1 -1
  18. package/dist/browser/index.js.map +3 -3
  19. package/dist/browser/kit.js +1 -1
  20. package/dist/browser/manifest.json +1 -1
  21. package/dist/kit/cartesian.d.ts +6 -4
  22. package/dist/kit/cartesian.d.ts.map +1 -1
  23. package/dist/kit/cartesian.js +5 -3
  24. package/dist/kit/cartesian.js.map +1 -1
  25. package/dist/kit/style.d.ts +339 -76
  26. package/dist/kit/style.d.ts.map +1 -1
  27. package/dist/kit/style.js +196 -101
  28. package/dist/kit/style.js.map +1 -1
  29. package/dist/line-chart/line-chart.d.ts +81 -57
  30. package/dist/line-chart/line-chart.d.ts.map +1 -1
  31. package/dist/line-chart/line-chart.js +9 -7
  32. package/dist/line-chart/line-chart.js.map +1 -1
  33. package/dist/pie-chart/pie-chart.d.ts +43 -43
  34. package/dist/pie-chart/pie-chart.d.ts.map +1 -1
  35. package/dist/pie-chart/pie-chart.js +2 -1
  36. package/dist/pie-chart/pie-chart.js.map +1 -1
  37. package/dist/pie-chart/shared.d.ts +15 -8
  38. package/dist/pie-chart/shared.d.ts.map +1 -1
  39. package/dist/pie-chart/shared.js +21 -20
  40. package/dist/pie-chart/shared.js.map +1 -1
  41. package/dist/radar-chart/index.d.ts +1 -1
  42. package/dist/radar-chart/index.d.ts.map +1 -1
  43. package/dist/radar-chart/index.js +1 -1
  44. package/dist/radar-chart/index.js.map +1 -1
  45. package/dist/radar-chart/radar-chart.d.ts +37 -43
  46. package/dist/radar-chart/radar-chart.d.ts.map +1 -1
  47. package/dist/radar-chart/radar-chart.js +12 -12
  48. package/dist/radar-chart/radar-chart.js.map +1 -1
  49. package/dist/radar-chart/radar-grid.d.ts +7 -1
  50. package/dist/radar-chart/radar-grid.d.ts.map +1 -1
  51. package/dist/radar-chart/radar-grid.js +17 -15
  52. package/dist/radar-chart/radar-grid.js.map +1 -1
  53. package/dist/radar-chart/shared.d.ts +7 -2
  54. package/dist/radar-chart/shared.d.ts.map +1 -1
  55. package/dist/radar-chart/shared.js +6 -0
  56. package/dist/radar-chart/shared.js.map +1 -1
  57. package/dist/radial-chart/radial-chart.d.ts +37 -43
  58. package/dist/radial-chart/radial-chart.d.ts.map +1 -1
  59. package/dist/radial-chart/radial-chart.js +2 -1
  60. package/dist/radial-chart/radial-chart.js.map +1 -1
  61. package/dist/radial-chart/shared.d.ts +13 -9
  62. package/dist/radial-chart/shared.d.ts.map +1 -1
  63. package/dist/radial-chart/shared.js +15 -16
  64. package/dist/radial-chart/shared.js.map +1 -1
  65. package/package.json +4 -4
  66. package/src/bar-chart/bar-chart.ts +17 -6
  67. package/src/bar-chart/chart-bars.ts +48 -9
  68. package/src/bar-chart/shared.ts +20 -5
  69. package/src/kit/cartesian.ts +10 -7
  70. package/src/kit/style.ts +214 -118
  71. package/src/line-chart/line-chart.ts +14 -8
  72. package/src/pie-chart/pie-chart.ts +3 -1
  73. package/src/pie-chart/shared.ts +30 -22
  74. package/src/radar-chart/index.ts +1 -1
  75. package/src/radar-chart/radar-chart.ts +16 -13
  76. package/src/radar-chart/radar-grid.ts +18 -13
  77. package/src/radar-chart/shared.ts +9 -2
  78. package/src/radial-chart/radial-chart.ts +3 -1
  79. package/src/radial-chart/shared.ts +30 -18
  80. package/dist/browser/chunks/chunk-R3Q5EE6E.js +0 -2
  81. package/dist/browser/chunks/chunk-R3Q5EE6E.js.map +0 -7
@@ -1 +1 @@
1
- {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/radial-chart/shared.ts"],"names":[],"mappings":"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","sourcesContent":["import { Types, clamp, type DataRecord, type TextStyle, type Fill as _Fill, type FillResolved as _FillResolved } from \"@motionscript/sdk\"\n\nimport { CHART_COLORS, CHART_LEGEND, CHART_TEXT, legendConfigOf, type ChartStyleBase, paletteFill } from \"../kit/style\"\nimport type { LegendConfig } from \"../kit/shared\"\nimport { num, text } from \"../kit/shared\"\nimport type { CenterConfig, PieSlice } from \"../kit/polar\"\n\n/** One ring: a row's caption, magnitude and paint. The same shape as a pie's slice, so the legend is shared. */\nexport type RadialRow = PieSlice\n\nexport function resolveRadialRows(data: readonly DataRecord[], category: string, value: string, style: Pick<ChartStyleBase, \"palette\">): RadialRow[] {\n return data.map((row, i) => ({ category: text(row, category), value: num(row, value), fill: paletteFill(style, i) }))\n}\n\n/** A row's plotted value: negatives and non-finite values count as 0. */\nexport function rowValue(row: RadialRow): number {\n return row.value > 0 && Number.isFinite(row.value) ? row.value : 0\n}\n\n/** What a full sweep stands for: `max` when it is positive, else the largest value (1 when every value is 0). */\nexport function radialMax(rows: readonly RadialRow[], max: number): number {\n if (max > 0 && Number.isFinite(max)) return max\n let largest = 0\n for (const row of rows) largest = Math.max(largest, rowValue(row))\n return largest > 0 ? largest : 1\n}\n\n/** The share of a full sweep each row fills, `[0, 1]`. */\nexport function radialFractions(rows: readonly RadialRow[], max: number): number[] {\n const top = radialMax(rows, max)\n return rows.map((row) => clamp(rowValue(row) / top, 0, 1))\n}\n\n/** The ring geometry and look, from `style`. */\nexport interface RadialMarks {\n innerRadius: number\n ringGap: number\n /** Degrees of a full ring. */\n sweep: number\n /** Degrees clockwise from 12 o'clock. */\n startAngle: number\n track: boolean\n roundCaps: boolean\n}\n\nexport interface RadialStyle extends ChartStyleBase {\n readonly innerRadius: number\n readonly ringGap: number\n readonly sweep: number\n readonly startAngle: number\n readonly track: _FillResolved[]\n readonly roundCaps: boolean\n readonly ringLabels: boolean\n}\n\nexport const RADIAL_STYLE_GROUPS = {\n colors: CHART_COLORS,\n text: CHART_TEXT,\n marks: {\n /** Where the innermost ring starts, as a fraction of the outer radius. */\n innerRadius: Types.number(0.3, { min: 0, max: 0.9, step: 0.05 }),\n /** Space between neighbouring rings, px. */\n ringGap: Types.number(8, { min: 0, max: 60, step: 1, unit: \"px\" }),\n /** Degrees a full ring spans: 360 a circle, 270 a gauge-like open ring. */\n sweep: Types.number(360, { min: 30, max: 360, step: 5, unit: \"deg\" }),\n /** Degrees clockwise from 12 o'clock where every ring begins. */\n startAngle: Types.number(0, { min: -360, max: 360, step: 5, unit: \"deg\" }),\n /** The full ring under each arc; empty draws none. */\n track: Types.fill(\"white/10\"),\n /** Round the ends of the arcs and tracks. */\n roundCaps: Types.boolean(true),\n },\n labels: {\n /** The category written at each ring's start, in the muted colour. */\n ringLabels: Types.boolean(false),\n },\n legend: CHART_LEGEND,\n}\n\nexport function marksOf(style: RadialStyle): RadialMarks {\n return {\n innerRadius: style.innerRadius,\n ringGap: style.ringGap,\n sweep: style.sweep,\n startAngle: style.startAngle,\n track: style.track.length > 0,\n roundCaps: style.roundCaps,\n }\n}\n\nexport function legendOf(style: RadialStyle): LegendConfig {\n return legendConfigOf(style)\n}\n\nfunction face(style: RadialStyle): Pick<TextStyle, \"fontFamily\"> {\n return style.fontFamily ? { fontFamily: style.fontFamily } : {}\n}\n\nexport function ringLabelOf(style: RadialStyle): TextStyle {\n return { ...face(style), fontSize: style.fontSize, fontWeight: style.fontWeight, textAlign: \"center\", fill: style.muted }\n}\n\nexport function centerOf(style: RadialStyle): CenterConfig {\n return {\n labelStyle: {\n ...face(style),\n fontSize: Math.round(style.fontSize * 1.8),\n fontWeight: Math.min(900, style.fontWeight + 300),\n textAlign: \"center\",\n fill: style.foreground,\n },\n captionStyle: { ...face(style), fontSize: Math.round(style.fontSize * 0.8), fontWeight: style.fontWeight, textAlign: \"center\", fill: style.muted },\n }\n}\n"]}
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/radial-chart/shared.ts"],"names":[],"mappings":"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","sourcesContent":["import { Types, clamp, type DataRecord, type TextStyle, type Fill as _Fill, type FillResolved as _FillResolved } from \"@motionscript/sdk\"\n\nimport {\n CHART_BACKGROUND,\n CHART_FOREGROUND,\n CHART_LEGEND,\n CHART_MUTED,\n CHART_SERIES,\n LEGEND_TEXT,\n legendConfigOf,\n paletteFill,\n placedText,\n sizeOf,\n type ChartStyleBase,\n} from \"../kit/style\"\nimport type { LegendConfig } from \"../kit/shared\"\nimport { num, text } from \"../kit/shared\"\nimport type { CenterConfig, PieSlice } from \"../kit/polar\"\n\n/** One ring: a row's caption, magnitude and paint. The same shape as a pie's slice, so the legend is shared. */\nexport type RadialRow = PieSlice\n\nexport function resolveRadialRows(data: readonly DataRecord[], category: string, value: string, style: Pick<ChartStyleBase, \"seriesFills\">): RadialRow[] {\n return data.map((row, i) => ({ category: text(row, category), value: num(row, value), fill: paletteFill(style, i) }))\n}\n\n/** A row's plotted value: negatives and non-finite values count as 0. */\nexport function rowValue(row: RadialRow): number {\n return row.value > 0 && Number.isFinite(row.value) ? row.value : 0\n}\n\n/** What a full sweep stands for: `max` when it is positive, else the largest value (1 when every value is 0). */\nexport function radialMax(rows: readonly RadialRow[], max: number): number {\n if (max > 0 && Number.isFinite(max)) return max\n let largest = 0\n for (const row of rows) largest = Math.max(largest, rowValue(row))\n return largest > 0 ? largest : 1\n}\n\n/** The share of a full sweep each row fills, `[0, 1]`. */\nexport function radialFractions(rows: readonly RadialRow[], max: number): number[] {\n const top = radialMax(rows, max)\n return rows.map((row) => clamp(rowValue(row) / top, 0, 1))\n}\n\n/** The ring geometry and look, from `style`. */\nexport interface RadialMarks {\n innerRadius: number\n ringGap: number\n /** Degrees of a full ring. */\n sweep: number\n /** Degrees clockwise from 12 o'clock. */\n startAngle: number\n track: boolean\n roundCaps: boolean\n}\n\nexport interface RadialStyle extends ChartStyleBase {\n readonly innerRadius: number\n readonly ringGap: number\n readonly sweep: number\n readonly startAngle: number\n readonly track: _FillResolved[]\n readonly roundCaps: boolean\n readonly ringLabels: boolean\n readonly labelText: TextStyle\n readonly centerText: TextStyle\n}\n\nexport const RADIAL_STYLE_GROUPS = {\n series: CHART_SERIES,\n background: CHART_BACKGROUND,\n text: {\n /** The category at each ring's start, and the caption under the centre. */\n labelText: Types.typography({ fontSize: 28, fontWeight: 500, fill: CHART_MUTED }),\n /** The summary in the middle. */\n centerText: Types.typography({ fontSize: 50, fontWeight: 800, fill: CHART_FOREGROUND }),\n ...LEGEND_TEXT,\n },\n marks: {\n /** Where the innermost ring starts, as a fraction of the outer radius. */\n innerRadius: Types.number(0.3, { min: 0, max: 0.9, step: 0.05 }),\n /** Space between neighbouring rings, px. */\n ringGap: Types.number(8, { min: 0, max: 60, step: 1, unit: \"px\" }),\n /** Degrees a full ring spans: 360 a circle, 270 a gauge-like open ring. */\n sweep: Types.number(360, { min: 30, max: 360, step: 5, unit: \"deg\" }),\n /** Degrees clockwise from 12 o'clock where every ring begins. */\n startAngle: Types.number(0, { min: -360, max: 360, step: 5, unit: \"deg\" }),\n /** The full ring under each arc; empty draws none. */\n track: Types.fill(\"white/10\"),\n /** Round the ends of the arcs and tracks. */\n roundCaps: Types.boolean(true),\n },\n labels: {\n /** The category written at each ring's start, set in the label text. */\n ringLabels: Types.boolean(false),\n },\n legend: CHART_LEGEND,\n}\n\nexport function marksOf(style: RadialStyle): RadialMarks {\n return {\n innerRadius: style.innerRadius,\n ringGap: style.ringGap,\n sweep: style.sweep,\n startAngle: style.startAngle,\n track: style.track.length > 0,\n roundCaps: style.roundCaps,\n }\n}\n\nexport function legendOf(style: RadialStyle): LegendConfig {\n return legendConfigOf(style)\n}\n\nexport function ringLabelOf(style: RadialStyle): TextStyle {\n return placedText(style.labelText)\n}\n\nexport function centerOf(style: RadialStyle): CenterConfig {\n const size = sizeOf(style.centerText, 50)\n return {\n labelStyle: placedText(style.centerText),\n captionStyle: placedText(style.labelText, \"center\", { fontSize: Math.round(size * 0.45) }),\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motionscript/charts",
3
- "version": "0.1.1-alpha.1",
3
+ "version": "0.1.1-alpha.2",
4
4
  "type": "module",
5
5
  "description": "Animated data charts for Motion Script: a line chart, a bar chart and a pie chart sharing one axis, legend and theme kit.",
6
6
  "repository": {
@@ -50,7 +50,7 @@
50
50
  "license": "Apache-2.0",
51
51
  "author": "Motionscript",
52
52
  "peerDependencies": {
53
- "@motionscript/sdk": "^0.1.1-alpha.1"
53
+ "@motionscript/sdk": "^0.1.1-alpha.2"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/node": "^25.6.2",
@@ -61,8 +61,8 @@
61
61
  "typescript": "^6.0.3",
62
62
  "typescript-eslint": "^8.48.0",
63
63
  "vitest": "^4.1.5",
64
- "@motionscript/core": "0.1.1-alpha.1",
65
- "@motionscript/sdk": "0.1.1-alpha.1"
64
+ "@motionscript/core": "0.1.1-alpha.2",
65
+ "@motionscript/sdk": "0.1.1-alpha.2"
66
66
  },
67
67
  "scripts": {
68
68
  "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json --resolve-full-paths && node ../../../scripts/build-browser.mjs",
@@ -11,6 +11,11 @@ import {
11
11
  // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.
12
12
  type Node2D,
13
13
  type Node2DProps as _Node2DProps,
14
+ type TextStyle as _TextStyle,
15
+ type CornerRadiusResolved as _CornerRadiusResolved,
16
+ type CornerStyleResolved as _CornerStyleResolved,
17
+ type RectCornerRadius as _RectCornerRadius,
18
+ type RectCornerStyle as _RectCornerStyle,
14
19
  type Fill as _Fill,
15
20
  type FillResolved as _FillResolved,
16
21
  type Stroke as _Stroke,
@@ -44,12 +49,14 @@ import {
44
49
  } from "../kit/shared"
45
50
  import {
46
51
  CARTESIAN_GRID,
47
- CHART_COLORS,
52
+ CARTESIAN_TEXT,
48
53
  CHART_LEGEND,
54
+ CHART_SERIES,
49
55
  CHART_STYLE_FAMILY,
50
56
  CHART_STYLE_PRESETS,
51
- CHART_TEXT,
57
+ GRID_BACKGROUND,
52
58
  axisConfigOf,
59
+ migrateChartStyle,
53
60
  legendConfigOf,
54
61
  seriesFill,
55
62
  valueText,
@@ -70,7 +77,7 @@ import {
70
77
 
71
78
  const BAR_STYLE = Types.style(
72
79
  CHART_STYLE_FAMILY,
73
- { colors: CHART_COLORS, text: CHART_TEXT, grid: CARTESIAN_GRID, marks: BAR_MARKS, labels: BAR_LABELS, legend: CHART_LEGEND },
80
+ { series: CHART_SERIES, background: GRID_BACKGROUND, text: CARTESIAN_TEXT, grid: CARTESIAN_GRID, marks: BAR_MARKS, labels: BAR_LABELS, legend: CHART_LEGEND },
74
81
  { presets: CHART_STYLE_PRESETS }
75
82
  )
76
83
 
@@ -195,6 +202,7 @@ function seriesBars(s: BarState, index: number, plot: Size2D): BarGeometry[] {
195
202
  from: valueToPx(domain, along, span.from),
196
203
  to: valueToPx(domain, along, span.to),
197
204
  round: span.outer,
205
+ grounded: !stacked || span.from === 0,
198
206
  caption,
199
207
  }
200
208
  })
@@ -283,6 +291,7 @@ export const BarChart = component2d({
283
291
  weights: (state) => state.series.map((s) => ((s.enabled ?? true) ? 1 : 0)),
284
292
  showing: (state) => state.series.map((s) => s.enabled ?? true),
285
293
  },
294
+ migrate: { style: migrateChartStyle("cartesian") },
286
295
  allowedChildren: [],
287
296
  children: (state) => {
288
297
  const style = () => state.style
@@ -358,12 +367,13 @@ export const BarChart = component2d({
358
367
  baseline: () => valueToPx(domain(), runs(state, plotSize()).along, baselineValue(domain())),
359
368
  horizontal,
360
369
  fill: () => seriesFill(style(), s, i),
361
- cornerRadius: () => style().barRadius,
370
+ cornerRadius: () => style().barCornerRadius,
371
+ cornerStyle: () => style().barCornerStyle,
362
372
  growth: () => state.growth,
363
373
  dims: () => categoryDims(state),
364
374
  // A figure past a stack's end prints the stack's total; a percent stack has none worth printing, so prints its shares inside.
365
375
  values: () => (built.values === "end" && state.stack === "percent" ? "inside" : built.values),
366
- valueStyle: () => ({ ...valueText(style()), fontSize: Math.round(style().fontSize * 0.85) }),
376
+ valueStyle: () => valueText(style()),
367
377
  enabled: () => state.showing[i] ?? (s.enabled ?? true),
368
378
  }) as unknown as Node2D
369
379
  )
@@ -385,7 +395,7 @@ export const BarChart = component2d({
385
395
  plot = new Rect({
386
396
  children: [
387
397
  new ChartCanvas({
388
- fill: () => style().background,
398
+ fill: () => style().gridBackground,
389
399
  xGridStroke: () => style().verticalGrid,
390
400
  yGridStroke: () => style().horizontalGrid,
391
401
  xMarkers: horizontal ? () => domain().ticks : boundaries,
@@ -402,6 +412,7 @@ export const BarChart = component2d({
402
412
 
403
413
  return asParts([
404
414
  cartesianFrame({
415
+ background: () => style().background,
405
416
  left,
406
417
  ghost,
407
418
  plot,
@@ -11,7 +11,11 @@ import {
11
11
  // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.
12
12
  type Node2D,
13
13
  type Node2DProps as _Node2DProps,
14
+ type RectCornerRadius as _RectCornerRadius,
15
+ type RectCornerStyle as _RectCornerStyle,
14
16
  type TextStyle,
17
+ type CornerRadiusResolved,
18
+ type CornerStyleResolved,
15
19
  } from "@motionscript/sdk"
16
20
 
17
21
  import { boundText } from "../kit/cartesian"
@@ -35,8 +39,10 @@ export interface BarGeometry {
35
39
  from: number
36
40
  /** The far end at full growth. */
37
41
  to: number
38
- /** Whether the far end takes the corner radius (the stack's last segment on its side). */
42
+ /** Whether the far end takes the far corners (the stack's last segment on its side). */
39
43
  round: boolean
44
+ /** Whether the base takes the base corners (it stands on the baseline: every grouped bar, a stack's first segment). */
45
+ grounded?: boolean
40
46
  /** The figure printed by the bar; empty for none. */
41
47
  caption: string
42
48
  }
@@ -106,7 +112,9 @@ export const ChartBars = component2d({
106
112
  /** Bars grow along x from a category column rather than along y from a row. */
107
113
  horizontal: false,
108
114
  fill: Types.custom<Fill, FillResolved[]>([], { resolve: fillOps.resolve, lerp: fillOps.lerp }),
109
- cornerRadius: 8,
115
+ /** As for a bar standing up from the baseline: `topLeft`/`topRight` round its far end, the bottom pair its base. */
116
+ cornerRadius: Types.cornerRadius(8),
117
+ cornerStyle: Types.cornerStyle("rounded"),
110
118
  /**
111
119
  * Rise of every bar in `[0, 1]` about the baseline, not each bar's own base:
112
120
  * every segment travels by the same factor, so a stack stays flush
@@ -163,17 +171,13 @@ export const ChartBars = component2d({
163
171
  const seam = bar.round ? 0 : 1
164
172
  const length = Math.abs(ends.end - ends.base) + seam
165
173
  if (length <= seam) continue
166
- const radius = bar.round ? Math.max(0, Math.min(state.cornerRadius, length, bar.thickness / 2)) : 0
167
174
  const forward = ends.end >= ends.base
168
175
  const mid = (ends.base + ends.end) / 2 + (forward ? seam : -seam) / 2
169
- // Corner keys are visual (y-up): only the far end rounds.
170
- const far = state.horizontal
171
- ? (forward ? { topRight: radius, bottomRight: radius, topLeft: 0, bottomLeft: 0 } : { topLeft: radius, bottomLeft: radius, topRight: 0, bottomRight: 0 })
172
- : (forward ? { topLeft: radius, topRight: radius, bottomLeft: 0, bottomRight: 0 } : { bottomLeft: radius, bottomRight: radius, topLeft: 0, topRight: 0 })
176
+ const corners = barCorners(state, bar, forward, length)
173
177
  // One `Graphics2D` per bar: opacity belongs to the command list, and the spotlight dims single bars.
174
178
  const g = new Graphics2D().rect(state.horizontal
175
- ? { x: mid, y: bar.center, width: length, height: bar.thickness, cornerRadius: radius > 0 ? far : 0 }
176
- : { x: bar.center, y: mid, width: bar.thickness, height: length, cornerRadius: radius > 0 ? far : 0 })
179
+ ? { x: mid, y: bar.center, width: length, height: bar.thickness, ...corners }
180
+ : { x: bar.center, y: mid, width: bar.thickness, height: length, ...corners })
177
181
  g.fill(state.fill)
178
182
  ctx.draw(g.opacity(dimAt(state, i)))
179
183
  }
@@ -184,6 +188,41 @@ export const ChartBars = component2d({
184
188
 
185
189
  export type ChartBars = NodeOf<typeof ChartBars>
186
190
 
191
+ type Corners<T> = { topLeft: T; topRight: T; bottomRight: T; bottomLeft: T }
192
+
193
+ /**
194
+ * Turns corners written for a bar standing up into the drawn bar's visual
195
+ * (y-up) corners: mirrored for a bar below zero, turned a quarter clockwise
196
+ * for one growing right, and both for one growing left.
197
+ */
198
+ function orient<T>(c: Corners<T>, horizontal: boolean, forward: boolean): Corners<T> {
199
+ if (!horizontal) return forward ? c : { topLeft: c.bottomLeft, topRight: c.bottomRight, bottomRight: c.topRight, bottomLeft: c.topLeft }
200
+ return forward
201
+ ? { topLeft: c.bottomLeft, topRight: c.topLeft, bottomRight: c.topRight, bottomLeft: c.bottomRight }
202
+ : { topLeft: c.topLeft, topRight: c.bottomLeft, bottomRight: c.bottomRight, bottomLeft: c.topRight }
203
+ }
204
+
205
+ /** Bar `bar`'s rect corners: the far pair only where its stack ends, the base pair only on the baseline, each held inside the bar. */
206
+ export function barCorners(
207
+ state: { readonly cornerRadius: CornerRadiusResolved; readonly cornerStyle: CornerStyleResolved; readonly horizontal: boolean },
208
+ bar: BarGeometry,
209
+ forward: boolean,
210
+ length: number
211
+ ): { cornerRadius: Corners<number>; cornerStyle: CornerStyleResolved } {
212
+ const grounded = bar.grounded ?? true
213
+ const r = state.cornerRadius
214
+ const ends = (bar.round ? 1 : 0) + (grounded ? 1 : 0)
215
+ const cap = Math.max(0, Math.min(bar.thickness / 2, ends > 1 ? length / 2 : length))
216
+ const fit = (v: number, on: boolean) => (on ? Math.max(0, Math.min(v, cap)) : 0)
217
+ const upright: Corners<number> = {
218
+ topLeft: fit(r.topLeft, bar.round),
219
+ topRight: fit(r.topRight, bar.round),
220
+ bottomRight: fit(r.bottomRight, grounded),
221
+ bottomLeft: fit(r.bottomLeft, grounded),
222
+ }
223
+ return { cornerRadius: orient(upright, state.horizontal, forward), cornerStyle: orient(state.cornerStyle, state.horizontal, forward) }
224
+ }
225
+
187
226
  /** A figure as the bar prints it when the chart gives no caption. */
188
227
  export function barCaption(value: number): string {
189
228
  return value === 0 ? "" : formatMarker(value)
@@ -1,4 +1,14 @@
1
- import { Types, fillOps, type DataRecord, type Fill } from "@motionscript/sdk"
1
+ import {
2
+ Types,
3
+ fillOps,
4
+ type DataRecord,
5
+ type Fill,
6
+ // Named so the emitted marks types reach these through the sdk, not core.
7
+ type CornerRadiusResolved as _CornerRadiusResolved,
8
+ type CornerStyleResolved as _CornerStyleResolved,
9
+ type RectCornerRadius as _RectCornerRadius,
10
+ type RectCornerStyle as _RectCornerStyle,
11
+ } from "@motionscript/sdk"
2
12
 
3
13
  import { niceRange, num, text, type ChartSeries, type Range } from "../kit/shared"
4
14
 
@@ -110,12 +120,17 @@ export function inkOn(fill: Fill): string {
110
120
  return luminance > 0.4 ? "#0a0a0a" : "white"
111
121
  }
112
122
 
113
- /** A bar chart's own look: how round and how wide the bars are. */
123
+ /** A bar chart's own look: its corners, and how wide the bars are. */
114
124
  export const BAR_MARKS = {
115
- /** Radius of each bar's far corners; a stacked segment inside the stack stays square. */
116
- barRadius: Types.number(10, { min: 0, max: 64, step: 1, unit: "px" }),
125
+ /**
126
+ * Each corner as for a bar standing up from the baseline: the top pair round
127
+ * its far end, the bottom pair its base. Inside a stack, a segment's far end
128
+ * stays square unless it ends the stack, and its base unless it starts it.
129
+ */
130
+ barCornerRadius: Types.cornerRadius({ topLeft: 10, topRight: 10, bottomRight: 0, bottomLeft: 0 }),
131
+ barCornerStyle: Types.cornerStyle("rounded"),
117
132
  /** How much of its category slot a bar group fills, the rest spacing the groups. */
118
- barWidth: Types.number(0.7, { min: 0.05, max: 1, step: 0.05 }),
133
+ barWidth: Types.number(0.7, { min: 0.05, max: 1, step: 0.05, unit: "%", scale: 100 }),
119
134
  /** Space between the bars of one group, side by side. */
120
135
  barGap: Types.number(6, { min: 0, max: 40, step: 1, unit: "px" }),
121
136
  }
@@ -12,24 +12,26 @@ import {
12
12
  type TextStyle,
13
13
  } from "@motionscript/sdk"
14
14
 
15
- import type { CartesianStyle, LegendPosition } from "./style"
15
+ import { sizeOf, type CartesianStyle, type LegendPosition } from "./style"
16
16
 
17
17
  /** Space between the frame's rows and columns: the plot and the legend, the axes and the legend. */
18
- export function frameGap(style: Pick<CartesianStyle, "fontSize">): number {
19
- return Math.round(style.fontSize * 0.9)
18
+ export function frameGap(style: Pick<CartesianStyle, "legendText">): number {
19
+ return Math.round(sizeOf(style.legendText) * 0.9)
20
20
  }
21
21
 
22
22
  /** Room above the plot for the top value marker, which is centred on the top grid line. */
23
- export function headroom(style: Pick<CartesianStyle, "fontSize">): number {
24
- return Math.ceil(style.fontSize * 0.7)
23
+ export function headroom(style: Pick<CartesianStyle, "yLabelText">): number {
24
+ return Math.ceil(sizeOf(style.yLabelText) * 0.7)
25
25
  }
26
26
 
27
27
  /** How far an end marker of a horizontal value axis may hang past the plot: half a short caption. */
28
- export function overhang(style: Pick<CartesianStyle, "fontSize">): number {
29
- return Math.ceil(style.fontSize * 1.1)
28
+ export function overhang(style: Pick<CartesianStyle, "xLabelText">): number {
29
+ return Math.ceil(sizeOf(style.xLabelText) * 1.1)
30
30
  }
31
31
 
32
32
  interface CartesianParts {
33
+ /** Behind the whole chart, axes and legend included. */
34
+ readonly background?: () => Fill
33
35
  /** The axis beside the plot, on its left. */
34
36
  readonly left?: Node2D
35
37
  /** An invisible twin of `left` under the plot, so the row there starts at the plot's left edge. */
@@ -72,6 +74,7 @@ export function cartesianFrame(parts: CartesianParts): Node2D {
72
74
  height: "fill",
73
75
  gap: parts.gap,
74
76
  padding: parts.padding,
77
+ ...(parts.background ? { fill: parts.background } : {}),
75
78
  children: [
76
79
  ...(legend && parts.legendAt === "top" ? [across(legend)] : []),
77
80
  main,