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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ggaction",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "Build charts through immutable, traceable graphical actions.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Hyeon Jeon",
|
|
@@ -111,18 +111,25 @@
|
|
|
111
111
|
"docs:signatures:check": "node scripts/generate-doc-signatures.js --check",
|
|
112
112
|
"examples:index": "node scripts/generate-examples-readme.js",
|
|
113
113
|
"examples:index:check": "node scripts/generate-examples-readme.js --check",
|
|
114
|
-
"docs:generate": "npm run examples:index && npm run docs:reference && npm run contracts:cards && npm run docs:actions && npm run docs:signatures && npm run docs:
|
|
114
|
+
"docs:generate": "npm run docs:provenance && npm run docs:workflows && npm run examples:index && npm run docs:capabilities && npm run docs:reference && npm run contracts:relations && npm run contracts:cards && npm run docs:picker && npm run docs:scale-editors && npm run docs:actions && npm run docs:signatures && npm run docs:snippets && npm run docs:metadata && npm run docs:search && npm run docs:images && npm run docs:machine && npm run docs:llms",
|
|
115
115
|
"docs:build": "bundle exec jekyll build --source docs --destination _site --baseurl /ggaction",
|
|
116
116
|
"docs:verify": "npm run docs:preflight && npm run docs:generate && npm run test:docs && npm run docs:build && npm run test:docs:built && npm run test:docs:browser",
|
|
117
117
|
"contracts:catalog": "node scripts/generate-action-catalog.js",
|
|
118
118
|
"contracts:catalog:check": "node scripts/generate-action-catalog.js --check",
|
|
119
|
+
"contracts:relations": "node scripts/generate-action-relationships.js",
|
|
120
|
+
"contracts:relations:check": "node scripts/generate-action-relationships.js --check",
|
|
119
121
|
"contracts:cards": "node scripts/generate-action-cards.js",
|
|
120
122
|
"contracts:cards:check": "node scripts/generate-action-cards.js --check",
|
|
121
123
|
"package:check": "node scripts/package-artifact.js --check",
|
|
122
124
|
"package:pack": "node scripts/package-artifact.js --pack",
|
|
123
125
|
"package:bundle": "node scripts/measure-browser-bundle.js",
|
|
124
126
|
"test:package": "node scripts/package-consumer.js",
|
|
125
|
-
"release:notes": "node scripts/release-notes.js"
|
|
127
|
+
"release:notes": "node scripts/release-notes.js",
|
|
128
|
+
"docs:workflows": "node scripts/generate-doc-workflows.js",
|
|
129
|
+
"docs:provenance": "node scripts/doc-provenance.js",
|
|
130
|
+
"docs:picker": "node scripts/generate-doc-chart-picker.js",
|
|
131
|
+
"docs:snippets": "node scripts/generate-doc-snippet-contracts.js",
|
|
132
|
+
"docs:scale-editors": "node scripts/generate-doc-scale-editors.js"
|
|
126
133
|
},
|
|
127
134
|
"engines": {
|
|
128
135
|
"node": ">=20"
|
package/src/actions/basic.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { registerBasicCanvasActions } from "./canvas/index.js";
|
|
2
2
|
import { registerBasicChartActions } from "./charts/basic.js";
|
|
3
|
-
import {
|
|
3
|
+
import { registerBasicCoordinateActions } from "./coordinates/actions.js";
|
|
4
4
|
import { registerBasicDataActions } from "./data/basic.js";
|
|
5
5
|
import { registerBasicEncodingActions } from "./encodings/basic.js";
|
|
6
6
|
import { registerBasicGuideActions } from "./guides/basic.js";
|
|
7
7
|
import { registerBasicMarkActions } from "./marks/basic.js";
|
|
8
8
|
import { registerBasicPrimitiveActions } from "./primitives/index.js";
|
|
9
9
|
import { registerBasicScaleActions } from "./scales/index.js";
|
|
10
|
+
import { registerThemeActions } from "./theme/index.js";
|
|
10
11
|
|
|
11
12
|
export function registerBasicActions(ProgramClass) {
|
|
13
|
+
registerThemeActions(ProgramClass);
|
|
12
14
|
registerBasicPrimitiveActions(ProgramClass);
|
|
13
15
|
registerBasicCanvasActions(ProgramClass);
|
|
14
16
|
registerBasicDataActions(ProgramClass);
|
|
15
17
|
registerBasicMarkActions(ProgramClass);
|
|
16
18
|
registerBasicScaleActions(ProgramClass);
|
|
17
19
|
registerBasicEncodingActions(ProgramClass);
|
|
18
|
-
|
|
20
|
+
registerBasicCoordinateActions(ProgramClass);
|
|
19
21
|
registerBasicGuideActions(ProgramClass);
|
|
20
22
|
registerBasicChartActions(ProgramClass);
|
|
21
23
|
}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { action } from "../../core/action.js";
|
|
2
2
|
import { validateKeys } from "../../core/validation.js";
|
|
3
|
+
import {
|
|
4
|
+
requestedStrokeDetails,
|
|
5
|
+
STROKE_STYLE_PROPERTIES
|
|
6
|
+
} from "../../grammar/strokeStyle.js";
|
|
3
7
|
|
|
4
8
|
const MEDIAN_OPTIONS = Object.freeze([
|
|
5
9
|
"id", "owner", "data", "category", "categoryType", "measure",
|
|
6
10
|
"coordinate", "categoryScale", "measureScale", "orientation", "stroke",
|
|
7
|
-
"strokeWidth"
|
|
11
|
+
"strokeWidth", ...STROKE_STYLE_PROPERTIES
|
|
8
12
|
]);
|
|
9
13
|
const OUTLIER_OPTIONS = Object.freeze([
|
|
10
14
|
"id", "data", "category", "categoryType", "measure", "coordinate",
|
|
11
|
-
"categoryScale", "measureScale", "orientation", "shape", "radius", "opacity"
|
|
15
|
+
"categoryScale", "measureScale", "orientation", "shape", "radius", "opacity",
|
|
16
|
+
...STROKE_STYLE_PROPERTIES
|
|
12
17
|
]);
|
|
13
18
|
|
|
14
19
|
export const createBoxMedian = action(
|
|
@@ -23,7 +28,11 @@ export const createBoxMedian = action(
|
|
|
23
28
|
}
|
|
24
29
|
const categoryAction = args.orientation === "vertical" ? "encodeX" : "encodeY";
|
|
25
30
|
const measureAction = args.orientation === "vertical" ? "encodeY" : "encodeX";
|
|
26
|
-
let next = this.createRuleMark({
|
|
31
|
+
let next = this.createRuleMark({
|
|
32
|
+
id: args.id,
|
|
33
|
+
data: args.data,
|
|
34
|
+
...requestedStrokeDetails(args, "Box median")
|
|
35
|
+
});
|
|
27
36
|
next = next[categoryAction]({
|
|
28
37
|
target: args.id,
|
|
29
38
|
field: args.category,
|
|
@@ -65,7 +74,8 @@ export const createBoxOutliers = action(
|
|
|
65
74
|
id: args.id,
|
|
66
75
|
data: args.data,
|
|
67
76
|
shape: args.shape,
|
|
68
|
-
fill: "#111111"
|
|
77
|
+
fill: "#111111",
|
|
78
|
+
...requestedStrokeDetails(args, "Box outlier")
|
|
69
79
|
});
|
|
70
80
|
next = next[categoryAction]({
|
|
71
81
|
target: args.id,
|
|
@@ -24,6 +24,8 @@ import {
|
|
|
24
24
|
resolveBoxWidth
|
|
25
25
|
} from "./options.js";
|
|
26
26
|
import { resolveBoxOrientation } from "./resolve.js";
|
|
27
|
+
import { requestedRectStyleDetails } from "../../grammar/roundedRect.js";
|
|
28
|
+
import { requestedStrokeDetails } from "../../grammar/strokeStyle.js";
|
|
27
29
|
|
|
28
30
|
const OPTIONS = Object.freeze([
|
|
29
31
|
"target", "data", "x", "y", "whisker", "width", "outliers", "box",
|
|
@@ -252,6 +254,8 @@ export const editBoxPlot = action(
|
|
|
252
254
|
{ ...current.outlier, ...outlierPatch },
|
|
253
255
|
"editBoxPlot"
|
|
254
256
|
);
|
|
257
|
+
const boxStyleDetails = requestedRectStyleDetails(box, "Box body");
|
|
258
|
+
const medianStyleDetails = requestedStrokeDetails(median, "Box median");
|
|
255
259
|
if (Object.hasOwn(boxPatch, "fill") && owner.encoding?.color !== undefined) {
|
|
256
260
|
throw new Error(
|
|
257
261
|
"editBoxPlot box.fill cannot be combined with a color encoding."
|
|
@@ -333,6 +337,7 @@ export const editBoxPlot = action(
|
|
|
333
337
|
...next.markConfigs[current.whiskerId],
|
|
334
338
|
errorBar: {
|
|
335
339
|
...next.markConfigs[current.whiskerId].errorBar,
|
|
340
|
+
source: summaryRevision.id,
|
|
336
341
|
data: summaryRevision.id
|
|
337
342
|
}
|
|
338
343
|
});
|
|
@@ -360,7 +365,10 @@ export const editBoxPlot = action(
|
|
|
360
365
|
fill: box.fill,
|
|
361
366
|
opacity: box.opacity,
|
|
362
367
|
stroke: box.stroke,
|
|
363
|
-
strokeWidth: box.strokeWidth
|
|
368
|
+
strokeWidth: box.strokeWidth,
|
|
369
|
+
...(Object.keys(boxStyleDetails).length === 0
|
|
370
|
+
? {}
|
|
371
|
+
: { barAppearance: boxStyleDetails })
|
|
364
372
|
});
|
|
365
373
|
next = updateBoxPositions(next, owner, current, candidate, {
|
|
366
374
|
outlierDataId: outlierRevision?.id,
|
|
@@ -385,7 +393,8 @@ export const editBoxPlot = action(
|
|
|
385
393
|
measureScale: measure.scale,
|
|
386
394
|
shape: outlier.shape,
|
|
387
395
|
radius: outlier.radius,
|
|
388
|
-
opacity: outlier.opacity
|
|
396
|
+
opacity: outlier.opacity,
|
|
397
|
+
...requestedStrokeDetails(outlier, "Box outlier")
|
|
389
398
|
});
|
|
390
399
|
}
|
|
391
400
|
next = next
|
|
@@ -394,12 +403,19 @@ export const editBoxPlot = action(
|
|
|
394
403
|
target: current.medianId,
|
|
395
404
|
value: median.strokeWidth
|
|
396
405
|
});
|
|
406
|
+
if (Object.keys(medianStyleDetails).length > 0) {
|
|
407
|
+
next = next.editRuleMark({
|
|
408
|
+
target: current.medianId,
|
|
409
|
+
...medianStyleDetails
|
|
410
|
+
});
|
|
411
|
+
}
|
|
397
412
|
if (findLayer(next, current.outlierId) !== undefined) {
|
|
398
413
|
next = next
|
|
399
414
|
.editPointMark({
|
|
400
415
|
target: current.outlierId,
|
|
401
416
|
shape: outlier.shape,
|
|
402
|
-
opacity: outlier.opacity
|
|
417
|
+
opacity: outlier.opacity,
|
|
418
|
+
...requestedStrokeDetails(outlier, "Box outlier")
|
|
403
419
|
})
|
|
404
420
|
.encodeRadius({ target: current.outlierId, value: outlier.radius });
|
|
405
421
|
}
|
|
@@ -445,7 +461,10 @@ export const editBoxPlot = action(
|
|
|
445
461
|
fill: box.fill,
|
|
446
462
|
opacity: box.opacity,
|
|
447
463
|
stroke: box.stroke,
|
|
448
|
-
strokeWidth: box.strokeWidth
|
|
464
|
+
strokeWidth: box.strokeWidth,
|
|
465
|
+
...(Object.keys(boxStyleDetails).length === 0
|
|
466
|
+
? {}
|
|
467
|
+
: { barAppearance: boxStyleDetails })
|
|
449
468
|
});
|
|
450
469
|
|
|
451
470
|
if (changesBox) next = next.rematerializeBarMark({ id: owner.id });
|
|
@@ -456,13 +475,20 @@ export const editBoxPlot = action(
|
|
|
456
475
|
target: current.medianId,
|
|
457
476
|
value: median.strokeWidth
|
|
458
477
|
});
|
|
478
|
+
if (Object.keys(medianStyleDetails).length > 0) {
|
|
479
|
+
next = next.editRuleMark({
|
|
480
|
+
target: current.medianId,
|
|
481
|
+
...medianStyleDetails
|
|
482
|
+
});
|
|
483
|
+
}
|
|
459
484
|
}
|
|
460
485
|
if (findLayer(next, current.outlierId) !== undefined && changesOutlier) {
|
|
461
486
|
next = next
|
|
462
487
|
.editPointMark({
|
|
463
488
|
target: current.outlierId,
|
|
464
489
|
shape: outlier.shape,
|
|
465
|
-
opacity: outlier.opacity
|
|
490
|
+
opacity: outlier.opacity,
|
|
491
|
+
...requestedStrokeDetails(outlier, "Box outlier")
|
|
466
492
|
})
|
|
467
493
|
.encodeRadius({ target: current.outlierId, value: outlier.radius });
|
|
468
494
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyFacadeGuides } from "../charts/shared.js";
|
|
1
2
|
import { action } from "../../core/action.js";
|
|
2
3
|
import { validateUserId } from "../../core/identifiers.js";
|
|
3
4
|
import {
|
|
@@ -8,6 +9,8 @@ import {
|
|
|
8
9
|
import { findDataset } from "../../selectors/datasets.js";
|
|
9
10
|
import { findLayer } from "../../selectors/layers.js";
|
|
10
11
|
import { resolveBoxOrientation } from "./resolve.js";
|
|
12
|
+
import { requestedRectStyleDetails } from "../../grammar/roundedRect.js";
|
|
13
|
+
import { requestedStrokeDetails } from "../../grammar/strokeStyle.js";
|
|
11
14
|
import { resolvePlotGraphicPlacement } from
|
|
12
15
|
"../../materialization/graphicHierarchy.js";
|
|
13
16
|
|
|
@@ -42,6 +45,7 @@ export const materializeBoxPlot = action(
|
|
|
42
45
|
const whiskerId = `${ownerId}Whisker`;
|
|
43
46
|
const medianId = `${ownerId}Median`;
|
|
44
47
|
const outlierId = `${ownerId}Outliers`;
|
|
48
|
+
const boxStyleDetails = requestedRectStyleDetails(config.box, "Box body");
|
|
45
49
|
let next = this._withMarkConfig(ownerId, {
|
|
46
50
|
...this.markConfigs[ownerId],
|
|
47
51
|
boxPlot: {
|
|
@@ -60,7 +64,10 @@ export const materializeBoxPlot = action(
|
|
|
60
64
|
fill: config.box.fill,
|
|
61
65
|
opacity: config.box.opacity,
|
|
62
66
|
stroke: config.box.stroke,
|
|
63
|
-
strokeWidth: config.box.strokeWidth
|
|
67
|
+
strokeWidth: config.box.strokeWidth,
|
|
68
|
+
...(Object.keys(boxStyleDetails).length === 0
|
|
69
|
+
? {}
|
|
70
|
+
: { barAppearance: boxStyleDetails })
|
|
64
71
|
}).createBoxSummaryData({
|
|
65
72
|
id: summaryId,
|
|
66
73
|
source,
|
|
@@ -164,7 +171,8 @@ export const materializeBoxPlot = action(
|
|
|
164
171
|
categoryScale: category.scale,
|
|
165
172
|
measureScale: measure.scale,
|
|
166
173
|
stroke: config.median.stroke,
|
|
167
|
-
strokeWidth: config.median.strokeWidth
|
|
174
|
+
strokeWidth: config.median.strokeWidth,
|
|
175
|
+
...requestedStrokeDetails(config.median, "Box median")
|
|
168
176
|
});
|
|
169
177
|
if (hasOutliers) {
|
|
170
178
|
next = next.createBoxOutliers({
|
|
@@ -179,7 +187,8 @@ export const materializeBoxPlot = action(
|
|
|
179
187
|
measureScale: measure.scale,
|
|
180
188
|
shape: config.outlier.shape,
|
|
181
189
|
radius: config.outlier.radius,
|
|
182
|
-
opacity: config.outlier.opacity
|
|
190
|
+
opacity: config.outlier.opacity,
|
|
191
|
+
...requestedStrokeDetails(config.outlier, "Box outlier")
|
|
183
192
|
});
|
|
184
193
|
}
|
|
185
194
|
next = next._withMarkConfig(ownerId, {
|
|
@@ -190,7 +199,7 @@ export const materializeBoxPlot = action(
|
|
|
190
199
|
}
|
|
191
200
|
});
|
|
192
201
|
if (config.guides !== false) {
|
|
193
|
-
next = next
|
|
202
|
+
next = applyFacadeGuides(next, config.guides, ownerId);
|
|
194
203
|
}
|
|
195
204
|
return next._withContext({ currentMark: ownerId, currentData: source });
|
|
196
205
|
}
|
|
@@ -7,6 +7,14 @@ import {
|
|
|
7
7
|
validateUnitInterval
|
|
8
8
|
} from "../../core/validation.js";
|
|
9
9
|
import { validatePointShape } from "../../grammar/pointShapes.js";
|
|
10
|
+
import {
|
|
11
|
+
requestedRectStyleDetails,
|
|
12
|
+
RECT_STYLE_PROPERTIES
|
|
13
|
+
} from "../../grammar/roundedRect.js";
|
|
14
|
+
import {
|
|
15
|
+
requestedStrokeDetails,
|
|
16
|
+
STROKE_STYLE_PROPERTIES
|
|
17
|
+
} from "../../grammar/strokeStyle.js";
|
|
10
18
|
import { DEFAULT_COLORS } from "../../theme/defaults.js";
|
|
11
19
|
import { normalizeGuides } from "../charts/shared.js";
|
|
12
20
|
|
|
@@ -45,24 +53,30 @@ function plainOptions(value, keys, label, operation = "createBoxPlot") {
|
|
|
45
53
|
return value;
|
|
46
54
|
}
|
|
47
55
|
|
|
48
|
-
function resolveAppearance(value, defaults, label, operation) {
|
|
56
|
+
function resolveAppearance(value, defaults, label, operation, details) {
|
|
49
57
|
const options = plainOptions(
|
|
50
58
|
value,
|
|
51
|
-
|
|
59
|
+
[
|
|
60
|
+
...Object.keys(defaults),
|
|
61
|
+
...(details === requestedRectStyleDetails
|
|
62
|
+
? RECT_STYLE_PROPERTIES
|
|
63
|
+
: STROKE_STYLE_PROPERTIES)
|
|
64
|
+
],
|
|
52
65
|
label,
|
|
53
66
|
operation
|
|
54
67
|
);
|
|
55
|
-
return Object.freeze(
|
|
56
|
-
Object.entries(defaults).map(([key, fallback]) => [
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
return Object.freeze({
|
|
69
|
+
...Object.fromEntries(Object.entries(defaults).map(([key, fallback]) => [
|
|
70
|
+
key,
|
|
71
|
+
options[key] === undefined
|
|
72
|
+
? fallback
|
|
73
|
+
: APPEARANCE_VALIDATORS[key](
|
|
74
|
+
options[key],
|
|
75
|
+
`${operation} ${label}.${key}`
|
|
76
|
+
)
|
|
77
|
+
])),
|
|
78
|
+
...details(options, `${operation} ${label}`)
|
|
79
|
+
});
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
export function resolveBoxPosition(value, label, operation = "createBoxPlot") {
|
|
@@ -122,13 +136,31 @@ export function resolveBoxWidth(value, operation = "createBoxPlot") {
|
|
|
122
136
|
}
|
|
123
137
|
|
|
124
138
|
export function resolveBoxAppearance(value, operation = "createBoxPlot") {
|
|
125
|
-
return resolveAppearance(
|
|
139
|
+
return resolveAppearance(
|
|
140
|
+
value,
|
|
141
|
+
DEFAULT_BOX,
|
|
142
|
+
"box",
|
|
143
|
+
operation,
|
|
144
|
+
requestedRectStyleDetails
|
|
145
|
+
);
|
|
126
146
|
}
|
|
127
147
|
|
|
128
148
|
export function resolveBoxMedianAppearance(value, operation = "createBoxPlot") {
|
|
129
|
-
return resolveAppearance(
|
|
149
|
+
return resolveAppearance(
|
|
150
|
+
value,
|
|
151
|
+
DEFAULT_MEDIAN,
|
|
152
|
+
"median",
|
|
153
|
+
operation,
|
|
154
|
+
requestedStrokeDetails
|
|
155
|
+
);
|
|
130
156
|
}
|
|
131
157
|
|
|
132
158
|
export function resolveBoxOutlierAppearance(value, operation = "createBoxPlot") {
|
|
133
|
-
return resolveAppearance(
|
|
159
|
+
return resolveAppearance(
|
|
160
|
+
value,
|
|
161
|
+
DEFAULT_OUTLIER,
|
|
162
|
+
"outlier",
|
|
163
|
+
operation,
|
|
164
|
+
requestedStrokeDetails
|
|
165
|
+
);
|
|
134
166
|
}
|
|
@@ -161,7 +161,9 @@ export const createBasicCanvas = action(
|
|
|
161
161
|
background: Object.hasOwn(args, "background")
|
|
162
162
|
? args.background
|
|
163
163
|
: DEFAULT_CANVAS.background,
|
|
164
|
-
margin:
|
|
164
|
+
margin: Object.hasOwn(args, "margin")
|
|
165
|
+
? normalizeMargin(args.margin, DEFAULT_MARGIN)
|
|
166
|
+
: DEFAULT_MARGIN,
|
|
165
167
|
size: {
|
|
166
168
|
width: Object.hasOwn(args, "width") ? "explicit" : "auto",
|
|
167
169
|
height: Object.hasOwn(args, "height") ? "explicit" : "auto"
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import {
|
|
3
|
+
validateNonNegativeFinite,
|
|
4
|
+
validatePositiveFinite,
|
|
5
|
+
validateOptionObject
|
|
6
|
+
} from "../../core/validation.js";
|
|
7
|
+
import { resolveGraphicBounds } from "../../layout/canvas.js";
|
|
8
|
+
import { findCanvasGraphic } from
|
|
9
|
+
"../../materialization/graphicHierarchy.js";
|
|
10
|
+
|
|
11
|
+
const OPTIONS = Object.freeze([
|
|
12
|
+
"padding",
|
|
13
|
+
"minPlotWidth",
|
|
14
|
+
"minPlotHeight",
|
|
15
|
+
"iterationLimit",
|
|
16
|
+
"overflow"
|
|
17
|
+
]);
|
|
18
|
+
const SIDES = Object.freeze(["top", "right", "bottom", "left"]);
|
|
19
|
+
const DEFAULTS = Object.freeze({
|
|
20
|
+
padding: 0,
|
|
21
|
+
minPlotWidth: 160,
|
|
22
|
+
minPlotHeight: 120,
|
|
23
|
+
iterationLimit: 32,
|
|
24
|
+
overflow: "error"
|
|
25
|
+
});
|
|
26
|
+
const UNITS_PER_PIXEL = 4;
|
|
27
|
+
|
|
28
|
+
function normalizeOptions(args) {
|
|
29
|
+
validateOptionObject(args, OPTIONS, "fitCanvas", { allowEmpty: true });
|
|
30
|
+
const policy = { ...DEFAULTS, ...args };
|
|
31
|
+
validateNonNegativeFinite(policy.padding, "fitCanvas padding");
|
|
32
|
+
validatePositiveFinite(policy.minPlotWidth, "fitCanvas minPlotWidth");
|
|
33
|
+
validatePositiveFinite(policy.minPlotHeight, "fitCanvas minPlotHeight");
|
|
34
|
+
if (!Number.isInteger(policy.iterationLimit) ||
|
|
35
|
+
policy.iterationLimit <= 0 || policy.iterationLimit > 64) {
|
|
36
|
+
throw new RangeError(
|
|
37
|
+
"fitCanvas iterationLimit must be an integer from 1 through 64."
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
if (!["error", "report"].includes(policy.overflow)) {
|
|
41
|
+
throw new Error(`Unsupported fitCanvas overflow policy "${policy.overflow}".`);
|
|
42
|
+
}
|
|
43
|
+
return policy;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function probe(program, margin) {
|
|
47
|
+
try {
|
|
48
|
+
return { ok: true, program: program.editCanvas({ margin }) };
|
|
49
|
+
} catch (error) {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
message: error instanceof Error ? error.message : String(error)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function marginWith(margin, side, value) {
|
|
58
|
+
return { ...margin, [side]: value };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function minimizeSide(program, margin, side, policy) {
|
|
62
|
+
const current = margin[side];
|
|
63
|
+
const floor = Math.ceil(policy.padding * UNITS_PER_PIXEL) /
|
|
64
|
+
UNITS_PER_PIXEL;
|
|
65
|
+
if (floor > current) {
|
|
66
|
+
const expanded = marginWith(margin, side, floor);
|
|
67
|
+
const result = probe(program, expanded);
|
|
68
|
+
return result.ok
|
|
69
|
+
? { program: result.program, margin: expanded, iterations: 1, issues: [] }
|
|
70
|
+
: {
|
|
71
|
+
program,
|
|
72
|
+
margin,
|
|
73
|
+
iterations: 1,
|
|
74
|
+
issues: [`${side} padding: ${result.message}`]
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let low = Math.ceil(policy.padding * UNITS_PER_PIXEL);
|
|
79
|
+
let high = Math.floor(current * UNITS_PER_PIXEL);
|
|
80
|
+
if (low > high) {
|
|
81
|
+
return { program, margin, iterations: 0, issues: [] };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const cache = new Map();
|
|
85
|
+
const test = units => {
|
|
86
|
+
if (cache.has(units)) return cache.get(units);
|
|
87
|
+
const result = probe(
|
|
88
|
+
program,
|
|
89
|
+
marginWith(margin, side, units / UNITS_PER_PIXEL)
|
|
90
|
+
);
|
|
91
|
+
cache.set(units, result);
|
|
92
|
+
return result;
|
|
93
|
+
};
|
|
94
|
+
let iterations = 0;
|
|
95
|
+
const upper = test(high);
|
|
96
|
+
iterations += 1;
|
|
97
|
+
if (!upper.ok) {
|
|
98
|
+
return { program, margin, iterations, issues: [] };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
while (low < high && iterations < policy.iterationLimit) {
|
|
102
|
+
const middle = Math.floor((low + high) / 2);
|
|
103
|
+
const result = test(middle);
|
|
104
|
+
iterations += 1;
|
|
105
|
+
if (result.ok) high = middle;
|
|
106
|
+
else low = middle + 1;
|
|
107
|
+
}
|
|
108
|
+
const best = test(high);
|
|
109
|
+
const fittedMargin = marginWith(
|
|
110
|
+
margin,
|
|
111
|
+
side,
|
|
112
|
+
high / UNITS_PER_PIXEL
|
|
113
|
+
);
|
|
114
|
+
return {
|
|
115
|
+
program: best.program,
|
|
116
|
+
margin: fittedMargin,
|
|
117
|
+
iterations,
|
|
118
|
+
issues: low === high
|
|
119
|
+
? []
|
|
120
|
+
: [`${side} fitting reached iterationLimit ${policy.iterationLimit}`]
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function fitMargins(program, policy) {
|
|
125
|
+
let probeProgram = program;
|
|
126
|
+
let margin = { ...program.materializationConfigs.canvas.margin };
|
|
127
|
+
let iterations = 0;
|
|
128
|
+
const issues = [];
|
|
129
|
+
for (const side of SIDES) {
|
|
130
|
+
const result = minimizeSide(probeProgram, margin, side, policy);
|
|
131
|
+
probeProgram = result.program;
|
|
132
|
+
margin = result.margin;
|
|
133
|
+
iterations += result.iterations;
|
|
134
|
+
issues.push(...result.issues);
|
|
135
|
+
}
|
|
136
|
+
return { margin, iterations, issues };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function sameMargin(first, second) {
|
|
140
|
+
return SIDES.every(side => first[side] === second[side]);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function samePolicy(first, second) {
|
|
144
|
+
return OPTIONS.every(option => first?.[option] === second[option]);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function layoutSignature(program) {
|
|
148
|
+
const source = JSON.stringify({
|
|
149
|
+
graphicSpec: program.graphicSpec,
|
|
150
|
+
canvas: program.materializationConfigs.canvas,
|
|
151
|
+
guides: program.materializationConfigs.guides,
|
|
152
|
+
title: program.materializationConfigs.title
|
|
153
|
+
});
|
|
154
|
+
let hash = 0xcbf29ce484222325n;
|
|
155
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
156
|
+
hash ^= BigInt(source.charCodeAt(index));
|
|
157
|
+
hash = BigInt.asUintN(64, hash * 0x100000001b3n);
|
|
158
|
+
}
|
|
159
|
+
return hash.toString(16).padStart(16, "0");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export const fitCanvas = action(
|
|
163
|
+
{
|
|
164
|
+
op: "fitCanvas",
|
|
165
|
+
description: "Fit Canvas margins around existing layout resources."
|
|
166
|
+
},
|
|
167
|
+
function (args = {}) {
|
|
168
|
+
const policy = normalizeOptions(args);
|
|
169
|
+
const canvas = findCanvasGraphic(this);
|
|
170
|
+
if (canvas?.type !== "canvas" ||
|
|
171
|
+
this.materializationConfigs.canvas?.margin === undefined) {
|
|
172
|
+
throw new Error("fitCanvas requires an existing Canvas.");
|
|
173
|
+
}
|
|
174
|
+
const previous = this.materializationConfigs.fitting;
|
|
175
|
+
if (samePolicy(previous?.policy, policy) &&
|
|
176
|
+
previous?.result?.signature === layoutSignature(this)) {
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
const sourceMargin = this.materializationConfigs.canvas.margin;
|
|
180
|
+
const fitted = fitMargins(this, policy);
|
|
181
|
+
let next = sameMargin(sourceMargin, fitted.margin)
|
|
182
|
+
? this
|
|
183
|
+
: this.editCanvas({ margin: fitted.margin });
|
|
184
|
+
const plot = resolveGraphicBounds(next);
|
|
185
|
+
if (plot.width < policy.minPlotWidth) {
|
|
186
|
+
fitted.issues.push(
|
|
187
|
+
`plot width ${plot.width} is smaller than minPlotWidth ${policy.minPlotWidth}`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
if (plot.height < policy.minPlotHeight) {
|
|
191
|
+
fitted.issues.push(
|
|
192
|
+
`plot height ${plot.height} is smaller than minPlotHeight ${policy.minPlotHeight}`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
if (fitted.issues.length > 0 && policy.overflow === "error") {
|
|
196
|
+
throw new Error(`fitCanvas overflow: ${fitted.issues.join("; ")}.`);
|
|
197
|
+
}
|
|
198
|
+
next = next._withMaterializationConfig(["fitting"], {
|
|
199
|
+
policy,
|
|
200
|
+
result: {
|
|
201
|
+
status: fitted.issues.length === 0 ? "fit" : "overflow",
|
|
202
|
+
margin: fitted.margin,
|
|
203
|
+
plot: { width: plot.width, height: plot.height },
|
|
204
|
+
iterations: fitted.iterations,
|
|
205
|
+
issues: fitted.issues,
|
|
206
|
+
signature: layoutSignature(next)
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
return next;
|
|
210
|
+
}
|
|
211
|
+
);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { createBasicCanvas, createCanvas, editCanvas } from "./actions.js";
|
|
2
|
+
import { fitCanvas } from "./fitting.js";
|
|
2
3
|
|
|
3
4
|
export function registerCanvasActions(ProgramClass) {
|
|
4
5
|
ProgramClass.prototype.editCanvas = editCanvas;
|
|
5
6
|
ProgramClass.prototype.createCanvas = createCanvas;
|
|
7
|
+
ProgramClass.prototype.fitCanvas = fitCanvas;
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
export function registerBasicCanvasActions(ProgramClass) {
|
|
@@ -8,9 +8,13 @@ import {
|
|
|
8
8
|
applyMaterializationPlan,
|
|
9
9
|
planScaleGuideRematerialization
|
|
10
10
|
} from "../../materialization/dependencies.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
getMarkMaterializationStep,
|
|
13
|
+
getSourceDependentMarkSteps
|
|
14
|
+
} from "../../materialization/marks/index.js";
|
|
12
15
|
import { buildMaterializationPlan } from "../../materialization/planner.js";
|
|
13
16
|
import {
|
|
17
|
+
CATEGORY_ORDER_CHANNELS,
|
|
14
18
|
normalizeCategoryOrder,
|
|
15
19
|
resolveCategoryOrder
|
|
16
20
|
} from "../../grammar/categoryOrder.js";
|
|
@@ -19,8 +23,8 @@ const ORDER_OPTIONS = Object.freeze(["target", "channel", "values", "by", "direc
|
|
|
19
23
|
const REMOVE_OPTIONS = Object.freeze(["target", "channel"]);
|
|
20
24
|
|
|
21
25
|
function resolveAssignment(program, args, operation, { activeOnly = false } = {}) {
|
|
22
|
-
if (!
|
|
23
|
-
throw new Error(`${operation} channel must be x or
|
|
26
|
+
if (!CATEGORY_ORDER_CHANNELS.includes(args.channel)) {
|
|
27
|
+
throw new Error(`${operation} channel must be x, y, or theta.`);
|
|
24
28
|
}
|
|
25
29
|
const requested = args.target === undefined
|
|
26
30
|
? undefined
|
|
@@ -55,14 +59,22 @@ function resolveAssignment(program, args, operation, { activeOnly = false } = {}
|
|
|
55
59
|
|
|
56
60
|
function rematerializeAssignment(program, scaleId) {
|
|
57
61
|
const consumers = findScaleConsumers(program, scaleId);
|
|
62
|
+
const directMarks = consumers.map(consumer =>
|
|
63
|
+
getMarkMaterializationStep(program, consumer.layer)
|
|
64
|
+
);
|
|
58
65
|
return applyMaterializationPlan(program, buildMaterializationPlan({
|
|
59
66
|
scales: [{
|
|
60
67
|
op: "rematerializeScale",
|
|
61
68
|
args: { id: scaleId, guides: false, marks: false }
|
|
62
69
|
}],
|
|
63
|
-
marks:
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
marks: [
|
|
71
|
+
...directMarks,
|
|
72
|
+
...directMarks.flatMap(step =>
|
|
73
|
+
step === undefined
|
|
74
|
+
? []
|
|
75
|
+
: getSourceDependentMarkSteps(program, step.args.id)
|
|
76
|
+
)
|
|
77
|
+
],
|
|
66
78
|
guides: planScaleGuideRematerialization(program, scaleId)
|
|
67
79
|
}));
|
|
68
80
|
}
|