ggaction 0.0.12 → 0.0.15
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 +127 -0
- package/README.md +20 -11
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -1
- package/knowledge/action-cards.schema.json +1 -1
- package/knowledge/extension-authoring.md +2 -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 -3
- package/src/actions/basic.js +4 -2
- package/src/actions/boxPlots/components.js +14 -4
- package/src/actions/boxPlots/edit.js +31 -5
- package/src/actions/boxPlots/materialize.js +13 -4
- package/src/actions/boxPlots/options.js +48 -16
- package/src/actions/canvas/actions.js +3 -1
- package/src/actions/canvas/fitting.js +211 -0
- package/src/actions/canvas/index.js +2 -0
- package/src/actions/categoryOrder/index.js +18 -6
- package/src/actions/charts/area.js +51 -0
- package/src/actions/charts/bar.js +13 -6
- package/src/actions/charts/beeswarm.js +70 -0
- package/src/actions/charts/density.js +48 -0
- package/src/actions/charts/ecdf.js +186 -0
- package/src/actions/charts/endpoints.js +473 -0
- package/src/actions/charts/heatmap.js +4 -3
- package/src/actions/charts/histogram.js +5 -4
- package/src/actions/charts/horizon.js +73 -0
- package/src/actions/charts/index.js +37 -0
- package/src/actions/charts/interval-regression.js +172 -0
- package/src/actions/charts/line.js +8 -6
- package/src/actions/charts/parallel.js +8 -2
- package/src/actions/charts/pie.js +52 -0
- package/src/actions/charts/polar.js +134 -0
- package/src/actions/charts/radar.js +305 -0
- package/src/actions/charts/radial.js +50 -0
- package/src/actions/charts/raincloud.js +422 -0
- package/src/actions/charts/rug-strip.js +283 -0
- package/src/actions/charts/scatter.js +11 -3
- package/src/actions/charts/shared.js +69 -10
- package/src/actions/composition/actions.js +129 -3
- package/src/actions/coordinates/actions.js +1 -1
- package/src/actions/coordinates/edit.js +76 -0
- package/src/actions/data/bin.js +29 -0
- package/src/actions/data/bin2d.js +57 -185
- package/src/actions/data/complete.js +26 -0
- package/src/actions/data/computed.js +24 -0
- package/src/actions/data/create.js +2 -2
- package/src/actions/data/density.js +12 -20
- package/src/actions/data/derived.js +111 -15
- package/src/actions/data/ecdf.js +38 -0
- package/src/actions/data/edit.js +539 -0
- package/src/actions/data/filter.js +226 -30
- package/src/actions/data/fold.js +24 -0
- package/src/actions/data/impute.js +26 -0
- package/src/actions/data/index.js +38 -0
- package/src/actions/data/interval.js +2 -0
- package/src/actions/data/intervalEdit.js +86 -4
- package/src/actions/data/normalize.js +27 -0
- package/src/actions/data/regression.js +19 -21
- package/src/actions/data/revisionBindings.js +145 -0
- package/src/actions/data/shared.js +20 -4
- package/src/actions/data/stack.js +26 -0
- package/src/actions/data/summary.js +26 -0
- package/src/actions/data/timeUnit.js +4 -1
- package/src/actions/data/window.js +1 -1
- package/src/actions/encodings/appearance.js +32 -13
- package/src/actions/encodings/barWidth.js +8 -0
- package/src/actions/encodings/basic.js +2 -0
- package/src/actions/encodings/channels.js +415 -0
- package/src/actions/encodings/color/continuous.js +19 -59
- package/src/actions/encodings/color/index.js +30 -23
- package/src/actions/encodings/color/layout.js +3 -97
- package/src/actions/encodings/color/policy.js +5 -8
- package/src/actions/encodings/density/resolve.js +37 -5
- package/src/actions/encodings/density.js +124 -34
- package/src/actions/encodings/histogram.js +3 -2
- package/src/actions/encodings/horizon/resolve.js +8 -4
- package/src/actions/encodings/horizon.js +18 -8
- package/src/actions/encodings/index.js +6 -0
- package/src/actions/encodings/offset.js +30 -29
- package/src/actions/encodings/parallel.js +2 -8
- package/src/actions/encodings/position/apply.js +63 -11
- package/src/actions/encodings/position/index.js +52 -6
- package/src/actions/encodings/position/policies/arc.js +14 -0
- package/src/actions/encodings/position/policies/area.js +4 -42
- package/src/actions/encodings/position/policies/bar.js +57 -13
- package/src/actions/encodings/position/resolve.js +88 -26
- package/src/actions/encodings/ranged.js +123 -97
- package/src/actions/encodings/remove.js +44 -34
- package/src/actions/encodings/ruleAppearance.js +24 -32
- package/src/actions/encodings/seriesLayout.js +105 -0
- package/src/actions/encodings/shared.js +107 -37
- package/src/actions/encodings/stroke.js +226 -0
- package/src/actions/encodings/strokeDash.js +9 -6
- package/src/actions/encodings/temporal.js +7 -0
- package/src/actions/encodings/text.js +30 -15
- package/src/actions/errorBands/create.js +41 -8
- package/src/actions/errorBands/edit.js +360 -25
- package/src/actions/errorBars/create.js +56 -17
- package/src/actions/errorBars/edit.js +372 -21
- package/src/actions/facets/actions.js +619 -41
- package/src/actions/facets/derive.js +139 -25
- package/src/actions/gradientPlots/components.js +43 -13
- package/src/actions/gradientPlots/edit.js +14 -4
- package/src/actions/gradientPlots/materialize.js +8 -8
- package/src/actions/gradientPlots/options.js +10 -2
- package/src/actions/guides/applicability.js +28 -19
- package/src/actions/guides/axes/axes.js +4 -3
- package/src/actions/guides/axes/axis.js +18 -18
- package/src/actions/guides/axes/components.js +43 -0
- package/src/actions/guides/axes/edit.js +16 -51
- package/src/actions/guides/axes/labels.js +184 -28
- package/src/actions/guides/axes/lines.js +13 -7
- package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
- package/src/actions/guides/axes/parallel/policy.js +137 -0
- package/src/actions/guides/axes/parallel/resolve.js +50 -25
- package/src/actions/guides/axes/parallel.js +93 -217
- package/src/actions/guides/axes/policy.js +21 -67
- package/src/actions/guides/axes/tickGroups.js +15 -8
- package/src/actions/guides/axes/ticks.js +11 -6
- package/src/actions/guides/axes/titles.js +32 -11
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facade.js +106 -0
- package/src/actions/guides/facadeAxes.js +200 -0
- package/src/actions/guides/facadeLegend.js +63 -0
- package/src/actions/guides/grids/grid.js +37 -33
- package/src/actions/guides/grids/resolve.js +17 -4
- package/src/actions/guides/guides.js +54 -31
- package/src/actions/guides/legends/blocks.js +426 -0
- package/src/actions/guides/legends/categorical/actions.js +309 -279
- package/src/actions/guides/legends/categorical/components.js +21 -17
- package/src/actions/guides/legends/categorical/index.js +37 -33
- package/src/actions/guides/legends/categorical/layout.js +123 -354
- package/src/actions/guides/legends/categorical/options.js +32 -16
- package/src/actions/guides/legends/categorical/recipes.js +114 -9
- package/src/actions/guides/legends/categorical/resolve.js +30 -15
- package/src/actions/guides/legends/categorical/symbols.js +87 -33
- package/src/actions/guides/legends/continuous/common.js +99 -26
- package/src/actions/guides/legends/continuous/gradient.js +120 -89
- package/src/actions/guides/legends/continuous/index.js +8 -2
- package/src/actions/guides/legends/continuous/interval.js +79 -95
- package/src/actions/guides/legends/continuous/opacity.js +113 -89
- package/src/actions/guides/legends/continuous/stroke.js +525 -0
- package/src/actions/guides/legends/creation.js +40 -0
- package/src/actions/guides/legends/edit.js +321 -90
- package/src/actions/guides/legends/focused.js +2 -2
- package/src/actions/guides/legends/index.js +2 -0
- package/src/actions/guides/legends/lane.js +149 -102
- package/src/actions/guides/legends/lifecycle.js +143 -0
- package/src/actions/guides/legends/remove.js +43 -78
- package/src/actions/guides/legends/sampling.js +140 -0
- package/src/actions/guides/legends/size.js +213 -116
- package/src/actions/guides/legends/strokeWidth.js +121 -73
- package/src/actions/guides/legends/target.js +123 -0
- package/src/actions/guides/legends/transition.js +192 -0
- package/src/actions/guides/polar/axes/facade.js +81 -66
- package/src/actions/guides/polar/axes/labels.js +28 -8
- package/src/actions/guides/polar/axes/lines.js +12 -5
- package/src/actions/guides/polar/axes/shared.js +22 -0
- package/src/actions/guides/polar/axes/ticks.js +6 -5
- package/src/actions/guides/polar/axes/titles.js +6 -5
- package/src/actions/guides/polar/grids.js +8 -8
- package/src/actions/guides/polar/resolve.js +12 -8
- package/src/actions/guides/reuse.js +44 -0
- package/src/actions/guides/tickValues.js +19 -21
- package/src/actions/index.js +15 -2
- package/src/actions/intervals/resolve.js +35 -15
- package/src/actions/marks/arc/actions.js +78 -20
- package/src/actions/marks/area/actions.js +51 -12
- package/src/actions/marks/area/materialize.js +22 -3
- package/src/actions/marks/bar/create.js +11 -2
- package/src/actions/marks/bar/edit.js +20 -3
- package/src/actions/marks/bar/materialize.js +33 -9
- package/src/actions/marks/index.js +14 -0
- package/src/actions/marks/line/actions.js +71 -113
- package/src/actions/marks/line/materialize.js +57 -56
- package/src/actions/marks/point/create.js +8 -2
- package/src/actions/marks/point/edit.js +29 -21
- package/src/actions/marks/point/index.js +1 -0
- package/src/actions/marks/point/jitter.js +3 -0
- package/src/actions/marks/point/materialize.js +154 -16
- package/src/actions/marks/point/packing.js +68 -0
- package/src/actions/marks/rect/actions.js +33 -12
- package/src/actions/marks/references.js +658 -0
- package/src/actions/marks/remove.js +87 -4
- package/src/actions/marks/rule/actions.js +77 -9
- package/src/actions/marks/rule/appearance.js +26 -0
- package/src/actions/marks/shared.js +13 -13
- package/src/actions/marks/text/actions.js +296 -13
- package/src/actions/marks/text/index.js +4 -0
- package/src/actions/marks/text/layout.js +156 -8
- package/src/actions/marks/text/removal.js +173 -0
- package/src/actions/marks/text/selection.js +73 -0
- package/src/actions/marks/tick/actions.js +22 -5
- package/src/actions/primitives/createGraphics.js +3 -1
- package/src/actions/primitives/editGraphics.js +26 -0
- package/src/actions/primitives/semantic.js +3 -2
- package/src/actions/primitives/semanticAction.js +120 -10
- package/src/actions/primitives/semanticValidation/guide.js +10 -0
- package/src/actions/primitives/semanticValidation/index.js +9 -1
- package/src/actions/primitives/semanticValidation/layer.js +23 -1
- package/src/actions/primitives/semanticValidation/scale.js +38 -8
- package/src/actions/regression/components.js +26 -11
- package/src/actions/regression/create.js +11 -3
- package/src/actions/regression/edit.js +40 -13
- package/src/actions/regression/resolve.js +1 -1
- package/src/actions/resources/index.js +7 -0
- package/src/actions/resources/remove.js +172 -0
- package/src/actions/scales/channels.js +212 -0
- package/src/actions/scales/consumers/common.js +21 -54
- package/src/actions/scales/consumers/families.js +28 -14
- package/src/actions/scales/consumers/index.js +42 -4
- package/src/actions/scales/consumers/seriesLayout.js +57 -12
- package/src/actions/scales/create.js +58 -26
- package/src/actions/scales/definitions.js +109 -39
- package/src/actions/scales/edit.js +101 -34
- package/src/actions/scales/editPolicy.js +95 -35
- package/src/actions/scales/index.js +6 -0
- package/src/actions/scales/materialize.js +14 -49
- package/src/actions/scales/offset.js +48 -0
- package/src/actions/scales/parallel.js +71 -0
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/preview.js +140 -0
- package/src/actions/scales/quantitativeColor.js +9 -5
- package/src/actions/selection/actions.js +154 -30
- package/src/actions/selection/index.js +3 -0
- package/src/actions/theme/actions.js +98 -0
- package/src/actions/theme/composition.js +238 -0
- package/src/actions/theme/index.js +16 -0
- package/src/actions/theme/reconcile.js +1130 -0
- package/src/actions/theme/state.js +160 -0
- package/src/actions/titles/actions.js +11 -10
- package/src/actions/titles/resolve.js +1 -40
- package/src/actions/violinPlots/create.js +90 -43
- package/src/actions/violinPlots/edit.js +192 -0
- package/src/actions/violinPlots/index.js +2 -0
- package/src/core/ChartProgram.js +7 -0
- package/src/core/action.js +32 -2
- package/src/core/compositionState.js +141 -8
- package/src/core/programState.js +20 -0
- package/src/core/resourceReferences.js +332 -0
- package/src/core/vocabulary.js +16 -3
- package/src/grammar/aggregate.js +66 -23
- package/src/grammar/arcs.js +58 -1
- package/src/grammar/areaEndpoints.js +46 -0
- package/src/grammar/areaSeries.js +87 -41
- package/src/grammar/bars/aggregate.js +37 -13
- package/src/grammar/bars/geometry.js +44 -0
- package/src/grammar/bars/policy.js +9 -7
- package/src/grammar/bin.js +226 -0
- package/src/grammar/categoryOrder.js +34 -0
- package/src/grammar/complete.js +279 -0
- package/src/grammar/computed.js +351 -0
- package/src/grammar/coordinates.js +37 -0
- package/src/grammar/density.js +239 -37
- package/src/grammar/displayLabels.js +57 -0
- package/src/grammar/ecdf.js +198 -0
- package/src/grammar/facets/dependencies.js +133 -1
- package/src/grammar/facets/guides.js +3 -0
- package/src/grammar/facets/index.js +123 -52
- package/src/grammar/facets/scales.js +15 -18
- package/src/grammar/fold.js +123 -0
- package/src/grammar/horizon.js +6 -4
- package/src/grammar/impute.js +340 -0
- package/src/grammar/interval.js +37 -20
- package/src/grammar/lineSeries.js +20 -63
- package/src/grammar/markFilter.js +22 -4
- package/src/grammar/markLabelSelection.js +57 -0
- package/src/grammar/markLabels.js +110 -0
- package/src/grammar/normalize.js +366 -0
- package/src/grammar/parallelAxisTitles.js +16 -0
- package/src/grammar/pathSeries.js +109 -0
- package/src/grammar/pointPacking.js +183 -0
- package/src/grammar/pointShapes.js +13 -4
- package/src/grammar/polar.js +90 -8
- package/src/grammar/positionDatum.js +12 -0
- package/src/grammar/rects.js +9 -1
- package/src/grammar/regression/derive.js +4 -0
- package/src/grammar/regression/index.js +8 -2
- package/src/grammar/regression/models.js +22 -7
- package/src/grammar/regression/parameters.js +63 -13
- package/src/grammar/rotation.js +29 -0
- package/src/grammar/roundedRect.js +180 -0
- package/src/grammar/rules.js +8 -21
- package/src/grammar/scales/color.js +23 -1
- package/src/grammar/scales/colorConsumers.js +57 -0
- package/src/grammar/scales/continuous.js +2 -2
- package/src/grammar/scales/definition.js +42 -5
- package/src/grammar/scales/discretized.js +11 -5
- package/src/grammar/scales/fields.js +38 -8
- package/src/grammar/scales/index.js +1 -0
- package/src/grammar/scales/mapping.js +2 -0
- package/src/grammar/scales/ordinal.js +12 -3
- package/src/grammar/scales/policies.js +2 -2
- package/src/grammar/scales/radial.js +55 -0
- package/src/grammar/scales/size.js +456 -0
- package/src/grammar/scales/types.js +1 -0
- package/src/grammar/schemas/concreteGraphic.js +14 -2
- package/src/grammar/schemas/graphic.js +15 -0
- package/src/grammar/schemas/graphicBounds.js +179 -30
- package/src/grammar/schemas/semanticPath.js +23 -4
- package/src/grammar/stack.js +170 -0
- package/src/grammar/statisticalReference.js +103 -0
- package/src/grammar/statistics/confidenceInterval.js +101 -0
- package/src/grammar/strokeStyle.js +77 -0
- package/src/grammar/summary.js +245 -0
- package/src/grammar/text.js +22 -16
- package/src/grammar/timeUnit.js +234 -13
- package/src/grammar/transformTopology.js +10 -0
- package/src/grammar/transforms.js +418 -16
- package/src/grammar/valueFormat.js +112 -0
- package/src/grammar/weightedStatistics.js +374 -0
- package/src/grammar/window.js +232 -21
- package/src/layout/aspect.js +80 -0
- package/src/layout/facets.js +100 -12
- package/src/layout/guideCollisions.js +12 -0
- package/src/layout/labels.js +343 -0
- package/src/layout/legend.js +2 -10
- package/src/layout/legendItems.js +102 -0
- package/src/layout/legendLane.js +100 -11
- package/src/materialization/bars/aggregate.js +56 -147
- package/src/materialization/bars/histogram.js +104 -20
- package/src/materialization/bars/ranged.js +19 -3
- package/src/materialization/bars/resolve.js +7 -4
- package/src/materialization/categorySlotOffset.js +40 -0
- package/src/materialization/composition.js +3 -1
- package/src/materialization/compositionSnapshot.js +1 -1
- package/src/materialization/coordinateBounds.js +204 -0
- package/src/materialization/dependencies.js +93 -16
- package/src/materialization/encodings.js +1 -1
- package/src/materialization/facetGuides/legacyCategorical.js +3 -0
- package/src/materialization/facetGuides/placement.js +8 -2
- package/src/materialization/facetGuides/preparation.js +12 -7
- package/src/materialization/facetHeaders.js +346 -0
- package/src/materialization/facets.js +23 -66
- package/src/materialization/guides/layout.js +57 -0
- package/src/materialization/guides/resources.js +28 -2
- package/src/materialization/legends.js +11 -2
- package/src/materialization/marks/capabilities.js +20 -5
- package/src/materialization/marks/grouping.js +52 -0
- package/src/materialization/marks/index.js +68 -27
- package/src/materialization/marks/policies.js +4 -1
- package/src/materialization/rect.js +55 -42
- package/src/materialization/rectConfig.js +5 -1
- package/src/materialization/rowEncoding.js +13 -13
- package/src/materialization/scaleGuideDependencies.js +8 -5
- package/src/materialization/scales/map.js +6 -3
- package/src/materialization/scales/policies/arc.js +42 -0
- package/src/materialization/scales/policies/binnedPosition.js +7 -4
- package/src/materialization/scales/policies/offset.js +50 -8
- package/src/materialization/scales/policies/series.js +5 -1
- package/src/materialization/scales/resolve.js +65 -17
- package/src/materialization/selection/compatibility.js +11 -0
- package/src/materialization/selection/items/arc.js +28 -4
- package/src/materialization/selection/items/bar.js +42 -71
- package/src/materialization/selection/items/common.js +27 -6
- package/src/materialization/selection/items/path.js +3 -13
- package/src/materialization/selection/items/rect.js +35 -8
- package/src/materialization/selection/path.js +1 -1
- package/src/materialization/selection/point.js +3 -1
- package/src/materialization/text.js +324 -14
- package/src/mcp/adapter.js +2 -2
- package/src/renderers/canvas/circle.js +2 -3
- package/src/renderers/canvas/line.js +2 -3
- package/src/renderers/canvas/native.js +4 -4
- package/src/renderers/canvas/path.js +2 -3
- package/src/renderers/canvas/rect.js +2 -3
- package/src/renderers/canvas/stroke.js +11 -0
- package/src/renderers/svg.js +19 -0
- package/src/selectors/datasets.js +44 -0
- package/src/selectors/layers.js +7 -2
- package/src/selectors/scales.js +6 -0
- package/src/theme/defaults.js +140 -0
- package/types/basic.d.ts +17 -2
- package/types/index.d.ts +211 -0
- package/types/program.d.ts +2182 -204
- package/src/materialization/bars/grouped.js +0 -144
|
@@ -11,9 +11,16 @@ import {
|
|
|
11
11
|
normalizePointJitterPolicy,
|
|
12
12
|
resolvePointJitter
|
|
13
13
|
} from "../../../grammar/jitter.js";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
normalizePointPackingPolicy,
|
|
16
|
+
resolvePointPacking
|
|
17
|
+
} from "../../../grammar/pointPacking.js";
|
|
18
|
+
import { polarToCartesian } from "../../../grammar/polar.js";
|
|
15
19
|
import { resolveDirectionValues } from "../../../grammar/direction.js";
|
|
16
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
resolveCoordinateBounds,
|
|
22
|
+
resolveCoordinatePolarFrame
|
|
23
|
+
} from "../../../materialization/coordinateBounds.js";
|
|
17
24
|
import { validateMarkOptions } from "../shared.js";
|
|
18
25
|
import {
|
|
19
26
|
DEFAULT_COLORS,
|
|
@@ -21,12 +28,17 @@ import {
|
|
|
21
28
|
} from "../../../theme/defaults.js";
|
|
22
29
|
import { findDataset } from "../../../selectors/datasets.js";
|
|
23
30
|
import { findLayer } from "../../../selectors/layers.js";
|
|
31
|
+
import { canMaterializePoint } from "../../../materialization/marks/capabilities.js";
|
|
24
32
|
import { rematerializeHighlightBaseline } from "../lifecycle.js";
|
|
25
33
|
import {
|
|
26
34
|
resolveRowEncodingValues,
|
|
27
35
|
resolveRowPositionValues
|
|
28
36
|
} from
|
|
29
37
|
"../../../materialization/rowEncoding.js";
|
|
38
|
+
import { offsetCategoryPositions } from
|
|
39
|
+
"../../../materialization/categorySlotOffset.js";
|
|
40
|
+
import { requestedStrokeDetails } from
|
|
41
|
+
"../../../grammar/strokeStyle.js";
|
|
30
42
|
|
|
31
43
|
const REMATERIALIZE_OPTIONS = Object.freeze(["id"]);
|
|
32
44
|
const DEFAULT_POINT_FILL = DEFAULT_COLORS.mark;
|
|
@@ -45,7 +57,7 @@ function resolvePointPositions(program, layer, dataset) {
|
|
|
45
57
|
if (theta === undefined || radius === undefined) {
|
|
46
58
|
return { x: undefined, y: undefined };
|
|
47
59
|
}
|
|
48
|
-
const frame =
|
|
60
|
+
const frame = resolveCoordinatePolarFrame(program, layer.coordinate);
|
|
49
61
|
const positions = theta.map((angle, index) => {
|
|
50
62
|
const distance = radius[index];
|
|
51
63
|
return Number.isFinite(angle) && Number.isFinite(distance)
|
|
@@ -69,7 +81,11 @@ function incompleteShapeGraphic(shape, properties, angle) {
|
|
|
69
81
|
? "path"
|
|
70
82
|
: getPointGraphicType(shape);
|
|
71
83
|
const incomplete = type === "path"
|
|
72
|
-
? {
|
|
84
|
+
? {
|
|
85
|
+
fill: properties.fill,
|
|
86
|
+
opacity: properties.opacity,
|
|
87
|
+
...requestedStrokeDetails(properties, "Point mark")
|
|
88
|
+
}
|
|
73
89
|
: properties;
|
|
74
90
|
return { type, properties: compactProperties(incomplete) };
|
|
75
91
|
}
|
|
@@ -173,7 +189,7 @@ function applyPointJitter(program, {
|
|
|
173
189
|
`Point jitter on "${id}" requires a resolved ${policy.channel} scale.`
|
|
174
190
|
);
|
|
175
191
|
}
|
|
176
|
-
const bounds =
|
|
192
|
+
const bounds = resolveCoordinateBounds(program, layer.coordinate);
|
|
177
193
|
const entries = resolveJitterEntries({
|
|
178
194
|
policy,
|
|
179
195
|
dataset,
|
|
@@ -210,6 +226,80 @@ function applyPointJitter(program, {
|
|
|
210
226
|
};
|
|
211
227
|
}
|
|
212
228
|
|
|
229
|
+
function applyPointPacking(program, {
|
|
230
|
+
id,
|
|
231
|
+
layer,
|
|
232
|
+
dataset,
|
|
233
|
+
x,
|
|
234
|
+
y,
|
|
235
|
+
area,
|
|
236
|
+
shapes,
|
|
237
|
+
angles,
|
|
238
|
+
config,
|
|
239
|
+
graphic,
|
|
240
|
+
existingChildren
|
|
241
|
+
}) {
|
|
242
|
+
const stored = program.materializationConfigs.pointPacking?.[id];
|
|
243
|
+
if (stored === undefined) return { program, x, y };
|
|
244
|
+
const policy = normalizePointPackingPolicy(stored);
|
|
245
|
+
const encoding = layer.encoding?.[policy.channel];
|
|
246
|
+
const scale = program.resolvedScales[encoding?.scale];
|
|
247
|
+
if (scale === undefined) {
|
|
248
|
+
throw new Error(`Point packing on "${id}" requires a resolved ${policy.channel} scale.`);
|
|
249
|
+
}
|
|
250
|
+
const seen = new Set();
|
|
251
|
+
const entries = dataset.values.flatMap((row, index) => {
|
|
252
|
+
if (!Number.isFinite(x?.[index]) || !Number.isFinite(y?.[index])) return [];
|
|
253
|
+
const rawIdentity = policy.key === undefined ? index : row[policy.key];
|
|
254
|
+
const identity = canonicalJitterScalar(
|
|
255
|
+
rawIdentity,
|
|
256
|
+
policy.key === undefined
|
|
257
|
+
? "Point packing source index"
|
|
258
|
+
: `Point packing key field "${policy.key}"`
|
|
259
|
+
);
|
|
260
|
+
if (seen.has(identity)) {
|
|
261
|
+
throw new Error(`Point packing key field "${policy.key}" must be unique for materialized items.`);
|
|
262
|
+
}
|
|
263
|
+
seen.add(identity);
|
|
264
|
+
const resolvedArea = resolvedPointArea(
|
|
265
|
+
area, index, config, existingChildren, graphic.type
|
|
266
|
+
);
|
|
267
|
+
const extent = resolvePointShapeExtent({
|
|
268
|
+
shape: shapes[index],
|
|
269
|
+
area: resolvedArea,
|
|
270
|
+
strokeWidth: typeof config.stroke === "string" ? config.strokeWidth ?? 1 : 0,
|
|
271
|
+
angle: angles?.[index]
|
|
272
|
+
});
|
|
273
|
+
return [{
|
|
274
|
+
index,
|
|
275
|
+
identity,
|
|
276
|
+
base: policy.channel === "x" ? x[index] : y[index],
|
|
277
|
+
fixed: policy.channel === "x" ? y[index] : x[index],
|
|
278
|
+
halfExtent: extent[policy.channel],
|
|
279
|
+
fixedHalfExtent: extent[policy.channel === "x" ? "y" : "x"]
|
|
280
|
+
}];
|
|
281
|
+
});
|
|
282
|
+
const bounds = resolveCoordinateBounds(program, layer.coordinate);
|
|
283
|
+
const resolution = resolvePointPacking({
|
|
284
|
+
target: id,
|
|
285
|
+
policy,
|
|
286
|
+
scale,
|
|
287
|
+
entries,
|
|
288
|
+
plotMinimum: policy.channel === "x" ? bounds.x : bounds.y,
|
|
289
|
+
plotMaximum: policy.channel === "x" ? bounds.x + bounds.width : bounds.y + bounds.height
|
|
290
|
+
});
|
|
291
|
+
const values = [...(policy.channel === "x" ? x : y)];
|
|
292
|
+
for (const item of resolution.items) values[item.index] = item.final;
|
|
293
|
+
return {
|
|
294
|
+
program: program._withMaterializationConfig(["pointPacking", id], {
|
|
295
|
+
...policy,
|
|
296
|
+
resolved: resolution
|
|
297
|
+
}),
|
|
298
|
+
x: policy.channel === "x" ? values : x,
|
|
299
|
+
y: policy.channel === "y" ? values : y
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
213
303
|
export const rematerializePointMark = action(
|
|
214
304
|
{
|
|
215
305
|
op: "rematerializePointMark",
|
|
@@ -251,8 +341,13 @@ export const rematerializePointMark = action(
|
|
|
251
341
|
throw new Error(`Point mark "${id}" requires an existing dataset.`);
|
|
252
342
|
}
|
|
253
343
|
|
|
344
|
+
if (graphic.items.length === 0 && !canMaterializePoint(resolved, layer)) {
|
|
345
|
+
return resolved;
|
|
346
|
+
}
|
|
347
|
+
|
|
254
348
|
const positions = resolvePointPositions(resolved, layer, dataset);
|
|
255
349
|
const mappedFill = resolveRowEncodingValues(resolved, layer, dataset, "color");
|
|
350
|
+
const mappedStroke = resolveRowEncodingValues(resolved, layer, dataset, "stroke");
|
|
256
351
|
const area = resolveRowEncodingValues(resolved, layer, dataset, "size");
|
|
257
352
|
const encodedShape = resolveRowEncodingValues(resolved, layer, dataset, "shape");
|
|
258
353
|
const encodedOpacity = resolveRowEncodingValues(
|
|
@@ -263,6 +358,7 @@ export const rematerializePointMark = action(
|
|
|
263
358
|
);
|
|
264
359
|
const angles = resolveDirectionValues(dataset.values, layer.encoding?.angle);
|
|
265
360
|
const config = resolved.markConfigs[id] ?? {};
|
|
361
|
+
const strokeDetails = requestedStrokeDetails(config, "Point mark");
|
|
266
362
|
const fill = mappedFill ?? config.fill ?? DEFAULT_POINT_FILL;
|
|
267
363
|
const shapes = encodedShape ?? dataset.values.map(() => config.shape ?? "circle");
|
|
268
364
|
const existingChildren = graphic.items ?? [];
|
|
@@ -272,7 +368,13 @@ export const rematerializePointMark = action(
|
|
|
272
368
|
angles !== undefined ||
|
|
273
369
|
graphic.type === "collection" ||
|
|
274
370
|
getPointGraphicType(constantShape) !== graphic.type;
|
|
275
|
-
|
|
371
|
+
if (
|
|
372
|
+
resolved.materializationConfigs.jitters?.[id] !== undefined &&
|
|
373
|
+
resolved.materializationConfigs.pointPacking?.[id] !== undefined
|
|
374
|
+
) {
|
|
375
|
+
throw new Error(`Point mark "${id}" cannot combine jitter and packing.`);
|
|
376
|
+
}
|
|
377
|
+
const packed = applyPointPacking(resolved, {
|
|
276
378
|
id,
|
|
277
379
|
layer,
|
|
278
380
|
dataset,
|
|
@@ -285,7 +387,20 @@ export const rematerializePointMark = action(
|
|
|
285
387
|
graphic,
|
|
286
388
|
existingChildren
|
|
287
389
|
});
|
|
288
|
-
const
|
|
390
|
+
const jittered = applyPointJitter(packed.program, {
|
|
391
|
+
id,
|
|
392
|
+
layer,
|
|
393
|
+
dataset,
|
|
394
|
+
x: packed.x,
|
|
395
|
+
y: packed.y,
|
|
396
|
+
area,
|
|
397
|
+
shapes,
|
|
398
|
+
angles,
|
|
399
|
+
config,
|
|
400
|
+
graphic,
|
|
401
|
+
existingChildren
|
|
402
|
+
});
|
|
403
|
+
const { x, y } = offsetCategoryPositions(jittered.program, layer, jittered);
|
|
289
404
|
|
|
290
405
|
if (requiresMixedCollection) {
|
|
291
406
|
const items = dataset.values.map((_, index) => {
|
|
@@ -313,7 +428,8 @@ export const rematerializePointMark = action(
|
|
|
313
428
|
x: centerX,
|
|
314
429
|
y: centerY,
|
|
315
430
|
fill: color,
|
|
316
|
-
opacity
|
|
431
|
+
opacity,
|
|
432
|
+
...strokeDetails
|
|
317
433
|
}, angle);
|
|
318
434
|
}
|
|
319
435
|
return createPointShapeGraphic({
|
|
@@ -322,12 +438,15 @@ export const rematerializePointMark = action(
|
|
|
322
438
|
y: centerY,
|
|
323
439
|
area: resolvedArea,
|
|
324
440
|
fill: color,
|
|
325
|
-
...(typeof config.stroke === "string"
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
441
|
+
...(typeof (mappedStroke?.[index] ?? config.stroke) === "string"
|
|
442
|
+
? { stroke: mappedStroke?.[index] ?? config.stroke }
|
|
443
|
+
: {}),
|
|
444
|
+
...((mappedStroke !== undefined || config.strokeWidth !== undefined)
|
|
445
|
+
? { strokeWidth: config.strokeWidth ?? 1 }
|
|
446
|
+
: {}),
|
|
329
447
|
opacity,
|
|
330
|
-
angle
|
|
448
|
+
angle,
|
|
449
|
+
...strokeDetails
|
|
331
450
|
});
|
|
332
451
|
});
|
|
333
452
|
return jittered.program.editGraphics({
|
|
@@ -399,15 +518,31 @@ export const rematerializePointMark = action(
|
|
|
399
518
|
next = next
|
|
400
519
|
.editGraphics({ target: id, property: "width", value: sides })
|
|
401
520
|
.editGraphics({ target: id, property: "height", value: sides })
|
|
402
|
-
.editGraphics({
|
|
403
|
-
|
|
521
|
+
.editGraphics({
|
|
522
|
+
target: id,
|
|
523
|
+
property: "stroke",
|
|
524
|
+
value: mappedStroke ?? fill ?? DEFAULT_POINT_FILL
|
|
525
|
+
})
|
|
526
|
+
.editGraphics({
|
|
527
|
+
target: id,
|
|
528
|
+
property: "strokeWidth",
|
|
529
|
+
value: mappedStroke === undefined ? 0 : config.strokeWidth ?? 1
|
|
530
|
+
});
|
|
404
531
|
}
|
|
405
532
|
if (config.opacity !== undefined) {
|
|
406
533
|
next = next.editGraphics({ target: id, property: "opacity", value: config.opacity });
|
|
407
534
|
} else if (encodedOpacity !== undefined) {
|
|
408
535
|
next = next.editGraphics({ target: id, property: "opacity", value: encodedOpacity });
|
|
409
536
|
}
|
|
410
|
-
if (
|
|
537
|
+
if (mappedStroke !== undefined) {
|
|
538
|
+
next = next
|
|
539
|
+
.editGraphics({ target: id, property: "stroke", value: mappedStroke })
|
|
540
|
+
.editGraphics({
|
|
541
|
+
target: id,
|
|
542
|
+
property: "strokeWidth",
|
|
543
|
+
value: config.strokeWidth ?? 1
|
|
544
|
+
});
|
|
545
|
+
} else if (config.stroke === false) {
|
|
411
546
|
next = next
|
|
412
547
|
.editGraphics({ target: id, property: "stroke", value: "transparent" })
|
|
413
548
|
.editGraphics({ target: id, property: "strokeWidth", value: 0 });
|
|
@@ -419,6 +554,9 @@ export const rematerializePointMark = action(
|
|
|
419
554
|
value: config.strokeWidth
|
|
420
555
|
});
|
|
421
556
|
}
|
|
557
|
+
for (const [property, value] of Object.entries(strokeDetails)) {
|
|
558
|
+
next = next.editGraphics({ target: id, property, value });
|
|
559
|
+
}
|
|
422
560
|
return next;
|
|
423
561
|
}
|
|
424
562
|
);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { action } from "../../../core/action.js";
|
|
2
|
+
import { validateUserId } from "../../../core/identifiers.js";
|
|
3
|
+
import { validateKeys } from "../../../core/validation.js";
|
|
4
|
+
import { normalizePointPackingPolicy } from "../../../grammar/pointPacking.js";
|
|
5
|
+
import { findCoordinate } from "../../../selectors/coordinates.js";
|
|
6
|
+
import { resolveEligibleLayer } from "../../../selectors/layers.js";
|
|
7
|
+
|
|
8
|
+
const OPTIONS = Object.freeze([
|
|
9
|
+
"target", "channel", "maxOffset", "padding", "key", "overflow"
|
|
10
|
+
]);
|
|
11
|
+
const REMOVE_OPTIONS = Object.freeze(["target"]);
|
|
12
|
+
|
|
13
|
+
function isPackablePoint(program, layer, channel) {
|
|
14
|
+
const packed = layer.encoding?.[channel];
|
|
15
|
+
const fixed = layer.encoding?.[channel === "x" ? "y" : "x"];
|
|
16
|
+
return layer.mark?.type === "point" &&
|
|
17
|
+
["nominal", "ordinal"].includes(packed?.fieldType) &&
|
|
18
|
+
["quantitative", "temporal"].includes(fixed?.fieldType) &&
|
|
19
|
+
packed.scale !== undefined && fixed.scale !== undefined &&
|
|
20
|
+
findCoordinate(program, layer.coordinate)?.type === "cartesian";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function target(program, requested, channel) {
|
|
24
|
+
return resolveEligibleLayer(program, {
|
|
25
|
+
target: requested === undefined ? undefined : validateUserId(requested, "Point packing target"),
|
|
26
|
+
predicate: layer => isPackablePoint(program, layer, channel),
|
|
27
|
+
label: "point packing"
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const packPoints = action(
|
|
32
|
+
{ op: "packPoints", description: "Pack point glyphs deterministically within categorical slots." },
|
|
33
|
+
function (args = {}) {
|
|
34
|
+
validateKeys(args, OPTIONS, "packPoints");
|
|
35
|
+
const policy = normalizePointPackingPolicy(args);
|
|
36
|
+
const layer = target(this, args.target, policy.channel);
|
|
37
|
+
if (this.materializationConfigs.jitters?.[layer.id] !== undefined) {
|
|
38
|
+
throw new Error(`Point packing on "${layer.id}" conflicts with point jitter.`);
|
|
39
|
+
}
|
|
40
|
+
return this
|
|
41
|
+
._withMaterializationConfig(["pointPacking", layer.id], policy)
|
|
42
|
+
.rematerializePointMark({ id: layer.id });
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
export const removePointPacking = action(
|
|
47
|
+
{ op: "removePointPacking", description: "Remove point packing and restore semantic positions." },
|
|
48
|
+
function (args = {}) {
|
|
49
|
+
validateKeys(args, REMOVE_OPTIONS, "removePointPacking");
|
|
50
|
+
const requested = args.target === undefined
|
|
51
|
+
? undefined
|
|
52
|
+
: validateUserId(args.target, "Point packing target");
|
|
53
|
+
const layer = resolveEligibleLayer(this, {
|
|
54
|
+
target: requested,
|
|
55
|
+
predicate: candidate => candidate.mark?.type === "point" &&
|
|
56
|
+
this.materializationConfigs.pointPacking?.[candidate.id] !== undefined,
|
|
57
|
+
label: "point packing"
|
|
58
|
+
});
|
|
59
|
+
return this
|
|
60
|
+
._withoutMaterializationConfig(["pointPacking", layer.id])
|
|
61
|
+
.rematerializePointMark({ id: layer.id });
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
export function registerPointPackingActions(ProgramClass) {
|
|
66
|
+
ProgramClass.prototype.packPoints = packPoints;
|
|
67
|
+
ProgramClass.prototype.removePointPacking = removePointPacking;
|
|
68
|
+
}
|
|
@@ -19,12 +19,20 @@ import {
|
|
|
19
19
|
resolveMarkId,
|
|
20
20
|
validateMarkOptions
|
|
21
21
|
} from "../shared.js";
|
|
22
|
+
import { replaceGraphicItems } from "../../primitives/editGraphics.js";
|
|
22
23
|
import { resolveMarkGraphicPlacement } from
|
|
23
24
|
"../../../materialization/graphicHierarchy.js";
|
|
25
|
+
import { rematerializeExistingLegend } from "../../encodings/shared.js";
|
|
24
26
|
import { rematerializeHighlightBaseline } from "../lifecycle.js";
|
|
27
|
+
import {
|
|
28
|
+
requestedRectStyleDetails
|
|
29
|
+
} from "../../../grammar/roundedRect.js";
|
|
30
|
+
import { STROKE_STYLE_PROPERTIES } from
|
|
31
|
+
"../../../grammar/strokeStyle.js";
|
|
25
32
|
|
|
26
33
|
const STYLE_OPTIONS = Object.freeze([
|
|
27
|
-
"fill", "opacity", "stroke", "strokeWidth"
|
|
34
|
+
"fill", "opacity", "stroke", "strokeWidth", "cornerRadius",
|
|
35
|
+
...STROKE_STYLE_PROPERTIES
|
|
28
36
|
]);
|
|
29
37
|
const CREATE_OPTIONS = Object.freeze(["id", "data", ...STYLE_OPTIONS]);
|
|
30
38
|
const EDIT_OPTIONS = Object.freeze(["target", ...STYLE_OPTIONS]);
|
|
@@ -45,6 +53,7 @@ const createRectMark = action(
|
|
|
45
53
|
{ op: "createRectMark", description: "Create a semantic rectangular cell layer." },
|
|
46
54
|
function (args = {}) {
|
|
47
55
|
validateMarkOptions(args, CREATE_OPTIONS, "createRectMark");
|
|
56
|
+
requestedRectStyleDetails(args, "createRectMark");
|
|
48
57
|
const id = resolveMarkId(this, args.id, {
|
|
49
58
|
defaultId: "rect",
|
|
50
59
|
label: "Rect mark id",
|
|
@@ -99,7 +108,13 @@ const rematerializeRectMark = action(
|
|
|
99
108
|
const layer = findLayer(this, id);
|
|
100
109
|
const graphic = this.graphicSpec.objects[id];
|
|
101
110
|
if (layer?.mark?.type !== "rect") throw new Error(`Unknown rect mark "${id}".`);
|
|
102
|
-
|
|
111
|
+
const roundedCollection = graphic?.type === "collection" &&
|
|
112
|
+
Object.hasOwn(this.markConfigs[id] ?? {}, "cornerRadius") &&
|
|
113
|
+
graphic.items?.every(item => ["rect", "path"].includes(item.type));
|
|
114
|
+
if (
|
|
115
|
+
(graphic?.type !== "rect" && !roundedCollection) ||
|
|
116
|
+
!Array.isArray(graphic.items)
|
|
117
|
+
) {
|
|
103
118
|
throw new Error(`Rect mark "${id}" requires rect collection graphics.`);
|
|
104
119
|
}
|
|
105
120
|
if (this.markConfigs[id]?.gradientPlot?.materialized === true) {
|
|
@@ -110,13 +125,11 @@ const rematerializeRectMark = action(
|
|
|
110
125
|
throw new Error(`Rect mark "${id}" requires an existing dataset.`);
|
|
111
126
|
}
|
|
112
127
|
if (!canMaterializeRect(this, layer)) {
|
|
113
|
-
return
|
|
114
|
-
? this
|
|
115
|
-
: this.editGraphics({ target: id, property: "items", value: [] });
|
|
128
|
+
return replaceGraphicItems(this, id, "rect", []);
|
|
116
129
|
}
|
|
117
130
|
let next = this;
|
|
118
131
|
if (args.scales !== false) {
|
|
119
|
-
const ids = new Set(["x", "y", "x2", "y2", "color"].flatMap(channel => {
|
|
132
|
+
const ids = new Set(["x", "y", "x2", "y2", "color", "stroke"].flatMap(channel => {
|
|
120
133
|
const scale = layer.encoding?.[channel]?.scale;
|
|
121
134
|
return scale === undefined ? [] : [scale];
|
|
122
135
|
}));
|
|
@@ -124,11 +137,12 @@ const rematerializeRectMark = action(
|
|
|
124
137
|
next = next.rematerializeScale({ id: scale, marks: false });
|
|
125
138
|
}
|
|
126
139
|
}
|
|
127
|
-
return
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
140
|
+
return replaceGraphicItems(
|
|
141
|
+
next,
|
|
142
|
+
id,
|
|
143
|
+
"rect",
|
|
144
|
+
resolveRectGraphicItems(next, layer, dataset)
|
|
145
|
+
);
|
|
132
146
|
}
|
|
133
147
|
);
|
|
134
148
|
|
|
@@ -136,6 +150,7 @@ const editRectMark = action(
|
|
|
136
150
|
{ op: "editRectMark", description: "Edit rectangular cell appearance." },
|
|
137
151
|
function (args = {}) {
|
|
138
152
|
validateMarkOptions(args, EDIT_OPTIONS, "editRectMark");
|
|
153
|
+
requestedRectStyleDetails(args, "editRectMark");
|
|
139
154
|
if (!STYLE_OPTIONS.some(option => Object.hasOwn(args, option))) {
|
|
140
155
|
throw new Error("editRectMark requires at least one editable property.");
|
|
141
156
|
}
|
|
@@ -143,6 +158,11 @@ const editRectMark = action(
|
|
|
143
158
|
if (Object.hasOwn(args, "fill") && layer.encoding?.color !== undefined) {
|
|
144
159
|
throw new Error("editRectMark fill cannot be combined with a color encoding.");
|
|
145
160
|
}
|
|
161
|
+
if (Object.hasOwn(args, "stroke") && layer.encoding?.stroke !== undefined) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
"editRectMark stroke conflicts with a field encoding; use encodeStroke with value to replace it."
|
|
164
|
+
);
|
|
165
|
+
}
|
|
146
166
|
const next = this._withMarkConfig(
|
|
147
167
|
layer.id,
|
|
148
168
|
{
|
|
@@ -155,9 +175,10 @@ const editRectMark = action(
|
|
|
155
175
|
: this.markConfigs[layer.id]?.fillExplicit ?? false
|
|
156
176
|
}
|
|
157
177
|
);
|
|
158
|
-
|
|
178
|
+
const materialized = canMaterializeRect(next, layer)
|
|
159
179
|
? next.rematerializeRectMark({ id: layer.id })
|
|
160
180
|
: next;
|
|
181
|
+
return rematerializeExistingLegend(materialized);
|
|
161
182
|
}
|
|
162
183
|
);
|
|
163
184
|
|