ggaction 0.0.2 → 0.0.4

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 (242) hide show
  1. package/CHANGELOG.md +55 -2
  2. package/README.md +95 -72
  3. package/package.json +15 -5
  4. package/src/actions/boxPlots/components.js +3 -3
  5. package/src/actions/boxPlots/edit.js +277 -0
  6. package/src/actions/boxPlots/index.js +2 -0
  7. package/src/actions/boxPlots/materialize.js +12 -1
  8. package/src/actions/boxPlots/options.js +25 -14
  9. package/src/actions/canvas/actions.js +21 -17
  10. package/src/actions/composition/actions.js +247 -0
  11. package/src/actions/composition/index.js +1 -0
  12. package/src/actions/coordinates/actions.js +2 -5
  13. package/src/actions/data/density.js +7 -1
  14. package/src/actions/data/filter.js +3 -1
  15. package/src/actions/data/regression.js +1 -1
  16. package/src/actions/encodings/appearance.js +12 -1
  17. package/src/actions/encodings/barWidth.js +5 -3
  18. package/src/actions/encodings/color/continuous.js +137 -0
  19. package/src/actions/encodings/color/index.js +151 -0
  20. package/src/actions/encodings/color/layout.js +91 -0
  21. package/src/actions/encodings/color/policy.js +88 -0
  22. package/src/actions/encodings/index.js +4 -2
  23. package/src/actions/encodings/offset.js +91 -73
  24. package/src/actions/encodings/position/apply.js +66 -0
  25. package/src/actions/encodings/position/index.js +130 -0
  26. package/src/actions/encodings/position/policies/arc.js +26 -0
  27. package/src/actions/encodings/position/policies/bar.js +9 -5
  28. package/src/actions/encodings/position/policies/index.js +5 -1
  29. package/src/actions/encodings/position/policies/line.js +20 -3
  30. package/src/actions/encodings/position/resolve.js +58 -9
  31. package/src/actions/encodings/ranged.js +30 -8
  32. package/src/actions/encodings/shared.js +17 -21
  33. package/src/actions/encodings/strokeDash.js +1 -1
  34. package/src/actions/encodings/text.js +73 -0
  35. package/src/actions/errorBands/create.js +37 -29
  36. package/src/actions/errorBands/edit.js +206 -0
  37. package/src/actions/errorBands/index.js +9 -0
  38. package/src/actions/errorBands/options.js +39 -0
  39. package/src/actions/errorBars/create.js +50 -43
  40. package/src/actions/errorBars/edit.js +130 -0
  41. package/src/actions/errorBars/index.js +3 -0
  42. package/src/actions/errorBars/options.js +52 -0
  43. package/src/actions/facets/actions.js +162 -0
  44. package/src/actions/facets/derive.js +200 -0
  45. package/src/actions/facets/guides.js +16 -0
  46. package/src/actions/facets/index.js +1 -0
  47. package/src/actions/facets/replay.js +69 -0
  48. package/src/actions/guides/applicability.js +113 -0
  49. package/src/actions/guides/axes/axes.js +70 -16
  50. package/src/actions/guides/axes/edit.js +126 -0
  51. package/src/actions/guides/axes/index.js +12 -0
  52. package/src/actions/guides/axes/labels.js +22 -19
  53. package/src/actions/guides/axes/remove.js +60 -0
  54. package/src/actions/guides/axes/tickGroups.js +7 -5
  55. package/src/actions/guides/axes/ticks.js +10 -5
  56. package/src/actions/guides/axes/titles.js +8 -6
  57. package/src/actions/guides/grids/grid.js +144 -15
  58. package/src/actions/guides/grids/resolve.js +6 -14
  59. package/src/actions/guides/guides.js +17 -75
  60. package/src/actions/guides/index.js +6 -0
  61. package/src/actions/guides/legends/categorical/actions.js +48 -9
  62. package/src/actions/guides/legends/categorical/components.js +16 -16
  63. package/src/actions/guides/legends/categorical/index.js +14 -14
  64. package/src/actions/guides/legends/categorical/layout.js +7 -3
  65. package/src/actions/guides/legends/categorical/options.js +2 -5
  66. package/src/actions/guides/legends/categorical/resolve.js +2 -2
  67. package/src/actions/guides/legends/categorical/symbols.js +14 -14
  68. package/src/actions/guides/legends/continuous/common.js +7 -7
  69. package/src/actions/guides/legends/continuous/interval.js +6 -6
  70. package/src/actions/guides/legends/continuous/opacity.js +1 -1
  71. package/src/actions/guides/legends/edit.js +4 -26
  72. package/src/actions/guides/legends/focused.js +63 -0
  73. package/src/actions/guides/legends/index.js +4 -0
  74. package/src/actions/guides/legends/remove.js +46 -0
  75. package/src/actions/guides/legends/size.js +13 -15
  76. package/src/actions/guides/legends/target.js +25 -0
  77. package/src/actions/guides/polar/axes/facade.js +219 -0
  78. package/src/actions/guides/polar/axes/index.js +55 -0
  79. package/src/actions/guides/polar/axes/labels.js +188 -0
  80. package/src/actions/guides/polar/axes/lines.js +113 -0
  81. package/src/actions/guides/polar/axes/shared.js +112 -0
  82. package/src/actions/guides/polar/axes/ticks.js +164 -0
  83. package/src/actions/guides/polar/axes/titles.js +165 -0
  84. package/src/actions/guides/polar/grids.js +252 -0
  85. package/src/actions/guides/polar/index.js +2 -0
  86. package/src/actions/guides/polar/resolve.js +269 -0
  87. package/src/actions/guides/tickValues.js +1 -1
  88. package/src/actions/index.js +4 -0
  89. package/src/actions/intervals/resolve.js +1 -1
  90. package/src/actions/marks/arc/actions.js +239 -0
  91. package/src/actions/marks/arc/index.js +1 -0
  92. package/src/actions/marks/{area.js → area/actions.js} +51 -38
  93. package/src/actions/marks/area/index.js +4 -0
  94. package/src/actions/marks/bar/create.js +26 -5
  95. package/src/actions/marks/bar/edit.js +1 -1
  96. package/src/actions/marks/bar/materialize.js +25 -31
  97. package/src/actions/marks/index.js +12 -4
  98. package/src/actions/marks/lifecycle.js +27 -0
  99. package/src/actions/marks/line/actions.js +373 -0
  100. package/src/actions/marks/line/index.js +1 -0
  101. package/src/actions/marks/{point.js → point/actions.js} +67 -108
  102. package/src/actions/marks/point/index.js +1 -0
  103. package/src/actions/marks/rect/actions.js +165 -0
  104. package/src/actions/marks/rect/index.js +1 -0
  105. package/src/actions/marks/remove.js +214 -0
  106. package/src/actions/marks/{rule.js → rule/actions.js} +34 -28
  107. package/src/actions/marks/rule/index.js +1 -0
  108. package/src/actions/marks/shared.js +156 -25
  109. package/src/actions/marks/text/actions.js +192 -0
  110. package/src/actions/marks/text/index.js +1 -0
  111. package/src/actions/primitives/createGraphics.js +13 -6
  112. package/src/actions/primitives/editGraphics.js +10 -3
  113. package/src/actions/primitives/semantic.js +26 -3
  114. package/src/actions/primitives/semanticValue.js +26 -4
  115. package/src/actions/regression/components.js +2 -2
  116. package/src/actions/regression/create.js +19 -2
  117. package/src/actions/regression/edit.js +247 -0
  118. package/src/actions/regression/index.js +3 -0
  119. package/src/actions/regression/resolve.js +3 -1
  120. package/src/actions/scales/consumers/common.js +91 -0
  121. package/src/actions/scales/consumers/families.js +47 -0
  122. package/src/actions/scales/consumers/index.js +41 -0
  123. package/src/actions/scales/{consumers.js → consumers/seriesLayout.js} +14 -127
  124. package/src/actions/scales/create.js +1 -1
  125. package/src/actions/scales/definitions.js +15 -6
  126. package/src/actions/scales/edit.js +45 -22
  127. package/src/actions/scales/materialize.js +58 -409
  128. package/src/actions/selection/actions.js +38 -24
  129. package/src/actions/selection/index.js +2 -0
  130. package/src/actions/titles/actions.js +63 -2
  131. package/src/actions/titles/index.js +2 -0
  132. package/src/actions/titles/resolve.js +12 -17
  133. package/src/composition.js +10 -0
  134. package/src/core/ChartProgram.js +49 -65
  135. package/src/core/action.js +10 -0
  136. package/src/core/compositionState.js +169 -0
  137. package/src/core/materializationState.js +65 -0
  138. package/src/core/programState.js +28 -0
  139. package/src/core/validation.js +16 -0
  140. package/src/core/vocabulary.js +154 -4
  141. package/src/grammar/arcs.js +154 -0
  142. package/src/grammar/areaSeries.js +1 -1
  143. package/src/grammar/bars/aggregate.js +3 -3
  144. package/src/grammar/bars/geometry.js +3 -3
  145. package/src/grammar/bars/policy.js +7 -1
  146. package/src/grammar/coordinates.js +3 -7
  147. package/src/grammar/density.js +21 -2
  148. package/src/grammar/facets/dependencies.js +185 -0
  149. package/src/grammar/facets/guides.js +257 -0
  150. package/src/grammar/facets/index.js +122 -0
  151. package/src/grammar/facets/scales.js +254 -0
  152. package/src/grammar/interval.js +2 -96
  153. package/src/grammar/lineSeries.js +112 -4
  154. package/src/grammar/palettes.js +5 -7
  155. package/src/grammar/polar.js +102 -0
  156. package/src/grammar/polarGuides.js +243 -0
  157. package/src/grammar/polarLineCommands.js +43 -0
  158. package/src/grammar/polarPaths.js +161 -0
  159. package/src/grammar/positionCompatibility.js +18 -2
  160. package/src/grammar/rects.js +26 -0
  161. package/src/grammar/regression/derive.js +97 -0
  162. package/src/grammar/regression/index.js +22 -0
  163. package/src/grammar/regression/models.js +282 -0
  164. package/src/grammar/regression/parameters.js +106 -0
  165. package/src/grammar/rules.js +1 -1
  166. package/src/grammar/scales/color.js +4 -2
  167. package/src/grammar/scales/continuous.js +6 -1
  168. package/src/grammar/scales/fields.js +7 -0
  169. package/src/grammar/scales/ordinal.js +5 -2
  170. package/src/grammar/scales/policies.js +1 -1
  171. package/src/grammar/schemas/graphic.js +1 -1
  172. package/src/grammar/schemas/graphicBounds.js +64 -9
  173. package/src/grammar/schemas/graphicTree.js +44 -10
  174. package/src/grammar/schemas/semanticPath.js +31 -9
  175. package/src/grammar/seriesLayout.js +13 -2
  176. package/src/grammar/statistics/studentT.js +98 -0
  177. package/src/grammar/text.js +71 -0
  178. package/src/grammar/ticks.js +50 -13
  179. package/src/grammar/transforms.js +1 -1
  180. package/src/index.js +1 -0
  181. package/src/layout/composition.js +310 -0
  182. package/src/layout/facets.js +131 -0
  183. package/src/layout/title.js +6 -0
  184. package/src/materialization/bars/aggregate.js +6 -3
  185. package/src/materialization/bars/grouped.js +75 -37
  186. package/src/materialization/bars/histogram.js +1 -1
  187. package/src/materialization/bars/ranged.js +1 -1
  188. package/src/materialization/composition.js +152 -0
  189. package/src/materialization/compositionSnapshot.js +114 -0
  190. package/src/materialization/density.js +1 -1
  191. package/src/materialization/dependencies.js +47 -73
  192. package/src/materialization/encodings.js +14 -28
  193. package/src/materialization/facetGuides/index.js +2 -0
  194. package/src/materialization/facetGuides/legacyCategorical.js +125 -0
  195. package/src/materialization/facetGuides/placement.js +126 -0
  196. package/src/materialization/facetGuides/preparation.js +188 -0
  197. package/src/materialization/facets.js +214 -0
  198. package/src/materialization/graphicHierarchy.js +15 -5
  199. package/src/materialization/guides/resources.js +46 -0
  200. package/src/materialization/marks/capabilities.js +141 -0
  201. package/src/materialization/marks/index.js +105 -0
  202. package/src/materialization/marks/policies.js +87 -0
  203. package/src/materialization/planner.js +105 -12
  204. package/src/materialization/rect.js +138 -0
  205. package/src/materialization/rectConfig.js +38 -0
  206. package/src/materialization/rowEncoding.js +33 -0
  207. package/src/materialization/scaleGuideDependencies.js +94 -0
  208. package/src/materialization/scales/map.js +40 -0
  209. package/src/materialization/scales/policies/arc.js +37 -0
  210. package/src/materialization/scales/policies/bar.js +132 -0
  211. package/src/materialization/scales/policies/series.js +76 -0
  212. package/src/materialization/scales/resolve.js +326 -0
  213. package/src/materialization/selection/items/arc.js +29 -0
  214. package/src/materialization/selection/items/bar.js +255 -0
  215. package/src/materialization/selection/items/common.js +131 -0
  216. package/src/materialization/selection/items/index.js +6 -0
  217. package/src/materialization/selection/items/path.js +59 -0
  218. package/src/materialization/selection/items/point.js +55 -0
  219. package/src/materialization/selection/items/rect.js +23 -0
  220. package/src/materialization/selection/items/rule.js +25 -0
  221. package/src/materialization/selection/policies/arc.js +10 -0
  222. package/src/materialization/selection/policies/area.js +1 -1
  223. package/src/materialization/selection/policies/bar.js +1 -1
  224. package/src/materialization/selection/policies/index.js +5 -1
  225. package/src/materialization/selection/policies/line.js +1 -1
  226. package/src/materialization/selection/policies/point.js +1 -1
  227. package/src/materialization/selection/policies/rect.js +10 -0
  228. package/src/materialization/selection/policies/rule.js +1 -1
  229. package/src/materialization/selection/styles.js +25 -17
  230. package/src/materialization/text.js +156 -0
  231. package/src/renderers/canvas/index.js +68 -22
  232. package/src/renderers/canvas/text.js +14 -1
  233. package/types/extension.d.ts +5 -1
  234. package/types/index.d.ts +56 -1
  235. package/types/program.d.ts +664 -27
  236. package/src/actions/encodings/color.js +0 -329
  237. package/src/actions/encodings/position.js +0 -176
  238. package/src/actions/marks/line.js +0 -248
  239. package/src/grammar/regression.js +0 -578
  240. package/src/grammar/scales.js +0 -1
  241. package/src/materialization/marks.js +0 -110
  242. package/src/materialization/selection/items.js +0 -503
@@ -0,0 +1,326 @@
1
+ import {
2
+ isContinuousColorScaleType,
3
+ isDiscretePositionScaleType,
4
+ isDiscretizedColorScaleType,
5
+ isOrdinalScaleType,
6
+ isTransformedScaleType,
7
+ normalizeTransformParameters,
8
+ resolveColorRange,
9
+ resolveContinuousDomain,
10
+ resolveDiscretePositionScale,
11
+ resolveDiscretizedColorScale,
12
+ resolveOpacityRange,
13
+ resolveOrdinalDomain,
14
+ resolveOrdinalOffsetScale,
15
+ resolveOrdinalPositionScale,
16
+ resolveScaleRange,
17
+ resolveSequentialColorStops,
18
+ resolveShapeRange,
19
+ resolveSizeRange,
20
+ resolveStrokeDashRange,
21
+ resolveTransformedDomain,
22
+ SCALE_ROLES,
23
+ validateLinearScaleType,
24
+ validateOrdinalScaleType,
25
+ validateScaleTypeForRole,
26
+ validateTimeScaleType
27
+ } from "../../grammar/scales/index.js";
28
+ import { resolveArcAutoPositionRange } from "./policies/arc.js";
29
+ import {
30
+ resolveBinnedBarDomain,
31
+ resolveOffsetScalePolicy,
32
+ resolveTemporalBarBand
33
+ } from "./policies/bar.js";
34
+ import { resolveSeriesLayoutDomain } from "./policies/series.js";
35
+ import { OFFSET_POSITION_CHANNELS } from "../../core/vocabulary.js";
36
+
37
+ function resolveDefaultDomain({
38
+ scale,
39
+ allValues,
40
+ isOrdinalAppearance,
41
+ isOrdinalOffset,
42
+ isOrdinalPosition,
43
+ isDiscretePosition
44
+ }) {
45
+ if (
46
+ isOrdinalAppearance ||
47
+ isOrdinalOffset ||
48
+ isOrdinalPosition ||
49
+ isDiscretePosition
50
+ ) {
51
+ return resolveOrdinalDomain(scale.domain, allValues);
52
+ }
53
+ if (isTransformedScaleType(scale.type)) {
54
+ return resolveTransformedDomain({
55
+ type: scale.type,
56
+ domain: scale.domain,
57
+ values: allValues,
58
+ nice: scale.nice ?? false,
59
+ zero: scale.zero ?? false,
60
+ ...(scale.base === undefined ? {} : { base: scale.base }),
61
+ ...(scale.exponent === undefined ? {} : { exponent: scale.exponent }),
62
+ ...(scale.constant === undefined ? {} : { constant: scale.constant })
63
+ });
64
+ }
65
+ return resolveContinuousDomain({
66
+ domain: scale.domain,
67
+ values: allValues,
68
+ type: scale.type,
69
+ nice: scale.nice,
70
+ zero: scale.zero
71
+ });
72
+ }
73
+
74
+ function resolveRange({
75
+ scale,
76
+ channel,
77
+ domain,
78
+ bounds,
79
+ consumers,
80
+ markConfigs,
81
+ isSequentialColor,
82
+ isDiscretizedColor,
83
+ isOrdinalOffset,
84
+ discretizedScale
85
+ }) {
86
+ if (channel === "color") {
87
+ if (isDiscretizedColor) return discretizedScale.range;
88
+ if (isSequentialColor) return resolveSequentialColorStops(scale.range);
89
+ return resolveColorRange(scale.range, domain.length);
90
+ }
91
+ if (channel === "strokeDash") return resolveStrokeDashRange(scale.range);
92
+ if (channel === "shape") return resolveShapeRange(scale.range);
93
+ if (channel === "size") return resolveSizeRange(scale.range);
94
+ if (channel === "opacity") return resolveOpacityRange(scale.range);
95
+ if (isOrdinalOffset) return undefined;
96
+ return resolveArcAutoPositionRange({
97
+ consumers,
98
+ scale,
99
+ channel,
100
+ domain,
101
+ range: resolveScaleRange(scale.range, channel, bounds),
102
+ markConfigs
103
+ });
104
+ }
105
+
106
+ function resolveContinuousScale({
107
+ scale,
108
+ domain,
109
+ range,
110
+ isSequentialColor,
111
+ isOrdinalAppearance
112
+ }) {
113
+ return {
114
+ type: isSequentialColor
115
+ ? "sequential"
116
+ : isOrdinalAppearance
117
+ ? validateOrdinalScaleType(scale.type)
118
+ : scale.type === "time"
119
+ ? validateTimeScaleType(scale.type)
120
+ : isTransformedScaleType(scale.type)
121
+ ? validateScaleTypeForRole(
122
+ scale.type,
123
+ SCALE_ROLES.quantitativePosition
124
+ )
125
+ : validateLinearScaleType(scale.type),
126
+ domain,
127
+ range,
128
+ ...(scale.clamp === undefined ? {} : { clamp: scale.clamp }),
129
+ ...(scale.type === "log"
130
+ ? {
131
+ base: normalizeTransformParameters("log", {
132
+ ...(scale.base === undefined ? {} : { base: scale.base })
133
+ }).base
134
+ }
135
+ : scale.type === "pow"
136
+ ? {
137
+ exponent: normalizeTransformParameters("pow", {
138
+ ...(scale.exponent === undefined
139
+ ? {}
140
+ : { exponent: scale.exponent })
141
+ }).exponent
142
+ }
143
+ : scale.type === "symlog"
144
+ ? {
145
+ constant: normalizeTransformParameters("symlog", {
146
+ ...(scale.constant === undefined
147
+ ? {}
148
+ : { constant: scale.constant })
149
+ }).constant
150
+ }
151
+ : {}),
152
+ ...(isSequentialColor ? { interpolate: scale.interpolate ?? "rgb" } : {}),
153
+ ...(Object.hasOwn(scale, "unknown") ? { unknown: scale.unknown } : {})
154
+ };
155
+ }
156
+
157
+ function reverseResolvedScale(scale) {
158
+ const range = [...scale.range].reverse();
159
+ const direction = Math.sign(range[1] - range[0]) || 1;
160
+ const offset = scale.start === undefined
161
+ ? undefined
162
+ : Math.abs(scale.start - scale.range[0]);
163
+ return {
164
+ ...scale,
165
+ range,
166
+ ...(scale.step === undefined ? {} : { step: -scale.step }),
167
+ ...(offset === undefined ? {} : { start: range[0] + direction * offset })
168
+ };
169
+ }
170
+
171
+ export function resolveScaleMaterialization({
172
+ id,
173
+ scale,
174
+ channel,
175
+ consumers,
176
+ valuesByConsumer,
177
+ bounds,
178
+ resolvedScales,
179
+ markConfigs
180
+ }) {
181
+ const allValues = valuesByConsumer
182
+ .flatMap(item => item.values)
183
+ .filter(value => value !== undefined);
184
+ const isSequentialColor = channel === "color" &&
185
+ isContinuousColorScaleType(scale.type);
186
+ const isDiscretizedColor = channel === "color" &&
187
+ isDiscretizedColorScaleType(scale.type);
188
+ const isOrdinalAppearance =
189
+ ["color", "strokeDash", "shape"].includes(channel) &&
190
+ isOrdinalScaleType(scale.type);
191
+ const isOrdinalOffset = OFFSET_POSITION_CHANNELS.includes(channel);
192
+ const isOrdinalPosition =
193
+ !isOrdinalAppearance && !isOrdinalOffset && isOrdinalScaleType(scale.type);
194
+ const isDiscretePosition =
195
+ !isOrdinalAppearance &&
196
+ !isOrdinalOffset &&
197
+ isDiscretePositionScaleType(scale.type);
198
+
199
+ let discretizedScale;
200
+ if (isDiscretizedColor) {
201
+ discretizedScale = resolveDiscretizedColorScale({
202
+ type: scale.type,
203
+ domain: scale.domain,
204
+ range: scale.range,
205
+ values: allValues
206
+ });
207
+ }
208
+ const binnedDomain = isDiscretizedColor
209
+ ? undefined
210
+ : resolveBinnedBarDomain({
211
+ valuesByConsumer,
212
+ channel,
213
+ scale,
214
+ id,
215
+ allValues
216
+ });
217
+ const seriesLayouts = valuesByConsumer.map(item => item.seriesLayout);
218
+ const seriesDomain = isDiscretizedColor || binnedDomain !== undefined
219
+ ? undefined
220
+ : resolveSeriesLayoutDomain({
221
+ id,
222
+ scale,
223
+ valuesByConsumer,
224
+ seriesLayouts
225
+ });
226
+ const domain = isDiscretizedColor
227
+ ? discretizedScale.domain
228
+ : binnedDomain ?? seriesDomain ?? resolveDefaultDomain({
229
+ scale,
230
+ allValues,
231
+ isOrdinalAppearance,
232
+ isOrdinalOffset,
233
+ isOrdinalPosition,
234
+ isDiscretePosition
235
+ });
236
+ const range = resolveRange({
237
+ scale,
238
+ channel,
239
+ domain,
240
+ bounds,
241
+ consumers,
242
+ markConfigs,
243
+ isSequentialColor,
244
+ isDiscretizedColor,
245
+ isOrdinalOffset,
246
+ discretizedScale
247
+ });
248
+ if (channel === "shape" && domain.length > range.length) {
249
+ throw new Error(
250
+ `Shape scale "${id}" requires at least one distinct shape per domain value.`
251
+ );
252
+ }
253
+
254
+ let resolvedScale = isDiscretizedColor
255
+ ? discretizedScale
256
+ : isOrdinalOffset
257
+ ? resolveOrdinalOffsetScale({
258
+ domain: scale.domain,
259
+ values: allValues,
260
+ range: scale.range,
261
+ ...resolveOffsetScalePolicy({
262
+ consumers,
263
+ resolvedScales,
264
+ markConfigs,
265
+ id,
266
+ channel
267
+ }),
268
+ channel
269
+ })
270
+ : isDiscretePosition
271
+ ? resolveDiscretePositionScale({
272
+ type: scale.type,
273
+ domain: scale.domain,
274
+ values: allValues,
275
+ range: channel === "theta" && consumers.every(
276
+ consumer => consumer.layer.mark?.type === "arc"
277
+ ) ? range : scale.range,
278
+ channel,
279
+ bounds,
280
+ ...(scale.type === "band"
281
+ ? {
282
+ paddingInner: scale.paddingInner,
283
+ paddingOuter: scale.paddingOuter
284
+ }
285
+ : { padding: scale.padding }),
286
+ align: scale.align,
287
+ unknown: scale.unknown
288
+ })
289
+ : isOrdinalPosition
290
+ ? resolveOrdinalPositionScale({
291
+ domain: scale.domain,
292
+ values: allValues,
293
+ range: scale.range,
294
+ channel,
295
+ bounds,
296
+ unknown: scale.unknown
297
+ })
298
+ : resolveContinuousScale({
299
+ scale,
300
+ domain,
301
+ range,
302
+ isSequentialColor,
303
+ isOrdinalAppearance
304
+ });
305
+ if (Object.hasOwn(scale, "unknown") && !Object.hasOwn(resolvedScale, "unknown")) {
306
+ resolvedScale = { ...resolvedScale, unknown: scale.unknown };
307
+ }
308
+ if (scale.type === "time" && ["x", "y"].includes(channel)) {
309
+ const temporalBarBand = resolveTemporalBarBand(
310
+ consumers,
311
+ domain,
312
+ range,
313
+ allValues
314
+ );
315
+ if (temporalBarBand !== undefined) {
316
+ resolvedScale = {
317
+ ...resolvedScale,
318
+ range: temporalBarBand.range,
319
+ bandwidth: temporalBarBand.bandwidth
320
+ };
321
+ }
322
+ }
323
+ return scale.reverse === true
324
+ ? reverseResolvedScale(resolvedScale)
325
+ : resolvedScale;
326
+ }
@@ -0,0 +1,29 @@
1
+ import { deriveArcSectors } from "../../../grammar/arcs.js";
2
+ import { resolvePolarFrame } from "../../../grammar/polar.js";
3
+ import { resolveGraphicBounds } from "../../../layout/canvas.js";
4
+ import { finalizeItems, uniqueFields } from "./common.js";
5
+
6
+ export function resolveArcItems(program, layer, dataset) {
7
+ const thetaScale = program.resolvedScales[layer.encoding?.theta?.scale];
8
+ const radiusScale = program.resolvedScales[layer.encoding?.radius?.scale];
9
+ const frame = resolvePolarFrame(resolveGraphicBounds(program));
10
+ const derived = deriveArcSectors(dataset.values, layer, {
11
+ thetaScale,
12
+ ...(radiusScale === undefined ? {} : { radiusScale }),
13
+ frame,
14
+ innerRadiusRatio: program.markConfigs[layer.id]?.innerRadius ?? 0
15
+ });
16
+ const definitions = derived.sectors.map(sector => {
17
+ const members = sector.sourceIndices.map(index => dataset.values[index]);
18
+ return {
19
+ fields: uniqueFields(members),
20
+ channels: {
21
+ theta: sector.theta,
22
+ ...(sector.radius === undefined ? {} : { radius: sector.radius }),
23
+ ...(sector.color === undefined ? {} : { color: sector.color })
24
+ },
25
+ members
26
+ };
27
+ });
28
+ return finalizeItems(program, layer, "sector", definitions, "path");
29
+ }
@@ -0,0 +1,255 @@
1
+ import { deriveBarAggregates } from "../../../grammar/bars/aggregate.js";
2
+ import {
3
+ BAR_GRAINS,
4
+ resolveBarChannels,
5
+ resolveBarColorLayout,
6
+ resolveBarOffsetChannel,
7
+ resolveBarGrain
8
+ } from "../../../grammar/bars/policy.js";
9
+ import { layoutSeriesPartition } from "../../../grammar/seriesLayout.js";
10
+ import {
11
+ readNominalField,
12
+ readTemporalField
13
+ } from "../../../grammar/scales/index.js";
14
+ import { findSemanticScale } from "../../../selectors/scales.js";
15
+ import { deriveHistogramSegments } from "../../bars/histogram.js";
16
+ import {
17
+ channelMapFromRow,
18
+ finalizeItems,
19
+ itemKey,
20
+ ownFields,
21
+ uniqueFields
22
+ } from "./common.js";
23
+
24
+ function categoryValues(rows, encoding) {
25
+ return encoding.fieldType === "temporal"
26
+ ? readTemporalField(rows, encoding.field)
27
+ : readNominalField(rows, encoding.field);
28
+ }
29
+
30
+ function aggregateMembers(rows, layer, cell, channels) {
31
+ const category = layer.encoding[channels.category];
32
+ const categories = categoryValues(rows, category);
33
+ const categoryValue = cell[channels.category];
34
+ const color = layer.encoding?.color;
35
+ const colors = !["nominal", "ordinal"].includes(color?.fieldType)
36
+ ? undefined
37
+ : readNominalField(rows, color.field);
38
+ return rows.filter((_, index) =>
39
+ categories[index] === categoryValue &&
40
+ (colors === undefined || colors[index] === cell.color)
41
+ );
42
+ }
43
+
44
+ function aggregateCellDefinitions(program, layer, dataset) {
45
+ const channels = resolveBarChannels(layer);
46
+ const derived = deriveBarAggregates(dataset.values, layer).values;
47
+ const categoryEncoding = layer.encoding[channels.category];
48
+ const categoryScale = program.resolvedScales[categoryEncoding.scale];
49
+ const measureScale = program.resolvedScales[
50
+ layer.encoding[channels.measure].scale
51
+ ];
52
+ const colorEncoding = layer.encoding?.color;
53
+ const colorScale = program.resolvedScales[colorEncoding?.scale];
54
+ const offsetChannel = resolveBarOffsetChannel(layer);
55
+ const offsetScale = program.resolvedScales[layer.encoding?.[offsetChannel]?.scale];
56
+ const layout = resolveBarColorLayout(layer);
57
+
58
+ function definition(cell, start, end) {
59
+ const members = aggregateMembers(dataset.values, layer, cell, channels);
60
+ return {
61
+ fields: uniqueFields(members),
62
+ channels: {
63
+ [channels.category]: cell[channels.category],
64
+ [channels.measure]: start,
65
+ [`${channels.measure}2`]: end,
66
+ ...(cell.color === undefined ? {} : { color: cell.color }),
67
+ ...(layer.encoding?.[offsetChannel] === undefined
68
+ ? {}
69
+ : { [offsetChannel]: cell.color })
70
+ },
71
+ members
72
+ };
73
+ }
74
+
75
+ if (colorEncoding?.fieldType === "quantitative") {
76
+ const cells = new Map(derived.map(cell => [
77
+ cell[channels.category],
78
+ cell
79
+ ]));
80
+ const categories = ["ordinal", "band", "point"].includes(categoryScale.type)
81
+ ? categoryScale.domain
82
+ : [...new Set(derived.map(cell => cell[channels.category]))]
83
+ .sort((left, right) => left - right);
84
+ return categories.flatMap(category => {
85
+ const cell = cells.get(category);
86
+ return cell === undefined
87
+ ? []
88
+ : [definition(cell, measureScale.domain[0], cell[channels.measure])];
89
+ });
90
+ }
91
+
92
+ if (layout === "group" && offsetScale !== undefined) {
93
+ const categoryIndex = new Map(
94
+ categoryScale.domain.map((value, index) => [value, index])
95
+ );
96
+ const offsetIndex = new Map(
97
+ offsetScale.domain.map((value, index) => [value, index])
98
+ );
99
+ const cells = [...derived].sort((left, right) =>
100
+ categoryIndex.get(left[channels.category]) -
101
+ categoryIndex.get(right[channels.category]) ||
102
+ offsetIndex.get(left.color) - offsetIndex.get(right.color)
103
+ );
104
+ return cells.map(cell => definition(
105
+ cell,
106
+ measureScale.domain[0],
107
+ cell[channels.measure]
108
+ ));
109
+ }
110
+
111
+ const categories = ["ordinal", "band", "point"].includes(categoryScale.type)
112
+ ? categoryScale.domain
113
+ : [...new Set(derived.map(cell => cell[channels.category]))]
114
+ .sort((left, right) => left - right);
115
+ const series = colorScale?.domain ?? [undefined];
116
+ const lookup = new Map(derived.map(cell => [
117
+ JSON.stringify([cell[channels.category], cell.color]),
118
+ cell
119
+ ]));
120
+ const baseline = layout === "overlay" ? measureScale.domain[0] : 0;
121
+ return categories.flatMap(category => {
122
+ const cells = series.map(color =>
123
+ lookup.get(JSON.stringify([category, color]))
124
+ );
125
+ const values = cells.map(cell => cell?.[channels.measure] ?? 0);
126
+ return layoutSeriesPartition(values, layout, { baseline }).flatMap(segment => {
127
+ const cell = cells[segment.index];
128
+ return cell === undefined
129
+ ? []
130
+ : [definition(cell, segment.start, segment.end)];
131
+ });
132
+ });
133
+ }
134
+
135
+ function histogramDefinitions(program, layer, dataset) {
136
+ const xEncoding = layer.encoding.x;
137
+ const colorEncoding = layer.encoding?.color;
138
+ const segments = deriveHistogramSegments({
139
+ dataset,
140
+ layer,
141
+ xEncoding,
142
+ xScale: findSemanticScale(program, xEncoding.scale),
143
+ resolvedScales: program.resolvedScales
144
+ });
145
+ const colorScale = program.resolvedScales[colorEncoding?.scale];
146
+ return segments.map(segment => {
147
+ const colorValue = colorScale?.domain[segment.category];
148
+ return {
149
+ fields: uniqueFields(segment.members),
150
+ channels: {
151
+ x: segment.start,
152
+ x2: segment.end,
153
+ y: segment.stackStart,
154
+ y2: segment.stackEnd,
155
+ ...(colorValue === undefined ? {} : { color: colorValue })
156
+ },
157
+ members: segment.members
158
+ };
159
+ });
160
+ }
161
+
162
+ function rangedDefinitions(layer, dataset) {
163
+ return dataset.values.map(row => ({
164
+ fields: ownFields(row),
165
+ channels: channelMapFromRow(row, layer),
166
+ members: [row]
167
+ }));
168
+ }
169
+
170
+ function sharedChannels(definitions) {
171
+ if (definitions.length === 0) return {};
172
+ const shared = { ...definitions[0].channels };
173
+ for (const channel of Object.keys(shared)) {
174
+ if (!definitions.every(definition =>
175
+ definition.channels[channel] === shared[channel]
176
+ )) delete shared[channel];
177
+ }
178
+ return shared;
179
+ }
180
+
181
+ function barStackDefinitions(layer, barGrain, definitions) {
182
+ const layout = resolveBarColorLayout(layer);
183
+ if (!["stack", "fill", "diverging"].includes(layout)) {
184
+ throw new Error(
185
+ `Bar mark "${layer.id}" does not define stacked items at its current "${layout}" layout.`
186
+ );
187
+ }
188
+ if (barGrain === BAR_GRAINS.ranged) {
189
+ throw new Error(`Ranged bar mark "${layer.id}" has no stack grain.`);
190
+ }
191
+ const channels = resolveBarChannels(layer);
192
+ const categoryChannels = barGrain === BAR_GRAINS.histogram
193
+ ? ["x", "x2"]
194
+ : [channels.category];
195
+ const measure = channels.measure;
196
+ const secondary = `${measure}2`;
197
+ const groups = new Map();
198
+ definitions.forEach((definition, index) => {
199
+ const key = JSON.stringify(categoryChannels.map(channel =>
200
+ definition.channels[channel]
201
+ ));
202
+ const group = groups.get(key) ?? [];
203
+ group.push({ definition, index });
204
+ groups.set(key, group);
205
+ });
206
+
207
+ return [...groups.values()].map((entries, index) => {
208
+ const grouped = entries.map(entry => entry.definition);
209
+ const endpoints = grouped.flatMap(definition => [
210
+ definition.channels[measure],
211
+ definition.channels[secondary]
212
+ ]).filter(Number.isFinite);
213
+ if (endpoints.length === 0) {
214
+ throw new Error(
215
+ `Bar mark "${layer.id}" stack ${index} has no resolved semantic endpoints.`
216
+ );
217
+ }
218
+ const members = grouped.flatMap(definition => definition.members);
219
+ return {
220
+ key: itemKey(layer, "stack", index),
221
+ fields: uniqueFields(members),
222
+ channels: {
223
+ ...sharedChannels(grouped),
224
+ [measure]: Math.min(...endpoints),
225
+ [secondary]: Math.max(...endpoints)
226
+ },
227
+ members,
228
+ graphicIndices: entries.map(entry => entry.index)
229
+ };
230
+ });
231
+ }
232
+
233
+ export function resolveBarItems(program, layer, dataset, selectionGrain) {
234
+ const grain = resolveBarGrain(layer);
235
+ const definitions = grain === BAR_GRAINS.histogram
236
+ ? histogramDefinitions(program, layer, dataset)
237
+ : grain === BAR_GRAINS.aggregate
238
+ ? aggregateCellDefinitions(program, layer, dataset)
239
+ : grain === BAR_GRAINS.ranged
240
+ ? rangedDefinitions(layer, dataset)
241
+ : undefined;
242
+ if (definitions === undefined) {
243
+ throw new Error(`Bar mark "${layer.id}" is incomplete for selection.`);
244
+ }
245
+ if (selectionGrain === "stack") {
246
+ return finalizeItems(
247
+ program,
248
+ layer,
249
+ "stack",
250
+ barStackDefinitions(layer, grain, definitions),
251
+ "rect"
252
+ );
253
+ }
254
+ return finalizeItems(program, layer, grain, definitions, "rect");
255
+ }