ggaction 0.0.12 → 0.0.15

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 (378) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/README.md +20 -11
  3. package/knowledge/action-card.schema.json +1 -1
  4. package/knowledge/action-cards.json +1 -1
  5. package/knowledge/action-cards.schema.json +1 -1
  6. package/knowledge/extension-authoring.md +2 -1
  7. package/knowledge/intent-taxonomy.json +1 -1
  8. package/knowledge/mcp-resources.json +1 -1
  9. package/knowledge/task-resolver.js +1 -1
  10. package/package.json +10 -3
  11. package/src/actions/basic.js +4 -2
  12. package/src/actions/boxPlots/components.js +14 -4
  13. package/src/actions/boxPlots/edit.js +31 -5
  14. package/src/actions/boxPlots/materialize.js +13 -4
  15. package/src/actions/boxPlots/options.js +48 -16
  16. package/src/actions/canvas/actions.js +3 -1
  17. package/src/actions/canvas/fitting.js +211 -0
  18. package/src/actions/canvas/index.js +2 -0
  19. package/src/actions/categoryOrder/index.js +18 -6
  20. package/src/actions/charts/area.js +51 -0
  21. package/src/actions/charts/bar.js +13 -6
  22. package/src/actions/charts/beeswarm.js +70 -0
  23. package/src/actions/charts/density.js +48 -0
  24. package/src/actions/charts/ecdf.js +186 -0
  25. package/src/actions/charts/endpoints.js +473 -0
  26. package/src/actions/charts/heatmap.js +4 -3
  27. package/src/actions/charts/histogram.js +5 -4
  28. package/src/actions/charts/horizon.js +73 -0
  29. package/src/actions/charts/index.js +37 -0
  30. package/src/actions/charts/interval-regression.js +172 -0
  31. package/src/actions/charts/line.js +8 -6
  32. package/src/actions/charts/parallel.js +8 -2
  33. package/src/actions/charts/pie.js +52 -0
  34. package/src/actions/charts/polar.js +134 -0
  35. package/src/actions/charts/radar.js +305 -0
  36. package/src/actions/charts/radial.js +50 -0
  37. package/src/actions/charts/raincloud.js +422 -0
  38. package/src/actions/charts/rug-strip.js +283 -0
  39. package/src/actions/charts/scatter.js +11 -3
  40. package/src/actions/charts/shared.js +69 -10
  41. package/src/actions/composition/actions.js +129 -3
  42. package/src/actions/coordinates/actions.js +1 -1
  43. package/src/actions/coordinates/edit.js +76 -0
  44. package/src/actions/data/bin.js +29 -0
  45. package/src/actions/data/bin2d.js +57 -185
  46. package/src/actions/data/complete.js +26 -0
  47. package/src/actions/data/computed.js +24 -0
  48. package/src/actions/data/create.js +2 -2
  49. package/src/actions/data/density.js +12 -20
  50. package/src/actions/data/derived.js +111 -15
  51. package/src/actions/data/ecdf.js +38 -0
  52. package/src/actions/data/edit.js +539 -0
  53. package/src/actions/data/filter.js +226 -30
  54. package/src/actions/data/fold.js +24 -0
  55. package/src/actions/data/impute.js +26 -0
  56. package/src/actions/data/index.js +38 -0
  57. package/src/actions/data/interval.js +2 -0
  58. package/src/actions/data/intervalEdit.js +86 -4
  59. package/src/actions/data/normalize.js +27 -0
  60. package/src/actions/data/regression.js +19 -21
  61. package/src/actions/data/revisionBindings.js +145 -0
  62. package/src/actions/data/shared.js +20 -4
  63. package/src/actions/data/stack.js +26 -0
  64. package/src/actions/data/summary.js +26 -0
  65. package/src/actions/data/timeUnit.js +4 -1
  66. package/src/actions/data/window.js +1 -1
  67. package/src/actions/encodings/appearance.js +32 -13
  68. package/src/actions/encodings/barWidth.js +8 -0
  69. package/src/actions/encodings/basic.js +2 -0
  70. package/src/actions/encodings/channels.js +415 -0
  71. package/src/actions/encodings/color/continuous.js +19 -59
  72. package/src/actions/encodings/color/index.js +30 -23
  73. package/src/actions/encodings/color/layout.js +3 -97
  74. package/src/actions/encodings/color/policy.js +5 -8
  75. package/src/actions/encodings/density/resolve.js +37 -5
  76. package/src/actions/encodings/density.js +124 -34
  77. package/src/actions/encodings/histogram.js +3 -2
  78. package/src/actions/encodings/horizon/resolve.js +8 -4
  79. package/src/actions/encodings/horizon.js +18 -8
  80. package/src/actions/encodings/index.js +6 -0
  81. package/src/actions/encodings/offset.js +30 -29
  82. package/src/actions/encodings/parallel.js +2 -8
  83. package/src/actions/encodings/position/apply.js +63 -11
  84. package/src/actions/encodings/position/index.js +52 -6
  85. package/src/actions/encodings/position/policies/arc.js +14 -0
  86. package/src/actions/encodings/position/policies/area.js +4 -42
  87. package/src/actions/encodings/position/policies/bar.js +57 -13
  88. package/src/actions/encodings/position/resolve.js +88 -26
  89. package/src/actions/encodings/ranged.js +123 -97
  90. package/src/actions/encodings/remove.js +44 -34
  91. package/src/actions/encodings/ruleAppearance.js +24 -32
  92. package/src/actions/encodings/seriesLayout.js +105 -0
  93. package/src/actions/encodings/shared.js +107 -37
  94. package/src/actions/encodings/stroke.js +226 -0
  95. package/src/actions/encodings/strokeDash.js +9 -6
  96. package/src/actions/encodings/temporal.js +7 -0
  97. package/src/actions/encodings/text.js +30 -15
  98. package/src/actions/errorBands/create.js +41 -8
  99. package/src/actions/errorBands/edit.js +360 -25
  100. package/src/actions/errorBars/create.js +56 -17
  101. package/src/actions/errorBars/edit.js +372 -21
  102. package/src/actions/facets/actions.js +619 -41
  103. package/src/actions/facets/derive.js +139 -25
  104. package/src/actions/gradientPlots/components.js +43 -13
  105. package/src/actions/gradientPlots/edit.js +14 -4
  106. package/src/actions/gradientPlots/materialize.js +8 -8
  107. package/src/actions/gradientPlots/options.js +10 -2
  108. package/src/actions/guides/applicability.js +28 -19
  109. package/src/actions/guides/axes/axes.js +4 -3
  110. package/src/actions/guides/axes/axis.js +18 -18
  111. package/src/actions/guides/axes/components.js +43 -0
  112. package/src/actions/guides/axes/edit.js +16 -51
  113. package/src/actions/guides/axes/labels.js +184 -28
  114. package/src/actions/guides/axes/lines.js +13 -7
  115. package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
  116. package/src/actions/guides/axes/parallel/policy.js +137 -0
  117. package/src/actions/guides/axes/parallel/resolve.js +50 -25
  118. package/src/actions/guides/axes/parallel.js +93 -217
  119. package/src/actions/guides/axes/policy.js +21 -67
  120. package/src/actions/guides/axes/tickGroups.js +15 -8
  121. package/src/actions/guides/axes/ticks.js +11 -6
  122. package/src/actions/guides/axes/titles.js +32 -11
  123. package/src/actions/guides/basic.js +2 -0
  124. package/src/actions/guides/facade.js +106 -0
  125. package/src/actions/guides/facadeAxes.js +200 -0
  126. package/src/actions/guides/facadeLegend.js +63 -0
  127. package/src/actions/guides/grids/grid.js +37 -33
  128. package/src/actions/guides/grids/resolve.js +17 -4
  129. package/src/actions/guides/guides.js +54 -31
  130. package/src/actions/guides/legends/blocks.js +426 -0
  131. package/src/actions/guides/legends/categorical/actions.js +309 -279
  132. package/src/actions/guides/legends/categorical/components.js +21 -17
  133. package/src/actions/guides/legends/categorical/index.js +37 -33
  134. package/src/actions/guides/legends/categorical/layout.js +123 -354
  135. package/src/actions/guides/legends/categorical/options.js +32 -16
  136. package/src/actions/guides/legends/categorical/recipes.js +114 -9
  137. package/src/actions/guides/legends/categorical/resolve.js +30 -15
  138. package/src/actions/guides/legends/categorical/symbols.js +87 -33
  139. package/src/actions/guides/legends/continuous/common.js +99 -26
  140. package/src/actions/guides/legends/continuous/gradient.js +120 -89
  141. package/src/actions/guides/legends/continuous/index.js +8 -2
  142. package/src/actions/guides/legends/continuous/interval.js +79 -95
  143. package/src/actions/guides/legends/continuous/opacity.js +113 -89
  144. package/src/actions/guides/legends/continuous/stroke.js +525 -0
  145. package/src/actions/guides/legends/creation.js +40 -0
  146. package/src/actions/guides/legends/edit.js +321 -90
  147. package/src/actions/guides/legends/focused.js +2 -2
  148. package/src/actions/guides/legends/index.js +2 -0
  149. package/src/actions/guides/legends/lane.js +149 -102
  150. package/src/actions/guides/legends/lifecycle.js +143 -0
  151. package/src/actions/guides/legends/remove.js +43 -78
  152. package/src/actions/guides/legends/sampling.js +140 -0
  153. package/src/actions/guides/legends/size.js +213 -116
  154. package/src/actions/guides/legends/strokeWidth.js +121 -73
  155. package/src/actions/guides/legends/target.js +123 -0
  156. package/src/actions/guides/legends/transition.js +192 -0
  157. package/src/actions/guides/polar/axes/facade.js +81 -66
  158. package/src/actions/guides/polar/axes/labels.js +28 -8
  159. package/src/actions/guides/polar/axes/lines.js +12 -5
  160. package/src/actions/guides/polar/axes/shared.js +22 -0
  161. package/src/actions/guides/polar/axes/ticks.js +6 -5
  162. package/src/actions/guides/polar/axes/titles.js +6 -5
  163. package/src/actions/guides/polar/grids.js +8 -8
  164. package/src/actions/guides/polar/resolve.js +12 -8
  165. package/src/actions/guides/reuse.js +44 -0
  166. package/src/actions/guides/tickValues.js +19 -21
  167. package/src/actions/index.js +15 -2
  168. package/src/actions/intervals/resolve.js +35 -15
  169. package/src/actions/marks/arc/actions.js +78 -20
  170. package/src/actions/marks/area/actions.js +51 -12
  171. package/src/actions/marks/area/materialize.js +22 -3
  172. package/src/actions/marks/bar/create.js +11 -2
  173. package/src/actions/marks/bar/edit.js +20 -3
  174. package/src/actions/marks/bar/materialize.js +33 -9
  175. package/src/actions/marks/index.js +14 -0
  176. package/src/actions/marks/line/actions.js +71 -113
  177. package/src/actions/marks/line/materialize.js +57 -56
  178. package/src/actions/marks/point/create.js +8 -2
  179. package/src/actions/marks/point/edit.js +29 -21
  180. package/src/actions/marks/point/index.js +1 -0
  181. package/src/actions/marks/point/jitter.js +3 -0
  182. package/src/actions/marks/point/materialize.js +154 -16
  183. package/src/actions/marks/point/packing.js +68 -0
  184. package/src/actions/marks/rect/actions.js +33 -12
  185. package/src/actions/marks/references.js +658 -0
  186. package/src/actions/marks/remove.js +87 -4
  187. package/src/actions/marks/rule/actions.js +77 -9
  188. package/src/actions/marks/rule/appearance.js +26 -0
  189. package/src/actions/marks/shared.js +13 -13
  190. package/src/actions/marks/text/actions.js +296 -13
  191. package/src/actions/marks/text/index.js +4 -0
  192. package/src/actions/marks/text/layout.js +156 -8
  193. package/src/actions/marks/text/removal.js +173 -0
  194. package/src/actions/marks/text/selection.js +73 -0
  195. package/src/actions/marks/tick/actions.js +22 -5
  196. package/src/actions/primitives/createGraphics.js +3 -1
  197. package/src/actions/primitives/editGraphics.js +26 -0
  198. package/src/actions/primitives/semantic.js +3 -2
  199. package/src/actions/primitives/semanticAction.js +120 -10
  200. package/src/actions/primitives/semanticValidation/guide.js +10 -0
  201. package/src/actions/primitives/semanticValidation/index.js +9 -1
  202. package/src/actions/primitives/semanticValidation/layer.js +23 -1
  203. package/src/actions/primitives/semanticValidation/scale.js +38 -8
  204. package/src/actions/regression/components.js +26 -11
  205. package/src/actions/regression/create.js +11 -3
  206. package/src/actions/regression/edit.js +40 -13
  207. package/src/actions/regression/resolve.js +1 -1
  208. package/src/actions/resources/index.js +7 -0
  209. package/src/actions/resources/remove.js +172 -0
  210. package/src/actions/scales/channels.js +212 -0
  211. package/src/actions/scales/consumers/common.js +21 -54
  212. package/src/actions/scales/consumers/families.js +28 -14
  213. package/src/actions/scales/consumers/index.js +42 -4
  214. package/src/actions/scales/consumers/seriesLayout.js +57 -12
  215. package/src/actions/scales/create.js +58 -26
  216. package/src/actions/scales/definitions.js +109 -39
  217. package/src/actions/scales/edit.js +101 -34
  218. package/src/actions/scales/editPolicy.js +95 -35
  219. package/src/actions/scales/index.js +6 -0
  220. package/src/actions/scales/materialize.js +14 -49
  221. package/src/actions/scales/offset.js +48 -0
  222. package/src/actions/scales/parallel.js +71 -0
  223. package/src/actions/scales/patch.js +1 -1
  224. package/src/actions/scales/preview.js +140 -0
  225. package/src/actions/scales/quantitativeColor.js +9 -5
  226. package/src/actions/selection/actions.js +154 -30
  227. package/src/actions/selection/index.js +3 -0
  228. package/src/actions/theme/actions.js +98 -0
  229. package/src/actions/theme/composition.js +238 -0
  230. package/src/actions/theme/index.js +16 -0
  231. package/src/actions/theme/reconcile.js +1130 -0
  232. package/src/actions/theme/state.js +160 -0
  233. package/src/actions/titles/actions.js +11 -10
  234. package/src/actions/titles/resolve.js +1 -40
  235. package/src/actions/violinPlots/create.js +90 -43
  236. package/src/actions/violinPlots/edit.js +192 -0
  237. package/src/actions/violinPlots/index.js +2 -0
  238. package/src/core/ChartProgram.js +7 -0
  239. package/src/core/action.js +32 -2
  240. package/src/core/compositionState.js +141 -8
  241. package/src/core/programState.js +20 -0
  242. package/src/core/resourceReferences.js +332 -0
  243. package/src/core/vocabulary.js +16 -3
  244. package/src/grammar/aggregate.js +66 -23
  245. package/src/grammar/arcs.js +58 -1
  246. package/src/grammar/areaEndpoints.js +46 -0
  247. package/src/grammar/areaSeries.js +87 -41
  248. package/src/grammar/bars/aggregate.js +37 -13
  249. package/src/grammar/bars/geometry.js +44 -0
  250. package/src/grammar/bars/policy.js +9 -7
  251. package/src/grammar/bin.js +226 -0
  252. package/src/grammar/categoryOrder.js +34 -0
  253. package/src/grammar/complete.js +279 -0
  254. package/src/grammar/computed.js +351 -0
  255. package/src/grammar/coordinates.js +37 -0
  256. package/src/grammar/density.js +239 -37
  257. package/src/grammar/displayLabels.js +57 -0
  258. package/src/grammar/ecdf.js +198 -0
  259. package/src/grammar/facets/dependencies.js +133 -1
  260. package/src/grammar/facets/guides.js +3 -0
  261. package/src/grammar/facets/index.js +123 -52
  262. package/src/grammar/facets/scales.js +15 -18
  263. package/src/grammar/fold.js +123 -0
  264. package/src/grammar/horizon.js +6 -4
  265. package/src/grammar/impute.js +340 -0
  266. package/src/grammar/interval.js +37 -20
  267. package/src/grammar/lineSeries.js +20 -63
  268. package/src/grammar/markFilter.js +22 -4
  269. package/src/grammar/markLabelSelection.js +57 -0
  270. package/src/grammar/markLabels.js +110 -0
  271. package/src/grammar/normalize.js +366 -0
  272. package/src/grammar/parallelAxisTitles.js +16 -0
  273. package/src/grammar/pathSeries.js +109 -0
  274. package/src/grammar/pointPacking.js +183 -0
  275. package/src/grammar/pointShapes.js +13 -4
  276. package/src/grammar/polar.js +90 -8
  277. package/src/grammar/positionDatum.js +12 -0
  278. package/src/grammar/rects.js +9 -1
  279. package/src/grammar/regression/derive.js +4 -0
  280. package/src/grammar/regression/index.js +8 -2
  281. package/src/grammar/regression/models.js +22 -7
  282. package/src/grammar/regression/parameters.js +63 -13
  283. package/src/grammar/rotation.js +29 -0
  284. package/src/grammar/roundedRect.js +180 -0
  285. package/src/grammar/rules.js +8 -21
  286. package/src/grammar/scales/color.js +23 -1
  287. package/src/grammar/scales/colorConsumers.js +57 -0
  288. package/src/grammar/scales/continuous.js +2 -2
  289. package/src/grammar/scales/definition.js +42 -5
  290. package/src/grammar/scales/discretized.js +11 -5
  291. package/src/grammar/scales/fields.js +38 -8
  292. package/src/grammar/scales/index.js +1 -0
  293. package/src/grammar/scales/mapping.js +2 -0
  294. package/src/grammar/scales/ordinal.js +12 -3
  295. package/src/grammar/scales/policies.js +2 -2
  296. package/src/grammar/scales/radial.js +55 -0
  297. package/src/grammar/scales/size.js +456 -0
  298. package/src/grammar/scales/types.js +1 -0
  299. package/src/grammar/schemas/concreteGraphic.js +14 -2
  300. package/src/grammar/schemas/graphic.js +15 -0
  301. package/src/grammar/schemas/graphicBounds.js +179 -30
  302. package/src/grammar/schemas/semanticPath.js +23 -4
  303. package/src/grammar/stack.js +170 -0
  304. package/src/grammar/statisticalReference.js +103 -0
  305. package/src/grammar/statistics/confidenceInterval.js +101 -0
  306. package/src/grammar/strokeStyle.js +77 -0
  307. package/src/grammar/summary.js +245 -0
  308. package/src/grammar/text.js +22 -16
  309. package/src/grammar/timeUnit.js +234 -13
  310. package/src/grammar/transformTopology.js +10 -0
  311. package/src/grammar/transforms.js +418 -16
  312. package/src/grammar/valueFormat.js +112 -0
  313. package/src/grammar/weightedStatistics.js +374 -0
  314. package/src/grammar/window.js +232 -21
  315. package/src/layout/aspect.js +80 -0
  316. package/src/layout/facets.js +100 -12
  317. package/src/layout/guideCollisions.js +12 -0
  318. package/src/layout/labels.js +343 -0
  319. package/src/layout/legend.js +2 -10
  320. package/src/layout/legendItems.js +102 -0
  321. package/src/layout/legendLane.js +100 -11
  322. package/src/materialization/bars/aggregate.js +56 -147
  323. package/src/materialization/bars/histogram.js +104 -20
  324. package/src/materialization/bars/ranged.js +19 -3
  325. package/src/materialization/bars/resolve.js +7 -4
  326. package/src/materialization/categorySlotOffset.js +40 -0
  327. package/src/materialization/composition.js +3 -1
  328. package/src/materialization/compositionSnapshot.js +1 -1
  329. package/src/materialization/coordinateBounds.js +204 -0
  330. package/src/materialization/dependencies.js +93 -16
  331. package/src/materialization/encodings.js +1 -1
  332. package/src/materialization/facetGuides/legacyCategorical.js +3 -0
  333. package/src/materialization/facetGuides/placement.js +8 -2
  334. package/src/materialization/facetGuides/preparation.js +12 -7
  335. package/src/materialization/facetHeaders.js +346 -0
  336. package/src/materialization/facets.js +23 -66
  337. package/src/materialization/guides/layout.js +57 -0
  338. package/src/materialization/guides/resources.js +28 -2
  339. package/src/materialization/legends.js +11 -2
  340. package/src/materialization/marks/capabilities.js +20 -5
  341. package/src/materialization/marks/grouping.js +52 -0
  342. package/src/materialization/marks/index.js +68 -27
  343. package/src/materialization/marks/policies.js +4 -1
  344. package/src/materialization/rect.js +55 -42
  345. package/src/materialization/rectConfig.js +5 -1
  346. package/src/materialization/rowEncoding.js +13 -13
  347. package/src/materialization/scaleGuideDependencies.js +8 -5
  348. package/src/materialization/scales/map.js +6 -3
  349. package/src/materialization/scales/policies/arc.js +42 -0
  350. package/src/materialization/scales/policies/binnedPosition.js +7 -4
  351. package/src/materialization/scales/policies/offset.js +50 -8
  352. package/src/materialization/scales/policies/series.js +5 -1
  353. package/src/materialization/scales/resolve.js +65 -17
  354. package/src/materialization/selection/compatibility.js +11 -0
  355. package/src/materialization/selection/items/arc.js +28 -4
  356. package/src/materialization/selection/items/bar.js +42 -71
  357. package/src/materialization/selection/items/common.js +27 -6
  358. package/src/materialization/selection/items/path.js +3 -13
  359. package/src/materialization/selection/items/rect.js +35 -8
  360. package/src/materialization/selection/path.js +1 -1
  361. package/src/materialization/selection/point.js +3 -1
  362. package/src/materialization/text.js +324 -14
  363. package/src/mcp/adapter.js +2 -2
  364. package/src/renderers/canvas/circle.js +2 -3
  365. package/src/renderers/canvas/line.js +2 -3
  366. package/src/renderers/canvas/native.js +4 -4
  367. package/src/renderers/canvas/path.js +2 -3
  368. package/src/renderers/canvas/rect.js +2 -3
  369. package/src/renderers/canvas/stroke.js +11 -0
  370. package/src/renderers/svg.js +19 -0
  371. package/src/selectors/datasets.js +44 -0
  372. package/src/selectors/layers.js +7 -2
  373. package/src/selectors/scales.js +6 -0
  374. package/src/theme/defaults.js +140 -0
  375. package/types/basic.d.ts +17 -2
  376. package/types/index.d.ts +211 -0
  377. package/types/program.d.ts +2182 -204
  378. package/src/materialization/bars/grouped.js +0 -144
@@ -11,9 +11,16 @@ import {
11
11
  normalizePointJitterPolicy,
12
12
  resolvePointJitter
13
13
  } from "../../../grammar/jitter.js";
14
- import { polarToCartesian, resolvePolarFrame } from "../../../grammar/polar.js";
14
+ import {
15
+ normalizePointPackingPolicy,
16
+ resolvePointPacking
17
+ } from "../../../grammar/pointPacking.js";
18
+ import { polarToCartesian } from "../../../grammar/polar.js";
15
19
  import { resolveDirectionValues } from "../../../grammar/direction.js";
16
- import { resolveGraphicBounds } from "../../../layout/canvas.js";
20
+ import {
21
+ resolveCoordinateBounds,
22
+ resolveCoordinatePolarFrame
23
+ } from "../../../materialization/coordinateBounds.js";
17
24
  import { validateMarkOptions } from "../shared.js";
18
25
  import {
19
26
  DEFAULT_COLORS,
@@ -21,12 +28,17 @@ import {
21
28
  } from "../../../theme/defaults.js";
22
29
  import { findDataset } from "../../../selectors/datasets.js";
23
30
  import { findLayer } from "../../../selectors/layers.js";
31
+ import { canMaterializePoint } from "../../../materialization/marks/capabilities.js";
24
32
  import { rematerializeHighlightBaseline } from "../lifecycle.js";
25
33
  import {
26
34
  resolveRowEncodingValues,
27
35
  resolveRowPositionValues
28
36
  } from
29
37
  "../../../materialization/rowEncoding.js";
38
+ import { offsetCategoryPositions } from
39
+ "../../../materialization/categorySlotOffset.js";
40
+ import { requestedStrokeDetails } from
41
+ "../../../grammar/strokeStyle.js";
30
42
 
31
43
  const REMATERIALIZE_OPTIONS = Object.freeze(["id"]);
32
44
  const DEFAULT_POINT_FILL = DEFAULT_COLORS.mark;
@@ -45,7 +57,7 @@ function resolvePointPositions(program, layer, dataset) {
45
57
  if (theta === undefined || radius === undefined) {
46
58
  return { x: undefined, y: undefined };
47
59
  }
48
- const frame = resolvePolarFrame(resolveGraphicBounds(program));
60
+ const frame = resolveCoordinatePolarFrame(program, layer.coordinate);
49
61
  const positions = theta.map((angle, index) => {
50
62
  const distance = radius[index];
51
63
  return Number.isFinite(angle) && Number.isFinite(distance)
@@ -69,7 +81,11 @@ function incompleteShapeGraphic(shape, properties, angle) {
69
81
  ? "path"
70
82
  : getPointGraphicType(shape);
71
83
  const incomplete = type === "path"
72
- ? { fill: properties.fill, opacity: properties.opacity }
84
+ ? {
85
+ fill: properties.fill,
86
+ opacity: properties.opacity,
87
+ ...requestedStrokeDetails(properties, "Point mark")
88
+ }
73
89
  : properties;
74
90
  return { type, properties: compactProperties(incomplete) };
75
91
  }
@@ -173,7 +189,7 @@ function applyPointJitter(program, {
173
189
  `Point jitter on "${id}" requires a resolved ${policy.channel} scale.`
174
190
  );
175
191
  }
176
- const bounds = resolveGraphicBounds(program);
192
+ const bounds = resolveCoordinateBounds(program, layer.coordinate);
177
193
  const entries = resolveJitterEntries({
178
194
  policy,
179
195
  dataset,
@@ -210,6 +226,80 @@ function applyPointJitter(program, {
210
226
  };
211
227
  }
212
228
 
229
+ function applyPointPacking(program, {
230
+ id,
231
+ layer,
232
+ dataset,
233
+ x,
234
+ y,
235
+ area,
236
+ shapes,
237
+ angles,
238
+ config,
239
+ graphic,
240
+ existingChildren
241
+ }) {
242
+ const stored = program.materializationConfigs.pointPacking?.[id];
243
+ if (stored === undefined) return { program, x, y };
244
+ const policy = normalizePointPackingPolicy(stored);
245
+ const encoding = layer.encoding?.[policy.channel];
246
+ const scale = program.resolvedScales[encoding?.scale];
247
+ if (scale === undefined) {
248
+ throw new Error(`Point packing on "${id}" requires a resolved ${policy.channel} scale.`);
249
+ }
250
+ const seen = new Set();
251
+ const entries = dataset.values.flatMap((row, index) => {
252
+ if (!Number.isFinite(x?.[index]) || !Number.isFinite(y?.[index])) return [];
253
+ const rawIdentity = policy.key === undefined ? index : row[policy.key];
254
+ const identity = canonicalJitterScalar(
255
+ rawIdentity,
256
+ policy.key === undefined
257
+ ? "Point packing source index"
258
+ : `Point packing key field "${policy.key}"`
259
+ );
260
+ if (seen.has(identity)) {
261
+ throw new Error(`Point packing key field "${policy.key}" must be unique for materialized items.`);
262
+ }
263
+ seen.add(identity);
264
+ const resolvedArea = resolvedPointArea(
265
+ area, index, config, existingChildren, graphic.type
266
+ );
267
+ const extent = resolvePointShapeExtent({
268
+ shape: shapes[index],
269
+ area: resolvedArea,
270
+ strokeWidth: typeof config.stroke === "string" ? config.strokeWidth ?? 1 : 0,
271
+ angle: angles?.[index]
272
+ });
273
+ return [{
274
+ index,
275
+ identity,
276
+ base: policy.channel === "x" ? x[index] : y[index],
277
+ fixed: policy.channel === "x" ? y[index] : x[index],
278
+ halfExtent: extent[policy.channel],
279
+ fixedHalfExtent: extent[policy.channel === "x" ? "y" : "x"]
280
+ }];
281
+ });
282
+ const bounds = resolveCoordinateBounds(program, layer.coordinate);
283
+ const resolution = resolvePointPacking({
284
+ target: id,
285
+ policy,
286
+ scale,
287
+ entries,
288
+ plotMinimum: policy.channel === "x" ? bounds.x : bounds.y,
289
+ plotMaximum: policy.channel === "x" ? bounds.x + bounds.width : bounds.y + bounds.height
290
+ });
291
+ const values = [...(policy.channel === "x" ? x : y)];
292
+ for (const item of resolution.items) values[item.index] = item.final;
293
+ return {
294
+ program: program._withMaterializationConfig(["pointPacking", id], {
295
+ ...policy,
296
+ resolved: resolution
297
+ }),
298
+ x: policy.channel === "x" ? values : x,
299
+ y: policy.channel === "y" ? values : y
300
+ };
301
+ }
302
+
213
303
  export const rematerializePointMark = action(
214
304
  {
215
305
  op: "rematerializePointMark",
@@ -251,8 +341,13 @@ export const rematerializePointMark = action(
251
341
  throw new Error(`Point mark "${id}" requires an existing dataset.`);
252
342
  }
253
343
 
344
+ if (graphic.items.length === 0 && !canMaterializePoint(resolved, layer)) {
345
+ return resolved;
346
+ }
347
+
254
348
  const positions = resolvePointPositions(resolved, layer, dataset);
255
349
  const mappedFill = resolveRowEncodingValues(resolved, layer, dataset, "color");
350
+ const mappedStroke = resolveRowEncodingValues(resolved, layer, dataset, "stroke");
256
351
  const area = resolveRowEncodingValues(resolved, layer, dataset, "size");
257
352
  const encodedShape = resolveRowEncodingValues(resolved, layer, dataset, "shape");
258
353
  const encodedOpacity = resolveRowEncodingValues(
@@ -263,6 +358,7 @@ export const rematerializePointMark = action(
263
358
  );
264
359
  const angles = resolveDirectionValues(dataset.values, layer.encoding?.angle);
265
360
  const config = resolved.markConfigs[id] ?? {};
361
+ const strokeDetails = requestedStrokeDetails(config, "Point mark");
266
362
  const fill = mappedFill ?? config.fill ?? DEFAULT_POINT_FILL;
267
363
  const shapes = encodedShape ?? dataset.values.map(() => config.shape ?? "circle");
268
364
  const existingChildren = graphic.items ?? [];
@@ -272,7 +368,13 @@ export const rematerializePointMark = action(
272
368
  angles !== undefined ||
273
369
  graphic.type === "collection" ||
274
370
  getPointGraphicType(constantShape) !== graphic.type;
275
- const jittered = applyPointJitter(resolved, {
371
+ if (
372
+ resolved.materializationConfigs.jitters?.[id] !== undefined &&
373
+ resolved.materializationConfigs.pointPacking?.[id] !== undefined
374
+ ) {
375
+ throw new Error(`Point mark "${id}" cannot combine jitter and packing.`);
376
+ }
377
+ const packed = applyPointPacking(resolved, {
276
378
  id,
277
379
  layer,
278
380
  dataset,
@@ -285,7 +387,20 @@ export const rematerializePointMark = action(
285
387
  graphic,
286
388
  existingChildren
287
389
  });
288
- const { x, y } = jittered;
390
+ const jittered = applyPointJitter(packed.program, {
391
+ id,
392
+ layer,
393
+ dataset,
394
+ x: packed.x,
395
+ y: packed.y,
396
+ area,
397
+ shapes,
398
+ angles,
399
+ config,
400
+ graphic,
401
+ existingChildren
402
+ });
403
+ const { x, y } = offsetCategoryPositions(jittered.program, layer, jittered);
289
404
 
290
405
  if (requiresMixedCollection) {
291
406
  const items = dataset.values.map((_, index) => {
@@ -313,7 +428,8 @@ export const rematerializePointMark = action(
313
428
  x: centerX,
314
429
  y: centerY,
315
430
  fill: color,
316
- opacity
431
+ opacity,
432
+ ...strokeDetails
317
433
  }, angle);
318
434
  }
319
435
  return createPointShapeGraphic({
@@ -322,12 +438,15 @@ export const rematerializePointMark = action(
322
438
  y: centerY,
323
439
  area: resolvedArea,
324
440
  fill: color,
325
- ...(typeof config.stroke === "string" ? { stroke: config.stroke } : {}),
326
- ...(config.strokeWidth === undefined
327
- ? {}
328
- : { strokeWidth: config.strokeWidth }),
441
+ ...(typeof (mappedStroke?.[index] ?? config.stroke) === "string"
442
+ ? { stroke: mappedStroke?.[index] ?? config.stroke }
443
+ : {}),
444
+ ...((mappedStroke !== undefined || config.strokeWidth !== undefined)
445
+ ? { strokeWidth: config.strokeWidth ?? 1 }
446
+ : {}),
329
447
  opacity,
330
- angle
448
+ angle,
449
+ ...strokeDetails
331
450
  });
332
451
  });
333
452
  return jittered.program.editGraphics({
@@ -399,15 +518,31 @@ export const rematerializePointMark = action(
399
518
  next = next
400
519
  .editGraphics({ target: id, property: "width", value: sides })
401
520
  .editGraphics({ target: id, property: "height", value: sides })
402
- .editGraphics({ target: id, property: "stroke", value: fill ?? DEFAULT_POINT_FILL })
403
- .editGraphics({ target: id, property: "strokeWidth", value: 0 });
521
+ .editGraphics({
522
+ target: id,
523
+ property: "stroke",
524
+ value: mappedStroke ?? fill ?? DEFAULT_POINT_FILL
525
+ })
526
+ .editGraphics({
527
+ target: id,
528
+ property: "strokeWidth",
529
+ value: mappedStroke === undefined ? 0 : config.strokeWidth ?? 1
530
+ });
404
531
  }
405
532
  if (config.opacity !== undefined) {
406
533
  next = next.editGraphics({ target: id, property: "opacity", value: config.opacity });
407
534
  } else if (encodedOpacity !== undefined) {
408
535
  next = next.editGraphics({ target: id, property: "opacity", value: encodedOpacity });
409
536
  }
410
- if (config.stroke === false) {
537
+ if (mappedStroke !== undefined) {
538
+ next = next
539
+ .editGraphics({ target: id, property: "stroke", value: mappedStroke })
540
+ .editGraphics({
541
+ target: id,
542
+ property: "strokeWidth",
543
+ value: config.strokeWidth ?? 1
544
+ });
545
+ } else if (config.stroke === false) {
411
546
  next = next
412
547
  .editGraphics({ target: id, property: "stroke", value: "transparent" })
413
548
  .editGraphics({ target: id, property: "strokeWidth", value: 0 });
@@ -419,6 +554,9 @@ export const rematerializePointMark = action(
419
554
  value: config.strokeWidth
420
555
  });
421
556
  }
557
+ for (const [property, value] of Object.entries(strokeDetails)) {
558
+ next = next.editGraphics({ target: id, property, value });
559
+ }
422
560
  return next;
423
561
  }
424
562
  );
@@ -0,0 +1,68 @@
1
+ import { action } from "../../../core/action.js";
2
+ import { validateUserId } from "../../../core/identifiers.js";
3
+ import { validateKeys } from "../../../core/validation.js";
4
+ import { normalizePointPackingPolicy } from "../../../grammar/pointPacking.js";
5
+ import { findCoordinate } from "../../../selectors/coordinates.js";
6
+ import { resolveEligibleLayer } from "../../../selectors/layers.js";
7
+
8
+ const OPTIONS = Object.freeze([
9
+ "target", "channel", "maxOffset", "padding", "key", "overflow"
10
+ ]);
11
+ const REMOVE_OPTIONS = Object.freeze(["target"]);
12
+
13
+ function isPackablePoint(program, layer, channel) {
14
+ const packed = layer.encoding?.[channel];
15
+ const fixed = layer.encoding?.[channel === "x" ? "y" : "x"];
16
+ return layer.mark?.type === "point" &&
17
+ ["nominal", "ordinal"].includes(packed?.fieldType) &&
18
+ ["quantitative", "temporal"].includes(fixed?.fieldType) &&
19
+ packed.scale !== undefined && fixed.scale !== undefined &&
20
+ findCoordinate(program, layer.coordinate)?.type === "cartesian";
21
+ }
22
+
23
+ function target(program, requested, channel) {
24
+ return resolveEligibleLayer(program, {
25
+ target: requested === undefined ? undefined : validateUserId(requested, "Point packing target"),
26
+ predicate: layer => isPackablePoint(program, layer, channel),
27
+ label: "point packing"
28
+ });
29
+ }
30
+
31
+ export const packPoints = action(
32
+ { op: "packPoints", description: "Pack point glyphs deterministically within categorical slots." },
33
+ function (args = {}) {
34
+ validateKeys(args, OPTIONS, "packPoints");
35
+ const policy = normalizePointPackingPolicy(args);
36
+ const layer = target(this, args.target, policy.channel);
37
+ if (this.materializationConfigs.jitters?.[layer.id] !== undefined) {
38
+ throw new Error(`Point packing on "${layer.id}" conflicts with point jitter.`);
39
+ }
40
+ return this
41
+ ._withMaterializationConfig(["pointPacking", layer.id], policy)
42
+ .rematerializePointMark({ id: layer.id });
43
+ }
44
+ );
45
+
46
+ export const removePointPacking = action(
47
+ { op: "removePointPacking", description: "Remove point packing and restore semantic positions." },
48
+ function (args = {}) {
49
+ validateKeys(args, REMOVE_OPTIONS, "removePointPacking");
50
+ const requested = args.target === undefined
51
+ ? undefined
52
+ : validateUserId(args.target, "Point packing target");
53
+ const layer = resolveEligibleLayer(this, {
54
+ target: requested,
55
+ predicate: candidate => candidate.mark?.type === "point" &&
56
+ this.materializationConfigs.pointPacking?.[candidate.id] !== undefined,
57
+ label: "point packing"
58
+ });
59
+ return this
60
+ ._withoutMaterializationConfig(["pointPacking", layer.id])
61
+ .rematerializePointMark({ id: layer.id });
62
+ }
63
+ );
64
+
65
+ export function registerPointPackingActions(ProgramClass) {
66
+ ProgramClass.prototype.packPoints = packPoints;
67
+ ProgramClass.prototype.removePointPacking = removePointPacking;
68
+ }
@@ -19,12 +19,20 @@ import {
19
19
  resolveMarkId,
20
20
  validateMarkOptions
21
21
  } from "../shared.js";
22
+ import { replaceGraphicItems } from "../../primitives/editGraphics.js";
22
23
  import { resolveMarkGraphicPlacement } from
23
24
  "../../../materialization/graphicHierarchy.js";
25
+ import { rematerializeExistingLegend } from "../../encodings/shared.js";
24
26
  import { rematerializeHighlightBaseline } from "../lifecycle.js";
27
+ import {
28
+ requestedRectStyleDetails
29
+ } from "../../../grammar/roundedRect.js";
30
+ import { STROKE_STYLE_PROPERTIES } from
31
+ "../../../grammar/strokeStyle.js";
25
32
 
26
33
  const STYLE_OPTIONS = Object.freeze([
27
- "fill", "opacity", "stroke", "strokeWidth"
34
+ "fill", "opacity", "stroke", "strokeWidth", "cornerRadius",
35
+ ...STROKE_STYLE_PROPERTIES
28
36
  ]);
29
37
  const CREATE_OPTIONS = Object.freeze(["id", "data", ...STYLE_OPTIONS]);
30
38
  const EDIT_OPTIONS = Object.freeze(["target", ...STYLE_OPTIONS]);
@@ -45,6 +53,7 @@ const createRectMark = action(
45
53
  { op: "createRectMark", description: "Create a semantic rectangular cell layer." },
46
54
  function (args = {}) {
47
55
  validateMarkOptions(args, CREATE_OPTIONS, "createRectMark");
56
+ requestedRectStyleDetails(args, "createRectMark");
48
57
  const id = resolveMarkId(this, args.id, {
49
58
  defaultId: "rect",
50
59
  label: "Rect mark id",
@@ -99,7 +108,13 @@ const rematerializeRectMark = action(
99
108
  const layer = findLayer(this, id);
100
109
  const graphic = this.graphicSpec.objects[id];
101
110
  if (layer?.mark?.type !== "rect") throw new Error(`Unknown rect mark "${id}".`);
102
- if (graphic?.type !== "rect" || !Array.isArray(graphic.items)) {
111
+ const roundedCollection = graphic?.type === "collection" &&
112
+ Object.hasOwn(this.markConfigs[id] ?? {}, "cornerRadius") &&
113
+ graphic.items?.every(item => ["rect", "path"].includes(item.type));
114
+ if (
115
+ (graphic?.type !== "rect" && !roundedCollection) ||
116
+ !Array.isArray(graphic.items)
117
+ ) {
103
118
  throw new Error(`Rect mark "${id}" requires rect collection graphics.`);
104
119
  }
105
120
  if (this.markConfigs[id]?.gradientPlot?.materialized === true) {
@@ -110,13 +125,11 @@ const rematerializeRectMark = action(
110
125
  throw new Error(`Rect mark "${id}" requires an existing dataset.`);
111
126
  }
112
127
  if (!canMaterializeRect(this, layer)) {
113
- return graphic.items.length === 0
114
- ? this
115
- : this.editGraphics({ target: id, property: "items", value: [] });
128
+ return replaceGraphicItems(this, id, "rect", []);
116
129
  }
117
130
  let next = this;
118
131
  if (args.scales !== false) {
119
- const ids = new Set(["x", "y", "x2", "y2", "color"].flatMap(channel => {
132
+ const ids = new Set(["x", "y", "x2", "y2", "color", "stroke"].flatMap(channel => {
120
133
  const scale = layer.encoding?.[channel]?.scale;
121
134
  return scale === undefined ? [] : [scale];
122
135
  }));
@@ -124,11 +137,12 @@ const rematerializeRectMark = action(
124
137
  next = next.rematerializeScale({ id: scale, marks: false });
125
138
  }
126
139
  }
127
- return next.editGraphics({
128
- target: id,
129
- property: "items",
130
- value: resolveRectGraphicItems(next, layer, dataset)
131
- });
140
+ return replaceGraphicItems(
141
+ next,
142
+ id,
143
+ "rect",
144
+ resolveRectGraphicItems(next, layer, dataset)
145
+ );
132
146
  }
133
147
  );
134
148
 
@@ -136,6 +150,7 @@ const editRectMark = action(
136
150
  { op: "editRectMark", description: "Edit rectangular cell appearance." },
137
151
  function (args = {}) {
138
152
  validateMarkOptions(args, EDIT_OPTIONS, "editRectMark");
153
+ requestedRectStyleDetails(args, "editRectMark");
139
154
  if (!STYLE_OPTIONS.some(option => Object.hasOwn(args, option))) {
140
155
  throw new Error("editRectMark requires at least one editable property.");
141
156
  }
@@ -143,6 +158,11 @@ const editRectMark = action(
143
158
  if (Object.hasOwn(args, "fill") && layer.encoding?.color !== undefined) {
144
159
  throw new Error("editRectMark fill cannot be combined with a color encoding.");
145
160
  }
161
+ if (Object.hasOwn(args, "stroke") && layer.encoding?.stroke !== undefined) {
162
+ throw new Error(
163
+ "editRectMark stroke conflicts with a field encoding; use encodeStroke with value to replace it."
164
+ );
165
+ }
146
166
  const next = this._withMarkConfig(
147
167
  layer.id,
148
168
  {
@@ -155,9 +175,10 @@ const editRectMark = action(
155
175
  : this.markConfigs[layer.id]?.fillExplicit ?? false
156
176
  }
157
177
  );
158
- return canMaterializeRect(next, layer)
178
+ const materialized = canMaterializeRect(next, layer)
159
179
  ? next.rematerializeRectMark({ id: layer.id })
160
180
  : next;
181
+ return rematerializeExistingLegend(materialized);
161
182
  }
162
183
  );
163
184