ggaction 0.0.13 → 0.0.16

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 (359) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/README.md +41 -12
  3. package/knowledge/action-cards.json +1 -1
  4. package/knowledge/intent-taxonomy.json +1 -1
  5. package/knowledge/mcp-resources.json +1 -1
  6. package/knowledge/task-packet.schema.json +1 -1
  7. package/knowledge/task-resolver.js +1 -1
  8. package/package.json +39 -7
  9. package/src/BasicChartProgram.js +3 -1
  10. package/src/ChartProgram.js +2 -0
  11. package/src/accessibility.js +157 -0
  12. package/src/actions/basic.js +2 -2
  13. package/src/actions/boxPlots/components.js +20 -12
  14. package/src/actions/boxPlots/create.js +4 -5
  15. package/src/actions/boxPlots/edit.js +34 -9
  16. package/src/actions/boxPlots/materialize.js +12 -4
  17. package/src/actions/boxPlots/options.js +48 -16
  18. package/src/actions/canvas/actions.js +3 -3
  19. package/src/actions/canvas/fitting.js +1 -1
  20. package/src/actions/categoryOrder/index.js +21 -12
  21. package/src/actions/charts/area.js +6 -2
  22. package/src/actions/charts/bar.js +3 -2
  23. package/src/actions/charts/beeswarm.js +1 -1
  24. package/src/actions/charts/density.js +7 -3
  25. package/src/actions/charts/ecdf.js +6 -3
  26. package/src/actions/charts/endpoints.js +29 -7
  27. package/src/actions/charts/heatmap.js +3 -2
  28. package/src/actions/charts/histogram.js +5 -4
  29. package/src/actions/charts/horizon.js +4 -2
  30. package/src/actions/charts/interval-regression.js +7 -4
  31. package/src/actions/charts/line.js +4 -2
  32. package/src/actions/charts/parallel.js +9 -3
  33. package/src/actions/charts/pie.js +11 -3
  34. package/src/actions/charts/polar.js +17 -6
  35. package/src/actions/charts/radar.js +9 -3
  36. package/src/actions/charts/radial.js +12 -4
  37. package/src/actions/charts/raincloud.js +2 -2
  38. package/src/actions/charts/rug-strip.js +8 -4
  39. package/src/actions/charts/scatter.js +4 -2
  40. package/src/actions/composition/actions.js +29 -24
  41. package/src/actions/coordinates/actions.js +2 -2
  42. package/src/actions/coordinates/edit.js +76 -0
  43. package/src/actions/data/basicBin2d.js +4 -5
  44. package/src/actions/data/bin.js +3 -3
  45. package/src/actions/data/bin2d.js +63 -193
  46. package/src/actions/data/bin2dMaterialize.js +4 -5
  47. package/src/actions/data/box.js +4 -4
  48. package/src/actions/data/complete.js +26 -0
  49. package/src/actions/data/computed.js +4 -4
  50. package/src/actions/data/create.js +7 -14
  51. package/src/actions/data/density.js +15 -23
  52. package/src/actions/data/derived.js +57 -31
  53. package/src/actions/data/ecdf.js +2 -2
  54. package/src/actions/data/edit.js +551 -0
  55. package/src/actions/data/filter.js +28 -22
  56. package/src/actions/data/fold.js +2 -2
  57. package/src/actions/data/gradientProfile.js +2 -2
  58. package/src/actions/data/horizon.js +2 -2
  59. package/src/actions/data/impute.js +26 -0
  60. package/src/actions/data/index.js +62 -47
  61. package/src/actions/data/interval.js +2 -2
  62. package/src/actions/data/intervalEdit.js +26 -18
  63. package/src/actions/data/normalize.js +27 -0
  64. package/src/actions/data/regression.js +21 -27
  65. package/src/actions/data/revise.js +48 -0
  66. package/src/actions/data/revisionBindings.js +125 -0
  67. package/src/actions/data/shared.js +20 -4
  68. package/src/actions/data/stack.js +2 -2
  69. package/src/actions/data/summary.js +3 -3
  70. package/src/actions/data/timeUnit.js +6 -3
  71. package/src/actions/data/window.js +3 -3
  72. package/src/actions/encodings/angle.js +1 -1
  73. package/src/actions/encodings/appearance.js +4 -4
  74. package/src/actions/encodings/barWidth.js +1 -1
  75. package/src/actions/encodings/channels.js +415 -0
  76. package/src/actions/encodings/color/index.js +3 -2
  77. package/src/actions/encodings/density.js +7 -4
  78. package/src/actions/encodings/histogram.js +4 -3
  79. package/src/actions/encodings/horizon.js +6 -8
  80. package/src/actions/encodings/index.js +4 -0
  81. package/src/actions/encodings/offset.js +22 -15
  82. package/src/actions/encodings/parallel.js +4 -5
  83. package/src/actions/encodings/pathOrder.js +6 -11
  84. package/src/actions/encodings/position/apply.js +53 -7
  85. package/src/actions/encodings/position/index.js +14 -6
  86. package/src/actions/encodings/position/policies/bar.js +9 -0
  87. package/src/actions/encodings/position/resolve.js +24 -7
  88. package/src/actions/encodings/ranged.js +5 -5
  89. package/src/actions/encodings/remove.js +13 -9
  90. package/src/actions/encodings/ruleAppearance.js +2 -24
  91. package/src/actions/encodings/seriesLayout.js +2 -1
  92. package/src/actions/encodings/shared.js +82 -14
  93. package/src/actions/encodings/stroke.js +226 -0
  94. package/src/actions/encodings/strokeDash.js +2 -2
  95. package/src/actions/encodings/text.js +1 -1
  96. package/src/actions/errorBands/create.js +34 -20
  97. package/src/actions/errorBands/edit.js +34 -137
  98. package/src/actions/errorBars/create.js +43 -20
  99. package/src/actions/errorBars/edit.js +29 -125
  100. package/src/actions/facets/actions.js +267 -73
  101. package/src/actions/facets/derive.js +99 -28
  102. package/src/actions/facets/guides.js +4 -5
  103. package/src/actions/facets/replay.js +4 -5
  104. package/src/actions/gradientPlots/components.js +15 -8
  105. package/src/actions/gradientPlots/create.js +4 -5
  106. package/src/actions/gradientPlots/edit.js +18 -9
  107. package/src/actions/gradientPlots/materialize.js +5 -3
  108. package/src/actions/gradientPlots/options.js +10 -2
  109. package/src/actions/gradientPlots/rebind.js +4 -5
  110. package/src/actions/guides/applicability.js +8 -2
  111. package/src/actions/guides/axes/axes.js +23 -4
  112. package/src/actions/guides/axes/axis.js +2 -2
  113. package/src/actions/guides/axes/edit.js +2 -2
  114. package/src/actions/guides/axes/labels.js +67 -21
  115. package/src/actions/guides/axes/lines.js +15 -9
  116. package/src/actions/guides/axes/parallel/lifecycle.js +13 -13
  117. package/src/actions/guides/axes/parallel/resolve.js +6 -2
  118. package/src/actions/guides/axes/parallel.js +5 -5
  119. package/src/actions/guides/axes/remove.js +4 -4
  120. package/src/actions/guides/axes/tickGroups.js +6 -5
  121. package/src/actions/guides/axes/ticks.js +19 -6
  122. package/src/actions/guides/axes/titles.js +21 -13
  123. package/src/actions/guides/grids/grid.js +18 -29
  124. package/src/actions/guides/grids/resolve.js +7 -2
  125. package/src/actions/guides/guides.js +20 -4
  126. package/src/actions/guides/legends/blocks.js +426 -0
  127. package/src/actions/guides/legends/categorical/actions.js +138 -52
  128. package/src/actions/guides/legends/categorical/components.js +55 -45
  129. package/src/actions/guides/legends/categorical/index.js +37 -33
  130. package/src/actions/guides/legends/categorical/layout.js +98 -16
  131. package/src/actions/guides/legends/categorical/options.js +2 -2
  132. package/src/actions/guides/legends/categorical/recipes.js +111 -20
  133. package/src/actions/guides/legends/categorical/resolve.js +12 -8
  134. package/src/actions/guides/legends/categorical/symbols.js +104 -52
  135. package/src/actions/guides/legends/continuous/common.js +76 -20
  136. package/src/actions/guides/legends/continuous/gradient.js +95 -158
  137. package/src/actions/guides/legends/continuous/index.js +2 -0
  138. package/src/actions/guides/legends/continuous/interval.js +21 -16
  139. package/src/actions/guides/legends/continuous/opacity.js +37 -83
  140. package/src/actions/guides/legends/continuous/stroke.js +225 -0
  141. package/src/actions/guides/legends/creation.js +14 -1
  142. package/src/actions/guides/legends/edit.js +159 -60
  143. package/src/actions/guides/legends/focused.js +5 -5
  144. package/src/actions/guides/legends/index.js +2 -0
  145. package/src/actions/guides/legends/lane.js +59 -24
  146. package/src/actions/guides/legends/lifecycle.js +19 -8
  147. package/src/actions/guides/legends/remove.js +17 -8
  148. package/src/actions/guides/legends/sampling.js +140 -0
  149. package/src/actions/guides/legends/size.js +100 -108
  150. package/src/actions/guides/legends/strokeWidth.js +38 -92
  151. package/src/actions/guides/legends/target.js +112 -1
  152. package/src/actions/guides/legends/transition.js +165 -16
  153. package/src/actions/guides/polar/axes/facade.js +8 -8
  154. package/src/actions/guides/polar/axes/labels.js +29 -10
  155. package/src/actions/guides/polar/axes/lines.js +19 -19
  156. package/src/actions/guides/polar/axes/shared.js +16 -0
  157. package/src/actions/guides/polar/axes/ticks.js +10 -16
  158. package/src/actions/guides/polar/axes/titles.js +6 -6
  159. package/src/actions/guides/polar/grids.js +12 -14
  160. package/src/actions/guides/polar/resolve.js +12 -8
  161. package/src/actions/index.js +16 -3
  162. package/src/actions/intervals/revision.js +126 -0
  163. package/src/actions/marks/arc/actions.js +80 -22
  164. package/src/actions/marks/area/actions.js +38 -13
  165. package/src/actions/marks/area/materialize.js +17 -1
  166. package/src/actions/marks/bar/create.js +12 -3
  167. package/src/actions/marks/bar/edit.js +21 -4
  168. package/src/actions/marks/bar/materialize.js +34 -40
  169. package/src/actions/marks/index.js +10 -1
  170. package/src/actions/marks/line/actions.js +40 -17
  171. package/src/actions/marks/line/materialize.js +29 -10
  172. package/src/actions/marks/point/create.js +9 -3
  173. package/src/actions/marks/point/edit.js +19 -7
  174. package/src/actions/marks/point/jitter.js +6 -8
  175. package/src/actions/marks/point/materialize.js +48 -22
  176. package/src/actions/marks/point/packing.js +6 -8
  177. package/src/actions/marks/rect/actions.js +36 -15
  178. package/src/actions/marks/references.js +602 -33
  179. package/src/actions/marks/remove.js +67 -41
  180. package/src/actions/marks/rule/actions.js +51 -14
  181. package/src/actions/marks/shared.js +1 -1
  182. package/src/actions/marks/text/actions.js +79 -14
  183. package/src/actions/marks/text/index.js +4 -0
  184. package/src/actions/marks/text/layout.js +157 -17
  185. package/src/actions/marks/text/removal.js +173 -0
  186. package/src/actions/marks/text/selection.js +72 -0
  187. package/src/actions/marks/tick/actions.js +25 -8
  188. package/src/actions/primitives/createGraphics.js +5 -4
  189. package/src/actions/primitives/editGraphics.js +31 -4
  190. package/src/actions/primitives/graphicProperties.js +6 -0
  191. package/src/actions/primitives/semantic.js +11 -3
  192. package/src/actions/primitives/semanticAction.js +90 -14
  193. package/src/actions/primitives/semanticValidation/dataset.js +2 -4
  194. package/src/actions/primitives/semanticValidation/guide.js +1 -1
  195. package/src/actions/primitives/semanticValidation/index.js +9 -1
  196. package/src/actions/primitives/semanticValidation/layer.js +5 -0
  197. package/src/actions/primitives/semanticValidation/scale.js +28 -7
  198. package/src/actions/regression/components.js +36 -25
  199. package/src/actions/regression/create.js +13 -6
  200. package/src/actions/regression/edit.js +24 -11
  201. package/src/actions/resources/index.js +7 -0
  202. package/src/actions/resources/remove.js +173 -0
  203. package/src/actions/scales/channels.js +212 -0
  204. package/src/actions/scales/consumers/common.js +1 -1
  205. package/src/actions/scales/consumers/families.js +3 -2
  206. package/src/actions/scales/consumers/index.js +19 -2
  207. package/src/actions/scales/consumers/seriesLayout.js +28 -14
  208. package/src/actions/scales/create.js +55 -23
  209. package/src/actions/scales/definitions.js +94 -40
  210. package/src/actions/scales/edit.js +67 -12
  211. package/src/actions/scales/editPolicy.js +81 -11
  212. package/src/actions/scales/index.js +6 -0
  213. package/src/actions/scales/materialize.js +5 -4
  214. package/src/actions/scales/offset.js +48 -0
  215. package/src/actions/scales/parallel.js +71 -0
  216. package/src/actions/scales/preview.js +111 -10
  217. package/src/actions/scales/quantitativeColor.js +4 -5
  218. package/src/actions/selection/actions.js +179 -66
  219. package/src/actions/selection/index.js +3 -0
  220. package/src/actions/textMetrics/index.js +56 -0
  221. package/src/actions/theme/actions.js +70 -15
  222. package/src/actions/theme/composition.js +238 -0
  223. package/src/actions/theme/index.js +9 -2
  224. package/src/actions/theme/reconcile.js +313 -121
  225. package/src/actions/theme/state.js +160 -0
  226. package/src/actions/titles/actions.js +8 -8
  227. package/src/actions/titles/resolve.js +3 -3
  228. package/src/actions/violinPlots/create.js +6 -3
  229. package/src/actions/violinPlots/edit.js +2 -1
  230. package/src/core/action.js +117 -35
  231. package/src/core/compositionState.js +6 -1
  232. package/src/core/diagnostics.js +16 -0
  233. package/src/core/extensionRegistry.js +27 -0
  234. package/src/core/font.js +18 -0
  235. package/src/core/identifiers.js +5 -4
  236. package/src/core/immutable.js +10 -3
  237. package/src/core/optionRequirements.js +8 -0
  238. package/src/core/programState.js +21 -1
  239. package/src/core/resourceReferences.js +341 -0
  240. package/src/core/textMetricProfile.js +28 -0
  241. package/src/core/textMetrics.js +24 -3
  242. package/src/core/validation.js +22 -10
  243. package/src/core/vocabulary.js +16 -3
  244. package/src/diagnostics.js +1 -0
  245. package/src/grammar/bars/aggregate.js +4 -1
  246. package/src/grammar/bars/geometry.js +44 -0
  247. package/src/grammar/bin.js +54 -9
  248. package/src/grammar/complete.js +279 -0
  249. package/src/grammar/computed.js +265 -64
  250. package/src/grammar/coordinates.js +37 -0
  251. package/src/grammar/density.js +239 -37
  252. package/src/grammar/displayLabels.js +57 -0
  253. package/src/grammar/facets/dependencies.js +133 -1
  254. package/src/grammar/facets/guides.js +3 -0
  255. package/src/grammar/facets/index.js +7 -47
  256. package/src/grammar/facets/scales.js +15 -18
  257. package/src/grammar/impute.js +341 -0
  258. package/src/grammar/markLabelSelection.js +57 -0
  259. package/src/grammar/markLabels.js +18 -1
  260. package/src/grammar/normalize.js +366 -0
  261. package/src/grammar/pathSeries.js +26 -8
  262. package/src/grammar/pointShapes.js +13 -4
  263. package/src/grammar/polar.js +90 -8
  264. package/src/grammar/regression/index.js +1 -0
  265. package/src/grammar/regression/parameters.js +23 -0
  266. package/src/grammar/roundedRect.js +180 -0
  267. package/src/grammar/rules.js +1 -0
  268. package/src/grammar/scales/colorConsumers.js +20 -1
  269. package/src/grammar/scales/continuous.js +2 -2
  270. package/src/grammar/scales/definition.js +27 -4
  271. package/src/grammar/scales/index.js +1 -0
  272. package/src/grammar/scales/ordinal.js +12 -3
  273. package/src/grammar/scales/policies.js +2 -2
  274. package/src/grammar/scales/size.js +456 -0
  275. package/src/grammar/scales/types.js +8 -0
  276. package/src/grammar/schemas/concreteGraphic.js +14 -2
  277. package/src/grammar/schemas/graphic.js +15 -0
  278. package/src/grammar/schemas/graphicBounds.js +190 -41
  279. package/src/grammar/schemas/semanticPath.js +7 -2
  280. package/src/grammar/statisticalReference.js +103 -0
  281. package/src/grammar/strokeStyle.js +77 -0
  282. package/src/grammar/summary.js +78 -16
  283. package/src/grammar/timeUnit.js +216 -5
  284. package/src/grammar/transformTopology.js +4 -0
  285. package/src/grammar/transforms.js +394 -28
  286. package/src/grammar/weightedStatistics.js +374 -0
  287. package/src/grammar/window.js +221 -16
  288. package/src/layout/aspect.js +80 -0
  289. package/src/layout/facets.js +59 -5
  290. package/src/layout/labels.js +351 -7
  291. package/src/layout/legend.js +4 -4
  292. package/src/layout/legendItems.js +6 -6
  293. package/src/layout/text.js +10 -9
  294. package/src/layout/title.js +9 -9
  295. package/src/materialization/bars/aggregate.js +25 -2
  296. package/src/materialization/bars/histogram.js +84 -24
  297. package/src/materialization/bars/ranged.js +19 -3
  298. package/src/materialization/bars/resolve.js +7 -4
  299. package/src/materialization/composition.js +3 -1
  300. package/src/materialization/coordinateBounds.js +204 -0
  301. package/src/materialization/dependencies.js +36 -0
  302. package/src/materialization/facetGuides/legacyCategorical.js +3 -0
  303. package/src/materialization/facetGuides/placement.js +9 -3
  304. package/src/materialization/facetGuides/preparation.js +13 -8
  305. package/src/materialization/facetHeaders.js +347 -0
  306. package/src/materialization/facets.js +15 -65
  307. package/src/materialization/guides/layout.js +3 -3
  308. package/src/materialization/guides/resources.js +26 -0
  309. package/src/materialization/legends.js +1 -1
  310. package/src/materialization/marks/index.js +11 -5
  311. package/src/materialization/marks/policies.js +2 -1
  312. package/src/materialization/rect.js +25 -12
  313. package/src/materialization/rectConfig.js +5 -1
  314. package/src/materialization/scaleGuideDependencies.js +10 -0
  315. package/src/materialization/scales/map.js +5 -3
  316. package/src/materialization/scales/policies/binnedPosition.js +2 -2
  317. package/src/materialization/scales/policies/offset.js +50 -8
  318. package/src/materialization/scales/policies/series.js +4 -1
  319. package/src/materialization/scales/resolve.js +22 -6
  320. package/src/materialization/selection/items/arc.js +28 -4
  321. package/src/materialization/selection/items/bar.js +30 -3
  322. package/src/materialization/selection/items/common.js +20 -5
  323. package/src/materialization/selection/items/rect.js +28 -4
  324. package/src/materialization/selection/path.js +1 -1
  325. package/src/materialization/selection/point.js +3 -1
  326. package/src/materialization/text.js +265 -14
  327. package/src/materialization/typography.js +31 -0
  328. package/src/mcp/cli.js +5 -3
  329. package/src/persistence/codec.js +65 -0
  330. package/src/persistence/validation.js +185 -0
  331. package/src/persistence.js +74 -0
  332. package/src/renderers/canvas/circle.js +2 -3
  333. package/src/renderers/canvas/index.js +5 -4
  334. package/src/renderers/canvas/line.js +2 -3
  335. package/src/renderers/canvas/native.js +4 -4
  336. package/src/renderers/canvas/path.js +2 -3
  337. package/src/renderers/canvas/rect.js +2 -3
  338. package/src/renderers/canvas/stroke.js +11 -0
  339. package/src/renderers/nativeBackend.js +10 -0
  340. package/src/renderers/options.js +13 -0
  341. package/src/renderers/pdf.js +25 -54
  342. package/src/renderers/png.js +22 -17
  343. package/src/renderers/svg.js +29 -14
  344. package/src/renderers/text.js +1 -11
  345. package/src/selectors/coordinates.js +3 -1
  346. package/src/selectors/datasets.js +38 -5
  347. package/src/selectors/layers.js +10 -4
  348. package/src/selectors/markOwners.js +46 -0
  349. package/src/selectors/scales.js +6 -4
  350. package/src/theme/defaults.js +103 -1
  351. package/src/version.js +2 -0
  352. package/types/accessibility.d.ts +37 -0
  353. package/types/basic.d.ts +10 -2
  354. package/types/diagnostics.d.ts +15 -0
  355. package/types/index.d.ts +90 -0
  356. package/types/pdf.d.ts +9 -0
  357. package/types/persistence.d.ts +7 -0
  358. package/types/png.d.ts +9 -0
  359. package/types/program.d.ts +944 -118
@@ -0,0 +1,180 @@
1
+ import { freezeOwned, isPlainObject } from "../core/immutable.js";
2
+ import {
3
+ requestedStrokeDetails,
4
+ STROKE_STYLE_PROPERTIES
5
+ } from "./strokeStyle.js";
6
+
7
+ export const ROUNDED_RECT_K = 4 * (Math.sqrt(2) - 1) / 3;
8
+ export const RECT_STYLE_PROPERTIES = Object.freeze([
9
+ "cornerRadius",
10
+ ...STROKE_STYLE_PROPERTIES
11
+ ]);
12
+
13
+ function requireFinite(value, property) {
14
+ if (!Number.isFinite(value)) {
15
+ throw new TypeError(`Rounded rect ${property} must be a finite number.`);
16
+ }
17
+ return value;
18
+ }
19
+
20
+ export function validateCornerRadius(value, label = "Rect style") {
21
+ if (!Number.isFinite(value)) {
22
+ throw new TypeError(`${label} cornerRadius must be a finite number.`);
23
+ }
24
+ if (value < 0) {
25
+ throw new RangeError(`${label} cornerRadius must not be negative.`);
26
+ }
27
+ return value;
28
+ }
29
+
30
+ export function requestedRectStyleDetails(request = {}, label = "Rect style") {
31
+ if (!isPlainObject(request)) {
32
+ throw new TypeError(`${label} details must be a plain object.`);
33
+ }
34
+ return freezeOwned({
35
+ ...requestedStrokeDetails(request, label),
36
+ ...(Object.hasOwn(request, "cornerRadius")
37
+ ? { cornerRadius: validateCornerRadius(request.cornerRadius, label) }
38
+ : {})
39
+ });
40
+ }
41
+
42
+ export function normalizeRectGeometry({ x, y, width, height }) {
43
+ x = requireFinite(x, "x");
44
+ y = requireFinite(y, "y");
45
+ width = requireFinite(width, "width");
46
+ height = requireFinite(height, "height");
47
+ if (width < 0) {
48
+ x += width;
49
+ width = -width;
50
+ }
51
+ if (height < 0) {
52
+ y += height;
53
+ height = -height;
54
+ }
55
+ return freezeOwned({ x, y, width, height });
56
+ }
57
+
58
+ export function resolveRoundedRectRadius(width, height, requestedRadius) {
59
+ requireFinite(width, "width");
60
+ requireFinite(height, "height");
61
+ validateCornerRadius(requestedRadius, "Rounded rect");
62
+ if (width < 0 || height < 0) {
63
+ throw new RangeError("Rounded rect width and height must not be negative.");
64
+ }
65
+ return Math.min(requestedRadius, width / 2, height / 2);
66
+ }
67
+
68
+ export function roundedRectCommands({ x, y, width, height, radius }) {
69
+ const geometry = normalizeRectGeometry({ x, y, width, height });
70
+ const r = resolveRoundedRectRadius(
71
+ geometry.width,
72
+ geometry.height,
73
+ radius
74
+ );
75
+ if (!(r > 0)) {
76
+ throw new RangeError("Rounded rect commands require a positive resolved radius.");
77
+ }
78
+ const right = geometry.x + geometry.width;
79
+ const bottom = geometry.y + geometry.height;
80
+ const c = ROUNDED_RECT_K * r;
81
+ return freezeOwned([
82
+ { op: "M", x: geometry.x + r, y: geometry.y },
83
+ { op: "L", x: right - r, y: geometry.y },
84
+ {
85
+ op: "C",
86
+ x1: right - r + c,
87
+ y1: geometry.y,
88
+ x2: right,
89
+ y2: geometry.y + r - c,
90
+ x: right,
91
+ y: geometry.y + r
92
+ },
93
+ { op: "L", x: right, y: bottom - r },
94
+ {
95
+ op: "C",
96
+ x1: right,
97
+ y1: bottom - r + c,
98
+ x2: right - r + c,
99
+ y2: bottom,
100
+ x: right - r,
101
+ y: bottom
102
+ },
103
+ { op: "L", x: geometry.x + r, y: bottom },
104
+ {
105
+ op: "C",
106
+ x1: geometry.x + r - c,
107
+ y1: bottom,
108
+ x2: geometry.x,
109
+ y2: bottom - r + c,
110
+ x: geometry.x,
111
+ y: bottom - r
112
+ },
113
+ { op: "L", x: geometry.x, y: geometry.y + r },
114
+ {
115
+ op: "C",
116
+ x1: geometry.x,
117
+ y1: geometry.y + r - c,
118
+ x2: geometry.x + r - c,
119
+ y2: geometry.y,
120
+ x: geometry.x + r,
121
+ y: geometry.y
122
+ },
123
+ { op: "Z" }
124
+ ].map(command => freezeOwned(command)));
125
+ }
126
+
127
+ export function materializeRectItem(properties, requestedRadius = 0) {
128
+ if (!isPlainObject(properties)) {
129
+ throw new TypeError("Rounded rect item properties must be a plain object.");
130
+ }
131
+ const geometry = normalizeRectGeometry(properties);
132
+ const radius = resolveRoundedRectRadius(
133
+ geometry.width,
134
+ geometry.height,
135
+ requestedRadius
136
+ );
137
+ const appearance = Object.fromEntries(Object.entries(properties).filter(
138
+ ([key]) => !["x", "y", "width", "height"].includes(key)
139
+ ));
140
+ return radius === 0
141
+ ? freezeOwned({
142
+ type: "rect",
143
+ properties: freezeOwned({ ...geometry, ...appearance })
144
+ })
145
+ : freezeOwned({
146
+ type: "path",
147
+ properties: freezeOwned({
148
+ commands: roundedRectCommands({ ...geometry, radius }),
149
+ ...appearance
150
+ })
151
+ });
152
+ }
153
+
154
+ export function readRectItemGeometry(item) {
155
+ if (!isPlainObject(item) || !isPlainObject(item.properties)) return undefined;
156
+ if (item.type === "rect") {
157
+ try {
158
+ return normalizeRectGeometry(item.properties);
159
+ } catch {
160
+ return undefined;
161
+ }
162
+ }
163
+ const commands = item.type === "path" ? item.properties.commands : undefined;
164
+ if (
165
+ !Array.isArray(commands) ||
166
+ commands.length !== 10 ||
167
+ commands.map(command => command?.op).join("") !== "MLCLCLCLCZ"
168
+ ) return undefined;
169
+ const geometry = {
170
+ x: commands[7].x,
171
+ y: commands[0].y,
172
+ width: commands[2].x - commands[7].x,
173
+ height: commands[4].y - commands[0].y
174
+ };
175
+ try {
176
+ return normalizeRectGeometry(geometry);
177
+ } catch {
178
+ return undefined;
179
+ }
180
+ }
@@ -11,6 +11,7 @@ const VALUE_CHANNELS = Object.freeze([
11
11
  ...ENDPOINT_CHANNELS,
12
12
  "xOffset",
13
13
  "yOffset",
14
+ "stroke",
14
15
  "strokeDash",
15
16
  "strokeWidth",
16
17
  "opacity"
@@ -2,8 +2,27 @@ import { BAR_GRAINS, resolveBarChannels, resolveBarGrain } from "../bars/policy.
2
2
  import { validateAggregate, validateAggregateFieldType } from "../aggregate.js";
3
3
  import { isDiscretizedColorScaleType } from "./types.js";
4
4
 
5
- export function validateContinuousColorConsumer(layer, encoding, scale, { inferAggregate = false } = {}) {
5
+ export function validateContinuousColorConsumer(layer, encoding, scale, {
6
+ inferAggregate = false,
7
+ channel = "color"
8
+ } = {}) {
6
9
  const kind = layer.mark?.type;
10
+ if (channel === "stroke") {
11
+ if (!["point", "line", "area", "bar", "rect", "arc", "rule", "tick"].includes(kind)) {
12
+ throw new Error("Continuous stroke requires a supported graphical mark consumer.");
13
+ }
14
+ if (!['quantitative', 'temporal'].includes(encoding.fieldType) ||
15
+ (isDiscretizedColorScaleType(scale.type) && encoding.fieldType !== "quantitative")) {
16
+ throw new Error(`Scale type "${scale.type}" has an incompatible stroke field type.`);
17
+ }
18
+ if (encoding.aggregate !== undefined) {
19
+ throw new Error("Continuous stroke does not support aggregate.");
20
+ }
21
+ if (Object.hasOwn(scale, "unknown") && kind !== "point") {
22
+ throw new Error("Continuous stroke scale unknown currently requires a row-owned point mark.");
23
+ }
24
+ return undefined;
25
+ }
7
26
  if (!["point", "bar", "rect"].includes(kind)) {
8
27
  throw new Error("Continuous color requires a Point, aggregate Bar, or Rect consumer.");
9
28
  }
@@ -94,10 +94,10 @@ export function resolveContinuousDomain({ domain, values, type, nice, zero }) {
94
94
  return resolved;
95
95
  }
96
96
 
97
- export function resolveScaleRange(range, channel, bounds) {
97
+ export function resolveScaleRange(range, channel, bounds, polarFrame) {
98
98
  validatePositionChannel(channel);
99
99
  if (POLAR_POSITION_CHANNELS.includes(channel)) {
100
- return resolvePolarScaleRange(range, channel, bounds);
100
+ return resolvePolarScaleRange(range, channel, bounds, polarFrame);
101
101
  }
102
102
  const validated = validateScaleRange(range);
103
103
  if (validated !== "auto") return validated;
@@ -24,13 +24,35 @@ function retainedValue(previous, patch, defaults, property, typeChanged) {
24
24
  }
25
25
 
26
26
  function validateBandParameters(definition, previous, patch, defaults, typeChanged) {
27
+ const hasPadding = Object.hasOwn(patch, "padding");
28
+ if (
29
+ hasPadding &&
30
+ (Object.hasOwn(patch, "paddingInner") || Object.hasOwn(patch, "paddingOuter"))
31
+ ) {
32
+ throw new Error(
33
+ "Scale padding cannot be combined with paddingInner or paddingOuter."
34
+ );
35
+ }
36
+ if (
37
+ hasPadding &&
38
+ (!Number.isFinite(patch.padding) || patch.padding < 0 || patch.padding >= 1)
39
+ ) {
40
+ throw new RangeError(
41
+ "Scale padding must be from 0 (inclusive) to 1 (exclusive)."
42
+ );
43
+ }
44
+ const normalizedPatch = hasPadding
45
+ ? { ...patch, paddingInner: patch.padding, paddingOuter: patch.padding }
46
+ : patch;
27
47
  const paddingInner = retainedValue(
28
- previous, patch, defaults, "paddingInner", typeChanged
48
+ previous, normalizedPatch, defaults, "paddingInner", typeChanged
29
49
  ) ?? 0;
30
50
  const paddingOuter = retainedValue(
31
- previous, patch, defaults, "paddingOuter", typeChanged
51
+ previous, normalizedPatch, defaults, "paddingOuter", typeChanged
32
52
  ) ?? 0;
33
- const align = retainedValue(previous, patch, defaults, "align", typeChanged) ?? 0.5;
53
+ const align = retainedValue(
54
+ previous, normalizedPatch, defaults, "align", typeChanged
55
+ ) ?? 0.5;
34
56
  if (!Number.isFinite(paddingInner) || paddingInner < 0 || paddingInner >= 1) {
35
57
  throw new RangeError(
36
58
  "Scale paddingInner must be from 0 (inclusive) to 1 (exclusive)."
@@ -66,6 +88,7 @@ export function normalizeScaleDefinition({
66
88
  defaults = {},
67
89
  retainCoreOnTypeChange = false,
68
90
  retainCompatibleOnTypeChange = false,
91
+ allowOrdinalBandParameters = false,
69
92
  validateDomain,
70
93
  validateRange
71
94
  }) {
@@ -143,7 +166,7 @@ export function normalizeScaleDefinition({
143
166
  );
144
167
  if (midpoint !== undefined) definition.midpoint = midpoint;
145
168
 
146
- if (type === "band") {
169
+ if (type === "band" || (type === "ordinal" && allowOrdinalBandParameters)) {
147
170
  validateBandParameters(definition, previous, patch, defaults, typeChanged);
148
171
  } else if (type === "point") {
149
172
  validatePointParameters(definition, previous, patch, defaults, typeChanged);
@@ -7,6 +7,7 @@ export * from "./fields.js";
7
7
  export * from "./mapping.js";
8
8
  export * from "./ordinal.js";
9
9
  export * from "./policies.js";
10
+ export * from "./size.js";
10
11
  export * from "./transformed.js";
11
12
  export * from "./types.js";
12
13
  export * from "./validation.js";
@@ -196,6 +196,7 @@ export function resolveOrdinalOffsetScale({
196
196
  parentBandwidth,
197
197
  paddingInner = 0,
198
198
  paddingOuter = 0,
199
+ align = 0.5,
199
200
  channel = "xOffset"
200
201
  }) {
201
202
  const resolvedDomain = resolveOrdinalDomain(domain, values);
@@ -217,6 +218,9 @@ export function resolveOrdinalOffsetScale({
217
218
  "Offset scale paddingOuter must be a non-negative finite number."
218
219
  );
219
220
  }
221
+ if (!Number.isFinite(align) || align < 0 || align > 1) {
222
+ throw new RangeError("Offset scale align must be between 0 and 1.");
223
+ }
220
224
  validateDomainValues(resolvedDomain, values, undefined, "ordinal");
221
225
  const denominator = Math.max(
222
226
  1,
@@ -231,15 +235,18 @@ export function resolveOrdinalOffsetScale({
231
235
  if (!Number.isFinite(bandwidth) || bandwidth <= 0) {
232
236
  throw new Error("Offset scale padding must leave a positive bandwidth.");
233
237
  }
238
+ const occupied = step * (resolvedDomain.length - paddingInner);
239
+ const start = resolvedRange[0] + (resolvedRange[1] - resolvedRange[0] - occupied) * align;
234
240
  return cloneAndFreeze({
235
241
  type: "ordinal",
236
242
  domain: resolvedDomain,
237
243
  range: resolvedRange,
238
244
  step,
239
- start: resolvedRange[0] + step * paddingOuter,
245
+ start,
240
246
  bandwidth,
241
247
  paddingInner,
242
- paddingOuter
248
+ paddingOuter,
249
+ align
243
250
  });
244
251
  }
245
252
 
@@ -296,7 +303,9 @@ export function mapOrdinalOffsetValues(values, scale) {
296
303
  return finitePosition(
297
304
  direct,
298
305
  scale,
299
- (scale.paddingOuter + index + (1 - scale.paddingInner) / 2) /
306
+ (scale.paddingOuter * 2 * scale.align +
307
+ index +
308
+ (1 - scale.paddingInner) / 2) /
300
309
  denominator
301
310
  );
302
311
  }));
@@ -9,9 +9,9 @@ export function validateScaleUnknown(channel, value) {
9
9
  }
10
10
  return value;
11
11
  }
12
- if (channel === "color") {
12
+ if (["color", "stroke"].includes(channel)) {
13
13
  if (typeof value !== "string" || value.length === 0) {
14
- throw new TypeError("Scale unknown for color must be a non-empty color string.");
14
+ throw new TypeError(`Scale unknown for ${channel} must be a non-empty color string.`);
15
15
  }
16
16
  return value;
17
17
  }