matterviz 0.3.2 → 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 +123 -82
- package/dist/Icon.svelte +18 -12
- package/dist/MillerIndexInput.svelte +27 -21
- package/dist/api/optimade.js +6 -6
- package/dist/app.css +216 -207
- package/dist/brillouin/BrillouinZone.svelte +292 -149
- package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
- package/dist/brillouin/BrillouinZoneExportPane.svelte +69 -42
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
- package/dist/brillouin/BrillouinZoneScene.svelte +275 -163
- 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 +162 -27
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +451 -281
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2148 -1642
- package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -5
- 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.js +1 -2
- package/dist/chempot-diagram/compute.d.ts +10 -0
- package/dist/chempot-diagram/compute.js +250 -88
- package/dist/chempot-diagram/index.d.ts +2 -1
- package/dist/chempot-diagram/index.js +2 -1
- package/dist/chempot-diagram/temperature.js +8 -9
- package/dist/chempot-diagram/types.d.ts +3 -0
- package/dist/chempot-diagram/types.js +1 -0
- package/dist/colors/index.d.ts +1 -1
- package/dist/colors/index.js +5 -3
- package/dist/composition/BarChart.svelte +128 -55
- package/dist/composition/BubbleChart.svelte +102 -49
- package/dist/composition/Composition.svelte +100 -79
- package/dist/composition/Formula.svelte +108 -62
- package/dist/composition/FormulaFilter.svelte +665 -537
- package/dist/composition/PieChart.svelte +183 -108
- 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 -40
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull2D.svelte +549 -360
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +1296 -827
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +1004 -688
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullControls.svelte +115 -28
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
- package/dist/convex-hull/ConvexHullStats.svelte +425 -328
- package/dist/convex-hull/ConvexHullTooltip.svelte +40 -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.js +8 -4
- package/dist/convex-hull/gas-thermodynamics.js +17 -12
- package/dist/convex-hull/helpers.d.ts +9 -0
- package/dist/convex-hull/helpers.js +77 -34
- package/dist/convex-hull/thermodynamics.js +61 -56
- package/dist/convex-hull/types.d.ts +9 -14
- package/dist/convex-hull/types.js +0 -17
- package/dist/coordination/CoordinationBarPlot.svelte +227 -154
- package/dist/element/BohrAtom.svelte +55 -12
- 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/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 +328 -187
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +535 -342
- 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 +24 -14
- package/dist/fermi-surface/symmetry.js +2 -7
- package/dist/fermi-surface/types.d.ts +3 -5
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +1019 -765
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +1 -1
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +76 -22
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +2 -3
- package/dist/icons.js +47 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/io/decompress.js +1 -1
- package/dist/io/export.d.ts +3 -0
- package/dist/io/export.js +129 -143
- package/dist/io/is-binary.js +2 -3
- package/dist/io/url-drop.js +1 -2
- package/dist/isosurface/Isosurface.svelte +202 -148
- package/dist/isosurface/IsosurfaceControls.svelte +46 -28
- package/dist/isosurface/parse.js +34 -29
- package/dist/isosurface/slice.js +5 -10
- package/dist/isosurface/types.d.ts +2 -1
- package/dist/isosurface/types.js +61 -12
- package/dist/labels.js +11 -8
- package/dist/layout/FullscreenToggle.svelte +11 -2
- package/dist/layout/InfoCard.svelte +38 -6
- package/dist/layout/InfoTag.svelte +63 -32
- package/dist/layout/PropertyFilter.svelte +82 -37
- package/dist/layout/SettingsSection.svelte +85 -55
- package/dist/layout/SubpageGrid.svelte +10 -2
- package/dist/layout/json-tree/JsonNode.svelte +183 -138
- package/dist/layout/json-tree/JsonTree.svelte +499 -413
- package/dist/layout/json-tree/JsonValue.svelte +127 -99
- package/dist/layout/json-tree/utils.js +4 -2
- package/dist/marching-cubes.js +25 -2
- package/dist/math.d.ts +13 -17
- package/dist/math.js +133 -67
- package/dist/overlays/ContextMenu.svelte +65 -40
- package/dist/overlays/DraggablePane.svelte +211 -139
- 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 +446 -309
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramControls.svelte +102 -43
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +63 -40
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +71 -28
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +158 -101
- 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/parse.js +10 -9
- package/dist/phase-diagram/svg-to-diagram.js +53 -49
- package/dist/phase-diagram/utils.d.ts +1 -0
- package/dist/phase-diagram/utils.js +80 -25
- package/dist/plot/AxisLabel.svelte +28 -3
- package/dist/plot/BarPlot.svelte +1182 -734
- package/dist/plot/BarPlot.svelte.d.ts +2 -2
- package/dist/plot/BarPlotControls.svelte +31 -5
- package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ColorBar.svelte +479 -329
- package/dist/plot/ColorScaleSelect.svelte +27 -6
- package/dist/plot/ElementScatter.svelte +36 -15
- package/dist/plot/FillArea.svelte +152 -95
- package/dist/plot/Histogram.svelte +934 -571
- package/dist/plot/Histogram.svelte.d.ts +1 -1
- package/dist/plot/HistogramControls.svelte +53 -9
- package/dist/plot/HistogramControls.svelte.d.ts +1 -1
- 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 +157 -114
- 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 -147
- package/dist/plot/ReferenceLine.svelte +76 -22
- package/dist/plot/ReferenceLine3D.svelte +132 -107
- package/dist/plot/ReferencePlane.svelte +146 -121
- package/dist/plot/ScatterPlot.svelte +1681 -1091
- package/dist/plot/ScatterPlot.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3D.svelte +256 -131
- package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3DControls.svelte +113 -63
- package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
- package/dist/plot/ScatterPlot3DScene.svelte +608 -403
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlotControls.svelte +65 -25
- 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 +55 -3
- package/dist/plot/ZoomRect.svelte +4 -2
- 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/layout.d.ts +4 -1
- package/dist/plot/layout.js +33 -14
- package/dist/plot/reference-line.d.ts +2 -2
- package/dist/plot/reference-line.js +7 -5
- package/dist/plot/scales.js +24 -36
- package/dist/plot/types.d.ts +11 -23
- package/dist/plot/types.js +6 -11
- package/dist/plot/utils/label-placement.d.ts +32 -15
- package/dist/plot/utils/label-placement.js +227 -66
- package/dist/plot/utils/series-visibility.js +2 -3
- package/dist/rdf/RdfPlot.svelte +143 -91
- 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 +18 -6
- package/dist/settings.js +46 -16
- package/dist/spectral/Bands.svelte +632 -453
- 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.js +55 -43
- 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 +215 -134
- package/dist/structure/Bond.svelte +73 -47
- package/dist/structure/CanvasTooltip.svelte +10 -2
- package/dist/structure/CellSelect.svelte +72 -45
- package/dist/structure/Cylinder.svelte +33 -17
- package/dist/structure/Lattice.svelte +88 -33
- package/dist/structure/Structure.svelte +1063 -797
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureControls.svelte +349 -118
- package/dist/structure/StructureExportPane.svelte +124 -89
- package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +304 -237
- package/dist/structure/StructureScene.svelte +879 -443
- package/dist/structure/StructureScene.svelte.d.ts +15 -7
- package/dist/structure/atom-properties.js +8 -8
- package/dist/structure/bonding.js +6 -7
- package/dist/structure/export.js +14 -29
- package/dist/structure/ferrox-wasm.js +1 -1
- package/dist/structure/index.d.ts +13 -3
- package/dist/structure/index.js +83 -23
- package/dist/structure/measure.d.ts +2 -2
- package/dist/structure/measure.js +4 -44
- package/dist/structure/parse.js +113 -141
- package/dist/structure/partial-occupancy.js +7 -10
- 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 +1 -2
- package/dist/symmetry/SymmetryStats.svelte +84 -41
- package/dist/symmetry/WyckoffTable.svelte +26 -6
- package/dist/symmetry/cell-transform.js +5 -3
- package/dist/symmetry/index.js +8 -7
- package/dist/symmetry/spacegroups.js +148 -148
- package/dist/table/HeatmapTable.svelte +790 -554
- package/dist/table/HeatmapTable.svelte.d.ts +1 -1
- package/dist/table/ToggleMenu.svelte +125 -92
- 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 +758 -558
- package/dist/trajectory/TrajectoryError.svelte +14 -3
- package/dist/trajectory/TrajectoryExportPane.svelte +137 -83
- package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
- package/dist/trajectory/extract.js +10 -26
- package/dist/trajectory/format-detect.js +5 -5
- package/dist/trajectory/frame-reader.d.ts +1 -1
- package/dist/trajectory/frame-reader.js +5 -12
- package/dist/trajectory/helpers.d.ts +0 -1
- package/dist/trajectory/helpers.js +2 -17
- package/dist/trajectory/index.js +14 -12
- package/dist/trajectory/parse/ase.js +5 -4
- package/dist/trajectory/parse/hdf5.js +26 -18
- package/dist/trajectory/parse/index.js +13 -18
- package/dist/trajectory/parse/lammps.js +17 -7
- package/dist/trajectory/parse/vasp.js +5 -2
- package/dist/trajectory/parse/xyz.js +8 -7
- package/dist/trajectory/plotting.js +13 -8
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +13 -0
- package/dist/xrd/XrdPlot.svelte +337 -247
- package/dist/xrd/broadening.js +14 -9
- package/dist/xrd/calc-xrd.js +12 -18
- package/dist/xrd/parse.d.ts +1 -1
- package/dist/xrd/parse.js +17 -17
- package/package.json +99 -103
- package/readme.md +1 -1
- /package/dist/theme/{themes.js → themes.mjs} +0 -0
|
@@ -1,409 +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
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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({
|
|
24
99
|
...default_hull_config,
|
|
25
100
|
point_size: 6, // Binary diagrams use slightly smaller points
|
|
26
101
|
...config,
|
|
27
102
|
colors: { ...default_hull_config.colors, ...(config.colors || {}) },
|
|
28
103
|
margin: { t: 40, r: 40, b: 60, l: 60, ...(config.margin || {}) },
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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, {
|
|
46
133
|
interpolate: interpolate_temperature,
|
|
47
134
|
max_interpolation_gap,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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(() => {
|
|
59
185
|
if (pd_data.elements.length > 2) {
|
|
60
|
-
|
|
61
|
-
|
|
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 []
|
|
62
192
|
}
|
|
63
|
-
return pd_data.elements
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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[] = []
|
|
71
204
|
for (const entry of raw_entries) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const is_element = is_unary_entry(entry);
|
|
81
|
-
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 })
|
|
82
213
|
}
|
|
83
214
|
// Ensure elemental references at x=0 and x=1 with y=0 to close the hull
|
|
84
|
-
const el_a = coords.find((e) =>
|
|
85
|
-
|
|
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
|
+
)
|
|
86
221
|
if (!el_a) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
+
})
|
|
96
231
|
}
|
|
97
232
|
if (!el_b) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
+
})
|
|
107
242
|
}
|
|
108
|
-
return coords
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
243
|
+
return coords
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const coords_entries = $derived.by(() => {
|
|
247
|
+
if (elements.length !== 2) return []
|
|
113
248
|
try {
|
|
114
|
-
|
|
249
|
+
return compute_binary_coordinates(pd_data.entries, elements)
|
|
250
|
+
} catch (error) {
|
|
251
|
+
console.error(`Error computing binary coordinates:`, error)
|
|
252
|
+
return []
|
|
115
253
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
});
|
|
121
|
-
// Compute hull and enrich entries with e_above_hull (before filtering)
|
|
122
|
-
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(() => {
|
|
123
258
|
if (coords_entries.length === 0) {
|
|
124
|
-
|
|
259
|
+
return { all_enriched_entries: [], hull_points: [] }
|
|
125
260
|
}
|
|
261
|
+
|
|
126
262
|
// Build lower hull input: one minimum-energy point per composition x.
|
|
127
|
-
|
|
263
|
+
// Excluded entries don't participate in hull construction.
|
|
264
|
+
const min_y_by_x = new SvelteMap<number, number>()
|
|
128
265
|
for (const entry of coords_entries) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
+
}
|
|
133
271
|
}
|
|
272
|
+
|
|
134
273
|
const hull_input = [...min_y_by_x].map(([x_coord, min_y]) => ({
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}))
|
|
138
|
-
const hull_points = thermo.compute_lower_hull_2d(hull_input)
|
|
274
|
+
x: x_coord,
|
|
275
|
+
y: min_y,
|
|
276
|
+
}))
|
|
277
|
+
const hull_points = thermo.compute_lower_hull_2d(hull_input)
|
|
278
|
+
|
|
139
279
|
const all_enriched_entries = coords_entries.map((entry) => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const auto_default_threshold = $derived(helpers.compute_auto_hull_dist_threshold(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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(() => {
|
|
157
302
|
if (!initialized && all_enriched_entries.length > 0) {
|
|
158
|
-
|
|
159
|
-
|
|
303
|
+
initialized = true
|
|
304
|
+
max_hull_dist_show_phases = auto_default_threshold
|
|
160
305
|
}
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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[] = []
|
|
173
324
|
for (const entry of plot_entries) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
else
|
|
177
|
-
unstable.push(entry);
|
|
325
|
+
if (entry.is_stable) stable.push(entry)
|
|
326
|
+
else unstable.push(entry)
|
|
178
327
|
}
|
|
179
|
-
stable_entries = stable
|
|
180
|
-
unstable_entries = unstable
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
+
}>({
|
|
189
345
|
open: false,
|
|
190
346
|
structure: null,
|
|
191
347
|
entry: null,
|
|
192
348
|
place_right: true,
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const min_y_data = Math.min(...ys)
|
|
201
|
-
const max_y_data = Math.max(...ys)
|
|
202
|
-
const span = Math.max(1e-9, max_y_data - min_y_data)
|
|
203
|
-
const pad = 0.05 * span
|
|
204
|
-
return [min_y_data - pad, max_y_data + pad]
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
+
|
|
219
379
|
// Single pass: extract x, y, color_values, and point_style simultaneously
|
|
220
|
-
const x_vals = new Array(count)
|
|
221
|
-
const y_vals = new Array(count)
|
|
222
|
-
const color_values = is_energy_mode ? new Array(count) : []
|
|
223
|
-
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
|
+
|
|
224
385
|
for (let idx = 0; idx < count; idx++) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
+
}
|
|
248
410
|
}
|
|
411
|
+
|
|
249
412
|
return {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
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 = []
|
|
262
425
|
for (let idx = 0; idx < hull_points.length - 1; idx++) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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
|
+
})
|
|
275
440
|
}
|
|
276
|
-
return segments
|
|
277
|
-
})
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
//
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
|
310
480
|
// Use auto-computed threshold based on entry count instead of static default
|
|
311
|
-
max_hull_dist_show_phases = auto_default_threshold
|
|
312
|
-
max_hull_dist_show_labels = DEFAULTS.convex_hull.binary.max_hull_dist_show_labels
|
|
313
|
-
reset_counter += 1
|
|
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
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
+
) {
|
|
351
529
|
await helpers.copy_entry_to_clipboard(entry, position, (visible, pos) => {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
})
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
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()
|
|
368
549
|
const is_double_click = last_clicked_entry === entry &&
|
|
369
|
-
|
|
550
|
+
now - last_click_time < 300
|
|
551
|
+
|
|
370
552
|
if (is_double_click) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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
|
+
}
|
|
395
577
|
}
|
|
578
|
+
}
|
|
396
579
|
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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`};
|
|
404
590
|
--hull-unstable-color:${merged_config.colors?.unstable || `#E69F00`};
|
|
405
591
|
--hull-edge-color:${merged_config.colors?.edge || `var(--text-color, #212121)`};
|
|
406
|
-
--hull-text-color:${
|
|
592
|
+
--hull-text-color:${
|
|
593
|
+
merged_config.colors?.annotation || `var(--text-color, #212121)`
|
|
594
|
+
};`,
|
|
595
|
+
)
|
|
407
596
|
</script>
|
|
408
597
|
|
|
409
598
|
<svelte:document
|
|
@@ -561,7 +750,7 @@ let style = $derived(`--hull-stable-color:${merged_config.colors?.stable || `#00
|
|
|
561
750
|
selected_entry,
|
|
562
751
|
})}
|
|
563
752
|
<h3 style="position: absolute; left: 1em; top: 1ex; margin: 0">
|
|
564
|
-
{@html merged_controls.title || phase_stats?.chemical_system || ``}
|
|
753
|
+
{@html sanitize_html(merged_controls.title || phase_stats?.chemical_system || ``)}
|
|
565
754
|
</h3>
|
|
566
755
|
|
|
567
756
|
<ClickFeedback
|