svelteplot 0.12.0 → 0.14.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/group.d.ts +1 -1
- package/dist/helpers/group.js +3 -3
- 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 +7 -3
- package/dist/marks/Contour.svelte +684 -0
- package/dist/marks/Contour.svelte.d.ts +152 -0
- package/dist/marks/DelaunayLink.svelte +127 -0
- package/dist/marks/DelaunayLink.svelte.d.ts +175 -0
- package/dist/marks/DelaunayMesh.svelte +102 -0
- package/dist/marks/DelaunayMesh.svelte.d.ts +172 -0
- package/dist/marks/Density.svelte +461 -0
- package/dist/marks/Density.svelte.d.ts +87 -0
- package/dist/marks/Hull.svelte +103 -0
- package/dist/marks/Hull.svelte.d.ts +175 -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/Voronoi.svelte +118 -0
- package/dist/marks/Voronoi.svelte.d.ts +172 -0
- package/dist/marks/VoronoiMesh.svelte +109 -0
- package/dist/marks/VoronoiMesh.svelte.d.ts +172 -0
- 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/DensityCanvas.svelte +118 -0
- package/dist/marks/helpers/DensityCanvas.svelte.d.ts +18 -0
- package/dist/marks/helpers/GeoPathCanvas.svelte +125 -0
- package/dist/marks/helpers/GeoPathCanvas.svelte.d.ts +24 -0
- package/dist/marks/helpers/GeoPathGroup.svelte +103 -0
- package/dist/marks/helpers/GeoPathGroup.svelte.d.ts +37 -0
- 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/PathGroup.svelte +100 -0
- package/dist/marks/helpers/PathGroup.svelte.d.ts +16 -0
- package/dist/marks/helpers/PathItems.svelte +112 -0
- package/dist/marks/helpers/PathItems.svelte.d.ts +16 -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 +7 -0
- package/dist/marks/index.js +7 -0
- package/dist/types/mark.d.ts +1 -1
- package/dist/types/plot.d.ts +33 -1
- package/package.json +185 -181
|
@@ -0,0 +1,152 @@
|
|
|
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
|
+
/** Render using a canvas element instead of SVG paths. */
|
|
104
|
+
canvas?: boolean;
|
|
105
|
+
/** the horizontal facet channel */
|
|
106
|
+
fx?: ChannelAccessor<Datum>;
|
|
107
|
+
/** the vertical facet channel */
|
|
108
|
+
fy?: ChannelAccessor<Datum>;
|
|
109
|
+
};
|
|
110
|
+
exports: {};
|
|
111
|
+
bindings: "";
|
|
112
|
+
slots: {};
|
|
113
|
+
events: {};
|
|
114
|
+
};
|
|
115
|
+
declare class __sveltets_Render<Datum extends DataRow> {
|
|
116
|
+
props(): ReturnType<typeof $$render<Datum>>['props'];
|
|
117
|
+
events(): ReturnType<typeof $$render<Datum>>['events'];
|
|
118
|
+
slots(): ReturnType<typeof $$render<Datum>>['slots'];
|
|
119
|
+
bindings(): "";
|
|
120
|
+
exports(): {};
|
|
121
|
+
}
|
|
122
|
+
interface $$IsomorphicComponent {
|
|
123
|
+
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']>> & {
|
|
124
|
+
$$bindings?: ReturnType<__sveltets_Render<Datum>['bindings']>;
|
|
125
|
+
} & ReturnType<__sveltets_Render<Datum>['exports']>;
|
|
126
|
+
<Datum extends DataRow>(internal: unknown, props: ReturnType<__sveltets_Render<Datum>['props']> & {}): ReturnType<__sveltets_Render<Datum>['exports']>;
|
|
127
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Renders contour lines (or filled contour bands) from a scalar field using
|
|
131
|
+
* the marching-squares algorithm.
|
|
132
|
+
*
|
|
133
|
+
* Supports the same three input modes as the `Raster` mark:
|
|
134
|
+
*
|
|
135
|
+
* **Dense grid mode** (`data` is a flat row-major array, `width`/`height` are
|
|
136
|
+
* set, no `x`/`y` channels): each datum is its own scalar value (unless `value`
|
|
137
|
+
* is specified).
|
|
138
|
+
*
|
|
139
|
+
* **Function sampling mode** (`data` is omitted/null, `value` is an
|
|
140
|
+
* `(x, y) => number` function): the function is evaluated on a pixel grid.
|
|
141
|
+
*
|
|
142
|
+
* **Scatter interpolation mode** (`data` is an array with `x`/`y` channels):
|
|
143
|
+
* each datum contributes a position and scalar value; the mark spatially
|
|
144
|
+
* interpolates over the grid before running marching squares.
|
|
145
|
+
*
|
|
146
|
+
* Styling: `fill` and `stroke` accept ordinary CSS color strings **or** the
|
|
147
|
+
* special keyword `"value"`, which maps each contour level's threshold through
|
|
148
|
+
* the plot's color scale. Defaults: `fill="none"`, `stroke="currentColor"`.
|
|
149
|
+
*/
|
|
150
|
+
declare const Contour: $$IsomorphicComponent;
|
|
151
|
+
type Contour<Datum extends DataRow> = InstanceType<typeof Contour<Datum>>;
|
|
152
|
+
export default Contour;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<!-- @component
|
|
2
|
+
Renders individual Delaunay triangulation edges as separate paths,
|
|
3
|
+
allowing per-edge styling based on the source data point.
|
|
4
|
+
-->
|
|
5
|
+
<script lang="ts" generics="Datum = DataRecord">
|
|
6
|
+
interface DelaunayLinkMarkProps extends BaseMarkProps<Datum>, LinkableMarkProps<Datum> {
|
|
7
|
+
/** the input data array */
|
|
8
|
+
data?: Datum[];
|
|
9
|
+
/** the horizontal position channel */
|
|
10
|
+
x?: ChannelAccessor<Datum>;
|
|
11
|
+
/** the vertical position channel */
|
|
12
|
+
y?: ChannelAccessor<Datum>;
|
|
13
|
+
/** the grouping channel; separate triangulations per group */
|
|
14
|
+
z?: ChannelAccessor<Datum>;
|
|
15
|
+
/** Render using a canvas element instead of SVG paths. */
|
|
16
|
+
canvas?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
import { Delaunay } from 'd3-delaunay';
|
|
20
|
+
import type {
|
|
21
|
+
DataRecord,
|
|
22
|
+
BaseMarkProps,
|
|
23
|
+
ChannelAccessor,
|
|
24
|
+
LinkableMarkProps,
|
|
25
|
+
MarkType,
|
|
26
|
+
ScaledDataRecord
|
|
27
|
+
} from '../types/index.js';
|
|
28
|
+
import { groupFacetsAndZ } from '../helpers/group.js';
|
|
29
|
+
import { recordizeXY } from '../transforms/recordize.js';
|
|
30
|
+
import { sort } from '../index.js';
|
|
31
|
+
import Mark from '../Mark.svelte';
|
|
32
|
+
import PathItems from './helpers/PathItems.svelte';
|
|
33
|
+
import { usePlot } from '../hooks/usePlot.svelte.js';
|
|
34
|
+
import { getPlotDefaults } from '../hooks/plotDefaults.js';
|
|
35
|
+
import { SvelteSet } from 'svelte/reactivity';
|
|
36
|
+
|
|
37
|
+
const DEFAULTS = {
|
|
38
|
+
...getPlotDefaults().delaunayLink
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
let markProps: DelaunayLinkMarkProps = $props();
|
|
42
|
+
|
|
43
|
+
const {
|
|
44
|
+
data = [] as Datum[],
|
|
45
|
+
class: className = 'delaunay-link',
|
|
46
|
+
canvas = false,
|
|
47
|
+
...options
|
|
48
|
+
}: DelaunayLinkMarkProps = $derived({ ...DEFAULTS, ...markProps });
|
|
49
|
+
|
|
50
|
+
const args = $derived(
|
|
51
|
+
sort(
|
|
52
|
+
recordizeXY({
|
|
53
|
+
data: data as any[],
|
|
54
|
+
...options
|
|
55
|
+
})
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const plot = usePlot();
|
|
60
|
+
|
|
61
|
+
function computeEdges(scaledData: ScaledDataRecord[]) {
|
|
62
|
+
const scaledByDatum = new Map(scaledData.map((d) => [d.datum, d]));
|
|
63
|
+
const results: { path: string; datum: ScaledDataRecord }[] = [];
|
|
64
|
+
|
|
65
|
+
groupFacetsAndZ(
|
|
66
|
+
scaledData.map((d) => d.datum),
|
|
67
|
+
args,
|
|
68
|
+
(groupItems) => {
|
|
69
|
+
const groupScaled = groupItems
|
|
70
|
+
.map((d) => scaledByDatum.get(d))
|
|
71
|
+
.filter(
|
|
72
|
+
(d): d is ScaledDataRecord =>
|
|
73
|
+
d !== undefined &&
|
|
74
|
+
d.valid &&
|
|
75
|
+
Number.isFinite(d.x as number) &&
|
|
76
|
+
Number.isFinite(d.y as number)
|
|
77
|
+
);
|
|
78
|
+
if (groupScaled.length < 2) return;
|
|
79
|
+
|
|
80
|
+
const delaunay = Delaunay.from(
|
|
81
|
+
groupScaled,
|
|
82
|
+
(d) => d.x as number,
|
|
83
|
+
(d) => d.y as number
|
|
84
|
+
);
|
|
85
|
+
const { halfedges, triangles } = delaunay;
|
|
86
|
+
const seen = new SvelteSet<string>();
|
|
87
|
+
|
|
88
|
+
for (let i = 0; i < halfedges.length; i++) {
|
|
89
|
+
const j = halfedges[i];
|
|
90
|
+
if (j < i && j !== -1) continue;
|
|
91
|
+
const a = triangles[i];
|
|
92
|
+
const b = triangles[i % 3 === 2 ? i - 2 : i + 1];
|
|
93
|
+
const key = a < b ? `${a},${b}` : `${b},${a}`;
|
|
94
|
+
if (seen.has(key)) continue;
|
|
95
|
+
seen.add(key);
|
|
96
|
+
|
|
97
|
+
const p = groupScaled[a];
|
|
98
|
+
const q = groupScaled[b];
|
|
99
|
+
results.push({
|
|
100
|
+
datum: p,
|
|
101
|
+
path: `M${p.x},${p.y}L${q.x},${q.y}`
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
false
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
return results;
|
|
109
|
+
}
|
|
110
|
+
</script>
|
|
111
|
+
|
|
112
|
+
<Mark
|
|
113
|
+
type={'delaunayLink' as MarkType}
|
|
114
|
+
channels={['x', 'y', 'fill', 'opacity', 'stroke', 'fillOpacity', 'strokeOpacity']}
|
|
115
|
+
defaults={{ fill: 'none', stroke: 'currentColor' }}
|
|
116
|
+
{...args}>
|
|
117
|
+
{#snippet children({ mark, usedScales, scaledData })}
|
|
118
|
+
<PathItems
|
|
119
|
+
paths={computeEdges(scaledData)}
|
|
120
|
+
{args}
|
|
121
|
+
{options}
|
|
122
|
+
{className}
|
|
123
|
+
{usedScales}
|
|
124
|
+
{plot}
|
|
125
|
+
{canvas} />
|
|
126
|
+
{/snippet}
|
|
127
|
+
</Mark>
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import type { DataRecord, ChannelAccessor, LinkableMarkProps } from '../types/index.js';
|
|
2
|
+
declare function $$render<Datum = DataRecord>(): {
|
|
3
|
+
props: Partial<{
|
|
4
|
+
filter: import("../types/index.js").ConstantAccessor<boolean, Datum>;
|
|
5
|
+
facet: "auto" | "include" | "exclude";
|
|
6
|
+
fx: ChannelAccessor<Datum>;
|
|
7
|
+
fy: ChannelAccessor<Datum>;
|
|
8
|
+
dx: import("../types/index.js").ConstantAccessor<number, Datum>;
|
|
9
|
+
dy: import("../types/index.js").ConstantAccessor<number, Datum>;
|
|
10
|
+
dodgeX: import("../transforms/dodge.js").DodgeXOptions;
|
|
11
|
+
dodgeY: import("../transforms/dodge.js").DodgeYOptions;
|
|
12
|
+
fill: ChannelAccessor<Datum>;
|
|
13
|
+
fillOpacity: import("../types/index.js").ConstantAccessor<number, Datum>;
|
|
14
|
+
fontFamily: import("../types/index.js").ConstantAccessor<import("csstype").Property.FontFamily, Datum>;
|
|
15
|
+
fontSize: import("../types/index.js").ConstantAccessor<import("csstype").Property.FontSize<number>, Datum>;
|
|
16
|
+
fontStyle: import("../types/index.js").ConstantAccessor<import("csstype").Property.FontStyle, Datum>;
|
|
17
|
+
fontVariant: import("../types/index.js").ConstantAccessor<import("csstype").Property.FontVariant, Datum>;
|
|
18
|
+
fontWeight: import("../types/index.js").ConstantAccessor<import("csstype").Property.FontWeight, Datum>;
|
|
19
|
+
letterSpacing: import("../types/index.js").ConstantAccessor<import("csstype").Property.LetterSpacing<0 | (string & {})>, Datum>;
|
|
20
|
+
wordSpacing: import("../types/index.js").ConstantAccessor<import("csstype").Property.WordSpacing<0 | (string & {})>, Datum>;
|
|
21
|
+
textAnchor: import("../types/index.js").ConstantAccessor<import("csstype").Property.TextAnchor, Datum>;
|
|
22
|
+
textTransform: import("../types/index.js").ConstantAccessor<import("csstype").Property.TextTransform, Datum>;
|
|
23
|
+
textDecoration: import("../types/index.js").ConstantAccessor<import("csstype").Property.TextDecoration<0 | (string & {})>, Datum>;
|
|
24
|
+
sort: ((a: import("../types/data.js").RawValue, b: import("../types/data.js").RawValue) => number) | {
|
|
25
|
+
channel: string;
|
|
26
|
+
order?: "ascending" | "descending";
|
|
27
|
+
} | import("../types/index.js").ConstantAccessor<import("../types/data.js").RawValue, Datum>;
|
|
28
|
+
stroke: ChannelAccessor<Datum>;
|
|
29
|
+
strokeWidth: import("../types/index.js").ConstantAccessor<number, Datum>;
|
|
30
|
+
strokeOpacity: import("../types/index.js").ConstantAccessor<number, Datum>;
|
|
31
|
+
strokeLinejoin: import("../types/index.js").ConstantAccessor<import("csstype").Property.StrokeLinejoin, Datum>;
|
|
32
|
+
strokeLinecap: import("../types/index.js").ConstantAccessor<import("csstype").Property.StrokeLinecap, Datum>;
|
|
33
|
+
strokeMiterlimit: import("../types/index.js").ConstantAccessor<number, Datum>;
|
|
34
|
+
opacity: ChannelAccessor<Datum>;
|
|
35
|
+
strokeDasharray: import("../types/index.js").ConstantAccessor<string, Datum>;
|
|
36
|
+
strokeDashoffset: import("../types/index.js").ConstantAccessor<number, Datum>;
|
|
37
|
+
blend: import("../types/index.js").ConstantAccessor<import("csstype").Property.MixBlendMode, Datum>;
|
|
38
|
+
mixBlendMode: import("../types/index.js").ConstantAccessor<import("csstype").Property.MixBlendMode, Datum>;
|
|
39
|
+
clipPath: string;
|
|
40
|
+
mask: string;
|
|
41
|
+
imageFilter: import("../types/index.js").ConstantAccessor<string, Datum>;
|
|
42
|
+
shapeRendering: import("../types/index.js").ConstantAccessor<import("csstype").Property.ShapeRendering, Datum>;
|
|
43
|
+
paintOrder: import("../types/index.js").ConstantAccessor<string, Datum>;
|
|
44
|
+
onclick: (event: Event & {
|
|
45
|
+
currentTarget: SVGPathElement;
|
|
46
|
+
}, datum: Datum, index: number) => void;
|
|
47
|
+
ondblclick: (event: Event & {
|
|
48
|
+
currentTarget: SVGPathElement;
|
|
49
|
+
}, datum: Datum, index: number) => void;
|
|
50
|
+
onmouseup: (event: Event & {
|
|
51
|
+
currentTarget: SVGPathElement;
|
|
52
|
+
}, datum: Datum, index: number) => void;
|
|
53
|
+
onmousedown: (event: Event & {
|
|
54
|
+
currentTarget: SVGPathElement;
|
|
55
|
+
}, datum: Datum, index: number) => void;
|
|
56
|
+
onmouseenter: (event: Event & {
|
|
57
|
+
currentTarget: SVGPathElement;
|
|
58
|
+
}, datum: Datum, index: number) => void;
|
|
59
|
+
onmousemove: (event: Event & {
|
|
60
|
+
currentTarget: SVGPathElement;
|
|
61
|
+
}, datum: Datum, index: number) => void;
|
|
62
|
+
onmouseleave: (event: Event & {
|
|
63
|
+
currentTarget: SVGPathElement;
|
|
64
|
+
}, datum: Datum, index: number) => void;
|
|
65
|
+
onmouseout: (event: Event & {
|
|
66
|
+
currentTarget: SVGPathElement;
|
|
67
|
+
}, datum: Datum, index: number) => void;
|
|
68
|
+
onmouseover: (event: Event & {
|
|
69
|
+
currentTarget: SVGPathElement;
|
|
70
|
+
}, datum: Datum, index: number) => void;
|
|
71
|
+
onpointercancel: (event: Event & {
|
|
72
|
+
currentTarget: SVGPathElement;
|
|
73
|
+
}, datum: Datum, index: number) => void;
|
|
74
|
+
onpointerdown: (event: Event & {
|
|
75
|
+
currentTarget: SVGPathElement;
|
|
76
|
+
}, datum: Datum, index: number) => void;
|
|
77
|
+
onpointerup: (event: Event & {
|
|
78
|
+
currentTarget: SVGPathElement;
|
|
79
|
+
}, datum: Datum, index: number) => void;
|
|
80
|
+
onpointerenter: (event: Event & {
|
|
81
|
+
currentTarget: SVGPathElement;
|
|
82
|
+
}, datum: Datum, index: number) => void;
|
|
83
|
+
onpointerleave: (event: Event & {
|
|
84
|
+
currentTarget: SVGPathElement;
|
|
85
|
+
}, datum: Datum, index: number) => void;
|
|
86
|
+
onpointermove: (event: Event & {
|
|
87
|
+
currentTarget: SVGPathElement;
|
|
88
|
+
}, datum: Datum, index: number) => void;
|
|
89
|
+
onpointerover: (event: Event & {
|
|
90
|
+
currentTarget: SVGPathElement;
|
|
91
|
+
}, datum: Datum, index: number) => void;
|
|
92
|
+
onpointerout: (event: Event & {
|
|
93
|
+
currentTarget: SVGPathElement;
|
|
94
|
+
}, datum: Datum, index: number) => void;
|
|
95
|
+
ondrag: (event: Event & {
|
|
96
|
+
currentTarget: SVGPathElement;
|
|
97
|
+
}, datum: Datum, index: number) => void;
|
|
98
|
+
ondrop: (event: Event & {
|
|
99
|
+
currentTarget: SVGPathElement;
|
|
100
|
+
}, datum: Datum, index: number) => void;
|
|
101
|
+
ondragstart: (event: Event & {
|
|
102
|
+
currentTarget: SVGPathElement;
|
|
103
|
+
}, datum: Datum, index: number) => void;
|
|
104
|
+
ondragenter: (event: Event & {
|
|
105
|
+
currentTarget: SVGPathElement;
|
|
106
|
+
}, datum: Datum, index: number) => void;
|
|
107
|
+
ondragleave: (event: Event & {
|
|
108
|
+
currentTarget: SVGPathElement;
|
|
109
|
+
}, datum: Datum, index: number) => void;
|
|
110
|
+
ondragover: (event: Event & {
|
|
111
|
+
currentTarget: SVGPathElement;
|
|
112
|
+
}, datum: Datum, index: number) => void;
|
|
113
|
+
ondragend: (event: Event & {
|
|
114
|
+
currentTarget: SVGPathElement;
|
|
115
|
+
}, datum: Datum, index: number) => void;
|
|
116
|
+
ontouchstart: (event: Event & {
|
|
117
|
+
currentTarget: SVGPathElement;
|
|
118
|
+
}, datum: Datum, index: number) => void;
|
|
119
|
+
ontouchmove: (event: Event & {
|
|
120
|
+
currentTarget: SVGPathElement;
|
|
121
|
+
}, datum: Datum, index: number) => void;
|
|
122
|
+
ontouchend: (event: Event & {
|
|
123
|
+
currentTarget: SVGPathElement;
|
|
124
|
+
}, datum: Datum, index: number) => void;
|
|
125
|
+
ontouchcancel: (event: Event & {
|
|
126
|
+
currentTarget: SVGPathElement;
|
|
127
|
+
}, datum: Datum, index: number) => void;
|
|
128
|
+
oncontextmenu: (event: Event & {
|
|
129
|
+
currentTarget: SVGPathElement;
|
|
130
|
+
}, datum: Datum, index: number) => void;
|
|
131
|
+
onwheel: (event: Event & {
|
|
132
|
+
currentTarget: SVGPathElement;
|
|
133
|
+
}, datum: Datum, index: number) => void;
|
|
134
|
+
class: string;
|
|
135
|
+
style: string;
|
|
136
|
+
cursor: import("../types/index.js").ConstantAccessor<import("csstype").Property.Cursor, Datum>;
|
|
137
|
+
title: import("../types/index.js").ConstantAccessor<string, Datum>;
|
|
138
|
+
}> & LinkableMarkProps<Datum> & {
|
|
139
|
+
/** the input data array */
|
|
140
|
+
data?: Datum[];
|
|
141
|
+
/** the horizontal position channel */
|
|
142
|
+
x?: ChannelAccessor<Datum>;
|
|
143
|
+
/** the vertical position channel */
|
|
144
|
+
y?: ChannelAccessor<Datum>;
|
|
145
|
+
/** the grouping channel; separate triangulations per group */
|
|
146
|
+
z?: ChannelAccessor<Datum>;
|
|
147
|
+
/** Render using a canvas element instead of SVG paths. */
|
|
148
|
+
canvas?: boolean;
|
|
149
|
+
};
|
|
150
|
+
exports: {};
|
|
151
|
+
bindings: "";
|
|
152
|
+
slots: {};
|
|
153
|
+
events: {};
|
|
154
|
+
};
|
|
155
|
+
declare class __sveltets_Render<Datum = DataRecord> {
|
|
156
|
+
props(): ReturnType<typeof $$render<Datum>>['props'];
|
|
157
|
+
events(): ReturnType<typeof $$render<Datum>>['events'];
|
|
158
|
+
slots(): ReturnType<typeof $$render<Datum>>['slots'];
|
|
159
|
+
bindings(): "";
|
|
160
|
+
exports(): {};
|
|
161
|
+
}
|
|
162
|
+
interface $$IsomorphicComponent {
|
|
163
|
+
new <Datum = DataRecord>(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']>> & {
|
|
164
|
+
$$bindings?: ReturnType<__sveltets_Render<Datum>['bindings']>;
|
|
165
|
+
} & ReturnType<__sveltets_Render<Datum>['exports']>;
|
|
166
|
+
<Datum = DataRecord>(internal: unknown, props: ReturnType<__sveltets_Render<Datum>['props']> & {}): ReturnType<__sveltets_Render<Datum>['exports']>;
|
|
167
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Renders individual Delaunay triangulation edges as separate paths,
|
|
171
|
+
* allowing per-edge styling based on the source data point.
|
|
172
|
+
*/
|
|
173
|
+
declare const DelaunayLink: $$IsomorphicComponent;
|
|
174
|
+
type DelaunayLink<Datum = DataRecord> = InstanceType<typeof DelaunayLink<Datum>>;
|
|
175
|
+
export default DelaunayLink;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<!-- @component
|
|
2
|
+
Renders the full Delaunay triangulation as a single SVG path.
|
|
3
|
+
-->
|
|
4
|
+
<script lang="ts" generics="Datum = DataRecord">
|
|
5
|
+
interface DelaunayMeshMarkProps extends BaseMarkProps<Datum> {
|
|
6
|
+
/** the input data array */
|
|
7
|
+
data?: Datum[];
|
|
8
|
+
/** the horizontal position channel */
|
|
9
|
+
x?: ChannelAccessor<Datum>;
|
|
10
|
+
/** the vertical position channel */
|
|
11
|
+
y?: ChannelAccessor<Datum>;
|
|
12
|
+
/** the grouping channel; separate triangulations per group */
|
|
13
|
+
z?: ChannelAccessor<Datum>;
|
|
14
|
+
/** Render using a canvas element instead of SVG paths. */
|
|
15
|
+
canvas?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
import { Delaunay } from 'd3-delaunay';
|
|
19
|
+
import type {
|
|
20
|
+
DataRecord,
|
|
21
|
+
BaseMarkProps,
|
|
22
|
+
ChannelAccessor,
|
|
23
|
+
MarkType,
|
|
24
|
+
ScaledDataRecord
|
|
25
|
+
} from '../types/index.js';
|
|
26
|
+
import { groupFacetsAndZ } from '../helpers/group.js';
|
|
27
|
+
import { recordizeXY } from '../transforms/recordize.js';
|
|
28
|
+
import Mark from '../Mark.svelte';
|
|
29
|
+
import PathGroup from './helpers/PathGroup.svelte';
|
|
30
|
+
import { usePlot } from '../hooks/usePlot.svelte.js';
|
|
31
|
+
import { getPlotDefaults } from '../hooks/plotDefaults.js';
|
|
32
|
+
|
|
33
|
+
const DEFAULTS = {
|
|
34
|
+
...getPlotDefaults().delaunayMesh
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
let markProps: DelaunayMeshMarkProps = $props();
|
|
38
|
+
|
|
39
|
+
const {
|
|
40
|
+
data = [] as Datum[],
|
|
41
|
+
class: className = 'delaunay-mesh',
|
|
42
|
+
canvas = false,
|
|
43
|
+
...options
|
|
44
|
+
}: DelaunayMeshMarkProps = $derived({ ...DEFAULTS, ...markProps });
|
|
45
|
+
|
|
46
|
+
const args = $derived(
|
|
47
|
+
recordizeXY({
|
|
48
|
+
data: data as any[],
|
|
49
|
+
...options
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const plot = usePlot();
|
|
54
|
+
|
|
55
|
+
function computeMeshPaths(scaledData: ScaledDataRecord[]) {
|
|
56
|
+
const scaledByDatum = new Map(scaledData.map((d) => [d.datum, d]));
|
|
57
|
+
const meshes: { path: string; datum: ScaledDataRecord }[] = [];
|
|
58
|
+
|
|
59
|
+
groupFacetsAndZ(
|
|
60
|
+
scaledData.map((d) => d.datum),
|
|
61
|
+
args,
|
|
62
|
+
(groupItems) => {
|
|
63
|
+
const groupScaled = groupItems
|
|
64
|
+
.map((d) => scaledByDatum.get(d))
|
|
65
|
+
.filter(
|
|
66
|
+
(d): d is ScaledDataRecord =>
|
|
67
|
+
d !== undefined &&
|
|
68
|
+
d.valid &&
|
|
69
|
+
Number.isFinite(d.x as number) &&
|
|
70
|
+
Number.isFinite(d.y as number)
|
|
71
|
+
);
|
|
72
|
+
if (groupScaled.length < 2) return;
|
|
73
|
+
const delaunay = Delaunay.from(
|
|
74
|
+
groupScaled,
|
|
75
|
+
(d) => d.x as number,
|
|
76
|
+
(d) => d.y as number
|
|
77
|
+
);
|
|
78
|
+
const path = delaunay.render();
|
|
79
|
+
if (path) meshes.push({ path, datum: groupScaled[0] });
|
|
80
|
+
},
|
|
81
|
+
false
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return meshes;
|
|
85
|
+
}
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<Mark
|
|
89
|
+
type={'delaunayMesh' as MarkType}
|
|
90
|
+
channels={['x', 'y', 'fill', 'stroke', 'strokeOpacity', 'fillOpacity', 'opacity']}
|
|
91
|
+
defaults={{ fill: 'none', stroke: 'currentColor' }}
|
|
92
|
+
{...args}>
|
|
93
|
+
{#snippet children({ scaledData, usedScales })}
|
|
94
|
+
<PathGroup
|
|
95
|
+
paths={computeMeshPaths(scaledData)}
|
|
96
|
+
{args}
|
|
97
|
+
{className}
|
|
98
|
+
{usedScales}
|
|
99
|
+
{plot}
|
|
100
|
+
{canvas} />
|
|
101
|
+
{/snippet}
|
|
102
|
+
</Mark>
|