stream-charts 1.2.0 → 2.0.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 (463) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +809 -1022
  3. package/changes.md +40 -0
  4. package/dist/app/charts/Chart.d.ts +35 -139
  5. package/dist/app/charts/Chart.d.ts.map +1 -0
  6. package/dist/app/charts/Chart.js +74 -65
  7. package/dist/app/charts/Chart.js.map +1 -1
  8. package/dist/app/charts/axes/AxesState.d.ts +26 -18
  9. package/dist/app/charts/axes/AxesState.d.ts.map +1 -0
  10. package/dist/app/charts/axes/AxesState.js +26 -24
  11. package/dist/app/charts/axes/AxesState.js.map +1 -1
  12. package/dist/app/charts/axes/AxisInterval.d.ts +44 -26
  13. package/dist/app/charts/axes/AxisInterval.d.ts.map +1 -0
  14. package/dist/app/charts/axes/AxisInterval.js +48 -29
  15. package/dist/app/charts/axes/AxisInterval.js.map +1 -1
  16. package/dist/app/charts/axes/BaseAxisRange.d.ts +46 -22
  17. package/dist/app/charts/axes/BaseAxisRange.d.ts.map +1 -0
  18. package/dist/app/charts/axes/BaseAxisRange.js +17 -12
  19. package/dist/app/charts/axes/BaseAxisRange.js.map +1 -1
  20. package/dist/app/charts/axes/ContinuousAxis.d.ts +47 -6
  21. package/dist/app/charts/axes/ContinuousAxis.d.ts.map +1 -0
  22. package/dist/app/charts/axes/ContinuousAxis.js +160 -27
  23. package/dist/app/charts/axes/ContinuousAxis.js.map +1 -1
  24. package/dist/app/charts/axes/ContinuousAxisRange.d.ts +29 -10
  25. package/dist/app/charts/axes/ContinuousAxisRange.d.ts.map +1 -0
  26. package/dist/app/charts/axes/ContinuousAxisRange.js +29 -11
  27. package/dist/app/charts/axes/ContinuousAxisRange.js.map +1 -1
  28. package/dist/app/charts/axes/EmptyAxis.d.ts +5 -2
  29. package/dist/app/charts/axes/EmptyAxis.d.ts.map +1 -0
  30. package/dist/app/charts/axes/EmptyAxis.js +30 -9
  31. package/dist/app/charts/axes/EmptyAxis.js.map +1 -1
  32. package/dist/app/charts/axes/OrdinalAxis.d.ts +5 -3
  33. package/dist/app/charts/axes/OrdinalAxis.d.ts.map +1 -0
  34. package/dist/app/charts/axes/OrdinalAxis.js +39 -16
  35. package/dist/app/charts/axes/OrdinalAxis.js.map +1 -1
  36. package/dist/app/charts/axes/OrdinalAxisRange.d.ts +76 -13
  37. package/dist/app/charts/axes/OrdinalAxisRange.d.ts.map +1 -0
  38. package/dist/app/charts/axes/OrdinalAxisRange.js +95 -16
  39. package/dist/app/charts/axes/OrdinalAxisRange.js.map +1 -1
  40. package/dist/app/charts/axes/autoScale.d.ts +111 -0
  41. package/dist/app/charts/axes/autoScale.d.ts.map +1 -0
  42. package/dist/app/charts/axes/autoScale.js +188 -0
  43. package/dist/app/charts/axes/autoScale.js.map +1 -0
  44. package/dist/app/charts/axes/axes.d.ts +273 -150
  45. package/dist/app/charts/axes/axes.d.ts.map +1 -0
  46. package/dist/app/charts/axes/axes.js +808 -583
  47. package/dist/app/charts/axes/axes.js.map +1 -1
  48. package/dist/app/charts/d3types.d.ts +40 -11
  49. package/dist/app/charts/d3types.d.ts.map +1 -0
  50. package/dist/app/charts/d3types.js +3 -0
  51. package/dist/app/charts/d3types.js.map +1 -1
  52. package/dist/app/charts/datasources/StreamingDataSource.d.ts +149 -0
  53. package/dist/app/charts/datasources/StreamingDataSource.d.ts.map +1 -0
  54. package/dist/app/charts/datasources/StreamingDataSource.js +110 -0
  55. package/dist/app/charts/datasources/StreamingDataSource.js.map +1 -0
  56. package/dist/app/charts/datasources/iteratesDataSource.d.ts +73 -0
  57. package/dist/app/charts/datasources/iteratesDataSource.d.ts.map +1 -0
  58. package/dist/app/charts/datasources/iteratesDataSource.js +87 -0
  59. package/dist/app/charts/datasources/iteratesDataSource.js.map +1 -0
  60. package/dist/app/charts/datasources/ordinalDataSource.d.ts +75 -0
  61. package/dist/app/charts/datasources/ordinalDataSource.d.ts.map +1 -0
  62. package/dist/app/charts/datasources/ordinalDataSource.js +135 -0
  63. package/dist/app/charts/datasources/ordinalDataSource.js.map +1 -0
  64. package/dist/app/charts/datasources/outlierDataSource.d.ts +53 -0
  65. package/dist/app/charts/datasources/outlierDataSource.d.ts.map +1 -0
  66. package/dist/app/charts/datasources/outlierDataSource.js +57 -0
  67. package/dist/app/charts/datasources/outlierDataSource.js.map +1 -0
  68. package/dist/app/charts/datasources/timeSeriesDataSource.d.ts +77 -0
  69. package/dist/app/charts/datasources/timeSeriesDataSource.d.ts.map +1 -0
  70. package/dist/app/charts/datasources/timeSeriesDataSource.js +85 -0
  71. package/dist/app/charts/datasources/timeSeriesDataSource.js.map +1 -0
  72. package/dist/app/charts/filters/regexFilter.d.ts +31 -4
  73. package/dist/app/charts/filters/regexFilter.d.ts.map +1 -0
  74. package/dist/app/charts/filters/regexFilter.js +38 -4
  75. package/dist/app/charts/filters/regexFilter.js.map +1 -1
  76. package/dist/app/charts/hooks/AxesProvider.d.ts +21 -0
  77. package/dist/app/charts/hooks/AxesProvider.d.ts.map +1 -0
  78. package/dist/app/charts/hooks/AxesProvider.js +213 -0
  79. package/dist/app/charts/hooks/AxesProvider.js.map +1 -0
  80. package/dist/app/charts/hooks/CanvasSurfaceProvider.d.ts +25 -0
  81. package/dist/app/charts/hooks/CanvasSurfaceProvider.d.ts.map +1 -0
  82. package/dist/app/charts/hooks/CanvasSurfaceProvider.js +76 -0
  83. package/dist/app/charts/hooks/CanvasSurfaceProvider.js.map +1 -0
  84. package/dist/app/charts/hooks/ChartProvider.d.ts +40 -0
  85. package/dist/app/charts/hooks/ChartProvider.d.ts.map +1 -0
  86. package/dist/app/charts/hooks/ChartProvider.js +33 -0
  87. package/dist/app/charts/hooks/ChartProvider.js.map +1 -0
  88. package/dist/app/charts/hooks/InteractionProvider.d.ts +23 -0
  89. package/dist/app/charts/hooks/InteractionProvider.d.ts.map +1 -0
  90. package/dist/app/charts/hooks/InteractionProvider.js +60 -0
  91. package/dist/app/charts/hooks/InteractionProvider.js.map +1 -0
  92. package/dist/app/charts/hooks/PlotDimensionsProvider.d.ts +16 -0
  93. package/dist/app/charts/hooks/PlotDimensionsProvider.d.ts.map +1 -0
  94. package/dist/app/charts/hooks/PlotDimensionsProvider.js +76 -0
  95. package/dist/app/charts/hooks/PlotDimensionsProvider.js.map +1 -0
  96. package/dist/app/charts/hooks/defaultAxesValues.d.ts +11 -0
  97. package/dist/app/charts/hooks/defaultAxesValues.d.ts.map +1 -0
  98. package/dist/app/charts/hooks/defaultAxesValues.js +36 -0
  99. package/dist/app/charts/hooks/defaultAxesValues.js.map +1 -0
  100. package/dist/app/charts/hooks/defaultMouseValues.d.ts +9 -0
  101. package/dist/app/charts/hooks/defaultMouseValues.d.ts.map +1 -0
  102. package/dist/app/charts/hooks/defaultMouseValues.js +23 -0
  103. package/dist/app/charts/hooks/defaultMouseValues.js.map +1 -0
  104. package/dist/app/charts/hooks/defaultPlotDimensions.d.ts +13 -0
  105. package/dist/app/charts/hooks/defaultPlotDimensions.d.ts.map +1 -0
  106. package/dist/app/charts/hooks/defaultPlotDimensions.js +23 -0
  107. package/dist/app/charts/hooks/defaultPlotDimensions.js.map +1 -0
  108. package/dist/app/charts/hooks/defaultTooltipValues.d.ts +9 -0
  109. package/dist/app/charts/hooks/defaultTooltipValues.d.ts.map +1 -0
  110. package/dist/app/charts/hooks/defaultTooltipValues.js +21 -0
  111. package/dist/app/charts/hooks/defaultTooltipValues.js.map +1 -0
  112. package/dist/app/charts/hooks/defaultUseChartValues.d.ts +14 -0
  113. package/dist/app/charts/hooks/defaultUseChartValues.d.ts.map +1 -0
  114. package/dist/app/charts/hooks/defaultUseChartValues.js +30 -0
  115. package/dist/app/charts/hooks/defaultUseChartValues.js.map +1 -0
  116. package/dist/app/charts/hooks/useAxes.d.ts +49 -36
  117. package/dist/app/charts/hooks/useAxes.d.ts.map +1 -0
  118. package/dist/app/charts/hooks/useAxes.js +16 -176
  119. package/dist/app/charts/hooks/useAxes.js.map +1 -1
  120. package/dist/app/charts/hooks/useCanvasSurface.d.ts +25 -0
  121. package/dist/app/charts/hooks/useCanvasSurface.d.ts.map +1 -0
  122. package/dist/app/charts/hooks/useCanvasSurface.js +18 -0
  123. package/dist/app/charts/hooks/useCanvasSurface.js.map +1 -0
  124. package/dist/app/charts/hooks/useChart.d.ts +30 -44
  125. package/dist/app/charts/hooks/useChart.d.ts.map +1 -0
  126. package/dist/app/charts/hooks/useChart.js +26 -48
  127. package/dist/app/charts/hooks/useChart.js.map +1 -1
  128. package/dist/app/charts/hooks/useDataSource.d.ts +68 -0
  129. package/dist/app/charts/hooks/useDataSource.d.ts.map +1 -0
  130. package/dist/app/charts/hooks/useDataSource.js +63 -0
  131. package/dist/app/charts/hooks/useDataSource.js.map +1 -0
  132. package/dist/app/charts/hooks/useHoveredSeries.d.ts +25 -0
  133. package/dist/app/charts/hooks/useHoveredSeries.d.ts.map +1 -0
  134. package/dist/app/charts/hooks/useHoveredSeries.js +15 -0
  135. package/dist/app/charts/hooks/useHoveredSeries.js.map +1 -0
  136. package/dist/app/charts/hooks/useMouse.d.ts +46 -22
  137. package/dist/app/charts/hooks/useMouse.d.ts.map +1 -0
  138. package/dist/app/charts/hooks/useMouse.js +12 -56
  139. package/dist/app/charts/hooks/useMouse.js.map +1 -1
  140. package/dist/app/charts/hooks/usePlotDimensions.d.ts +25 -13
  141. package/dist/app/charts/hooks/usePlotDimensions.d.ts.map +1 -0
  142. package/dist/app/charts/hooks/usePlotDimensions.js +6 -62
  143. package/dist/app/charts/hooks/usePlotDimensions.js.map +1 -1
  144. package/dist/app/charts/hooks/useTooltip.d.ts +24 -26
  145. package/dist/app/charts/hooks/useTooltip.d.ts.map +1 -0
  146. package/dist/app/charts/hooks/useTooltip.js +12 -42
  147. package/dist/app/charts/hooks/useTooltip.js.map +1 -1
  148. package/dist/app/charts/index.d.ts +66 -0
  149. package/dist/app/charts/index.d.ts.map +1 -0
  150. package/dist/app/charts/index.js +87 -0
  151. package/dist/app/charts/index.js.map +1 -0
  152. package/dist/app/charts/legends/Legend.d.ts +33 -12
  153. package/dist/app/charts/legends/Legend.d.ts.map +1 -0
  154. package/dist/app/charts/legends/Legend.js +138 -379
  155. package/dist/app/charts/legends/Legend.js.map +1 -1
  156. package/dist/app/charts/legends/constants.d.ts +19 -0
  157. package/dist/app/charts/legends/constants.d.ts.map +1 -0
  158. package/dist/app/charts/legends/constants.js +34 -0
  159. package/dist/app/charts/legends/constants.js.map +1 -0
  160. package/dist/app/charts/observables/ChartData.d.ts +1 -0
  161. package/dist/app/charts/observables/ChartData.d.ts.map +1 -0
  162. package/dist/app/charts/observables/iterates.d.ts +13 -7
  163. package/dist/app/charts/observables/iterates.d.ts.map +1 -0
  164. package/dist/app/charts/observables/iterates.js +42 -9
  165. package/dist/app/charts/observables/iterates.js.map +1 -1
  166. package/dist/app/charts/observables/ordinals.d.ts +70 -12
  167. package/dist/app/charts/observables/ordinals.d.ts.map +1 -0
  168. package/dist/app/charts/observables/ordinals.js +127 -48
  169. package/dist/app/charts/observables/ordinals.js.map +1 -1
  170. package/dist/app/charts/observables/outliers.d.ts +18 -0
  171. package/dist/app/charts/observables/outliers.d.ts.map +1 -0
  172. package/dist/app/charts/observables/outliers.js +2 -0
  173. package/dist/app/charts/observables/outliers.js.map +1 -0
  174. package/dist/app/charts/plots/BarPlot.d.ts +87 -13
  175. package/dist/app/charts/plots/BarPlot.d.ts.map +1 -0
  176. package/dist/app/charts/plots/BarPlot.js +603 -356
  177. package/dist/app/charts/plots/BarPlot.js.map +1 -1
  178. package/dist/app/charts/plots/OutlierPlot.d.ts +123 -0
  179. package/dist/app/charts/plots/OutlierPlot.d.ts.map +1 -0
  180. package/dist/app/charts/plots/OutlierPlot.js +430 -0
  181. package/dist/app/charts/plots/OutlierPlot.js.map +1 -0
  182. package/dist/app/charts/plots/PoincarePlot.d.ts +92 -14
  183. package/dist/app/charts/plots/PoincarePlot.d.ts.map +1 -0
  184. package/dist/app/charts/plots/PoincarePlot.js +523 -364
  185. package/dist/app/charts/plots/PoincarePlot.js.map +1 -1
  186. package/dist/app/charts/plots/RasterPlot.d.ts +31 -10
  187. package/dist/app/charts/plots/RasterPlot.d.ts.map +1 -0
  188. package/dist/app/charts/plots/RasterPlot.js +198 -306
  189. package/dist/app/charts/plots/RasterPlot.js.map +1 -1
  190. package/dist/app/charts/plots/ScatterPlot.d.ts +77 -21
  191. package/dist/app/charts/plots/ScatterPlot.d.ts.map +1 -0
  192. package/dist/app/charts/plots/ScatterPlot.js +359 -302
  193. package/dist/app/charts/plots/ScatterPlot.js.map +1 -1
  194. package/dist/app/charts/plots/autoScaleYAxes.d.ts +43 -0
  195. package/dist/app/charts/plots/autoScaleYAxes.d.ts.map +1 -0
  196. package/dist/app/charts/plots/autoScaleYAxes.js +71 -0
  197. package/dist/app/charts/plots/autoScaleYAxes.js.map +1 -0
  198. package/dist/app/charts/plots/colorizedLine.d.ts +61 -0
  199. package/dist/app/charts/plots/colorizedLine.d.ts.map +1 -0
  200. package/dist/app/charts/plots/colorizedLine.js +157 -0
  201. package/dist/app/charts/plots/colorizedLine.js.map +1 -0
  202. package/dist/app/charts/plots/constants.d.ts +7 -0
  203. package/dist/app/charts/plots/constants.d.ts.map +1 -0
  204. package/dist/app/charts/plots/constants.js +11 -0
  205. package/dist/app/charts/plots/constants.js.map +1 -0
  206. package/dist/app/charts/plots/hitTesting.d.ts +88 -0
  207. package/dist/app/charts/plots/hitTesting.d.ts.map +1 -0
  208. package/dist/app/charts/plots/hitTesting.js +136 -0
  209. package/dist/app/charts/plots/hitTesting.js.map +1 -0
  210. package/dist/app/charts/plots/plot.d.ts +52 -16
  211. package/dist/app/charts/plots/plot.d.ts.map +1 -0
  212. package/dist/app/charts/plots/plot.js +108 -35
  213. package/dist/app/charts/plots/plot.js.map +1 -1
  214. package/dist/app/charts/plots/useCanvasZoomPan.d.ts +119 -0
  215. package/dist/app/charts/plots/useCanvasZoomPan.d.ts.map +1 -0
  216. package/dist/app/charts/plots/useCanvasZoomPan.js +113 -0
  217. package/dist/app/charts/plots/useCanvasZoomPan.js.map +1 -0
  218. package/dist/app/charts/plots/useStreamingTimeAxes.d.ts +103 -0
  219. package/dist/app/charts/plots/useStreamingTimeAxes.d.ts.map +1 -0
  220. package/dist/app/charts/plots/useStreamingTimeAxes.js +257 -0
  221. package/dist/app/charts/plots/useStreamingTimeAxes.js.map +1 -0
  222. package/dist/app/charts/series/baseSeries.d.ts +60 -8
  223. package/dist/app/charts/series/baseSeries.d.ts.map +1 -0
  224. package/dist/app/charts/series/baseSeries.js +53 -8
  225. package/dist/app/charts/series/baseSeries.js.map +1 -1
  226. package/dist/app/charts/series/iterateSeries.d.ts +9 -8
  227. package/dist/app/charts/series/iterateSeries.d.ts.map +1 -0
  228. package/dist/app/charts/series/iterateSeries.js +7 -7
  229. package/dist/app/charts/series/iterateSeries.js.map +1 -1
  230. package/dist/app/charts/series/ordinalSeries.d.ts +27 -13
  231. package/dist/app/charts/series/ordinalSeries.d.ts.map +1 -0
  232. package/dist/app/charts/series/ordinalSeries.js +29 -15
  233. package/dist/app/charts/series/ordinalSeries.js.map +1 -1
  234. package/dist/app/charts/series/outlierSeries.d.ts +142 -0
  235. package/dist/app/charts/series/outlierSeries.d.ts.map +1 -0
  236. package/dist/app/charts/series/outlierSeries.js +82 -0
  237. package/dist/app/charts/series/outlierSeries.js.map +1 -0
  238. package/dist/app/charts/series/timeSeries.d.ts +13 -9
  239. package/dist/app/charts/series/timeSeries.d.ts.map +1 -0
  240. package/dist/app/charts/series/timeSeries.js +6 -3
  241. package/dist/app/charts/series/timeSeries.js.map +1 -1
  242. package/dist/app/charts/series/timeSeriesChartData.d.ts +18 -10
  243. package/dist/app/charts/series/timeSeriesChartData.d.ts.map +1 -0
  244. package/dist/app/charts/series/timeSeriesChartData.js +28 -14
  245. package/dist/app/charts/series/timeSeriesChartData.js.map +1 -1
  246. package/dist/app/charts/styling/barPlotStyle.d.ts +46 -2
  247. package/dist/app/charts/styling/barPlotStyle.d.ts.map +1 -0
  248. package/dist/app/charts/styling/barPlotStyle.js +37 -3
  249. package/dist/app/charts/styling/barPlotStyle.js.map +1 -1
  250. package/dist/app/charts/styling/canvasStyle.d.ts +53 -0
  251. package/dist/app/charts/styling/canvasStyle.d.ts.map +1 -0
  252. package/dist/app/charts/styling/canvasStyle.js +71 -0
  253. package/dist/app/charts/styling/canvasStyle.js.map +1 -0
  254. package/dist/app/charts/styling/margins.d.ts +21 -2
  255. package/dist/app/charts/styling/margins.d.ts.map +1 -0
  256. package/dist/app/charts/styling/margins.js +25 -4
  257. package/dist/app/charts/styling/margins.js.map +1 -1
  258. package/dist/app/charts/styling/svgStyle.d.ts +10 -56
  259. package/dist/app/charts/styling/svgStyle.d.ts.map +1 -0
  260. package/dist/app/charts/styling/svgStyle.js +3 -78
  261. package/dist/app/charts/styling/svgStyle.js.map +1 -1
  262. package/dist/app/charts/subscriptions/viewDrivers.d.ts +124 -0
  263. package/dist/app/charts/subscriptions/viewDrivers.d.ts.map +1 -0
  264. package/dist/app/charts/subscriptions/viewDrivers.js +338 -0
  265. package/dist/app/charts/subscriptions/viewDrivers.js.map +1 -0
  266. package/dist/app/charts/tooltips/BarPlotTooltipContent.d.ts +12 -78
  267. package/dist/app/charts/tooltips/BarPlotTooltipContent.d.ts.map +1 -0
  268. package/dist/app/charts/tooltips/BarPlotTooltipContent.js +65 -257
  269. package/dist/app/charts/tooltips/BarPlotTooltipContent.js.map +1 -1
  270. package/dist/app/charts/tooltips/DefaultOutlierTooltipContent.d.ts +9 -0
  271. package/dist/app/charts/tooltips/DefaultOutlierTooltipContent.d.ts.map +1 -0
  272. package/dist/app/charts/tooltips/DefaultOutlierTooltipContent.js +16 -0
  273. package/dist/app/charts/tooltips/DefaultOutlierTooltipContent.js.map +1 -0
  274. package/dist/app/charts/tooltips/OutlierPlotTooltipContent.d.ts +30 -0
  275. package/dist/app/charts/tooltips/OutlierPlotTooltipContent.d.ts.map +1 -0
  276. package/dist/app/charts/tooltips/OutlierPlotTooltipContent.js +26 -0
  277. package/dist/app/charts/tooltips/OutlierPlotTooltipContent.js.map +1 -0
  278. package/dist/app/charts/tooltips/PoincarePlotTooltipContent.d.ts +22 -72
  279. package/dist/app/charts/tooltips/PoincarePlotTooltipContent.d.ts.map +1 -0
  280. package/dist/app/charts/tooltips/PoincarePlotTooltipContent.js +104 -171
  281. package/dist/app/charts/tooltips/PoincarePlotTooltipContent.js.map +1 -1
  282. package/dist/app/charts/tooltips/RasterPlotTooltipContent.d.ts +32 -8
  283. package/dist/app/charts/tooltips/RasterPlotTooltipContent.d.ts.map +1 -0
  284. package/dist/app/charts/tooltips/RasterPlotTooltipContent.js +62 -115
  285. package/dist/app/charts/tooltips/RasterPlotTooltipContent.js.map +1 -1
  286. package/dist/app/charts/tooltips/ScatterPlotTooltipContent.d.ts +39 -12
  287. package/dist/app/charts/tooltips/ScatterPlotTooltipContent.d.ts.map +1 -0
  288. package/dist/app/charts/tooltips/ScatterPlotTooltipContent.js +162 -134
  289. package/dist/app/charts/tooltips/ScatterPlotTooltipContent.js.map +1 -1
  290. package/dist/app/charts/tooltips/Tooltip.d.ts +27 -32
  291. package/dist/app/charts/tooltips/Tooltip.d.ts.map +1 -0
  292. package/dist/app/charts/tooltips/Tooltip.js +37 -81
  293. package/dist/app/charts/tooltips/Tooltip.js.map +1 -1
  294. package/dist/app/charts/tooltips/TooltipContent.d.ts +81 -0
  295. package/dist/app/charts/tooltips/TooltipContent.d.ts.map +1 -0
  296. package/dist/app/charts/tooltips/TooltipContent.js +99 -0
  297. package/dist/app/charts/tooltips/TooltipContent.js.map +1 -0
  298. package/dist/app/charts/tooltips/tooltipUtils.d.ts +186 -36
  299. package/dist/app/charts/tooltips/tooltipUtils.d.ts.map +1 -0
  300. package/dist/app/charts/tooltips/tooltipUtils.js +277 -48
  301. package/dist/app/charts/tooltips/tooltipUtils.js.map +1 -1
  302. package/dist/app/charts/tooltips/useOutlierTooltip.d.ts +59 -0
  303. package/dist/app/charts/tooltips/useOutlierTooltip.d.ts.map +1 -0
  304. package/dist/app/charts/tooltips/useOutlierTooltip.js +16 -0
  305. package/dist/app/charts/tooltips/useOutlierTooltip.js.map +1 -0
  306. package/dist/app/charts/tooltips/useTooltipPlacement.d.ts +51 -0
  307. package/dist/app/charts/tooltips/useTooltipPlacement.d.ts.map +1 -0
  308. package/dist/app/charts/tooltips/useTooltipPlacement.js +52 -0
  309. package/dist/app/charts/tooltips/useTooltipPlacement.js.map +1 -0
  310. package/dist/app/charts/tooltips/useTooltipStyle.d.ts +11 -0
  311. package/dist/app/charts/tooltips/useTooltipStyle.d.ts.map +1 -0
  312. package/dist/app/charts/tooltips/useTooltipStyle.js +14 -0
  313. package/dist/app/charts/tooltips/useTooltipStyle.js.map +1 -0
  314. package/dist/app/charts/trackers/Tracker.d.ts +22 -6
  315. package/dist/app/charts/trackers/Tracker.d.ts.map +1 -0
  316. package/dist/app/charts/trackers/Tracker.js +36 -22
  317. package/dist/app/charts/trackers/Tracker.js.map +1 -1
  318. package/dist/app/charts/trackers/trackerUtils.d.ts +40 -18
  319. package/dist/app/charts/trackers/trackerUtils.d.ts.map +1 -0
  320. package/dist/app/charts/trackers/trackerUtils.js +213 -300
  321. package/dist/app/charts/trackers/trackerUtils.js.map +1 -1
  322. package/dist/app/charts/utils.d.ts +72 -33
  323. package/dist/app/charts/utils.d.ts.map +1 -0
  324. package/dist/app/charts/utils.js +87 -42
  325. package/dist/app/charts/utils.js.map +1 -1
  326. package/dist/index.d.ts +2 -45
  327. package/dist/index.d.ts.map +1 -0
  328. package/dist/index.js +2 -72
  329. package/dist/index.js.map +1 -1
  330. package/package.json +87 -46
  331. package/src/app/charts/Chart.tsx +132 -168
  332. package/src/app/charts/axes/AxesState.ts +27 -29
  333. package/src/app/charts/axes/AxisInterval.ts +57 -32
  334. package/src/app/charts/axes/BaseAxisRange.ts +47 -27
  335. package/src/app/charts/axes/ContinuousAxis.tsx +235 -39
  336. package/src/app/charts/axes/ContinuousAxisRange.ts +29 -11
  337. package/src/app/charts/axes/EmptyAxis.tsx +52 -17
  338. package/src/app/charts/axes/OrdinalAxis.tsx +54 -28
  339. package/src/app/charts/axes/OrdinalAxisRange.ts +96 -16
  340. package/src/app/charts/axes/autoScale.ts +244 -0
  341. package/src/app/charts/axes/axes.ts +954 -735
  342. package/src/app/charts/d3types.ts +42 -13
  343. package/src/app/charts/datasources/StreamingDataSource.ts +214 -0
  344. package/src/app/charts/datasources/iteratesDataSource.ts +122 -0
  345. package/src/app/charts/datasources/ordinalDataSource.ts +205 -0
  346. package/src/app/charts/datasources/outlierDataSource.ts +90 -0
  347. package/src/app/charts/datasources/timeSeriesDataSource.ts +133 -0
  348. package/src/app/charts/filters/regexFilter.ts +45 -5
  349. package/src/app/charts/hooks/AxesProvider.tsx +301 -0
  350. package/src/app/charts/hooks/CanvasSurfaceProvider.tsx +117 -0
  351. package/src/app/charts/hooks/ChartProvider.tsx +79 -0
  352. package/src/app/charts/hooks/InteractionProvider.tsx +88 -0
  353. package/src/app/charts/hooks/PlotDimensionsProvider.tsx +109 -0
  354. package/src/app/charts/hooks/defaultAxesValues.ts +40 -0
  355. package/src/app/charts/hooks/defaultMouseValues.ts +24 -0
  356. package/src/app/charts/hooks/defaultPlotDimensions.ts +27 -0
  357. package/src/app/charts/hooks/defaultTooltipValues.tsx +23 -0
  358. package/src/app/charts/hooks/defaultUseChartValues.tsx +36 -0
  359. package/src/app/charts/hooks/useAxes.tsx +60 -228
  360. package/src/app/charts/hooks/useCanvasSurface.tsx +35 -0
  361. package/src/app/charts/hooks/useChart.tsx +47 -106
  362. package/src/app/charts/hooks/useDataSource.tsx +106 -0
  363. package/src/app/charts/hooks/useHoveredSeries.tsx +33 -0
  364. package/src/app/charts/hooks/useMouse.tsx +58 -82
  365. package/src/app/charts/hooks/usePlotDimensions.tsx +25 -89
  366. package/src/app/charts/hooks/useTooltip.tsx +32 -63
  367. package/src/app/charts/index.ts +213 -0
  368. package/src/app/charts/legends/Legend.tsx +198 -479
  369. package/src/app/charts/legends/constants.ts +37 -0
  370. package/src/app/charts/observables/iterates.ts +32 -12
  371. package/src/app/charts/observables/ordinals.ts +141 -67
  372. package/src/app/charts/observables/outliers.ts +20 -0
  373. package/src/app/charts/plots/BarPlot.tsx +784 -666
  374. package/src/app/charts/plots/OutlierPlot.tsx +685 -0
  375. package/src/app/charts/plots/PoincarePlot.tsx +707 -489
  376. package/src/app/charts/plots/RasterPlot.tsx +253 -445
  377. package/src/app/charts/plots/ScatterPlot.tsx +480 -463
  378. package/src/app/charts/plots/autoScaleYAxes.ts +102 -0
  379. package/src/app/charts/plots/colorizedLine.ts +185 -0
  380. package/src/app/charts/plots/constants.ts +16 -0
  381. package/src/app/charts/plots/hitTesting.ts +198 -0
  382. package/src/app/charts/plots/plot.ts +138 -44
  383. package/src/app/charts/plots/useCanvasZoomPan.ts +240 -0
  384. package/src/app/charts/plots/useStreamingTimeAxes.ts +384 -0
  385. package/src/app/charts/series/baseSeries.ts +81 -14
  386. package/src/app/charts/series/iterateSeries.ts +8 -8
  387. package/src/app/charts/series/ordinalSeries.ts +35 -20
  388. package/src/app/charts/series/outlierSeries.ts +188 -0
  389. package/src/app/charts/series/timeSeries.ts +14 -12
  390. package/src/app/charts/series/timeSeriesChartData.ts +31 -17
  391. package/src/app/charts/styling/barPlotStyle.ts +48 -5
  392. package/src/app/charts/styling/canvasStyle.ts +86 -0
  393. package/src/app/charts/styling/margins.ts +28 -4
  394. package/src/app/charts/styling/svgStyle.ts +10 -91
  395. package/src/app/charts/subscriptions/viewDrivers.ts +447 -0
  396. package/src/app/charts/tooltips/BarPlotTooltipContent.tsx +156 -380
  397. package/src/app/charts/tooltips/DefaultOutlierTooltipContent.tsx +40 -0
  398. package/src/app/charts/tooltips/OutlierPlotTooltipContent.tsx +67 -0
  399. package/src/app/charts/tooltips/PoincarePlotTooltipContent.tsx +229 -292
  400. package/src/app/charts/tooltips/RasterPlotTooltipContent.tsx +164 -217
  401. package/src/app/charts/tooltips/ScatterPlotTooltipContent.tsx +373 -236
  402. package/src/app/charts/tooltips/Tooltip.tsx +58 -124
  403. package/src/app/charts/tooltips/TooltipContent.tsx +177 -0
  404. package/src/app/charts/tooltips/tooltipUtils.ts +359 -61
  405. package/src/app/charts/tooltips/useOutlierTooltip.tsx +68 -0
  406. package/src/app/charts/tooltips/useTooltipPlacement.ts +86 -0
  407. package/src/app/charts/tooltips/useTooltipStyle.ts +15 -0
  408. package/src/app/charts/trackers/Tracker.tsx +68 -33
  409. package/src/app/charts/trackers/trackerUtils.ts +263 -376
  410. package/src/app/charts/utils.ts +90 -48
  411. package/src/index.ts +2 -322
  412. package/dist/app/charts/axes/AxesState.test.d.ts +0 -1
  413. package/dist/app/charts/axes/AxesState.test.js +0 -30
  414. package/dist/app/charts/axes/AxesState.test.js.map +0 -1
  415. package/dist/app/charts/axes/AxisInterval.test.d.ts +0 -1
  416. package/dist/app/charts/axes/AxisInterval.test.js +0 -120
  417. package/dist/app/charts/axes/AxisInterval.test.js.map +0 -1
  418. package/dist/app/charts/axes/ContinuousAxisRange.test.d.ts +0 -1
  419. package/dist/app/charts/axes/ContinuousAxisRange.test.js +0 -47
  420. package/dist/app/charts/axes/ContinuousAxisRange.test.js.map +0 -1
  421. package/dist/app/charts/axes/OrdinalAxisRange.test.d.ts +0 -1
  422. package/dist/app/charts/axes/OrdinalAxisRange.test.js +0 -48
  423. package/dist/app/charts/axes/OrdinalAxisRange.test.js.map +0 -1
  424. package/dist/app/charts/filters/regexFilter.test.d.ts +0 -1
  425. package/dist/app/charts/filters/regexFilter.test.js +0 -11
  426. package/dist/app/charts/filters/regexFilter.test.js.map +0 -1
  427. package/dist/app/charts/hooks/useDataObservable.d.ts +0 -72
  428. package/dist/app/charts/hooks/useDataObservable.js +0 -74
  429. package/dist/app/charts/hooks/useDataObservable.js.map +0 -1
  430. package/dist/app/charts/hooks/useInitialData.d.ts +0 -36
  431. package/dist/app/charts/hooks/useInitialData.js +0 -28
  432. package/dist/app/charts/hooks/useInitialData.js.map +0 -1
  433. package/dist/app/charts/observables/iterates.test.d.ts +0 -1
  434. package/dist/app/charts/observables/iterates.test.js +0 -180
  435. package/dist/app/charts/observables/iterates.test.js.map +0 -1
  436. package/dist/app/charts/observables/ordinals.test.d.ts +0 -1
  437. package/dist/app/charts/observables/ordinals.test.js +0 -102
  438. package/dist/app/charts/observables/ordinals.test.js.map +0 -1
  439. package/dist/app/charts/series/datumSeries.test.d.ts +0 -1
  440. package/dist/app/charts/series/datumSeries.test.js +0 -50
  441. package/dist/app/charts/series/datumSeries.test.js.map +0 -1
  442. package/dist/app/charts/styling/margins.test.d.ts +0 -1
  443. package/dist/app/charts/styling/margins.test.js +0 -19
  444. package/dist/app/charts/styling/margins.test.js.map +0 -1
  445. package/dist/app/charts/subscriptions/subscription.test.d.ts +0 -1
  446. package/dist/app/charts/subscriptions/subscription.test.js +0 -395
  447. package/dist/app/charts/subscriptions/subscription.test.js.map +0 -1
  448. package/dist/app/charts/subscriptions/subscriptions.d.ts +0 -100
  449. package/dist/app/charts/subscriptions/subscriptions.js +0 -449
  450. package/dist/app/charts/subscriptions/subscriptions.js.map +0 -1
  451. package/src/app/charts/axes/AxesState.test.ts +0 -34
  452. package/src/app/charts/axes/AxisInterval.test.ts +0 -143
  453. package/src/app/charts/axes/ContinuousAxisRange.test.ts +0 -55
  454. package/src/app/charts/axes/OrdinalAxisRange.test.ts +0 -55
  455. package/src/app/charts/filters/regexFilter.test.ts +0 -13
  456. package/src/app/charts/hooks/useDataObservable.tsx +0 -170
  457. package/src/app/charts/hooks/useInitialData.tsx +0 -64
  458. package/src/app/charts/observables/iterates.test.ts +0 -201
  459. package/src/app/charts/observables/ordinals.test.ts +0 -135
  460. package/src/app/charts/series/datumSeries.test.ts +0 -60
  461. package/src/app/charts/styling/margins.test.ts +0 -22
  462. package/src/app/charts/subscriptions/subscription.test.ts +0 -499
  463. package/src/app/charts/subscriptions/subscriptions.ts +0 -635
@@ -1,40 +1,54 @@
1
- import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useMemo, useRef } from 'react';
1
+ import { useCallback, useEffect, useEffectEvent, useMemo, useRef } from 'react';
2
+ import { useCanvasZoomPan } from "./useCanvasZoomPan";
3
3
  import { useChart } from "../hooks/useChart";
4
4
  import * as d3 from "d3";
5
- import { setClipPathG } from "./plot";
6
- import { axesZoomHandler, defaultLineStyle, panHandler2D } from "../axes/axes";
7
- import { formatTime, noop, textDimensions } from "../utils";
8
- import { subscriptionIteratesFor } from "../subscriptions/subscriptions";
9
- import { useDataObservable } from "../hooks/useDataObservable";
5
+ import { clipToArea } from "./plot";
6
+ import { axesZoomHandler, AxisType, checkAxisType, defaultLineStyle, panHandler2D } from "../axes/axes";
7
+ import { canvasLocalPoint, seriesAt } from "./hitTesting";
8
+ import { fontStringFor, formatTime, noop, textDimensions } from "../utils";
9
+ import { iteratesViewDriverFor } from "../subscriptions/viewDrivers";
10
+ import { useDataSource, useDataSourceRunning, useInitialData, useStreamingSettings } from "../hooks/useDataSource";
10
11
  import { usePlotDimensions } from "../hooks/usePlotDimensions";
11
- import { useInitialData } from "../hooks/useInitialData";
12
12
  import { useTooltip } from "../hooks/useTooltip";
13
13
  import { ContinuousAxisRange } from "../axes/ContinuousAxisRange";
14
+ import { AxisInterval } from "../axes/AxisInterval";
15
+ import { warnAboutIgnoredAutoScale } from "../axes/autoScale";
16
+ import { FastShiftArray } from "fast-shift-array";
17
+ import { DEFAULT_LINE_COLOR_STEPS, DEFAULT_LINE_END_COLOR, DEFAULT_LINE_START_COLOR, pointColorScale, strokeColorizedLine } from "./colorizedLine";
18
+ /**
19
+ * Creates an axis range (with its original range the same as its current range) from each axis'
20
+ * current domain
21
+ * @param axes The axes (continuous numeric ones), by their IDs
22
+ * @return The axes' ranges, by their IDs
23
+ */
14
24
  function generateAxisRangeMap(axes) {
15
25
  return new Map(Array.from(axes.entries()).map(([id, axis]) => {
16
26
  const [start, end] = axis.scale.domain();
17
27
  return [id, ContinuousAxisRange.from(start, end)];
18
28
  }));
19
29
  }
20
- // A sentinel that represents that no curve factory is to be used, which means
21
- // that no line will be drawn between the iterates (kinda gross)
22
- export const NoCurveFactory = undefined;
23
30
  /**
24
- * Renders a streaming Poincare (iterates) plot for the series in the initial data and those sourced by the
25
- * observable specified as a property in the {@link Chart}. This component uses the {@link useChart}
31
+ * Renders a streaming Poincare (iterates) plot for the series held by the {@link StreamingDataSource}
32
+ * handed to the {@link Chart} (its `dataSource` prop). This component uses the {@link useChart}
26
33
  * hook, and therefore must be a child of the {@link Chart} in order to be plugged in to the
27
34
  * chart ecosystem (axes, tracker, tooltip).
28
35
  *
29
- * @param props The properties associated with the scatter plot
36
+ * Internally, this no longer creates/updates SVG `<path>`/`<circle>`/`<text>` elements. Instead, it
37
+ * registers a single draw function with the chart's {@link CanvasContext} that redraws every
38
+ * series' line and points from scratch each time the canvas repaints. The old version's
39
+ * hover-a-point-and-annotate-its-neighbors behavior used direct `d3.select('#specific-id')` lookups
40
+ * into already-rendered SVG elements from a separate pass after the main render; since canvas has
41
+ * no persistent elements to reach back into, this is restructured so the single draw pass checks
42
+ * "is this point hovered, or a neighbor of the hovered point" and renders the highlight/label
43
+ * inline, driven by a `hoveredPointRef` updated on `mousemove`.
44
+ *
45
+ * @param props The properties associated with the Poincaré plot
46
+ * @return `null` (the plot draws onto the chart's canvas)
30
47
  * @example
31
48
  * ```typescript
32
- * <ScatterPlot
49
+ * <PoincarePlot
33
50
  * interpolation={interpolation}
34
- * axisAssignments={new Map([
35
- * ['test2', assignAxes("x-axis-2", "y-axis-2")],
36
- * ])}
37
- * dropDataAfter={10000}
51
+ * showPoints={true}
38
52
  * panEnabled={true}
39
53
  * zoomEnabled={true}
40
54
  * zoomKeyModifiersRequired={true}
@@ -42,46 +56,315 @@ export const NoCurveFactory = undefined;
42
56
  * ```
43
57
  */
44
58
  export function PoincarePlot(props) {
45
- const { chartId, container, mainG, axes, backgroundColor, seriesStyles, seriesFilter, mouse } = useChart();
59
+ var _a;
60
+ const { chartId, canvasContext, axes, backgroundColor, seriesStyles, seriesFilter, mouse } = useChart();
46
61
  const { xAxesState, yAxesState, setAxisIntervalFor, updateAxisRanges = noop, axisRangeFor, addAxesRangesUpdateHandler, removeAxesRangesUpdateHandler, } = axes;
62
+ // this plot doesn't auto-scale its y-axes, so warn about any that ask to be (see
63
+ // `ContinuousAxis`'s `autoScale` prop)
64
+ useEffect(() => warnAboutIgnoredAutoScale("PoincarePlot", yAxesState.axes), [yAxesState]);
47
65
  const { mouseOverHandlerFor, mouseLeaveHandlerFor } = mouse;
48
66
  const { plotDimensions, margin, } = usePlotDimensions();
49
- const { seriesObservable, windowingTime = 100, shouldSubscribe, onSubscribe = noop, onUpdateData, onUpdateChartTime = noop, } = useDataObservable();
67
+ const { windowingTime = 100, onUpdateChartTime = noop, } = useStreamingSettings();
68
+ // the application-owned source of the data, and whether it's running (streaming)
69
+ const dataSource = useDataSource();
70
+ const running = useDataSourceRunning(dataSource);
50
71
  const { initialData } = useInitialData();
51
72
  const { visibilityState: tooltipVisible } = useTooltip();
52
- const { interpolation, showPoints = true, dropDataAfter = 1000, panEnabled = false, zoomEnabled = false, zoomKeyModifiersRequired = true, zoomMinScaleFactor = 0, zoomMaxScaleFactor = 1, } = props;
73
+ const { interpolation, showPoints = true, panEnabled = false, zoomEnabled = false, zoomKeyModifiersRequired = true, zoomMinScaleFactor = 0, zoomMaxScaleFactor = 1, highlightAxesOnMouseOver = false, colorizeLines = false, lineStartColor = DEFAULT_LINE_START_COLOR, lineEndColor = DEFAULT_LINE_END_COLOR, lineColorSteps = DEFAULT_LINE_COLOR_STEPS, zoomState = undefined, onZoomStateChange = noop, onZoomReset = noop, } = props;
53
74
  // why do "dataRef" and "seriesRef" both hold on to the same underlying data? for performance.
54
75
  //
55
76
  // the "dataRef" and "seriesRef" both point to the same underlying data, a collection
56
- // of series. The series in "dataRef" are bound to the DOM elements through d3. The "seriesRef" series
57
- // are the ones that are updated as new data is streamed in.
77
+ // of series (held, and updated as new data is streamed in, by the data source). The series in
78
+ // "dataRef" are read by the canvas draw function, and the "seriesRef" map is for looking a
79
+ // series up by its name.
58
80
  //
59
81
  // the "dataRef" object holds on to a copy of the initial data (which is an array of
60
- // time-series, e.i. an array of BaseSeries<OrdinalDatum> objects). The slice just creates a copy of
82
+ // iterate series, e.i. an array of BaseSeries<IterateDatum> objects). The slice just creates a copy of
61
83
  // the array, but the references to the BaseSeries objects are the same and still point to the same
62
84
  // data as the "initialData" array.
63
85
  //
64
- // the "seriesRef" object is a reference to a map (series_name -> BaseSeries<OrdinalDatum>) which is
65
- // used to update the data in the series. When new data enters, it is appended to one or more series.
86
+ // the "seriesRef" object is a reference to a map (series_name -> BaseSeries<IterateDatum>), used
87
+ // to find the series under the mouse (for the tooltip).
66
88
  //
67
- // the series in the "dataRef" object are the ones bound to the DOM elements in d3, and so as these
68
- // are updated, d3 will update the DOM elements (the elements in this plot).
89
+ // the series in the "dataRef" object are the ones read by the next redraw, so as these are
90
+ // updated, the next canvas repaint picks up the new data.
69
91
  const dataRef = useRef(initialData.slice());
70
92
  const seriesRef = useRef(new Map(initialData.map(series => [series.name, series])));
71
93
  // map(axis_id -> current_time) -- maps the axis ID to the current time for that axis
72
94
  const currentTimeRef = useRef(0);
73
95
  const xAxisRangesRef = useRef(new Map());
74
96
  const yAxisRangesRef = useRef(new Map());
75
- const subscriptionRef = useRef(undefined);
76
- const isSubscriptionClosed = () => subscriptionRef.current === undefined || subscriptionRef.current.closed;
77
- const allowTooltip = useRef(isSubscriptionClosed());
78
- // so that we can reset the zoom when the axes-bounds change, we hold on to the zoom-behaviour
79
- // and the zoom-selection so that we can reset the transform to the identity
80
- const zoomRef = useRef(undefined);
81
- const zoomSelectionRef = useRef(undefined);
82
- // calculates the distinct axis IDs that cover all the series in the plot
83
- const xAxesForSeries = useMemo(() => xAxesState.axisIds(), [xAxesState]);
84
- const yAxesForSeries = useMemo(() => yAxesState.axisIds(), [yAxesState]);
97
+ // tooltips are only shown while the data source is paused (and not while panning)
98
+ const allowTooltip = useRef(!((_a = dataSource === null || dataSource === void 0 ? void 0 : dataSource.running) !== null && _a !== void 0 ? _a : false));
99
+ // the last-drawn geometry for each series' points, in canvas coordinates, used for
100
+ // hit-testing mouse hover on `mousemove` (see the effect below that wires up the listener).
101
+ // Also keeps the corresponding `IteratePointSeries` so the hovered/neighbor points' data can
102
+ // be looked up by index.
103
+ const geometryRef = useRef(new Map());
104
+ const plotDataRef = useRef(new Map());
105
+ // which point (series + index) is currently hovered, so the draw pass can highlight it and
106
+ // its neighbors, and so we know when to fire a "leave"
107
+ const hoveredPointRef = useRef(undefined);
108
+ const updatePlot = useCallback(
109
+ /**
110
+ * (Re-)registers this plot's draw function with the canvas context and requests a redraw.
111
+ *
112
+ * Pan/zoom behavior setup lives in a separate effect (see below), not here -- this
113
+ * function runs on every data tick, and recreating/reattaching a `d3.drag()`/`d3.zoom()`
114
+ * behavior that often is pure overhead unrelated to drawing the new data.
115
+ * @param cc The canvas context to register the draw function with
116
+ */
117
+ (cc) => {
118
+ onUpdateChartTime(currentTimeRef.current);
119
+ // the colorized lines' color at a point, by the point's overall sequence number in its
120
+ // series (see `colorizeLines`)
121
+ const lineColorOf = pointColorScale(lineStartColor, lineEndColor, lineColorSteps);
122
+ const draw = (context) => {
123
+ const { context2D } = context;
124
+ // create a map associating series-names to their time-series.
125
+ const boundedSeries = new Map(dataRef.current.map(series => {
126
+ return [
127
+ series.name,
128
+ series.data
129
+ .filter(datum => !isNaN(datum.iterateN))
130
+ .map((datum, index) => ({
131
+ n: datum.iterateN,
132
+ n_1: datum.iterateN_1,
133
+ time: datum.time,
134
+ index: index
135
+ }))
136
+ ];
137
+ }));
138
+ context2D.save();
139
+ clipToArea(context, plotDimensions, { x: margin.left, y: margin.top });
140
+ context2D.translate(margin.left, margin.top);
141
+ // ---
142
+ // todo only want to do this once, on the first plot, and then leave it,
143
+ // unless the axes are updated, also needs to be removed/added when the
144
+ // plot size changes
145
+ // (the same, checked axes as the series' below: the default x- and y-axes)
146
+ const [xAxis, yAxis] = axesFor(axisId => xAxesState.axisFor(axisId).getOrUndefined(), axisId => yAxesState.axisFor(axisId).getOrUndefined());
147
+ if (xAxis === undefined || yAxis === undefined)
148
+ throw new Error('No default axis found');
149
+ // ---
150
+ const [xStart, xEnd] = xAxesState.axisDefaultId()
151
+ .map(id => { var _a; return ((_a = xAxisRangesRef.current.get(id)) === null || _a === void 0 ? void 0 : _a.original.asTuple()) || [0, 0]; })
152
+ .getOrElse([0, 0]);
153
+ const [yStart, yEnd] = yAxesState.axisDefaultId()
154
+ .map(id => { var _a; return ((_a = yAxisRangesRef.current.get(id)) === null || _a === void 0 ? void 0 : _a.original.asTuple()) || [0, 0]; })
155
+ .getOrElse([0, 0]);
156
+ // the fn = fn+1 diagonal reference line (no hover behavior, matching the old
157
+ // version's explicitly no-op mouseenter/mouseleave handlers)
158
+ context2D.strokeStyle = "grey";
159
+ context2D.lineWidth = 1;
160
+ context2D.beginPath();
161
+ context2D.moveTo(xAxis.scale(xStart), yAxis.scale(yStart));
162
+ context2D.lineTo(xAxis.scale(xEnd), yAxis.scale(yEnd));
163
+ context2D.stroke();
164
+ const newGeometry = new Map();
165
+ const newPlotDataBySeries = new Map();
166
+ const hovered = hoveredPointRef.current;
167
+ boundedSeries.forEach((data, name) => {
168
+ var _a;
169
+ // grab the x and y axes assigned to the series, and if either or both
170
+ // axes aren't found, then give up and return
171
+ const [xAxisLinear, yAxisLinear] = axesFor(axisId => xAxesState.axisFor(axisId).getOrUndefined(), axisId => yAxesState.axisFor(axisId).getOrUndefined());
172
+ if (xAxisLinear === undefined || yAxisLinear === undefined)
173
+ return;
174
+ // grab the style for the series
175
+ const seriesLineStyle = seriesStyles.get(name) || {
176
+ ...defaultLineStyle(),
177
+ highlightColor: defaultLineStyle().color
178
+ };
179
+ // only show the data for which the filter matches
180
+ const plotData = (name.match(seriesFilter)) ? data : FastShiftArray.empty();
181
+ newPlotDataBySeries.set(name, plotData);
182
+ // create the time-series path (when no interpolation is given, don't draw a
183
+ // connecting line at all -- matches the old version's `mainGElem.selectAll(...).remove()`)
184
+ // when colorizing the lines, a point's overall sequence number in its series is
185
+ // the number of points dropped from the series so far (when the data source
186
+ // counts them) plus its index -- the line leaving a point, and the point itself,
187
+ // are colored by it
188
+ const dropped = colorizeLines ? droppedCountFor(dataSource, name) : 0;
189
+ if (interpolation !== undefined) {
190
+ const pathGenerator = d3.line()
191
+ .x(d => xAxis.scale(d.n || 0))
192
+ .y(d => yAxis.scale(d.n_1 || 0))
193
+ .curve(interpolation);
194
+ context2D.lineWidth = seriesLineStyle.lineWidth;
195
+ if (colorizeLines) {
196
+ // the line's color changes along it, one step per point
197
+ const linePoints = Array.from(plotData);
198
+ strokeColorizedLine(context2D, linePoints, d => xAxis.scale(d.n || 0), d => yAxis.scale(d.n_1 || 0), interpolation, index => lineColorOf(dropped + linePoints[index].index));
199
+ }
200
+ else {
201
+ context2D.strokeStyle = seriesLineStyle.color;
202
+ context2D.stroke(new Path2D((_a = pathGenerator(Array.from(plotData))) !== null && _a !== void 0 ? _a : ""));
203
+ }
204
+ }
205
+ // when specified, show a circle for the actual data point
206
+ const points = [];
207
+ if (showPoints) {
208
+ const isThisSeriesHovered = (hovered === null || hovered === void 0 ? void 0 : hovered.seriesName) === name;
209
+ plotData.forEach((d) => {
210
+ const x = xAxisLinear.scale(d.n) || 0;
211
+ const y = yAxisLinear.scale(d.n_1) || 0;
212
+ points.push([x + margin.left, y + margin.top]);
213
+ const isHoveredPoint = isThisSeriesHovered && hovered.index === d.index;
214
+ const isNeighborOfHovered = isThisSeriesHovered &&
215
+ (hovered.index === d.index - 1 || hovered.index === d.index + 1);
216
+ if (isHoveredPoint) {
217
+ // the hovered point itself: enlarged, highlight-colored, no label
218
+ // (matches the old version, which only labeled the *neighbors*)
219
+ context2D.fillStyle = seriesLineStyle.highlightColor;
220
+ context2D.beginPath();
221
+ context2D.arc(x, y, 5, 0, 2 * Math.PI);
222
+ context2D.fill();
223
+ }
224
+ else if (isNeighborOfHovered) {
225
+ // a neighbor of the hovered point: enlarged, brighter fill, stroked,
226
+ // with a floating "n = i; t = X ms" label above it
227
+ context2D.fillStyle = d3.rgb(seriesLineStyle.highlightColor).brighter(0.7).toString();
228
+ context2D.strokeStyle = seriesLineStyle.color;
229
+ context2D.lineWidth = seriesLineStyle.lineWidth;
230
+ context2D.beginPath();
231
+ context2D.arc(x, y, 5, 0, 2 * Math.PI);
232
+ context2D.fill();
233
+ context2D.stroke();
234
+ const label = `n = ${d.index}; t = ${formatTime(d.time)} ms`;
235
+ context2D.font = fontStringFor(11, 'sans-serif', 700);
236
+ const { width, height } = textDimensions(context2D, label);
237
+ const padding = 4;
238
+ const circleRadius = 5;
239
+ const circleStroke = seriesLineStyle.lineWidth;
240
+ context2D.fillStyle = backgroundColor;
241
+ context2D.fillRect(x - padding / 2 - 8, y - padding / 2 - circleRadius - circleStroke - height, width + padding, height + padding / 2);
242
+ context2D.fillStyle = seriesLineStyle.highlightColor;
243
+ context2D.textAlign = 'left';
244
+ context2D.textBaseline = 'alphabetic';
245
+ context2D.fillText(label, x - 8, y - circleRadius - circleStroke - padding);
246
+ }
247
+ else {
248
+ // normal, unhovered point (in the color of the line leaving it, when
249
+ // the lines are colorized)
250
+ context2D.fillStyle = colorizeLines ?
251
+ lineColorOf(dropped + d.index) :
252
+ seriesLineStyle.color;
253
+ context2D.beginPath();
254
+ context2D.arc(x, y, 2, 0, 2 * Math.PI);
255
+ context2D.fill();
256
+ }
257
+ });
258
+ }
259
+ newGeometry.set(`${name}::points`, { points, hitRadius: 6 });
260
+ });
261
+ geometryRef.current = newGeometry;
262
+ plotDataRef.current = newPlotDataBySeries;
263
+ context2D.restore();
264
+ };
265
+ cc.register(`poincare-plot-${chartId}`, draw, 10);
266
+ cc.requestRedraw();
267
+ }, [
268
+ chartId, onUpdateChartTime, plotDimensions, xAxesState, yAxesState, seriesStyles, seriesFilter,
269
+ showPoints, interpolation, backgroundColor, colorizeLines, lineStartColor, lineEndColor,
270
+ lineColorSteps, dataSource, margin.left, margin.top
271
+ ]);
272
+ // need to keep the function references for use by the subscription, which forms a closure
273
+ // on them. without the references, the closures become stale, and resizing during streaming
274
+ // doesn't work properly
275
+ const updatePlotRef = useRef(updatePlot);
276
+ useEffect(() => {
277
+ updatePlotRef.current = updatePlot;
278
+ }, [updatePlot]);
279
+ // hands the current zoom/pan state (d3-zoom's cumulative `k`, and every axis' current domain) to
280
+ // the `onZoomStateChange` callback
281
+ const onZoomStateChangeRef = useRef(onZoomStateChange);
282
+ useEffect(() => {
283
+ onZoomStateChangeRef.current = onZoomStateChange;
284
+ }, [onZoomStateChange]);
285
+ const reportZoomState = useCallback(() => {
286
+ const ranges = {};
287
+ xAxisRangesRef.current.forEach((range, axisId) => ranges[axisId] = range.current.asTuple());
288
+ yAxisRangesRef.current.forEach((range, axisId) => ranges[axisId] = range.current.asTuple());
289
+ const k = canvasContext !== null ? d3.zoomTransform(canvasContext.canvas).k : 1;
290
+ onZoomStateChangeRef.current({ k, ranges });
291
+ }, [canvasContext]);
292
+ // panning (dragging) and zooming (the mouse wheel) -- see `useCanvasZoomPan`. Fully zooming out
293
+ // (back to a scale of 1) snaps the axes back to their original ranges.
294
+ const { resetZoomTransform, setZoomScale } = useCanvasZoomPan(canvasContext, {
295
+ panEnabled,
296
+ zoomEnabled,
297
+ keyModifiersRequired: zoomKeyModifiersRequired,
298
+ scaleExtent: [zoomMinScaleFactor, zoomMaxScaleFactor],
299
+ translateExtent: [[margin.left, margin.top], [plotDimensions.width, plotDimensions.height]],
300
+ // (tooltips are suppressed while panning and zooming, so that one isn't left behind)
301
+ onPanStart: () => {
302
+ allowTooltip.current = false;
303
+ },
304
+ onPan: (dx, dy) => {
305
+ if (canvasContext === null)
306
+ return;
307
+ onPan(dx, dy, plotDimensions, dataRef.current.map(series => series.name), xAxisRangesRef.current, yAxisRangesRef.current);
308
+ updatePlotRef.current(canvasContext);
309
+ },
310
+ onPanEnd: () => {
311
+ var _a;
312
+ allowTooltip.current = !((_a = dataSource === null || dataSource === void 0 ? void 0 : dataSource.running) !== null && _a !== void 0 ? _a : false);
313
+ reportZoomState();
314
+ },
315
+ onZoom: ({ factor, k, x, y }) => {
316
+ if (canvasContext === null)
317
+ return;
318
+ allowTooltip.current = false;
319
+ if (k === 1) {
320
+ snapToOriginalRanges();
321
+ }
322
+ else {
323
+ onZoom(factor, x - margin.left, y - margin.top, plotDimensions, xAxisRangesRef.current, yAxisRangesRef.current);
324
+ }
325
+ updatePlotRef.current(canvasContext);
326
+ reportZoomState();
327
+ allowTooltip.current = true;
328
+ },
329
+ onZoomReset,
330
+ resetView: () => {
331
+ snapToOriginalRanges();
332
+ if (canvasContext !== null)
333
+ updatePlotRef.current(canvasContext);
334
+ reportZoomState();
335
+ },
336
+ });
337
+ // the zoom state to restore, captured once at mount (see the `zoomState` prop); cleared once
338
+ // it has been applied (see `restoreZoomState` below), so it's only ever applied once
339
+ const pendingZoomStateRef = useRef(zoomState);
340
+ // applies the pending zoom state (if any) to the axes' ranges and to d3-zoom's transform on the
341
+ // canvas (a remounted plot has a brand-new canvas, whose transform starts at identity). Each
342
+ // range keeps its `.original` -- the axis' real, un-zoomed domain -- since the zoom handler's
343
+ // constraint is measured against it. Waits until the axes' ranges and the canvas exist.
344
+ const restoreZoomState = useEffectEvent(() => {
345
+ const pending = pendingZoomStateRef.current;
346
+ if (pending === undefined || canvasContext === null)
347
+ return;
348
+ if (xAxisRangesRef.current.size === 0 || yAxisRangesRef.current.size === 0)
349
+ return;
350
+ Object.entries(pending.ranges).forEach(([axisId, [start, end]]) => {
351
+ var _a;
352
+ const interval = AxisInterval.from(start, end);
353
+ const ranges = xAxisRangesRef.current.has(axisId) ? xAxisRangesRef.current : yAxisRangesRef.current;
354
+ const range = ranges.get(axisId);
355
+ const axis = (_a = xAxesState.axisFor(axisId).getOrUndefined()) !== null && _a !== void 0 ? _a : yAxesState.axisFor(axisId).getOrUndefined();
356
+ if (range === undefined || axis === undefined)
357
+ return;
358
+ ranges.set(axisId, range.update(start, end));
359
+ setAxisIntervalFor(axisId, interval);
360
+ axis.update(interval, plotDimensions, margin);
361
+ });
362
+ // `__zoom` is where d3-zoom keeps an element's transform; setting it directly (rather than
363
+ // through `zoom.transform`) avoids dispatching a synthetic "zoom" event
364
+ setZoomScale(pending.k);
365
+ pendingZoomStateRef.current = undefined;
366
+ updatePlotRef.current(canvasContext);
367
+ });
85
368
  // when the axes are available, then set the reference, but only once
86
369
  useEffect(() => {
87
370
  if (xAxesState.axes.size > 0 && xAxisRangesRef.current.size === 0) {
@@ -90,16 +373,22 @@ export function PoincarePlot(props) {
90
373
  if (yAxesState.axes.size > 0 && yAxisRangesRef.current.size === 0) {
91
374
  yAxisRangesRef.current = generateAxisRangeMap(yAxesState.axes);
92
375
  }
93
- }, [xAxesState, yAxesState]);
376
+ // restore a zoom state handed in from a previous instance (only does anything the first
377
+ // time the axes and canvas are all available -- see `restoreZoomState`)
378
+ restoreZoomState();
379
+ }, [xAxesState, yAxesState, canvasContext]);
380
+ // calculates the distinct axis IDs that cover all the series in the plot
381
+ const xAxesForSeries = useMemo(() => xAxesState.axisIds(), [xAxesState]);
382
+ const yAxesForSeries = useMemo(() => yAxesState.axisIds(), [yAxesState]);
94
383
  // update the plot with the new axes bounds
95
384
  const updateRangesAndPlot = useCallback(() => {
96
- if (mainG !== null) {
97
- updatePlotRef.current(mainG);
385
+ if (canvasContext !== null) {
386
+ updatePlotRef.current(canvasContext);
98
387
  }
99
- }, [mainG]);
388
+ }, [canvasContext]);
100
389
  // todo find better way
101
390
  // when the initial data changes, then reset the plot. note that the initial data doesn't change
102
- // during the normal course of updates from the observable, only when the plot is restarted.
391
+ // during the normal course of updates from the data source, only when the data source is replaced.
103
392
  useEffect(() => {
104
393
  dataRef.current = initialData.slice();
105
394
  seriesRef.current = new Map(initialData.map(series => [series.name, series]));
@@ -107,23 +396,33 @@ export function PoincarePlot(props) {
107
396
  updateRangesAndPlot();
108
397
  }, [initialData, updateRangesAndPlot]);
109
398
  /**
110
- * When the axes bounds have changed, we need to reset the range references so that
111
- * the new axis ranges are used
399
+ * When the axes bounds have changed (from outside, e.g. a different iterate function), resets
400
+ * the range references so that the new axis ranges are used -- as the axes' new original
401
+ * (un-zoomed) domains -- resets the zoom, and reports the (reset) zoom state
112
402
  * @param updates The updates to the axes
113
403
  */
114
404
  const updatedBoundsHandler = useCallback((updates) => {
405
+ // the new bounds become the axes' new un-zoomed (original) domains -- the zoom is reset
406
+ // to identity below, so they're the baseline the zoom constraint and the fully-zoomed-out
407
+ // snap (see `snapToOriginalRanges`) must measure against. The update itself can't be
408
+ // used as-is: `ContinuousAxis` carries the *previous* `.original` forward on a domain
409
+ // change, which would leave the zoom clamped to (and snapping back to) the old domain,
410
+ // e.g. [0, 1] after switching to an iterate function whose range is [-1, 1].
115
411
  updates.forEach((update, axisId) => {
412
+ const [start, end] = update.current.asTuple();
116
413
  if (xAxisRangesRef.current.has(axisId)) {
117
- xAxisRangesRef.current.set(axisId, update);
414
+ xAxisRangesRef.current.set(axisId, ContinuousAxisRange.from(start, end));
118
415
  }
119
416
  if (yAxisRangesRef.current.has(axisId)) {
120
- yAxisRangesRef.current.set(axisId, update);
417
+ yAxisRangesRef.current.set(axisId, ContinuousAxisRange.from(start, end));
121
418
  }
122
419
  });
123
- if (zoomEnabled && zoomSelectionRef.current !== undefined && zoomRef.current !== undefined) {
124
- zoomSelectionRef.current.call(zoomRef.current.transform, d3.zoomIdentity);
125
- }
126
- }, [zoomEnabled]);
420
+ if (zoomEnabled)
421
+ resetZoomTransform();
422
+ // the axes' bounds were changed from outside (e.g. a different iterate function), which
423
+ // resets the zoom, so a previously reported zoom state no longer applies
424
+ reportZoomState();
425
+ }, [zoomEnabled, resetZoomTransform, reportZoomState]);
127
426
  // strange construct so that we only add the update handler when the chart ID
128
427
  // changes, and not when the addAxesBoundsUpdateHandler or removeAxesBoundsUpdateHandler
129
428
  // which they do, and that breaks the updates...someone, please teach me react
@@ -142,360 +441,220 @@ export function PoincarePlot(props) {
142
441
  };
143
442
  }, [chartId, updatedBoundsHandler]);
144
443
  /**
145
- * Adjusts the time-range and updates the plot when the plot is dragged to the left or right
146
- * @param x The amount that the plot is dragged
444
+ * Adjusts the x- and y-axes' ranges when the plot is dragged (the caller redraws the plot)
445
+ * @param x The amount that the plot is dragged in x
446
+ * @param y The amount that the plot is dragged in y
147
447
  * @param plotDimensions The dimensions of the plot
148
448
  * @param series An array of series names
149
- * @param ranges A map holding the axis ID and its associated time range
449
+ * @param xRanges A map holding the x-axis ID and its associated range
450
+ * @param yRanges A map holding the y-axis ID and its associated range
150
451
  */
151
452
  const onPan = useCallback((x, y, plotDimensions, series, xRanges, yRanges) => panHandler2D(xAxesForSeries, yAxesForSeries, margin, setAxisIntervalFor, xAxesState, yAxesState)(x, y, plotDimensions, series, xRanges, yRanges), [xAxesForSeries, yAxesForSeries, margin, setAxisIntervalFor, xAxesState, yAxesState]);
453
+ /**
454
+ * Restores every axis to its original (un-zoomed) domain. Used when d3-zoom's cumulative `k`
455
+ * returns to exactly 1 (fully zoomed out): composing pivot-preserving zooms at *different*
456
+ * pivots isn't position-preserving (zooming in at one spot and back out at another nets a
457
+ * translation, as in any pinch-zoom UI), and the zoom constraint clamps that translated window
458
+ * to the original bounds -- so without this, zooming in at one spot and out at another stops
459
+ * short of the full view on both axes, with `k` already at its maximum. d3-zoom's `scaleExtent`
460
+ * clamps `k` to *exactly* 1 at that limit, so the equality check reliably detects it. (This is
461
+ * the continuous-axis counterpart of `OrdinalAxisRange.scaledCumulative`'s `factor === 1` case.)
462
+ */
463
+ const snapToOriginalRanges = useCallback(() => {
464
+ const snap = (ranges, axesState) => ranges.forEach((range, axisId) => {
465
+ ranges.set(axisId, range.update(range.original.start, range.original.end));
466
+ setAxisIntervalFor(axisId, range.original);
467
+ axesState.axisFor(axisId).ifPresent(axis => axis.update(range.original, plotDimensions, margin));
468
+ });
469
+ snap(xAxisRangesRef.current, xAxesState);
470
+ snap(yAxisRangesRef.current, yAxesState);
471
+ }, [setAxisIntervalFor, xAxesState, yAxesState, plotDimensions, margin]);
152
472
  /**
153
473
  * Called when the user uses the scroll wheel (or scroll gesture) to zoom in or out. Zooms in/out
154
474
  * at the location of the mouse when the scroll wheel or gesture was applied. Unlike time-series
155
475
  * plots, the iterates plot zooms the x- and y-axis at the same rate.
156
- * @param transform The d3 zoom transformation information
476
+ * @param zoomFactor The *incremental* zoom scale factor for this event -- see
477
+ * `useCanvasZoomPan` for how d3-zoom's own cumulative `k` is converted to this incremental
478
+ * form before this is called.
157
479
  * @param x The x-position of the mouse when the scroll wheel or gesture is used
158
480
  * @param y The y-position of the mouse when the scroll wheel or gesture is used
159
481
  * @param plotDimensions The dimensions of the plot
160
- * @param series An array of series names
161
- * @param ranges A map holding the axis ID and its associated time-range
162
- */
163
- const onZoom = useCallback((transform, x, y, plotDimensions, xRanges, yRanges) => axesZoomHandler(xAxesForSeries, yAxesForSeries, margin, setAxisIntervalFor, xAxesState, yAxesState, [zoomMinScaleFactor, zoomMaxScaleFactor])(transform, [x, y], plotDimensions, xRanges, yRanges), [xAxesForSeries, yAxesForSeries, margin, setAxisIntervalFor, xAxesState, yAxesState, zoomMinScaleFactor, zoomMaxScaleFactor]);
164
- const updatePlot = useCallback(
165
- /**
166
- * Updates the plot data for the specified time-range, which may have changed due to zoom or pan
167
- * @param mainGElem The main <g> element selection for that holds the plot
482
+ * @param xRanges A map holding the x-axis ID and its associated range
483
+ * @param yRanges A map holding the y-axis ID and its associated range
168
484
  */
169
- (mainGElem) => {
170
- if (container) {
171
- onUpdateChartTime(currentTimeRef.current);
172
- // select the svg element bind the data to them
173
- const svg = d3.select(container);
174
- // create a map associating series-names to their time-series.
175
- //
176
- // performance-related confusion: wondering where the dataRef is updated? well it isn't
177
- // directly. The dataRef holds on to an array of references to the Series. And so does the
178
- // seriesRef, though it uses a map(series_name -> series). The seriesRef is used to append
179
- // data to the underlying Series, and the dataRef is used so that we can just use
180
- // dataRef.current and don't have to do Array.from(seriesRef.current.values()) which
181
- // creates a temporary array
182
- // const offset = 1
183
- const boundedSeries = new Map(dataRef.current.map(series => {
184
- return [
185
- series.name,
186
- series.data
187
- .filter(datum => !isNaN(datum.iterateN))
188
- .map((datum, index) => ({
189
- n: datum.iterateN,
190
- n_1: datum.iterateN_1,
191
- time: datum.time,
192
- index: index
193
- }))
194
- ];
195
- }));
196
- // set up panning
197
- if (panEnabled) {
198
- const drag = d3.drag()
199
- .on("start", () => {
200
- d3.select(container).style("cursor", "move");
201
- // during panning, we need to disable viewing the tooltip to prevent
202
- // tooltips from rendering but not getting removed
203
- allowTooltip.current = false;
204
- })
205
- .on("drag", event => {
206
- onPan(event.dx, event.dy, plotDimensions, Array.from(boundedSeries.keys()), xAxisRangesRef.current, yAxisRangesRef.current);
207
- updatePlotRef.current(mainGElem);
208
- })
209
- .on("end", () => {
210
- d3.select(container).style("cursor", "auto");
211
- // during panning, we disabled viewing the tooltip to prevent
212
- // tooltips from rendering but not getting removed, now that panning
213
- // is over, allow tooltips to render again
214
- allowTooltip.current = isSubscriptionClosed();
215
- });
216
- svg.call(drag);
217
- }
218
- // set up for zooming
219
- if (zoomEnabled) {
220
- zoomRef.current = d3.zoom()
221
- .filter(event => !zoomKeyModifiersRequired || event.shiftKey || event.ctrlKey)
222
- .scaleExtent([zoomMinScaleFactor, zoomMaxScaleFactor])
223
- .translateExtent([[margin.left, margin.top], [plotDimensions.width, plotDimensions.height]])
224
- .on("zoom", event => {
225
- allowTooltip.current = false;
226
- if (event.sourceEvent !== null) {
227
- onZoom(event.transform, event.sourceEvent.offsetX - margin.left, event.sourceEvent.offsetY - margin.top, plotDimensions, xAxisRangesRef.current, yAxisRangesRef.current);
228
- updatePlotRef.current(mainGElem);
229
- }
230
- allowTooltip.current = true;
231
- });
232
- zoomSelectionRef.current = svg.call(zoomRef.current);
233
- }
234
- // define the clip-path so that the series lines don't go beyond the plot area
235
- const clipPathId = setClipPathG(chartId, mainGElem, plotDimensions);
236
- // ---
237
- // todo only want to do this once, on the first plot, and then leave it,
238
- // unless the axes are updated, also needs to be removed/added when the
239
- // plot size changes
240
- const xAxis = xAxesState.defaultAxis().getOrThrow(() => new Error('No default axis found'));
241
- const yAxis = yAxesState.defaultAxis().getOrThrow(() => new Error('No default axis found'));
242
- const lineGenerator = d3.line()
243
- .x(d => xAxis.scale(d[0] || 0))
244
- .y(d => yAxis.scale(d[1] || 0));
245
- // ---
246
- const [xStart, xEnd] = xAxesState.axisDefaultId()
247
- .map(id => { var _a; return ((_a = xAxisRangesRef.current.get(id)) === null || _a === void 0 ? void 0 : _a.original.asTuple()) || [0, 0]; })
248
- .getOrElse([0, 0]);
249
- const [yStart, yEnd] = yAxesState.axisDefaultId()
250
- .map(id => { var _a; return ((_a = yAxisRangesRef.current.get(id)) === null || _a === void 0 ? void 0 : _a.original.asTuple()) || [0, 0]; })
251
- .getOrElse([0, 0]);
252
- // const [xStart, xEnd] = xAxisRangesRef.current.get(xAxesState.axisDefaultId())?.original.asTuple() || [0, 0]
253
- // const [yStart, yEnd] = yAxisRangesRef.current.get(yAxesState.axisDefaultId())?.original.asTuple() || [0, 0]
254
- mainGElem
255
- .selectAll(`#fn-equals-fn1-${chartId}-poincare`)
256
- .data([[[xStart, yStart], [xEnd, yEnd]]])
257
- .join(enter => enter
258
- .select("path")
259
- .style("stroke", "grey")
260
- .style("fill", "none")
261
- .style("stroke-width", "1px")
262
- .attr("class", `fn-equals-fn1-poincare`)
263
- .attr("id", `#fn-equals-fn1-${chartId}-poincare`)
264
- .attr('transform', `translate(${margin.left}, ${margin.top})`)
265
- .attr("d", lineGenerator), update => update.remove(), exit => exit.remove())
266
- // clear out mouse-enter callbacks for the diagonal line
267
- .on("mouseenter", () => {
268
- })
269
- .on("mouseleave", () => {
270
- });
271
- boundedSeries.forEach((data, name) => {
272
- // grab the x and y axes assigned to the series, and if either or both
273
- // axes aren't found, then give up and return
274
- const [xAxisLinear, yAxisLinear] = axesFor(axisId => xAxesState.axisFor(axisId).getOrUndefined(), axisId => yAxesState.axisFor(axisId).getOrUndefined());
275
- if (xAxisLinear === undefined || yAxisLinear === undefined)
276
- return;
277
- // grab the style for the series
278
- const seriesLineStyle = seriesStyles.get(name) || Object.assign(Object.assign({}, defaultLineStyle()), { highlightColor: defaultLineStyle().color });
279
- // only show the data for which the filter matches
280
- const plotData = (name.match(seriesFilter)) ? data : [];
281
- const pathGenerator = d3.line()
282
- .x(d => xAxis.scale(d.n || 0))
283
- .y(d => yAxis.scale(d.n_1 || 0));
284
- if (interpolation === undefined) {
285
- mainGElem
286
- .selectAll(`#${name}-${chartId}-poincare`)
287
- .remove();
288
- }
289
- else {
290
- // create the time-series paths
291
- mainGElem
292
- .selectAll(`#${name}-${chartId}-poincare`)
293
- .data([[], plotData], () => `${name}`)
294
- .join(enter => enter
295
- .append("path")
296
- .attr("class", 'iterate-series-lines')
297
- .attr("id", `${name}-${chartId}-poincare`)
298
- .attr("d", pathGenerator.curve(interpolation))
299
- .style("fill", "none")
300
- .style("stroke", seriesLineStyle.color)
301
- .style("stroke-width", seriesLineStyle.lineWidth)
302
- .attr('transform', `translate(${margin.left}, ${margin.top})`)
303
- .attr("clip-path", `url(#${clipPathId})`), update => update, exit => exit.remove());
304
- }
305
- // when specified, show a circle for the actual data point
306
- if (showPoints) {
307
- mainGElem
308
- .selectAll(`.${name}-${chartId}-poincare-points`)
309
- .data(plotData, () => `${name}`)
310
- .join(enter => enter
311
- .append("circle")
312
- .attr("class", `${name}-${chartId}-poincare-points`)
313
- .attr("id", (_, index) => `${name}-${chartId}-poincare-point-${index}`)
314
- .attr("fill", seriesLineStyle.color)
315
- .attr("stroke", "none")
316
- .attr("cx", (d) => xAxisLinear.scale(d.n) || 0)
317
- .attr("cy", (d) => yAxisLinear.scale(d.n_1) || 0)
318
- .attr("r", 2)
319
- .attr('transform', `translate(${margin.left}, ${margin.top})`)
320
- .attr("clip-path", `url(#${clipPathId})`), update => update
321
- .attr("cx", (d) => xAxisLinear.scale(d.n) || 0)
322
- .attr("cy", (d) => yAxisLinear.scale(d.n_1) || 0), exit => exit.remove())
323
- .on("mouseenter", (event, datum) => {
324
- if (allowTooltip.current && tooltipVisible) {
325
- return handleMouseEnterPoint(chartId, name, container, event, datum, plotData, xAxisLinear, yAxisLinear, margin, seriesLineStyle, backgroundColor, allowTooltip.current, mouseOverHandlerFor(`tooltip-${chartId}`));
326
- }
327
- return _jsx(_Fragment, {});
328
- })
329
- .on("mouseleave", () => {
330
- handleMouseLeavePoint(chartId, name, seriesLineStyle.color, mouseLeaveHandlerFor(`tooltip-${chartId}`));
331
- });
332
- }
333
- });
334
- }
335
- }, [
336
- container, onUpdateChartTime, panEnabled, zoomEnabled, chartId, plotDimensions, margin,
337
- xAxesState, yAxesState,
338
- onPan,
339
- zoomMinScaleFactor, zoomMaxScaleFactor, zoomKeyModifiersRequired, onZoom,
340
- seriesStyles, seriesFilter, showPoints,
341
- interpolation, backgroundColor,
342
- mouseOverHandlerFor, mouseLeaveHandlerFor,
343
- tooltipVisible,
344
- ]);
345
- // need to keep the function references for use by the subscription, which forms a closure
346
- // on them. without the references, the closures become stale, and resizing during streaming
347
- // doesn't work properly
348
- const updatePlotRef = useRef(updatePlot);
349
- useEffect(() => {
350
- updatePlotRef.current = updatePlot;
351
- }, [updatePlot]);
485
+ const onZoom = useCallback((zoomFactor, x, y, plotDimensions, xRanges, yRanges) => axesZoomHandler(xAxesForSeries, yAxesForSeries, margin, setAxisIntervalFor, xAxesState, yAxesState, [zoomMinScaleFactor, zoomMaxScaleFactor])(zoomFactor, [x, y], plotDimensions, xRanges, yRanges), [xAxesForSeries, yAxesForSeries, margin, setAxisIntervalFor, xAxesState, yAxesState, zoomMinScaleFactor, zoomMaxScaleFactor]);
352
486
  const onUpdateAxesBoundsRef = useRef(updateAxisRanges);
353
487
  useEffect(() => {
354
488
  onUpdateAxesBoundsRef.current = updateAxisRanges;
355
489
  }, [updateAxisRanges]);
356
- // memoized function for subscribing to the chart-data observable
357
- const subscribe = useCallback(() => {
358
- if (seriesObservable === undefined || mainG === null)
359
- return undefined;
360
- return subscriptionIteratesFor(seriesObservable, onSubscribe, windowingTime, xAxesState, yAxesState, onUpdateData, dropDataAfter, updateRangesAndPlot,
361
- // as new data flows into the subscription, the subscription
362
- // updates this map directly (for performance)
363
- seriesRef.current, end => currentTimeRef.current = end);
364
- }, [
365
- dropDataAfter, mainG,
366
- onSubscribe, onUpdateData,
367
- seriesObservable, updateRangesAndPlot, windowingTime,
368
- xAxesState, yAxesState,
369
- ]);
370
490
  // updates the plot when the interpolation and filter change, because the updatePlot
371
491
  // callback has changed.
372
492
  useEffect(() => {
373
- if (container && mainG) {
374
- updatePlot(mainG);
493
+ if (canvasContext) {
494
+ updatePlot(canvasContext);
375
495
  }
376
- }, [axisRangeFor, container, mainG, updatePlot]);
377
- // subscribe/unsubscribe to the observable chart data. when the `shouldSubscribe`
378
- // is changed to `true` and we haven't subscribed yet, then subscribe. when the
379
- // `shouldSubscribe` is `false` and we had subscribed, then unsubscribe. otherwise,
380
- // do nothing.
496
+ }, [axisRangeFor, canvasContext, updatePlot]);
497
+ // wires up a single mousemove/mouseleave listener on the shared canvas to replace the old
498
+ // per-element SVG mouseenter/mouseleave handlers on the point circles. Hit-tests the mouse
499
+ // position against the last-drawn point geometry (see `geometryRef`, populated by the draw
500
+ // function above).
381
501
  useEffect(() => {
382
- var _a;
383
- if (shouldSubscribe && subscriptionRef.current === undefined) {
384
- subscriptionRef.current = subscribe();
385
- allowTooltip.current = false;
386
- }
387
- else if (!shouldSubscribe && subscriptionRef.current !== undefined) {
388
- (_a = subscriptionRef.current) === null || _a === void 0 ? void 0 : _a.unsubscribe();
389
- subscriptionRef.current = undefined;
502
+ if (!canvasContext)
503
+ return;
504
+ const canvas = canvasContext.canvas;
505
+ // highlights (or un-highlights) the shared x- and y-axes -- keyed on the series name
506
+ // alone (not the specific hovered point index), so moving the mouse between a
507
+ // series' own points doesn't flicker the highlight off and on
508
+ const highlightAxesFor = (seriesName, isHighlighted) => {
509
+ if (!highlightAxesOnMouseOver)
510
+ return;
511
+ const [xAxis, yAxis] = axesFor(axisId => xAxesState.axisFor(axisId).getOrUndefined(), axisId => yAxesState.axisFor(axisId).getOrUndefined());
512
+ const { highlightColor, highlightWidth } = seriesStyles.get(seriesName) || defaultLineStyle();
513
+ xAxis === null || xAxis === void 0 ? void 0 : xAxis.setHighlighted(isHighlighted, highlightColor, highlightWidth);
514
+ yAxis === null || yAxis === void 0 ? void 0 : yAxis.setHighlighted(isHighlighted, highlightColor, highlightWidth);
515
+ };
516
+ const handleMove = (event) => {
517
+ if (!allowTooltip.current || !tooltipVisible)
518
+ return;
519
+ const [x, y] = canvasLocalPoint(event, canvas);
520
+ const hit = seriesAt(x, y, geometryRef.current);
521
+ const hitSeriesName = hit === null || hit === void 0 ? void 0 : hit.name.replace(/::points$/, '');
522
+ const previous = hoveredPointRef.current;
523
+ if ((previous === null || previous === void 0 ? void 0 : previous.seriesName) !== hitSeriesName) {
524
+ if (previous !== undefined)
525
+ highlightAxesFor(previous.seriesName, false);
526
+ if (hitSeriesName !== undefined)
527
+ highlightAxesFor(hitSeriesName, true);
528
+ }
529
+ const sameAsBefore = previous !== undefined && hit !== undefined &&
530
+ previous.seriesName === hitSeriesName && previous.index === hit.index;
531
+ if (sameAsBefore)
532
+ return;
533
+ if (previous !== undefined) {
534
+ handleMouseLeavePoint(previous.seriesName, mouseLeaveHandlerFor(`tooltip-${chartId}`));
535
+ }
536
+ if (hit !== undefined && hitSeriesName !== undefined) {
537
+ const plotData = plotDataRef.current.get(hitSeriesName);
538
+ if (plotData !== undefined) {
539
+ handleMouseEnterPoint(hitSeriesName, plotData[hit.index], plotData, [x, y], mouseOverHandlerFor(`tooltip-${chartId}`));
540
+ }
541
+ }
542
+ hoveredPointRef.current = hit !== undefined && hitSeriesName !== undefined ?
543
+ { seriesName: hitSeriesName, index: hit.index } :
544
+ undefined;
545
+ // hover state affects the highlight/label drawing, so request a redraw
546
+ canvasContext.requestRedraw();
547
+ };
548
+ const handleLeaveCanvas = () => {
549
+ const previous = hoveredPointRef.current;
550
+ if (previous !== undefined) {
551
+ handleMouseLeavePoint(previous.seriesName, mouseLeaveHandlerFor(`tooltip-${chartId}`));
552
+ highlightAxesFor(previous.seriesName, false);
553
+ hoveredPointRef.current = undefined;
554
+ canvasContext.requestRedraw();
555
+ }
556
+ };
557
+ canvas.addEventListener('mousemove', handleMove);
558
+ canvas.addEventListener('mouseleave', handleLeaveCanvas);
559
+ return () => {
560
+ canvas.removeEventListener('mousemove', handleMove);
561
+ canvas.removeEventListener('mouseleave', handleLeaveCanvas);
562
+ };
563
+ }, [
564
+ canvasContext, chartId, mouseOverHandlerFor, mouseLeaveHandlerFor, tooltipVisible,
565
+ highlightAxesOnMouseOver, xAxesState, yAxesState, seriesStyles
566
+ ]);
567
+ // the latest `updateRangesAndPlot`, for the view driver to call -- the driver is created once
568
+ // per run (see below), so it must not capture a stale one (e.g. bound to an older canvas)
569
+ const updateRangesAndPlotRef = useRef(updateRangesAndPlot);
570
+ useEffect(() => {
571
+ updateRangesAndPlotRef.current = updateRangesAndPlot;
572
+ }, [updateRangesAndPlot]);
573
+ // creates the view driver that keeps this plot's view in step with the data source, using the
574
+ // current settings -- see `useStreamingTimeAxes`'s identical effect event
575
+ const createViewDriver = useEffectEvent((source) => iteratesViewDriverFor(source, windowingTime, xAxesState, yAxesState, () => updateRangesAndPlotRef.current(), end => currentTimeRef.current = end));
576
+ // while the data source is running (and this plot has a canvas), drive the view from the
577
+ // data source's updates -- see `useStreamingTimeAxes`'s identical effect. The driver belongs to this plot
578
+ // instance alone and is always torn down with it.
579
+ useEffect(() => {
580
+ if (dataSource === undefined || !running || canvasContext === null)
581
+ return;
582
+ allowTooltip.current = false;
583
+ const driver = createViewDriver(dataSource);
584
+ return () => {
585
+ driver.unsubscribe();
390
586
  allowTooltip.current = true;
587
+ };
588
+ }, [dataSource, running, canvasContext]);
589
+ // this plot needs a data source (see the `Chart`'s `dataSource` prop)
590
+ useEffect(() => {
591
+ if (dataSource === undefined) {
592
+ console.error("PoincarePlot requires a data source; set the enclosing Chart's `dataSource` prop");
391
593
  }
392
- }, [shouldSubscribe, subscribe]);
594
+ }, [dataSource]);
595
+ // unregister this plot's draw function on unmount
596
+ useEffect(() => {
597
+ return () => {
598
+ if (canvasContext) {
599
+ canvasContext.unregister(`poincare-plot-${chartId}`);
600
+ }
601
+ };
602
+ }, [canvasContext, chartId]);
393
603
  return null;
394
604
  }
395
605
  /**
396
- * Attempts to locate the x- and y-axes for the specified series. If no axis is found for the
397
- * series name, then uses the default returned by the useChart() hook
606
+ * @param dataSource The chart's data source
607
+ * @param seriesName The name of the series
608
+ * @return The number of points dropped from the series so far, when the data source counts them
609
+ * (an {@link IteratesDataSource} does); otherwise 0
610
+ */
611
+ function droppedCountFor(dataSource, seriesName) {
612
+ return dataSource !== null && typeof dataSource === 'object' && 'droppedCountFor' in dataSource ?
613
+ dataSource.droppedCountFor(seriesName) :
614
+ 0;
615
+ }
616
+ /**
617
+ * Locates the plot's x- and y-axes: always the default (first) ones, since every series of a
618
+ * Poincaré plot shares the same axes
398
619
  * @param xAxisFor The function that accepts an axis ID and returns the corresponding x-axis
399
620
  * @param yAxisFor The function that accepts an axis ID and returns the corresponding y-axis
621
+ * @return The x- and y-axes (either is actually `undefined` when it isn't found, despite the type)
622
+ * @throws Error when either axis isn't a continuous numeric axis
400
623
  */
401
624
  function axesFor(xAxisFor, yAxisFor) {
402
625
  const xAxis = xAxisFor("");
403
- const xAxisLinear = xAxis;
404
626
  const yAxis = yAxisFor("");
627
+ checkAxisType("PoincarePlot", "x", xAxis, AxisType.ContinuousNumeric);
628
+ checkAxisType("PoincarePlot", "y", yAxis, AxisType.ContinuousNumeric);
629
+ const xAxisLinear = xAxis;
405
630
  const yAxisLinear = yAxis;
406
- if (xAxis && !xAxisLinear) {
407
- throw Error("Poincare plot requires that x-axis be of type LinearAxis");
408
- }
409
- if (yAxis && !yAxisLinear) {
410
- throw Error("Poincare plot requires that y-axis be of type LinearAxis");
411
- }
412
631
  return [xAxisLinear, yAxisLinear];
413
632
  }
414
633
  /**
415
- * @param chartId The ID of the chart
634
+ * Reports the tooltip for the hovered point. Replaces the old version, which also directly
635
+ * mutated the hovered SVG `<circle>`'s radius/fill (and its neighbors') as a side effect; that
636
+ * highlighting is now handled entirely by the draw function reading `hoveredPointRef` (see
637
+ * `updatePlot`'s `draw`) rather than by touching elements here.
416
638
  * @param seriesName The name of the series (i.e. the neuron ID)
417
- * @param container The SVG container holding the plot
418
- * @param event The mouse event that triggered this call
419
- * @param datum The datum over which the mouse has entered
639
+ * @param datum The hovered point, or `undefined` if the index doesn't resolve to a point
420
640
  * @param plotData The iterates series
421
- * @param xAxisLinear The x-axis (f[n](x))
422
- * @param yAxisLinear The y-axis (f[n+1](x))
423
- * @param margin The plot margin
424
- * @param seriesStyle The series style information (needed for (un)highlighting)
425
- * @param backgroundColor
426
- * @param allowTooltip When set to `false` won't show tooltip, even if it is visible (used by pan)
641
+ * @param mouseCoords The `[x, y]` position of the mouse, in canvas coordinates
427
642
  * @param mouseOverHandlerFor The handler for the mouse-over (registered by the <Tooltip/>)
428
643
  */
429
- function handleMouseEnterPoint(chartId, seriesName, container, event, datum, plotData, xAxisLinear, yAxisLinear, margin, seriesStyle, backgroundColor, allowTooltip, mouseOverHandlerFor) {
430
- const { color, highlightColor, lineWidth } = seriesStyle;
431
- const padding = 4;
432
- const circleRadius = 5;
433
- const circleStroke = lineWidth;
434
- const circle = event.currentTarget;
435
- d3.select(circle)
436
- .attr("r", circleRadius)
437
- .style("fill", highlightColor);
438
- const index = plotData
439
- .findIndex(point => point.n === datum.n && point.n_1 === datum.n_1);
440
- /**
441
- * Displays basic information about an iterate, generally its neighbors
442
- * @param index The index of the iterate
443
- */
444
- function showInfo(index) {
445
- d3.select(`#${seriesName}-${chartId}-poincare-point-${index}`)
446
- .attr("r", circleRadius)
447
- .style("fill", d3.rgb(highlightColor).brighter(0.7).toString())
448
- .style("stroke-width", circleStroke)
449
- .style("stroke", color);
450
- // grab the (x, y)-coordinates for the plot
451
- const iterateN = xAxisLinear.scale(plotData[index].n) + margin.left;
452
- const iterateN_1 = yAxisLinear.scale(plotData[index].n_1) + margin.top;
453
- const svg = d3.select(container);
454
- // add a rectangle that serves as the background for the text (to make the
455
- // text readable when the chart is busy)
456
- const rect = svg
457
- .append("rect")
458
- .attr("class", `${seriesName}-${chartId}-poincare-point-text-background`);
459
- // add the information about the iterate as a text element
460
- const textElement = svg
461
- .append("text")
462
- .attr('class', `${seriesName}-${chartId}-poincare-point-text`)
463
- .attr('fill', highlightColor)
464
- .attr('font-family', 'sans-serif')
465
- .attr('font-size', 11)
466
- .attr('font-weight', 700)
467
- .text(`n = ${index}; t = ${formatTime(plotData[index].time)} ms`);
468
- // calculate the width and height of the text element
469
- const { width, height } = textDimensions(textElement);
470
- textElement
471
- .attr("transform", `translate(${iterateN - 8}, ${iterateN_1 - circleRadius - circleStroke - padding})`);
472
- rect
473
- .attr("x", iterateN - padding / 2 - 8)
474
- .attr("y", iterateN_1 - padding / 2 - circleRadius - circleStroke - height)
475
- .attr("width", width + padding)
476
- .attr("height", height + padding / 2)
477
- .style("fill", backgroundColor);
478
- }
479
- if (index > 0) {
480
- showInfo(index - 1);
481
- }
482
- if (index < plotData.length - 1) {
483
- showInfo(index + 1);
484
- }
485
- const [x, y] = d3.pointer(event, container);
486
- const currentDatum = (index >= 0) ? plotData[index] : { time: 0, n: 0, n_1: 0, index };
487
- if (mouseOverHandlerFor && allowTooltip) {
488
- mouseOverHandlerFor(seriesName, currentDatum.time, { series: plotData.map(ip => ({ iterateN: ip.n, iterateN_1: ip.n_1, time: ip.time })), metadata: {} }, [x, y]);
644
+ function handleMouseEnterPoint(seriesName, datum, plotData, mouseCoords, mouseOverHandlerFor) {
645
+ if (datum === undefined)
646
+ return;
647
+ if (mouseOverHandlerFor) {
648
+ mouseOverHandlerFor(seriesName, datum.time, { series: plotData.map(ip => ({ iterateN: ip.n, iterateN_1: ip.n_1, time: ip.time })), metadata: {} }, mouseCoords);
489
649
  }
490
650
  }
491
- function handleMouseLeavePoint(chartId, seriesName, color, mouseLeaverHandlerFor) {
492
- d3.selectAll(`.${seriesName}-${chartId}-poincare-points`)
493
- .attr("r", 2)
494
- .style("fill", color)
495
- .style("stroke", "none");
496
- d3.selectAll(`.${seriesName}-${chartId}-poincare-point-arrows`).remove();
497
- d3.selectAll(`.${seriesName}-${chartId}-poincare-point-text`).remove();
498
- d3.selectAll(`.${seriesName}-${chartId}-poincare-point-text-background`).remove();
651
+ /**
652
+ * Calls the mouse-leave-series handler registered for this series (the highlight is cleared by
653
+ * the draw function, once the hovered point is cleared)
654
+ * @param seriesName The name of the series
655
+ * @param mouseLeaverHandlerFor Registered handler for the series when the mouse leaves
656
+ */
657
+ function handleMouseLeavePoint(seriesName, mouseLeaverHandlerFor) {
499
658
  if (mouseLeaverHandlerFor) {
500
659
  mouseLeaverHandlerFor(seriesName);
501
660
  }