svelteplot 0.11.1 → 0.12.0-pr-523.0

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.
@@ -0,0 +1,516 @@
1
+ <!-- @component
2
+ Renders contour lines (or filled contour bands) from a scalar field using
3
+ the marching-squares algorithm.
4
+
5
+ Supports the same three input modes as the `Raster` mark:
6
+
7
+ **Dense grid mode** (`data` is a flat row-major array, `width`/`height` are
8
+ set, no `x`/`y` channels): each datum is its own scalar value (unless `value`
9
+ is specified).
10
+
11
+ **Function sampling mode** (`data` is omitted/null, `value` is an
12
+ `(x, y) => number` function): the function is evaluated on a pixel grid.
13
+
14
+ **Scatter interpolation mode** (`data` is an array with `x`/`y` channels):
15
+ each datum contributes a position and scalar value; the mark spatially
16
+ interpolates over the grid before running marching squares.
17
+
18
+ Styling: `fill` and `stroke` accept ordinary CSS color strings **or** the
19
+ special keyword `"value"`, which maps each contour level's threshold through
20
+ the plot's color scale. Defaults: `fill="none"`, `stroke="currentColor"`.
21
+ -->
22
+ <script lang="ts" generics="Datum extends DataRow">
23
+ interface ContourMarkProps {
24
+ /**
25
+ * Input data. For **dense grid** mode supply a flat row-major array and
26
+ * set `width`/`height`. Omit (or set null) for **function-sampling**
27
+ * mode. For **scatter interpolation** supply an array of records with
28
+ * `x`/`y` channels.
29
+ */
30
+ data?: Datum[] | null;
31
+ /** x position channel (scatter / dense-grid mode) */
32
+ x?: ChannelAccessor<Datum>;
33
+ /** y position channel (scatter / dense-grid mode) */
34
+ y?: ChannelAccessor<Datum>;
35
+ /**
36
+ * Scalar field accessor, identity function for dense grid, or an
37
+ * `(x, y) => number` function for function-sampling mode.
38
+ */
39
+ value?: ChannelAccessor<Datum> | ((x: number, y: number) => number);
40
+ /**
41
+ * Contour threshold levels. Can be:
42
+ * - a **count** (number): approximately that many nicely-spaced levels
43
+ * - an explicit **array** of threshold values
44
+ * - a **function** `(values, min, max) => number[]`
45
+ * - a d3 **threshold scheme** object with `.floor()` / `.range()`
46
+ *
47
+ * Defaults to Sturges' formula applied to the value range.
48
+ */
49
+ thresholds?:
50
+ | number
51
+ | number[]
52
+ | ((values: number[], min: number, max: number) => number[])
53
+ | { floor(x: number): number; range(a: number, b: number): number[] };
54
+ /**
55
+ * Step interval between contour levels (alternative to `thresholds`).
56
+ * Can be a number (constant step) or an interval object with `.floor()`
57
+ * / `.range()`.
58
+ */
59
+ interval?: number | { floor(x: number): number; range(a: number, b: number): number[] };
60
+ /**
61
+ * Whether to apply linear interpolation when tracing contour edges
62
+ * (default `true`). Set to `false` for a blockier, faster appearance.
63
+ */
64
+ smooth?: boolean;
65
+ /** left bound of the domain in data coordinates */
66
+ x1?: number;
67
+ /** top bound of the domain in data coordinates */
68
+ y1?: number;
69
+ /** right bound of the domain in data coordinates */
70
+ x2?: number;
71
+ /** bottom bound of the domain in data coordinates */
72
+ y2?: number;
73
+ /**
74
+ * Explicit grid width; required for dense grid mode, overrides
75
+ * `pixelSize` in other modes.
76
+ */
77
+ width?: number;
78
+ /**
79
+ * Explicit grid height; required for dense grid mode, overrides
80
+ * `pixelSize` in other modes.
81
+ */
82
+ height?: number;
83
+ /** pixel size in screen pixels (default 2) */
84
+ pixelSize?: number;
85
+ /** Gaussian blur radius applied before contouring (default 0) */
86
+ blur?: number;
87
+ /**
88
+ * Spatial interpolation for scatter mode:
89
+ * `"none"` | `"nearest"` | `"barycentric"` | `"random-walk"` or a
90
+ * custom `(index, w, h, X, Y, V) => W` function.
91
+ * Defaults to `"nearest"` when data is provided.
92
+ */
93
+ interpolate?: 'none' | 'nearest' | 'barycentric' | 'random-walk' | InterpolateFunction;
94
+ /**
95
+ * Fill color for contour polygons. Use `"value"` to map each
96
+ * threshold level through the plot's color scale. Default `"none"`.
97
+ */
98
+ fill?: string;
99
+ /**
100
+ * Stroke color for contour lines. Use `"value"` to map each
101
+ * threshold level through the plot's color scale. Default
102
+ * `"currentColor"`.
103
+ */
104
+ stroke?: string;
105
+ strokeWidth?: number;
106
+ strokeOpacity?: number;
107
+ fillOpacity?: number;
108
+ opacity?: number;
109
+ strokeMiterlimit?: number;
110
+ clipPath?: string;
111
+ class?: string;
112
+ }
113
+
114
+ import type {
115
+ DataRow,
116
+ DataRecord,
117
+ ChannelAccessor,
118
+ ScaledDataRecord,
119
+ MarkType
120
+ } from '../types/index.js';
121
+ import { blur2, ticks, nice, thresholdSturges } from 'd3-array';
122
+ import { contours } from 'd3-contour';
123
+ import { geoPath } from 'd3-geo';
124
+ import Mark from '../Mark.svelte';
125
+ import { usePlot } from '../hooks/usePlot.svelte.js';
126
+ import {
127
+ interpolateNone,
128
+ interpolateNearest,
129
+ interpolatorBarycentric,
130
+ interpolatorRandomWalk,
131
+ type InterpolateFunction
132
+ } from '../helpers/rasterInterpolate.js';
133
+ import { X, Y, RAW_VALUE } from '../transforms/recordize.js';
134
+ import { scaleLinear } from 'd3-scale';
135
+
136
+ let markProps: ContourMarkProps = $props();
137
+
138
+ const {
139
+ data,
140
+ value,
141
+ x1: x1Prop,
142
+ y1: y1Prop,
143
+ x2: x2Prop,
144
+ y2: y2Prop,
145
+ width: widthProp,
146
+ height: heightProp,
147
+ pixelSize = 4,
148
+ blur = 0,
149
+ smooth = true,
150
+ thresholds,
151
+ interval,
152
+ interpolate,
153
+ fill = 'none',
154
+ stroke,
155
+ strokeWidth,
156
+ strokeOpacity,
157
+ fillOpacity,
158
+ opacity,
159
+ strokeMiterlimit = 1,
160
+ clipPath,
161
+ class: className = '',
162
+ ...options
163
+ }: ContourMarkProps = $derived({ ...markProps });
164
+
165
+ const plot = usePlot();
166
+
167
+ /** No data: value is an (x,y) function */
168
+ const isSamplerMode = $derived(data == null);
169
+
170
+ /**
171
+ * Dense grid: data is a flat array, width+height are given, no x/y channels.
172
+ */
173
+ const isDenseGridMode = $derived(
174
+ data != null &&
175
+ widthProp != null &&
176
+ heightProp != null &&
177
+ (options as any).x == null &&
178
+ (options as any).y == null
179
+ );
180
+
181
+ const interpolateFn = $derived(resolveInterpolate(interpolate));
182
+
183
+ /**
184
+ * Coarse 20×20 sample of the value function in data-space.
185
+ * Used only to give `<Mark>` representative values for color-scale
186
+ * domain/type detection. The full-resolution grid is (re-)computed on
187
+ * every render inside `computeContours`, so impure functions (closures
188
+ * over reactive state, `performance.now()`, etc.) always reflect the
189
+ * current render state.
190
+ */
191
+ const samplerSampleValues = $derived.by((): number[] | null => {
192
+ if (!isSamplerMode || typeof value !== 'function') return null;
193
+ if (x1Prop == null || x2Prop == null || y1Prop == null || y2Prop == null) return null;
194
+ const SAMPLES = 20;
195
+ const dx = x2Prop - x1Prop;
196
+ const dy = y2Prop - y1Prop;
197
+ const fn = value as (x: number, y: number) => number;
198
+ const out: number[] = [];
199
+ for (let yi = 0; yi < SAMPLES; yi++) {
200
+ for (let xi = 0; xi < SAMPLES; xi++) {
201
+ const v = fn(
202
+ x1Prop + ((xi + 0.5) / SAMPLES) * dx,
203
+ y1Prop + ((yi + 0.5) / SAMPLES) * dy
204
+ );
205
+ if (isFinite(v)) out.push(v);
206
+ }
207
+ }
208
+ return out.length > 0 ? out : null;
209
+ });
210
+
211
+ function resolveInterpolate(interp: ContourMarkProps['interpolate']): InterpolateFunction {
212
+ if (typeof interp === 'function') return interp;
213
+ // Default to nearest for scatter mode
214
+ const resolved = interp ?? (isSamplerMode || isDenseGridMode ? 'none' : 'nearest');
215
+ switch (String(resolved).toLowerCase()) {
216
+ case 'none':
217
+ return interpolateNone;
218
+ case 'nearest':
219
+ return interpolateNearest;
220
+ case 'barycentric':
221
+ return interpolatorBarycentric();
222
+ case 'random-walk':
223
+ return interpolatorRandomWalk();
224
+ }
225
+ throw new Error(`invalid interpolate: ${interp}`);
226
+ }
227
+
228
+ /** Pixel-space bounds of the plot area. */
229
+ function getBounds() {
230
+ const facetWidth = plot.facetWidth ?? 100;
231
+ const facetHeight = plot.facetHeight ?? 100;
232
+ const marginLeft = plot.options.marginLeft ?? 0;
233
+ const marginTop = plot.options.marginTop ?? 0;
234
+ return {
235
+ bx1: marginLeft,
236
+ by1: marginTop,
237
+ bx2: marginLeft + facetWidth,
238
+ by2: marginTop + facetHeight
239
+ };
240
+ }
241
+
242
+ /** Resolve a value accessor on a single datum. */
243
+ function resolveValue(datum: any): number | null {
244
+ if (value == null) return typeof datum === 'number' ? datum : null;
245
+ if (typeof value === 'string') return datum[value] ?? null;
246
+ if (typeof value === 'function') return (value as (d: any) => number)(datum);
247
+ return null;
248
+ }
249
+
250
+ type ContourGeometry = {
251
+ type: 'MultiPolygon';
252
+ coordinates: number[][][][];
253
+ value: number;
254
+ };
255
+
256
+ /**
257
+ * Compute the scalar-field grid and run marching squares.
258
+ * Returns an array of GeoJSON MultiPolygon objects in SVG pixel coordinates,
259
+ * each carrying the threshold `value` that produced it.
260
+ */
261
+ function computeContours(scaledData: ScaledDataRecord[]): ContourGeometry[] | null {
262
+ const { bx1, by1, bx2, by2 } = getBounds();
263
+ const dx = bx2 - bx1;
264
+ const dy = by2 - by1;
265
+ const w = widthProp ?? Math.round(Math.abs(dx) / pixelSize);
266
+ const h = heightProp ?? Math.round(Math.abs(dy) / pixelSize);
267
+ if (w <= 0 || h <= 0) return null;
268
+ const n = w * h;
269
+
270
+ // --- Build numeric value grid V ---
271
+ let V: number[] | null = null;
272
+
273
+ if (isDenseGridMode) {
274
+ // Dense grid: each datum is a value; flip rows (y-up → screen y-down)
275
+ const Vraw = (data as any[]).map((d) => resolveValue(d) ?? NaN);
276
+ V = new Array(n);
277
+ for (let row = 0; row < h; ++row) {
278
+ const srcRow = h - 1 - row;
279
+ for (let col = 0; col < w; ++col) {
280
+ V[row * w + col] = Vraw[srcRow * w + col];
281
+ }
282
+ }
283
+ } else if (isSamplerMode) {
284
+ // Sampler mode: evaluate (x, y) => number on the full pixel grid
285
+ if (typeof value !== 'function') return null;
286
+ const xScale = scaleLinear().range([x1Prop!, x2Prop!]).domain([bx1, bx2]);
287
+ const yScale = scaleLinear().range([y1Prop!, y2Prop!]).domain([by1, by2]);
288
+ const kx = dx / w;
289
+ const ky = dy / h;
290
+ V = new Array(n);
291
+ let i = 0;
292
+ for (let yi = 0.5; yi < h; ++yi) {
293
+ for (let xi = 0.5; xi < w; ++xi, ++i) {
294
+ V[i] = (value as (x: number, y: number) => number)(
295
+ xScale(bx1 + xi * kx),
296
+ yScale(by1 + yi * ky)
297
+ );
298
+ }
299
+ }
300
+ } else if (scaledData.length > 0) {
301
+ // Scatter interpolation mode
302
+ const validData = scaledData.filter((d) => d.valid && d.x != null && d.y != null);
303
+ if (validData.length === 0) return null;
304
+ const kx = w / dx;
305
+ const ky = h / dy;
306
+ const index = validData.map((_, i) => i);
307
+ const IX = new Float64Array(validData.map((d) => ((d.x as number) - bx1) * kx));
308
+ const IY = new Float64Array(validData.map((d) => ((d.y as number) - by1) * ky));
309
+ const rawValues = validData.map((d) => d.resolved?.fill);
310
+ if (rawValues.some((v) => v != null)) {
311
+ V = Array.from(interpolateFn(index, w, h, IX, IY, rawValues));
312
+ }
313
+ }
314
+
315
+ if (!V) return null;
316
+
317
+ // --- Optional Gaussian blur ---
318
+ if (blur > 0) blur2({ data: V, width: w, height: h }, blur);
319
+
320
+ // --- Compute thresholds ---
321
+ const T = computeThresholds(V, w, h, thresholds, interval);
322
+ if (T.length === 0) return null;
323
+
324
+ // --- Run marching squares ---
325
+ const kx = w / dx;
326
+ const ky = h / dy;
327
+ const contourFn = contours().size([w, h]).smooth(smooth);
328
+
329
+ const contourData: ContourGeometry[] = T.map((t) => {
330
+ const geom = contourFn.contour(V!, t) as ContourGeometry;
331
+
332
+ // Rescale from grid coordinates to SVG pixel coordinates
333
+ for (const rings of geom.coordinates) {
334
+ for (const ring of rings) {
335
+ for (const point of ring) {
336
+ point[0] = point[0] / kx + bx1;
337
+ point[1] = point[1] / ky + by1;
338
+ }
339
+ }
340
+ }
341
+
342
+ return geom;
343
+ });
344
+
345
+ return contourData;
346
+ }
347
+
348
+ /** Compute an array of threshold tick values from the V grid. */
349
+ function computeThresholds(
350
+ V: number[],
351
+ w: number,
352
+ h: number,
353
+ thresholdSpec: ContourMarkProps['thresholds'],
354
+ intervalSpec: ContourMarkProps['interval']
355
+ ): number[] {
356
+ // Compute finite extent
357
+ let vMin = Infinity,
358
+ vMax = -Infinity;
359
+ for (const v of V) {
360
+ if (isFinite(v)) {
361
+ if (v < vMin) vMin = v;
362
+ if (v > vMax) vMax = v;
363
+ }
364
+ }
365
+ if (!isFinite(vMin) || !isFinite(vMax) || vMin === vMax) return [];
366
+
367
+ // Resolve interval shorthand into a thresholds-compatible object
368
+ let t: ContourMarkProps['thresholds'] = thresholdSpec;
369
+ if (t == null && intervalSpec != null) {
370
+ if (typeof intervalSpec === 'number') {
371
+ const step = intervalSpec;
372
+ t = {
373
+ floor: (x: number) => Math.floor(x / step) * step,
374
+ range: (a: number, b: number) => {
375
+ const result: number[] = [];
376
+ let v = Math.ceil(a / step) * step;
377
+ while (v < b) {
378
+ result.push(v);
379
+ v += step;
380
+ }
381
+ return result;
382
+ }
383
+ };
384
+ } else {
385
+ t = intervalSpec;
386
+ }
387
+ }
388
+
389
+ // Default to Sturges' formula (returns a count, handled below)
390
+ const tSpec: any = t ?? thresholdSturges;
391
+
392
+ // Interval object with floor/range
393
+ if (typeof tSpec === 'object' && tSpec !== null && 'range' in tSpec) {
394
+ return tSpec.range(tSpec.floor(vMin), vMax);
395
+ }
396
+
397
+ // Already an array
398
+ if (Array.isArray(tSpec)) return tSpec;
399
+
400
+ // Function form — may return a count (number) or an array of thresholds
401
+ let resolved: any = tSpec;
402
+ if (typeof tSpec === 'function') {
403
+ const finiteV = V.filter(isFinite);
404
+ resolved = tSpec(finiteV, vMin, vMax);
405
+ }
406
+
407
+ if (Array.isArray(resolved)) return resolved;
408
+
409
+ // Count form: approximately `resolved` nicely-spaced levels
410
+ const count = resolved as number;
411
+ const [nMin, nMax] = nice(vMin, vMax, count) as [number, number];
412
+ const tz = ticks(nMin, nMax, count);
413
+ while (tz.length > 0 && tz[tz.length - 1] >= vMax) tz.pop();
414
+ while (tz.length > 1 && tz[1] < vMin) tz.shift();
415
+ return tz;
416
+ }
417
+
418
+ /** Resolve a fill or stroke prop for a given contour level. */
419
+ function resolveColor(prop: string | undefined, threshold: number): string {
420
+ if (prop === 'value') {
421
+ return (plot.scales.color?.fn(threshold) as string) ?? 'currentColor';
422
+ }
423
+ return prop ?? 'none';
424
+ }
425
+
426
+ /** When a fill is active, stroke defaults to none; otherwise currentColor. */
427
+ const effectiveStroke = $derived(stroke ?? (fill !== 'none' ? 'none' : 'currentColor'));
428
+
429
+ /** Build the inline style string for a single contour path. */
430
+ function contourStyle(threshold: number): string {
431
+ const parts: string[] = [];
432
+ parts.push(`fill:${resolveColor(fill, threshold)}`);
433
+ parts.push(`stroke:${resolveColor(effectiveStroke, threshold)}`);
434
+ if (strokeWidth != null) parts.push(`stroke-width:${strokeWidth}`);
435
+ if (strokeOpacity != null) parts.push(`stroke-opacity:${strokeOpacity}`);
436
+ if (fillOpacity != null) parts.push(`fill-opacity:${fillOpacity}`);
437
+ if (opacity != null) parts.push(`opacity:${opacity}`);
438
+ if (strokeMiterlimit != null) parts.push(`stroke-miterlimit:${strokeMiterlimit}`);
439
+ return parts.join(';');
440
+ }
441
+
442
+ const path = geoPath();
443
+
444
+ // --- Mark registration data ---
445
+
446
+ /** For dense grid mode: synthetic records with symbol-keyed x/y/value. */
447
+ const denseMarkData = $derived(
448
+ isDenseGridMode
449
+ ? (data as any[]).map((d, i) => ({
450
+ [X]: i % widthProp!,
451
+ [Y]: Math.floor(i / widthProp!),
452
+ [RAW_VALUE]: resolveValue(d)
453
+ }))
454
+ : null
455
+ );
456
+
457
+ /**
458
+ * For sampler mode: corner records to establish x/y scale domains, plus
459
+ * the coarse sample values for color-scale domain registration.
460
+ */
461
+ const samplerMarkData = $derived.by(() => {
462
+ if (!isSamplerMode) return null;
463
+ const x1 = x1Prop,
464
+ x2 = x2Prop,
465
+ y1 = y1Prop,
466
+ y2 = y2Prop;
467
+ if (x1 == null || x2 == null || y1 == null || y2 == null) return null;
468
+ const samples = samplerSampleValues;
469
+ const records: any[] = [
470
+ { [X]: x1, [Y]: y1 },
471
+ { [X]: x2, [Y]: y2 }
472
+ ];
473
+ if (samples) {
474
+ for (const v of samples) {
475
+ records.push({ [X]: x1, [Y]: y1, [RAW_VALUE]: v });
476
+ }
477
+ }
478
+ return records as DataRecord[];
479
+ });
480
+
481
+ // Always include 'fill' so the color scale domain is built from scalar field
482
+ // values in all three modes. The actual fill/stroke style is resolved
483
+ // per-path in the template.
484
+ const markChannels = ['x', 'y', 'fill'] as const;
485
+
486
+ const markFill = $derived(
487
+ isDenseGridMode || isSamplerMode ? (RAW_VALUE as any) : (value as any)
488
+ );
489
+
490
+ const markX = $derived(isDenseGridMode || isSamplerMode ? (X as any) : undefined);
491
+ const markY = $derived(isDenseGridMode || isSamplerMode ? (Y as any) : undefined);
492
+ </script>
493
+
494
+ <Mark
495
+ type={'contour' as MarkType}
496
+ data={isDenseGridMode
497
+ ? (denseMarkData as DataRecord[])
498
+ : isSamplerMode
499
+ ? ((samplerMarkData ?? []) as DataRecord[])
500
+ : ((data ?? []) as DataRecord[])}
501
+ channels={markChannels as any}
502
+ x={markX}
503
+ y={markY}
504
+ fill={markFill}
505
+ {...options}>
506
+ {#snippet children({ scaledData })}
507
+ {@const contourPaths = computeContours(scaledData)}
508
+ {#if contourPaths}
509
+ <g clip-path={clipPath} class={className || null} aria-label="contour">
510
+ {#each contourPaths as contourGeom (contourGeom.value)}
511
+ <path d={path(contourGeom as any)} style={contourStyle(contourGeom.value)} />
512
+ {/each}
513
+ </g>
514
+ {/if}
515
+ {/snippet}
516
+ </Mark>
@@ -0,0 +1,138 @@
1
+ import type { DataRow, ChannelAccessor } from '../types/index.js';
2
+ import { type InterpolateFunction } from '../helpers/rasterInterpolate.js';
3
+ declare function $$render<Datum extends DataRow>(): {
4
+ props: {
5
+ /**
6
+ * Input data. For **dense grid** mode supply a flat row-major array and
7
+ * set `width`/`height`. Omit (or set null) for **function-sampling**
8
+ * mode. For **scatter interpolation** supply an array of records with
9
+ * `x`/`y` channels.
10
+ */
11
+ data?: Datum[] | null;
12
+ /** x position channel (scatter / dense-grid mode) */
13
+ x?: ChannelAccessor<Datum>;
14
+ /** y position channel (scatter / dense-grid mode) */
15
+ y?: ChannelAccessor<Datum>;
16
+ /**
17
+ * Scalar field accessor, identity function for dense grid, or an
18
+ * `(x, y) => number` function for function-sampling mode.
19
+ */
20
+ value?: ChannelAccessor<Datum> | ((x: number, y: number) => number);
21
+ /**
22
+ * Contour threshold levels. Can be:
23
+ * - a **count** (number): approximately that many nicely-spaced levels
24
+ * - an explicit **array** of threshold values
25
+ * - a **function** `(values, min, max) => number[]`
26
+ * - a d3 **threshold scheme** object with `.floor()` / `.range()`
27
+ *
28
+ * Defaults to Sturges' formula applied to the value range.
29
+ */
30
+ thresholds?: number | number[] | ((values: number[], min: number, max: number) => number[]) | {
31
+ floor(x: number): number;
32
+ range(a: number, b: number): number[];
33
+ };
34
+ /**
35
+ * Step interval between contour levels (alternative to `thresholds`).
36
+ * Can be a number (constant step) or an interval object with `.floor()`
37
+ * / `.range()`.
38
+ */
39
+ interval?: number | {
40
+ floor(x: number): number;
41
+ range(a: number, b: number): number[];
42
+ };
43
+ /**
44
+ * Whether to apply linear interpolation when tracing contour edges
45
+ * (default `true`). Set to `false` for a blockier, faster appearance.
46
+ */
47
+ smooth?: boolean;
48
+ /** left bound of the domain in data coordinates */
49
+ x1?: number;
50
+ /** top bound of the domain in data coordinates */
51
+ y1?: number;
52
+ /** right bound of the domain in data coordinates */
53
+ x2?: number;
54
+ /** bottom bound of the domain in data coordinates */
55
+ y2?: number;
56
+ /**
57
+ * Explicit grid width; required for dense grid mode, overrides
58
+ * `pixelSize` in other modes.
59
+ */
60
+ width?: number;
61
+ /**
62
+ * Explicit grid height; required for dense grid mode, overrides
63
+ * `pixelSize` in other modes.
64
+ */
65
+ height?: number;
66
+ /** pixel size in screen pixels (default 2) */
67
+ pixelSize?: number;
68
+ /** Gaussian blur radius applied before contouring (default 0) */
69
+ blur?: number;
70
+ /**
71
+ * Spatial interpolation for scatter mode:
72
+ * `"none"` | `"nearest"` | `"barycentric"` | `"random-walk"` or a
73
+ * custom `(index, w, h, X, Y, V) => W` function.
74
+ * Defaults to `"nearest"` when data is provided.
75
+ */
76
+ interpolate?: "none" | "nearest" | "barycentric" | "random-walk" | InterpolateFunction;
77
+ /**
78
+ * Fill color for contour polygons. Use `"value"` to map each
79
+ * threshold level through the plot's color scale. Default `"none"`.
80
+ */
81
+ fill?: string;
82
+ /**
83
+ * Stroke color for contour lines. Use `"value"` to map each
84
+ * threshold level through the plot's color scale. Default
85
+ * `"currentColor"`.
86
+ */
87
+ stroke?: string;
88
+ strokeWidth?: number;
89
+ strokeOpacity?: number;
90
+ fillOpacity?: number;
91
+ opacity?: number;
92
+ strokeMiterlimit?: number;
93
+ clipPath?: string;
94
+ class?: string;
95
+ };
96
+ exports: {};
97
+ bindings: "";
98
+ slots: {};
99
+ events: {};
100
+ };
101
+ declare class __sveltets_Render<Datum extends DataRow> {
102
+ props(): ReturnType<typeof $$render<Datum>>['props'];
103
+ events(): ReturnType<typeof $$render<Datum>>['events'];
104
+ slots(): ReturnType<typeof $$render<Datum>>['slots'];
105
+ bindings(): "";
106
+ exports(): {};
107
+ }
108
+ interface $$IsomorphicComponent {
109
+ new <Datum extends DataRow>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Datum>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Datum>['props']>, ReturnType<__sveltets_Render<Datum>['events']>, ReturnType<__sveltets_Render<Datum>['slots']>> & {
110
+ $$bindings?: ReturnType<__sveltets_Render<Datum>['bindings']>;
111
+ } & ReturnType<__sveltets_Render<Datum>['exports']>;
112
+ <Datum extends DataRow>(internal: unknown, props: ReturnType<__sveltets_Render<Datum>['props']> & {}): ReturnType<__sveltets_Render<Datum>['exports']>;
113
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
114
+ }
115
+ /**
116
+ * Renders contour lines (or filled contour bands) from a scalar field using
117
+ * the marching-squares algorithm.
118
+ *
119
+ * Supports the same three input modes as the `Raster` mark:
120
+ *
121
+ * **Dense grid mode** (`data` is a flat row-major array, `width`/`height` are
122
+ * set, no `x`/`y` channels): each datum is its own scalar value (unless `value`
123
+ * is specified).
124
+ *
125
+ * **Function sampling mode** (`data` is omitted/null, `value` is an
126
+ * `(x, y) => number` function): the function is evaluated on a pixel grid.
127
+ *
128
+ * **Scatter interpolation mode** (`data` is an array with `x`/`y` channels):
129
+ * each datum contributes a position and scalar value; the mark spatially
130
+ * interpolates over the grid before running marching squares.
131
+ *
132
+ * Styling: `fill` and `stroke` accept ordinary CSS color strings **or** the
133
+ * special keyword `"value"`, which maps each contour level's threshold through
134
+ * the plot's color scale. Defaults: `fill="none"`, `stroke="currentColor"`.
135
+ */
136
+ declare const Contour: $$IsomorphicComponent;
137
+ type Contour<Datum extends DataRow> = InstanceType<typeof Contour<Datum>>;
138
+ export default Contour;