svelteplot 0.10.3 → 0.11.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/Mark.svelte +42 -25
- package/dist/Mark.svelte.d.ts +111 -32
- package/dist/Plot.svelte +21 -15
- package/dist/core/Facet.svelte +1 -1
- package/dist/core/FacetAxes.svelte +13 -8
- package/dist/core/FacetGrid.svelte +4 -4
- package/dist/core/Plot.svelte +41 -35
- package/dist/helpers/autoScales.d.ts +3 -3
- package/dist/helpers/autoScales.js +28 -18
- package/dist/helpers/autoTicks.js +2 -0
- package/dist/helpers/callWithProps.d.ts +1 -2
- package/dist/helpers/facets.js +0 -1
- package/dist/helpers/index.js +1 -1
- package/dist/helpers/mergeDeep.d.ts +1 -3
- package/dist/helpers/mergeDeep.js +15 -16
- package/dist/helpers/projection.d.ts +4 -3
- package/dist/helpers/projection.js +17 -5
- package/dist/helpers/reduce.d.ts +4 -4
- package/dist/helpers/reduce.js +6 -4
- package/dist/helpers/regressionLoess.js +2 -1
- package/dist/helpers/resolve.d.ts +6 -3
- package/dist/helpers/resolve.js +25 -16
- package/dist/helpers/scales.d.ts +10 -10
- package/dist/helpers/scales.js +43 -13
- package/dist/helpers/time.d.ts +10 -3
- package/dist/helpers/time.js +2 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/plotDefaults.d.ts +3 -1
- package/dist/hooks/plotDefaults.js +33 -1
- package/dist/hooks/usePlot.svelte.d.ts +10 -25
- package/dist/hooks/usePlot.svelte.js +8 -7
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -3
- package/dist/marks/Area.svelte +24 -13
- package/dist/marks/Area.svelte.d.ts +118 -34
- package/dist/marks/AreaX.svelte +42 -8
- package/dist/marks/AreaX.svelte.d.ts +154 -71
- package/dist/marks/AreaY.svelte +42 -8
- package/dist/marks/AreaY.svelte.d.ts +154 -71
- package/dist/marks/Arrow.svelte +42 -23
- package/dist/marks/Arrow.svelte.d.ts +114 -35
- package/dist/marks/AxisX.svelte +43 -28
- package/dist/marks/AxisX.svelte.d.ts +125 -40
- package/dist/marks/AxisY.svelte +43 -26
- package/dist/marks/AxisY.svelte.d.ts +127 -40
- package/dist/marks/BarX.svelte +12 -10
- package/dist/marks/BarX.svelte.d.ts +104 -32
- package/dist/marks/BarY.svelte +11 -10
- package/dist/marks/BarY.svelte.d.ts +106 -34
- package/dist/marks/BollingerX.svelte +4 -7
- package/dist/marks/BollingerX.svelte.d.ts +105 -30
- package/dist/marks/BollingerY.svelte +3 -0
- package/dist/marks/BollingerY.svelte.d.ts +105 -30
- package/dist/marks/BoxX.svelte +3 -3
- package/dist/marks/BoxY.svelte +12 -9
- package/dist/marks/BoxY.svelte.d.ts +128 -53
- package/dist/marks/Brush.svelte +26 -21
- package/dist/marks/Brush.svelte.d.ts +119 -60
- package/dist/marks/Cell.svelte +13 -9
- package/dist/marks/Cell.svelte.d.ts +105 -30
- package/dist/marks/CellX.svelte +2 -1
- package/dist/marks/CellX.svelte.d.ts +105 -32
- package/dist/marks/CellY.svelte +2 -1
- package/dist/marks/CellY.svelte.d.ts +105 -32
- package/dist/marks/ColorLegend.svelte +24 -13
- package/dist/marks/ColorLegend.svelte.d.ts +1 -0
- package/dist/marks/CustomMark.svelte +16 -10
- package/dist/marks/CustomMark.svelte.d.ts +112 -31
- package/dist/marks/CustomMarkHTML.svelte +8 -2
- package/dist/marks/CustomMarkHTML.svelte.d.ts +8 -2
- package/dist/marks/DifferenceY.svelte +31 -20
- package/dist/marks/DifferenceY.svelte.d.ts +134 -55
- package/dist/marks/Dot.svelte +21 -11
- package/dist/marks/Dot.svelte.d.ts +117 -38
- package/dist/marks/DotX.svelte +2 -0
- package/dist/marks/DotX.svelte.d.ts +136 -62
- package/dist/marks/DotY.svelte +1 -0
- package/dist/marks/DotY.svelte.d.ts +135 -62
- package/dist/marks/Frame.svelte +47 -9
- package/dist/marks/Frame.svelte.d.ts +124 -41
- package/dist/marks/Geo.svelte +21 -12
- package/dist/marks/Geo.svelte.d.ts +105 -30
- package/dist/marks/Graticule.svelte +3 -0
- package/dist/marks/Graticule.svelte.d.ts +3 -0
- package/dist/marks/GridX.svelte +31 -16
- package/dist/marks/GridX.svelte.d.ts +108 -32
- package/dist/marks/GridY.svelte +30 -15
- package/dist/marks/GridY.svelte.d.ts +108 -32
- package/dist/marks/HTMLTooltip.svelte +14 -7
- package/dist/marks/HTMLTooltip.svelte.d.ts +7 -0
- package/dist/marks/Image.svelte +50 -25
- package/dist/marks/Image.svelte.d.ts +117 -35
- package/dist/marks/Line.svelte +67 -44
- package/dist/marks/Line.svelte.d.ts +119 -30
- package/dist/marks/LineX.svelte +2 -1
- package/dist/marks/LineX.svelte.d.ts +142 -69
- package/dist/marks/LineY.svelte +2 -1
- package/dist/marks/LineY.svelte.d.ts +142 -69
- package/dist/marks/Link.svelte +70 -46
- package/dist/marks/Link.svelte.d.ts +126 -41
- package/dist/marks/Pointer.svelte +24 -15
- package/dist/marks/Pointer.svelte.d.ts +7 -0
- package/dist/marks/Rect.svelte +13 -5
- package/dist/marks/Rect.svelte.d.ts +116 -35
- package/dist/marks/RectX.svelte +6 -3
- package/dist/marks/RectX.svelte.d.ts +158 -12
- package/dist/marks/RectY.svelte +6 -3
- package/dist/marks/RectY.svelte.d.ts +158 -12
- package/dist/marks/RegressionX.svelte +13 -6
- package/dist/marks/RegressionX.svelte.d.ts +8 -3
- package/dist/marks/RegressionY.svelte +13 -6
- package/dist/marks/RegressionY.svelte.d.ts +8 -3
- package/dist/marks/RuleX.svelte +18 -11
- package/dist/marks/RuleX.svelte.d.ts +112 -32
- package/dist/marks/RuleY.svelte +19 -12
- package/dist/marks/RuleY.svelte.d.ts +114 -34
- package/dist/marks/Spike.svelte +11 -5
- package/dist/marks/Spike.svelte.d.ts +146 -68
- package/dist/marks/Text.svelte +24 -7
- package/dist/marks/Text.svelte.d.ts +253 -75
- package/dist/marks/TickX.svelte +56 -48
- package/dist/marks/TickX.svelte.d.ts +114 -40
- package/dist/marks/TickY.svelte +59 -51
- package/dist/marks/TickY.svelte.d.ts +117 -43
- package/dist/marks/Trail.svelte +25 -13
- package/dist/marks/Trail.svelte.d.ts +116 -33
- package/dist/marks/Vector.svelte +20 -11
- package/dist/marks/Vector.svelte.d.ts +116 -35
- package/dist/marks/WaffleX.svelte +18 -16
- package/dist/marks/WaffleX.svelte.d.ts +131 -57
- package/dist/marks/WaffleY.svelte +16 -15
- package/dist/marks/WaffleY.svelte.d.ts +129 -56
- package/dist/marks/helpers/Anchor.svelte +17 -2
- package/dist/marks/helpers/Anchor.svelte.d.ts +16 -1
- package/dist/marks/helpers/AreaCanvas.svelte +8 -8
- package/dist/marks/helpers/BaseAxisX.svelte +38 -41
- package/dist/marks/helpers/BaseAxisX.svelte.d.ts +11 -17
- package/dist/marks/helpers/BaseAxisY.svelte +35 -35
- package/dist/marks/helpers/BaseAxisY.svelte.d.ts +12 -15
- package/dist/marks/helpers/Box.svelte +35 -28
- package/dist/marks/helpers/Box.svelte.d.ts +122 -50
- package/dist/marks/helpers/DotCanvas.svelte +11 -9
- package/dist/marks/helpers/GeoCanvas.svelte +7 -6
- package/dist/marks/helpers/LineCanvas.svelte +7 -7
- package/dist/marks/helpers/LinearGradientX.svelte +2 -2
- package/dist/marks/helpers/LinearGradientX.svelte.d.ts +1 -1
- package/dist/marks/helpers/LinearGradientY.svelte +2 -2
- package/dist/marks/helpers/LinearGradientY.svelte.d.ts +1 -1
- package/dist/marks/helpers/Marker.svelte +2 -2
- package/dist/marks/helpers/MarkerPath.svelte +15 -12
- package/dist/marks/helpers/MarkerPath.svelte.d.ts +105 -32
- package/dist/marks/helpers/MultilineText.svelte +24 -17
- package/dist/marks/helpers/MultilineText.svelte.d.ts +1 -1
- package/dist/marks/helpers/RectCanvas.svelte +31 -26
- package/dist/marks/helpers/RectPath.svelte +2 -2
- package/dist/marks/helpers/Regression.svelte +176 -86
- package/dist/marks/helpers/Regression.svelte.d.ts +20 -8
- package/dist/marks/helpers/RuleCanvas.svelte +9 -6
- package/dist/marks/helpers/TextCanvas.svelte +13 -9
- package/dist/marks/helpers/TextCanvas.svelte.d.ts +6 -6
- package/dist/marks/helpers/TickCanvas.svelte +6 -5
- package/dist/marks/helpers/TrailCanvas.svelte +16 -18
- package/dist/marks/helpers/TrailCanvas.svelte.d.ts +3 -5
- package/dist/marks/helpers/canvas.js +16 -9
- package/dist/marks/helpers/events.d.ts +2 -2
- package/dist/marks/helpers/events.js +14 -7
- package/dist/marks/helpers/waffle.d.ts +3 -3
- package/dist/marks/helpers/waffle.js +6 -4
- package/dist/regression/polynomial.d.ts +1 -1
- package/dist/regression/polynomial.js +5 -5
- package/dist/regression/utils/determination.d.ts +1 -1
- package/dist/regression/utils/determination.js +1 -1
- package/dist/regression/utils/geometry.d.ts +1 -1
- package/dist/regression/utils/interpose.d.ts +1 -1
- package/dist/regression/utils/interpose.js +1 -1
- package/dist/regression/utils/points.d.ts +1 -1
- package/dist/transforms/bin.d.ts +3 -3
- package/dist/transforms/bin.js +29 -20
- package/dist/transforms/bollinger.d.ts +8 -0
- package/dist/transforms/bollinger.js +9 -1
- package/dist/transforms/centroid.d.ts +4 -0
- package/dist/transforms/centroid.js +4 -0
- package/dist/transforms/density.d.ts +4 -4
- package/dist/transforms/density.js +20 -13
- package/dist/transforms/dodge.d.ts +12 -1
- package/dist/transforms/dodge.js +15 -6
- package/dist/transforms/group.d.ts +141 -4
- package/dist/transforms/group.js +4 -1
- package/dist/transforms/interval.d.ts +204 -60
- package/dist/transforms/jitter.d.ts +421 -4
- package/dist/transforms/jitter.js +10 -1
- package/dist/transforms/map.d.ts +412 -4
- package/dist/transforms/map.js +3 -3
- package/dist/transforms/normalize.d.ts +276 -5
- package/dist/transforms/normalize.js +5 -3
- package/dist/transforms/recordize.d.ts +17 -5
- package/dist/transforms/recordize.js +13 -9
- package/dist/transforms/rename.d.ts +11 -4
- package/dist/transforms/rename.js +7 -2
- package/dist/transforms/select.d.ts +722 -210
- package/dist/transforms/select.js +13 -1
- package/dist/transforms/shift.d.ts +8 -0
- package/dist/transforms/shift.js +20 -6
- package/dist/transforms/sort.d.ts +13 -258
- package/dist/transforms/sort.js +13 -10
- package/dist/transforms/stack.d.ts +58 -9
- package/dist/transforms/stack.js +27 -11
- package/dist/transforms/window.d.ts +221 -66
- package/dist/transforms/window.js +8 -2
- package/dist/types/axes.d.ts +43 -0
- package/dist/types/axes.js +1 -0
- package/dist/types/channel.d.ts +30 -2
- package/dist/types/data.d.ts +14 -1
- package/dist/types/facet.d.ts +5 -0
- package/dist/types/index.d.ts +33 -8
- package/dist/types/index.js +11 -7
- package/dist/types/mark.d.ts +124 -35
- package/dist/types/plot.d.ts +118 -16
- package/dist/types/scale.d.ts +125 -8
- package/package.json +178 -175
- package/dist/helpers/autoTicks.d.ts +0 -12
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
<script module lang="ts">
|
|
2
|
-
import type { BaseMarkProps, ChannelAccessor
|
|
2
|
+
import type { BaseMarkProps, ChannelAccessor } from '../../types/index.js';
|
|
3
3
|
|
|
4
|
-
type RegressionType = 'linear' | 'quad' | 'poly' | 'exp' | 'log' | 'pow' | 'loess';
|
|
4
|
+
export type RegressionType = 'linear' | 'quad' | 'poly' | 'exp' | 'log' | 'pow' | 'loess';
|
|
5
5
|
|
|
6
|
-
export type
|
|
6
|
+
export type RegressionOptions = BaseMarkProps & {
|
|
7
|
+
/** the horizontal position channel; bound to the x scale */
|
|
7
8
|
x: ChannelAccessor;
|
|
9
|
+
/** the vertical position channel; bound to the y scale */
|
|
8
10
|
y: ChannelAccessor;
|
|
9
|
-
type
|
|
11
|
+
/** the regression model type */
|
|
12
|
+
type?: RegressionType;
|
|
10
13
|
/**
|
|
11
14
|
* If order is specified, sets the regression's order to the specified number.
|
|
12
15
|
* For example, if order is set to 4, the regression generator will perform a
|
|
@@ -16,13 +19,13 @@
|
|
|
16
19
|
* regression line will fit your data with a high determination coefficient,
|
|
17
20
|
* it may have little predictive power for data outside of your domain.
|
|
18
21
|
*/
|
|
19
|
-
order
|
|
20
|
-
|
|
21
|
-
base
|
|
22
|
-
|
|
23
|
-
span
|
|
24
|
-
|
|
25
|
-
confidence
|
|
22
|
+
order?: number;
|
|
23
|
+
/** the base for logarithmic regression */
|
|
24
|
+
base?: number;
|
|
25
|
+
/** the bandwidth for LOESS regression, as a fraction of the data range (0 to 1) */
|
|
26
|
+
span?: number;
|
|
27
|
+
/** the confidence level for confidence bands (e.g. 0.95 for 95% confidence) */
|
|
28
|
+
confidence?: number | false;
|
|
26
29
|
};
|
|
27
30
|
</script>
|
|
28
31
|
|
|
@@ -42,25 +45,64 @@
|
|
|
42
45
|
import { resolveChannel } from '../../helpers/resolve.js';
|
|
43
46
|
import { confidenceInterval } from '../../helpers/math.js';
|
|
44
47
|
import callWithProps from '../../helpers/callWithProps.js';
|
|
45
|
-
import {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
[
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
48
|
+
import type { DataRecord, FacetContext, RawValue } from '../../types/index.js';
|
|
49
|
+
import { usePlot } from '../../hooks/usePlot.svelte.js';
|
|
50
|
+
|
|
51
|
+
type RegressionData = { x: number; y: number };
|
|
52
|
+
type RegressionOutput = [number, number][] & {
|
|
53
|
+
predict?: (x: number) => number;
|
|
54
|
+
predictMany?: (points: number[]) => number[];
|
|
55
|
+
};
|
|
56
|
+
type RegressionGenerator = ((data: RegressionData[]) => RegressionOutput) & {
|
|
57
|
+
x: (fn: (d: RegressionData) => number) => RegressionGenerator;
|
|
58
|
+
y: (fn: (d: RegressionData) => number) => RegressionGenerator;
|
|
59
|
+
domain?: (domain: [number, number]) => RegressionGenerator;
|
|
60
|
+
order?: (order: number) => RegressionGenerator;
|
|
61
|
+
base?: (base: number) => RegressionGenerator;
|
|
62
|
+
bandwidth?: (span: number) => RegressionGenerator;
|
|
63
|
+
};
|
|
64
|
+
type RegressionFactory = () => RegressionGenerator;
|
|
65
|
+
|
|
66
|
+
interface RegressionProps extends RegressionOptions {
|
|
67
|
+
data: DataRecord[];
|
|
68
|
+
dependent: 'x' | 'y';
|
|
69
|
+
canvas?: boolean;
|
|
61
70
|
}
|
|
62
71
|
|
|
63
|
-
|
|
72
|
+
// Normalize all regression constructors behind one callable adapter shape.
|
|
73
|
+
const regressions: Record<RegressionType, RegressionFactory> = {
|
|
74
|
+
linear: regressionLinear as RegressionFactory,
|
|
75
|
+
quad: regressionQuad as RegressionFactory,
|
|
76
|
+
poly: regressionPoly as RegressionFactory,
|
|
77
|
+
exp: regressionExp as RegressionFactory,
|
|
78
|
+
log: regressionLog as RegressionFactory,
|
|
79
|
+
pow: regressionPow as RegressionFactory,
|
|
80
|
+
loess: regressionLoess as RegressionFactory
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
function maybeRegression(name: RegressionType): RegressionFactory {
|
|
84
|
+
const fn = regressions[name];
|
|
85
|
+
if (fn) return fn;
|
|
86
|
+
throw new Error(`unknown regression ${name}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Regression engines operate on numeric domains; convert Date channels to epoch ms.
|
|
90
|
+
function toNumeric(value: RawValue): number {
|
|
91
|
+
return value instanceof Date ? value.valueOf() : Number(value);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Convert generated points back to Date for time scales so downstream marks render correctly.
|
|
95
|
+
function toOutputX(value: number, scaleType: string): RawValue {
|
|
96
|
+
return scaleType === 'time' ? new Date(value) : value;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function makeTicks(domain: [number, number], count = 40): number[] {
|
|
100
|
+
const [start, end] = domain;
|
|
101
|
+
if (start === end) return [start];
|
|
102
|
+
const tickCount = Math.max(1, count);
|
|
103
|
+
const step = (end - start) / tickCount;
|
|
104
|
+
return Array.from({ length: tickCount + 1 }, (_, i) => start + i * step);
|
|
105
|
+
}
|
|
64
106
|
|
|
65
107
|
const plot = usePlot();
|
|
66
108
|
|
|
@@ -69,91 +111,138 @@
|
|
|
69
111
|
dependent,
|
|
70
112
|
type = 'linear',
|
|
71
113
|
order = 3,
|
|
72
|
-
base =
|
|
114
|
+
base = Math.E,
|
|
73
115
|
span = 0.3,
|
|
74
116
|
confidence = 0.99,
|
|
75
|
-
class: className =
|
|
117
|
+
class: className = '',
|
|
118
|
+
canvas = false,
|
|
76
119
|
...options
|
|
77
|
-
}:
|
|
120
|
+
}: RegressionProps = $props();
|
|
78
121
|
|
|
79
122
|
const { getTestFacet } = getContext<FacetContext>('svelteplot/facet');
|
|
80
|
-
|
|
123
|
+
const testFacet = $derived(getTestFacet());
|
|
124
|
+
|
|
125
|
+
const filteredData = $derived(data.filter((d) => testFacet(d, options as any)));
|
|
81
126
|
|
|
82
|
-
|
|
127
|
+
const independent: 'x' | 'y' = $derived(dependent === 'x' ? 'y' : 'x');
|
|
83
128
|
|
|
84
|
-
|
|
129
|
+
const regressionFn = $derived(maybeRegression(type));
|
|
85
130
|
|
|
86
|
-
|
|
131
|
+
// Build a clean numeric input set for regression fitting, dropping invalid rows early.
|
|
132
|
+
const regressionInput = $derived(
|
|
133
|
+
filteredData
|
|
134
|
+
.map((d) => ({
|
|
135
|
+
x: toNumeric(resolveChannel(independent, d, options as any)),
|
|
136
|
+
y: toNumeric(resolveChannel(dependent, d, options as any))
|
|
137
|
+
}))
|
|
138
|
+
.filter(({ x, y }) => Number.isFinite(x) && Number.isFinite(y))
|
|
139
|
+
);
|
|
87
140
|
|
|
88
|
-
|
|
141
|
+
const independentDomain = $derived.by(() => {
|
|
142
|
+
// Prefer the active scale domain, but fall back to observed data when the scale
|
|
143
|
+
// is still initializing (common in tests and first render passes).
|
|
144
|
+
const scaleDomain = plot.scales[independent].domain;
|
|
145
|
+
const scaleStart = toNumeric(scaleDomain[0]);
|
|
146
|
+
const scaleEnd = toNumeric(scaleDomain.at(-1) ?? scaleDomain[0]);
|
|
147
|
+
if (Number.isFinite(scaleStart) && Number.isFinite(scaleEnd)) {
|
|
148
|
+
return scaleStart <= scaleEnd
|
|
149
|
+
? ([scaleStart, scaleEnd] as [number, number])
|
|
150
|
+
: ([scaleEnd, scaleStart] as [number, number]);
|
|
151
|
+
}
|
|
152
|
+
if (regressionInput.length === 0) return null;
|
|
153
|
+
let min = Infinity;
|
|
154
|
+
let max = -Infinity;
|
|
155
|
+
for (const point of regressionInput) {
|
|
156
|
+
if (point.x < min) min = point.x;
|
|
157
|
+
if (point.x > max) max = point.x;
|
|
158
|
+
}
|
|
159
|
+
if (!Number.isFinite(min) || !Number.isFinite(max)) return null;
|
|
160
|
+
return min <= max ? ([min, max] as [number, number]) : ([max, min] as [number, number]);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const regression = $derived(
|
|
89
164
|
callWithProps(regressionFn, [], {
|
|
90
|
-
x: (d) =>
|
|
91
|
-
y: (d) =>
|
|
165
|
+
x: (d: RegressionData) => d.x,
|
|
166
|
+
y: (d: RegressionData) => d.y,
|
|
92
167
|
...(type === 'poly' ? { order } : {}),
|
|
93
168
|
...(type === 'log' ? { base } : {}),
|
|
94
|
-
...(
|
|
169
|
+
...(type !== 'loess' && independentDomain != null ? { domain: independentDomain } : {}),
|
|
95
170
|
...(type === 'loess' ? { bandwidth: span } : {})
|
|
96
|
-
})(
|
|
171
|
+
})(regressionInput)
|
|
97
172
|
);
|
|
98
173
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
})
|
|
116
|
-
: regression.map(([__x, __y]) => ({
|
|
117
|
-
__x: plot.scales[independent].type === 'time' ? new Date(__x) : __x,
|
|
118
|
-
__y
|
|
119
|
-
}))
|
|
120
|
-
);
|
|
174
|
+
const regrPoints = $derived.by(() => {
|
|
175
|
+
if (independentDomain == null) return [] as number[];
|
|
176
|
+
// Use scale ticks when available; otherwise synthesize evenly spaced samples.
|
|
177
|
+
const ticks = plot.scales[independent].fn
|
|
178
|
+
.ticks(40)
|
|
179
|
+
.map(toNumeric)
|
|
180
|
+
.filter((value): value is number => Number.isFinite(value));
|
|
181
|
+
const points = ticks.length > 0 ? ticks : makeTicks(independentDomain, 40);
|
|
182
|
+
return [
|
|
183
|
+
...new Set(
|
|
184
|
+
[independentDomain[0], ...points, independentDomain[1]].filter(
|
|
185
|
+
(value): value is number => Number.isFinite(value)
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
].sort((a, b) => a - b);
|
|
189
|
+
});
|
|
121
190
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
191
|
+
const regrData = $derived.by(() => {
|
|
192
|
+
// Prefer batch prediction when supported, then per-point predict, then raw curve output.
|
|
193
|
+
if (typeof regression.predictMany === 'function') {
|
|
194
|
+
return regression.predictMany(regrPoints).map((__y, i) => ({
|
|
195
|
+
__x: toOutputX(regrPoints[i], plot.scales[independent].type),
|
|
196
|
+
__y
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
if (typeof regression.predict === 'function') {
|
|
200
|
+
return regrPoints.map((point) => ({
|
|
201
|
+
__x: toOutputX(point, plot.scales[independent].type),
|
|
202
|
+
__y: regression.predict!(point)
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
return regression.map(([__x, __y]) => ({
|
|
206
|
+
__x: toOutputX(__x, plot.scales[independent].type),
|
|
207
|
+
__y
|
|
208
|
+
}));
|
|
209
|
+
});
|
|
125
210
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
?
|
|
129
|
-
data
|
|
130
|
-
.map((d) => ({
|
|
131
|
-
x: resolveChannel(independent, d, options),
|
|
132
|
-
y: resolveChannel(dependent, d, options)
|
|
133
|
-
}))
|
|
134
|
-
.filter(
|
|
135
|
-
({ x, y }) => (Number.isFinite(x) || isDate(x)) && Number.isFinite(y)
|
|
136
|
-
),
|
|
137
|
-
regression.predict,
|
|
138
|
-
1 - confidence
|
|
139
|
-
)
|
|
211
|
+
const stroke = $derived(
|
|
212
|
+
options.stroke != null && filteredData.length
|
|
213
|
+
? resolveChannel('stroke', filteredData[0], options as any)
|
|
140
214
|
: null
|
|
141
215
|
);
|
|
142
216
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
217
|
+
const confBandGen = $derived(
|
|
218
|
+
// Confidence bands require a predictor function and at least 3 points.
|
|
219
|
+
confidence !== false &&
|
|
220
|
+
typeof confidence === 'number' &&
|
|
221
|
+
typeof regression.predict === 'function' &&
|
|
222
|
+
regressionInput.length > 2
|
|
223
|
+
? confidenceInterval(regressionInput, regression.predict, 1 - confidence)
|
|
224
|
+
: null
|
|
150
225
|
);
|
|
226
|
+
|
|
227
|
+
const confBandData = $derived.by(() => {
|
|
228
|
+
if (confBandGen == null) return [];
|
|
229
|
+
return regrPoints.map((x) => {
|
|
230
|
+
const { x: __x, left, right } = confBandGen(x);
|
|
231
|
+
return {
|
|
232
|
+
__x: toOutputX(__x, plot.scales[independent].type),
|
|
233
|
+
__y1: left,
|
|
234
|
+
__y2: right
|
|
235
|
+
};
|
|
236
|
+
});
|
|
237
|
+
});
|
|
151
238
|
</script>
|
|
152
239
|
|
|
153
240
|
{#if filteredData.length}
|
|
241
|
+
<!-- TODO: when canvas rendering is requested, render both marks in same <canvas> -->
|
|
154
242
|
<g class="regression-{independent} {className || ''}">
|
|
155
243
|
<Line
|
|
156
244
|
data={regrData}
|
|
245
|
+
{canvas}
|
|
157
246
|
{...{
|
|
158
247
|
...options,
|
|
159
248
|
fx: null,
|
|
@@ -165,6 +254,7 @@
|
|
|
165
254
|
{#if confBandData.length}
|
|
166
255
|
<Area
|
|
167
256
|
data={confBandData}
|
|
257
|
+
{canvas}
|
|
168
258
|
{...dependent === 'y'
|
|
169
259
|
? { x1: '__x', y1: '__y1', y2: '__y2' }
|
|
170
260
|
: { y1: '__x', x1: '__y1', x2: '__y2' }}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { BaseMarkProps, ChannelAccessor } from '../../types/index.js';
|
|
2
|
-
type RegressionType = 'linear' | 'quad' | 'poly' | 'exp' | 'log' | 'pow' | 'loess';
|
|
3
|
-
export type
|
|
2
|
+
export type RegressionType = 'linear' | 'quad' | 'poly' | 'exp' | 'log' | 'pow' | 'loess';
|
|
3
|
+
export type RegressionOptions = BaseMarkProps & {
|
|
4
|
+
/** the horizontal position channel; bound to the x scale */
|
|
4
5
|
x: ChannelAccessor;
|
|
6
|
+
/** the vertical position channel; bound to the y scale */
|
|
5
7
|
y: ChannelAccessor;
|
|
6
|
-
type
|
|
8
|
+
/** the regression model type */
|
|
9
|
+
type?: RegressionType;
|
|
7
10
|
/**
|
|
8
11
|
* If order is specified, sets the regression's order to the specified number.
|
|
9
12
|
* For example, if order is set to 4, the regression generator will perform a
|
|
@@ -13,11 +16,20 @@ export type RegressionMarkProps = BaseMarkProps & {
|
|
|
13
16
|
* regression line will fit your data with a high determination coefficient,
|
|
14
17
|
* it may have little predictive power for data outside of your domain.
|
|
15
18
|
*/
|
|
16
|
-
order
|
|
17
|
-
base
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
order?: number;
|
|
20
|
+
/** the base for logarithmic regression */
|
|
21
|
+
base?: number;
|
|
22
|
+
/** the bandwidth for LOESS regression, as a fraction of the data range (0 to 1) */
|
|
23
|
+
span?: number;
|
|
24
|
+
/** the confidence level for confidence bands (e.g. 0.95 for 95% confidence) */
|
|
25
|
+
confidence?: number | false;
|
|
20
26
|
};
|
|
21
|
-
|
|
27
|
+
import type { DataRecord } from '../../types/index.js';
|
|
28
|
+
interface RegressionProps extends RegressionOptions {
|
|
29
|
+
data: DataRecord[];
|
|
30
|
+
dependent: 'x' | 'y';
|
|
31
|
+
canvas?: boolean;
|
|
32
|
+
}
|
|
33
|
+
declare const Regression: import("svelte").Component<RegressionProps, {}, "">;
|
|
22
34
|
type Regression = ReturnType<typeof Regression>;
|
|
23
35
|
export default Regression;
|
|
@@ -62,21 +62,24 @@ Helper component for rendering Rule marks (RuleX and RuleY) in canvas
|
|
|
62
62
|
for (const datum of data) {
|
|
63
63
|
if (!datum.valid) continue;
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
const styleProps = resolveScaledStyleProps(
|
|
66
66
|
datum.datum,
|
|
67
67
|
options,
|
|
68
68
|
usedScales,
|
|
69
69
|
plot,
|
|
70
70
|
'stroke'
|
|
71
|
-
)
|
|
71
|
+
) as Record<string, unknown>;
|
|
72
72
|
|
|
73
|
-
const opacity = maybeOpacity(
|
|
74
|
-
const strokeOpacity = maybeOpacity(
|
|
73
|
+
const opacity = maybeOpacity(styleProps['opacity']);
|
|
74
|
+
const strokeOpacity = maybeOpacity(styleProps['stroke-opacity']);
|
|
75
75
|
|
|
76
|
-
stroke = resolveColor(
|
|
76
|
+
const stroke = resolveColor(
|
|
77
|
+
String(styleProps.stroke || 'currentColor'),
|
|
78
|
+
canvas
|
|
79
|
+
);
|
|
77
80
|
|
|
78
81
|
if (stroke && stroke !== 'none') {
|
|
79
|
-
const resolvedLinecap =
|
|
82
|
+
const resolvedLinecap = styleProps['stroke-linecap'] as
|
|
80
83
|
| CanvasLineCap
|
|
81
84
|
| undefined
|
|
82
85
|
| null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
<script lang="ts" generics="Datum
|
|
2
|
-
interface TextCanvasProps<Datum
|
|
1
|
+
<script lang="ts" generics="Datum = DataRecord | GeoJSON.GeoJsonObject">
|
|
2
|
+
interface TextCanvasProps<Datum> {
|
|
3
3
|
data: ScaledDataRecord<Datum>[];
|
|
4
4
|
options: BaseMarkProps<Datum> & {
|
|
5
5
|
x?: ChannelAccessor<Datum>;
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
UsedScales
|
|
48
48
|
} from '../../types/index.js';
|
|
49
49
|
import { resolveProp, resolveScaledStyleProps } from '../../helpers/resolve.js';
|
|
50
|
-
import { CSS_VAR } from '../../constants';
|
|
51
|
-
import { maybeFromPixel, maybeFromRem } from '../../helpers/getBaseStyles';
|
|
50
|
+
import { CSS_VAR } from '../../constants.js';
|
|
51
|
+
import { maybeFromPixel, maybeFromRem } from '../../helpers/getBaseStyles.js';
|
|
52
52
|
import { usePlot } from '../../hooks/usePlot.svelte.js';
|
|
53
53
|
import CanvasLayer from './CanvasLayer.svelte';
|
|
54
54
|
import { resolveColor } from './canvas.js';
|
|
@@ -164,7 +164,11 @@
|
|
|
164
164
|
for (const datum of data) {
|
|
165
165
|
if (!datum.valid) continue;
|
|
166
166
|
|
|
167
|
-
const frameAnchor = resolveProp(
|
|
167
|
+
const frameAnchor = resolveProp(
|
|
168
|
+
options.frameAnchor,
|
|
169
|
+
datum.datum as any,
|
|
170
|
+
'middle'
|
|
171
|
+
);
|
|
168
172
|
const isLeft =
|
|
169
173
|
frameAnchor === 'left' ||
|
|
170
174
|
frameAnchor === 'top-left' ||
|
|
@@ -206,7 +210,7 @@
|
|
|
206
210
|
const lineAnchor = normalizeLineAnchor(
|
|
207
211
|
resolveProp(
|
|
208
212
|
options.lineAnchor,
|
|
209
|
-
datum.datum,
|
|
213
|
+
datum.datum as any,
|
|
210
214
|
options.y != null
|
|
211
215
|
? 'middle'
|
|
212
216
|
: isTop
|
|
@@ -218,7 +222,7 @@
|
|
|
218
222
|
);
|
|
219
223
|
const defaultTextAnchor = isLeft ? 'start' : isRight ? 'end' : 'middle';
|
|
220
224
|
const styleProps = resolveScaledStyleProps(
|
|
221
|
-
datum.datum,
|
|
225
|
+
datum.datum as any,
|
|
222
226
|
{
|
|
223
227
|
...DEFAULT_TEXT_OPTIONS,
|
|
224
228
|
textAnchor: defaultTextAnchor,
|
|
@@ -227,7 +231,7 @@
|
|
|
227
231
|
usedScales,
|
|
228
232
|
plot,
|
|
229
233
|
'fill'
|
|
230
|
-
)
|
|
234
|
+
) as Record<string, unknown>;
|
|
231
235
|
|
|
232
236
|
const inheritedFontSize = inheritedFontStyles.fontSize || '12px';
|
|
233
237
|
const { css: fontSize, numeric: fontSizePx } = toFontSize(
|
|
@@ -259,7 +263,7 @@
|
|
|
259
263
|
Math.PI) /
|
|
260
264
|
180;
|
|
261
265
|
|
|
262
|
-
const textLines = String(resolveProp(options.text, datum.datum, ''))
|
|
266
|
+
const textLines = String(resolveProp(options.text, datum.datum as any, ''))
|
|
263
267
|
.split('\n')
|
|
264
268
|
.map((line) => textTransform(line, textTransformValue));
|
|
265
269
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as CSS from 'csstype';
|
|
2
2
|
import type { BaseMarkProps, ChannelAccessor, ConstantAccessor, DataRecord, ScaledDataRecord, UsedScales } from '../../types/index.js';
|
|
3
|
-
interface TextCanvasProps<Datum
|
|
3
|
+
interface TextCanvasProps<Datum> {
|
|
4
4
|
data: ScaledDataRecord<Datum>[];
|
|
5
5
|
options: BaseMarkProps<Datum> & {
|
|
6
6
|
x?: ChannelAccessor<Datum>;
|
|
@@ -24,14 +24,14 @@ interface TextCanvasProps<Datum extends DataRecord> {
|
|
|
24
24
|
};
|
|
25
25
|
usedScales: UsedScales;
|
|
26
26
|
}
|
|
27
|
-
declare function $$render<Datum
|
|
27
|
+
declare function $$render<Datum = DataRecord | GeoJSON.GeoJsonObject>(): {
|
|
28
28
|
props: TextCanvasProps<Datum>;
|
|
29
29
|
exports: {};
|
|
30
30
|
bindings: "";
|
|
31
31
|
slots: {};
|
|
32
32
|
events: {};
|
|
33
33
|
};
|
|
34
|
-
declare class __sveltets_Render<Datum
|
|
34
|
+
declare class __sveltets_Render<Datum = DataRecord | GeoJSON.GeoJsonObject> {
|
|
35
35
|
props(): ReturnType<typeof $$render<Datum>>['props'];
|
|
36
36
|
events(): ReturnType<typeof $$render<Datum>>['events'];
|
|
37
37
|
slots(): ReturnType<typeof $$render<Datum>>['slots'];
|
|
@@ -39,12 +39,12 @@ declare class __sveltets_Render<Datum extends DataRecord> {
|
|
|
39
39
|
exports(): {};
|
|
40
40
|
}
|
|
41
41
|
interface $$IsomorphicComponent {
|
|
42
|
-
new <Datum
|
|
42
|
+
new <Datum = DataRecord | GeoJSON.GeoJsonObject>(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']>> & {
|
|
43
43
|
$$bindings?: ReturnType<__sveltets_Render<Datum>['bindings']>;
|
|
44
44
|
} & ReturnType<__sveltets_Render<Datum>['exports']>;
|
|
45
|
-
<Datum
|
|
45
|
+
<Datum = DataRecord | GeoJSON.GeoJsonObject>(internal: unknown, props: ReturnType<__sveltets_Render<Datum>['props']> & {}): ReturnType<__sveltets_Render<Datum>['exports']>;
|
|
46
46
|
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
47
47
|
}
|
|
48
48
|
declare const TextCanvas: $$IsomorphicComponent;
|
|
49
|
-
type TextCanvas<Datum
|
|
49
|
+
type TextCanvas<Datum = DataRecord | GeoJSON.GeoJsonObject> = InstanceType<typeof TextCanvas<Datum>>;
|
|
50
50
|
export default TextCanvas;
|
|
@@ -96,17 +96,17 @@
|
|
|
96
96
|
};
|
|
97
97
|
|
|
98
98
|
const prepareStyle = (datum: ScaledDataRecord<Datum>) => {
|
|
99
|
-
|
|
99
|
+
const styleProps = resolveScaledStyleProps(
|
|
100
100
|
datum.datum,
|
|
101
101
|
options,
|
|
102
102
|
usedScales,
|
|
103
103
|
plot,
|
|
104
104
|
'stroke'
|
|
105
|
-
)
|
|
105
|
+
) as Record<string, unknown>;
|
|
106
106
|
|
|
107
|
-
const opacity = maybeOpacity(
|
|
108
|
-
const strokeOpacity = maybeOpacity(
|
|
109
|
-
const lineCap = normalizeLineCap(
|
|
107
|
+
const opacity = maybeOpacity(styleProps['opacity']);
|
|
108
|
+
const strokeOpacity = maybeOpacity(styleProps['stroke-opacity']);
|
|
109
|
+
const lineCap = normalizeLineCap(styleProps['stroke-linecap']);
|
|
110
110
|
const strokeWidth = +(resolveProp(
|
|
111
111
|
options.strokeWidth,
|
|
112
112
|
datum.datum,
|
|
@@ -121,6 +121,7 @@
|
|
|
121
121
|
| number
|
|
122
122
|
| string;
|
|
123
123
|
|
|
124
|
+
const stroke = styleProps.stroke;
|
|
124
125
|
const strokeValue = String(stroke || 'currentColor');
|
|
125
126
|
const alpha = opacity * strokeOpacity;
|
|
126
127
|
const styleKey = `${strokeValue}|${lineCap}|${strokeWidth}|${alpha}`;
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
<script lang="ts" generics="Datum extends DataRecord">
|
|
2
2
|
import type {
|
|
3
|
-
Mark,
|
|
4
|
-
BaseMarkProps,
|
|
5
3
|
ScaledDataRecord,
|
|
6
4
|
UsedScales,
|
|
7
5
|
CurveName,
|
|
8
|
-
ConstantAccessor
|
|
6
|
+
ConstantAccessor,
|
|
7
|
+
DataRecord,
|
|
8
|
+
ChannelAccessor
|
|
9
9
|
} from '../../types/index.js';
|
|
10
10
|
import CanvasLayer from './CanvasLayer.svelte';
|
|
11
11
|
import type { Attachment } from 'svelte/attachments';
|
|
12
12
|
import { devicePixelRatio } from 'svelte/reactivity/window';
|
|
13
13
|
import { resolveColor } from './canvas.js';
|
|
14
14
|
import type { CurveFactory } from 'd3-shape';
|
|
15
|
-
import { trailPath, type TrailSample } from './trail';
|
|
16
|
-
import { resolveProp, resolveScaledStyleProps } from '../../helpers/resolve';
|
|
17
|
-
import { usePlot } from '../../
|
|
15
|
+
import { trailPath, type TrailSample } from './trail.js';
|
|
16
|
+
import { resolveProp, resolveScaledStyleProps } from '../../helpers/resolve.js';
|
|
17
|
+
import { usePlot } from '../../index.js';
|
|
18
18
|
|
|
19
|
-
interface TrailCanvasProps<Datum> {
|
|
19
|
+
interface TrailCanvasProps<Datum extends object> {
|
|
20
20
|
curve?: CurveName | CurveFactory;
|
|
21
21
|
tension?: number;
|
|
22
22
|
cap?: 'butt' | 'round';
|
|
@@ -24,10 +24,8 @@
|
|
|
24
24
|
data: ScaledDataRecord<Datum>[][];
|
|
25
25
|
usedScales: UsedScales;
|
|
26
26
|
options: {
|
|
27
|
-
fill?:
|
|
27
|
+
fill?: ChannelAccessor<Datum>;
|
|
28
28
|
defined?: ConstantAccessor<boolean, Datum>;
|
|
29
|
-
opacity?: ConstantAccessor<number, Datum>;
|
|
30
|
-
'fill-opacity'?: ConstantAccessor<number, Datum>;
|
|
31
29
|
};
|
|
32
30
|
}
|
|
33
31
|
|
|
@@ -71,22 +69,22 @@
|
|
|
71
69
|
const defined = trailData.map(
|
|
72
70
|
(d) =>
|
|
73
71
|
d.valid &&
|
|
74
|
-
d.r >= 0 &&
|
|
75
|
-
(resolveProp(options.defined, d.datum, true) ?? true)
|
|
72
|
+
(d.r ?? 0) >= 0 &&
|
|
73
|
+
(resolveProp(options.defined as any, d.datum, true) ?? true)
|
|
76
74
|
);
|
|
77
75
|
|
|
78
|
-
|
|
76
|
+
const styleProps = resolveScaledStyleProps(
|
|
79
77
|
firstPoint.datum,
|
|
80
|
-
options,
|
|
78
|
+
options as any,
|
|
81
79
|
usedScales,
|
|
82
80
|
plot,
|
|
83
81
|
'fill'
|
|
84
|
-
)
|
|
82
|
+
) as Record<string, unknown>;
|
|
85
83
|
|
|
86
|
-
const opacity = maybeOpacity(
|
|
87
|
-
const fillOpacity = maybeOpacity(
|
|
84
|
+
const opacity = maybeOpacity(styleProps['opacity']);
|
|
85
|
+
const fillOpacity = maybeOpacity(styleProps['fill-opacity']);
|
|
88
86
|
|
|
89
|
-
fill = resolveColor(fill, canvas);
|
|
87
|
+
const fill = resolveColor(String(styleProps.fill || 'currentColor'), canvas);
|
|
90
88
|
|
|
91
89
|
context.fillStyle = fill ? fill : 'currentColor';
|
|
92
90
|
context.beginPath();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ScaledDataRecord, UsedScales, CurveName, ConstantAccessor } from '../../types/index.js';
|
|
1
|
+
import type { ScaledDataRecord, UsedScales, CurveName, ConstantAccessor, DataRecord, ChannelAccessor } from '../../types/index.js';
|
|
2
2
|
import type { CurveFactory } from 'd3-shape';
|
|
3
|
-
interface TrailCanvasProps<Datum> {
|
|
3
|
+
interface TrailCanvasProps<Datum extends object> {
|
|
4
4
|
curve?: CurveName | CurveFactory;
|
|
5
5
|
tension?: number;
|
|
6
6
|
cap?: 'butt' | 'round';
|
|
@@ -8,10 +8,8 @@ interface TrailCanvasProps<Datum> {
|
|
|
8
8
|
data: ScaledDataRecord<Datum>[][];
|
|
9
9
|
usedScales: UsedScales;
|
|
10
10
|
options: {
|
|
11
|
-
fill?:
|
|
11
|
+
fill?: ChannelAccessor<Datum>;
|
|
12
12
|
defined?: ConstantAccessor<boolean, Datum>;
|
|
13
|
-
opacity?: ConstantAccessor<number, Datum>;
|
|
14
|
-
'fill-opacity'?: ConstantAccessor<number, Datum>;
|
|
15
13
|
};
|
|
16
14
|
}
|
|
17
15
|
declare function $$render<Datum extends DataRecord>(): {
|