matterviz 0.3.1 → 0.3.3
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 -2
- package/dist/FilePicker.svelte +154 -96
- package/dist/Icon.svelte +20 -14
- package/dist/MillerIndexInput.svelte +27 -21
- package/dist/api/optimade.js +6 -6
- package/dist/app.css +216 -178
- package/dist/brillouin/BrillouinZone.svelte +299 -198
- package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
- package/dist/brillouin/BrillouinZoneExportPane.svelte +74 -55
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
- package/dist/brillouin/BrillouinZoneScene.svelte +277 -165
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
- package/dist/brillouin/compute.js +11 -6
- package/dist/chempot-diagram/ChemPotDiagram.svelte +327 -0
- package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +847 -0
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +3194 -0
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
- package/dist/chempot-diagram/ChemPotScene3D.svelte +11 -0
- package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
- package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
- package/dist/chempot-diagram/async-compute.svelte.js +77 -0
- package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
- package/dist/chempot-diagram/chempot-worker.js +11 -0
- package/dist/chempot-diagram/color.d.ts +10 -0
- package/dist/chempot-diagram/color.js +32 -0
- package/dist/chempot-diagram/compute.d.ts +48 -0
- package/dist/chempot-diagram/compute.js +812 -0
- package/dist/chempot-diagram/index.d.ts +6 -0
- package/dist/chempot-diagram/index.js +6 -0
- package/dist/chempot-diagram/pointer.d.ts +16 -0
- package/dist/chempot-diagram/pointer.js +40 -0
- package/dist/chempot-diagram/temperature.d.ts +15 -0
- package/dist/chempot-diagram/temperature.js +36 -0
- package/dist/chempot-diagram/types.d.ts +86 -0
- package/dist/chempot-diagram/types.js +28 -0
- package/dist/colors/index.d.ts +3 -1
- package/dist/colors/index.js +9 -3
- package/dist/composition/BarChart.svelte +141 -77
- package/dist/composition/BubbleChart.svelte +107 -52
- package/dist/composition/Composition.svelte +100 -79
- package/dist/composition/Formula.svelte +108 -62
- package/dist/composition/FormulaFilter.svelte +973 -353
- package/dist/composition/FormulaFilter.svelte.d.ts +35 -1
- package/dist/composition/PieChart.svelte +199 -99
- package/dist/composition/PieChart.svelte.d.ts +1 -1
- package/dist/composition/format.d.ts +5 -0
- package/dist/composition/format.js +20 -3
- package/dist/composition/parse.js +14 -9
- package/dist/convex-hull/ConvexHull.svelte +93 -38
- package/dist/convex-hull/ConvexHull2D.svelte +551 -393
- package/dist/convex-hull/ConvexHull3D.svelte +1303 -825
- package/dist/convex-hull/ConvexHull4D.svelte +1012 -686
- package/dist/convex-hull/ConvexHullControls.svelte +115 -28
- package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
- package/dist/convex-hull/ConvexHullStats.svelte +821 -249
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
- package/dist/convex-hull/ConvexHullTooltip.svelte +41 -16
- package/dist/convex-hull/GasPressureControls.svelte +104 -61
- package/dist/convex-hull/StructurePopup.svelte +25 -4
- package/dist/convex-hull/TemperatureSlider.svelte +45 -25
- package/dist/convex-hull/barycentric-coords.js +13 -7
- package/dist/convex-hull/demo-temperature.d.ts +6 -0
- package/dist/convex-hull/demo-temperature.js +40 -0
- package/dist/convex-hull/gas-thermodynamics.js +17 -12
- package/dist/convex-hull/helpers.d.ts +10 -1
- package/dist/convex-hull/helpers.js +79 -38
- package/dist/convex-hull/index.d.ts +1 -0
- package/dist/convex-hull/index.js +1 -0
- package/dist/convex-hull/thermodynamics.d.ts +8 -21
- package/dist/convex-hull/thermodynamics.js +163 -69
- package/dist/convex-hull/types.d.ts +12 -12
- package/dist/convex-hull/types.js +0 -12
- package/dist/coordination/CoordinationBarPlot.svelte +232 -176
- package/dist/element/BohrAtom.svelte +56 -13
- package/dist/element/ElementHeading.svelte +7 -2
- package/dist/element/ElementPhoto.svelte +15 -9
- package/dist/element/ElementStats.svelte +10 -4
- package/dist/element/ElementTile.svelte +137 -73
- package/dist/element/Nucleus.svelte +39 -11
- package/dist/element/data.js +2 -14
- package/dist/element/data.json.gz +0 -0
- package/dist/element/types.d.ts +1 -0
- package/dist/feedback/ClickFeedback.svelte +16 -5
- package/dist/feedback/DragOverlay.svelte +10 -2
- package/dist/feedback/Spinner.svelte +4 -2
- package/dist/feedback/StatusMessage.svelte +8 -2
- package/dist/fermi-surface/FermiSlice.svelte +118 -88
- package/dist/fermi-surface/FermiSurface.svelte +336 -239
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
- package/dist/fermi-surface/FermiSurfaceScene.svelte +536 -343
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
- package/dist/fermi-surface/compute.js +16 -20
- package/dist/fermi-surface/parse.js +37 -33
- package/dist/fermi-surface/symmetry.js +2 -7
- package/dist/fermi-surface/types.d.ts +3 -5
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +1527 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +30 -0
- package/dist/heatmap-matrix/index.d.ts +53 -0
- package/dist/heatmap-matrix/index.js +100 -0
- package/dist/heatmap-matrix/shared.d.ts +2 -0
- package/dist/heatmap-matrix/shared.js +4 -0
- package/dist/icons.d.ts +111 -0
- package/dist/icons.js +158 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +5 -2
- package/dist/io/decompress.js +1 -1
- package/dist/io/export.d.ts +3 -0
- package/dist/io/export.js +138 -140
- package/dist/io/file-drop.d.ts +7 -0
- package/dist/io/file-drop.js +43 -0
- package/dist/io/index.d.ts +2 -2
- package/dist/io/index.js +2 -112
- package/dist/io/is-binary.js +2 -3
- package/dist/io/types.d.ts +1 -0
- package/dist/io/url-drop.d.ts +2 -0
- package/dist/io/url-drop.js +117 -0
- package/dist/isosurface/Isosurface.svelte +220 -110
- package/dist/isosurface/IsosurfaceControls.svelte +65 -28
- package/dist/isosurface/parse.js +104 -56
- package/dist/isosurface/slice.d.ts +2 -1
- package/dist/isosurface/slice.js +8 -13
- package/dist/isosurface/types.d.ts +14 -1
- package/dist/isosurface/types.js +152 -5
- package/dist/labels.d.ts +2 -1
- package/dist/labels.js +12 -8
- package/dist/layout/FullscreenToggle.svelte +11 -2
- package/dist/layout/InfoCard.svelte +38 -6
- package/dist/layout/InfoTag.svelte +125 -94
- package/dist/layout/PropertyFilter.svelte +82 -37
- package/dist/layout/SettingsSection.svelte +85 -55
- package/dist/layout/SubpageGrid.svelte +82 -0
- package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
- package/dist/layout/index.d.ts +1 -0
- package/dist/layout/index.js +1 -0
- package/dist/layout/json-tree/JsonNode.svelte +266 -223
- package/dist/layout/json-tree/JsonTree.svelte +516 -429
- package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
- package/dist/layout/json-tree/JsonValue.svelte +281 -173
- package/dist/layout/json-tree/types.d.ts +10 -2
- package/dist/layout/json-tree/utils.d.ts +2 -0
- package/dist/layout/json-tree/utils.js +37 -2
- package/dist/marching-cubes.js +25 -2
- package/dist/math.d.ts +20 -17
- package/dist/math.js +474 -57
- package/dist/overlays/ContextMenu.svelte +66 -40
- package/dist/overlays/DraggablePane.svelte +331 -154
- package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
- package/dist/periodic-table/PeriodicTable.svelte +278 -145
- package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
- package/dist/periodic-table/PropertySelect.svelte +25 -7
- package/dist/periodic-table/TableInset.svelte +8 -3
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +559 -267
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
- package/dist/phase-diagram/PhaseDiagramControls.svelte +131 -51
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +126 -0
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +160 -110
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +8 -1
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +217 -86
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
- package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
- package/dist/phase-diagram/build-diagram.js +9 -9
- package/dist/phase-diagram/colors.js +1 -3
- package/dist/phase-diagram/index.d.ts +2 -0
- package/dist/phase-diagram/index.js +2 -0
- package/dist/phase-diagram/parse.js +10 -9
- package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
- package/dist/phase-diagram/svg-to-diagram.js +869 -0
- package/dist/phase-diagram/types.d.ts +10 -0
- package/dist/phase-diagram/utils.d.ts +8 -4
- package/dist/phase-diagram/utils.js +219 -74
- package/dist/plot/AxisLabel.svelte +51 -0
- package/dist/plot/AxisLabel.svelte.d.ts +16 -0
- package/dist/plot/BarPlot.svelte +1461 -768
- package/dist/plot/BarPlot.svelte.d.ts +3 -3
- package/dist/plot/BarPlotControls.svelte +33 -6
- package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ColorBar.svelte +533 -383
- package/dist/plot/ColorBar.svelte.d.ts +1 -1
- package/dist/plot/ColorScaleSelect.svelte +28 -7
- package/dist/plot/ElementScatter.svelte +38 -16
- package/dist/plot/FillArea.svelte +152 -92
- package/dist/plot/Histogram.svelte +1162 -709
- package/dist/plot/Histogram.svelte.d.ts +1 -1
- package/dist/plot/HistogramControls.svelte +81 -18
- package/dist/plot/HistogramControls.svelte.d.ts +6 -2
- package/dist/plot/InteractiveAxisLabel.svelte +34 -11
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
- package/dist/plot/Line.svelte +63 -28
- package/dist/plot/PlotControls.svelte +221 -96
- package/dist/plot/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/PlotLegend.svelte +174 -91
- package/dist/plot/PlotTooltip.svelte +45 -6
- package/dist/plot/PortalSelect.svelte +175 -146
- package/dist/plot/ReferenceLine.svelte +77 -22
- package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
- package/dist/plot/ReferenceLine3D.svelte +132 -107
- package/dist/plot/ReferencePlane.svelte +146 -123
- package/dist/plot/ScatterPlot.svelte +1880 -1156
- package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
- package/dist/plot/ScatterPlot3D.svelte +256 -131
- package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3DControls.svelte +300 -297
- package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
- package/dist/plot/ScatterPlot3DScene.svelte +608 -406
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlotControls.svelte +150 -70
- package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ScatterPoint.svelte +98 -26
- package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
- package/dist/plot/SpacegroupBarPlot.svelte +142 -85
- package/dist/plot/Surface3D.svelte +159 -108
- package/dist/plot/ZeroLines.svelte +96 -0
- package/dist/plot/ZeroLines.svelte.d.ts +32 -0
- package/dist/plot/ZoomRect.svelte +23 -0
- package/dist/plot/ZoomRect.svelte.d.ts +8 -0
- package/dist/plot/axis-utils.d.ts +1 -1
- package/dist/plot/axis-utils.js +1 -3
- package/dist/plot/data-cleaning.js +12 -28
- package/dist/plot/data-transform.js +2 -1
- package/dist/plot/fill-utils.js +2 -0
- package/dist/plot/index.d.ts +6 -2
- package/dist/plot/index.js +6 -2
- package/dist/plot/interactions.d.ts +8 -10
- package/dist/plot/interactions.js +2 -3
- package/dist/plot/layout.d.ts +11 -2
- package/dist/plot/layout.js +44 -17
- package/dist/plot/reference-line.d.ts +5 -22
- package/dist/plot/reference-line.js +12 -84
- package/dist/plot/scales.js +24 -36
- package/dist/plot/types.d.ts +53 -40
- package/dist/plot/types.js +12 -7
- package/dist/plot/utils/label-placement.d.ts +32 -15
- package/dist/plot/utils/label-placement.js +227 -63
- package/dist/plot/utils/series-visibility.js +2 -3
- package/dist/plot/utils.d.ts +1 -0
- package/dist/plot/utils.js +14 -0
- package/dist/rdf/RdfPlot.svelte +173 -132
- package/dist/rdf/calc-rdf.js +4 -5
- package/dist/sanitize.d.ts +4 -0
- package/dist/sanitize.js +107 -0
- package/dist/settings.d.ts +21 -6
- package/dist/settings.js +63 -19
- package/dist/spectral/Bands.svelte +963 -412
- package/dist/spectral/Bands.svelte.d.ts +22 -2
- package/dist/spectral/BandsAndDos.svelte +90 -49
- package/dist/spectral/BrillouinBandsDos.svelte +151 -93
- package/dist/spectral/Dos.svelte +389 -258
- package/dist/spectral/helpers.d.ts +23 -1
- package/dist/spectral/helpers.js +119 -51
- package/dist/spectral/types.d.ts +2 -0
- package/dist/state.svelte.d.ts +1 -1
- package/dist/state.svelte.js +3 -2
- package/dist/structure/Arrow.svelte +59 -20
- package/dist/structure/AtomLegend.svelte +231 -129
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/Bond.svelte +73 -47
- package/dist/structure/CanvasTooltip.svelte +10 -2
- package/dist/structure/CellSelect.svelte +148 -51
- package/dist/structure/Cylinder.svelte +33 -17
- package/dist/structure/Lattice.svelte +88 -33
- package/dist/structure/Structure.svelte +1077 -821
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureControls.svelte +373 -139
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureExportPane.svelte +124 -89
- package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +304 -231
- package/dist/structure/StructureScene.svelte +919 -445
- package/dist/structure/StructureScene.svelte.d.ts +16 -7
- package/dist/structure/atom-properties.d.ts +6 -2
- package/dist/structure/atom-properties.js +42 -29
- package/dist/structure/bonding.js +6 -7
- package/dist/structure/export.js +22 -34
- package/dist/structure/ferrox-wasm-types.d.ts +3 -2
- package/dist/structure/ferrox-wasm-types.js +0 -3
- package/dist/structure/ferrox-wasm.d.ts +3 -2
- package/dist/structure/ferrox-wasm.js +2 -3
- package/dist/structure/index.d.ts +16 -0
- package/dist/structure/index.js +88 -6
- package/dist/structure/measure.d.ts +2 -2
- package/dist/structure/measure.js +4 -44
- package/dist/structure/parse.js +130 -155
- package/dist/structure/partial-occupancy.d.ts +25 -0
- package/dist/structure/partial-occupancy.js +99 -0
- package/dist/structure/pbc.d.ts +1 -0
- package/dist/structure/pbc.js +16 -6
- package/dist/structure/supercell.d.ts +2 -2
- package/dist/structure/supercell.js +12 -22
- package/dist/structure/validation.js +5 -3
- package/dist/symmetry/SymmetryStats.svelte +94 -37
- package/dist/symmetry/WyckoffTable.svelte +42 -14
- package/dist/symmetry/cell-transform.js +5 -3
- package/dist/symmetry/index.d.ts +7 -4
- package/dist/symmetry/index.js +87 -21
- package/dist/symmetry/spacegroups.js +148 -148
- package/dist/table/HeatmapTable.svelte +1112 -516
- package/dist/table/HeatmapTable.svelte.d.ts +12 -1
- package/dist/table/ToggleMenu.svelte +125 -90
- package/dist/table/index.d.ts +2 -0
- package/dist/table/index.js +2 -4
- package/dist/theme/ThemeControl.svelte +21 -12
- package/dist/time.js +4 -1
- package/dist/tooltip/TooltipContent.svelte +33 -8
- package/dist/trajectory/Trajectory.svelte +889 -687
- package/dist/trajectory/TrajectoryError.svelte +14 -3
- package/dist/trajectory/TrajectoryExportPane.svelte +148 -90
- package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
- package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
- package/dist/trajectory/constants.d.ts +6 -0
- package/dist/trajectory/constants.js +7 -0
- package/dist/trajectory/extract.js +13 -31
- package/dist/trajectory/format-detect.d.ts +9 -0
- package/dist/trajectory/format-detect.js +76 -0
- package/dist/trajectory/frame-reader.d.ts +17 -0
- package/dist/trajectory/frame-reader.js +332 -0
- package/dist/trajectory/helpers.d.ts +14 -0
- package/dist/trajectory/helpers.js +172 -0
- package/dist/trajectory/index.d.ts +1 -0
- package/dist/trajectory/index.js +23 -14
- package/dist/trajectory/parse/ase.d.ts +2 -0
- package/dist/trajectory/parse/ase.js +77 -0
- package/dist/trajectory/parse/hdf5.d.ts +2 -0
- package/dist/trajectory/parse/hdf5.js +129 -0
- package/dist/trajectory/parse/index.d.ts +12 -0
- package/dist/trajectory/parse/index.js +299 -0
- package/dist/trajectory/parse/lammps.d.ts +5 -0
- package/dist/trajectory/parse/lammps.js +179 -0
- package/dist/trajectory/parse/vasp.d.ts +2 -0
- package/dist/trajectory/parse/vasp.js +68 -0
- package/dist/trajectory/parse/xyz.d.ts +2 -0
- package/dist/trajectory/parse/xyz.js +110 -0
- package/dist/trajectory/plotting.js +13 -8
- package/dist/trajectory/types.d.ts +11 -0
- package/dist/trajectory/types.js +1 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +17 -0
- package/dist/xrd/XrdPlot.svelte +337 -245
- package/dist/xrd/broadening.js +14 -9
- package/dist/xrd/calc-xrd.js +12 -19
- package/dist/xrd/parse.d.ts +1 -1
- package/dist/xrd/parse.js +17 -17
- package/package.json +103 -101
- package/readme.md +4 -4
- package/dist/trajectory/parse.d.ts +0 -42
- package/dist/trajectory/parse.js +0 -1267
- /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
- /package/dist/theme/{themes.js → themes.mjs} +0 -0
|
@@ -1,466 +1,660 @@
|
|
|
1
1
|
<script
|
|
2
2
|
lang="ts"
|
|
3
3
|
generics="Metadata extends Record<string, unknown> = Record<string, unknown>"
|
|
4
|
-
>
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
4
|
+
>
|
|
5
|
+
import type { D3ColorSchemeName, D3InterpolateName } from '../colors'
|
|
6
|
+
import { format_num } from '../labels'
|
|
7
|
+
import type { Vec2, Vec3 } from '../math'
|
|
8
|
+
import type {
|
|
9
|
+
AxisConfig3D,
|
|
10
|
+
CameraProjection3D,
|
|
11
|
+
DataSeries3D,
|
|
12
|
+
DisplayConfig3D,
|
|
13
|
+
InternalPoint3D,
|
|
14
|
+
RefLine3D,
|
|
15
|
+
RefPlane,
|
|
16
|
+
ScaleType,
|
|
17
|
+
Scatter3DHandlerEvent,
|
|
18
|
+
StyleOverrides3D,
|
|
19
|
+
Surface3DConfig,
|
|
20
|
+
} from './types'
|
|
21
|
+
import { T, useTask, useThrelte } from '@threlte/core'
|
|
22
|
+
import * as extras from '@threlte/extras'
|
|
23
|
+
import { scaleLinear } from 'd3-scale'
|
|
24
|
+
import { type ComponentProps, onDestroy, type Snippet, untrack } from 'svelte'
|
|
25
|
+
import type { Camera, Scene } from 'three'
|
|
26
|
+
import * as THREE from 'three'
|
|
27
|
+
import { Line2 } from 'three/examples/jsm/lines/Line2.js'
|
|
28
|
+
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'
|
|
29
|
+
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'
|
|
30
|
+
import { get_series_color } from './data-transform'
|
|
31
|
+
import { normalize_to_scene } from './reference-line'
|
|
32
|
+
import ReferenceLine3D from './ReferenceLine3D.svelte'
|
|
33
|
+
import ReferencePlane from './ReferencePlane.svelte'
|
|
34
|
+
import { create_color_scale, create_size_scale } from './scales'
|
|
35
|
+
import Surface3D from './Surface3D.svelte'
|
|
36
|
+
|
|
37
|
+
let {
|
|
38
|
+
series = [],
|
|
39
|
+
series_visibility = [],
|
|
40
|
+
x_axis = {},
|
|
41
|
+
y_axis = {},
|
|
42
|
+
z_axis = {},
|
|
43
|
+
display = {},
|
|
44
|
+
styles = {},
|
|
45
|
+
surfaces = [],
|
|
46
|
+
ref_lines = [],
|
|
47
|
+
ref_planes = [],
|
|
48
|
+
color_scale = { type: `linear`, scheme: `interpolateViridis` },
|
|
49
|
+
size_scale = { type: `linear`, radius_range: [0.05, 0.2] },
|
|
50
|
+
camera_position = [10, 10, 10] as Vec3,
|
|
51
|
+
camera_projection = `perspective` as CameraProjection3D,
|
|
52
|
+
auto_rotate = 0,
|
|
53
|
+
rotation_damping = 0,
|
|
54
|
+
fov = 50,
|
|
55
|
+
min_zoom = 0.1,
|
|
56
|
+
max_zoom = 100,
|
|
57
|
+
rotate_speed = 2,
|
|
58
|
+
zoom_speed = 2,
|
|
59
|
+
pan_speed = 2,
|
|
60
|
+
ambient_light = 0.6,
|
|
61
|
+
directional_light = 0.8,
|
|
62
|
+
sphere_segments = 16,
|
|
63
|
+
gizmo = true,
|
|
64
|
+
hovered_point = $bindable(null),
|
|
65
|
+
on_point_click,
|
|
66
|
+
on_point_hover,
|
|
67
|
+
tooltip,
|
|
68
|
+
scene = $bindable(),
|
|
69
|
+
camera = $bindable(),
|
|
70
|
+
orbit_controls = $bindable(),
|
|
71
|
+
width = 0,
|
|
72
|
+
height = 0,
|
|
73
|
+
}: {
|
|
74
|
+
series?: DataSeries3D<Metadata>[]
|
|
75
|
+
series_visibility?: boolean[]
|
|
76
|
+
x_axis?: AxisConfig3D
|
|
77
|
+
y_axis?: AxisConfig3D
|
|
78
|
+
z_axis?: AxisConfig3D
|
|
79
|
+
display?: DisplayConfig3D
|
|
80
|
+
styles?: StyleOverrides3D
|
|
81
|
+
surfaces?: Surface3DConfig[]
|
|
82
|
+
ref_lines?: RefLine3D[]
|
|
83
|
+
ref_planes?: RefPlane[]
|
|
84
|
+
color_scale?: {
|
|
85
|
+
type?: ScaleType
|
|
86
|
+
scheme?: D3ColorSchemeName | D3InterpolateName
|
|
87
|
+
value_range?: [number, number]
|
|
88
|
+
}
|
|
89
|
+
size_scale?: {
|
|
90
|
+
type?: ScaleType
|
|
91
|
+
radius_range?: [number, number]
|
|
92
|
+
value_range?: [number, number]
|
|
93
|
+
}
|
|
94
|
+
camera_position?: Vec3
|
|
95
|
+
camera_projection?: CameraProjection3D
|
|
96
|
+
auto_rotate?: number
|
|
97
|
+
rotation_damping?: number
|
|
98
|
+
fov?: number
|
|
99
|
+
min_zoom?: number
|
|
100
|
+
max_zoom?: number
|
|
101
|
+
rotate_speed?: number
|
|
102
|
+
zoom_speed?: number
|
|
103
|
+
pan_speed?: number
|
|
104
|
+
ambient_light?: number
|
|
105
|
+
directional_light?: number
|
|
106
|
+
sphere_segments?: number
|
|
107
|
+
gizmo?: boolean | ComponentProps<typeof extras.Gizmo>
|
|
108
|
+
hovered_point?: InternalPoint3D<Metadata> | null
|
|
109
|
+
on_point_click?: (data: Scatter3DHandlerEvent<Metadata>) => void
|
|
110
|
+
on_point_hover?: (data: Scatter3DHandlerEvent<Metadata> | null) => void
|
|
111
|
+
tooltip?: Snippet<[Scatter3DHandlerEvent<Metadata>]>
|
|
112
|
+
scene?: Scene
|
|
113
|
+
camera?: Camera
|
|
114
|
+
orbit_controls?: ComponentProps<typeof extras.OrbitControls>[`ref`]
|
|
115
|
+
width?: number
|
|
116
|
+
height?: number
|
|
117
|
+
} = $props()
|
|
118
|
+
|
|
119
|
+
const threlte = useThrelte()
|
|
120
|
+
$effect(() => {
|
|
121
|
+
scene = threlte.scene
|
|
122
|
+
camera = threlte.camera.current
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
extras.interactivity()
|
|
126
|
+
|
|
127
|
+
// Scene dimensions: x/y are horizontal (2:2), z is vertical (1)
|
|
128
|
+
// Note: In Three.js, Y is vertical. We map user's Z → Three.js Y (vertical)
|
|
129
|
+
// and user's Y → Three.js Z (depth). So scene_z here refers to Three.js Y.
|
|
130
|
+
const scene_x = 10 // user X → Three.js X (horizontal)
|
|
131
|
+
const scene_y = 10 // user Y → Three.js Z (depth/horizontal)
|
|
132
|
+
const scene_z = 5 // user Z → Three.js Y (vertical)
|
|
133
|
+
const half_x = scene_x / 2
|
|
134
|
+
const half_y = scene_y / 2
|
|
135
|
+
const half_z = scene_z / 2
|
|
136
|
+
|
|
137
|
+
// Dynamic backside positions - axes/grids/planes always face away from camera
|
|
138
|
+
// pos.x/y/z are the Three.js positions where axes attach (backside of cube)
|
|
139
|
+
let pos = $state({ x: -half_x, y: -half_z, z: -half_y })
|
|
140
|
+
|
|
141
|
+
// Update backside positions when camera crosses axis planes
|
|
142
|
+
useTask(() => {
|
|
143
|
+
if (!camera) return
|
|
144
|
+
const cam = camera.position
|
|
42
145
|
// Only update when sign changes to avoid triggering geometry recreation every frame
|
|
43
|
-
const new_x = cam.x > 0 ? -half_x : half_x
|
|
44
|
-
const new_y = cam.y > 0 ? -half_z : half_z
|
|
45
|
-
const new_z = cam.z > 0 ? -half_y : half_y
|
|
46
|
-
if (pos.x !== new_x)
|
|
47
|
-
|
|
48
|
-
if (pos.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
146
|
+
const new_x = cam.x > 0 ? -half_x : half_x
|
|
147
|
+
const new_y = cam.y > 0 ? -half_z : half_z
|
|
148
|
+
const new_z = cam.z > 0 ? -half_y : half_y
|
|
149
|
+
if (pos.x !== new_x) pos.x = new_x
|
|
150
|
+
if (pos.y !== new_y) pos.y = new_y
|
|
151
|
+
if (pos.z !== new_z) pos.z = new_z
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
// Sign helpers for tick/label offsets (point outward from cube center)
|
|
155
|
+
const sign_x = $derived(pos.x < 0 ? -1 : 1)
|
|
156
|
+
const sign_y = $derived(pos.y < 0 ? -1 : 1)
|
|
157
|
+
|
|
158
|
+
// Flatten all points from series
|
|
159
|
+
let all_points = $derived(
|
|
160
|
+
series
|
|
161
|
+
.filter(Boolean)
|
|
162
|
+
.flatMap((srs, series_idx) =>
|
|
163
|
+
srs.x.map((x_val, point_idx) => ({
|
|
164
|
+
x: x_val,
|
|
165
|
+
y: srs.y[point_idx],
|
|
166
|
+
z: srs.z[point_idx],
|
|
167
|
+
series_idx,
|
|
168
|
+
point_idx,
|
|
169
|
+
color_value: srs.color_values?.[point_idx] ?? null,
|
|
170
|
+
size_value: srs.size_values?.[point_idx] ?? null,
|
|
171
|
+
metadata: Array.isArray(srs.metadata)
|
|
172
|
+
? srs.metadata[point_idx]
|
|
173
|
+
: srs.metadata,
|
|
174
|
+
point_style: Array.isArray(srs.point_style)
|
|
175
|
+
? srs.point_style[point_idx]
|
|
176
|
+
: srs.point_style,
|
|
177
|
+
}))
|
|
178
|
+
),
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
// Sample surface points for range calculation (10x10 grid)
|
|
182
|
+
function sample_surface(
|
|
183
|
+
surface: Surface3DConfig,
|
|
184
|
+
): { x: number; y: number; z: number }[] {
|
|
185
|
+
const n = 10
|
|
186
|
+
const pts: { x: number; y: number; z: number }[] = []
|
|
78
187
|
if (surface.type === `grid` && surface.z_fn) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
188
|
+
const [x0, x1] = surface.x_range ?? [-1, 1]
|
|
189
|
+
const [y0, y1] = surface.y_range ?? [-1, 1]
|
|
190
|
+
for (let i = 0; i <= n; i++) {
|
|
191
|
+
for (let j = 0; j <= n; j++) {
|
|
192
|
+
const x = x0 + (i / n) * (x1 - x0), y = y0 + (j / n) * (y1 - y0)
|
|
193
|
+
pts.push({ x, y, z: surface.z_fn(x, y) })
|
|
86
194
|
}
|
|
87
|
-
|
|
88
|
-
else if (surface.type === `parametric` && surface.parametric_fn) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
195
|
+
}
|
|
196
|
+
} else if (surface.type === `parametric` && surface.parametric_fn) {
|
|
197
|
+
const [u0, u1] = surface.u_range ?? [0, 1]
|
|
198
|
+
const [v0, v1] = surface.v_range ?? [0, 1]
|
|
199
|
+
for (let i = 0; i <= n; i++) {
|
|
200
|
+
for (let j = 0; j <= n; j++) {
|
|
201
|
+
pts.push(
|
|
202
|
+
surface.parametric_fn(u0 + (i / n) * (u1 - u0), v0 + (j / n) * (v1 - v0)),
|
|
203
|
+
)
|
|
95
204
|
}
|
|
205
|
+
}
|
|
206
|
+
} else if (surface.type === `triangulated` && surface.points) {
|
|
207
|
+
pts.push(...surface.points)
|
|
96
208
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const valid = values.filter(isFinite)
|
|
107
|
-
if (!valid.length)
|
|
108
|
-
|
|
109
|
-
let [min, max] = [Math.min(...valid), Math.max(...valid)];
|
|
209
|
+
return pts.filter((pt) => isFinite(pt.x) && isFinite(pt.y) && isFinite(pt.z))
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Compute axis range with D3's nice() for clean boundaries
|
|
213
|
+
function compute_range(
|
|
214
|
+
values: number[],
|
|
215
|
+
range?: [number | null, number | null],
|
|
216
|
+
): Vec2 {
|
|
217
|
+
if (range?.[0] != null && range?.[1] != null) return range as Vec2
|
|
218
|
+
const valid = values.filter(isFinite)
|
|
219
|
+
if (!valid.length) return [0, 1]
|
|
220
|
+
let [min, max] = [Math.min(...valid), Math.max(...valid)]
|
|
110
221
|
const pad = min === max
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (range?.[0] == null)
|
|
114
|
-
|
|
115
|
-
if (range?.[1] == null)
|
|
116
|
-
max += pad;
|
|
222
|
+
? (min === 0 ? 1 : Math.abs(min * 0.1))
|
|
223
|
+
: (max - min) * 0.05
|
|
224
|
+
if (range?.[0] == null) min -= pad
|
|
225
|
+
if (range?.[1] == null) max += pad
|
|
117
226
|
return scaleLinear().domain([range?.[0] ?? min, range?.[1] ?? max]).nice()
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
let
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
],
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
227
|
+
.domain() as Vec2
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Collect xyz values from points and surfaces
|
|
231
|
+
let surface_samples = $derived(surfaces.flatMap(sample_surface))
|
|
232
|
+
let x_range = $derived(
|
|
233
|
+
compute_range([
|
|
234
|
+
...all_points.map((point) => point.x),
|
|
235
|
+
...surface_samples.map((point) => point.x),
|
|
236
|
+
], x_axis.range),
|
|
237
|
+
)
|
|
238
|
+
let y_range = $derived(
|
|
239
|
+
compute_range([
|
|
240
|
+
...all_points.map((point) => point.y),
|
|
241
|
+
...surface_samples.map((point) => point.y),
|
|
242
|
+
], y_axis.range),
|
|
243
|
+
)
|
|
244
|
+
let z_range = $derived(
|
|
245
|
+
compute_range([
|
|
246
|
+
...all_points.map((point) => point.z),
|
|
247
|
+
...surface_samples.map((point) => point.z),
|
|
248
|
+
], z_axis.range),
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
const normalize_x = (value: number) => normalize_to_scene(value, x_range, scene_x)
|
|
252
|
+
const normalize_y = (value: number) => normalize_to_scene(value, y_range, scene_y)
|
|
253
|
+
const normalize_z = (value: number) => normalize_to_scene(value, z_range, scene_z)
|
|
254
|
+
|
|
255
|
+
// Color/size scales
|
|
256
|
+
let all_color_values = $derived(
|
|
257
|
+
all_points.map((pt) => pt.color_value).filter((val): val is number => val != null),
|
|
258
|
+
)
|
|
259
|
+
let auto_color_range: [number, number] = $derived.by(() => {
|
|
260
|
+
if (!all_color_values.length) return [0, 1]
|
|
261
|
+
let min = all_color_values[0]
|
|
262
|
+
let max = all_color_values[0]
|
|
148
263
|
for (const val of all_color_values) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
else if (val > max)
|
|
152
|
-
max = val;
|
|
264
|
+
if (val < min) min = val
|
|
265
|
+
else if (val > max) max = val
|
|
153
266
|
}
|
|
154
|
-
return [min, max]
|
|
155
|
-
})
|
|
156
|
-
let all_size_values = $derived(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
267
|
+
return [min, max]
|
|
268
|
+
})
|
|
269
|
+
let all_size_values = $derived(
|
|
270
|
+
all_points.map((pt) => pt.size_value).filter((val): val is number => val != null),
|
|
271
|
+
)
|
|
272
|
+
let color_scale_fn = $derived(create_color_scale(color_scale, auto_color_range))
|
|
273
|
+
let size_scale_fn = $derived(create_size_scale(size_scale, all_size_values))
|
|
274
|
+
|
|
275
|
+
// Process points with normalized positions
|
|
276
|
+
// Swap Y/Z for Three.js: user Z → Three.js Y (vertical), user Y → Three.js Z (depth)
|
|
277
|
+
let processed_points = $derived(
|
|
278
|
+
all_points.map((pt): InternalPoint3D<Metadata> => ({
|
|
279
|
+
...pt,
|
|
280
|
+
x: normalize_x(pt.x), // user X → Three.js X
|
|
281
|
+
y: normalize_z(pt.z), // user Z → Three.js Y (vertical)
|
|
282
|
+
z: normalize_y(pt.y), // user Y → Three.js Z (depth)
|
|
283
|
+
})),
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
// Group points by radius, with per-instance colors
|
|
287
|
+
type RadiusGroup = {
|
|
288
|
+
radius: number
|
|
289
|
+
points: InternalPoint3D<Metadata>[]
|
|
290
|
+
colors: string[]
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
let radius_groups = $derived.by((): RadiusGroup[] => {
|
|
294
|
+
const groups: Record<string, RadiusGroup> = {}
|
|
169
295
|
for (const pt of processed_points) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
groups[key].colors.push(color);
|
|
296
|
+
const srs = series[pt.series_idx]
|
|
297
|
+
if (!(series_visibility[pt.series_idx] ?? srs?.visible ?? true)) continue
|
|
298
|
+
const color = pt.color_value != null
|
|
299
|
+
? color_scale_fn(pt.color_value)
|
|
300
|
+
: pt.point_style?.fill ?? get_series_color(pt.series_idx)
|
|
301
|
+
const radius = pt.size_value != null
|
|
302
|
+
? size_scale_fn(pt.size_value)
|
|
303
|
+
: (pt.point_style?.radius ?? styles.point?.size ?? 2) * 0.05
|
|
304
|
+
const key = radius.toFixed(4)
|
|
305
|
+
;(groups[key] ??= { radius, points: [], colors: [] }).points.push(pt)
|
|
306
|
+
groups[key].colors.push(color)
|
|
182
307
|
}
|
|
183
|
-
return Object.values(groups)
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
let
|
|
188
|
-
let
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
308
|
+
return Object.values(groups)
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
// Projection settings - render point shadows on background planes
|
|
312
|
+
let proj_opacity = $derived(display.projection_opacity ?? 0.3)
|
|
313
|
+
let proj_scale = $derived(display.projection_scale ?? 0.5)
|
|
314
|
+
|
|
315
|
+
// Projection plane configs: each fixes one axis to the backside position
|
|
316
|
+
type ProjectionConfig = {
|
|
317
|
+
key: `xy` | `xz` | `yz`
|
|
318
|
+
get_pos: (pt: InternalPoint3D<Metadata>) => Vec3
|
|
319
|
+
}
|
|
320
|
+
let projection_configs = $derived(
|
|
321
|
+
([`xy`, `xz`, `yz`] as const)
|
|
322
|
+
.filter((key) => display.projections?.[key])
|
|
323
|
+
.map((key): ProjectionConfig => ({
|
|
324
|
+
key,
|
|
325
|
+
get_pos: key === `xy`
|
|
326
|
+
? (pt) => [pt.x, pos.y, pt.z]
|
|
327
|
+
: key === `xz`
|
|
328
|
+
? (pt) => [pt.x, pt.y, pos.z]
|
|
329
|
+
: (pt) => [pos.x, pt.y, pt.z],
|
|
330
|
+
})),
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
// Series line data for connecting points
|
|
334
|
+
type SeriesLineData = {
|
|
335
|
+
series_idx: number
|
|
336
|
+
color: string
|
|
337
|
+
width: number
|
|
338
|
+
dashed: boolean
|
|
339
|
+
line2: Line2
|
|
340
|
+
geometry: LineGeometry
|
|
341
|
+
material: LineMaterial
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Track previous lines for cleanup
|
|
345
|
+
let series_lines: SeriesLineData[] = $state([])
|
|
346
|
+
|
|
347
|
+
$effect(() => {
|
|
201
348
|
// Dispose old lines before creating new ones
|
|
202
349
|
for (const line_data of untrack(() => series_lines)) {
|
|
203
|
-
|
|
204
|
-
|
|
350
|
+
line_data.geometry.dispose()
|
|
351
|
+
line_data.material.dispose()
|
|
205
352
|
}
|
|
206
|
-
|
|
353
|
+
|
|
354
|
+
const lines: SeriesLineData[] = []
|
|
207
355
|
for (let series_idx = 0; series_idx < series.length; series_idx++) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
356
|
+
const srs = series[series_idx]
|
|
357
|
+
if (!srs?.line_style) continue
|
|
358
|
+
if (!(series_visibility[series_idx] ?? srs.visible ?? true)) continue
|
|
359
|
+
|
|
360
|
+
// Get points for this series in order
|
|
361
|
+
const series_points = processed_points
|
|
362
|
+
.filter((pt) => pt.series_idx === series_idx)
|
|
363
|
+
.sort((a, b) => a.point_idx - b.point_idx)
|
|
364
|
+
|
|
365
|
+
if (series_points.length < 2) continue
|
|
366
|
+
|
|
367
|
+
// Create fat line geometry (LineGeometry for Line2)
|
|
368
|
+
const positions: number[] = []
|
|
369
|
+
for (const pt of series_points) {
|
|
370
|
+
positions.push(pt.x, pt.y, pt.z)
|
|
371
|
+
}
|
|
372
|
+
const geometry = new LineGeometry()
|
|
373
|
+
geometry.setPositions(positions)
|
|
374
|
+
|
|
375
|
+
// Determine line style
|
|
376
|
+
const line_style = srs.line_style
|
|
377
|
+
const color = line_style.stroke ??
|
|
378
|
+
(Array.isArray(srs.point_style)
|
|
379
|
+
? srs.point_style[0]?.fill
|
|
380
|
+
: srs.point_style?.fill) ??
|
|
381
|
+
get_series_color(series_idx)
|
|
382
|
+
const line_width = line_style.stroke_width ?? 2
|
|
383
|
+
const dashed = Boolean(line_style.line_dash)
|
|
384
|
+
|
|
385
|
+
// Create LineMaterial for fat lines (linewidth is in pixels when resolution is set)
|
|
386
|
+
// Use placeholder resolution; the separate resolution effect updates it
|
|
387
|
+
const material = new LineMaterial({
|
|
388
|
+
color: new THREE.Color(color).getHex(),
|
|
389
|
+
linewidth: line_width, // Width in pixels
|
|
390
|
+
dashed,
|
|
391
|
+
dashScale: dashed ? 2 : 1,
|
|
392
|
+
dashSize: 0.1,
|
|
393
|
+
gapSize: 0.05,
|
|
394
|
+
resolution: new THREE.Vector2(1, 1),
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
const line2 = new Line2(geometry, material)
|
|
398
|
+
line2.computeLineDistances()
|
|
399
|
+
|
|
400
|
+
lines.push({
|
|
401
|
+
series_idx,
|
|
402
|
+
color,
|
|
403
|
+
width: line_width,
|
|
404
|
+
dashed,
|
|
405
|
+
line2,
|
|
406
|
+
geometry,
|
|
407
|
+
material,
|
|
408
|
+
})
|
|
257
409
|
}
|
|
258
|
-
series_lines = lines
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const
|
|
410
|
+
series_lines = lines
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
// Update LineMaterial resolution when canvas size changes
|
|
414
|
+
$effect(() => {
|
|
415
|
+
const canvas_width = width || 1
|
|
416
|
+
const canvas_height = height || 1
|
|
264
417
|
for (const line_data of series_lines) {
|
|
265
|
-
|
|
418
|
+
line_data.material.resolution.set(canvas_width, canvas_height)
|
|
266
419
|
}
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
// Cleanup on component destroy
|
|
423
|
+
onDestroy(() => {
|
|
270
424
|
for (const { geometry, material } of series_lines) {
|
|
271
|
-
|
|
272
|
-
|
|
425
|
+
geometry.dispose()
|
|
426
|
+
material.dispose()
|
|
273
427
|
}
|
|
274
|
-
Object.values(axis_geometries).forEach((
|
|
428
|
+
Object.values(axis_geometries).forEach((geom) => geom.dispose())
|
|
275
429
|
for (const data of Object.values(axis_geom_data)) {
|
|
276
|
-
|
|
277
|
-
|
|
430
|
+
data.tick_geoms.forEach((geom) => geom.dispose())
|
|
431
|
+
data.grid_geoms.flat().forEach((geom) => geom.dispose())
|
|
278
432
|
}
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
// Generate axis ticks using D3's smart tick generation
|
|
436
|
+
function gen_ticks(
|
|
437
|
+
range: [number, number],
|
|
438
|
+
ticks?: AxisConfig3D[`ticks`],
|
|
439
|
+
): number[] {
|
|
440
|
+
if (Array.isArray(ticks)) return ticks
|
|
441
|
+
const [min, max] = range
|
|
442
|
+
if (!isFinite(min) || !isFinite(max) || min === max) return [min]
|
|
443
|
+
const count = typeof ticks === `number` ? ticks : 5
|
|
444
|
+
return scaleLinear().domain([min, max]).ticks(count)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
let x_ticks = $derived(gen_ticks(x_range, x_axis.ticks))
|
|
448
|
+
let y_ticks = $derived(gen_ticks(y_range, y_axis.ticks))
|
|
449
|
+
let z_ticks = $derived(gen_ticks(z_range, z_axis.ticks))
|
|
450
|
+
|
|
451
|
+
// Create axis line geometry - reuses a shared Float32Array for efficiency
|
|
452
|
+
function create_line_geometry(start: Vec3, end: Vec3): THREE.BufferGeometry {
|
|
453
|
+
const geometry = new THREE.BufferGeometry()
|
|
454
|
+
const positions = new Float32Array([...start, ...end])
|
|
455
|
+
geometry.setAttribute(`position`, new THREE.BufferAttribute(positions, 3))
|
|
456
|
+
return geometry
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Build event data for point interactions
|
|
460
|
+
function make_event_data(
|
|
461
|
+
point: InternalPoint3D<Metadata>,
|
|
462
|
+
event?: MouseEvent,
|
|
463
|
+
): Scatter3DHandlerEvent<Metadata> | null {
|
|
464
|
+
const orig = all_points.find(
|
|
465
|
+
(pt) => pt.series_idx === point.series_idx && pt.point_idx === point.point_idx,
|
|
466
|
+
)
|
|
467
|
+
if (!orig) return null
|
|
305
468
|
return {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
469
|
+
x: orig.x,
|
|
470
|
+
y: orig.y,
|
|
471
|
+
z: orig.z,
|
|
472
|
+
metadata: point.metadata ?? null,
|
|
473
|
+
label: series[point.series_idx]?.label ?? null,
|
|
474
|
+
series_idx: point.series_idx,
|
|
475
|
+
x_axis,
|
|
476
|
+
y_axis,
|
|
477
|
+
z_axis,
|
|
478
|
+
x_formatted: format_num(orig.x, x_axis.format || `.3~g`),
|
|
479
|
+
y_formatted: format_num(orig.y, y_axis.format || `.3~g`),
|
|
480
|
+
z_formatted: format_num(orig.z, z_axis.format || `.3~g`),
|
|
481
|
+
color_value: point.color_value,
|
|
482
|
+
fullscreen: false,
|
|
483
|
+
event,
|
|
484
|
+
point,
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function handle_point_enter(point: InternalPoint3D<Metadata>) {
|
|
489
|
+
hovered_point = point
|
|
490
|
+
const data = make_event_data(point)
|
|
491
|
+
if (data) on_point_hover?.(data)
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function handle_point_click(point: InternalPoint3D<Metadata>, event: MouseEvent) {
|
|
495
|
+
const data = make_event_data(point, event)
|
|
496
|
+
if (data) on_point_click?.(data)
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Gizmo props - parent (ScatterPlot3D) handles className and ColorBar offset adjustments
|
|
500
|
+
let gizmo_props = $derived(
|
|
501
|
+
gizmo === false
|
|
502
|
+
? null
|
|
503
|
+
: gizmo === true
|
|
504
|
+
? { background: { enabled: false }, offset: { left: 5, bottom: 5 } }
|
|
505
|
+
: gizmo,
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
// Orbit controls - snappy with minimal inertia
|
|
509
|
+
let orbit_controls_props = $derived({
|
|
343
510
|
enableRotate: rotate_speed > 0,
|
|
344
511
|
rotateSpeed: rotate_speed,
|
|
345
512
|
enableZoom: zoom_speed > 0,
|
|
346
513
|
zoomSpeed: zoom_speed,
|
|
347
514
|
enablePan: pan_speed > 0,
|
|
348
515
|
panSpeed: pan_speed,
|
|
349
|
-
target: [0, 0, 0],
|
|
516
|
+
target: [0, 0, 0] as Vec3,
|
|
350
517
|
maxZoom: max_zoom,
|
|
351
518
|
minZoom: min_zoom,
|
|
352
519
|
autoRotate: Boolean(auto_rotate),
|
|
353
520
|
autoRotateSpeed: auto_rotate,
|
|
354
521
|
enableDamping: rotation_damping > 0,
|
|
355
522
|
dampingFactor: rotation_damping,
|
|
356
|
-
})
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
523
|
+
})
|
|
524
|
+
|
|
525
|
+
// Axis configuration for rendering
|
|
526
|
+
const tick_length = 0.15
|
|
527
|
+
type AxisKey = `x` | `y` | `z`
|
|
528
|
+
const AXIS_KEYS: readonly AxisKey[] = [`x`, `y`, `z`]
|
|
529
|
+
|
|
530
|
+
// Main axis line geometries - updated when backside positions change
|
|
531
|
+
let axis_geometries: Record<AxisKey, THREE.BufferGeometry> = $state({
|
|
362
532
|
x: create_line_geometry([-half_x, -half_z, -half_y], [half_x, -half_z, -half_y]),
|
|
363
533
|
y: create_line_geometry([-half_x, -half_z, -half_y], [-half_x, -half_z, half_y]),
|
|
364
534
|
z: create_line_geometry([-half_x, -half_z, -half_y], [-half_x, half_z, -half_y]),
|
|
365
|
-
})
|
|
366
|
-
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
$effect(() => {
|
|
367
538
|
// Capture pos values for dependency tracking
|
|
368
|
-
const { x: px, y: py, z: pz } = pos
|
|
539
|
+
const { x: px, y: py, z: pz } = pos
|
|
369
540
|
untrack(() => {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
});
|
|
541
|
+
for (const key of AXIS_KEYS) axis_geometries[key].dispose()
|
|
542
|
+
})
|
|
373
543
|
// X-axis: spans full X, positioned at backside Y and Z
|
|
374
|
-
axis_geometries.x = create_line_geometry([-half_x, py, pz], [half_x, py, pz])
|
|
544
|
+
axis_geometries.x = create_line_geometry([-half_x, py, pz], [half_x, py, pz])
|
|
375
545
|
// Y-axis (user Y → Three.js Z): spans full Z, positioned at backside X and Y
|
|
376
|
-
axis_geometries.y = create_line_geometry([px, py, -half_y], [px, py, half_y])
|
|
546
|
+
axis_geometries.y = create_line_geometry([px, py, -half_y], [px, py, half_y])
|
|
377
547
|
// Z-axis (user Z → Three.js Y): spans full Y, positioned at backside X and Z
|
|
378
|
-
axis_geometries.z = create_line_geometry([px, -half_z, pz], [px, half_z, pz])
|
|
379
|
-
})
|
|
380
|
-
|
|
381
|
-
|
|
548
|
+
axis_geometries.z = create_line_geometry([px, -half_z, pz], [px, half_z, pz])
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
// Axis rendering config - all positions use backside `pos` values
|
|
552
|
+
let axes_config = $derived([
|
|
382
553
|
{
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
554
|
+
key: `x` as AxisKey,
|
|
555
|
+
color: `#ef4444`,
|
|
556
|
+
axis: x_axis,
|
|
557
|
+
ticks: x_ticks,
|
|
558
|
+
range: x_range,
|
|
559
|
+
get_tick_pos: (val: number): Vec3 => [normalize_x(val), pos.y, pos.z],
|
|
560
|
+
get_tick_end: (
|
|
561
|
+
val: number,
|
|
562
|
+
): Vec3 => [normalize_x(val), pos.y + sign_y * tick_length, pos.z],
|
|
563
|
+
get_grid_lines: (val: number): [Vec3, Vec3][] => {
|
|
564
|
+
const px = normalize_x(val)
|
|
565
|
+
return [
|
|
566
|
+
[[px, -half_z, pos.z], [px, half_z, pos.z]],
|
|
567
|
+
[[px, pos.y, -half_y], [px, pos.y, half_y]],
|
|
568
|
+
]
|
|
569
|
+
},
|
|
570
|
+
tick_label_pos: (
|
|
571
|
+
val: number,
|
|
572
|
+
): Vec3 => [normalize_x(val), pos.y + sign_y * 0.4, pos.z],
|
|
573
|
+
axis_label_pos: [0, pos.y + sign_y * 0.9, pos.z] as Vec3,
|
|
399
574
|
},
|
|
400
575
|
{
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
576
|
+
key: `y` as AxisKey,
|
|
577
|
+
color: `#22c55e`,
|
|
578
|
+
axis: y_axis,
|
|
579
|
+
ticks: y_ticks,
|
|
580
|
+
range: y_range,
|
|
581
|
+
get_tick_pos: (val: number): Vec3 => [pos.x, pos.y, normalize_y(val)],
|
|
582
|
+
get_tick_end: (
|
|
583
|
+
val: number,
|
|
584
|
+
): Vec3 => [pos.x, pos.y + sign_y * tick_length, normalize_y(val)],
|
|
585
|
+
get_grid_lines: (val: number): [Vec3, Vec3][] => {
|
|
586
|
+
const py = normalize_y(val)
|
|
587
|
+
return [
|
|
588
|
+
[[-half_x, pos.y, py], [half_x, pos.y, py]],
|
|
589
|
+
[[pos.x, -half_z, py], [pos.x, half_z, py]],
|
|
590
|
+
]
|
|
591
|
+
},
|
|
592
|
+
tick_label_pos: (
|
|
593
|
+
val: number,
|
|
594
|
+
): Vec3 => [pos.x + sign_x * 0.5, pos.y + sign_y * 0.4, normalize_y(val)],
|
|
595
|
+
axis_label_pos: [
|
|
596
|
+
pos.x,
|
|
597
|
+
pos.y + sign_y * 0.9,
|
|
598
|
+
pos.z < 0 ? half_y + 0.5 : -half_y - 0.5,
|
|
599
|
+
] as Vec3,
|
|
421
600
|
},
|
|
422
601
|
{
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
602
|
+
key: `z` as AxisKey,
|
|
603
|
+
color: `#3b82f6`,
|
|
604
|
+
axis: z_axis,
|
|
605
|
+
ticks: z_ticks,
|
|
606
|
+
range: z_range,
|
|
607
|
+
get_tick_pos: (val: number): Vec3 => [pos.x, normalize_z(val), pos.z],
|
|
608
|
+
get_tick_end: (
|
|
609
|
+
val: number,
|
|
610
|
+
): Vec3 => [pos.x + sign_x * tick_length, normalize_z(val), pos.z],
|
|
611
|
+
get_grid_lines: (val: number): [Vec3, Vec3][] => {
|
|
612
|
+
const pz = normalize_z(val)
|
|
613
|
+
return [
|
|
614
|
+
[[-half_x, pz, pos.z], [half_x, pz, pos.z]],
|
|
615
|
+
[[pos.x, pz, -half_y], [pos.x, pz, half_y]],
|
|
616
|
+
]
|
|
617
|
+
},
|
|
618
|
+
tick_label_pos: (
|
|
619
|
+
val: number,
|
|
620
|
+
): Vec3 => [pos.x + sign_x * 0.5, normalize_z(val), pos.z],
|
|
621
|
+
axis_label_pos: [pos.x + sign_x, 0, pos.z] as Vec3,
|
|
439
622
|
},
|
|
440
|
-
])
|
|
441
|
-
|
|
442
|
-
|
|
623
|
+
])
|
|
624
|
+
|
|
625
|
+
// Pre-computed geometries for tick marks and grid lines, indexed by axis and tick position
|
|
626
|
+
type AxisGeomData = {
|
|
627
|
+
tick_geoms: THREE.BufferGeometry[]
|
|
628
|
+
grid_geoms: THREE.BufferGeometry[][]
|
|
629
|
+
}
|
|
630
|
+
const empty_geom_data = (): AxisGeomData => ({ tick_geoms: [], grid_geoms: [] })
|
|
631
|
+
let axis_geom_data: Record<AxisKey, AxisGeomData> = $state({
|
|
443
632
|
x: empty_geom_data(),
|
|
444
633
|
y: empty_geom_data(),
|
|
445
634
|
z: empty_geom_data(),
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
635
|
+
})
|
|
636
|
+
|
|
637
|
+
// Recreate tick/grid geometries when axes config changes
|
|
638
|
+
$effect(() => {
|
|
639
|
+
const config = axes_config
|
|
450
640
|
// Dispose old geometries (untracked to avoid dependency cycle)
|
|
451
641
|
untrack(() => {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
})
|
|
642
|
+
for (const key of AXIS_KEYS) {
|
|
643
|
+
axis_geom_data[key].tick_geoms.forEach((geom) => geom.dispose())
|
|
644
|
+
axis_geom_data[key].grid_geoms.flat().forEach((geom) => geom.dispose())
|
|
645
|
+
}
|
|
646
|
+
})
|
|
457
647
|
for (const { key, ticks, get_tick_pos, get_tick_end, get_grid_lines } of config) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
648
|
+
axis_geom_data[key] = {
|
|
649
|
+
tick_geoms: ticks.map((val) =>
|
|
650
|
+
create_line_geometry(get_tick_pos(val), get_tick_end(val))
|
|
651
|
+
),
|
|
652
|
+
grid_geoms: ticks.map((val) =>
|
|
653
|
+
get_grid_lines(val).map(([start, end]) => create_line_geometry(start, end))
|
|
654
|
+
),
|
|
655
|
+
}
|
|
462
656
|
}
|
|
463
|
-
})
|
|
657
|
+
})
|
|
464
658
|
</script>
|
|
465
659
|
|
|
466
660
|
{#if camera_projection === `perspective`}
|
|
@@ -599,7 +793,11 @@ $effect(() => {
|
|
|
599
793
|
|
|
600
794
|
<!-- Instanced scatter points with per-instance colors and event handling -->
|
|
601
795
|
{#each radius_groups as group (group.radius)}
|
|
602
|
-
<extras.InstancedMesh
|
|
796
|
+
<extras.InstancedMesh
|
|
797
|
+
limit={group.points.length}
|
|
798
|
+
range={group.points.length}
|
|
799
|
+
frustumCulled={false}
|
|
800
|
+
>
|
|
603
801
|
<T.SphereGeometry args={[1, sphere_segments, sphere_segments]} />
|
|
604
802
|
<T.MeshStandardMaterial vertexColors={false} />
|
|
605
803
|
{#each group.points as point, idx (`${point.series_idx}-${point.point_idx}`)}
|
|
@@ -621,7 +819,11 @@ $effect(() => {
|
|
|
621
819
|
<!-- Plane Projections - render point shadows on enabled background planes -->
|
|
622
820
|
{#each projection_configs as { key, get_pos } (key)}
|
|
623
821
|
{#each radius_groups as group (group.radius)}
|
|
624
|
-
<extras.InstancedMesh
|
|
822
|
+
<extras.InstancedMesh
|
|
823
|
+
limit={group.points.length}
|
|
824
|
+
range={group.points.length}
|
|
825
|
+
frustumCulled={false}
|
|
826
|
+
>
|
|
625
827
|
<T.SphereGeometry args={[1, 8, 8]} />
|
|
626
828
|
<T.MeshBasicMaterial transparent opacity={proj_opacity} depthWrite={false} />
|
|
627
829
|
{#each group.points as point, idx (`${key}-${point.series_idx}-${point.point_idx}`)}
|