ggaction 0.0.4 → 0.0.5

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 (157) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +35 -13
  3. package/package.json +18 -7
  4. package/src/actions/boxPlots/create.js +13 -7
  5. package/src/actions/boxPlots/edit.js +31 -35
  6. package/src/actions/boxPlots/materialize.js +3 -0
  7. package/src/actions/boxPlots/options.js +8 -1
  8. package/src/actions/boxPlots/resolve.js +10 -2
  9. package/src/actions/charts/bar.js +50 -0
  10. package/src/actions/charts/heatmap.js +205 -0
  11. package/src/actions/charts/histogram.js +58 -0
  12. package/src/actions/charts/index.js +15 -0
  13. package/src/actions/charts/line.js +69 -0
  14. package/src/actions/charts/parallel.js +76 -0
  15. package/src/actions/charts/scatter.js +56 -0
  16. package/src/actions/charts/shared.js +121 -0
  17. package/src/actions/coordinates/parallel.js +50 -0
  18. package/src/actions/data/bin2d.js +137 -0
  19. package/src/actions/data/density.js +54 -13
  20. package/src/actions/data/derived.js +22 -0
  21. package/src/actions/data/filter.js +5 -0
  22. package/src/actions/data/gradientProfile.js +65 -0
  23. package/src/actions/data/horizon.js +71 -0
  24. package/src/actions/data/index.js +29 -2
  25. package/src/actions/data/window.js +50 -0
  26. package/src/actions/encodings/color/index.js +17 -1
  27. package/src/actions/encodings/color/layout.js +8 -0
  28. package/src/actions/encodings/density/resolve.js +109 -0
  29. package/src/actions/encodings/density.js +193 -88
  30. package/src/actions/encodings/horizon/resolve.js +283 -0
  31. package/src/actions/encodings/horizon.js +364 -0
  32. package/src/actions/encodings/index.js +6 -0
  33. package/src/actions/encodings/parallel.js +115 -0
  34. package/src/actions/encodings/pathOrder.js +131 -0
  35. package/src/actions/encodings/position/apply.js +73 -4
  36. package/src/actions/encodings/position/index.js +15 -2
  37. package/src/actions/encodings/position/policies/arc.js +27 -4
  38. package/src/actions/encodings/position/policies/area.js +33 -2
  39. package/src/actions/encodings/position/policies/index.js +7 -2
  40. package/src/actions/encodings/position/policies/line.js +18 -2
  41. package/src/actions/encodings/position/resolve.js +24 -2
  42. package/src/actions/encodings/ranged.js +3 -1
  43. package/src/actions/encodings/ruleAppearance.js +88 -10
  44. package/src/actions/facets/actions.js +1 -2
  45. package/src/actions/facets/derive.js +33 -3
  46. package/src/actions/facets/replay.js +7 -36
  47. package/src/actions/gradientPlots/components.js +177 -0
  48. package/src/actions/gradientPlots/create.js +96 -0
  49. package/src/actions/gradientPlots/edit.js +153 -0
  50. package/src/actions/gradientPlots/index.js +24 -0
  51. package/src/actions/gradientPlots/materialize.js +287 -0
  52. package/src/actions/gradientPlots/options.js +120 -0
  53. package/src/actions/gradientPlots/paint.js +107 -0
  54. package/src/actions/gradientPlots/rebind.js +51 -0
  55. package/src/actions/gradientPlots/resolve.js +81 -0
  56. package/src/actions/guides/applicability.js +44 -17
  57. package/src/actions/guides/axes/axes.js +37 -3
  58. package/src/actions/guides/axes/index.js +2 -0
  59. package/src/actions/guides/axes/labels.js +11 -6
  60. package/src/actions/guides/axes/parallel/resolve.js +86 -0
  61. package/src/actions/guides/axes/parallel.js +228 -0
  62. package/src/actions/guides/axes/titles.js +11 -8
  63. package/src/actions/guides/guides.js +60 -5
  64. package/src/actions/guides/legends/categorical/actions.js +48 -11
  65. package/src/actions/guides/legends/edit.js +5 -0
  66. package/src/actions/guides/legends/index.js +2 -0
  67. package/src/actions/guides/legends/remove.js +4 -11
  68. package/src/actions/guides/legends/strokeWidth.js +170 -0
  69. package/src/actions/index.js +6 -0
  70. package/src/actions/marks/area/actions.js +60 -136
  71. package/src/actions/marks/area/materialize.js +163 -0
  72. package/src/actions/marks/index.js +5 -1
  73. package/src/actions/marks/line/actions.js +99 -80
  74. package/src/actions/marks/line/materialize.js +146 -0
  75. package/src/actions/marks/point/create.js +76 -0
  76. package/src/actions/marks/point/edit.js +85 -0
  77. package/src/actions/marks/point/index.js +11 -1
  78. package/src/actions/marks/point/jitter.js +72 -0
  79. package/src/actions/marks/point/{actions.js → materialize.js} +175 -170
  80. package/src/actions/marks/rect/actions.js +3 -0
  81. package/src/actions/marks/remove.js +13 -2
  82. package/src/actions/marks/rule/actions.js +25 -3
  83. package/src/actions/marks/text/actions.js +19 -4
  84. package/src/actions/marks/text/index.js +7 -1
  85. package/src/actions/marks/text/layout.js +321 -0
  86. package/src/actions/primitives/semanticValidation/dataset.js +22 -0
  87. package/src/actions/primitives/semanticValidation/guide.js +43 -0
  88. package/src/actions/primitives/semanticValidation/index.js +23 -0
  89. package/src/actions/primitives/semanticValidation/layer.js +74 -0
  90. package/src/actions/primitives/semanticValidation/scale.js +74 -0
  91. package/src/actions/primitives/semanticValidation/shared.js +5 -0
  92. package/src/actions/primitives/semanticValue.js +1 -204
  93. package/src/actions/regression/edit.js +19 -21
  94. package/src/actions/scales/consumers/common.js +18 -0
  95. package/src/actions/scales/consumers/families.js +14 -3
  96. package/src/actions/scales/definitions.js +30 -0
  97. package/src/actions/scales/edit.js +6 -204
  98. package/src/actions/scales/editPolicy.js +214 -0
  99. package/src/actions/scales/materialize.js +1 -1
  100. package/src/actions/scales/patch.js +26 -0
  101. package/src/actions/selection/actions.js +28 -3
  102. package/src/actions/violinPlots/create.js +198 -0
  103. package/src/actions/violinPlots/index.js +5 -0
  104. package/src/core/textMetrics.js +47 -0
  105. package/src/core/vocabulary.js +7 -2
  106. package/src/grammar/arcs.js +49 -12
  107. package/src/grammar/areaSeries.js +41 -5
  108. package/src/grammar/bin2d.js +333 -0
  109. package/src/grammar/categoricalDensity.js +192 -0
  110. package/src/grammar/coordinates.js +1 -1
  111. package/src/grammar/curveCommands.js +3 -1
  112. package/src/grammar/density.js +252 -26
  113. package/src/grammar/facets/dependencies.js +3 -11
  114. package/src/grammar/facets/index.js +3 -1
  115. package/src/grammar/gradientProfile.js +213 -0
  116. package/src/grammar/horizon.js +455 -0
  117. package/src/grammar/jitter.js +197 -0
  118. package/src/grammar/lineSeries.js +69 -0
  119. package/src/grammar/paint.js +88 -0
  120. package/src/grammar/parallelCoordinates.js +213 -0
  121. package/src/grammar/pathOrder.js +35 -0
  122. package/src/grammar/pointShapes.js +24 -0
  123. package/src/grammar/rules.js +1 -0
  124. package/src/grammar/scales/appearance.js +21 -0
  125. package/src/grammar/schemas/concreteGraphic.js +11 -1
  126. package/src/grammar/schemas/graphicBounds.js +19 -37
  127. package/src/grammar/schemas/semanticPath.js +17 -2
  128. package/src/grammar/transforms.js +100 -11
  129. package/src/grammar/window.js +339 -0
  130. package/src/layout/labels.js +270 -0
  131. package/src/layout/text.js +4 -1
  132. package/src/layout/title.js +10 -20
  133. package/src/materialization/dataProvenance.js +56 -2
  134. package/src/materialization/dependencies.js +9 -14
  135. package/src/materialization/guides/resources.js +74 -24
  136. package/src/materialization/horizon.js +29 -0
  137. package/src/materialization/layout.js +15 -0
  138. package/src/materialization/marks/capabilities.js +21 -1
  139. package/src/materialization/marks/index.js +3 -0
  140. package/src/materialization/marks/pathOrder.js +71 -0
  141. package/src/materialization/marks/policies.js +6 -2
  142. package/src/materialization/scaleGuideDependencies.js +8 -0
  143. package/src/materialization/scales/resolve.js +2 -0
  144. package/src/materialization/selection/items/path.js +27 -1
  145. package/src/materialization/selection/items/point.js +1 -5
  146. package/src/materialization/selection/items/rect.js +21 -0
  147. package/src/materialization/selection/styles.js +20 -9
  148. package/src/materialization/text.js +12 -4
  149. package/src/renderers/canvas/fill.js +33 -0
  150. package/src/renderers/canvas/index.js +7 -0
  151. package/src/renderers/canvas/path.js +7 -4
  152. package/src/renderers/canvas/rect.js +9 -3
  153. package/src/selectors/datasets.js +4 -0
  154. package/src/theme/defaults.js +4 -0
  155. package/types/extension.d.ts +6 -0
  156. package/types/index.d.ts +76 -0
  157. package/types/program.d.ts +632 -10
@@ -3,9 +3,11 @@ import { validateUserId } from "../../core/identifiers.js";
3
3
  import { validateKeys } from "../../core/validation.js";
4
4
  import { hasDataset } from "../../selectors/index.js";
5
5
  import { findDataset } from "../../selectors/datasets.js";
6
+ import { requireLayer } from "../../selectors/layers.js";
6
7
 
7
8
  const OPTIONS = Object.freeze(["id", "source", "transform"]);
8
9
  const RELEASE_OPTIONS = Object.freeze(["id"]);
10
+ const REBIND_OPTIONS = Object.freeze(["id", "data"]);
9
11
 
10
12
  export const createDerivedData = action(
11
13
  { op: "createDerivedData", description: "Create an immutable derived dataset definition." },
@@ -50,3 +52,23 @@ export const releaseDerivedData = action(
50
52
  });
51
53
  }
52
54
  );
55
+
56
+ export const rebindLayerData = action(
57
+ {
58
+ op: "rebindLayerData",
59
+ description: "Rebind one semantic layer to an existing dataset."
60
+ },
61
+ function (args = {}) {
62
+ validateKeys(args, REBIND_OPTIONS, "rebindLayerData");
63
+ const id = validateUserId(args.id, "Layer id");
64
+ const data = validateUserId(args.data, "Layer dataset id");
65
+ requireLayer(this, id);
66
+ if (!hasDataset(this, data)) {
67
+ throw new Error(`Layer dataset "${data}" does not exist.`);
68
+ }
69
+ return this.editSemantic({
70
+ property: `layer[${id}].data`,
71
+ value: data
72
+ });
73
+ }
74
+ );
@@ -125,6 +125,11 @@ export const filterMarks = action(
125
125
  candidate.data !== undefined &&
126
126
  findSelectionPolicy(candidate.mark?.type) !== undefined
127
127
  });
128
+ if (this.markConfigs[layer.id]?.itemFilterable === false) {
129
+ throw new Error(
130
+ `filterMarks does not support composite mark "${layer.id}"; filter its source dataset first.`
131
+ );
132
+ }
128
133
  const derivedId = `${layer.id}FilteredData`;
129
134
  if (hasDataset(this, derivedId)) {
130
135
  throw new Error(`Dataset "${derivedId}" already exists.`);
@@ -0,0 +1,65 @@
1
+ import { action } from "../../core/action.js";
2
+ import { validateUserId } from "../../core/identifiers.js";
3
+ import { validateKeys } from "../../core/validation.js";
4
+ import {
5
+ deriveGradientProfiles,
6
+ normalizeGradientProfileTransform
7
+ } from "../../grammar/gradientProfile.js";
8
+ import { MATERIALIZE_OPTIONS, requireDerivedDataset } from "./shared.js";
9
+
10
+ const OPTIONS = Object.freeze([
11
+ "id", "source", "category", "field", "bandwidth", "extent", "steps",
12
+ "kernel", "normalization", "center", "as"
13
+ ]);
14
+
15
+ export const materializeGradientProfileData = action(
16
+ {
17
+ op: "materializeGradientProfileData",
18
+ description: "Materialize one categorical density-profile dataset."
19
+ },
20
+ function (args = {}) {
21
+ validateKeys(
22
+ args,
23
+ MATERIALIZE_OPTIONS,
24
+ "materializeGradientProfileData"
25
+ );
26
+ const { id, source, transform } = requireDerivedDataset(
27
+ this,
28
+ args.id,
29
+ "gradientProfile"
30
+ );
31
+ const result = deriveGradientProfiles(source.values, transform);
32
+ return this
33
+ .editSemantic({
34
+ property: `dataset[${id}].transform`,
35
+ value: [{
36
+ ...transform,
37
+ resolved: {
38
+ bandwidth: result.bandwidth,
39
+ extent: result.extent,
40
+ intensityDomain: result.intensityDomain
41
+ }
42
+ }]
43
+ })
44
+ .editSemantic({ property: `dataset[${id}].values`, value: result.values });
45
+ }
46
+ );
47
+
48
+ export const createGradientProfileData = action(
49
+ {
50
+ op: "createGradientProfileData",
51
+ description: "Create one immutable categorical density-profile dataset."
52
+ },
53
+ function (args = {}) {
54
+ validateKeys(args, OPTIONS, "createGradientProfileData");
55
+ const id = validateUserId(args.id, "Gradient profile dataset id");
56
+ const source = validateUserId(
57
+ args.source ?? this.context.currentData,
58
+ "Gradient profile source dataset id"
59
+ );
60
+ const transform = normalizeGradientProfileTransform(args);
61
+ return this
62
+ .createDerivedData({ id, source, transform: [transform] })
63
+ .materializeGradientProfileData({ id });
64
+ }
65
+ );
@@ -0,0 +1,71 @@
1
+ import { action } from "../../core/action.js";
2
+ import { validateUserId } from "../../core/identifiers.js";
3
+ import { validateKeys } from "../../core/validation.js";
4
+ import {
5
+ deriveHorizon,
6
+ validateHorizonTransform
7
+ } from "../../grammar/horizon.js";
8
+ import { MATERIALIZE_OPTIONS, requireDerivedDataset } from "./shared.js";
9
+
10
+ const OPTIONS = Object.freeze([
11
+ "id", "source", "x", "y", "groupBy", "bands", "baseline", "extent",
12
+ "resolve", "missing", "overflow", "palette", "as"
13
+ ]);
14
+
15
+ function requestedTransform(args) {
16
+ return validateHorizonTransform({
17
+ type: "horizon",
18
+ x: args.x,
19
+ y: args.y,
20
+ ...(args.groupBy === undefined ? {} : { groupBy: args.groupBy }),
21
+ bands: args.bands ?? 3,
22
+ baseline: args.baseline ?? 0,
23
+ extent: args.extent ?? "auto",
24
+ resolve: args.resolve ?? "shared",
25
+ missing: args.missing ?? "break",
26
+ overflow: args.overflow ?? "clip",
27
+ palette: args.palette ?? {},
28
+ as: args.as
29
+ });
30
+ }
31
+
32
+ export const materializeHorizonData = action(
33
+ {
34
+ op: "materializeHorizonData",
35
+ description: "Materialize one immutable Horizon band dataset."
36
+ },
37
+ function (args = {}) {
38
+ validateKeys(args, MATERIALIZE_OPTIONS, "materializeHorizonData");
39
+ const { id, source, transform } = requireDerivedDataset(
40
+ this,
41
+ args.id,
42
+ "horizon"
43
+ );
44
+ const result = deriveHorizon(source.values, transform);
45
+ return this
46
+ .editSemantic({
47
+ property: `dataset[${id}].transform`,
48
+ value: [{ ...result.transform, resolved: result.resolved }]
49
+ })
50
+ .editSemantic({
51
+ property: `dataset[${id}].values`,
52
+ value: result.values
53
+ });
54
+ }
55
+ );
56
+
57
+ export const createHorizonData = action(
58
+ {
59
+ op: "createHorizonData",
60
+ description: "Create one immutable Horizon band dataset."
61
+ },
62
+ function (args = {}) {
63
+ validateKeys(args, OPTIONS, "createHorizonData");
64
+ const id = validateUserId(args.id, "Horizon dataset id");
65
+ const source = validateUserId(args.source, "Horizon source dataset id");
66
+ const transform = requestedTransform(args);
67
+ return this
68
+ .createDerivedData({ id, source, transform: [transform] })
69
+ .materializeHorizonData({ id });
70
+ }
71
+ );
@@ -1,5 +1,9 @@
1
1
  import { createData } from "./create.js";
2
- import { createDerivedData, releaseDerivedData } from "./derived.js";
2
+ import {
3
+ createDerivedData,
4
+ rebindLayerData,
5
+ releaseDerivedData
6
+ } from "./derived.js";
3
7
  import {
4
8
  filterData,
5
9
  filterMarks,
@@ -7,24 +11,47 @@ import {
7
11
  materializeMarkFilteredData
8
12
  } from "./filter.js";
9
13
  import { createRegressionData, materializeRegressionData } from "./regression.js";
10
- import { createDensityData, materializeDensityData } from "./density.js";
14
+ import {
15
+ createCategoricalDensityData,
16
+ createDensityData,
17
+ materializeDensityData
18
+ } from "./density.js";
19
+ import {
20
+ createGradientProfileData,
21
+ materializeGradientProfileData
22
+ } from "./gradientProfile.js";
11
23
  import { createIntervalData, materializeIntervalData } from "./interval.js";
24
+ import { createHorizonData, materializeHorizonData } from "./horizon.js";
25
+ import { createWindowData, materializeWindowData } from "./window.js";
26
+ import { createBin2DData, materializeBin2DData } from "./bin2d.js";
12
27
  import { createBoxSummaryData, createBoxOutlierData, materializeBoxSummaryData, materializeBoxOutlierData } from "./box.js";
13
28
 
14
29
  export function registerDataActions(ProgramClass) {
15
30
  ProgramClass.prototype.createData = createData;
16
31
  ProgramClass.prototype.createDerivedData = createDerivedData;
17
32
  ProgramClass.prototype.releaseDerivedData = releaseDerivedData;
33
+ ProgramClass.prototype.rebindLayerData = rebindLayerData;
18
34
  ProgramClass.prototype.createDensityData = createDensityData;
35
+ ProgramClass.prototype.createCategoricalDensityData =
36
+ createCategoricalDensityData;
19
37
  ProgramClass.prototype.materializeFilteredData = materializeFilteredData;
20
38
  ProgramClass.prototype.materializeMarkFilteredData = materializeMarkFilteredData;
21
39
  ProgramClass.prototype.filterData = filterData;
22
40
  ProgramClass.prototype.filterMarks = filterMarks;
23
41
  ProgramClass.prototype.materializeRegressionData = materializeRegressionData;
24
42
  ProgramClass.prototype.materializeDensityData = materializeDensityData;
43
+ ProgramClass.prototype.createGradientProfileData = createGradientProfileData;
44
+ ProgramClass.prototype.materializeGradientProfileData =
45
+ materializeGradientProfileData;
25
46
  ProgramClass.prototype.createRegressionData = createRegressionData;
26
47
  ProgramClass.prototype.materializeIntervalData = materializeIntervalData;
27
48
  ProgramClass.prototype.createIntervalData = createIntervalData;
49
+ ProgramClass.prototype.createHorizonData = createHorizonData;
50
+ ProgramClass.prototype.materializeHorizonData = materializeHorizonData;
51
+ ProgramClass.prototype.createWindowData = createWindowData;
52
+ ProgramClass.prototype.materializeWindowData = materializeWindowData;
53
+ ProgramClass.prototype.createBin2DData = createBin2DData;
54
+ ProgramClass.prototype.materializeBin2DData = materializeBin2DData;
28
55
  ProgramClass.prototype.createBoxSummaryData = createBoxSummaryData;
29
56
  ProgramClass.prototype.createBoxOutlierData = createBoxOutlierData;
30
57
  ProgramClass.prototype.materializeBoxSummaryData = materializeBoxSummaryData;
@@ -0,0 +1,50 @@
1
+ import { action } from "../../core/action.js";
2
+ import { validateUserId } from "../../core/identifiers.js";
3
+ import { validateKeys } from "../../core/validation.js";
4
+ import {
5
+ deriveWindowRows,
6
+ normalizeWindowTransform
7
+ } from "../../grammar/window.js";
8
+ import { MATERIALIZE_OPTIONS, requireDerivedDataset } from "./shared.js";
9
+
10
+ const OPTIONS = Object.freeze([
11
+ "id", "source", "partitionBy", "sortBy", "operations"
12
+ ]);
13
+
14
+ export const materializeWindowData = action(
15
+ {
16
+ op: "materializeWindowData",
17
+ description: "Materialize one immutable window-derived dataset."
18
+ },
19
+ function (args = {}) {
20
+ validateKeys(args, MATERIALIZE_OPTIONS, "materializeWindowData");
21
+ const { id, source, transform } = requireDerivedDataset(
22
+ this,
23
+ args.id,
24
+ "window"
25
+ );
26
+ return this.editSemantic({
27
+ property: `dataset[${id}].values`,
28
+ value: deriveWindowRows(source.values, transform)
29
+ });
30
+ }
31
+ );
32
+
33
+ export const createWindowData = action(
34
+ {
35
+ op: "createWindowData",
36
+ description: "Create immutable partitioned window values."
37
+ },
38
+ function (args = {}) {
39
+ validateKeys(args, OPTIONS, "createWindowData");
40
+ const id = validateUserId(args.id, "Window dataset id");
41
+ const source = validateUserId(
42
+ args.source ?? this.context.currentData,
43
+ "Source dataset id"
44
+ );
45
+ const transform = normalizeWindowTransform(args);
46
+ return this
47
+ .createDerivedData({ id, source, transform: [transform] })
48
+ .materializeWindowData({ id });
49
+ }
50
+ );
@@ -19,6 +19,8 @@ import { applyMaterializationPlan } from "../../../materialization/dependencies.
19
19
  import {
20
20
  planEncodingRematerialization
21
21
  } from "../../../materialization/encodings.js";
22
+ import { findUpstreamTransform } from
23
+ "../../../materialization/dataProvenance.js";
22
24
  import { encodeContinuousColor } from "./continuous.js";
23
25
  import {
24
26
  applyColorLayoutCompanion,
@@ -56,10 +58,24 @@ const encodeColor = action(
56
58
  "color mark"
57
59
  );
58
60
  assertNoConstantColor(this, layer);
61
+ const densityTransform = dataset.transform?.length === 1 &&
62
+ dataset.transform[0].type === "density"
63
+ ? dataset.transform[0]
64
+ : undefined;
65
+ const densitySeriesFields = densityTransform?.placement?.type === "category"
66
+ ? [densityTransform.groupBy, densityTransform.placement.split?.field]
67
+ .filter(Boolean)
68
+ : [];
69
+ const horizonTransform = findUpstreamTransform(this, dataset, "horizon");
70
+ const horizonColor = horizonTransform !== undefined &&
71
+ horizonTransform.as?.color === args.field &&
72
+ layer.encoding?.group?.field === horizonTransform.as?.group;
59
73
  if (
60
74
  layer.mark.type === "area" &&
61
75
  (layer.encoding?.group?.field === undefined ||
62
- layer.encoding.group.field !== args.field)
76
+ layer.encoding.group.field !== args.field) &&
77
+ !densitySeriesFields.includes(args.field) &&
78
+ !horizonColor
63
79
  ) {
64
80
  throw new Error(
65
81
  "Area color encoding must match an existing group encoding."
@@ -18,6 +18,14 @@ export function applyColorLayoutCompanion(
18
18
  ) {
19
19
  return program;
20
20
  }
21
+ if (
22
+ layer.mark.type === "area" &&
23
+ [layer.encoding?.x, layer.encoding?.y].some(
24
+ encoding => ["nominal", "ordinal"].includes(encoding?.fieldType)
25
+ )
26
+ ) {
27
+ return program;
28
+ }
21
29
  if (isRangedArea(layer)) return program;
22
30
  const channels = layer.mark.type === "bar"
23
31
  ? resolveBarChannels(layer)
@@ -0,0 +1,109 @@
1
+ import { isPlainObject } from "../../../core/immutable.js";
2
+ import { findSemanticScale } from "../../../selectors/scales.js";
3
+
4
+ export function requireDensityField(value, label) {
5
+ if (typeof value !== "string" || value.length === 0) {
6
+ throw new TypeError(`${label} must be a non-empty string.`);
7
+ }
8
+ return value;
9
+ }
10
+
11
+ export function resolveDensityScaleOptions(value, defaults, label) {
12
+ if (value !== undefined && !isPlainObject(value)) {
13
+ throw new TypeError(`${label} must be a plain object.`);
14
+ }
15
+ return { ...defaults, ...(value ?? {}) };
16
+ }
17
+
18
+ export function resolveDensityCategoryScaleOptions(value, fallbackId) {
19
+ const scale = resolveDensityScaleOptions(
20
+ value,
21
+ { type: "band" },
22
+ "Density placement scale"
23
+ );
24
+ if ((scale.type ?? "band") !== "band") {
25
+ throw new Error('Density category placement requires scale type "band".');
26
+ }
27
+ return {
28
+ ...(fallbackId === undefined ? {} : { id: fallbackId }),
29
+ ...scale,
30
+ type: "band"
31
+ };
32
+ }
33
+
34
+ function valueScaleForTransition(program, layer, transform) {
35
+ const densityChannel = transform.placement?.channel ?? (
36
+ layer.encoding.x.field === transform.as[1] ? "x" : "y"
37
+ );
38
+ const valueChannel = densityChannel === "x" ? "y" : "x";
39
+ return findSemanticScale(program, layer.encoding[valueChannel].scale);
40
+ }
41
+
42
+ export function resolveDensityTransitionScaleDefinitions(
43
+ program,
44
+ layer,
45
+ transform,
46
+ placement,
47
+ rawPlacement
48
+ ) {
49
+ const valueScale = valueScaleForTransition(program, layer, transform);
50
+ if (valueScale === undefined) {
51
+ throw new Error(`Density area "${layer.id}" requires its value scale.`);
52
+ }
53
+ const { id: _valueId, range: _valueRange, ...valueDefinition } = valueScale;
54
+ void _valueId;
55
+ void _valueRange;
56
+ const valueChannel = placement?.channel === "x" ? "y" : "x";
57
+ const companionChannel = valueChannel === "x" ? "y" : "x";
58
+ const companionDefinition = placement === undefined
59
+ ? { type: "linear", domain: "auto", range: "auto", nice: true, zero: true }
60
+ : resolveDensityCategoryScaleOptions(rawPlacement.scale);
61
+ return {
62
+ [valueChannel]: { ...valueDefinition, range: "auto" },
63
+ [companionChannel]: companionDefinition
64
+ };
65
+ }
66
+
67
+ export function resolveDensityPositionDefinition({
68
+ layer,
69
+ output,
70
+ groupBy,
71
+ placement,
72
+ coordinate,
73
+ valueScale,
74
+ densityScale,
75
+ placementScale
76
+ }) {
77
+ if (placement === undefined) {
78
+ const xIsDensity = layer.densityChannel === "x";
79
+ return {
80
+ x: {
81
+ field: output[xIsDensity ? 1 : 0],
82
+ fieldType: "quantitative",
83
+ scale: xIsDensity ? densityScale : valueScale,
84
+ coordinate
85
+ },
86
+ y: {
87
+ field: output[xIsDensity ? 0 : 1],
88
+ fieldType: "quantitative",
89
+ scale: xIsDensity ? valueScale : densityScale,
90
+ coordinate
91
+ }
92
+ };
93
+ }
94
+ const category = {
95
+ field: placement.categoryField,
96
+ fieldType: "nominal",
97
+ scale: placementScale,
98
+ coordinate
99
+ };
100
+ const value = {
101
+ field: output[0],
102
+ fieldType: "quantitative",
103
+ scale: valueScale,
104
+ coordinate
105
+ };
106
+ return placement.channel === "x"
107
+ ? { x: category, y: value }
108
+ : { x: value, y: category };
109
+ }