matterviz 0.4.0 → 0.4.2
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/dist/EmptyState.svelte +10 -4
- package/dist/FilePicker.svelte +12 -14
- package/dist/Icon.svelte +7 -4
- package/dist/MillerIndexInput.svelte +2 -2
- package/dist/app.css +53 -0
- package/dist/brillouin/BrillouinZone.svelte +132 -219
- package/dist/brillouin/BrillouinZone.svelte.d.ts +9 -20
- package/dist/brillouin/BrillouinZoneControls.svelte +3 -6
- package/dist/brillouin/BrillouinZoneExportPane.svelte +47 -96
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +15 -42
- package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +2 -3
- package/dist/brillouin/BrillouinZoneScene.svelte +63 -230
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +3 -23
- package/dist/brillouin/ReciprocalVectors.svelte +39 -0
- package/dist/brillouin/ReciprocalVectors.svelte.d.ts +9 -0
- package/dist/brillouin/compute.d.ts +2 -0
- package/dist/brillouin/compute.js +80 -77
- package/dist/brillouin/geometry.d.ts +8 -0
- package/dist/brillouin/geometry.js +57 -0
- package/dist/brillouin/index.d.ts +2 -0
- package/dist/brillouin/index.js +2 -0
- package/dist/brillouin/types.d.ts +2 -2
- package/dist/chempot-diagram/ChemPotDiagram.svelte +33 -38
- package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +1 -1
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +143 -246
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +4 -1
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +652 -976
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +7 -1
- package/dist/chempot-diagram/async-compute.svelte.js +1 -1
- package/dist/chempot-diagram/color.d.ts +3 -6
- package/dist/chempot-diagram/color.js +5 -5
- package/dist/chempot-diagram/compute.d.ts +3 -3
- package/dist/chempot-diagram/compute.js +3 -1
- package/dist/chempot-diagram/controls-state.svelte.d.ts +10 -0
- package/dist/chempot-diagram/controls-state.svelte.js +42 -0
- package/dist/chempot-diagram/export.d.ts +47 -0
- package/dist/chempot-diagram/export.js +133 -0
- package/dist/chempot-diagram/index.d.ts +1 -0
- package/dist/chempot-diagram/index.js +1 -0
- package/dist/chempot-diagram/pointer.d.ts +0 -10
- package/dist/chempot-diagram/pointer.js +4 -4
- package/dist/chempot-diagram/types.d.ts +3 -3
- package/dist/colors/index.d.ts +0 -4
- package/dist/colors/index.js +21 -22
- package/dist/composition/BarChart.svelte +37 -31
- package/dist/composition/BubbleChart.svelte +17 -17
- package/dist/composition/Composition.svelte +7 -11
- package/dist/composition/Formula.svelte +18 -25
- package/dist/composition/FormulaFilter.svelte +96 -98
- package/dist/composition/PieChart.svelte +60 -46
- package/dist/composition/chem-sys.js +3 -2
- package/dist/composition/format.js +4 -3
- package/dist/composition/index.d.ts +2 -0
- package/dist/composition/index.js +14 -0
- package/dist/composition/parse.d.ts +2 -3
- package/dist/composition/parse.js +27 -28
- package/dist/constants.js +11 -9
- package/dist/controls.d.ts +1 -0
- package/dist/controls.js +0 -1
- package/dist/convex-hull/ConvexHull.svelte +20 -25
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -4
- package/dist/convex-hull/ConvexHull2D.svelte +176 -256
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +269 -804
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +236 -755
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullChrome.svelte +264 -0
- package/dist/convex-hull/ConvexHullChrome.svelte.d.ts +30 -0
- package/dist/convex-hull/ConvexHullControls.svelte +128 -49
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +7 -6
- package/dist/convex-hull/ConvexHullInfoPane.svelte +18 -5
- package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +6 -5
- package/dist/convex-hull/ConvexHullStats.svelte +210 -351
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +3 -1
- package/dist/convex-hull/ConvexHullTooltip.svelte +16 -14
- package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +2 -1
- package/dist/convex-hull/GasPressureControls.svelte +5 -12
- package/dist/convex-hull/StructurePopup.svelte +17 -21
- package/dist/convex-hull/StructurePopup.svelte.d.ts +2 -0
- package/dist/convex-hull/TemperatureSlider.svelte +4 -10
- package/dist/convex-hull/barycentric-coords.d.ts +2 -4
- package/dist/convex-hull/barycentric-coords.js +6 -33
- package/dist/convex-hull/canvas-interactions.svelte.d.ts +79 -0
- package/dist/convex-hull/canvas-interactions.svelte.js +279 -0
- package/dist/convex-hull/helpers.d.ts +47 -17
- package/dist/convex-hull/helpers.js +186 -101
- package/dist/convex-hull/hull-state.svelte.d.ts +44 -0
- package/dist/convex-hull/hull-state.svelte.js +124 -0
- package/dist/convex-hull/index.d.ts +9 -7
- package/dist/convex-hull/index.js +7 -2
- package/dist/convex-hull/thermodynamics.js +109 -937
- package/dist/convex-hull/types.d.ts +12 -4
- package/dist/convex-hull/types.js +12 -0
- package/dist/coordination/CoordinationBarPlot.svelte +41 -43
- package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
- package/dist/coordination/calc-coordination.d.ts +1 -1
- package/dist/coordination/calc-coordination.js +28 -25
- package/dist/element/BohrAtom.svelte +6 -8
- package/dist/element/ElementHeading.svelte +4 -1
- package/dist/element/ElementPhoto.svelte +11 -9
- package/dist/element/ElementStats.svelte +12 -10
- package/dist/element/ElementTile.svelte +24 -29
- package/dist/element/ElementTile.svelte.d.ts +1 -1
- package/dist/element/Nucleus.svelte +4 -2
- package/dist/element/data.d.ts +1 -2
- package/dist/element/helpers.d.ts +4 -0
- package/dist/element/helpers.js +18 -0
- package/dist/element/index.d.ts +1 -0
- package/dist/element/index.js +3 -0
- package/dist/feedback/DragOverlay.svelte +5 -3
- package/dist/feedback/DragOverlay.svelte.d.ts +2 -1
- package/dist/feedback/StatusMessage.svelte +20 -7
- package/dist/fermi-surface/FermiSlice.svelte +21 -18
- package/dist/fermi-surface/FermiSurface.svelte +78 -165
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +9 -20
- package/dist/fermi-surface/FermiSurfaceControls.svelte +6 -20
- package/dist/fermi-surface/FermiSurfaceScene.svelte +93 -272
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +3 -23
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +9 -6
- package/dist/fermi-surface/compute.js +14 -13
- package/dist/fermi-surface/export.js +4 -15
- package/dist/fermi-surface/index.d.ts +0 -1
- package/dist/fermi-surface/index.js +0 -1
- package/dist/fermi-surface/parse.d.ts +1 -1
- package/dist/fermi-surface/parse.js +84 -97
- package/dist/fermi-surface/types.d.ts +2 -2
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +161 -185
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +6 -5
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +12 -9
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +5 -5
- package/dist/heatmap-matrix/index.d.ts +8 -2
- package/dist/icons.d.ts +4 -0
- package/dist/icons.js +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/io/ExportPane.svelte +170 -0
- package/dist/io/ExportPane.svelte.d.ts +17 -0
- package/dist/io/decompress.d.ts +2 -1
- package/dist/io/decompress.js +22 -36
- package/dist/io/export.d.ts +9 -1
- package/dist/io/export.js +37 -29
- package/dist/io/fetch.d.ts +2 -4
- package/dist/io/fetch.js +0 -11
- package/dist/io/file-drop.d.ts +7 -0
- package/dist/io/file-drop.js +13 -0
- package/dist/io/index.d.ts +1 -0
- package/dist/io/index.js +3 -0
- package/dist/io/is-binary.d.ts +9 -0
- package/dist/io/is-binary.js +45 -1
- package/dist/io/types.d.ts +13 -0
- package/dist/io/url-drop.js +13 -25
- package/dist/isosurface/Isosurface.svelte +17 -23
- package/dist/isosurface/IsosurfaceControls.svelte +37 -42
- package/dist/isosurface/parse-vaspwave.d.ts +3 -0
- package/dist/isosurface/parse-vaspwave.js +138 -0
- package/dist/isosurface/parse.js +50 -48
- package/dist/labels.d.ts +6 -2
- package/dist/labels.js +44 -14
- package/dist/layout/FullscreenButton.svelte +34 -0
- package/dist/layout/FullscreenButton.svelte.d.ts +10 -0
- package/dist/layout/FullscreenToggle.svelte +12 -12
- package/dist/layout/InfoCard.svelte +7 -18
- package/dist/layout/InfoTag.svelte +1 -3
- package/dist/layout/NumberRangeInput.svelte +46 -0
- package/dist/layout/NumberRangeInput.svelte.d.ts +12 -0
- package/dist/layout/PropertyFilter.svelte +2 -6
- package/dist/layout/SettingsSection.svelte +21 -17
- package/dist/layout/SubpageGrid.svelte +3 -1
- package/dist/layout/ViewerChrome.svelte +115 -0
- package/dist/layout/ViewerChrome.svelte.d.ts +18 -0
- package/dist/layout/fullscreen.d.ts +4 -0
- package/dist/layout/fullscreen.js +3 -5
- package/dist/layout/fullscreen.svelte.d.ts +8 -0
- package/dist/layout/fullscreen.svelte.js +36 -0
- package/dist/layout/index.d.ts +4 -0
- package/dist/layout/index.js +4 -0
- package/dist/layout/json-tree/JsonNode.svelte +8 -11
- package/dist/layout/json-tree/JsonTree.svelte +53 -77
- package/dist/layout/json-tree/JsonValue.svelte +7 -15
- package/dist/layout/json-tree/utils.js +14 -23
- package/dist/math.d.ts +9 -3
- package/dist/math.js +37 -21
- package/dist/overlays/ContextMenu.svelte +4 -2
- package/dist/overlays/CopyButton.svelte +11 -5
- package/dist/overlays/DragControlTab.svelte +6 -1
- package/dist/overlays/DraggablePane.svelte +81 -26
- package/dist/overlays/DraggablePane.svelte.d.ts +1 -0
- package/dist/overlays/GlassChip.svelte +30 -0
- package/dist/overlays/GlassChip.svelte.d.ts +8 -0
- package/dist/overlays/InfoPaneCards.svelte +11 -23
- package/dist/overlays/index.d.ts +11 -0
- package/dist/overlays/index.js +26 -0
- package/dist/overlays/portal.d.ts +2 -0
- package/dist/overlays/portal.js +14 -0
- package/dist/periodic-table/PeriodicTable.svelte +115 -146
- package/dist/periodic-table/PeriodicTable.svelte.d.ts +5 -3
- package/dist/periodic-table/PeriodicTableControls.svelte +11 -63
- package/dist/periodic-table/TableInset.svelte +9 -4
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +50 -71
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -8
- package/dist/phase-diagram/PhaseDiagramControls.svelte +73 -96
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +4 -3
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +4 -2
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +2 -3
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +49 -134
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +3 -4
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +54 -58
- package/dist/phase-diagram/TdbInfoPanel.svelte +21 -15
- package/dist/phase-diagram/build-diagram.js +3 -3
- package/dist/phase-diagram/colors.js +1 -1
- package/dist/phase-diagram/diagram-input.d.ts +1 -2
- package/dist/phase-diagram/parse.d.ts +2 -1
- package/dist/phase-diagram/parse.js +15 -15
- package/dist/phase-diagram/svg-to-diagram.js +21 -15
- package/dist/phase-diagram/utils.js +2 -3
- package/dist/plot/bar/BarPlot.svelte +494 -692
- package/dist/plot/bar/BarPlot.svelte.d.ts +9 -15
- package/dist/plot/bar/BarPlotControls.svelte +1 -3
- package/dist/plot/bar/BarPlotControls.svelte.d.ts +5 -7
- package/dist/plot/bar/SpacegroupBarPlot.svelte +31 -37
- package/dist/plot/bar/data.d.ts +5 -4
- package/dist/plot/bar/data.js +2 -1
- package/dist/plot/box/BoxPlot.svelte +361 -439
- package/dist/plot/box/BoxPlot.svelte.d.ts +6 -3
- package/dist/plot/box/BoxPlotControls.svelte.d.ts +1 -1
- package/dist/plot/box/Violin.svelte.d.ts +2 -1
- package/dist/plot/box/box-plot.d.ts +3 -2
- package/dist/plot/box/box-plot.js +6 -3
- package/dist/plot/box/kde.d.ts +2 -1
- package/dist/plot/box/kde.js +4 -4
- package/dist/plot/core/auto-place.d.ts +1 -1
- package/dist/plot/core/auto-place.js +5 -2
- package/dist/plot/core/axis-utils.d.ts +12 -7
- package/dist/plot/core/axis-utils.js +48 -48
- package/dist/plot/core/components/AxisLabel.svelte +82 -20
- package/dist/plot/core/components/AxisLabel.svelte.d.ts +1 -0
- package/dist/plot/core/components/ColorBar.svelte +106 -111
- package/dist/plot/core/components/ColorBar.svelte.d.ts +5 -4
- package/dist/plot/core/components/ColorScaleSelect.svelte +1 -1
- package/dist/plot/core/components/FillArea.svelte +10 -17
- package/dist/plot/core/components/HierarchyControls.svelte +249 -0
- package/dist/plot/{sunburst/SunburstControls.svelte.d.ts → core/components/HierarchyControls.svelte.d.ts} +12 -8
- package/dist/plot/core/components/InteractiveAxisLabel.svelte +1 -1
- package/dist/plot/core/components/InteractiveAxisLabel.svelte.d.ts +1 -1
- package/dist/plot/core/components/Line.svelte +21 -11
- package/dist/plot/core/components/Line.svelte.d.ts +5 -2
- package/dist/plot/core/components/PlotAxis.svelte +42 -18
- package/dist/plot/core/components/PlotAxis.svelte.d.ts +2 -1
- package/dist/plot/core/components/PlotControls.svelte +65 -41
- package/dist/plot/core/components/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/core/components/PlotLegend.svelte +31 -35
- package/dist/plot/core/components/PlotMarginals.svelte +616 -0
- package/dist/plot/core/components/PlotMarginals.svelte.d.ts +15 -0
- package/dist/plot/core/components/PlotTooltip.svelte +1 -1
- package/dist/plot/core/components/PortalSelect.svelte +4 -8
- package/dist/plot/core/components/ReferenceLine.svelte +27 -24
- package/dist/plot/core/components/ReferenceLine3D.svelte +56 -59
- package/dist/plot/core/components/ReferenceLine3D.svelte.d.ts +4 -4
- package/dist/plot/core/components/ReferencePlane.svelte +9 -10
- package/dist/plot/core/components/ReferencePlane.svelte.d.ts +4 -4
- package/dist/plot/core/components/ZeroLines.svelte +4 -8
- package/dist/plot/core/components/ZoomRect.svelte +1 -2
- package/dist/plot/core/components/index.d.ts +1 -0
- package/dist/plot/core/components/index.js +1 -0
- package/dist/plot/core/data-cleaning-signal.d.ts +46 -0
- package/dist/plot/core/data-cleaning-signal.js +467 -0
- package/dist/plot/core/data-cleaning.d.ts +2 -42
- package/dist/plot/core/data-cleaning.js +29 -476
- package/dist/plot/core/data-transform.js +9 -5
- package/dist/plot/core/fill-utils.d.ts +7 -4
- package/dist/plot/core/fill-utils.js +34 -9
- package/dist/plot/core/index.d.ts +1 -0
- package/dist/plot/core/index.js +1 -0
- package/dist/plot/core/interactions.d.ts +5 -1
- package/dist/plot/core/interactions.js +14 -2
- package/dist/plot/core/layout.d.ts +3 -1
- package/dist/plot/core/layout.js +22 -13
- package/dist/plot/core/marginals.d.ts +170 -0
- package/dist/plot/core/marginals.js +436 -0
- package/dist/plot/core/pan-zoom.svelte.d.ts +35 -0
- package/dist/plot/core/pan-zoom.svelte.js +221 -0
- package/dist/plot/core/placed-tween.svelte.d.ts +21 -0
- package/dist/plot/core/placed-tween.svelte.js +68 -0
- package/dist/plot/core/reference-line.d.ts +10 -10
- package/dist/plot/core/reference-line.js +7 -7
- package/dist/plot/core/scales.d.ts +40 -25
- package/dist/plot/core/scales.js +49 -21
- package/dist/plot/core/svg.d.ts +2 -1
- package/dist/plot/core/types/fills.d.ts +101 -0
- package/dist/plot/core/types/fills.js +15 -0
- package/dist/plot/core/types/plot-3d.d.ts +87 -0
- package/dist/plot/core/types/plot-3d.js +4 -0
- package/dist/plot/core/types/reference-lines.d.ts +140 -0
- package/dist/plot/core/types/reference-lines.js +9 -0
- package/dist/plot/core/types.d.ts +47 -331
- package/dist/plot/core/types.js +23 -20
- package/dist/plot/core/utils/hierarchy-chart.d.ts +51 -0
- package/dist/plot/core/utils/hierarchy-chart.js +179 -0
- package/dist/plot/core/utils/hierarchy-labels.d.ts +20 -0
- package/dist/plot/core/utils/hierarchy-labels.js +99 -0
- package/dist/plot/core/utils/label-placement.d.ts +1 -1
- package/dist/plot/core/utils/label-placement.js +5 -5
- package/dist/plot/core/utils.d.ts +2 -1
- package/dist/plot/histogram/Histogram.svelte +373 -631
- package/dist/plot/histogram/Histogram.svelte.d.ts +7 -7
- package/dist/plot/histogram/HistogramControls.svelte +30 -28
- package/dist/plot/histogram/HistogramControls.svelte.d.ts +1 -1
- package/dist/plot/histogram/histogram.d.ts +31 -0
- package/dist/plot/histogram/histogram.js +70 -0
- package/dist/plot/histogram/index.d.ts +1 -0
- package/dist/plot/histogram/index.js +1 -0
- package/dist/plot/index.d.ts +1 -0
- package/dist/plot/index.js +1 -0
- package/dist/plot/sankey/Sankey.svelte +79 -73
- package/dist/plot/sankey/Sankey.svelte.d.ts +3 -3
- package/dist/plot/sankey/SankeyControls.svelte +22 -16
- package/dist/plot/sankey/sankey.js +3 -1
- package/dist/plot/scatter/BinnedScatterPlot.svelte +264 -112
- package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +11 -4
- package/dist/plot/scatter/ElementScatter.svelte +4 -1
- package/dist/plot/scatter/ScatterPlot.svelte +623 -821
- package/dist/plot/scatter/ScatterPlot.svelte.d.ts +9 -15
- package/dist/plot/scatter/ScatterPlotControls.svelte +18 -19
- package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/scatter/ScatterPoint.svelte +16 -9
- package/dist/plot/scatter/binned-scatter-types.d.ts +6 -12
- package/dist/plot/scatter/binned-scatter-types.js +6 -1
- package/dist/plot/scatter/index.d.ts +1 -1
- package/dist/plot/scatter/scatter-data.d.ts +1 -1
- package/dist/plot/scatter/scatter-data.js +8 -7
- package/dist/plot/scatter-3d/ScatterPlot3D.svelte +40 -60
- package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +6 -14
- package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +17 -15
- package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte.d.ts +5 -5
- package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +232 -234
- package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +5 -14
- package/dist/plot/scatter-3d/Surface3D.svelte +9 -17
- package/dist/plot/scatter-3d/Surface3D.svelte.d.ts +4 -3
- package/dist/plot/sunburst/Sunburst.svelte +256 -338
- package/dist/plot/sunburst/Sunburst.svelte.d.ts +4 -3
- package/dist/plot/sunburst/index.d.ts +1 -1
- package/dist/plot/sunburst/index.js +2 -1
- package/dist/plot/sunburst/render.d.ts +2 -2
- package/dist/plot/sunburst/render.js +47 -22
- package/dist/plot/sunburst/sunburst.d.ts +5 -1
- package/dist/plot/sunburst/sunburst.js +14 -4
- package/dist/plot/treemap/Treemap.svelte +907 -0
- package/dist/plot/treemap/Treemap.svelte.d.ts +84 -0
- package/dist/plot/treemap/index.d.ts +3 -0
- package/dist/plot/treemap/index.js +4 -0
- package/dist/plot/treemap/treemap.d.ts +26 -0
- package/dist/plot/treemap/treemap.js +99 -0
- package/dist/rdf/RdfPlot.svelte +22 -26
- package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
- package/dist/rdf/calc-rdf.js +4 -8
- package/dist/sanitize.js +14 -3
- package/dist/scene/SceneCamera.svelte +62 -0
- package/dist/scene/SceneCamera.svelte.d.ts +19 -0
- package/dist/scene/bind-renderer.svelte.d.ts +2 -0
- package/dist/scene/bind-renderer.svelte.js +22 -0
- package/dist/scene/index.d.ts +4 -0
- package/dist/scene/index.js +5 -0
- package/dist/scene/props.js +52 -0
- package/dist/scene/types.d.ts +26 -0
- package/dist/scene/types.js +1 -0
- package/dist/settings.d.ts +33 -11
- package/dist/settings.js +136 -51
- package/dist/spectral/Bands.svelte +231 -237
- package/dist/spectral/Bands.svelte.d.ts +3 -2
- package/dist/spectral/BandsAndDos.svelte +29 -29
- package/dist/spectral/BrillouinBandsDos.svelte +21 -29
- package/dist/spectral/Dos.svelte +75 -77
- package/dist/spectral/Dos.svelte.d.ts +2 -1
- package/dist/spectral/helpers.d.ts +9 -9
- package/dist/spectral/helpers.js +66 -70
- package/dist/state.svelte.d.ts +0 -7
- package/dist/state.svelte.js +5 -24
- package/dist/structure/Arrow.svelte +6 -21
- package/dist/structure/ArrowInstances.svelte +160 -0
- package/dist/structure/ArrowInstances.svelte.d.ts +16 -0
- package/dist/structure/AtomLegend.svelte +115 -75
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/Bond.svelte +31 -23
- package/dist/structure/CanvasTooltip.svelte +16 -3
- package/dist/structure/CellSelect.svelte +22 -29
- package/dist/structure/CellSelect.svelte.d.ts +2 -1
- package/dist/structure/Cylinder.svelte +24 -40
- package/dist/structure/InstancedAtoms.svelte +114 -0
- package/dist/structure/InstancedAtoms.svelte.d.ts +15 -0
- package/dist/structure/Lattice.svelte +85 -149
- package/dist/structure/SiteLabels.svelte +122 -0
- package/dist/structure/SiteLabels.svelte.d.ts +18 -0
- package/dist/structure/Structure.svelte +725 -735
- package/dist/structure/Structure.svelte.d.ts +8 -16
- package/dist/structure/StructureCarousel.svelte +630 -0
- package/dist/structure/StructureCarousel.svelte.d.ts +19 -0
- package/dist/structure/StructureControls.svelte +298 -325
- package/dist/structure/StructureControls.svelte.d.ts +5 -3
- package/dist/structure/StructureExportPane.svelte +62 -168
- package/dist/structure/StructureExportPane.svelte.d.ts +4 -5
- package/dist/structure/StructureInfoPane.svelte +77 -69
- package/dist/structure/StructureInfoPane.svelte.d.ts +6 -6
- package/dist/structure/StructureScene.svelte +758 -600
- package/dist/structure/StructureScene.svelte.d.ts +24 -20
- package/dist/structure/StructureViewport.svelte +386 -0
- package/dist/structure/StructureViewport.svelte.d.ts +63 -0
- package/dist/structure/atom-label-placement.d.ts +18 -0
- package/dist/structure/atom-label-placement.js +115 -0
- package/dist/structure/atom-properties.d.ts +3 -1
- package/dist/structure/atom-properties.js +115 -54
- package/dist/structure/bond-order-perception.js +2 -4
- package/dist/structure/bonding.d.ts +12 -2
- package/dist/structure/bonding.js +172 -73
- package/dist/structure/edit-history.d.ts +6 -0
- package/dist/structure/edit-history.js +27 -0
- package/dist/structure/export.d.ts +25 -4
- package/dist/structure/export.js +103 -153
- package/dist/structure/format-detect.d.ts +1 -0
- package/dist/structure/format-detect.js +22 -0
- package/dist/structure/geometry.d.ts +9 -0
- package/dist/structure/geometry.js +27 -0
- package/dist/structure/index.d.ts +18 -1
- package/dist/structure/index.js +15 -2
- package/dist/structure/measure.d.ts +1 -1
- package/dist/structure/measure.js +6 -12
- package/dist/structure/parse.d.ts +5 -3
- package/dist/structure/parse.js +551 -567
- package/dist/structure/partial-occupancy.d.ts +0 -1
- package/dist/structure/partial-occupancy.js +5 -3
- package/dist/structure/pbc.d.ts +1 -1
- package/dist/structure/pbc.js +204 -51
- package/dist/structure/polyhedra.d.ts +40 -0
- package/dist/structure/polyhedra.js +587 -0
- package/dist/structure/site.d.ts +4 -0
- package/dist/structure/site.js +1 -0
- package/dist/structure/supercell.d.ts +1 -2
- package/dist/structure/supercell.js +10 -15
- package/dist/structure/validation.js +5 -6
- package/dist/symmetry/SymmetryElementControls.svelte +69 -0
- package/dist/symmetry/SymmetryElementControls.svelte.d.ts +9 -0
- package/dist/symmetry/SymmetryElements.svelte +350 -0
- package/dist/symmetry/SymmetryElements.svelte.d.ts +24 -0
- package/dist/symmetry/SymmetryStats.svelte +142 -49
- package/dist/symmetry/WyckoffTable.svelte +58 -16
- package/dist/symmetry/WyckoffTable.svelte.d.ts +3 -0
- package/dist/symmetry/cell-transform.js +8 -14
- package/dist/symmetry/index.d.ts +15 -4
- package/dist/symmetry/index.js +308 -81
- package/dist/symmetry/spacegroups.d.ts +5 -1
- package/dist/symmetry/spacegroups.js +18 -3
- package/dist/symmetry/symmetry-elements.d.ts +33 -0
- package/dist/symmetry/symmetry-elements.js +521 -0
- package/dist/symmetry/wyckoff-db.d.ts +9 -0
- package/dist/symmetry/wyckoff-db.js +87 -0
- package/dist/table/HeatmapTable.svelte +1104 -378
- package/dist/table/HeatmapTable.svelte.d.ts +12 -2
- package/dist/table/ToggleMenu.svelte +5 -12
- package/dist/table/index.d.ts +21 -6
- package/dist/table/index.js +34 -21
- package/dist/theme/ThemeControl.svelte +16 -8
- package/dist/theme/index.d.ts +1 -5
- package/dist/theme/index.js +4 -10
- package/dist/theme/themes.mjs +14 -7
- package/dist/tooltip/KCoords.svelte +4 -1
- package/dist/tooltip/TooltipContent.svelte +2 -5
- package/dist/trajectory/Trajectory.svelte +556 -352
- package/dist/trajectory/Trajectory.svelte.d.ts +10 -22
- package/dist/trajectory/TrajectoryError.svelte +11 -8
- package/dist/trajectory/TrajectoryExportPane.svelte +28 -54
- package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +4 -5
- package/dist/trajectory/TrajectoryInfoPane.svelte +88 -99
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +3 -2
- package/dist/trajectory/constants.js +6 -2
- package/dist/trajectory/extract.js +73 -71
- package/dist/trajectory/format-detect.d.ts +0 -1
- package/dist/trajectory/format-detect.js +13 -17
- package/dist/trajectory/frame-reader.d.ts +2 -1
- package/dist/trajectory/frame-reader.js +74 -136
- package/dist/trajectory/helpers.d.ts +12 -2
- package/dist/trajectory/helpers.js +68 -37
- package/dist/trajectory/index.d.ts +1 -0
- package/dist/trajectory/index.js +3 -0
- package/dist/trajectory/parse/ase.d.ts +9 -1
- package/dist/trajectory/parse/ase.js +47 -32
- package/dist/trajectory/parse/diagnostics.d.ts +3 -0
- package/dist/trajectory/parse/diagnostics.js +14 -0
- package/dist/trajectory/parse/h5-utils.d.ts +13 -0
- package/dist/trajectory/parse/h5-utils.js +109 -0
- package/dist/trajectory/parse/hdf5.d.ts +1 -1
- package/dist/trajectory/parse/hdf5.js +116 -106
- package/dist/trajectory/parse/index.d.ts +3 -4
- package/dist/trajectory/parse/index.js +68 -119
- package/dist/trajectory/parse/lammps.d.ts +0 -2
- package/dist/trajectory/parse/lammps.js +14 -16
- package/dist/trajectory/parse/pymatgen.d.ts +2 -0
- package/dist/trajectory/parse/pymatgen.js +74 -0
- package/dist/trajectory/parse/vasp.js +13 -9
- package/dist/trajectory/parse/vaspout-electronic.d.ts +13 -0
- package/dist/trajectory/parse/vaspout-electronic.js +207 -0
- package/dist/trajectory/parse/vaspout-h5.d.ts +5 -0
- package/dist/trajectory/parse/vaspout-h5.js +284 -0
- package/dist/trajectory/parse/xyz.d.ts +9 -21
- package/dist/trajectory/parse/xyz.js +38 -39
- package/dist/trajectory/plotting.d.ts +8 -10
- package/dist/trajectory/plotting.js +102 -127
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +8 -1
- package/dist/xrd/XrdPlot.svelte +56 -85
- package/dist/xrd/broadening.d.ts +2 -1
- package/dist/xrd/broadening.js +2 -1
- package/dist/xrd/calc-xrd.js +8 -15
- package/dist/xrd/index.d.ts +2 -2
- package/dist/xrd/parse.d.ts +0 -1
- package/dist/xrd/parse.js +56 -73
- package/package.json +77 -36
- package/dist/api/mp.d.ts +0 -6
- package/dist/api/mp.js +0 -22
- package/dist/element/data.json +0 -11864
- package/dist/fermi-surface/marching-cubes.d.ts +0 -2
- package/dist/fermi-surface/marching-cubes.js +0 -2
- package/dist/plot/core/hover-lock.svelte.d.ts +0 -14
- package/dist/plot/core/hover-lock.svelte.js +0 -45
- package/dist/plot/sunburst/SunburstControls.svelte +0 -200
- package/dist/structure/label-placement.d.ts +0 -14
- package/dist/structure/label-placement.js +0 -72
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { format_value_or_num } from '../../labels'
|
|
3
3
|
import { FullscreenToggle, set_fullscreen_bg } from '../../layout'
|
|
4
4
|
import type {
|
|
5
5
|
AxisLoadError,
|
|
@@ -15,29 +15,35 @@
|
|
|
15
15
|
HistogramControls,
|
|
16
16
|
PlotAxis,
|
|
17
17
|
PlotLegend,
|
|
18
|
+
PlotMarginals,
|
|
18
19
|
ReferenceLine,
|
|
19
20
|
} from '..'
|
|
20
|
-
import type {
|
|
21
|
-
import {
|
|
22
|
-
|
|
21
|
+
import type { MarginalSeriesInput, MarginalsProp } from '../core/marginals'
|
|
22
|
+
import {
|
|
23
|
+
add_sides,
|
|
24
|
+
marginal_axis,
|
|
25
|
+
marginal_axis_presence,
|
|
26
|
+
normalize_marginals,
|
|
27
|
+
reserve_marginal_pad,
|
|
28
|
+
} from '../core/marginals'
|
|
23
29
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
AXIS_DEFAULTS,
|
|
31
|
+
type AxisChangeState,
|
|
32
|
+
create_axis_loader,
|
|
33
|
+
} from '../core/axis-utils'
|
|
34
|
+
import { extract_series_color, prepare_legend_data } from '../core/data-transform'
|
|
35
|
+
import { create_placed_tween } from '../core/placed-tween.svelte'
|
|
36
|
+
import { create_pan_zoom } from '../core/pan-zoom.svelte'
|
|
27
37
|
import { create_legend_visibility } from '../core/utils/series-visibility'
|
|
28
38
|
import {
|
|
29
|
-
|
|
30
|
-
MIN_TOUCH_DISTANCE_PIXELS,
|
|
31
|
-
pan_range_by_pixels,
|
|
32
|
-
PINCH_ZOOM_THRESHOLD,
|
|
33
|
-
remove_drag_listeners,
|
|
39
|
+
invert_rect_range,
|
|
34
40
|
resolve_axis_ranges,
|
|
35
|
-
sorted_range,
|
|
36
41
|
vec2_equal,
|
|
37
|
-
zoom_range_by_factor,
|
|
38
42
|
} from '../core/interactions'
|
|
39
43
|
import {
|
|
44
|
+
AXIS_TITLE_OFFSET,
|
|
40
45
|
calc_auto_padding,
|
|
46
|
+
DEFAULT_PLOT_PADDING,
|
|
41
47
|
filter_padding,
|
|
42
48
|
LABEL_GAP_DEFAULT,
|
|
43
49
|
y2_axis_label_x,
|
|
@@ -49,11 +55,12 @@
|
|
|
49
55
|
has_explicit_position,
|
|
50
56
|
measured_footprint,
|
|
51
57
|
place_decorations,
|
|
58
|
+
placed_coords,
|
|
52
59
|
} from '../core/auto-place'
|
|
53
60
|
import type { IndexedRefLine } from '../core/reference-line'
|
|
54
61
|
import { group_ref_lines_by_z, index_ref_lines } from '../core/reference-line'
|
|
55
62
|
import {
|
|
56
|
-
|
|
63
|
+
create_axis_scales,
|
|
57
64
|
generate_ticks,
|
|
58
65
|
get_nice_data_range,
|
|
59
66
|
get_tick_label,
|
|
@@ -61,20 +68,20 @@
|
|
|
61
68
|
import type {
|
|
62
69
|
BasePlotProps,
|
|
63
70
|
DataSeries,
|
|
64
|
-
InitialRanges,
|
|
65
71
|
LegendConfig,
|
|
66
72
|
PlotConfig,
|
|
67
|
-
ScaleType,
|
|
68
73
|
} from '../core/types'
|
|
69
|
-
import {
|
|
74
|
+
import {
|
|
75
|
+
compute_count_range,
|
|
76
|
+
compute_histogram_bins,
|
|
77
|
+
log_safe_range,
|
|
78
|
+
} from './histogram'
|
|
70
79
|
import ZeroLines from '../core/components/ZeroLines.svelte'
|
|
71
80
|
import ZoomRect from '../core/components/ZoomRect.svelte'
|
|
72
81
|
import { DEFAULTS } from '../../settings'
|
|
73
|
-
import { bin, max } from 'd3-array'
|
|
74
82
|
import type { Snippet } from 'svelte'
|
|
75
83
|
import { onDestroy, untrack } from 'svelte'
|
|
76
84
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
77
|
-
import { Tween } from 'svelte/motion'
|
|
78
85
|
import type { Vec2 } from '../../math'
|
|
79
86
|
import PlotTooltip from '../core/components/PlotTooltip.svelte'
|
|
80
87
|
import { bar_path } from '../core/svg'
|
|
@@ -87,8 +94,8 @@
|
|
|
87
94
|
y_axis: y_axis_init = {},
|
|
88
95
|
y2_axis: y2_axis_init = {},
|
|
89
96
|
display: display_init = DEFAULTS.histogram.display,
|
|
90
|
-
range_padding = 0
|
|
91
|
-
padding =
|
|
97
|
+
range_padding = 0,
|
|
98
|
+
padding = DEFAULT_PLOT_PADDING,
|
|
92
99
|
bins = $bindable(100),
|
|
93
100
|
show_legend = $bindable(true),
|
|
94
101
|
legend = {},
|
|
@@ -117,49 +124,47 @@
|
|
|
117
124
|
on_axis_change,
|
|
118
125
|
on_error,
|
|
119
126
|
pan = {},
|
|
127
|
+
marginals = false,
|
|
120
128
|
...rest
|
|
121
|
-
}: HTMLAttributes<HTMLDivElement> &
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
[
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
data: {
|
|
129
|
+
}: HTMLAttributes<HTMLDivElement> &
|
|
130
|
+
BasePlotProps &
|
|
131
|
+
PlotConfig & {
|
|
132
|
+
series: DataSeries[]
|
|
133
|
+
// Component-specific props
|
|
134
|
+
bins?: number
|
|
135
|
+
show_legend?: boolean
|
|
136
|
+
legend?: LegendConfig | null
|
|
137
|
+
bar?: BarStyle
|
|
138
|
+
selected_property?: string
|
|
139
|
+
mode?: `single` | `overlay`
|
|
140
|
+
tooltip?: Snippet<[HistogramHandlerProps]>
|
|
141
|
+
header_controls?: Snippet<[{ height: number; width: number; fullscreen: boolean }]>
|
|
142
|
+
controls_extra?: Snippet<[Required<PlotConfig>]>
|
|
143
|
+
change?: (data: { value: number; count: number; property: string } | null) => void
|
|
144
|
+
on_bar_click?: (data: {
|
|
138
145
|
value: number
|
|
139
146
|
count: number
|
|
140
147
|
property: string
|
|
141
148
|
event: MouseEvent | KeyboardEvent
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
pan?: PanConfig
|
|
162
|
-
} = $props()
|
|
149
|
+
}) => void
|
|
150
|
+
on_bar_hover?: (
|
|
151
|
+
data: { value: number; count: number; property: string; event: MouseEvent } | null,
|
|
152
|
+
) => void
|
|
153
|
+
ref_lines?: RefLine[]
|
|
154
|
+
on_ref_line_click?: (event: RefLineEvent) => void
|
|
155
|
+
on_ref_line_hover?: (event: RefLineEvent | null) => void
|
|
156
|
+
on_series_toggle?: (series_idx: number) => void
|
|
157
|
+
// Interactive axis props
|
|
158
|
+
data_loader?: DataLoaderFn
|
|
159
|
+
on_axis_change?: (
|
|
160
|
+
axis: `x` | `x2` | `y` | `y2`,
|
|
161
|
+
key: string,
|
|
162
|
+
new_series: DataSeries[],
|
|
163
|
+
) => void
|
|
164
|
+
on_error?: (error: AxisLoadError) => void
|
|
165
|
+
pan?: PanConfig
|
|
166
|
+
marginals?: MarginalsProp
|
|
167
|
+
} = $props()
|
|
163
168
|
|
|
164
169
|
// Local state for controls (initialized from props, owned by this component)
|
|
165
170
|
// Include key AXIS_DEFAULTS props (range, ticks, scale_type) that PlotControls needs
|
|
@@ -169,25 +174,29 @@
|
|
|
169
174
|
let bar = $state(untrack(() => ({ ...DEFAULTS.histogram.bar, ...bar_init })))
|
|
170
175
|
let x_axis = $state(untrack(() => ({ ...axis_state_defaults, ...x_axis_init })))
|
|
171
176
|
// x2-axis needs different default label_shift for top-side positioning
|
|
172
|
-
let x2_axis = $state(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
let x2_axis = $state(
|
|
178
|
+
untrack(() => ({
|
|
179
|
+
...axis_state_defaults,
|
|
180
|
+
label_shift: { x: 0, y: AXIS_TITLE_OFFSET },
|
|
181
|
+
...x2_axis_init,
|
|
182
|
+
})),
|
|
183
|
+
)
|
|
177
184
|
let y_axis = $state(untrack(() => ({ ...axis_state_defaults, ...y_axis_init })))
|
|
178
185
|
// y2 title stays vertically centered; its x position is computed by y2_axis_label_x
|
|
179
|
-
let y2_axis = $state(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
untrack(() => ({ ...DEFAULTS.histogram.display, ...display_init })),
|
|
186
|
+
let y2_axis = $state(
|
|
187
|
+
untrack(() => ({
|
|
188
|
+
...axis_state_defaults,
|
|
189
|
+
label_shift: { x: 0, y: 0 },
|
|
190
|
+
...y2_axis_init,
|
|
191
|
+
})),
|
|
186
192
|
)
|
|
193
|
+
let display = $state(untrack(() => ({ ...DEFAULTS.histogram.display, ...display_init })))
|
|
187
194
|
|
|
188
195
|
// Merge component-specific defaults with local state (format comes from here, not AXIS_DEFAULTS)
|
|
189
|
-
|
|
190
|
-
|
|
196
|
+
// No default tick format: PlotAxis falls back to format_num, which uses SI
|
|
197
|
+
// prefixes above 1 but plain decimals below (0.2 must not render as 200m)
|
|
198
|
+
const final_x_axis = $derived({ label: `Value`, ...x_axis })
|
|
199
|
+
const final_x2_axis = $derived({ label: `Value`, ...x2_axis })
|
|
191
200
|
const final_y_axis = $derived({ label: `Count`, format: `d`, ...y_axis })
|
|
192
201
|
const final_bar = $derived({ ...DEFAULTS.histogram.bar, ...bar })
|
|
193
202
|
const final_y2_axis = $derived({ label: `Count`, format: `d`, ...y2_axis })
|
|
@@ -206,33 +215,11 @@
|
|
|
206
215
|
let axis_loading = $state<`x` | `x2` | `y` | `y2` | null>(null)
|
|
207
216
|
|
|
208
217
|
// Compute ref_lines with index and group by z-index (using shared utilities)
|
|
209
|
-
let
|
|
210
|
-
let ref_lines_by_z = $derived(group_ref_lines_by_z(indexed_ref_lines))
|
|
211
|
-
let drag_state = $state<{
|
|
212
|
-
start: { x: number; y: number } | null
|
|
213
|
-
current: { x: number; y: number } | null
|
|
214
|
-
bounds: DOMRect | null
|
|
215
|
-
}>({ start: null, current: null, bounds: null })
|
|
216
|
-
|
|
217
|
-
// Pan state
|
|
218
|
-
let is_focused = $state(false)
|
|
219
|
-
let shift_held = $state(false)
|
|
220
|
-
let pan_drag_state = $state<
|
|
221
|
-
InitialRanges & { start: { x: number; y: number } } | null
|
|
222
|
-
>(null)
|
|
223
|
-
let touch_state = $state<
|
|
224
|
-
InitialRanges & { start_touches: { x: number; y: number }[] } | null
|
|
225
|
-
>(null)
|
|
218
|
+
let ref_lines_by_z = $derived(group_ref_lines_by_z(index_ref_lines(ref_lines)))
|
|
226
219
|
|
|
227
220
|
// Legend placement stability state
|
|
228
221
|
let legend_element = $state<HTMLDivElement | undefined>()
|
|
229
222
|
let hovered_legend_series_idx = $state<number | null>(null)
|
|
230
|
-
const legend_hover = create_hover_lock()
|
|
231
|
-
const dim_tracker = create_dimension_tracker()
|
|
232
|
-
let has_initial_legend_placement = $state(false)
|
|
233
|
-
|
|
234
|
-
// Clear pending hover lock timeout on unmount
|
|
235
|
-
$effect(() => () => legend_hover.cleanup())
|
|
236
223
|
|
|
237
224
|
// Derived data
|
|
238
225
|
type IndexedSeries = { series_data: DataSeries; series_idx: number }
|
|
@@ -250,29 +237,24 @@
|
|
|
250
237
|
let selected_series_entries = $derived<IndexedSeries[]>(
|
|
251
238
|
series
|
|
252
239
|
.map((series_data: DataSeries, series_idx: number) => ({ series_data, series_idx }))
|
|
253
|
-
.filter(
|
|
254
|
-
(series_data
|
|
255
|
-
|
|
240
|
+
.filter(
|
|
241
|
+
({ series_data }) =>
|
|
242
|
+
(series_data.visible ?? true) &&
|
|
243
|
+
(mode !== `single` || !selected_property || series_data.label === selected_property),
|
|
256
244
|
),
|
|
257
245
|
)
|
|
258
|
-
let selected_series = $derived(
|
|
259
|
-
selected_series_entries.map(({ series_data }) => series_data),
|
|
260
|
-
)
|
|
246
|
+
let selected_series = $derived(selected_series_entries.map(({ series_data }) => series_data))
|
|
261
247
|
|
|
262
248
|
// Separate series by y-axis
|
|
263
249
|
let y1_series = $derived(
|
|
264
250
|
selected_series.filter((srs: DataSeries) => (srs.y_axis ?? `y1`) === `y1`),
|
|
265
251
|
)
|
|
266
|
-
let y2_series = $derived(
|
|
267
|
-
|
|
268
|
-
)
|
|
269
|
-
let x2_series = $derived(
|
|
270
|
-
selected_series.filter((srs: DataSeries) => srs.x_axis === `x2`),
|
|
271
|
-
)
|
|
252
|
+
let y2_series = $derived(selected_series.filter((srs: DataSeries) => srs.y_axis === `y2`))
|
|
253
|
+
let x2_series = $derived(selected_series.filter((srs: DataSeries) => srs.x_axis === `x2`))
|
|
272
254
|
|
|
273
255
|
let auto_ranges = $derived.by(() => {
|
|
274
256
|
// Only x1 series contribute to the x1 auto-range (x2 series get their own domain below)
|
|
275
|
-
const x1_values = selected_series.flatMap((srs) => srs.x_axis === `x2` ? [] : srs.y)
|
|
257
|
+
const x1_values = selected_series.flatMap((srs) => (srs.x_axis === `x2` ? [] : srs.y))
|
|
276
258
|
const auto_x = get_nice_data_range(
|
|
277
259
|
x1_values.map((val) => ({ x: val, y: 0 })),
|
|
278
260
|
({ x }) => x,
|
|
@@ -283,68 +265,29 @@
|
|
|
283
265
|
)
|
|
284
266
|
|
|
285
267
|
const x2_values = x2_series.flatMap((srs: DataSeries) => srs.y)
|
|
286
|
-
const auto_x2 =
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
scale_type:
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
// out-of-domain values, so binning x2 series over the x1 domain skews max_count)
|
|
310
|
-
const max_count = Math.max(
|
|
311
|
-
0,
|
|
312
|
-
...series_list.map((srs: DataSeries) => {
|
|
313
|
-
const hist = bin().domain(srs.x_axis === `x2` ? auto_x2 : auto_x).thresholds(bins)
|
|
314
|
-
return max(hist(srs.y), (data) => data.length) || 0
|
|
315
|
-
}),
|
|
316
|
-
)
|
|
317
|
-
|
|
318
|
-
// If there's effectively no data, avoid log-range issues (counts can't be <= 0 on log)
|
|
319
|
-
if (max_count <= 0) {
|
|
320
|
-
const fallback = type_name === `log` ? 1 : 0
|
|
321
|
-
return [fallback, 1]
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const [y0, y1] = get_nice_data_range(
|
|
325
|
-
[{ x: 0, y: 0 }, { x: max_count, y: 0 }],
|
|
326
|
-
({ x }) => x,
|
|
327
|
-
y_limit,
|
|
328
|
-
scale_type,
|
|
329
|
-
range_padding,
|
|
330
|
-
false,
|
|
331
|
-
)
|
|
332
|
-
// For log scale, minimum must be >= 1 (count can't be 0 on log)
|
|
333
|
-
// For linear/arcsinh, start from 0
|
|
334
|
-
const y_min = type_name === `log` ? Math.max(1, y0) : Math.max(0, y0)
|
|
335
|
-
return [y_min, y1]
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const y1_range = calc_y_range(
|
|
339
|
-
y1_series,
|
|
340
|
-
final_y_axis.range ?? [null, null],
|
|
341
|
-
final_y_axis.scale_type ?? `linear`,
|
|
342
|
-
)
|
|
343
|
-
const y2_auto_range = calc_y_range(
|
|
344
|
-
y2_series,
|
|
345
|
-
final_y2_axis.range ?? [null, null],
|
|
346
|
-
final_y2_axis.scale_type ?? `linear`,
|
|
347
|
-
)
|
|
268
|
+
const auto_x2 =
|
|
269
|
+
x2_values.length > 0
|
|
270
|
+
? get_nice_data_range(
|
|
271
|
+
x2_values.map((val) => ({ x: val, y: 0 })),
|
|
272
|
+
({ x }) => x,
|
|
273
|
+
final_x2_axis.range ?? [null, null],
|
|
274
|
+
final_x2_axis.scale_type ?? `linear`,
|
|
275
|
+
range_padding,
|
|
276
|
+
false,
|
|
277
|
+
)
|
|
278
|
+
: ([0, 1] as Vec2)
|
|
279
|
+
|
|
280
|
+
const count_cfg = { x_domain: auto_x, x2_domain: auto_x2, bin_count: bins, range_padding }
|
|
281
|
+
const y1_range = compute_count_range(y1_series, {
|
|
282
|
+
...count_cfg,
|
|
283
|
+
scale_type: final_y_axis.scale_type ?? `linear`,
|
|
284
|
+
y_limit: log_safe_range(final_y_axis),
|
|
285
|
+
})
|
|
286
|
+
const y2_auto_range = compute_count_range(y2_series, {
|
|
287
|
+
...count_cfg,
|
|
288
|
+
scale_type: final_y2_axis.scale_type ?? `linear`,
|
|
289
|
+
y_limit: log_safe_range(final_y2_axis),
|
|
290
|
+
})
|
|
348
291
|
|
|
349
292
|
return { x: auto_x, x2: auto_x2, y: y1_range, y2: y2_auto_range }
|
|
350
293
|
})
|
|
@@ -366,10 +309,16 @@
|
|
|
366
309
|
})
|
|
367
310
|
|
|
368
311
|
$effect(() => {
|
|
369
|
-
// Supports one-sided range pinning (null bounds fall back to auto); returns null
|
|
370
|
-
//
|
|
312
|
+
// Supports one-sided range pinning (null bounds fall back to auto); returns null for transient
|
|
313
|
+
// non-finite bounds (skip: writing NaN breaks scales and loops here). y/y2 ranges are
|
|
314
|
+
// log-sanitized so an invalid (<= 0) log lower falls back to the auto count minimum.
|
|
371
315
|
const next = resolve_axis_ranges(
|
|
372
|
-
{
|
|
316
|
+
{
|
|
317
|
+
x: final_x_axis,
|
|
318
|
+
x2: final_x2_axis,
|
|
319
|
+
y: { range: log_safe_range(final_y_axis) },
|
|
320
|
+
y2: { range: log_safe_range(final_y2_axis) },
|
|
321
|
+
},
|
|
373
322
|
auto_ranges,
|
|
374
323
|
)
|
|
375
324
|
if (!next) return
|
|
@@ -378,15 +327,16 @@
|
|
|
378
327
|
// (reading + writing the same state otherwise causes effect_update_depth_exceeded).
|
|
379
328
|
const init = untrack(() => ranges.initial)
|
|
380
329
|
if (!vec2_equal(init.x, next.x)) [ranges.initial.x, ranges.current.x] = [next.x, next.x]
|
|
381
|
-
if (!vec2_equal(init.x2, next.x2))
|
|
330
|
+
if (!vec2_equal(init.x2, next.x2))
|
|
331
|
+
[ranges.initial.x2, ranges.current.x2] = [next.x2, next.x2]
|
|
382
332
|
if (!vec2_equal(init.y, next.y)) [ranges.initial.y, ranges.current.y] = [next.y, next.y]
|
|
383
|
-
if (!vec2_equal(init.y2, next.y2))
|
|
333
|
+
if (!vec2_equal(init.y2, next.y2))
|
|
334
|
+
[ranges.initial.y2, ranges.current.y2] = [next.y2, next.y2]
|
|
384
335
|
})
|
|
385
336
|
|
|
386
337
|
// Layout: dynamic padding based on tick label widths
|
|
387
|
-
const default_padding = { t: 20, b: 60, l: 60, r: 20 }
|
|
388
338
|
// base_pad reserves space for tick labels/axis titles; pad (below) adds decoration reservations
|
|
389
|
-
let base_pad = $derived(filter_padding(padding,
|
|
339
|
+
let base_pad = $derived(filter_padding(padding, DEFAULT_PLOT_PADDING))
|
|
390
340
|
|
|
391
341
|
// Update padding based on tick label widths (untrack breaks circular dependency)
|
|
392
342
|
$effect(() => {
|
|
@@ -394,19 +344,23 @@
|
|
|
394
344
|
const current_ticks_y = untrack(() => ticks.y)
|
|
395
345
|
const current_ticks_y2 = untrack(() => ticks.y2)
|
|
396
346
|
|
|
397
|
-
const new_pad =
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
347
|
+
const new_pad =
|
|
348
|
+
width && height && current_ticks_y.length > 0
|
|
349
|
+
? calc_auto_padding({
|
|
350
|
+
padding,
|
|
351
|
+
default_padding: DEFAULT_PLOT_PADDING,
|
|
352
|
+
x2_axis: { ...final_x2_axis, tick_values: current_ticks_x2 },
|
|
353
|
+
y_axis: { ...final_y_axis, tick_values: current_ticks_y },
|
|
354
|
+
y2_axis: { ...final_y2_axis, tick_values: current_ticks_y2 },
|
|
355
|
+
})
|
|
356
|
+
: filter_padding(padding, DEFAULT_PLOT_PADDING)
|
|
406
357
|
|
|
407
358
|
// Add y2 axis label space (calc_auto_padding only accounts for tick labels)
|
|
408
359
|
if (
|
|
409
|
-
width &&
|
|
360
|
+
width &&
|
|
361
|
+
height &&
|
|
362
|
+
y2_series.length > 0 &&
|
|
363
|
+
current_ticks_y2.length > 0 &&
|
|
410
364
|
final_y2_axis.label
|
|
411
365
|
) {
|
|
412
366
|
const inside = final_y2_axis.tick?.label?.inside ?? false
|
|
@@ -422,28 +376,31 @@
|
|
|
422
376
|
|
|
423
377
|
// Add x2 axis label space (mirroring y2 logic for top padding)
|
|
424
378
|
if (
|
|
425
|
-
width &&
|
|
379
|
+
width &&
|
|
380
|
+
height &&
|
|
381
|
+
x2_series.length > 0 &&
|
|
382
|
+
current_ticks_x2.length > 0 &&
|
|
426
383
|
final_x2_axis.label
|
|
427
384
|
) {
|
|
428
385
|
const inside = final_x2_axis.tick?.label?.inside ?? false
|
|
429
|
-
const tick_shift = inside
|
|
430
|
-
? 0
|
|
431
|
-
: Math.abs(final_x2_axis.tick?.label?.shift?.y ?? 0) + 8
|
|
386
|
+
const tick_shift = inside ? 0 : Math.abs(final_x2_axis.tick?.label?.shift?.y ?? 0) + 8
|
|
432
387
|
const label_thickness = Math.round(12 * 1.2)
|
|
433
|
-
new_pad.t = Math.max(
|
|
434
|
-
new_pad.t,
|
|
435
|
-
tick_shift + LABEL_GAP_DEFAULT + label_thickness,
|
|
436
|
-
)
|
|
388
|
+
new_pad.t = Math.max(new_pad.t, tick_shift + LABEL_GAP_DEFAULT + label_thickness)
|
|
437
389
|
}
|
|
438
390
|
|
|
439
391
|
// Only update if padding actually changed
|
|
440
392
|
if (
|
|
441
|
-
base_pad.t !== new_pad.t ||
|
|
442
|
-
base_pad.
|
|
443
|
-
|
|
393
|
+
base_pad.t !== new_pad.t ||
|
|
394
|
+
base_pad.b !== new_pad.b ||
|
|
395
|
+
base_pad.l !== new_pad.l ||
|
|
396
|
+
base_pad.r !== new_pad.r
|
|
397
|
+
)
|
|
398
|
+
base_pad = new_pad
|
|
444
399
|
})
|
|
445
400
|
|
|
446
|
-
const legend_footprint = $derived(
|
|
401
|
+
const legend_footprint = $derived(
|
|
402
|
+
measured_footprint(legend_element, { width: 120, height: 60 }),
|
|
403
|
+
)
|
|
447
404
|
const legend_has_explicit_pos = $derived(has_explicit_position(legend?.style))
|
|
448
405
|
|
|
449
406
|
// Obstacle field in normalized [0,1] plot coords (y=0 at top). Each filled bar is modeled as a
|
|
@@ -481,66 +438,64 @@
|
|
|
481
438
|
height,
|
|
482
439
|
obstacles_norm,
|
|
483
440
|
// gate on legend_element (the render signal) not legend_data, whose entries can read pad
|
|
484
|
-
legend:
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
441
|
+
legend:
|
|
442
|
+
show_legend &&
|
|
443
|
+
legend != null &&
|
|
444
|
+
series.length > 1 &&
|
|
445
|
+
legend_element != null &&
|
|
446
|
+
!legend_has_explicit_pos
|
|
447
|
+
? { footprint: legend_footprint, clearance: legend?.axis_clearance }
|
|
448
|
+
: null,
|
|
449
|
+
}),
|
|
450
|
+
)
|
|
451
|
+
// Resolve marginals (default: CDF strip on top) and reserve outer-band padding. Pass the
|
|
452
|
+
// histogram's `bins` as the marginal's histogram bin count (NOT `size`/thickness) so a
|
|
453
|
+
// `histogram` marginal inherits the main binning via normalize_marginals' merge; `cdf`
|
|
454
|
+
// ignores `bins`. Samples lie along x and equal series.y, so the adapter maps y -> x.
|
|
455
|
+
const resolved_marginals = $derived(
|
|
456
|
+
normalize_marginals(marginals, { top: { type: `cdf`, bins } }),
|
|
457
|
+
)
|
|
458
|
+
const pad = $derived(add_sides(decor.pad, reserve_marginal_pad(resolved_marginals)))
|
|
459
|
+
// a lone series uses the configured bar color; with multiple, each gets its own
|
|
460
|
+
const series_color = (series_data: DataSeries) =>
|
|
461
|
+
selected_series.length === 1 ? final_bar.color : extract_series_color(series_data)
|
|
462
|
+
const marginal_series = $derived<MarginalSeriesInput[]>(
|
|
463
|
+
selected_series_entries.map(({ series_data }) => ({
|
|
464
|
+
x: series_data.y ?? [],
|
|
465
|
+
color: series_color(series_data),
|
|
466
|
+
label: series_data.label,
|
|
467
|
+
visible: true,
|
|
468
|
+
x_axis: series_data.x_axis,
|
|
469
|
+
y_axis: series_data.y_axis,
|
|
470
|
+
})),
|
|
471
|
+
)
|
|
472
|
+
const marginal_has_axis = $derived(
|
|
473
|
+
marginal_axis_presence(x2_series.length > 0, y2_series.length > 0),
|
|
489
474
|
)
|
|
490
|
-
const pad = $derived(decor.pad)
|
|
491
475
|
const legend_auto_outside = $derived(decor.legend_outside)
|
|
492
476
|
const legend_outside_x = $derived(decor.legend_pos.x)
|
|
493
477
|
const legend_outside_y = $derived(decor.legend_pos.y)
|
|
494
478
|
|
|
495
|
-
// Scales and data
|
|
496
|
-
let scales = $derived(
|
|
497
|
-
|
|
498
|
-
final_x_axis
|
|
499
|
-
ranges.current
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
final_x2_axis.scale_type ?? `linear`,
|
|
504
|
-
ranges.current.x2,
|
|
505
|
-
[pad.l, width - pad.r],
|
|
506
|
-
),
|
|
507
|
-
y: create_scale(
|
|
508
|
-
final_y_axis.scale_type ?? `linear`,
|
|
509
|
-
ranges.current.y,
|
|
510
|
-
[height - pad.b, pad.t],
|
|
511
|
-
),
|
|
512
|
-
y2: create_scale(
|
|
513
|
-
final_y2_axis.scale_type ?? `linear`,
|
|
514
|
-
ranges.current.y2,
|
|
515
|
-
[height - pad.b, pad.t],
|
|
479
|
+
// Scales and data (x/x2 share the horizontal pixel span, y/y2 the inverted vertical one)
|
|
480
|
+
let scales = $derived(
|
|
481
|
+
create_axis_scales(
|
|
482
|
+
{ x: final_x_axis, x2: final_x2_axis, y: final_y_axis, y2: final_y2_axis },
|
|
483
|
+
ranges.current,
|
|
484
|
+
pad,
|
|
485
|
+
width,
|
|
486
|
+
height,
|
|
516
487
|
),
|
|
517
|
-
|
|
488
|
+
)
|
|
518
489
|
|
|
519
490
|
// Pad-independent binning (no pixel scales) so the auto-place obstacle field can reuse it
|
|
520
491
|
let histogram_bins = $derived.by(() => {
|
|
521
492
|
if (selected_series.length === 0 || !width || !height) return []
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
.
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
return selected_series_entries.map(({ series_data, series_idx }) => {
|
|
529
|
-
const use_x2 = series_data.x_axis === `x2`
|
|
530
|
-
const active_hist = use_x2 && x2_hist_generator ? x2_hist_generator : hist_generator
|
|
531
|
-
const bins_arr = active_hist(series_data.y)
|
|
532
|
-
return {
|
|
533
|
-
id: series_data.id ?? series_idx,
|
|
534
|
-
series_idx,
|
|
535
|
-
label: series_data.label || `Series ${series_idx + 1}`,
|
|
536
|
-
color: selected_series.length === 1
|
|
537
|
-
? final_bar.color
|
|
538
|
-
: extract_series_color(series_data),
|
|
539
|
-
bins: bins_arr,
|
|
540
|
-
max_count: max(bins_arr, (data) => data.length) || 0,
|
|
541
|
-
x_axis: series_data.x_axis,
|
|
542
|
-
y_axis: series_data.y_axis,
|
|
543
|
-
}
|
|
493
|
+
return compute_histogram_bins(selected_series_entries, {
|
|
494
|
+
x_domain: ranges.current.x,
|
|
495
|
+
x2_domain: ranges.current.x2,
|
|
496
|
+
has_x2: x2_series.length > 0,
|
|
497
|
+
bin_count: bins,
|
|
498
|
+
series_color,
|
|
544
499
|
})
|
|
545
500
|
})
|
|
546
501
|
// Render-time data adds the pixel scales (pad-dependent)
|
|
@@ -552,43 +507,25 @@
|
|
|
552
507
|
})),
|
|
553
508
|
)
|
|
554
509
|
|
|
555
|
-
let ticks = $derived({
|
|
556
|
-
x
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
y: width && height
|
|
575
|
-
? generate_ticks(
|
|
576
|
-
ranges.current.y,
|
|
577
|
-
final_y_axis.scale_type ?? `linear`,
|
|
578
|
-
final_y_axis.ticks,
|
|
579
|
-
scales.y,
|
|
580
|
-
{ default_count: 6 },
|
|
581
|
-
)
|
|
582
|
-
: [],
|
|
583
|
-
y2: width && height && y2_series.length > 0
|
|
584
|
-
? generate_ticks(
|
|
585
|
-
ranges.current.y2,
|
|
586
|
-
final_y2_axis.scale_type ?? `linear`,
|
|
587
|
-
final_y2_axis.ticks,
|
|
588
|
-
scales.y2,
|
|
589
|
-
{ default_count: 6 },
|
|
590
|
-
)
|
|
591
|
-
: [],
|
|
510
|
+
let ticks = $derived.by(() => {
|
|
511
|
+
// x/y always render; x2/y2 only when their series exist (else their scale is a [0,1] sentinel)
|
|
512
|
+
const axis_ticks = (
|
|
513
|
+
axis: typeof final_x_axis,
|
|
514
|
+
range: Vec2,
|
|
515
|
+
scale: typeof scales.x,
|
|
516
|
+
default_count: number,
|
|
517
|
+
show = true,
|
|
518
|
+
) =>
|
|
519
|
+
width && height && show
|
|
520
|
+
? generate_ticks(range, axis.scale_type ?? `linear`, axis.ticks, scale, {
|
|
521
|
+
default_count,
|
|
522
|
+
})
|
|
523
|
+
: []
|
|
524
|
+
const x = axis_ticks(final_x_axis, ranges.current.x, scales.x, 8)
|
|
525
|
+
const x2 = axis_ticks(final_x2_axis, ranges.current.x2, scales.x2, 8, x2_series.length > 0)
|
|
526
|
+
const y = axis_ticks(final_y_axis, ranges.current.y, scales.y, 6)
|
|
527
|
+
const y2 = axis_ticks(final_y2_axis, ranges.current.y2, scales.y2, 6, y2_series.length > 0)
|
|
528
|
+
return { x, x2, y, y2 }
|
|
592
529
|
})
|
|
593
530
|
|
|
594
531
|
// Cache measured tick-label widths so expensive text measurement only runs
|
|
@@ -644,249 +581,61 @@
|
|
|
644
581
|
return result
|
|
645
582
|
})
|
|
646
583
|
|
|
647
|
-
// Tweened legend coordinates
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
{
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
// Update legend position with stability checks
|
|
655
|
-
$effect(() => {
|
|
656
|
-
if (!width || !height || !legend_placement) return
|
|
657
|
-
|
|
658
|
-
// Track dimensions for resize detection
|
|
659
|
-
const dims_changed = dim_tracker.has_changed(width, height)
|
|
660
|
-
if (dims_changed) dim_tracker.update(width, height)
|
|
661
|
-
|
|
662
|
-
// Only update if: resize occurred, OR (not hover-locked AND (responsive OR not yet initially placed))
|
|
663
|
-
const is_responsive = legend?.responsive ?? false
|
|
664
|
-
const should_update = dims_changed || (!legend_hover.is_locked.current &&
|
|
665
|
-
(is_responsive || !has_initial_legend_placement))
|
|
666
|
-
|
|
667
|
-
if (should_update) {
|
|
668
|
-
tweened_legend_coords.set(
|
|
669
|
-
{ x: legend_placement.x, y: legend_placement.y },
|
|
670
|
-
// Skip animation on initial placement to avoid jump from (0, 0)
|
|
671
|
-
has_initial_legend_placement ? undefined : { duration: 0 },
|
|
672
|
-
)
|
|
673
|
-
// Only lock position after we have actual measured size
|
|
674
|
-
if (legend_element) {
|
|
675
|
-
has_initial_legend_placement = true
|
|
676
|
-
}
|
|
677
|
-
}
|
|
584
|
+
// Tweened legend coordinates with shared placement stability gating
|
|
585
|
+
const legend_tween = create_placed_tween({
|
|
586
|
+
placement: () => legend_placement,
|
|
587
|
+
dims: () => ({ width, height }),
|
|
588
|
+
responsive: () => legend?.responsive ?? false,
|
|
589
|
+
element: () => legend_element,
|
|
590
|
+
tween: () => legend?.tween,
|
|
678
591
|
})
|
|
679
592
|
|
|
680
|
-
//
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
593
|
+
// Shared pan/zoom/touch/drag-rect interaction controller
|
|
594
|
+
const pan_zoom = create_pan_zoom({
|
|
595
|
+
ranges: () => ranges.current,
|
|
596
|
+
scale_type: (axis) =>
|
|
597
|
+
({ x: final_x_axis, x2: final_x2_axis, y: final_y_axis, y2: final_y2_axis })[axis]
|
|
598
|
+
.scale_type,
|
|
599
|
+
// Clamp to at least 1 to avoid Infinity deltas when padding equals container size
|
|
600
|
+
plot_dims: () => ({
|
|
601
|
+
width: Math.max(1, width - pad.l - pad.r),
|
|
602
|
+
height: Math.max(1, height - pad.t - pad.b),
|
|
603
|
+
}),
|
|
604
|
+
pan: () => pan,
|
|
605
|
+
set_range: (axis, range) => (ranges.current[axis] = range),
|
|
606
|
+
svg: () => svg_element,
|
|
607
|
+
on_rect_zoom: (start, current) => {
|
|
608
|
+
// Update axis ranges to trigger reactivity and prevent effect from overriding
|
|
609
|
+
const next_x = invert_rect_range(scales.x, start.x, current.x)
|
|
610
|
+
if (!next_x) return
|
|
611
|
+
x_axis = { ...x_axis, range: next_x }
|
|
612
|
+
// gate x2/y2 on series presence: their scales are [0, 1] sentinels otherwise,
|
|
613
|
+
// so inverting would store a phantom range in the bindable prop
|
|
614
|
+
const next_x2 =
|
|
615
|
+
x2_series.length > 0 ? invert_rect_range(scales.x2, start.x, current.x) : null
|
|
616
|
+
if (next_x2) x2_axis = { ...x2_axis, range: next_x2 }
|
|
617
|
+
const next_y = invert_rect_range(scales.y, start.y, current.y)
|
|
618
|
+
if (next_y) y_axis = { ...y_axis, range: next_y }
|
|
619
|
+
const next_y2 =
|
|
620
|
+
y2_series.length > 0 ? invert_rect_range(scales.y2, start.y, current.y) : null
|
|
621
|
+
if (next_y2) y2_axis = { ...y2_axis, range: next_y2 }
|
|
622
|
+
},
|
|
623
|
+
on_reset: () => {
|
|
624
|
+
// Reset zoom to initial ranges (undo any pan/zoom)
|
|
625
|
+
ranges.current = {
|
|
626
|
+
x: [...ranges.initial.x] as Vec2,
|
|
627
|
+
x2: [...ranges.initial.x2] as Vec2,
|
|
628
|
+
y: [...ranges.initial.y] as Vec2,
|
|
629
|
+
y2: [...ranges.initial.y2] as Vec2,
|
|
707
630
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
x: evt.clientX - drag_state.bounds.left,
|
|
715
|
-
y: evt.clientY - drag_state.bounds.top,
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
const on_window_mouse_up = () => {
|
|
720
|
-
handle_zoom()
|
|
721
|
-
drag_state = { start: null, current: null, bounds: null }
|
|
722
|
-
window.removeEventListener(`mousemove`, on_window_mouse_move)
|
|
723
|
-
window.removeEventListener(`mouseup`, on_window_mouse_up)
|
|
724
|
-
document.body.style.cursor = `default`
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
// Pan/zoom all four axes from an interaction-start snapshot, each in its own
|
|
728
|
-
// scale's transform space (log axes pan by a constant factor, linear by a shift).
|
|
729
|
-
// Plot dims clamped to 1px so degenerate containers can't produce Infinity deltas.
|
|
730
|
-
const pan_all_axes = (init: InitialRanges, dx_px: number, dy_px: number) => {
|
|
731
|
-
const plot_width = Math.max(1, width - pad.l - pad.r)
|
|
732
|
-
const plot_height = Math.max(1, height - pad.t - pad.b)
|
|
733
|
-
ranges.current.x = pan_range_by_pixels(init.initial_x_range, dx_px, plot_width, final_x_axis.scale_type)
|
|
734
|
-
ranges.current.x2 = pan_range_by_pixels(init.initial_x2_range, dx_px, plot_width, final_x2_axis.scale_type)
|
|
735
|
-
ranges.current.y = pan_range_by_pixels(init.initial_y_range, dy_px, plot_height, final_y_axis.scale_type)
|
|
736
|
-
ranges.current.y2 = pan_range_by_pixels(init.initial_y2_range, dy_px, plot_height, final_y2_axis.scale_type)
|
|
737
|
-
}
|
|
738
|
-
const zoom_all_axes = (init: InitialRanges, factor: number) => {
|
|
739
|
-
ranges.current.x = zoom_range_by_factor(init.initial_x_range, factor, final_x_axis.scale_type)
|
|
740
|
-
ranges.current.x2 = zoom_range_by_factor(init.initial_x2_range, factor, final_x2_axis.scale_type)
|
|
741
|
-
ranges.current.y = zoom_range_by_factor(init.initial_y_range, factor, final_y_axis.scale_type)
|
|
742
|
-
ranges.current.y2 = zoom_range_by_factor(init.initial_y2_range, factor, final_y2_axis.scale_type)
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
// Pan drag handler (drag direction inverted on x for natural pan feel)
|
|
746
|
-
const on_pan_move = (evt: MouseEvent) => {
|
|
747
|
-
if (!pan_drag_state) return
|
|
748
|
-
const sensitivity = pan?.drag_sensitivity ?? 1
|
|
749
|
-
pan_all_axes(
|
|
750
|
-
pan_drag_state,
|
|
751
|
-
-(evt.clientX - pan_drag_state.start.x) * sensitivity,
|
|
752
|
-
(evt.clientY - pan_drag_state.start.y) * sensitivity,
|
|
753
|
-
)
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
const on_pan_end = () => {
|
|
757
|
-
pan_drag_state = null
|
|
758
|
-
document.body.style.cursor = ``
|
|
759
|
-
window.removeEventListener(`mousemove`, on_pan_move)
|
|
760
|
-
window.removeEventListener(`mouseup`, on_pan_end)
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
// Tear down any window listeners + cursor override if the component unmounts mid-drag
|
|
764
|
-
// (mouseup/panend would otherwise never fire, leaking listeners and a stuck cursor).
|
|
765
|
-
// onDestroy also runs during SSR teardown, where window/document don't exist.
|
|
766
|
-
onDestroy(() => {
|
|
767
|
-
remove_drag_listeners([on_window_mouse_move, on_pan_move], [on_window_mouse_up, on_pan_end])
|
|
768
|
-
drag_state = { start: null, current: null, bounds: null }
|
|
769
|
-
pan_drag_state = null
|
|
631
|
+
// Also reset axis props so future data changes recalculate auto ranges
|
|
632
|
+
x_axis = { ...x_axis, range: [null, null] }
|
|
633
|
+
x2_axis = { ...x2_axis, range: [null, null] }
|
|
634
|
+
y_axis = { ...y_axis, range: [null, null] }
|
|
635
|
+
y2_axis = { ...y2_axis, range: [null, null] }
|
|
636
|
+
},
|
|
770
637
|
})
|
|
771
|
-
|
|
772
|
-
function handle_mouse_down(evt: MouseEvent) {
|
|
773
|
-
const coords = get_relative_coords(evt)
|
|
774
|
-
if (!coords || !svg_element) return
|
|
775
|
-
|
|
776
|
-
// Check if pan is enabled and shift is held for pan mode
|
|
777
|
-
const pan_enabled = pan?.enabled !== false
|
|
778
|
-
if (pan_enabled && evt.shiftKey) {
|
|
779
|
-
evt.preventDefault()
|
|
780
|
-
pan_drag_state = {
|
|
781
|
-
start: { x: evt.clientX, y: evt.clientY },
|
|
782
|
-
initial_x_range: [...ranges.current.x] as [number, number],
|
|
783
|
-
initial_x2_range: [...ranges.current.x2] as [number, number],
|
|
784
|
-
initial_y_range: [...ranges.current.y] as [number, number],
|
|
785
|
-
initial_y2_range: [...ranges.current.y2] as [number, number],
|
|
786
|
-
}
|
|
787
|
-
document.body.style.cursor = `grabbing`
|
|
788
|
-
window.addEventListener(`mousemove`, on_pan_move)
|
|
789
|
-
window.addEventListener(`mouseup`, on_pan_end)
|
|
790
|
-
return
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
drag_state = {
|
|
794
|
-
start: coords,
|
|
795
|
-
current: coords,
|
|
796
|
-
bounds: svg_element.getBoundingClientRect(),
|
|
797
|
-
}
|
|
798
|
-
window.addEventListener(`mousemove`, on_window_mouse_move)
|
|
799
|
-
window.addEventListener(`mouseup`, on_window_mouse_up)
|
|
800
|
-
evt.preventDefault()
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
// Wheel handler for pan (requires focus and shift)
|
|
804
|
-
function handle_wheel(evt: WheelEvent) {
|
|
805
|
-
const pan_enabled = pan?.enabled !== false
|
|
806
|
-
// Only capture wheel when focused AND Shift is held
|
|
807
|
-
// Use shift_held state (tracked via keydown/keyup) for compatibility with synthetic events
|
|
808
|
-
if (!pan_enabled || !is_focused || !shift_held) return
|
|
809
|
-
|
|
810
|
-
evt.preventDefault()
|
|
811
|
-
|
|
812
|
-
// Clamp to at least 1 to avoid Infinity deltas when padding equals container size
|
|
813
|
-
const plot_width = Math.max(1, width - pad.l - pad.r)
|
|
814
|
-
const plot_height = Math.max(1, height - pad.t - pad.b)
|
|
815
|
-
const sensitivity = pan?.wheel_sensitivity ?? 1
|
|
816
|
-
|
|
817
|
-
// Pan along the dominant wheel direction
|
|
818
|
-
if (Math.abs(evt.deltaX) > Math.abs(evt.deltaY)) {
|
|
819
|
-
const dx = evt.deltaX * sensitivity
|
|
820
|
-
ranges.current.x = pan_range_by_pixels(ranges.current.x, dx, plot_width, final_x_axis.scale_type)
|
|
821
|
-
ranges.current.x2 = pan_range_by_pixels(ranges.current.x2, dx, plot_width, final_x2_axis.scale_type)
|
|
822
|
-
} else {
|
|
823
|
-
const dy = evt.deltaY * sensitivity
|
|
824
|
-
ranges.current.y = pan_range_by_pixels(ranges.current.y, dy, plot_height, final_y_axis.scale_type)
|
|
825
|
-
ranges.current.y2 = pan_range_by_pixels(ranges.current.y2, dy, plot_height, final_y2_axis.scale_type)
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
// Touch handlers for pinch-zoom and two-finger pan
|
|
830
|
-
function handle_touch_start(evt: TouchEvent) {
|
|
831
|
-
const touch_enabled = pan?.enabled !== false && pan?.touch_enabled !== false
|
|
832
|
-
if (!touch_enabled || evt.touches.length !== 2) return
|
|
833
|
-
|
|
834
|
-
evt.preventDefault()
|
|
835
|
-
const touches = Array.from(evt.touches)
|
|
836
|
-
touch_state = {
|
|
837
|
-
start_touches: touches.map((touch) => ({ x: touch.clientX, y: touch.clientY })),
|
|
838
|
-
initial_x_range: [...ranges.current.x] as [number, number],
|
|
839
|
-
initial_x2_range: [...ranges.current.x2] as [number, number],
|
|
840
|
-
initial_y_range: [...ranges.current.y] as [number, number],
|
|
841
|
-
initial_y2_range: [...ranges.current.y2] as [number, number],
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
function handle_touch_move(evt: TouchEvent) {
|
|
846
|
-
if (!touch_state || evt.touches.length !== 2) return
|
|
847
|
-
evt.preventDefault()
|
|
848
|
-
|
|
849
|
-
const [t1, t2] = Array.from(evt.touches)
|
|
850
|
-
const [s1, s2] = touch_state.start_touches
|
|
851
|
-
|
|
852
|
-
// Calculate center movement for pan
|
|
853
|
-
const start_center = { x: (s1.x + s2.x) / 2, y: (s1.y + s2.y) / 2 }
|
|
854
|
-
const curr_center = {
|
|
855
|
-
x: (t1.clientX + t2.clientX) / 2,
|
|
856
|
-
y: (t1.clientY + t2.clientY) / 2,
|
|
857
|
-
}
|
|
858
|
-
const dx = curr_center.x - start_center.x
|
|
859
|
-
const dy = curr_center.y - start_center.y
|
|
860
|
-
|
|
861
|
-
// Calculate pinch scale (curr/start so spread = zoom out, pinch = zoom in)
|
|
862
|
-
const start_dist = Math.hypot(s2.x - s1.x, s2.y - s1.y)
|
|
863
|
-
// ignore near-coincident touches so curr_dist / start_dist can't blow up the scale
|
|
864
|
-
if (start_dist < MIN_TOUCH_DISTANCE_PIXELS) return
|
|
865
|
-
const curr_dist = Math.hypot(t2.clientX - t1.clientX, t2.clientY - t1.clientY)
|
|
866
|
-
const scale = curr_dist / start_dist
|
|
867
|
-
|
|
868
|
-
// Pinch zoom about the view center (spread = zoom in, pinch = zoom out)
|
|
869
|
-
if (Math.abs(scale - 1) > PINCH_ZOOM_THRESHOLD && scale > Number.EPSILON) {
|
|
870
|
-
zoom_all_axes(touch_state, scale)
|
|
871
|
-
} else pan_all_axes(touch_state, -dx, dy)
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
function handle_touch_end() {
|
|
875
|
-
touch_state = null
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
function handle_double_click() {
|
|
879
|
-
// Reset zoom to initial ranges (undo any pan/zoom)
|
|
880
|
-
ranges.current.x = [...ranges.initial.x] as [number, number]
|
|
881
|
-
ranges.current.x2 = [...ranges.initial.x2] as [number, number]
|
|
882
|
-
ranges.current.y = [...ranges.initial.y] as [number, number]
|
|
883
|
-
ranges.current.y2 = [...ranges.initial.y2] as [number, number]
|
|
884
|
-
// Also reset axis props so future data changes recalculate auto ranges
|
|
885
|
-
x_axis = { ...x_axis, range: [null, null] }
|
|
886
|
-
x2_axis = { ...x2_axis, range: [null, null] }
|
|
887
|
-
y_axis = { ...y_axis, range: [null, null] }
|
|
888
|
-
y2_axis = { ...y2_axis, range: [null, null] }
|
|
889
|
-
}
|
|
638
|
+
onDestroy(() => pan_zoom.destroy())
|
|
890
639
|
|
|
891
640
|
function handle_mouse_move(
|
|
892
641
|
evt: MouseEvent,
|
|
@@ -918,7 +667,10 @@
|
|
|
918
667
|
on_bar_hover?.({ value, count, property, event: evt })
|
|
919
668
|
}
|
|
920
669
|
|
|
921
|
-
const legend_vis = create_legend_visibility(
|
|
670
|
+
const legend_vis = create_legend_visibility(
|
|
671
|
+
() => series,
|
|
672
|
+
(next) => (series = next),
|
|
673
|
+
)
|
|
922
674
|
|
|
923
675
|
// Set theme-aware background when entering fullscreen
|
|
924
676
|
$effect(() => {
|
|
@@ -926,31 +678,24 @@
|
|
|
926
678
|
})
|
|
927
679
|
|
|
928
680
|
// State accessors for shared axis change handler
|
|
681
|
+
// Spread into existing state in each setter to preserve merged type structure
|
|
929
682
|
const axis_state: AxisChangeState<DataSeries> = {
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
},
|
|
936
|
-
set_axis: (axis, config) => {
|
|
937
|
-
// Spread into existing state to preserve merged type structure
|
|
938
|
-
if (axis === `x`) x_axis = { ...x_axis, ...config }
|
|
939
|
-
else if (axis === `x2`) x2_axis = { ...x2_axis, ...config }
|
|
940
|
-
else if (axis === `y`) y_axis = { ...y_axis, ...config }
|
|
941
|
-
else y2_axis = { ...y2_axis, ...config }
|
|
683
|
+
axes: {
|
|
684
|
+
x: { get: () => x_axis, set: (config) => (x_axis = { ...x_axis, ...config }) },
|
|
685
|
+
x2: { get: () => x2_axis, set: (config) => (x2_axis = { ...x2_axis, ...config }) },
|
|
686
|
+
y: { get: () => y_axis, set: (config) => (y_axis = { ...y_axis, ...config }) },
|
|
687
|
+
y2: { get: () => y2_axis, set: (config) => (y2_axis = { ...y2_axis, ...config }) },
|
|
942
688
|
},
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
get_loading: () => axis_loading,
|
|
946
|
-
set_loading: (axis) => (axis_loading = axis),
|
|
689
|
+
series: { get: () => series, set: (next) => (series = next) },
|
|
690
|
+
loading: { get: () => axis_loading, set: (axis) => (axis_loading = axis) },
|
|
947
691
|
}
|
|
948
692
|
|
|
949
693
|
// Shared handler + one-shot auto-load bound to this component's state
|
|
950
|
-
const { handle_axis_change, try_auto_load } = create_axis_loader(
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
694
|
+
const { handle_axis_change, try_auto_load } = create_axis_loader(axis_state, () => ({
|
|
695
|
+
data_loader,
|
|
696
|
+
on_axis_change,
|
|
697
|
+
on_error,
|
|
698
|
+
}))
|
|
954
699
|
$effect(try_auto_load)
|
|
955
700
|
</script>
|
|
956
701
|
|
|
@@ -988,11 +733,9 @@
|
|
|
988
733
|
evt.preventDefault()
|
|
989
734
|
fullscreen = false
|
|
990
735
|
}
|
|
991
|
-
|
|
992
|
-
}}
|
|
993
|
-
onkeyup={(evt) => {
|
|
994
|
-
if (evt.key === `Shift`) shift_held = false
|
|
736
|
+
pan_zoom.on_window_key_down(evt)
|
|
995
737
|
}}
|
|
738
|
+
onkeyup={pan_zoom.on_window_key_up}
|
|
996
739
|
/>
|
|
997
740
|
|
|
998
741
|
<div
|
|
@@ -1017,38 +760,25 @@
|
|
|
1017
760
|
bind:this={svg_element}
|
|
1018
761
|
role="application"
|
|
1019
762
|
aria-label={rest[`aria-label`] ??
|
|
1020
|
-
|
|
1021
|
-
`Histogram`)}
|
|
763
|
+
([final_x_axis.label, final_y_axis.label].filter(Boolean).join(` vs `) || `Histogram`)}
|
|
1022
764
|
tabindex="0"
|
|
1023
|
-
onfocusin={() => (
|
|
1024
|
-
onfocusout={() => (
|
|
765
|
+
onfocusin={() => pan_zoom.set_focused(true)}
|
|
766
|
+
onfocusout={() => pan_zoom.set_focused(false)}
|
|
1025
767
|
onmouseenter={() => (hovered = true)}
|
|
1026
|
-
onmousedown={
|
|
768
|
+
onmousedown={pan_zoom.on_mouse_down}
|
|
1027
769
|
onmouseleave={() => {
|
|
1028
770
|
hovered = false
|
|
1029
771
|
hover_info = null
|
|
1030
772
|
on_bar_hover?.(null)
|
|
1031
773
|
}}
|
|
1032
|
-
ondblclick={
|
|
1033
|
-
onwheel={
|
|
1034
|
-
ontouchstart={
|
|
1035
|
-
ontouchmove={
|
|
1036
|
-
ontouchend={
|
|
1037
|
-
ontouchcancel={
|
|
1038
|
-
style:cursor={
|
|
1039
|
-
|
|
1040
|
-
: shift_held && pan?.enabled !== false
|
|
1041
|
-
? `grab`
|
|
1042
|
-
: `crosshair`}
|
|
1043
|
-
onkeydown={(event) => {
|
|
1044
|
-
if (event.key === `Escape` && drag_state.start) {
|
|
1045
|
-
drag_state = { start: null, current: null, bounds: null }
|
|
1046
|
-
}
|
|
1047
|
-
if ([`Enter`, ` `].includes(event.key)) {
|
|
1048
|
-
event.preventDefault()
|
|
1049
|
-
handle_double_click()
|
|
1050
|
-
}
|
|
1051
|
-
}}
|
|
774
|
+
ondblclick={pan_zoom.reset_view}
|
|
775
|
+
onwheel={pan_zoom.on_wheel}
|
|
776
|
+
ontouchstart={pan_zoom.on_touch_start}
|
|
777
|
+
ontouchmove={pan_zoom.on_touch_move}
|
|
778
|
+
ontouchend={pan_zoom.on_touch_end}
|
|
779
|
+
ontouchcancel={pan_zoom.on_touch_end}
|
|
780
|
+
style:cursor={pan_zoom.cursor}
|
|
781
|
+
onkeydown={pan_zoom.on_key_down}
|
|
1052
782
|
>
|
|
1053
783
|
<!-- Define clip path for chart area -->
|
|
1054
784
|
<defs>
|
|
@@ -1065,7 +795,7 @@
|
|
|
1065
795
|
<!-- Reference lines: below grid (must render first to appear behind grid) -->
|
|
1066
796
|
{@render ref_lines_layer(ref_lines_by_z.below_grid)}
|
|
1067
797
|
|
|
1068
|
-
<ZoomRect start={
|
|
798
|
+
<ZoomRect start={pan_zoom.drag_start} current={pan_zoom.drag_current} />
|
|
1069
799
|
|
|
1070
800
|
<ZeroLines
|
|
1071
801
|
{display}
|
|
@@ -1097,17 +827,17 @@
|
|
|
1097
827
|
<!-- X-axis -->
|
|
1098
828
|
<PlotAxis
|
|
1099
829
|
side="x"
|
|
1100
|
-
ticks={ticks.x
|
|
830
|
+
ticks={ticks.x}
|
|
1101
831
|
place={scales.x}
|
|
1102
832
|
axis={final_x_axis}
|
|
1103
|
-
domain={ranges.current.x
|
|
833
|
+
domain={ranges.current.x}
|
|
1104
834
|
{pad}
|
|
1105
835
|
{width}
|
|
1106
836
|
{height}
|
|
1107
837
|
show_grid={display.x_grid}
|
|
1108
838
|
tick_label={(tick) => get_tick_label(tick, final_x_axis.ticks)}
|
|
1109
839
|
label_x={(pad.l + width - pad.r) / 2 + (final_x_axis.label_shift?.x ?? 0)}
|
|
1110
|
-
label_y={height -
|
|
840
|
+
label_y={height - pad.b + AXIS_TITLE_OFFSET + (final_x_axis.label_shift?.y ?? 0)}
|
|
1111
841
|
axis_loading={axis_loading === `x`}
|
|
1112
842
|
on_axis_change={(key) => handle_axis_change(`x`, key)}
|
|
1113
843
|
/>
|
|
@@ -1116,17 +846,17 @@
|
|
|
1116
846
|
{#if x2_series.length > 0}
|
|
1117
847
|
<PlotAxis
|
|
1118
848
|
side="x2"
|
|
1119
|
-
ticks={ticks.x2
|
|
849
|
+
ticks={ticks.x2}
|
|
1120
850
|
place={scales.x2}
|
|
1121
851
|
axis={final_x2_axis}
|
|
1122
|
-
domain={ranges.current.x2
|
|
852
|
+
domain={ranges.current.x2}
|
|
1123
853
|
{pad}
|
|
1124
854
|
{width}
|
|
1125
855
|
{height}
|
|
1126
856
|
show_grid={display.x2_grid}
|
|
1127
857
|
tick_label={(tick) => get_tick_label(tick, final_x2_axis.ticks)}
|
|
1128
858
|
label_x={(pad.l + width - pad.r) / 2 + (final_x2_axis.label_shift?.x ?? 0)}
|
|
1129
|
-
label_y={Math.max(12, pad.t - (final_x2_axis.label_shift?.y ??
|
|
859
|
+
label_y={Math.max(12, pad.t - (final_x2_axis.label_shift?.y ?? AXIS_TITLE_OFFSET))}
|
|
1130
860
|
axis_loading={axis_loading === `x2`}
|
|
1131
861
|
on_axis_change={(key) => handle_axis_change(`x2`, key)}
|
|
1132
862
|
/>
|
|
@@ -1135,10 +865,10 @@
|
|
|
1135
865
|
<!-- Y-axis -->
|
|
1136
866
|
<PlotAxis
|
|
1137
867
|
side="y"
|
|
1138
|
-
ticks={ticks.y
|
|
868
|
+
ticks={ticks.y}
|
|
1139
869
|
place={scales.y}
|
|
1140
870
|
axis={final_y_axis}
|
|
1141
|
-
domain={ranges.current.y
|
|
871
|
+
domain={ranges.current.y}
|
|
1142
872
|
{pad}
|
|
1143
873
|
{width}
|
|
1144
874
|
{height}
|
|
@@ -1146,7 +876,8 @@
|
|
|
1146
876
|
tick_label={(tick) => get_tick_label(tick, final_y_axis.ticks)}
|
|
1147
877
|
label_x={Math.max(
|
|
1148
878
|
12,
|
|
1149
|
-
pad.l -
|
|
879
|
+
pad.l -
|
|
880
|
+
(final_y_axis.tick?.label?.inside ? 0 : tick_label_widths.y_max) -
|
|
1150
881
|
LABEL_GAP_DEFAULT,
|
|
1151
882
|
) + (final_y_axis.label_shift?.x ?? 0)}
|
|
1152
883
|
label_y={pad.t + (height - pad.t - pad.b) / 2 + (final_y_axis.label_shift?.y ?? 0)}
|
|
@@ -1158,10 +889,10 @@
|
|
|
1158
889
|
{#if y2_series.length > 0}
|
|
1159
890
|
<PlotAxis
|
|
1160
891
|
side="y2"
|
|
1161
|
-
ticks={ticks.y2
|
|
892
|
+
ticks={ticks.y2}
|
|
1162
893
|
place={scales.y2}
|
|
1163
894
|
axis={final_y2_axis}
|
|
1164
|
-
domain={ranges.current.y2
|
|
895
|
+
domain={ranges.current.y2}
|
|
1165
896
|
{pad}
|
|
1166
897
|
{width}
|
|
1167
898
|
{height}
|
|
@@ -1175,24 +906,19 @@
|
|
|
1175
906
|
{/if}
|
|
1176
907
|
|
|
1177
908
|
<!-- Histogram bars (rendered after axes so bars appear above grid lines) -->
|
|
1178
|
-
{#each histogram_data as
|
|
1179
|
-
{ id, bins, color, label, x_scale, y_scale, x_axis: srs_x_axis, y_axis, series_idx },
|
|
1180
|
-
idx
|
|
1181
|
-
(id ?? idx)
|
|
1182
|
-
}
|
|
909
|
+
{#each histogram_data as { id, bins, color, label, x_scale, y_scale, x_axis: srs_x_axis, y_axis, series_idx }, idx (id ?? idx)}
|
|
1183
910
|
<g
|
|
1184
911
|
class="histogram-series"
|
|
1185
912
|
data-series-idx={series_idx}
|
|
1186
913
|
clip-path="url(#{clip_path_id})"
|
|
1187
|
-
opacity={hovered_legend_series_idx !== null &&
|
|
1188
|
-
hovered_legend_series_idx !== series_idx
|
|
914
|
+
opacity={hovered_legend_series_idx !== null && hovered_legend_series_idx !== series_idx
|
|
1189
915
|
? 0.25
|
|
1190
916
|
: 1}
|
|
1191
917
|
>
|
|
1192
918
|
{#each bins as bin, bin_idx (bin_idx)}
|
|
1193
919
|
{@const bar_x = x_scale(bin.x0!)}
|
|
1194
920
|
{@const bar_width = Math.max(1, Math.abs(x_scale(bin.x1!) - bar_x))}
|
|
1195
|
-
{@const bar_height = Math.max(0,
|
|
921
|
+
{@const bar_height = Math.max(0, height - pad.b - y_scale(bin.length))}
|
|
1196
922
|
{@const bar_y = y_scale(bin.length)}
|
|
1197
923
|
{@const value = (bin.x0! + bin.x1!) / 2}
|
|
1198
924
|
{#if bar_height > 0}
|
|
@@ -1212,22 +938,22 @@
|
|
|
1212
938
|
role="button"
|
|
1213
939
|
tabindex="0"
|
|
1214
940
|
onmousemove={(evt) =>
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
941
|
+
handle_mouse_move(
|
|
942
|
+
evt,
|
|
943
|
+
value,
|
|
944
|
+
bin.length,
|
|
945
|
+
label,
|
|
946
|
+
(y_axis ?? `y1`) as `y1` | `y2`,
|
|
947
|
+
series_idx,
|
|
948
|
+
(srs_x_axis ?? `x1`) as `x1` | `x2`,
|
|
949
|
+
)}
|
|
1224
950
|
onmouseleave={() => {
|
|
1225
951
|
hover_info = null
|
|
1226
952
|
change(null)
|
|
1227
953
|
on_bar_hover?.(null)
|
|
1228
954
|
}}
|
|
1229
955
|
onclick={(event) =>
|
|
1230
|
-
|
|
956
|
+
on_bar_click?.({ value, count: bin.length, property: label, event })}
|
|
1231
957
|
onkeydown={(event: KeyboardEvent) => {
|
|
1232
958
|
if ([`Enter`, ` `].includes(event.key)) {
|
|
1233
959
|
event.preventDefault()
|
|
@@ -1243,6 +969,23 @@
|
|
|
1243
969
|
|
|
1244
970
|
<!-- Reference lines: above all -->
|
|
1245
971
|
{@render ref_lines_layer(ref_lines_by_z.above_all)}
|
|
972
|
+
|
|
973
|
+
<!-- Marginal distribution strips -->
|
|
974
|
+
<PlotMarginals
|
|
975
|
+
marginals={resolved_marginals}
|
|
976
|
+
series={marginal_series}
|
|
977
|
+
{width}
|
|
978
|
+
{height}
|
|
979
|
+
{pad}
|
|
980
|
+
has_axis={marginal_has_axis}
|
|
981
|
+
axes={{
|
|
982
|
+
x1: marginal_axis(scales.x, ranges.current.x, final_x_axis),
|
|
983
|
+
x2: marginal_axis(scales.x2, ranges.current.x2, final_x2_axis),
|
|
984
|
+
y1: marginal_axis(scales.y, ranges.current.y, final_y_axis),
|
|
985
|
+
y2: marginal_axis(scales.y2, ranges.current.y2, final_y2_axis),
|
|
986
|
+
}}
|
|
987
|
+
id={clip_path_id}
|
|
988
|
+
/>
|
|
1246
989
|
</svg>
|
|
1247
990
|
|
|
1248
991
|
<!-- Tooltip (outside SVG for proper HTML rendering) -->
|
|
@@ -1260,8 +1003,8 @@
|
|
|
1260
1003
|
{#if tooltip}
|
|
1261
1004
|
{@render tooltip({ ...hover_info, fullscreen })}
|
|
1262
1005
|
{:else}
|
|
1263
|
-
<div>Value: {
|
|
1264
|
-
<div>Count: {
|
|
1006
|
+
<div>Value: {format_value_or_num(value, hover_info.x_axis.format)}</div>
|
|
1007
|
+
<div>Count: {format_value_or_num(count, hover_info.y_axis.format)}</div>
|
|
1265
1008
|
{#if mode === `overlay`}<div>{property}</div>{/if}
|
|
1266
1009
|
{/if}
|
|
1267
1010
|
</PlotTooltip>
|
|
@@ -1300,36 +1043,33 @@
|
|
|
1300
1043
|
{/if}
|
|
1301
1044
|
|
|
1302
1045
|
{#if show_legend && legend != null && series.length > 1}
|
|
1303
|
-
{@const
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
: legend_placement
|
|
1311
|
-
? tweened_legend_coords.current.y
|
|
1312
|
-
: pad.t + 10}
|
|
1046
|
+
{@const legend_pos = placed_coords(
|
|
1047
|
+
legend_auto_outside,
|
|
1048
|
+
{ x: legend_outside_x, y: legend_outside_y },
|
|
1049
|
+
legend_placement,
|
|
1050
|
+
legend_tween.coords.current,
|
|
1051
|
+
{ x: pad.l + 10, y: pad.t + 10 },
|
|
1052
|
+
)}
|
|
1313
1053
|
<PlotLegend
|
|
1314
1054
|
bind:root_element={legend_element}
|
|
1315
1055
|
{...legend}
|
|
1316
1056
|
series_data={legend_data}
|
|
1317
|
-
on_toggle={legend?.on_toggle ??
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1057
|
+
on_toggle={legend?.on_toggle ??
|
|
1058
|
+
((series_idx: number) => {
|
|
1059
|
+
if (series_idx < 0 || series_idx >= series.length) return
|
|
1060
|
+
legend_vis.on_toggle(series_idx)
|
|
1061
|
+
on_series_toggle(series_idx)
|
|
1062
|
+
})}
|
|
1322
1063
|
on_double_click={legend?.on_double_click ?? legend_vis.on_double_click}
|
|
1323
|
-
on_hover_change={
|
|
1064
|
+
on_hover_change={legend_tween.set_locked}
|
|
1324
1065
|
on_item_hover={(item) =>
|
|
1325
|
-
(hovered_legend_series_idx =
|
|
1326
|
-
? item.series_idx
|
|
1327
|
-
: null)}
|
|
1066
|
+
(hovered_legend_series_idx =
|
|
1067
|
+
item != null && item.series_idx >= 0 ? item.series_idx : null)}
|
|
1328
1068
|
active_series_idx={hover_info?.series_idx ?? hovered_legend_series_idx}
|
|
1329
1069
|
style={`
|
|
1330
1070
|
position: absolute;
|
|
1331
|
-
left: ${
|
|
1332
|
-
top: ${
|
|
1071
|
+
left: ${legend_pos.x}px;
|
|
1072
|
+
top: ${legend_pos.y}px;
|
|
1333
1073
|
pointer-events: auto;
|
|
1334
1074
|
${legend?.style || ``}
|
|
1335
1075
|
`}
|
|
@@ -1390,7 +1130,9 @@
|
|
|
1390
1130
|
.histogram :global(.pane-toggle),
|
|
1391
1131
|
.histogram .header-controls {
|
|
1392
1132
|
opacity: 0;
|
|
1393
|
-
transition:
|
|
1133
|
+
transition:
|
|
1134
|
+
opacity 0.2s,
|
|
1135
|
+
background-color 0.2s;
|
|
1394
1136
|
}
|
|
1395
1137
|
.histogram:hover :global(.pane-toggle),
|
|
1396
1138
|
.histogram:hover .header-controls,
|