ggaction 0.0.16 → 0.0.18
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.
- package/CHANGELOG.md +66 -0
- package/README.md +3 -2
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/knowledge/task-resolver.js +1 -1
- package/package.json +10 -4
- package/src/BasicChartProgram.js +1 -1
- package/src/accessibility.js +3 -2
- package/src/actions/boxPlots/create.js +14 -4
- package/src/actions/boxPlots/edit.js +39 -33
- package/src/actions/boxPlots/materialize.js +7 -3
- package/src/actions/boxPlots/options.js +37 -11
- package/src/actions/canvas/actions.js +70 -95
- package/src/actions/canvas/fitting.js +16 -1
- package/src/actions/canvas/index.js +3 -0
- package/src/actions/canvas/plotBorder.js +28 -0
- package/src/actions/charts/area.js +2 -2
- package/src/actions/charts/bar.js +34 -7
- package/src/actions/charts/ecdf.js +4 -18
- package/src/actions/charts/endpoints.js +4 -18
- package/src/actions/charts/heatmap.js +4 -4
- package/src/actions/charts/index.js +2 -0
- package/src/actions/charts/interval-regression.js +2 -2
- package/src/actions/charts/line.js +1 -1
- package/src/actions/charts/pie.js +3 -47
- package/src/actions/charts/radial.js +34 -12
- package/src/actions/charts/scatter.js +16 -2
- package/src/actions/charts/shared.js +2 -2
- package/src/actions/charts/text.js +35 -0
- package/src/actions/composition/actions.js +6 -2
- package/src/actions/data/bin.js +1 -1
- package/src/actions/data/box.js +2 -1
- package/src/actions/data/create.js +18 -5
- package/src/actions/data/density.js +1 -1
- package/src/actions/data/derived.js +15 -1
- package/src/actions/data/edit.js +20 -7
- package/src/actions/data/filter.js +15 -4
- package/src/actions/data/index.js +3 -0
- package/src/actions/data/interval.js +2 -0
- package/src/actions/data/regression.js +6 -2
- package/src/actions/data/revise.js +37 -5
- package/src/actions/data/shared.js +33 -4
- package/src/actions/data/sort.js +21 -0
- package/src/actions/data/stack.js +3 -1
- package/src/actions/data/summary.js +1 -1
- package/src/actions/encodings/angle.js +4 -1
- package/src/actions/encodings/appearance.js +43 -31
- package/src/actions/encodings/barWidth.js +36 -11
- package/src/actions/encodings/channels.js +0 -1
- package/src/actions/encodings/color/continuous.js +5 -2
- package/src/actions/encodings/color/index.js +5 -2
- package/src/actions/encodings/color/policy.js +10 -3
- package/src/actions/encodings/density/resolve.js +1 -6
- package/src/actions/encodings/offset.js +4 -1
- package/src/actions/encodings/position/policies/arc.js +12 -10
- package/src/actions/encodings/position/policies/area.js +3 -3
- package/src/actions/encodings/position/policies/bar.js +15 -6
- package/src/actions/encodings/position/policies/line.js +17 -6
- package/src/actions/encodings/position/resolve.js +9 -3
- package/src/actions/encodings/ranged.js +23 -1
- package/src/actions/encodings/remove.js +3 -2
- package/src/actions/encodings/ruleAppearance.js +4 -1
- package/src/actions/encodings/seriesLayout.js +1 -0
- package/src/actions/encodings/stroke.js +4 -1
- package/src/actions/encodings/strokeDash.js +6 -1
- package/src/actions/encodings/text.js +12 -4
- package/src/actions/errorBands/create.js +2 -2
- package/src/actions/errorBands/edit.js +2 -1
- package/src/actions/facets/actions.js +158 -240
- package/src/actions/gradientPlots/resolve.js +2 -17
- package/src/actions/guides/applicability.js +1 -1
- package/src/actions/guides/axes/axis.js +16 -4
- package/src/actions/guides/axes/edit.js +4 -4
- package/src/actions/guides/axes/labels.js +120 -46
- package/src/actions/guides/axes/lines.js +6 -5
- package/src/actions/guides/axes/parallel/lifecycle.js +1 -1
- package/src/actions/guides/axes/parallel/policy.js +8 -7
- package/src/actions/guides/axes/remove.js +3 -2
- package/src/actions/guides/axes/tickGroups.js +5 -2
- package/src/actions/guides/axes/ticks.js +6 -3
- package/src/actions/guides/axes/titles.js +71 -26
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facadeLegend.js +10 -2
- package/src/actions/guides/grids/grid.js +7 -1
- package/src/actions/guides/grids/resolve.js +59 -12
- package/src/actions/guides/legends/blocks.js +3 -1
- package/src/actions/guides/legends/categorical/actions.js +70 -17
- package/src/actions/guides/legends/categorical/components.js +12 -8
- package/src/actions/guides/legends/categorical/index.js +6 -0
- package/src/actions/guides/legends/categorical/layout.js +37 -8
- package/src/actions/guides/legends/categorical/options.js +31 -20
- package/src/actions/guides/legends/categorical/resolve.js +2 -2
- package/src/actions/guides/legends/categorical/symbols.js +18 -12
- package/src/actions/guides/legends/categorical/validation.js +1 -6
- package/src/actions/guides/legends/continuous/colorSymbols.js +1 -0
- package/src/actions/guides/legends/continuous/common.js +1 -447
- package/src/actions/guides/legends/continuous/gradient.js +47 -34
- package/src/actions/guides/legends/continuous/interval.js +4 -4
- package/src/actions/guides/legends/continuous/opacity.js +19 -12
- package/src/actions/guides/legends/edit.js +1 -604
- package/src/actions/guides/legends/focused.js +2 -2
- package/src/actions/guides/legends/group.js +1 -0
- package/src/actions/guides/legends/lane.js +1 -516
- package/src/actions/guides/legends/remove.js +3 -0
- package/src/actions/guides/legends/sampling.js +1 -1
- package/src/actions/guides/legends/size.js +51 -43
- package/src/actions/guides/legends/strokeWidth.js +11 -29
- package/src/actions/guides/legends/target.js +3 -3
- package/src/actions/guides/legends/transition.js +21 -7
- package/src/actions/guides/polar/axes/labels.js +4 -2
- package/src/actions/guides/polar/axes/titles.js +7 -5
- package/src/actions/guides/polar/resolve.js +6 -14
- package/src/actions/index.js +16 -16
- package/src/actions/marks/arc/actions.js +3 -10
- package/src/actions/marks/area/actions.js +8 -5
- package/src/actions/marks/area/materialize.js +8 -9
- package/src/actions/marks/bar/create.js +17 -4
- package/src/actions/marks/bar/edit.js +18 -7
- package/src/actions/marks/bar/materialize.js +9 -6
- package/src/actions/marks/line/actions.js +11 -4
- package/src/actions/marks/line/materialize.js +9 -5
- package/src/actions/marks/point/create.js +8 -1
- package/src/actions/marks/point/edit.js +13 -6
- package/src/actions/marks/point/materialize.js +4 -2
- package/src/actions/marks/rect/actions.js +26 -11
- package/src/actions/marks/remove.js +2 -0
- package/src/actions/marks/rule/actions.js +31 -12
- package/src/actions/marks/text/actions.js +42 -11
- package/src/actions/marks/text/layout.js +3 -0
- package/src/actions/marks/tick/actions.js +23 -8
- package/src/actions/primitives/semanticAction.js +37 -3
- package/src/actions/primitives/semanticValidation/dataset.js +13 -1
- package/src/actions/primitives/semanticValidation/layer.js +26 -2
- package/src/actions/primitives/semanticValidation/scale.js +6 -0
- package/src/actions/regression/create.js +26 -5
- package/src/actions/regression/edit.js +50 -15
- package/src/actions/regression/follow.js +22 -0
- package/src/actions/regression/resolve.js +2 -6
- package/src/actions/scales/channels.js +20 -15
- package/src/actions/scales/consumers/common.js +2 -1
- package/src/actions/scales/consumers/families.js +16 -5
- package/src/actions/scales/create.js +10 -4
- package/src/actions/scales/definitions.js +1 -425
- package/src/actions/scales/edit.js +3 -3
- package/src/actions/scales/editPolicy.js +9 -3
- package/src/actions/scales/materialize.js +1 -1
- package/src/actions/scales/parallel.js +1 -1
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/preview.js +49 -2
- package/src/actions/scales/quantitativeColor.js +3 -3
- package/src/actions/selection/actions.js +1 -13
- package/src/actions/theme/reconcile.js +83 -27
- package/src/actions/theme/state.js +12 -0
- package/src/actions/titles/actions.js +5 -18
- package/src/actions/titles/resolve.js +41 -19
- package/src/actions/violinPlots/edit.js +3 -20
- package/src/core/action.js +3 -2
- package/src/core/compositionState.js +4 -1
- package/src/core/font.js +9 -2
- package/src/core/optionRequirements.js +1 -0
- package/src/core/programState.js +5 -1
- package/src/core/resourceReferences.js +3 -0
- package/src/core/textMetricProfile.js +3 -2
- package/src/core/textMetrics.js +41 -4
- package/src/core/validation.js +26 -0
- package/src/core/vocabulary.js +4 -0
- package/src/grammar/aggregate.js +4 -18
- package/src/grammar/arcs.js +1 -386
- package/src/grammar/areaPaint.js +10 -0
- package/src/grammar/areaSeries.js +18 -10
- package/src/grammar/bars/geometry.js +30 -3
- package/src/grammar/bars/policy.js +12 -1
- package/src/grammar/bin.js +41 -22
- package/src/grammar/bin2d.js +1 -6
- package/src/grammar/boxPlot.js +56 -9
- package/src/grammar/categoryOrder.js +1 -6
- package/src/grammar/complete.js +15 -19
- package/src/grammar/computed.js +92 -25
- package/src/grammar/curveCommands.js +29 -16
- package/src/grammar/datasetSchema.js +382 -0
- package/src/grammar/density.js +53 -5
- package/src/grammar/displayLabels.js +28 -0
- package/src/grammar/ecdf.js +2 -9
- package/src/grammar/facets/dependencies.js +6 -2
- package/src/grammar/facets/guides.js +5 -0
- package/src/grammar/facets/index.js +2 -5
- package/src/grammar/filter.js +133 -39
- package/src/grammar/fold.js +2 -12
- package/src/grammar/gradientProfile.js +1 -6
- package/src/grammar/impute.js +21 -48
- package/src/grammar/interval.js +34 -10
- package/src/grammar/itemMissing.js +59 -0
- package/src/grammar/legendGradient.js +18 -0
- package/src/grammar/legendGroups.js +1 -0
- package/src/grammar/legendTitleStyle.js +22 -0
- package/src/grammar/lineSeries.js +32 -17
- package/src/grammar/markLabels.js +5 -2
- package/src/grammar/normalize.js +4 -54
- package/src/grammar/numeric.js +1 -290
- package/src/grammar/parallelCoordinates.js +1 -6
- package/src/grammar/parseTime.js +41 -0
- package/src/grammar/pathCommands.js +11 -4
- package/src/grammar/polar.js +1 -184
- package/src/grammar/positionCompatibility.js +3 -3
- package/src/grammar/regression/derive.js +63 -16
- package/src/grammar/regression/index.js +1 -0
- package/src/grammar/regression/models.js +116 -59
- package/src/grammar/regression/parameters.js +69 -6
- package/src/grammar/roundedRect.js +42 -54
- package/src/grammar/scales/appearance.js +1 -206
- package/src/grammar/scales/color.js +1 -382
- package/src/grammar/scales/colorConsumers.js +5 -2
- package/src/grammar/scales/definition.js +1 -190
- package/src/grammar/scales/discretized.js +1 -195
- package/src/grammar/scales/size.js +1 -456
- package/src/grammar/scales/temporal.js +2 -13
- package/src/grammar/scales/transformed.js +1 -383
- package/src/grammar/scales/types.js +1 -1
- package/src/grammar/scales/validation.js +1 -66
- package/src/grammar/schemas/concreteGraphic.js +11 -0
- package/src/grammar/schemas/graphic.js +3 -0
- package/src/grammar/schemas/semanticPath.js +5 -3
- package/src/grammar/sort.js +88 -0
- package/src/grammar/stack.js +2 -10
- package/src/grammar/statisticalReference.js +1 -6
- package/src/grammar/summary.js +120 -45
- package/src/grammar/text.js +19 -0
- package/src/grammar/timeUnit.js +16 -7
- package/src/grammar/transformKeys.js +47 -0
- package/src/grammar/transformTopology.js +1 -0
- package/src/grammar/transforms.js +25 -12
- package/src/grammar/valueFormat.js +17 -5
- package/src/grammar/weightedStatistics.js +1 -6
- package/src/grammar/window.js +4 -58
- package/src/inspection.js +495 -0
- package/src/inspectionDescriptors.js +3 -0
- package/src/layout/canvas.js +1 -110
- package/src/layout/facets.js +1 -244
- package/src/layout/labels.js +1 -0
- package/src/layout/legend.js +13 -0
- package/src/layout/legendGroup.js +1 -0
- package/src/layout/legendItems.js +59 -15
- package/src/layout/legendLane.js +1 -441
- package/src/layout/legendText.js +1 -0
- package/src/layout/title.js +35 -13
- package/src/materialization/bars/aggregate.js +11 -5
- package/src/materialization/bars/histogram.js +4 -1
- package/src/materialization/bars/ranged.js +19 -6
- package/src/materialization/bars/resolve.js +26 -3
- package/src/materialization/coordinateBounds.js +8 -0
- package/src/materialization/dependencies.js +4 -44
- package/src/materialization/facetGuides/layout.js +1 -0
- package/src/materialization/facetGuides/placement.js +19 -8
- package/src/materialization/facetGuides/preparation.js +15 -7
- package/src/materialization/facetHeaders.js +1 -347
- package/src/materialization/facets.js +1 -185
- package/src/materialization/graphicHierarchy.js +1 -1
- package/src/materialization/guides/layout.js +1 -57
- package/src/materialization/guides/resources.js +1 -123
- package/src/materialization/layout.js +53 -0
- package/src/materialization/legendGroups.js +1 -0
- package/src/materialization/legendText.js +1 -0
- package/src/materialization/legends.js +1 -1
- package/src/materialization/marks/capabilities.js +5 -6
- package/src/materialization/rect.js +1 -1
- package/src/materialization/revisionIdentity.js +19 -0
- package/src/materialization/scaleGuideDependencies.js +1 -1
- package/src/materialization/scales/map.js +3 -0
- package/src/materialization/scales/policies/arc.js +8 -7
- package/src/materialization/scales/policies/bar.js +19 -7
- package/src/materialization/scales/policies/series.js +2 -1
- package/src/materialization/scales/resolve.js +72 -7
- package/src/materialization/selection/items/arc.js +1 -1
- package/src/materialization/selection/items/bar.js +7 -6
- package/src/materialization/selection/items/path.js +7 -6
- package/src/materialization/selection/items/rect.js +2 -2
- package/src/materialization/text.js +17 -6
- package/src/materialization/textLines.js +15 -0
- package/src/persistence/codec.js +37 -6
- package/src/persistence/validation.js +47 -0
- package/src/persistence.js +94 -8
- package/src/renderers/canvas/circle.js +6 -3
- package/src/renderers/canvas/fill.js +7 -1
- package/src/renderers/canvas/index.js +2 -1
- package/src/renderers/canvas/line.js +1 -0
- package/src/renderers/canvas/path.js +1 -1
- package/src/renderers/canvas/rect.js +11 -8
- package/src/renderers/canvas/stroke.js +2 -1
- package/src/renderers/canvas/text.js +5 -3
- package/src/renderers/svg.js +4 -1
- package/src/selectors/layers.js +19 -0
- package/src/theme/defaults.js +8 -2
- package/src/version.js +1 -1
- package/types/basic.d.ts +1 -0
- package/types/index.d.ts +7 -0
- package/types/inspection.d.ts +17 -0
- package/types/program.d.ts +494 -112
|
@@ -1,382 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { validateGeneratedItemLimit } from "../../core/validation.js";
|
|
3
|
-
import {
|
|
4
|
-
normalizePalette,
|
|
5
|
-
resolveContinuousPalette
|
|
6
|
-
} from "../palettes.js";
|
|
7
|
-
import { inverseLerp } from "../numeric.js";
|
|
8
|
-
|
|
9
|
-
export const CONTINUOUS_COLOR_INTERPOLATIONS = cloneAndFreeze([
|
|
10
|
-
"rgb",
|
|
11
|
-
"hsl",
|
|
12
|
-
"hsl-long",
|
|
13
|
-
"lab",
|
|
14
|
-
"hcl",
|
|
15
|
-
"hcl-long",
|
|
16
|
-
"cubehelix",
|
|
17
|
-
"cubehelix-long"
|
|
18
|
-
]);
|
|
19
|
-
|
|
20
|
-
const BASIC_COLORS = Object.freeze({
|
|
21
|
-
black: "#000000",
|
|
22
|
-
blue: "#0000ff",
|
|
23
|
-
cyan: "#00ffff",
|
|
24
|
-
gray: "#808080",
|
|
25
|
-
green: "#008000",
|
|
26
|
-
grey: "#808080",
|
|
27
|
-
magenta: "#ff00ff",
|
|
28
|
-
navy: "#000080",
|
|
29
|
-
orange: "#ffa500",
|
|
30
|
-
purple: "#800080",
|
|
31
|
-
red: "#ff0000",
|
|
32
|
-
teal: "#008080",
|
|
33
|
-
white: "#ffffff",
|
|
34
|
-
yellow: "#ffff00"
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
function clamp(value, minimum = 0, maximum = 1) {
|
|
38
|
-
return Math.max(minimum, Math.min(maximum, value));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function hue(value) {
|
|
42
|
-
return ((value % 360) + 360) % 360;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function hslToRgb({ h, s, l }) {
|
|
46
|
-
const chroma = (1 - Math.abs(2 * l - 1)) * s;
|
|
47
|
-
const segment = hue(h) / 60;
|
|
48
|
-
const second = chroma * (1 - Math.abs(segment % 2 - 1));
|
|
49
|
-
const [red, green, blue] = segment < 1 ? [chroma, second, 0]
|
|
50
|
-
: segment < 2 ? [second, chroma, 0]
|
|
51
|
-
: segment < 3 ? [0, chroma, second]
|
|
52
|
-
: segment < 4 ? [0, second, chroma]
|
|
53
|
-
: segment < 5 ? [second, 0, chroma]
|
|
54
|
-
: [chroma, 0, second];
|
|
55
|
-
const match = l - chroma / 2;
|
|
56
|
-
return { r: red + match, g: green + match, b: blue + match };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function parseColor(value) {
|
|
60
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
61
|
-
throw new TypeError("Continuous color range requires non-empty color strings.");
|
|
62
|
-
}
|
|
63
|
-
const normalized = BASIC_COLORS[value.toLowerCase()] ?? value.toLowerCase();
|
|
64
|
-
let match = normalized.match(/^#([0-9a-f]{3})$/i);
|
|
65
|
-
if (match) {
|
|
66
|
-
return Object.fromEntries(["r", "g", "b"].map((channel, index) => [
|
|
67
|
-
channel,
|
|
68
|
-
Number.parseInt(match[1][index].repeat(2), 16) / 255
|
|
69
|
-
]));
|
|
70
|
-
}
|
|
71
|
-
match = normalized.match(/^#([0-9a-f]{6})$/i);
|
|
72
|
-
if (match) {
|
|
73
|
-
return {
|
|
74
|
-
r: Number.parseInt(match[1].slice(0, 2), 16) / 255,
|
|
75
|
-
g: Number.parseInt(match[1].slice(2, 4), 16) / 255,
|
|
76
|
-
b: Number.parseInt(match[1].slice(4, 6), 16) / 255
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
match = normalized.match(
|
|
80
|
-
/^rgb\(\s*([\d.]+)%?\s*,\s*([\d.]+)%?\s*,\s*([\d.]+)%?\s*\)$/
|
|
81
|
-
);
|
|
82
|
-
if (match) {
|
|
83
|
-
const percentage = normalized.includes("%");
|
|
84
|
-
const divisor = percentage ? 100 : 255;
|
|
85
|
-
const channels = match.slice(1).map(item => Number(item) / divisor);
|
|
86
|
-
if (channels.every(item => Number.isFinite(item) && item >= 0 && item <= 1)) {
|
|
87
|
-
return { r: channels[0], g: channels[1], b: channels[2] };
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
match = normalized.match(
|
|
91
|
-
/^hsl\(\s*([-\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%\s*\)$/
|
|
92
|
-
);
|
|
93
|
-
if (match) {
|
|
94
|
-
const h = Number(match[1]);
|
|
95
|
-
const s = Number(match[2]) / 100;
|
|
96
|
-
const l = Number(match[3]) / 100;
|
|
97
|
-
if ([h, s, l].every(Number.isFinite) && s >= 0 && s <= 1 && l >= 0 && l <= 1) {
|
|
98
|
-
return hslToRgb({ h, s, l });
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
throw new Error(`Unsupported continuous color "${value}".`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function rgbToHex({ r, g, b }) {
|
|
105
|
-
return `#${[r, g, b].map(channel =>
|
|
106
|
-
Math.round(clamp(channel) * 255).toString(16).padStart(2, "0")
|
|
107
|
-
).join("")}`;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function byteChannels({ r, g, b }) {
|
|
111
|
-
return [r, g, b].map(channel => Math.round(channel * 255));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function rgbToHsl({ r, g, b }) {
|
|
115
|
-
const maximum = Math.max(r, g, b);
|
|
116
|
-
const minimum = Math.min(r, g, b);
|
|
117
|
-
const delta = maximum - minimum;
|
|
118
|
-
const l = (maximum + minimum) / 2;
|
|
119
|
-
if (delta === 0) return { h: 0, s: 0, l };
|
|
120
|
-
const s = delta / (1 - Math.abs(2 * l - 1));
|
|
121
|
-
const raw = maximum === r
|
|
122
|
-
? ((g - b) / delta) % 6
|
|
123
|
-
: maximum === g
|
|
124
|
-
? (b - r) / delta + 2
|
|
125
|
-
: (r - g) / delta + 4;
|
|
126
|
-
return { h: hue(raw * 60), s, l };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function linearRgb(channel) {
|
|
130
|
-
return channel <= 0.04045
|
|
131
|
-
? channel / 12.92
|
|
132
|
-
: ((channel + 0.055) / 1.055) ** 2.4;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function encodedRgb(channel) {
|
|
136
|
-
return channel <= 0.0031308
|
|
137
|
-
? 12.92 * channel
|
|
138
|
-
: 1.055 * channel ** (1 / 2.4) - 0.055;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function rgbToLab(color) {
|
|
142
|
-
const r = linearRgb(color.r);
|
|
143
|
-
const g = linearRgb(color.g);
|
|
144
|
-
const b = linearRgb(color.b);
|
|
145
|
-
const x = (0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / 0.95047;
|
|
146
|
-
const y = 0.2126729 * r + 0.7151522 * g + 0.072175 * b;
|
|
147
|
-
const z = (0.0193339 * r + 0.119192 * g + 0.9503041 * b) / 1.08883;
|
|
148
|
-
const convert = value => value > 216 / 24389
|
|
149
|
-
? Math.cbrt(value)
|
|
150
|
-
: 841 / 108 * value + 4 / 29;
|
|
151
|
-
const fx = convert(x);
|
|
152
|
-
const fy = convert(y);
|
|
153
|
-
const fz = convert(z);
|
|
154
|
-
return { l: 116 * fy - 16, a: 500 * (fx - fy), b: 200 * (fy - fz) };
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function labToRgb({ l, a, b }) {
|
|
158
|
-
const fy = (l + 16) / 116;
|
|
159
|
-
const fx = fy + a / 500;
|
|
160
|
-
const fz = fy - b / 200;
|
|
161
|
-
const convert = value => value ** 3 > 216 / 24389
|
|
162
|
-
? value ** 3
|
|
163
|
-
: 108 / 841 * (value - 4 / 29);
|
|
164
|
-
const x = 0.95047 * convert(fx);
|
|
165
|
-
const y = convert(fy);
|
|
166
|
-
const z = 1.08883 * convert(fz);
|
|
167
|
-
return {
|
|
168
|
-
r: encodedRgb(3.2404542 * x - 1.5371385 * y - 0.4985314 * z),
|
|
169
|
-
g: encodedRgb(-0.969266 * x + 1.8760108 * y + 0.041556 * z),
|
|
170
|
-
b: encodedRgb(0.0556434 * x - 0.2040259 * y + 1.0572252 * z)
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function labToHcl({ l, a, b }) {
|
|
175
|
-
return { h: hue(Math.atan2(b, a) * 180 / Math.PI), c: Math.hypot(a, b), l };
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function hclToLab({ h, c, l }) {
|
|
179
|
-
const radians = h * Math.PI / 180;
|
|
180
|
-
return { l, a: Math.cos(radians) * c, b: Math.sin(radians) * c };
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function rgbToCubehelix({ r, g, b }) {
|
|
184
|
-
const A = -0.14861;
|
|
185
|
-
const B = 1.78277;
|
|
186
|
-
const C = -0.29227;
|
|
187
|
-
const D = -0.90649;
|
|
188
|
-
const E = 1.97294;
|
|
189
|
-
const ED = E * D;
|
|
190
|
-
const EB = E * B;
|
|
191
|
-
const BC_DA = B * C - D * A;
|
|
192
|
-
const l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB);
|
|
193
|
-
const blue = b - l;
|
|
194
|
-
const k = (E * (g - l) - C * blue) / D;
|
|
195
|
-
const denominator = E * l * (1 - l);
|
|
196
|
-
return {
|
|
197
|
-
h: hue(Math.atan2(k, blue) * 180 / Math.PI - 120),
|
|
198
|
-
s: denominator === 0 ? 0 : Math.hypot(k, blue) / denominator,
|
|
199
|
-
l
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function cubehelixToRgb({ h, s, l }) {
|
|
204
|
-
const angle = (h + 120) * Math.PI / 180;
|
|
205
|
-
const amplitude = s * l * (1 - l);
|
|
206
|
-
const cosine = Math.cos(angle);
|
|
207
|
-
const sine = Math.sin(angle);
|
|
208
|
-
return {
|
|
209
|
-
r: l + amplitude * (-0.14861 * cosine + 1.78277 * sine),
|
|
210
|
-
g: l + amplitude * (-0.29227 * cosine - 0.90649 * sine),
|
|
211
|
-
b: l + amplitude * (1.97294 * cosine)
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function interpolateHue(left, right, amount, long) {
|
|
216
|
-
let delta = right - left;
|
|
217
|
-
if (!long) delta = ((delta + 540) % 360) - 180;
|
|
218
|
-
return hue(left + delta * amount);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function mix(left, right, amount, method) {
|
|
222
|
-
if (method.startsWith("hsl")) {
|
|
223
|
-
const a = rgbToHsl(left);
|
|
224
|
-
const b = rgbToHsl(right);
|
|
225
|
-
return hslToRgb({
|
|
226
|
-
h: interpolateHue(a.h, b.h, amount, method.endsWith("-long")),
|
|
227
|
-
s: a.s + (b.s - a.s) * amount,
|
|
228
|
-
l: a.l + (b.l - a.l) * amount
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
if (method === "lab") {
|
|
232
|
-
const a = rgbToLab(left);
|
|
233
|
-
const b = rgbToLab(right);
|
|
234
|
-
return labToRgb({
|
|
235
|
-
l: a.l + (b.l - a.l) * amount,
|
|
236
|
-
a: a.a + (b.a - a.a) * amount,
|
|
237
|
-
b: a.b + (b.b - a.b) * amount
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
if (method.startsWith("hcl")) {
|
|
241
|
-
const a = labToHcl(rgbToLab(left));
|
|
242
|
-
const b = labToHcl(rgbToLab(right));
|
|
243
|
-
return labToRgb(hclToLab({
|
|
244
|
-
h: interpolateHue(a.h, b.h, amount, method.endsWith("-long")),
|
|
245
|
-
c: a.c + (b.c - a.c) * amount,
|
|
246
|
-
l: a.l + (b.l - a.l) * amount
|
|
247
|
-
}));
|
|
248
|
-
}
|
|
249
|
-
const a = rgbToCubehelix(left);
|
|
250
|
-
const b = rgbToCubehelix(right);
|
|
251
|
-
return cubehelixToRgb({
|
|
252
|
-
h: interpolateHue(a.h, b.h, amount, method.endsWith("-long")),
|
|
253
|
-
s: a.s + (b.s - a.s) * amount,
|
|
254
|
-
l: a.l + (b.l - a.l) * amount
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
export function validateContinuousColorInterpolation(value) {
|
|
259
|
-
if (!CONTINUOUS_COLOR_INTERPOLATIONS.includes(value)) {
|
|
260
|
-
throw new Error(`Unsupported continuous color interpolation "${value}".`);
|
|
261
|
-
}
|
|
262
|
-
return value;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
export function validateSequentialColorRange(value) {
|
|
266
|
-
if (value === "auto") return value;
|
|
267
|
-
if (Array.isArray(value)) {
|
|
268
|
-
validateGeneratedItemLimit(value.length, "Sequential color range length");
|
|
269
|
-
if (value.length < 2) {
|
|
270
|
-
throw new TypeError("Sequential color range requires at least two colors.");
|
|
271
|
-
}
|
|
272
|
-
value.forEach(parseColor);
|
|
273
|
-
return cloneAndFreeze(value);
|
|
274
|
-
}
|
|
275
|
-
if (
|
|
276
|
-
!isPlainObject(value) ||
|
|
277
|
-
Object.keys(value).length !== 1 ||
|
|
278
|
-
!Object.hasOwn(value, "palette")
|
|
279
|
-
) {
|
|
280
|
-
throw new TypeError("Sequential color range requires colors or a palette descriptor.");
|
|
281
|
-
}
|
|
282
|
-
const palette = normalizePalette(value.palette);
|
|
283
|
-
if (palette.count !== undefined && palette.count < 2) {
|
|
284
|
-
throw new RangeError(
|
|
285
|
-
"Sequential palette count must be an integer of at least 2."
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
return cloneAndFreeze({ palette });
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
export function resolveSequentialColorStops(value) {
|
|
292
|
-
const range = validateSequentialColorRange(value);
|
|
293
|
-
if (range === "auto") return resolveContinuousPalette("viridis");
|
|
294
|
-
return Array.isArray(range)
|
|
295
|
-
? cloneAndFreeze(range.map(color => rgbToHex(parseColor(color))))
|
|
296
|
-
: resolveContinuousPalette(range.palette);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function interpolateColors(colors, position, method, shouldClamp) {
|
|
300
|
-
const bounded = shouldClamp ? clamp(position) : position;
|
|
301
|
-
const scaled = bounded * (colors.length - 1);
|
|
302
|
-
const left = Math.max(0, Math.min(colors.length - 2, Math.floor(scaled)));
|
|
303
|
-
const right = left + 1;
|
|
304
|
-
const amount = scaled - left;
|
|
305
|
-
if (method === "rgb") {
|
|
306
|
-
const leftChannels = byteChannels(colors[left]);
|
|
307
|
-
const rightChannels = byteChannels(colors[right]);
|
|
308
|
-
return `#${leftChannels.map((channel, index) =>
|
|
309
|
-
Math.round(clamp(
|
|
310
|
-
channel + (rightChannels[index] - channel) * amount,
|
|
311
|
-
0,
|
|
312
|
-
255
|
|
313
|
-
)).toString(16).padStart(2, "0")
|
|
314
|
-
).join("")}`;
|
|
315
|
-
}
|
|
316
|
-
return rgbToHex(mix(colors[left], colors[right], amount, method));
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
export function interpolateColorStops(stops, position, interpolation = "rgb") {
|
|
320
|
-
const method = validateContinuousColorInterpolation(interpolation);
|
|
321
|
-
if (!Array.isArray(stops) || stops.length < 2) {
|
|
322
|
-
throw new TypeError("Color interpolation requires at least two stops.");
|
|
323
|
-
}
|
|
324
|
-
const colors = stops.map(parseColor);
|
|
325
|
-
return interpolateColors(colors, position, method, true);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
export function validateSequentialMidpoint(value, type, domain = "auto") {
|
|
329
|
-
if (value === undefined || value === "auto") return undefined;
|
|
330
|
-
if (type !== "sequential") {
|
|
331
|
-
throw new Error(`Scale type "${type}" does not support midpoint.`);
|
|
332
|
-
}
|
|
333
|
-
if (!Number.isFinite(value)) {
|
|
334
|
-
throw new TypeError("Scale midpoint must be finite or auto.");
|
|
335
|
-
}
|
|
336
|
-
if (domain !== "auto" && (
|
|
337
|
-
!Array.isArray(domain) || domain.length !== 2 || !domain.every(Number.isFinite) ||
|
|
338
|
-
!(value > Math.min(...domain) && value < Math.max(...domain))
|
|
339
|
-
)) {
|
|
340
|
-
throw new RangeError("Scale midpoint must lie strictly inside its resolved domain.");
|
|
341
|
-
}
|
|
342
|
-
return value;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
export function mapSequentialColors(
|
|
346
|
-
values,
|
|
347
|
-
domain,
|
|
348
|
-
stops,
|
|
349
|
-
options = {}
|
|
350
|
-
) {
|
|
351
|
-
const { interpolation = "rgb", clamp: shouldClamp = false } = options;
|
|
352
|
-
const hasUnknown = Object.hasOwn(options, "unknown");
|
|
353
|
-
if (!Array.isArray(domain) || domain.length !== 2 || !domain.every(Number.isFinite)) {
|
|
354
|
-
throw new TypeError("Sequential color domain must contain two finite numbers.");
|
|
355
|
-
}
|
|
356
|
-
if (!Array.isArray(values) || (!hasUnknown && !values.every(Number.isFinite))) {
|
|
357
|
-
throw new TypeError("Sequential color values must be finite numbers.");
|
|
358
|
-
}
|
|
359
|
-
if (typeof shouldClamp !== "boolean") {
|
|
360
|
-
throw new TypeError("Sequential color clamp must be a boolean.");
|
|
361
|
-
}
|
|
362
|
-
const midpoint = validateSequentialMidpoint(options.midpoint, "sequential", domain);
|
|
363
|
-
const [start, end] = domain;
|
|
364
|
-
const constant = start === end;
|
|
365
|
-
const method = validateContinuousColorInterpolation(interpolation);
|
|
366
|
-
if (!Array.isArray(stops) || stops.length < 2) {
|
|
367
|
-
throw new TypeError("Color interpolation requires at least two stops.");
|
|
368
|
-
}
|
|
369
|
-
const colors = stops.map(parseColor);
|
|
370
|
-
return cloneAndFreeze(values.map(value => {
|
|
371
|
-
if (!Number.isFinite(value)) {
|
|
372
|
-
if (hasUnknown) return options.unknown;
|
|
373
|
-
throw new TypeError("Sequential color values must be finite numbers.");
|
|
374
|
-
}
|
|
375
|
-
const raw = midpoint === undefined
|
|
376
|
-
? constant ? 0.5 : inverseLerp(value, start, end)
|
|
377
|
-
: (end > start ? value <= midpoint : value >= midpoint)
|
|
378
|
-
? inverseLerp(value, start, midpoint) / 2
|
|
379
|
-
: 0.5 + inverseLerp(value, midpoint, end) / 2;
|
|
380
|
-
return interpolateColors(colors, raw, method, shouldClamp);
|
|
381
|
-
}));
|
|
382
|
-
}
|
|
1
|
+
import{mapTransformedValues as $}from"./transformed.js";import{cloneAndFreeze as p,isPlainObject as k}from"../../core/immutable.js";import{validateGeneratedItemLimit as F}from"../../core/validation.js";import{normalizePalette as P,resolveContinuousPalette as T}from"../palettes.js";import{inverseLerp as w}from"../numeric.js";const B=p(["rgb","hsl","hsl-long","lab","hcl","hcl-long","cubehelix","cubehelix-long"]),D=Object.freeze({black:"#000000",blue:"#0000ff",cyan:"#00ffff",gray:"#808080",green:"#008000",grey:"#808080",magenta:"#ff00ff",navy:"#000080",orange:"#ffa500",purple:"#800080",red:"#ff0000",teal:"#008080",white:"#ffffff",yellow:"#ffff00"});function M(t,r=0,n=1){return Math.max(r,Math.min(n,t))}function m(t){return(t%360+360)%360}function x({h:t,s:r,l:n}){const e=(1-Math.abs(2*n-1))*r,s=m(t)/60,i=e*(1-Math.abs(s%2-1)),[o,c,l]=s<1?[e,i,0]:s<2?[i,e,0]:s<3?[0,e,i]:s<4?[0,i,e]:s<5?[i,0,e]:[e,0,i],a=n-e/2;return{r:o+a,g:c+a,b:l+a}}function y(t){if(typeof t!="string"||t.length===0)throw new TypeError("Continuous color range requires non-empty color strings.");const r=D[t.toLowerCase()]??t.toLowerCase();let n=r.match(/^#([0-9a-f]{3})$/i);if(n)return Object.fromEntries(["r","g","b"].map((e,s)=>[e,Number.parseInt(n[1][s].repeat(2),16)/255]));if(n=r.match(/^#([0-9a-f]{6})$/i),n)return{r:Number.parseInt(n[1].slice(0,2),16)/255,g:Number.parseInt(n[1].slice(2,4),16)/255,b:Number.parseInt(n[1].slice(4,6),16)/255};if(n=r.match(/^rgb\(\s*([\d.]+)%?\s*,\s*([\d.]+)%?\s*,\s*([\d.]+)%?\s*\)$/),n){const s=r.includes("%")?100:255,i=n.slice(1).map(o=>Number(o)/s);if(i.every(o=>Number.isFinite(o)&&o>=0&&o<=1))return{r:i[0],g:i[1],b:i[2]}}if(n=r.match(/^hsl\(\s*([-\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%\s*\)$/),n){const e=Number(n[1]),s=Number(n[2])/100,i=Number(n[3])/100;if([e,s,i].every(Number.isFinite)&&s>=0&&s<=1&&i>=0&&i<=1)return x({h:e,s,l:i})}throw new Error(`Unsupported continuous color "${t}".`)}function q({r:t,g:r,b:n}){return`#${[t,r,n].map(e=>Math.round(M(e)*255).toString(16).padStart(2,"0")).join("")}`}function N({r:t,g:r,b:n}){return[t,r,n].map(e=>Math.round(e*255))}function A({r:t,g:r,b:n}){const e=Math.max(t,r,n),s=Math.min(t,r,n),i=e-s,o=(e+s)/2;if(i===0)return{h:0,s:0,l:o};const c=i/(1-Math.abs(2*o-1)),l=e===t?(r-n)/i%6:e===r?(n-t)/i+2:(t-r)/i+4;return{h:m(l*60),s:c,l:o}}function C(t){return t<=.04045?t/12.92:((t+.055)/1.055)**2.4}function E(t){return t<=.0031308?12.92*t:1.055*t**(1/2.4)-.055}function d(t){const r=C(t.r),n=C(t.g),e=C(t.b),s=(.4124564*r+.3575761*n+.1804375*e)/.95047,i=.2126729*r+.7151522*n+.072175*e,o=(.0193339*r+.119192*n+.9503041*e)/1.08883,c=u=>u>216/24389?Math.cbrt(u):841/108*u+4/29,l=c(s),a=c(i),f=c(o);return{l:116*a-16,a:500*(l-a),b:200*(a-f)}}function O({l:t,a:r,b:n}){const e=(t+16)/116,s=e+r/500,i=e-n/200,o=f=>f**3>216/24389?f**3:108/841*(f-4/29),c=.95047*o(s),l=o(e),a=1.08883*o(i);return{r:E(3.2404542*c-1.5371385*l-.4985314*a),g:E(-.969266*c+1.8760108*l+.041556*a),b:E(.0556434*c-.2040259*l+1.0572252*a)}}function I({l:t,a:r,b:n}){return{h:m(Math.atan2(n,r)*180/Math.PI),c:Math.hypot(r,n),l:t}}function U({h:t,c:r,l:n}){const e=t*Math.PI/180;return{l:n,a:Math.cos(e)*r,b:Math.sin(e)*r}}function R({r:t,g:r,b:n}){const l=-1.7884503806,a=1.97294*1.78277,f=1.78277*-.29227- -.90649*-.14861,u=(f*n+l*t-a*r)/(f+l-a),b=n-u,h=(1.97294*(r-u)- -.29227*b)/-.90649,g=1.97294*u*(1-u);return{h:m(Math.atan2(h,b)*180/Math.PI-120),s:g===0?0:Math.hypot(h,b)/g,l:u}}function W({h:t,s:r,l:n}){const e=(t+120)*Math.PI/180,s=r*n*(1-n),i=Math.cos(e),o=Math.sin(e);return{r:n+s*(-.14861*i+1.78277*o),g:n+s*(-.29227*i-.90649*o),b:n+s*(1.97294*i)}}function S(t,r,n,e){let s=r-t;return e||(s=(s+540)%360-180),m(t+s*n)}function H(t,r,n,e){if(e.startsWith("hsl")){const o=A(t),c=A(r);return x({h:S(o.h,c.h,n,e.endsWith("-long")),s:o.s+(c.s-o.s)*n,l:o.l+(c.l-o.l)*n})}if(e==="lab"){const o=d(t),c=d(r);return O({l:o.l+(c.l-o.l)*n,a:o.a+(c.a-o.a)*n,b:o.b+(c.b-o.b)*n})}if(e.startsWith("hcl")){const o=I(d(t)),c=I(d(r));return O(U({h:S(o.h,c.h,n,e.endsWith("-long")),c:o.c+(c.c-o.c)*n,l:o.l+(c.l-o.l)*n}))}const s=R(t),i=R(r);return W({h:S(s.h,i.h,n,e.endsWith("-long")),s:s.s+(i.s-s.s)*n,l:s.l+(i.l-s.l)*n})}function j(t){if(!B.includes(t))throw new Error(`Unsupported continuous color interpolation "${t}".`);return t}function _(t){if(t==="auto")return t;if(Array.isArray(t)){if(F(t.length,"Sequential color range length"),t.length<2)throw new TypeError("Sequential color range requires at least two colors.");return t.forEach(y),p(t)}if(!k(t)||Object.keys(t).length!==1||!Object.hasOwn(t,"palette"))throw new TypeError("Sequential color range requires colors or a palette descriptor.");const r=P(t.palette);if(r.count!==void 0&&r.count<2)throw new RangeError("Sequential palette count must be an integer of at least 2.");return p({palette:r})}function Y(t){const r=_(t);return r==="auto"?T("viridis"):Array.isArray(r)?p(r.map(n=>q(y(n)))):T(r.palette)}function z(t,r,n,e){const i=(e?M(r):r)*(t.length-1),o=Math.max(0,Math.min(t.length-2,Math.floor(i))),c=o+1,l=i-o;if(n==="rgb"){const a=N(t[o]),f=N(t[c]);return`#${a.map((u,b)=>Math.round(M(u+(f[b]-u)*l,0,255)).toString(16).padStart(2,"0")).join("")}`}return q(H(t[o],t[c],l,n))}function Z(t,r,n="rgb"){const e=j(n);if(!Array.isArray(t)||t.length<2)throw new TypeError("Color interpolation requires at least two stops.");const s=t.map(y);return z(s,r,e,!0)}function L(t,r,n="auto"){if(!(t===void 0||t==="auto")){if(r!=="sequential")throw new Error(`Scale type "${r}" does not support midpoint.`);if(!Number.isFinite(t))throw new TypeError("Scale midpoint must be finite or auto.");if(n!=="auto"&&(!Array.isArray(n)||n.length!==2||!n.every(Number.isFinite)||!(t>Math.min(...n)&&t<Math.max(...n))))throw new RangeError("Scale midpoint must lie strictly inside its resolved domain.");return t}}function G(t,r,n,e={}){const{interpolation:s="rgb",clamp:i=!1}=e;if(["log","sqrt","symlog"].includes(e.type)){L(e.midpoint,e.type,r);const h=$(t,r,[0,1],{type:e.type,clamp:i,...e.type==="log"?{base:e.base}:{},...e.type==="symlog"?{constant:e.constant}:{},...Object.hasOwn(e,"unknown")?{unknown:NaN}:{}});return G(h,[0,1],n,{...e,type:"sequential"})}const o=Object.hasOwn(e,"unknown");if(!Array.isArray(r)||r.length!==2||!r.every(Number.isFinite))throw new TypeError("Sequential color domain must contain two finite numbers.");if(!Array.isArray(t)||!o&&!t.every(Number.isFinite))throw new TypeError("Sequential color values must be finite numbers.");if(typeof i!="boolean")throw new TypeError("Sequential color clamp must be a boolean.");const c=L(e.midpoint,"sequential",r),[l,a]=r,f=l===a,u=j(s);if(!Array.isArray(n)||n.length<2)throw new TypeError("Color interpolation requires at least two stops.");const b=n.map(y);return p(t.map(h=>{if(!Number.isFinite(h)){if(o)return e.unknown;throw new TypeError("Sequential color values must be finite numbers.")}const g=c===void 0?f?.5:w(h,l,a):(a>l?h<=c:h>=c)?w(h,l,c)/2:.5+w(h,c,a)/2;return z(b,g,u,i)}))}export{B as CONTINUOUS_COLOR_INTERPOLATIONS,Z as interpolateColorStops,G as mapSequentialColors,Y as resolveSequentialColorStops,j as validateContinuousColorInterpolation,_ as validateSequentialColorRange,L as validateSequentialMidpoint};
|
|
@@ -7,6 +7,9 @@ export function validateContinuousColorConsumer(layer, encoding, scale, {
|
|
|
7
7
|
channel = "color"
|
|
8
8
|
} = {}) {
|
|
9
9
|
const kind = layer.mark?.type;
|
|
10
|
+
if (["log", "sqrt", "symlog"].includes(scale.type) && encoding.fieldType !== "quantitative") {
|
|
11
|
+
throw new Error(`Scale type "${scale.type}" requires quantitative color.`);
|
|
12
|
+
}
|
|
10
13
|
if (channel === "stroke") {
|
|
11
14
|
if (!["point", "line", "area", "bar", "rect", "arc", "rule", "tick"].includes(kind)) {
|
|
12
15
|
throw new Error("Continuous stroke requires a supported graphical mark consumer.");
|
|
@@ -23,8 +26,8 @@ export function validateContinuousColorConsumer(layer, encoding, scale, {
|
|
|
23
26
|
}
|
|
24
27
|
return undefined;
|
|
25
28
|
}
|
|
26
|
-
if (!["point", "bar", "rect"].includes(kind)) {
|
|
27
|
-
throw new Error("Continuous color requires a Point, aggregate Bar, or
|
|
29
|
+
if (!["point", "bar", "rect", "text"].includes(kind)) {
|
|
30
|
+
throw new Error("Continuous color requires a Point, aggregate Bar, Rect, or Text consumer.");
|
|
28
31
|
}
|
|
29
32
|
if (!["quantitative", "temporal"].includes(encoding.fieldType) ||
|
|
30
33
|
(isDiscretizedColorScaleType(scale.type) && encoding.fieldType !== "quantitative")) {
|
|
@@ -1,190 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { validateContinuousColorInterpolation, validateSequentialMidpoint } from "./color.js";
|
|
3
|
-
import { isTransformedScaleType } from "./mapping.js";
|
|
4
|
-
import {
|
|
5
|
-
normalizeTransformParameters,
|
|
6
|
-
validateTransformedDomain
|
|
7
|
-
} from "./transformed.js";
|
|
8
|
-
import {
|
|
9
|
-
isContinuousColorScaleType,
|
|
10
|
-
validateScalePropertyForType
|
|
11
|
-
} from "./types.js";
|
|
12
|
-
|
|
13
|
-
const BOOLEAN_PROPERTIES = Object.freeze([
|
|
14
|
-
"nice", "zero", "clamp", "reverse"
|
|
15
|
-
]);
|
|
16
|
-
const TYPE_PARAMETERS = Object.freeze(["base", "exponent", "constant"]);
|
|
17
|
-
|
|
18
|
-
function retainedValue(previous, patch, defaults, property, typeChanged) {
|
|
19
|
-
if (Object.hasOwn(patch, property)) return patch[property];
|
|
20
|
-
if (!typeChanged && Object.hasOwn(previous, property)) {
|
|
21
|
-
return previous[property];
|
|
22
|
-
}
|
|
23
|
-
return defaults[property];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function validateBandParameters(definition, previous, patch, defaults, typeChanged) {
|
|
27
|
-
const hasPadding = Object.hasOwn(patch, "padding");
|
|
28
|
-
if (
|
|
29
|
-
hasPadding &&
|
|
30
|
-
(Object.hasOwn(patch, "paddingInner") || Object.hasOwn(patch, "paddingOuter"))
|
|
31
|
-
) {
|
|
32
|
-
throw new Error(
|
|
33
|
-
"Scale padding cannot be combined with paddingInner or paddingOuter."
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
if (
|
|
37
|
-
hasPadding &&
|
|
38
|
-
(!Number.isFinite(patch.padding) || patch.padding < 0 || patch.padding >= 1)
|
|
39
|
-
) {
|
|
40
|
-
throw new RangeError(
|
|
41
|
-
"Scale padding must be from 0 (inclusive) to 1 (exclusive)."
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
const normalizedPatch = hasPadding
|
|
45
|
-
? { ...patch, paddingInner: patch.padding, paddingOuter: patch.padding }
|
|
46
|
-
: patch;
|
|
47
|
-
const paddingInner = retainedValue(
|
|
48
|
-
previous, normalizedPatch, defaults, "paddingInner", typeChanged
|
|
49
|
-
) ?? 0;
|
|
50
|
-
const paddingOuter = retainedValue(
|
|
51
|
-
previous, normalizedPatch, defaults, "paddingOuter", typeChanged
|
|
52
|
-
) ?? 0;
|
|
53
|
-
const align = retainedValue(
|
|
54
|
-
previous, normalizedPatch, defaults, "align", typeChanged
|
|
55
|
-
) ?? 0.5;
|
|
56
|
-
if (!Number.isFinite(paddingInner) || paddingInner < 0 || paddingInner >= 1) {
|
|
57
|
-
throw new RangeError(
|
|
58
|
-
"Scale paddingInner must be from 0 (inclusive) to 1 (exclusive)."
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
if (!Number.isFinite(paddingOuter) || paddingOuter < 0) {
|
|
62
|
-
throw new RangeError("Scale paddingOuter must be non-negative and finite.");
|
|
63
|
-
}
|
|
64
|
-
if (!Number.isFinite(align) || align < 0 || align > 1) {
|
|
65
|
-
throw new RangeError("Scale align must be between 0 and 1.");
|
|
66
|
-
}
|
|
67
|
-
Object.assign(definition, { paddingInner, paddingOuter, align });
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function validatePointParameters(definition, previous, patch, defaults, typeChanged) {
|
|
71
|
-
const padding = retainedValue(
|
|
72
|
-
previous, patch, defaults, "padding", typeChanged
|
|
73
|
-
) ?? 0.5;
|
|
74
|
-
const align = retainedValue(previous, patch, defaults, "align", typeChanged) ?? 0.5;
|
|
75
|
-
if (!Number.isFinite(padding) || padding < 0) {
|
|
76
|
-
throw new RangeError("Scale padding must be non-negative and finite.");
|
|
77
|
-
}
|
|
78
|
-
if (!Number.isFinite(align) || align < 0 || align > 1) {
|
|
79
|
-
throw new RangeError("Scale align must be between 0 and 1.");
|
|
80
|
-
}
|
|
81
|
-
Object.assign(definition, { padding, align });
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function normalizeScaleDefinition({
|
|
85
|
-
type,
|
|
86
|
-
previous = {},
|
|
87
|
-
patch = {},
|
|
88
|
-
defaults = {},
|
|
89
|
-
retainCoreOnTypeChange = false,
|
|
90
|
-
retainCompatibleOnTypeChange = false,
|
|
91
|
-
allowOrdinalBandParameters = false,
|
|
92
|
-
validateDomain,
|
|
93
|
-
validateRange
|
|
94
|
-
}) {
|
|
95
|
-
const typeChanged = previous.type !== undefined && previous.type !== type;
|
|
96
|
-
const rawDomain = Object.hasOwn(patch, "domain")
|
|
97
|
-
? patch.domain
|
|
98
|
-
: (!typeChanged || retainCoreOnTypeChange) && Object.hasOwn(previous, "domain")
|
|
99
|
-
? previous.domain
|
|
100
|
-
: defaults.domain ?? "auto";
|
|
101
|
-
const rawRange = Object.hasOwn(patch, "range")
|
|
102
|
-
? patch.range
|
|
103
|
-
: (!typeChanged || retainCoreOnTypeChange) && Object.hasOwn(previous, "range")
|
|
104
|
-
? previous.range
|
|
105
|
-
: defaults.range ?? "auto";
|
|
106
|
-
const definition = {
|
|
107
|
-
type,
|
|
108
|
-
domain: validateDomain(type, rawDomain),
|
|
109
|
-
range: validateRange(type, rawRange)
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
for (const property of BOOLEAN_PROPERTIES) {
|
|
113
|
-
let value = retainedValue(previous, patch, defaults, property, typeChanged);
|
|
114
|
-
if (
|
|
115
|
-
value === undefined &&
|
|
116
|
-
typeChanged &&
|
|
117
|
-
retainCompatibleOnTypeChange &&
|
|
118
|
-
Object.hasOwn(previous, property)
|
|
119
|
-
) {
|
|
120
|
-
try {
|
|
121
|
-
validateScalePropertyForType(type, property);
|
|
122
|
-
value = previous[property];
|
|
123
|
-
} catch {
|
|
124
|
-
value = undefined;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (value === undefined) continue;
|
|
128
|
-
if (typeof value !== "boolean") {
|
|
129
|
-
throw new TypeError(`Scale ${property} must be a boolean.`);
|
|
130
|
-
}
|
|
131
|
-
validateScalePropertyForType(type, property);
|
|
132
|
-
definition[property] = value;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const requested = Object.fromEntries(TYPE_PARAMETERS.flatMap(property => {
|
|
136
|
-
const value = retainedValue(previous, patch, defaults, property, typeChanged);
|
|
137
|
-
return value === undefined ? [] : [[property, value]];
|
|
138
|
-
}));
|
|
139
|
-
if (isTransformedScaleType(type)) {
|
|
140
|
-
const parameters = normalizeTransformParameters(type, requested);
|
|
141
|
-
if (type === "log") definition.base = parameters.base;
|
|
142
|
-
if (type === "pow") definition.exponent = parameters.exponent;
|
|
143
|
-
if (type === "symlog") definition.constant = parameters.constant;
|
|
144
|
-
if (definition.domain !== "auto") {
|
|
145
|
-
validateTransformedDomain(type, definition.domain, requested);
|
|
146
|
-
}
|
|
147
|
-
} else {
|
|
148
|
-
for (const property of Object.keys(requested)) {
|
|
149
|
-
validateScalePropertyForType(type, property);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const interpolate = retainedValue(
|
|
154
|
-
previous, patch, defaults, "interpolate", typeChanged
|
|
155
|
-
);
|
|
156
|
-
if (isContinuousColorScaleType(type)) {
|
|
157
|
-
definition.interpolate = validateContinuousColorInterpolation(
|
|
158
|
-
interpolate ?? "rgb"
|
|
159
|
-
);
|
|
160
|
-
} else if (interpolate !== undefined) {
|
|
161
|
-
throw new Error(`Scale type "${type}" does not support interpolate.`);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const midpoint = validateSequentialMidpoint(
|
|
165
|
-
retainedValue(previous, patch, defaults, "midpoint", typeChanged), type, definition.domain
|
|
166
|
-
);
|
|
167
|
-
if (midpoint !== undefined) definition.midpoint = midpoint;
|
|
168
|
-
|
|
169
|
-
if (type === "band" || (type === "ordinal" && allowOrdinalBandParameters)) {
|
|
170
|
-
validateBandParameters(definition, previous, patch, defaults, typeChanged);
|
|
171
|
-
} else if (type === "point") {
|
|
172
|
-
validatePointParameters(definition, previous, patch, defaults, typeChanged);
|
|
173
|
-
} else {
|
|
174
|
-
for (const property of ["paddingInner", "paddingOuter", "padding", "align"]) {
|
|
175
|
-
if (Object.hasOwn(patch, property)) {
|
|
176
|
-
validateScalePropertyForType(type, property);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const radialMapping = Object.hasOwn(patch, "radialMapping") ? patch.radialMapping : previous.radialMapping;
|
|
182
|
-
if (radialMapping !== undefined) {
|
|
183
|
-
definition.radialMapping = radialMapping;
|
|
184
|
-
validateMeasuredRadiusScale({ ...definition,
|
|
185
|
-
...(Object.hasOwn(patch, "unknown") || Object.hasOwn(previous, "unknown")
|
|
186
|
-
? { unknown: patch.unknown ?? previous.unknown } : {})
|
|
187
|
-
}, { allowAuto: true });
|
|
188
|
-
}
|
|
189
|
-
return definition;
|
|
190
|
-
}
|
|
1
|
+
import{validateMeasuredRadiusScale as I}from"./radial.js";import{validateContinuousColorInterpolation as M,validateSequentialMidpoint as x}from"./color.js";import{isTransformedScaleType as T}from"./mapping.js";import{normalizeTransformParameters as D,validateTransformedDomain as F}from"./transformed.js";import{isContinuousColorScaleType as N,validateScalePropertyForType as O}from"./types.js";const k=Object.freeze(["nice","zero","clamp","reverse"]),z=Object.freeze(["base","exponent","constant"]);function m(i,t,n,e,o){return Object.hasOwn(t,e)?t[e]:!o&&Object.hasOwn(i,e)?i[e]:n[e]}function q(i,t,n,e,o){const l=Object.hasOwn(n,"padding");if(l&&(Object.hasOwn(n,"paddingInner")||Object.hasOwn(n,"paddingOuter")))throw new Error("Scale padding cannot be combined with paddingInner or paddingOuter.");if(l&&(!Number.isFinite(n.padding)||n.padding<0||n.padding>=1))throw new RangeError("Scale padding must be from 0 (inclusive) to 1 (exclusive).");const d=l?{...n,paddingInner:n.padding,paddingOuter:n.padding}:n,g=m(t,d,e,"paddingInner",o)??0,w=m(t,d,e,"paddingOuter",o)??0,c=m(t,d,e,"align",o)??.5;if(!Number.isFinite(g)||g<0||g>=1)throw new RangeError("Scale paddingInner must be from 0 (inclusive) to 1 (exclusive).");if(!Number.isFinite(w)||w<0)throw new RangeError("Scale paddingOuter must be non-negative and finite.");if(!Number.isFinite(c)||c<0||c>1)throw new RangeError("Scale align must be between 0 and 1.");Object.assign(i,{paddingInner:g,paddingOuter:w,align:c})}function A(i,t,n,e,o){const l=m(t,n,e,"padding",o)??.5,d=m(t,n,e,"align",o)??.5;if(!Number.isFinite(l)||l<0)throw new RangeError("Scale padding must be non-negative and finite.");if(!Number.isFinite(d)||d<0||d>1)throw new RangeError("Scale align must be between 0 and 1.");Object.assign(i,{padding:l,align:d})}function V({type:i,color:t=!1,previous:n={},patch:e={},defaults:o={},retainCoreOnTypeChange:l=!1,retainCompatibleOnTypeChange:d=!1,allowOrdinalBandParameters:g=!1,validateDomain:w,validateRange:c}){const s=n.type!==void 0&&n.type!==i,R=Object.hasOwn(e,"domain")?e.domain:(!s||l)&&Object.hasOwn(n,"domain")?n.domain:o.domain??"auto",P=Object.hasOwn(e,"range")?e.range:(!s||l)&&Object.hasOwn(n,"range")?n.range:o.range??"auto",r={type:i,domain:w(i,R),range:c(i,P)},u=m(n,e,o,"emptyDomain",s);if(u!==void 0){if(!["preserve","require-explicit"].includes(u))throw new Error('Scale emptyDomain must be "preserve" or "require-explicit".');r.emptyDomain=u}for(const a of k){let f=m(n,e,o,a,s);if(f===void 0&&s&&d&&Object.hasOwn(n,a))try{O(i,a),f=n[a]}catch{f=void 0}if(f!==void 0){if(typeof f!="boolean")throw new TypeError(`Scale ${a} must be a boolean.`);O(i,a),r[a]=f}}const j=Object.fromEntries(z.flatMap(a=>{const f=m(n,e,o,a,s);return f===void 0?[]:[[a,f]]}));if(T(i)){const a=D(i,j);i==="log"&&(r.base=a.base),i==="pow"&&(r.exponent=a.exponent),i==="symlog"&&(r.constant=a.constant),r.domain!=="auto"&&F(i,r.domain,j)}else for(const a of Object.keys(j))O(i,a);let b=m(n,e,o,"interpolate",s);if(i==="sequential"||t&&N(i))b===void 0&&d&&(b=n.interpolate),r.interpolate=M(b??"rgb");else if(b!==void 0)throw new Error(`Scale type "${i}" does not support interpolate.`);const S=x(m(n,e,o,"midpoint",s),i,r.domain);if(S!==void 0&&(r.midpoint=S),i==="band"||i==="ordinal"&&g)q(r,n,e,o,s);else if(i==="point")A(r,n,e,o,s);else for(const a of["paddingInner","paddingOuter","padding","align"])Object.hasOwn(e,a)&&O(i,a);const E=Object.hasOwn(e,"radialMapping")?e.radialMapping:n.radialMapping;return E!==void 0&&(r.radialMapping=E,I({...r,...Object.hasOwn(e,"unknown")||Object.hasOwn(n,"unknown")?{unknown:e.unknown??n.unknown}:{}},{allowAuto:!0})),r}export{V as normalizeScaleDefinition};
|