matterviz 0.3.4 → 0.3.6
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/.vscode/launch.json +13 -0
- package/.vscodeignore +7 -0
- package/dist/assets/STLExporter-BpTH3YHE.js +8 -0
- package/dist/assets/browser-DdDecX_W.js +1 -0
- package/dist/assets/export-qgn-H9y6.js +2 -0
- package/dist/assets/main-DiKYzti2.css +1 -0
- package/dist/assets/moyo_wasm_bg-0ocwg7xY.wasm +0 -0
- package/dist/extension.js +31293 -0
- package/dist/src/lib/FilePicker.svelte +360 -0
- package/dist/src/lib/MillerIndexInput.svelte +66 -0
- package/dist/src/lib/api/mp.ts +26 -0
- package/dist/src/lib/api/optimade.ts +204 -0
- package/dist/src/lib/app.css +247 -0
- package/dist/src/lib/brillouin/BrillouinZone.svelte +549 -0
- package/dist/src/lib/brillouin/BrillouinZoneControls.svelte +144 -0
- package/dist/src/lib/brillouin/BrillouinZoneExportPane.svelte +146 -0
- package/dist/src/lib/brillouin/BrillouinZoneInfoPane.svelte +146 -0
- package/dist/src/lib/brillouin/BrillouinZoneScene.svelte +476 -0
- package/dist/src/lib/brillouin/BrillouinZoneTooltip.svelte +92 -0
- package/dist/src/lib/brillouin/compute.ts +529 -0
- package/dist/src/lib/brillouin/index.ts +8 -0
- package/dist/src/lib/brillouin/types.ts +51 -0
- package/dist/src/lib/chempot-diagram/ChemPotDiagram.svelte +327 -0
- package/dist/src/lib/chempot-diagram/ChemPotDiagram2D.svelte +846 -0
- package/dist/src/lib/chempot-diagram/ChemPotDiagram3D.svelte +3193 -0
- package/dist/src/lib/chempot-diagram/async-compute.svelte.ts +94 -0
- package/dist/src/lib/chempot-diagram/chempot-worker.ts +11 -0
- package/dist/src/lib/chempot-diagram/color.ts +42 -0
- package/dist/src/lib/chempot-diagram/compute.ts +1014 -0
- package/dist/src/lib/chempot-diagram/index.ts +6 -0
- package/dist/src/lib/chempot-diagram/pointer.ts +56 -0
- package/dist/src/lib/chempot-diagram/temperature.ts +77 -0
- package/dist/src/lib/chempot-diagram/types.ts +130 -0
- package/dist/src/lib/colors/index.ts +249 -0
- package/dist/src/lib/composition/BarChart.svelte +297 -0
- package/dist/src/lib/composition/BubbleChart.svelte +218 -0
- package/dist/src/lib/composition/Composition.svelte +165 -0
- package/dist/src/lib/composition/Formula.svelte +268 -0
- package/dist/src/lib/composition/FormulaFilter.svelte +1257 -0
- package/dist/src/lib/composition/PieChart.svelte +323 -0
- package/dist/src/lib/composition/format.ts +155 -0
- package/dist/src/lib/composition/index.ts +37 -0
- package/dist/src/lib/composition/parse.ts +605 -0
- package/dist/src/lib/constants.ts +134 -0
- package/dist/src/lib/controls.ts +42 -0
- package/dist/src/lib/convex-hull/ConvexHull.svelte +157 -0
- package/dist/src/lib/convex-hull/ConvexHull2D.svelte +825 -0
- package/dist/src/lib/convex-hull/ConvexHull3D.svelte +1801 -0
- package/dist/src/lib/convex-hull/ConvexHull4D.svelte +1398 -0
- package/dist/src/lib/convex-hull/ConvexHullControls.svelte +535 -0
- package/dist/src/lib/convex-hull/ConvexHullInfoPane.svelte +125 -0
- package/dist/src/lib/convex-hull/ConvexHullStats.svelte +929 -0
- package/dist/src/lib/convex-hull/ConvexHullTooltip.svelte +131 -0
- package/dist/src/lib/convex-hull/GasPressureControls.svelte +247 -0
- package/dist/src/lib/convex-hull/StructurePopup.svelte +151 -0
- package/dist/src/lib/convex-hull/TemperatureSlider.svelte +140 -0
- package/dist/src/lib/convex-hull/barycentric-coords.ts +246 -0
- package/dist/src/lib/convex-hull/demo-temperature.ts +63 -0
- package/dist/src/lib/convex-hull/gas-thermodynamics.ts +405 -0
- package/dist/src/lib/convex-hull/helpers.ts +932 -0
- package/dist/src/lib/convex-hull/index.ts +202 -0
- package/dist/src/lib/convex-hull/thermodynamics.ts +2192 -0
- package/dist/src/lib/convex-hull/types.ts +267 -0
- package/dist/src/lib/coordination/CoordinationBarPlot.svelte +311 -0
- package/dist/src/lib/coordination/calc-coordination.ts +93 -0
- package/dist/src/lib/coordination/index.ts +9 -0
- package/dist/src/lib/effects.svelte.ts +48 -0
- package/dist/src/lib/element/BohrAtom.svelte +147 -0
- package/dist/src/lib/element/ElementHeading.svelte +26 -0
- package/dist/src/lib/element/ElementPhoto.svelte +57 -0
- package/dist/src/lib/element/ElementStats.svelte +80 -0
- package/dist/src/lib/element/ElementTile.svelte +484 -0
- package/dist/src/lib/element/data.json.gz.d.ts +4 -0
- package/dist/src/lib/element/data.ts +14 -0
- package/dist/src/lib/element/index.ts +8 -0
- package/dist/src/lib/element/types.ts +62 -0
- package/dist/src/lib/feedback/ClickFeedback.svelte +58 -0
- package/dist/src/lib/feedback/DragOverlay.svelte +42 -0
- package/dist/src/lib/feedback/index.ts +4 -0
- package/dist/src/lib/fermi-surface/FermiSlice.svelte +189 -0
- package/dist/src/lib/fermi-surface/FermiSurface.svelte +600 -0
- package/dist/src/lib/fermi-surface/FermiSurfaceControls.svelte +448 -0
- package/dist/src/lib/fermi-surface/FermiSurfaceScene.svelte +794 -0
- package/dist/src/lib/fermi-surface/FermiSurfaceTooltip.svelte +111 -0
- package/dist/src/lib/fermi-surface/compute.ts +728 -0
- package/dist/src/lib/fermi-surface/constants.ts +32 -0
- package/dist/src/lib/fermi-surface/export.ts +64 -0
- package/dist/src/lib/fermi-surface/index.ts +14 -0
- package/dist/src/lib/fermi-surface/marching-cubes.ts +3 -0
- package/dist/src/lib/fermi-surface/parse.ts +574 -0
- package/dist/src/lib/fermi-surface/symmetry.ts +56 -0
- package/dist/src/lib/fermi-surface/types.ts +159 -0
- package/dist/src/lib/heatmap-matrix/HeatmapMatrix.svelte +1545 -0
- package/dist/src/lib/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
- package/dist/src/lib/heatmap-matrix/index.ts +167 -0
- package/dist/src/lib/heatmap-matrix/shared.ts +7 -0
- package/dist/src/lib/icons.ts +650 -0
- package/dist/src/lib/index.ts +61 -0
- package/dist/src/lib/io/decompress.ts +92 -0
- package/dist/src/lib/io/export.ts +385 -0
- package/dist/src/lib/io/fetch.ts +46 -0
- package/dist/src/lib/io/file-drop.ts +51 -0
- package/dist/src/lib/io/index.ts +7 -0
- package/dist/src/lib/io/is-binary.ts +24 -0
- package/dist/src/lib/io/types.ts +8 -0
- package/dist/src/lib/io/url-drop.ts +141 -0
- package/dist/src/lib/isosurface/Isosurface.svelte +285 -0
- package/dist/src/lib/isosurface/IsosurfaceControls.svelte +277 -0
- package/dist/src/lib/isosurface/index.ts +7 -0
- package/dist/src/lib/isosurface/parse.ts +656 -0
- package/dist/src/lib/isosurface/slice.ts +175 -0
- package/dist/src/lib/isosurface/types.ts +309 -0
- package/dist/src/lib/labels.ts +320 -0
- package/dist/src/lib/layout/FullscreenToggle.svelte +50 -0
- package/dist/src/lib/layout/InfoCard.svelte +120 -0
- package/dist/src/lib/layout/InfoTag.svelte +185 -0
- package/dist/src/lib/layout/PropertyFilter.svelte +246 -0
- package/dist/src/lib/layout/SettingsSection.svelte +148 -0
- package/dist/src/lib/layout/SubpageGrid.svelte +82 -0
- package/dist/src/lib/layout/fullscreen.ts +65 -0
- package/dist/src/lib/layout/index.ts +11 -0
- package/dist/src/lib/layout/json-tree/JsonNode.svelte +548 -0
- package/dist/src/lib/layout/json-tree/JsonTree.svelte +1230 -0
- package/dist/src/lib/layout/json-tree/JsonValue.svelte +334 -0
- package/dist/src/lib/layout/json-tree/index.ts +3 -0
- package/dist/src/lib/layout/json-tree/types.ts +126 -0
- package/dist/src/lib/layout/json-tree/utils.ts +682 -0
- package/dist/src/lib/marching-cubes.ts +614 -0
- package/dist/src/lib/math.ts +1081 -0
- package/dist/src/lib/overlays/ContextMenu.svelte +162 -0
- package/dist/src/lib/overlays/CopyButton.svelte +45 -0
- package/dist/src/lib/overlays/DragControlTab.svelte +98 -0
- package/dist/src/lib/overlays/DraggablePane.svelte +487 -0
- package/dist/src/lib/overlays/InfoPaneCards.svelte +149 -0
- package/dist/src/lib/overlays/index.ts +3 -0
- package/dist/src/lib/periodic-table/PeriodicTable.svelte +469 -0
- package/dist/src/lib/periodic-table/PeriodicTableControls.svelte +557 -0
- package/dist/src/lib/periodic-table/PropertySelect.svelte +37 -0
- package/dist/src/lib/periodic-table/index.ts +12 -0
- package/dist/src/lib/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +1086 -0
- package/dist/src/lib/phase-diagram/PhaseDiagramControls.svelte +444 -0
- package/dist/src/lib/phase-diagram/PhaseDiagramEditorPane.svelte +126 -0
- package/dist/src/lib/phase-diagram/PhaseDiagramExportPane.svelte +184 -0
- package/dist/src/lib/phase-diagram/PhaseDiagramTooltip.svelte +391 -0
- package/dist/src/lib/phase-diagram/TdbInfoPanel.svelte +203 -0
- package/dist/src/lib/phase-diagram/build-diagram.ts +186 -0
- package/dist/src/lib/phase-diagram/colors.ts +58 -0
- package/dist/src/lib/phase-diagram/diagram-input.ts +40 -0
- package/dist/src/lib/phase-diagram/index.ts +13 -0
- package/dist/src/lib/phase-diagram/parse.ts +348 -0
- package/dist/src/lib/phase-diagram/svg-to-diagram.ts +1023 -0
- package/dist/src/lib/phase-diagram/types.ts +144 -0
- package/dist/src/lib/phase-diagram/utils.ts +775 -0
- package/dist/src/lib/plot/AxisLabel.svelte +51 -0
- package/dist/src/lib/plot/BarPlot.svelte +2113 -0
- package/dist/src/lib/plot/BarPlotControls.svelte +66 -0
- package/dist/src/lib/plot/BinnedScatterPlot.svelte +1114 -0
- package/dist/src/lib/plot/ColorBar.svelte +721 -0
- package/dist/src/lib/plot/ColorScaleSelect.svelte +54 -0
- package/dist/src/lib/plot/ElementScatter.svelte +63 -0
- package/dist/src/lib/plot/FillArea.svelte +223 -0
- package/dist/src/lib/plot/Histogram.svelte +1558 -0
- package/dist/src/lib/plot/HistogramControls.svelte +212 -0
- package/dist/src/lib/plot/InteractiveAxisLabel.svelte +96 -0
- package/dist/src/lib/plot/Line.svelte +84 -0
- package/dist/src/lib/plot/PlotAxis.svelte +169 -0
- package/dist/src/lib/plot/PlotControls.svelte +537 -0
- package/dist/src/lib/plot/PlotLegend.svelte +569 -0
- package/dist/src/lib/plot/PlotTooltip.svelte +67 -0
- package/dist/src/lib/plot/PortalSelect.svelte +253 -0
- package/dist/src/lib/plot/ReferenceLine3D.svelte +156 -0
- package/dist/src/lib/plot/ReferencePlane.svelte +175 -0
- package/dist/src/lib/plot/ScatterPlot.svelte +2778 -0
- package/dist/src/lib/plot/ScatterPlot3D.svelte +529 -0
- package/dist/src/lib/plot/ScatterPlot3DControls.svelte +437 -0
- package/dist/src/lib/plot/ScatterPlot3DScene.svelte +912 -0
- package/dist/src/lib/plot/ScatterPlotControls.svelte +306 -0
- package/dist/src/lib/plot/ScatterPoint.svelte +182 -0
- package/dist/src/lib/plot/SpacegroupBarPlot.svelte +293 -0
- package/dist/src/lib/plot/Surface3D.svelte +197 -0
- package/dist/src/lib/plot/ZeroLines.svelte +97 -0
- package/dist/src/lib/plot/ZoomRect.svelte +23 -0
- package/dist/src/lib/plot/adaptive-density.ts +316 -0
- package/dist/src/lib/plot/auto-place.ts +184 -0
- package/dist/src/lib/plot/axis-utils.ts +122 -0
- package/dist/src/lib/plot/binned-scatter-types.ts +83 -0
- package/dist/src/lib/plot/data-cleaning.ts +1069 -0
- package/dist/src/lib/plot/data-transform.ts +69 -0
- package/dist/src/lib/plot/defaults.ts +9 -0
- package/dist/src/lib/plot/fill-utils.ts +494 -0
- package/dist/src/lib/plot/hover-lock.svelte.ts +60 -0
- package/dist/src/lib/plot/index.ts +53 -0
- package/dist/src/lib/plot/interactions.ts +119 -0
- package/dist/src/lib/plot/layout.ts +425 -0
- package/dist/src/lib/plot/reference-line.ts +426 -0
- package/dist/src/lib/plot/scales.ts +654 -0
- package/dist/src/lib/plot/svg.ts +23 -0
- package/dist/src/lib/plot/types.ts +1144 -0
- package/dist/src/lib/plot/utils/label-placement.ts +541 -0
- package/dist/src/lib/plot/utils/series-visibility.ts +140 -0
- package/dist/src/lib/plot/utils.ts +11 -0
- package/dist/src/lib/rdf/RdfPlot.svelte +247 -0
- package/dist/src/lib/rdf/calc-rdf.ts +167 -0
- package/dist/src/lib/rdf/index.ts +27 -0
- package/dist/src/lib/sanitize.ts +126 -0
- package/dist/src/lib/settings.ts +1479 -0
- package/dist/src/lib/spectral/Bands.svelte +1040 -0
- package/dist/src/lib/spectral/BandsAndDos.svelte +134 -0
- package/dist/src/lib/spectral/BrillouinBandsDos.svelte +252 -0
- package/dist/src/lib/spectral/Dos.svelte +697 -0
- package/dist/src/lib/spectral/helpers.ts +1381 -0
- package/dist/src/lib/spectral/index.ts +8 -0
- package/dist/src/lib/spectral/types.ts +112 -0
- package/dist/src/lib/state.svelte.ts +64 -0
- package/dist/src/lib/structure/Arrow.svelte +72 -0
- package/dist/src/lib/structure/AtomLegend.svelte +815 -0
- package/dist/src/lib/structure/Bond.svelte +140 -0
- package/dist/src/lib/structure/CanvasTooltip.svelte +33 -0
- package/dist/src/lib/structure/CellSelect.svelte +349 -0
- package/dist/src/lib/structure/Cylinder.svelte +45 -0
- package/dist/src/lib/structure/Lattice.svelte +196 -0
- package/dist/src/lib/structure/Structure.svelte +2248 -0
- package/dist/src/lib/structure/StructureControls.svelte +1273 -0
- package/dist/src/lib/structure/StructureExportPane.svelte +252 -0
- package/dist/src/lib/structure/StructureInfoPane.svelte +737 -0
- package/dist/src/lib/structure/StructureScene.svelte +2255 -0
- package/dist/src/lib/structure/atom-properties.ts +316 -0
- package/dist/src/lib/structure/bond-order-perception.ts +447 -0
- package/dist/src/lib/structure/bonding.ts +944 -0
- package/dist/src/lib/structure/export.ts +861 -0
- package/dist/src/lib/structure/index.ts +291 -0
- package/dist/src/lib/structure/label-placement.ts +130 -0
- package/dist/src/lib/structure/measure.ts +45 -0
- package/dist/src/lib/structure/parse.ts +1705 -0
- package/dist/src/lib/structure/partial-occupancy.ts +183 -0
- package/dist/src/lib/structure/pbc.ts +164 -0
- package/dist/src/lib/structure/supercell.ts +226 -0
- package/dist/src/lib/structure/validation.ts +11 -0
- package/dist/src/lib/symmetry/SymmetryStats.svelte +226 -0
- package/dist/src/lib/symmetry/WyckoffTable.svelte +120 -0
- package/dist/src/lib/symmetry/cell-transform.ts +118 -0
- package/dist/src/lib/symmetry/index.ts +348 -0
- package/dist/src/lib/symmetry/spacegroups.ts +404 -0
- package/dist/src/lib/table/HeatmapTable.svelte +1833 -0
- package/dist/src/lib/table/ToggleMenu.svelte +385 -0
- package/dist/src/lib/table/index.ts +139 -0
- package/dist/src/lib/theme/ThemeControl.svelte +53 -0
- package/dist/src/lib/theme/index.ts +107 -0
- package/dist/src/lib/theme/themes.mjs +297 -0
- package/dist/src/lib/time.ts +71 -0
- package/dist/src/lib/tooltip/TooltipContent.svelte +58 -0
- package/dist/src/lib/tooltip/index.ts +2 -0
- package/dist/src/lib/tooltip/types.ts +13 -0
- package/dist/src/lib/trajectory/Trajectory.svelte +1545 -0
- package/dist/src/lib/trajectory/TrajectoryError.svelte +128 -0
- package/dist/src/lib/trajectory/TrajectoryExportPane.svelte +357 -0
- package/dist/src/lib/trajectory/TrajectoryInfoPane.svelte +313 -0
- package/dist/src/lib/trajectory/constants.ts +7 -0
- package/dist/src/lib/trajectory/extract.ts +196 -0
- package/dist/src/lib/trajectory/format-detect.ts +96 -0
- package/dist/src/lib/trajectory/frame-reader.ts +456 -0
- package/dist/src/lib/trajectory/helpers.ts +217 -0
- package/dist/src/lib/trajectory/index.ts +218 -0
- package/dist/src/lib/trajectory/parse/ase.ts +109 -0
- package/dist/src/lib/trajectory/parse/hdf5.ts +173 -0
- package/dist/src/lib/trajectory/parse/index.ts +411 -0
- package/dist/src/lib/trajectory/parse/lammps.ts +215 -0
- package/dist/src/lib/trajectory/parse/vasp.ts +102 -0
- package/dist/src/lib/trajectory/parse/xyz.ts +143 -0
- package/dist/src/lib/trajectory/plotting.ts +599 -0
- package/dist/src/lib/trajectory/types.ts +13 -0
- package/dist/src/lib/utils.ts +56 -0
- package/dist/src/lib/xrd/XrdPlot.svelte +615 -0
- package/dist/src/lib/xrd/broadening.ts +130 -0
- package/dist/src/lib/xrd/calc-xrd.ts +397 -0
- package/dist/src/lib/xrd/index.ts +38 -0
- package/dist/src/lib/xrd/parse.ts +858 -0
- package/dist/webview.js +29421 -0
- package/icon.png +0 -0
- package/license +1 -1
- package/matterviz-0.3.2.vsix +0 -0
- package/matterviz-0.3.4.vsix +0 -0
- package/matterviz-0.3.5.vsix +0 -0
- package/package.json +1461 -231
- package/readme.md +171 -98
- package/scripts/sync-config.ts +101 -0
- package/src/declarations.d.ts +2 -0
- package/src/extension.ts +972 -0
- package/src/node-io.ts +65 -0
- package/src/types.ts +17 -0
- package/src/webview/JsonBrowser.svelte +1079 -0
- package/src/webview/PlotPanel.svelte +346 -0
- package/src/webview/detect.ts +444 -0
- package/src/webview/main.ts +764 -0
- package/src/webview/plot-utils.ts +250 -0
- package/test-fixtures/all-viz-types.json.gz +0 -0
- package/test-fixtures/plot-demo-data.json.gz +0 -0
- package/tests/detect.test.ts +604 -0
- package/tests/extension.test.ts +2041 -0
- package/tests/node-io.test.ts +39 -0
- package/tests/plot-utils.test.ts +302 -0
- package/tests/vite-plugin-json-gz.test.ts +114 -0
- package/tests/vscode-mock.ts +18 -0
- package/tests/webview.test.ts +231 -0
- package/tsconfig.json +20 -0
- package/vite-plugin-json-gz.ts +29 -0
- package/vite.config.ts +34 -0
- package/vite.extension.config.ts +34 -0
- package/dist/EmptyState.svelte.d.ts +0 -9
- package/dist/FilePicker.svelte +0 -360
- package/dist/FilePicker.svelte.d.ts +0 -17
- package/dist/Icon.svelte.d.ts +0 -13
- package/dist/MillerIndexInput.svelte +0 -66
- package/dist/MillerIndexInput.svelte.d.ts +0 -7
- package/dist/api/mp.d.ts +0 -6
- package/dist/api/mp.js +0 -22
- package/dist/api/optimade.d.ts +0 -45
- package/dist/api/optimade.js +0 -135
- package/dist/app.css +0 -240
- package/dist/brillouin/BrillouinZone.svelte +0 -543
- package/dist/brillouin/BrillouinZone.svelte.d.ts +0 -83
- package/dist/brillouin/BrillouinZoneControls.svelte +0 -144
- package/dist/brillouin/BrillouinZoneControls.svelte.d.ts +0 -17
- package/dist/brillouin/BrillouinZoneExportPane.svelte +0 -148
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +0 -15
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +0 -146
- package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +0 -13
- package/dist/brillouin/BrillouinZoneScene.svelte +0 -476
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +0 -48
- package/dist/brillouin/BrillouinZoneTooltip.svelte +0 -92
- package/dist/brillouin/BrillouinZoneTooltip.svelte.d.ts +0 -8
- package/dist/brillouin/compute.d.ts +0 -17
- package/dist/brillouin/compute.js +0 -422
- package/dist/brillouin/index.d.ts +0 -8
- package/dist/brillouin/index.js +0 -8
- package/dist/brillouin/types.d.ts +0 -48
- package/dist/brillouin/types.js +0 -1
- package/dist/chempot-diagram/ChemPotDiagram.svelte +0 -327
- package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +0 -13
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +0 -847
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +0 -16
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +0 -3194
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +0 -16
- package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +0 -7
- package/dist/chempot-diagram/async-compute.svelte.d.ts +0 -3
- package/dist/chempot-diagram/async-compute.svelte.js +0 -77
- package/dist/chempot-diagram/chempot-worker.d.ts +0 -1
- package/dist/chempot-diagram/chempot-worker.js +0 -11
- package/dist/chempot-diagram/color.d.ts +0 -10
- package/dist/chempot-diagram/color.js +0 -32
- package/dist/chempot-diagram/compute.d.ts +0 -48
- package/dist/chempot-diagram/compute.js +0 -812
- package/dist/chempot-diagram/index.d.ts +0 -6
- package/dist/chempot-diagram/index.js +0 -6
- package/dist/chempot-diagram/pointer.d.ts +0 -16
- package/dist/chempot-diagram/pointer.js +0 -40
- package/dist/chempot-diagram/temperature.d.ts +0 -15
- package/dist/chempot-diagram/temperature.js +0 -36
- package/dist/chempot-diagram/types.d.ts +0 -86
- package/dist/chempot-diagram/types.js +0 -28
- package/dist/colors/index.d.ts +0 -47
- package/dist/colors/index.js +0 -203
- package/dist/composition/BarChart.svelte +0 -297
- package/dist/composition/BarChart.svelte.d.ts +0 -39
- package/dist/composition/BubbleChart.svelte +0 -218
- package/dist/composition/BubbleChart.svelte.d.ts +0 -28
- package/dist/composition/Composition.svelte +0 -164
- package/dist/composition/Composition.svelte.d.ts +0 -15
- package/dist/composition/Formula.svelte +0 -265
- package/dist/composition/Formula.svelte.d.ts +0 -19
- package/dist/composition/FormulaFilter.svelte +0 -1259
- package/dist/composition/FormulaFilter.svelte.d.ts +0 -51
- package/dist/composition/PieChart.svelte +0 -323
- package/dist/composition/PieChart.svelte.d.ts +0 -37
- package/dist/composition/format.d.ts +0 -15
- package/dist/composition/format.js +0 -109
- package/dist/composition/index.d.ts +0 -20
- package/dist/composition/index.js +0 -14
- package/dist/composition/parse.d.ts +0 -55
- package/dist/composition/parse.js +0 -459
- package/dist/constants.d.ts +0 -29
- package/dist/constants.js +0 -105
- package/dist/controls.d.ts +0 -14
- package/dist/controls.js +0 -30
- package/dist/convex-hull/ConvexHull.svelte +0 -157
- package/dist/convex-hull/ConvexHull.svelte.d.ts +0 -13
- package/dist/convex-hull/ConvexHull2D.svelte +0 -813
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +0 -11
- package/dist/convex-hull/ConvexHull3D.svelte +0 -1788
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +0 -8
- package/dist/convex-hull/ConvexHull4D.svelte +0 -1374
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +0 -8
- package/dist/convex-hull/ConvexHullControls.svelte +0 -546
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +0 -48
- package/dist/convex-hull/ConvexHullInfoPane.svelte +0 -115
- package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +0 -18
- package/dist/convex-hull/ConvexHullStats.svelte +0 -905
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +0 -15
- package/dist/convex-hull/ConvexHullTooltip.svelte +0 -131
- package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +0 -33
- package/dist/convex-hull/GasPressureControls.svelte +0 -247
- package/dist/convex-hull/GasPressureControls.svelte.d.ts +0 -11
- package/dist/convex-hull/StructurePopup.svelte +0 -116
- package/dist/convex-hull/StructurePopup.svelte.d.ts +0 -18
- package/dist/convex-hull/TemperatureSlider.svelte +0 -137
- package/dist/convex-hull/TemperatureSlider.svelte.d.ts +0 -8
- package/dist/convex-hull/barycentric-coords.d.ts +0 -18
- package/dist/convex-hull/barycentric-coords.js +0 -182
- package/dist/convex-hull/demo-temperature.d.ts +0 -6
- package/dist/convex-hull/demo-temperature.js +0 -40
- package/dist/convex-hull/gas-thermodynamics.d.ts +0 -16
- package/dist/convex-hull/gas-thermodynamics.js +0 -316
- package/dist/convex-hull/helpers.d.ts +0 -103
- package/dist/convex-hull/helpers.js +0 -671
- package/dist/convex-hull/index.d.ts +0 -118
- package/dist/convex-hull/index.js +0 -57
- package/dist/convex-hull/thermodynamics.d.ts +0 -66
- package/dist/convex-hull/thermodynamics.js +0 -1752
- package/dist/convex-hull/types.d.ts +0 -162
- package/dist/convex-hull/types.js +0 -36
- package/dist/coordination/CoordinationBarPlot.svelte +0 -311
- package/dist/coordination/CoordinationBarPlot.svelte.d.ts +0 -30
- package/dist/coordination/calc-coordination.d.ts +0 -15
- package/dist/coordination/calc-coordination.js +0 -63
- package/dist/coordination/index.d.ts +0 -8
- package/dist/coordination/index.js +0 -7
- package/dist/element/BohrAtom.svelte +0 -149
- package/dist/element/BohrAtom.svelte.d.ts +0 -20
- package/dist/element/ElementHeading.svelte +0 -26
- package/dist/element/ElementHeading.svelte.d.ts +0 -8
- package/dist/element/ElementPhoto.svelte +0 -57
- package/dist/element/ElementPhoto.svelte.d.ts +0 -9
- package/dist/element/ElementStats.svelte +0 -80
- package/dist/element/ElementStats.svelte.d.ts +0 -8
- package/dist/element/ElementTile.svelte +0 -484
- package/dist/element/ElementTile.svelte.d.ts +0 -29
- package/dist/element/Nucleus.svelte.d.ts +0 -17
- package/dist/element/data.d.ts +0 -3
- package/dist/element/data.js +0 -2
- package/dist/element/data.json.gz.d.ts +0 -2
- package/dist/element/index.d.ts +0 -8
- package/dist/element/index.js +0 -8
- package/dist/element/types.d.ts +0 -57
- package/dist/element/types.js +0 -1
- package/dist/feedback/ClickFeedback.svelte +0 -58
- package/dist/feedback/ClickFeedback.svelte.d.ts +0 -12
- package/dist/feedback/DragOverlay.svelte +0 -42
- package/dist/feedback/DragOverlay.svelte.d.ts +0 -7
- package/dist/feedback/Spinner.svelte.d.ts +0 -7
- package/dist/feedback/StatusMessage.svelte.d.ts +0 -9
- package/dist/feedback/index.d.ts +0 -4
- package/dist/feedback/index.js +0 -4
- package/dist/fermi-surface/FermiSlice.svelte +0 -189
- package/dist/fermi-surface/FermiSlice.svelte.d.ts +0 -24
- package/dist/fermi-surface/FermiSurface.svelte +0 -597
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +0 -83
- package/dist/fermi-surface/FermiSurfaceControls.svelte +0 -452
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +0 -35
- package/dist/fermi-surface/FermiSurfaceScene.svelte +0 -792
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +0 -50
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +0 -111
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte.d.ts +0 -8
- package/dist/fermi-surface/compute.d.ts +0 -5
- package/dist/fermi-surface/compute.js +0 -538
- package/dist/fermi-surface/constants.d.ts +0 -9
- package/dist/fermi-surface/constants.js +0 -27
- package/dist/fermi-surface/export.d.ts +0 -5
- package/dist/fermi-surface/export.js +0 -63
- package/dist/fermi-surface/index.d.ts +0 -12
- package/dist/fermi-surface/index.js +0 -13
- package/dist/fermi-surface/marching-cubes.d.ts +0 -2
- package/dist/fermi-surface/marching-cubes.js +0 -2
- package/dist/fermi-surface/parse.d.ts +0 -2
- package/dist/fermi-surface/parse.js +0 -495
- package/dist/fermi-surface/symmetry.d.ts +0 -3
- package/dist/fermi-surface/symmetry.js +0 -46
- package/dist/fermi-surface/types.d.ts +0 -113
- package/dist/fermi-surface/types.js +0 -4
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +0 -1527
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +0 -110
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +0 -225
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +0 -30
- package/dist/heatmap-matrix/index.d.ts +0 -53
- package/dist/heatmap-matrix/index.js +0 -100
- package/dist/heatmap-matrix/shared.d.ts +0 -2
- package/dist/heatmap-matrix/shared.js +0 -4
- package/dist/icons.d.ts +0 -569
- package/dist/icons.js +0 -648
- package/dist/index.d.ts +0 -39
- package/dist/index.js +0 -39
- package/dist/io/decompress.d.ts +0 -10
- package/dist/io/decompress.js +0 -69
- package/dist/io/export.d.ts +0 -16
- package/dist/io/export.js +0 -312
- package/dist/io/fetch.d.ts +0 -5
- package/dist/io/fetch.js +0 -39
- package/dist/io/file-drop.d.ts +0 -7
- package/dist/io/file-drop.js +0 -43
- package/dist/io/index.d.ts +0 -7
- package/dist/io/index.js +0 -7
- package/dist/io/is-binary.d.ts +0 -1
- package/dist/io/is-binary.js +0 -5
- package/dist/io/types.d.ts +0 -8
- package/dist/io/types.js +0 -1
- package/dist/io/url-drop.d.ts +0 -2
- package/dist/io/url-drop.js +0 -117
- package/dist/isosurface/Isosurface.svelte +0 -285
- package/dist/isosurface/Isosurface.svelte.d.ts +0 -8
- package/dist/isosurface/IsosurfaceControls.svelte +0 -291
- package/dist/isosurface/IsosurfaceControls.svelte.d.ts +0 -9
- package/dist/isosurface/index.d.ts +0 -5
- package/dist/isosurface/index.js +0 -6
- package/dist/isosurface/parse.d.ts +0 -6
- package/dist/isosurface/parse.js +0 -553
- package/dist/isosurface/slice.d.ts +0 -11
- package/dist/isosurface/slice.js +0 -140
- package/dist/isosurface/types.d.ts +0 -56
- package/dist/isosurface/types.js +0 -227
- package/dist/labels.d.ts +0 -53
- package/dist/labels.js +0 -274
- package/dist/layout/FullscreenToggle.svelte +0 -50
- package/dist/layout/FullscreenToggle.svelte.d.ts +0 -7
- package/dist/layout/InfoCard.svelte +0 -120
- package/dist/layout/InfoCard.svelte.d.ts +0 -21
- package/dist/layout/InfoTag.svelte +0 -183
- package/dist/layout/InfoTag.svelte.d.ts +0 -19
- package/dist/layout/PropertyFilter.svelte +0 -244
- package/dist/layout/PropertyFilter.svelte.d.ts +0 -24
- package/dist/layout/SettingsSection.svelte +0 -148
- package/dist/layout/SettingsSection.svelte.d.ts +0 -17
- package/dist/layout/SubpageGrid.svelte +0 -82
- package/dist/layout/SubpageGrid.svelte.d.ts +0 -14
- package/dist/layout/fullscreen.d.ts +0 -9
- package/dist/layout/fullscreen.js +0 -53
- package/dist/layout/index.d.ts +0 -10
- package/dist/layout/index.js +0 -8
- package/dist/layout/json-tree/JsonNode.svelte +0 -547
- package/dist/layout/json-tree/JsonNode.svelte.d.ts +0 -11
- package/dist/layout/json-tree/JsonTree.svelte +0 -1222
- package/dist/layout/json-tree/JsonTree.svelte.d.ts +0 -6
- package/dist/layout/json-tree/JsonValue.svelte +0 -334
- package/dist/layout/json-tree/JsonValue.svelte.d.ts +0 -9
- package/dist/layout/json-tree/index.d.ts +0 -3
- package/dist/layout/json-tree/index.js +0 -3
- package/dist/layout/json-tree/types.d.ts +0 -73
- package/dist/layout/json-tree/types.js +0 -3
- package/dist/layout/json-tree/utils.d.ts +0 -29
- package/dist/layout/json-tree/utils.js +0 -648
- package/dist/marching-cubes.d.ts +0 -14
- package/dist/marching-cubes.js +0 -542
- package/dist/math.d.ts +0 -91
- package/dist/math.js +0 -896
- package/dist/overlays/ContextMenu.svelte +0 -162
- package/dist/overlays/ContextMenu.svelte.d.ts +0 -25
- package/dist/overlays/DraggablePane.svelte +0 -564
- package/dist/overlays/DraggablePane.svelte.d.ts +0 -36
- package/dist/overlays/index.d.ts +0 -2
- package/dist/overlays/index.js +0 -2
- package/dist/periodic-table/PeriodicTable.svelte +0 -469
- package/dist/periodic-table/PeriodicTable.svelte.d.ts +0 -55
- package/dist/periodic-table/PeriodicTableControls.svelte +0 -557
- package/dist/periodic-table/PeriodicTableControls.svelte.d.ts +0 -24
- package/dist/periodic-table/PropertySelect.svelte +0 -37
- package/dist/periodic-table/PropertySelect.svelte.d.ts +0 -13
- package/dist/periodic-table/TableInset.svelte.d.ts +0 -9
- package/dist/periodic-table/index.d.ts +0 -10
- package/dist/periodic-table/index.js +0 -4
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +0 -1086
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +0 -44
- package/dist/phase-diagram/PhaseDiagramControls.svelte +0 -451
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +0 -30
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +0 -126
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +0 -15
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +0 -192
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +0 -19
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +0 -392
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +0 -16
- package/dist/phase-diagram/TdbInfoPanel.svelte +0 -203
- package/dist/phase-diagram/TdbInfoPanel.svelte.d.ts +0 -12
- package/dist/phase-diagram/build-diagram.d.ts +0 -11
- package/dist/phase-diagram/build-diagram.js +0 -167
- package/dist/phase-diagram/colors.d.ts +0 -35
- package/dist/phase-diagram/colors.js +0 -51
- package/dist/phase-diagram/diagram-input.d.ts +0 -33
- package/dist/phase-diagram/diagram-input.js +0 -3
- package/dist/phase-diagram/index.d.ts +0 -13
- package/dist/phase-diagram/index.js +0 -13
- package/dist/phase-diagram/parse.d.ts +0 -55
- package/dist/phase-diagram/parse.js +0 -276
- package/dist/phase-diagram/svg-to-diagram.d.ts +0 -2
- package/dist/phase-diagram/svg-to-diagram.js +0 -869
- package/dist/phase-diagram/types.d.ts +0 -99
- package/dist/phase-diagram/types.js +0 -1
- package/dist/phase-diagram/utils.d.ts +0 -118
- package/dist/phase-diagram/utils.js +0 -606
- package/dist/plot/AxisLabel.svelte +0 -51
- package/dist/plot/AxisLabel.svelte.d.ts +0 -16
- package/dist/plot/BarPlot.svelte +0 -2256
- package/dist/plot/BarPlot.svelte.d.ts +0 -82
- package/dist/plot/BarPlotControls.svelte +0 -66
- package/dist/plot/BarPlotControls.svelte.d.ts +0 -18
- package/dist/plot/ColorBar.svelte +0 -719
- package/dist/plot/ColorBar.svelte.d.ts +0 -31
- package/dist/plot/ColorScaleSelect.svelte +0 -54
- package/dist/plot/ColorScaleSelect.svelte.d.ts +0 -15
- package/dist/plot/ElementScatter.svelte +0 -63
- package/dist/plot/ElementScatter.svelte.d.ts +0 -14
- package/dist/plot/FillArea.svelte +0 -226
- package/dist/plot/FillArea.svelte.d.ts +0 -20
- package/dist/plot/Histogram.svelte +0 -1654
- package/dist/plot/Histogram.svelte.d.ts +0 -50
- package/dist/plot/HistogramControls.svelte +0 -212
- package/dist/plot/HistogramControls.svelte.d.ts +0 -22
- package/dist/plot/InteractiveAxisLabel.svelte +0 -94
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +0 -14
- package/dist/plot/Line.svelte +0 -85
- package/dist/plot/Line.svelte.d.ts +0 -15
- package/dist/plot/PlotControls.svelte +0 -537
- package/dist/plot/PlotControls.svelte.d.ts +0 -4
- package/dist/plot/PlotLegend.svelte +0 -498
- package/dist/plot/PlotLegend.svelte.d.ts +0 -25
- package/dist/plot/PlotTooltip.svelte +0 -67
- package/dist/plot/PlotTooltip.svelte.d.ts +0 -17
- package/dist/plot/PortalSelect.svelte +0 -253
- package/dist/plot/PortalSelect.svelte.d.ts +0 -16
- package/dist/plot/ReferenceLine.svelte.d.ts +0 -20
- package/dist/plot/ReferenceLine3D.svelte +0 -154
- package/dist/plot/ReferenceLine3D.svelte.d.ts +0 -14
- package/dist/plot/ReferencePlane.svelte +0 -178
- package/dist/plot/ReferencePlane.svelte.d.ts +0 -14
- package/dist/plot/ScatterPlot.svelte +0 -2831
- package/dist/plot/ScatterPlot.svelte.d.ts +0 -92
- package/dist/plot/ScatterPlot3D.svelte +0 -499
- package/dist/plot/ScatterPlot3D.svelte.d.ts +0 -94
- package/dist/plot/ScatterPlot3DControls.svelte +0 -437
- package/dist/plot/ScatterPlot3DControls.svelte.d.ts +0 -20
- package/dist/plot/ScatterPlot3DScene.svelte +0 -912
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +0 -74
- package/dist/plot/ScatterPlotControls.svelte +0 -307
- package/dist/plot/ScatterPlotControls.svelte.d.ts +0 -17
- package/dist/plot/ScatterPoint.svelte +0 -185
- package/dist/plot/ScatterPoint.svelte.d.ts +0 -19
- package/dist/plot/SpacegroupBarPlot.svelte +0 -292
- package/dist/plot/SpacegroupBarPlot.svelte.d.ts +0 -9
- package/dist/plot/Surface3D.svelte +0 -200
- package/dist/plot/Surface3D.svelte.d.ts +0 -13
- package/dist/plot/ZeroLines.svelte +0 -96
- package/dist/plot/ZeroLines.svelte.d.ts +0 -32
- package/dist/plot/ZoomRect.svelte +0 -23
- package/dist/plot/ZoomRect.svelte.d.ts +0 -8
- package/dist/plot/axis-utils.d.ts +0 -19
- package/dist/plot/axis-utils.js +0 -80
- package/dist/plot/data-cleaning.d.ts +0 -37
- package/dist/plot/data-cleaning.js +0 -855
- package/dist/plot/data-transform.d.ts +0 -16
- package/dist/plot/data-transform.js +0 -45
- package/dist/plot/defaults.d.ts +0 -19
- package/dist/plot/defaults.js +0 -9
- package/dist/plot/fill-utils.d.ts +0 -51
- package/dist/plot/fill-utils.js +0 -337
- package/dist/plot/hover-lock.svelte.d.ts +0 -14
- package/dist/plot/hover-lock.svelte.js +0 -46
- package/dist/plot/index.d.ts +0 -43
- package/dist/plot/index.js +0 -37
- package/dist/plot/interactions.d.ts +0 -12
- package/dist/plot/interactions.js +0 -100
- package/dist/plot/layout.d.ts +0 -60
- package/dist/plot/layout.js +0 -230
- package/dist/plot/reference-line.d.ts +0 -60
- package/dist/plot/reference-line.js +0 -316
- package/dist/plot/scales.d.ts +0 -48
- package/dist/plot/scales.js +0 -484
- package/dist/plot/svg.d.ts +0 -1
- package/dist/plot/svg.js +0 -11
- package/dist/plot/types.d.ts +0 -863
- package/dist/plot/types.js +0 -103
- package/dist/plot/utils/label-placement.d.ts +0 -47
- package/dist/plot/utils/label-placement.js +0 -256
- package/dist/plot/utils/series-visibility.d.ts +0 -9
- package/dist/plot/utils/series-visibility.js +0 -67
- package/dist/plot/utils.d.ts +0 -1
- package/dist/plot/utils.js +0 -14
- package/dist/rdf/RdfPlot.svelte +0 -247
- package/dist/rdf/RdfPlot.svelte.d.ts +0 -27
- package/dist/rdf/calc-rdf.d.ts +0 -4
- package/dist/rdf/calc-rdf.js +0 -111
- package/dist/rdf/index.d.ts +0 -23
- package/dist/rdf/index.js +0 -2
- package/dist/sanitize.d.ts +0 -4
- package/dist/sanitize.js +0 -107
- package/dist/settings.d.ts +0 -253
- package/dist/settings.js +0 -1123
- package/dist/spectral/Bands.svelte +0 -1040
- package/dist/spectral/Bands.svelte.d.ts +0 -40
- package/dist/spectral/BandsAndDos.svelte +0 -128
- package/dist/spectral/BandsAndDos.svelte.d.ts +0 -18
- package/dist/spectral/BrillouinBandsDos.svelte +0 -248
- package/dist/spectral/BrillouinBandsDos.svelte.d.ts +0 -20
- package/dist/spectral/Dos.svelte +0 -697
- package/dist/spectral/Dos.svelte.d.ts +0 -29
- package/dist/spectral/helpers.d.ts +0 -117
- package/dist/spectral/helpers.js +0 -1023
- package/dist/spectral/index.d.ts +0 -6
- package/dist/spectral/index.js +0 -7
- package/dist/spectral/types.d.ts +0 -84
- package/dist/spectral/types.js +0 -2
- package/dist/state.svelte.d.ts +0 -25
- package/dist/state.svelte.js +0 -45
- package/dist/structure/Arrow.svelte +0 -72
- package/dist/structure/Arrow.svelte.d.ts +0 -15
- package/dist/structure/AtomLegend.svelte +0 -798
- package/dist/structure/AtomLegend.svelte.d.ts +0 -34
- package/dist/structure/Bond.svelte +0 -140
- package/dist/structure/Bond.svelte.d.ts +0 -9
- package/dist/structure/CanvasTooltip.svelte +0 -33
- package/dist/structure/CanvasTooltip.svelte.d.ts +0 -12
- package/dist/structure/CellSelect.svelte +0 -351
- package/dist/structure/CellSelect.svelte.d.ts +0 -13
- package/dist/structure/Cylinder.svelte +0 -45
- package/dist/structure/Cylinder.svelte.d.ts +0 -10
- package/dist/structure/Lattice.svelte +0 -196
- package/dist/structure/Lattice.svelte.d.ts +0 -17
- package/dist/structure/Structure.svelte +0 -1857
- package/dist/structure/Structure.svelte.d.ts +0 -83
- package/dist/structure/StructureControls.svelte +0 -1184
- package/dist/structure/StructureControls.svelte.d.ts +0 -31
- package/dist/structure/StructureExportPane.svelte +0 -251
- package/dist/structure/StructureExportPane.svelte.d.ts +0 -17
- package/dist/structure/StructureInfoPane.svelte +0 -434
- package/dist/structure/StructureInfoPane.svelte.d.ts +0 -18
- package/dist/structure/StructureScene.svelte +0 -1574
- package/dist/structure/StructureScene.svelte.d.ts +0 -104
- package/dist/structure/atom-properties.d.ts +0 -37
- package/dist/structure/atom-properties.js +0 -198
- package/dist/structure/bonding.d.ts +0 -33
- package/dist/structure/bonding.js +0 -304
- package/dist/structure/export.d.ts +0 -20
- package/dist/structure/export.js +0 -725
- package/dist/structure/ferrox-wasm-types.d.ts +0 -46
- package/dist/structure/ferrox-wasm-types.js +0 -18
- package/dist/structure/ferrox-wasm.d.ts +0 -94
- package/dist/structure/ferrox-wasm.js +0 -249
- package/dist/structure/index.d.ts +0 -110
- package/dist/structure/index.js +0 -168
- package/dist/structure/measure.d.ts +0 -6
- package/dist/structure/measure.js +0 -29
- package/dist/structure/parse.d.ts +0 -65
- package/dist/structure/parse.js +0 -1374
- package/dist/structure/partial-occupancy.d.ts +0 -25
- package/dist/structure/partial-occupancy.js +0 -99
- package/dist/structure/pbc.d.ts +0 -9
- package/dist/structure/pbc.js +0 -123
- package/dist/structure/supercell.d.ts +0 -8
- package/dist/structure/supercell.js +0 -137
- package/dist/structure/validation.d.ts +0 -2
- package/dist/structure/validation.js +0 -10
- package/dist/symmetry/SymmetryStats.svelte +0 -226
- package/dist/symmetry/SymmetryStats.svelte.d.ts +0 -21
- package/dist/symmetry/WyckoffTable.svelte +0 -113
- package/dist/symmetry/WyckoffTable.svelte.d.ts +0 -11
- package/dist/symmetry/cell-transform.d.ts +0 -12
- package/dist/symmetry/cell-transform.js +0 -77
- package/dist/symmetry/index.d.ts +0 -43
- package/dist/symmetry/index.js +0 -229
- package/dist/symmetry/spacegroups.d.ts +0 -9
- package/dist/symmetry/spacegroups.js +0 -394
- package/dist/table/HeatmapTable.svelte +0 -1854
- package/dist/table/HeatmapTable.svelte.d.ts +0 -49
- package/dist/table/ToggleMenu.svelte +0 -376
- package/dist/table/ToggleMenu.svelte.d.ts +0 -11
- package/dist/table/index.d.ts +0 -74
- package/dist/table/index.js +0 -38
- package/dist/theme/ThemeControl.svelte +0 -53
- package/dist/theme/ThemeControl.svelte.d.ts +0 -9
- package/dist/theme/index.d.ts +0 -29
- package/dist/theme/index.js +0 -79
- package/dist/theme/themes.mjs +0 -285
- package/dist/time.d.ts +0 -4
- package/dist/time.js +0 -70
- package/dist/tooltip/TooltipContent.svelte +0 -58
- package/dist/tooltip/TooltipContent.svelte.d.ts +0 -31
- package/dist/tooltip/index.d.ts +0 -2
- package/dist/tooltip/index.js +0 -2
- package/dist/tooltip/types.d.ts +0 -8
- package/dist/tooltip/types.js +0 -1
- package/dist/trajectory/Trajectory.svelte +0 -1517
- package/dist/trajectory/Trajectory.svelte.d.ts +0 -77
- package/dist/trajectory/TrajectoryError.svelte +0 -128
- package/dist/trajectory/TrajectoryError.svelte.d.ts +0 -13
- package/dist/trajectory/TrajectoryExportPane.svelte +0 -357
- package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +0 -17
- package/dist/trajectory/TrajectoryInfoPane.svelte +0 -387
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +0 -17
- package/dist/trajectory/constants.d.ts +0 -6
- package/dist/trajectory/constants.js +0 -7
- package/dist/trajectory/extract.d.ts +0 -5
- package/dist/trajectory/extract.js +0 -162
- package/dist/trajectory/format-detect.d.ts +0 -9
- package/dist/trajectory/format-detect.js +0 -76
- package/dist/trajectory/frame-reader.d.ts +0 -17
- package/dist/trajectory/frame-reader.js +0 -332
- package/dist/trajectory/helpers.d.ts +0 -14
- package/dist/trajectory/helpers.js +0 -172
- package/dist/trajectory/index.d.ts +0 -63
- package/dist/trajectory/index.js +0 -126
- package/dist/trajectory/parse/ase.d.ts +0 -2
- package/dist/trajectory/parse/ase.js +0 -77
- package/dist/trajectory/parse/hdf5.d.ts +0 -2
- package/dist/trajectory/parse/hdf5.js +0 -129
- package/dist/trajectory/parse/index.d.ts +0 -12
- package/dist/trajectory/parse/index.js +0 -299
- package/dist/trajectory/parse/lammps.d.ts +0 -5
- package/dist/trajectory/parse/lammps.js +0 -179
- package/dist/trajectory/parse/vasp.d.ts +0 -2
- package/dist/trajectory/parse/vasp.js +0 -68
- package/dist/trajectory/parse/xyz.d.ts +0 -2
- package/dist/trajectory/parse/xyz.js +0 -110
- package/dist/trajectory/plotting.d.ts +0 -28
- package/dist/trajectory/plotting.js +0 -423
- package/dist/trajectory/types.d.ts +0 -11
- package/dist/trajectory/types.js +0 -1
- package/dist/utils.d.ts +0 -5
- package/dist/utils.js +0 -36
- package/dist/xrd/XrdPlot.svelte +0 -615
- package/dist/xrd/XrdPlot.svelte.d.ts +0 -28
- package/dist/xrd/broadening.d.ts +0 -20
- package/dist/xrd/broadening.js +0 -97
- package/dist/xrd/calc-xrd.d.ts +0 -37
- package/dist/xrd/calc-xrd.js +0 -337
- package/dist/xrd/index.d.ts +0 -37
- package/dist/xrd/index.js +0 -4
- package/dist/xrd/parse.d.ts +0 -13
- package/dist/xrd/parse.js +0 -749
- /package/dist/{EmptyState.svelte → src/lib/EmptyState.svelte} +0 -0
- /package/dist/{Icon.svelte → src/lib/Icon.svelte} +0 -0
- /package/dist/{chempot-diagram → src/lib/chempot-diagram}/ChemPotScene3D.svelte +0 -0
- /package/dist/{colors → src/lib/colors}/alloy-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/dark-mode-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/jmol-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/muted-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/pastel-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/vesta-colors.json +0 -0
- /package/dist/{element → src/lib/element}/Nucleus.svelte +0 -0
- /package/dist/{element → src/lib/element}/data.json +0 -0
- /package/dist/{element → src/lib/element}/data.json.gz +0 -0
- /package/dist/{element → src/lib/element}/data.schema.json +0 -0
- /package/dist/{element-image-urls.json → src/lib/element-image-urls.json} +0 -0
- /package/dist/{feedback → src/lib/feedback}/Spinner.svelte +0 -0
- /package/dist/{feedback → src/lib/feedback}/StatusMessage.svelte +0 -0
- /package/dist/{periodic-table → src/lib/periodic-table}/TableInset.svelte +0 -0
- /package/dist/{plot → src/lib/plot}/ReferenceLine.svelte +0 -0
- /package/dist/{xrd → src/lib/xrd}/atomic_scattering_params.json +0 -0
|
@@ -0,0 +1,1801 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { D3InterpolateName } from '$lib/colors'
|
|
3
|
+
import {
|
|
4
|
+
add_alpha,
|
|
5
|
+
AXIS_COLORS,
|
|
6
|
+
is_dark_mode,
|
|
7
|
+
NEG_AXIS_COLORS,
|
|
8
|
+
PLOT_COLORS,
|
|
9
|
+
vesta_hex,
|
|
10
|
+
watch_dark_mode,
|
|
11
|
+
} from '$lib/colors'
|
|
12
|
+
import { get_formula_label_segments } from '$lib/composition/format'
|
|
13
|
+
import type { FormulaLabelSegment } from '$lib/composition/format'
|
|
14
|
+
import { normalize_show_controls } from '$lib/controls'
|
|
15
|
+
import { sanitize_html } from '$lib/sanitize'
|
|
16
|
+
import { ClickFeedback, DragOverlay, Spinner } from '$lib/feedback'
|
|
17
|
+
import Icon from '$lib/Icon.svelte'
|
|
18
|
+
import { format_num } from '$lib/labels'
|
|
19
|
+
import {
|
|
20
|
+
set_fullscreen_bg,
|
|
21
|
+
setup_fullscreen_effect,
|
|
22
|
+
toggle_fullscreen,
|
|
23
|
+
} from '$lib/layout'
|
|
24
|
+
import { to_radians, type Point3D, type Vec3 } from '$lib/math'
|
|
25
|
+
import { ColorBar, PlotTooltip } from '$lib/plot'
|
|
26
|
+
import { centered_rect, pad_rect, rects_overlap, rect_within_rect } from '$lib/plot/layout'
|
|
27
|
+
import type { Rect } from '$lib/plot/layout'
|
|
28
|
+
import { create_pulse_animation } from '$lib/effects.svelte'
|
|
29
|
+
import { DEFAULTS } from '$lib/settings'
|
|
30
|
+
import type { AnyStructure } from '$lib/structure'
|
|
31
|
+
import { Canvas, T } from '@threlte/core'
|
|
32
|
+
import * as extras from '@threlte/extras'
|
|
33
|
+
import { ticks } from 'd3-array'
|
|
34
|
+
import { PerspectiveCamera, WebGLRenderer } from 'three'
|
|
35
|
+
import type { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
|
36
|
+
import {
|
|
37
|
+
get_ternary_3d_coordinates,
|
|
38
|
+
get_triangle_centroid,
|
|
39
|
+
get_triangle_edges,
|
|
40
|
+
get_triangle_vertical_edges,
|
|
41
|
+
TRIANGLE_VERTICES,
|
|
42
|
+
} from './barycentric-coords'
|
|
43
|
+
import ConvexHullControls from './ConvexHullControls.svelte'
|
|
44
|
+
import ConvexHullInfoPane from './ConvexHullInfoPane.svelte'
|
|
45
|
+
import ConvexHullTooltip from './ConvexHullTooltip.svelte'
|
|
46
|
+
import GasPressureControls from './GasPressureControls.svelte'
|
|
47
|
+
import * as helpers from './helpers'
|
|
48
|
+
import type { BaseConvexHullProps, Hull3DProps } from './index'
|
|
49
|
+
import { CONVEX_HULL_STYLE, default_controls, default_hull_config } from './index'
|
|
50
|
+
import StructurePopup from './StructurePopup.svelte'
|
|
51
|
+
import TemperatureSlider from './TemperatureSlider.svelte'
|
|
52
|
+
import * as thermo from './thermodynamics'
|
|
53
|
+
import type {
|
|
54
|
+
ConvexHullEntry,
|
|
55
|
+
ConvexHullTriangle,
|
|
56
|
+
HighlightStyle,
|
|
57
|
+
HoverData3D,
|
|
58
|
+
HullFaceColorMode,
|
|
59
|
+
LabelPlacement,
|
|
60
|
+
} from './types'
|
|
61
|
+
import { compute_hull_stability } from './helpers'
|
|
62
|
+
|
|
63
|
+
let {
|
|
64
|
+
entries = [],
|
|
65
|
+
controls = {},
|
|
66
|
+
config = {},
|
|
67
|
+
on_point_click,
|
|
68
|
+
on_point_hover,
|
|
69
|
+
fullscreen = $bindable(DEFAULTS.convex_hull.ternary.fullscreen),
|
|
70
|
+
enable_fullscreen = true,
|
|
71
|
+
enable_info_pane = true,
|
|
72
|
+
wrapper = $bindable(),
|
|
73
|
+
label_threshold = 50,
|
|
74
|
+
show_stable = $bindable(DEFAULTS.convex_hull.ternary.show_stable),
|
|
75
|
+
show_unstable = $bindable(DEFAULTS.convex_hull.ternary.show_unstable),
|
|
76
|
+
show_hull_faces = $bindable(DEFAULTS.convex_hull.ternary.show_hull_faces),
|
|
77
|
+
hull_face_opacity = $bindable(DEFAULTS.convex_hull.ternary.hull_face_opacity),
|
|
78
|
+
hull_face_color_mode = $bindable(
|
|
79
|
+
DEFAULTS.convex_hull.ternary.hull_face_color_mode as HullFaceColorMode,
|
|
80
|
+
),
|
|
81
|
+
element_colors = vesta_hex,
|
|
82
|
+
color_mode = $bindable(DEFAULTS.convex_hull.ternary.color_mode),
|
|
83
|
+
color_scale = $bindable(
|
|
84
|
+
DEFAULTS.convex_hull.ternary.color_scale as D3InterpolateName,
|
|
85
|
+
),
|
|
86
|
+
info_pane_open = $bindable(DEFAULTS.convex_hull.ternary.info_pane_open),
|
|
87
|
+
legend_pane_open = $bindable(DEFAULTS.convex_hull.ternary.legend_pane_open),
|
|
88
|
+
max_hull_dist_show_phases = $bindable(
|
|
89
|
+
DEFAULTS.convex_hull.ternary.max_hull_dist_show_phases,
|
|
90
|
+
),
|
|
91
|
+
max_hull_dist_show_labels = $bindable(
|
|
92
|
+
DEFAULTS.convex_hull.ternary.max_hull_dist_show_labels,
|
|
93
|
+
),
|
|
94
|
+
show_stable_labels = $bindable(DEFAULTS.convex_hull.ternary.show_stable_labels),
|
|
95
|
+
show_unstable_labels = $bindable(
|
|
96
|
+
DEFAULTS.convex_hull.ternary.show_unstable_labels,
|
|
97
|
+
),
|
|
98
|
+
on_file_drop,
|
|
99
|
+
enable_click_selection = true,
|
|
100
|
+
enable_structure_preview = true,
|
|
101
|
+
energy_source_mode = $bindable(`precomputed`),
|
|
102
|
+
phase_stats = $bindable(null),
|
|
103
|
+
stable_entries = $bindable([]),
|
|
104
|
+
unstable_entries = $bindable([]),
|
|
105
|
+
highlighted_entries = $bindable([]),
|
|
106
|
+
highlight_style = {},
|
|
107
|
+
selected_entry = $bindable(null),
|
|
108
|
+
temperature = $bindable(),
|
|
109
|
+
interpolate_temperature = true,
|
|
110
|
+
max_interpolation_gap = 500,
|
|
111
|
+
gizmo = true,
|
|
112
|
+
gas_config,
|
|
113
|
+
gas_pressures = $bindable({}),
|
|
114
|
+
children,
|
|
115
|
+
tooltip,
|
|
116
|
+
...rest
|
|
117
|
+
}: BaseConvexHullProps<ConvexHullEntry> & Hull3DProps & {
|
|
118
|
+
highlight_style?: HighlightStyle
|
|
119
|
+
} = $props()
|
|
120
|
+
|
|
121
|
+
const merged_controls = $derived({ ...default_controls, ...controls })
|
|
122
|
+
const controls_config = $derived(normalize_show_controls(merged_controls.show))
|
|
123
|
+
const merged_config = $derived({
|
|
124
|
+
...default_hull_config,
|
|
125
|
+
...config,
|
|
126
|
+
colors: { ...default_hull_config.colors, ...config.colors },
|
|
127
|
+
margin: { t: 40, r: 40, b: 60, l: 60, ...config.margin },
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
// Temperature-dependent free energy support
|
|
131
|
+
const { has_temp_data, available_temperatures } = $derived(
|
|
132
|
+
helpers.analyze_temperature_data(entries),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
// Initialize or reset temperature when it's undefined or no longer valid
|
|
136
|
+
$effect(() => {
|
|
137
|
+
if (
|
|
138
|
+
has_temp_data &&
|
|
139
|
+
available_temperatures.length > 0 &&
|
|
140
|
+
(temperature === undefined || !available_temperatures.includes(temperature))
|
|
141
|
+
) temperature = available_temperatures[0]
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
// Filter entries by temperature when in temperature mode
|
|
145
|
+
const temp_filtered_entries = $derived(
|
|
146
|
+
has_temp_data && temperature !== undefined
|
|
147
|
+
? helpers.filter_entries_at_temperature(entries, temperature, {
|
|
148
|
+
interpolate: interpolate_temperature,
|
|
149
|
+
max_interpolation_gap,
|
|
150
|
+
})
|
|
151
|
+
: entries,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
// Gas-dependent chemical potential support (corrections based on T, P)
|
|
155
|
+
// Default to DEFAULT_GAS_TEMP (room temperature) when no temperature specified
|
|
156
|
+
const {
|
|
157
|
+
entries: gas_corrected_entries,
|
|
158
|
+
analysis: gas_analysis,
|
|
159
|
+
merged_config: merged_gas_config,
|
|
160
|
+
} = $derived(
|
|
161
|
+
helpers.get_gas_corrected_entries(
|
|
162
|
+
temp_filtered_entries,
|
|
163
|
+
gas_config,
|
|
164
|
+
gas_pressures,
|
|
165
|
+
temperature ?? helpers.DEFAULT_GAS_TEMP,
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
let { // Compute energy mode information
|
|
170
|
+
has_precomputed_e_form,
|
|
171
|
+
has_precomputed_hull,
|
|
172
|
+
can_compute_e_form,
|
|
173
|
+
can_compute_hull,
|
|
174
|
+
energy_mode,
|
|
175
|
+
unary_refs,
|
|
176
|
+
} = $derived(
|
|
177
|
+
helpers.compute_energy_mode_info(
|
|
178
|
+
gas_corrected_entries,
|
|
179
|
+
thermo.find_lowest_energy_unary_refs,
|
|
180
|
+
energy_source_mode,
|
|
181
|
+
),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
const effective_entries = $derived(
|
|
185
|
+
helpers.get_effective_entries(
|
|
186
|
+
gas_corrected_entries,
|
|
187
|
+
energy_mode,
|
|
188
|
+
unary_refs,
|
|
189
|
+
thermo.compute_e_form_per_atom,
|
|
190
|
+
),
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
// Process convex hull data with unified PhaseData interface using effective entries
|
|
194
|
+
const pd_data = $derived(thermo.process_hull_entries(effective_entries))
|
|
195
|
+
|
|
196
|
+
// Pre-compute polymorph stats once for O(1) tooltip lookups
|
|
197
|
+
const polymorph_stats_map = $derived(
|
|
198
|
+
helpers.compute_all_polymorph_stats(effective_entries),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
const elements = $derived.by(() => {
|
|
202
|
+
if (pd_data.elements.length > 3) {
|
|
203
|
+
console.error(
|
|
204
|
+
`ConvexHull3D: Dataset contains ${pd_data.elements.length} elements, but ternary diagrams require exactly 3. Found: [${
|
|
205
|
+
pd_data.elements.join(`, `)
|
|
206
|
+
}]`,
|
|
207
|
+
)
|
|
208
|
+
return []
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return pd_data.elements
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
// 1) Raw 3D coordinates (formation-energy z), independent of hull state
|
|
215
|
+
const coords_entries = $derived.by(() => {
|
|
216
|
+
if (elements.length !== 3) return []
|
|
217
|
+
try {
|
|
218
|
+
// Pass precomputed el_refs to avoid recomputing in error diagnostics
|
|
219
|
+
const coords = get_ternary_3d_coordinates(
|
|
220
|
+
pd_data.entries,
|
|
221
|
+
elements,
|
|
222
|
+
pd_data.el_refs,
|
|
223
|
+
)
|
|
224
|
+
return coords
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error(`Error computing ternary coordinates:`, error)
|
|
227
|
+
return []
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
// Compute lower convex hull faces (triangles) for 3D rendering (low energy hull only)
|
|
232
|
+
// Must be defined before all_enriched_entries which uses hull_model
|
|
233
|
+
const hull_faces = $derived.by((): ConvexHullTriangle[] => {
|
|
234
|
+
if (coords_entries.length === 0) return []
|
|
235
|
+
// Excluded entries don't participate in hull construction
|
|
236
|
+
const hull_entries = coords_entries.filter((entry) => !entry.exclude_from_hull)
|
|
237
|
+
if (hull_entries.length === 0) return []
|
|
238
|
+
const points = hull_entries.map(({ x, y, z }) => ({ x, y, z }))
|
|
239
|
+
try {
|
|
240
|
+
return thermo.compute_lower_hull_triangles(points)
|
|
241
|
+
} catch (error) {
|
|
242
|
+
console.error(`Error computing convex hull:`, error)
|
|
243
|
+
return []
|
|
244
|
+
}
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
// Cached hull model for e_above_hull queries; recompute only when faces change
|
|
248
|
+
let hull_model = $derived.by(() => thermo.build_lower_hull_model(hull_faces))
|
|
249
|
+
|
|
250
|
+
// Enrich coords with e_above_hull from cached hull model (before filtering)
|
|
251
|
+
const all_enriched_entries = $derived.by(() => {
|
|
252
|
+
if (coords_entries.length === 0) return []
|
|
253
|
+
if (energy_mode !== `on-the-fly`) return coords_entries
|
|
254
|
+
const pts = coords_entries.map(({ x, y, z }) => ({ x, y, z }))
|
|
255
|
+
const raw_dists = thermo.compute_e_above_hull_for_points(pts, hull_model)
|
|
256
|
+
return coords_entries.map((entry, idx) => ({
|
|
257
|
+
...entry, ...compute_hull_stability(raw_dists[idx], entry.exclude_from_hull),
|
|
258
|
+
}))
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
// Auto threshold: show all for few entries, use default for many, interpolate between
|
|
262
|
+
const max_hull_dist_in_data = $derived(
|
|
263
|
+
helpers.calc_max_hull_dist_in_data(all_enriched_entries),
|
|
264
|
+
)
|
|
265
|
+
const auto_default_threshold = $derived(helpers.compute_auto_hull_dist_threshold(
|
|
266
|
+
all_enriched_entries.length,
|
|
267
|
+
max_hull_dist_in_data,
|
|
268
|
+
DEFAULTS.convex_hull.ternary.max_hull_dist_show_phases,
|
|
269
|
+
))
|
|
270
|
+
|
|
271
|
+
const next_auto_threshold = helpers.auto_threshold_reset(
|
|
272
|
+
DEFAULTS.convex_hull.ternary.max_hull_dist_show_phases,
|
|
273
|
+
)
|
|
274
|
+
$effect(() => {
|
|
275
|
+
max_hull_dist_show_phases = next_auto_threshold(
|
|
276
|
+
entries,
|
|
277
|
+
max_hull_dist_show_phases,
|
|
278
|
+
auto_default_threshold,
|
|
279
|
+
) ?? max_hull_dist_show_phases
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
// Filter by threshold; visibility is a view predicate, not entry state.
|
|
283
|
+
const plot_entries = $derived(
|
|
284
|
+
all_enriched_entries.filter((entry) =>
|
|
285
|
+
(entry.e_above_hull ?? 0) <= max_hull_dist_show_phases
|
|
286
|
+
),
|
|
287
|
+
)
|
|
288
|
+
const visible_entries = $derived(helpers.visible_entries(
|
|
289
|
+
plot_entries,
|
|
290
|
+
show_stable,
|
|
291
|
+
show_unstable,
|
|
292
|
+
))
|
|
293
|
+
|
|
294
|
+
$effect(() => {
|
|
295
|
+
stable_entries = plot_entries.filter(helpers.entry_is_stable)
|
|
296
|
+
unstable_entries = plot_entries.filter(helpers.entry_is_unstable)
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
// Canvas rendering
|
|
300
|
+
let canvas: HTMLCanvasElement | undefined = undefined
|
|
301
|
+
let ctx: CanvasRenderingContext2D | null = null
|
|
302
|
+
|
|
303
|
+
// Performance optimization
|
|
304
|
+
let frame_id = 0
|
|
305
|
+
|
|
306
|
+
const camera_default = {
|
|
307
|
+
elevation: DEFAULTS.convex_hull.ternary.camera_elevation,
|
|
308
|
+
azimuth: DEFAULTS.convex_hull.ternary.camera_azimuth,
|
|
309
|
+
zoom: DEFAULTS.convex_hull.ternary.camera_zoom,
|
|
310
|
+
center_x: 0,
|
|
311
|
+
center_y: -50, // Shift up to better show the formation energy funnel
|
|
312
|
+
}
|
|
313
|
+
let camera = $state({ ...camera_default })
|
|
314
|
+
|
|
315
|
+
// === Gizmo state & coordinate mapping ===
|
|
316
|
+
// ConvexHull3D uses Rz(azimuth) then Rx(-elevation), viewing along -z_rotated.
|
|
317
|
+
// These helpers convert between that system and Three.js camera position/up.
|
|
318
|
+
const GIZMO_CAM_DIST = 5
|
|
319
|
+
const MIN_ELEV_FOR_Z_AXIS = 5 // degrees — below this, z-axis ticks collapse to a point
|
|
320
|
+
let gizmo_cam_ref = $state<PerspectiveCamera>()
|
|
321
|
+
let gizmo_orbit_ref = $state<OrbitControls | undefined>(undefined)
|
|
322
|
+
let gizmo_active = $state(false)
|
|
323
|
+
|
|
324
|
+
// Convert elevation/azimuth (degrees) to Three.js camera position + up vector.
|
|
325
|
+
function gizmo_camera(
|
|
326
|
+
elev_deg: number,
|
|
327
|
+
azim_deg: number,
|
|
328
|
+
): { position: Vec3; up: Vec3 } {
|
|
329
|
+
const [elev, azim] = [to_radians(elev_deg), to_radians(azim_deg)]
|
|
330
|
+
const [se, ce, sa, ca] = [
|
|
331
|
+
Math.sin(elev),
|
|
332
|
+
Math.cos(elev),
|
|
333
|
+
Math.sin(azim),
|
|
334
|
+
Math.cos(azim),
|
|
335
|
+
]
|
|
336
|
+
return {
|
|
337
|
+
position: [
|
|
338
|
+
-sa * se * GIZMO_CAM_DIST,
|
|
339
|
+
-ca * se * GIZMO_CAM_DIST,
|
|
340
|
+
ce * GIZMO_CAM_DIST,
|
|
341
|
+
],
|
|
342
|
+
up: [sa * ce, ca * ce, se],
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Derived gizmo camera state, avoids recomputing in the template
|
|
347
|
+
const gizmo_cam_state = $derived(gizmo_camera(camera.elevation, camera.azimuth))
|
|
348
|
+
|
|
349
|
+
// Center camera on the triangle's visual center for a given elevation.
|
|
350
|
+
// The centroid (rotation center) sits at 1/3 height while the bbox
|
|
351
|
+
// center is at 1/2 height — a difference of sqrt(3)/12 in data units.
|
|
352
|
+
// Scale by cos(elevation) so offset only applies in near-top-down views.
|
|
353
|
+
function center_camera(elev_deg: number): void {
|
|
354
|
+
camera.center_x = 0
|
|
355
|
+
// 0.6 matches the draw_data_points() scale factor that maps data coords to canvas pixels
|
|
356
|
+
const scale = Math.min(canvas_dims.width, canvas_dims.height) * 0.6 * camera.zoom
|
|
357
|
+
camera.center_y = Math.sqrt(3) / 12 * scale * Math.cos(to_radians(elev_deg))
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Sync: ConvexHull3D → Three.js gizmo camera (on main canvas drag)
|
|
361
|
+
$effect(() => {
|
|
362
|
+
if (gizmo_active) return
|
|
363
|
+
const cam = gizmo_cam_ref
|
|
364
|
+
if (!cam) return
|
|
365
|
+
const { position, up } = gizmo_camera(camera.elevation, camera.azimuth)
|
|
366
|
+
cam.position.set(...position)
|
|
367
|
+
cam.up.set(...up)
|
|
368
|
+
cam.lookAt(0, 0, 0)
|
|
369
|
+
gizmo_orbit_ref?.update?.()
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
// Sync: gizmo → ConvexHull3D (during and after gizmo animation)
|
|
373
|
+
function sync_gizmo_to_camera(): void {
|
|
374
|
+
const cam = gizmo_cam_ref
|
|
375
|
+
if (!cam) return
|
|
376
|
+
const { x: cx, y: cy, z: cz } = cam.position
|
|
377
|
+
const dist = Math.sqrt(cx * cx + cy * cy + cz * cz)
|
|
378
|
+
if (dist < 1e-6) return
|
|
379
|
+
const elev_rad = Math.acos(Math.max(-1, Math.min(1, cz / dist)))
|
|
380
|
+
const sin_elev = Math.sin(elev_rad)
|
|
381
|
+
const azim_deg = Math.abs(sin_elev) > 1e-6
|
|
382
|
+
? Math.atan2(-cx / (dist * sin_elev), -cy / (dist * sin_elev)) * 180 / Math.PI
|
|
383
|
+
: 0
|
|
384
|
+
const elev_deg = elev_rad * 180 / Math.PI
|
|
385
|
+
camera.elevation = elev_deg
|
|
386
|
+
camera.azimuth = azim_deg
|
|
387
|
+
center_camera(elev_deg)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Gizmo axis colors (constant — AXIS_COLORS/NEG_AXIS_COLORS never change)
|
|
391
|
+
const gizmo_axis_options = Object.fromEntries(
|
|
392
|
+
[...AXIS_COLORS, ...NEG_AXIS_COLORS].map((
|
|
393
|
+
[axis, color, hover_color],
|
|
394
|
+
) => [axis, {
|
|
395
|
+
color,
|
|
396
|
+
labelColor: `#111`,
|
|
397
|
+
opacity: 0.85,
|
|
398
|
+
hover: { color: hover_color, labelColor: `#222`, opacity: 1 },
|
|
399
|
+
}]),
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
// Extract placement from gizmo options (not a Threlte Gizmo prop)
|
|
403
|
+
const gizmo_placement = $derived(
|
|
404
|
+
typeof gizmo === `object` && gizmo?.placement ? gizmo.placement : `top-right`,
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
// Merge constant axis options with consumer overrides (exclude our custom placement)
|
|
408
|
+
const gizmo_props = $derived.by(() => {
|
|
409
|
+
if (typeof gizmo !== `object` || !gizmo) {
|
|
410
|
+
return { background: { enabled: false }, size: 80, ...gizmo_axis_options }
|
|
411
|
+
}
|
|
412
|
+
const { placement: _, ...threlte_opts } = gizmo
|
|
413
|
+
return {
|
|
414
|
+
background: { enabled: false },
|
|
415
|
+
size: 80,
|
|
416
|
+
...gizmo_axis_options,
|
|
417
|
+
...threlte_opts,
|
|
418
|
+
}
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
// Interaction state
|
|
422
|
+
let is_dragging = $state(false)
|
|
423
|
+
let drag_started = $state(false)
|
|
424
|
+
let last_mouse = $state({ x: 0, y: 0 })
|
|
425
|
+
let hover_data = $state<HoverData3D<ConvexHullEntry> | null>(null)
|
|
426
|
+
let copy_feedback = $state({ visible: false, position: { x: 0, y: 0 } })
|
|
427
|
+
|
|
428
|
+
// Drag and drop state
|
|
429
|
+
let drag_over = $state(false)
|
|
430
|
+
|
|
431
|
+
// Structure popup state
|
|
432
|
+
let modal_open = $state(false)
|
|
433
|
+
let selected_structure = $state<AnyStructure | null>(null)
|
|
434
|
+
let modal_place_right = $state(true)
|
|
435
|
+
$effect(() => {
|
|
436
|
+
const current_selection = helpers.current_entry(selected_entry, plot_entries)
|
|
437
|
+
const stale_selection = selected_entry && !current_selection
|
|
438
|
+
if (stale_selection) selected_entry = null
|
|
439
|
+
else if (current_selection && current_selection !== selected_entry) {
|
|
440
|
+
selected_entry = current_selection
|
|
441
|
+
}
|
|
442
|
+
const current_hover = helpers.current_entry(hover_data?.entry, plot_entries)
|
|
443
|
+
if (hover_data?.entry && !current_hover) {
|
|
444
|
+
hover_data = null
|
|
445
|
+
on_point_hover?.(null)
|
|
446
|
+
} else if (hover_data && current_hover && current_hover !== hover_data.entry) {
|
|
447
|
+
hover_data = { ...hover_data, entry: current_hover }
|
|
448
|
+
}
|
|
449
|
+
if (modal_open) {
|
|
450
|
+
const structure = current_selection && extract_structure_from_entry(current_selection)
|
|
451
|
+
if (structure) selected_structure = structure
|
|
452
|
+
else {
|
|
453
|
+
modal_open = false
|
|
454
|
+
selected_structure = null
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
// Hull face color (customizable via controls)
|
|
460
|
+
let hull_face_color = $state(`#4caf50`)
|
|
461
|
+
|
|
462
|
+
// Pulsating highlight for selected point
|
|
463
|
+
const pulse = create_pulse_animation(
|
|
464
|
+
() => selected_entry !== null || highlighted_entries.length > 0,
|
|
465
|
+
{ on_tick: render_once },
|
|
466
|
+
)
|
|
467
|
+
let pulse_opacity = $derived(0.3 + 0.4 * pulse.unit)
|
|
468
|
+
|
|
469
|
+
// Merge highlight style with defaults
|
|
470
|
+
const merged_highlight_style = $derived(
|
|
471
|
+
helpers.merge_highlight_style(highlight_style),
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
// Helper to check if entry is highlighted
|
|
475
|
+
const is_highlighted = (entry: ConvexHullEntry): boolean =>
|
|
476
|
+
helpers.is_entry_highlighted(entry, highlighted_entries)
|
|
477
|
+
|
|
478
|
+
// Re-render when important state changes
|
|
479
|
+
$effect(() => {
|
|
480
|
+
// oxfmt-ignore
|
|
481
|
+
void [show_hull_faces, color_mode, color_scale, show_stable_labels, show_unstable_labels, max_hull_dist_show_labels, camera.elevation, camera.azimuth, camera.zoom, camera.center_x, camera.center_y, plot_entries, hull_face_color, hull_face_opacity, hull_face_color_mode, element_colors, highlighted_entries, text_color] // track reactively
|
|
482
|
+
|
|
483
|
+
render_once()
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
// Function to extract structure data from a convex hull entry
|
|
487
|
+
function extract_structure_from_entry(
|
|
488
|
+
entry: ConvexHullEntry,
|
|
489
|
+
): AnyStructure | null {
|
|
490
|
+
const orig_entry = entries.find((ent) => ent.entry_id === entry.entry_id)
|
|
491
|
+
return orig_entry?.structure as AnyStructure || null
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const reset_camera = () => Object.assign(camera, camera_default)
|
|
495
|
+
function reset_all() {
|
|
496
|
+
reset_camera()
|
|
497
|
+
fullscreen = DEFAULTS.convex_hull.ternary.fullscreen
|
|
498
|
+
info_pane_open = DEFAULTS.convex_hull.ternary.info_pane_open
|
|
499
|
+
legend_pane_open = DEFAULTS.convex_hull.ternary.legend_pane_open
|
|
500
|
+
color_mode = DEFAULTS.convex_hull.ternary.color_mode
|
|
501
|
+
color_scale = DEFAULTS.convex_hull.ternary.color_scale as D3InterpolateName
|
|
502
|
+
show_stable = DEFAULTS.convex_hull.ternary.show_stable
|
|
503
|
+
show_unstable = DEFAULTS.convex_hull.ternary.show_unstable
|
|
504
|
+
show_stable_labels = DEFAULTS.convex_hull.ternary.show_stable_labels
|
|
505
|
+
show_unstable_labels = DEFAULTS.convex_hull.ternary.show_unstable_labels
|
|
506
|
+
max_hull_dist_show_labels = DEFAULTS.convex_hull.ternary.max_hull_dist_show_labels
|
|
507
|
+
// Use auto-computed threshold based on entry count instead of static default
|
|
508
|
+
max_hull_dist_show_phases = auto_default_threshold
|
|
509
|
+
show_hull_faces = DEFAULTS.convex_hull.ternary.show_hull_faces
|
|
510
|
+
hull_face_color = DEFAULTS.convex_hull.ternary.hull_face_color
|
|
511
|
+
hull_face_opacity = DEFAULTS.convex_hull.ternary.hull_face_opacity
|
|
512
|
+
hull_face_color_mode = DEFAULTS.convex_hull.ternary
|
|
513
|
+
.hull_face_color_mode as HullFaceColorMode
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const handle_keydown = (event: KeyboardEvent) => {
|
|
517
|
+
const target = event.target
|
|
518
|
+
if (target instanceof HTMLElement && target.tagName.match(/INPUT|TEXTAREA/)) return
|
|
519
|
+
|
|
520
|
+
// Stop propagation if event came from canvas to prevent wrapper's handler
|
|
521
|
+
// from running again (both have onkeydown, causing duplicate handling)
|
|
522
|
+
if (target === canvas) {
|
|
523
|
+
event.stopPropagation()
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (event.key === `Escape` && modal_open) {
|
|
527
|
+
close_structure_popup()
|
|
528
|
+
return
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Handle Enter for keyboard accessibility - select hovered entry
|
|
532
|
+
if (event.key === `Enter`) {
|
|
533
|
+
const entry = hover_data?.entry
|
|
534
|
+
if (entry) {
|
|
535
|
+
on_point_click?.(entry)
|
|
536
|
+
if (enable_click_selection) {
|
|
537
|
+
selected_entry = entry
|
|
538
|
+
if (enable_structure_preview) {
|
|
539
|
+
const structure = extract_structure_from_entry(entry)
|
|
540
|
+
if (structure) {
|
|
541
|
+
selected_structure = structure
|
|
542
|
+
modal_place_right = helpers.calculate_modal_side(wrapper)
|
|
543
|
+
modal_open = true
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
} else if (modal_open) {
|
|
548
|
+
close_structure_popup()
|
|
549
|
+
}
|
|
550
|
+
return
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const actions: Record<string, () => void> = {
|
|
554
|
+
r: reset_camera,
|
|
555
|
+
t: () => {
|
|
556
|
+
camera.elevation = 0
|
|
557
|
+
camera.azimuth = 0
|
|
558
|
+
center_camera(0)
|
|
559
|
+
},
|
|
560
|
+
b: () => color_mode = color_mode === `stability` ? `energy` : `stability`,
|
|
561
|
+
s: () => show_stable = !show_stable,
|
|
562
|
+
u: () => show_unstable = !show_unstable,
|
|
563
|
+
h: () => show_hull_faces = !show_hull_faces,
|
|
564
|
+
l: () => show_stable_labels = !show_stable_labels,
|
|
565
|
+
}
|
|
566
|
+
actions[event.key.toLowerCase()]?.()
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async function handle_file_drop(event: DragEvent): Promise<void> {
|
|
570
|
+
drag_over = false
|
|
571
|
+
const data = await helpers.parse_hull_entries_from_drop(event)
|
|
572
|
+
if (data) on_file_drop?.(data)
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
async function copy_entry_data(
|
|
576
|
+
entry: ConvexHullEntry,
|
|
577
|
+
position: { x: number; y: number },
|
|
578
|
+
) {
|
|
579
|
+
await helpers.copy_entry_to_clipboard(entry, position, (visible, pos) => {
|
|
580
|
+
copy_feedback.visible = visible
|
|
581
|
+
copy_feedback.position = pos
|
|
582
|
+
})
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const get_point_color = (entry: ConvexHullEntry): string =>
|
|
586
|
+
helpers.get_point_color_for_entry(
|
|
587
|
+
entry,
|
|
588
|
+
color_mode,
|
|
589
|
+
merged_config.colors,
|
|
590
|
+
energy_color_scale,
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
// Cache energy color scale per frame/setting
|
|
594
|
+
const energy_color_scale = $derived.by(() =>
|
|
595
|
+
helpers.get_energy_color_scale(color_mode, color_scale, plot_entries)
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
// Convex hull statistics - compute internally and expose via bindable prop
|
|
599
|
+
$effect(() => {
|
|
600
|
+
phase_stats = thermo.get_convex_hull_stats(plot_entries, elements, 3)
|
|
601
|
+
})
|
|
602
|
+
|
|
603
|
+
// 3D to 2D projection for ternary diagrams
|
|
604
|
+
function project_3d_point(
|
|
605
|
+
x: number,
|
|
606
|
+
y: number,
|
|
607
|
+
z: number,
|
|
608
|
+
): { x: number; y: number; depth: number } {
|
|
609
|
+
if (!canvas) return { x: 0, y: 0, depth: 0 }
|
|
610
|
+
|
|
611
|
+
const [elev, azim] = [
|
|
612
|
+
(camera.elevation * Math.PI) / 180,
|
|
613
|
+
(camera.azimuth * Math.PI) / 180,
|
|
614
|
+
]
|
|
615
|
+
const [cos_az, sin_az, cos_el, sin_el] = [
|
|
616
|
+
Math.cos(azim),
|
|
617
|
+
Math.sin(azim),
|
|
618
|
+
Math.cos(-elev),
|
|
619
|
+
Math.sin(-elev),
|
|
620
|
+
]
|
|
621
|
+
const centroid = get_triangle_centroid()
|
|
622
|
+
const { center: e_ctr, z_scale } = energy_range
|
|
623
|
+
|
|
624
|
+
const [dx, dy, dz] = [x - centroid.x, y - centroid.y, (z - e_ctr) * z_scale]
|
|
625
|
+
const [x1, y1] = [dx * cos_az - dy * sin_az, dx * sin_az + dy * cos_az]
|
|
626
|
+
const [y2, z2] = [y1 * cos_el - dz * sin_el, y1 * sin_el + dz * cos_el]
|
|
627
|
+
|
|
628
|
+
// Use Math.min for consistent scaling with cached canvas dimensions
|
|
629
|
+
const scale = Math.min(canvas_dims.width, canvas_dims.height) * 0.6 * camera.zoom
|
|
630
|
+
return {
|
|
631
|
+
x: canvas_dims.width / 2 + camera.center_x + x1 * scale,
|
|
632
|
+
y: canvas_dims.height / 2 + camera.center_y - y2 * scale,
|
|
633
|
+
depth: z2,
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function draw_structure_outline(): void {
|
|
638
|
+
if (!ctx || !canvas) return
|
|
639
|
+
|
|
640
|
+
// Set consistent style for all triangle structure lines
|
|
641
|
+
ctx.strokeStyle = CONVEX_HULL_STYLE.structure_line.color
|
|
642
|
+
ctx.lineWidth = CONVEX_HULL_STYLE.structure_line.line_width
|
|
643
|
+
ctx.setLineDash(CONVEX_HULL_STYLE.structure_line.dash) // Dashed lines for all structure lines
|
|
644
|
+
|
|
645
|
+
// Draw triangle base and vertical edges
|
|
646
|
+
draw_triangle_structure()
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function draw_triangle_structure(): void {
|
|
650
|
+
if (!ctx || !canvas) return
|
|
651
|
+
|
|
652
|
+
// Get formation energy range for vertical edges
|
|
653
|
+
const e_form_min = energy_range.min // Includes 0 for elemental references
|
|
654
|
+
const e_form_max = energy_range.max // Includes 0 for elemental references
|
|
655
|
+
|
|
656
|
+
// Draw base triangle edges (top triangle at formation energy = 0)
|
|
657
|
+
const triangle_edges = get_triangle_edges()
|
|
658
|
+
ctx.beginPath()
|
|
659
|
+
for (const [v1, v2] of triangle_edges) {
|
|
660
|
+
const proj1 = project_3d_point(v1.x, v1.y, 0) // Base triangle at formation energy = 0
|
|
661
|
+
const proj2 = project_3d_point(v2.x, v2.y, 0)
|
|
662
|
+
|
|
663
|
+
ctx.moveTo(proj1.x, proj1.y)
|
|
664
|
+
ctx.lineTo(proj2.x, proj2.y)
|
|
665
|
+
}
|
|
666
|
+
ctx.stroke()
|
|
667
|
+
|
|
668
|
+
// Draw vertical edges from corners (from most negative to 0 formation energy)
|
|
669
|
+
const vertical_edges = get_triangle_vertical_edges(
|
|
670
|
+
e_form_min,
|
|
671
|
+
e_form_max,
|
|
672
|
+
)
|
|
673
|
+
ctx.beginPath()
|
|
674
|
+
for (const [v1, v2] of vertical_edges) {
|
|
675
|
+
const proj1 = project_3d_point(v1.x, v1.y, v1.z)
|
|
676
|
+
const proj2 = project_3d_point(v2.x, v2.y, v2.z)
|
|
677
|
+
|
|
678
|
+
ctx.moveTo(proj1.x, proj1.y)
|
|
679
|
+
ctx.lineTo(proj2.x, proj2.y)
|
|
680
|
+
}
|
|
681
|
+
ctx.stroke()
|
|
682
|
+
|
|
683
|
+
// Draw bottom triangle (connecting the bottom tips of vertical lines)
|
|
684
|
+
const bottom_triangle_edges = get_triangle_edges()
|
|
685
|
+
ctx.beginPath()
|
|
686
|
+
for (const [v1, v2] of bottom_triangle_edges) {
|
|
687
|
+
const proj1 = project_3d_point(v1.x, v1.y, e_form_min) // Bottom triangle at most negative energy
|
|
688
|
+
const proj2 = project_3d_point(v2.x, v2.y, e_form_min)
|
|
689
|
+
|
|
690
|
+
ctx.moveTo(proj1.x, proj1.y)
|
|
691
|
+
ctx.lineTo(proj2.x, proj2.y)
|
|
692
|
+
}
|
|
693
|
+
ctx.stroke()
|
|
694
|
+
|
|
695
|
+
// Reset stroke style to default for other elements
|
|
696
|
+
const styles = getComputedStyle(canvas)
|
|
697
|
+
ctx.strokeStyle = styles.getPropertyValue(`--hull-edge-color`) || `#212121`
|
|
698
|
+
ctx.setLineDash([]) // Reset line dash for other drawing operations
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function draw_element_labels(): void {
|
|
702
|
+
if (!ctx || elements.length !== 3) return
|
|
703
|
+
|
|
704
|
+
ctx.save()
|
|
705
|
+
|
|
706
|
+
// Draw element labels outside triangle corners
|
|
707
|
+
const centroid = get_triangle_centroid()
|
|
708
|
+
ctx.fillStyle = text_color
|
|
709
|
+
ctx.font = `bold 16px Arial`
|
|
710
|
+
ctx.textAlign = `center`
|
|
711
|
+
ctx.textBaseline = `middle`
|
|
712
|
+
|
|
713
|
+
for (
|
|
714
|
+
let idx = 0;
|
|
715
|
+
idx < TRIANGLE_VERTICES.length && idx < elements.length;
|
|
716
|
+
idx++
|
|
717
|
+
) {
|
|
718
|
+
const [x, y] = TRIANGLE_VERTICES[idx]
|
|
719
|
+
const dx = x - centroid.x
|
|
720
|
+
const dy = y - centroid.y
|
|
721
|
+
const length = Math.sqrt(dx * dx + dy * dy)
|
|
722
|
+
const distance = 0.05
|
|
723
|
+
|
|
724
|
+
const label_pos = {
|
|
725
|
+
x: x + (dx / length) * distance,
|
|
726
|
+
y: y + (dy / length) * distance,
|
|
727
|
+
z: 0,
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const proj = project_3d_point(label_pos.x, label_pos.y, label_pos.z)
|
|
731
|
+
ctx.fillText(elements[idx], proj.x, proj.y)
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
ctx.restore()
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function draw_z_axis_ticks(): void {
|
|
738
|
+
if (!ctx || elements.length !== 3) return
|
|
739
|
+
// Hide z-axis in near-top-down views where ticks collapse to a point
|
|
740
|
+
if (Math.abs(camera.elevation) < MIN_ELEV_FOR_Z_AXIS) return
|
|
741
|
+
|
|
742
|
+
const { min: e_min, max: e_max, center: e_mid } = energy_range
|
|
743
|
+
if (Math.abs(e_max - e_min) < 1e-6) return
|
|
744
|
+
|
|
745
|
+
// Find the vertex that projects to the leftmost x-position (changes with rotation)
|
|
746
|
+
const projected_vertices = TRIANGLE_VERTICES.map(([vx, vy]) =>
|
|
747
|
+
project_3d_point(vx, vy, e_mid)
|
|
748
|
+
)
|
|
749
|
+
const leftmost_idx = projected_vertices.reduce(
|
|
750
|
+
(
|
|
751
|
+
min_idx,
|
|
752
|
+
proj,
|
|
753
|
+
idx,
|
|
754
|
+
) => (proj.x < projected_vertices[min_idx].x ? idx : min_idx),
|
|
755
|
+
0,
|
|
756
|
+
)
|
|
757
|
+
const [axis_x, axis_y] = TRIANGLE_VERTICES[leftmost_idx]
|
|
758
|
+
const tick_len = 6 * canvas_dims.scale
|
|
759
|
+
|
|
760
|
+
ctx.save()
|
|
761
|
+
ctx.fillStyle = text_color
|
|
762
|
+
ctx.textAlign = `right`
|
|
763
|
+
ctx.textBaseline = `middle`
|
|
764
|
+
ctx.strokeStyle = CONVEX_HULL_STYLE.structure_line.color
|
|
765
|
+
ctx.font = `${merged_config.font_size}px Arial`
|
|
766
|
+
|
|
767
|
+
for (const tick of ticks(e_min, e_max, 5)) {
|
|
768
|
+
const { x, y } = project_3d_point(axis_x, axis_y, tick)
|
|
769
|
+
ctx.beginPath()
|
|
770
|
+
ctx.moveTo(x - tick_len, y)
|
|
771
|
+
ctx.lineTo(x, y)
|
|
772
|
+
ctx.stroke()
|
|
773
|
+
ctx.fillText(format_num(tick, `.2~`), x - tick_len - 4, y)
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Rotated axis label: Eform (eV/atom) with "form" as subscript
|
|
777
|
+
const { x: lx, y: ly } = project_3d_point(axis_x, axis_y, e_mid)
|
|
778
|
+
const fs = merged_config.font_size ?? 12
|
|
779
|
+
const sub_fs = Math.round(fs * 0.75)
|
|
780
|
+
ctx.translate(lx - 50 * canvas_dims.scale, ly)
|
|
781
|
+
ctx.rotate(-Math.PI / 2)
|
|
782
|
+
ctx.textAlign = `left`
|
|
783
|
+
// Measure widths in each font, then draw — reordered to minimize font switches
|
|
784
|
+
ctx.font = `bold ${fs}px Arial`
|
|
785
|
+
const e_width = ctx.measureText(`E`).width
|
|
786
|
+
const suffix_width = ctx.measureText(` (eV/atom)`).width
|
|
787
|
+
ctx.font = `${sub_fs}px Arial`
|
|
788
|
+
const form_width = ctx.measureText(`form`).width
|
|
789
|
+
const offset = -(e_width + form_width + suffix_width) / 2
|
|
790
|
+
// Draw subscript while sub-font is still active
|
|
791
|
+
ctx.fillText(`form`, offset + e_width, fs * 0.3)
|
|
792
|
+
ctx.font = `bold ${fs}px Arial`
|
|
793
|
+
ctx.fillText(`E`, offset, 0)
|
|
794
|
+
ctx.fillText(` (eV/atom)`, offset + e_width + form_width, 0)
|
|
795
|
+
ctx.restore()
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function draw_convex_hull_faces(): void {
|
|
799
|
+
if (!ctx || !show_hull_faces || hull_faces.length === 0) return
|
|
800
|
+
|
|
801
|
+
// Lazy computation for uniform mode: normalize alpha by formation energy
|
|
802
|
+
let norm_alpha: ((e_form: number) => number) | null = null
|
|
803
|
+
if (hull_face_color_mode === `uniform`) {
|
|
804
|
+
const min_uniform_e_form = energy_range.min
|
|
805
|
+
norm_alpha = (e_form: number) => {
|
|
806
|
+
const alpha_fraction = Math.max(
|
|
807
|
+
0,
|
|
808
|
+
Math.min(1, (0 - e_form) / Math.max(1e-6, 0 - min_uniform_e_form)),
|
|
809
|
+
)
|
|
810
|
+
return alpha_fraction * hull_face_opacity
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// Lazy computation for formation_energy mode
|
|
815
|
+
let energy_face_scale: ((val: number) => string) | null = null
|
|
816
|
+
let min_face_e_form = 0
|
|
817
|
+
if (hull_face_color_mode === `formation_energy`) {
|
|
818
|
+
const all_e_form = hull_faces.flatMap((tri) =>
|
|
819
|
+
tri.vertices.map((vertex) => vertex.z)
|
|
820
|
+
)
|
|
821
|
+
min_face_e_form = Math.min(...all_e_form)
|
|
822
|
+
energy_face_scale = helpers.get_energy_color_scale(
|
|
823
|
+
`energy`,
|
|
824
|
+
color_scale,
|
|
825
|
+
all_e_form.map((e_form) => ({
|
|
826
|
+
e_above_hull: e_form - min_face_e_form,
|
|
827
|
+
})), // Normalize to 0-based
|
|
828
|
+
)
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Helper to get face color based on mode
|
|
832
|
+
const get_face_color = (
|
|
833
|
+
tri: typeof hull_faces[0],
|
|
834
|
+
tri_idx: number,
|
|
835
|
+
): string => {
|
|
836
|
+
if (hull_face_color_mode === `uniform`) {
|
|
837
|
+
return hull_face_color
|
|
838
|
+
}
|
|
839
|
+
if (hull_face_color_mode === `formation_energy`) {
|
|
840
|
+
const avg_e_form = (tri.vertices[0].z + tri.vertices[1].z + tri.vertices[2].z) / 3
|
|
841
|
+
return energy_face_scale?.(avg_e_form - min_face_e_form) ?? hull_face_color
|
|
842
|
+
}
|
|
843
|
+
if (hull_face_color_mode === `dominant_element`) {
|
|
844
|
+
// Find element vertex closest to face centroid in 2D ternary space
|
|
845
|
+
const { x: cx, y: cy } = tri.centroid
|
|
846
|
+
const dists = TRIANGLE_VERTICES.map(([tx, ty]) =>
|
|
847
|
+
Math.hypot(cx - tx, cy - ty)
|
|
848
|
+
)
|
|
849
|
+
const el = elements[dists.indexOf(Math.min(...dists))]
|
|
850
|
+
return element_colors[el] ?? `#888888`
|
|
851
|
+
}
|
|
852
|
+
if (hull_face_color_mode === `facet_index`) {
|
|
853
|
+
return PLOT_COLORS[tri_idx % PLOT_COLORS.length]
|
|
854
|
+
}
|
|
855
|
+
return hull_face_color
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// Sort faces by depth for proper rendering
|
|
859
|
+
const faces_with_depth = hull_faces.map((tri, tri_idx) => {
|
|
860
|
+
const centroid_proj = project_3d_point(
|
|
861
|
+
tri.centroid.x,
|
|
862
|
+
tri.centroid.y,
|
|
863
|
+
tri.centroid.z,
|
|
864
|
+
)
|
|
865
|
+
return { tri, tri_idx, depth: centroid_proj.depth }
|
|
866
|
+
})
|
|
867
|
+
|
|
868
|
+
faces_with_depth.sort((left, right) => left.depth - right.depth) // Back to front
|
|
869
|
+
|
|
870
|
+
// Draw each face (lower hull only)
|
|
871
|
+
for (const { tri, tri_idx } of faces_with_depth) {
|
|
872
|
+
const [p1, p2, p3] = tri.vertices
|
|
873
|
+
|
|
874
|
+
const proj1 = project_3d_point(p1.x, p1.y, p1.z)
|
|
875
|
+
const proj2 = project_3d_point(p2.x, p2.y, p2.z)
|
|
876
|
+
const proj3 = project_3d_point(p3.x, p3.y, p3.z)
|
|
877
|
+
|
|
878
|
+
const face_color = get_face_color(tri, tri_idx)
|
|
879
|
+
|
|
880
|
+
// For uniform mode, use gradient with variable opacity
|
|
881
|
+
// For other modes, use fixed opacity
|
|
882
|
+
if (hull_face_color_mode === `uniform`) {
|
|
883
|
+
// Build per-face linear gradient in screen space matching linear variation of formation energy
|
|
884
|
+
const a1 = norm_alpha?.(p1.z) ?? 0
|
|
885
|
+
const a2 = norm_alpha?.(p2.z) ?? 0
|
|
886
|
+
const a3 = norm_alpha?.(p3.z) ?? 0
|
|
887
|
+
|
|
888
|
+
// Solve a*x + b*y + c = alpha at the three projected vertices
|
|
889
|
+
const x1 = proj1.x, y1 = proj1.y
|
|
890
|
+
const x2 = proj2.x, y2 = proj2.y
|
|
891
|
+
const x3 = proj3.x, y3 = proj3.y
|
|
892
|
+
const det = x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)
|
|
893
|
+
let coef_a = 0, coef_b = 0, coef_c = (a1 + a2 + a3) / 3
|
|
894
|
+
if (Math.abs(det) > 1e-9) {
|
|
895
|
+
coef_a = (a1 * (y2 - y3) + a2 * (y3 - y1) + a3 * (y1 - y2)) / det
|
|
896
|
+
coef_b = (a1 * (x3 - x2) + a2 * (x1 - x3) + a3 * (x2 - x1)) / det
|
|
897
|
+
coef_c = (a1 * (x2 * y3 - x3 * y2) + a2 * (x3 * y1 - x1 * y3) +
|
|
898
|
+
a3 * (x1 * y2 - x2 * y1)) /
|
|
899
|
+
det
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
// Helper to draw filled+stroked triangle
|
|
903
|
+
const draw_tri = (fill: string | CanvasGradient, stroke_alpha: number) => {
|
|
904
|
+
if (!ctx) return
|
|
905
|
+
ctx.save()
|
|
906
|
+
ctx.beginPath()
|
|
907
|
+
ctx.moveTo(proj1.x, proj1.y)
|
|
908
|
+
ctx.lineTo(proj2.x, proj2.y)
|
|
909
|
+
ctx.lineTo(proj3.x, proj3.y)
|
|
910
|
+
ctx.closePath()
|
|
911
|
+
ctx.fillStyle = fill
|
|
912
|
+
ctx.fill()
|
|
913
|
+
ctx.strokeStyle = add_alpha(face_color, Math.min(0.6, stroke_alpha))
|
|
914
|
+
ctx.lineWidth = 1
|
|
915
|
+
ctx.stroke()
|
|
916
|
+
ctx.restore()
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// Gradient direction is the screen-space gradient of alpha
|
|
920
|
+
const mag = Math.hypot(coef_a, coef_b)
|
|
921
|
+
if (mag < 1e-9) {
|
|
922
|
+
// Fallback: uniform fill if nearly flat
|
|
923
|
+
const avg_alpha = (a1 + a2 + a3) / 3
|
|
924
|
+
draw_tri(add_alpha(face_color, avg_alpha), avg_alpha * 3)
|
|
925
|
+
} else {
|
|
926
|
+
const vx = coef_a / mag
|
|
927
|
+
const vy = coef_b / mag
|
|
928
|
+
const cx = (x1 + x2 + x3) / 3
|
|
929
|
+
const cy = (y1 + y2 + y3) / 3
|
|
930
|
+
const alpha_c = coef_a * cx + coef_b * cy + coef_c
|
|
931
|
+
const alpha_min = Math.min(a1, a2, a3)
|
|
932
|
+
const alpha_max = Math.max(a1, a2, a3)
|
|
933
|
+
const s_min = (alpha_min - alpha_c) / mag
|
|
934
|
+
const s_max = (alpha_max - alpha_c) / mag
|
|
935
|
+
|
|
936
|
+
const grad = ctx.createLinearGradient(
|
|
937
|
+
cx + vx * s_min,
|
|
938
|
+
cy + vy * s_min,
|
|
939
|
+
cx + vx * s_max,
|
|
940
|
+
cy + vy * s_max,
|
|
941
|
+
)
|
|
942
|
+
grad.addColorStop(0, add_alpha(face_color, alpha_min))
|
|
943
|
+
grad.addColorStop(1, add_alpha(face_color, alpha_max))
|
|
944
|
+
draw_tri(grad, alpha_max * 3)
|
|
945
|
+
}
|
|
946
|
+
} else {
|
|
947
|
+
// Non-uniform modes: solid color with fixed opacity
|
|
948
|
+
ctx.save()
|
|
949
|
+
ctx.beginPath()
|
|
950
|
+
ctx.moveTo(proj1.x, proj1.y)
|
|
951
|
+
ctx.lineTo(proj2.x, proj2.y)
|
|
952
|
+
ctx.lineTo(proj3.x, proj3.y)
|
|
953
|
+
ctx.closePath()
|
|
954
|
+
ctx.fillStyle = add_alpha(face_color, hull_face_opacity)
|
|
955
|
+
ctx.fill()
|
|
956
|
+
ctx.strokeStyle = add_alpha(face_color, Math.min(0.6, hull_face_opacity * 3))
|
|
957
|
+
ctx.lineWidth = 1
|
|
958
|
+
ctx.stroke()
|
|
959
|
+
ctx.restore()
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// Formation energy color bar helpers
|
|
965
|
+
const e_form_range = $derived.by((): [number, number] => {
|
|
966
|
+
const min_fe = plot_entries.length ? energy_range.min : -1
|
|
967
|
+
return [min_fe, 0]
|
|
968
|
+
})
|
|
969
|
+
|
|
970
|
+
const e_form_color_scale_fn = $derived.by(() => {
|
|
971
|
+
const [min_fe, max_fe] = e_form_range
|
|
972
|
+
const denom = Math.max(1e-6, max_fe - min_fe)
|
|
973
|
+
return (value: number) => {
|
|
974
|
+
// alpha 0 at 0 eV, goes to hull_face_opacity at most negative energy
|
|
975
|
+
const energy_fraction = Math.max(0, Math.min(1, (value - min_fe) / denom))
|
|
976
|
+
const alpha = (1 - energy_fraction) * hull_face_opacity
|
|
977
|
+
return add_alpha(hull_face_color, alpha)
|
|
978
|
+
}
|
|
979
|
+
})
|
|
980
|
+
|
|
981
|
+
function draw_data_points(): void {
|
|
982
|
+
if (!ctx || sorted_points_cache.length === 0) return
|
|
983
|
+
|
|
984
|
+
for (const { entry, projected } of sorted_points_cache) {
|
|
985
|
+
const is_stable = helpers.entry_is_stable(entry)
|
|
986
|
+
const is_entry_highlighted = is_highlighted(entry)
|
|
987
|
+
const color = get_point_color(entry)
|
|
988
|
+
const size = (entry.size || (is_stable ? 6 : 4)) * canvas_dims.scale
|
|
989
|
+
const marker = entry.marker || `circle`
|
|
990
|
+
|
|
991
|
+
// Shadow
|
|
992
|
+
const shadow_offset = Math.abs(entry.z) * 0.1 * canvas_dims.scale
|
|
993
|
+
ctx.fillStyle = `rgba(0, 0, 0, 0.2)`
|
|
994
|
+
const shadow_path = helpers.create_marker_path(size * 0.8, marker)
|
|
995
|
+
ctx.save()
|
|
996
|
+
ctx.translate(projected.x + shadow_offset, projected.y + shadow_offset)
|
|
997
|
+
ctx.fill(shadow_path)
|
|
998
|
+
ctx.restore()
|
|
999
|
+
|
|
1000
|
+
// Highlights
|
|
1001
|
+
if (selected_entry && entry.entry_id === selected_entry.entry_id) {
|
|
1002
|
+
helpers.draw_selection_highlight(
|
|
1003
|
+
ctx,
|
|
1004
|
+
projected,
|
|
1005
|
+
size,
|
|
1006
|
+
canvas_dims.scale,
|
|
1007
|
+
pulse.time,
|
|
1008
|
+
pulse_opacity,
|
|
1009
|
+
)
|
|
1010
|
+
}
|
|
1011
|
+
if (is_entry_highlighted) {
|
|
1012
|
+
helpers.draw_highlight_effect(
|
|
1013
|
+
ctx,
|
|
1014
|
+
projected,
|
|
1015
|
+
size,
|
|
1016
|
+
canvas_dims.scale,
|
|
1017
|
+
pulse.time,
|
|
1018
|
+
merged_highlight_style,
|
|
1019
|
+
)
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// Main point with marker symbol
|
|
1023
|
+
ctx.fillStyle =
|
|
1024
|
+
is_entry_highlighted && merged_highlight_style.effect === `color`
|
|
1025
|
+
? merged_highlight_style.color
|
|
1026
|
+
: color
|
|
1027
|
+
ctx.strokeStyle = is_stable ? `#ffffff` : `#000000`
|
|
1028
|
+
ctx.lineWidth = 0.5 * canvas_dims.scale
|
|
1029
|
+
const marker_path = helpers.create_marker_path(size, marker)
|
|
1030
|
+
ctx.save()
|
|
1031
|
+
ctx.translate(projected.x, projected.y)
|
|
1032
|
+
ctx.fill(marker_path)
|
|
1033
|
+
ctx.stroke(marker_path)
|
|
1034
|
+
ctx.restore()
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
const hull_label_font_size = 12
|
|
1039
|
+
const hull_label_subscript_font_size = 11
|
|
1040
|
+
const hull_label_font = `${hull_label_font_size}px Arial`
|
|
1041
|
+
const hull_label_subscript_font = `${hull_label_subscript_font_size}px Arial`
|
|
1042
|
+
|
|
1043
|
+
function label_priority_energy(entry: ConvexHullEntry): number {
|
|
1044
|
+
for (const value of [
|
|
1045
|
+
entry.e_form_per_atom,
|
|
1046
|
+
entry.z,
|
|
1047
|
+
entry.energy_per_atom,
|
|
1048
|
+
entry.energy,
|
|
1049
|
+
entry.e_above_hull,
|
|
1050
|
+
]) {
|
|
1051
|
+
if (typeof value === `number` && Number.isFinite(value)) return value
|
|
1052
|
+
}
|
|
1053
|
+
return Number.POSITIVE_INFINITY
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
function get_label_placements(
|
|
1057
|
+
projected: { x: number; y: number },
|
|
1058
|
+
point_size: number,
|
|
1059
|
+
text_width: number,
|
|
1060
|
+
text_height: number,
|
|
1061
|
+
): LabelPlacement[] {
|
|
1062
|
+
const padding = Math.max(1, 2 * canvas_dims.scale)
|
|
1063
|
+
const gap = point_size + 4 * canvas_dims.scale
|
|
1064
|
+
const side_gap = point_size + 5 * canvas_dims.scale
|
|
1065
|
+
const placements = [
|
|
1066
|
+
{ x: projected.x, y: projected.y + gap },
|
|
1067
|
+
{ x: projected.x, y: projected.y - gap - text_height },
|
|
1068
|
+
{ x: projected.x + side_gap + text_width / 2, y: projected.y - text_height / 2 },
|
|
1069
|
+
{ x: projected.x - side_gap - text_width / 2, y: projected.y - text_height / 2 },
|
|
1070
|
+
{ x: projected.x + side_gap + text_width / 2, y: projected.y + gap },
|
|
1071
|
+
{ x: projected.x - side_gap - text_width / 2, y: projected.y + gap },
|
|
1072
|
+
{ x: projected.x + side_gap + text_width / 2, y: projected.y - gap - text_height },
|
|
1073
|
+
{ x: projected.x - side_gap - text_width / 2, y: projected.y - gap - text_height },
|
|
1074
|
+
]
|
|
1075
|
+
|
|
1076
|
+
return placements.map((placement) => ({
|
|
1077
|
+
...placement,
|
|
1078
|
+
rect: pad_rect(
|
|
1079
|
+
centered_rect(placement.x, placement.y, text_width, text_height),
|
|
1080
|
+
padding,
|
|
1081
|
+
),
|
|
1082
|
+
}))
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function measure_formula_segments(
|
|
1086
|
+
context: CanvasRenderingContext2D,
|
|
1087
|
+
segments: FormulaLabelSegment[],
|
|
1088
|
+
): number {
|
|
1089
|
+
context.save()
|
|
1090
|
+
const width = segments.reduce((sum, segment) => {
|
|
1091
|
+
context.font = segment.subscript ? hull_label_subscript_font : hull_label_font
|
|
1092
|
+
return sum + context.measureText(segment.text).width
|
|
1093
|
+
}, 0)
|
|
1094
|
+
context.restore()
|
|
1095
|
+
return width
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
function draw_formula_segments(
|
|
1099
|
+
context: CanvasRenderingContext2D,
|
|
1100
|
+
segments: FormulaLabelSegment[],
|
|
1101
|
+
center_x: number,
|
|
1102
|
+
top_y: number,
|
|
1103
|
+
text_width: number,
|
|
1104
|
+
): void {
|
|
1105
|
+
const subscript_offset = hull_label_font_size * 0.28
|
|
1106
|
+
let text_x = center_x - text_width / 2
|
|
1107
|
+
|
|
1108
|
+
context.save()
|
|
1109
|
+
context.textAlign = `left`
|
|
1110
|
+
context.textBaseline = `top`
|
|
1111
|
+
for (const segment of segments) {
|
|
1112
|
+
context.font = segment.subscript ? hull_label_subscript_font : hull_label_font
|
|
1113
|
+
context.fillText(
|
|
1114
|
+
segment.text,
|
|
1115
|
+
text_x,
|
|
1116
|
+
top_y + (segment.subscript ? subscript_offset : 0),
|
|
1117
|
+
)
|
|
1118
|
+
text_x += context.measureText(segment.text).width
|
|
1119
|
+
}
|
|
1120
|
+
context.restore()
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
function draw_hull_labels(): void {
|
|
1124
|
+
if (!ctx || !merged_config.show_labels) return
|
|
1125
|
+
|
|
1126
|
+
ctx.fillStyle = text_color
|
|
1127
|
+
ctx.font = hull_label_font
|
|
1128
|
+
ctx.textAlign = `center`
|
|
1129
|
+
ctx.textBaseline = `top`
|
|
1130
|
+
const label_height = hull_label_font_size + 2
|
|
1131
|
+
|
|
1132
|
+
const label_entries = helpers.get_composition_label_entries(
|
|
1133
|
+
visible_entries.filter((entry) => {
|
|
1134
|
+
if (entry.is_element) return false
|
|
1135
|
+
const is_stable_point = helpers.entry_is_stable(entry)
|
|
1136
|
+
return (is_stable_point && show_stable_labels) ||
|
|
1137
|
+
(!is_stable_point && show_unstable_labels &&
|
|
1138
|
+
(entry.e_above_hull ?? 0) <= max_hull_dist_show_labels)
|
|
1139
|
+
}),
|
|
1140
|
+
)
|
|
1141
|
+
.sort((entry_1, entry_2) => {
|
|
1142
|
+
const energy_diff = label_priority_energy(entry_1) -
|
|
1143
|
+
label_priority_energy(entry_2)
|
|
1144
|
+
if (energy_diff !== 0) return energy_diff
|
|
1145
|
+
return (entry_1.e_above_hull ?? 0) - (entry_2.e_above_hull ?? 0)
|
|
1146
|
+
})
|
|
1147
|
+
|
|
1148
|
+
const occupied_rects: Rect[] = []
|
|
1149
|
+
const canvas_rect: Rect = {
|
|
1150
|
+
x: 0,
|
|
1151
|
+
y: 0,
|
|
1152
|
+
width: canvas_dims.width,
|
|
1153
|
+
height: canvas_dims.height,
|
|
1154
|
+
}
|
|
1155
|
+
for (const entry of label_entries) {
|
|
1156
|
+
const projected = project_3d_point(entry.x, entry.y, entry.z)
|
|
1157
|
+
const formula_segments = get_formula_label_segments(
|
|
1158
|
+
helpers.get_entry_label(entry, elements),
|
|
1159
|
+
)
|
|
1160
|
+
const is_stable_point = helpers.entry_is_stable(entry)
|
|
1161
|
+
const point_size = (entry.size || (is_stable_point ? 6 : 4)) * canvas_dims.scale
|
|
1162
|
+
const text_width = measure_formula_segments(ctx, formula_segments)
|
|
1163
|
+
const placements = get_label_placements(
|
|
1164
|
+
projected,
|
|
1165
|
+
point_size,
|
|
1166
|
+
text_width,
|
|
1167
|
+
label_height,
|
|
1168
|
+
)
|
|
1169
|
+
const placement = placements.find((candidate) =>
|
|
1170
|
+
rect_within_rect(candidate.rect, canvas_rect) &&
|
|
1171
|
+
!occupied_rects.some((occupied_rect) =>
|
|
1172
|
+
rects_overlap(candidate.rect, occupied_rect)
|
|
1173
|
+
)
|
|
1174
|
+
)
|
|
1175
|
+
if (!placement) continue
|
|
1176
|
+
|
|
1177
|
+
occupied_rects.push(placement.rect)
|
|
1178
|
+
draw_formula_segments(ctx, formula_segments, placement.x, placement.y, text_width)
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
function render_frame(): void {
|
|
1183
|
+
if (!ctx || !canvas) return
|
|
1184
|
+
|
|
1185
|
+
// Use CSS dimensions for rendering
|
|
1186
|
+
const display_width = canvas.clientWidth || 600
|
|
1187
|
+
const display_height = canvas.clientHeight || 600
|
|
1188
|
+
|
|
1189
|
+
// Clear canvas
|
|
1190
|
+
ctx.clearRect(0, 0, display_width, display_height)
|
|
1191
|
+
|
|
1192
|
+
// Set background - use transparent to inherit from container
|
|
1193
|
+
ctx.fillStyle = `transparent`
|
|
1194
|
+
ctx.fillRect(0, 0, display_width, display_height)
|
|
1195
|
+
|
|
1196
|
+
if (elements.length !== 3) {
|
|
1197
|
+
if (elements.length > 0) {
|
|
1198
|
+
ctx.fillStyle = text_color
|
|
1199
|
+
ctx.font = `16px Arial`
|
|
1200
|
+
ctx.textAlign = `center`
|
|
1201
|
+
ctx.textBaseline = `middle`
|
|
1202
|
+
ctx.fillText(
|
|
1203
|
+
`Ternary convex hull requires exactly 3 elements (got ${elements.length})`,
|
|
1204
|
+
display_width / 2,
|
|
1205
|
+
display_height / 2,
|
|
1206
|
+
)
|
|
1207
|
+
}
|
|
1208
|
+
return
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
draw_structure_outline()
|
|
1212
|
+
draw_convex_hull_faces() // behind points
|
|
1213
|
+
draw_z_axis_ticks() // after faces for visibility at high opacity
|
|
1214
|
+
draw_data_points()
|
|
1215
|
+
draw_hull_labels()
|
|
1216
|
+
draw_element_labels()
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
function handle_mouse_down(event: MouseEvent) {
|
|
1220
|
+
is_dragging = true
|
|
1221
|
+
drag_started = false
|
|
1222
|
+
hover_data = null
|
|
1223
|
+
on_point_hover?.(null)
|
|
1224
|
+
last_mouse = { x: event.clientX, y: event.clientY }
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
const handle_mouse_move = (event: MouseEvent) => {
|
|
1228
|
+
if (!is_dragging) return
|
|
1229
|
+
const [dx, dy] = [event.clientX - last_mouse.x, event.clientY - last_mouse.y]
|
|
1230
|
+
|
|
1231
|
+
// Mark as drag if any movement occurred
|
|
1232
|
+
if (dx !== 0 || dy !== 0) drag_started = true
|
|
1233
|
+
|
|
1234
|
+
// With Cmd/Ctrl held: pan the view instead of rotating
|
|
1235
|
+
if (event.metaKey || event.ctrlKey) {
|
|
1236
|
+
camera.center_x += dx
|
|
1237
|
+
camera.center_y += dy
|
|
1238
|
+
} else {
|
|
1239
|
+
// Horizontal drag -> azimuth rotation around z-axis
|
|
1240
|
+
camera.azimuth += dx * 0.3 // Positive dx (drag right) rotates clockwise
|
|
1241
|
+
|
|
1242
|
+
// Vertical drag -> elevation angle (full range)
|
|
1243
|
+
camera.elevation -= dy * 0.3 // Positive dy (drag down) tilts view down
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
last_mouse = { x: event.clientX, y: event.clientY }
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
const handle_wheel = (event: WheelEvent) => {
|
|
1250
|
+
event.preventDefault()
|
|
1251
|
+
camera.zoom = Math.max(
|
|
1252
|
+
0.5,
|
|
1253
|
+
Math.min(10, camera.zoom * (event.deltaY > 0 ? 0.98 : 1.02)),
|
|
1254
|
+
)
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
const handle_hover = (event: MouseEvent) => {
|
|
1258
|
+
if (is_dragging) return
|
|
1259
|
+
const entry = find_entry_at_mouse(event)
|
|
1260
|
+
hover_data = entry
|
|
1261
|
+
? { entry, position: { x: event.clientX, y: event.clientY } }
|
|
1262
|
+
: null
|
|
1263
|
+
on_point_hover?.(hover_data)
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
const find_entry_at_mouse = (event: MouseEvent): ConvexHullEntry | null =>
|
|
1267
|
+
helpers.find_hull_entry_at_mouse(
|
|
1268
|
+
canvas,
|
|
1269
|
+
event,
|
|
1270
|
+
visible_entries,
|
|
1271
|
+
(x: number, y: number, z: number) => {
|
|
1272
|
+
const pt = project_3d_point(x, y, z)
|
|
1273
|
+
return { x: pt.x, y: pt.y }
|
|
1274
|
+
},
|
|
1275
|
+
)
|
|
1276
|
+
|
|
1277
|
+
const handle_click = (event: MouseEvent) => {
|
|
1278
|
+
event.stopPropagation()
|
|
1279
|
+
// Check if this was a drag operation (any mouse movement during drag)
|
|
1280
|
+
const was_drag = drag_started
|
|
1281
|
+
drag_started = false // Reset for next interaction
|
|
1282
|
+
if (was_drag) return // Don't trigger click if this was a drag
|
|
1283
|
+
|
|
1284
|
+
const entry = find_entry_at_mouse(event)
|
|
1285
|
+
if (!entry) {
|
|
1286
|
+
if (modal_open) close_structure_popup()
|
|
1287
|
+
return
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
on_point_click?.(entry)
|
|
1291
|
+
|
|
1292
|
+
if (enable_click_selection) {
|
|
1293
|
+
selected_entry = entry
|
|
1294
|
+
if (enable_structure_preview) {
|
|
1295
|
+
const structure = extract_structure_from_entry(entry)
|
|
1296
|
+
if (structure) {
|
|
1297
|
+
selected_structure = structure
|
|
1298
|
+
modal_place_right = helpers.calculate_modal_side(wrapper)
|
|
1299
|
+
modal_open = true
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
function close_structure_popup() {
|
|
1306
|
+
modal_open = false
|
|
1307
|
+
selected_structure = null
|
|
1308
|
+
selected_entry = null
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
const handle_double_click = (event: MouseEvent) => {
|
|
1312
|
+
const entry = find_entry_at_mouse(event)
|
|
1313
|
+
if (entry) {
|
|
1314
|
+
copy_entry_data(entry, {
|
|
1315
|
+
x: event.clientX,
|
|
1316
|
+
y: event.clientY,
|
|
1317
|
+
})
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
function render_once() {
|
|
1322
|
+
if (!frame_id) {
|
|
1323
|
+
frame_id = requestAnimationFrame(() => {
|
|
1324
|
+
render_frame()
|
|
1325
|
+
frame_id = 0
|
|
1326
|
+
})
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
function update_canvas_size() {
|
|
1331
|
+
if (!canvas) return
|
|
1332
|
+
const dpr = globalThis.devicePixelRatio || 1
|
|
1333
|
+
const container = canvas.parentElement
|
|
1334
|
+
const rect = container?.getBoundingClientRect()
|
|
1335
|
+
const [w, h] = rect ? [rect.width, rect.height] : [400, 400]
|
|
1336
|
+
|
|
1337
|
+
// Only update canvas dimensions if they actually changed
|
|
1338
|
+
// (assigning canvas.width/height clears the canvas even if values are the same)
|
|
1339
|
+
const new_width = Math.max(0, Math.round(w * dpr))
|
|
1340
|
+
const new_height = Math.max(0, Math.round(h * dpr))
|
|
1341
|
+
if (!ctx || canvas.width !== new_width || canvas.height !== new_height) {
|
|
1342
|
+
canvas.width = new_width
|
|
1343
|
+
canvas.height = new_height
|
|
1344
|
+
ctx = canvas.getContext(`2d`)
|
|
1345
|
+
if (ctx) {
|
|
1346
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
|
|
1347
|
+
ctx.imageSmoothingEnabled = true
|
|
1348
|
+
ctx.imageSmoothingQuality = `high`
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
canvas_dims = { width: w, height: h, scale: Math.min(w, h) / 600 }
|
|
1352
|
+
render_once()
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
// Reactive dark mode detection for canvas text color
|
|
1356
|
+
let dark_mode = $state(is_dark_mode())
|
|
1357
|
+
$effect(() => watch_dark_mode((dark) => dark_mode = dark))
|
|
1358
|
+
const text_color = $derived(helpers.get_canvas_text_color(dark_mode))
|
|
1359
|
+
|
|
1360
|
+
$effect(() => {
|
|
1361
|
+
if (!canvas) return
|
|
1362
|
+
|
|
1363
|
+
// Initial setup
|
|
1364
|
+
update_canvas_size()
|
|
1365
|
+
|
|
1366
|
+
// Watch for resize events - only update canvas, don't reset camera
|
|
1367
|
+
const resize_observer = new ResizeObserver(update_canvas_size)
|
|
1368
|
+
|
|
1369
|
+
const container = canvas.parentElement
|
|
1370
|
+
if (container) {
|
|
1371
|
+
resize_observer.observe(container)
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
return () => { // Cleanup on unmount
|
|
1375
|
+
if (frame_id) cancelAnimationFrame(frame_id)
|
|
1376
|
+
resize_observer.disconnect()
|
|
1377
|
+
}
|
|
1378
|
+
})
|
|
1379
|
+
|
|
1380
|
+
// Fullscreen handling with camera reset
|
|
1381
|
+
let was_fullscreen = $state(fullscreen)
|
|
1382
|
+
$effect(() => {
|
|
1383
|
+
setup_fullscreen_effect(fullscreen, wrapper, (entering_fullscreen) => {
|
|
1384
|
+
if (entering_fullscreen !== was_fullscreen) {
|
|
1385
|
+
camera.center_x = 0
|
|
1386
|
+
camera.center_y = -50
|
|
1387
|
+
was_fullscreen = entering_fullscreen
|
|
1388
|
+
}
|
|
1389
|
+
})
|
|
1390
|
+
set_fullscreen_bg(wrapper, fullscreen, `--hull-3d-bg-fullscreen`)
|
|
1391
|
+
})
|
|
1392
|
+
|
|
1393
|
+
// Performance: Cache canvas dimensions and formation energy range
|
|
1394
|
+
let canvas_dims = $state({ width: 600, height: 600, scale: 1 })
|
|
1395
|
+
const energy_range = $derived.by(() => {
|
|
1396
|
+
let min = 0
|
|
1397
|
+
let max = 0
|
|
1398
|
+
for (const entry of all_enriched_entries) {
|
|
1399
|
+
const energy = entry.e_form_per_atom ?? 0
|
|
1400
|
+
min = Math.min(min, energy)
|
|
1401
|
+
max = Math.max(max, energy)
|
|
1402
|
+
}
|
|
1403
|
+
const z_scale = 0.75 / Math.max(max - min, 0.001)
|
|
1404
|
+
return { min, max, center: (min + max) / 2, z_scale }
|
|
1405
|
+
})
|
|
1406
|
+
|
|
1407
|
+
// Performance: Pre-compute and cache all point projections + depth sorting
|
|
1408
|
+
const sorted_points_cache = $derived.by(() => {
|
|
1409
|
+
if (!canvas || visible_entries.length === 0) return []
|
|
1410
|
+
return visible_entries
|
|
1411
|
+
.map((entry) => ({
|
|
1412
|
+
entry,
|
|
1413
|
+
projected: project_3d_point(entry.x, entry.y, entry.z),
|
|
1414
|
+
}))
|
|
1415
|
+
.sort((left, right) => left.projected.depth - right.projected.depth)
|
|
1416
|
+
})
|
|
1417
|
+
|
|
1418
|
+
let style = $derived(
|
|
1419
|
+
`--hull-stable-color:${merged_config.colors?.stable || `#0072B2`};
|
|
1420
|
+
--hull-unstable-color:${merged_config.colors?.unstable || `#E69F00`};
|
|
1421
|
+
--hull-edge-color:${merged_config.colors?.edge || `var(--text-color, #212121)`};
|
|
1422
|
+
--hull-text-color:${
|
|
1423
|
+
merged_config.colors?.annotation || `var(--text-color, #212121)`
|
|
1424
|
+
}`,
|
|
1425
|
+
)
|
|
1426
|
+
</script>
|
|
1427
|
+
|
|
1428
|
+
<svelte:document
|
|
1429
|
+
onfullscreenchange={() => {
|
|
1430
|
+
fullscreen = Boolean(document.fullscreenElement)
|
|
1431
|
+
}}
|
|
1432
|
+
onmousemove={handle_mouse_move}
|
|
1433
|
+
onmouseup={() => ([is_dragging, drag_started] = [false, false])}
|
|
1434
|
+
/>
|
|
1435
|
+
|
|
1436
|
+
<div
|
|
1437
|
+
{...rest}
|
|
1438
|
+
class="convex-hull-3d {rest.class ?? ``}"
|
|
1439
|
+
class:dragover={drag_over}
|
|
1440
|
+
style={`${style}; ${rest.style ?? ``}`}
|
|
1441
|
+
data-has-selection={selected_entry !== null}
|
|
1442
|
+
data-has-hover={hover_data !== null}
|
|
1443
|
+
data-is-dragging={is_dragging}
|
|
1444
|
+
bind:this={wrapper}
|
|
1445
|
+
role="application"
|
|
1446
|
+
tabindex="-1"
|
|
1447
|
+
onkeydown={handle_keydown}
|
|
1448
|
+
ondrop={handle_file_drop}
|
|
1449
|
+
ondragover={(event) => {
|
|
1450
|
+
event.preventDefault()
|
|
1451
|
+
drag_over = true
|
|
1452
|
+
}}
|
|
1453
|
+
ondragleave={(event) => {
|
|
1454
|
+
event.preventDefault()
|
|
1455
|
+
drag_over = false
|
|
1456
|
+
}}
|
|
1457
|
+
aria-label="Ternary convex hull visualization"
|
|
1458
|
+
>
|
|
1459
|
+
{@render children?.({
|
|
1460
|
+
stable_entries,
|
|
1461
|
+
unstable_entries,
|
|
1462
|
+
highlighted_entries,
|
|
1463
|
+
selected_entry,
|
|
1464
|
+
})}
|
|
1465
|
+
<h3 style="position: absolute; left: 1em; top: 1ex; margin: 0; font-weight: 500">
|
|
1466
|
+
{@html sanitize_html(merged_controls.title || phase_stats?.chemical_system || ``)}
|
|
1467
|
+
</h3>
|
|
1468
|
+
<canvas
|
|
1469
|
+
bind:this={canvas}
|
|
1470
|
+
tabindex="0"
|
|
1471
|
+
aria-label={merged_controls.title || phase_stats?.chemical_system || `3D Convex Hull`}
|
|
1472
|
+
onmousedown={handle_mouse_down}
|
|
1473
|
+
onmousemove={handle_hover}
|
|
1474
|
+
onclick={handle_click}
|
|
1475
|
+
onkeydown={handle_keydown}
|
|
1476
|
+
ondblclick={handle_double_click}
|
|
1477
|
+
onwheel={handle_wheel}
|
|
1478
|
+
></canvas>
|
|
1479
|
+
|
|
1480
|
+
{#if entries.length === 0}
|
|
1481
|
+
<Spinner
|
|
1482
|
+
text="Loading data..."
|
|
1483
|
+
style="position: absolute; inset: 0; display: flex; align-items: center; justify-content: center"
|
|
1484
|
+
/>
|
|
1485
|
+
{/if}
|
|
1486
|
+
|
|
1487
|
+
<!-- Formation Energy Color Bar (bottom-left corner) -->
|
|
1488
|
+
{#if color_mode === `energy` && plot_entries.length > 0}
|
|
1489
|
+
{@const hull_distances = plot_entries
|
|
1490
|
+
.map((entry) => entry.e_above_hull)
|
|
1491
|
+
.filter((value): value is number => typeof value === `number`)}
|
|
1492
|
+
{@const min_energy = hull_distances.length > 0 ? Math.min(...hull_distances) : 0}
|
|
1493
|
+
{@const max_energy = hull_distances.length > 0 ? Math.max(...hull_distances, 0.1) : 0.1}
|
|
1494
|
+
<ColorBar
|
|
1495
|
+
title="Energy above hull (eV/atom)"
|
|
1496
|
+
range={[min_energy, max_energy]}
|
|
1497
|
+
{color_scale}
|
|
1498
|
+
wrapper_style="position: absolute; bottom: 16px; left: 1em; width: 200px;"
|
|
1499
|
+
bar_style="height: 12px;"
|
|
1500
|
+
title_style="margin-bottom: 4px;"
|
|
1501
|
+
/>
|
|
1502
|
+
{/if}
|
|
1503
|
+
|
|
1504
|
+
<!-- Formation Energy Faces Color Bar (bottom-right corner) -->
|
|
1505
|
+
<!-- Only show for uniform/formation_energy modes where face color relates to E_form -->
|
|
1506
|
+
{#if plot_entries.length > 0 && show_hull_faces &&
|
|
1507
|
+
(hull_face_color_mode === `uniform` ||
|
|
1508
|
+
hull_face_color_mode === `formation_energy`)}
|
|
1509
|
+
<ColorBar
|
|
1510
|
+
title="Formation energy (eV/atom)"
|
|
1511
|
+
color_scale_fn={e_form_color_scale_fn}
|
|
1512
|
+
color_scale_domain={e_form_range}
|
|
1513
|
+
range={e_form_range}
|
|
1514
|
+
wrapper_style="position: absolute; bottom: 16px; right: 1em; width: 200px;"
|
|
1515
|
+
bar_style="height: 12px;"
|
|
1516
|
+
title_style="margin-bottom: 4px;"
|
|
1517
|
+
/>
|
|
1518
|
+
{/if}
|
|
1519
|
+
|
|
1520
|
+
<!-- Control buttons (top-right corner) -->
|
|
1521
|
+
{#if controls_config.mode !== `never`}
|
|
1522
|
+
<section class="control-buttons {controls_config.class}">
|
|
1523
|
+
{#if controls_config.visible(`reset`)}
|
|
1524
|
+
<button
|
|
1525
|
+
type="button"
|
|
1526
|
+
onclick={reset_all}
|
|
1527
|
+
title="Reset view and settings"
|
|
1528
|
+
class="reset-camera-btn"
|
|
1529
|
+
>
|
|
1530
|
+
<Icon icon="Reset" />
|
|
1531
|
+
</button>
|
|
1532
|
+
{/if}
|
|
1533
|
+
|
|
1534
|
+
{#if enable_info_pane && phase_stats && controls_config.visible(`info-pane`)}
|
|
1535
|
+
<ConvexHullInfoPane
|
|
1536
|
+
bind:pane_open={info_pane_open}
|
|
1537
|
+
{phase_stats}
|
|
1538
|
+
{stable_entries}
|
|
1539
|
+
{unstable_entries}
|
|
1540
|
+
{show_stable}
|
|
1541
|
+
{show_unstable}
|
|
1542
|
+
{max_hull_dist_show_phases}
|
|
1543
|
+
{max_hull_dist_show_labels}
|
|
1544
|
+
{label_threshold}
|
|
1545
|
+
toggle_props={{ class: `info-btn` }}
|
|
1546
|
+
/>
|
|
1547
|
+
{/if}
|
|
1548
|
+
|
|
1549
|
+
{#if enable_fullscreen && controls_config.visible(`fullscreen`)}
|
|
1550
|
+
<button
|
|
1551
|
+
type="button"
|
|
1552
|
+
onclick={() => toggle_fullscreen(wrapper)}
|
|
1553
|
+
title="{fullscreen ? `Exit` : `Enter`} fullscreen"
|
|
1554
|
+
class="fullscreen-btn"
|
|
1555
|
+
>
|
|
1556
|
+
<Icon icon="{fullscreen ? `Exit` : ``}Fullscreen" />
|
|
1557
|
+
</button>
|
|
1558
|
+
{/if}
|
|
1559
|
+
|
|
1560
|
+
<!-- Legend controls pane -->
|
|
1561
|
+
{#if controls_config.visible(`controls`)}
|
|
1562
|
+
<ConvexHullControls
|
|
1563
|
+
bind:controls_open={legend_pane_open}
|
|
1564
|
+
bind:color_mode
|
|
1565
|
+
bind:color_scale
|
|
1566
|
+
bind:show_stable
|
|
1567
|
+
bind:show_unstable
|
|
1568
|
+
bind:show_stable_labels
|
|
1569
|
+
bind:show_unstable_labels
|
|
1570
|
+
bind:max_hull_dist_show_phases
|
|
1571
|
+
bind:max_hull_dist_show_labels
|
|
1572
|
+
{max_hull_dist_in_data}
|
|
1573
|
+
{stable_entries}
|
|
1574
|
+
{unstable_entries}
|
|
1575
|
+
{camera}
|
|
1576
|
+
{merged_controls}
|
|
1577
|
+
toggle_props={{ class: `legend-controls-btn` }}
|
|
1578
|
+
{show_hull_faces}
|
|
1579
|
+
on_hull_faces_change={(value: boolean) => show_hull_faces = value}
|
|
1580
|
+
{hull_face_color}
|
|
1581
|
+
on_hull_face_color_change={(value: string) => hull_face_color = value}
|
|
1582
|
+
{hull_face_opacity}
|
|
1583
|
+
on_hull_face_opacity_change={(value: number) => hull_face_opacity = value}
|
|
1584
|
+
{hull_face_color_mode}
|
|
1585
|
+
on_hull_face_color_mode_change={(value: HullFaceColorMode) =>
|
|
1586
|
+
hull_face_color_mode = value}
|
|
1587
|
+
bind:energy_source_mode
|
|
1588
|
+
{has_precomputed_e_form}
|
|
1589
|
+
{can_compute_e_form}
|
|
1590
|
+
{has_precomputed_hull}
|
|
1591
|
+
{can_compute_hull}
|
|
1592
|
+
/>
|
|
1593
|
+
{/if}
|
|
1594
|
+
</section>
|
|
1595
|
+
{/if}
|
|
1596
|
+
|
|
1597
|
+
<!-- Orientation gizmo (configurable placement, default top-right) -->
|
|
1598
|
+
{#if gizmo && typeof WebGLRenderingContext !== `undefined`}
|
|
1599
|
+
<div class="gizmo-wrapper {controls_config.class}" data-placement={gizmo_placement}>
|
|
1600
|
+
<Canvas
|
|
1601
|
+
createRenderer={(cvs: HTMLCanvasElement) =>
|
|
1602
|
+
new WebGLRenderer({ canvas: cvs, alpha: true, antialias: true })}
|
|
1603
|
+
>
|
|
1604
|
+
<T.PerspectiveCamera
|
|
1605
|
+
makeDefault
|
|
1606
|
+
bind:ref={gizmo_cam_ref}
|
|
1607
|
+
position={gizmo_cam_state.position}
|
|
1608
|
+
up={gizmo_cam_state.up}
|
|
1609
|
+
fov={50}
|
|
1610
|
+
>
|
|
1611
|
+
<extras.OrbitControls
|
|
1612
|
+
bind:ref={gizmo_orbit_ref}
|
|
1613
|
+
enableRotate={false}
|
|
1614
|
+
enableZoom={false}
|
|
1615
|
+
enablePan={false}
|
|
1616
|
+
>
|
|
1617
|
+
<extras.Gizmo
|
|
1618
|
+
{...gizmo_props}
|
|
1619
|
+
onstart={() => (gizmo_active = true)}
|
|
1620
|
+
onchange={sync_gizmo_to_camera}
|
|
1621
|
+
onend={() => {
|
|
1622
|
+
sync_gizmo_to_camera()
|
|
1623
|
+
gizmo_active = false
|
|
1624
|
+
}}
|
|
1625
|
+
/>
|
|
1626
|
+
</extras.OrbitControls>
|
|
1627
|
+
</T.PerspectiveCamera>
|
|
1628
|
+
</Canvas>
|
|
1629
|
+
</div>
|
|
1630
|
+
{/if}
|
|
1631
|
+
|
|
1632
|
+
{#if (has_temp_data && temperature !== undefined) ||
|
|
1633
|
+
(gas_analysis.has_gas_dependent_elements && merged_gas_config)}
|
|
1634
|
+
<div class="right-controls">
|
|
1635
|
+
{#if has_temp_data && temperature !== undefined}
|
|
1636
|
+
<TemperatureSlider {available_temperatures} bind:temperature />
|
|
1637
|
+
{/if}
|
|
1638
|
+
{#if gas_analysis.has_gas_dependent_elements && merged_gas_config}
|
|
1639
|
+
<GasPressureControls
|
|
1640
|
+
config={merged_gas_config}
|
|
1641
|
+
bind:pressures={gas_pressures}
|
|
1642
|
+
temperature={temperature ?? 300}
|
|
1643
|
+
/>
|
|
1644
|
+
{/if}
|
|
1645
|
+
</div>
|
|
1646
|
+
{/if}
|
|
1647
|
+
|
|
1648
|
+
<!-- Hover tooltip -->
|
|
1649
|
+
{#if hover_data}
|
|
1650
|
+
{@const { entry, position } = hover_data}
|
|
1651
|
+
{@const entry_highlight = is_highlighted(entry) ? merged_highlight_style : undefined}
|
|
1652
|
+
{@const tooltip_style =
|
|
1653
|
+
`z-index: ${CONVEX_HULL_STYLE.z_index.tooltip}; backdrop-filter: blur(4px);
|
|
1654
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);`}
|
|
1655
|
+
<PlotTooltip
|
|
1656
|
+
x={position.x}
|
|
1657
|
+
y={position.y}
|
|
1658
|
+
offset={{ x: 10, y: -10 }}
|
|
1659
|
+
bg_color={get_point_color(entry)}
|
|
1660
|
+
fixed
|
|
1661
|
+
style={tooltip_style}
|
|
1662
|
+
>
|
|
1663
|
+
<ConvexHullTooltip
|
|
1664
|
+
{entry}
|
|
1665
|
+
{polymorph_stats_map}
|
|
1666
|
+
highlight_style={entry_highlight}
|
|
1667
|
+
{tooltip}
|
|
1668
|
+
/>
|
|
1669
|
+
</PlotTooltip>
|
|
1670
|
+
{/if}
|
|
1671
|
+
|
|
1672
|
+
<ClickFeedback bind:visible={copy_feedback.visible} position={copy_feedback.position} />
|
|
1673
|
+
<DragOverlay visible={drag_over} />
|
|
1674
|
+
|
|
1675
|
+
{#if modal_open && selected_structure}
|
|
1676
|
+
<StructurePopup
|
|
1677
|
+
structure={selected_structure}
|
|
1678
|
+
place_right={modal_place_right}
|
|
1679
|
+
stats={{
|
|
1680
|
+
id: selected_entry?.entry_id,
|
|
1681
|
+
e_above_hull: selected_entry?.e_above_hull,
|
|
1682
|
+
e_form: selected_entry?.e_form_per_atom,
|
|
1683
|
+
}}
|
|
1684
|
+
onclose={close_structure_popup}
|
|
1685
|
+
/>
|
|
1686
|
+
{/if}
|
|
1687
|
+
</div>
|
|
1688
|
+
|
|
1689
|
+
<style>
|
|
1690
|
+
.convex-hull-3d {
|
|
1691
|
+
position: relative;
|
|
1692
|
+
container-type: size; /* enable cqh/cqw for responsive sizing */
|
|
1693
|
+
width: 100%;
|
|
1694
|
+
height: var(--hull-height, 500px);
|
|
1695
|
+
background: var(--hull-3d-bg, var(--hull-bg));
|
|
1696
|
+
border-radius: var(--hull-border-radius, var(--border-radius, 3pt));
|
|
1697
|
+
}
|
|
1698
|
+
.convex-hull-3d:fullscreen {
|
|
1699
|
+
border-radius: 0;
|
|
1700
|
+
background: var(--hull-3d-bg-fullscreen, var(--hull-3d-bg, var(--hull-bg)));
|
|
1701
|
+
overflow: hidden;
|
|
1702
|
+
}
|
|
1703
|
+
.convex-hull-3d.dragover {
|
|
1704
|
+
border: 2px dashed var(--accent-color, #1976d2);
|
|
1705
|
+
}
|
|
1706
|
+
canvas {
|
|
1707
|
+
width: 100%;
|
|
1708
|
+
height: 100%;
|
|
1709
|
+
cursor: grab;
|
|
1710
|
+
}
|
|
1711
|
+
canvas:active {
|
|
1712
|
+
cursor: grabbing;
|
|
1713
|
+
}
|
|
1714
|
+
.right-controls {
|
|
1715
|
+
position: absolute;
|
|
1716
|
+
top: calc(1ex + 50px);
|
|
1717
|
+
right: 1ex;
|
|
1718
|
+
z-index: 2;
|
|
1719
|
+
pointer-events: auto;
|
|
1720
|
+
display: flex;
|
|
1721
|
+
flex-direction: column;
|
|
1722
|
+
align-items: flex-end;
|
|
1723
|
+
gap: 6px;
|
|
1724
|
+
}
|
|
1725
|
+
.right-controls :global(.temperature-slider),
|
|
1726
|
+
.right-controls :global(.pressure-controls) {
|
|
1727
|
+
position: static;
|
|
1728
|
+
}
|
|
1729
|
+
/* align both vertical range inputs at the same x position */
|
|
1730
|
+
.right-controls :global(.slider-wrapper) {
|
|
1731
|
+
justify-content: flex-end;
|
|
1732
|
+
}
|
|
1733
|
+
.gizmo-wrapper {
|
|
1734
|
+
position: absolute;
|
|
1735
|
+
width: clamp(80px, 18cqmin, 110px);
|
|
1736
|
+
height: clamp(80px, 18cqmin, 110px);
|
|
1737
|
+
pointer-events: auto;
|
|
1738
|
+
isolation: isolate; /* contain z-index: 1000 from three-viewport-gizmo overlay */
|
|
1739
|
+
transition: opacity 0.2s ease-in-out;
|
|
1740
|
+
}
|
|
1741
|
+
.gizmo-wrapper[data-placement='top-right'] {
|
|
1742
|
+
top: 1.8em;
|
|
1743
|
+
right: 1ex;
|
|
1744
|
+
}
|
|
1745
|
+
.gizmo-wrapper[data-placement='top-left'] {
|
|
1746
|
+
top: 1.8em;
|
|
1747
|
+
left: 1ex;
|
|
1748
|
+
}
|
|
1749
|
+
.gizmo-wrapper[data-placement='bottom-right'] {
|
|
1750
|
+
bottom: 2.5em;
|
|
1751
|
+
right: 1ex;
|
|
1752
|
+
}
|
|
1753
|
+
.gizmo-wrapper[data-placement='bottom-left'] {
|
|
1754
|
+
bottom: 2.5em;
|
|
1755
|
+
left: 1ex;
|
|
1756
|
+
}
|
|
1757
|
+
.gizmo-wrapper.hover-visible {
|
|
1758
|
+
opacity: 0;
|
|
1759
|
+
pointer-events: none;
|
|
1760
|
+
}
|
|
1761
|
+
.convex-hull-3d:hover .gizmo-wrapper.hover-visible,
|
|
1762
|
+
.convex-hull-3d:focus-within .gizmo-wrapper.hover-visible {
|
|
1763
|
+
opacity: 1;
|
|
1764
|
+
pointer-events: auto;
|
|
1765
|
+
}
|
|
1766
|
+
.control-buttons {
|
|
1767
|
+
position: absolute;
|
|
1768
|
+
top: 1ex;
|
|
1769
|
+
right: 1ex;
|
|
1770
|
+
display: flex;
|
|
1771
|
+
gap: 8px;
|
|
1772
|
+
transition: opacity 0.2s ease-in-out;
|
|
1773
|
+
}
|
|
1774
|
+
.control-buttons.hover-visible {
|
|
1775
|
+
opacity: 0;
|
|
1776
|
+
pointer-events: none;
|
|
1777
|
+
}
|
|
1778
|
+
.convex-hull-3d:hover .control-buttons.hover-visible,
|
|
1779
|
+
.convex-hull-3d:focus-within .control-buttons.hover-visible {
|
|
1780
|
+
opacity: 1;
|
|
1781
|
+
pointer-events: auto;
|
|
1782
|
+
}
|
|
1783
|
+
.control-buttons.always-visible {
|
|
1784
|
+
opacity: 1;
|
|
1785
|
+
pointer-events: auto;
|
|
1786
|
+
}
|
|
1787
|
+
.control-buttons :global(button) {
|
|
1788
|
+
background: transparent;
|
|
1789
|
+
border: none;
|
|
1790
|
+
padding: 4px;
|
|
1791
|
+
cursor: pointer;
|
|
1792
|
+
border-radius: 3px;
|
|
1793
|
+
color: var(--text-color, currentColor);
|
|
1794
|
+
transition: background-color 0.2s;
|
|
1795
|
+
display: flex;
|
|
1796
|
+
font-size: clamp(0.85em, 2cqmin, 1.3em);
|
|
1797
|
+
}
|
|
1798
|
+
.control-buttons :global(button):hover {
|
|
1799
|
+
background-color: color-mix(in srgb, currentColor 8%, transparent);
|
|
1800
|
+
}
|
|
1801
|
+
</style>
|