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
@@ -1,4 +1,4 @@
1
- import { action } from "../../core/action.js";
1
+ import { closedAction } from "../../core/action.js";
2
2
  import { isPlainObject } from "../../core/immutable.js";
3
3
  import { validateUserId } from "../../core/identifiers.js";
4
4
  import {
@@ -9,6 +9,10 @@ import {
9
9
  } from "../../core/validation.js";
10
10
  import { normalizeRegressionParameters } from "../../grammar/regression/index.js";
11
11
  import { validateCurveInterpolation } from "../../grammar/curveCommands.js";
12
+ import {
13
+ requestedStrokeDetails,
14
+ STROKE_STYLE_PROPERTIES
15
+ } from "../../grammar/strokeStyle.js";
12
16
  import { planDerivedDataRevision } from
13
17
  "../../materialization/dataProvenance.js";
14
18
  import { findLayer } from "../../selectors/layers.js";
@@ -21,9 +25,12 @@ const OPTIONS = Object.freeze([
21
25
  "confidenceMethod", "level", "confidence", "interval", "band", "line"
22
26
  ]);
23
27
  const BAND_OPTIONS = Object.freeze([
24
- "color", "opacity", "stroke", "strokeWidth", "curve"
28
+ "color", "opacity", "stroke", "strokeWidth", "curve",
29
+ ...STROKE_STYLE_PROPERTIES
30
+ ]);
31
+ const LINE_OPTIONS = Object.freeze([
32
+ "strokeWidth", "curve", ...STROKE_STYLE_PROPERTIES
25
33
  ]);
26
- const LINE_OPTIONS = Object.freeze(["strokeWidth", "curve"]);
27
34
 
28
35
  function resolveRegressionOwner(program, requested) {
29
36
  const eligible = program.semanticSpec.layers.filter(
@@ -80,6 +87,7 @@ function validateBandPatch(value) {
80
87
  if (Object.hasOwn(patch, "curve")) {
81
88
  validateCurveInterpolation(patch.curve);
82
89
  }
90
+ requestedStrokeDetails(patch, "editRegression band");
83
91
  return patch;
84
92
  }
85
93
 
@@ -94,6 +102,7 @@ function validateLinePatch(value) {
94
102
  if (Object.hasOwn(patch, "curve")) {
95
103
  validateCurveInterpolation(patch.curve);
96
104
  }
105
+ requestedStrokeDetails(patch, "editRegression line");
97
106
  return patch;
98
107
  }
99
108
 
@@ -225,13 +234,12 @@ function updateRegressionBandSemantics(program, bandId, { x, groupBy }) {
225
234
  });
226
235
  }
227
236
 
228
- export const editRegression = action(
237
+ export const editRegression = /* @__PURE__ */ closedAction(
229
238
  {
230
239
  op: "editRegression",
231
240
  description: "Revise a regression model and its owned visible components."
232
- },
241
+ }, OPTIONS,
233
242
  function (args = {}) {
234
- validateKeys(args, OPTIONS, "editRegression");
235
243
  if (!OPTIONS.slice(1).some(key => Object.hasOwn(args, key))) {
236
244
  throw new Error("editRegression requires a statistical or component option.");
237
245
  }
@@ -325,7 +333,11 @@ export const editRegression = action(
325
333
  const bandConfig = next.markConfigs[bandId];
326
334
  next = next._withMarkConfig(bandId, {
327
335
  ...bandConfig,
328
- errorBand: { ...bandConfig.errorBand, data: dataId }
336
+ errorBand: {
337
+ ...bandConfig.errorBand,
338
+ source: dataId,
339
+ data: dataId
340
+ }
329
341
  });
330
342
  }
331
343
 
@@ -356,10 +368,7 @@ export const editRegression = action(
356
368
  next = next.rematerializeLineMark({ id: current.lineId });
357
369
  }
358
370
 
359
- if (changesStatistics) {
360
- next = next.releaseDerivedData(revision.release);
361
- }
362
- return next._withMarkConfig(owner.id, {
371
+ next = next._withMarkConfig(owner.id, {
363
372
  ...next.markConfigs[owner.id],
364
373
  regression: {
365
374
  ...current,
@@ -373,6 +382,10 @@ export const editRegression = action(
373
382
  parameters
374
383
  }
375
384
  });
385
+ if (changesStatistics) {
386
+ next = next.releaseDerivedData(revision.release);
387
+ }
388
+ return next;
376
389
  };
377
390
  return applyEdit(this);
378
391
  }
@@ -0,0 +1,7 @@
1
+ import { removeCoordinate, removeData, removeScale } from "./remove.js";
2
+
3
+ export function registerResourceActions(ProgramClass) {
4
+ ProgramClass.prototype.removeData = removeData;
5
+ ProgramClass.prototype.removeScale = removeScale;
6
+ ProgramClass.prototype.removeCoordinate = removeCoordinate;
7
+ }
@@ -0,0 +1,173 @@
1
+ import { annotateError } from "../../core/diagnostics.js";
2
+ import { action } from "../../core/action.js";
3
+ import { validateUserId } from "../../core/identifiers.js";
4
+ import { validateOptionObject } from "../../core/validation.js";
5
+ import {
6
+ canonicalResourcePath,
7
+ collectResourceReferences
8
+ } from "../../core/resourceReferences.js";
9
+ import { releaseNamedResourceOwnership } from "../../core/programState.js";
10
+ import { findCoordinate } from "../../selectors/coordinates.js";
11
+ import { findDataset } from "../../selectors/datasets.js";
12
+ import { findLayer } from "../../selectors/layers.js";
13
+ import { findSemanticScale } from "../../selectors/scales.js";
14
+
15
+ const OPTIONS = Object.freeze(["id"]);
16
+
17
+ function dataOwners(program) {
18
+ return Object.entries(program.materializationConfigs.data ?? {}).flatMap(
19
+ ([family, owners]) => Object.entries(owners ?? {}).flatMap(([owner, config]) =>
20
+ typeof config?.current === "string"
21
+ ? [{ family, owner, current: config.current }]
22
+ : []
23
+ )
24
+ );
25
+ }
26
+
27
+ function ownedGeneratedDatasetIds(program) {
28
+ const result = new Set();
29
+ for (const [id, config] of Object.entries(program.markConfigs)) {
30
+ const layer = findLayer(program, id);
31
+ for (const value of [
32
+ config.errorBar?.data,
33
+ config.errorBand?.data,
34
+ config.regression?.dataId,
35
+ config.boxPlot?.summaryId,
36
+ config.boxPlot?.outlierDataId,
37
+ config.gradientPlot?.profileId,
38
+ config.violinPlot?.materialized === true ? layer?.data : undefined,
39
+ config.ecdfPlot?.data,
40
+ config.endpointPlot?.data,
41
+ config.statisticalReference?.dataId
42
+ ]) {
43
+ if (typeof value === "string") result.add(value);
44
+ }
45
+ const dataset = findDataset(program, layer?.data);
46
+ if (dataset?.transform?.[0]?.type === "markFilter") result.add(dataset.id);
47
+ }
48
+ return Object.freeze([...result].sort());
49
+ }
50
+
51
+ function exactResource(program, kind, id) {
52
+ if (kind === "data") return findDataset(program, id);
53
+ if (kind === "scale") return findSemanticScale(program, id);
54
+ return findCoordinate(program, id);
55
+ }
56
+
57
+ function existingKind(program, id) {
58
+ if (findDataset(program, id) !== undefined ||
59
+ dataOwners(program).some(owner => owner.owner === id)) return "data";
60
+ if (findSemanticScale(program, id) !== undefined) return "scale";
61
+ if (findCoordinate(program, id) !== undefined) {
62
+ return "coordinate";
63
+ }
64
+ if (findLayer(program, id) !== undefined) return "mark";
65
+ return undefined;
66
+ }
67
+
68
+ export function resolveResourceOwnership(program, { kind, id }) {
69
+ const resource = exactResource(program, kind, id);
70
+ if (kind !== "data") {
71
+ if (resource !== undefined) return { ownership: "named", resource };
72
+ const other = existingKind(program, id);
73
+ if (other !== undefined) {
74
+ throw annotateError(new Error(`Resource "${id}" exists as ${other}, not ${kind}.`), { code: "incompatible-resource", resourceId: id });
75
+ }
76
+ throw annotateError(new Error(`Unknown ${kind} "${id}".`), { code: "missing-resource", resourceId: id });
77
+ }
78
+
79
+ const owners = dataOwners(program).filter(owner => owner.owner === id);
80
+ if (owners.length > 1) {
81
+ throw annotateError(new Error(`Data resource "${id}" has ambiguous logical ownership.`), { code: "ambiguous-resource", resourceId: id });
82
+ }
83
+ if (owners.length === 1) {
84
+ const owner = owners[0];
85
+ const current = exactResource(program, "data", owner.current);
86
+ if (current === undefined) {
87
+ throw annotateError(new Error(`Data owner "${id}" has no current dataset.`), { code: "missing-resource", resourceId: id });
88
+ }
89
+ return { ownership: "standalone", resource: current, dataOwner: owner };
90
+ }
91
+ if (resource !== undefined) {
92
+ if (ownedGeneratedDatasetIds(program).includes(id) ||
93
+ dataOwners(program).some(owner => owner.current === id)) {
94
+ throw new Error(
95
+ `Dataset "${id}" is chart-owned; use its owning resource action.`
96
+ );
97
+ }
98
+ return { ownership: "named", resource };
99
+ }
100
+ const other = existingKind(program, id);
101
+ if (other !== undefined) {
102
+ throw annotateError(new Error(`Resource "${id}" exists as ${other}, not data.`), { code: "incompatible-resource", resourceId: id });
103
+ }
104
+ throw annotateError(new Error(`Unknown data "${id}".`), { code: "missing-resource", resourceId: id });
105
+ }
106
+
107
+ function assertSupportedProgram(program, operation) {
108
+ if (program.compositionSpec !== undefined && program.compositionSpec.type !== "facet") {
109
+ throw new Error(`${operation} supports unit and facet composition programs.`);
110
+ }
111
+ }
112
+
113
+ function formatReference(value) {
114
+ return `${value.ownerKind} "${value.ownerId}" at ` +
115
+ canonicalResourcePath(value.path);
116
+ }
117
+
118
+ export function planResourceRemoval(program, { kind, id, operation }) {
119
+ assertSupportedProgram(program, operation);
120
+ const resolved = resolveResourceOwnership(program, { kind, id });
121
+ const actualId = resolved.resource.id;
122
+ const references = collectResourceReferences(program, { kind, id: actualId });
123
+ const live = references.filter(value => value.strength === "live" && !(
124
+ resolved.ownership === "standalone" &&
125
+ value.ownerKind === "dataOwner" &&
126
+ value.ownerId === resolved.dataOwner.owner &&
127
+ value.path.at(-1) === "current"
128
+ ));
129
+ if (live.length > 0) {
130
+ throw annotateError(new Error(
131
+ `Cannot remove ${kind} "${id}"; live references: ` +
132
+ live.map(formatReference).join("; ")
133
+ ), { code: "resource-in-use", resourceId: id, candidates: [...new Set(live.map(value => value.ownerId))] });
134
+ }
135
+ return Object.freeze({
136
+ kind,
137
+ id,
138
+ semanticIds: Object.freeze([actualId]),
139
+ ...(resolved.dataOwner === undefined
140
+ ? {}
141
+ : { dataOwner: Object.freeze({ ...resolved.dataOwner }) })
142
+ });
143
+ }
144
+
145
+ function removeResource(kind, operation, description) {
146
+ return action(
147
+ { op: operation, description, scope: "any" },
148
+ function (args = {}) {
149
+ validateOptionObject(args, OPTIONS, operation);
150
+ const id = validateUserId(args.id, `${operation} id`);
151
+ const plan = planResourceRemoval(this, { kind, id, operation });
152
+ // Release a validated logical owner before removing its physical revision.
153
+ // The primitive then checks the remaining consumers without mistaking the
154
+ // owner's own replay entry for an independently chart-owned resource.
155
+ let next = releaseNamedResourceOwnership(this, plan);
156
+ const selector = kind === "data" ? "dataset" : kind;
157
+ for (const semanticId of plan.semanticIds) {
158
+ next = next.editSemantic({ property: `${selector}[${semanticId}]`, remove: true });
159
+ }
160
+ return next;
161
+ }
162
+ );
163
+ }
164
+
165
+ export const removeData = /* @__PURE__ */ removeResource(
166
+ "data", "removeData", "Remove one unreferenced named dataset."
167
+ );
168
+ export const removeScale = /* @__PURE__ */ removeResource(
169
+ "scale", "removeScale", "Remove one unreferenced named scale."
170
+ );
171
+ export const removeCoordinate = /* @__PURE__ */ removeResource(
172
+ "coordinate", "removeCoordinate", "Remove one unreferenced named coordinate."
173
+ );
@@ -0,0 +1,212 @@
1
+ import { action } from "../../core/action.js";
2
+ import { validateUserId } from "../../core/identifiers.js";
3
+ import { validateOptionObject } from "../../core/validation.js";
4
+ import { normalizePositionScaleChannel } from "../../core/vocabulary.js";
5
+ import { findLayer } from "../../selectors/layers.js";
6
+ import { requireSemanticScale } from "../../selectors/scales.js";
7
+ import { findScaleConsumers } from "./consumers/index.js";
8
+
9
+ const DEFINITIONS = Object.freeze({
10
+ editXScale: Object.freeze({
11
+ channel: "x",
12
+ options: Object.freeze([
13
+ "type", "domain", "range", "nice", "zero", "clamp", "reverse",
14
+ "base", "exponent", "constant", "paddingInner", "paddingOuter",
15
+ "padding", "align", "unknown"
16
+ ])
17
+ }),
18
+ editYScale: Object.freeze({
19
+ channel: "y",
20
+ options: Object.freeze([
21
+ "type", "domain", "range", "nice", "zero", "clamp", "reverse",
22
+ "base", "exponent", "constant", "paddingInner", "paddingOuter",
23
+ "padding", "align", "unknown"
24
+ ])
25
+ }),
26
+ editThetaScale: Object.freeze({
27
+ channel: "theta",
28
+ options: Object.freeze([
29
+ "type", "domain", "range", "nice", "zero", "clamp", "reverse",
30
+ "paddingInner", "paddingOuter", "padding", "align"
31
+ ])
32
+ }),
33
+ editRScale: Object.freeze({
34
+ channel: "radius",
35
+ options: Object.freeze([
36
+ "type", "domain", "range", "nice", "zero", "clamp", "reverse",
37
+ "base", "exponent", "constant", "radialMapping"
38
+ ])
39
+ }),
40
+ editColorScale: Object.freeze({
41
+ channel: "color",
42
+ options: Object.freeze([
43
+ "type", "domain", "range", "clamp", "reverse", "palette",
44
+ "interpolate", "midpoint", "unknown"
45
+ ])
46
+ }),
47
+ editStrokeScale: Object.freeze({
48
+ channel: "stroke",
49
+ targetOnly: true,
50
+ options: Object.freeze([
51
+ "type", "domain", "range", "clamp", "reverse", "palette",
52
+ "interpolate", "midpoint", "unknown"
53
+ ])
54
+ }),
55
+ editSizeScale: Object.freeze({
56
+ channel: "size",
57
+ options: Object.freeze([
58
+ "type", "domain", "range", "unknown", "clamp", "reverse", "base",
59
+ "exponent"
60
+ ])
61
+ }),
62
+ editOpacityScale: Object.freeze({
63
+ channel: "opacity",
64
+ options: Object.freeze([
65
+ "type", "domain", "range", "nice", "zero", "clamp", "reverse", "unknown"
66
+ ])
67
+ }),
68
+ editShapeScale: Object.freeze({
69
+ channel: "shape",
70
+ options: Object.freeze(["type", "domain", "range", "unknown"])
71
+ }),
72
+ editStrokeWidthScale: Object.freeze({
73
+ channel: "strokeWidth",
74
+ options: Object.freeze([
75
+ "type", "domain", "range", "nice", "zero", "clamp", "reverse",
76
+ "base", "exponent", "constant"
77
+ ])
78
+ }),
79
+ editStrokeDashScale: Object.freeze({
80
+ channel: "strokeDash",
81
+ options: Object.freeze(["type", "domain", "range"])
82
+ })
83
+ });
84
+
85
+ function scaleIdsForLayer(program, layer, channel) {
86
+ if (layer === undefined) return [];
87
+ return [...new Set(allConsumers(program)
88
+ .filter(consumer =>
89
+ consumer.layer.id === layer.id &&
90
+ normalizePositionScaleChannel(consumer.channel) === channel
91
+ )
92
+ .map(consumer => consumer.encoding.scale))];
93
+ }
94
+
95
+ function allConsumers(program) {
96
+ return program.semanticSpec.scales.flatMap(scale =>
97
+ findScaleConsumers(program, scale.id)
98
+ );
99
+ }
100
+
101
+ function channelScaleIds(program, channel) {
102
+ return [...new Set(allConsumers(program)
103
+ .filter(consumer => normalizePositionScaleChannel(consumer.channel) === channel)
104
+ .map(consumer => consumer.encoding.scale))];
105
+ }
106
+
107
+ function requireSingleScale(ids, operation, scope) {
108
+ if (ids.length === 1) return ids[0];
109
+ if (ids.length === 0) {
110
+ throw new Error(`${operation} could not find a ${scope} scale.`);
111
+ }
112
+ throw new Error(`${operation} ${scope} scale is ambiguous; provide id or target.`);
113
+ }
114
+
115
+ function validateScaleChannel(program, id, channel, operation) {
116
+ requireSemanticScale(program, id);
117
+ const consumers = findScaleConsumers(program, id);
118
+ if (consumers.length === 0) {
119
+ throw new Error(`${operation} requires a scale bound to the ${channel} channel.`);
120
+ }
121
+ const compatible = ["color", "stroke"].includes(channel)
122
+ ? new Set(["color", "stroke"])
123
+ : new Set([channel]);
124
+ if (!consumers.some(consumer =>
125
+ normalizePositionScaleChannel(consumer.channel) === channel
126
+ ) || consumers.some(consumer =>
127
+ !compatible.has(normalizePositionScaleChannel(consumer.channel))
128
+ )) {
129
+ throw new Error(
130
+ `Scale "${id}" must include a ${channel} channel consumer and no incompatible channel consumers.`
131
+ );
132
+ }
133
+ return id;
134
+ }
135
+
136
+ function resolveScaleId(program, args, channel, operation) {
137
+ const explicitId = args.id === undefined
138
+ ? undefined
139
+ : validateUserId(args.id, "Scale id");
140
+ const target = args.target === undefined
141
+ ? undefined
142
+ : validateUserId(args.target, "Mark id");
143
+ const targetLayer = target === undefined ? undefined : findLayer(program, target);
144
+ if (target !== undefined && targetLayer === undefined) {
145
+ throw new Error(`Unknown mark target "${target}".`);
146
+ }
147
+ const targetId = targetLayer === undefined
148
+ ? undefined
149
+ : requireSingleScale(scaleIdsForLayer(program, targetLayer, channel), operation, `target ${channel}`);
150
+ if (explicitId !== undefined && targetId !== undefined && explicitId !== targetId) {
151
+ throw new Error(`${operation} id "${explicitId}" does not match target "${target}" scale "${targetId}".`);
152
+ }
153
+ if (explicitId !== undefined) {
154
+ return validateScaleChannel(program, explicitId, channel, operation);
155
+ }
156
+ if (targetId !== undefined) return validateScaleChannel(program, targetId, channel, operation);
157
+
158
+ const current = findLayer(program, program.context.currentMark);
159
+ const currentIds = scaleIdsForLayer(program, current, channel);
160
+ if (currentIds.length > 1) {
161
+ throw new Error(`${operation} current-mark ${channel} scale is ambiguous; provide id or target.`);
162
+ }
163
+ if (currentIds.length === 1) {
164
+ return validateScaleChannel(program, currentIds[0], channel, operation);
165
+ }
166
+ return validateScaleChannel(
167
+ program,
168
+ requireSingleScale(channelScaleIds(program, channel), operation, channel),
169
+ channel,
170
+ operation
171
+ );
172
+ }
173
+
174
+ function createChannelScaleEditor(operation, definition) {
175
+ return action(
176
+ {
177
+ op: operation,
178
+ description: `Edit the scale bound to the ${definition.channel} channel.`
179
+ },
180
+ function (args = {}) {
181
+ validateOptionObject(
182
+ args,
183
+ [...(definition.targetOnly === true ? [] : ["id"]), "target", ...definition.options],
184
+ operation
185
+ );
186
+ if (definition.targetOnly === true && args.target === undefined) {
187
+ throw new Error(`${operation} requires target.`);
188
+ }
189
+ if (!definition.options.some(property => Object.hasOwn(args, property))) {
190
+ throw new Error(`${operation} requires at least one editable property.`);
191
+ }
192
+ const id = resolveScaleId(this, args, definition.channel, operation);
193
+ const patch = Object.fromEntries(
194
+ Object.entries(args).filter(([property]) => property !== "target")
195
+ );
196
+ return this.editScale({ ...patch, id });
197
+ }
198
+ );
199
+ }
200
+
201
+ export const channelScaleEditors = Object.freeze(Object.fromEntries(
202
+ Object.entries(DEFINITIONS).map(([operation, definition]) => [
203
+ operation,
204
+ createChannelScaleEditor(operation, definition)
205
+ ])
206
+ ));
207
+
208
+ export function registerChannelScaleActions(ProgramClass) {
209
+ for (const [operation, editor] of Object.entries(channelScaleEditors)) {
210
+ ProgramClass.prototype[operation] = editor;
211
+ }
212
+ }
@@ -43,7 +43,7 @@ export function requireConsumerDataset(program, consumer) {
43
43
  }
44
44
 
45
45
  export function isDirectCategoricalConsumer(consumer) {
46
- return ["color", "strokeDash", "xOffset", "yOffset", "shape"].includes(
46
+ return ["color", "stroke", "strokeDash", "xOffset", "yOffset", "shape"].includes(
47
47
  consumer.channel
48
48
  ) && ["nominal", "ordinal"].includes(consumer.encoding.fieldType);
49
49
  }
@@ -45,7 +45,7 @@ export function resolveMarkFamilyConsumerValues(program, consumer, dataset) {
45
45
  }
46
46
  const { layer, channel } = consumer;
47
47
  const encoding = layer.encoding ?? {};
48
- const appearance = ["strokeWidth", "opacity"].includes(channel);
48
+ const appearance = ["stroke", "strokeWidth", "opacity"].includes(channel);
49
49
  if (
50
50
  layer.mark?.type === "line" &&
51
51
  ((["x", "y"].includes(channel) && encoding.x !== undefined &&
@@ -69,7 +69,8 @@ export function resolveMarkFamilyConsumerValues(program, consumer, dataset) {
69
69
  dataset.values,
70
70
  derived.series,
71
71
  consumer.encoding.field,
72
- channel
72
+ channel,
73
+ consumer.encoding
73
74
  )
74
75
  : channel === "x" ? derived.xValues : derived.yValues
75
76
  };
@@ -9,6 +9,8 @@ import {
9
9
  } from "./common.js";
10
10
  import { resolveMarkFamilyConsumerValues } from "./families.js";
11
11
  import { CATEGORY_ORDER_CHANNELS, resolveCategoryOrder } from "../../../grammar/categoryOrder.js";
12
+ import { resolveHistogramInput } from
13
+ "../../../materialization/bars/histogram.js";
12
14
 
13
15
  export { findScale, findScaleConsumers } from "./common.js";
14
16
  export {
@@ -16,8 +18,7 @@ export {
16
18
  resolveSeriesLayoutScaleValues
17
19
  } from "./seriesLayout.js";
18
20
 
19
- export function resolveConsumerValues(program, consumer) {
20
- const dataset = requireConsumerDataset(program, consumer);
21
+ export function resolveConsumerValuesForDataset(program, consumer, dataset) {
21
22
  if (consumer.layer.mark.type === "rect") return resolveMarkFamilyConsumerValues(program, consumer, dataset).values;
22
23
  if (Object.hasOwn(consumer.encoding, "datum")) {
23
24
  if (consumer.layer.mark.type === "area") {
@@ -49,6 +50,14 @@ export function resolveConsumerValues(program, consumer) {
49
50
  );
50
51
  }
51
52
  const scale = findScale(program, consumer.encoding.scale);
53
+ if (
54
+ consumer.layer.mark.type === "bar" &&
55
+ consumer.channel === "x" &&
56
+ consumer.encoding.bin !== undefined &&
57
+ consumer.encoding.weight !== undefined
58
+ ) {
59
+ return resolveHistogramInput(dataset, consumer.encoding).values;
60
+ }
52
61
  if (isDirectCategoricalConsumer(consumer)) {
53
62
  return readConsumerFieldValues(program, consumer, dataset, scale);
54
63
  }
@@ -58,6 +67,14 @@ export function resolveConsumerValues(program, consumer) {
58
67
  : readConsumerFieldValues(program, consumer, dataset, scale);
59
68
  }
60
69
 
70
+ export function resolveConsumerValues(program, consumer) {
71
+ return resolveConsumerValuesForDataset(
72
+ program,
73
+ consumer,
74
+ requireConsumerDataset(program, consumer)
75
+ );
76
+ }
77
+
61
78
  export function resolveConsumerCategoryOrder(program, consumer) {
62
79
  const order = consumer.encoding.categoryOrder;
63
80
  if (order === undefined) return undefined;
@@ -9,7 +9,6 @@ import {
9
9
  resolveBarGrain
10
10
  } from "../../../grammar/bars/policy.js";
11
11
  import {
12
- countHistogramBins,
13
12
  findHistogramBinIndex,
14
13
  resolveHistogramBins
15
14
  } from "../../../grammar/histogram.js";
@@ -22,11 +21,12 @@ import {
22
21
  } from "../../../grammar/seriesLayout.js";
23
22
  import {
24
23
  readNominalField,
25
- readQuantitativeField,
26
24
  resolveOrdinalDomain
27
25
  } from "../../../grammar/scales/index.js";
28
26
  import { findDataset } from "../../../selectors/datasets.js";
29
27
  import { findScale } from "./common.js";
28
+ import { resolveHistogramInput } from
29
+ "../../../materialization/bars/histogram.js";
30
30
 
31
31
  export function resolveHistogramCountValues(program, consumer) {
32
32
  const xEncoding = consumer.layer.encoding?.x;
@@ -44,7 +44,8 @@ export function resolveHistogramCountValues(program, consumer) {
44
44
  `Histogram mark "${consumer.layer.id}" requires an existing dataset.`
45
45
  );
46
46
  }
47
- const xValues = readQuantitativeField(dataset.values, xEncoding.field);
47
+ const input = resolveHistogramInput(dataset, xEncoding);
48
+ const xValues = input.values;
48
49
  const bins = resolveHistogramBins({
49
50
  values: xValues,
50
51
  bin: xEncoding.bin,
@@ -52,7 +53,12 @@ export function resolveHistogramCountValues(program, consumer) {
52
53
  nice: xScale.nice ?? true,
53
54
  zero: xScale.zero ?? false
54
55
  });
55
- return countHistogramBins(xValues, bins.boundaries);
56
+ const cells = bins.boundaries.slice(0, -1).map(() => []);
57
+ xValues.forEach((value, index) => {
58
+ const bin = findHistogramBinIndex(value, bins.boundaries);
59
+ if (bin !== -1) cells[bin].push(input.entries[index]);
60
+ });
61
+ return cells.map(input.mass);
56
62
  }
57
63
 
58
64
  function resolveHistogramPartitions(program, consumer) {
@@ -60,7 +66,8 @@ function resolveHistogramPartitions(program, consumer) {
60
66
  const dataset = findDataset(program, layer.data);
61
67
  const xEncoding = layer.encoding.x;
62
68
  const xScale = findScale(program, xEncoding.scale);
63
- const xValues = readQuantitativeField(dataset.values, xEncoding.field);
69
+ const input = resolveHistogramInput(dataset, xEncoding);
70
+ const xValues = input.values;
64
71
  const bins = resolveHistogramBins({
65
72
  values: xValues,
66
73
  bin: xEncoding.bin,
@@ -70,19 +77,24 @@ function resolveHistogramPartitions(program, consumer) {
70
77
  });
71
78
  const colorEncoding = layer.encoding?.color;
72
79
  if (layer.encoding?.group !== undefined) {
73
- const identity = readSeriesIdentity(dataset.values, layer);
80
+ const identity = readSeriesIdentity(input.rows, layer);
74
81
  validateGeneratedItemLimit((bins.boundaries.length - 1) * identity.domain.length, "Histogram layout cell count");
75
- const counts = bins.boundaries.slice(0, -1).map(() => identity.domain.map(() => 0));
82
+ const cells = bins.boundaries.slice(0, -1).map(() => identity.domain.map(() => []));
76
83
  const groups = new Map(identity.domain.map((value, index) => [value, index]));
77
84
  xValues.forEach((value, index) => { const bin = findHistogramBinIndex(value, bins.boundaries);
78
- if (bin !== -1) counts[bin][groups.get(identity.values[index])] += 1; });
79
- return counts;
85
+ if (bin !== -1) cells[bin][groups.get(identity.values[index])].push(input.entries[index]); });
86
+ return cells.map(partition => partition.map(input.mass));
80
87
  }
81
88
  if (colorEncoding?.scale === undefined) {
82
- return countHistogramBins(xValues, bins.boundaries).map(value => [value]);
89
+ const cells = bins.boundaries.slice(0, -1).map(() => []);
90
+ xValues.forEach((value, index) => {
91
+ const bin = findHistogramBinIndex(value, bins.boundaries);
92
+ if (bin !== -1) cells[bin].push(input.entries[index]);
93
+ });
94
+ return cells.map(entries => [input.mass(entries)]);
83
95
  }
84
96
  const colorScale = findScale(program, colorEncoding.scale);
85
- const colorValues = readNominalField(dataset.values, colorEncoding.field);
97
+ const colorValues = readNominalField(input.rows, colorEncoding.field);
86
98
  const colorDomain = resolveOrdinalDomain(colorScale.domain, colorValues);
87
99
  validateGeneratedItemLimit(
88
100
  (bins.boundaries.length - 1) * colorDomain.length,
@@ -90,14 +102,16 @@ function resolveHistogramPartitions(program, consumer) {
90
102
  );
91
103
  const colorIndex = new Map(colorDomain.map((value, index) => [value, index]));
92
104
  const partitions = bins.boundaries.slice(0, -1).map(() =>
93
- colorDomain.map(() => 0)
105
+ colorDomain.map(() => [])
94
106
  );
95
107
  for (let index = 0; index < xValues.length; index += 1) {
96
108
  const bin = findHistogramBinIndex(xValues[index], bins.boundaries);
97
109
  const color = colorIndex.get(colorValues[index]);
98
- if (bin !== -1 && color !== undefined) partitions[bin][color] += 1;
110
+ if (bin !== -1 && color !== undefined) {
111
+ partitions[bin][color].push(input.entries[index]);
112
+ }
99
113
  }
100
- return partitions;
114
+ return partitions.map(partition => partition.map(input.mass));
101
115
  }
102
116
 
103
117
  function resolveAggregatePartitions(program, consumer) {