svelteplot 0.11.0 → 0.11.1-pr-520.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 (94) hide show
  1. package/dist/Mark.svelte +6 -1
  2. package/dist/Mark.svelte.d.ts +1 -1
  3. package/dist/constants.js +2 -0
  4. package/dist/core/Plot.svelte +32 -3
  5. package/dist/helpers/arrowPath.js +10 -5
  6. package/dist/helpers/autoScales.js +7 -3
  7. package/dist/helpers/autoTicks.js +4 -4
  8. package/dist/helpers/autoTimeFormat.js +22 -12
  9. package/dist/helpers/colors.d.ts +4 -4
  10. package/dist/helpers/facets.d.ts +42 -1
  11. package/dist/helpers/facets.js +83 -0
  12. package/dist/helpers/math.js +1 -1
  13. package/dist/helpers/noise.js +1 -1
  14. package/dist/helpers/rasterInterpolate.d.ts +26 -0
  15. package/dist/helpers/rasterInterpolate.js +220 -0
  16. package/dist/helpers/roundedRect.js +1 -1
  17. package/dist/helpers/scales.d.ts +1 -0
  18. package/dist/helpers/scales.js +8 -5
  19. package/dist/helpers/time.js +1 -1
  20. package/dist/helpers/typeChecks.d.ts +1 -0
  21. package/dist/helpers/typeChecks.js +3 -0
  22. package/dist/marks/Area.svelte.d.ts +1 -1
  23. package/dist/marks/AreaX.svelte.d.ts +1 -1
  24. package/dist/marks/AreaY.svelte.d.ts +1 -1
  25. package/dist/marks/Arrow.svelte.d.ts +1 -1
  26. package/dist/marks/AxisX.svelte +8 -3
  27. package/dist/marks/AxisX.svelte.d.ts +1 -1
  28. package/dist/marks/AxisY.svelte +8 -3
  29. package/dist/marks/AxisY.svelte.d.ts +1 -1
  30. package/dist/marks/BarX.svelte.d.ts +1 -1
  31. package/dist/marks/BarY.svelte.d.ts +1 -1
  32. package/dist/marks/BollingerX.svelte.d.ts +1 -1
  33. package/dist/marks/BollingerY.svelte.d.ts +1 -1
  34. package/dist/marks/BoxY.svelte.d.ts +1 -1
  35. package/dist/marks/Brush.svelte.d.ts +1 -1
  36. package/dist/marks/Cell.svelte.d.ts +1 -1
  37. package/dist/marks/CellX.svelte.d.ts +1 -1
  38. package/dist/marks/CellY.svelte.d.ts +1 -1
  39. package/dist/marks/CustomMark.svelte.d.ts +1 -1
  40. package/dist/marks/DifferenceY.svelte.d.ts +1 -1
  41. package/dist/marks/Dot.svelte.d.ts +1 -1
  42. package/dist/marks/DotX.svelte.d.ts +1 -1
  43. package/dist/marks/DotY.svelte.d.ts +1 -1
  44. package/dist/marks/Frame.svelte.d.ts +1 -1
  45. package/dist/marks/Geo.svelte.d.ts +1 -1
  46. package/dist/marks/GridX.svelte.d.ts +1 -1
  47. package/dist/marks/GridY.svelte.d.ts +1 -1
  48. package/dist/marks/HTMLTooltip.svelte +28 -25
  49. package/dist/marks/Image.svelte.d.ts +1 -1
  50. package/dist/marks/Line.svelte +52 -15
  51. package/dist/marks/Line.svelte.d.ts +1 -1
  52. package/dist/marks/LineX.svelte.d.ts +1 -1
  53. package/dist/marks/LineY.svelte.d.ts +1 -1
  54. package/dist/marks/Link.svelte.d.ts +1 -1
  55. package/dist/marks/Pointer.svelte +31 -29
  56. package/dist/marks/Raster.svelte +414 -0
  57. package/dist/marks/Raster.svelte.d.ts +94 -0
  58. package/dist/marks/Rect.svelte.d.ts +1 -1
  59. package/dist/marks/RectX.svelte.d.ts +1 -1
  60. package/dist/marks/RectY.svelte.d.ts +1 -1
  61. package/dist/marks/RuleX.svelte.d.ts +1 -1
  62. package/dist/marks/RuleY.svelte.d.ts +1 -1
  63. package/dist/marks/Spike.svelte.d.ts +1 -1
  64. package/dist/marks/Text.svelte +7 -5
  65. package/dist/marks/Text.svelte.d.ts +2 -2
  66. package/dist/marks/TickX.svelte.d.ts +1 -1
  67. package/dist/marks/TickY.svelte.d.ts +1 -1
  68. package/dist/marks/Trail.svelte.d.ts +1 -1
  69. package/dist/marks/Vector.svelte.d.ts +1 -1
  70. package/dist/marks/WaffleX.svelte.d.ts +1 -1
  71. package/dist/marks/WaffleY.svelte.d.ts +1 -1
  72. package/dist/marks/helpers/Box.svelte.d.ts +1 -1
  73. package/dist/marks/helpers/MarkerPath.svelte.d.ts +1 -1
  74. package/dist/marks/helpers/Regression.svelte +2 -1
  75. package/dist/marks/helpers/trail.js +1 -1
  76. package/dist/marks/helpers/waffle.js +1 -1
  77. package/dist/marks/index.d.ts +1 -0
  78. package/dist/marks/index.js +1 -0
  79. package/dist/regression/polynomial.js +2 -2
  80. package/dist/transforms/bollinger.js +6 -3
  81. package/dist/transforms/density.js +3 -3
  82. package/dist/transforms/group.d.ts +1 -1
  83. package/dist/transforms/interval.d.ts +2 -2
  84. package/dist/transforms/jitter.d.ts +3 -3
  85. package/dist/transforms/map.d.ts +3 -3
  86. package/dist/transforms/map.js +2 -2
  87. package/dist/transforms/normalize.d.ts +2 -2
  88. package/dist/transforms/normalize.js +1 -1
  89. package/dist/transforms/select.d.ts +7 -7
  90. package/dist/transforms/window.d.ts +2 -2
  91. package/dist/types/mark.d.ts +3 -3
  92. package/dist/types/plot.d.ts +8 -1
  93. package/dist/types/scale.d.ts +2 -2
  94. package/package.json +183 -179
package/dist/Mark.svelte CHANGED
@@ -297,7 +297,12 @@
297
297
  : plot.scales[scale].fn(value)
298
298
  : value;
299
299
 
300
- out.valid = out.valid && (scale === 'color' || isValid(value));
300
+ out.valid =
301
+ out.valid &&
302
+ (scale === 'color' ||
303
+ scale === 'fx' ||
304
+ scale === 'fy' ||
305
+ isValid(value));
301
306
 
302
307
  // apply dx/dy transform
303
308
  (out as any)[channel] =
@@ -14,7 +14,7 @@ declare function $$render<Datum extends DataRecord>(): {
14
14
  fill: ChannelAccessor<Datum>;
15
15
  fillOpacity: import("./types/index.js").ConstantAccessor<number, Datum>;
16
16
  fontFamily: import("./types/index.js").ConstantAccessor<import("csstype").Property.FontFamily, Datum>;
17
- fontSize: import("./types/index.js").ConstantAccessor<number | "-moz-initial" | "inherit" | "initial" | "revert" | "revert-layer" | "unset" | "math" | (string & {}) | "large" | "medium" | "small" | "x-large" | "x-small" | "xx-large" | "xx-small" | "xxx-large" | "larger" | "smaller", Datum>;
17
+ fontSize: import("./types/index.js").ConstantAccessor<import("csstype").Property.FontSize<number>, Datum>;
18
18
  fontStyle: import("./types/index.js").ConstantAccessor<import("csstype").Property.FontStyle, Datum>;
19
19
  fontVariant: import("./types/index.js").ConstantAccessor<import("csstype").Property.FontVariant, Datum>;
20
20
  fontWeight: import("./types/index.js").ConstantAccessor<import("csstype").Property.FontWeight, Datum>;
package/dist/constants.js CHANGED
@@ -68,6 +68,7 @@ export const VALID_SCALE_TYPES = {
68
68
  'log',
69
69
  'symlog',
70
70
  'time',
71
+ 'utc',
71
72
  'ordinal',
72
73
  'band',
73
74
  'point',
@@ -80,6 +81,7 @@ export const VALID_SCALE_TYPES = {
80
81
  'log',
81
82
  'symlog',
82
83
  'time',
84
+ 'utc',
83
85
  'ordinal',
84
86
  'band',
85
87
  'point',
@@ -11,6 +11,7 @@
11
11
  import { setContext } from 'svelte';
12
12
  import { SvelteMap } from 'svelte/reactivity';
13
13
  import { writable } from 'svelte/store';
14
+ import { scaleBand } from 'd3-scale';
14
15
 
15
16
  import type {
16
17
  PlotOptions,
@@ -28,7 +29,7 @@
28
29
  import FacetGrid from './FacetGrid.svelte';
29
30
 
30
31
  import mergeDeep from '../helpers/mergeDeep.js';
31
- import { computeScales, projectXY } from '../helpers/scales.js';
32
+ import { computeScales, normalizeScaleFn, projectXY } from '../helpers/scales.js';
32
33
  import { CHANNEL_SCALE, SCALES } from '../constants.js';
33
34
  import { getPlotDefaults, setPlotDefaults } from '../hooks/plotDefaults.js';
34
35
  import { maybeNumber } from '../helpers/index.js';
@@ -73,6 +74,7 @@
73
74
  colorScheme: 'turbo',
74
75
  unknown: '#cccccc99',
75
76
  sortOrdinalDomains: true,
77
+ divergingColorScheme: 'RdBu',
76
78
  categoricalColorScheme: 'observable10',
77
79
  pointScaleHeight: 20,
78
80
  bandScaleHeight: 30,
@@ -285,11 +287,38 @@
285
287
  marks,
286
288
  DEFAULTS
287
289
  );
290
+ // Fix fx/fy scale ranges: computeScales creates them with empty ranges
291
+ // because getScaleRange has no case for fx/fy. We set the correct range
292
+ // here using overall plotWidth/plotHeight, matching FacetGrid's layout.
293
+ if (scales.fx.domain.length > 0) {
294
+ const fxOpts = plotOptions.fx;
295
+ const fxPaddingInner = fxOpts?.paddingInner ?? fxOpts?.padding ?? 0.1;
296
+ const fxFn = scaleBand()
297
+ .domain(scales.fx.domain as string[])
298
+ .paddingOuter(0)
299
+ .paddingInner(scales.fx.domain.length > 1 ? fxPaddingInner : 0)
300
+ .rangeRound([0, plotWidth]) as any;
301
+ fxFn.ticks = () => scales.fx.domain;
302
+ scales.fx.fn = normalizeScaleFn(fxFn);
303
+ scales.fx.range = fxFn.range();
304
+ }
305
+ if (scales.fy.domain.length > 0) {
306
+ const fyOpts = plotOptions.fy;
307
+ const fyPaddingInner = fyOpts?.paddingInner ?? fyOpts?.padding ?? 0.1;
308
+ const fyFn = scaleBand()
309
+ .domain(scales.fy.domain as string[])
310
+ .paddingOuter(0)
311
+ .paddingInner(scales.fy.domain.length > 1 ? fyPaddingInner : 0)
312
+ .rangeRound([0, plotHeight]) as any;
313
+ fyFn.ticks = () => scales.fy.domain;
314
+ scales.fy.fn = normalizeScaleFn(fyFn);
315
+ scales.fy.range = fyFn.range();
316
+ }
288
317
  const colorSymbolRedundant =
289
318
  scales.color.uniqueScaleProps?.size === 1 &&
290
319
  scales.symbol.uniqueScaleProps?.size === 1 &&
291
- [...scales.color.uniqueScaleProps?.values()][0] ===
292
- [...scales.symbol.uniqueScaleProps?.values()][0];
320
+ [...(scales.color.uniqueScaleProps?.values() ?? [])][0] ===
321
+ [...(scales.symbol.uniqueScaleProps?.values() ?? [])][0];
293
322
  return {
294
323
  options: plotOptions,
295
324
  width,
@@ -53,16 +53,21 @@ export function arrowPath(x1, y1, x2, y2, insetStart, insetEnd, headAngle, headL
53
53
  if (insetEnd) {
54
54
  const [x, y] = circleCircleIntersect([cx, cy, r], [x2, y2, insetEnd], sign * Math.sign(insetEnd));
55
55
  lineAngle += Math.atan2(y - cy, x - cx) - Math.atan2(y2 - cy, x2 - cx);
56
- ((x2 = x), (y2 = y));
56
+ x2 = x;
57
+ y2 = y;
57
58
  }
58
59
  }
59
60
  else {
60
61
  // For inset straight arrows, offset along the straight line.
61
62
  const dx = x2 - x1, dy = y2 - y1, d = Math.hypot(dx, dy);
62
- if (insetStart)
63
- ((x1 += (dx / d) * insetStart), (y1 += (dy / d) * insetStart));
64
- if (insetEnd)
65
- ((x2 -= (dx / d) * insetEnd), (y2 -= (dy / d) * insetEnd));
63
+ if (insetStart) {
64
+ x1 += (dx / d) * insetStart;
65
+ y1 += (dy / d) * insetStart;
66
+ }
67
+ if (insetEnd) {
68
+ x2 -= (dx / d) * insetEnd;
69
+ y2 -= (dy / d) * insetEnd;
70
+ }
66
71
  }
67
72
  }
68
73
  // The angle of the arrow as it approaches the endpoint, and the
@@ -1,4 +1,4 @@
1
- import { scaleBand, scaleDiverging, scaleDivergingLog, scaleDivergingPow, scaleDivergingSqrt, scaleDivergingSymlog, scaleLinear, scaleLog, scaleOrdinal, scalePoint, scalePow, scaleQuantile, scaleQuantize, scaleSequential, scaleSequentialLog, scaleSequentialPow, scaleSequentialQuantile, scaleSequentialSqrt, scaleSequentialSymlog, scaleSqrt, scaleSymlog, scaleThreshold, scaleTime } from 'd3-scale';
1
+ import { scaleBand, scaleDiverging, scaleDivergingLog, scaleDivergingPow, scaleDivergingSqrt, scaleDivergingSymlog, scaleLinear, scaleLog, scaleOrdinal, scalePoint, scalePow, scaleQuantile, scaleQuantize, scaleSequential, scaleSequentialLog, scaleSequentialPow, scaleSequentialQuantile, scaleSequentialSqrt, scaleSequentialSymlog, scaleSqrt, scaleSymlog, scaleThreshold, scaleTime, scaleUtc } from 'd3-scale';
2
2
  import { range as d3Range } from 'd3-array';
3
3
  import { categoricalSchemes, isCategoricalScheme, isDivergingScheme, isOrdinalScheme, isQuantitativeScheme, ordinalScheme, quantitativeScheme } from './colors.js';
4
4
  import callWithProps from './callWithProps.js';
@@ -6,11 +6,13 @@ import { interpolateLab, interpolateRound } from 'd3-interpolate';
6
6
  import { coalesce, maybeNumber } from './index.js';
7
7
  import { getLogTicks } from './getLogTicks.js';
8
8
  import { isPlainObject } from 'es-toolkit';
9
+ import { isTemporalScale } from './typeChecks.js';
9
10
  const Scales = {
10
11
  point: scalePoint,
11
12
  band: scaleBand,
12
13
  linear: scaleLinear,
13
14
  time: scaleTime,
15
+ utc: scaleUtc,
14
16
  sqrt: scaleSqrt,
15
17
  pow: scalePow,
16
18
  log: scaleLog,
@@ -55,7 +57,7 @@ export function autoScale({ name, type, domain, scaleOptions, plotOptions, plotW
55
57
  const scaleProps = {
56
58
  domain,
57
59
  range,
58
- ...((type === 'linear' || type === 'log' || type === 'time') && scaleOptions.nice
60
+ ...((type === 'linear' || type === 'log' || isTemporalScale(type)) && scaleOptions.nice
59
61
  ? {
60
62
  nice: scaleOptions.nice ? niceTickCount : true
61
63
  }
@@ -169,10 +171,12 @@ export function autoScaleColor({ type, domain, scaleOptions, plotOptions: _plotO
169
171
  }
170
172
  else if (SequentialScales[type] ||
171
173
  DivergingScales[type]) {
174
+ const isDivergingType = DivergingScales.hasOwnProperty(type);
172
175
  // continuous color scale
173
176
  const scale = (SequentialScales[type] ||
174
177
  DivergingScales[type]);
175
- const scheme_ = scheme || plotDefaults.colorScheme;
178
+ const scheme_ = scheme ||
179
+ (isDivergingType ? plotDefaults.divergingColorScheme : plotDefaults.colorScheme);
176
180
  if (interpolate) {
177
181
  // user-defined interpolation function [0, 1] -> color
178
182
  fn = scale(domain, interpolate);
@@ -1,6 +1,6 @@
1
- import { maybeTimeInterval } from './time.js';
1
+ import { maybeTimeInterval, maybeUtcInterval } from './time.js';
2
2
  import { extent, range as rangei } from 'd3-array';
3
- export function maybeInterval(interval) {
3
+ export function maybeInterval(interval, scaleType) {
4
4
  if (interval == null)
5
5
  return;
6
6
  if (typeof interval === 'number') {
@@ -22,7 +22,7 @@ export function maybeInterval(interval) {
22
22
  };
23
23
  }
24
24
  if (typeof interval === 'string')
25
- return maybeTimeInterval(interval);
25
+ return scaleType === 'utc' ? maybeUtcInterval(interval) : maybeTimeInterval(interval);
26
26
  if (typeof interval.floor !== 'function')
27
27
  throw new Error('invalid interval; missing floor method');
28
28
  if (typeof interval.offset !== 'function')
@@ -36,7 +36,7 @@ export function autoTicks(type, ticks, interval, domain, scaleFn, count) {
36
36
  const [lo, hi] = extent(domain);
37
37
  if (lo == null || hi == null)
38
38
  return [];
39
- const I = maybeInterval(interval);
39
+ const I = maybeInterval(interval, type);
40
40
  if (!I)
41
41
  return [];
42
42
  return I.range(lo, I.offset(hi)).filter((d) => d >= lo && d <= hi);
@@ -5,33 +5,43 @@ const DATE_TIME = {
5
5
  month: 'short',
6
6
  day: 'numeric'
7
7
  };
8
- const autoFormatDateTime = (locale) => {
9
- const format = new Intl.DateTimeFormat(locale, DATE_TIME).format;
10
- return (date) => format(date).replace(', ', '\n');
8
+ const autoFormatDateTime = (locale, utc) => {
9
+ const formatter = new Intl.DateTimeFormat(locale, {
10
+ ...DATE_TIME,
11
+ ...(utc ? { timeZone: 'UTC' } : {})
12
+ });
13
+ return (date) => formatter.format(date).replace(', ', '\n');
11
14
  };
12
15
  const DAY_MONTH = {
13
16
  month: 'short',
14
17
  day: 'numeric'
15
18
  };
16
- const autoFormatDayMonth = (locale) => {
17
- const format = new Intl.DateTimeFormat(locale, DAY_MONTH).format;
18
- return (date) => format(date).replace(' ', '\n');
19
+ const autoFormatDayMonth = (locale, utc) => {
20
+ const formatter = new Intl.DateTimeFormat(locale, {
21
+ ...DAY_MONTH,
22
+ ...(utc ? { timeZone: 'UTC' } : {})
23
+ });
24
+ return (date) => formatter.format(date).replace(' ', '\n');
19
25
  };
20
26
  const MONTH_YEAR = {
21
27
  month: 'short',
22
28
  year: 'numeric'
23
29
  };
24
- const autoFormatMonthYear = (locale) => {
25
- const format = new Intl.DateTimeFormat(locale, MONTH_YEAR).format;
26
- return (date) => format(date).replace(' ', '\n');
30
+ const autoFormatMonthYear = (locale, utc) => {
31
+ const formatter = new Intl.DateTimeFormat(locale, {
32
+ ...MONTH_YEAR,
33
+ ...(utc ? { timeZone: 'UTC' } : {})
34
+ });
35
+ return (date) => formatter.format(date).replace(' ', '\n');
27
36
  };
28
37
  export default function autoTimeFormat(x, plotWidth, plotLocale) {
38
+ const utc = x.type === 'utc';
29
39
  const daysPer100Px = ((toNumber(x.domain[1]) - toNumber(x.domain[0])) / plotWidth / 864e5) * 100;
30
40
  const format = daysPer100Px < 1
31
- ? autoFormatDateTime(plotLocale)
41
+ ? autoFormatDateTime(plotLocale, utc)
32
42
  : daysPer100Px < 30
33
- ? autoFormatDayMonth(plotLocale)
34
- : autoFormatMonthYear(plotLocale);
43
+ ? autoFormatDayMonth(plotLocale, utc)
44
+ : autoFormatMonthYear(plotLocale, utc);
35
45
  return (date) => format(date).split('\n');
36
46
  }
37
47
  function toNumber(d) {
@@ -4,10 +4,10 @@ export declare const categoricalSchemes: Map<string, readonly string[]>;
4
4
  export declare function isCategoricalScheme(scheme: string): boolean;
5
5
  type SchemeGetter = (n: number) => readonly string[];
6
6
  export declare function isOrdinalScheme(scheme: ColorScheme): boolean;
7
- export declare function ordinalScheme(scheme: ColorScheme | string): SchemeGetter;
8
- export declare function ordinalRange(scheme: ColorScheme | string, length: number): readonly string[];
9
- export declare function maybeBooleanRange(domain: boolean[], scheme?: ColorScheme | string): unknown[] | undefined;
7
+ export declare function ordinalScheme(scheme: ColorScheme | (string & {})): SchemeGetter;
8
+ export declare function ordinalRange(scheme: ColorScheme | (string & {}), length: number): readonly string[];
9
+ export declare function maybeBooleanRange(domain: boolean[], scheme?: ColorScheme | (string & {})): unknown[] | undefined;
10
10
  export declare function isQuantitativeScheme(scheme: string): boolean;
11
- export declare function quantitativeScheme(scheme: ColorScheme | string): typeof interpolateBrBG | undefined;
11
+ export declare function quantitativeScheme(scheme: ColorScheme | (string & {})): typeof interpolateBrBG | undefined;
12
12
  export declare function isDivergingScheme(scheme: string): boolean;
13
13
  export {};
@@ -1,4 +1,4 @@
1
- import type { GenericMarkOptions, Mark, RawValue } from '../types/index.js';
1
+ import type { GenericMarkOptions, Mark, PlotState, RawValue } from '../types/index.js';
2
2
  /**
3
3
  * This function tracks which facets are "empty", meaning that they don't contain
4
4
  * any "facetted" data points. This can happen when fx and fy are combined and
@@ -10,3 +10,44 @@ import type { GenericMarkOptions, Mark, RawValue } from '../types/index.js';
10
10
  * @returns
11
11
  */
12
12
  export declare function getEmptyFacets(marks: Mark<GenericMarkOptions>[], fxValues: RawValue[], fyValues: RawValue[]): Map<RawValue, Map<RawValue, boolean>>;
13
+ /**
14
+ * Stable string key for a (fxValue, fyValue) pair, used as Map keys
15
+ * for the keyed tree map in Pointer/HTMLTooltip.
16
+ */
17
+ export declare function facetKey(fxValue: RawValue | boolean, fyValue: RawValue | boolean): string;
18
+ /**
19
+ * Inverts a d3 band scale: given a pixel position, returns the domain value
20
+ * whose band contains that position, or undefined if outside all bands.
21
+ *
22
+ * d3.scaleBand has no .invert(), so we iterate the domain (O(n), n = facet count,
23
+ * typically <20).
24
+ */
25
+ export declare function invertBand(scale: {
26
+ (value: string): number | undefined;
27
+ bandwidth(): number;
28
+ }, domain: readonly (string | RawValue)[], pixelPos: number): RawValue | undefined;
29
+ /**
30
+ * Walk up the DOM from `target` to find the nearest `g.facet` element.
31
+ * Returns the facet x/y indices from `data-facet-x` and `data-facet-y`
32
+ * attributes, or null if no facet element is found.
33
+ */
34
+ export declare function findFacetFromDOM(target: Element | null): {
35
+ fxIndex: number;
36
+ fyIndex: number;
37
+ } | null;
38
+ /**
39
+ * Detect which facet the mouse event is in and compute the pixel offset.
40
+ *
41
+ * Strategy: try DOM walk first (fast, reliable when the event target is inside
42
+ * a facet `<g>`). Fall back to inverting the fx/fy band scales from the mouse
43
+ * position (works in jsdom where getBoundingClientRect returns zeros).
44
+ *
45
+ * Returns { fxValue, fyValue, offsetX, offsetY } where offset is the pixel
46
+ * translation of the facet from the plot body origin.
47
+ */
48
+ export declare function detectFacet(evt: MouseEvent, plot: PlotState): {
49
+ fxValue: RawValue | boolean;
50
+ fyValue: RawValue | boolean;
51
+ offsetX: number;
52
+ offsetY: number;
53
+ };
@@ -46,3 +46,86 @@ export function getEmptyFacets(marks, fxValues, fyValues) {
46
46
  }
47
47
  return out;
48
48
  }
49
+ /**
50
+ * Stable string key for a (fxValue, fyValue) pair, used as Map keys
51
+ * for the keyed tree map in Pointer/HTMLTooltip.
52
+ */
53
+ export function facetKey(fxValue, fyValue) {
54
+ return JSON.stringify([fxValue, fyValue]);
55
+ }
56
+ /**
57
+ * Inverts a d3 band scale: given a pixel position, returns the domain value
58
+ * whose band contains that position, or undefined if outside all bands.
59
+ *
60
+ * d3.scaleBand has no .invert(), so we iterate the domain (O(n), n = facet count,
61
+ * typically <20).
62
+ */
63
+ export function invertBand(scale, domain, pixelPos) {
64
+ const bw = scale.bandwidth();
65
+ for (const value of domain) {
66
+ const start = scale(value);
67
+ if (start != null && pixelPos >= start && pixelPos < start + bw) {
68
+ return value;
69
+ }
70
+ }
71
+ return undefined;
72
+ }
73
+ /**
74
+ * Walk up the DOM from `target` to find the nearest `g.facet` element.
75
+ * Returns the facet x/y indices from `data-facet-x` and `data-facet-y`
76
+ * attributes, or null if no facet element is found.
77
+ */
78
+ export function findFacetFromDOM(target) {
79
+ let el = target;
80
+ while (el) {
81
+ if (el.classList?.contains('facet')) {
82
+ const fxIndex = parseInt(el.dataset?.facetX ?? '0', 10);
83
+ const fyIndex = parseInt(el.dataset?.facetY ?? '0', 10);
84
+ return { fxIndex, fyIndex };
85
+ }
86
+ el = el.parentElement;
87
+ }
88
+ return null;
89
+ }
90
+ /**
91
+ * Detect which facet the mouse event is in and compute the pixel offset.
92
+ *
93
+ * Strategy: try DOM walk first (fast, reliable when the event target is inside
94
+ * a facet `<g>`). Fall back to inverting the fx/fy band scales from the mouse
95
+ * position (works in jsdom where getBoundingClientRect returns zeros).
96
+ *
97
+ * Returns { fxValue, fyValue, offsetX, offsetY } where offset is the pixel
98
+ * translation of the facet from the plot body origin.
99
+ */
100
+ export function detectFacet(evt, plot) {
101
+ const fxScale = plot.scales.fx;
102
+ const fyScale = plot.scales.fy;
103
+ const fxDomain = fxScale.domain;
104
+ const fyDomain = fyScale.domain;
105
+ const hasFx = fxDomain.length > 0;
106
+ const hasFy = fyDomain.length > 0;
107
+ // Try DOM walk
108
+ const facetInfo = findFacetFromDOM(evt.target);
109
+ if (facetInfo) {
110
+ const fxValue = hasFx ? fxDomain[facetInfo.fxIndex] : true;
111
+ const fyValue = hasFy ? fyDomain[facetInfo.fyIndex] : true;
112
+ return {
113
+ fxValue,
114
+ fyValue,
115
+ offsetX: hasFx ? (fxScale.fn(fxValue) ?? 0) : 0,
116
+ offsetY: hasFy ? (fyScale.fn(fyValue) ?? 0) : 0
117
+ };
118
+ }
119
+ // Fallback: invert mouse position against band scales
120
+ const bodyRect = plot.body.getBoundingClientRect();
121
+ const svgX = evt.clientX - bodyRect.left;
122
+ const svgY = evt.clientY - bodyRect.top;
123
+ const fxValue = hasFx ? (invertBand(fxScale.fn, fxDomain, svgX) ?? fxDomain[0]) : true;
124
+ const fyValue = hasFy ? (invertBand(fyScale.fn, fyDomain, svgY) ?? fyDomain[0]) : true;
125
+ return {
126
+ fxValue,
127
+ fyValue,
128
+ offsetX: hasFx ? (fxScale.fn(fxValue) ?? 0) : 0,
129
+ offsetY: hasFy ? (fyScale.fn(fyValue) ?? 0) : 0
130
+ };
131
+ }
@@ -16,7 +16,7 @@ export function normdev(p) {
16
16
  return -Infinity;
17
17
  if (p == 1)
18
18
  return Infinity;
19
- const a0 = 3.387132872796366608, a1 = 1.3314166789178437745e2, a2 = 1.9715909503065514427e3, a3 = 1.3731693765509461125e4, a4 = 4.5921953931549871457e4, a5 = 6.7265770927008700853e4, a6 = 3.3430575583588128105e4, a7 = 2.5090809287301226727e3, b1 = 4.2313330701600911252e1, b2 = 6.871870074920579083e2, b3 = 5.3941960214247511077e3, b4 = 2.1213794301586595867e4, b5 = 3.930789580009271061e4, b6 = 2.8729085735721942674e4, b7 = 5.226495278852854561e3, c0 = 1.42343711074968357734, c1 = 4.6303378461565452959, c2 = 5.7694972214606914055, c3 = 3.64784832476320460504, c4 = 1.27045825245236838258, c5 = 2.4178072517745061177e-1, c6 = 2.27238449892691845833e-2, c7 = 7.7454501427834140764e-4, d1 = 2.05319162663775882187, d2 = 1.6763848301838038494, d3 = 6.8976733498510000455e-1, d4 = 1.4810397642748007459e-1, d5 = 1.51986665636164571966e-2, d6 = 5.475938084995344946e-4, d7 = 1.05075007164441684324e-9, e0 = 6.6579046435011037772, e1 = 5.4637849111641143699, e2 = 1.7848265399172913358, e3 = 2.9656057182850489123e-1, e4 = 2.6532189526576123093e-2, e5 = 1.2426609473880784386e-3, e6 = 2.71155556874348757815e-5, e7 = 2.01033439929228813265e-7, f1 = 5.9983220655588793769e-1, f2 = 1.3692988092273580531e-1, f3 = 1.48753612908506148525e-2, f4 = 7.868691311456132591e-4, f5 = 1.8463183175100546818e-5, f6 = 1.4215117583164458887e-7, f7 = 2.04426310338993978564e-15;
19
+ const a0 = 3.3871328727963665, a1 = 133.14166789178438, a2 = 1971.5909503065513, a3 = 13731.69376550946, a4 = 45921.95393154987, a5 = 67265.7709270087, a6 = 33430.57558358813, a7 = 2509.0809287301227, b1 = 42.31333070160091, b2 = 687.1870074920579, b3 = 5394.196021424751, b4 = 21213.794301586597, b5 = 39307.89580009271, b6 = 28729.085735721943, b7 = 5226.495278852854, c0 = 1.4234371107496835, c1 = 4.630337846156546, c2 = 5.769497221460691, c3 = 3.6478483247632045, c4 = 1.2704582524523684, c5 = 0.2417807251774506, c6 = 0.022723844989269184, c7 = 0.0007745450142783414, d1 = 2.053191626637759, d2 = 1.6763848301838038, d3 = 0.6897673349851, d4 = 0.14810397642748008, d5 = 0.015198666563616457, d6 = 0.0005475938084995345, d7 = 1.0507500716444169e-9, e0 = 6.657904643501103, e1 = 5.463784911164114, e2 = 1.7848265399172913, e3 = 0.29656057182850487, e4 = 0.026532189526576124, e5 = 0.0012426609473880784, e6 = 0.000027115555687434876, e7 = 2.0103343992922881e-7, f1 = 0.599832206555888, f2 = 0.1369298809227358, f3 = 0.014875361290850615, f4 = 0.0007868691311456133, f5 = 0.000018463183175100548, f6 = 1.421511758316446e-7, f7 = 2.0442631033899397e-15;
20
20
  const q = p - 0.5;
21
21
  let r, z;
22
22
  // p close to 0.5
@@ -9,7 +9,7 @@ const scaled_cosine = (i) => 0.5 * (1.0 - Math.cos(i * Math.PI));
9
9
  let perlin; // will be initialized lazily by noise() or noiseSeed()
10
10
  export function noise(x, y = 0, z = 0) {
11
11
  if (perlin == null) {
12
- perlin = new Array(PERLIN_SIZE + 1);
12
+ perlin = Array.from({ length: PERLIN_SIZE + 1 });
13
13
  for (let i = 0; i < PERLIN_SIZE + 1; i++) {
14
14
  perlin[i] = Math.random();
15
15
  }
@@ -0,0 +1,26 @@
1
+ export type InterpolateFunction = (index: number[], width: number, height: number, X: Float64Array, Y: Float64Array, V: ArrayLike<any>) => ArrayLike<any>;
2
+ /**
3
+ * Simple forward mapping: each sample is binned to its nearest pixel.
4
+ * If multiple samples map to the same pixel, the last one wins.
5
+ */
6
+ export declare function interpolateNone(index: number[], width: number, height: number, X: Float64Array, Y: Float64Array, V: ArrayLike<any>): any[];
7
+ /**
8
+ * Nearest-neighbor interpolation using Delaunay triangulation.
9
+ */
10
+ export declare function interpolateNearest(index: number[], width: number, height: number, X: Float64Array, Y: Float64Array, V: ArrayLike<any>): any;
11
+ /**
12
+ * Barycentric interpolation: fills the interior of each Delaunay triangle with
13
+ * barycentric-weighted values, then extrapolates exterior pixels to the hull.
14
+ */
15
+ export declare function interpolatorBarycentric({ random }?: {
16
+ random?: (() => number) | undefined;
17
+ }): InterpolateFunction;
18
+ /**
19
+ * Walk-on-spheres algorithm for smooth interpolation.
20
+ * https://observablehq.com/@observablehq/walk-on-spheres-precision
21
+ */
22
+ export declare function interpolatorRandomWalk({ random, minDistance, maxSteps }?: {
23
+ random?: (() => number) | undefined;
24
+ minDistance?: number | undefined;
25
+ maxSteps?: number | undefined;
26
+ }): InterpolateFunction;