semiotic 3.0.0-beta.2 → 3.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +93 -753
- package/README.md +92 -7
- package/ai/dist/componentRegistry.js +26 -0
- package/ai/dist/{ai/mcp-server.js → mcp-server.js} +1 -1
- package/ai/dist/{ai/renderHOCToSVG.js → renderHOCToSVG.js} +2 -2
- package/dist/ChartContainer.d.ts +60 -0
- package/dist/DetailsPanel.d.ts +37 -0
- package/dist/LinkedCharts.d.ts +2 -0
- package/dist/Tooltip/Tooltip.d.ts +2 -2
- package/dist/charts/index.d.ts +1 -1
- package/dist/charts/ordinal/BarChart.d.ts +1 -0
- package/dist/charts/ordinal/BoxPlot.d.ts +1 -0
- package/dist/charts/ordinal/DonutChart.d.ts +1 -0
- package/dist/charts/ordinal/DotPlot.d.ts +1 -0
- package/dist/charts/ordinal/GroupedBarChart.d.ts +1 -0
- package/dist/charts/ordinal/Histogram.d.ts +1 -0
- package/dist/charts/ordinal/PieChart.d.ts +1 -0
- package/dist/charts/ordinal/RidgelinePlot.d.ts +1 -0
- package/dist/charts/ordinal/StackedBarChart.d.ts +1 -0
- package/dist/charts/ordinal/SwarmPlot.d.ts +1 -0
- package/dist/charts/ordinal/ViolinPlot.d.ts +1 -0
- package/dist/charts/realtime/RealtimeHeatmap.d.ts +11 -0
- package/dist/charts/realtime/RealtimeHistogram.d.ts +15 -1
- package/dist/charts/realtime/RealtimeLineChart.d.ts +6 -0
- package/dist/charts/realtime/RealtimeSwarmChart.d.ts +6 -0
- package/dist/charts/realtime/RealtimeWaterfallChart.d.ts +6 -0
- package/dist/charts/shared/annotationRules.d.ts +3 -0
- package/dist/charts/shared/colorUtils.d.ts +17 -11
- package/dist/charts/shared/hooks.d.ts +96 -1
- package/dist/charts/shared/loess.d.ts +13 -0
- package/dist/charts/shared/networkUtils.d.ts +31 -0
- package/dist/charts/shared/tooltipUtils.d.ts +16 -0
- package/dist/charts/shared/types.d.ts +17 -2
- package/dist/charts/shared/validateChartData.d.ts +2 -3
- package/dist/charts/shared/validateProps.d.ts +18 -0
- package/dist/charts/xy/AreaChart.d.ts +4 -0
- package/dist/charts/xy/BubbleChart.d.ts +6 -0
- package/dist/charts/xy/Heatmap.d.ts +4 -0
- package/dist/charts/xy/LineChart.d.ts +6 -0
- package/dist/charts/xy/Scatterplot.d.ts +4 -0
- package/dist/charts/xy/StackedAreaChart.d.ts +4 -0
- package/dist/data/fromVegaLite.d.ts +48 -0
- package/dist/export/chartConfig.d.ts +29 -0
- package/dist/export/selectionSerializer.d.ts +20 -0
- package/dist/geometry/sankeyLinks.d.ts +1 -1
- package/dist/network.min.js +1 -1
- package/dist/network.module.min.js +1 -1
- package/dist/ordinal.min.js +1 -1
- package/dist/ordinal.module.min.js +1 -1
- package/dist/realtime/types.d.ts +20 -6
- package/dist/realtime.min.js +1 -1
- package/dist/realtime.module.min.js +1 -1
- package/dist/semiotic-ai.d.ts +14 -0
- package/dist/semiotic-ai.min.js +1 -1
- package/dist/semiotic-ai.module.min.js +1 -1
- package/dist/semiotic-data.d.ts +2 -0
- package/dist/semiotic-data.min.js +1 -1
- package/dist/semiotic-data.module.min.js +1 -1
- package/dist/semiotic-network.d.ts +9 -19
- package/dist/semiotic-ordinal.d.ts +12 -14
- package/dist/semiotic-xy.d.ts +12 -18
- package/dist/semiotic.d.ts +16 -13
- package/dist/semiotic.min.js +1 -1
- package/dist/semiotic.module.min.js +1 -1
- package/dist/server.min.js +1 -1
- package/dist/server.module.min.js +1 -1
- package/dist/store/ObservationStore.d.ts +61 -0
- package/dist/store/SelectionStore.d.ts +9 -1
- package/dist/store/ThemeStore.d.ts +6 -1
- package/dist/store/TooltipStore.d.ts +3 -1
- package/dist/store/createStore.d.ts +4 -1
- package/dist/store/useObservation.d.ts +18 -0
- package/dist/stream/MarginalGraphics.d.ts +1 -1
- package/dist/stream/NetworkPipelineStore.d.ts +44 -0
- package/dist/stream/OrdinalCanvasHitTester.d.ts +1 -0
- package/dist/stream/OrdinalSVGOverlay.d.ts +6 -2
- package/dist/stream/ParticlePool.d.ts +2 -1
- package/dist/stream/PipelineStore.d.ts +11 -0
- package/dist/stream/SVGOverlay.d.ts +21 -2
- package/dist/stream/SceneGraph.d.ts +1 -1
- package/dist/stream/networkTypes.d.ts +58 -1
- package/dist/stream/ordinalTypes.d.ts +13 -0
- package/dist/stream/types.d.ts +14 -0
- package/dist/types/annotationTypes.d.ts +10 -0
- package/dist/types/networkTypes.d.ts +1 -2
- package/dist/xy.min.js +1 -1
- package/dist/xy.module.min.js +1 -1
- package/package.json +30 -42
- package/ai/dist/ai/componentRegistry.js +0 -45
- package/ai/dist/src/components/Annotation.js +0 -358
- package/ai/dist/src/components/AnnotationLayer/AnnotationLayer.js +0 -369
- package/ai/dist/src/components/Axis/Axis.js +0 -374
- package/ai/dist/src/components/Axis/axisTitle.js +0 -14
- package/ai/dist/src/components/Axis/index.js +0 -7
- package/ai/dist/src/components/Axis/summaryGraphic.js +0 -37
- package/ai/dist/src/components/Brush.js +0 -84
- package/ai/dist/src/components/ChartErrorBoundary.js +0 -91
- package/ai/dist/src/components/DividedLine.js +0 -65
- package/ai/dist/src/components/Legend.js +0 -140
- package/ai/dist/src/components/LinkedCharts.js +0 -95
- package/ai/dist/src/components/ThemeProvider.js +0 -79
- package/ai/dist/src/components/Tooltip/Tooltip.js +0 -309
- package/ai/dist/src/components/TooltipPositioner/index.js +0 -132
- package/ai/dist/src/components/annotationLayerBehavior/annotationHandling.js +0 -73
- package/ai/dist/src/components/annotationLayerBehavior/d3labeler.js +0 -254
- package/ai/dist/src/components/annotationRules/baseRules.js +0 -150
- package/ai/dist/src/components/annotationRules/networkframeRules.js +0 -196
- package/ai/dist/src/components/annotationRules/xyframeRules.js +0 -297
- package/ai/dist/src/components/batchWork.js +0 -35
- package/ai/dist/src/components/charts/index.js +0 -109
- package/ai/dist/src/components/charts/network/ChordDiagram.js +0 -142
- package/ai/dist/src/components/charts/network/CirclePack.js +0 -108
- package/ai/dist/src/components/charts/network/ForceDirectedGraph.js +0 -121
- package/ai/dist/src/components/charts/network/SankeyDiagram.js +0 -155
- package/ai/dist/src/components/charts/network/TreeDiagram.js +0 -110
- package/ai/dist/src/components/charts/network/Treemap.js +0 -106
- package/ai/dist/src/components/charts/ordinal/BarChart.js +0 -156
- package/ai/dist/src/components/charts/ordinal/BoxPlot.js +0 -139
- package/ai/dist/src/components/charts/ordinal/DonutChart.js +0 -130
- package/ai/dist/src/components/charts/ordinal/DotPlot.js +0 -126
- package/ai/dist/src/components/charts/ordinal/GroupedBarChart.js +0 -129
- package/ai/dist/src/components/charts/ordinal/Histogram.js +0 -132
- package/ai/dist/src/components/charts/ordinal/PieChart.js +0 -128
- package/ai/dist/src/components/charts/ordinal/RidgelinePlot.js +0 -130
- package/ai/dist/src/components/charts/ordinal/StackedBarChart.js +0 -130
- package/ai/dist/src/components/charts/ordinal/SwarmPlot.js +0 -147
- package/ai/dist/src/components/charts/ordinal/ViolinPlot.js +0 -138
- package/ai/dist/src/components/charts/realtime/RealtimeHeatmap.js +0 -79
- package/ai/dist/src/components/charts/realtime/RealtimeHistogram.js +0 -114
- package/ai/dist/src/components/charts/realtime/RealtimeLineChart.js +0 -93
- package/ai/dist/src/components/charts/realtime/RealtimeSwarmChart.js +0 -105
- package/ai/dist/src/components/charts/realtime/RealtimeWaterfallChart.js +0 -106
- package/ai/dist/src/components/charts/shared/ChartError.js +0 -72
- package/ai/dist/src/components/charts/shared/colorUtils.js +0 -138
- package/ai/dist/src/components/charts/shared/formatUtils.js +0 -213
- package/ai/dist/src/components/charts/shared/hooks.js +0 -49
- package/ai/dist/src/components/charts/shared/legendUtils.js +0 -57
- package/ai/dist/src/components/charts/shared/selectionUtils.js +0 -67
- package/ai/dist/src/components/charts/shared/tooltipUtils.js +0 -79
- package/ai/dist/src/components/charts/shared/types.js +0 -2
- package/ai/dist/src/components/charts/shared/validateChartData.js +0 -82
- package/ai/dist/src/components/charts/shared/validateProps.js +0 -736
- package/ai/dist/src/components/charts/xy/AreaChart.js +0 -230
- package/ai/dist/src/components/charts/xy/BubbleChart.js +0 -251
- package/ai/dist/src/components/charts/xy/Heatmap.js +0 -235
- package/ai/dist/src/components/charts/xy/LineChart.js +0 -307
- package/ai/dist/src/components/charts/xy/MinimapChart.js +0 -298
- package/ai/dist/src/components/charts/xy/Scatterplot.js +0 -172
- package/ai/dist/src/components/charts/xy/ScatterplotMatrix.js +0 -426
- package/ai/dist/src/components/charts/xy/StackedAreaChart.js +0 -231
- package/ai/dist/src/components/constants/coordinateNames.js +0 -11
- package/ai/dist/src/components/constants/frame_props.js +0 -251
- package/ai/dist/src/components/data/dataFunctions.js +0 -487
- package/ai/dist/src/components/data/multiAccessorUtils.js +0 -14
- package/ai/dist/src/components/data/transforms.js +0 -143
- package/ai/dist/src/components/data/unflowedFunctions.js +0 -5
- package/ai/dist/src/components/export/exportChart.js +0 -121
- package/ai/dist/src/components/generic_utilities/functions.js +0 -5
- package/ai/dist/src/components/geometry/areaDrawing.js +0 -312
- package/ai/dist/src/components/geometry/contourLayout.js +0 -73
- package/ai/dist/src/components/geometry/hexbinLayout.js +0 -163
- package/ai/dist/src/components/geometry/lineDrawing.js +0 -356
- package/ai/dist/src/components/geometry/sankeyLinks.js +0 -331
- package/ai/dist/src/components/geometry/summaryLayouts.js +0 -136
- package/ai/dist/src/components/index.js +0 -18
- package/ai/dist/src/components/processing/InteractionItems.js +0 -223
- package/ai/dist/src/components/processing/hierarchyUtils.js +0 -104
- package/ai/dist/src/components/processing/layouts/chordLayout.js +0 -58
- package/ai/dist/src/components/processing/layouts/forceLayout.js +0 -142
- package/ai/dist/src/components/processing/layouts/hierarchyLayout.js +0 -31
- package/ai/dist/src/components/processing/layouts/index.js +0 -32
- package/ai/dist/src/components/processing/layouts/sankeyLayout.js +0 -96
- package/ai/dist/src/components/processing/layouts/simpleLayouts.js +0 -34
- package/ai/dist/src/components/processing/layouts/types.js +0 -2
- package/ai/dist/src/components/processing/networkDefaults.js +0 -39
- package/ai/dist/src/components/realtime/BinAccumulator.js +0 -36
- package/ai/dist/src/components/realtime/IncrementalExtent.js +0 -55
- package/ai/dist/src/components/realtime/RingBuffer.js +0 -104
- package/ai/dist/src/components/realtime/renderers/barRenderer.js +0 -133
- package/ai/dist/src/components/realtime/renderers/candlestickRenderer.js +0 -7
- package/ai/dist/src/components/realtime/renderers/lineRenderer.js +0 -164
- package/ai/dist/src/components/realtime/renderers/swarmRenderer.js +0 -91
- package/ai/dist/src/components/realtime/renderers/types.js +0 -2
- package/ai/dist/src/components/realtime/renderers/waterfallRenderer.js +0 -163
- package/ai/dist/src/components/realtime/types.js +0 -2
- package/ai/dist/src/components/semiotic-ai.js +0 -89
- package/ai/dist/src/components/semiotic-data.js +0 -12
- package/ai/dist/src/components/semiotic-network.js +0 -38
- package/ai/dist/src/components/semiotic-ordinal.js +0 -28
- package/ai/dist/src/components/semiotic-realtime.js +0 -30
- package/ai/dist/src/components/semiotic-server.js +0 -8
- package/ai/dist/src/components/semiotic-xy.js +0 -35
- package/ai/dist/src/components/semiotic.js +0 -109
- package/ai/dist/src/components/server/renderToStaticSVG.js +0 -594
- package/ai/dist/src/components/store/SelectionStore.js +0 -91
- package/ai/dist/src/components/store/ThemeStore.js +0 -78
- package/ai/dist/src/components/store/TooltipStore.js +0 -13
- package/ai/dist/src/components/store/createStore.js +0 -81
- package/ai/dist/src/components/store/useSelection.js +0 -133
- package/ai/dist/src/components/stream/CanvasHitTester.js +0 -164
- package/ai/dist/src/components/stream/DataSourceAdapter.js +0 -99
- package/ai/dist/src/components/stream/MarginalGraphics.js +0 -266
- package/ai/dist/src/components/stream/NetworkCanvasHitTester.js +0 -228
- package/ai/dist/src/components/stream/NetworkPipelineStore.js +0 -498
- package/ai/dist/src/components/stream/NetworkSVGOverlay.js +0 -70
- package/ai/dist/src/components/stream/NetworkSceneGraph.js +0 -38
- package/ai/dist/src/components/stream/OrdinalCanvasHitTester.js +0 -146
- package/ai/dist/src/components/stream/OrdinalPipelineStore.js +0 -786
- package/ai/dist/src/components/stream/OrdinalSVGOverlay.js +0 -130
- package/ai/dist/src/components/stream/ParticlePool.js +0 -174
- package/ai/dist/src/components/stream/PipelineStore.js +0 -1243
- package/ai/dist/src/components/stream/SVGOverlay.js +0 -129
- package/ai/dist/src/components/stream/SceneGraph.js +0 -132
- package/ai/dist/src/components/stream/StreamNetworkFrame.js +0 -561
- package/ai/dist/src/components/stream/StreamOrdinalFrame.js +0 -492
- package/ai/dist/src/components/stream/StreamXYFrame.js +0 -590
- package/ai/dist/src/components/stream/accessorUtils.js +0 -20
- package/ai/dist/src/components/stream/index.js +0 -32
- package/ai/dist/src/components/stream/layouts/chordLayoutPlugin.js +0 -352
- package/ai/dist/src/components/stream/layouts/forceLayoutPlugin.js +0 -230
- package/ai/dist/src/components/stream/layouts/hierarchyLayoutPlugin.js +0 -568
- package/ai/dist/src/components/stream/layouts/index.js +0 -28
- package/ai/dist/src/components/stream/layouts/sankeyLayoutPlugin.js +0 -245
- package/ai/dist/src/components/stream/networkTypes.js +0 -17
- package/ai/dist/src/components/stream/ordinalSceneBuilders/barScene.js +0 -126
- package/ai/dist/src/components/stream/ordinalSceneBuilders/connectorScene.js +0 -62
- package/ai/dist/src/components/stream/ordinalSceneBuilders/pieScene.js +0 -33
- package/ai/dist/src/components/stream/ordinalSceneBuilders/pointScene.js +0 -63
- package/ai/dist/src/components/stream/ordinalSceneBuilders/statisticalScene.js +0 -278
- package/ai/dist/src/components/stream/ordinalSceneBuilders/timelineScene.js +0 -30
- package/ai/dist/src/components/stream/ordinalSceneBuilders/types.js +0 -2
- package/ai/dist/src/components/stream/ordinalTypes.js +0 -2
- package/ai/dist/src/components/stream/renderers/areaCanvasRenderer.js +0 -48
- package/ai/dist/src/components/stream/renderers/barCanvasRenderer.js +0 -70
- package/ai/dist/src/components/stream/renderers/boxplotCanvasRenderer.js +0 -75
- package/ai/dist/src/components/stream/renderers/candlestickCanvasRenderer.js +0 -28
- package/ai/dist/src/components/stream/renderers/connectorCanvasRenderer.js +0 -47
- package/ai/dist/src/components/stream/renderers/heatmapCanvasRenderer.js +0 -31
- package/ai/dist/src/components/stream/renderers/lineCanvasRenderer.js +0 -140
- package/ai/dist/src/components/stream/renderers/networkArcRenderer.js +0 -38
- package/ai/dist/src/components/stream/renderers/networkCircleRenderer.js +0 -37
- package/ai/dist/src/components/stream/renderers/networkEdgeRenderer.js +0 -102
- package/ai/dist/src/components/stream/renderers/networkParticleRenderer.js +0 -63
- package/ai/dist/src/components/stream/renderers/networkRectRenderer.js +0 -35
- package/ai/dist/src/components/stream/renderers/pointCanvasRenderer.js +0 -38
- package/ai/dist/src/components/stream/renderers/swarmCanvasRenderer.js +0 -10
- package/ai/dist/src/components/stream/renderers/types.js +0 -2
- package/ai/dist/src/components/stream/renderers/violinCanvasRenderer.js +0 -47
- package/ai/dist/src/components/stream/renderers/waterfallCanvasRenderer.js +0 -38
- package/ai/dist/src/components/stream/renderers/wedgeCanvasRenderer.js +0 -33
- package/ai/dist/src/components/stream/types.js +0 -2
- package/ai/dist/src/components/types/annotationTypes.js +0 -2
- package/ai/dist/src/components/types/canvasTypes.js +0 -2
- package/ai/dist/src/components/types/generalTypes.js +0 -2
- package/ai/dist/src/components/types/interactionTypes.js +0 -2
- package/ai/dist/src/components/types/legendTypes.js +0 -2
- package/ai/dist/src/components/types/networkTypes.js +0 -2
- package/ai/dist/src/components/types/ordinalTypes.js +0 -2
- package/ai/dist/src/components/types/xyTypes.js +0 -2
- package/ai/dist/src/components/useBoundingRect.js +0 -24
- package/ai/dist/src/components/visualizationLayerBehavior/axis.js +0 -301
- package/ai/dist/src/components/visualizationLayerBehavior/general.js +0 -435
- package/ai/dist/src/setupTests.js +0 -4
- package/dist/AnnotationLayer/AnnotationLayer.d.ts +0 -25
- package/dist/Axis/Axis.d.ts +0 -7
- package/dist/Axis/axisTitle.d.ts +0 -10
- package/dist/Axis/index.d.ts +0 -2
- package/dist/Axis/summaryGraphic.d.ts +0 -17
- package/dist/Brush.d.ts +0 -12
- package/dist/DividedLine.d.ts +0 -16
- package/dist/TooltipPositioner/index.d.ts +0 -7
- package/dist/annotationLayerBehavior/annotationHandling.d.ts +0 -19
- package/dist/annotationLayerBehavior/d3labeler.d.ts +0 -9
- package/dist/annotationRules/baseRules.d.ts +0 -25
- package/dist/annotationRules/networkframeRules.d.ts +0 -48
- package/dist/annotationRules/xyframeRules.d.ts +0 -117
- package/dist/batchWork.d.ts +0 -6
- package/dist/constants/coordinateNames.d.ts +0 -8
- package/dist/constants/frame_props.d.ts +0 -13
- package/dist/data/dataFunctions.d.ts +0 -45
- package/dist/data/multiAccessorUtils.d.ts +0 -1
- package/dist/data/unflowedFunctions.d.ts +0 -1
- package/dist/generic_utilities/functions.d.ts +0 -1
- package/dist/geometry/areaDrawing.d.ts +0 -21
- package/dist/geometry/contourLayout.d.ts +0 -6
- package/dist/geometry/hexbinLayout.d.ts +0 -7
- package/dist/geometry/lineDrawing.d.ts +0 -71
- package/dist/geometry/summaryLayouts.d.ts +0 -45
- package/dist/index.d.ts +0 -1
- package/dist/network.js +0 -7495
- package/dist/network.js.map +0 -1
- package/dist/network.module.js +0 -7458
- package/dist/network.module.js.map +0 -1
- package/dist/ordinal.js +0 -6497
- package/dist/ordinal.js.map +0 -1
- package/dist/ordinal.module.js +0 -6465
- package/dist/ordinal.module.js.map +0 -1
- package/dist/processing/InteractionItems.d.ts +0 -13
- package/dist/processing/hierarchyUtils.d.ts +0 -16
- package/dist/processing/layouts/chordLayout.d.ts +0 -2
- package/dist/processing/layouts/forceLayout.d.ts +0 -3
- package/dist/processing/layouts/hierarchyLayout.d.ts +0 -10
- package/dist/processing/layouts/index.d.ts +0 -8
- package/dist/processing/layouts/sankeyLayout.d.ts +0 -8
- package/dist/processing/layouts/simpleLayouts.d.ts +0 -7
- package/dist/processing/layouts/types.d.ts +0 -17
- package/dist/processing/networkDefaults.d.ts +0 -36
- package/dist/realtime/renderers/barRenderer.d.ts +0 -2
- package/dist/realtime/renderers/candlestickRenderer.d.ts +0 -2
- package/dist/realtime/renderers/lineRenderer.d.ts +0 -2
- package/dist/realtime/renderers/swarmRenderer.d.ts +0 -2
- package/dist/realtime.js +0 -7072
- package/dist/realtime.js.map +0 -1
- package/dist/realtime.module.js +0 -7043
- package/dist/realtime.module.js.map +0 -1
- package/dist/semiotic-ai.js +0 -13323
- package/dist/semiotic-ai.js.map +0 -1
- package/dist/semiotic-ai.module.js +0 -13264
- package/dist/semiotic-ai.module.js.map +0 -1
- package/dist/semiotic-data.js +0 -141
- package/dist/semiotic-data.js.map +0 -1
- package/dist/semiotic-data.module.js +0 -136
- package/dist/semiotic-data.module.js.map +0 -1
- package/dist/semiotic.js +0 -16351
- package/dist/semiotic.js.map +0 -1
- package/dist/semiotic.module.js +0 -16265
- package/dist/semiotic.module.js.map +0 -1
- package/dist/server.js +0 -5191
- package/dist/server.js.map +0 -1
- package/dist/server.module.js +0 -5166
- package/dist/server.module.js.map +0 -1
- package/dist/stream/NetworkSceneGraph.d.ts +0 -14
- package/dist/stream/index.d.ts +0 -16
- package/dist/types/canvasTypes.d.ts +0 -9
- package/dist/types/xyTypes.d.ts +0 -24
- package/dist/useBoundingRect.d.ts +0 -2
- package/dist/visualizationLayerBehavior/axis.d.ts +0 -36
- package/dist/visualizationLayerBehavior/general.d.ts +0 -80
- package/dist/xy.js +0 -6993
- package/dist/xy.js.map +0 -1
- package/dist/xy.module.js +0 -6957
- package/dist/xy.module.js.map +0 -1
|
@@ -1,1243 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PipelineStore = void 0;
|
|
4
|
-
const d3_scale_1 = require("d3-scale");
|
|
5
|
-
const RingBuffer_1 = require("../realtime/RingBuffer");
|
|
6
|
-
const IncrementalExtent_1 = require("../realtime/IncrementalExtent");
|
|
7
|
-
const BinAccumulator_1 = require("../realtime/BinAccumulator");
|
|
8
|
-
const waterfallRenderer_1 = require("../realtime/renderers/waterfallRenderer");
|
|
9
|
-
const SceneGraph_1 = require("./SceneGraph");
|
|
10
|
-
const accessorUtils_1 = require("./accessorUtils");
|
|
11
|
-
// ── Axis direction helpers ─────────────────────────────────────────────
|
|
12
|
-
function getTimeAxis(arrowOfTime) {
|
|
13
|
-
return arrowOfTime === "up" || arrowOfTime === "down" ? "y" : "x";
|
|
14
|
-
}
|
|
15
|
-
// ── PipelineStore ──────────────────────────────────────────────────────
|
|
16
|
-
class PipelineStore {
|
|
17
|
-
constructor(config) {
|
|
18
|
-
this.xExtent = new IncrementalExtent_1.IncrementalExtent();
|
|
19
|
-
this.yExtent = new IncrementalExtent_1.IncrementalExtent();
|
|
20
|
-
// ── Pulse tracking ──────────────────────────────────────────────────
|
|
21
|
-
this.timestampBuffer = null;
|
|
22
|
-
// ── Transition animation ────────────────────────────────────────────
|
|
23
|
-
this.activeTransition = null;
|
|
24
|
-
this.prevPositionMap = new Map();
|
|
25
|
-
// ── Staleness tracking ──────────────────────────────────────────────
|
|
26
|
-
this.lastIngestTime = 0;
|
|
27
|
-
this.scales = null;
|
|
28
|
-
this.scene = [];
|
|
29
|
-
this.version = 0;
|
|
30
|
-
this.config = config;
|
|
31
|
-
this.buffer = new RingBuffer_1.RingBuffer(config.windowSize);
|
|
32
|
-
this.growingCap = config.windowSize;
|
|
33
|
-
// Resolve accessors based on streaming vs bounded mode.
|
|
34
|
-
// Streaming types use time/value defaults; bounded types use x/y defaults.
|
|
35
|
-
const isStreamingType = ["bar", "swarm", "waterfall"].includes(config.chartType);
|
|
36
|
-
const useStreamingDefaults = isStreamingType || config.runtimeMode === "streaming";
|
|
37
|
-
if (useStreamingDefaults) {
|
|
38
|
-
this.getX = (0, accessorUtils_1.resolveAccessor)(config.timeAccessor || config.xAccessor, "time");
|
|
39
|
-
this.getY = (0, accessorUtils_1.resolveAccessor)(config.valueAccessor || config.yAccessor, "value");
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
this.getX = (0, accessorUtils_1.resolveAccessor)(config.xAccessor, "x");
|
|
43
|
-
this.getY = (0, accessorUtils_1.resolveAccessor)(config.yAccessor, "y");
|
|
44
|
-
}
|
|
45
|
-
this.getGroup = (0, accessorUtils_1.resolveStringAccessor)(config.groupAccessor);
|
|
46
|
-
this.getCategory = (0, accessorUtils_1.resolveStringAccessor)(config.categoryAccessor);
|
|
47
|
-
this.getSize = config.sizeAccessor
|
|
48
|
-
? (0, accessorUtils_1.resolveAccessor)(config.sizeAccessor, "size")
|
|
49
|
-
: undefined;
|
|
50
|
-
this.getColor = (0, accessorUtils_1.resolveStringAccessor)(config.colorAccessor);
|
|
51
|
-
this.getBounds = config.boundsAccessor
|
|
52
|
-
? (0, accessorUtils_1.resolveAccessor)(config.boundsAccessor, "bounds")
|
|
53
|
-
: undefined;
|
|
54
|
-
// Candlestick accessors
|
|
55
|
-
if (config.chartType === "candlestick") {
|
|
56
|
-
this.getOpen = (0, accessorUtils_1.resolveAccessor)(config.openAccessor, "open");
|
|
57
|
-
this.getHigh = (0, accessorUtils_1.resolveAccessor)(config.highAccessor, "high");
|
|
58
|
-
this.getLow = (0, accessorUtils_1.resolveAccessor)(config.lowAccessor, "low");
|
|
59
|
-
this.getClose = (0, accessorUtils_1.resolveAccessor)(config.closeAccessor, "close");
|
|
60
|
-
}
|
|
61
|
-
// Pulse: parallel timestamp buffer
|
|
62
|
-
if (config.pulse) {
|
|
63
|
-
this.timestampBuffer = new RingBuffer_1.RingBuffer(config.windowSize);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Process a changeset from DataSourceAdapter.
|
|
68
|
-
* Returns true if the scene needs re-rendering.
|
|
69
|
-
*/
|
|
70
|
-
ingest(changeset) {
|
|
71
|
-
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
72
|
-
this.lastIngestTime = now;
|
|
73
|
-
if (changeset.bounded) {
|
|
74
|
-
// Full replacement for bounded data
|
|
75
|
-
this.buffer.clear();
|
|
76
|
-
this.xExtent.clear();
|
|
77
|
-
this.yExtent.clear();
|
|
78
|
-
if (this.timestampBuffer)
|
|
79
|
-
this.timestampBuffer.clear();
|
|
80
|
-
// Auto-resize buffer to fit all bounded data.
|
|
81
|
-
// totalSize is set when data is progressively chunked — pre-allocate
|
|
82
|
-
// for the full dataset so subsequent append chunks don't evict.
|
|
83
|
-
const targetSize = changeset.totalSize || changeset.inserts.length;
|
|
84
|
-
if (targetSize > this.buffer.capacity) {
|
|
85
|
-
this.buffer.resize(targetSize);
|
|
86
|
-
if (this.timestampBuffer && targetSize > this.timestampBuffer.capacity) {
|
|
87
|
-
this.timestampBuffer.resize(targetSize);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
for (const d of changeset.inserts) {
|
|
91
|
-
this.buffer.push(d);
|
|
92
|
-
if (this.timestampBuffer)
|
|
93
|
-
this.timestampBuffer.push(now);
|
|
94
|
-
this.xExtent.push(this.getX(d));
|
|
95
|
-
if (this.config.chartType === "candlestick" && this.getHigh && this.getLow) {
|
|
96
|
-
this.yExtent.push(this.getHigh(d));
|
|
97
|
-
this.yExtent.push(this.getLow(d));
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
this.yExtent.push(this.getY(d));
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
// Streaming append
|
|
106
|
-
for (const d of changeset.inserts) {
|
|
107
|
-
if (this.config.windowMode === "growing" && this.buffer.full) {
|
|
108
|
-
this.growingCap *= 2;
|
|
109
|
-
this.buffer.resize(this.growingCap);
|
|
110
|
-
if (this.timestampBuffer)
|
|
111
|
-
this.timestampBuffer.resize(this.growingCap);
|
|
112
|
-
}
|
|
113
|
-
const evicted = this.buffer.push(d);
|
|
114
|
-
if (this.timestampBuffer)
|
|
115
|
-
this.timestampBuffer.push(now);
|
|
116
|
-
this.xExtent.push(this.getX(d));
|
|
117
|
-
if (this.config.chartType === "candlestick" && this.getHigh && this.getLow) {
|
|
118
|
-
this.yExtent.push(this.getHigh(d));
|
|
119
|
-
this.yExtent.push(this.getLow(d));
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
this.yExtent.push(this.getY(d));
|
|
123
|
-
}
|
|
124
|
-
if (evicted != null) {
|
|
125
|
-
this.xExtent.evict(this.getX(evicted));
|
|
126
|
-
if (this.config.chartType === "candlestick" && this.getHigh && this.getLow) {
|
|
127
|
-
this.yExtent.evict(this.getHigh(evicted));
|
|
128
|
-
this.yExtent.evict(this.getLow(evicted));
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
this.yExtent.evict(this.getY(evicted));
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Recompute scales and scene graph for the current buffer contents.
|
|
140
|
-
*/
|
|
141
|
-
computeScene(layout) {
|
|
142
|
-
const { config, buffer } = this;
|
|
143
|
-
// Recalculate dirty extents
|
|
144
|
-
if (this.xExtent.dirty) {
|
|
145
|
-
this.xExtent.recalculate(buffer, this.getX);
|
|
146
|
-
}
|
|
147
|
-
if (this.yExtent.dirty) {
|
|
148
|
-
if (config.chartType === "candlestick" && this.getHigh && this.getLow) {
|
|
149
|
-
// Candlestick y-extent spans high→low, not a single y accessor
|
|
150
|
-
this.yExtent.clear();
|
|
151
|
-
for (const d of buffer) {
|
|
152
|
-
this.yExtent.push(this.getHigh(d));
|
|
153
|
-
this.yExtent.push(this.getLow(d));
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
this.yExtent.recalculate(buffer, this.getY);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// Resolve domains — merge user-specified extents with data extents
|
|
161
|
-
const dataXDomain = this.xExtent.extent;
|
|
162
|
-
const dataYDomain = this.yExtent.extent;
|
|
163
|
-
let xDomain = config.xExtent
|
|
164
|
-
? [
|
|
165
|
-
config.xExtent[0] ?? dataXDomain[0],
|
|
166
|
-
config.xExtent[1] ?? dataXDomain[1]
|
|
167
|
-
]
|
|
168
|
-
: dataXDomain;
|
|
169
|
-
let yDomain = config.yExtent
|
|
170
|
-
? [
|
|
171
|
-
config.yExtent[0] ?? dataYDomain[0],
|
|
172
|
-
config.yExtent[1] ?? dataYDomain[1]
|
|
173
|
-
]
|
|
174
|
-
: dataYDomain;
|
|
175
|
-
// Determine if extents are fully user-specified (no padding needed)
|
|
176
|
-
const yFullySpecified = config.yExtent && config.yExtent[0] != null && config.yExtent[1] != null;
|
|
177
|
-
const xFullySpecified = config.xExtent && config.xExtent[0] != null && config.xExtent[1] != null;
|
|
178
|
-
// Chart-type specific extent adjustments
|
|
179
|
-
if (config.chartType === "stackedarea" && !yFullySpecified && buffer.size > 0) {
|
|
180
|
-
// Stacked areas: y-extent must cover the cumulative sums, not raw values
|
|
181
|
-
if (config.normalize) {
|
|
182
|
-
// Normalized: all stacks sum to 1.0
|
|
183
|
-
yDomain = [0, 1 + config.extentPadding];
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
const data = buffer.toArray();
|
|
187
|
-
const groups = this.groupData(data);
|
|
188
|
-
// Build per-x-value totals across all groups
|
|
189
|
-
const xTotals = new Map();
|
|
190
|
-
for (const g of groups) {
|
|
191
|
-
for (const d of g.data) {
|
|
192
|
-
const x = this.getX(d);
|
|
193
|
-
const y = this.getY(d);
|
|
194
|
-
if (x != null && y != null && !Number.isNaN(x) && !Number.isNaN(y)) {
|
|
195
|
-
xTotals.set(x, (xTotals.get(x) || 0) + y);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
let maxStacked = 0;
|
|
200
|
-
for (const total of xTotals.values()) {
|
|
201
|
-
if (total > maxStacked)
|
|
202
|
-
maxStacked = total;
|
|
203
|
-
}
|
|
204
|
-
const pad = maxStacked > 0 ? maxStacked * config.extentPadding : 1;
|
|
205
|
-
yDomain = [0, maxStacked + pad];
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
else if (config.chartType === "bar" && config.binSize && !yFullySpecified && buffer.size > 0) {
|
|
209
|
-
const [, maxTotal] = (0, BinAccumulator_1.computeBinExtent)(buffer, this.getX, this.getY, config.binSize, this.getCategory);
|
|
210
|
-
yDomain = [0, maxTotal + maxTotal * config.extentPadding];
|
|
211
|
-
}
|
|
212
|
-
else if (config.chartType === "waterfall" && !yFullySpecified && buffer.size > 0) {
|
|
213
|
-
const [minCum, maxCum] = (0, waterfallRenderer_1.computeWaterfallExtent)(buffer, this.getY);
|
|
214
|
-
const range = maxCum - minCum;
|
|
215
|
-
const pad = range > 0 ? range * config.extentPadding : 1;
|
|
216
|
-
yDomain = [
|
|
217
|
-
Math.min(0, minCum - Math.abs(pad)),
|
|
218
|
-
Math.max(0, maxCum + Math.abs(pad))
|
|
219
|
-
];
|
|
220
|
-
}
|
|
221
|
-
else if (!yFullySpecified && yDomain[0] !== Infinity) {
|
|
222
|
-
// Expand extent to include bounds/uncertainty offsets
|
|
223
|
-
if (this.getBounds) {
|
|
224
|
-
const data = buffer.toArray();
|
|
225
|
-
for (const d of data) {
|
|
226
|
-
const y = this.getY(d);
|
|
227
|
-
const offset = this.getBounds(d);
|
|
228
|
-
if (y == null || Number.isNaN(y) || !offset)
|
|
229
|
-
continue;
|
|
230
|
-
if (y + offset > yDomain[1])
|
|
231
|
-
yDomain[1] = y + offset;
|
|
232
|
-
if (y - offset < yDomain[0])
|
|
233
|
-
yDomain[0] = y - offset;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
const range = yDomain[1] - yDomain[0];
|
|
237
|
-
const pad = range > 0 ? range * config.extentPadding : 1;
|
|
238
|
-
// Only pad the data-derived side; preserve user-specified bounds
|
|
239
|
-
const userMin = config.yExtent?.[0];
|
|
240
|
-
const userMax = config.yExtent?.[1];
|
|
241
|
-
yDomain = [
|
|
242
|
-
userMin != null ? yDomain[0] : yDomain[0] - pad,
|
|
243
|
-
userMax != null ? yDomain[1] : yDomain[1] + pad
|
|
244
|
-
];
|
|
245
|
-
}
|
|
246
|
-
// Handle degenerate extents
|
|
247
|
-
if (xDomain[0] === Infinity || xDomain[1] === -Infinity)
|
|
248
|
-
xDomain = [0, 1];
|
|
249
|
-
if (yDomain[0] === Infinity || yDomain[1] === -Infinity)
|
|
250
|
-
yDomain = [0, 1];
|
|
251
|
-
// Build scales
|
|
252
|
-
// For streaming charts, use time/value axes based on arrowOfTime
|
|
253
|
-
const isStreaming = config.arrowOfTime !== undefined;
|
|
254
|
-
if (isStreaming) {
|
|
255
|
-
const timeAxis = getTimeAxis(config.arrowOfTime);
|
|
256
|
-
if (timeAxis === "x") {
|
|
257
|
-
const xRange = config.arrowOfTime === "right"
|
|
258
|
-
? [0, layout.width]
|
|
259
|
-
: [layout.width, 0];
|
|
260
|
-
this.scales = {
|
|
261
|
-
x: (0, d3_scale_1.scaleLinear)().domain(xDomain).range(xRange),
|
|
262
|
-
y: (0, d3_scale_1.scaleLinear)().domain(yDomain).range([layout.height, 0])
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
const yRange = config.arrowOfTime === "down"
|
|
267
|
-
? [0, layout.height]
|
|
268
|
-
: [layout.height, 0];
|
|
269
|
-
this.scales = {
|
|
270
|
-
x: (0, d3_scale_1.scaleLinear)().domain(yDomain).range([0, layout.width]),
|
|
271
|
-
y: (0, d3_scale_1.scaleLinear)().domain(xDomain).range(yRange)
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
else {
|
|
276
|
-
this.scales = {
|
|
277
|
-
x: (0, d3_scale_1.scaleLinear)().domain(xDomain).range([0, layout.width]),
|
|
278
|
-
y: (0, d3_scale_1.scaleLinear)().domain(yDomain).range([layout.height, 0])
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
// Snapshot positions for transition animation (before rebuild)
|
|
282
|
-
if (this.config.transition && this.scene.length > 0) {
|
|
283
|
-
this.snapshotPositions();
|
|
284
|
-
}
|
|
285
|
-
// Build scene graph based on chart type
|
|
286
|
-
const data = buffer.toArray();
|
|
287
|
-
this.scene = this.buildSceneNodes(layout);
|
|
288
|
-
// Apply decay opacity to discrete nodes
|
|
289
|
-
if (this.config.decay) {
|
|
290
|
-
this.applyDecay(this.scene, data);
|
|
291
|
-
}
|
|
292
|
-
// Apply pulse glow to discrete nodes
|
|
293
|
-
if (this.config.pulse) {
|
|
294
|
-
this.applyPulse(this.scene, data);
|
|
295
|
-
}
|
|
296
|
-
// Start transition animation from old to new positions
|
|
297
|
-
if (this.config.transition && this.prevPositionMap.size > 0) {
|
|
298
|
-
this.startTransition();
|
|
299
|
-
}
|
|
300
|
-
this.version++;
|
|
301
|
-
}
|
|
302
|
-
buildSceneNodes(layout) {
|
|
303
|
-
const { config, buffer, scales } = this;
|
|
304
|
-
if (!scales || buffer.size === 0)
|
|
305
|
-
return [];
|
|
306
|
-
const data = buffer.toArray();
|
|
307
|
-
switch (config.chartType) {
|
|
308
|
-
case "line":
|
|
309
|
-
return this.buildLineScene(data);
|
|
310
|
-
case "area":
|
|
311
|
-
return this.buildAreaScene(data);
|
|
312
|
-
case "stackedarea":
|
|
313
|
-
return this.buildStackedAreaScene(data);
|
|
314
|
-
case "scatter":
|
|
315
|
-
case "bubble":
|
|
316
|
-
return this.buildPointScene(data);
|
|
317
|
-
case "heatmap":
|
|
318
|
-
return this.buildHeatmapScene(data, layout);
|
|
319
|
-
case "bar":
|
|
320
|
-
return this.buildBarScene(data);
|
|
321
|
-
case "swarm":
|
|
322
|
-
return this.buildSwarmScene(data);
|
|
323
|
-
case "waterfall":
|
|
324
|
-
return this.buildWaterfallScene(data, layout);
|
|
325
|
-
case "candlestick":
|
|
326
|
-
return this.buildCandlestickScene(data, layout);
|
|
327
|
-
default:
|
|
328
|
-
return [];
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
buildLineScene(data) {
|
|
332
|
-
const groups = this.groupData(data);
|
|
333
|
-
const nodes = [];
|
|
334
|
-
// Extract color thresholds from annotations (if any)
|
|
335
|
-
const colorThresholds = this.config.annotations
|
|
336
|
-
?.filter((a) => a.type === "threshold" && a.color)
|
|
337
|
-
.map((a) => ({
|
|
338
|
-
value: a.value,
|
|
339
|
-
color: a.color,
|
|
340
|
-
thresholdType: (a.thresholdType || "greater")
|
|
341
|
-
}));
|
|
342
|
-
// Build bounds areas first so they render behind lines
|
|
343
|
-
if (this.getBounds) {
|
|
344
|
-
for (const g of groups) {
|
|
345
|
-
const boundsNode = this.buildBoundsForGroup(g.data, g.key);
|
|
346
|
-
if (boundsNode)
|
|
347
|
-
nodes.push(boundsNode);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
for (const g of groups) {
|
|
351
|
-
const style = this.resolveLineStyle(g.key, g.data[0]);
|
|
352
|
-
const lineNode = (0, SceneGraph_1.buildLineNode)(g.data, this.scales, this.getX, this.getY, style, g.key);
|
|
353
|
-
// Attach threshold info for the renderer
|
|
354
|
-
if (colorThresholds && colorThresholds.length > 0) {
|
|
355
|
-
lineNode.colorThresholds = colorThresholds;
|
|
356
|
-
}
|
|
357
|
-
nodes.push(lineNode);
|
|
358
|
-
}
|
|
359
|
-
return nodes;
|
|
360
|
-
}
|
|
361
|
-
buildAreaScene(data) {
|
|
362
|
-
const groups = this.groupData(data);
|
|
363
|
-
const nodes = [];
|
|
364
|
-
// Use the bottom of the y domain as the baseline so areas fill to the chart edge
|
|
365
|
-
const yDomain = this.scales.y.domain();
|
|
366
|
-
const baseline = yDomain[0];
|
|
367
|
-
for (const g of groups) {
|
|
368
|
-
const style = this.resolveAreaStyle(g.key, g.data[0]);
|
|
369
|
-
nodes.push((0, SceneGraph_1.buildAreaNode)(g.data, this.scales, this.getX, this.getY, baseline, style, g.key));
|
|
370
|
-
}
|
|
371
|
-
return nodes;
|
|
372
|
-
}
|
|
373
|
-
buildStackedAreaScene(data) {
|
|
374
|
-
const groups = this.groupData(data);
|
|
375
|
-
const styleFn = (group, sampleDatum) => this.resolveAreaStyle(group, sampleDatum);
|
|
376
|
-
return (0, SceneGraph_1.buildStackedAreaNodes)(groups, this.scales, this.getX, this.getY, styleFn, this.config.normalize);
|
|
377
|
-
}
|
|
378
|
-
buildPointScene(data) {
|
|
379
|
-
const nodes = [];
|
|
380
|
-
const defaultR = this.config.chartType === "bubble" ? 10 : 5;
|
|
381
|
-
const sizeRange = this.config.sizeRange || [3, 15];
|
|
382
|
-
// Compute size scale if sizeAccessor is set and no pointStyle handles it
|
|
383
|
-
let sizeScale = null;
|
|
384
|
-
if (this.getSize && !this.config.pointStyle) {
|
|
385
|
-
const sizes = data.map(d => this.getSize(d)).filter(s => s != null && !Number.isNaN(s));
|
|
386
|
-
if (sizes.length > 0) {
|
|
387
|
-
const minSize = Math.min(...sizes);
|
|
388
|
-
const maxSize = Math.max(...sizes);
|
|
389
|
-
sizeScale = (s) => {
|
|
390
|
-
if (minSize === maxSize)
|
|
391
|
-
return (sizeRange[0] + sizeRange[1]) / 2;
|
|
392
|
-
return sizeRange[0] + ((s - minSize) / (maxSize - minSize)) * (sizeRange[1] - sizeRange[0]);
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
// Build color map from colorAccessor if no pointStyle handles it
|
|
397
|
-
let colorMap = null;
|
|
398
|
-
if (this.getColor && !this.config.pointStyle) {
|
|
399
|
-
const categories = new Set();
|
|
400
|
-
for (const d of data) {
|
|
401
|
-
const c = this.getColor(d);
|
|
402
|
-
if (c)
|
|
403
|
-
categories.add(c);
|
|
404
|
-
}
|
|
405
|
-
const palette = Array.isArray(this.config.colorScheme) ? this.config.colorScheme
|
|
406
|
-
: ["#4e79a7", "#f28e2b", "#e15759", "#76b7b2", "#59a14f", "#edc948", "#b07aa1", "#ff9da7", "#9c755f", "#bab0ac"];
|
|
407
|
-
colorMap = new Map();
|
|
408
|
-
let ci = 0;
|
|
409
|
-
for (const cat of categories) {
|
|
410
|
-
colorMap.set(cat, palette[ci % palette.length]);
|
|
411
|
-
ci++;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
for (const d of data) {
|
|
415
|
-
let style = this.config.pointStyle ? this.config.pointStyle(d) : { fill: "#4e79a7", opacity: 0.8 };
|
|
416
|
-
// Apply size from accessor if pointStyle doesn't provide it
|
|
417
|
-
let r = style.r || defaultR;
|
|
418
|
-
if (sizeScale && this.getSize) {
|
|
419
|
-
const sizeVal = this.getSize(d);
|
|
420
|
-
if (sizeVal != null && !Number.isNaN(sizeVal)) {
|
|
421
|
-
r = sizeScale(sizeVal);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
// Apply color from accessor if pointStyle doesn't provide a custom fill
|
|
425
|
-
if (colorMap && this.getColor) {
|
|
426
|
-
const colorVal = this.getColor(d);
|
|
427
|
-
if (colorVal && colorMap.has(colorVal)) {
|
|
428
|
-
style = { ...style, fill: colorMap.get(colorVal) };
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
const node = (0, SceneGraph_1.buildPointNode)(d, this.scales, this.getX, this.getY, r, style);
|
|
432
|
-
if (node)
|
|
433
|
-
nodes.push(node);
|
|
434
|
-
}
|
|
435
|
-
return nodes;
|
|
436
|
-
}
|
|
437
|
-
buildHeatmapScene(data, layout) {
|
|
438
|
-
const nodes = [];
|
|
439
|
-
// Streaming heatmap: 2D grid binning with aggregation
|
|
440
|
-
if (this.config.heatmapAggregation) {
|
|
441
|
-
return this.buildStreamingHeatmapScene(data, layout);
|
|
442
|
-
}
|
|
443
|
-
const getVal = (0, accessorUtils_1.resolveAccessor)(this.config.valueAccessor, "value");
|
|
444
|
-
// Determine grid dimensions from unique x/y values
|
|
445
|
-
const xSet = new Set();
|
|
446
|
-
const ySet = new Set();
|
|
447
|
-
for (const d of data) {
|
|
448
|
-
xSet.add(this.getX(d));
|
|
449
|
-
ySet.add(this.getY(d));
|
|
450
|
-
}
|
|
451
|
-
const xValues = Array.from(xSet).sort((a, b) => a - b);
|
|
452
|
-
const yValues = Array.from(ySet).sort((a, b) => a - b);
|
|
453
|
-
if (xValues.length === 0 || yValues.length === 0)
|
|
454
|
-
return nodes;
|
|
455
|
-
const cellW = layout.width / xValues.length;
|
|
456
|
-
const cellH = layout.height / yValues.length;
|
|
457
|
-
// Build value lookup
|
|
458
|
-
const valueMap = new Map();
|
|
459
|
-
for (const d of data) {
|
|
460
|
-
const key = `${this.getX(d)}_${this.getY(d)}`;
|
|
461
|
-
valueMap.set(key, { val: getVal(d), datum: d });
|
|
462
|
-
}
|
|
463
|
-
// Compute value range for color
|
|
464
|
-
let minVal = Infinity;
|
|
465
|
-
let maxVal = -Infinity;
|
|
466
|
-
for (const { val } of valueMap.values()) {
|
|
467
|
-
if (val < minVal)
|
|
468
|
-
minVal = val;
|
|
469
|
-
if (val > maxVal)
|
|
470
|
-
maxVal = val;
|
|
471
|
-
}
|
|
472
|
-
const valRange = maxVal - minVal || 1;
|
|
473
|
-
for (let xi = 0; xi < xValues.length; xi++) {
|
|
474
|
-
for (let yi = 0; yi < yValues.length; yi++) {
|
|
475
|
-
const key = `${xValues[xi]}_${yValues[yi]}`;
|
|
476
|
-
const entry = valueMap.get(key);
|
|
477
|
-
if (!entry)
|
|
478
|
-
continue;
|
|
479
|
-
const t = (entry.val - minVal) / valRange;
|
|
480
|
-
// Default to blues interpolation
|
|
481
|
-
const r = Math.round(220 - 180 * t);
|
|
482
|
-
const g = Math.round(220 - 100 * t);
|
|
483
|
-
const b = Math.round(255 - 50 * t);
|
|
484
|
-
const fill = `rgb(${r},${g},${b})`;
|
|
485
|
-
nodes.push((0, SceneGraph_1.buildHeatcellNode)(xi * cellW, (yValues.length - 1 - yi) * cellH, cellW, cellH, fill, entry.datum));
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
return nodes;
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* Streaming heatmap: discretize continuous x/y into a grid and aggregate.
|
|
492
|
-
*/
|
|
493
|
-
buildStreamingHeatmapScene(data, layout) {
|
|
494
|
-
const nodes = [];
|
|
495
|
-
const xBins = this.config.heatmapXBins ?? 20;
|
|
496
|
-
const yBins = this.config.heatmapYBins ?? 20;
|
|
497
|
-
const agg = this.config.heatmapAggregation ?? "count";
|
|
498
|
-
const getVal = (0, accessorUtils_1.resolveAccessor)(this.config.valueAccessor, "value");
|
|
499
|
-
if (!this.scales || data.length === 0)
|
|
500
|
-
return nodes;
|
|
501
|
-
const [xMin, xMax] = this.scales.x.domain();
|
|
502
|
-
const [yMin, yMax] = this.scales.y.domain();
|
|
503
|
-
const xRange = xMax - xMin || 1;
|
|
504
|
-
const yRange = yMax - yMin || 1;
|
|
505
|
-
const xBinSize = xRange / xBins;
|
|
506
|
-
const yBinSize = yRange / yBins;
|
|
507
|
-
// Grid cells: [xi][yi] → { sum, count }
|
|
508
|
-
const grid = new Map();
|
|
509
|
-
for (const d of data) {
|
|
510
|
-
const xVal = this.getX(d);
|
|
511
|
-
const yVal = this.getY(d);
|
|
512
|
-
const xi = Math.min(Math.floor((xVal - xMin) / xBinSize), xBins - 1);
|
|
513
|
-
const yi = Math.min(Math.floor((yVal - yMin) / yBinSize), yBins - 1);
|
|
514
|
-
if (xi < 0 || yi < 0)
|
|
515
|
-
continue;
|
|
516
|
-
const key = `${xi}_${yi}`;
|
|
517
|
-
let cell = grid.get(key);
|
|
518
|
-
if (!cell) {
|
|
519
|
-
cell = { sum: 0, count: 0, data: [] };
|
|
520
|
-
grid.set(key, cell);
|
|
521
|
-
}
|
|
522
|
-
cell.count++;
|
|
523
|
-
cell.sum += getVal(d);
|
|
524
|
-
cell.data.push(d);
|
|
525
|
-
}
|
|
526
|
-
// Compute aggregated values and find range
|
|
527
|
-
let minVal = Infinity;
|
|
528
|
-
let maxVal = -Infinity;
|
|
529
|
-
const cellValues = new Map();
|
|
530
|
-
for (const [key, cell] of grid) {
|
|
531
|
-
let val;
|
|
532
|
-
switch (agg) {
|
|
533
|
-
case "sum":
|
|
534
|
-
val = cell.sum;
|
|
535
|
-
break;
|
|
536
|
-
case "mean":
|
|
537
|
-
val = cell.count > 0 ? cell.sum / cell.count : 0;
|
|
538
|
-
break;
|
|
539
|
-
default:
|
|
540
|
-
val = cell.count;
|
|
541
|
-
break;
|
|
542
|
-
}
|
|
543
|
-
cellValues.set(key, val);
|
|
544
|
-
if (val < minVal)
|
|
545
|
-
minVal = val;
|
|
546
|
-
if (val > maxVal)
|
|
547
|
-
maxVal = val;
|
|
548
|
-
}
|
|
549
|
-
const valRange = maxVal - minVal || 1;
|
|
550
|
-
const cellW = layout.width / xBins;
|
|
551
|
-
const cellH = layout.height / yBins;
|
|
552
|
-
for (const [key, val] of cellValues) {
|
|
553
|
-
const [xiStr, yiStr] = key.split("_");
|
|
554
|
-
const xi = +xiStr;
|
|
555
|
-
const yi = +yiStr;
|
|
556
|
-
const t = (val - minVal) / valRange;
|
|
557
|
-
const r = Math.round(220 - 180 * t);
|
|
558
|
-
const g = Math.round(220 - 100 * t);
|
|
559
|
-
const b = Math.round(255 - 50 * t);
|
|
560
|
-
const fill = `rgb(${r},${g},${b})`;
|
|
561
|
-
const cell = grid.get(key);
|
|
562
|
-
nodes.push((0, SceneGraph_1.buildHeatcellNode)(xi * cellW, (yBins - 1 - yi) * cellH, cellW, cellH, fill, { xi, yi, value: val, count: cell.count, sum: cell.sum, data: cell.data }));
|
|
563
|
-
}
|
|
564
|
-
return nodes;
|
|
565
|
-
}
|
|
566
|
-
buildBarScene(data) {
|
|
567
|
-
if (!this.config.binSize)
|
|
568
|
-
return [];
|
|
569
|
-
const bins = (0, BinAccumulator_1.computeBins)(data, this.getX, this.getY, this.config.binSize, this.getCategory);
|
|
570
|
-
if (bins.size === 0)
|
|
571
|
-
return [];
|
|
572
|
-
// Establish a global category order that is stable across frames.
|
|
573
|
-
// Use barColors keys first (preserves user-specified order), then any
|
|
574
|
-
// additional categories sorted alphabetically. This prevents flicker
|
|
575
|
-
// when bins partially exit the sliding window and their per-category
|
|
576
|
-
// values shrink/disappear — without a fixed order the Map iteration
|
|
577
|
-
// order shifts and stacked segments jump around.
|
|
578
|
-
let categoryOrder = null;
|
|
579
|
-
if (this.getCategory) {
|
|
580
|
-
const allCategories = new Set();
|
|
581
|
-
for (const bin of bins.values()) {
|
|
582
|
-
for (const cat of bin.categories.keys()) {
|
|
583
|
-
allCategories.add(cat);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
const colorKeys = this.config.barColors ? Object.keys(this.config.barColors) : [];
|
|
587
|
-
const listed = new Set(colorKeys);
|
|
588
|
-
const unlisted = Array.from(allCategories).filter(c => !listed.has(c)).sort();
|
|
589
|
-
categoryOrder = [...colorKeys.filter(k => allCategories.has(k)), ...unlisted];
|
|
590
|
-
}
|
|
591
|
-
const nodes = [];
|
|
592
|
-
const scales = this.scales;
|
|
593
|
-
const [domainMin, domainMax] = scales.x.domain();
|
|
594
|
-
const gap = this.config.barColors ? 1 : 1;
|
|
595
|
-
for (const bin of bins.values()) {
|
|
596
|
-
const clampedStart = Math.max(bin.start, domainMin);
|
|
597
|
-
const clampedEnd = Math.min(bin.end, domainMax);
|
|
598
|
-
if (clampedStart >= clampedEnd)
|
|
599
|
-
continue;
|
|
600
|
-
const rawX0 = scales.x(clampedStart);
|
|
601
|
-
const rawX1 = scales.x(clampedEnd);
|
|
602
|
-
const x0 = Math.min(rawX0, rawX1) + gap / 2;
|
|
603
|
-
const x1 = Math.max(rawX0, rawX1) - gap / 2;
|
|
604
|
-
const barWidth = x1 - x0;
|
|
605
|
-
if (barWidth <= 0)
|
|
606
|
-
continue;
|
|
607
|
-
if (categoryOrder && bin.categories.size > 0) {
|
|
608
|
-
let cumulativeBase = 0;
|
|
609
|
-
for (const cat of categoryOrder) {
|
|
610
|
-
const catVal = bin.categories.get(cat) || 0;
|
|
611
|
-
if (catVal === 0)
|
|
612
|
-
continue;
|
|
613
|
-
const yBottom = scales.y(cumulativeBase);
|
|
614
|
-
const yTop = scales.y(cumulativeBase + catVal);
|
|
615
|
-
const rectY = Math.min(yBottom, yTop);
|
|
616
|
-
const rectH = Math.abs(yBottom - yTop);
|
|
617
|
-
nodes.push((0, SceneGraph_1.buildRectNode)(x0, rectY, barWidth, rectH, { fill: this.config.barColors?.[cat] || "#4e79a7" }, { binStart: bin.start, binEnd: bin.end, total: bin.total, category: cat, categoryValue: catVal }, cat));
|
|
618
|
-
cumulativeBase += catVal;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
else {
|
|
622
|
-
const yZero = scales.y(0);
|
|
623
|
-
const yTop = scales.y(bin.total);
|
|
624
|
-
const rectY = Math.min(yZero, yTop);
|
|
625
|
-
const rectH = Math.abs(yZero - yTop);
|
|
626
|
-
nodes.push((0, SceneGraph_1.buildRectNode)(x0, rectY, barWidth, rectH, { fill: "#007bff" }, { binStart: bin.start, binEnd: bin.end, total: bin.total }));
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
return nodes;
|
|
630
|
-
}
|
|
631
|
-
buildSwarmScene(data) {
|
|
632
|
-
const nodes = [];
|
|
633
|
-
const swarm = this.config.swarmStyle || {};
|
|
634
|
-
const radius = swarm.radius ?? 3;
|
|
635
|
-
const defaultFill = swarm.fill ?? "#007bff";
|
|
636
|
-
const opacity = swarm.opacity ?? 0.7;
|
|
637
|
-
const stroke = swarm.stroke;
|
|
638
|
-
const strokeWidth = swarm.strokeWidth;
|
|
639
|
-
for (const d of data) {
|
|
640
|
-
const xVal = this.getX(d);
|
|
641
|
-
const yVal = this.getY(d);
|
|
642
|
-
if (yVal == null || Number.isNaN(yVal))
|
|
643
|
-
continue;
|
|
644
|
-
const x = this.scales.x(xVal);
|
|
645
|
-
const y = this.scales.y(yVal);
|
|
646
|
-
let fill = defaultFill;
|
|
647
|
-
if (this.getCategory) {
|
|
648
|
-
const cat = this.getCategory(d);
|
|
649
|
-
fill = this.config.barColors?.[cat] || fill;
|
|
650
|
-
}
|
|
651
|
-
nodes.push({
|
|
652
|
-
type: "point",
|
|
653
|
-
x, y, r: radius,
|
|
654
|
-
style: { fill, opacity, stroke, strokeWidth },
|
|
655
|
-
datum: d
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
return nodes;
|
|
659
|
-
}
|
|
660
|
-
buildWaterfallScene(data, layout) {
|
|
661
|
-
const nodes = [];
|
|
662
|
-
const scales = this.scales;
|
|
663
|
-
// Filter valid data
|
|
664
|
-
const arr = data.filter(d => {
|
|
665
|
-
const v = this.getY(d);
|
|
666
|
-
return v != null && !Number.isNaN(v);
|
|
667
|
-
});
|
|
668
|
-
if (arr.length === 0)
|
|
669
|
-
return nodes;
|
|
670
|
-
const gap = 1;
|
|
671
|
-
let baseline = 0;
|
|
672
|
-
for (let i = 0; i < arr.length; i++) {
|
|
673
|
-
const d = arr[i];
|
|
674
|
-
const t = this.getX(d);
|
|
675
|
-
const delta = this.getY(d);
|
|
676
|
-
const cumEnd = baseline + delta;
|
|
677
|
-
// Compute bar width from time gap
|
|
678
|
-
let barWidthTime;
|
|
679
|
-
if (i < arr.length - 1) {
|
|
680
|
-
barWidthTime = this.getX(arr[i + 1]) - t;
|
|
681
|
-
}
|
|
682
|
-
else if (i > 0) {
|
|
683
|
-
barWidthTime = t - this.getX(arr[i - 1]);
|
|
684
|
-
}
|
|
685
|
-
else {
|
|
686
|
-
barWidthTime = 0;
|
|
687
|
-
}
|
|
688
|
-
const rawX0 = scales.x(t);
|
|
689
|
-
const rawX1 = barWidthTime !== 0 ? scales.x(t + barWidthTime) : rawX0 + layout.width / 10;
|
|
690
|
-
const x0 = Math.min(rawX0, rawX1) + gap / 2;
|
|
691
|
-
const x1 = Math.max(rawX0, rawX1) - gap / 2;
|
|
692
|
-
const barWidth = x1 - x0;
|
|
693
|
-
if (barWidth <= 0) {
|
|
694
|
-
baseline = cumEnd;
|
|
695
|
-
continue;
|
|
696
|
-
}
|
|
697
|
-
const yBaseline = scales.y(baseline);
|
|
698
|
-
const yTop = scales.y(cumEnd);
|
|
699
|
-
const rectY = Math.min(yBaseline, yTop);
|
|
700
|
-
const rectH = Math.abs(yBaseline - yTop);
|
|
701
|
-
const fill = delta >= 0 ? "#28a745" : "#dc3545";
|
|
702
|
-
nodes.push((0, SceneGraph_1.buildRectNode)(x0, rectY, barWidth, rectH, { fill }, { ...d, baseline, cumEnd, delta }));
|
|
703
|
-
baseline = cumEnd;
|
|
704
|
-
}
|
|
705
|
-
return nodes;
|
|
706
|
-
}
|
|
707
|
-
buildCandlestickScene(data, layout) {
|
|
708
|
-
if (!this.getOpen || !this.getHigh || !this.getLow || !this.getClose || !this.scales)
|
|
709
|
-
return [];
|
|
710
|
-
const nodes = [];
|
|
711
|
-
const cs = this.config.candlestickStyle || {};
|
|
712
|
-
const upColor = cs.upColor || "#28a745";
|
|
713
|
-
const downColor = cs.downColor || "#dc3545";
|
|
714
|
-
const wickColor = cs.wickColor || "#333";
|
|
715
|
-
const wickWidth = cs.wickWidth || 1;
|
|
716
|
-
// Compute body width from data spacing
|
|
717
|
-
const sortedX = data
|
|
718
|
-
.map(d => this.getX(d))
|
|
719
|
-
.filter(x => x != null && !Number.isNaN(x))
|
|
720
|
-
.sort((a, b) => a - b);
|
|
721
|
-
let bodyWidth = cs.bodyWidth || 6;
|
|
722
|
-
if (!cs.bodyWidth && sortedX.length > 1) {
|
|
723
|
-
// Auto-size: 60% of the minimum gap between adjacent x values
|
|
724
|
-
let minGap = Infinity;
|
|
725
|
-
for (let i = 1; i < sortedX.length; i++) {
|
|
726
|
-
const gap = Math.abs(this.scales.x(sortedX[i]) - this.scales.x(sortedX[i - 1]));
|
|
727
|
-
if (gap > 0 && gap < minGap)
|
|
728
|
-
minGap = gap;
|
|
729
|
-
}
|
|
730
|
-
if (minGap !== Infinity) {
|
|
731
|
-
bodyWidth = Math.max(2, Math.min(minGap * 0.6, 20));
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
for (const d of data) {
|
|
735
|
-
const xVal = this.getX(d);
|
|
736
|
-
if (xVal == null || Number.isNaN(xVal))
|
|
737
|
-
continue;
|
|
738
|
-
const open = this.getOpen(d);
|
|
739
|
-
const high = this.getHigh(d);
|
|
740
|
-
const low = this.getLow(d);
|
|
741
|
-
const close = this.getClose(d);
|
|
742
|
-
if ([open, high, low, close].some(v => v == null || Number.isNaN(v)))
|
|
743
|
-
continue;
|
|
744
|
-
const isUp = close >= open;
|
|
745
|
-
nodes.push({
|
|
746
|
-
type: "candlestick",
|
|
747
|
-
x: this.scales.x(xVal),
|
|
748
|
-
openY: this.scales.y(open),
|
|
749
|
-
closeY: this.scales.y(close),
|
|
750
|
-
highY: this.scales.y(high),
|
|
751
|
-
lowY: this.scales.y(low),
|
|
752
|
-
bodyWidth,
|
|
753
|
-
upColor,
|
|
754
|
-
downColor,
|
|
755
|
-
wickColor,
|
|
756
|
-
wickWidth,
|
|
757
|
-
isUp,
|
|
758
|
-
datum: d
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
return nodes;
|
|
762
|
-
}
|
|
763
|
-
// ── Bounds helpers ───────────────────────────────────────────────────
|
|
764
|
-
buildBoundsForGroup(data, group) {
|
|
765
|
-
if (!this.getBounds || !this.scales)
|
|
766
|
-
return null;
|
|
767
|
-
const topPath = [];
|
|
768
|
-
const bottomPath = [];
|
|
769
|
-
for (const d of data) {
|
|
770
|
-
const x = this.getX(d);
|
|
771
|
-
const y = this.getY(d);
|
|
772
|
-
if (x == null || y == null || Number.isNaN(x) || Number.isNaN(y))
|
|
773
|
-
continue;
|
|
774
|
-
const offset = this.getBounds(d);
|
|
775
|
-
const px = this.scales.x(x);
|
|
776
|
-
if (!offset || offset === 0) {
|
|
777
|
-
// No bounds at this point — collapse to the line
|
|
778
|
-
const py = this.scales.y(y);
|
|
779
|
-
topPath.push([px, py]);
|
|
780
|
-
bottomPath.push([px, py]);
|
|
781
|
-
}
|
|
782
|
-
else {
|
|
783
|
-
topPath.push([px, this.scales.y(y + offset)]);
|
|
784
|
-
bottomPath.push([px, this.scales.y(y - offset)]);
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
if (topPath.length < 2)
|
|
788
|
-
return null;
|
|
789
|
-
return {
|
|
790
|
-
type: "area",
|
|
791
|
-
topPath,
|
|
792
|
-
bottomPath,
|
|
793
|
-
style: this.resolveBoundsStyle(group, data[0]),
|
|
794
|
-
datum: data,
|
|
795
|
-
group,
|
|
796
|
-
interactive: false
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
resolveBoundsStyle(group, sampleDatum) {
|
|
800
|
-
const bs = this.config.boundsStyle;
|
|
801
|
-
if (typeof bs === "function") {
|
|
802
|
-
return bs(sampleDatum || {}, group);
|
|
803
|
-
}
|
|
804
|
-
if (bs && typeof bs === "object") {
|
|
805
|
-
return bs;
|
|
806
|
-
}
|
|
807
|
-
// Default: match line color with low opacity
|
|
808
|
-
const lineStyle = this.resolveLineStyle(group, sampleDatum);
|
|
809
|
-
return {
|
|
810
|
-
fill: lineStyle.stroke || "#4e79a7",
|
|
811
|
-
fillOpacity: 0.2,
|
|
812
|
-
stroke: "none"
|
|
813
|
-
};
|
|
814
|
-
}
|
|
815
|
-
// ── Decay ────────────────────────────────────────────────────────────
|
|
816
|
-
/**
|
|
817
|
-
* Compute decay opacity for a datum at `bufferIndex` out of `bufferSize` items.
|
|
818
|
-
* Index 0 = oldest, bufferSize-1 = newest. Returns 0–1.
|
|
819
|
-
*/
|
|
820
|
-
computeDecayOpacity(bufferIndex, bufferSize) {
|
|
821
|
-
const decay = this.config.decay;
|
|
822
|
-
if (!decay || bufferSize <= 1)
|
|
823
|
-
return 1;
|
|
824
|
-
const minOpacity = decay.minOpacity ?? 0.1;
|
|
825
|
-
// age: 0 = newest, bufferSize-1 = oldest
|
|
826
|
-
const age = bufferSize - 1 - bufferIndex;
|
|
827
|
-
switch (decay.type) {
|
|
828
|
-
case "linear": {
|
|
829
|
-
const t = 1 - age / (bufferSize - 1);
|
|
830
|
-
return minOpacity + t * (1 - minOpacity);
|
|
831
|
-
}
|
|
832
|
-
case "exponential": {
|
|
833
|
-
const halfLife = decay.halfLife ?? bufferSize / 2;
|
|
834
|
-
const t = Math.pow(0.5, age / halfLife);
|
|
835
|
-
return minOpacity + t * (1 - minOpacity);
|
|
836
|
-
}
|
|
837
|
-
case "step": {
|
|
838
|
-
const threshold = decay.stepThreshold ?? bufferSize * 0.5;
|
|
839
|
-
return age < threshold ? 1 : minOpacity;
|
|
840
|
-
}
|
|
841
|
-
default:
|
|
842
|
-
return 1;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
/**
|
|
846
|
-
* Apply decay opacity to a list of discrete scene nodes.
|
|
847
|
-
* Uses the datum's index in the buffer data array.
|
|
848
|
-
*/
|
|
849
|
-
applyDecay(nodes, data) {
|
|
850
|
-
if (!this.config.decay)
|
|
851
|
-
return;
|
|
852
|
-
const bufferSize = data.length;
|
|
853
|
-
if (bufferSize <= 1)
|
|
854
|
-
return;
|
|
855
|
-
// Build datum→index lookup
|
|
856
|
-
const indexMap = new Map();
|
|
857
|
-
for (let i = 0; i < data.length; i++) {
|
|
858
|
-
indexMap.set(data[i], i);
|
|
859
|
-
}
|
|
860
|
-
for (const node of nodes) {
|
|
861
|
-
if (node.type === "line" || node.type === "area")
|
|
862
|
-
continue;
|
|
863
|
-
const idx = indexMap.get(node.datum);
|
|
864
|
-
if (idx == null)
|
|
865
|
-
continue;
|
|
866
|
-
const decayOpacity = this.computeDecayOpacity(idx, bufferSize);
|
|
867
|
-
if (node.type === "heatcell") {
|
|
868
|
-
;
|
|
869
|
-
node.style = { opacity: decayOpacity };
|
|
870
|
-
}
|
|
871
|
-
else if (node.type === "candlestick") {
|
|
872
|
-
// Candlestick doesn't have a style object — store opacity for renderer
|
|
873
|
-
;
|
|
874
|
-
node._decayOpacity = decayOpacity;
|
|
875
|
-
}
|
|
876
|
-
else {
|
|
877
|
-
const baseOpacity = node.style?.opacity ?? 1;
|
|
878
|
-
node.style = { ...node.style, opacity: baseOpacity * decayOpacity };
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
// ── Pulse ───────────────────────────────────────────────────────────
|
|
883
|
-
/**
|
|
884
|
-
* Compute pulse intensity for a datum inserted at `insertTime`.
|
|
885
|
-
* Returns 0–1 (1 = just inserted, 0 = pulse expired).
|
|
886
|
-
*/
|
|
887
|
-
computePulseIntensity(insertTime, now) {
|
|
888
|
-
const pulse = this.config.pulse;
|
|
889
|
-
if (!pulse)
|
|
890
|
-
return 0;
|
|
891
|
-
const duration = pulse.duration ?? 500;
|
|
892
|
-
const age = now - insertTime;
|
|
893
|
-
if (age >= duration)
|
|
894
|
-
return 0;
|
|
895
|
-
return 1 - age / duration;
|
|
896
|
-
}
|
|
897
|
-
/**
|
|
898
|
-
* Apply pulse glow to discrete scene nodes.
|
|
899
|
-
*/
|
|
900
|
-
applyPulse(nodes, data) {
|
|
901
|
-
if (!this.config.pulse || !this.timestampBuffer)
|
|
902
|
-
return;
|
|
903
|
-
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
904
|
-
const pulseColor = this.config.pulse.color ?? "rgba(255,255,255,0.6)";
|
|
905
|
-
// Build datum→index lookup
|
|
906
|
-
const indexMap = new Map();
|
|
907
|
-
for (let i = 0; i < data.length; i++) {
|
|
908
|
-
indexMap.set(data[i], i);
|
|
909
|
-
}
|
|
910
|
-
for (const node of nodes) {
|
|
911
|
-
if (node.type === "line" || node.type === "area")
|
|
912
|
-
continue;
|
|
913
|
-
const idx = indexMap.get(node.datum);
|
|
914
|
-
if (idx == null)
|
|
915
|
-
continue;
|
|
916
|
-
const insertTime = this.timestampBuffer.get(idx);
|
|
917
|
-
if (insertTime == null)
|
|
918
|
-
continue;
|
|
919
|
-
const intensity = this.computePulseIntensity(insertTime, now);
|
|
920
|
-
if (intensity > 0) {
|
|
921
|
-
node._pulseIntensity = intensity;
|
|
922
|
-
node._pulseColor = pulseColor;
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
/**
|
|
927
|
-
* Returns true if there are active pulse animations that need continuous rendering.
|
|
928
|
-
*/
|
|
929
|
-
get hasActivePulses() {
|
|
930
|
-
if (!this.config.pulse || !this.timestampBuffer || this.timestampBuffer.size === 0)
|
|
931
|
-
return false;
|
|
932
|
-
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
933
|
-
const duration = this.config.pulse.duration ?? 500;
|
|
934
|
-
const newest = this.timestampBuffer.peek();
|
|
935
|
-
return newest != null && (now - newest) < duration;
|
|
936
|
-
}
|
|
937
|
-
// ── Transitions ─────────────────────────────────────────────────────
|
|
938
|
-
/**
|
|
939
|
-
* Snapshot current scene node positions before rebuild.
|
|
940
|
-
*/
|
|
941
|
-
snapshotPositions() {
|
|
942
|
-
this.prevPositionMap.clear();
|
|
943
|
-
for (let i = 0; i < this.scene.length; i++) {
|
|
944
|
-
const node = this.scene[i];
|
|
945
|
-
const key = this.getNodeIdentity(node, i);
|
|
946
|
-
if (!key)
|
|
947
|
-
continue;
|
|
948
|
-
if (node.type === "point") {
|
|
949
|
-
this.prevPositionMap.set(key, { x: node.x, y: node.y, r: node.r });
|
|
950
|
-
}
|
|
951
|
-
else if (node.type === "rect") {
|
|
952
|
-
this.prevPositionMap.set(key, { x: node.x, y: node.y, w: node.w, h: node.h });
|
|
953
|
-
}
|
|
954
|
-
else if (node.type === "heatcell") {
|
|
955
|
-
this.prevPositionMap.set(key, { x: node.x, y: node.y, w: node.w, h: node.h });
|
|
956
|
-
}
|
|
957
|
-
else if (node.type === "candlestick") {
|
|
958
|
-
this.prevPositionMap.set(key, { x: node.x, y: node.openY });
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
/**
|
|
963
|
-
* Get a stable identity key for a scene node.
|
|
964
|
-
*/
|
|
965
|
-
getNodeIdentity(node, index) {
|
|
966
|
-
switch (node.type) {
|
|
967
|
-
case "point":
|
|
968
|
-
return `p:${node.datum === undefined ? index : this.getX(node.datum)}_${this.getY(node.datum)}`;
|
|
969
|
-
case "rect":
|
|
970
|
-
return `r:${node.group || ""}:${node.datum?.binStart ?? node.datum?.category ?? index}`;
|
|
971
|
-
case "heatcell":
|
|
972
|
-
return `h:${node.x}_${node.y}`;
|
|
973
|
-
case "candlestick":
|
|
974
|
-
return `c:${this.getX(node.datum)}`;
|
|
975
|
-
default:
|
|
976
|
-
return null;
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
/**
|
|
980
|
-
* After scene rebuild, set up transition from old to new positions.
|
|
981
|
-
*/
|
|
982
|
-
startTransition() {
|
|
983
|
-
if (!this.config.transition || this.prevPositionMap.size === 0)
|
|
984
|
-
return;
|
|
985
|
-
const duration = this.config.transition.duration ?? 300;
|
|
986
|
-
let hasChanges = false;
|
|
987
|
-
for (let i = 0; i < this.scene.length; i++) {
|
|
988
|
-
const node = this.scene[i];
|
|
989
|
-
const key = this.getNodeIdentity(node, i);
|
|
990
|
-
if (!key)
|
|
991
|
-
continue;
|
|
992
|
-
const prev = this.prevPositionMap.get(key);
|
|
993
|
-
if (!prev)
|
|
994
|
-
continue;
|
|
995
|
-
// Store target positions and restore previous for animation start
|
|
996
|
-
if (node.type === "point") {
|
|
997
|
-
const target = { x: node.x, y: node.y, r: node.r };
|
|
998
|
-
if (prev.x !== target.x || prev.y !== target.y) {
|
|
999
|
-
node._targetX = target.x;
|
|
1000
|
-
node._targetY = target.y;
|
|
1001
|
-
node._targetR = target.r;
|
|
1002
|
-
node.x = prev.x;
|
|
1003
|
-
node.y = prev.y;
|
|
1004
|
-
node.r = prev.r ?? node.r;
|
|
1005
|
-
hasChanges = true;
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
else if (node.type === "rect") {
|
|
1009
|
-
const target = { x: node.x, y: node.y, w: node.w, h: node.h };
|
|
1010
|
-
if (prev.x !== target.x || prev.y !== target.y || prev.w !== target.w || prev.h !== target.h) {
|
|
1011
|
-
node._targetX = target.x;
|
|
1012
|
-
node._targetY = target.y;
|
|
1013
|
-
node._targetW = target.w;
|
|
1014
|
-
node._targetH = target.h;
|
|
1015
|
-
node.x = prev.x;
|
|
1016
|
-
node.y = prev.y;
|
|
1017
|
-
node.w = prev.w ?? node.w;
|
|
1018
|
-
node.h = prev.h ?? node.h;
|
|
1019
|
-
hasChanges = true;
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
else if (node.type === "heatcell") {
|
|
1023
|
-
const target = { x: node.x, y: node.y, w: node.w, h: node.h };
|
|
1024
|
-
if (prev.x !== target.x || prev.y !== target.y) {
|
|
1025
|
-
node._targetX = target.x;
|
|
1026
|
-
node._targetY = target.y;
|
|
1027
|
-
node._targetW = target.w;
|
|
1028
|
-
node._targetH = target.h;
|
|
1029
|
-
node.x = prev.x;
|
|
1030
|
-
node.y = prev.y;
|
|
1031
|
-
node.w = prev.w ?? node.w;
|
|
1032
|
-
node.h = prev.h ?? node.h;
|
|
1033
|
-
hasChanges = true;
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
if (hasChanges) {
|
|
1038
|
-
this.activeTransition = {
|
|
1039
|
-
startTime: typeof performance !== "undefined" ? performance.now() : Date.now(),
|
|
1040
|
-
duration
|
|
1041
|
-
};
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
/**
|
|
1045
|
-
* Advance the transition animation. Returns true if still animating.
|
|
1046
|
-
*/
|
|
1047
|
-
advanceTransition(now) {
|
|
1048
|
-
if (!this.activeTransition)
|
|
1049
|
-
return false;
|
|
1050
|
-
const elapsed = now - this.activeTransition.startTime;
|
|
1051
|
-
const rawT = Math.min(elapsed / this.activeTransition.duration, 1);
|
|
1052
|
-
// Ease-out cubic (or linear)
|
|
1053
|
-
const t = this.config.transition?.easing === "linear"
|
|
1054
|
-
? rawT
|
|
1055
|
-
: 1 - Math.pow(1 - rawT, 3);
|
|
1056
|
-
for (const node of this.scene) {
|
|
1057
|
-
if (node.type === "point") {
|
|
1058
|
-
if (node._targetX === undefined)
|
|
1059
|
-
continue;
|
|
1060
|
-
const key = this.getNodeIdentity(node, 0);
|
|
1061
|
-
if (!key)
|
|
1062
|
-
continue;
|
|
1063
|
-
const prev = this.prevPositionMap.get(key);
|
|
1064
|
-
if (!prev)
|
|
1065
|
-
continue;
|
|
1066
|
-
node.x = prev.x + (node._targetX - prev.x) * t;
|
|
1067
|
-
node.y = prev.y + (node._targetY - prev.y) * t;
|
|
1068
|
-
if (node._targetR !== undefined && prev.r !== undefined) {
|
|
1069
|
-
node.r = prev.r + (node._targetR - prev.r) * t;
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
else if (node.type === "rect") {
|
|
1073
|
-
if (node._targetX === undefined)
|
|
1074
|
-
continue;
|
|
1075
|
-
const key = this.getNodeIdentity(node, 0);
|
|
1076
|
-
if (!key)
|
|
1077
|
-
continue;
|
|
1078
|
-
const prev = this.prevPositionMap.get(key);
|
|
1079
|
-
if (!prev)
|
|
1080
|
-
continue;
|
|
1081
|
-
node.x = prev.x + (node._targetX - prev.x) * t;
|
|
1082
|
-
node.y = prev.y + (node._targetY - prev.y) * t;
|
|
1083
|
-
if (prev.w !== undefined)
|
|
1084
|
-
node.w = prev.w + (node._targetW - prev.w) * t;
|
|
1085
|
-
if (prev.h !== undefined)
|
|
1086
|
-
node.h = prev.h + (node._targetH - prev.h) * t;
|
|
1087
|
-
}
|
|
1088
|
-
else if (node.type === "heatcell") {
|
|
1089
|
-
if (node._targetX === undefined)
|
|
1090
|
-
continue;
|
|
1091
|
-
const key = this.getNodeIdentity(node, 0);
|
|
1092
|
-
if (!key)
|
|
1093
|
-
continue;
|
|
1094
|
-
const prev = this.prevPositionMap.get(key);
|
|
1095
|
-
if (!prev)
|
|
1096
|
-
continue;
|
|
1097
|
-
node.x = prev.x + (node._targetX - prev.x) * t;
|
|
1098
|
-
node.y = prev.y + (node._targetY - prev.y) * t;
|
|
1099
|
-
if (prev.w !== undefined)
|
|
1100
|
-
node.w = prev.w + (node._targetW - prev.w) * t;
|
|
1101
|
-
if (prev.h !== undefined)
|
|
1102
|
-
node.h = prev.h + (node._targetH - prev.h) * t;
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
if (rawT >= 1) {
|
|
1106
|
-
// Snap to targets and clear transition
|
|
1107
|
-
for (const node of this.scene) {
|
|
1108
|
-
if (node.type === "point") {
|
|
1109
|
-
if (node._targetX === undefined)
|
|
1110
|
-
continue;
|
|
1111
|
-
node.x = node._targetX;
|
|
1112
|
-
node.y = node._targetY;
|
|
1113
|
-
if (node._targetR !== undefined)
|
|
1114
|
-
node.r = node._targetR;
|
|
1115
|
-
node._targetX = undefined;
|
|
1116
|
-
node._targetY = undefined;
|
|
1117
|
-
node._targetR = undefined;
|
|
1118
|
-
}
|
|
1119
|
-
else if (node.type === "rect") {
|
|
1120
|
-
if (node._targetX === undefined)
|
|
1121
|
-
continue;
|
|
1122
|
-
node.x = node._targetX;
|
|
1123
|
-
node.y = node._targetY;
|
|
1124
|
-
node.w = node._targetW;
|
|
1125
|
-
node.h = node._targetH;
|
|
1126
|
-
node._targetX = undefined;
|
|
1127
|
-
node._targetY = undefined;
|
|
1128
|
-
node._targetW = undefined;
|
|
1129
|
-
node._targetH = undefined;
|
|
1130
|
-
}
|
|
1131
|
-
else if (node.type === "heatcell") {
|
|
1132
|
-
if (node._targetX === undefined)
|
|
1133
|
-
continue;
|
|
1134
|
-
node.x = node._targetX;
|
|
1135
|
-
node.y = node._targetY;
|
|
1136
|
-
node.w = node._targetW;
|
|
1137
|
-
node.h = node._targetH;
|
|
1138
|
-
node._targetX = undefined;
|
|
1139
|
-
node._targetY = undefined;
|
|
1140
|
-
node._targetW = undefined;
|
|
1141
|
-
node._targetH = undefined;
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
this.activeTransition = null;
|
|
1145
|
-
return false;
|
|
1146
|
-
}
|
|
1147
|
-
return true;
|
|
1148
|
-
}
|
|
1149
|
-
// ── Helpers ──────────────────────────────────────────────────────────
|
|
1150
|
-
groupData(data) {
|
|
1151
|
-
if (!this.getGroup) {
|
|
1152
|
-
return [{ key: "_default", data }];
|
|
1153
|
-
}
|
|
1154
|
-
const groups = new Map();
|
|
1155
|
-
for (const d of data) {
|
|
1156
|
-
const key = this.getGroup(d);
|
|
1157
|
-
if (!groups.has(key))
|
|
1158
|
-
groups.set(key, []);
|
|
1159
|
-
groups.get(key).push(d);
|
|
1160
|
-
}
|
|
1161
|
-
return Array.from(groups.entries()).map(([key, data]) => ({ key, data }));
|
|
1162
|
-
}
|
|
1163
|
-
resolveLineStyle(group, sampleDatum) {
|
|
1164
|
-
const ls = this.config.lineStyle;
|
|
1165
|
-
if (typeof ls === "function") {
|
|
1166
|
-
return ls(sampleDatum || {}, group);
|
|
1167
|
-
}
|
|
1168
|
-
if (ls && typeof ls === "object") {
|
|
1169
|
-
return {
|
|
1170
|
-
stroke: ls.stroke || "#007bff",
|
|
1171
|
-
strokeWidth: ls.strokeWidth || 2,
|
|
1172
|
-
strokeDasharray: ls.strokeDasharray,
|
|
1173
|
-
fill: ls.fill,
|
|
1174
|
-
fillOpacity: ls.fillOpacity,
|
|
1175
|
-
opacity: ls.opacity
|
|
1176
|
-
};
|
|
1177
|
-
}
|
|
1178
|
-
return { stroke: "#007bff", strokeWidth: 2 };
|
|
1179
|
-
}
|
|
1180
|
-
resolveAreaStyle(group, sampleDatum) {
|
|
1181
|
-
if (this.config.areaStyle) {
|
|
1182
|
-
return this.config.areaStyle(sampleDatum || {});
|
|
1183
|
-
}
|
|
1184
|
-
// Fall back to lineStyle — AreaChart passes area styling via lineStyle
|
|
1185
|
-
const ls = this.config.lineStyle;
|
|
1186
|
-
if (typeof ls === "function") {
|
|
1187
|
-
return ls(sampleDatum || {}, group);
|
|
1188
|
-
}
|
|
1189
|
-
if (ls && typeof ls === "object") {
|
|
1190
|
-
return {
|
|
1191
|
-
fill: ls.fill || ls.stroke || "#4e79a7",
|
|
1192
|
-
fillOpacity: ls.fillOpacity ?? 0.7,
|
|
1193
|
-
stroke: ls.stroke || "#4e79a7",
|
|
1194
|
-
strokeWidth: ls.strokeWidth || 2
|
|
1195
|
-
};
|
|
1196
|
-
}
|
|
1197
|
-
return { fill: "#4e79a7", fillOpacity: 0.7, stroke: "#4e79a7", strokeWidth: 2 };
|
|
1198
|
-
}
|
|
1199
|
-
// ── Public accessors ─────────────────────────────────────────────────
|
|
1200
|
-
getData() {
|
|
1201
|
-
return this.buffer.toArray();
|
|
1202
|
-
}
|
|
1203
|
-
getExtents() {
|
|
1204
|
-
if (this.xExtent.min === Infinity)
|
|
1205
|
-
return null;
|
|
1206
|
-
return {
|
|
1207
|
-
x: this.xExtent.extent,
|
|
1208
|
-
y: this.yExtent.extent
|
|
1209
|
-
};
|
|
1210
|
-
}
|
|
1211
|
-
clear() {
|
|
1212
|
-
this.buffer.clear();
|
|
1213
|
-
this.xExtent.clear();
|
|
1214
|
-
this.yExtent.clear();
|
|
1215
|
-
if (this.timestampBuffer)
|
|
1216
|
-
this.timestampBuffer.clear();
|
|
1217
|
-
this.prevPositionMap.clear();
|
|
1218
|
-
this.activeTransition = null;
|
|
1219
|
-
this.lastIngestTime = 0;
|
|
1220
|
-
this.scales = null;
|
|
1221
|
-
this.scene = [];
|
|
1222
|
-
this.version++;
|
|
1223
|
-
}
|
|
1224
|
-
get size() {
|
|
1225
|
-
return this.buffer.size;
|
|
1226
|
-
}
|
|
1227
|
-
getBuffer() {
|
|
1228
|
-
return this.buffer;
|
|
1229
|
-
}
|
|
1230
|
-
getXAccessor() {
|
|
1231
|
-
return this.getX;
|
|
1232
|
-
}
|
|
1233
|
-
getYAccessor() {
|
|
1234
|
-
return this.getY;
|
|
1235
|
-
}
|
|
1236
|
-
getCategoryAccessor() {
|
|
1237
|
-
return this.getCategory;
|
|
1238
|
-
}
|
|
1239
|
-
updateConfig(config) {
|
|
1240
|
-
Object.assign(this.config, config);
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
exports.PipelineStore = PipelineStore;
|