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.
Files changed (222) hide show
  1. package/dist/Mark.svelte +42 -25
  2. package/dist/Mark.svelte.d.ts +111 -32
  3. package/dist/Plot.svelte +21 -15
  4. package/dist/core/Facet.svelte +1 -1
  5. package/dist/core/FacetAxes.svelte +13 -8
  6. package/dist/core/FacetGrid.svelte +4 -4
  7. package/dist/core/Plot.svelte +41 -35
  8. package/dist/helpers/autoScales.d.ts +3 -3
  9. package/dist/helpers/autoScales.js +28 -18
  10. package/dist/helpers/autoTicks.js +2 -0
  11. package/dist/helpers/callWithProps.d.ts +1 -2
  12. package/dist/helpers/facets.js +0 -1
  13. package/dist/helpers/index.js +1 -1
  14. package/dist/helpers/mergeDeep.d.ts +1 -3
  15. package/dist/helpers/mergeDeep.js +15 -16
  16. package/dist/helpers/projection.d.ts +4 -3
  17. package/dist/helpers/projection.js +17 -5
  18. package/dist/helpers/reduce.d.ts +4 -4
  19. package/dist/helpers/reduce.js +6 -4
  20. package/dist/helpers/regressionLoess.js +2 -1
  21. package/dist/helpers/resolve.d.ts +6 -3
  22. package/dist/helpers/resolve.js +25 -16
  23. package/dist/helpers/scales.d.ts +10 -10
  24. package/dist/helpers/scales.js +43 -13
  25. package/dist/helpers/time.d.ts +10 -3
  26. package/dist/helpers/time.js +2 -1
  27. package/dist/hooks/index.d.ts +2 -0
  28. package/dist/hooks/index.js +2 -0
  29. package/dist/hooks/plotDefaults.d.ts +3 -1
  30. package/dist/hooks/plotDefaults.js +33 -1
  31. package/dist/hooks/usePlot.svelte.d.ts +10 -25
  32. package/dist/hooks/usePlot.svelte.js +8 -7
  33. package/dist/index.d.ts +1 -2
  34. package/dist/index.js +1 -3
  35. package/dist/marks/Area.svelte +24 -13
  36. package/dist/marks/Area.svelte.d.ts +118 -34
  37. package/dist/marks/AreaX.svelte +42 -8
  38. package/dist/marks/AreaX.svelte.d.ts +154 -71
  39. package/dist/marks/AreaY.svelte +42 -8
  40. package/dist/marks/AreaY.svelte.d.ts +154 -71
  41. package/dist/marks/Arrow.svelte +42 -23
  42. package/dist/marks/Arrow.svelte.d.ts +114 -35
  43. package/dist/marks/AxisX.svelte +43 -28
  44. package/dist/marks/AxisX.svelte.d.ts +125 -40
  45. package/dist/marks/AxisY.svelte +43 -26
  46. package/dist/marks/AxisY.svelte.d.ts +127 -40
  47. package/dist/marks/BarX.svelte +12 -10
  48. package/dist/marks/BarX.svelte.d.ts +104 -32
  49. package/dist/marks/BarY.svelte +11 -10
  50. package/dist/marks/BarY.svelte.d.ts +106 -34
  51. package/dist/marks/BollingerX.svelte +4 -7
  52. package/dist/marks/BollingerX.svelte.d.ts +105 -30
  53. package/dist/marks/BollingerY.svelte +3 -0
  54. package/dist/marks/BollingerY.svelte.d.ts +105 -30
  55. package/dist/marks/BoxX.svelte +3 -3
  56. package/dist/marks/BoxY.svelte +12 -9
  57. package/dist/marks/BoxY.svelte.d.ts +128 -53
  58. package/dist/marks/Brush.svelte +26 -21
  59. package/dist/marks/Brush.svelte.d.ts +119 -60
  60. package/dist/marks/Cell.svelte +13 -9
  61. package/dist/marks/Cell.svelte.d.ts +105 -30
  62. package/dist/marks/CellX.svelte +2 -1
  63. package/dist/marks/CellX.svelte.d.ts +105 -32
  64. package/dist/marks/CellY.svelte +2 -1
  65. package/dist/marks/CellY.svelte.d.ts +105 -32
  66. package/dist/marks/ColorLegend.svelte +24 -13
  67. package/dist/marks/ColorLegend.svelte.d.ts +1 -0
  68. package/dist/marks/CustomMark.svelte +16 -10
  69. package/dist/marks/CustomMark.svelte.d.ts +112 -31
  70. package/dist/marks/CustomMarkHTML.svelte +8 -2
  71. package/dist/marks/CustomMarkHTML.svelte.d.ts +8 -2
  72. package/dist/marks/DifferenceY.svelte +31 -20
  73. package/dist/marks/DifferenceY.svelte.d.ts +134 -55
  74. package/dist/marks/Dot.svelte +21 -11
  75. package/dist/marks/Dot.svelte.d.ts +117 -38
  76. package/dist/marks/DotX.svelte +2 -0
  77. package/dist/marks/DotX.svelte.d.ts +136 -62
  78. package/dist/marks/DotY.svelte +1 -0
  79. package/dist/marks/DotY.svelte.d.ts +135 -62
  80. package/dist/marks/Frame.svelte +47 -9
  81. package/dist/marks/Frame.svelte.d.ts +124 -41
  82. package/dist/marks/Geo.svelte +21 -12
  83. package/dist/marks/Geo.svelte.d.ts +105 -30
  84. package/dist/marks/Graticule.svelte +3 -0
  85. package/dist/marks/Graticule.svelte.d.ts +3 -0
  86. package/dist/marks/GridX.svelte +31 -16
  87. package/dist/marks/GridX.svelte.d.ts +108 -32
  88. package/dist/marks/GridY.svelte +30 -15
  89. package/dist/marks/GridY.svelte.d.ts +108 -32
  90. package/dist/marks/HTMLTooltip.svelte +14 -7
  91. package/dist/marks/HTMLTooltip.svelte.d.ts +7 -0
  92. package/dist/marks/Image.svelte +50 -25
  93. package/dist/marks/Image.svelte.d.ts +117 -35
  94. package/dist/marks/Line.svelte +67 -44
  95. package/dist/marks/Line.svelte.d.ts +119 -30
  96. package/dist/marks/LineX.svelte +2 -1
  97. package/dist/marks/LineX.svelte.d.ts +142 -69
  98. package/dist/marks/LineY.svelte +2 -1
  99. package/dist/marks/LineY.svelte.d.ts +142 -69
  100. package/dist/marks/Link.svelte +70 -46
  101. package/dist/marks/Link.svelte.d.ts +126 -41
  102. package/dist/marks/Pointer.svelte +24 -15
  103. package/dist/marks/Pointer.svelte.d.ts +7 -0
  104. package/dist/marks/Rect.svelte +13 -5
  105. package/dist/marks/Rect.svelte.d.ts +116 -35
  106. package/dist/marks/RectX.svelte +6 -3
  107. package/dist/marks/RectX.svelte.d.ts +158 -12
  108. package/dist/marks/RectY.svelte +6 -3
  109. package/dist/marks/RectY.svelte.d.ts +158 -12
  110. package/dist/marks/RegressionX.svelte +13 -6
  111. package/dist/marks/RegressionX.svelte.d.ts +8 -3
  112. package/dist/marks/RegressionY.svelte +13 -6
  113. package/dist/marks/RegressionY.svelte.d.ts +8 -3
  114. package/dist/marks/RuleX.svelte +18 -11
  115. package/dist/marks/RuleX.svelte.d.ts +112 -32
  116. package/dist/marks/RuleY.svelte +19 -12
  117. package/dist/marks/RuleY.svelte.d.ts +114 -34
  118. package/dist/marks/Spike.svelte +11 -5
  119. package/dist/marks/Spike.svelte.d.ts +146 -68
  120. package/dist/marks/Text.svelte +24 -7
  121. package/dist/marks/Text.svelte.d.ts +253 -75
  122. package/dist/marks/TickX.svelte +56 -48
  123. package/dist/marks/TickX.svelte.d.ts +114 -40
  124. package/dist/marks/TickY.svelte +59 -51
  125. package/dist/marks/TickY.svelte.d.ts +117 -43
  126. package/dist/marks/Trail.svelte +25 -13
  127. package/dist/marks/Trail.svelte.d.ts +116 -33
  128. package/dist/marks/Vector.svelte +20 -11
  129. package/dist/marks/Vector.svelte.d.ts +116 -35
  130. package/dist/marks/WaffleX.svelte +18 -16
  131. package/dist/marks/WaffleX.svelte.d.ts +131 -57
  132. package/dist/marks/WaffleY.svelte +16 -15
  133. package/dist/marks/WaffleY.svelte.d.ts +129 -56
  134. package/dist/marks/helpers/Anchor.svelte +17 -2
  135. package/dist/marks/helpers/Anchor.svelte.d.ts +16 -1
  136. package/dist/marks/helpers/AreaCanvas.svelte +8 -8
  137. package/dist/marks/helpers/BaseAxisX.svelte +38 -41
  138. package/dist/marks/helpers/BaseAxisX.svelte.d.ts +11 -17
  139. package/dist/marks/helpers/BaseAxisY.svelte +35 -35
  140. package/dist/marks/helpers/BaseAxisY.svelte.d.ts +12 -15
  141. package/dist/marks/helpers/Box.svelte +35 -28
  142. package/dist/marks/helpers/Box.svelte.d.ts +122 -50
  143. package/dist/marks/helpers/DotCanvas.svelte +11 -9
  144. package/dist/marks/helpers/GeoCanvas.svelte +7 -6
  145. package/dist/marks/helpers/LineCanvas.svelte +7 -7
  146. package/dist/marks/helpers/LinearGradientX.svelte +2 -2
  147. package/dist/marks/helpers/LinearGradientX.svelte.d.ts +1 -1
  148. package/dist/marks/helpers/LinearGradientY.svelte +2 -2
  149. package/dist/marks/helpers/LinearGradientY.svelte.d.ts +1 -1
  150. package/dist/marks/helpers/Marker.svelte +2 -2
  151. package/dist/marks/helpers/MarkerPath.svelte +15 -12
  152. package/dist/marks/helpers/MarkerPath.svelte.d.ts +105 -32
  153. package/dist/marks/helpers/MultilineText.svelte +24 -17
  154. package/dist/marks/helpers/MultilineText.svelte.d.ts +1 -1
  155. package/dist/marks/helpers/RectCanvas.svelte +31 -26
  156. package/dist/marks/helpers/RectPath.svelte +2 -2
  157. package/dist/marks/helpers/Regression.svelte +176 -86
  158. package/dist/marks/helpers/Regression.svelte.d.ts +20 -8
  159. package/dist/marks/helpers/RuleCanvas.svelte +9 -6
  160. package/dist/marks/helpers/TextCanvas.svelte +13 -9
  161. package/dist/marks/helpers/TextCanvas.svelte.d.ts +6 -6
  162. package/dist/marks/helpers/TickCanvas.svelte +6 -5
  163. package/dist/marks/helpers/TrailCanvas.svelte +16 -18
  164. package/dist/marks/helpers/TrailCanvas.svelte.d.ts +3 -5
  165. package/dist/marks/helpers/canvas.js +16 -9
  166. package/dist/marks/helpers/events.d.ts +2 -2
  167. package/dist/marks/helpers/events.js +14 -7
  168. package/dist/marks/helpers/waffle.d.ts +3 -3
  169. package/dist/marks/helpers/waffle.js +6 -4
  170. package/dist/regression/polynomial.d.ts +1 -1
  171. package/dist/regression/polynomial.js +5 -5
  172. package/dist/regression/utils/determination.d.ts +1 -1
  173. package/dist/regression/utils/determination.js +1 -1
  174. package/dist/regression/utils/geometry.d.ts +1 -1
  175. package/dist/regression/utils/interpose.d.ts +1 -1
  176. package/dist/regression/utils/interpose.js +1 -1
  177. package/dist/regression/utils/points.d.ts +1 -1
  178. package/dist/transforms/bin.d.ts +3 -3
  179. package/dist/transforms/bin.js +29 -20
  180. package/dist/transforms/bollinger.d.ts +8 -0
  181. package/dist/transforms/bollinger.js +9 -1
  182. package/dist/transforms/centroid.d.ts +4 -0
  183. package/dist/transforms/centroid.js +4 -0
  184. package/dist/transforms/density.d.ts +4 -4
  185. package/dist/transforms/density.js +20 -13
  186. package/dist/transforms/dodge.d.ts +12 -1
  187. package/dist/transforms/dodge.js +15 -6
  188. package/dist/transforms/group.d.ts +141 -4
  189. package/dist/transforms/group.js +4 -1
  190. package/dist/transforms/interval.d.ts +204 -60
  191. package/dist/transforms/jitter.d.ts +421 -4
  192. package/dist/transforms/jitter.js +10 -1
  193. package/dist/transforms/map.d.ts +412 -4
  194. package/dist/transforms/map.js +3 -3
  195. package/dist/transforms/normalize.d.ts +276 -5
  196. package/dist/transforms/normalize.js +5 -3
  197. package/dist/transforms/recordize.d.ts +17 -5
  198. package/dist/transforms/recordize.js +13 -9
  199. package/dist/transforms/rename.d.ts +11 -4
  200. package/dist/transforms/rename.js +7 -2
  201. package/dist/transforms/select.d.ts +722 -210
  202. package/dist/transforms/select.js +13 -1
  203. package/dist/transforms/shift.d.ts +8 -0
  204. package/dist/transforms/shift.js +20 -6
  205. package/dist/transforms/sort.d.ts +13 -258
  206. package/dist/transforms/sort.js +13 -10
  207. package/dist/transforms/stack.d.ts +58 -9
  208. package/dist/transforms/stack.js +27 -11
  209. package/dist/transforms/window.d.ts +221 -66
  210. package/dist/transforms/window.js +8 -2
  211. package/dist/types/axes.d.ts +43 -0
  212. package/dist/types/axes.js +1 -0
  213. package/dist/types/channel.d.ts +30 -2
  214. package/dist/types/data.d.ts +14 -1
  215. package/dist/types/facet.d.ts +5 -0
  216. package/dist/types/index.d.ts +33 -8
  217. package/dist/types/index.js +11 -7
  218. package/dist/types/mark.d.ts +124 -35
  219. package/dist/types/plot.d.ts +118 -16
  220. package/dist/types/scale.d.ts +125 -8
  221. package/package.json +178 -175
  222. package/dist/helpers/autoTicks.d.ts +0 -12
@@ -1,12 +1,15 @@
1
1
  <script module lang="ts">
2
- import type { BaseMarkProps, ChannelAccessor, FacetContext } from '../../types/index.js';
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 RegressionMarkProps = BaseMarkProps & {
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: RegressionType;
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: number;
20
- // for log
21
- base: number;
22
- // for loess
23
- span: number;
24
- // for confidence bands
25
- confidence: number;
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 { isDate } from '../../helpers/typeChecks.js';
46
-
47
- const regressions = new Map<RegressionType, typeof regressionLinear>([
48
- ['linear', regressionLinear],
49
- ['quad', regressionQuad],
50
- ['poly', regressionPoly],
51
- ['exp', regressionExp],
52
- ['log', regressionLog],
53
- ['pow', regressionPow],
54
- ['loess', regressionLoess]
55
- ]);
56
-
57
- function maybeRegression(name: string) {
58
- name = `${name}`.toLowerCase();
59
- if (regressions.has(name)) return regressions.get(name);
60
- throw new Error('unknown regression ' + name);
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
- import { usePlot } from '../../hooks/usePlot.svelte.js';
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 = 2.71828,
114
+ base = Math.E,
73
115
  span = 0.3,
74
116
  confidence = 0.99,
75
- class: className = null,
117
+ class: className = '',
118
+ canvas = false,
76
119
  ...options
77
- }: RegressionMarkProps & { dependent: 'x' | 'y' } = $props();
120
+ }: RegressionProps = $props();
78
121
 
79
122
  const { getTestFacet } = getContext<FacetContext>('svelteplot/facet');
80
- let testFacet = $derived(getTestFacet());
123
+ const testFacet = $derived(getTestFacet());
124
+
125
+ const filteredData = $derived(data.filter((d) => testFacet(d, options as any)));
81
126
 
82
- let filteredData = $derived(data.filter((d) => testFacet(d, options)));
127
+ const independent: 'x' | 'y' = $derived(dependent === 'x' ? 'y' : 'x');
83
128
 
84
- let independent: 'x' | 'y' = $derived(dependent === 'x' ? 'y' : 'x');
129
+ const regressionFn = $derived(maybeRegression(type));
85
130
 
86
- let regressionFn = $derived(maybeRegression(type));
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
- let regression = $derived(
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) => resolveChannel(independent, d, options),
91
- y: (d) => resolveChannel(dependent, d, options),
165
+ x: (d: RegressionData) => d.x,
166
+ y: (d: RegressionData) => d.y,
92
167
  ...(type === 'poly' ? { order } : {}),
93
168
  ...(type === 'log' ? { base } : {}),
94
- ...(!type.startsWith('loess') ? { domain: plot.scales[independent].domain } : {}),
169
+ ...(type !== 'loess' && independentDomain != null ? { domain: independentDomain } : {}),
95
170
  ...(type === 'loess' ? { bandwidth: span } : {})
96
- })(filteredData)
171
+ })(regressionInput)
97
172
  );
98
173
 
99
- let regrPoints = $derived([
100
- ...new Set([
101
- plot.scales[independent].domain[0],
102
- ...plot.scales[independent].fn.ticks(40),
103
- plot.scales[independent].domain[1]
104
- ])
105
- ]);
106
-
107
- let regrData = $derived(
108
- regression.predictMany
109
- ? regression.predictMany(regrPoints).map((__y, i) => ({ __x: regrPoints[i], __y }))
110
- : regression.predict
111
- ? regrPoints.map((__x) => {
112
- // const __x = x;
113
- const __y = regression.predict(__x);
114
- return { __x, __y };
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
- let stroke = $derived(
123
- options.stroke != null ? resolveChannel('stroke', filteredData[0], options) : null
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
- let confBandGen = $derived(
127
- confidence !== false && regression.predict
128
- ? confidenceInterval(
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
- let confBandData = $derived(
144
- confidence !== false && regression.predict
145
- ? regrPoints.map((x) => {
146
- const { x: __x, left, right } = confBandGen(x);
147
- return { __x, __y1: left, __y2: right };
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 RegressionMarkProps = BaseMarkProps & {
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: RegressionType;
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: number;
17
- base: number;
18
- span: number;
19
- confidence: number;
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
- declare const Regression: import("svelte").Component<any, {}, "">;
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
- let { stroke, ...restStyles } = resolveScaledStyleProps(
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(restStyles['opacity']);
74
- const strokeOpacity = maybeOpacity(restStyles['stroke-opacity']);
73
+ const opacity = maybeOpacity(styleProps['opacity']);
74
+ const strokeOpacity = maybeOpacity(styleProps['stroke-opacity']);
75
75
 
76
- stroke = resolveColor(stroke || 'currentColor', canvas);
76
+ const stroke = resolveColor(
77
+ String(styleProps.stroke || 'currentColor'),
78
+ canvas
79
+ );
77
80
 
78
81
  if (stroke && stroke !== 'none') {
79
- const resolvedLinecap = restStyles['stroke-linecap'] as
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 extends DataRecord">
2
- interface TextCanvasProps<Datum extends DataRecord> {
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(options.frameAnchor, datum.datum, 'middle');
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 extends DataRecord> {
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 extends DataRecord>(): {
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 extends DataRecord> {
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 extends 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']>> & {
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 extends DataRecord>(internal: unknown, props: ReturnType<__sveltets_Render<Datum>['props']> & {}): ReturnType<__sveltets_Render<Datum>['exports']>;
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 extends DataRecord> = InstanceType<typeof 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
- let { stroke, ...restStyles } = resolveScaledStyleProps(
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(restStyles['opacity']);
108
- const strokeOpacity = maybeOpacity(restStyles['stroke-opacity']);
109
- const lineCap = normalizeLineCap(restStyles['stroke-linecap']);
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 '../../hooks/usePlot.svelte.js';
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?: ConstantAccessor<string, Datum>;
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
- let { fill, ...restStyles } = resolveScaledStyleProps(
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(restStyles['opacity']);
87
- const fillOpacity = maybeOpacity(restStyles['fill-opacity']);
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?: ConstantAccessor<string, Datum>;
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>(): {