matterviz 0.3.6 → 0.3.7

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.
Files changed (863) hide show
  1. package/dist/EmptyState.svelte.d.ts +9 -0
  2. package/dist/FilePicker.svelte +360 -0
  3. package/dist/FilePicker.svelte.d.ts +17 -0
  4. package/dist/Icon.svelte.d.ts +13 -0
  5. package/dist/MillerIndexInput.svelte +66 -0
  6. package/dist/MillerIndexInput.svelte.d.ts +7 -0
  7. package/dist/api/mp.d.ts +6 -0
  8. package/dist/api/mp.js +22 -0
  9. package/dist/api/optimade.d.ts +45 -0
  10. package/dist/api/optimade.js +135 -0
  11. package/dist/brillouin/BrillouinZone.svelte +549 -0
  12. package/dist/brillouin/BrillouinZone.svelte.d.ts +83 -0
  13. package/dist/brillouin/BrillouinZoneControls.svelte +144 -0
  14. package/dist/brillouin/BrillouinZoneControls.svelte.d.ts +17 -0
  15. package/dist/brillouin/BrillouinZoneExportPane.svelte +146 -0
  16. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +15 -0
  17. package/dist/brillouin/BrillouinZoneInfoPane.svelte +146 -0
  18. package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +13 -0
  19. package/dist/brillouin/BrillouinZoneScene.svelte +476 -0
  20. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +48 -0
  21. package/dist/brillouin/BrillouinZoneTooltip.svelte +92 -0
  22. package/dist/brillouin/BrillouinZoneTooltip.svelte.d.ts +8 -0
  23. package/dist/brillouin/compute.d.ts +17 -0
  24. package/dist/brillouin/compute.js +426 -0
  25. package/dist/brillouin/index.d.ts +8 -0
  26. package/dist/brillouin/index.js +7 -0
  27. package/dist/brillouin/types.d.ts +43 -0
  28. package/dist/brillouin/types.js +1 -0
  29. package/dist/chempot-diagram/ChemPotDiagram.svelte +327 -0
  30. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  31. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +846 -0
  32. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  33. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +3193 -0
  34. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  35. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  36. package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
  37. package/dist/chempot-diagram/async-compute.svelte.js +78 -0
  38. package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
  39. package/dist/chempot-diagram/chempot-worker.js +11 -0
  40. package/dist/chempot-diagram/color.d.ts +10 -0
  41. package/dist/chempot-diagram/color.js +32 -0
  42. package/dist/chempot-diagram/compute.d.ts +48 -0
  43. package/dist/chempot-diagram/compute.js +806 -0
  44. package/dist/chempot-diagram/index.d.ts +6 -0
  45. package/dist/chempot-diagram/index.js +6 -0
  46. package/dist/chempot-diagram/pointer.d.ts +16 -0
  47. package/dist/chempot-diagram/pointer.js +40 -0
  48. package/dist/chempot-diagram/temperature.d.ts +15 -0
  49. package/dist/chempot-diagram/temperature.js +34 -0
  50. package/dist/chempot-diagram/types.d.ts +81 -0
  51. package/dist/chempot-diagram/types.js +28 -0
  52. package/dist/colors/index.d.ts +47 -0
  53. package/dist/colors/index.js +203 -0
  54. package/dist/composition/BarChart.svelte +297 -0
  55. package/dist/composition/BarChart.svelte.d.ts +39 -0
  56. package/dist/composition/BubbleChart.svelte +218 -0
  57. package/dist/composition/BubbleChart.svelte.d.ts +28 -0
  58. package/dist/composition/Composition.svelte +165 -0
  59. package/dist/composition/Composition.svelte.d.ts +15 -0
  60. package/dist/composition/Formula.svelte +268 -0
  61. package/dist/composition/Formula.svelte.d.ts +19 -0
  62. package/dist/composition/FormulaFilter.svelte +1257 -0
  63. package/dist/composition/FormulaFilter.svelte.d.ts +51 -0
  64. package/dist/composition/PieChart.svelte +323 -0
  65. package/dist/composition/PieChart.svelte.d.ts +37 -0
  66. package/dist/composition/format.d.ts +15 -0
  67. package/dist/composition/format.js +109 -0
  68. package/dist/composition/index.d.ts +20 -0
  69. package/dist/composition/index.js +14 -0
  70. package/dist/composition/parse.d.ts +56 -0
  71. package/dist/composition/parse.js +474 -0
  72. package/dist/constants.d.ts +29 -0
  73. package/dist/constants.js +99 -0
  74. package/dist/controls.d.ts +14 -0
  75. package/dist/controls.js +30 -0
  76. package/dist/convex-hull/ConvexHull.svelte +157 -0
  77. package/dist/convex-hull/ConvexHull.svelte.d.ts +13 -0
  78. package/dist/convex-hull/ConvexHull2D.svelte +825 -0
  79. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +11 -0
  80. package/dist/convex-hull/ConvexHull3D.svelte +1801 -0
  81. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +8 -0
  82. package/dist/convex-hull/ConvexHull4D.svelte +1398 -0
  83. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +8 -0
  84. package/dist/convex-hull/ConvexHullControls.svelte +535 -0
  85. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +48 -0
  86. package/dist/convex-hull/ConvexHullInfoPane.svelte +125 -0
  87. package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +20 -0
  88. package/dist/convex-hull/ConvexHullStats.svelte +929 -0
  89. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +17 -0
  90. package/dist/convex-hull/ConvexHullTooltip.svelte +131 -0
  91. package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +33 -0
  92. package/dist/convex-hull/GasPressureControls.svelte +247 -0
  93. package/dist/convex-hull/GasPressureControls.svelte.d.ts +11 -0
  94. package/dist/convex-hull/StructurePopup.svelte +151 -0
  95. package/dist/convex-hull/StructurePopup.svelte.d.ts +18 -0
  96. package/dist/convex-hull/TemperatureSlider.svelte.d.ts +8 -0
  97. package/dist/convex-hull/barycentric-coords.d.ts +18 -0
  98. package/dist/convex-hull/barycentric-coords.js +182 -0
  99. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  100. package/dist/convex-hull/demo-temperature.js +40 -0
  101. package/dist/convex-hull/gas-thermodynamics.d.ts +16 -0
  102. package/dist/convex-hull/gas-thermodynamics.js +314 -0
  103. package/dist/convex-hull/helpers.d.ts +114 -0
  104. package/dist/convex-hull/helpers.js +710 -0
  105. package/dist/convex-hull/index.d.ts +119 -0
  106. package/dist/convex-hull/index.js +58 -0
  107. package/dist/convex-hull/thermodynamics.d.ts +67 -0
  108. package/dist/convex-hull/thermodynamics.js +1752 -0
  109. package/dist/convex-hull/types.d.ts +162 -0
  110. package/dist/convex-hull/types.js +36 -0
  111. package/dist/coordination/CoordinationBarPlot.svelte +311 -0
  112. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +30 -0
  113. package/dist/coordination/calc-coordination.d.ts +15 -0
  114. package/dist/coordination/calc-coordination.js +63 -0
  115. package/dist/coordination/index.d.ts +8 -0
  116. package/dist/coordination/index.js +7 -0
  117. package/dist/effects.svelte.d.ts +12 -0
  118. package/dist/effects.svelte.js +37 -0
  119. package/dist/element/BohrAtom.svelte.d.ts +20 -0
  120. package/dist/element/ElementHeading.svelte +26 -0
  121. package/dist/element/ElementHeading.svelte.d.ts +8 -0
  122. package/dist/element/ElementPhoto.svelte +57 -0
  123. package/dist/element/ElementPhoto.svelte.d.ts +9 -0
  124. package/dist/element/ElementStats.svelte +80 -0
  125. package/dist/element/ElementStats.svelte.d.ts +8 -0
  126. package/dist/element/ElementTile.svelte +484 -0
  127. package/dist/element/ElementTile.svelte.d.ts +29 -0
  128. package/dist/element/Nucleus.svelte.d.ts +17 -0
  129. package/dist/element/data.d.ts +2 -0
  130. package/dist/element/data.js +2 -0
  131. package/dist/element/index.d.ts +8 -0
  132. package/dist/element/index.js +7 -0
  133. package/dist/element/types.d.ts +57 -0
  134. package/dist/element/types.js +1 -0
  135. package/dist/feedback/ClickFeedback.svelte +58 -0
  136. package/dist/feedback/ClickFeedback.svelte.d.ts +12 -0
  137. package/dist/feedback/DragOverlay.svelte +42 -0
  138. package/dist/feedback/DragOverlay.svelte.d.ts +7 -0
  139. package/dist/feedback/Spinner.svelte.d.ts +7 -0
  140. package/dist/feedback/StatusMessage.svelte.d.ts +9 -0
  141. package/dist/feedback/index.d.ts +4 -0
  142. package/dist/feedback/index.js +4 -0
  143. package/dist/fermi-surface/FermiSlice.svelte +189 -0
  144. package/dist/fermi-surface/FermiSlice.svelte.d.ts +24 -0
  145. package/dist/fermi-surface/FermiSurface.svelte +600 -0
  146. package/dist/fermi-surface/FermiSurface.svelte.d.ts +83 -0
  147. package/dist/fermi-surface/FermiSurfaceControls.svelte +448 -0
  148. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +35 -0
  149. package/dist/fermi-surface/FermiSurfaceScene.svelte +794 -0
  150. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +50 -0
  151. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +111 -0
  152. package/dist/fermi-surface/FermiSurfaceTooltip.svelte.d.ts +8 -0
  153. package/dist/fermi-surface/compute.d.ts +5 -0
  154. package/dist/fermi-surface/compute.js +538 -0
  155. package/dist/fermi-surface/constants.d.ts +9 -0
  156. package/dist/fermi-surface/constants.js +27 -0
  157. package/dist/fermi-surface/export.d.ts +5 -0
  158. package/dist/fermi-surface/export.js +50 -0
  159. package/dist/fermi-surface/index.d.ts +12 -0
  160. package/dist/fermi-surface/index.js +13 -0
  161. package/dist/fermi-surface/marching-cubes.d.ts +2 -0
  162. package/dist/fermi-surface/marching-cubes.js +2 -0
  163. package/dist/fermi-surface/parse.d.ts +2 -0
  164. package/dist/fermi-surface/parse.js +491 -0
  165. package/dist/fermi-surface/symmetry.d.ts +3 -0
  166. package/dist/fermi-surface/symmetry.js +46 -0
  167. package/dist/fermi-surface/types.d.ts +110 -0
  168. package/dist/fermi-surface/types.js +4 -0
  169. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1545 -0
  170. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  171. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
  172. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +30 -0
  173. package/dist/heatmap-matrix/index.d.ts +53 -0
  174. package/dist/heatmap-matrix/index.js +100 -0
  175. package/dist/heatmap-matrix/shared.d.ts +2 -0
  176. package/dist/heatmap-matrix/shared.js +4 -0
  177. package/dist/icons.d.ts +569 -0
  178. package/dist/icons.js +648 -0
  179. package/dist/index.d.ts +39 -0
  180. package/dist/index.js +39 -0
  181. package/dist/io/decompress.d.ts +11 -0
  182. package/dist/io/decompress.js +74 -0
  183. package/dist/io/export.d.ts +16 -0
  184. package/dist/io/export.js +316 -0
  185. package/dist/io/fetch.d.ts +5 -0
  186. package/dist/io/fetch.js +39 -0
  187. package/dist/io/file-drop.d.ts +7 -0
  188. package/dist/io/file-drop.js +43 -0
  189. package/dist/io/index.d.ts +7 -0
  190. package/dist/io/index.js +6 -0
  191. package/dist/io/is-binary.d.ts +1 -0
  192. package/dist/io/is-binary.js +20 -0
  193. package/dist/io/types.d.ts +8 -0
  194. package/dist/io/types.js +1 -0
  195. package/dist/io/url-drop.d.ts +2 -0
  196. package/dist/io/url-drop.js +123 -0
  197. package/dist/isosurface/Isosurface.svelte +285 -0
  198. package/dist/isosurface/Isosurface.svelte.d.ts +8 -0
  199. package/dist/isosurface/IsosurfaceControls.svelte +277 -0
  200. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +9 -0
  201. package/dist/isosurface/index.d.ts +5 -0
  202. package/dist/isosurface/index.js +6 -0
  203. package/dist/isosurface/parse.d.ts +6 -0
  204. package/dist/isosurface/parse.js +553 -0
  205. package/dist/isosurface/slice.d.ts +11 -0
  206. package/dist/isosurface/slice.js +140 -0
  207. package/dist/isosurface/types.d.ts +56 -0
  208. package/dist/isosurface/types.js +227 -0
  209. package/dist/labels.d.ts +53 -0
  210. package/dist/labels.js +277 -0
  211. package/dist/layout/FullscreenToggle.svelte +50 -0
  212. package/dist/layout/FullscreenToggle.svelte.d.ts +7 -0
  213. package/dist/layout/InfoCard.svelte +120 -0
  214. package/dist/layout/InfoCard.svelte.d.ts +21 -0
  215. package/dist/layout/InfoTag.svelte +185 -0
  216. package/dist/layout/InfoTag.svelte.d.ts +19 -0
  217. package/dist/layout/PropertyFilter.svelte +246 -0
  218. package/dist/layout/PropertyFilter.svelte.d.ts +24 -0
  219. package/dist/layout/SettingsSection.svelte +148 -0
  220. package/dist/layout/SettingsSection.svelte.d.ts +17 -0
  221. package/dist/layout/SubpageGrid.svelte +82 -0
  222. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  223. package/dist/layout/fullscreen.d.ts +9 -0
  224. package/dist/layout/fullscreen.js +53 -0
  225. package/dist/layout/index.d.ts +10 -0
  226. package/dist/layout/index.js +8 -0
  227. package/dist/layout/json-tree/JsonNode.svelte +548 -0
  228. package/dist/layout/json-tree/JsonNode.svelte.d.ts +11 -0
  229. package/dist/layout/json-tree/JsonTree.svelte +1230 -0
  230. package/dist/layout/json-tree/JsonTree.svelte.d.ts +6 -0
  231. package/dist/layout/json-tree/JsonValue.svelte.d.ts +9 -0
  232. package/dist/layout/json-tree/index.d.ts +3 -0
  233. package/dist/layout/json-tree/index.js +3 -0
  234. package/dist/layout/json-tree/types.d.ts +74 -0
  235. package/dist/layout/json-tree/types.js +2 -0
  236. package/dist/layout/json-tree/utils.d.ts +29 -0
  237. package/dist/layout/json-tree/utils.js +641 -0
  238. package/dist/marching-cubes.d.ts +14 -0
  239. package/dist/marching-cubes.js +540 -0
  240. package/dist/math.d.ts +101 -0
  241. package/dist/math.js +905 -0
  242. package/dist/overlays/ContextMenu.svelte +162 -0
  243. package/dist/overlays/ContextMenu.svelte.d.ts +25 -0
  244. package/dist/overlays/CopyButton.svelte +45 -0
  245. package/dist/overlays/CopyButton.svelte.d.ts +8 -0
  246. package/dist/overlays/DragControlTab.svelte +98 -0
  247. package/dist/overlays/DragControlTab.svelte.d.ts +8 -0
  248. package/dist/overlays/DraggablePane.svelte +487 -0
  249. package/dist/overlays/DraggablePane.svelte.d.ts +36 -0
  250. package/dist/overlays/InfoPaneCards.svelte +149 -0
  251. package/dist/overlays/InfoPaneCards.svelte.d.ts +22 -0
  252. package/dist/overlays/index.d.ts +3 -0
  253. package/dist/overlays/index.js +3 -0
  254. package/dist/periodic-table/PeriodicTable.svelte +469 -0
  255. package/dist/periodic-table/PeriodicTable.svelte.d.ts +55 -0
  256. package/dist/periodic-table/PeriodicTableControls.svelte +557 -0
  257. package/dist/periodic-table/PeriodicTableControls.svelte.d.ts +24 -0
  258. package/dist/periodic-table/PropertySelect.svelte +37 -0
  259. package/dist/periodic-table/PropertySelect.svelte.d.ts +13 -0
  260. package/dist/periodic-table/TableInset.svelte.d.ts +9 -0
  261. package/dist/periodic-table/index.d.ts +10 -0
  262. package/dist/periodic-table/index.js +4 -0
  263. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +1086 -0
  264. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +44 -0
  265. package/dist/phase-diagram/PhaseDiagramControls.svelte +444 -0
  266. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +30 -0
  267. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +126 -0
  268. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  269. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +184 -0
  270. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +19 -0
  271. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +391 -0
  272. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +16 -0
  273. package/dist/phase-diagram/TdbInfoPanel.svelte +203 -0
  274. package/dist/phase-diagram/TdbInfoPanel.svelte.d.ts +12 -0
  275. package/dist/phase-diagram/build-diagram.d.ts +11 -0
  276. package/dist/phase-diagram/build-diagram.js +160 -0
  277. package/dist/phase-diagram/colors.d.ts +35 -0
  278. package/dist/phase-diagram/colors.js +51 -0
  279. package/dist/phase-diagram/diagram-input.d.ts +29 -0
  280. package/dist/phase-diagram/diagram-input.js +3 -0
  281. package/dist/phase-diagram/index.d.ts +13 -0
  282. package/dist/phase-diagram/index.js +11 -0
  283. package/dist/phase-diagram/parse.d.ts +55 -0
  284. package/dist/phase-diagram/parse.js +272 -0
  285. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  286. package/dist/phase-diagram/svg-to-diagram.js +867 -0
  287. package/dist/phase-diagram/types.d.ts +93 -0
  288. package/dist/phase-diagram/types.js +1 -0
  289. package/dist/phase-diagram/utils.d.ts +118 -0
  290. package/dist/phase-diagram/utils.js +604 -0
  291. package/dist/plot/AxisLabel.svelte +51 -0
  292. package/dist/plot/AxisLabel.svelte.d.ts +16 -0
  293. package/dist/plot/BarPlot.svelte +2113 -0
  294. package/dist/plot/BarPlot.svelte.d.ts +84 -0
  295. package/dist/plot/BarPlotControls.svelte +66 -0
  296. package/dist/plot/BarPlotControls.svelte.d.ts +18 -0
  297. package/dist/plot/BinnedScatterPlot.svelte +1114 -0
  298. package/dist/plot/BinnedScatterPlot.svelte.d.ts +66 -0
  299. package/dist/plot/ColorBar.svelte +721 -0
  300. package/dist/plot/ColorBar.svelte.d.ts +31 -0
  301. package/dist/plot/ColorScaleSelect.svelte +54 -0
  302. package/dist/plot/ColorScaleSelect.svelte.d.ts +15 -0
  303. package/dist/plot/ElementScatter.svelte +63 -0
  304. package/dist/plot/ElementScatter.svelte.d.ts +14 -0
  305. package/dist/plot/FillArea.svelte.d.ts +21 -0
  306. package/dist/plot/Histogram.svelte +1558 -0
  307. package/dist/plot/Histogram.svelte.d.ts +50 -0
  308. package/dist/plot/HistogramControls.svelte +212 -0
  309. package/dist/plot/HistogramControls.svelte.d.ts +22 -0
  310. package/dist/plot/InteractiveAxisLabel.svelte +96 -0
  311. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +14 -0
  312. package/dist/plot/Line.svelte +84 -0
  313. package/dist/plot/Line.svelte.d.ts +15 -0
  314. package/dist/plot/PlotAxis.svelte +169 -0
  315. package/dist/plot/PlotAxis.svelte.d.ts +24 -0
  316. package/dist/plot/PlotControls.svelte +537 -0
  317. package/dist/plot/PlotControls.svelte.d.ts +4 -0
  318. package/dist/plot/PlotLegend.svelte +569 -0
  319. package/dist/plot/PlotLegend.svelte.d.ts +29 -0
  320. package/dist/plot/PlotTooltip.svelte +67 -0
  321. package/dist/plot/PlotTooltip.svelte.d.ts +17 -0
  322. package/dist/plot/PortalSelect.svelte +253 -0
  323. package/dist/plot/PortalSelect.svelte.d.ts +16 -0
  324. package/dist/plot/ReferenceLine.svelte.d.ts +20 -0
  325. package/dist/plot/ReferenceLine3D.svelte +156 -0
  326. package/dist/plot/ReferenceLine3D.svelte.d.ts +14 -0
  327. package/dist/plot/ReferencePlane.svelte +175 -0
  328. package/dist/plot/ReferencePlane.svelte.d.ts +14 -0
  329. package/dist/plot/ScatterPlot.svelte +2778 -0
  330. package/dist/plot/ScatterPlot.svelte.d.ts +96 -0
  331. package/dist/plot/ScatterPlot3D.svelte +529 -0
  332. package/dist/plot/ScatterPlot3D.svelte.d.ts +95 -0
  333. package/dist/plot/ScatterPlot3DControls.svelte +437 -0
  334. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +20 -0
  335. package/dist/plot/ScatterPlot3DScene.svelte +912 -0
  336. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +74 -0
  337. package/dist/plot/ScatterPlotControls.svelte +306 -0
  338. package/dist/plot/ScatterPlotControls.svelte.d.ts +17 -0
  339. package/dist/plot/ScatterPoint.svelte +182 -0
  340. package/dist/plot/ScatterPoint.svelte.d.ts +22 -0
  341. package/dist/plot/SpacegroupBarPlot.svelte +293 -0
  342. package/dist/plot/SpacegroupBarPlot.svelte.d.ts +9 -0
  343. package/dist/plot/Surface3D.svelte +197 -0
  344. package/dist/plot/Surface3D.svelte.d.ts +13 -0
  345. package/dist/plot/ZeroLines.svelte +97 -0
  346. package/dist/plot/ZeroLines.svelte.d.ts +33 -0
  347. package/dist/plot/ZoomRect.svelte +23 -0
  348. package/dist/plot/ZoomRect.svelte.d.ts +8 -0
  349. package/dist/plot/adaptive-density.d.ts +69 -0
  350. package/dist/plot/adaptive-density.js +191 -0
  351. package/dist/plot/auto-place.d.ts +43 -0
  352. package/dist/plot/auto-place.js +122 -0
  353. package/dist/plot/axis-utils.d.ts +19 -0
  354. package/dist/plot/axis-utils.js +78 -0
  355. package/dist/plot/binned-scatter-types.d.ts +59 -0
  356. package/dist/plot/binned-scatter-types.js +1 -0
  357. package/dist/plot/data-cleaning.d.ts +37 -0
  358. package/dist/plot/data-cleaning.js +855 -0
  359. package/dist/plot/data-transform.d.ts +16 -0
  360. package/dist/plot/data-transform.js +45 -0
  361. package/dist/plot/defaults.d.ts +19 -0
  362. package/dist/plot/defaults.js +9 -0
  363. package/dist/plot/fill-utils.d.ts +46 -0
  364. package/dist/plot/fill-utils.js +322 -0
  365. package/dist/plot/hover-lock.svelte.d.ts +14 -0
  366. package/dist/plot/hover-lock.svelte.js +46 -0
  367. package/dist/plot/index.d.ts +41 -0
  368. package/dist/plot/index.js +39 -0
  369. package/dist/plot/interactions.d.ts +12 -0
  370. package/dist/plot/interactions.js +101 -0
  371. package/dist/plot/layout.d.ts +78 -0
  372. package/dist/plot/layout.js +273 -0
  373. package/dist/plot/reference-line.d.ts +60 -0
  374. package/dist/plot/reference-line.js +314 -0
  375. package/dist/plot/scales.d.ts +48 -0
  376. package/dist/plot/scales.js +481 -0
  377. package/dist/plot/svg.d.ts +1 -0
  378. package/dist/plot/svg.js +11 -0
  379. package/dist/plot/types.d.ts +831 -0
  380. package/dist/plot/types.js +99 -0
  381. package/dist/plot/utils/label-placement.d.ts +68 -0
  382. package/dist/plot/utils/label-placement.js +326 -0
  383. package/dist/plot/utils/series-visibility.d.ts +15 -0
  384. package/dist/plot/utils/series-visibility.js +85 -0
  385. package/dist/plot/utils.d.ts +1 -0
  386. package/dist/plot/utils.js +14 -0
  387. package/dist/rdf/RdfPlot.svelte +247 -0
  388. package/dist/rdf/RdfPlot.svelte.d.ts +27 -0
  389. package/dist/rdf/calc-rdf.d.ts +4 -0
  390. package/dist/rdf/calc-rdf.js +111 -0
  391. package/dist/rdf/index.d.ts +23 -0
  392. package/dist/rdf/index.js +2 -0
  393. package/dist/sanitize.d.ts +6 -0
  394. package/dist/sanitize.js +116 -0
  395. package/dist/settings.d.ts +255 -0
  396. package/dist/settings.js +1132 -0
  397. package/dist/spectral/Bands.svelte +1040 -0
  398. package/dist/spectral/Bands.svelte.d.ts +40 -0
  399. package/dist/spectral/BandsAndDos.svelte +134 -0
  400. package/dist/spectral/BandsAndDos.svelte.d.ts +18 -0
  401. package/dist/spectral/BrillouinBandsDos.svelte +252 -0
  402. package/dist/spectral/BrillouinBandsDos.svelte.d.ts +20 -0
  403. package/dist/spectral/Dos.svelte +697 -0
  404. package/dist/spectral/Dos.svelte.d.ts +29 -0
  405. package/dist/spectral/helpers.d.ts +119 -0
  406. package/dist/spectral/helpers.js +1032 -0
  407. package/dist/spectral/index.d.ts +6 -0
  408. package/dist/spectral/index.js +6 -0
  409. package/dist/spectral/types.d.ts +84 -0
  410. package/dist/spectral/types.js +2 -0
  411. package/dist/state.svelte.d.ts +25 -0
  412. package/dist/state.svelte.js +45 -0
  413. package/dist/structure/Arrow.svelte +72 -0
  414. package/dist/structure/Arrow.svelte.d.ts +15 -0
  415. package/dist/structure/AtomLegend.svelte +815 -0
  416. package/dist/structure/AtomLegend.svelte.d.ts +35 -0
  417. package/dist/structure/Bond.svelte +140 -0
  418. package/dist/structure/Bond.svelte.d.ts +9 -0
  419. package/dist/structure/CanvasTooltip.svelte +33 -0
  420. package/dist/structure/CanvasTooltip.svelte.d.ts +12 -0
  421. package/dist/structure/CellSelect.svelte +349 -0
  422. package/dist/structure/CellSelect.svelte.d.ts +13 -0
  423. package/dist/structure/Cylinder.svelte +45 -0
  424. package/dist/structure/Cylinder.svelte.d.ts +10 -0
  425. package/dist/structure/Lattice.svelte +196 -0
  426. package/dist/structure/Lattice.svelte.d.ts +17 -0
  427. package/dist/structure/Structure.svelte +2248 -0
  428. package/dist/structure/Structure.svelte.d.ts +89 -0
  429. package/dist/structure/StructureControls.svelte +1273 -0
  430. package/dist/structure/StructureControls.svelte.d.ts +31 -0
  431. package/dist/structure/StructureExportPane.svelte +252 -0
  432. package/dist/structure/StructureExportPane.svelte.d.ts +17 -0
  433. package/dist/structure/StructureInfoPane.svelte +737 -0
  434. package/dist/structure/StructureInfoPane.svelte.d.ts +19 -0
  435. package/dist/structure/StructureScene.svelte +2255 -0
  436. package/dist/structure/StructureScene.svelte.d.ts +111 -0
  437. package/dist/structure/atom-properties.d.ts +37 -0
  438. package/dist/structure/atom-properties.js +200 -0
  439. package/dist/structure/bond-order-perception.d.ts +13 -0
  440. package/dist/structure/bond-order-perception.js +384 -0
  441. package/dist/structure/bonding.d.ts +68 -0
  442. package/dist/structure/bonding.js +696 -0
  443. package/dist/structure/export.d.ts +20 -0
  444. package/dist/structure/export.js +727 -0
  445. package/dist/structure/index.d.ts +126 -0
  446. package/dist/structure/index.js +169 -0
  447. package/dist/structure/label-placement.d.ts +14 -0
  448. package/dist/structure/label-placement.js +72 -0
  449. package/dist/structure/measure.d.ts +6 -0
  450. package/dist/structure/measure.js +29 -0
  451. package/dist/structure/parse.d.ts +66 -0
  452. package/dist/structure/parse.js +1392 -0
  453. package/dist/structure/partial-occupancy.d.ts +25 -0
  454. package/dist/structure/partial-occupancy.js +99 -0
  455. package/dist/structure/pbc.d.ts +9 -0
  456. package/dist/structure/pbc.js +123 -0
  457. package/dist/structure/supercell.d.ts +8 -0
  458. package/dist/structure/supercell.js +170 -0
  459. package/dist/structure/validation.d.ts +2 -0
  460. package/dist/structure/validation.js +10 -0
  461. package/dist/symmetry/SymmetryStats.svelte +226 -0
  462. package/dist/symmetry/SymmetryStats.svelte.d.ts +21 -0
  463. package/dist/symmetry/WyckoffTable.svelte +120 -0
  464. package/dist/symmetry/WyckoffTable.svelte.d.ts +11 -0
  465. package/dist/symmetry/cell-transform.d.ts +12 -0
  466. package/dist/symmetry/cell-transform.js +91 -0
  467. package/dist/symmetry/index.d.ts +43 -0
  468. package/dist/symmetry/index.js +228 -0
  469. package/dist/symmetry/spacegroups.d.ts +9 -0
  470. package/dist/symmetry/spacegroups.js +394 -0
  471. package/dist/table/HeatmapTable.svelte +1833 -0
  472. package/dist/table/HeatmapTable.svelte.d.ts +49 -0
  473. package/dist/table/ToggleMenu.svelte +385 -0
  474. package/dist/table/ToggleMenu.svelte.d.ts +11 -0
  475. package/dist/table/index.d.ts +74 -0
  476. package/dist/table/index.js +38 -0
  477. package/dist/theme/ThemeControl.svelte +53 -0
  478. package/dist/theme/ThemeControl.svelte.d.ts +9 -0
  479. package/dist/theme/index.d.ts +29 -0
  480. package/dist/theme/index.js +79 -0
  481. package/dist/time.d.ts +4 -0
  482. package/dist/time.js +70 -0
  483. package/dist/tooltip/TooltipContent.svelte +58 -0
  484. package/dist/tooltip/TooltipContent.svelte.d.ts +31 -0
  485. package/dist/tooltip/index.d.ts +2 -0
  486. package/dist/tooltip/index.js +1 -0
  487. package/dist/tooltip/types.d.ts +8 -0
  488. package/dist/tooltip/types.js +1 -0
  489. package/dist/trajectory/Trajectory.svelte +1545 -0
  490. package/dist/trajectory/Trajectory.svelte.d.ts +77 -0
  491. package/dist/trajectory/TrajectoryError.svelte +128 -0
  492. package/dist/trajectory/TrajectoryError.svelte.d.ts +13 -0
  493. package/dist/trajectory/TrajectoryExportPane.svelte +357 -0
  494. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +17 -0
  495. package/dist/trajectory/TrajectoryInfoPane.svelte +313 -0
  496. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +17 -0
  497. package/dist/trajectory/constants.d.ts +6 -0
  498. package/dist/trajectory/constants.js +7 -0
  499. package/dist/trajectory/extract.d.ts +5 -0
  500. package/dist/trajectory/extract.js +162 -0
  501. package/dist/trajectory/format-detect.d.ts +9 -0
  502. package/dist/trajectory/format-detect.js +76 -0
  503. package/dist/trajectory/frame-reader.d.ts +17 -0
  504. package/dist/trajectory/frame-reader.js +332 -0
  505. package/dist/trajectory/helpers.d.ts +15 -0
  506. package/dist/trajectory/helpers.js +164 -0
  507. package/dist/trajectory/index.d.ts +63 -0
  508. package/dist/trajectory/index.js +126 -0
  509. package/dist/trajectory/parse/ase.d.ts +2 -0
  510. package/dist/trajectory/parse/ase.js +73 -0
  511. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  512. package/dist/trajectory/parse/hdf5.js +127 -0
  513. package/dist/trajectory/parse/index.d.ts +12 -0
  514. package/dist/trajectory/parse/index.js +298 -0
  515. package/dist/trajectory/parse/lammps.d.ts +5 -0
  516. package/dist/trajectory/parse/lammps.js +179 -0
  517. package/dist/trajectory/parse/vasp.d.ts +2 -0
  518. package/dist/trajectory/parse/vasp.js +68 -0
  519. package/dist/trajectory/parse/xyz.d.ts +2 -0
  520. package/dist/trajectory/parse/xyz.js +110 -0
  521. package/dist/trajectory/plotting.d.ts +28 -0
  522. package/dist/trajectory/plotting.js +423 -0
  523. package/dist/trajectory/types.d.ts +11 -0
  524. package/dist/trajectory/types.js +1 -0
  525. package/dist/utils.d.ts +6 -0
  526. package/dist/utils.js +45 -0
  527. package/dist/xrd/XrdPlot.svelte +615 -0
  528. package/dist/xrd/XrdPlot.svelte.d.ts +28 -0
  529. package/dist/xrd/broadening.d.ts +20 -0
  530. package/dist/xrd/broadening.js +97 -0
  531. package/dist/xrd/calc-xrd.d.ts +37 -0
  532. package/dist/xrd/calc-xrd.js +336 -0
  533. package/dist/xrd/index.d.ts +37 -0
  534. package/dist/xrd/index.js +4 -0
  535. package/dist/xrd/parse.d.ts +13 -0
  536. package/dist/xrd/parse.js +749 -0
  537. package/license +1 -1
  538. package/package.json +232 -1457
  539. package/readme.md +98 -171
  540. package/.vscode/launch.json +0 -13
  541. package/.vscodeignore +0 -7
  542. package/dist/assets/STLExporter-BpTH3YHE.js +0 -8
  543. package/dist/assets/browser-DdDecX_W.js +0 -1
  544. package/dist/assets/export-qgn-H9y6.js +0 -2
  545. package/dist/assets/main-DiKYzti2.css +0 -1
  546. package/dist/assets/moyo_wasm_bg-0ocwg7xY.wasm +0 -0
  547. package/dist/extension.js +0 -31293
  548. package/dist/src/lib/FilePicker.svelte +0 -360
  549. package/dist/src/lib/MillerIndexInput.svelte +0 -66
  550. package/dist/src/lib/api/mp.ts +0 -26
  551. package/dist/src/lib/api/optimade.ts +0 -204
  552. package/dist/src/lib/brillouin/BrillouinZone.svelte +0 -549
  553. package/dist/src/lib/brillouin/BrillouinZoneControls.svelte +0 -144
  554. package/dist/src/lib/brillouin/BrillouinZoneExportPane.svelte +0 -146
  555. package/dist/src/lib/brillouin/BrillouinZoneInfoPane.svelte +0 -146
  556. package/dist/src/lib/brillouin/BrillouinZoneScene.svelte +0 -476
  557. package/dist/src/lib/brillouin/BrillouinZoneTooltip.svelte +0 -92
  558. package/dist/src/lib/brillouin/compute.ts +0 -529
  559. package/dist/src/lib/brillouin/index.ts +0 -8
  560. package/dist/src/lib/brillouin/types.ts +0 -51
  561. package/dist/src/lib/chempot-diagram/ChemPotDiagram.svelte +0 -327
  562. package/dist/src/lib/chempot-diagram/ChemPotDiagram2D.svelte +0 -846
  563. package/dist/src/lib/chempot-diagram/ChemPotDiagram3D.svelte +0 -3193
  564. package/dist/src/lib/chempot-diagram/async-compute.svelte.ts +0 -94
  565. package/dist/src/lib/chempot-diagram/chempot-worker.ts +0 -11
  566. package/dist/src/lib/chempot-diagram/color.ts +0 -42
  567. package/dist/src/lib/chempot-diagram/compute.ts +0 -1014
  568. package/dist/src/lib/chempot-diagram/index.ts +0 -6
  569. package/dist/src/lib/chempot-diagram/pointer.ts +0 -56
  570. package/dist/src/lib/chempot-diagram/temperature.ts +0 -77
  571. package/dist/src/lib/chempot-diagram/types.ts +0 -130
  572. package/dist/src/lib/colors/index.ts +0 -249
  573. package/dist/src/lib/composition/BarChart.svelte +0 -297
  574. package/dist/src/lib/composition/BubbleChart.svelte +0 -218
  575. package/dist/src/lib/composition/Composition.svelte +0 -165
  576. package/dist/src/lib/composition/Formula.svelte +0 -268
  577. package/dist/src/lib/composition/FormulaFilter.svelte +0 -1257
  578. package/dist/src/lib/composition/PieChart.svelte +0 -323
  579. package/dist/src/lib/composition/format.ts +0 -155
  580. package/dist/src/lib/composition/index.ts +0 -37
  581. package/dist/src/lib/composition/parse.ts +0 -605
  582. package/dist/src/lib/constants.ts +0 -134
  583. package/dist/src/lib/controls.ts +0 -42
  584. package/dist/src/lib/convex-hull/ConvexHull.svelte +0 -157
  585. package/dist/src/lib/convex-hull/ConvexHull2D.svelte +0 -825
  586. package/dist/src/lib/convex-hull/ConvexHull3D.svelte +0 -1801
  587. package/dist/src/lib/convex-hull/ConvexHull4D.svelte +0 -1398
  588. package/dist/src/lib/convex-hull/ConvexHullControls.svelte +0 -535
  589. package/dist/src/lib/convex-hull/ConvexHullInfoPane.svelte +0 -125
  590. package/dist/src/lib/convex-hull/ConvexHullStats.svelte +0 -929
  591. package/dist/src/lib/convex-hull/ConvexHullTooltip.svelte +0 -131
  592. package/dist/src/lib/convex-hull/GasPressureControls.svelte +0 -247
  593. package/dist/src/lib/convex-hull/StructurePopup.svelte +0 -151
  594. package/dist/src/lib/convex-hull/barycentric-coords.ts +0 -246
  595. package/dist/src/lib/convex-hull/demo-temperature.ts +0 -63
  596. package/dist/src/lib/convex-hull/gas-thermodynamics.ts +0 -405
  597. package/dist/src/lib/convex-hull/helpers.ts +0 -932
  598. package/dist/src/lib/convex-hull/index.ts +0 -202
  599. package/dist/src/lib/convex-hull/thermodynamics.ts +0 -2192
  600. package/dist/src/lib/convex-hull/types.ts +0 -267
  601. package/dist/src/lib/coordination/CoordinationBarPlot.svelte +0 -311
  602. package/dist/src/lib/coordination/calc-coordination.ts +0 -93
  603. package/dist/src/lib/coordination/index.ts +0 -9
  604. package/dist/src/lib/effects.svelte.ts +0 -48
  605. package/dist/src/lib/element/ElementHeading.svelte +0 -26
  606. package/dist/src/lib/element/ElementPhoto.svelte +0 -57
  607. package/dist/src/lib/element/ElementStats.svelte +0 -80
  608. package/dist/src/lib/element/ElementTile.svelte +0 -484
  609. package/dist/src/lib/element/data.ts +0 -14
  610. package/dist/src/lib/element/index.ts +0 -8
  611. package/dist/src/lib/element/types.ts +0 -62
  612. package/dist/src/lib/feedback/ClickFeedback.svelte +0 -58
  613. package/dist/src/lib/feedback/DragOverlay.svelte +0 -42
  614. package/dist/src/lib/feedback/index.ts +0 -4
  615. package/dist/src/lib/fermi-surface/FermiSlice.svelte +0 -189
  616. package/dist/src/lib/fermi-surface/FermiSurface.svelte +0 -600
  617. package/dist/src/lib/fermi-surface/FermiSurfaceControls.svelte +0 -448
  618. package/dist/src/lib/fermi-surface/FermiSurfaceScene.svelte +0 -794
  619. package/dist/src/lib/fermi-surface/FermiSurfaceTooltip.svelte +0 -111
  620. package/dist/src/lib/fermi-surface/compute.ts +0 -728
  621. package/dist/src/lib/fermi-surface/constants.ts +0 -32
  622. package/dist/src/lib/fermi-surface/export.ts +0 -64
  623. package/dist/src/lib/fermi-surface/index.ts +0 -14
  624. package/dist/src/lib/fermi-surface/marching-cubes.ts +0 -3
  625. package/dist/src/lib/fermi-surface/parse.ts +0 -574
  626. package/dist/src/lib/fermi-surface/symmetry.ts +0 -56
  627. package/dist/src/lib/fermi-surface/types.ts +0 -159
  628. package/dist/src/lib/heatmap-matrix/HeatmapMatrix.svelte +0 -1545
  629. package/dist/src/lib/heatmap-matrix/HeatmapMatrixControls.svelte +0 -225
  630. package/dist/src/lib/heatmap-matrix/index.ts +0 -167
  631. package/dist/src/lib/heatmap-matrix/shared.ts +0 -7
  632. package/dist/src/lib/icons.ts +0 -650
  633. package/dist/src/lib/index.ts +0 -61
  634. package/dist/src/lib/io/decompress.ts +0 -92
  635. package/dist/src/lib/io/export.ts +0 -385
  636. package/dist/src/lib/io/fetch.ts +0 -46
  637. package/dist/src/lib/io/file-drop.ts +0 -51
  638. package/dist/src/lib/io/index.ts +0 -7
  639. package/dist/src/lib/io/is-binary.ts +0 -24
  640. package/dist/src/lib/io/types.ts +0 -8
  641. package/dist/src/lib/io/url-drop.ts +0 -141
  642. package/dist/src/lib/isosurface/Isosurface.svelte +0 -285
  643. package/dist/src/lib/isosurface/IsosurfaceControls.svelte +0 -277
  644. package/dist/src/lib/isosurface/index.ts +0 -7
  645. package/dist/src/lib/isosurface/parse.ts +0 -656
  646. package/dist/src/lib/isosurface/slice.ts +0 -175
  647. package/dist/src/lib/isosurface/types.ts +0 -309
  648. package/dist/src/lib/labels.ts +0 -320
  649. package/dist/src/lib/layout/FullscreenToggle.svelte +0 -50
  650. package/dist/src/lib/layout/InfoCard.svelte +0 -120
  651. package/dist/src/lib/layout/InfoTag.svelte +0 -185
  652. package/dist/src/lib/layout/PropertyFilter.svelte +0 -246
  653. package/dist/src/lib/layout/SettingsSection.svelte +0 -148
  654. package/dist/src/lib/layout/SubpageGrid.svelte +0 -82
  655. package/dist/src/lib/layout/fullscreen.ts +0 -65
  656. package/dist/src/lib/layout/index.ts +0 -11
  657. package/dist/src/lib/layout/json-tree/JsonNode.svelte +0 -548
  658. package/dist/src/lib/layout/json-tree/JsonTree.svelte +0 -1230
  659. package/dist/src/lib/layout/json-tree/index.ts +0 -3
  660. package/dist/src/lib/layout/json-tree/types.ts +0 -126
  661. package/dist/src/lib/layout/json-tree/utils.ts +0 -682
  662. package/dist/src/lib/marching-cubes.ts +0 -614
  663. package/dist/src/lib/math.ts +0 -1081
  664. package/dist/src/lib/overlays/ContextMenu.svelte +0 -162
  665. package/dist/src/lib/overlays/CopyButton.svelte +0 -45
  666. package/dist/src/lib/overlays/DragControlTab.svelte +0 -98
  667. package/dist/src/lib/overlays/DraggablePane.svelte +0 -487
  668. package/dist/src/lib/overlays/InfoPaneCards.svelte +0 -149
  669. package/dist/src/lib/overlays/index.ts +0 -3
  670. package/dist/src/lib/periodic-table/PeriodicTable.svelte +0 -469
  671. package/dist/src/lib/periodic-table/PeriodicTableControls.svelte +0 -557
  672. package/dist/src/lib/periodic-table/PropertySelect.svelte +0 -37
  673. package/dist/src/lib/periodic-table/index.ts +0 -12
  674. package/dist/src/lib/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +0 -1086
  675. package/dist/src/lib/phase-diagram/PhaseDiagramControls.svelte +0 -444
  676. package/dist/src/lib/phase-diagram/PhaseDiagramEditorPane.svelte +0 -126
  677. package/dist/src/lib/phase-diagram/PhaseDiagramExportPane.svelte +0 -184
  678. package/dist/src/lib/phase-diagram/PhaseDiagramTooltip.svelte +0 -391
  679. package/dist/src/lib/phase-diagram/TdbInfoPanel.svelte +0 -203
  680. package/dist/src/lib/phase-diagram/build-diagram.ts +0 -186
  681. package/dist/src/lib/phase-diagram/colors.ts +0 -58
  682. package/dist/src/lib/phase-diagram/diagram-input.ts +0 -40
  683. package/dist/src/lib/phase-diagram/index.ts +0 -13
  684. package/dist/src/lib/phase-diagram/parse.ts +0 -348
  685. package/dist/src/lib/phase-diagram/svg-to-diagram.ts +0 -1023
  686. package/dist/src/lib/phase-diagram/types.ts +0 -144
  687. package/dist/src/lib/phase-diagram/utils.ts +0 -775
  688. package/dist/src/lib/plot/AxisLabel.svelte +0 -51
  689. package/dist/src/lib/plot/BarPlot.svelte +0 -2113
  690. package/dist/src/lib/plot/BarPlotControls.svelte +0 -66
  691. package/dist/src/lib/plot/BinnedScatterPlot.svelte +0 -1114
  692. package/dist/src/lib/plot/ColorBar.svelte +0 -721
  693. package/dist/src/lib/plot/ColorScaleSelect.svelte +0 -54
  694. package/dist/src/lib/plot/ElementScatter.svelte +0 -63
  695. package/dist/src/lib/plot/Histogram.svelte +0 -1558
  696. package/dist/src/lib/plot/HistogramControls.svelte +0 -212
  697. package/dist/src/lib/plot/InteractiveAxisLabel.svelte +0 -96
  698. package/dist/src/lib/plot/Line.svelte +0 -84
  699. package/dist/src/lib/plot/PlotAxis.svelte +0 -169
  700. package/dist/src/lib/plot/PlotControls.svelte +0 -537
  701. package/dist/src/lib/plot/PlotLegend.svelte +0 -569
  702. package/dist/src/lib/plot/PlotTooltip.svelte +0 -67
  703. package/dist/src/lib/plot/PortalSelect.svelte +0 -253
  704. package/dist/src/lib/plot/ReferenceLine3D.svelte +0 -156
  705. package/dist/src/lib/plot/ReferencePlane.svelte +0 -175
  706. package/dist/src/lib/plot/ScatterPlot.svelte +0 -2778
  707. package/dist/src/lib/plot/ScatterPlot3D.svelte +0 -529
  708. package/dist/src/lib/plot/ScatterPlot3DControls.svelte +0 -437
  709. package/dist/src/lib/plot/ScatterPlot3DScene.svelte +0 -912
  710. package/dist/src/lib/plot/ScatterPlotControls.svelte +0 -306
  711. package/dist/src/lib/plot/ScatterPoint.svelte +0 -182
  712. package/dist/src/lib/plot/SpacegroupBarPlot.svelte +0 -293
  713. package/dist/src/lib/plot/Surface3D.svelte +0 -197
  714. package/dist/src/lib/plot/ZeroLines.svelte +0 -97
  715. package/dist/src/lib/plot/ZoomRect.svelte +0 -23
  716. package/dist/src/lib/plot/adaptive-density.ts +0 -316
  717. package/dist/src/lib/plot/auto-place.ts +0 -184
  718. package/dist/src/lib/plot/axis-utils.ts +0 -122
  719. package/dist/src/lib/plot/binned-scatter-types.ts +0 -83
  720. package/dist/src/lib/plot/data-cleaning.ts +0 -1069
  721. package/dist/src/lib/plot/data-transform.ts +0 -69
  722. package/dist/src/lib/plot/defaults.ts +0 -9
  723. package/dist/src/lib/plot/fill-utils.ts +0 -494
  724. package/dist/src/lib/plot/hover-lock.svelte.ts +0 -60
  725. package/dist/src/lib/plot/index.ts +0 -53
  726. package/dist/src/lib/plot/interactions.ts +0 -119
  727. package/dist/src/lib/plot/layout.ts +0 -425
  728. package/dist/src/lib/plot/reference-line.ts +0 -426
  729. package/dist/src/lib/plot/scales.ts +0 -654
  730. package/dist/src/lib/plot/svg.ts +0 -23
  731. package/dist/src/lib/plot/types.ts +0 -1144
  732. package/dist/src/lib/plot/utils/label-placement.ts +0 -541
  733. package/dist/src/lib/plot/utils/series-visibility.ts +0 -140
  734. package/dist/src/lib/plot/utils.ts +0 -11
  735. package/dist/src/lib/rdf/RdfPlot.svelte +0 -247
  736. package/dist/src/lib/rdf/calc-rdf.ts +0 -167
  737. package/dist/src/lib/rdf/index.ts +0 -27
  738. package/dist/src/lib/sanitize.ts +0 -126
  739. package/dist/src/lib/settings.ts +0 -1479
  740. package/dist/src/lib/spectral/Bands.svelte +0 -1040
  741. package/dist/src/lib/spectral/BandsAndDos.svelte +0 -134
  742. package/dist/src/lib/spectral/BrillouinBandsDos.svelte +0 -252
  743. package/dist/src/lib/spectral/Dos.svelte +0 -697
  744. package/dist/src/lib/spectral/helpers.ts +0 -1381
  745. package/dist/src/lib/spectral/index.ts +0 -8
  746. package/dist/src/lib/spectral/types.ts +0 -112
  747. package/dist/src/lib/state.svelte.ts +0 -64
  748. package/dist/src/lib/structure/Arrow.svelte +0 -72
  749. package/dist/src/lib/structure/AtomLegend.svelte +0 -815
  750. package/dist/src/lib/structure/Bond.svelte +0 -140
  751. package/dist/src/lib/structure/CanvasTooltip.svelte +0 -33
  752. package/dist/src/lib/structure/CellSelect.svelte +0 -349
  753. package/dist/src/lib/structure/Cylinder.svelte +0 -45
  754. package/dist/src/lib/structure/Lattice.svelte +0 -196
  755. package/dist/src/lib/structure/Structure.svelte +0 -2248
  756. package/dist/src/lib/structure/StructureControls.svelte +0 -1273
  757. package/dist/src/lib/structure/StructureExportPane.svelte +0 -252
  758. package/dist/src/lib/structure/StructureInfoPane.svelte +0 -737
  759. package/dist/src/lib/structure/StructureScene.svelte +0 -2255
  760. package/dist/src/lib/structure/atom-properties.ts +0 -316
  761. package/dist/src/lib/structure/bond-order-perception.ts +0 -447
  762. package/dist/src/lib/structure/bonding.ts +0 -944
  763. package/dist/src/lib/structure/export.ts +0 -861
  764. package/dist/src/lib/structure/index.ts +0 -291
  765. package/dist/src/lib/structure/label-placement.ts +0 -130
  766. package/dist/src/lib/structure/measure.ts +0 -45
  767. package/dist/src/lib/structure/parse.ts +0 -1705
  768. package/dist/src/lib/structure/partial-occupancy.ts +0 -183
  769. package/dist/src/lib/structure/pbc.ts +0 -164
  770. package/dist/src/lib/structure/supercell.ts +0 -226
  771. package/dist/src/lib/structure/validation.ts +0 -11
  772. package/dist/src/lib/symmetry/SymmetryStats.svelte +0 -226
  773. package/dist/src/lib/symmetry/WyckoffTable.svelte +0 -120
  774. package/dist/src/lib/symmetry/cell-transform.ts +0 -118
  775. package/dist/src/lib/symmetry/index.ts +0 -348
  776. package/dist/src/lib/symmetry/spacegroups.ts +0 -404
  777. package/dist/src/lib/table/HeatmapTable.svelte +0 -1833
  778. package/dist/src/lib/table/ToggleMenu.svelte +0 -385
  779. package/dist/src/lib/table/index.ts +0 -139
  780. package/dist/src/lib/theme/ThemeControl.svelte +0 -53
  781. package/dist/src/lib/theme/index.ts +0 -107
  782. package/dist/src/lib/time.ts +0 -71
  783. package/dist/src/lib/tooltip/TooltipContent.svelte +0 -58
  784. package/dist/src/lib/tooltip/index.ts +0 -2
  785. package/dist/src/lib/tooltip/types.ts +0 -13
  786. package/dist/src/lib/trajectory/Trajectory.svelte +0 -1545
  787. package/dist/src/lib/trajectory/TrajectoryError.svelte +0 -128
  788. package/dist/src/lib/trajectory/TrajectoryExportPane.svelte +0 -357
  789. package/dist/src/lib/trajectory/TrajectoryInfoPane.svelte +0 -313
  790. package/dist/src/lib/trajectory/constants.ts +0 -7
  791. package/dist/src/lib/trajectory/extract.ts +0 -196
  792. package/dist/src/lib/trajectory/format-detect.ts +0 -96
  793. package/dist/src/lib/trajectory/frame-reader.ts +0 -456
  794. package/dist/src/lib/trajectory/helpers.ts +0 -217
  795. package/dist/src/lib/trajectory/index.ts +0 -218
  796. package/dist/src/lib/trajectory/parse/ase.ts +0 -109
  797. package/dist/src/lib/trajectory/parse/hdf5.ts +0 -173
  798. package/dist/src/lib/trajectory/parse/index.ts +0 -411
  799. package/dist/src/lib/trajectory/parse/lammps.ts +0 -215
  800. package/dist/src/lib/trajectory/parse/vasp.ts +0 -102
  801. package/dist/src/lib/trajectory/parse/xyz.ts +0 -143
  802. package/dist/src/lib/trajectory/plotting.ts +0 -599
  803. package/dist/src/lib/trajectory/types.ts +0 -13
  804. package/dist/src/lib/utils.ts +0 -56
  805. package/dist/src/lib/xrd/XrdPlot.svelte +0 -615
  806. package/dist/src/lib/xrd/broadening.ts +0 -130
  807. package/dist/src/lib/xrd/calc-xrd.ts +0 -397
  808. package/dist/src/lib/xrd/index.ts +0 -38
  809. package/dist/src/lib/xrd/parse.ts +0 -858
  810. package/dist/webview.js +0 -29421
  811. package/icon.png +0 -0
  812. package/matterviz-0.3.2.vsix +0 -0
  813. package/matterviz-0.3.4.vsix +0 -0
  814. package/matterviz-0.3.5.vsix +0 -0
  815. package/scripts/sync-config.ts +0 -101
  816. package/src/declarations.d.ts +0 -2
  817. package/src/extension.ts +0 -972
  818. package/src/node-io.ts +0 -65
  819. package/src/types.ts +0 -17
  820. package/src/webview/JsonBrowser.svelte +0 -1079
  821. package/src/webview/PlotPanel.svelte +0 -346
  822. package/src/webview/detect.ts +0 -444
  823. package/src/webview/main.ts +0 -764
  824. package/src/webview/plot-utils.ts +0 -250
  825. package/test-fixtures/all-viz-types.json.gz +0 -0
  826. package/test-fixtures/plot-demo-data.json.gz +0 -0
  827. package/tests/detect.test.ts +0 -604
  828. package/tests/extension.test.ts +0 -2041
  829. package/tests/node-io.test.ts +0 -39
  830. package/tests/plot-utils.test.ts +0 -302
  831. package/tests/vite-plugin-json-gz.test.ts +0 -114
  832. package/tests/vscode-mock.ts +0 -18
  833. package/tests/webview.test.ts +0 -231
  834. package/tsconfig.json +0 -20
  835. package/vite-plugin-json-gz.ts +0 -29
  836. package/vite.config.ts +0 -34
  837. package/vite.extension.config.ts +0 -34
  838. /package/dist/{src/lib/EmptyState.svelte → EmptyState.svelte} +0 -0
  839. /package/dist/{src/lib/Icon.svelte → Icon.svelte} +0 -0
  840. /package/dist/{src/lib/app.css → app.css} +0 -0
  841. /package/dist/{src/lib/chempot-diagram → chempot-diagram}/ChemPotScene3D.svelte +0 -0
  842. /package/dist/{src/lib/colors → colors}/alloy-colors.json +0 -0
  843. /package/dist/{src/lib/colors → colors}/dark-mode-colors.json +0 -0
  844. /package/dist/{src/lib/colors → colors}/jmol-colors.json +0 -0
  845. /package/dist/{src/lib/colors → colors}/muted-colors.json +0 -0
  846. /package/dist/{src/lib/colors → colors}/pastel-colors.json +0 -0
  847. /package/dist/{src/lib/colors → colors}/vesta-colors.json +0 -0
  848. /package/dist/{src/lib/convex-hull → convex-hull}/TemperatureSlider.svelte +0 -0
  849. /package/dist/{src/lib/element → element}/BohrAtom.svelte +0 -0
  850. /package/dist/{src/lib/element → element}/Nucleus.svelte +0 -0
  851. /package/dist/{src/lib/element → element}/data.json +0 -0
  852. /package/dist/{src/lib/element → element}/data.json.gz +0 -0
  853. /package/dist/{src/lib/element → element}/data.json.gz.d.ts +0 -0
  854. /package/dist/{src/lib/element → element}/data.schema.json +0 -0
  855. /package/dist/{src/lib/element-image-urls.json → element-image-urls.json} +0 -0
  856. /package/dist/{src/lib/feedback → feedback}/Spinner.svelte +0 -0
  857. /package/dist/{src/lib/feedback → feedback}/StatusMessage.svelte +0 -0
  858. /package/dist/{src/lib/layout → layout}/json-tree/JsonValue.svelte +0 -0
  859. /package/dist/{src/lib/periodic-table → periodic-table}/TableInset.svelte +0 -0
  860. /package/dist/{src/lib/plot → plot}/FillArea.svelte +0 -0
  861. /package/dist/{src/lib/plot → plot}/ReferenceLine.svelte +0 -0
  862. /package/dist/{src/lib/theme → theme}/themes.mjs +0 -0
  863. /package/dist/{src/lib/xrd → xrd}/atomic_scattering_params.json +0 -0
@@ -0,0 +1,1801 @@
1
+ <script lang="ts">
2
+ import type { D3InterpolateName } from '../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 '../colors'
12
+ import { get_formula_label_segments } from '../composition/format'
13
+ import type { FormulaLabelSegment } from '../composition/format'
14
+ import { normalize_show_controls } from '../controls'
15
+ import { sanitize_html } from '../sanitize'
16
+ import { ClickFeedback, DragOverlay, Spinner } from '../feedback'
17
+ import Icon from '../Icon.svelte'
18
+ import { format_num } from '../labels'
19
+ import {
20
+ set_fullscreen_bg,
21
+ setup_fullscreen_effect,
22
+ toggle_fullscreen,
23
+ } from '../layout'
24
+ import { to_radians, type Point3D, type Vec3 } from '../math'
25
+ import { ColorBar, PlotTooltip } from '../plot'
26
+ import { centered_rect, pad_rect, rects_overlap, rect_within_rect } from '../plot/layout'
27
+ import type { Rect } from '../plot/layout'
28
+ import { create_pulse_animation } from '../effects.svelte'
29
+ import { DEFAULTS } from '../settings'
30
+ import type { AnyStructure } from '../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>