ggaction 0.0.18 → 0.0.19
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 +50 -0
- package/README.md +6 -2
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/package.json +1 -1
- package/src/actions/canvas/fitting.js +6 -2
- package/src/actions/charts/heatmap.js +4 -3
- package/src/actions/data/derived.js +11 -1
- package/src/actions/data/edit.js +2 -0
- package/src/actions/data/index.js +4 -0
- package/src/actions/data/regression.js +24 -16
- package/src/actions/data/regressionStatistics.js +73 -0
- package/src/actions/data/shared.js +5 -2
- package/src/actions/distributions/revision.js +24 -18
- package/src/actions/encodings/remove.js +11 -6
- package/src/actions/encodings/shared.js +26 -24
- package/src/actions/facets/actions.js +18 -7
- package/src/actions/facets/guides.js +2 -1
- package/src/actions/guides/axes/axes.js +30 -11
- package/src/actions/guides/axes/axis.js +58 -8
- package/src/actions/guides/axes/components.js +13 -13
- package/src/actions/guides/axes/edit.js +36 -24
- package/src/actions/guides/axes/labels.js +37 -27
- package/src/actions/guides/axes/lines.js +22 -20
- package/src/actions/guides/axes/remove.js +10 -6
- package/src/actions/guides/axes/tickGroups.js +8 -7
- package/src/actions/guides/axes/ticks.js +31 -22
- package/src/actions/guides/axes/titles.js +53 -60
- package/src/actions/guides/facadeAxes.js +33 -24
- package/src/actions/guides/facadeLegend.js +3 -3
- package/src/actions/guides/grids/grid.js +1 -1
- package/src/actions/guides/grids/resolve.js +48 -51
- package/src/actions/guides/legends/blocks.js +43 -19
- package/src/actions/guides/legends/categorical/actions.js +30 -7
- package/src/actions/guides/legends/categorical/components.js +3 -10
- package/src/actions/guides/legends/categorical/index.js +2 -0
- package/src/actions/guides/legends/categorical/layout.js +9 -13
- package/src/actions/guides/legends/categorical/options.js +8 -2
- package/src/actions/guides/legends/categorical/recipes.js +7 -3
- package/src/actions/guides/legends/categorical/resolve.js +9 -2
- package/src/actions/guides/legends/categorical/symbols.js +22 -8
- package/src/actions/guides/legends/colorSize.js +110 -0
- package/src/actions/guides/legends/combine.js +37 -0
- package/src/actions/guides/legends/continuous/colorSymbols.js +1 -1
- package/src/actions/guides/legends/continuous/common.js +1 -1
- package/src/actions/guides/legends/continuous/gradient.js +27 -13
- package/src/actions/guides/legends/continuous/opacity.js +16 -11
- package/src/actions/guides/legends/creation.js +2 -0
- package/src/actions/guides/legends/edit.js +1 -1
- package/src/actions/guides/legends/focused.js +6 -5
- package/src/actions/guides/legends/lane.js +1 -1
- package/src/actions/guides/legends/lifecycle.js +2 -1
- package/src/actions/guides/legends/remove.js +6 -3
- package/src/actions/guides/legends/size.js +16 -6
- package/src/actions/guides/legends/strokeWidth.js +2 -0
- package/src/actions/guides/legends/target.js +2 -79
- package/src/actions/guides/legends/transition.js +24 -5
- package/src/actions/guides/tickValues.js +8 -5
- package/src/actions/marks/bar/edit.js +23 -12
- package/src/actions/marks/bar/materialize.js +6 -1
- package/src/actions/marks/point/materialize.js +5 -1
- package/src/actions/marks/remove.js +10 -10
- package/src/actions/primitives/createGraphics.js +3 -2
- package/src/actions/primitives/editGraphics.js +15 -11
- package/src/actions/primitives/graphicProperties.js +1 -6
- package/src/actions/primitives/semanticAction.js +27 -1
- package/src/actions/primitives/semanticValidation/guide.js +11 -1
- package/src/actions/primitives/semanticValidation/layer.js +4 -0
- package/src/actions/regression/edit.js +15 -3
- package/src/actions/scales/channels.js +2 -2
- package/src/actions/scales/create.js +3 -3
- package/src/actions/scales/definitions.js +1 -1
- package/src/actions/scales/edit.js +1 -1
- package/src/actions/scales/editPolicy.js +3 -0
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/quantitativeColor.js +8 -3
- package/src/actions/theme/reconcile.js +50 -23
- package/src/core/compositionState.js +2 -2
- package/src/core/graphicProperties.js +1 -0
- package/src/core/programState.js +4 -0
- package/src/core/resourceReferences.js +2 -2
- package/src/core/validation.js +9 -0
- package/src/core/vocabulary.js +3 -0
- package/src/grammar/axisTextPlacement.js +1 -0
- package/src/grammar/colorSizeLegend.js +39 -0
- package/src/grammar/computed.js +23 -1
- package/src/grammar/datasetSchema.js +10 -2
- package/src/grammar/displayLabels.js +8 -0
- package/src/grammar/facets/guides.js +54 -31
- package/src/grammar/facets/legendIntent.js +65 -0
- package/src/grammar/guideDash.js +1 -0
- package/src/grammar/legendBlocks.js +1 -0
- package/src/grammar/legendBorder.js +9 -0
- package/src/grammar/legendClip.js +19 -0
- package/src/grammar/legendCombinations.js +45 -0
- package/src/grammar/legendGradient.js +9 -0
- package/src/grammar/legendGroups.js +1 -1
- package/src/grammar/legendOpacity.js +6 -0
- package/src/grammar/pointShapes.js +16 -2
- package/src/grammar/regression/derive.js +9 -1
- package/src/grammar/regression/models.js +60 -0
- package/src/grammar/regression/parameters.js +9 -1
- package/src/grammar/regression/statistics.js +64 -0
- package/src/grammar/scales/color.js +1 -1
- package/src/grammar/scales/colorConsumers.js +1 -1
- package/src/grammar/scales/definition.js +1 -1
- package/src/grammar/scales/types.js +1 -1
- package/src/grammar/schemas/concreteGraphic.js +3 -0
- package/src/grammar/schemas/graphic.js +3 -0
- package/src/grammar/schemas/graphicBounds.js +31 -7
- package/src/grammar/schemas/semanticPath.js +17 -2
- package/src/grammar/summary.js +1 -1
- package/src/grammar/timeUnit.js +6 -4
- package/src/grammar/transformTopology.js +1 -0
- package/src/grammar/transforms.js +11 -1
- package/src/inspectionDescriptors.js +1 -1
- package/src/layout/guideCollisions.js +9 -1
- package/src/layout/legendGroup.js +1 -1
- package/src/materialization/compositionSnapshot.js +2 -7
- package/src/materialization/coordinateBounds.js +17 -4
- package/src/materialization/facetGuides/edit.js +30 -0
- package/src/materialization/facetGuides/layout.js +1 -1
- package/src/materialization/facetGuides/owner.js +125 -0
- package/src/materialization/facetGuides/ownerLayout.js +71 -0
- package/src/materialization/facetGuides/placement.js +52 -6
- package/src/materialization/facetGuides/preparation.js +7 -0
- package/src/materialization/facets.js +1 -1
- package/src/materialization/graphicHierarchy.js +5 -1
- package/src/materialization/graphicTranslation.js +73 -0
- package/src/materialization/guides/layout.js +1 -1
- package/src/materialization/guides/resources.js +1 -1
- package/src/materialization/legendBorder.js +28 -0
- package/src/materialization/legendClip.js +76 -0
- package/src/materialization/legendGroups.js +1 -1
- package/src/materialization/legends.js +1 -1
- package/src/materialization/scaleGuideDependencies.js +13 -14
- package/src/materialization/scales/map.js +3 -0
- package/src/materialization/scales/resolve.js +2 -0
- package/src/materialization/typography.js +10 -2
- package/src/persistence/validation.js +174 -1
- package/src/persistence.js +1 -1
- package/src/renderers/canvas/index.js +20 -10
- package/src/renderers/canvas/isolated.js +166 -0
- package/src/renderers/canvas/limits.js +3 -0
- package/src/renderers/canvas/rect.js +1 -1
- package/src/renderers/pdf.js +2 -2
- package/src/renderers/png.js +2 -2
- package/src/renderers/svg.js +10 -3
- package/src/selectors/axes.js +1 -0
- package/src/selectors/graphicCapacity.js +38 -0
- package/src/version.js +1 -1
- package/types/basic.d.ts +2 -0
- package/types/index.d.ts +5 -0
- package/types/program.d.ts +206 -61
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { resolvePrimitiveGraphicBounds } from "../../grammar/schemas/graphicBounds.js";
|
|
2
|
+
import { walkGraphicTreeEvents } from "../../grammar/schemas/graphicTree.js";
|
|
3
|
+
import { MAX_RASTER_DIMENSION, MAX_RASTER_PIXELS } from "./limits.js";
|
|
4
|
+
|
|
5
|
+
function canvasFactory(context, supplied) {
|
|
6
|
+
if (supplied !== undefined) return supplied;
|
|
7
|
+
const canvas = context.canvas;
|
|
8
|
+
if (typeof canvas?.ownerDocument?.createElement === "function") {
|
|
9
|
+
return (width, height) => {
|
|
10
|
+
const source = canvas.ownerDocument.createElement("canvas");
|
|
11
|
+
source.width = width;
|
|
12
|
+
source.height = height;
|
|
13
|
+
return source;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
if (canvas?.constructor && canvas.constructor !== Object) {
|
|
17
|
+
return (width, height) => new canvas.constructor(width, height);
|
|
18
|
+
}
|
|
19
|
+
throw new TypeError("Isolated glyph painting requires a compatible Canvas surface.");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function requireSurface(surface) {
|
|
23
|
+
const context = surface?.getContext?.("2d");
|
|
24
|
+
if (!context || ["save", "restore", "scale", "translate", "beginPath", "fillRect",
|
|
25
|
+
"fill", "stroke", "moveTo", "lineTo", "bezierCurveTo", "closePath", "setLineDash"]
|
|
26
|
+
.some(method => typeof context[method] !== "function")) {
|
|
27
|
+
throw new TypeError("Isolated glyph painting requires a compatible Canvas 2D surface.");
|
|
28
|
+
}
|
|
29
|
+
return context;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// This is renderer-private state: no surface or backend object enters graphicSpec.
|
|
33
|
+
export function prepareIsolatedGlyphs(target, context, { pixelRatio = 1, createCanvas } = {}) {
|
|
34
|
+
const requested = Object.values(target.graphicSpec.objects).some(object =>
|
|
35
|
+
["normal","difference"].includes(object.properties?.blend) ||
|
|
36
|
+
object.items?.some(item => ["normal","difference"].includes(item.properties?.blend)));
|
|
37
|
+
if (!requested) return undefined;
|
|
38
|
+
if (typeof context.drawImage !== "function" || typeof context.globalCompositeOperation !== "string") {
|
|
39
|
+
throw new TypeError("Isolated glyph painting requires Canvas drawImage and compositing support.");
|
|
40
|
+
}
|
|
41
|
+
const factory = canvasFactory(context, createCanvas);
|
|
42
|
+
let surface;
|
|
43
|
+
try {
|
|
44
|
+
surface = factory(1, 1);
|
|
45
|
+
const source = requireSurface(surface);
|
|
46
|
+
const hasGradient = Object.values(target.graphicSpec.objects).some(object =>
|
|
47
|
+
[object, ...(object.items ?? [])].some(item =>
|
|
48
|
+
["normal","difference"].includes(item.properties?.blend) && item.properties.fill?.type === "linear-gradient"));
|
|
49
|
+
const normal=Object.values(target.graphicSpec.objects).some(object=>
|
|
50
|
+
[object,...(object.items??[])].some(item=>item.properties?.blend==="normal"));
|
|
51
|
+
if(normal){
|
|
52
|
+
if(typeof source.setTransform!=="function")throw new TypeError("Normal glyph opacity requires Canvas transform support.");
|
|
53
|
+
source.save();try{
|
|
54
|
+
source.globalCompositeOperation="destination-in";
|
|
55
|
+
if(source.globalCompositeOperation!=="destination-in")throw new TypeError("Normal glyph opacity requires source attenuation.");
|
|
56
|
+
}finally{source.restore();}
|
|
57
|
+
const circle=Object.values(target.graphicSpec.objects).some(object=>
|
|
58
|
+
[object,...(object.items??[])].some(item=>item.properties?.blend==="normal"&&(item.type??object.type)==="circle"));
|
|
59
|
+
if(circle&&typeof source.arc!=="function")throw new TypeError("Normal circle painting requires Canvas arcs.");
|
|
60
|
+
}
|
|
61
|
+
if (hasGradient && (typeof source.createLinearGradient !== "function" ||
|
|
62
|
+
typeof source.createLinearGradient(0, 0, 1, 0)?.addColorStop !== "function")) {
|
|
63
|
+
throw new TypeError("Isolated gradient painting requires Canvas linear gradients.");
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
throw new TypeError("Isolated glyph painting requires a compatible Canvas surface.", {cause:error});
|
|
67
|
+
}
|
|
68
|
+
context.save();
|
|
69
|
+
try {
|
|
70
|
+
const modes=new Set();
|
|
71
|
+
for(const object of Object.values(target.graphicSpec.objects))for(const item of [object,...(object.items??[])]){
|
|
72
|
+
if(item.properties?.blend!==undefined)modes.add(item.properties.blend==="normal"?"source-over":item.properties.blend);
|
|
73
|
+
}
|
|
74
|
+
for(const mode of modes){
|
|
75
|
+
context.globalCompositeOperation=mode;
|
|
76
|
+
if(context.globalCompositeOperation!==mode)throw new TypeError(`Canvas context does not support ${mode} compositing.`);
|
|
77
|
+
}
|
|
78
|
+
} finally { context.restore(); }
|
|
79
|
+
|
|
80
|
+
const plans = new Map();
|
|
81
|
+
// Ordered root graphics may be siblings of the Canvas, not its descendants.
|
|
82
|
+
const frames = [{offset:[0,0],clip:{left:0,top:0,right:target.width,bottom:target.height}}];
|
|
83
|
+
function plan(id, object, type) {
|
|
84
|
+
const properties = object.properties;
|
|
85
|
+
if (!["normal","difference"].includes(properties?.blend)) return;
|
|
86
|
+
const bounds = resolvePrimitiveGraphicBounds(type, properties);
|
|
87
|
+
if (!bounds) throw new Error(`Graphic "${id}" requires finite isolated paint bounds.`);
|
|
88
|
+
const {offset: [x, y], clip} = frames.at(-1);
|
|
89
|
+
const clipped = {
|
|
90
|
+
left: Math.max(bounds.left + x, clip.left),
|
|
91
|
+
right: Math.min(bounds.right + x, clip.right),
|
|
92
|
+
top: Math.max(bounds.top + y, clip.top),
|
|
93
|
+
bottom: Math.min(bounds.bottom + y, clip.bottom)
|
|
94
|
+
};
|
|
95
|
+
if (clipped.right <= clipped.left || clipped.bottom <= clipped.top) {
|
|
96
|
+
plans.set(id, {empty: true});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// Align to destination pixels, including fractional nested translations.
|
|
100
|
+
const left = Math.max(Math.floor(clip.left * pixelRatio), Math.floor(clipped.left * pixelRatio) - 1);
|
|
101
|
+
const top = Math.max(Math.floor(clip.top * pixelRatio), Math.floor(clipped.top * pixelRatio) - 1);
|
|
102
|
+
const right = Math.min(Math.ceil(clip.right * pixelRatio), Math.ceil(clipped.right * pixelRatio) + 1);
|
|
103
|
+
const bottom = Math.min(Math.ceil(clip.bottom * pixelRatio), Math.ceil(clipped.bottom * pixelRatio) + 1);
|
|
104
|
+
const width = right - left, height = bottom - top;
|
|
105
|
+
if (![width, height].every(Number.isSafeInteger) || width < 1 || height < 1 ||
|
|
106
|
+
width > MAX_RASTER_DIMENSION || height > MAX_RASTER_DIMENSION ||
|
|
107
|
+
width * height > MAX_RASTER_PIXELS) {
|
|
108
|
+
throw new RangeError(`Graphic "${id}" isolated paint surface exceeds the raster allocation limit.`);
|
|
109
|
+
}
|
|
110
|
+
plans.set(id, {width, height, left: left / pixelRatio - x, top: top / pixelRatio - y});
|
|
111
|
+
}
|
|
112
|
+
walkGraphicTreeEvents(target.graphicSpec, {
|
|
113
|
+
enter({id, object}) {
|
|
114
|
+
if (object.type === "canvas") {
|
|
115
|
+
const parent = frames.at(-1);
|
|
116
|
+
const offset = id === target.canvasId ? [0, 0] :
|
|
117
|
+
[parent.offset[0] + object.properties.x, parent.offset[1] + object.properties.y];
|
|
118
|
+
const properties = object.properties;
|
|
119
|
+
const local = {left: offset[0], top: offset[1],
|
|
120
|
+
right: offset[0] + properties.width, bottom: offset[1] + properties.height};
|
|
121
|
+
const clip = {
|
|
122
|
+
left: Math.max(parent.clip.left, local.left), top: Math.max(parent.clip.top, local.top),
|
|
123
|
+
right: Math.min(parent.clip.right, local.right), bottom: Math.min(parent.clip.bottom, local.bottom)
|
|
124
|
+
};
|
|
125
|
+
frames.push({offset, clip});
|
|
126
|
+
}
|
|
127
|
+
if (!object.items) plan(id, object, object.type);
|
|
128
|
+
},
|
|
129
|
+
item({id, object, owner}) { plan(id, object, object.type ?? owner.type); },
|
|
130
|
+
exit({object}) { if (object.type === "canvas") frames.pop(); }
|
|
131
|
+
});
|
|
132
|
+
return {plans, surface, pixelRatio};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Paint fill and outline together with opacity 1, then composite the source once.
|
|
136
|
+
export function drawIsolatedGlyph(context, id, graphic, draw, prepared) {
|
|
137
|
+
const plan = prepared?.plans.get(id);
|
|
138
|
+
if (!plan) throw new Error(`Graphic "${id}" has no prepared isolated paint source.`);
|
|
139
|
+
if (plan.empty) return;
|
|
140
|
+
const {surface, pixelRatio} = prepared;
|
|
141
|
+
surface.width = plan.width;
|
|
142
|
+
surface.height = plan.height;
|
|
143
|
+
const source = requireSurface(surface);
|
|
144
|
+
source.scale(pixelRatio, pixelRatio);
|
|
145
|
+
source.translate(-plan.left, -plan.top);
|
|
146
|
+
const {blend, opacity = 1, ...properties} = graphic.properties;
|
|
147
|
+
draw(source, id, {...graphic, properties: {...properties, opacity: 1}});
|
|
148
|
+
if(blend==="normal"){
|
|
149
|
+
// Attenuate the complete source once. drawImage(globalAlpha) introduces
|
|
150
|
+
// additional native image-alpha quantization; source masking avoids it.
|
|
151
|
+
source.save();
|
|
152
|
+
try{
|
|
153
|
+
source.setTransform(1,0,0,1,0,0);
|
|
154
|
+
source.globalCompositeOperation="destination-in";
|
|
155
|
+
source.globalAlpha=opacity;
|
|
156
|
+
source.fillStyle="#fff";
|
|
157
|
+
source.fillRect(0,0,plan.width,plan.height);
|
|
158
|
+
}finally{source.restore();}
|
|
159
|
+
}
|
|
160
|
+
context.save();
|
|
161
|
+
try {
|
|
162
|
+
context.globalAlpha = blend==="normal"?1:opacity;
|
|
163
|
+
context.globalCompositeOperation = blend==="normal"?"source-over":blend;
|
|
164
|
+
context.drawImage(surface, plan.left, plan.top, plan.width / pixelRatio, plan.height / pixelRatio);
|
|
165
|
+
} finally { context.restore(); }
|
|
166
|
+
}
|
package/src/renderers/pdf.js
CHANGED
|
@@ -73,10 +73,10 @@ export async function renderToPDFBuffer(program, options = {}) {
|
|
|
73
73
|
const target = resolveGraphicRenderTarget(graphicSpec);
|
|
74
74
|
requirePDFPageDimensions(target);
|
|
75
75
|
preflightCanvasGraphicSpec(target);
|
|
76
|
-
const { PDFDocument } = await loadNativeBackend();
|
|
76
|
+
const { PDFDocument, createCanvas } = await loadNativeBackend();
|
|
77
77
|
const document = new PDFDocument(metadata);
|
|
78
78
|
const context = document.beginPage(target.width, target.height);
|
|
79
|
-
drawResolvedGraphicSpec(target, context);
|
|
79
|
+
drawResolvedGraphicSpec(target, context, {createCanvas});
|
|
80
80
|
document.endPage();
|
|
81
81
|
const buffer = document.close();
|
|
82
82
|
return Object.freeze({
|
package/src/renderers/png.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { renderCanvasProgram } from "./canvas/index.js";
|
|
4
4
|
import { validateRendererOptions } from "./options.js";
|
|
5
5
|
import { loadNativeBackend } from "./nativeBackend.js";
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ export async function renderToPNGBuffer(program, options = {}) {
|
|
|
12
12
|
const { pixelRatio = 1 } = options;
|
|
13
13
|
const { createCanvas } = await loadNativeBackend();
|
|
14
14
|
const canvas = createCanvas(1, 1);
|
|
15
|
-
|
|
15
|
+
renderCanvasProgram(program, canvas.getContext("2d"), { pixelRatio }, createCanvas);
|
|
16
16
|
// Canvas drawing is synchronous; encoding uses the native async worker.
|
|
17
17
|
const buffer = await canvas.encode("png");
|
|
18
18
|
return Object.freeze({
|
package/src/renderers/svg.js
CHANGED
|
@@ -265,7 +265,8 @@ function serializeRect(state, graphicId, properties) {
|
|
|
265
265
|
["stroke", properties.stroke],
|
|
266
266
|
["stroke-width", formatNumber(strokeWidth)],
|
|
267
267
|
...strokeDetailAttributes(properties, strokeWidth),
|
|
268
|
-
["opacity", formatNumber(requireOpacity(properties, graphicId))]
|
|
268
|
+
["opacity", formatNumber(requireOpacity(properties, graphicId))],
|
|
269
|
+
["style", properties.blend === "difference" ? "mix-blend-mode:difference" : undefined]
|
|
269
270
|
]);
|
|
270
271
|
}
|
|
271
272
|
|
|
@@ -362,7 +363,8 @@ function serializePath(state, graphicId, properties) {
|
|
|
362
363
|
["stroke-dasharray", strokeDash.length === 0
|
|
363
364
|
? undefined
|
|
364
365
|
: strokeDash.map(formatNumber).join(" ")],
|
|
365
|
-
["opacity", formatNumber(requireOpacity(properties, graphicId))]
|
|
366
|
+
["opacity", formatNumber(requireOpacity(properties, graphicId))],
|
|
367
|
+
["style", properties.blend === "difference" ? "mix-blend-mode:difference" : undefined]
|
|
366
368
|
]);
|
|
367
369
|
}
|
|
368
370
|
|
|
@@ -582,6 +584,10 @@ export function renderToSVG(program, options = {}) {
|
|
|
582
584
|
resourceNamespace:
|
|
583
585
|
resolvedOptions.resourceNamespace ?? resourceNamespace(graphicSpec)
|
|
584
586
|
};
|
|
587
|
+
walkGraphicTreeEvents(graphicSpec, {
|
|
588
|
+
enter({object}) { if (object.properties?.blend === "difference") state.hasBlend = true; },
|
|
589
|
+
item({object}) { if (object.properties?.blend === "difference") state.hasBlend = true; }
|
|
590
|
+
});
|
|
585
591
|
const body = serializeBody(
|
|
586
592
|
graphicSpec,
|
|
587
593
|
rootId,
|
|
@@ -605,6 +611,7 @@ export function renderToSVG(program, options = {}) {
|
|
|
605
611
|
["xmlns", SVG_NAMESPACE],
|
|
606
612
|
["width", formatNumber(width)],
|
|
607
613
|
["height", formatNumber(height)],
|
|
608
|
-
["viewBox", `0 0 ${formatNumber(width)} ${formatNumber(height)}`]
|
|
614
|
+
["viewBox", `0 0 ${formatNumber(width)} ${formatNumber(height)}`],
|
|
615
|
+
["style", state.hasBlend ? "isolation:isolate" : undefined]
|
|
609
616
|
], `${accessible}${definitions}${body}`);
|
|
610
617
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const p=Object.freeze(["x","y"]),w=Object.freeze(["line","ticks","labels","title"]);function x(n){const e=n.semanticSpec?.guides?.axis??{},a=(n.guideConfigs??n.materializationConfigs?.guides)?.axis??{};return[...new Set([...Object.keys(e),...Object.keys(a)])].flatMap(i=>{const s=Object.hasOwn(e,i)?e[i]:void 0,c=Object.hasOwn(a,i)?a[i]:void 0,d=p.includes(i)?i:s?.channel;if(!p.includes(d))return[];const t=[...new Set((n.semanticSpec.layers??[]).filter(f=>f.encoding?.[d]?.scale===s?.scale).map(f=>f.coordinate).filter(Boolean))];return[{id:i,channel:d,semantic:s,config:c,scale:s?.scale,coordinate:s?.coordinate??(t.length===1?t[0]:void 0),named:i!==d,sides:[...new Set(i!==d?[s.position]:w.map(f=>c?.[f]?.position).filter(Boolean))]}]})}function g(n,e){return n===e?{}:{target:n}}function O(n,e,a={},{create:i=!1,whole:s=!1}={}){const c=x(n),d=c.filter(r=>r.channel===e);let t;if(Object.hasOwn(a,"target")){const r=a.target;if(t=c.find(l=>l.id===r),t===void 0||t.channel!==e)throw new Error(`Unknown ${e}-axis target "${r}".`)}else if(t=d.find(r=>r.id===e),t===void 0){if(d.length>1)throw new Error(`Multiple ${e}-axes require an explicit target.`);t=d[0]}const{target:f,...o}=a,u=t?.id??e;if(!t?.named&&o.position!==void 0&&c.some(r=>r.named)){const r=[...new Set((n.semanticSpec.layers??[]).filter(l=>l.encoding?.[e]?.scale===(o.scale??t?.scale??e)).map(l=>l.coordinate).filter(Boolean))];m(n,{id:u,channel:e,coordinate:t?.coordinate??(r.length===1?r[0]:void 0),position:o.position})}if(t?.named){if(o.scale!==void 0&&o.scale!==t.scale||o.coordinate!==void 0&&o.coordinate!==t.coordinate)throw new Error(`Axis "${u}" resource binding does not match the requested filter.`);if(!s&&o.position!==void 0&&o.position!==t.semantic.position)throw new Error(`Named axis "${u}" components share one side; edit the complete axis position.`);i&&(o.scale??=t.scale),s||(o.position??=t.semantic.position)}return{id:u,owner:t,options:o}}function m(n,{id:e,channel:a,coordinate:i,position:s}){for(const c of x(n))if(c.id!==e&&c.coordinate===i&&c.sides.includes(s))throw new Error(`Axis side "${s}" is already occupied on coordinate "${i}".`)}function b(n,e=n){const a=["Line","Ticks","Labels","Title"];if(e!==n)return a.map(s=>`axis_${e}__${s}`);const i=n==="radius"?"radial":n;return a.map(s=>`${i}Axis${s}`)}export{m as assertCartesianAxisSideAvailable,b as axisGraphicIds,g as axisTargetArgs,x as cartesianAxisOwners,O as selectCartesianAxis};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { annotateError } from "../core/diagnostics.js";
|
|
2
|
+
import { findLayer } from "./layers.js";
|
|
3
|
+
import { findDataset } from "./datasets.js";
|
|
4
|
+
|
|
5
|
+
export function namespaceGraphicId(namespace, id) {
|
|
6
|
+
const encode = value => Array.from(value, character =>
|
|
7
|
+
character.codePointAt(0).toString(16).padStart(6, "0")
|
|
8
|
+
).join("");
|
|
9
|
+
return `g${encode(namespace)}_${id}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function pointOwners(program, id) {
|
|
13
|
+
const layer = findLayer(program, id);
|
|
14
|
+
const dataset = layer?.mark?.type === "point" ? findDataset(program, layer.data) : undefined;
|
|
15
|
+
const owners = Array.isArray(dataset?.values) ? [{ layer, dataset }] : [];
|
|
16
|
+
for (const childId of program.compositionSpec?.children ?? []) {
|
|
17
|
+
const prefix = namespaceGraphicId(`${program.compositionSpec.id}-${childId}`, "");
|
|
18
|
+
const child = program.children[childId];
|
|
19
|
+
if (!id.startsWith(prefix) || child === undefined) continue;
|
|
20
|
+
const localId = id.slice(prefix.length);
|
|
21
|
+
if (child.graphicSpec.objects[localId] === undefined) continue;
|
|
22
|
+
owners.push(...pointOwners(child, localId));
|
|
23
|
+
}
|
|
24
|
+
return owners;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function resolveGraphicItemCapacity(program, { id, label, type }) {
|
|
28
|
+
const owners = pointOwners(program, id);
|
|
29
|
+
if (owners.length > 1) throw annotateError(new Error("Point graphic ownership is ambiguous."), {
|
|
30
|
+
code: "ambiguous-resource", resourceId: id, candidates: owners.map(owner => owner.layer.id)
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
pointOwned: owners.length === 1,
|
|
34
|
+
rawCount: owners[0]?.dataset.values.length,
|
|
35
|
+
graphicType: type ?? program.graphicSpec.objects[id]?.type,
|
|
36
|
+
label
|
|
37
|
+
};
|
|
38
|
+
}
|
package/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Must match package.json; enforced by the snapshot producer-version test.
|
|
2
|
-
export const packageVersion = "0.0.
|
|
2
|
+
export const packageVersion = "0.0.19";
|
package/types/basic.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type {
|
|
|
4
4
|
AxisLabelOptions,
|
|
5
5
|
AxisLineStyleOptions,
|
|
6
6
|
AxisTickOptions,
|
|
7
|
+
CartesianGuideDashMap,
|
|
7
8
|
AxisTicksAndLabelsOptions,
|
|
8
9
|
AxisTitleOptions,
|
|
9
10
|
DisplayLabelMap,
|
|
@@ -15,6 +16,7 @@ export type {
|
|
|
15
16
|
CanvasOptions,
|
|
16
17
|
ColorEncodingOptions,
|
|
17
18
|
CompleteAxisOptions,
|
|
19
|
+
NamedCompleteAxis,
|
|
18
20
|
CreateAxesOptions,
|
|
19
21
|
CreateBarPlotOptions,
|
|
20
22
|
BarRangePositionChannel,
|
package/types/index.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export type {
|
|
|
21
21
|
AxisLabelStyleOptions,
|
|
22
22
|
AxisLineStyleOptions,
|
|
23
23
|
AxisTickOptions,
|
|
24
|
+
CartesianGuideDashMap,
|
|
24
25
|
AxisTicksAndLabelsOptions,
|
|
25
26
|
AxisTickStyleOptions,
|
|
26
27
|
AxisTitleOptions,
|
|
@@ -65,6 +66,7 @@ export type {
|
|
|
65
66
|
ShapeEncodingOptions,
|
|
66
67
|
AngleEncodingOptions,
|
|
67
68
|
CompleteAxisOptions,
|
|
69
|
+
NamedCompleteAxis,
|
|
68
70
|
ConcatCompositionSpec,
|
|
69
71
|
CompositionAlign,
|
|
70
72
|
CompositionOptions,
|
|
@@ -219,6 +221,7 @@ export type {
|
|
|
219
221
|
DurationWindowFrame,
|
|
220
222
|
DurationWindowUnit,
|
|
221
223
|
DatasetRegressionTransform,
|
|
224
|
+
DatasetRegressionStatisticsTransform,
|
|
222
225
|
DatasetScalar,
|
|
223
226
|
StatisticalWeight,
|
|
224
227
|
DatasetStackTransform,
|
|
@@ -238,6 +241,7 @@ export type {
|
|
|
238
241
|
EditDensityDataOptions,
|
|
239
242
|
EditStackDataOptions,
|
|
240
243
|
EditRegressionDataOptions,
|
|
244
|
+
EditRegressionStatisticsDataOptions,
|
|
241
245
|
EditIntervalDataOptions,
|
|
242
246
|
EditECDFDataOptions,
|
|
243
247
|
EditNormalizedDataOptions,
|
|
@@ -357,6 +361,7 @@ export type {
|
|
|
357
361
|
SecondaryPositionEncodingOptions,
|
|
358
362
|
RegressionBandOptions,
|
|
359
363
|
RegressionDataOptions,
|
|
364
|
+
RegressionStatisticsDataOptions,
|
|
360
365
|
RegressionInterval,
|
|
361
366
|
RegressionMethod,
|
|
362
367
|
RegressionOptions,
|