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,5 +1,5 @@
|
|
|
1
1
|
import { cloneAndFreeze } from "../../core/immutable.js";
|
|
2
|
-
import { utcTimestamp, validTimestamp } from "../timeUnit.js";
|
|
2
|
+
import { utcTimestamp, validTimestamp, offsetUtcTimeUnit } from "../timeUnit.js";
|
|
3
3
|
|
|
4
4
|
const TIME_UNITS = [
|
|
5
5
|
{ span: 2 * 365 * 24 * 60 * 60 * 1000, unit: "year" },
|
|
@@ -56,17 +56,6 @@ function floorUtc(timestamp, unit) {
|
|
|
56
56
|
return Math.floor(timestamp / 1000) * 1000;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function offsetUtc(timestamp, unit) {
|
|
60
|
-
const date = new Date(timestamp);
|
|
61
|
-
if (unit === "year") return utcTimestamp(date.getUTCFullYear() + 1);
|
|
62
|
-
if (unit === "month") {
|
|
63
|
-
return utcTimestamp(date.getUTCFullYear(), date.getUTCMonth() + 1);
|
|
64
|
-
}
|
|
65
|
-
if (unit === "day") return timestamp + 24 * 60 * 60 * 1000;
|
|
66
|
-
if (unit === "hour") return timestamp + 60 * 60 * 1000;
|
|
67
|
-
if (unit === "minute") return timestamp + 60 * 1000;
|
|
68
|
-
return timestamp + 1000;
|
|
69
|
-
}
|
|
70
59
|
|
|
71
60
|
export function niceTimeDomain(domain) {
|
|
72
61
|
const [minimum, maximum] = requireTimeDomain(domain);
|
|
@@ -74,7 +63,7 @@ export function niceTimeDomain(domain) {
|
|
|
74
63
|
const unit = TIME_UNITS.find(item => maximum - minimum >= item.span).unit;
|
|
75
64
|
const start = floorUtc(minimum, unit);
|
|
76
65
|
const endFloor = floorUtc(maximum, unit);
|
|
77
|
-
const end = endFloor === maximum ? maximum :
|
|
66
|
+
const end = endFloor === maximum ? maximum : offsetUtcTimeUnit(endFloor, unit);
|
|
78
67
|
if (![start, end].every(validTimestamp)) {
|
|
79
68
|
throw new RangeError("Nice time domain exceeds the supported date range.");
|
|
80
69
|
}
|
|
@@ -1,383 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { MAX_GENERATED_ITEMS } from "../../core/validation.js";
|
|
3
|
-
import { interpolateNumber, inverseLerp, numericExtent } from "../numeric.js";
|
|
4
|
-
import { niceTicks } from "../ticks.js";
|
|
5
|
-
import { niceLinearDomain } from "./continuous.js";
|
|
6
|
-
import {
|
|
7
|
-
SCALE_ROLES,
|
|
8
|
-
validateScaleTypeForRole
|
|
9
|
-
} from "./types.js";
|
|
10
|
-
import { validatePair } from "./validation.js";
|
|
11
|
-
const TRANSFORMED_TYPES = Object.freeze(["log", "pow", "sqrt", "symlog"]);
|
|
12
|
-
|
|
13
|
-
function positiveFinite(value, label) {
|
|
14
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
15
|
-
throw new RangeError(`${label} must be a positive finite number.`);
|
|
16
|
-
}
|
|
17
|
-
return value;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function normalizeTransformParameters(type, options = {}) {
|
|
21
|
-
if (!TRANSFORMED_TYPES.includes(type) && type !== "linear") {
|
|
22
|
-
throw new Error(`Scale type "${type}" is not a quantitative transform.`);
|
|
23
|
-
}
|
|
24
|
-
if (!isPlainObject(options)) {
|
|
25
|
-
throw new TypeError("Scale transform options must be a plain object.");
|
|
26
|
-
}
|
|
27
|
-
const allowed = type === "log"
|
|
28
|
-
? ["base"]
|
|
29
|
-
: type === "pow"
|
|
30
|
-
? ["exponent"]
|
|
31
|
-
: type === "symlog" ? ["constant"] : [];
|
|
32
|
-
for (const key of Object.keys(options)) {
|
|
33
|
-
if (!allowed.includes(key)) {
|
|
34
|
-
throw new Error(`Scale type "${type}" does not support ${key}.`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
if (type === "log") {
|
|
38
|
-
const base = positiveFinite(options.base ?? 10, "Log scale base");
|
|
39
|
-
if (base === 1) throw new RangeError("Log scale base must not equal 1.");
|
|
40
|
-
return cloneAndFreeze({ base });
|
|
41
|
-
}
|
|
42
|
-
if (type === "pow") {
|
|
43
|
-
return cloneAndFreeze({
|
|
44
|
-
exponent: positiveFinite(options.exponent ?? 1, "Power scale exponent")
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
if (type === "sqrt") return cloneAndFreeze({ exponent: 0.5 });
|
|
48
|
-
if (type === "symlog") {
|
|
49
|
-
return cloneAndFreeze({
|
|
50
|
-
constant: positiveFinite(options.constant ?? 1, "Symlog scale constant")
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
return cloneAndFreeze({});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function signedTransformProportion(value, start, end, transform) {
|
|
57
|
-
const transformed = [start, end, value].map(item =>
|
|
58
|
-
Math.sign(item) * transform(Math.abs(item))
|
|
59
|
-
);
|
|
60
|
-
return transformed[0] !== transformed[1] &&
|
|
61
|
-
transformed.every(Number.isFinite)
|
|
62
|
-
? inverseLerp(transformed[2], transformed[0], transformed[1])
|
|
63
|
-
: undefined;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function powerProportion(value, domain, exponent) {
|
|
67
|
-
const [start, end] = domain;
|
|
68
|
-
const direct = signedTransformProportion(
|
|
69
|
-
value, start, end, magnitude => magnitude ** exponent
|
|
70
|
-
);
|
|
71
|
-
if (direct !== undefined) return direct;
|
|
72
|
-
const scale = Math.max(Math.abs(start), Math.abs(end));
|
|
73
|
-
const normalized = signedTransformProportion(
|
|
74
|
-
value,
|
|
75
|
-
start,
|
|
76
|
-
end,
|
|
77
|
-
magnitude => magnitude === 0 ? 0 : Math.exp(exponent * (
|
|
78
|
-
Math.log(magnitude) - Math.log(scale)
|
|
79
|
-
))
|
|
80
|
-
);
|
|
81
|
-
if (normalized !== undefined) return normalized;
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
start !== 0 &&
|
|
85
|
-
end !== 0 &&
|
|
86
|
-
value !== 0 &&
|
|
87
|
-
Math.sign(start) === Math.sign(end)
|
|
88
|
-
) {
|
|
89
|
-
const endLogRatio = Math.log(Math.abs(end)) - Math.log(Math.abs(start));
|
|
90
|
-
const valueLogRatio = Math.log(Math.abs(value)) - Math.log(Math.abs(start));
|
|
91
|
-
if (Math.abs(exponent * endLogRatio) < 1e-8) {
|
|
92
|
-
return valueLogRatio / endLogRatio;
|
|
93
|
-
}
|
|
94
|
-
const proportion = Math.expm1(exponent * valueLogRatio) /
|
|
95
|
-
Math.expm1(exponent * endLogRatio);
|
|
96
|
-
if (Number.isFinite(proportion)) return proportion;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return inverseLerp(value, start, end);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function logOnePlusRatio(value, constant) {
|
|
103
|
-
if (value === 0) return 0;
|
|
104
|
-
const ratio = Math.abs(value) / constant;
|
|
105
|
-
if (Number.isFinite(ratio)) return Math.sign(value) * Math.log1p(ratio);
|
|
106
|
-
const logRatio = Math.log(Math.abs(value)) - Math.log(constant);
|
|
107
|
-
return Math.sign(value) * (
|
|
108
|
-
Math.max(0, logRatio) + Math.log1p(Math.exp(-Math.abs(logRatio)))
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function transformedProportion(value, domain, type, parameters) {
|
|
113
|
-
const [start, end] = domain;
|
|
114
|
-
if (type === "linear") return inverseLerp(value, start, end);
|
|
115
|
-
if (type === "log") {
|
|
116
|
-
const logarithm = Math.log10(parameters.base);
|
|
117
|
-
const direct = signedTransformProportion(
|
|
118
|
-
value, start, end, magnitude => Math.log10(magnitude) / logarithm
|
|
119
|
-
);
|
|
120
|
-
return direct ?? signedTransformProportion(
|
|
121
|
-
value, start, end, Math.log
|
|
122
|
-
) ?? inverseLerp(value, start, end);
|
|
123
|
-
}
|
|
124
|
-
if (type === "pow" || type === "sqrt") {
|
|
125
|
-
return powerProportion(value, domain, parameters.exponent);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const transformedStart = logOnePlusRatio(start, parameters.constant);
|
|
129
|
-
const transformedEnd = logOnePlusRatio(end, parameters.constant);
|
|
130
|
-
if (transformedStart === transformedEnd) {
|
|
131
|
-
return inverseLerp(value, start, end);
|
|
132
|
-
}
|
|
133
|
-
const proportion = inverseLerp(
|
|
134
|
-
logOnePlusRatio(value, parameters.constant),
|
|
135
|
-
transformedStart,
|
|
136
|
-
transformedEnd
|
|
137
|
-
);
|
|
138
|
-
return Number.isFinite(proportion) ? proportion : inverseLerp(value, start, end);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function validateTransformedDomain(type, domain, options = {}) {
|
|
142
|
-
validateScaleTypeForRole(type, SCALE_ROLES.quantitativePosition);
|
|
143
|
-
const validated = validatePair(domain, "Transformed scale domain");
|
|
144
|
-
if (validated[0] === validated[1]) {
|
|
145
|
-
throw new RangeError("Transformed scale domain values must be distinct.");
|
|
146
|
-
}
|
|
147
|
-
if (
|
|
148
|
-
type === "log" &&
|
|
149
|
-
(validated.includes(0) || Math.sign(validated[0]) !== Math.sign(validated[1]))
|
|
150
|
-
) {
|
|
151
|
-
throw new RangeError(
|
|
152
|
-
"Log scale domain must be strictly positive or strictly negative."
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
normalizeTransformParameters(type, options);
|
|
156
|
-
return validated;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function radixPower(radix, exponent, logarithm) {
|
|
160
|
-
const direct = Number.isFinite(radix) ? radix ** exponent : Number.NaN;
|
|
161
|
-
if (Number.isFinite(direct) && direct > 0) return direct;
|
|
162
|
-
const transformed = exponent * logarithm;
|
|
163
|
-
if (transformed > Math.log(Number.MAX_VALUE)) return Number.POSITIVE_INFINITY;
|
|
164
|
-
if (transformed < Math.log(Number.MIN_VALUE)) return 0;
|
|
165
|
-
return Math.exp(transformed);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function logDomainInfo(domain, base) {
|
|
169
|
-
const low = Math.min(...domain);
|
|
170
|
-
const high = Math.max(...domain);
|
|
171
|
-
const negative = high < 0;
|
|
172
|
-
return [
|
|
173
|
-
base < 1 ? -Math.log(base) : Math.log(base),
|
|
174
|
-
base < 1 ? 1 / base : base,
|
|
175
|
-
domain[0] > domain[1],
|
|
176
|
-
negative,
|
|
177
|
-
negative ? Math.abs(high) : low,
|
|
178
|
-
negative ? Math.abs(low) : high
|
|
179
|
-
];
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function niceLogDomain(domain, base) {
|
|
183
|
-
const [logarithm, radix, reversed, negative, minimum, maximum] =
|
|
184
|
-
logDomainInfo(domain, base);
|
|
185
|
-
let lower = radixPower(
|
|
186
|
-
radix,
|
|
187
|
-
Math.floor(Math.log(minimum) / logarithm),
|
|
188
|
-
logarithm
|
|
189
|
-
);
|
|
190
|
-
let upper = radixPower(
|
|
191
|
-
radix,
|
|
192
|
-
Math.ceil(Math.log(maximum) / logarithm),
|
|
193
|
-
logarithm
|
|
194
|
-
);
|
|
195
|
-
if (!Number.isFinite(lower) || lower <= 0 || lower > minimum) lower = minimum;
|
|
196
|
-
if (!Number.isFinite(upper) || upper < maximum) upper = maximum;
|
|
197
|
-
const result = negative ? [-upper, -lower] : [lower, upper];
|
|
198
|
-
return cloneAndFreeze(reversed ? result.reverse() : result);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function padAutomaticTransformedDomain(type, value, options) {
|
|
202
|
-
const parameters = normalizeTransformParameters(type, options);
|
|
203
|
-
if (value === 0) {
|
|
204
|
-
if (type === "log") {
|
|
205
|
-
throw new RangeError(
|
|
206
|
-
"Log scale domain must be strictly positive or strictly negative."
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
return [-1, 1];
|
|
210
|
-
}
|
|
211
|
-
const magnitude = Math.abs(value);
|
|
212
|
-
let lower = magnitude / 2;
|
|
213
|
-
let upper = magnitude * 2;
|
|
214
|
-
if (type === "pow" || type === "sqrt") {
|
|
215
|
-
upper = magnitude * (2 - 2 ** -parameters.exponent) **
|
|
216
|
-
(1 / parameters.exponent);
|
|
217
|
-
} else if (type === "symlog") {
|
|
218
|
-
const center = Math.abs(logOnePlusRatio(value, parameters.constant));
|
|
219
|
-
if (center === 0) return [-1, 1];
|
|
220
|
-
upper = parameters.constant * Math.expm1(
|
|
221
|
-
2 * center - logOnePlusRatio(lower, parameters.constant)
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
if (!(lower < magnitude && upper > magnitude && Number.isFinite(upper))) {
|
|
225
|
-
lower = magnitude / 2;
|
|
226
|
-
upper = Number.isFinite(magnitude * 2) ? magnitude * 2 : magnitude;
|
|
227
|
-
}
|
|
228
|
-
if (type === "log" && lower === 0) lower = magnitude;
|
|
229
|
-
return value > 0
|
|
230
|
-
? [lower, upper]
|
|
231
|
-
: [-upper, lower === 0 ? 0 : -lower];
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
export function resolveTransformedDomain({
|
|
235
|
-
type,
|
|
236
|
-
domain = "auto",
|
|
237
|
-
values,
|
|
238
|
-
nice = false,
|
|
239
|
-
zero = false,
|
|
240
|
-
...options
|
|
241
|
-
}) {
|
|
242
|
-
validateScaleTypeForRole(type, SCALE_ROLES.quantitativePosition);
|
|
243
|
-
if (!Array.isArray(values) || !values.every(Number.isFinite)) {
|
|
244
|
-
throw new TypeError("Transformed scale values must be finite numbers.");
|
|
245
|
-
}
|
|
246
|
-
if (typeof nice !== "boolean" || typeof zero !== "boolean") {
|
|
247
|
-
throw new TypeError("Scale nice and zero must be booleans.");
|
|
248
|
-
}
|
|
249
|
-
if (type === "log" && zero) {
|
|
250
|
-
throw new Error('Scale type "log" does not support zero.');
|
|
251
|
-
}
|
|
252
|
-
const explicit = domain !== "auto";
|
|
253
|
-
if (explicit) return validateTransformedDomain(type, domain, options);
|
|
254
|
-
if (values.length === 0) {
|
|
255
|
-
throw new Error("Cannot infer an automatic scale domain from no values.");
|
|
256
|
-
}
|
|
257
|
-
let resolved = numericExtent(values);
|
|
258
|
-
if (zero) resolved = [Math.min(0, resolved[0]), Math.max(0, resolved[1])];
|
|
259
|
-
if (resolved[0] === resolved[1]) {
|
|
260
|
-
resolved = padAutomaticTransformedDomain(type, resolved[0], options);
|
|
261
|
-
}
|
|
262
|
-
validateTransformedDomain(type, resolved, options);
|
|
263
|
-
if (nice) {
|
|
264
|
-
resolved = type === "log"
|
|
265
|
-
? niceLogDomain(resolved, normalizeTransformParameters(type, options).base)
|
|
266
|
-
: niceLinearDomain(resolved);
|
|
267
|
-
}
|
|
268
|
-
return validateTransformedDomain(type, resolved, options);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
export function resolveMappingRange(range, { reverse = false } = {}) {
|
|
272
|
-
const validated = validatePair(range, "Scale range");
|
|
273
|
-
if (typeof reverse !== "boolean") {
|
|
274
|
-
throw new TypeError("Scale reverse must be a boolean.");
|
|
275
|
-
}
|
|
276
|
-
return reverse
|
|
277
|
-
? cloneAndFreeze([...validated].reverse())
|
|
278
|
-
: validated;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
export function mapTransformedValues(
|
|
282
|
-
values,
|
|
283
|
-
domain,
|
|
284
|
-
range,
|
|
285
|
-
{ type = "linear", clamp = false, reverse = false, ...options } = {}
|
|
286
|
-
) {
|
|
287
|
-
if (!Array.isArray(values)) {
|
|
288
|
-
throw new TypeError("Scale values must be an array.");
|
|
289
|
-
}
|
|
290
|
-
if (typeof clamp !== "boolean") {
|
|
291
|
-
throw new TypeError("Scale clamp must be a boolean.");
|
|
292
|
-
}
|
|
293
|
-
const hasUnknown = Object.hasOwn(options, "unknown");
|
|
294
|
-
const { unknown, ...transformOptions } = options;
|
|
295
|
-
const parameters = normalizeTransformParameters(type, transformOptions);
|
|
296
|
-
const validatedDomain = validateTransformedDomain(type, domain, transformOptions);
|
|
297
|
-
const resolvedRange = resolveMappingRange(range, { reverse });
|
|
298
|
-
const span = resolvedRange[1] - resolvedRange[0];
|
|
299
|
-
return cloneAndFreeze(values.map(value => {
|
|
300
|
-
const valid = Number.isFinite(value) && !(type === "log" && (
|
|
301
|
-
value === 0 || Math.sign(value) !== Math.sign(validatedDomain[0])
|
|
302
|
-
));
|
|
303
|
-
if (!valid) {
|
|
304
|
-
if (hasUnknown) return unknown;
|
|
305
|
-
throw new TypeError(`Scale type "${type}" received an invalid value.`);
|
|
306
|
-
}
|
|
307
|
-
let proportion = transformedProportion(
|
|
308
|
-
value,
|
|
309
|
-
validatedDomain,
|
|
310
|
-
type,
|
|
311
|
-
parameters
|
|
312
|
-
);
|
|
313
|
-
if (clamp) proportion = Math.max(0, Math.min(1, proportion));
|
|
314
|
-
const direct = resolvedRange[0] + proportion * span;
|
|
315
|
-
return Number.isFinite(span) && Number.isFinite(direct) && (
|
|
316
|
-
proportion !== 1 ||
|
|
317
|
-
Math.abs(direct - resolvedRange[1]) <=
|
|
318
|
-
Number.EPSILON * Math.abs(resolvedRange[1])
|
|
319
|
-
)
|
|
320
|
-
? direct
|
|
321
|
-
: interpolateNumber(resolvedRange[0], resolvedRange[1], proportion);
|
|
322
|
-
}));
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
export function transformedTicks(type, domain, count, options = {}) {
|
|
326
|
-
const validated = validateTransformedDomain(type, domain, options);
|
|
327
|
-
if (!Number.isInteger(count) || count <= 0) {
|
|
328
|
-
throw new RangeError("Tick count must be a positive integer.");
|
|
329
|
-
}
|
|
330
|
-
if (type !== "log") return niceTicks(validated, count);
|
|
331
|
-
const { base } = normalizeTransformParameters(type, options);
|
|
332
|
-
const [
|
|
333
|
-
logarithm,
|
|
334
|
-
radix,
|
|
335
|
-
reversed,
|
|
336
|
-
negative,
|
|
337
|
-
minimumMagnitude,
|
|
338
|
-
maximumMagnitude
|
|
339
|
-
] = logDomainInfo(validated, base);
|
|
340
|
-
const start = Math.ceil(
|
|
341
|
-
Math.log(minimumMagnitude) / logarithm - 1e-12
|
|
342
|
-
);
|
|
343
|
-
const end = Math.floor(
|
|
344
|
-
Math.log(maximumMagnitude) / logarithm + 1e-12
|
|
345
|
-
);
|
|
346
|
-
const available = end - start + 1;
|
|
347
|
-
const tickCount = available <= MAX_GENERATED_ITEMS
|
|
348
|
-
? available
|
|
349
|
-
: Math.min(count, MAX_GENERATED_ITEMS);
|
|
350
|
-
const values = [];
|
|
351
|
-
for (let index = 0; index < tickCount; index += 1) {
|
|
352
|
-
const exponent = available <= MAX_GENERATED_ITEMS
|
|
353
|
-
? start + index
|
|
354
|
-
: Math.round(start + (end - start) * index / Math.max(1, tickCount - 1));
|
|
355
|
-
const magnitude = radixPower(radix, exponent, logarithm);
|
|
356
|
-
const value = (negative ? -1 : 1) * magnitude;
|
|
357
|
-
if (
|
|
358
|
-
Number.isFinite(value) &&
|
|
359
|
-
magnitude >= minimumMagnitude &&
|
|
360
|
-
magnitude <= maximumMagnitude &&
|
|
361
|
-
(values.length === 0 || value !== values.at(-1))
|
|
362
|
-
) {
|
|
363
|
-
values.push(value);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
if (negative) values.reverse();
|
|
367
|
-
if (reversed) values.reverse();
|
|
368
|
-
return cloneAndFreeze(values.length === 0 ? [...validated] : values);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
export function formatTransformedTick(type, value) {
|
|
372
|
-
if (type !== "log") return String(value);
|
|
373
|
-
const magnitude = Math.abs(value);
|
|
374
|
-
const units = [
|
|
375
|
-
[1_000_000_000, "B"],
|
|
376
|
-
[1_000_000, "M"],
|
|
377
|
-
[1_000, "K"]
|
|
378
|
-
];
|
|
379
|
-
const unit = units.find(([threshold]) => magnitude >= threshold);
|
|
380
|
-
if (unit === undefined) return String(value);
|
|
381
|
-
const scaled = value / unit[0];
|
|
382
|
-
return `${Number(scaled.toPrecision(3))}${unit[1]}`;
|
|
383
|
-
}
|
|
1
|
+
import{cloneAndFreeze as h,isPlainObject as I}from"../../core/immutable.js";import{MAX_GENERATED_ITEMS as R}from"../../core/validation.js";import{interpolateNumber as D,inverseLerp as b,numericExtent as q}from"../numeric.js";import{niceTicks as $}from"../ticks.js";import{niceLinearDomain as j}from"./continuous.js";import{SCALE_ROLES as L,validateScaleTypeForRole as A}from"./types.js";import{validatePair as _}from"./validation.js";const z=Object.freeze(["log","pow","sqrt","symlog"]);function v(t,e){if(!Number.isFinite(t)||t<=0)throw new RangeError(`${e} must be a positive finite number.`);return t}function p(t,e={}){if(!z.includes(t)&&t!=="linear")throw new Error(`Scale type "${t}" is not a quantitative transform.`);if(!I(e))throw new TypeError("Scale transform options must be a plain object.");const o=t==="log"?["base"]:t==="pow"?["exponent"]:t==="symlog"?["constant"]:[];for(const n of Object.keys(e))if(!o.includes(n))throw new Error(`Scale type "${t}" does not support ${n}.`);if(t==="log"){const n=v(e.base??10,"Log scale base");if(n===1)throw new RangeError("Log scale base must not equal 1.");return h({base:n})}return t==="pow"?h({exponent:v(e.exponent??1,"Power scale exponent")}):t==="sqrt"?h({exponent:.5}):t==="symlog"?h({constant:v(e.constant??1,"Symlog scale constant")}):h({})}function E(t,e,o,n){const r=[e,o,t].map(i=>Math.sign(i)*n(Math.abs(i)));return r[0]!==r[1]&&r.every(Number.isFinite)?b(r[2],r[0],r[1]):void 0}function V(t,e,o){const[n,r]=e,i=E(t,n,r,c=>c**o);if(i!==void 0)return i;const s=Math.max(Math.abs(n),Math.abs(r)),a=E(t,n,r,c=>c===0?0:Math.exp(o*(Math.log(c)-Math.log(s))));if(a!==void 0)return a;if(n!==0&&r!==0&&t!==0&&Math.sign(n)===Math.sign(r)){const c=Math.log(Math.abs(r))-Math.log(Math.abs(n)),f=Math.log(Math.abs(t))-Math.log(Math.abs(n));if(Math.abs(o*c)<1e-8)return f/c;const u=Math.expm1(o*f)/Math.expm1(o*c);if(Number.isFinite(u))return u}return b(t,n,r)}function N(t,e){if(t===0)return 0;const o=Math.abs(t)/e;if(Number.isFinite(o))return Math.sign(t)*Math.log1p(o);const n=Math.log(Math.abs(t))-Math.log(e);return Math.sign(t)*(Math.max(0,n)+Math.log1p(Math.exp(-Math.abs(n))))}function C(t,e,o,n){const[r,i]=e;if(o==="linear")return b(t,r,i);if(o==="log"){const f=Math.log10(n.base);return E(t,r,i,d=>Math.log10(d)/f)??E(t,r,i,Math.log)??b(t,r,i)}if(o==="pow"||o==="sqrt")return V(t,e,n.exponent);const s=N(r,n.constant),a=N(i,n.constant);if(s===a)return b(t,r,i);const c=b(N(t,n.constant),s,a);return Number.isFinite(c)?c:b(t,r,i)}function x(t,e,o={}){A(t,L.quantitativePosition);const n=_(e,"Transformed scale domain");if(n[0]===n[1])throw new RangeError("Transformed scale domain values must be distinct.");if(t==="log"&&(n.includes(0)||Math.sign(n[0])!==Math.sign(n[1])))throw new RangeError("Log scale domain must be strictly positive or strictly negative.");return p(t,o),n}function P(t,e,o){const n=Number.isFinite(t)?t**e:Number.NaN;if(Number.isFinite(n)&&n>0)return n;const r=e*o;return r>Math.log(Number.MAX_VALUE)?Number.POSITIVE_INFINITY:r<Math.log(Number.MIN_VALUE)?0:Math.exp(r)}function O(t,e){const o=Math.min(...t),n=Math.max(...t),r=n<0;return[e<1?-Math.log(e):Math.log(e),e<1?1/e:e,t[0]>t[1],r,r?Math.abs(n):o,r?Math.abs(o):n]}function U(t,e){const[o,n,r,i,s,a]=O(t,e);let c=P(n,Math.floor(Math.log(s)/o),o),f=P(n,Math.ceil(Math.log(a)/o),o);(!Number.isFinite(c)||c<=0||c>s)&&(c=s),(!Number.isFinite(f)||f<a)&&(f=a);const u=i?[-f,-c]:[c,f];return h(r?u.reverse():u)}function X(t,e,o){const n=p(t,o);if(e===0){if(t==="log")throw new RangeError("Log scale domain must be strictly positive or strictly negative.");return[-1,1]}const r=Math.abs(e);let i=r/2,s=r*2;if(t==="pow"||t==="sqrt")s=r*(2-2**-n.exponent)**(1/n.exponent);else if(t==="symlog"){const a=Math.abs(N(e,n.constant));if(a===0)return[-1,1];s=n.constant*Math.expm1(2*a-N(i,n.constant))}return i<r&&s>r&&Number.isFinite(s)||(i=r/2,s=Number.isFinite(r*2)?r*2:r),t==="log"&&i===0&&(i=r),e>0?[i,s]:[-s,i===0?0:-i]}function W({type:t,domain:e="auto",values:o,nice:n=!1,zero:r=!1,...i}){if(A(t,L.quantitativePosition),!Array.isArray(o)||!o.every(Number.isFinite))throw new TypeError("Transformed scale values must be finite numbers.");if(typeof n!="boolean"||typeof r!="boolean")throw new TypeError("Scale nice and zero must be booleans.");if(t==="log"&&r)throw new Error('Scale type "log" does not support zero.');if(e!=="auto")return x(t,e,i);if(o.length===0)throw new Error("Cannot infer an automatic scale domain from no values.");let a=q(o);return r&&(a=[Math.min(0,a[0]),Math.max(0,a[1])]),a[0]===a[1]&&(a=X(t,a[0],i)),x(t,a,i),n&&(a=t==="log"?U(a,p(t,i).base):j(a)),x(t,a,i)}function Y(t,{reverse:e=!1}={}){const o=_(t,"Scale range");if(typeof e!="boolean")throw new TypeError("Scale reverse must be a boolean.");return e?h([...o].reverse()):o}function Z(t,e,o,{type:n="linear",clamp:r=!1,reverse:i=!1,...s}={}){if(!Array.isArray(t))throw new TypeError("Scale values must be an array.");if(typeof r!="boolean")throw new TypeError("Scale clamp must be a boolean.");const a=Object.hasOwn(s,"unknown"),{unknown:c,...f}=s,u=p(n,f),d=x(n,e,f),l=Y(o,{reverse:i}),w=l[1]-l[0];return h(t.map(g=>{if(!(Number.isFinite(g)&&!(n==="log"&&(g===0||Math.sign(g)!==Math.sign(d[0]))))){if(a)return c;throw new TypeError(`Scale type "${n}" received an invalid value.`)}let m=C(g,d,n,u);r&&(m=Math.max(0,Math.min(1,m)));const M=l[0]+m*w;return Number.isFinite(w)&&Number.isFinite(M)&&(m!==1||Math.abs(M-l[1])<=Number.EPSILON*Math.abs(l[1]))?M:D(l[0],l[1],m)}))}function tt(t,e,o,n={}){const r=x(t,e,n);if(!Number.isInteger(o)||o<=0)throw new RangeError("Tick count must be a positive integer.");if(t!=="log")return $(r,o);const{base:i}=p(t,n),[s,a,c,f,u,d]=O(r,i),l=Math.ceil(Math.log(u)/s-1e-12),w=Math.floor(Math.log(d)/s+1e-12),g=w-l+1,T=g<=R?g:Math.min(o,R),m=[];for(let M=0;M<T;M+=1){const k=g<=R?l+M:Math.round(l+(w-l)*M/Math.max(1,T-1)),S=P(a,k,s),F=(f?-1:1)*S;Number.isFinite(F)&&S>=u&&S<=d&&(m.length===0||F!==m.at(-1))&&m.push(F)}return f&&m.reverse(),c&&m.reverse(),h(m.length===0?[...r]:m)}function nt(t,e){if(t!=="log")return String(e);const o=Math.abs(e),r=[[1e9,"B"],[1e6,"M"],[1e3,"K"]].find(([s])=>o>=s);if(r===void 0)return String(e);const i=e/r[0];return`${Number(i.toPrecision(3))}${r[1]}`}export{nt as formatTransformedTick,Z as mapTransformedValues,p as normalizeTransformParameters,Y as resolveMappingRange,W as resolveTransformedDomain,tt as transformedTicks,x as validateTransformedDomain};
|
|
@@ -16,7 +16,7 @@ export const SCALE_TYPES_BY_ROLE = Object.freeze({
|
|
|
16
16
|
[SCALE_ROLES.bandPosition]: Object.freeze(["band"]),
|
|
17
17
|
[SCALE_ROLES.pointPosition]: Object.freeze(["point"]),
|
|
18
18
|
[SCALE_ROLES.discreteAppearance]: Object.freeze(["ordinal"]),
|
|
19
|
-
[SCALE_ROLES.continuousColor]: Object.freeze(["sequential"]),
|
|
19
|
+
[SCALE_ROLES.continuousColor]: Object.freeze(["sequential", "log", "sqrt", "symlog"]),
|
|
20
20
|
[SCALE_ROLES.discretizedColor]: Object.freeze([
|
|
21
21
|
"quantize", "quantile", "threshold"
|
|
22
22
|
])
|
|
@@ -1,66 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { POSITION_CHANNELS } from "../../core/vocabulary.js";
|
|
3
|
-
import { validateCompleteScaleType } from "./types.js";
|
|
4
|
-
|
|
5
|
-
export function validatePair(value, label) {
|
|
6
|
-
if (
|
|
7
|
-
!Array.isArray(value) ||
|
|
8
|
-
value.length !== 2 ||
|
|
9
|
-
!value.every(Number.isFinite)
|
|
10
|
-
) {
|
|
11
|
-
throw new TypeError(`${label} must be "auto" or two finite numbers.`);
|
|
12
|
-
}
|
|
13
|
-
return cloneAndFreeze(value);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function validatePositionChannel(channel) {
|
|
17
|
-
if (!POSITION_CHANNELS.includes(channel)) {
|
|
18
|
-
throw new Error(`Unknown position channel "${channel}".`);
|
|
19
|
-
}
|
|
20
|
-
return channel;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function validateScaleType(type) {
|
|
24
|
-
return validateCompleteScaleType(type);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function validateSemanticScaleType(type) {
|
|
28
|
-
return validateCompleteScaleType(type);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function validateLinearScaleType(type) {
|
|
32
|
-
if (type !== "linear") {
|
|
33
|
-
throw new Error(`Unsupported position scale type "${type}".`);
|
|
34
|
-
}
|
|
35
|
-
return type;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function validateTimeScaleType(type) {
|
|
39
|
-
if (type !== "time") {
|
|
40
|
-
throw new Error(`Unsupported temporal scale type "${type}".`);
|
|
41
|
-
}
|
|
42
|
-
return type;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function validateOrdinalScaleType(type) {
|
|
46
|
-
if (type !== "ordinal") {
|
|
47
|
-
throw new Error(`Unsupported color scale type "${type}".`);
|
|
48
|
-
}
|
|
49
|
-
return type;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function validateScaleDomain(domain) {
|
|
53
|
-
return domain === "auto" ? domain : validatePair(domain, "Scale domain");
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function validateScaleRange(range) {
|
|
57
|
-
return range === "auto" ? range : validatePair(range, "Scale range");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function validateSemanticScaleDomain(domain) {
|
|
61
|
-
if (domain === "auto") return domain;
|
|
62
|
-
if (!Array.isArray(domain) || domain.length === 0) {
|
|
63
|
-
throw new TypeError("Scale domain must be \"auto\" or a non-empty array.");
|
|
64
|
-
}
|
|
65
|
-
return cloneAndFreeze(domain);
|
|
66
|
-
}
|
|
1
|
+
import{cloneAndFreeze as o}from"../../core/immutable.js";import{POSITION_CHANNELS as c}from"../../core/vocabulary.js";import{validateCompleteScaleType as i}from"./types.js";function u(r,e,t=1){if(!Array.isArray(r)||r.length<t||!r.every(Number.isFinite))throw new TypeError(`${e} must contain finite numbers.`);return r}function s(r,e){if(u(r,e),r.some((t,n)=>n>0&&t<=r[n-1]))throw new RangeError(`${e} must be strictly increasing.`);return r}function a(r,e){if(!Array.isArray(r)||r.length!==2||!r.every(Number.isFinite))throw new TypeError(`${e} must be "auto" or two finite numbers.`);return o(r)}function m(r){if(!c.includes(r))throw new Error(`Unknown position channel "${r}".`);return r}function y(r){return i(r)}function w(r){return i(r)}function S(r){if(r!=="linear")throw new Error(`Unsupported position scale type "${r}".`);return r}function d(r){if(r!=="time")throw new Error(`Unsupported temporal scale type "${r}".`);return r}function h(r){if(r!=="ordinal")throw new Error(`Unsupported color scale type "${r}".`);return r}function x(r){return r==="auto"?r:a(r,"Scale domain")}function T(r){return r==="auto"?r:a(r,"Scale range")}function A(r){if(r==="auto")return r;if(!Array.isArray(r)||r.length===0)throw new TypeError('Scale domain must be "auto" or a non-empty array.');return o(r)}export{u as validateFiniteScaleArray,s as validateIncreasingScaleArray,S as validateLinearScaleType,h as validateOrdinalScaleType,a as validatePair,m as validatePositionChannel,x as validateScaleDomain,T as validateScaleRange,y as validateScaleType,A as validateSemanticScaleDomain,w as validateSemanticScaleType,d as validateTimeScaleType};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { validateGeneratedItemLimit } from "../../core/validation.js";
|
|
2
|
+
import { validateFontStyle } from "../../core/font.js";
|
|
1
3
|
import { isPlainObject } from "../../core/immutable.js";
|
|
2
4
|
import { validatePathCommands } from "../pathCommands.js";
|
|
3
5
|
import { validateFillPaint } from "../paint.js";
|
|
@@ -25,6 +27,14 @@ const TEXT_BASELINES = new Set([
|
|
|
25
27
|
]);
|
|
26
28
|
|
|
27
29
|
export function validateConcreteGraphicValue(type, property, value) {
|
|
30
|
+
if (property === "lines") {
|
|
31
|
+
if (!Array.isArray(value) || value.length === 0 || value.some(line =>
|
|
32
|
+
!isPlainObject(line) || Object.keys(line).some(key => !["text", "x", "y"].includes(key)) ||
|
|
33
|
+
typeof line.text !== "string" || !Number.isFinite(line.x) || !Number.isFinite(line.y))) {
|
|
34
|
+
throw new TypeError("Text lines require non-empty resolved text/x/y records.");
|
|
35
|
+
}
|
|
36
|
+
validateGeneratedItemLimit(value.length, "Text line count");
|
|
37
|
+
}
|
|
28
38
|
if (property === "fill") {
|
|
29
39
|
if (typeof value === "string") {
|
|
30
40
|
validateFillPaint(value, `${type}.fill`);
|
|
@@ -60,6 +70,7 @@ export function validateConcreteGraphicValue(type, property, value) {
|
|
|
60
70
|
)) {
|
|
61
71
|
throw new TypeError("text.fontWeight must be a non-empty string or finite number.");
|
|
62
72
|
}
|
|
73
|
+
if (property === "fontStyle") validateFontStyle(value);
|
|
63
74
|
if (property === "textAlign" && !TEXT_ALIGNS.has(value)) {
|
|
64
75
|
throw new Error(`Unsupported text.textAlign "${value}".`);
|
|
65
76
|
}
|
|
@@ -33,6 +33,7 @@ const GRAPHIC_PROPERTIES = Object.freeze({
|
|
|
33
33
|
"fontSize",
|
|
34
34
|
"fontFamily",
|
|
35
35
|
"fontWeight",
|
|
36
|
+
"fontStyle",
|
|
36
37
|
"text",
|
|
37
38
|
"textAlign",
|
|
38
39
|
"textBaseline",
|
|
@@ -85,10 +86,12 @@ const GRAPHIC_PROPERTIES = Object.freeze({
|
|
|
85
86
|
"x",
|
|
86
87
|
"y",
|
|
87
88
|
"text",
|
|
89
|
+
"lines",
|
|
88
90
|
"fill",
|
|
89
91
|
"fontSize",
|
|
90
92
|
"fontFamily",
|
|
91
93
|
"fontWeight",
|
|
94
|
+
"fontStyle",
|
|
92
95
|
"textAlign",
|
|
93
96
|
"textBaseline",
|
|
94
97
|
"rotation",
|
|
@@ -23,7 +23,7 @@ const ENCODING_PATHS = Object.freeze([
|
|
|
23
23
|
"encoding.color.aggregate",
|
|
24
24
|
"encoding.group.fields",
|
|
25
25
|
"encoding.group.inferredFrom",
|
|
26
|
-
...["x", "y", "x2", "y2", "theta", "color"].map(channel =>
|
|
26
|
+
...["x", "y", "x2", "y2", "theta", "color", "stroke"].map(channel =>
|
|
27
27
|
`encoding.${channel}.temporalUnit`
|
|
28
28
|
),
|
|
29
29
|
"encoding.pathOrder.field",
|
|
@@ -49,7 +49,7 @@ const ENCODING_PATHS = Object.freeze([
|
|
|
49
49
|
const ENTITY_PATHS = Object.freeze({
|
|
50
50
|
dataset: {
|
|
51
51
|
collection: "datasets",
|
|
52
|
-
properties: new Set(["source", "transform", "values"])
|
|
52
|
+
properties: new Set(["source", "transform", "schema", "values"])
|
|
53
53
|
},
|
|
54
54
|
layer: {
|
|
55
55
|
collection: "layers",
|
|
@@ -64,6 +64,8 @@ const ENTITY_PATHS = Object.freeze({
|
|
|
64
64
|
"transform",
|
|
65
65
|
"mark.type",
|
|
66
66
|
"mark.missing",
|
|
67
|
+
"mark.orientation",
|
|
68
|
+
"derivedBindings.regression",
|
|
67
69
|
"layout.mode",
|
|
68
70
|
...ENCODING_PATHS
|
|
69
71
|
])
|
|
@@ -71,7 +73,7 @@ const ENTITY_PATHS = Object.freeze({
|
|
|
71
73
|
scale: {
|
|
72
74
|
collection: "scales",
|
|
73
75
|
properties: new Set([
|
|
74
|
-
"type", "domain", "range", "nice", "zero", "clamp", "reverse",
|
|
76
|
+
"type", "domain", "range", "emptyDomain", "nice", "zero", "clamp", "reverse",
|
|
75
77
|
"unknown", "base", "exponent", "constant", "interpolate", "radialMapping", "midpoint",
|
|
76
78
|
"paddingInner", "paddingOuter", "padding", "align"
|
|
77
79
|
])
|