svelteplot 0.12.0 → 0.13.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.
- package/dist/core/Plot.svelte +3 -6
- package/dist/helpers/scales.js +8 -0
- package/dist/helpers/vectorShapes.d.ts +13 -0
- package/dist/helpers/vectorShapes.js +57 -0
- package/dist/marks/Arrow.svelte +70 -59
- package/dist/marks/Arrow.svelte.d.ts +2 -0
- package/dist/marks/ColorLegend.svelte +3 -3
- package/dist/marks/Contour.svelte +693 -0
- package/dist/marks/Contour.svelte.d.ts +150 -0
- package/dist/marks/Image.svelte +37 -27
- package/dist/marks/Image.svelte.d.ts +2 -0
- package/dist/marks/Link.svelte +68 -50
- package/dist/marks/Link.svelte.d.ts +2 -0
- package/dist/marks/Raster.svelte +6 -1
- package/dist/marks/Vector.svelte +12 -81
- package/dist/marks/Vector.svelte.d.ts +2 -4
- package/dist/marks/helpers/ArrowCanvas.svelte +132 -0
- package/dist/marks/helpers/ArrowCanvas.svelte.d.ts +39 -0
- package/dist/marks/helpers/BaseAxisX.svelte +5 -7
- package/dist/marks/helpers/ImageCanvas.svelte +126 -0
- package/dist/marks/helpers/ImageCanvas.svelte.d.ts +34 -0
- package/dist/marks/helpers/LinkCanvas.svelte +103 -0
- package/dist/marks/helpers/LinkCanvas.svelte.d.ts +32 -0
- package/dist/marks/helpers/VectorCanvas.svelte +127 -0
- package/dist/marks/helpers/VectorCanvas.svelte.d.ts +36 -0
- package/dist/marks/index.d.ts +1 -0
- package/dist/marks/index.js +1 -0
- package/dist/types/plot.d.ts +9 -1
- package/package.json +185 -181
|
@@ -0,0 +1,150 @@
|
|
|
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 mode) */
|
|
13
|
+
x?: ChannelAccessor<Datum>;
|
|
14
|
+
/** y position channel (scatter 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
|
+
* **Shorthand**: if `value` is omitted and `fill` is a field name or
|
|
82
|
+
* accessor function (not a CSS color), it is automatically promoted to
|
|
83
|
+
* the `value` channel and `fill` is set to `"value"`.
|
|
84
|
+
*/
|
|
85
|
+
fill?: string | ChannelAccessor<Datum>;
|
|
86
|
+
/**
|
|
87
|
+
* Stroke color for contour lines. Use `"value"` to map each
|
|
88
|
+
* threshold level through the plot's color scale. Default
|
|
89
|
+
* `"currentColor"`.
|
|
90
|
+
*
|
|
91
|
+
* **Shorthand**: if `value` is omitted and `stroke` is a field name or
|
|
92
|
+
* accessor function (not a CSS color), it is automatically promoted to
|
|
93
|
+
* the `value` channel and `stroke` is set to `"value"`.
|
|
94
|
+
*/
|
|
95
|
+
stroke?: string | ChannelAccessor<Datum>;
|
|
96
|
+
strokeWidth?: number;
|
|
97
|
+
strokeOpacity?: number;
|
|
98
|
+
fillOpacity?: number;
|
|
99
|
+
opacity?: number;
|
|
100
|
+
strokeMiterlimit?: number;
|
|
101
|
+
clipPath?: string;
|
|
102
|
+
class?: string;
|
|
103
|
+
/** the horizontal facet channel */
|
|
104
|
+
fx?: ChannelAccessor<Datum>;
|
|
105
|
+
/** the vertical facet channel */
|
|
106
|
+
fy?: ChannelAccessor<Datum>;
|
|
107
|
+
};
|
|
108
|
+
exports: {};
|
|
109
|
+
bindings: "";
|
|
110
|
+
slots: {};
|
|
111
|
+
events: {};
|
|
112
|
+
};
|
|
113
|
+
declare class __sveltets_Render<Datum extends DataRow> {
|
|
114
|
+
props(): ReturnType<typeof $$render<Datum>>['props'];
|
|
115
|
+
events(): ReturnType<typeof $$render<Datum>>['events'];
|
|
116
|
+
slots(): ReturnType<typeof $$render<Datum>>['slots'];
|
|
117
|
+
bindings(): "";
|
|
118
|
+
exports(): {};
|
|
119
|
+
}
|
|
120
|
+
interface $$IsomorphicComponent {
|
|
121
|
+
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']>> & {
|
|
122
|
+
$$bindings?: ReturnType<__sveltets_Render<Datum>['bindings']>;
|
|
123
|
+
} & ReturnType<__sveltets_Render<Datum>['exports']>;
|
|
124
|
+
<Datum extends DataRow>(internal: unknown, props: ReturnType<__sveltets_Render<Datum>['props']> & {}): ReturnType<__sveltets_Render<Datum>['exports']>;
|
|
125
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Renders contour lines (or filled contour bands) from a scalar field using
|
|
129
|
+
* the marching-squares algorithm.
|
|
130
|
+
*
|
|
131
|
+
* Supports the same three input modes as the `Raster` mark:
|
|
132
|
+
*
|
|
133
|
+
* **Dense grid mode** (`data` is a flat row-major array, `width`/`height` are
|
|
134
|
+
* set, no `x`/`y` channels): each datum is its own scalar value (unless `value`
|
|
135
|
+
* is specified).
|
|
136
|
+
*
|
|
137
|
+
* **Function sampling mode** (`data` is omitted/null, `value` is an
|
|
138
|
+
* `(x, y) => number` function): the function is evaluated on a pixel grid.
|
|
139
|
+
*
|
|
140
|
+
* **Scatter interpolation mode** (`data` is an array with `x`/`y` channels):
|
|
141
|
+
* each datum contributes a position and scalar value; the mark spatially
|
|
142
|
+
* interpolates over the grid before running marching squares.
|
|
143
|
+
*
|
|
144
|
+
* Styling: `fill` and `stroke` accept ordinary CSS color strings **or** the
|
|
145
|
+
* special keyword `"value"`, which maps each contour level's threshold through
|
|
146
|
+
* the plot's color scale. Defaults: `fill="none"`, `stroke="currentColor"`.
|
|
147
|
+
*/
|
|
148
|
+
declare const Contour: $$IsomorphicComponent;
|
|
149
|
+
type Contour<Datum extends DataRow> = InstanceType<typeof Contour<Datum>>;
|
|
150
|
+
export default Contour;
|
package/dist/marks/Image.svelte
CHANGED
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
title?: ConstantAccessor<string, Datum>;
|
|
23
23
|
/** the SVG preserveAspectRatio attribute for the image (e.g. "xMidYMid meet") */
|
|
24
24
|
preserveAspectRatio?: string;
|
|
25
|
-
|
|
25
|
+
/** if true, renders using Canvas instead of SVG */
|
|
26
|
+
canvas?: boolean;
|
|
26
27
|
/** CSS class name(s) to apply to individual image elements */
|
|
27
28
|
imageClass?: ConstantAccessor<string, Datum>;
|
|
28
29
|
}
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
import { getPlotDefaults } from '../hooks/plotDefaults.js';
|
|
40
41
|
import { sort } from '../transforms/index.js';
|
|
41
42
|
import Anchor from './helpers/Anchor.svelte';
|
|
43
|
+
import ImageCanvas from './helpers/ImageCanvas.svelte';
|
|
42
44
|
import Mark from '../Mark.svelte';
|
|
43
45
|
|
|
44
46
|
let markProps: ImageMarkProps = $props();
|
|
@@ -51,6 +53,7 @@
|
|
|
51
53
|
|
|
52
54
|
const {
|
|
53
55
|
data = [{} as Datum],
|
|
56
|
+
canvas = false,
|
|
54
57
|
width,
|
|
55
58
|
height,
|
|
56
59
|
src,
|
|
@@ -71,31 +74,38 @@
|
|
|
71
74
|
channels={['x', 'y', 'r', 'fill', 'opacity', 'stroke', 'fillOpacity', 'strokeOpacity']}
|
|
72
75
|
{...args}
|
|
73
76
|
type="image">
|
|
74
|
-
{#snippet children({ scaledData })}
|
|
75
|
-
{#
|
|
76
|
-
|
|
77
|
-
{
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
77
|
+
{#snippet children({ scaledData, usedScales })}
|
|
78
|
+
{#if canvas}
|
|
79
|
+
<ImageCanvas
|
|
80
|
+
data={scaledData}
|
|
81
|
+
options={{ ...options, src, width, height }}
|
|
82
|
+
{usedScales} />
|
|
83
|
+
{:else}
|
|
84
|
+
{#each scaledData as record, i (i)}
|
|
85
|
+
{#if record.valid}
|
|
86
|
+
{@const w =
|
|
87
|
+
record.r !== undefined
|
|
88
|
+
? record.r * 2
|
|
89
|
+
: Number(resolveProp(width, record.datum, 20) ?? 20)}
|
|
90
|
+
{@const h =
|
|
91
|
+
record.r !== undefined
|
|
92
|
+
? record.r * 2
|
|
93
|
+
: Number(resolveProp(height || width, record.datum, 20) ?? 20)}
|
|
94
|
+
<Anchor {options} datum={record.datum}>
|
|
95
|
+
<image
|
|
96
|
+
class={resolveProp(imageClass, record.datum, null)}
|
|
97
|
+
href={resolveProp(src, record.datum, '')}
|
|
98
|
+
x={record.x! - w * 0.5}
|
|
99
|
+
y={record.y! - h * 0.5}
|
|
100
|
+
{preserveAspectRatio}
|
|
101
|
+
clip-path={record.r !== undefined ? `circle(${record.r}px)` : null}
|
|
102
|
+
width={w}
|
|
103
|
+
height={h}
|
|
104
|
+
>{#if title}<title>{resolveProp(title, record.datum, '')}</title
|
|
105
|
+
>{/if}</image>
|
|
106
|
+
</Anchor>
|
|
107
|
+
{/if}
|
|
108
|
+
{/each}
|
|
109
|
+
{/if}
|
|
100
110
|
{/snippet}
|
|
101
111
|
</Mark>
|
|
@@ -154,6 +154,8 @@ declare function $$render<Datum extends DataRecord>(): {
|
|
|
154
154
|
title?: ConstantAccessor<string, Datum>;
|
|
155
155
|
/** the SVG preserveAspectRatio attribute for the image (e.g. "xMidYMid meet") */
|
|
156
156
|
preserveAspectRatio?: string;
|
|
157
|
+
/** if true, renders using Canvas instead of SVG */
|
|
158
|
+
canvas?: boolean;
|
|
157
159
|
/** CSS class name(s) to apply to individual image elements */
|
|
158
160
|
imageClass?: ConstantAccessor<string, Datum>;
|
|
159
161
|
};
|
package/dist/marks/Link.svelte
CHANGED
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
textStartOffset?: ConstantAccessor<string, Datum>;
|
|
46
46
|
/** the stroke width for the text label rendered along the link */
|
|
47
47
|
textStrokeWidth?: ConstantAccessor<number, Datum>;
|
|
48
|
+
/** if true, renders using Canvas instead of SVG */
|
|
49
|
+
canvas?: boolean;
|
|
48
50
|
}
|
|
49
51
|
import type {
|
|
50
52
|
DataRecord,
|
|
@@ -65,6 +67,7 @@
|
|
|
65
67
|
import { maybeCurve } from '../helpers/curves.js';
|
|
66
68
|
import { geoPath } from 'd3-geo';
|
|
67
69
|
import { pick } from 'es-toolkit';
|
|
70
|
+
import LinkCanvas from './helpers/LinkCanvas.svelte';
|
|
68
71
|
import { sort } from '../transforms/sort.js';
|
|
69
72
|
import { indexData } from '../transforms/recordize.js';
|
|
70
73
|
import { getPlotDefaults } from '../hooks/plotDefaults.js';
|
|
@@ -81,6 +84,7 @@
|
|
|
81
84
|
tension = 0,
|
|
82
85
|
bend,
|
|
83
86
|
text,
|
|
87
|
+
canvas,
|
|
84
88
|
class: className = '',
|
|
85
89
|
...options
|
|
86
90
|
}: LinkMarkProps = $derived({
|
|
@@ -103,6 +107,13 @@
|
|
|
103
107
|
|
|
104
108
|
const sphericalLine = $derived(plot.scales.projection && curve === 'auto');
|
|
105
109
|
|
|
110
|
+
const curveFactory = $derived(
|
|
111
|
+
maybeCurve(
|
|
112
|
+
curve === 'auto' ? 'linear' : curve,
|
|
113
|
+
bend === true ? 0.6 : bend === false ? 0 : (bend ?? tension ?? 0)
|
|
114
|
+
)
|
|
115
|
+
);
|
|
116
|
+
|
|
106
117
|
const linePath: (d: ScaledDataRecord, reversed?: boolean) => string | null = $derived.by(() => {
|
|
107
118
|
const fn: D3Line<[number, number]> = line<[number, number]>()
|
|
108
119
|
.curve(
|
|
@@ -168,56 +179,63 @@
|
|
|
168
179
|
channels={['x1', 'y1', 'x2', 'y2', 'opacity', 'stroke', 'strokeOpacity']}
|
|
169
180
|
{...args}>
|
|
170
181
|
{#snippet children({ mark, scaledData, usedScales })}
|
|
171
|
-
|
|
172
|
-
{
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
{
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
{
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
182
|
+
{#if canvas}
|
|
183
|
+
<LinkCanvas data={scaledData} options={args} {usedScales} {curveFactory} />
|
|
184
|
+
{:else}
|
|
185
|
+
<g class={['link', className]} data-use-x={usedScales.x ? 1 : 0}>
|
|
186
|
+
{#each scaledData as d, i (i)}
|
|
187
|
+
{#if d.valid || true}
|
|
188
|
+
{@const [style, styleClass] = resolveStyles(
|
|
189
|
+
plot,
|
|
190
|
+
d,
|
|
191
|
+
{ strokeWidth: 1.6, ...args },
|
|
192
|
+
'stroke',
|
|
193
|
+
usedScales
|
|
194
|
+
)}
|
|
195
|
+
{@const [textStyle, textStyleClass] = resolveStyles(
|
|
196
|
+
plot,
|
|
197
|
+
d,
|
|
198
|
+
{
|
|
199
|
+
textAnchor: 'middle',
|
|
200
|
+
...pick(args, ['fontSize', 'fontWeight', 'fontStyle']),
|
|
201
|
+
fill: options.textFill || args.stroke,
|
|
202
|
+
stroke: options.textStroke,
|
|
203
|
+
strokeWidth: options.textStrokeWidth
|
|
204
|
+
},
|
|
205
|
+
'fill',
|
|
206
|
+
usedScales
|
|
207
|
+
)}
|
|
194
208
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
209
|
+
<MarkerPath
|
|
210
|
+
mark={{ ...mark, options: args }}
|
|
211
|
+
transform=""
|
|
212
|
+
scales={plot.scales}
|
|
213
|
+
markerStart={options.markerStart as boolean | MarkerShape | undefined}
|
|
214
|
+
markerEnd={options.markerEnd as boolean | MarkerShape | undefined}
|
|
215
|
+
marker={options.marker as boolean | MarkerShape | undefined}
|
|
216
|
+
markerScale={options.markerScale}
|
|
217
|
+
class={styleClass ?? undefined}
|
|
218
|
+
strokeWidth={options.strokeWidth as ConstantAccessor<number>}
|
|
219
|
+
datum={d.datum as DataRecord}
|
|
220
|
+
color={d.stroke ?? 'currentColor'}
|
|
221
|
+
d={(sphericalLine ? sphericalLinePath(d) : linePath(d)) ?? ''}
|
|
222
|
+
dInv={(sphericalLine
|
|
223
|
+
? sphericalLinePath(d, true)
|
|
224
|
+
: linePath(d, true)) ?? undefined}
|
|
225
|
+
style={style ?? ''}
|
|
226
|
+
text={text
|
|
227
|
+
? ((resolveProp(text, d.datum as Datum) as string) ?? '')
|
|
228
|
+
: ''}
|
|
229
|
+
startOffset={(resolveProp(
|
|
230
|
+
options.textStartOffset,
|
|
231
|
+
d.datum as Datum,
|
|
232
|
+
'50%'
|
|
233
|
+
) as string) ?? '50%'}
|
|
234
|
+
textStyle={textStyle ?? ''}
|
|
235
|
+
{textStyleClass} />
|
|
236
|
+
{/if}
|
|
237
|
+
{/each}
|
|
238
|
+
</g>
|
|
239
|
+
{/if}
|
|
222
240
|
{/snippet}
|
|
223
241
|
</Mark>
|
|
@@ -180,6 +180,8 @@ declare function $$render<Datum = DataRecord | GeoJSON.GeoJsonObject>(): {
|
|
|
180
180
|
textStartOffset?: ConstantAccessor<string, Datum>;
|
|
181
181
|
/** the stroke width for the text label rendered along the link */
|
|
182
182
|
textStrokeWidth?: ConstantAccessor<number, Datum>;
|
|
183
|
+
/** if true, renders using Canvas instead of SVG */
|
|
184
|
+
canvas?: boolean;
|
|
183
185
|
};
|
|
184
186
|
exports: {};
|
|
185
187
|
bindings: "";
|
package/dist/marks/Raster.svelte
CHANGED
|
@@ -90,6 +90,11 @@
|
|
|
90
90
|
} from '../helpers/rasterInterpolate.js';
|
|
91
91
|
import { X, Y, RAW_VALUE } from '../transforms/recordize.js';
|
|
92
92
|
import { scaleLinear } from 'd3-scale';
|
|
93
|
+
import { getPlotDefaults } from '../hooks/plotDefaults.js';
|
|
94
|
+
|
|
95
|
+
const DEFAULTS = {
|
|
96
|
+
...getPlotDefaults().raster
|
|
97
|
+
};
|
|
93
98
|
|
|
94
99
|
let markProps: RasterMarkProps = $props();
|
|
95
100
|
|
|
@@ -108,7 +113,7 @@
|
|
|
108
113
|
interpolate,
|
|
109
114
|
imageRendering = 'auto',
|
|
110
115
|
...options
|
|
111
|
-
}: RasterMarkProps = $derived({ ...markProps });
|
|
116
|
+
}: RasterMarkProps = $derived({ ...DEFAULTS, ...markProps });
|
|
112
117
|
|
|
113
118
|
const plot = usePlot();
|
|
114
119
|
|
package/dist/marks/Vector.svelte
CHANGED
|
@@ -3,10 +3,7 @@
|
|
|
3
3
|
The vector mark lets you place shapes (like arrows) on your plot.
|
|
4
4
|
-->
|
|
5
5
|
<script lang="ts" module>
|
|
6
|
-
type
|
|
7
|
-
export type ShapeRenderer = {
|
|
8
|
-
draw(context: D3Path, l: number, r: number): void;
|
|
9
|
-
};
|
|
6
|
+
export type { ShapeRenderer } from '../helpers/vectorShapes.js';
|
|
10
7
|
</script>
|
|
11
8
|
|
|
12
9
|
<script lang="ts" generics="Datum = DataRecord | GeoJSON.GeoJsonObject">
|
|
@@ -45,25 +42,23 @@
|
|
|
45
42
|
} from '../types/index.js';
|
|
46
43
|
|
|
47
44
|
import { getContext, type Snippet } from 'svelte';
|
|
48
|
-
import { pathRound as path } from 'd3-path';
|
|
49
45
|
|
|
50
46
|
import { resolveChannel, resolveProp, resolveStyles } from '../helpers/resolve.js';
|
|
51
47
|
import { sort } from '../index.js';
|
|
52
48
|
import Mark from '../Mark.svelte';
|
|
53
|
-
|
|
49
|
+
import VectorCanvas from './helpers/VectorCanvas.svelte';
|
|
54
50
|
import { isValid } from '../helpers/index.js';
|
|
55
51
|
import { addEventHandlers } from './helpers/events.js';
|
|
56
52
|
import { indexData } from '../transforms/recordize.js';
|
|
57
53
|
import { getPlotDefaults } from '../hooks/plotDefaults.js';
|
|
58
54
|
import { usePlot } from '../hooks/usePlot.svelte.js';
|
|
59
55
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const wingRatio = defaultRadius * 5;
|
|
56
|
+
import {
|
|
57
|
+
defaultRadius,
|
|
58
|
+
maybeShape,
|
|
59
|
+
shapePath,
|
|
60
|
+
type ShapeRenderer
|
|
61
|
+
} from '../helpers/vectorShapes.js';
|
|
67
62
|
|
|
68
63
|
let markProps: VectorMarkProps = $props();
|
|
69
64
|
const DEFAULTS = {
|
|
@@ -83,72 +78,6 @@
|
|
|
83
78
|
|
|
84
79
|
const plot = usePlot();
|
|
85
80
|
|
|
86
|
-
const shapeArrow: ShapeRenderer = {
|
|
87
|
-
draw(context: D3Path, l: number, r: number) {
|
|
88
|
-
const wing = (l * r) / wingRatio;
|
|
89
|
-
context.moveTo(0, 0);
|
|
90
|
-
context.lineTo(0, -l);
|
|
91
|
-
context.moveTo(-wing, wing - l);
|
|
92
|
-
context.lineTo(0, -l);
|
|
93
|
-
context.lineTo(wing, wing - l);
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const shapeSpike: ShapeRenderer = {
|
|
98
|
-
draw(context: D3Path, l: number, r: number) {
|
|
99
|
-
context.moveTo(-r, 0);
|
|
100
|
-
context.lineTo(0, -l);
|
|
101
|
-
context.lineTo(r, 0);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const shapeArrowFilled: ShapeRenderer = {
|
|
106
|
-
draw(context: D3Path, l: number, r: number) {
|
|
107
|
-
// const wing = (l * r) / wingRatio;
|
|
108
|
-
const headLength = Math.max(3, l * 0.3);
|
|
109
|
-
const headSpike = headLength * 0.2;
|
|
110
|
-
const headWidth = Math.max(2, l * 0.3);
|
|
111
|
-
const tailWidth = Math.max(2, l * 0.3) * 0.3;
|
|
112
|
-
|
|
113
|
-
context.moveTo(0, 0);
|
|
114
|
-
|
|
115
|
-
context.lineTo(tailWidth * 0.5, -l + headLength - headSpike);
|
|
116
|
-
context.lineTo(headWidth * 0.5, -l + headLength);
|
|
117
|
-
context.lineTo(0, -l);
|
|
118
|
-
context.lineTo(-headWidth * 0.5, -l + headLength);
|
|
119
|
-
context.lineTo(-tailWidth * 0.5, -l + headLength - headSpike);
|
|
120
|
-
|
|
121
|
-
context.closePath();
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const shapes = new Map([
|
|
126
|
-
['arrow', shapeArrow],
|
|
127
|
-
['arrow-filled', shapeArrowFilled],
|
|
128
|
-
['spike', shapeSpike]
|
|
129
|
-
]);
|
|
130
|
-
|
|
131
|
-
function isShapeObject(value: any): value is ShapeRenderer {
|
|
132
|
-
return value && typeof value.draw === 'function';
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function maybeShape(shape: 'arrow' | 'spike' | 'arrow-filled' | ShapeRenderer) {
|
|
136
|
-
if (isShapeObject(shape)) return shape;
|
|
137
|
-
const value = shapes.get(`${shape}`.toLowerCase());
|
|
138
|
-
if (value) return value;
|
|
139
|
-
throw new Error(`invalid shape: ${shape}`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function shapePath(
|
|
143
|
-
shape: 'arrow' | 'spike' | 'arrow-filled' | ShapeRenderer,
|
|
144
|
-
l: number,
|
|
145
|
-
r: number
|
|
146
|
-
) {
|
|
147
|
-
const context = path();
|
|
148
|
-
maybeShape(shape).draw(context, l, r);
|
|
149
|
-
return context.toString();
|
|
150
|
-
}
|
|
151
|
-
|
|
152
81
|
const args = $derived(
|
|
153
82
|
sort({
|
|
154
83
|
data: indexData(data as object[]) as unknown as Datum[],
|
|
@@ -177,8 +106,10 @@
|
|
|
177
106
|
{#snippet children({ scaledData, usedScales })}
|
|
178
107
|
<g class="vector" data-l={usedScales.length}>
|
|
179
108
|
{#if canvas}
|
|
180
|
-
<
|
|
181
|
-
|
|
109
|
+
<VectorCanvas
|
|
110
|
+
data={scaledData}
|
|
111
|
+
options={{ ...args, shape, anchor } as any}
|
|
112
|
+
{usedScales} />
|
|
182
113
|
{:else}
|
|
183
114
|
{#each scaledData as d, i (i)}
|
|
184
115
|
{#if d.valid && isValid(d.r)}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
type
|
|
2
|
-
export type ShapeRenderer = {
|
|
3
|
-
draw(context: D3Path, l: number, r: number): void;
|
|
4
|
-
};
|
|
1
|
+
export type { ShapeRenderer } from '../helpers/vectorShapes.js';
|
|
5
2
|
import type { DataRecord, ChannelAccessor } from '../types/index.js';
|
|
6
3
|
import { type Snippet } from 'svelte';
|
|
4
|
+
import { type ShapeRenderer } from '../helpers/vectorShapes.js';
|
|
7
5
|
declare function $$render<Datum = DataRecord | GeoJSON.GeoJsonObject>(): {
|
|
8
6
|
props: Partial<{
|
|
9
7
|
filter: import("../types/index.js").ConstantAccessor<boolean, Datum>;
|