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,440 +1,598 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { D3InterpolateName } from '../colors'
|
|
3
|
+
import type { CompositionType } from '../composition'
|
|
4
|
+
import { normalize_show_controls } from '../controls'
|
|
5
|
+
import { sanitize_html } from '../sanitize'
|
|
6
|
+
import type { ElementSymbol } from '../element'
|
|
7
|
+
import { ClickFeedback, DragOverlay } from '../feedback'
|
|
8
|
+
import Icon from '../Icon.svelte'
|
|
9
|
+
import type { D3SymbolName } from '../labels'
|
|
10
|
+
import { symbol_map } from '../labels'
|
|
11
|
+
import { set_fullscreen_bg, setup_fullscreen_effect } from '../layout'
|
|
12
|
+
import type {
|
|
13
|
+
AxisConfig,
|
|
14
|
+
ScatterHandlerEvent,
|
|
15
|
+
ScatterHandlerProps,
|
|
16
|
+
UserContentProps,
|
|
17
|
+
} from '../plot'
|
|
18
|
+
import { ScatterPlot } from '../plot'
|
|
19
|
+
import { DEFAULTS } from '../settings'
|
|
20
|
+
import type { AnyStructure } from '../structure'
|
|
21
|
+
import { SvelteMap } from 'svelte/reactivity'
|
|
22
|
+
import ConvexHullControls from './ConvexHullControls.svelte'
|
|
23
|
+
import ConvexHullInfoPane from './ConvexHullInfoPane.svelte'
|
|
24
|
+
import ConvexHullTooltip from './ConvexHullTooltip.svelte'
|
|
25
|
+
import GasPressureControls from './GasPressureControls.svelte'
|
|
26
|
+
import * as helpers from './helpers'
|
|
27
|
+
import type { BaseConvexHullProps } from './index'
|
|
28
|
+
import { CONVEX_HULL_STYLE, default_controls, default_hull_config } from './index'
|
|
29
|
+
import StructurePopup from './StructurePopup.svelte'
|
|
30
|
+
import TemperatureSlider from './TemperatureSlider.svelte'
|
|
31
|
+
import * as thermo from './thermodynamics'
|
|
32
|
+
import type {
|
|
33
|
+
ConvexHullEntry,
|
|
34
|
+
HighlightStyle,
|
|
35
|
+
HoverData3D,
|
|
36
|
+
PhaseData,
|
|
37
|
+
} from './types'
|
|
38
|
+
import { compute_hull_stability, is_unary_entry } from './helpers'
|
|
39
|
+
|
|
40
|
+
// Binary convex hull rendered as energy vs composition (x in [0, 1])
|
|
41
|
+
let {
|
|
42
|
+
entries = [],
|
|
43
|
+
controls = {},
|
|
44
|
+
config = {},
|
|
45
|
+
on_point_click,
|
|
46
|
+
on_point_hover,
|
|
47
|
+
fullscreen = $bindable(DEFAULTS.convex_hull.binary.fullscreen),
|
|
48
|
+
enable_info_pane = true,
|
|
49
|
+
wrapper = $bindable(),
|
|
50
|
+
label_threshold = 50,
|
|
51
|
+
show_stable = $bindable(DEFAULTS.convex_hull.binary.show_stable),
|
|
52
|
+
show_unstable = $bindable(DEFAULTS.convex_hull.binary.show_unstable),
|
|
53
|
+
color_mode = $bindable(DEFAULTS.convex_hull.binary.color_mode),
|
|
54
|
+
color_scale = $bindable(
|
|
55
|
+
DEFAULTS.convex_hull.binary.color_scale as D3InterpolateName,
|
|
56
|
+
),
|
|
57
|
+
info_pane_open = $bindable(DEFAULTS.convex_hull.binary.info_pane_open),
|
|
58
|
+
legend_pane_open = $bindable(DEFAULTS.convex_hull.binary.legend_pane_open),
|
|
59
|
+
max_hull_dist_show_phases = $bindable(
|
|
60
|
+
DEFAULTS.convex_hull.binary.max_hull_dist_show_phases,
|
|
61
|
+
),
|
|
62
|
+
max_hull_dist_show_labels = $bindable(
|
|
63
|
+
DEFAULTS.convex_hull.binary.max_hull_dist_show_labels,
|
|
64
|
+
),
|
|
65
|
+
show_stable_labels = $bindable(DEFAULTS.convex_hull.binary.show_stable_labels),
|
|
66
|
+
show_unstable_labels = $bindable(
|
|
67
|
+
DEFAULTS.convex_hull.binary.show_unstable_labels,
|
|
68
|
+
),
|
|
69
|
+
on_file_drop,
|
|
70
|
+
enable_click_selection = true,
|
|
71
|
+
enable_structure_preview = true,
|
|
72
|
+
energy_source_mode = $bindable(`precomputed`),
|
|
73
|
+
phase_stats = $bindable(null),
|
|
74
|
+
display = $bindable({ x_grid: false, y_grid: false }),
|
|
75
|
+
stable_entries = $bindable([]),
|
|
76
|
+
unstable_entries = $bindable([]),
|
|
77
|
+
highlighted_entries = $bindable([]),
|
|
78
|
+
highlight_style = {},
|
|
79
|
+
x_axis = {},
|
|
80
|
+
y_axis = {},
|
|
81
|
+
selected_entry = $bindable(null),
|
|
82
|
+
temperature = $bindable(),
|
|
83
|
+
interpolate_temperature = true,
|
|
84
|
+
max_interpolation_gap = 500,
|
|
85
|
+
gas_config,
|
|
86
|
+
gas_pressures = $bindable({}),
|
|
87
|
+
children,
|
|
88
|
+
tooltip: custom_tooltip,
|
|
89
|
+
...rest
|
|
90
|
+
}: BaseConvexHullProps<ConvexHullEntry> & {
|
|
91
|
+
highlight_style?: HighlightStyle
|
|
92
|
+
x_axis?: AxisConfig
|
|
93
|
+
y_axis?: AxisConfig
|
|
94
|
+
} = $props()
|
|
95
|
+
|
|
96
|
+
const merged_controls = $derived({ ...default_controls, ...controls })
|
|
97
|
+
const controls_config = $derived(normalize_show_controls(merged_controls.show))
|
|
98
|
+
const merged_config = $derived({
|
|
23
99
|
...default_hull_config,
|
|
24
100
|
point_size: 6, // Binary diagrams use slightly smaller points
|
|
25
101
|
...config,
|
|
26
102
|
colors: { ...default_hull_config.colors, ...(config.colors || {}) },
|
|
27
103
|
margin: { t: 40, r: 40, b: 60, l: 60, ...(config.margin || {}) },
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
// Merge highlight style with defaults (consistent with 3D/4D)
|
|
107
|
+
const merged_highlight_style = $derived(
|
|
108
|
+
helpers.merge_highlight_style(highlight_style),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
// Helper to check if entry is highlighted
|
|
112
|
+
const is_highlighted = (entry: ConvexHullEntry): boolean =>
|
|
113
|
+
helpers.is_entry_highlighted(entry, highlighted_entries)
|
|
114
|
+
|
|
115
|
+
// Temperature-dependent free energy support
|
|
116
|
+
const { has_temp_data, available_temperatures } = $derived(
|
|
117
|
+
helpers.analyze_temperature_data(entries),
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
// Initialize or reset temperature when it's undefined or no longer valid
|
|
121
|
+
$effect(() => {
|
|
122
|
+
if (
|
|
123
|
+
has_temp_data &&
|
|
124
|
+
available_temperatures.length > 0 &&
|
|
125
|
+
(temperature === undefined || !available_temperatures.includes(temperature))
|
|
126
|
+
) temperature = available_temperatures[0]
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// Filter entries by temperature when in temperature mode
|
|
130
|
+
const temp_filtered_entries = $derived(
|
|
131
|
+
has_temp_data && temperature !== undefined
|
|
132
|
+
? helpers.filter_entries_at_temperature(entries, temperature, {
|
|
45
133
|
interpolate: interpolate_temperature,
|
|
46
134
|
max_interpolation_gap,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
135
|
+
})
|
|
136
|
+
: entries,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
// Gas-dependent chemical potential support (corrections based on T, P)
|
|
140
|
+
const {
|
|
141
|
+
entries: gas_corrected_entries,
|
|
142
|
+
analysis: gas_analysis,
|
|
143
|
+
merged_config: merged_gas_config,
|
|
144
|
+
} = $derived(
|
|
145
|
+
helpers.get_gas_corrected_entries(
|
|
146
|
+
temp_filtered_entries,
|
|
147
|
+
gas_config,
|
|
148
|
+
gas_pressures,
|
|
149
|
+
temperature ?? helpers.DEFAULT_GAS_TEMP,
|
|
150
|
+
),
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
let { // Compute energy mode information
|
|
154
|
+
has_precomputed_e_form,
|
|
155
|
+
has_precomputed_hull,
|
|
156
|
+
can_compute_e_form,
|
|
157
|
+
can_compute_hull,
|
|
158
|
+
energy_mode,
|
|
159
|
+
unary_refs,
|
|
160
|
+
} = $derived(
|
|
161
|
+
helpers.compute_energy_mode_info(
|
|
162
|
+
gas_corrected_entries,
|
|
163
|
+
thermo.find_lowest_energy_unary_refs,
|
|
164
|
+
energy_source_mode,
|
|
165
|
+
),
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
const effective_entries = $derived(
|
|
169
|
+
helpers.get_effective_entries(
|
|
170
|
+
gas_corrected_entries,
|
|
171
|
+
energy_mode,
|
|
172
|
+
unary_refs,
|
|
173
|
+
thermo.compute_e_form_per_atom,
|
|
174
|
+
),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
// Process data and element set
|
|
178
|
+
const pd_data = $derived(thermo.process_hull_entries(effective_entries))
|
|
179
|
+
|
|
180
|
+
const polymorph_stats_map = $derived(
|
|
181
|
+
helpers.compute_all_polymorph_stats(effective_entries),
|
|
182
|
+
) // Pre-compute polymorph stats once for O(1) tooltip lookups
|
|
183
|
+
|
|
184
|
+
const elements = $derived.by(() => {
|
|
58
185
|
if (pd_data.elements.length > 2) {
|
|
59
|
-
|
|
60
|
-
|
|
186
|
+
console.error(
|
|
187
|
+
`ConvexHull2D: Dataset contains ${pd_data.elements.length} elements, but binary diagrams require exactly 2. Found: [${
|
|
188
|
+
pd_data.elements.join(`, `)
|
|
189
|
+
}]`,
|
|
190
|
+
)
|
|
191
|
+
return []
|
|
61
192
|
}
|
|
62
|
-
return pd_data.elements
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
193
|
+
return pd_data.elements
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// Coordinate computation ----------------------------------------------------
|
|
197
|
+
function compute_binary_coordinates(
|
|
198
|
+
raw_entries: PhaseData[],
|
|
199
|
+
elems: ElementSymbol[],
|
|
200
|
+
): ConvexHullEntry[] {
|
|
201
|
+
if (elems.length !== 2) return []
|
|
202
|
+
const [el1, el2] = elems
|
|
203
|
+
const coords: ConvexHullEntry[] = []
|
|
70
204
|
for (const entry of raw_entries) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const is_element = is_unary_entry(entry);
|
|
80
|
-
coords.push({ ...entry, x: frac_b, y: e_form, z: 0, is_element, visible: true });
|
|
205
|
+
// Require formation energy per atom to place along y
|
|
206
|
+
const e_form = entry.e_form_per_atom
|
|
207
|
+
if (typeof e_form !== `number`) continue
|
|
208
|
+
const total = Object.values(entry.composition).reduce((s, v) => s + v, 0)
|
|
209
|
+
if (total <= 0) continue
|
|
210
|
+
const frac_b = (entry.composition[el2] || 0) / total
|
|
211
|
+
const is_element = is_unary_entry(entry)
|
|
212
|
+
coords.push({ ...entry, x: frac_b, y: e_form, z: 0, is_element, visible: true })
|
|
81
213
|
}
|
|
82
214
|
// Ensure elemental references at x=0 and x=1 with y=0 to close the hull
|
|
83
|
-
const el_a = coords.find((e) =>
|
|
84
|
-
|
|
215
|
+
const el_a: ConvexHullEntry | undefined = coords.find((e) =>
|
|
216
|
+
e.is_element && e.x === 0
|
|
217
|
+
)
|
|
218
|
+
const el_b: ConvexHullEntry | undefined = coords.find((e) =>
|
|
219
|
+
e.is_element && e.x === 1
|
|
220
|
+
)
|
|
85
221
|
if (!el_a) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
222
|
+
coords.push({
|
|
223
|
+
composition: { [el1]: 1 } as CompositionType,
|
|
224
|
+
energy: 0,
|
|
225
|
+
x: 0,
|
|
226
|
+
y: 0,
|
|
227
|
+
z: 0,
|
|
228
|
+
is_element: true,
|
|
229
|
+
visible: true,
|
|
230
|
+
})
|
|
95
231
|
}
|
|
96
232
|
if (!el_b) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
233
|
+
coords.push({
|
|
234
|
+
composition: { [el2]: 1 } as CompositionType,
|
|
235
|
+
energy: 0,
|
|
236
|
+
x: 1,
|
|
237
|
+
y: 0,
|
|
238
|
+
z: 0,
|
|
239
|
+
is_element: true,
|
|
240
|
+
visible: true,
|
|
241
|
+
})
|
|
106
242
|
}
|
|
107
|
-
return coords
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const lower = [];
|
|
113
|
-
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
|
114
|
-
for (const p of sorted) {
|
|
115
|
-
while (lower.length >= 2 &&
|
|
116
|
-
cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0)
|
|
117
|
-
lower.pop();
|
|
118
|
-
lower.push(p);
|
|
119
|
-
}
|
|
120
|
-
return lower;
|
|
121
|
-
}
|
|
122
|
-
function interpolate_on_hull(hull, x) {
|
|
123
|
-
if (hull.length < 2)
|
|
124
|
-
return null;
|
|
125
|
-
if (x <= hull[0].x)
|
|
126
|
-
return hull[0].y;
|
|
127
|
-
if (x >= hull[hull.length - 1].x)
|
|
128
|
-
return hull[hull.length - 1].y;
|
|
129
|
-
for (let i = 0; i < hull.length - 1; i++) {
|
|
130
|
-
const p1 = hull[i];
|
|
131
|
-
const p2 = hull[i + 1];
|
|
132
|
-
if (x >= p1.x && x <= p2.x) {
|
|
133
|
-
const t = (x - p1.x) / Math.max(1e-12, p2.x - p1.x);
|
|
134
|
-
return p1.y * (1 - t) + p2.y * t;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
const coords_entries = $derived.by(() => {
|
|
140
|
-
if (elements.length !== 2)
|
|
141
|
-
return [];
|
|
243
|
+
return coords
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const coords_entries = $derived.by(() => {
|
|
247
|
+
if (elements.length !== 2) return []
|
|
142
248
|
try {
|
|
143
|
-
|
|
249
|
+
return compute_binary_coordinates(pd_data.entries, elements)
|
|
250
|
+
} catch (error) {
|
|
251
|
+
console.error(`Error computing binary coordinates:`, error)
|
|
252
|
+
return []
|
|
144
253
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
});
|
|
150
|
-
// Compute hull and enrich entries with e_above_hull (before filtering)
|
|
151
|
-
const { all_enriched_entries, hull_points } = $derived.by(() => {
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
// Compute hull and enrich entries with e_above_hull (before filtering)
|
|
257
|
+
const { all_enriched_entries, hull_points } = $derived.by(() => {
|
|
152
258
|
if (coords_entries.length === 0) {
|
|
153
|
-
|
|
259
|
+
return { all_enriched_entries: [], hull_points: [] }
|
|
154
260
|
}
|
|
155
|
-
|
|
156
|
-
//
|
|
157
|
-
|
|
261
|
+
|
|
262
|
+
// Build lower hull input: one minimum-energy point per composition x.
|
|
263
|
+
// Excluded entries don't participate in hull construction.
|
|
264
|
+
const min_y_by_x = new SvelteMap<number, number>()
|
|
158
265
|
for (const entry of coords_entries) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
266
|
+
if (entry.exclude_from_hull) continue
|
|
267
|
+
const current_min_y = min_y_by_x.get(entry.x)
|
|
268
|
+
if (current_min_y === undefined || entry.y < current_min_y) {
|
|
269
|
+
min_y_by_x.set(entry.x, entry.y)
|
|
270
|
+
}
|
|
164
271
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
272
|
+
|
|
273
|
+
const hull_input = [...min_y_by_x].map(([x_coord, min_y]) => ({
|
|
274
|
+
x: x_coord,
|
|
275
|
+
y: min_y,
|
|
276
|
+
}))
|
|
277
|
+
const hull_points = thermo.compute_lower_hull_2d(hull_input)
|
|
278
|
+
|
|
170
279
|
const all_enriched_entries = coords_entries.map((entry) => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const auto_default_threshold = $derived(helpers.compute_auto_hull_dist_threshold(
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
280
|
+
const y_hull = thermo.interpolate_hull_2d(hull_points, entry.x)
|
|
281
|
+
const raw_dist = y_hull == null ? 0 : entry.y - y_hull
|
|
282
|
+
return {
|
|
283
|
+
...entry, ...compute_hull_stability(raw_dist, entry.exclude_from_hull), visible: true,
|
|
284
|
+
}
|
|
285
|
+
})
|
|
286
|
+
return { all_enriched_entries, hull_points }
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
// Auto threshold: show all for few entries, use default for many, interpolate between
|
|
290
|
+
const max_hull_dist_in_data = $derived(
|
|
291
|
+
helpers.calc_max_hull_dist_in_data(all_enriched_entries),
|
|
292
|
+
)
|
|
293
|
+
const auto_default_threshold = $derived(helpers.compute_auto_hull_dist_threshold(
|
|
294
|
+
all_enriched_entries.length,
|
|
295
|
+
max_hull_dist_in_data,
|
|
296
|
+
DEFAULTS.convex_hull.binary.max_hull_dist_show_phases,
|
|
297
|
+
))
|
|
298
|
+
|
|
299
|
+
// Initialize threshold to auto value on first load
|
|
300
|
+
let initialized = $state(false)
|
|
301
|
+
$effect(() => {
|
|
188
302
|
if (!initialized && all_enriched_entries.length > 0) {
|
|
189
|
-
|
|
190
|
-
|
|
303
|
+
initialized = true
|
|
304
|
+
max_hull_dist_show_phases = auto_default_threshold
|
|
191
305
|
}
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
// Filter by threshold and compute visibility
|
|
309
|
+
const plot_entries = $derived(
|
|
310
|
+
all_enriched_entries
|
|
311
|
+
.filter((e) =>
|
|
312
|
+
e.is_stable || (e.e_above_hull ?? 0) <= max_hull_dist_show_phases
|
|
313
|
+
)
|
|
314
|
+
.map((e) => ({
|
|
315
|
+
...e,
|
|
316
|
+
visible: (e.is_stable && show_stable) || (!e.is_stable && show_unstable),
|
|
317
|
+
})),
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
// Update bindable entries arrays when plot_entries change (single pass)
|
|
321
|
+
$effect(() => {
|
|
322
|
+
const stable: ConvexHullEntry[] = []
|
|
323
|
+
const unstable: ConvexHullEntry[] = []
|
|
204
324
|
for (const entry of plot_entries) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
else
|
|
208
|
-
unstable.push(entry);
|
|
325
|
+
if (entry.is_stable) stable.push(entry)
|
|
326
|
+
else unstable.push(entry)
|
|
209
327
|
}
|
|
210
|
-
stable_entries = stable
|
|
211
|
-
unstable_entries = unstable
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
328
|
+
stable_entries = stable
|
|
329
|
+
unstable_entries = unstable
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
let reset_counter = $state(0)
|
|
333
|
+
// Drag and drop state (to match 3D/4D components)
|
|
334
|
+
let drag_over = $state(false)
|
|
335
|
+
// Copy feedback state
|
|
336
|
+
let copy_feedback = $state({ visible: false, position: { x: 0, y: 0 } })
|
|
337
|
+
|
|
338
|
+
// Structure popup state
|
|
339
|
+
let structure_popup = $state<{
|
|
340
|
+
open: boolean
|
|
341
|
+
structure: AnyStructure | null
|
|
342
|
+
entry: ConvexHullEntry | null
|
|
343
|
+
place_right: boolean
|
|
344
|
+
}>({
|
|
220
345
|
open: false,
|
|
221
346
|
structure: null,
|
|
222
347
|
entry: null,
|
|
223
348
|
place_right: true,
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const min_y_data = Math.min(...ys)
|
|
232
|
-
const max_y_data = Math.max(...ys)
|
|
233
|
-
const span = Math.max(1e-9, max_y_data - min_y_data)
|
|
234
|
-
const pad = 0.05 * span
|
|
235
|
-
return [min_y_data - pad, max_y_data + pad]
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
// Axis mapping helpers ------------------------------------------------------
|
|
352
|
+
const x_domain = $derived<[number, number]>([0, 1])
|
|
353
|
+
const y_domain = $derived.by((): [number, number] => {
|
|
354
|
+
const ys = plot_entries.map((entry) => entry.y)
|
|
355
|
+
if (ys.length === 0) return [-1, 0]
|
|
356
|
+
const min_y_data = Math.min(...ys)
|
|
357
|
+
const max_y_data = Math.max(...ys)
|
|
358
|
+
const span = Math.max(1e-9, max_y_data - min_y_data)
|
|
359
|
+
const pad = 0.05 * span
|
|
360
|
+
return [min_y_data - pad, max_y_data + pad]
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
// Build ScatterPlot series --------------------------------------------------
|
|
364
|
+
|
|
365
|
+
// Map MarkerSymbol to D3SymbolName (type-safe via symbol_map lookup)
|
|
366
|
+
const marker_to_d3_symbol = (marker?: string): D3SymbolName | undefined => {
|
|
367
|
+
if (!marker) return undefined
|
|
368
|
+
const name = marker.charAt(0).toUpperCase() + marker.slice(1)
|
|
369
|
+
return name in symbol_map ? (name as D3SymbolName) : undefined
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Pre-compute visible entries to avoid redundant filtering
|
|
373
|
+
const visible_entries = $derived(plot_entries.filter((e) => e.visible))
|
|
374
|
+
|
|
375
|
+
const scatter_points_series = $derived.by(() => {
|
|
376
|
+
const is_energy_mode = color_mode === `energy`
|
|
377
|
+
const count = visible_entries.length
|
|
378
|
+
|
|
250
379
|
// Single pass: extract x, y, color_values, and point_style simultaneously
|
|
251
|
-
const x_vals = new Array(count)
|
|
252
|
-
const y_vals = new Array(count)
|
|
253
|
-
const color_values = is_energy_mode ? new Array(count) : []
|
|
254
|
-
const point_style = new Array(count)
|
|
380
|
+
const x_vals: number[] = new Array(count)
|
|
381
|
+
const y_vals: number[] = new Array(count)
|
|
382
|
+
const color_values: number[] = is_energy_mode ? new Array(count) : []
|
|
383
|
+
const point_style = new Array(count)
|
|
384
|
+
|
|
255
385
|
for (let idx = 0; idx < count; idx++) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
386
|
+
const entry = visible_entries[idx]
|
|
387
|
+
x_vals[idx] = entry.x
|
|
388
|
+
y_vals[idx] = entry.y
|
|
389
|
+
if (is_energy_mode) color_values[idx] = entry.e_above_hull ?? 0
|
|
390
|
+
|
|
391
|
+
const is_stable = entry.is_stable || entry.e_above_hull === 0
|
|
392
|
+
const base_radius = entry.size || (is_stable ? 6 : 4)
|
|
393
|
+
const hl = is_highlighted(entry) ? merged_highlight_style : null
|
|
394
|
+
|
|
395
|
+
point_style[idx] = {
|
|
396
|
+
fill: hl && (hl.effect === `color` || hl.effect === `both`)
|
|
397
|
+
? hl.color
|
|
398
|
+
: is_energy_mode
|
|
399
|
+
? undefined
|
|
400
|
+
: merged_config.colors?.[is_stable ? `stable` : `unstable`],
|
|
401
|
+
stroke: is_stable ? `#ffffff` : `#000000`,
|
|
402
|
+
radius: hl && (hl.effect === `size` || hl.effect === `both`)
|
|
403
|
+
? base_radius * hl.size_multiplier
|
|
404
|
+
: base_radius,
|
|
405
|
+
symbol_type: marker_to_d3_symbol(entry.marker),
|
|
406
|
+
is_highlighted: !!hl,
|
|
407
|
+
highlight_effect: hl?.effect,
|
|
408
|
+
highlight_color: hl?.color,
|
|
409
|
+
}
|
|
279
410
|
}
|
|
411
|
+
|
|
280
412
|
return {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
})
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const segments = []
|
|
413
|
+
x: x_vals,
|
|
414
|
+
y: y_vals,
|
|
415
|
+
metadata: visible_entries,
|
|
416
|
+
markers: `points` as const,
|
|
417
|
+
point_style,
|
|
418
|
+
...(is_energy_mode ? { color_values } : {}),
|
|
419
|
+
}
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
const hull_segments_series = $derived.by(() => {
|
|
423
|
+
if (!merged_config.show_hull || hull_points.length < 2) return []
|
|
424
|
+
const segments = []
|
|
293
425
|
for (let idx = 0; idx < hull_points.length - 1; idx++) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
426
|
+
const p1 = hull_points[idx]
|
|
427
|
+
const p2 = hull_points[idx + 1]
|
|
428
|
+
segments.push({
|
|
429
|
+
x: [p1.x, p2.x] as const,
|
|
430
|
+
y: [p1.y, p2.y] as const,
|
|
431
|
+
markers: `line` as const,
|
|
432
|
+
line_style: {
|
|
433
|
+
stroke: CONVEX_HULL_STYLE.structure_line.color,
|
|
434
|
+
stroke_width: CONVEX_HULL_STYLE.structure_line.line_width,
|
|
435
|
+
line_dash: `${CONVEX_HULL_STYLE.structure_line.dash[0]},${
|
|
436
|
+
CONVEX_HULL_STYLE.structure_line.dash[1]
|
|
437
|
+
}`,
|
|
438
|
+
},
|
|
439
|
+
})
|
|
306
440
|
}
|
|
307
|
-
return segments
|
|
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
|
-
|
|
441
|
+
return segments
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
const scatter_series = $derived([scatter_points_series, ...hull_segments_series])
|
|
445
|
+
|
|
446
|
+
// Map selected_entry to ScatterPlot point index (series_idx: 0 = points series)
|
|
447
|
+
// Use object identity comparison (e === entry) instead of entry_id comparison
|
|
448
|
+
// because synthetic elemental entries lack entry_id, and undefined === undefined
|
|
449
|
+
// would incorrectly match the first entry with undefined entry_id
|
|
450
|
+
const selected_scatter_point = $derived.by(() => {
|
|
451
|
+
const entry = selected_entry
|
|
452
|
+
if (!entry) return null
|
|
453
|
+
const idx = visible_entries.findIndex((vis_entry) => vis_entry === entry)
|
|
454
|
+
return idx >= 0 ? { series_idx: 0, point_idx: idx } : null
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
// Convex hull statistics - compute internally and expose via bindable prop
|
|
458
|
+
$effect(() => {
|
|
459
|
+
phase_stats = thermo.get_convex_hull_stats(plot_entries, elements, 3)
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
function extract_structure_from_entry(
|
|
463
|
+
entry: ConvexHullEntry,
|
|
464
|
+
): AnyStructure | null {
|
|
465
|
+
if (!entry.entry_id) return null
|
|
466
|
+
const orig_entry = entries.find((ent) => ent.entry_id === entry.entry_id)
|
|
467
|
+
return orig_entry?.structure as AnyStructure || null
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function reset_all() {
|
|
471
|
+
fullscreen = DEFAULTS.convex_hull.binary.fullscreen
|
|
472
|
+
info_pane_open = DEFAULTS.convex_hull.binary.info_pane_open
|
|
473
|
+
legend_pane_open = DEFAULTS.convex_hull.binary.legend_pane_open
|
|
474
|
+
color_mode = DEFAULTS.convex_hull.binary.color_mode
|
|
475
|
+
color_scale = DEFAULTS.convex_hull.binary.color_scale as D3InterpolateName
|
|
476
|
+
show_stable = DEFAULTS.convex_hull.binary.show_stable
|
|
477
|
+
show_unstable = DEFAULTS.convex_hull.binary.show_unstable
|
|
478
|
+
show_stable_labels = DEFAULTS.convex_hull.binary.show_stable_labels
|
|
479
|
+
show_unstable_labels = DEFAULTS.convex_hull.binary.show_unstable_labels
|
|
341
480
|
// Use auto-computed threshold based on entry count instead of static default
|
|
342
|
-
max_hull_dist_show_phases = auto_default_threshold
|
|
343
|
-
max_hull_dist_show_labels = DEFAULTS.convex_hull.binary.max_hull_dist_show_labels
|
|
344
|
-
reset_counter += 1
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
481
|
+
max_hull_dist_show_phases = auto_default_threshold
|
|
482
|
+
max_hull_dist_show_labels = DEFAULTS.convex_hull.binary.max_hull_dist_show_labels
|
|
483
|
+
reset_counter += 1
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Track whether any settings differ from defaults (to show/hide reset button)
|
|
487
|
+
// Must match all properties that reset_all() resets
|
|
488
|
+
// Use auto_default_threshold for comparison since that's the effective default
|
|
489
|
+
const has_changes_to_reset = $derived(
|
|
490
|
+
fullscreen !== DEFAULTS.convex_hull.binary.fullscreen ||
|
|
491
|
+
info_pane_open !== DEFAULTS.convex_hull.binary.info_pane_open ||
|
|
492
|
+
legend_pane_open !== DEFAULTS.convex_hull.binary.legend_pane_open ||
|
|
493
|
+
color_mode !== DEFAULTS.convex_hull.binary.color_mode ||
|
|
494
|
+
color_scale !== DEFAULTS.convex_hull.binary.color_scale ||
|
|
495
|
+
show_stable !== DEFAULTS.convex_hull.binary.show_stable ||
|
|
496
|
+
show_unstable !== DEFAULTS.convex_hull.binary.show_unstable ||
|
|
497
|
+
show_stable_labels !== DEFAULTS.convex_hull.binary.show_stable_labels ||
|
|
498
|
+
show_unstable_labels !== DEFAULTS.convex_hull.binary.show_unstable_labels ||
|
|
499
|
+
// Compare with auto-computed threshold, with small tolerance for floating point
|
|
500
|
+
Math.abs(max_hull_dist_show_phases - auto_default_threshold) > 0.001 ||
|
|
501
|
+
max_hull_dist_show_labels !==
|
|
502
|
+
DEFAULTS.convex_hull.binary.max_hull_dist_show_labels,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
// Custom hover tooltip state used with ScatterPlot events
|
|
506
|
+
let hover_data = $state<HoverData3D<ConvexHullEntry> | null>(null)
|
|
507
|
+
|
|
508
|
+
const handle_keydown = (event: KeyboardEvent) => {
|
|
509
|
+
if ((event.target as HTMLElement).tagName.match(/INPUT|TEXTAREA/)) return
|
|
510
|
+
const actions: Record<string, () => void> = {
|
|
511
|
+
b: () => color_mode = color_mode === `stability` ? `energy` : `stability`,
|
|
512
|
+
s: () => show_stable = !show_stable,
|
|
513
|
+
u: () => show_unstable = !show_unstable,
|
|
514
|
+
l: () => show_stable_labels = !show_stable_labels,
|
|
515
|
+
}
|
|
516
|
+
actions[event.key.toLowerCase()]?.()
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
async function handle_file_drop(event: DragEvent): Promise<void> {
|
|
520
|
+
drag_over = false
|
|
521
|
+
const data = await helpers.parse_hull_entries_from_drop(event)
|
|
522
|
+
if (data) on_file_drop?.(data)
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
async function copy_entry_data(
|
|
526
|
+
entry: ConvexHullEntry,
|
|
527
|
+
position: { x: number; y: number },
|
|
528
|
+
) {
|
|
382
529
|
await helpers.copy_entry_to_clipboard(entry, position, (visible, pos) => {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
})
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
const
|
|
530
|
+
copy_feedback.visible = visible
|
|
531
|
+
copy_feedback.position = pos
|
|
532
|
+
})
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function close_structure_popup() {
|
|
536
|
+
structure_popup = { open: false, structure: null, entry: null, place_right: true }
|
|
537
|
+
selected_entry = null
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Track last clicked entry for double-click detection
|
|
541
|
+
let last_clicked_entry: ConvexHullEntry | null = null
|
|
542
|
+
let last_click_time = 0
|
|
543
|
+
|
|
544
|
+
function handle_point_click_internal(data: ScatterHandlerEvent<ConvexHullEntry>) {
|
|
545
|
+
const { metadata: entry, event } = data
|
|
546
|
+
if (!entry) return
|
|
547
|
+
|
|
548
|
+
const now = Date.now()
|
|
399
549
|
const is_double_click = last_clicked_entry === entry &&
|
|
400
|
-
|
|
550
|
+
now - last_click_time < 300
|
|
551
|
+
|
|
401
552
|
if (is_double_click) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
553
|
+
// Double-click: copy to clipboard
|
|
554
|
+
copy_entry_data(entry, { x: event.clientX, y: event.clientY })
|
|
555
|
+
last_clicked_entry = null
|
|
556
|
+
last_click_time = 0
|
|
557
|
+
} else {
|
|
558
|
+
// Single click: select entry and optionally show structure preview
|
|
559
|
+
last_clicked_entry = entry
|
|
560
|
+
last_click_time = now
|
|
561
|
+
|
|
562
|
+
on_point_click?.(entry)
|
|
563
|
+
if (enable_click_selection) {
|
|
564
|
+
selected_entry = entry
|
|
565
|
+
if (enable_structure_preview) {
|
|
566
|
+
const structure = extract_structure_from_entry(entry)
|
|
567
|
+
if (structure) {
|
|
568
|
+
const viewport_width = globalThis.innerWidth
|
|
569
|
+
const click_x = event.clientX
|
|
570
|
+
const space_on_right = viewport_width - click_x
|
|
571
|
+
const space_on_left = click_x
|
|
572
|
+
const place_right = space_on_right >= space_on_left
|
|
573
|
+
|
|
574
|
+
structure_popup = { open: true, structure, entry, place_right }
|
|
575
|
+
event.stopPropagation()
|
|
576
|
+
}
|
|
426
577
|
}
|
|
578
|
+
}
|
|
427
579
|
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Fullscreen handling
|
|
583
|
+
$effect(() => {
|
|
584
|
+
setup_fullscreen_effect(fullscreen, wrapper)
|
|
585
|
+
set_fullscreen_bg(wrapper, fullscreen, `--hull-2d-bg-fullscreen`)
|
|
586
|
+
})
|
|
587
|
+
|
|
588
|
+
let style = $derived(
|
|
589
|
+
`--hull-stable-color:${merged_config.colors?.stable || `#0072B2`};
|
|
435
590
|
--hull-unstable-color:${merged_config.colors?.unstable || `#E69F00`};
|
|
436
591
|
--hull-edge-color:${merged_config.colors?.edge || `var(--text-color, #212121)`};
|
|
437
|
-
--hull-text-color:${
|
|
592
|
+
--hull-text-color:${
|
|
593
|
+
merged_config.colors?.annotation || `var(--text-color, #212121)`
|
|
594
|
+
};`,
|
|
595
|
+
)
|
|
438
596
|
</script>
|
|
439
597
|
|
|
440
598
|
<svelte:document
|
|
@@ -592,7 +750,7 @@ let style = $derived(`--hull-stable-color:${merged_config.colors?.stable || `#00
|
|
|
592
750
|
selected_entry,
|
|
593
751
|
})}
|
|
594
752
|
<h3 style="position: absolute; left: 1em; top: 1ex; margin: 0">
|
|
595
|
-
{@html merged_controls.title || phase_stats?.chemical_system || ``}
|
|
753
|
+
{@html sanitize_html(merged_controls.title || phase_stats?.chemical_system || ``)}
|
|
596
754
|
</h3>
|
|
597
755
|
|
|
598
756
|
<ClickFeedback
|