@mui/x-charts-pro 9.8.0 → 9.10.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 (209) hide show
  1. package/BarChartPro/BarChartPro.d.mts +9 -1
  2. package/BarChartPro/BarChartPro.d.ts +9 -1
  3. package/BarChartPro/BarChartPro.js +14 -3
  4. package/BarChartPro/BarChartPro.mjs +14 -3
  5. package/BarChartPro/BarChartPro.plugins.d.mts +2 -1
  6. package/BarChartPro/BarChartPro.plugins.d.ts +2 -1
  7. package/BarChartPro/BarChartPro.plugins.js +2 -1
  8. package/BarChartPro/BarChartPro.plugins.mjs +2 -1
  9. package/CHANGELOG.md +230 -0
  10. package/ChartsContainerPro/useChartsContainerProProps.d.mts +6 -2
  11. package/ChartsContainerPro/useChartsContainerProProps.d.ts +6 -2
  12. package/ChartsContainerPro/useChartsContainerProProps.js +9 -1
  13. package/ChartsContainerPro/useChartsContainerProProps.mjs +8 -1
  14. package/ChartsDataProviderPro/ChartsDataProviderPro.d.mts +1 -1
  15. package/ChartsDataProviderPro/ChartsDataProviderPro.d.ts +1 -1
  16. package/ChartsDataProviderPro/ChartsDataProviderPro.js +14 -3
  17. package/ChartsDataProviderPro/ChartsDataProviderPro.mjs +14 -3
  18. package/ChartsDataProviderPro/useChartsDataProviderProProps.d.mts +1 -1
  19. package/ChartsDataProviderPro/useChartsDataProviderProProps.d.ts +1 -1
  20. package/ChartsToolbarPro/ChartsToolbarPro.d.mts +2 -1
  21. package/ChartsToolbarPro/ChartsToolbarPro.d.ts +2 -1
  22. package/ChartsToolbarPro/Toolbar.types.d.mts +2 -1
  23. package/ChartsToolbarPro/Toolbar.types.d.ts +2 -1
  24. package/ChartsToolbarPro/internals/ChartsMenu.d.mts +1 -1
  25. package/ChartsToolbarPro/internals/ChartsMenu.d.ts +1 -1
  26. package/ChartsZoomSlider/ChartsZoomSlider.d.mts +2 -1
  27. package/ChartsZoomSlider/ChartsZoomSlider.d.ts +2 -1
  28. package/ChartsZoomSlider/internals/ChartsAxisZoomSlider.d.mts +2 -1
  29. package/ChartsZoomSlider/internals/ChartsAxisZoomSlider.d.ts +2 -1
  30. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderActiveTrack.d.mts +2 -1
  31. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderActiveTrack.d.ts +2 -1
  32. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderPreview.d.mts +2 -1
  33. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderPreview.d.ts +2 -1
  34. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderPreviewContent.d.mts +2 -1
  35. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderPreviewContent.d.ts +2 -1
  36. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderTrack.d.mts +1 -1
  37. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderTrack.d.ts +1 -1
  38. package/ChartsZoomSlider/internals/ChartsTooltipZoomSliderValue.d.mts +1 -1
  39. package/ChartsZoomSlider/internals/ChartsTooltipZoomSliderValue.d.ts +1 -1
  40. package/ChartsZoomSlider/internals/chartsAxisZoomSliderTrackClasses.d.mts +1 -1
  41. package/ChartsZoomSlider/internals/chartsAxisZoomSliderTrackClasses.d.ts +1 -1
  42. package/ChartsZoomSlider/internals/previews/AreaPreviewPlot.d.mts +1 -1
  43. package/ChartsZoomSlider/internals/previews/AreaPreviewPlot.d.ts +1 -1
  44. package/ChartsZoomSlider/internals/previews/BarPreviewPlot.d.mts +1 -1
  45. package/ChartsZoomSlider/internals/previews/BarPreviewPlot.d.ts +1 -1
  46. package/ChartsZoomSlider/internals/previews/LineAreaPreviewPlot.d.mts +2 -1
  47. package/ChartsZoomSlider/internals/previews/LineAreaPreviewPlot.d.ts +2 -1
  48. package/ChartsZoomSlider/internals/previews/LinePreviewPlot.d.mts +1 -1
  49. package/ChartsZoomSlider/internals/previews/LinePreviewPlot.d.ts +1 -1
  50. package/ChartsZoomSlider/internals/previews/LinePreviewPlot.js +16 -1
  51. package/ChartsZoomSlider/internals/previews/LinePreviewPlot.mjs +17 -2
  52. package/ChartsZoomSlider/internals/previews/ScatterPreviewPlot.d.mts +2 -1
  53. package/ChartsZoomSlider/internals/previews/ScatterPreviewPlot.d.ts +2 -1
  54. package/FunnelChart/FocusedFunnelSection.d.mts +1 -1
  55. package/FunnelChart/FocusedFunnelSection.d.ts +1 -1
  56. package/FunnelChart/FunnelPlot.d.mts +1 -1
  57. package/FunnelChart/FunnelPlot.d.ts +1 -1
  58. package/FunnelChart/curves/getFunnelCurve.d.mts +1 -1
  59. package/FunnelChart/curves/getFunnelCurve.d.ts +1 -1
  60. package/FunnelChart/funnelClasses.d.mts +1 -1
  61. package/FunnelChart/funnelClasses.d.ts +1 -1
  62. package/FunnelChart/funnelPlotSlots.types.d.mts +3 -2
  63. package/FunnelChart/funnelPlotSlots.types.d.ts +3 -2
  64. package/FunnelChart/labelUtils.d.mts +2 -2
  65. package/FunnelChart/labelUtils.d.ts +2 -2
  66. package/FunnelChart/seriesConfig/index.js +2 -2
  67. package/FunnelChart/seriesConfig/index.mjs +2 -2
  68. package/FunnelChart/seriesConfig/tooltipPosition.d.mts +2 -3
  69. package/FunnelChart/seriesConfig/tooltipPosition.d.ts +2 -3
  70. package/FunnelChart/seriesConfig/tooltipPosition.js +11 -15
  71. package/FunnelChart/seriesConfig/tooltipPosition.mjs +11 -15
  72. package/Heatmap/FocusedHeatmapCell.d.mts +1 -1
  73. package/Heatmap/FocusedHeatmapCell.d.ts +1 -1
  74. package/Heatmap/Heatmap.d.mts +2 -1
  75. package/Heatmap/Heatmap.d.ts +2 -1
  76. package/Heatmap/HeatmapItem.d.mts +3 -2
  77. package/Heatmap/HeatmapItem.d.ts +3 -2
  78. package/Heatmap/HeatmapSVGPlot.d.mts +2 -1
  79. package/Heatmap/HeatmapSVGPlot.d.ts +2 -1
  80. package/Heatmap/HeatmapTooltip/HeatmapTooltip.classes.d.mts +1 -1
  81. package/Heatmap/HeatmapTooltip/HeatmapTooltip.classes.d.ts +1 -1
  82. package/Heatmap/HeatmapTooltip/HeatmapTooltip.d.mts +1 -1
  83. package/Heatmap/HeatmapTooltip/HeatmapTooltip.d.ts +1 -1
  84. package/Heatmap/HeatmapTooltip/HeatmapTooltip.types.d.mts +2 -1
  85. package/Heatmap/HeatmapTooltip/HeatmapTooltip.types.d.ts +2 -1
  86. package/Heatmap/HeatmapTooltip/HeatmapTooltipContent.d.mts +1 -1
  87. package/Heatmap/HeatmapTooltip/HeatmapTooltipContent.d.ts +1 -1
  88. package/Heatmap/seriesConfig/highlight.d.mts +2 -2
  89. package/Heatmap/seriesConfig/highlight.d.ts +2 -2
  90. package/Heatmap/seriesConfig/index.js +2 -2
  91. package/Heatmap/seriesConfig/index.mjs +2 -2
  92. package/Heatmap/seriesConfig/tooltipPosition.d.mts +2 -3
  93. package/Heatmap/seriesConfig/tooltipPosition.d.ts +2 -3
  94. package/Heatmap/seriesConfig/tooltipPosition.js +12 -16
  95. package/Heatmap/seriesConfig/tooltipPosition.mjs +12 -16
  96. package/LineChartPro/LineChartPro.d.mts +11 -1
  97. package/LineChartPro/LineChartPro.d.ts +11 -1
  98. package/LineChartPro/LineChartPro.js +16 -3
  99. package/LineChartPro/LineChartPro.mjs +16 -3
  100. package/LineChartPro/LineChartPro.plugins.d.mts +2 -1
  101. package/LineChartPro/LineChartPro.plugins.d.ts +2 -1
  102. package/LineChartPro/LineChartPro.plugins.js +2 -1
  103. package/LineChartPro/LineChartPro.plugins.mjs +2 -1
  104. package/SankeyChart/FocusedSankeyLink.d.mts +1 -1
  105. package/SankeyChart/FocusedSankeyLink.d.ts +1 -1
  106. package/SankeyChart/FocusedSankeyNode.d.mts +1 -1
  107. package/SankeyChart/FocusedSankeyNode.d.ts +1 -1
  108. package/SankeyChart/SankeyDataProvider.d.mts +1 -1
  109. package/SankeyChart/SankeyDataProvider.d.ts +1 -1
  110. package/SankeyChart/SankeyLinkLabelPlot.d.mts +1 -1
  111. package/SankeyChart/SankeyLinkLabelPlot.d.ts +1 -1
  112. package/SankeyChart/SankeyLinkPlot.d.mts +1 -1
  113. package/SankeyChart/SankeyLinkPlot.d.ts +1 -1
  114. package/SankeyChart/SankeyNodeLabelPlot.d.mts +1 -1
  115. package/SankeyChart/SankeyNodeLabelPlot.d.ts +1 -1
  116. package/SankeyChart/SankeyNodePlot.d.mts +1 -1
  117. package/SankeyChart/SankeyNodePlot.d.ts +1 -1
  118. package/SankeyChart/SankeyPlot.d.mts +1 -1
  119. package/SankeyChart/SankeyPlot.d.ts +1 -1
  120. package/SankeyChart/SankeyTooltip/SankeyTooltip.classes.d.mts +1 -1
  121. package/SankeyChart/SankeyTooltip/SankeyTooltip.classes.d.ts +1 -1
  122. package/SankeyChart/SankeyTooltip/SankeyTooltip.d.mts +1 -1
  123. package/SankeyChart/SankeyTooltip/SankeyTooltip.d.ts +1 -1
  124. package/SankeyChart/SankeyTooltip/SankeyTooltipContent.d.mts +1 -1
  125. package/SankeyChart/SankeyTooltip/SankeyTooltipContent.d.ts +1 -1
  126. package/SankeyChart/sankeyClasses.d.mts +1 -1
  127. package/SankeyChart/sankeyClasses.d.ts +1 -1
  128. package/SankeyChart/seriesConfig/createIsFaded.d.mts +1 -1
  129. package/SankeyChart/seriesConfig/createIsFaded.d.ts +1 -1
  130. package/SankeyChart/seriesConfig/index.js +2 -2
  131. package/SankeyChart/seriesConfig/index.mjs +2 -2
  132. package/SankeyChart/seriesConfig/tooltipPosition.d.mts +2 -3
  133. package/SankeyChart/seriesConfig/tooltipPosition.d.ts +2 -3
  134. package/SankeyChart/seriesConfig/tooltipPosition.js +6 -11
  135. package/SankeyChart/seriesConfig/tooltipPosition.mjs +5 -10
  136. package/SankeyChart/utils.d.mts +2 -2
  137. package/SankeyChart/utils.d.ts +2 -2
  138. package/ScatterChartPro/ScatterChartPro.d.mts +2 -1
  139. package/ScatterChartPro/ScatterChartPro.d.ts +2 -1
  140. package/index.js +1 -1
  141. package/index.mjs +1 -1
  142. package/internals/ChartsWatermark.d.mts +2 -1
  143. package/internals/ChartsWatermark.d.ts +2 -1
  144. package/internals/index.d.mts +2 -0
  145. package/internals/index.d.ts +2 -0
  146. package/internals/index.js +30 -0
  147. package/internals/index.mjs +2 -0
  148. package/internals/material/components/BaseMenuItem.d.mts +1 -1
  149. package/internals/material/components/BaseMenuItem.d.ts +1 -1
  150. package/internals/material/components/BasePopper.d.mts +2 -1
  151. package/internals/material/components/BasePopper.d.ts +2 -1
  152. package/internals/plugins/allPlugins.d.mts +2 -2
  153. package/internals/plugins/allPlugins.d.ts +2 -2
  154. package/internals/plugins/useChartProSampling/barSampler.d.mts +8 -0
  155. package/internals/plugins/useChartProSampling/barSampler.d.ts +8 -0
  156. package/internals/plugins/useChartProSampling/barSampler.js +18 -0
  157. package/internals/plugins/useChartProSampling/barSampler.mjs +12 -0
  158. package/internals/plugins/useChartProSampling/index.d.mts +2 -0
  159. package/internals/plugins/useChartProSampling/index.d.ts +2 -0
  160. package/internals/plugins/useChartProSampling/index.js +12 -0
  161. package/internals/plugins/useChartProSampling/index.mjs +1 -0
  162. package/internals/plugins/useChartProSampling/lineSampler.d.mts +8 -0
  163. package/internals/plugins/useChartProSampling/lineSampler.d.ts +8 -0
  164. package/internals/plugins/useChartProSampling/lineSampler.js +47 -0
  165. package/internals/plugins/useChartProSampling/lineSampler.mjs +41 -0
  166. package/internals/plugins/useChartProSampling/sampling.d.mts +38 -0
  167. package/internals/plugins/useChartProSampling/sampling.d.ts +38 -0
  168. package/internals/plugins/useChartProSampling/sampling.js +175 -0
  169. package/internals/plugins/useChartProSampling/sampling.line.d.mts +22 -0
  170. package/internals/plugins/useChartProSampling/sampling.line.d.ts +22 -0
  171. package/internals/plugins/useChartProSampling/sampling.line.js +172 -0
  172. package/internals/plugins/useChartProSampling/sampling.line.mjs +164 -0
  173. package/internals/plugins/useChartProSampling/sampling.mjs +164 -0
  174. package/internals/plugins/useChartProSampling/sampling.pyramid.types.d.mts +33 -0
  175. package/internals/plugins/useChartProSampling/sampling.pyramid.types.d.ts +33 -0
  176. package/internals/plugins/useChartProSampling/sampling.pyramid.types.js +5 -0
  177. package/internals/plugins/useChartProSampling/sampling.pyramid.types.mjs +1 -0
  178. package/internals/plugins/useChartProSampling/useChartProSampling.d.mts +4 -0
  179. package/internals/plugins/useChartProSampling/useChartProSampling.d.ts +4 -0
  180. package/internals/plugins/useChartProSampling/useChartProSampling.js +59 -0
  181. package/internals/plugins/useChartProSampling/useChartProSampling.mjs +52 -0
  182. package/internals/plugins/useChartProSampling/useChartProSampling.types.d.mts +25 -0
  183. package/internals/plugins/useChartProSampling/useChartProSampling.types.d.ts +25 -0
  184. package/internals/plugins/useChartProSampling/useChartProSampling.types.js +5 -0
  185. package/internals/plugins/useChartProSampling/useChartProSampling.types.mjs +1 -0
  186. package/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.d.mts +26 -26
  187. package/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.d.ts +26 -26
  188. package/internals/plugins/useChartProZoom/gestureHooks/usePanOnDrag.d.mts +1 -1
  189. package/internals/plugins/useChartProZoom/gestureHooks/usePanOnDrag.d.ts +1 -1
  190. package/internals/plugins/useChartProZoom/gestureHooks/usePanOnPressAndDrag.d.mts +1 -1
  191. package/internals/plugins/useChartProZoom/gestureHooks/usePanOnPressAndDrag.d.ts +1 -1
  192. package/internals/plugins/useChartProZoom/gestureHooks/usePanOnWheel.d.mts +1 -1
  193. package/internals/plugins/useChartProZoom/gestureHooks/usePanOnWheel.d.ts +1 -1
  194. package/internals/plugins/useChartProZoom/gestureHooks/useRegisterZoomGestures.d.mts +4 -3
  195. package/internals/plugins/useChartProZoom/gestureHooks/useRegisterZoomGestures.d.ts +4 -3
  196. package/internals/plugins/useChartProZoom/gestureHooks/useRegisterZoomGestures.mjs +1 -0
  197. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnBrush.d.mts +1 -1
  198. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnBrush.d.ts +1 -1
  199. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnDoubleTapReset.d.mts +1 -1
  200. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnDoubleTapReset.d.ts +1 -1
  201. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnPinch.d.mts +1 -1
  202. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnPinch.d.ts +1 -1
  203. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnTapAndDrag.d.mts +1 -1
  204. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnTapAndDrag.d.ts +1 -1
  205. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnWheel.d.mts +1 -1
  206. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnWheel.d.ts +1 -1
  207. package/package.json +6 -6
  208. package/typeOverloads/modules.d.mts +9 -1
  209. package/typeOverloads/modules.d.ts +9 -1
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "useChartProSampling", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _useChartProSampling.useChartProSampling;
10
+ }
11
+ });
12
+ var _useChartProSampling = require("./useChartProSampling");
@@ -0,0 +1 @@
1
+ export { useChartProSampling } from "./useChartProSampling.mjs";
@@ -0,0 +1,8 @@
1
+ import type { SamplingStrategy } from '@mui/x-charts/internals';
2
+ import type { SamplingPyramid } from "./sampling.pyramid.types.mjs";
3
+ /**
4
+ * Min/max LOD pyramid over the line's y values (single channel when null-free). Nulls get `+Inf`
5
+ * low / `-Inf` high so they never win an extremum, and go into `nullIndices` for the sampler to
6
+ * re-insert as line breaks.
7
+ */
8
+ export declare const lineSampler: SamplingStrategy<'line', SamplingPyramid>;
@@ -0,0 +1,8 @@
1
+ import type { SamplingStrategy } from '@mui/x-charts/internals';
2
+ import type { SamplingPyramid } from "./sampling.pyramid.types.js";
3
+ /**
4
+ * Min/max LOD pyramid over the line's y values (single channel when null-free). Nulls get `+Inf`
5
+ * low / `-Inf` high so they never win an extremum, and go into `nullIndices` for the sampler to
6
+ * re-insert as line breaks.
7
+ */
8
+ export declare const lineSampler: SamplingStrategy<'line', SamplingPyramid>;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.lineSampler = void 0;
7
+ var _sampling = require("./sampling");
8
+ var _sampling2 = require("./sampling.line");
9
+ /**
10
+ * Min/max LOD pyramid over the line's y values (single channel when null-free). Nulls get `+Inf`
11
+ * low / `-Inf` high so they never win an extremum, and go into `nullIndices` for the sampler to
12
+ * re-insert as line breaks.
13
+ */
14
+ const lineSampler = exports.lineSampler = {
15
+ build: series => {
16
+ const {
17
+ data,
18
+ visibleStackedData
19
+ } = series;
20
+ const length = visibleStackedData.length;
21
+ const values = new Float64Array(length);
22
+ let nullIndices = null;
23
+ for (let i = 0; i < length; i += 1) {
24
+ if (data[i] == null) {
25
+ (nullIndices ?? (nullIndices = [])).push(i);
26
+ } else {
27
+ values[i] = visibleStackedData[i][1];
28
+ }
29
+ }
30
+ if (!nullIndices) {
31
+ return (0, _sampling.buildSamplingPyramid)(values, values);
32
+ }
33
+
34
+ // Two channels only when gaps exist: nulls lose both extrema comparisons.
35
+ const low = values;
36
+ const high = Float64Array.from(values);
37
+ for (let i = 0; i < nullIndices.length; i += 1) {
38
+ low[nullIndices[i]] = Infinity;
39
+ high[nullIndices[i]] = -Infinity;
40
+ }
41
+ const pyramid = (0, _sampling.buildSamplingPyramid)(low, high);
42
+ pyramid.nullIndices = Int32Array.from(nullIndices);
43
+ return pyramid;
44
+ },
45
+ sample: _sampling2.sample,
46
+ bucketSizeAt: (span, context) => (0, _sampling.getSamplingBucketSize)(span, context.dataLength, context.availableSize, context.minSpan)
47
+ };
@@ -0,0 +1,41 @@
1
+ import { buildSamplingPyramid, getSamplingBucketSize } from "./sampling.mjs";
2
+ import { sample } from "./sampling.line.mjs";
3
+ /**
4
+ * Min/max LOD pyramid over the line's y values (single channel when null-free). Nulls get `+Inf`
5
+ * low / `-Inf` high so they never win an extremum, and go into `nullIndices` for the sampler to
6
+ * re-insert as line breaks.
7
+ */
8
+ export const lineSampler = {
9
+ build: series => {
10
+ const {
11
+ data,
12
+ visibleStackedData
13
+ } = series;
14
+ const length = visibleStackedData.length;
15
+ const values = new Float64Array(length);
16
+ let nullIndices = null;
17
+ for (let i = 0; i < length; i += 1) {
18
+ if (data[i] == null) {
19
+ (nullIndices ??= []).push(i);
20
+ } else {
21
+ values[i] = visibleStackedData[i][1];
22
+ }
23
+ }
24
+ if (!nullIndices) {
25
+ return buildSamplingPyramid(values, values);
26
+ }
27
+
28
+ // Two channels only when gaps exist: nulls lose both extrema comparisons.
29
+ const low = values;
30
+ const high = Float64Array.from(values);
31
+ for (let i = 0; i < nullIndices.length; i += 1) {
32
+ low[nullIndices[i]] = Infinity;
33
+ high[nullIndices[i]] = -Infinity;
34
+ }
35
+ const pyramid = buildSamplingPyramid(low, high);
36
+ pyramid.nullIndices = Int32Array.from(nullIndices);
37
+ return pyramid;
38
+ },
39
+ sample,
40
+ bucketSizeAt: (span, context) => getSamplingBucketSize(span, context.dataLength, context.availableSize, context.minSpan)
41
+ };
@@ -0,0 +1,38 @@
1
+ import type { ActiveSamplingLevel, SamplingPyramid } from "./sampling.pyramid.types.mjs";
2
+ /** Smallest element size (px) allowed before sampling kicks in. */
3
+ export declare const MIN_ELEMENT_SIZE_PX = 6;
4
+ /**
5
+ * Min zoom span (%) at which the data fills the view with `MIN_ELEMENT_SIZE_PX`-wide elements.
6
+ * Screen-derived, so elements never get thinner and the level count adapts to screen + data.
7
+ */
8
+ export declare function getSamplingMinSpan(dataLength: number, availableSizePx: number): number;
9
+ /**
10
+ * Hard ceiling on rendered elements, independent of zoom. Even at the deepest zoom (where elements
11
+ * would be wide enough to skip screen sampling), a view with more than this many points is still
12
+ * sampled enough levels to get under the cap — too many elements hurts performance whatever the size.
13
+ */
14
+ export declare const MAX_RENDERED_POINTS = 2000;
15
+ /**
16
+ * Builds the LOD pyramid for one series from its low/high value channels (for bars: the stacked
17
+ * `[base, top]` channels; for lines: the displayed `y` for both). Each bucket keeps the original
18
+ * index of its min (over `low`) and max (over `high`) — a min/max envelope, so spikes and troughs
19
+ * survive. Consumers read the values back from their own series data by index.
20
+ *
21
+ * Stored flat: all levels concatenated into `argMin`/`argMax`, with `offsets` marking level
22
+ * boundaries. Level 0 (no merge) is implicit; bucket indices are derived (`j * bucketSize`).
23
+ */
24
+ export declare function buildSamplingPyramid(low: ArrayLike<number>, high: ArrayLike<number>): SamplingPyramid;
25
+ /** Number of stored levels (finest `bucketSize 2` to coarsest). */
26
+ export declare function getSamplingLevelCount(pyramid: SamplingPyramid): number;
27
+ /**
28
+ * Picks the active level from the zoom span: level 0 (deepest zoom, `span <= minSpan`, or elements
29
+ * still wide enough) → no sampling (`null`, raw 1:1); each level up → bucket size ×2, chosen so
30
+ * sampled elements stay at least `MIN_ELEMENT_SIZE_PX` wide. Clamped to the pyramid depth.
31
+ * `bucketSize === 2 ** clampedLevel`, level buckets `offsets[clampedLevel - 1 .. clampedLevel]`.
32
+ */
33
+ export declare function selectSamplingLevel(pyramid: SamplingPyramid, currentSpan: number, availableSizePx: number, minSpan: number): ActiveSamplingLevel | null;
34
+ /**
35
+ * Active bucket size (`1` = no sampling) for an axis with no built pyramid (the axis highlight).
36
+ * Matches `selectSamplingLevel`'s `bucketSize`, derived from `dataLength` + screen + `minSpan`.
37
+ */
38
+ export declare function getSamplingBucketSize(currentSpan: number, dataLength: number, availableSizePx: number, minSpan: number): number;
@@ -0,0 +1,38 @@
1
+ import type { ActiveSamplingLevel, SamplingPyramid } from "./sampling.pyramid.types.js";
2
+ /** Smallest element size (px) allowed before sampling kicks in. */
3
+ export declare const MIN_ELEMENT_SIZE_PX = 6;
4
+ /**
5
+ * Min zoom span (%) at which the data fills the view with `MIN_ELEMENT_SIZE_PX`-wide elements.
6
+ * Screen-derived, so elements never get thinner and the level count adapts to screen + data.
7
+ */
8
+ export declare function getSamplingMinSpan(dataLength: number, availableSizePx: number): number;
9
+ /**
10
+ * Hard ceiling on rendered elements, independent of zoom. Even at the deepest zoom (where elements
11
+ * would be wide enough to skip screen sampling), a view with more than this many points is still
12
+ * sampled enough levels to get under the cap — too many elements hurts performance whatever the size.
13
+ */
14
+ export declare const MAX_RENDERED_POINTS = 2000;
15
+ /**
16
+ * Builds the LOD pyramid for one series from its low/high value channels (for bars: the stacked
17
+ * `[base, top]` channels; for lines: the displayed `y` for both). Each bucket keeps the original
18
+ * index of its min (over `low`) and max (over `high`) — a min/max envelope, so spikes and troughs
19
+ * survive. Consumers read the values back from their own series data by index.
20
+ *
21
+ * Stored flat: all levels concatenated into `argMin`/`argMax`, with `offsets` marking level
22
+ * boundaries. Level 0 (no merge) is implicit; bucket indices are derived (`j * bucketSize`).
23
+ */
24
+ export declare function buildSamplingPyramid(low: ArrayLike<number>, high: ArrayLike<number>): SamplingPyramid;
25
+ /** Number of stored levels (finest `bucketSize 2` to coarsest). */
26
+ export declare function getSamplingLevelCount(pyramid: SamplingPyramid): number;
27
+ /**
28
+ * Picks the active level from the zoom span: level 0 (deepest zoom, `span <= minSpan`, or elements
29
+ * still wide enough) → no sampling (`null`, raw 1:1); each level up → bucket size ×2, chosen so
30
+ * sampled elements stay at least `MIN_ELEMENT_SIZE_PX` wide. Clamped to the pyramid depth.
31
+ * `bucketSize === 2 ** clampedLevel`, level buckets `offsets[clampedLevel - 1 .. clampedLevel]`.
32
+ */
33
+ export declare function selectSamplingLevel(pyramid: SamplingPyramid, currentSpan: number, availableSizePx: number, minSpan: number): ActiveSamplingLevel | null;
34
+ /**
35
+ * Active bucket size (`1` = no sampling) for an axis with no built pyramid (the axis highlight).
36
+ * Matches `selectSamplingLevel`'s `bucketSize`, derived from `dataLength` + screen + `minSpan`.
37
+ */
38
+ export declare function getSamplingBucketSize(currentSpan: number, dataLength: number, availableSizePx: number, minSpan: number): number;
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.MIN_ELEMENT_SIZE_PX = exports.MAX_RENDERED_POINTS = void 0;
7
+ exports.buildSamplingPyramid = buildSamplingPyramid;
8
+ exports.getSamplingBucketSize = getSamplingBucketSize;
9
+ exports.getSamplingLevelCount = getSamplingLevelCount;
10
+ exports.getSamplingMinSpan = getSamplingMinSpan;
11
+ exports.selectSamplingLevel = selectSamplingLevel;
12
+ /** Smallest element size (px) allowed before sampling kicks in. */
13
+ const MIN_ELEMENT_SIZE_PX = exports.MIN_ELEMENT_SIZE_PX = 6;
14
+
15
+ /**
16
+ * Min zoom span (%) at which the data fills the view with `MIN_ELEMENT_SIZE_PX`-wide elements.
17
+ * Screen-derived, so elements never get thinner and the level count adapts to screen + data.
18
+ */
19
+ function getSamplingMinSpan(dataLength, availableSizePx) {
20
+ return 100 * availableSizePx / (MIN_ELEMENT_SIZE_PX * dataLength);
21
+ }
22
+ const EMPTY_PYRAMID_OFFSETS = new Int32Array(1);
23
+
24
+ /**
25
+ * Level 0 (raw) extends this factor above `minSpan` before the first sampled level kicks in — a
26
+ * half-octave of wiggle room so the deepest zoom stays unsampled without a knife-edge transition.
27
+ */
28
+ const RAW_LEVEL_SPAN_FACTOR = Math.SQRT2;
29
+
30
+ /**
31
+ * Hard ceiling on rendered elements, independent of zoom. Even at the deepest zoom (where elements
32
+ * would be wide enough to skip screen sampling), a view with more than this many points is still
33
+ * sampled enough levels to get under the cap — too many elements hurts performance whatever the size.
34
+ */
35
+ const MAX_RENDERED_POINTS = exports.MAX_RENDERED_POINTS = 2000;
36
+
37
+ /** Levels of sampling needed to bring `visiblePoints` under `MAX_RENDERED_POINTS` (0 if under). */
38
+ function pointCapLevel(visiblePoints) {
39
+ return visiblePoints > MAX_RENDERED_POINTS ? Math.ceil(Math.log2(visiblePoints / MAX_RENDERED_POINTS)) : 0;
40
+ }
41
+
42
+ /**
43
+ * Builds the LOD pyramid for one series from its low/high value channels (for bars: the stacked
44
+ * `[base, top]` channels; for lines: the displayed `y` for both). Each bucket keeps the original
45
+ * index of its min (over `low`) and max (over `high`) — a min/max envelope, so spikes and troughs
46
+ * survive. Consumers read the values back from their own series data by index.
47
+ *
48
+ * Stored flat: all levels concatenated into `argMin`/`argMax`, with `offsets` marking level
49
+ * boundaries. Level 0 (no merge) is implicit; bucket indices are derived (`j * bucketSize`).
50
+ */
51
+ function buildSamplingPyramid(low, high) {
52
+ const dataLength = low.length;
53
+ if (dataLength <= 1) {
54
+ return {
55
+ dataLength,
56
+ argMin: new Int32Array(0),
57
+ argMax: new Int32Array(0),
58
+ offsets: EMPTY_PYRAMID_OFFSETS
59
+ };
60
+ }
61
+ const levelCount = Math.ceil(Math.log2(dataLength));
62
+
63
+ // Level start offsets into the shared buffers; each level halves the previous count (round up).
64
+ const offsets = new Int32Array(levelCount + 1);
65
+ for (let levelIndex = 0, count = dataLength; levelIndex < levelCount; levelIndex += 1) {
66
+ count = Math.ceil(count / 2);
67
+ offsets[levelIndex + 1] = offsets[levelIndex] + count;
68
+ }
69
+ const argMin = new Int32Array(offsets[levelCount]);
70
+ const argMax = new Int32Array(offsets[levelCount]);
71
+
72
+ // Level 0 (bucketSize 2) merges adjacent raw points; the only level that scans raw data.
73
+ for (let j = 0, i = 0; i < dataLength; j += 1, i += 2) {
74
+ const next = i + 1;
75
+ let lo = i;
76
+ let hi = i;
77
+ if (next < dataLength) {
78
+ if (low[next] < low[i]) {
79
+ lo = next;
80
+ }
81
+ if (high[next] > high[i]) {
82
+ hi = next;
83
+ }
84
+ }
85
+ argMin[j] = lo;
86
+ argMax[j] = hi;
87
+ }
88
+
89
+ // Higher levels merge pairs of buckets from the level below (argMin/argMax are associative via
90
+ // value compare, so the result equals re-scanning raw data). n/2 + n/4 + ... → O(n) build.
91
+ for (let levelIndex = 1; levelIndex < levelCount; levelIndex += 1) {
92
+ const prevStart = offsets[levelIndex - 1];
93
+ const prevCount = offsets[levelIndex] - prevStart;
94
+ const start = offsets[levelIndex];
95
+ for (let i = 0, j = 0; i < prevCount; i += 2, j += 1) {
96
+ const a = prevStart + i;
97
+ const w = start + j;
98
+ if (i + 1 >= prevCount) {
99
+ argMin[w] = argMin[a];
100
+ argMax[w] = argMax[a];
101
+ } else {
102
+ const b = a + 1;
103
+ argMin[w] = low[argMin[a]] <= low[argMin[b]] ? argMin[a] : argMin[b];
104
+ argMax[w] = high[argMax[a]] >= high[argMax[b]] ? argMax[a] : argMax[b];
105
+ }
106
+ }
107
+ }
108
+ return {
109
+ dataLength,
110
+ argMin,
111
+ argMax,
112
+ offsets
113
+ };
114
+ }
115
+
116
+ /** Number of stored levels (finest `bucketSize 2` to coarsest). */
117
+ function getSamplingLevelCount(pyramid) {
118
+ return pyramid.offsets.length - 1;
119
+ }
120
+
121
+ /**
122
+ * Level-of-detail index for the current zoom:
123
+ * - Level 0 (no sampling, raw) at the deepest zoom (`currentSpan <= minSpan * RAW_LEVEL_SPAN_FACTOR`),
124
+ * whatever the bar size — fully zoomed in shows every point, up to `MAX_RENDERED_POINTS`.
125
+ * - Above that, the level is screen-defined: bucket size keeps elements at least
126
+ * `MIN_ELEMENT_SIZE_PX` wide (+1 per span doubling past that pixel threshold).
127
+ * - The `MAX_RENDERED_POINTS` cap applies at every zoom: a view with too many points is always
128
+ * sampled, even in the raw zone where the screen rule alone would render it untouched.
129
+ */
130
+ function levelIndexFor(currentSpan, dataLength, availableSizePx, minSpan) {
131
+ if (!(currentSpan > 0)) {
132
+ return 0;
133
+ }
134
+ const capLevel = pointCapLevel(dataLength * Math.min(currentSpan, 100) / 100);
135
+ if (currentSpan <= minSpan * RAW_LEVEL_SPAN_FACTOR) {
136
+ return capLevel; // raw zone, but still bounded by the point cap
137
+ }
138
+ const screenMinSpan = getSamplingMinSpan(dataLength, availableSizePx);
139
+ if (!(screenMinSpan > 0)) {
140
+ return capLevel;
141
+ }
142
+ return Math.max(capLevel, Math.round(Math.log2(currentSpan / screenMinSpan)));
143
+ }
144
+
145
+ /**
146
+ * Picks the active level from the zoom span: level 0 (deepest zoom, `span <= minSpan`, or elements
147
+ * still wide enough) → no sampling (`null`, raw 1:1); each level up → bucket size ×2, chosen so
148
+ * sampled elements stay at least `MIN_ELEMENT_SIZE_PX` wide. Clamped to the pyramid depth.
149
+ * `bucketSize === 2 ** clampedLevel`, level buckets `offsets[clampedLevel - 1 .. clampedLevel]`.
150
+ */
151
+ function selectSamplingLevel(pyramid, currentSpan, availableSizePx, minSpan) {
152
+ const levelCount = pyramid.offsets.length - 1;
153
+ const levelIndex = levelIndexFor(currentSpan, pyramid.dataLength, availableSizePx, minSpan);
154
+ if (levelIndex <= 0 || levelCount === 0) {
155
+ return null;
156
+ }
157
+ const clampedLevel = Math.min(levelIndex, levelCount);
158
+ return {
159
+ bucketSize: 2 ** clampedLevel,
160
+ start: pyramid.offsets[clampedLevel - 1],
161
+ end: pyramid.offsets[clampedLevel]
162
+ };
163
+ }
164
+
165
+ /**
166
+ * Active bucket size (`1` = no sampling) for an axis with no built pyramid (the axis highlight).
167
+ * Matches `selectSamplingLevel`'s `bucketSize`, derived from `dataLength` + screen + `minSpan`.
168
+ */
169
+ function getSamplingBucketSize(currentSpan, dataLength, availableSizePx, minSpan) {
170
+ const levelIndex = levelIndexFor(currentSpan, dataLength, availableSizePx, minSpan);
171
+ if (levelIndex <= 0 || dataLength <= 1) {
172
+ return 1;
173
+ }
174
+ return 2 ** Math.min(levelIndex, Math.ceil(Math.log2(dataLength)));
175
+ }
@@ -0,0 +1,22 @@
1
+ import type { LineSamplingAlgorithm, SampleContext, SampledBucket } from '@mui/x-charts/internals';
2
+ import type { SamplingPyramid } from "./sampling.pyramid.types.mjs";
3
+ /**
4
+ * The active level of detail as buckets for the current zoom, or `null` for no sampling. Each bucket
5
+ * carries its original-index span plus the ascending indices to render. The same output feeds both
6
+ * series types: lines flatten the indices into a polyline, bars draw one merged rect per bucket.
7
+ *
8
+ * `minmax` (default; bars use it) keeps {min, max} per bucket; `m4` also brackets them with the
9
+ * bucket's first/last (pixel-accurate); both read the shared `SamplingPyramid`. `lttb` ignores
10
+ * the pyramid and reduces on demand, reading raw values from `getValues` (called only for `lttb`)
11
+ * as a single whole-range bucket. Its budget matches the envelope methods' point density so it
12
+ * isn't visually sparser at the same zoom.
13
+ */
14
+ export declare function sampleBuckets(pyramid: SamplingPyramid, currentSpan: number, availableSizePx: number, minSpan: number, algorithm?: LineSamplingAlgorithm, getValues?: () => ArrayLike<number>): SampledBucket[] | null;
15
+ /** Shared `sample` for every series type; the per-type build feeds the channels into the pyramid. */
16
+ export declare function sample(context: SampleContext): SampledBucket[] | null;
17
+ /**
18
+ * Largest-Triangle-Three-Buckets downsampling. Returns `threshold` original indices (including the
19
+ * first and last) chosen to preserve the line's visual shape. Uses the data index as the x channel.
20
+ * @see https://skemman.is/handle/1946/15343 (Sveinn Steinarsson, 2013)
21
+ */
22
+ export declare function largestTriangleThreeBuckets(values: ArrayLike<number>, threshold: number): Int32Array;
@@ -0,0 +1,22 @@
1
+ import type { LineSamplingAlgorithm, SampleContext, SampledBucket } from '@mui/x-charts/internals';
2
+ import type { SamplingPyramid } from "./sampling.pyramid.types.js";
3
+ /**
4
+ * The active level of detail as buckets for the current zoom, or `null` for no sampling. Each bucket
5
+ * carries its original-index span plus the ascending indices to render. The same output feeds both
6
+ * series types: lines flatten the indices into a polyline, bars draw one merged rect per bucket.
7
+ *
8
+ * `minmax` (default; bars use it) keeps {min, max} per bucket; `m4` also brackets them with the
9
+ * bucket's first/last (pixel-accurate); both read the shared `SamplingPyramid`. `lttb` ignores
10
+ * the pyramid and reduces on demand, reading raw values from `getValues` (called only for `lttb`)
11
+ * as a single whole-range bucket. Its budget matches the envelope methods' point density so it
12
+ * isn't visually sparser at the same zoom.
13
+ */
14
+ export declare function sampleBuckets(pyramid: SamplingPyramid, currentSpan: number, availableSizePx: number, minSpan: number, algorithm?: LineSamplingAlgorithm, getValues?: () => ArrayLike<number>): SampledBucket[] | null;
15
+ /** Shared `sample` for every series type; the per-type build feeds the channels into the pyramid. */
16
+ export declare function sample(context: SampleContext): SampledBucket[] | null;
17
+ /**
18
+ * Largest-Triangle-Three-Buckets downsampling. Returns `threshold` original indices (including the
19
+ * first and last) chosen to preserve the line's visual shape. Uses the data index as the x channel.
20
+ * @see https://skemman.is/handle/1946/15343 (Sveinn Steinarsson, 2013)
21
+ */
22
+ export declare function largestTriangleThreeBuckets(values: ArrayLike<number>, threshold: number): Int32Array;
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.largestTriangleThreeBuckets = largestTriangleThreeBuckets;
7
+ exports.sample = sample;
8
+ exports.sampleBuckets = sampleBuckets;
9
+ var _sampling = require("./sampling");
10
+ /** Points the `minmax` envelope renders per bucket (min, max). Used to align the `lttb` budget. */
11
+ const ENVELOPE_POINTS_PER_BUCKET = 2;
12
+
13
+ /**
14
+ * The active level of detail as buckets for the current zoom, or `null` for no sampling. Each bucket
15
+ * carries its original-index span plus the ascending indices to render. The same output feeds both
16
+ * series types: lines flatten the indices into a polyline, bars draw one merged rect per bucket.
17
+ *
18
+ * `minmax` (default; bars use it) keeps {min, max} per bucket; `m4` also brackets them with the
19
+ * bucket's first/last (pixel-accurate); both read the shared `SamplingPyramid`. `lttb` ignores
20
+ * the pyramid and reduces on demand, reading raw values from `getValues` (called only for `lttb`)
21
+ * as a single whole-range bucket. Its budget matches the envelope methods' point density so it
22
+ * isn't visually sparser at the same zoom.
23
+ */
24
+ function sampleBuckets(pyramid, currentSpan, availableSizePx, minSpan, algorithm = 'minmax', getValues) {
25
+ const level = (0, _sampling.selectSamplingLevel)(pyramid, currentSpan, availableSizePx, minSpan);
26
+ if (!level) {
27
+ return null;
28
+ }
29
+ const {
30
+ bucketSize,
31
+ start,
32
+ end
33
+ } = level;
34
+ const {
35
+ nullIndices
36
+ } = pyramid;
37
+ const maxIndex = pyramid.dataLength - 1;
38
+ const nullLength = nullIndices?.length ?? 0;
39
+
40
+ // Builds one bucket's render indices: the ascending `candidates` (extrema/ends or the lttb points)
41
+ // with this bucket's null indices merged in, deduped, so the line breaks at gaps. `nullCursor`
42
+ // advances monotonically since buckets partition `[0, maxIndex]` left to right.
43
+ let nullCursor = 0;
44
+ const mergeBucket = (candidates, endIndex) => {
45
+ const indices = [];
46
+ let c = 0;
47
+ while (c < candidates.length || nullCursor < nullLength && nullIndices[nullCursor] <= endIndex) {
48
+ const nextCandidate = c < candidates.length ? candidates[c] : Infinity;
49
+ const nextNull = nullCursor < nullLength && nullIndices[nullCursor] <= endIndex ? nullIndices[nullCursor] : Infinity;
50
+ const next = Math.min(nextCandidate, nextNull);
51
+ if (indices[indices.length - 1] !== next) {
52
+ indices.push(next);
53
+ }
54
+ if (next === nextCandidate) {
55
+ c += 1;
56
+ }
57
+ if (next === nextNull) {
58
+ nullCursor += 1;
59
+ }
60
+ }
61
+ return Int32Array.from(indices);
62
+ };
63
+ if (algorithm === 'lttb') {
64
+ // LTTB emits one point per bucket, vs `ENVELOPE_POINTS_PER_BUCKET` for the min/max methods.
65
+ // Scale the budget by that factor so LTTB isn't sparser than them at the same zoom.
66
+ const indices = largestTriangleThreeBuckets(getValues(), Math.ceil(ENVELOPE_POINTS_PER_BUCKET * pyramid.dataLength / bucketSize));
67
+ return [{
68
+ startIndex: 0,
69
+ endIndex: maxIndex,
70
+ indices: mergeBucket(indices, maxIndex)
71
+ }];
72
+ }
73
+ const {
74
+ argMin,
75
+ argMax
76
+ } = pyramid;
77
+ const withEnds = algorithm === 'm4';
78
+ const buckets = [];
79
+ for (let j = start; j < end; j += 1) {
80
+ const bucket = j - start;
81
+ const startIndex = bucket * bucketSize;
82
+ const endIndex = Math.min(startIndex + bucketSize - 1, maxIndex);
83
+ // Extrema in ascending index order; `m4` also brackets them with the bucket's first/last.
84
+ const extremaFirst = Math.min(argMin[j], argMax[j]);
85
+ const extremaLast = Math.max(argMin[j], argMax[j]);
86
+ const candidates = withEnds ? [startIndex, extremaFirst, extremaLast, endIndex] : [extremaFirst, extremaLast];
87
+ buckets.push({
88
+ startIndex,
89
+ endIndex,
90
+ indices: mergeBucket(candidates, endIndex)
91
+ });
92
+ }
93
+ return buckets;
94
+ }
95
+
96
+ /** Shared `sample` for every series type; the per-type build feeds the channels into the pyramid. */
97
+ function sample(context) {
98
+ const {
99
+ zoom
100
+ } = context;
101
+ if (!zoom) {
102
+ return null;
103
+ }
104
+ return sampleBuckets(context.built, zoom.end - zoom.start, context.availableSize, context.minSpan, context.algorithm, context.getValues);
105
+ }
106
+
107
+ /**
108
+ * Largest-Triangle-Three-Buckets downsampling. Returns `threshold` original indices (including the
109
+ * first and last) chosen to preserve the line's visual shape. Uses the data index as the x channel.
110
+ * @see https://skemman.is/handle/1946/15343 (Sveinn Steinarsson, 2013)
111
+ */
112
+ function largestTriangleThreeBuckets(values, threshold) {
113
+ const dataLength = values.length;
114
+ if (threshold >= dataLength) {
115
+ return Int32Array.from({
116
+ length: dataLength
117
+ }, (_, i) => i);
118
+ }
119
+ if (threshold <= 2) {
120
+ // Keep just the endpoints; returning every index here would bypass the rendered-point cap.
121
+ return Int32Array.from([0, dataLength - 1]);
122
+ }
123
+ const sampled = new Int32Array(threshold);
124
+ let sampledIndex = 0;
125
+ sampled[sampledIndex] = 0; // always keep the first point
126
+ sampledIndex += 1;
127
+
128
+ // Middle buckets span indices [1, dataLength - 1); the last point is fixed separately.
129
+ const bucketSize = (dataLength - 2) / (threshold - 2);
130
+ let a = 0; // index of the previously selected point
131
+
132
+ for (let i = 0; i < threshold - 2; i += 1) {
133
+ // Average point of the next bucket (the third triangle vertex).
134
+ const avgStart = Math.floor((i + 1) * bucketSize) + 1;
135
+ let avgEnd = Math.floor((i + 2) * bucketSize) + 1;
136
+ if (avgEnd > dataLength) {
137
+ avgEnd = dataLength;
138
+ }
139
+ let avgX = 0;
140
+ let avgY = 0;
141
+ const avgCount = avgEnd - avgStart;
142
+ for (let k = avgStart; k < avgEnd; k += 1) {
143
+ avgX += k;
144
+ avgY += values[k];
145
+ }
146
+ if (avgCount > 0) {
147
+ avgX /= avgCount;
148
+ avgY /= avgCount;
149
+ } else {
150
+ avgX = dataLength - 1;
151
+ avgY = values[dataLength - 1];
152
+ }
153
+ const rangeStart = Math.floor(i * bucketSize) + 1;
154
+ const rangeEnd = Math.floor((i + 1) * bucketSize) + 1;
155
+ const ax = a;
156
+ const ay = values[a];
157
+ let maxArea = -1;
158
+ let chosen = rangeStart;
159
+ for (let k = rangeStart; k < rangeEnd; k += 1) {
160
+ const area = Math.abs((ax - avgX) * (values[k] - ay) - (ax - k) * (avgY - ay));
161
+ if (area > maxArea) {
162
+ maxArea = area;
163
+ chosen = k;
164
+ }
165
+ }
166
+ sampled[sampledIndex] = chosen;
167
+ sampledIndex += 1;
168
+ a = chosen;
169
+ }
170
+ sampled[sampledIndex] = dataLength - 1; // always keep the last point
171
+ return sampled;
172
+ }