matterviz 0.3.6 → 0.4.0

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 (926) 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 +44 -0
  5. package/dist/Icon.svelte.d.ts +13 -0
  6. package/dist/MillerIndexInput.svelte +66 -0
  7. package/dist/MillerIndexInput.svelte.d.ts +7 -0
  8. package/dist/api/mp.d.ts +6 -0
  9. package/dist/api/mp.js +22 -0
  10. package/dist/api/optimade.d.ts +45 -0
  11. package/dist/api/optimade.js +141 -0
  12. package/dist/app.css +244 -0
  13. package/dist/brillouin/BrillouinZone.svelte +554 -0
  14. package/dist/brillouin/BrillouinZone.svelte.d.ts +84 -0
  15. package/dist/brillouin/BrillouinZoneControls.svelte +144 -0
  16. package/dist/brillouin/BrillouinZoneControls.svelte.d.ts +17 -0
  17. package/dist/brillouin/BrillouinZoneExportPane.svelte +146 -0
  18. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +15 -0
  19. package/dist/brillouin/BrillouinZoneInfoPane.svelte +146 -0
  20. package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +13 -0
  21. package/dist/brillouin/BrillouinZoneScene.svelte +522 -0
  22. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +49 -0
  23. package/dist/brillouin/BrillouinZoneTooltip.svelte +83 -0
  24. package/dist/brillouin/BrillouinZoneTooltip.svelte.d.ts +8 -0
  25. package/dist/brillouin/compute.d.ts +17 -0
  26. package/dist/brillouin/compute.js +422 -0
  27. package/dist/brillouin/index.d.ts +8 -0
  28. package/dist/brillouin/index.js +7 -0
  29. package/dist/brillouin/types.d.ts +43 -0
  30. package/dist/brillouin/types.js +1 -0
  31. package/dist/chempot-diagram/ChemPotDiagram.svelte +328 -0
  32. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  33. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +843 -0
  34. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  35. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +3191 -0
  36. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  37. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  38. package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
  39. package/dist/chempot-diagram/async-compute.svelte.js +80 -0
  40. package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
  41. package/dist/chempot-diagram/chempot-worker.js +12 -0
  42. package/dist/chempot-diagram/color.d.ts +10 -0
  43. package/dist/chempot-diagram/color.js +32 -0
  44. package/dist/chempot-diagram/compute.d.ts +48 -0
  45. package/dist/chempot-diagram/compute.js +804 -0
  46. package/dist/chempot-diagram/index.d.ts +6 -0
  47. package/dist/chempot-diagram/index.js +6 -0
  48. package/dist/chempot-diagram/pointer.d.ts +16 -0
  49. package/dist/chempot-diagram/pointer.js +40 -0
  50. package/dist/chempot-diagram/temperature.d.ts +15 -0
  51. package/dist/chempot-diagram/temperature.js +34 -0
  52. package/dist/chempot-diagram/types.d.ts +81 -0
  53. package/dist/chempot-diagram/types.js +28 -0
  54. package/dist/colors/index.d.ts +47 -0
  55. package/dist/colors/index.js +204 -0
  56. package/dist/composition/BarChart.svelte +297 -0
  57. package/dist/composition/BarChart.svelte.d.ts +39 -0
  58. package/dist/composition/BubbleChart.svelte +218 -0
  59. package/dist/composition/BubbleChart.svelte.d.ts +28 -0
  60. package/dist/composition/Composition.svelte +165 -0
  61. package/dist/composition/Composition.svelte.d.ts +15 -0
  62. package/dist/composition/Formula.svelte +268 -0
  63. package/dist/composition/Formula.svelte.d.ts +19 -0
  64. package/dist/composition/FormulaFilter.svelte +1263 -0
  65. package/dist/composition/FormulaFilter.svelte.d.ts +51 -0
  66. package/dist/composition/PieChart.svelte +324 -0
  67. package/dist/composition/PieChart.svelte.d.ts +37 -0
  68. package/dist/composition/chem-sys.d.ts +8 -0
  69. package/dist/composition/chem-sys.js +85 -0
  70. package/dist/composition/format.d.ts +15 -0
  71. package/dist/composition/format.js +111 -0
  72. package/dist/composition/index.d.ts +21 -0
  73. package/dist/composition/index.js +15 -0
  74. package/dist/composition/parse.d.ts +56 -0
  75. package/dist/composition/parse.js +486 -0
  76. package/dist/constants.d.ts +29 -0
  77. package/dist/constants.js +99 -0
  78. package/dist/controls.d.ts +14 -0
  79. package/dist/controls.js +30 -0
  80. package/dist/convex-hull/ConvexHull.svelte +157 -0
  81. package/dist/convex-hull/ConvexHull.svelte.d.ts +13 -0
  82. package/dist/convex-hull/ConvexHull2D.svelte +827 -0
  83. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +11 -0
  84. package/dist/convex-hull/ConvexHull3D.svelte +1801 -0
  85. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +8 -0
  86. package/dist/convex-hull/ConvexHull4D.svelte +1394 -0
  87. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +8 -0
  88. package/dist/convex-hull/ConvexHullControls.svelte +535 -0
  89. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +48 -0
  90. package/dist/convex-hull/ConvexHullInfoPane.svelte +125 -0
  91. package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +20 -0
  92. package/dist/convex-hull/ConvexHullStats.svelte +929 -0
  93. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +17 -0
  94. package/dist/convex-hull/ConvexHullTooltip.svelte +131 -0
  95. package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +33 -0
  96. package/dist/convex-hull/GasPressureControls.svelte +247 -0
  97. package/dist/convex-hull/GasPressureControls.svelte.d.ts +11 -0
  98. package/dist/convex-hull/StructurePopup.svelte +151 -0
  99. package/dist/convex-hull/StructurePopup.svelte.d.ts +18 -0
  100. package/dist/convex-hull/TemperatureSlider.svelte +140 -0
  101. package/dist/convex-hull/TemperatureSlider.svelte.d.ts +8 -0
  102. package/dist/convex-hull/barycentric-coords.d.ts +18 -0
  103. package/dist/convex-hull/barycentric-coords.js +182 -0
  104. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  105. package/dist/convex-hull/demo-temperature.js +38 -0
  106. package/dist/convex-hull/gas-thermodynamics.d.ts +16 -0
  107. package/dist/convex-hull/gas-thermodynamics.js +306 -0
  108. package/dist/convex-hull/helpers.d.ts +117 -0
  109. package/dist/convex-hull/helpers.js +718 -0
  110. package/dist/convex-hull/index.d.ts +119 -0
  111. package/dist/convex-hull/index.js +58 -0
  112. package/dist/convex-hull/thermodynamics.d.ts +67 -0
  113. package/dist/convex-hull/thermodynamics.js +1757 -0
  114. package/dist/convex-hull/types.d.ts +162 -0
  115. package/dist/convex-hull/types.js +36 -0
  116. package/dist/coordination/CoordinationBarPlot.svelte +311 -0
  117. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +30 -0
  118. package/dist/coordination/calc-coordination.d.ts +15 -0
  119. package/dist/coordination/calc-coordination.js +63 -0
  120. package/dist/coordination/index.d.ts +8 -0
  121. package/dist/coordination/index.js +7 -0
  122. package/dist/effects.svelte.d.ts +12 -0
  123. package/dist/effects.svelte.js +37 -0
  124. package/dist/element/BohrAtom.svelte.d.ts +20 -0
  125. package/dist/element/ElementHeading.svelte +26 -0
  126. package/dist/element/ElementHeading.svelte.d.ts +8 -0
  127. package/dist/element/ElementPhoto.svelte +57 -0
  128. package/dist/element/ElementPhoto.svelte.d.ts +9 -0
  129. package/dist/element/ElementStats.svelte +80 -0
  130. package/dist/element/ElementStats.svelte.d.ts +8 -0
  131. package/dist/element/ElementTile.svelte +484 -0
  132. package/dist/element/ElementTile.svelte.d.ts +29 -0
  133. package/dist/element/Nucleus.svelte.d.ts +17 -0
  134. package/dist/element/data.d.ts +2 -0
  135. package/dist/element/data.js +2 -0
  136. package/dist/element/index.d.ts +8 -0
  137. package/dist/element/index.js +7 -0
  138. package/dist/element/types.d.ts +57 -0
  139. package/dist/element/types.js +1 -0
  140. package/dist/feedback/ClickFeedback.svelte +58 -0
  141. package/dist/feedback/ClickFeedback.svelte.d.ts +12 -0
  142. package/dist/feedback/DragOverlay.svelte +42 -0
  143. package/dist/feedback/DragOverlay.svelte.d.ts +7 -0
  144. package/dist/feedback/Spinner.svelte.d.ts +7 -0
  145. package/dist/feedback/StatusMessage.svelte.d.ts +9 -0
  146. package/dist/feedback/index.d.ts +4 -0
  147. package/dist/feedback/index.js +4 -0
  148. package/dist/fermi-surface/FermiSlice.svelte +197 -0
  149. package/dist/fermi-surface/FermiSlice.svelte.d.ts +24 -0
  150. package/dist/fermi-surface/FermiSurface.svelte +606 -0
  151. package/dist/fermi-surface/FermiSurface.svelte.d.ts +83 -0
  152. package/dist/fermi-surface/FermiSurfaceControls.svelte +448 -0
  153. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +35 -0
  154. package/dist/fermi-surface/FermiSurfaceScene.svelte +797 -0
  155. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +50 -0
  156. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +85 -0
  157. package/dist/fermi-surface/FermiSurfaceTooltip.svelte.d.ts +8 -0
  158. package/dist/fermi-surface/compute.d.ts +5 -0
  159. package/dist/fermi-surface/compute.js +538 -0
  160. package/dist/fermi-surface/constants.d.ts +9 -0
  161. package/dist/fermi-surface/constants.js +27 -0
  162. package/dist/fermi-surface/export.d.ts +5 -0
  163. package/dist/fermi-surface/export.js +51 -0
  164. package/dist/fermi-surface/index.d.ts +12 -0
  165. package/dist/fermi-surface/index.js +13 -0
  166. package/dist/fermi-surface/marching-cubes.d.ts +2 -0
  167. package/dist/fermi-surface/marching-cubes.js +2 -0
  168. package/dist/fermi-surface/parse.d.ts +2 -0
  169. package/dist/fermi-surface/parse.js +494 -0
  170. package/dist/fermi-surface/symmetry.d.ts +3 -0
  171. package/dist/fermi-surface/symmetry.js +46 -0
  172. package/dist/fermi-surface/types.d.ts +111 -0
  173. package/dist/fermi-surface/types.js +4 -0
  174. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1547 -0
  175. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  176. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
  177. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +30 -0
  178. package/dist/heatmap-matrix/index.d.ts +53 -0
  179. package/dist/heatmap-matrix/index.js +100 -0
  180. package/dist/heatmap-matrix/shared.d.ts +2 -0
  181. package/dist/heatmap-matrix/shared.js +4 -0
  182. package/dist/icons.d.ts +569 -0
  183. package/dist/icons.js +648 -0
  184. package/dist/index.d.ts +39 -0
  185. package/dist/index.js +39 -0
  186. package/dist/io/decompress.d.ts +11 -0
  187. package/dist/io/decompress.js +76 -0
  188. package/dist/io/export.d.ts +16 -0
  189. package/dist/io/export.js +338 -0
  190. package/dist/io/fetch.d.ts +5 -0
  191. package/dist/io/fetch.js +43 -0
  192. package/dist/io/file-drop.d.ts +7 -0
  193. package/dist/io/file-drop.js +42 -0
  194. package/dist/io/index.d.ts +7 -0
  195. package/dist/io/index.js +6 -0
  196. package/dist/io/is-binary.d.ts +1 -0
  197. package/dist/io/is-binary.js +20 -0
  198. package/dist/io/types.d.ts +8 -0
  199. package/dist/io/types.js +1 -0
  200. package/dist/io/url-drop.d.ts +2 -0
  201. package/dist/io/url-drop.js +154 -0
  202. package/dist/isosurface/Isosurface.svelte +285 -0
  203. package/dist/isosurface/Isosurface.svelte.d.ts +8 -0
  204. package/dist/isosurface/IsosurfaceControls.svelte +277 -0
  205. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +9 -0
  206. package/dist/isosurface/index.d.ts +5 -0
  207. package/dist/isosurface/index.js +6 -0
  208. package/dist/isosurface/parse.d.ts +6 -0
  209. package/dist/isosurface/parse.js +552 -0
  210. package/dist/isosurface/slice.d.ts +11 -0
  211. package/dist/isosurface/slice.js +141 -0
  212. package/dist/isosurface/types.d.ts +56 -0
  213. package/dist/isosurface/types.js +227 -0
  214. package/dist/keyboard.d.ts +3 -0
  215. package/dist/keyboard.js +23 -0
  216. package/dist/labels.d.ts +53 -0
  217. package/dist/labels.js +278 -0
  218. package/dist/layout/FullscreenToggle.svelte +50 -0
  219. package/dist/layout/FullscreenToggle.svelte.d.ts +7 -0
  220. package/dist/layout/InfoCard.svelte +120 -0
  221. package/dist/layout/InfoCard.svelte.d.ts +21 -0
  222. package/dist/layout/InfoTag.svelte +185 -0
  223. package/dist/layout/InfoTag.svelte.d.ts +19 -0
  224. package/dist/layout/PropertyFilter.svelte +247 -0
  225. package/dist/layout/PropertyFilter.svelte.d.ts +24 -0
  226. package/dist/layout/SettingsSection.svelte +148 -0
  227. package/dist/layout/SettingsSection.svelte.d.ts +17 -0
  228. package/dist/layout/SubpageGrid.svelte +82 -0
  229. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  230. package/dist/layout/fullscreen.d.ts +9 -0
  231. package/dist/layout/fullscreen.js +53 -0
  232. package/dist/layout/index.d.ts +10 -0
  233. package/dist/layout/index.js +8 -0
  234. package/dist/layout/json-tree/JsonNode.svelte +548 -0
  235. package/dist/layout/json-tree/JsonNode.svelte.d.ts +11 -0
  236. package/dist/layout/json-tree/JsonTree.svelte +1230 -0
  237. package/dist/layout/json-tree/JsonTree.svelte.d.ts +6 -0
  238. package/dist/layout/json-tree/JsonValue.svelte.d.ts +9 -0
  239. package/dist/layout/json-tree/index.d.ts +3 -0
  240. package/dist/layout/json-tree/index.js +3 -0
  241. package/dist/layout/json-tree/types.d.ts +74 -0
  242. package/dist/layout/json-tree/types.js +2 -0
  243. package/dist/layout/json-tree/utils.d.ts +29 -0
  244. package/dist/layout/json-tree/utils.js +642 -0
  245. package/dist/marching-cubes.d.ts +14 -0
  246. package/dist/marching-cubes.js +535 -0
  247. package/dist/math.d.ts +105 -0
  248. package/dist/math.js +920 -0
  249. package/dist/overlays/ContextMenu.svelte +162 -0
  250. package/dist/overlays/ContextMenu.svelte.d.ts +25 -0
  251. package/dist/overlays/CopyButton.svelte +45 -0
  252. package/dist/overlays/CopyButton.svelte.d.ts +8 -0
  253. package/dist/overlays/DragControlTab.svelte +98 -0
  254. package/dist/overlays/DragControlTab.svelte.d.ts +8 -0
  255. package/dist/overlays/DraggablePane.svelte +487 -0
  256. package/dist/overlays/DraggablePane.svelte.d.ts +36 -0
  257. package/dist/overlays/InfoPaneCards.svelte +149 -0
  258. package/dist/overlays/InfoPaneCards.svelte.d.ts +22 -0
  259. package/dist/overlays/index.d.ts +3 -0
  260. package/dist/overlays/index.js +3 -0
  261. package/dist/periodic-table/PeriodicTable.svelte +480 -0
  262. package/dist/periodic-table/PeriodicTable.svelte.d.ts +55 -0
  263. package/dist/periodic-table/PeriodicTableControls.svelte +557 -0
  264. package/dist/periodic-table/PeriodicTableControls.svelte.d.ts +24 -0
  265. package/dist/periodic-table/PropertySelect.svelte +38 -0
  266. package/dist/periodic-table/PropertySelect.svelte.d.ts +13 -0
  267. package/dist/periodic-table/TableInset.svelte.d.ts +9 -0
  268. package/dist/periodic-table/index.d.ts +10 -0
  269. package/dist/periodic-table/index.js +4 -0
  270. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +1092 -0
  271. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +44 -0
  272. package/dist/phase-diagram/PhaseDiagramControls.svelte +444 -0
  273. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +30 -0
  274. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +127 -0
  275. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  276. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +184 -0
  277. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +19 -0
  278. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +391 -0
  279. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +16 -0
  280. package/dist/phase-diagram/TdbInfoPanel.svelte +203 -0
  281. package/dist/phase-diagram/TdbInfoPanel.svelte.d.ts +12 -0
  282. package/dist/phase-diagram/build-diagram.d.ts +11 -0
  283. package/dist/phase-diagram/build-diagram.js +160 -0
  284. package/dist/phase-diagram/colors.d.ts +35 -0
  285. package/dist/phase-diagram/colors.js +51 -0
  286. package/dist/phase-diagram/diagram-input.d.ts +29 -0
  287. package/dist/phase-diagram/diagram-input.js +3 -0
  288. package/dist/phase-diagram/index.d.ts +13 -0
  289. package/dist/phase-diagram/index.js +11 -0
  290. package/dist/phase-diagram/parse.d.ts +55 -0
  291. package/dist/phase-diagram/parse.js +273 -0
  292. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  293. package/dist/phase-diagram/svg-to-diagram.js +867 -0
  294. package/dist/phase-diagram/types.d.ts +93 -0
  295. package/dist/phase-diagram/types.js +1 -0
  296. package/dist/phase-diagram/utils.d.ts +118 -0
  297. package/dist/phase-diagram/utils.js +600 -0
  298. package/dist/plot/bar/BarPlot.svelte +1755 -0
  299. package/dist/plot/bar/BarPlot.svelte.d.ts +84 -0
  300. package/dist/plot/bar/BarPlotControls.svelte +67 -0
  301. package/dist/plot/bar/BarPlotControls.svelte.d.ts +18 -0
  302. package/dist/plot/bar/SpacegroupBarPlot.svelte +293 -0
  303. package/dist/plot/bar/SpacegroupBarPlot.svelte.d.ts +9 -0
  304. package/dist/plot/bar/data.d.ts +40 -0
  305. package/dist/plot/bar/data.js +154 -0
  306. package/dist/plot/bar/geometry.d.ts +39 -0
  307. package/dist/plot/bar/geometry.js +60 -0
  308. package/dist/plot/bar/index.d.ts +3 -0
  309. package/dist/plot/bar/index.js +3 -0
  310. package/dist/plot/box/BoxPlot.svelte +1462 -0
  311. package/dist/plot/box/BoxPlot.svelte.d.ts +94 -0
  312. package/dist/plot/box/BoxPlotControls.svelte +109 -0
  313. package/dist/plot/box/BoxPlotControls.svelte.d.ts +19 -0
  314. package/dist/plot/box/Violin.svelte +14 -0
  315. package/dist/plot/box/Violin.svelte.d.ts +70 -0
  316. package/dist/plot/box/box-plot.d.ts +55 -0
  317. package/dist/plot/box/box-plot.js +126 -0
  318. package/dist/plot/box/index.d.ts +5 -0
  319. package/dist/plot/box/index.js +5 -0
  320. package/dist/plot/box/kde.d.ts +16 -0
  321. package/dist/plot/box/kde.js +160 -0
  322. package/dist/plot/box/quantile.d.ts +3 -0
  323. package/dist/plot/box/quantile.js +53 -0
  324. package/dist/plot/core/auto-place.d.ts +43 -0
  325. package/dist/plot/core/auto-place.js +122 -0
  326. package/dist/plot/core/axis-utils.d.ts +46 -0
  327. package/dist/plot/core/axis-utils.js +110 -0
  328. package/dist/plot/core/components/AxisLabel.svelte +51 -0
  329. package/dist/plot/core/components/AxisLabel.svelte.d.ts +16 -0
  330. package/dist/plot/core/components/ColorBar.svelte +724 -0
  331. package/dist/plot/core/components/ColorBar.svelte.d.ts +31 -0
  332. package/dist/plot/core/components/ColorScaleSelect.svelte +55 -0
  333. package/dist/plot/core/components/ColorScaleSelect.svelte.d.ts +15 -0
  334. package/dist/plot/core/components/ControlPane.svelte +46 -0
  335. package/dist/plot/core/components/ControlPane.svelte.d.ts +13 -0
  336. package/dist/plot/core/components/FillArea.svelte +234 -0
  337. package/dist/plot/core/components/FillArea.svelte.d.ts +21 -0
  338. package/dist/plot/core/components/InteractiveAxisLabel.svelte +96 -0
  339. package/dist/plot/core/components/InteractiveAxisLabel.svelte.d.ts +14 -0
  340. package/dist/plot/core/components/Line.svelte +101 -0
  341. package/dist/plot/core/components/Line.svelte.d.ts +15 -0
  342. package/dist/plot/core/components/PlotAxis.svelte +171 -0
  343. package/dist/plot/core/components/PlotAxis.svelte.d.ts +25 -0
  344. package/dist/plot/core/components/PlotControls.svelte +525 -0
  345. package/dist/plot/core/components/PlotControls.svelte.d.ts +4 -0
  346. package/dist/plot/core/components/PlotLegend.svelte +580 -0
  347. package/dist/plot/core/components/PlotLegend.svelte.d.ts +30 -0
  348. package/dist/plot/core/components/PlotTooltip.svelte +83 -0
  349. package/dist/plot/core/components/PlotTooltip.svelte.d.ts +25 -0
  350. package/dist/plot/core/components/PortalSelect.svelte +257 -0
  351. package/dist/plot/core/components/PortalSelect.svelte.d.ts +16 -0
  352. package/dist/plot/core/components/ReferenceLine.svelte +204 -0
  353. package/dist/plot/core/components/ReferenceLine.svelte.d.ts +20 -0
  354. package/dist/plot/core/components/ReferenceLine3D.svelte +156 -0
  355. package/dist/plot/core/components/ReferenceLine3D.svelte.d.ts +14 -0
  356. package/dist/plot/core/components/ReferencePlane.svelte +175 -0
  357. package/dist/plot/core/components/ReferencePlane.svelte.d.ts +14 -0
  358. package/dist/plot/core/components/ZeroLines.svelte +97 -0
  359. package/dist/plot/core/components/ZeroLines.svelte.d.ts +33 -0
  360. package/dist/plot/core/components/ZoomRect.svelte +23 -0
  361. package/dist/plot/core/components/ZoomRect.svelte.d.ts +8 -0
  362. package/dist/plot/core/components/index.d.ts +17 -0
  363. package/dist/plot/core/components/index.js +17 -0
  364. package/dist/plot/core/data-cleaning.d.ts +107 -0
  365. package/dist/plot/core/data-cleaning.js +853 -0
  366. package/dist/plot/core/data-transform.d.ts +16 -0
  367. package/dist/plot/core/data-transform.js +45 -0
  368. package/dist/plot/core/fill-utils.d.ts +33 -0
  369. package/dist/plot/core/fill-utils.js +388 -0
  370. package/dist/plot/core/hover-lock.svelte.d.ts +14 -0
  371. package/dist/plot/core/hover-lock.svelte.js +45 -0
  372. package/dist/plot/core/index.d.ts +10 -0
  373. package/dist/plot/core/index.js +11 -0
  374. package/dist/plot/core/interactions.d.ts +35 -0
  375. package/dist/plot/core/interactions.js +195 -0
  376. package/dist/plot/core/layout.d.ts +79 -0
  377. package/dist/plot/core/layout.js +281 -0
  378. package/dist/plot/core/reference-line.d.ts +60 -0
  379. package/dist/plot/core/reference-line.js +301 -0
  380. package/dist/plot/core/scales.d.ts +48 -0
  381. package/dist/plot/core/scales.js +480 -0
  382. package/dist/plot/core/svg.d.ts +2 -0
  383. package/dist/plot/core/svg.js +41 -0
  384. package/dist/plot/core/types.d.ts +771 -0
  385. package/dist/plot/core/types.js +99 -0
  386. package/dist/plot/core/utils/label-placement.d.ts +68 -0
  387. package/dist/plot/core/utils/label-placement.js +326 -0
  388. package/dist/plot/core/utils/series-visibility.d.ts +26 -0
  389. package/dist/plot/core/utils/series-visibility.js +112 -0
  390. package/dist/plot/core/utils.d.ts +11 -0
  391. package/dist/plot/core/utils.js +27 -0
  392. package/dist/plot/histogram/Histogram.svelte +1418 -0
  393. package/dist/plot/histogram/Histogram.svelte.d.ts +50 -0
  394. package/dist/plot/histogram/HistogramControls.svelte +212 -0
  395. package/dist/plot/histogram/HistogramControls.svelte.d.ts +22 -0
  396. package/dist/plot/histogram/index.d.ts +2 -0
  397. package/dist/plot/histogram/index.js +2 -0
  398. package/dist/plot/index.d.ts +8 -0
  399. package/dist/plot/index.js +10 -0
  400. package/dist/plot/sankey/Sankey.svelte +700 -0
  401. package/dist/plot/sankey/Sankey.svelte.d.ts +74 -0
  402. package/dist/plot/sankey/SankeyControls.svelte +98 -0
  403. package/dist/plot/sankey/SankeyControls.svelte.d.ts +19 -0
  404. package/dist/plot/sankey/index.d.ts +4 -0
  405. package/dist/plot/sankey/index.js +3 -0
  406. package/dist/plot/sankey/sankey-types.d.ts +42 -0
  407. package/dist/plot/sankey/sankey-types.js +4 -0
  408. package/dist/plot/sankey/sankey.d.ts +52 -0
  409. package/dist/plot/sankey/sankey.js +187 -0
  410. package/dist/plot/scatter/BinnedScatterPlot.svelte +1116 -0
  411. package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +66 -0
  412. package/dist/plot/scatter/ElementScatter.svelte +63 -0
  413. package/dist/plot/scatter/ElementScatter.svelte.d.ts +14 -0
  414. package/dist/plot/scatter/ScatterPlot.svelte +2357 -0
  415. package/dist/plot/scatter/ScatterPlot.svelte.d.ts +96 -0
  416. package/dist/plot/scatter/ScatterPlotControls.svelte +307 -0
  417. package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +17 -0
  418. package/dist/plot/scatter/ScatterPoint.svelte +182 -0
  419. package/dist/plot/scatter/ScatterPoint.svelte.d.ts +22 -0
  420. package/dist/plot/scatter/adaptive-density.d.ts +79 -0
  421. package/dist/plot/scatter/adaptive-density.js +217 -0
  422. package/dist/plot/scatter/binned-scatter-types.d.ts +59 -0
  423. package/dist/plot/scatter/binned-scatter-types.js +1 -0
  424. package/dist/plot/scatter/index.d.ts +7 -0
  425. package/dist/plot/scatter/index.js +5 -0
  426. package/dist/plot/scatter/scatter-data.d.ts +19 -0
  427. package/dist/plot/scatter/scatter-data.js +212 -0
  428. package/dist/plot/scatter-3d/ScatterPlot3D.svelte +531 -0
  429. package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +95 -0
  430. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +438 -0
  431. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte.d.ts +20 -0
  432. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +912 -0
  433. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +74 -0
  434. package/dist/plot/scatter-3d/Surface3D.svelte +197 -0
  435. package/dist/plot/scatter-3d/Surface3D.svelte.d.ts +13 -0
  436. package/dist/plot/scatter-3d/index.d.ts +4 -0
  437. package/dist/plot/scatter-3d/index.js +4 -0
  438. package/dist/plot/sunburst/Sunburst.svelte +1045 -0
  439. package/dist/plot/sunburst/Sunburst.svelte.d.ts +96 -0
  440. package/dist/plot/sunburst/SunburstControls.svelte +200 -0
  441. package/dist/plot/sunburst/SunburstControls.svelte.d.ts +26 -0
  442. package/dist/plot/sunburst/index.d.ts +4 -0
  443. package/dist/plot/sunburst/index.js +4 -0
  444. package/dist/plot/sunburst/render.d.ts +34 -0
  445. package/dist/plot/sunburst/render.js +122 -0
  446. package/dist/plot/sunburst/sunburst.d.ts +62 -0
  447. package/dist/plot/sunburst/sunburst.js +266 -0
  448. package/dist/rdf/RdfPlot.svelte +248 -0
  449. package/dist/rdf/RdfPlot.svelte.d.ts +27 -0
  450. package/dist/rdf/calc-rdf.d.ts +4 -0
  451. package/dist/rdf/calc-rdf.js +98 -0
  452. package/dist/rdf/index.d.ts +23 -0
  453. package/dist/rdf/index.js +2 -0
  454. package/dist/sanitize.d.ts +6 -0
  455. package/dist/sanitize.js +116 -0
  456. package/dist/settings.d.ts +319 -0
  457. package/dist/settings.js +1394 -0
  458. package/dist/spectral/Bands.svelte +1050 -0
  459. package/dist/spectral/Bands.svelte.d.ts +39 -0
  460. package/dist/spectral/BandsAndDos.svelte +134 -0
  461. package/dist/spectral/BandsAndDos.svelte.d.ts +18 -0
  462. package/dist/spectral/BrillouinBandsDos.svelte +264 -0
  463. package/dist/spectral/BrillouinBandsDos.svelte.d.ts +20 -0
  464. package/dist/spectral/Dos.svelte +688 -0
  465. package/dist/spectral/Dos.svelte.d.ts +29 -0
  466. package/dist/spectral/helpers.d.ts +121 -0
  467. package/dist/spectral/helpers.js +1098 -0
  468. package/dist/spectral/index.d.ts +6 -0
  469. package/dist/spectral/index.js +6 -0
  470. package/dist/spectral/types.d.ts +84 -0
  471. package/dist/spectral/types.js +2 -0
  472. package/dist/state.svelte.d.ts +25 -0
  473. package/dist/state.svelte.js +45 -0
  474. package/dist/structure/Arrow.svelte +72 -0
  475. package/dist/structure/Arrow.svelte.d.ts +15 -0
  476. package/dist/structure/AtomLegend.svelte +814 -0
  477. package/dist/structure/AtomLegend.svelte.d.ts +35 -0
  478. package/dist/structure/Bond.svelte +140 -0
  479. package/dist/structure/Bond.svelte.d.ts +9 -0
  480. package/dist/structure/CanvasTooltip.svelte +33 -0
  481. package/dist/structure/CanvasTooltip.svelte.d.ts +12 -0
  482. package/dist/structure/CellSelect.svelte +348 -0
  483. package/dist/structure/CellSelect.svelte.d.ts +13 -0
  484. package/dist/structure/Cylinder.svelte +49 -0
  485. package/dist/structure/Cylinder.svelte.d.ts +13 -0
  486. package/dist/structure/Lattice.svelte +196 -0
  487. package/dist/structure/Lattice.svelte.d.ts +17 -0
  488. package/dist/structure/Structure.svelte +2254 -0
  489. package/dist/structure/Structure.svelte.d.ts +89 -0
  490. package/dist/structure/StructureControls.svelte +1273 -0
  491. package/dist/structure/StructureControls.svelte.d.ts +31 -0
  492. package/dist/structure/StructureExportPane.svelte +252 -0
  493. package/dist/structure/StructureExportPane.svelte.d.ts +17 -0
  494. package/dist/structure/StructureInfoPane.svelte +736 -0
  495. package/dist/structure/StructureInfoPane.svelte.d.ts +19 -0
  496. package/dist/structure/StructureScene.svelte +2256 -0
  497. package/dist/structure/StructureScene.svelte.d.ts +111 -0
  498. package/dist/structure/atom-properties.d.ts +37 -0
  499. package/dist/structure/atom-properties.js +200 -0
  500. package/dist/structure/bond-order-perception.d.ts +13 -0
  501. package/dist/structure/bond-order-perception.js +384 -0
  502. package/dist/structure/bonding.d.ts +69 -0
  503. package/dist/structure/bonding.js +724 -0
  504. package/dist/structure/export.d.ts +20 -0
  505. package/dist/structure/export.js +731 -0
  506. package/dist/structure/index.d.ts +124 -0
  507. package/dist/structure/index.js +167 -0
  508. package/dist/structure/label-placement.d.ts +14 -0
  509. package/dist/structure/label-placement.js +72 -0
  510. package/dist/structure/measure.d.ts +7 -0
  511. package/dist/structure/measure.js +30 -0
  512. package/dist/structure/parse.d.ts +66 -0
  513. package/dist/structure/parse.js +1410 -0
  514. package/dist/structure/partial-occupancy.d.ts +25 -0
  515. package/dist/structure/partial-occupancy.js +99 -0
  516. package/dist/structure/pbc.d.ts +9 -0
  517. package/dist/structure/pbc.js +127 -0
  518. package/dist/structure/supercell.d.ts +8 -0
  519. package/dist/structure/supercell.js +170 -0
  520. package/dist/structure/validation.d.ts +2 -0
  521. package/dist/structure/validation.js +10 -0
  522. package/dist/symmetry/SymmetryStats.svelte +226 -0
  523. package/dist/symmetry/SymmetryStats.svelte.d.ts +21 -0
  524. package/dist/symmetry/WyckoffTable.svelte +120 -0
  525. package/dist/symmetry/WyckoffTable.svelte.d.ts +11 -0
  526. package/dist/symmetry/cell-transform.d.ts +12 -0
  527. package/dist/symmetry/cell-transform.js +91 -0
  528. package/dist/symmetry/index.d.ts +43 -0
  529. package/dist/symmetry/index.js +226 -0
  530. package/dist/symmetry/spacegroups.d.ts +16 -0
  531. package/dist/symmetry/spacegroups.js +429 -0
  532. package/dist/table/HeatmapTable.svelte +1885 -0
  533. package/dist/table/HeatmapTable.svelte.d.ts +49 -0
  534. package/dist/table/ToggleMenu.svelte +385 -0
  535. package/dist/table/ToggleMenu.svelte.d.ts +11 -0
  536. package/dist/table/index.d.ts +72 -0
  537. package/dist/table/index.js +38 -0
  538. package/dist/theme/ThemeControl.svelte +53 -0
  539. package/dist/theme/ThemeControl.svelte.d.ts +9 -0
  540. package/dist/theme/index.d.ts +29 -0
  541. package/dist/theme/index.js +79 -0
  542. package/dist/time.d.ts +4 -0
  543. package/dist/time.js +70 -0
  544. package/dist/tooltip/KCoords.svelte +45 -0
  545. package/dist/tooltip/KCoords.svelte.d.ts +8 -0
  546. package/dist/tooltip/TooltipContent.svelte +58 -0
  547. package/dist/tooltip/TooltipContent.svelte.d.ts +31 -0
  548. package/dist/tooltip/index.d.ts +3 -0
  549. package/dist/tooltip/index.js +2 -0
  550. package/dist/tooltip/types.d.ts +8 -0
  551. package/dist/tooltip/types.js +1 -0
  552. package/dist/trajectory/Trajectory.svelte +1571 -0
  553. package/dist/trajectory/Trajectory.svelte.d.ts +78 -0
  554. package/dist/trajectory/TrajectoryError.svelte +128 -0
  555. package/dist/trajectory/TrajectoryError.svelte.d.ts +13 -0
  556. package/dist/trajectory/TrajectoryExportPane.svelte +358 -0
  557. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +17 -0
  558. package/dist/trajectory/TrajectoryInfoPane.svelte +314 -0
  559. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +17 -0
  560. package/dist/trajectory/constants.d.ts +6 -0
  561. package/dist/trajectory/constants.js +7 -0
  562. package/dist/trajectory/extract.d.ts +5 -0
  563. package/dist/trajectory/extract.js +162 -0
  564. package/dist/trajectory/format-detect.d.ts +10 -0
  565. package/dist/trajectory/format-detect.js +90 -0
  566. package/dist/trajectory/frame-reader.d.ts +17 -0
  567. package/dist/trajectory/frame-reader.js +299 -0
  568. package/dist/trajectory/helpers.d.ts +15 -0
  569. package/dist/trajectory/helpers.js +164 -0
  570. package/dist/trajectory/index.d.ts +63 -0
  571. package/dist/trajectory/index.js +126 -0
  572. package/dist/trajectory/parse/ase.d.ts +2 -0
  573. package/dist/trajectory/parse/ase.js +73 -0
  574. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  575. package/dist/trajectory/parse/hdf5.js +127 -0
  576. package/dist/trajectory/parse/index.d.ts +12 -0
  577. package/dist/trajectory/parse/index.js +306 -0
  578. package/dist/trajectory/parse/lammps.d.ts +5 -0
  579. package/dist/trajectory/parse/lammps.js +179 -0
  580. package/dist/trajectory/parse/vasp.d.ts +2 -0
  581. package/dist/trajectory/parse/vasp.js +87 -0
  582. package/dist/trajectory/parse/xyz.d.ts +26 -0
  583. package/dist/trajectory/parse/xyz.js +123 -0
  584. package/dist/trajectory/plotting.d.ts +28 -0
  585. package/dist/trajectory/plotting.js +423 -0
  586. package/dist/trajectory/types.d.ts +11 -0
  587. package/dist/trajectory/types.js +1 -0
  588. package/dist/utils.d.ts +7 -0
  589. package/dist/utils.js +47 -0
  590. package/dist/xrd/XrdPlot.svelte +616 -0
  591. package/dist/xrd/XrdPlot.svelte.d.ts +28 -0
  592. package/dist/xrd/broadening.d.ts +20 -0
  593. package/dist/xrd/broadening.js +97 -0
  594. package/dist/xrd/calc-xrd.d.ts +37 -0
  595. package/dist/xrd/calc-xrd.js +339 -0
  596. package/dist/xrd/index.d.ts +37 -0
  597. package/dist/xrd/index.js +4 -0
  598. package/dist/xrd/parse.d.ts +13 -0
  599. package/dist/xrd/parse.js +749 -0
  600. package/license +1 -1
  601. package/package.json +237 -1458
  602. package/readme.md +98 -171
  603. package/.vscode/launch.json +0 -13
  604. package/.vscodeignore +0 -7
  605. package/dist/assets/STLExporter-BpTH3YHE.js +0 -8
  606. package/dist/assets/browser-DdDecX_W.js +0 -1
  607. package/dist/assets/export-qgn-H9y6.js +0 -2
  608. package/dist/assets/main-DiKYzti2.css +0 -1
  609. package/dist/assets/moyo_wasm_bg-0ocwg7xY.wasm +0 -0
  610. package/dist/extension.js +0 -31293
  611. package/dist/src/lib/FilePicker.svelte +0 -360
  612. package/dist/src/lib/Icon.svelte +0 -41
  613. package/dist/src/lib/MillerIndexInput.svelte +0 -66
  614. package/dist/src/lib/api/mp.ts +0 -26
  615. package/dist/src/lib/api/optimade.ts +0 -204
  616. package/dist/src/lib/app.css +0 -247
  617. package/dist/src/lib/brillouin/BrillouinZone.svelte +0 -549
  618. package/dist/src/lib/brillouin/BrillouinZoneControls.svelte +0 -144
  619. package/dist/src/lib/brillouin/BrillouinZoneExportPane.svelte +0 -146
  620. package/dist/src/lib/brillouin/BrillouinZoneInfoPane.svelte +0 -146
  621. package/dist/src/lib/brillouin/BrillouinZoneScene.svelte +0 -476
  622. package/dist/src/lib/brillouin/BrillouinZoneTooltip.svelte +0 -92
  623. package/dist/src/lib/brillouin/compute.ts +0 -529
  624. package/dist/src/lib/brillouin/index.ts +0 -8
  625. package/dist/src/lib/brillouin/types.ts +0 -51
  626. package/dist/src/lib/chempot-diagram/ChemPotDiagram.svelte +0 -327
  627. package/dist/src/lib/chempot-diagram/ChemPotDiagram2D.svelte +0 -846
  628. package/dist/src/lib/chempot-diagram/ChemPotDiagram3D.svelte +0 -3193
  629. package/dist/src/lib/chempot-diagram/async-compute.svelte.ts +0 -94
  630. package/dist/src/lib/chempot-diagram/chempot-worker.ts +0 -11
  631. package/dist/src/lib/chempot-diagram/color.ts +0 -42
  632. package/dist/src/lib/chempot-diagram/compute.ts +0 -1014
  633. package/dist/src/lib/chempot-diagram/index.ts +0 -6
  634. package/dist/src/lib/chempot-diagram/pointer.ts +0 -56
  635. package/dist/src/lib/chempot-diagram/temperature.ts +0 -77
  636. package/dist/src/lib/chempot-diagram/types.ts +0 -130
  637. package/dist/src/lib/colors/index.ts +0 -249
  638. package/dist/src/lib/composition/BarChart.svelte +0 -297
  639. package/dist/src/lib/composition/BubbleChart.svelte +0 -218
  640. package/dist/src/lib/composition/Composition.svelte +0 -165
  641. package/dist/src/lib/composition/Formula.svelte +0 -268
  642. package/dist/src/lib/composition/FormulaFilter.svelte +0 -1257
  643. package/dist/src/lib/composition/PieChart.svelte +0 -323
  644. package/dist/src/lib/composition/format.ts +0 -155
  645. package/dist/src/lib/composition/index.ts +0 -37
  646. package/dist/src/lib/composition/parse.ts +0 -605
  647. package/dist/src/lib/constants.ts +0 -134
  648. package/dist/src/lib/controls.ts +0 -42
  649. package/dist/src/lib/convex-hull/ConvexHull.svelte +0 -157
  650. package/dist/src/lib/convex-hull/ConvexHull2D.svelte +0 -825
  651. package/dist/src/lib/convex-hull/ConvexHull3D.svelte +0 -1801
  652. package/dist/src/lib/convex-hull/ConvexHull4D.svelte +0 -1398
  653. package/dist/src/lib/convex-hull/ConvexHullControls.svelte +0 -535
  654. package/dist/src/lib/convex-hull/ConvexHullInfoPane.svelte +0 -125
  655. package/dist/src/lib/convex-hull/ConvexHullStats.svelte +0 -929
  656. package/dist/src/lib/convex-hull/ConvexHullTooltip.svelte +0 -131
  657. package/dist/src/lib/convex-hull/GasPressureControls.svelte +0 -247
  658. package/dist/src/lib/convex-hull/StructurePopup.svelte +0 -151
  659. package/dist/src/lib/convex-hull/TemperatureSlider.svelte +0 -140
  660. package/dist/src/lib/convex-hull/barycentric-coords.ts +0 -246
  661. package/dist/src/lib/convex-hull/demo-temperature.ts +0 -63
  662. package/dist/src/lib/convex-hull/gas-thermodynamics.ts +0 -405
  663. package/dist/src/lib/convex-hull/helpers.ts +0 -932
  664. package/dist/src/lib/convex-hull/index.ts +0 -202
  665. package/dist/src/lib/convex-hull/thermodynamics.ts +0 -2192
  666. package/dist/src/lib/convex-hull/types.ts +0 -267
  667. package/dist/src/lib/coordination/CoordinationBarPlot.svelte +0 -311
  668. package/dist/src/lib/coordination/calc-coordination.ts +0 -93
  669. package/dist/src/lib/coordination/index.ts +0 -9
  670. package/dist/src/lib/effects.svelte.ts +0 -48
  671. package/dist/src/lib/element/ElementHeading.svelte +0 -26
  672. package/dist/src/lib/element/ElementPhoto.svelte +0 -57
  673. package/dist/src/lib/element/ElementStats.svelte +0 -80
  674. package/dist/src/lib/element/ElementTile.svelte +0 -484
  675. package/dist/src/lib/element/data.ts +0 -14
  676. package/dist/src/lib/element/index.ts +0 -8
  677. package/dist/src/lib/element/types.ts +0 -62
  678. package/dist/src/lib/feedback/ClickFeedback.svelte +0 -58
  679. package/dist/src/lib/feedback/DragOverlay.svelte +0 -42
  680. package/dist/src/lib/feedback/index.ts +0 -4
  681. package/dist/src/lib/fermi-surface/FermiSlice.svelte +0 -189
  682. package/dist/src/lib/fermi-surface/FermiSurface.svelte +0 -600
  683. package/dist/src/lib/fermi-surface/FermiSurfaceControls.svelte +0 -448
  684. package/dist/src/lib/fermi-surface/FermiSurfaceScene.svelte +0 -794
  685. package/dist/src/lib/fermi-surface/FermiSurfaceTooltip.svelte +0 -111
  686. package/dist/src/lib/fermi-surface/compute.ts +0 -728
  687. package/dist/src/lib/fermi-surface/constants.ts +0 -32
  688. package/dist/src/lib/fermi-surface/export.ts +0 -64
  689. package/dist/src/lib/fermi-surface/index.ts +0 -14
  690. package/dist/src/lib/fermi-surface/marching-cubes.ts +0 -3
  691. package/dist/src/lib/fermi-surface/parse.ts +0 -574
  692. package/dist/src/lib/fermi-surface/symmetry.ts +0 -56
  693. package/dist/src/lib/fermi-surface/types.ts +0 -159
  694. package/dist/src/lib/heatmap-matrix/HeatmapMatrix.svelte +0 -1545
  695. package/dist/src/lib/heatmap-matrix/HeatmapMatrixControls.svelte +0 -225
  696. package/dist/src/lib/heatmap-matrix/index.ts +0 -167
  697. package/dist/src/lib/heatmap-matrix/shared.ts +0 -7
  698. package/dist/src/lib/icons.ts +0 -650
  699. package/dist/src/lib/index.ts +0 -61
  700. package/dist/src/lib/io/decompress.ts +0 -92
  701. package/dist/src/lib/io/export.ts +0 -385
  702. package/dist/src/lib/io/fetch.ts +0 -46
  703. package/dist/src/lib/io/file-drop.ts +0 -51
  704. package/dist/src/lib/io/index.ts +0 -7
  705. package/dist/src/lib/io/is-binary.ts +0 -24
  706. package/dist/src/lib/io/types.ts +0 -8
  707. package/dist/src/lib/io/url-drop.ts +0 -141
  708. package/dist/src/lib/isosurface/Isosurface.svelte +0 -285
  709. package/dist/src/lib/isosurface/IsosurfaceControls.svelte +0 -277
  710. package/dist/src/lib/isosurface/index.ts +0 -7
  711. package/dist/src/lib/isosurface/parse.ts +0 -656
  712. package/dist/src/lib/isosurface/slice.ts +0 -175
  713. package/dist/src/lib/isosurface/types.ts +0 -309
  714. package/dist/src/lib/labels.ts +0 -320
  715. package/dist/src/lib/layout/FullscreenToggle.svelte +0 -50
  716. package/dist/src/lib/layout/InfoCard.svelte +0 -120
  717. package/dist/src/lib/layout/InfoTag.svelte +0 -185
  718. package/dist/src/lib/layout/PropertyFilter.svelte +0 -246
  719. package/dist/src/lib/layout/SettingsSection.svelte +0 -148
  720. package/dist/src/lib/layout/SubpageGrid.svelte +0 -82
  721. package/dist/src/lib/layout/fullscreen.ts +0 -65
  722. package/dist/src/lib/layout/index.ts +0 -11
  723. package/dist/src/lib/layout/json-tree/JsonNode.svelte +0 -548
  724. package/dist/src/lib/layout/json-tree/JsonTree.svelte +0 -1230
  725. package/dist/src/lib/layout/json-tree/index.ts +0 -3
  726. package/dist/src/lib/layout/json-tree/types.ts +0 -126
  727. package/dist/src/lib/layout/json-tree/utils.ts +0 -682
  728. package/dist/src/lib/marching-cubes.ts +0 -614
  729. package/dist/src/lib/math.ts +0 -1081
  730. package/dist/src/lib/overlays/ContextMenu.svelte +0 -162
  731. package/dist/src/lib/overlays/CopyButton.svelte +0 -45
  732. package/dist/src/lib/overlays/DragControlTab.svelte +0 -98
  733. package/dist/src/lib/overlays/DraggablePane.svelte +0 -487
  734. package/dist/src/lib/overlays/InfoPaneCards.svelte +0 -149
  735. package/dist/src/lib/overlays/index.ts +0 -3
  736. package/dist/src/lib/periodic-table/PeriodicTable.svelte +0 -469
  737. package/dist/src/lib/periodic-table/PeriodicTableControls.svelte +0 -557
  738. package/dist/src/lib/periodic-table/PropertySelect.svelte +0 -37
  739. package/dist/src/lib/periodic-table/index.ts +0 -12
  740. package/dist/src/lib/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +0 -1086
  741. package/dist/src/lib/phase-diagram/PhaseDiagramControls.svelte +0 -444
  742. package/dist/src/lib/phase-diagram/PhaseDiagramEditorPane.svelte +0 -126
  743. package/dist/src/lib/phase-diagram/PhaseDiagramExportPane.svelte +0 -184
  744. package/dist/src/lib/phase-diagram/PhaseDiagramTooltip.svelte +0 -391
  745. package/dist/src/lib/phase-diagram/TdbInfoPanel.svelte +0 -203
  746. package/dist/src/lib/phase-diagram/build-diagram.ts +0 -186
  747. package/dist/src/lib/phase-diagram/colors.ts +0 -58
  748. package/dist/src/lib/phase-diagram/diagram-input.ts +0 -40
  749. package/dist/src/lib/phase-diagram/index.ts +0 -13
  750. package/dist/src/lib/phase-diagram/parse.ts +0 -348
  751. package/dist/src/lib/phase-diagram/svg-to-diagram.ts +0 -1023
  752. package/dist/src/lib/phase-diagram/types.ts +0 -144
  753. package/dist/src/lib/phase-diagram/utils.ts +0 -775
  754. package/dist/src/lib/plot/AxisLabel.svelte +0 -51
  755. package/dist/src/lib/plot/BarPlot.svelte +0 -2113
  756. package/dist/src/lib/plot/BarPlotControls.svelte +0 -66
  757. package/dist/src/lib/plot/BinnedScatterPlot.svelte +0 -1114
  758. package/dist/src/lib/plot/ColorBar.svelte +0 -721
  759. package/dist/src/lib/plot/ColorScaleSelect.svelte +0 -54
  760. package/dist/src/lib/plot/ElementScatter.svelte +0 -63
  761. package/dist/src/lib/plot/FillArea.svelte +0 -223
  762. package/dist/src/lib/plot/Histogram.svelte +0 -1558
  763. package/dist/src/lib/plot/HistogramControls.svelte +0 -212
  764. package/dist/src/lib/plot/InteractiveAxisLabel.svelte +0 -96
  765. package/dist/src/lib/plot/Line.svelte +0 -84
  766. package/dist/src/lib/plot/PlotAxis.svelte +0 -169
  767. package/dist/src/lib/plot/PlotControls.svelte +0 -537
  768. package/dist/src/lib/plot/PlotLegend.svelte +0 -569
  769. package/dist/src/lib/plot/PlotTooltip.svelte +0 -67
  770. package/dist/src/lib/plot/PortalSelect.svelte +0 -253
  771. package/dist/src/lib/plot/ReferenceLine.svelte +0 -204
  772. package/dist/src/lib/plot/ReferenceLine3D.svelte +0 -156
  773. package/dist/src/lib/plot/ReferencePlane.svelte +0 -175
  774. package/dist/src/lib/plot/ScatterPlot.svelte +0 -2778
  775. package/dist/src/lib/plot/ScatterPlot3D.svelte +0 -529
  776. package/dist/src/lib/plot/ScatterPlot3DControls.svelte +0 -437
  777. package/dist/src/lib/plot/ScatterPlot3DScene.svelte +0 -912
  778. package/dist/src/lib/plot/ScatterPlotControls.svelte +0 -306
  779. package/dist/src/lib/plot/ScatterPoint.svelte +0 -182
  780. package/dist/src/lib/plot/SpacegroupBarPlot.svelte +0 -293
  781. package/dist/src/lib/plot/Surface3D.svelte +0 -197
  782. package/dist/src/lib/plot/ZeroLines.svelte +0 -97
  783. package/dist/src/lib/plot/ZoomRect.svelte +0 -23
  784. package/dist/src/lib/plot/adaptive-density.ts +0 -316
  785. package/dist/src/lib/plot/auto-place.ts +0 -184
  786. package/dist/src/lib/plot/axis-utils.ts +0 -122
  787. package/dist/src/lib/plot/binned-scatter-types.ts +0 -83
  788. package/dist/src/lib/plot/data-cleaning.ts +0 -1069
  789. package/dist/src/lib/plot/data-transform.ts +0 -69
  790. package/dist/src/lib/plot/defaults.ts +0 -9
  791. package/dist/src/lib/plot/fill-utils.ts +0 -494
  792. package/dist/src/lib/plot/hover-lock.svelte.ts +0 -60
  793. package/dist/src/lib/plot/index.ts +0 -53
  794. package/dist/src/lib/plot/interactions.ts +0 -119
  795. package/dist/src/lib/plot/layout.ts +0 -425
  796. package/dist/src/lib/plot/reference-line.ts +0 -426
  797. package/dist/src/lib/plot/scales.ts +0 -654
  798. package/dist/src/lib/plot/svg.ts +0 -23
  799. package/dist/src/lib/plot/types.ts +0 -1144
  800. package/dist/src/lib/plot/utils/label-placement.ts +0 -541
  801. package/dist/src/lib/plot/utils/series-visibility.ts +0 -140
  802. package/dist/src/lib/plot/utils.ts +0 -11
  803. package/dist/src/lib/rdf/RdfPlot.svelte +0 -247
  804. package/dist/src/lib/rdf/calc-rdf.ts +0 -167
  805. package/dist/src/lib/rdf/index.ts +0 -27
  806. package/dist/src/lib/sanitize.ts +0 -126
  807. package/dist/src/lib/settings.ts +0 -1479
  808. package/dist/src/lib/spectral/Bands.svelte +0 -1040
  809. package/dist/src/lib/spectral/BandsAndDos.svelte +0 -134
  810. package/dist/src/lib/spectral/BrillouinBandsDos.svelte +0 -252
  811. package/dist/src/lib/spectral/Dos.svelte +0 -697
  812. package/dist/src/lib/spectral/helpers.ts +0 -1381
  813. package/dist/src/lib/spectral/index.ts +0 -8
  814. package/dist/src/lib/spectral/types.ts +0 -112
  815. package/dist/src/lib/state.svelte.ts +0 -64
  816. package/dist/src/lib/structure/Arrow.svelte +0 -72
  817. package/dist/src/lib/structure/AtomLegend.svelte +0 -815
  818. package/dist/src/lib/structure/Bond.svelte +0 -140
  819. package/dist/src/lib/structure/CanvasTooltip.svelte +0 -33
  820. package/dist/src/lib/structure/CellSelect.svelte +0 -349
  821. package/dist/src/lib/structure/Cylinder.svelte +0 -45
  822. package/dist/src/lib/structure/Lattice.svelte +0 -196
  823. package/dist/src/lib/structure/Structure.svelte +0 -2248
  824. package/dist/src/lib/structure/StructureControls.svelte +0 -1273
  825. package/dist/src/lib/structure/StructureExportPane.svelte +0 -252
  826. package/dist/src/lib/structure/StructureInfoPane.svelte +0 -737
  827. package/dist/src/lib/structure/StructureScene.svelte +0 -2255
  828. package/dist/src/lib/structure/atom-properties.ts +0 -316
  829. package/dist/src/lib/structure/bond-order-perception.ts +0 -447
  830. package/dist/src/lib/structure/bonding.ts +0 -944
  831. package/dist/src/lib/structure/export.ts +0 -861
  832. package/dist/src/lib/structure/index.ts +0 -291
  833. package/dist/src/lib/structure/label-placement.ts +0 -130
  834. package/dist/src/lib/structure/measure.ts +0 -45
  835. package/dist/src/lib/structure/parse.ts +0 -1705
  836. package/dist/src/lib/structure/partial-occupancy.ts +0 -183
  837. package/dist/src/lib/structure/pbc.ts +0 -164
  838. package/dist/src/lib/structure/supercell.ts +0 -226
  839. package/dist/src/lib/structure/validation.ts +0 -11
  840. package/dist/src/lib/symmetry/SymmetryStats.svelte +0 -226
  841. package/dist/src/lib/symmetry/WyckoffTable.svelte +0 -120
  842. package/dist/src/lib/symmetry/cell-transform.ts +0 -118
  843. package/dist/src/lib/symmetry/index.ts +0 -348
  844. package/dist/src/lib/symmetry/spacegroups.ts +0 -404
  845. package/dist/src/lib/table/HeatmapTable.svelte +0 -1833
  846. package/dist/src/lib/table/ToggleMenu.svelte +0 -385
  847. package/dist/src/lib/table/index.ts +0 -139
  848. package/dist/src/lib/theme/ThemeControl.svelte +0 -53
  849. package/dist/src/lib/theme/index.ts +0 -107
  850. package/dist/src/lib/time.ts +0 -71
  851. package/dist/src/lib/tooltip/TooltipContent.svelte +0 -58
  852. package/dist/src/lib/tooltip/index.ts +0 -2
  853. package/dist/src/lib/tooltip/types.ts +0 -13
  854. package/dist/src/lib/trajectory/Trajectory.svelte +0 -1545
  855. package/dist/src/lib/trajectory/TrajectoryError.svelte +0 -128
  856. package/dist/src/lib/trajectory/TrajectoryExportPane.svelte +0 -357
  857. package/dist/src/lib/trajectory/TrajectoryInfoPane.svelte +0 -313
  858. package/dist/src/lib/trajectory/constants.ts +0 -7
  859. package/dist/src/lib/trajectory/extract.ts +0 -196
  860. package/dist/src/lib/trajectory/format-detect.ts +0 -96
  861. package/dist/src/lib/trajectory/frame-reader.ts +0 -456
  862. package/dist/src/lib/trajectory/helpers.ts +0 -217
  863. package/dist/src/lib/trajectory/index.ts +0 -218
  864. package/dist/src/lib/trajectory/parse/ase.ts +0 -109
  865. package/dist/src/lib/trajectory/parse/hdf5.ts +0 -173
  866. package/dist/src/lib/trajectory/parse/index.ts +0 -411
  867. package/dist/src/lib/trajectory/parse/lammps.ts +0 -215
  868. package/dist/src/lib/trajectory/parse/vasp.ts +0 -102
  869. package/dist/src/lib/trajectory/parse/xyz.ts +0 -143
  870. package/dist/src/lib/trajectory/plotting.ts +0 -599
  871. package/dist/src/lib/trajectory/types.ts +0 -13
  872. package/dist/src/lib/utils.ts +0 -56
  873. package/dist/src/lib/xrd/XrdPlot.svelte +0 -615
  874. package/dist/src/lib/xrd/broadening.ts +0 -130
  875. package/dist/src/lib/xrd/calc-xrd.ts +0 -397
  876. package/dist/src/lib/xrd/index.ts +0 -38
  877. package/dist/src/lib/xrd/parse.ts +0 -858
  878. package/dist/webview.js +0 -29421
  879. package/icon.png +0 -0
  880. package/matterviz-0.3.2.vsix +0 -0
  881. package/matterviz-0.3.4.vsix +0 -0
  882. package/matterviz-0.3.5.vsix +0 -0
  883. package/scripts/sync-config.ts +0 -101
  884. package/src/declarations.d.ts +0 -2
  885. package/src/extension.ts +0 -972
  886. package/src/node-io.ts +0 -65
  887. package/src/types.ts +0 -17
  888. package/src/webview/JsonBrowser.svelte +0 -1079
  889. package/src/webview/PlotPanel.svelte +0 -346
  890. package/src/webview/detect.ts +0 -444
  891. package/src/webview/main.ts +0 -764
  892. package/src/webview/plot-utils.ts +0 -250
  893. package/test-fixtures/all-viz-types.json.gz +0 -0
  894. package/test-fixtures/plot-demo-data.json.gz +0 -0
  895. package/tests/detect.test.ts +0 -604
  896. package/tests/extension.test.ts +0 -2041
  897. package/tests/node-io.test.ts +0 -39
  898. package/tests/plot-utils.test.ts +0 -302
  899. package/tests/vite-plugin-json-gz.test.ts +0 -114
  900. package/tests/vscode-mock.ts +0 -18
  901. package/tests/webview.test.ts +0 -231
  902. package/tsconfig.json +0 -20
  903. package/vite-plugin-json-gz.ts +0 -29
  904. package/vite.config.ts +0 -34
  905. package/vite.extension.config.ts +0 -34
  906. /package/dist/{src/lib/EmptyState.svelte → EmptyState.svelte} +0 -0
  907. /package/dist/{src/lib/chempot-diagram → chempot-diagram}/ChemPotScene3D.svelte +0 -0
  908. /package/dist/{src/lib/colors → colors}/alloy-colors.json +0 -0
  909. /package/dist/{src/lib/colors → colors}/dark-mode-colors.json +0 -0
  910. /package/dist/{src/lib/colors → colors}/jmol-colors.json +0 -0
  911. /package/dist/{src/lib/colors → colors}/muted-colors.json +0 -0
  912. /package/dist/{src/lib/colors → colors}/pastel-colors.json +0 -0
  913. /package/dist/{src/lib/colors → colors}/vesta-colors.json +0 -0
  914. /package/dist/{src/lib/element → element}/BohrAtom.svelte +0 -0
  915. /package/dist/{src/lib/element → element}/Nucleus.svelte +0 -0
  916. /package/dist/{src/lib/element → element}/data.json +0 -0
  917. /package/dist/{src/lib/element → element}/data.json.gz +0 -0
  918. /package/dist/{src/lib/element → element}/data.json.gz.d.ts +0 -0
  919. /package/dist/{src/lib/element → element}/data.schema.json +0 -0
  920. /package/dist/{src/lib/element-image-urls.json → element-image-urls.json} +0 -0
  921. /package/dist/{src/lib/feedback → feedback}/Spinner.svelte +0 -0
  922. /package/dist/{src/lib/feedback → feedback}/StatusMessage.svelte +0 -0
  923. /package/dist/{src/lib/layout → layout}/json-tree/JsonValue.svelte +0 -0
  924. /package/dist/{src/lib/periodic-table → periodic-table}/TableInset.svelte +0 -0
  925. /package/dist/{src/lib/theme → theme}/themes.mjs +0 -0
  926. /package/dist/{src/lib/xrd → xrd}/atomic_scattering_params.json +0 -0
@@ -0,0 +1,2256 @@
1
+ <script lang="ts">
2
+ import type { D3InterpolateName } from '../colors'
3
+ import { AXIS_COLORS, get_d3_interpolator, NEG_AXIS_COLORS } from '../colors'
4
+ import type { ElementSymbol } from '../element'
5
+ import { element_data } from '../element'
6
+ import Isosurface from '../isosurface/Isosurface.svelte'
7
+ import type { IsosurfaceSettings, VolumetricData } from '../isosurface/types'
8
+ import { DEFAULT_ISOSURFACE_SETTINGS } from '../isosurface/types'
9
+ import { format_num } from '../labels'
10
+ import type { Vec3 } from '../math'
11
+ import * as math from '../math'
12
+ import type {
13
+ CameraProjection,
14
+ ShowBonds,
15
+ VectorColorMode,
16
+ VectorLayerConfig,
17
+ } from '../settings'
18
+ import { DEFAULTS } from '../settings'
19
+ import { create_pulse_animation } from '../effects.svelte'
20
+ import { colors } from '../state.svelte'
21
+ import type {
22
+ AnyStructure,
23
+ BondEditMode,
24
+ BondOrder,
25
+ BondPair,
26
+ MeasureMode,
27
+ Site,
28
+ StructureBond,
29
+ } from './'
30
+ import {
31
+ Arrow,
32
+ atomic_radii,
33
+ Cylinder,
34
+ get_all_site_vectors,
35
+ get_center_of_mass,
36
+ get_structure_vector_keys,
37
+ Lattice,
38
+ VECTOR_PALETTE,
39
+ } from './'
40
+ import type { AtomColorConfig } from './atom-properties'
41
+ import {
42
+ get_orig_site_idx,
43
+ get_property_colors,
44
+ } from './atom-properties'
45
+ import * as measure from './measure'
46
+ import {
47
+ compute_slice_geometry,
48
+ merge_split_partial_sites,
49
+ PARTIAL_OCCUPANCY_CAP_ARC,
50
+ } from './partial-occupancy'
51
+ import type { MoyoDataset } from '@spglib/moyo-wasm'
52
+ import { T, useThrelte } from '@threlte/core'
53
+ import * as extras from '@threlte/extras'
54
+ import { type ComponentProps, type Snippet, untrack } from 'svelte'
55
+ import { SvelteMap, SvelteSet } from 'svelte/reactivity'
56
+ import { type Camera, Color, type Mesh, type Object3D, type Scene, Vector3 } from 'three'
57
+ import Bond from './Bond.svelte'
58
+ import type { BondEditResult, BondingStrategy, BondKeyTarget } from './bonding'
59
+ import {
60
+ add_or_restore_bond,
61
+ BONDING_STRATEGIES,
62
+ BOND_ORDER_OPTIONS,
63
+ canonicalize_bond_target,
64
+ delete_bond as apply_delete_bond,
65
+ get_bond_key,
66
+ get_bond_render_matrices,
67
+ get_explicit_bond_metadata,
68
+ set_bond_order as apply_set_bond_order,
69
+ structure_bond_to_bond_pair,
70
+ } from './bonding'
71
+ import {
72
+ CanvasTooltip,
73
+ compose_perceived_bonds,
74
+ perceive_bond_orders,
75
+ } from './index'
76
+ import {
77
+ choose_site_label_offset,
78
+ LABEL_OFFSET_EPS,
79
+ make_label_position_calculator,
80
+ } from './label-placement'
81
+
82
+ type InstancedAtomGroup = {
83
+ element: string
84
+ radius: number
85
+ color: string
86
+ is_image_atom: boolean
87
+ atoms: (typeof atom_data)[number][]
88
+ }
89
+
90
+ function instanced_atom_group_key(
91
+ { element, radius, color, is_image_atom, atoms }: InstancedAtomGroup,
92
+ measure_mode: MeasureMode,
93
+ ): string {
94
+ const edit_mode_image = measure_mode === `edit-atoms` && is_image_atom
95
+ return `${element}-${format_num(radius, `.3~`)}-${color}-${
96
+ is_image_atom ? `img` : `base`
97
+ }-${edit_mode_image}-${atoms.length}`
98
+ }
99
+
100
+ type EditableAtomHitTarget = {
101
+ site_idx: number
102
+ position: Vec3
103
+ radius: number
104
+ }
105
+
106
+ type BondContextMenu = {
107
+ site_idx_1: number
108
+ site_idx_2: number
109
+ cell_shift?: Vec3
110
+ position: Vec3
111
+ }
112
+
113
+ type BondPointerEvent = PointerEvent & {
114
+ nativeEvent?: PointerEvent
115
+ object?: Object3D
116
+ point?: Vector3
117
+ }
118
+
119
+ type BondContextMenuEvent = MouseEvent & {
120
+ nativeEvent?: MouseEvent
121
+ object?: Object3D
122
+ point?: Vector3
123
+ }
124
+
125
+ let {
126
+ structure = undefined,
127
+ base_structure = undefined,
128
+ atom_radius = DEFAULTS.structure.atom_radius,
129
+ same_size_atoms = false,
130
+ camera_position = DEFAULTS.structure.camera_position,
131
+ camera_target = undefined,
132
+ camera_projection = DEFAULTS.structure.camera_projection,
133
+ rotation_damping = DEFAULTS.structure.rotation_damping,
134
+ max_zoom = DEFAULTS.structure.max_zoom,
135
+ min_zoom = DEFAULTS.structure.min_zoom,
136
+ rotate_speed = DEFAULTS.structure.rotate_speed,
137
+ zoom_speed = DEFAULTS.structure.zoom_speed,
138
+ pan_speed = DEFAULTS.structure.pan_speed,
139
+ zoom_to_cursor = DEFAULTS.structure.zoom_to_cursor,
140
+ show_atoms = DEFAULTS.structure.show_atoms,
141
+ show_bonds = DEFAULTS.structure.show_bonds,
142
+ show_site_labels = DEFAULTS.structure.show_site_labels,
143
+ show_site_indices = DEFAULTS.structure.show_site_indices,
144
+ site_label_size = DEFAULTS.structure.site_label_size,
145
+ site_label_offset = $bindable(DEFAULTS.structure.site_label_offset),
146
+ site_label_bg_color = DEFAULTS.structure.site_label_bg_color,
147
+ site_label_color = DEFAULTS.structure.site_label_color,
148
+ site_label_padding = DEFAULTS.structure.site_label_padding,
149
+ vector_configs = $bindable<Record<string, VectorLayerConfig>>({}),
150
+ vector_scale = DEFAULTS.structure.vector_scale,
151
+ vector_color = DEFAULTS.structure.vector_color,
152
+ vector_color_mode = DEFAULTS.structure.vector_color_mode as VectorColorMode,
153
+ vector_color_scale = DEFAULTS.structure.vector_color_scale,
154
+ vector_normalize = DEFAULTS.structure.vector_normalize,
155
+ vector_uniform_thickness = DEFAULTS.structure.vector_uniform_thickness,
156
+ vector_origin_gap = DEFAULTS.structure.vector_origin_gap,
157
+ vector_shaft_radius = DEFAULTS.structure.vector_shaft_radius,
158
+ vector_arrow_head_radius = DEFAULTS.structure.vector_arrow_head_radius,
159
+ vector_arrow_head_length = DEFAULTS.structure.vector_arrow_head_length,
160
+ gizmo = DEFAULTS.structure.show_gizmo,
161
+ hovered_idx = $bindable(null),
162
+ hovered_site = $bindable(null),
163
+ float_fmt = `.3~f`,
164
+ auto_rotate = DEFAULTS.structure.auto_rotate,
165
+ bond_thickness = DEFAULTS.structure.bond_thickness,
166
+ bond_color = DEFAULTS.structure.bond_color,
167
+ bonding_strategy = DEFAULTS.structure.bonding_strategy,
168
+ auto_bond_order = DEFAULTS.structure.auto_bond_order,
169
+ aromatic_display = DEFAULTS.structure.aromatic_display,
170
+ bonding_options = {},
171
+ fov = DEFAULTS.structure.fov,
172
+ initial_zoom = DEFAULTS.structure.initial_zoom,
173
+ ambient_light = DEFAULTS.structure.ambient_light,
174
+ directional_light = DEFAULTS.structure.directional_light,
175
+ sphere_segments = DEFAULTS.structure.sphere_segments,
176
+ lattice_props = {},
177
+ atom_label,
178
+ camera_is_moving = $bindable(false),
179
+ width = 0,
180
+ height = 0,
181
+ measure_mode = `distance`,
182
+ selected_sites = $bindable([]),
183
+ measured_sites = $bindable([]),
184
+ added_bonds = $bindable([]),
185
+ removed_bonds = $bindable([]),
186
+ bond_order_overrides = $bindable([]),
187
+ bond_edits_enabled = true,
188
+ bond_edit_mode = $bindable<BondEditMode>(`add`),
189
+ bond_edit_order = 1,
190
+ selection_highlight_color = `#6cf0ff`,
191
+ // Active highlight group with different color
192
+ active_sites = $bindable([]),
193
+ active_highlight_color = `var(--struct-active-highlight-color, #2563eb)`,
194
+ rotation = DEFAULTS.structure.rotation,
195
+ scene = $bindable(),
196
+ camera = $bindable(),
197
+ orbit_controls = $bindable(),
198
+ rotation_target_ref = $bindable(),
199
+ initial_computed_zoom = $bindable(),
200
+ hidden_elements = $bindable(new SvelteSet()),
201
+ hidden_prop_vals = $bindable(new SvelteSet<number | string>()),
202
+ element_radius_overrides = $bindable<Partial<Record<ElementSymbol, number>>>({}),
203
+ site_radius_overrides = $bindable<SvelteMap<number, number>>(new SvelteMap()),
204
+ atom_color_config = {
205
+ mode: DEFAULTS.structure.atom_color_mode,
206
+ scale: DEFAULTS.structure.atom_color_scale as D3InterpolateName,
207
+ scale_type: DEFAULTS.structure.atom_color_scale_type,
208
+ },
209
+ sym_data = null,
210
+ // Edit-atoms mode callbacks
211
+ on_sites_moved,
212
+ on_operation_start,
213
+ on_bond_edit_start,
214
+ on_add_atom,
215
+ add_atom_mode = $bindable(false),
216
+ add_element = $bindable(`C`),
217
+ cursor = $bindable(`default`),
218
+ dragging_atoms = $bindable(false),
219
+ volumetric_data = undefined,
220
+ isosurface_settings = DEFAULT_ISOSURFACE_SETTINGS,
221
+ }: {
222
+ structure?: AnyStructure
223
+ base_structure?: AnyStructure // The original structure without image atoms, used for property color calculation
224
+ atom_radius?: number // scale factor for atomic radii
225
+ same_size_atoms?: boolean // whether to use the same radius for all atoms. if not, the radius will be
226
+ // determined by the atomic radius of the element
227
+ camera_position?: [x: number, y: number, z: number] // initial camera position from which to render the scene
228
+ camera_target?: Vec3 // external orbit-controls target for pan synchronization
229
+ camera_projection?: CameraProjection // camera projection type
230
+ rotation_damping?: number // rotation damping factor (how quickly the rotation comes to rest after mouse release)
231
+ // zoom level of the camera
232
+ max_zoom?: number
233
+ min_zoom?: number
234
+ rotate_speed?: number // rotation speed. set to 0 to disable rotation.
235
+ zoom_speed?: number // zoom speed. set to 0 to disable zooming.
236
+ pan_speed?: number // pan speed. set to 0 to disable panning.
237
+ zoom_to_cursor?: boolean // zoom toward cursor position instead of scene center
238
+ show_atoms?: boolean
239
+ show_bonds?: ShowBonds
240
+ show_site_labels?: boolean
241
+ show_site_indices?: boolean
242
+ vector_configs?: Record<string, VectorLayerConfig>
243
+ vector_scale?: number
244
+ vector_color?: string
245
+ vector_color_mode?: VectorColorMode
246
+ vector_color_scale?: D3InterpolateName
247
+ vector_normalize?: boolean
248
+ vector_uniform_thickness?: boolean
249
+ vector_origin_gap?: number
250
+ vector_shaft_radius?: number
251
+ vector_arrow_head_radius?: number
252
+ vector_arrow_head_length?: number
253
+ gizmo?: boolean | ComponentProps<typeof extras.Gizmo>
254
+ hovered_idx?: number | null
255
+ hovered_site?: Site | null
256
+ float_fmt?: string
257
+ auto_rotate?: number
258
+ initial_zoom?: number
259
+ bond_thickness?: number
260
+ bond_color?: string
261
+ bonding_strategy?: BondingStrategy
262
+ auto_bond_order?: boolean
263
+ aromatic_display?: `aromatic` | `kekule`
264
+ bonding_options?: Record<string, unknown>
265
+ fov?: number
266
+ ambient_light?: number
267
+ directional_light?: number
268
+ sphere_segments?: number
269
+ lattice_props?: ComponentProps<typeof Lattice>
270
+ atom_label?: Snippet<[{ site: Site; site_idx: number }]>
271
+ site_label_size?: number
272
+ site_label_offset?: Vec3
273
+ site_label_bg_color?: string
274
+ site_label_color?: string
275
+ site_label_padding?: number
276
+ camera_is_moving?: boolean // used to prevent tooltip from showing while camera is moving
277
+ width?: number // Viewer dimensions for responsive zoom
278
+ height?: number
279
+ // measurement props
280
+ measure_mode?: MeasureMode
281
+ selected_sites?: number[]
282
+ measured_sites?: number[]
283
+ added_bonds?: StructureBond[]
284
+ removed_bonds?: StructureBond[]
285
+ bond_order_overrides?: StructureBond[]
286
+ bond_edits_enabled?: boolean
287
+ bond_edit_mode?: BondEditMode
288
+ bond_edit_order?: BondOrder
289
+ selection_highlight_color?: string
290
+ // Support for active highlight group with different color
291
+ active_sites?: number[]
292
+ active_highlight_color?: string
293
+ rotation?: Vec3 // rotation control prop
294
+ // Expose scene and camera for external use (e.g. export pane)
295
+ scene?: Scene
296
+ camera?: Camera
297
+ orbit_controls?: ComponentProps<typeof extras.OrbitControls>[`ref`] // OrbitControls instance
298
+ rotation_target_ref?: Vec3 // Expose rotation target for reset
299
+ initial_computed_zoom?: number // Expose initial zoom for reset
300
+ hidden_elements?: Set<ElementSymbol>
301
+ hidden_prop_vals?: Set<number | string> // Track hidden property values (e.g. Wyckoff positions, coordination numbers)
302
+ element_radius_overrides?: Partial<Record<ElementSymbol, number>> // Per-element absolute radius in Angstroms
303
+ site_radius_overrides?: Map<number, number> | SvelteMap<number, number> // Per-site absolute radius in Angstroms
304
+ atom_color_config?: Partial<AtomColorConfig> // Atom coloring configuration
305
+ sym_data?: MoyoDataset | null // Symmetry data for Wyckoff coloring
306
+ // Edit-atoms mode callbacks and state
307
+ on_sites_moved?: (scene_indices: number[], delta: Vec3) => void
308
+ on_operation_start?: () => void
309
+ on_bond_edit_start?: () => void
310
+ on_add_atom?: (xyz: Vec3, element: ElementSymbol) => void
311
+ add_atom_mode?: boolean // whether user is in click-to-place add-atom sub-mode
312
+ add_element?: ElementSymbol // element to add when clicking in add-atom mode
313
+ cursor?: string // cursor style for the 3D canvas
314
+ dragging_atoms?: boolean // true while TransformControls drag is active (skips expensive recalculations)
315
+ volumetric_data?: VolumetricData // Active volumetric data for isosurface rendering
316
+ isosurface_settings?: IsosurfaceSettings // Isosurface rendering settings
317
+ } = $props()
318
+
319
+ const pulse = create_pulse_animation(
320
+ () => selected_sites.length > 0 || active_sites.length > 0,
321
+ { step: 0.015, frequency: 5 },
322
+ )
323
+ let pulse_opacity = $derived(0.15 + 0.25 * pulse.unit)
324
+
325
+ const threlte = useThrelte()
326
+ $effect(() => {
327
+ scene = threlte.scene
328
+ camera = threlte.camera.current
329
+ if (threlte.renderer) {
330
+ Object.assign(threlte.renderer.domElement, { __renderer: threlte.renderer })
331
+ }
332
+ })
333
+
334
+ // Expose rotation target for external reset
335
+ $effect(() => {
336
+ rotation_target_ref = rotation_target
337
+ })
338
+
339
+ // Track initial computed zoom for reset
340
+ let stored_initial_zoom = $state<number | undefined>(undefined)
341
+ $effect(() => {
342
+ if (stored_initial_zoom === undefined && computed_zoom > 0) {
343
+ stored_initial_zoom = computed_zoom
344
+ }
345
+ initial_computed_zoom = stored_initial_zoom
346
+ })
347
+
348
+ let bond_pairs: BondPair[] = $state([])
349
+ let atom_tooltip_active = $state(false)
350
+ let hovered_bond_key = $state<string | null>(null)
351
+ const ATOM_HOVER_CLEAR_DELAY_MS = 200
352
+ let clear_atom_hover_timeout: ReturnType<typeof setTimeout> | null = null
353
+
354
+ function cancel_atom_hover_clear(): void {
355
+ if (clear_atom_hover_timeout == null) return
356
+ clearTimeout(clear_atom_hover_timeout)
357
+ clear_atom_hover_timeout = null
358
+ }
359
+
360
+ function set_atom_hover(site_idx: number): void {
361
+ cancel_atom_hover_clear()
362
+ if (hovered_idx !== site_idx) hovered_idx = site_idx
363
+ if (!atom_tooltip_active) atom_tooltip_active = true
364
+ }
365
+
366
+ function schedule_atom_hover_clear(site_idx: number): void {
367
+ cancel_atom_hover_clear()
368
+ clear_atom_hover_timeout = setTimeout(() => {
369
+ clear_atom_hover_timeout = null
370
+ if (hovered_idx !== site_idx) return
371
+ hovered_idx = null
372
+ atom_tooltip_active = false
373
+ }, ATOM_HOVER_CLEAR_DELAY_MS)
374
+ }
375
+
376
+ const atom_hover_props = (site_idx: number | null, disabled = false) => ({
377
+ onpointerenter: () => {
378
+ if (!disabled && site_idx != null) set_atom_hover(site_idx)
379
+ },
380
+ onpointermove: () => {
381
+ if (!disabled && site_idx != null) set_atom_hover(site_idx)
382
+ },
383
+ onpointerleave: () => {
384
+ if (!disabled && site_idx != null) schedule_atom_hover_clear(site_idx)
385
+ },
386
+ })
387
+
388
+ // Cursor style for the canvas, derived from mode and hover state
389
+ let canvas_cursor = $derived.by(() => {
390
+ if (measure_mode === `edit-atoms` && add_atom_mode) return `crosshair`
391
+ if (measure_mode === `edit-bonds` && hovered_bond_key != null) {
392
+ return bond_edits_enabled ? `pointer` : `not-allowed`
393
+ }
394
+ if (hovered_idx != null) {
395
+ if (measure_mode === `edit-bonds`) {
396
+ return bond_edit_mode === `add` && can_select_bond_site(hovered_idx)
397
+ ? `pointer`
398
+ : `not-allowed`
399
+ }
400
+ if (measure_mode === `edit-atoms`) {
401
+ const site = structure?.sites?.[hovered_idx]
402
+ if (site?.properties?.orig_site_idx != null) return `not-allowed`
403
+ return `pointer`
404
+ }
405
+ return `pointer`
406
+ }
407
+ return `default`
408
+ })
409
+
410
+ // Desaturate a color by blending it toward gray (for ghosting image atoms in edit mode)
411
+ const gray = new Color(0x999999)
412
+ const desaturate = (hex: string | undefined, amount = 0.4): string =>
413
+ `#${new Color(hex ?? 0x999999).lerp(gray, amount).getHexString()}`
414
+
415
+ // === Edit-atoms mode state ===
416
+ let transform_object = $state<Mesh | undefined>(undefined)
417
+ // Plain variable — only used imperatively in TransformControls drag handlers
418
+ let drag_start_centroid: Vec3 | null = null
419
+ // Frozen centroid set on drag start. While non-null, the TransformControls mesh
420
+ // position stays at this fixed value so Svelte's reactive centroid updates (from
421
+ // PBC wrapping) don't fight TransformControls. Cleared on mouseUp so the mesh
422
+ // snaps to the new wrapped centroid.
423
+ let frozen_centroid = $state<Vec3 | null>(null)
424
+
425
+ let bond_context_menu = $state<BondContextMenu | null>(null)
426
+ // Threlte/HTML pointer events can close the visible menu before a button
427
+ // handler runs, so keep the target bond separately for menu actions.
428
+ let bond_context_target: BondContextMenu | null = null
429
+
430
+ function close_bond_context_menu() {
431
+ bond_context_menu = null
432
+ bond_context_target = null
433
+ }
434
+
435
+ const canonical_bond_target = (bond: BondKeyTarget): BondKeyTarget =>
436
+ canonicalize_bond_target(bond, structure?.sites)
437
+
438
+ const bond_key_for = (bond: BondKeyTarget): string => {
439
+ const target = canonical_bond_target(bond)
440
+ return get_bond_key(target.site_idx_1, target.site_idx_2, target.cell_shift)
441
+ }
442
+ const rendered_bond_key_for = (bond: BondKeyTarget): string =>
443
+ get_bond_key(bond.site_idx_1, bond.site_idx_2, bond.cell_shift)
444
+
445
+ const matches_bond_key = (bond: BondKeyTarget, key: string): boolean =>
446
+ bond_key_for(bond) === key
447
+
448
+ const find_added_bond_by_rendered_key = (key: string): StructureBond | undefined =>
449
+ added_bonds.find((bond) => rendered_bond_key_for(bond) === key)
450
+
451
+ function resolve_bond_edit_target(
452
+ site_idx_1: number,
453
+ site_idx_2: number,
454
+ cell_shift?: Vec3,
455
+ ): BondKeyTarget {
456
+ const rendered_target = { site_idx_1, site_idx_2, cell_shift }
457
+ const rendered_key = rendered_bond_key_for(rendered_target)
458
+ return find_added_bond_by_rendered_key(rendered_key) ??
459
+ canonical_bond_target(rendered_target)
460
+ }
461
+
462
+ const is_image_bond_site = (site_idx: number): boolean =>
463
+ structure?.sites?.[site_idx]?.properties?.orig_site_idx != null
464
+
465
+ const can_select_bond_site = (site_idx: number): boolean =>
466
+ bond_edits_enabled && structure?.sites?.[site_idx] != null
467
+
468
+ const can_edit_bond = (bond: BondKeyTarget): boolean => {
469
+ const target = canonical_bond_target(bond)
470
+ return bond_edits_enabled &&
471
+ !is_image_bond_site(target.site_idx_1) &&
472
+ !is_image_bond_site(target.site_idx_2)
473
+ }
474
+
475
+ const format_bond_order = (order: BondOrder | undefined): string =>
476
+ order === undefined ? `1` : `${order}`
477
+
478
+ function get_current_bond_order(
479
+ site_idx_1: number,
480
+ site_idx_2: number,
481
+ cell_shift?: Vec3,
482
+ ): BondOrder | undefined {
483
+ const key = get_bond_key(site_idx_1, site_idx_2, cell_shift)
484
+ return find_added_bond_by_rendered_key(key)?.order ??
485
+ bond_order_overrides.find((bond) => matches_bond_key(bond, key))?.order ??
486
+ added_bonds.find((bond) => matches_bond_key(bond, key))?.order ??
487
+ filtered_bond_pairs.find((bond) => matches_bond_key(bond, key))?.bond_order
488
+ }
489
+
490
+ const midpoint = (pos_1: Vec3, pos_2: Vec3): Vec3 => [
491
+ (pos_1[0] + pos_2[0]) / 2,
492
+ (pos_1[1] + pos_2[1]) / 2,
493
+ (pos_1[2] + pos_2[2]) / 2,
494
+ ]
495
+
496
+ const BOND_ENDPOINT_HIT_FRACTION = 0.3
497
+ const BOND_ENDPOINT_SITE_MATCH_TOLERANCE = 1e-6
498
+ const EDITABLE_ATOM_HIT_RADIUS_SCALE = 1.15
499
+ const skip_raycast = (): void => undefined
500
+
501
+ function apply_bond_transform(mesh: Mesh, bond: BondPair): void {
502
+ mesh.matrix.fromArray(bond.transform_matrix)
503
+ mesh.matrixWorldNeedsUpdate = true
504
+ }
505
+
506
+ function apply_non_raycastable_bond_hit_transform(mesh: Mesh, bond: BondPair): void {
507
+ apply_bond_transform(mesh, bond)
508
+ disable_raycast(mesh)
509
+ }
510
+
511
+ function disable_raycast(mesh: Mesh): void {
512
+ mesh.raycast = skip_raycast
513
+ }
514
+
515
+ function site_world_position(parent: Object3D, site: Site): Vector3 {
516
+ const position = new Vector3(...site.xyz)
517
+ return parent.localToWorld(position)
518
+ }
519
+
520
+ function get_bond_endpoint_site_idx(
521
+ site_idx: number,
522
+ world_position: Vector3,
523
+ parent: Object3D,
524
+ ): number {
525
+ if (!structure?.sites) return site_idx
526
+ const site = structure.sites[site_idx]
527
+ if (!site) return site_idx
528
+
529
+ const matches_world_position = (candidate_site: Site): boolean =>
530
+ site_world_position(parent, candidate_site).distanceTo(world_position) <=
531
+ BOND_ENDPOINT_SITE_MATCH_TOLERANCE
532
+
533
+ if (matches_world_position(site)) {
534
+ return site_idx
535
+ }
536
+
537
+ const image_site_idx = structure.sites.findIndex((candidate_site) =>
538
+ candidate_site.properties?.orig_site_idx === site_idx &&
539
+ matches_world_position(candidate_site)
540
+ )
541
+ return image_site_idx === -1 ? site_idx : image_site_idx
542
+ }
543
+
544
+ function get_bond_endpoint_hit_site_idx(
545
+ bond: BondPair,
546
+ event: BondPointerEvent,
547
+ ): number | null {
548
+ if (!event.point) return null
549
+ const parent = event.object?.parent
550
+ if (!parent) return null
551
+
552
+ const world_pos_1 = new Vector3(...bond.pos_1)
553
+ const world_pos_2 = new Vector3(...bond.pos_2)
554
+ parent.localToWorld(world_pos_1)
555
+ parent.localToWorld(world_pos_2)
556
+
557
+ const bond_vec = world_pos_2.clone().sub(world_pos_1)
558
+ const length_sq = bond_vec.lengthSq()
559
+ if (length_sq <= math.EPS) return null
560
+
561
+ const hit_vec = event.point.clone().sub(world_pos_1)
562
+ const bond_fraction = hit_vec.dot(bond_vec) / length_sq
563
+ if (bond_fraction <= BOND_ENDPOINT_HIT_FRACTION) {
564
+ return get_bond_endpoint_site_idx(bond.site_idx_1, world_pos_1, parent)
565
+ }
566
+ if (bond_fraction >= 1 - BOND_ENDPOINT_HIT_FRACTION) {
567
+ return get_bond_endpoint_site_idx(bond.site_idx_2, world_pos_2, parent)
568
+ }
569
+ return null
570
+ }
571
+
572
+ let label_screen_margin = $derived(site_label_size * 10 + site_label_padding)
573
+
574
+ function get_bond_context_menu_position(
575
+ bond: BondPair,
576
+ event?: BondContextMenuEvent,
577
+ ): Vec3 {
578
+ const parent = event?.object?.parent
579
+ if (!event?.point || !parent) return midpoint(bond.pos_1, bond.pos_2)
580
+
581
+ const local_point = event.point.clone()
582
+ parent.worldToLocal(local_point)
583
+ return [local_point.x, local_point.y, local_point.z]
584
+ }
585
+
586
+ function open_bond_context_menu(bond: BondPair, event?: BondContextMenuEvent) {
587
+ if (!can_edit_bond(bond)) return
588
+ bond_context_target = {
589
+ site_idx_1: bond.site_idx_1,
590
+ site_idx_2: bond.site_idx_2,
591
+ cell_shift: bond.cell_shift,
592
+ position: get_bond_context_menu_position(bond, event),
593
+ }
594
+ bond_context_menu = bond_context_target
595
+ }
596
+
597
+ const current_bond_edit_state = () => ({
598
+ added_bonds,
599
+ removed_bonds,
600
+ bond_order_overrides,
601
+ })
602
+
603
+ function apply_bond_edit_result(result: BondEditResult, close_menu = true) {
604
+ if (!result.changed) return
605
+ on_bond_edit_start?.()
606
+ added_bonds = result.state.added_bonds
607
+ removed_bonds = result.state.removed_bonds
608
+ bond_order_overrides = result.state.bond_order_overrides
609
+ if (close_menu) close_bond_context_menu()
610
+ }
611
+
612
+ const find_visible_bond = (
613
+ target: BondKeyTarget,
614
+ canonical_target: BondKeyTarget = target,
615
+ ): BondPair | undefined => {
616
+ const rendered_key = rendered_bond_key_for(target)
617
+ const canonical_key = bond_key_for(canonical_target)
618
+ return filtered_bond_pairs.find((bond) => rendered_bond_key_for(bond) === rendered_key) ??
619
+ filtered_bond_pairs.find((bond) => bond_key_for(bond) === canonical_key)
620
+ }
621
+
622
+ function open_bond_order_menu_for_target(
623
+ target: BondKeyTarget,
624
+ canonical_target: BondKeyTarget = target,
625
+ ) {
626
+ const bond = find_visible_bond(target, canonical_target)
627
+ if (bond) open_bond_context_menu(bond)
628
+ }
629
+
630
+ function add_or_restore_pair(site_idx_1: number, site_idx_2: number) {
631
+ const rendered_target = { site_idx_1, site_idx_2 }
632
+ if (!can_edit_bond(rendered_target)) return
633
+ const edit_state = current_bond_edit_state()
634
+ const canonical_target = canonical_bond_target(rendered_target)
635
+ const canonical_result = add_or_restore_bond(
636
+ edit_state,
637
+ canonical_target,
638
+ editable_perceived_bond_pairs,
639
+ bond_edit_order,
640
+ )
641
+ const use_rendered_target =
642
+ canonical_result.action === `added` &&
643
+ rendered_bond_key_for(canonical_target) !== rendered_bond_key_for(rendered_target)
644
+ const target = use_rendered_target ? rendered_target : canonical_target
645
+ const result = use_rendered_target
646
+ ? add_or_restore_bond(
647
+ edit_state,
648
+ rendered_target,
649
+ editable_perceived_bond_pairs,
650
+ bond_edit_order,
651
+ )
652
+ : canonical_result
653
+ if (result.action === `already-visible`) {
654
+ open_bond_order_menu_for_target(rendered_target, target)
655
+ return
656
+ }
657
+ apply_bond_edit_result(result, false)
658
+ }
659
+
660
+ function set_bond_order(
661
+ site_idx_1: number,
662
+ site_idx_2: number,
663
+ order: BondOrder,
664
+ cell_shift?: Vec3,
665
+ ) {
666
+ const target = resolve_bond_edit_target(site_idx_1, site_idx_2, cell_shift)
667
+ if (!can_edit_bond(target)) return
668
+ apply_bond_edit_result(
669
+ apply_set_bond_order(
670
+ current_bond_edit_state(),
671
+ target,
672
+ editable_perceived_bond_pairs,
673
+ order,
674
+ ),
675
+ )
676
+ }
677
+
678
+ function set_context_bond_order(order: BondOrder) {
679
+ const menu = bond_context_target ?? bond_context_menu
680
+ if (!menu) return
681
+ set_bond_order(menu.site_idx_1, menu.site_idx_2, order, menu.cell_shift)
682
+ }
683
+
684
+ function remove_bond(site_idx_1: number, site_idx_2: number, cell_shift?: Vec3) {
685
+ const target = resolve_bond_edit_target(site_idx_1, site_idx_2, cell_shift)
686
+ if (!can_edit_bond(target)) return
687
+ apply_bond_edit_result(
688
+ apply_delete_bond(
689
+ current_bond_edit_state(),
690
+ target,
691
+ editable_perceived_bond_pairs,
692
+ ),
693
+ )
694
+ }
695
+
696
+ function remove_context_bond() {
697
+ const menu = bond_context_target ?? bond_context_menu
698
+ if (!menu) return
699
+ remove_bond(menu.site_idx_1, menu.site_idx_2, menu.cell_shift)
700
+ }
701
+
702
+ // Deduplicate clicks: when a highlight sphere and the underlying atom both
703
+ // intercept the same native click, only the first intersection should fire.
704
+ // All threlte intersection events from one click share the same nativeEvent ref.
705
+ let last_native_event: Event | null = null
706
+ // extras.Instance does not always emit pointerdown, so edit-bonds also falls
707
+ // back to click. When pointerdown did fire, skip the matching click once.
708
+ let last_edit_bonds_pointerdown_site_idx: number | null = null
709
+ let clear_edit_bonds_pointerdown_site_timeout:
710
+ | ReturnType<typeof setTimeout>
711
+ | null = null
712
+
713
+ function remember_edit_bonds_pointerdown_site(site_idx: number) {
714
+ last_edit_bonds_pointerdown_site_idx = site_idx
715
+ if (clear_edit_bonds_pointerdown_site_timeout != null) {
716
+ clearTimeout(clear_edit_bonds_pointerdown_site_timeout)
717
+ }
718
+ clear_edit_bonds_pointerdown_site_timeout = setTimeout(() => {
719
+ last_edit_bonds_pointerdown_site_idx = null
720
+ clear_edit_bonds_pointerdown_site_timeout = null
721
+ }, 250)
722
+ }
723
+
724
+ function select_edit_bonds_site(site_idx: number, event: Event): void {
725
+ toggle_selection(site_idx, event)
726
+ remember_edit_bonds_pointerdown_site(site_idx)
727
+ }
728
+
729
+ function skip_duplicate_edit_bonds_click(site_idx: number) {
730
+ if (last_edit_bonds_pointerdown_site_idx !== site_idx) return false
731
+
732
+ last_edit_bonds_pointerdown_site_idx = null
733
+ if (clear_edit_bonds_pointerdown_site_timeout != null) {
734
+ clearTimeout(clear_edit_bonds_pointerdown_site_timeout)
735
+ clear_edit_bonds_pointerdown_site_timeout = null
736
+ }
737
+ return true
738
+ }
739
+
740
+ function toggle_selection(site_index: number, evt?: Event) {
741
+ evt?.stopPropagation?.()
742
+ const event_with_native = evt as Event & { nativeEvent?: unknown } | undefined
743
+ const native_event = event_with_native?.nativeEvent ?? evt
744
+ if (native_event instanceof Event) {
745
+ if (native_event === last_native_event) return
746
+ last_native_event = native_event
747
+ }
748
+
749
+ if (measure_mode === `edit-bonds`) {
750
+ if (bond_edit_mode === `delete`) {
751
+ measured_sites = []
752
+ selected_sites = []
753
+ return
754
+ }
755
+ if (!can_select_bond_site(site_index)) return
756
+ // In Add mode, select atom pairs without making existing bonds destructive.
757
+ const new_sites = measured_sites.includes(site_index)
758
+ ? measured_sites.filter((idx) => idx !== site_index)
759
+ : [...measured_sites, site_index]
760
+
761
+ measured_sites = new_sites
762
+ selected_sites = new_sites
763
+
764
+ // When two atoms are selected, add/restore or open order editing.
765
+ if (new_sites.length === 2) {
766
+ add_or_restore_pair(new_sites[0], new_sites[1])
767
+ measured_sites = []
768
+ selected_sites = []
769
+ }
770
+ return
771
+ }
772
+
773
+ if (measure_mode === `edit-atoms`) {
774
+ // Block image atoms (detected by orig_site_idx property from PBC)
775
+ const site = structure?.sites?.[site_index]
776
+ if (site?.properties?.orig_site_idx != null) return
777
+
778
+ const is_selected = selected_sites.includes(site_index)
779
+ const is_shift = evt instanceof MouseEvent && evt.shiftKey
780
+
781
+ // In edit-atoms mode, selected_sites and measured_sites always stay in sync
782
+ let new_sites: number[]
783
+ if (is_shift) {
784
+ // Multi-select: toggle this site in/out of selection
785
+ new_sites = is_selected
786
+ ? selected_sites.filter((idx) => idx !== site_index)
787
+ : [...selected_sites, site_index]
788
+ } else {
789
+ // Single-select: replace selection (or deselect if already selected)
790
+ new_sites = is_selected ? [] : [site_index]
791
+ }
792
+ selected_sites = new_sites
793
+ measured_sites = new_sites
794
+ return
795
+ }
796
+
797
+ if (
798
+ !measured_sites.includes(site_index) &&
799
+ measured_sites.length >= measure.MAX_SELECTED_SITES
800
+ ) {
801
+ console.warn(
802
+ `Selection size limit reached (${measure.MAX_SELECTED_SITES}). Deselect some sites first.`,
803
+ )
804
+ return
805
+ }
806
+
807
+ measured_sites = measured_sites.includes(site_index)
808
+ ? measured_sites.filter((idx) => idx !== site_index)
809
+ : [...measured_sites, site_index]
810
+ selected_sites = selected_sites.includes(site_index)
811
+ ? selected_sites.filter((idx) => idx !== site_index)
812
+ : [...selected_sites, site_index]
813
+ }
814
+
815
+ $effect(() => {
816
+ void structure
817
+ void measure_mode
818
+ void bond_edit_mode
819
+ void bond_edits_enabled
820
+ untrack(() => {
821
+ close_bond_context_menu()
822
+ hovered_bond_key = null
823
+ })
824
+ })
825
+
826
+ $effect(() => {
827
+ const count = structure?.sites?.length ?? 0
828
+ if (count <= 0) {
829
+ measured_sites = []
830
+ return
831
+ }
832
+ untrack(() => {
833
+ measured_sites = measured_sites.filter((idx) => idx >= 0 && idx < count)
834
+ })
835
+ })
836
+
837
+ $effect(() => {
838
+ cursor = canvas_cursor
839
+ })
840
+
841
+ extras.interactivity()
842
+ $effect.pre(() => {
843
+ hovered_site = structure?.sites?.[hovered_idx ?? -1] ?? null
844
+ })
845
+ let lattice = $derived(
846
+ structure && `lattice` in structure ? structure.lattice : null,
847
+ )
848
+
849
+ let visual_lattice = $derived(
850
+ base_structure && `lattice` in base_structure ? base_structure.lattice : lattice,
851
+ )
852
+
853
+ let rotation_target = $derived(
854
+ lattice
855
+ ? (math.scale(math.add(...lattice.matrix), 0.5) as Vec3)
856
+ : structure
857
+ ? get_center_of_mass(structure)
858
+ : [0, 0, 0] as Vec3,
859
+ )
860
+
861
+ let structure_size = $derived.by(() => {
862
+ if (lattice) return (lattice.a + lattice.b + lattice.c) / 2
863
+ if (!structure?.sites?.length) return 10
864
+
865
+ const ranges = [0, 1, 2].map((axis_idx) => {
866
+ const coords = structure.sites.map((site) => site.xyz[axis_idx])
867
+ return Math.max(...coords) - Math.min(...coords)
868
+ })
869
+ return Math.max(1, ...ranges)
870
+ })
871
+
872
+ // Characteristic inter-atomic spacing: cube root of volume per atom.
873
+ // Excludes PBC image atoms (orig_site_idx) so toggling image atoms doesn't affect arrow sizing.
874
+ let char_atom_spacing = $derived.by(() => {
875
+ if (!lattice || !structure?.sites?.length) return structure_size
876
+ const n_real = structure.sites.filter((site) =>
877
+ site.properties?.orig_site_idx == null
878
+ ).length
879
+ return n_real > 0 ? Math.cbrt(lattice.volume / n_real) : structure_size
880
+ })
881
+
882
+ // When uniform thickness is on, convert negative (length-relative) radii to
883
+ // positive (absolute) values scaled by inter-atomic spacing.
884
+ // Already-positive (absolute) values are preserved as-is.
885
+ let eff_shaft_radius = $derived(
886
+ vector_uniform_thickness && vector_shaft_radius < 0
887
+ ? char_atom_spacing * -vector_shaft_radius
888
+ : vector_shaft_radius,
889
+ )
890
+ let eff_head_radius = $derived(
891
+ vector_uniform_thickness && vector_arrow_head_radius < 0
892
+ ? char_atom_spacing * -vector_arrow_head_radius
893
+ : vector_arrow_head_radius,
894
+ )
895
+ let eff_head_length = $derived(
896
+ vector_uniform_thickness && vector_arrow_head_length < 0
897
+ ? char_atom_spacing * -vector_arrow_head_length
898
+ : vector_arrow_head_length,
899
+ )
900
+
901
+ // Compute dynamic camera clipping planes based on structure size
902
+ // This prevents z-fighting and disappearing objects when zooming in close on large supercells
903
+ let camera_near = $derived(Math.max(0.01, structure_size * 0.01))
904
+ let camera_far = $derived(Math.max(1000, structure_size * 100))
905
+
906
+ // Using $state because this is mutated in an effect based on viewport/structure size
907
+ let computed_zoom = $state(untrack(() => initial_zoom))
908
+ $effect(() => {
909
+ if (!(width > 0) || !(height > 0)) return
910
+ const structure_max_dim = Math.max(1, untrack(() => structure_size))
911
+ const viewer_min_dim = Math.min(width, height)
912
+ const scale_factor = viewer_min_dim / (structure_max_dim * 50) // 50px per unit
913
+ let new_zoom = initial_zoom * scale_factor
914
+ if (min_zoom && min_zoom > 0) new_zoom = Math.max(min_zoom, new_zoom)
915
+ if (max_zoom && max_zoom > 0) new_zoom = Math.min(max_zoom, new_zoom)
916
+ computed_zoom = new_zoom
917
+ })
918
+
919
+ $effect.pre(() => { // Simple initial camera auto-position: proportional to structure size and fov
920
+ if (camera_position.every((val) => val === 0) && structure) {
921
+ stored_initial_zoom = undefined
922
+ const distance = Math.max(1, structure_size) * (60 / fov)
923
+ camera_position = [distance, distance * 0.3, distance * 0.8]
924
+ }
925
+ })
926
+ $effect(() => {
927
+ if (structure && show_bonds !== `never`) {
928
+ // Determine if we should show bonds based on the setting and structure type
929
+ const should_show_bonds = show_bonds === `always` ||
930
+ (show_bonds === `crystals` && lattice) ||
931
+ (show_bonds === `molecules` && !lattice)
932
+
933
+ if (should_show_bonds) {
934
+ bond_pairs = BONDING_STRATEGIES[bonding_strategy](structure, bonding_options)
935
+ } else bond_pairs = []
936
+ } else bond_pairs = []
937
+ })
938
+
939
+ // Compute property-based colors when not using element coloring
940
+ // Use base_structure (original unit cell) for color calculation
941
+ let property_colors = $derived(
942
+ get_property_colors(
943
+ base_structure || structure,
944
+ atom_color_config,
945
+ bonding_strategy,
946
+ sym_data,
947
+ ),
948
+ )
949
+ // Compute weighted average radius for a site based on species occupancies
950
+ // Normalizes by total occupancy so vacancy-containing sites render at full size
951
+ const calc_weighted_radius = (site: Site): number => {
952
+ const total_occu = site.species.reduce((sum, { occu }) => sum + occu, 0)
953
+ const weighted_sum = site.species.reduce((sum, { element, occu }) => {
954
+ const override = element_radius_overrides?.[element as ElementSymbol]
955
+ return sum + occu * (override ?? atomic_radii[element] ?? 1)
956
+ }, 0)
957
+ return total_occu > 0 ? weighted_sum / total_occu : 1
958
+ }
959
+
960
+ let atom_data = $derived.by(() => {
961
+ if (!show_atoms || !structure?.sites) return []
962
+ const render_sites = merge_split_partial_sites(structure.sites, hidden_elements)
963
+ return render_sites.flatMap(({ site_idx, site, is_image_atom }) => {
964
+ const orig_idx = get_orig_site_idx(site, site_idx)
965
+
966
+ // Skip sites with hidden property values
967
+ const prop_val = property_colors?.values[orig_idx]
968
+ if (prop_val !== undefined && hidden_prop_vals.has(prop_val)) return []
969
+
970
+ // Calculate radius: same_size > site override > element override > default
971
+ // All radii scale uniformly with atom_radius for consistent slider behavior
972
+ const base_radius = same_size_atoms
973
+ ? 1
974
+ : site_radius_overrides?.get(site_idx) ?? calc_weighted_radius(site)
975
+ const radius = base_radius * atom_radius
976
+
977
+ // Use property color if available (e.g. coordination number, Wyckoff position)
978
+ // Otherwise, each species gets its own element color (important for disordered sites)
979
+ const site_property_color = property_colors?.colors[orig_idx]
980
+
981
+ const visible_species = site.species.filter(({ element }) =>
982
+ !hidden_elements.has(element)
983
+ )
984
+ const slice_geometry = compute_slice_geometry(visible_species)
985
+ return slice_geometry.map((slice_data) => {
986
+ return {
987
+ site_idx,
988
+ element: slice_data.element,
989
+ occupancy: slice_data.occupancy,
990
+ position: site.xyz,
991
+ radius,
992
+ color: site_property_color ?? colors.element?.[slice_data.element],
993
+ has_partial_occupancy: slice_data.occupancy < 1,
994
+ start_phi: slice_data.start_phi,
995
+ end_phi: slice_data.end_phi,
996
+ phi_length: slice_data.phi_length,
997
+ render_start_cap: slice_data.render_start_cap,
998
+ render_end_cap: slice_data.render_end_cap,
999
+ is_image_atom,
1000
+ }
1001
+ })
1002
+ })
1003
+ })
1004
+
1005
+ // Shared visibility check: site has at least one non-hidden element and
1006
+ // its property value (if any) isn't hidden. Used by both bond and vector filtering.
1007
+ const is_site_visible = (site_idx: number): boolean => {
1008
+ if (!structure?.sites) return false
1009
+ const site = structure.sites[site_idx]
1010
+ const has_visible_element = site?.species.some(({ element }) =>
1011
+ !hidden_elements.has(element)
1012
+ )
1013
+ const orig_idx = get_orig_site_idx(site, site_idx)
1014
+ const prop_val = property_colors?.values[orig_idx]
1015
+ const prop_visible = prop_val === undefined ||
1016
+ !hidden_prop_vals.has(prop_val)
1017
+ return has_visible_element && prop_visible
1018
+ }
1019
+
1020
+ // Perception layer: bond_pairs with optional bond-order perception applied.
1021
+ // Off by default (pass-through). Manual overrides are applied downstream in
1022
+ // filtered_bond_pairs, so they still win over perceived orders.
1023
+ let perceived_bond_pairs: BondPair[] = $derived.by(() => {
1024
+ if (!auto_bond_order || !structure?.sites || bond_pairs.length === 0) {
1025
+ return bond_pairs
1026
+ }
1027
+ const total_charge = (`charge` in structure ? structure.charge : 0) ?? 0
1028
+ const perceived = perceive_bond_orders(structure.sites, bond_pairs, {
1029
+ total_charge,
1030
+ })
1031
+ // Explicit structure.properties.bonds are user-authoritative and must
1032
+ // never be clobbered by perception. Composition + precedence is a pure,
1033
+ // unit-tested helper (see compose_perceived_bonds).
1034
+ return compose_perceived_bonds(
1035
+ perceived,
1036
+ get_explicit_bond_metadata(structure),
1037
+ aromatic_display,
1038
+ )
1039
+ })
1040
+
1041
+ let editable_perceived_bond_pairs = $derived(
1042
+ perceived_bond_pairs.map((bond) => ({ ...bond, ...canonical_bond_target(bond) })),
1043
+ )
1044
+
1045
+ let filtered_bond_pairs = $derived.by(() => {
1046
+ if (!structure?.sites) return perceived_bond_pairs
1047
+
1048
+ // Build set of removed bond keys for efficient lookup
1049
+ const removed_keys = new Set(
1050
+ removed_bonds.map(bond_key_for),
1051
+ )
1052
+ const added_keys = new Set(
1053
+ added_bonds.map(bond_key_for),
1054
+ )
1055
+ const order_overrides = new Map(
1056
+ bond_order_overrides.map((bond) => [bond_key_for(bond), bond.order]),
1057
+ )
1058
+
1059
+ // Filter calculated bonds: exclude removed, replaced by manual additions, and hidden.
1060
+ const calculated = perceived_bond_pairs
1061
+ .filter((bond) => {
1062
+ const key = bond_key_for(bond)
1063
+ if (removed_keys.has(key) || added_keys.has(key)) return false
1064
+ return is_site_visible(bond.site_idx_1) && is_site_visible(bond.site_idx_2)
1065
+ })
1066
+ .map((bond) => {
1067
+ const override = order_overrides.get(bond_key_for(bond))
1068
+ return override === undefined ? bond : { ...bond, bond_order: override }
1069
+ })
1070
+
1071
+ // Create BondPair objects for manually added bonds
1072
+ const added: BondPair[] = []
1073
+ for (const added_bond of added_bonds) {
1074
+ const { site_idx_1: idx_i, site_idx_2: idx_j } = added_bond
1075
+ if (!is_site_visible(idx_i) || !is_site_visible(idx_j)) continue
1076
+ added.push(structure_bond_to_bond_pair(structure, added_bond))
1077
+ }
1078
+
1079
+ return [...calculated, ...added]
1080
+ })
1081
+
1082
+ let editable_bond_pairs = $derived(
1083
+ bond_edits_enabled ? filtered_bond_pairs.filter(can_edit_bond) : [],
1084
+ )
1085
+
1086
+ let smart_site_label_offsets = $derived.by(() => {
1087
+ const offsets = new SvelteMap<number, Vec3>()
1088
+ if (filtered_bond_pairs.length === 0) return offsets
1089
+
1090
+ const bond_directions_by_site = new SvelteMap<number, Vec3[]>()
1091
+ const add_bond_direction = (site_idx: number, pos_1: Vec3, pos_2: Vec3) => {
1092
+ const direction = math.normalize_vec(
1093
+ math.subtract(pos_2, pos_1),
1094
+ [0, 0, 0],
1095
+ )
1096
+ if (Math.hypot(...direction) < LABEL_OFFSET_EPS) return
1097
+ bond_directions_by_site.set(site_idx, [
1098
+ ...(bond_directions_by_site.get(site_idx) ?? []),
1099
+ direction,
1100
+ ])
1101
+ }
1102
+
1103
+ for (const { site_idx_1, site_idx_2, pos_1, pos_2 } of filtered_bond_pairs) {
1104
+ add_bond_direction(site_idx_1, pos_1, pos_2)
1105
+ add_bond_direction(site_idx_2, pos_2, pos_1)
1106
+ }
1107
+ for (const [site_idx, bond_directions] of bond_directions_by_site) {
1108
+ offsets.set(site_idx, choose_site_label_offset(bond_directions, site_label_offset))
1109
+ }
1110
+ return offsets
1111
+ })
1112
+
1113
+ let instanced_bond_groups = $derived.by(() => {
1114
+ if (!structure?.sites || filtered_bond_pairs.length === 0) return []
1115
+
1116
+ const group = {
1117
+ thickness: bond_thickness,
1118
+ ambient_light,
1119
+ directional_light,
1120
+ instances: [] as {
1121
+ matrix: Float32Array
1122
+ color_start: string
1123
+ color_end: string
1124
+ }[],
1125
+ }
1126
+
1127
+ for (const bond_data of filtered_bond_pairs) {
1128
+ const site_a = structure.sites[bond_data.site_idx_1]
1129
+ const site_b = structure.sites[bond_data.site_idx_2]
1130
+
1131
+ const get_majority_color = (site: typeof site_a) => {
1132
+ if (!site?.species || site.species.length === 0) return bond_color
1133
+ const majority_species = site.species.reduce((max, spec) =>
1134
+ spec.occu > max.occu ? spec : max
1135
+ )
1136
+ return colors.element?.[majority_species.element] || bond_color
1137
+ }
1138
+
1139
+ const color_start = get_majority_color(site_a)
1140
+ const color_end = get_majority_color(site_b)
1141
+ for (const matrix of get_bond_render_matrices(bond_data, bond_thickness)) {
1142
+ group.instances.push({ matrix, color_start, color_end })
1143
+ }
1144
+ }
1145
+
1146
+ return group.instances.length > 0 ? [group] : []
1147
+ })
1148
+
1149
+ let radius_by_site_idx = $derived.by(() => {
1150
+ const map = new SvelteMap<number, number>()
1151
+ for (const atom of atom_data) {
1152
+ if (!map.has(atom.site_idx)) map.set(atom.site_idx, atom.radius)
1153
+ }
1154
+ return map
1155
+ })
1156
+
1157
+ let editable_atom_hit_targets = $derived.by(() => {
1158
+ if (
1159
+ measure_mode !== `edit-bonds` ||
1160
+ bond_edit_mode !== `add` ||
1161
+ !bond_edits_enabled
1162
+ ) {
1163
+ return []
1164
+ }
1165
+
1166
+ const targets = new SvelteMap<number, EditableAtomHitTarget>()
1167
+ for (const atom of atom_data) {
1168
+ if (!can_select_bond_site(atom.site_idx)) continue
1169
+ if (targets.has(atom.site_idx)) continue
1170
+ targets.set(atom.site_idx, {
1171
+ site_idx: atom.site_idx,
1172
+ position: atom.position,
1173
+ radius: atom.radius,
1174
+ })
1175
+ }
1176
+ return [...targets.values()]
1177
+ })
1178
+
1179
+ // Get radius for a site (for highlight fallback when site is hidden/filtered)
1180
+ // Checks site_radius_overrides first for consistency with visible atoms
1181
+ const get_site_radius = (site: Site, site_idx: number | null): number => {
1182
+ const override = site_idx !== null
1183
+ ? site_radius_overrides?.get(site_idx)
1184
+ : undefined
1185
+ const base_radius = same_size_atoms ? 1 : override ?? calc_weighted_radius(site)
1186
+ return base_radius * atom_radius
1187
+ }
1188
+
1189
+ // Interpolate between spin-down (#3498db blue) and spin-up (#e74c3c red)
1190
+ // based on the z-component direction of a magnetic vector
1191
+ function spin_direction_color(vec: Vec3): string {
1192
+ const mag = Math.hypot(...vec)
1193
+ const z_frac = mag > 1e-10 ? (vec[2] / mag + 1) / 2 : 0.5 // 0=down, 1=up
1194
+ const red = Math.round(52 + (231 - 52) * z_frac)
1195
+ const grn = Math.round(152 + (76 - 152) * z_frac)
1196
+ const blu = Math.round(219 + (60 - 219) * z_frac)
1197
+ return `#${red.toString(16).padStart(2, `0`)}${
1198
+ grn.toString(16).padStart(2, `0`)
1199
+ }${blu.toString(16).padStart(2, `0`)}`
1200
+ }
1201
+
1202
+ // Build one arrow layer per visible vector key. Auto-scales the longest
1203
+ // vector to 1.8× char_atom_spacing (cube root of volume per atom).
1204
+ // When vector_normalize is on, effective_max is 1 so all arrows get equal length.
1205
+ // Single active key preserves legacy coloring (element for force,
1206
+ // spin-direction for magmom/spin). Multiple keys use flat palette colors.
1207
+ let vector_layers = $derived.by(() => {
1208
+ if (!structure?.sites) return []
1209
+ const keys = get_structure_vector_keys(structure)
1210
+ const active_keys = keys.filter((key) => vector_configs[key]?.visible !== false)
1211
+ if (active_keys.length === 0) return []
1212
+
1213
+ // Build per-site lookup; skip hidden sites so they don't contribute
1214
+ // arrows or affect autoscaling. null entries = hidden site.
1215
+ const active_set = new Set(active_keys)
1216
+ let max_mag = 0
1217
+ const site_vec_maps = structure.sites.map((site, site_idx) => {
1218
+ if (!is_site_visible(site_idx)) return null
1219
+ const map = new SvelteMap<string, Vec3>()
1220
+ for (const { key, vec } of get_all_site_vectors(site)) {
1221
+ map.set(key, vec)
1222
+ if (active_set.has(key)) {
1223
+ max_mag = Math.max(max_mag, Math.hypot(...vec))
1224
+ }
1225
+ }
1226
+ return map
1227
+ })
1228
+
1229
+ // When normalize is on, treat all magnitudes as 1 so arrows have equal length
1230
+ const effective_max = vector_normalize ? 1 : max_mag
1231
+ const auto_scale = effective_max > 1e-10
1232
+ ? (char_atom_spacing * 1.8) / effective_max
1233
+ : 1
1234
+ const is_single = active_keys.length === 1
1235
+ const effective_global_scale = auto_scale * vector_scale
1236
+
1237
+ // When vector_origin_gap > 0 and multiple vectors exist at a site,
1238
+ // arrange arrow origins on a regular polygon centered on the atom, in a
1239
+ // plane perpendicular to the mean vector direction. The gap is a fraction
1240
+ // of the visual atom radius (0 = center, 0.5 = halfway to surface).
1241
+ // get_site_radius() returns the uniform scale applied to SphereGeometry(0.5),
1242
+ // so visual_radius = get_site_radius() * 0.5.
1243
+ const site_offsets = (vector_origin_gap > 0 && !is_single)
1244
+ ? structure.sites.map((site, site_idx) => {
1245
+ const vec_map = site_vec_maps[site_idx]
1246
+ if (!vec_map) return null
1247
+ const site_keys = active_keys.filter((key) => vec_map.has(key))
1248
+ const n_keys = site_keys.length
1249
+ if (n_keys <= 1) return null
1250
+ const visual_radius = get_site_radius(site, site_idx) * 0.5
1251
+ const gap_abs = vector_origin_gap * visual_radius
1252
+ let mean: Vec3 = [0, 0, 0]
1253
+ for (const key of site_keys) {
1254
+ const vec = vec_map.get(key)
1255
+ if (vec) mean = math.add(mean, math.normalize_vec(vec)) as Vec3
1256
+ }
1257
+ const mean_dir = math.normalize_vec(mean, [0, 1, 0] as Vec3)
1258
+ const [u_vec, v_vec] = math.compute_in_plane_basis(mean_dir)
1259
+ const offsets = new SvelteMap<string, Vec3>()
1260
+ for (const [idx, key] of site_keys.entries()) {
1261
+ const angle = (2 * Math.PI * idx) / n_keys
1262
+ const dx = math.scale(u_vec, gap_abs * Math.cos(angle)) as Vec3
1263
+ const dy = math.scale(v_vec, gap_abs * Math.sin(angle)) as Vec3
1264
+ offsets.set(key, math.add(dx, dy) as Vec3)
1265
+ }
1266
+ return offsets
1267
+ })
1268
+ : null
1269
+
1270
+ const mag_interpolator = get_d3_interpolator(vector_color_scale)
1271
+
1272
+ return active_keys.map((key, layer_idx) => {
1273
+ const layer_cfg = vector_configs[key]
1274
+ const layer_scale = effective_global_scale * (layer_cfg?.scale ?? 1.0)
1275
+ const layer_color = layer_cfg?.color ??
1276
+ VECTOR_PALETTE[layer_idx % VECTOR_PALETTE.length]
1277
+
1278
+ const arrows = structure.sites
1279
+ .map((site, site_idx) => {
1280
+ const vec_map = site_vec_maps[site_idx]
1281
+ if (!vec_map) return null
1282
+ const vec = vec_map.get(key)
1283
+ if (!vec) return null
1284
+
1285
+ // Resolve color mode: explicit per-key color always wins,
1286
+ // then multi-key uses palette, then mode-based coloring
1287
+ let arrow_color: string
1288
+ if (layer_cfg?.color) {
1289
+ arrow_color = layer_cfg.color
1290
+ } else if (!is_single) arrow_color = layer_color
1291
+ else {
1292
+ const effective_mode = vector_color_mode === `auto`
1293
+ ? (key.startsWith(`magmom`) || key.startsWith(`spin`)
1294
+ ? `spin_direction`
1295
+ : `element`)
1296
+ : vector_color_mode
1297
+ if (effective_mode === `magnitude`) {
1298
+ const mag = Math.hypot(...vec)
1299
+ const norm = max_mag > 1e-10 ? mag / max_mag : 0
1300
+ arrow_color = mag_interpolator(norm)
1301
+ } else if (effective_mode === `spin_direction`) {
1302
+ arrow_color = spin_direction_color(vec)
1303
+ } else if (effective_mode === `uniform`) {
1304
+ arrow_color = vector_color
1305
+ } else {
1306
+ const majority_element = site.species.length > 0
1307
+ ? site.species.reduce((max, spec) =>
1308
+ spec.occu > max.occu ? spec : max
1309
+ ).element
1310
+ : undefined
1311
+ arrow_color =
1312
+ (majority_element && colors.element?.[majority_element]) ||
1313
+ vector_color
1314
+ }
1315
+ }
1316
+
1317
+ const offset = site_offsets?.[site_idx]?.get(key)
1318
+ const position = offset ? math.add(site.xyz, offset) as Vec3 : site.xyz
1319
+ const arrow_vec = vector_normalize ? math.normalize_vec(vec) : vec
1320
+
1321
+ return {
1322
+ site_idx,
1323
+ position,
1324
+ vector: arrow_vec,
1325
+ scale: layer_scale,
1326
+ color: arrow_color,
1327
+ }
1328
+ })
1329
+ .filter((item): item is NonNullable<typeof item> => item !== null)
1330
+
1331
+ return { key, arrows }
1332
+ })
1333
+ })
1334
+
1335
+ let instanced_atom_groups = $derived(
1336
+ Object.values(
1337
+ atom_data
1338
+ .filter((atom) => !atom.has_partial_occupancy)
1339
+ .reduce(
1340
+ (groups, atom) => {
1341
+ const { element, radius, color, is_image_atom } = atom
1342
+ // Separate image atoms into their own groups for distinct styling in edit-atoms mode
1343
+ const key = `${element}-${format_num(radius, `.3~`)}-${color}-${
1344
+ is_image_atom ? `img` : `base`
1345
+ }`
1346
+ const bucket = groups[key] ||
1347
+ (groups[key] = { element, radius, color, is_image_atom, atoms: [] })
1348
+ bucket.atoms.push(atom)
1349
+ return groups
1350
+ },
1351
+ {} as Record<string, InstancedAtomGroup>,
1352
+ ),
1353
+ ),
1354
+ )
1355
+
1356
+ let unique_instanced_atoms = $derived(
1357
+ Object.values(
1358
+ instanced_atom_groups
1359
+ .flatMap((group) => group.atoms)
1360
+ .reduce((acc, atom) => {
1361
+ acc[atom.site_idx] = atom
1362
+ return acc
1363
+ }, {} as Record<number, (typeof atom_data)[number]>),
1364
+ ),
1365
+ )
1366
+
1367
+ let gizmo_props = $derived.by(() => {
1368
+ const axis_options = Object.fromEntries(
1369
+ [...AXIS_COLORS, ...NEG_AXIS_COLORS].map(([axis, color, hover_color]) => [
1370
+ axis,
1371
+ {
1372
+ color,
1373
+ labelColor: `#111`,
1374
+ opacity: axis.startsWith(`n`) ? 0.9 : 0.8,
1375
+ hover: {
1376
+ color: hover_color,
1377
+ labelColor: `#222222`,
1378
+ opacity: axis.startsWith(`n`) ? 1 : 0.9,
1379
+ },
1380
+ },
1381
+ ]),
1382
+ )
1383
+ return {
1384
+ background: { enabled: false },
1385
+ className: `responsive-gizmo`,
1386
+ ...axis_options,
1387
+ ...(typeof gizmo === `boolean` ? {} : gizmo),
1388
+ offset: { left: 5, bottom: 5 },
1389
+ }
1390
+ })
1391
+
1392
+ let orbit_controls_props = $derived({
1393
+ position: [0, 0, 0],
1394
+ enableRotate: rotate_speed > 0,
1395
+ rotateSpeed: rotate_speed,
1396
+ enableZoom: zoom_speed > 0,
1397
+ zoomSpeed: camera_projection === `orthographic` ? zoom_speed * 2 : zoom_speed,
1398
+ zoomToCursor: zoom_to_cursor,
1399
+ enablePan: pan_speed > 0,
1400
+ panSpeed: pan_speed,
1401
+ target: camera_target ?? rotation_target,
1402
+ maxZoom: max_zoom,
1403
+ minZoom: min_zoom,
1404
+ autoRotate: Boolean(auto_rotate),
1405
+ autoRotateSpeed: auto_rotate,
1406
+ enableDamping: Boolean(rotation_damping),
1407
+ dampingFactor: rotation_damping,
1408
+ onstart: () => {
1409
+ camera_is_moving = true
1410
+ cancel_atom_hover_clear()
1411
+ hovered_idx = null
1412
+ bond_context_menu = null
1413
+ },
1414
+ onend: () => {
1415
+ camera_is_moving = false
1416
+ },
1417
+ })
1418
+
1419
+ let measure_line_color = $derived.by(() => {
1420
+ if (typeof window === `undefined`) return
1421
+ const root_styles = getComputedStyle(document.documentElement)
1422
+ const text_color = root_styles.getPropertyValue(`--text-color`).trim()
1423
+ return text_color || `#808080`
1424
+ })
1425
+ </script>
1426
+
1427
+ {#snippet bond_instanced_mesh_snippet(
1428
+ group: ComponentProps<typeof Bond>[`group`],
1429
+ )}
1430
+ {#key group.instances.length}
1431
+ <Bond {group} />
1432
+ {/key}
1433
+ {/snippet}
1434
+
1435
+ {#snippet site_label_snippet(position: Vec3, site_idx: number)}
1436
+ {@const site = structure!.sites[site_idx]}
1437
+ {@const visual_radius = (radius_by_site_idx.get(site_idx) ?? 1) * 0.5}
1438
+ <extras.HTML
1439
+ center
1440
+ position={position}
1441
+ calculatePosition={make_label_position_calculator(
1442
+ position,
1443
+ () => smart_site_label_offsets.get(site_idx) ?? site_label_offset,
1444
+ visual_radius,
1445
+ label_screen_margin,
1446
+ )}
1447
+ >
1448
+ {#if atom_label}
1449
+ {@render atom_label({ site, site_idx })}
1450
+ {:else}
1451
+ <button
1452
+ type="button"
1453
+ class="atom-label"
1454
+ style:font-size="{site_label_size * 0.85}em"
1455
+ style:background={site_label_bg_color}
1456
+ style:padding="{site_label_padding}px"
1457
+ style:color={site_label_color}
1458
+ onpointerdown={(event) => {
1459
+ event.preventDefault()
1460
+ event.stopImmediatePropagation()
1461
+ toggle_selection(site_idx, event)
1462
+ }}
1463
+ onclick={(event) => {
1464
+ event.preventDefault()
1465
+ event.stopImmediatePropagation()
1466
+ }}
1467
+ onkeydown={(event) => {
1468
+ if (event.key !== `Enter` && event.key !== ` `) return
1469
+ event.preventDefault()
1470
+ event.stopPropagation()
1471
+ toggle_selection(site_idx, event)
1472
+ }}
1473
+ >
1474
+ {#if show_site_labels}
1475
+ {#if site.species.length === 1}
1476
+ {site.species[0].element}{#if show_site_indices}-{site_idx + 1}{/if}
1477
+ {:else}
1478
+ {#each site.species as
1479
+ { element, occu, oxidation_state }
1480
+ (`${element}-${occu}-${oxidation_state}`)
1481
+ }
1482
+ {element}<sub>{format_num(occu, `.3~`).replace(`0.`, `.`)}</sub>
1483
+ {/each}
1484
+ {#if show_site_indices}-{site_idx + 1}{/if}
1485
+ {/if}
1486
+ {:else if show_site_indices}
1487
+ {site_idx + 1}
1488
+ {/if}
1489
+ </button>
1490
+ {/if}
1491
+ </extras.HTML>
1492
+ {/snippet}
1493
+
1494
+ {#if camera_projection === `perspective`}
1495
+ <T.PerspectiveCamera
1496
+ makeDefault
1497
+ position={camera_position}
1498
+ {fov}
1499
+ near={camera_near}
1500
+ far={camera_far}
1501
+ >
1502
+ <extras.OrbitControls bind:ref={orbit_controls} {...orbit_controls_props}>
1503
+ {#if gizmo}<extras.Gizmo {...gizmo_props} />{/if}
1504
+ </extras.OrbitControls>
1505
+ </T.PerspectiveCamera>
1506
+ {:else}
1507
+ <T.OrthographicCamera
1508
+ makeDefault
1509
+ position={camera_position}
1510
+ zoom={computed_zoom}
1511
+ near={-100}
1512
+ far={camera_far}
1513
+ >
1514
+ <extras.OrbitControls bind:ref={orbit_controls} {...orbit_controls_props}>
1515
+ {#if gizmo}<extras.Gizmo {...gizmo_props} />{/if}
1516
+ </extras.OrbitControls>
1517
+ </T.OrthographicCamera>
1518
+ {/if}
1519
+
1520
+ <T.DirectionalLight position={[3, 10, 10]} intensity={directional_light} />
1521
+ <T.AmbientLight intensity={ambient_light} />
1522
+
1523
+ <!-- Apply manual rotation around center: translate to origin, rotate, translate back -->
1524
+ <T.Group position={rotation_target}>
1525
+ <T.Group {rotation}>
1526
+ <T.Group position={math.scale(rotation_target, -1)}>
1527
+ {#if show_atoms}
1528
+ <!-- Instanced rendering for full occupancy atoms -->
1529
+ {#each instanced_atom_groups as atom_group (instanced_atom_group_key(atom_group, measure_mode))}
1530
+ {@const { element, radius, color, is_image_atom, atoms } = atom_group}
1531
+ {@const edit_mode_image = measure_mode === `edit-atoms` && is_image_atom}
1532
+ <extras.InstancedMesh
1533
+ key={instanced_atom_group_key(atom_group, measure_mode)}
1534
+ limit={atoms.length}
1535
+ range={atoms.length}
1536
+ frustumCulled={false}
1537
+ >
1538
+ <T.SphereGeometry args={[0.5, sphere_segments, sphere_segments]} />
1539
+ <T.MeshStandardMaterial
1540
+ color={edit_mode_image ? desaturate(color) : color}
1541
+ opacity={edit_mode_image ? 0.5 : 1}
1542
+ transparent={edit_mode_image}
1543
+ />
1544
+ {#each atoms as atom (atom.site_idx)}
1545
+ <extras.Instance
1546
+ position={atom.position}
1547
+ scale={atom.radius}
1548
+ {...atom_hover_props(atom.site_idx, edit_mode_image)}
1549
+ onpointerdown={(event: PointerEvent) => {
1550
+ if (
1551
+ edit_mode_image ||
1552
+ measure_mode !== `edit-bonds` ||
1553
+ bond_edit_mode !== `add`
1554
+ ) {
1555
+ return
1556
+ }
1557
+ select_edit_bonds_site(atom.site_idx, event)
1558
+ }}
1559
+ onclick={(event: MouseEvent) => {
1560
+ if (edit_mode_image) return
1561
+ if (measure_mode === `edit-bonds`) {
1562
+ if (bond_edit_mode !== `add`) return
1563
+ if (skip_duplicate_edit_bonds_click(atom.site_idx)) {
1564
+ event.stopPropagation()
1565
+ return
1566
+ }
1567
+ }
1568
+ toggle_selection(atom.site_idx, event)
1569
+ }}
1570
+ />
1571
+ {/each}
1572
+ </extras.InstancedMesh>
1573
+ {/each}
1574
+
1575
+ <!-- Regular rendering for partial occupancy atoms -->
1576
+ {#each atom_data.filter((atom) => atom.has_partial_occupancy) as
1577
+ atom
1578
+ (atom.site_idx + atom.element + atom.occupancy)
1579
+ }
1580
+ {@const partial_edit_image = measure_mode === `edit-atoms` && atom.is_image_atom}
1581
+ {@const ghost_opacity = partial_edit_image ? 0.5 : 1}
1582
+ <T.Group
1583
+ position={atom.position}
1584
+ scale={atom.radius}
1585
+ {...atom_hover_props(atom.site_idx, partial_edit_image)}
1586
+ onpointerdown={(event: PointerEvent) => {
1587
+ if (
1588
+ partial_edit_image ||
1589
+ measure_mode !== `edit-bonds` ||
1590
+ bond_edit_mode !== `add`
1591
+ ) {
1592
+ return
1593
+ }
1594
+ select_edit_bonds_site(atom.site_idx, event)
1595
+ }}
1596
+ onclick={(event: MouseEvent) => {
1597
+ if (partial_edit_image) return
1598
+ if (measure_mode === `edit-bonds`) {
1599
+ if (bond_edit_mode !== `add`) return
1600
+ if (skip_duplicate_edit_bonds_click(atom.site_idx)) {
1601
+ event.stopPropagation()
1602
+ return
1603
+ }
1604
+ }
1605
+ toggle_selection(atom.site_idx, event)
1606
+ }}
1607
+ >
1608
+ {@const partial_color = partial_edit_image
1609
+ ? desaturate(atom.color)
1610
+ : atom.color}
1611
+ <T.Mesh>
1612
+ <T.SphereGeometry
1613
+ args={[
1614
+ 0.5,
1615
+ sphere_segments,
1616
+ sphere_segments,
1617
+ atom.start_phi,
1618
+ atom.phi_length,
1619
+ ]}
1620
+ />
1621
+ <T.MeshStandardMaterial
1622
+ color={partial_color}
1623
+ opacity={ghost_opacity}
1624
+ transparent={partial_edit_image}
1625
+ />
1626
+ </T.Mesh>
1627
+
1628
+ {#if atom.has_partial_occupancy && atom.render_start_cap}
1629
+ <T.Mesh rotation={[0, atom.start_phi, 0]}>
1630
+ <T.CircleGeometry
1631
+ args={[
1632
+ 0.5,
1633
+ sphere_segments,
1634
+ PARTIAL_OCCUPANCY_CAP_ARC.start_cap_arc_start,
1635
+ PARTIAL_OCCUPANCY_CAP_ARC.arc_length,
1636
+ ]}
1637
+ />
1638
+ <T.MeshStandardMaterial
1639
+ color={partial_color}
1640
+ side={2}
1641
+ opacity={ghost_opacity}
1642
+ transparent={partial_edit_image}
1643
+ />
1644
+ </T.Mesh>
1645
+ {/if}
1646
+ {#if atom.has_partial_occupancy && atom.render_end_cap}
1647
+ <T.Mesh rotation={[0, atom.end_phi, 0]}>
1648
+ <T.CircleGeometry
1649
+ args={[
1650
+ 0.5,
1651
+ sphere_segments,
1652
+ PARTIAL_OCCUPANCY_CAP_ARC.end_cap_arc_start,
1653
+ PARTIAL_OCCUPANCY_CAP_ARC.arc_length,
1654
+ ]}
1655
+ />
1656
+ <T.MeshStandardMaterial
1657
+ color={partial_color}
1658
+ side={2}
1659
+ opacity={ghost_opacity}
1660
+ transparent={partial_edit_image}
1661
+ />
1662
+ </T.Mesh>
1663
+ {/if}
1664
+ </T.Group>
1665
+
1666
+ <!-- Render label only for the first species of this site to avoid duplicates -->
1667
+ {#if (show_site_labels || show_site_indices) &&
1668
+ atom.element === structure!.sites[atom.site_idx].species[0].element}
1669
+ {@render site_label_snippet(atom.position, atom.site_idx)}
1670
+ {/if}
1671
+ {/each}
1672
+
1673
+ <!-- Site labels/indices for instanced atoms -->
1674
+ {#if show_site_labels || show_site_indices}
1675
+ {#each unique_instanced_atoms as atom (atom.site_idx)}
1676
+ {@render site_label_snippet(atom.position, atom.site_idx)}
1677
+ {/each}
1678
+ {/if}
1679
+ {/if}
1680
+
1681
+ {#each vector_layers as layer (layer.key)}
1682
+ {#each layer.arrows as arrow (`${layer.key}-${arrow.site_idx}`)}
1683
+ <Arrow
1684
+ {...arrow}
1685
+ shaft_radius={eff_shaft_radius}
1686
+ arrow_head_radius={eff_head_radius}
1687
+ arrow_head_length={eff_head_length}
1688
+ />
1689
+ {/each}
1690
+ {/each}
1691
+
1692
+ <!-- Instanced bond rendering with gradient colors -->
1693
+ {#if instanced_bond_groups.length > 0}
1694
+ {#each instanced_bond_groups as group (group.thickness + group.instances.length)}
1695
+ {@render bond_instanced_mesh_snippet(group)}
1696
+ {/each}
1697
+ {/if}
1698
+
1699
+ <!-- Clickable bond hit-test cylinders in edit-bonds mode -->
1700
+ {#if measure_mode === `edit-bonds` && editable_bond_pairs.length > 0}
1701
+ {#each editable_bond_pairs as
1702
+ bond
1703
+ (`bond-hit-${bond_edit_mode}-${rendered_bond_key_for(bond)}`)
1704
+ }
1705
+ {@const bond_key = rendered_bond_key_for(bond)}
1706
+ {@const is_hovered = hovered_bond_key === bond_key}
1707
+ {@const is_delete_mode = bond_edit_mode === `delete`}
1708
+ {@const bond_hit_radius =
1709
+ bond_thickness * (is_delete_mode ? 5 : 1.25)}
1710
+ {@const bond_hover_radius = bond_thickness * 1.1}
1711
+ <T.Mesh
1712
+ matrixAutoUpdate={false}
1713
+ oncreate={(ref) => apply_bond_transform(ref, bond)}
1714
+ onpointerdown={(event: BondPointerEvent) => {
1715
+ if (event.nativeEvent?.button === 2) return
1716
+ event.stopPropagation()
1717
+ if (is_delete_mode) {
1718
+ remove_bond(bond.site_idx_1, bond.site_idx_2, bond.cell_shift)
1719
+ measured_sites = []
1720
+ selected_sites = []
1721
+ hovered_bond_key = null
1722
+ } else {
1723
+ const endpoint_site_idx = get_bond_endpoint_hit_site_idx(bond, event)
1724
+ if (endpoint_site_idx != null) {
1725
+ select_edit_bonds_site(endpoint_site_idx, event)
1726
+ }
1727
+ }
1728
+ }}
1729
+ oncontextmenu={(event: BondContextMenuEvent) => {
1730
+ event.nativeEvent?.preventDefault()
1731
+ event.stopPropagation?.()
1732
+ open_bond_context_menu(bond, event)
1733
+ }}
1734
+ onpointerenter={() => (hovered_bond_key = bond_key)}
1735
+ onpointermove={() => (hovered_bond_key = bond_key)}
1736
+ onpointerleave={() => (hovered_bond_key = null)}
1737
+ >
1738
+ <T.CylinderGeometry
1739
+ args={[
1740
+ bond_hit_radius,
1741
+ bond_hit_radius,
1742
+ 1,
1743
+ 6,
1744
+ ]}
1745
+ />
1746
+ <T.MeshBasicMaterial
1747
+ transparent
1748
+ opacity={0}
1749
+ depthWrite={false}
1750
+ />
1751
+ </T.Mesh>
1752
+ {#if is_hovered}
1753
+ <T.Mesh
1754
+ matrixAutoUpdate={false}
1755
+ oncreate={(ref) => apply_non_raycastable_bond_hit_transform(ref, bond)}
1756
+ >
1757
+ <T.CylinderGeometry args={[bond_hover_radius, bond_hover_radius, 1, 6]} />
1758
+ <T.MeshBasicMaterial
1759
+ transparent
1760
+ opacity={0.25}
1761
+ color={is_delete_mode ? `#ff4444` : `#6cf0ff`}
1762
+ depthWrite={false}
1763
+ />
1764
+ </T.Mesh>
1765
+ {/if}
1766
+ {/each}
1767
+ {/if}
1768
+
1769
+ {#if editable_atom_hit_targets.length > 0}
1770
+ {#each editable_atom_hit_targets as atom_hit (atom_hit.site_idx)}
1771
+ <T.Mesh
1772
+ position={atom_hit.position}
1773
+ scale={atom_hit.radius * EDITABLE_ATOM_HIT_RADIUS_SCALE}
1774
+ {...atom_hover_props(atom_hit.site_idx)}
1775
+ onpointerdown={(event: PointerEvent) => {
1776
+ select_edit_bonds_site(atom_hit.site_idx, event)
1777
+ }}
1778
+ >
1779
+ <T.SphereGeometry args={[0.5, 12, 12]} />
1780
+ <T.MeshBasicMaterial
1781
+ transparent
1782
+ opacity={0}
1783
+ depthWrite={false}
1784
+ />
1785
+ </T.Mesh>
1786
+ {/each}
1787
+ {/if}
1788
+
1789
+ {#if measure_mode === `edit-bonds` && bond_context_menu}
1790
+ {@const current_order = get_current_bond_order(
1791
+ bond_context_menu.site_idx_1,
1792
+ bond_context_menu.site_idx_2,
1793
+ bond_context_menu.cell_shift,
1794
+ )}
1795
+ <extras.HTML autoRender={false} position={bond_context_menu.position}>
1796
+ <div class="bond-context-menu">
1797
+ <strong>Bond Order ({format_bond_order(current_order)})</strong>
1798
+ {#each BOND_ORDER_OPTIONS as { order, label } (label)}
1799
+ <button
1800
+ type="button"
1801
+ onpointerdown={(event: PointerEvent) => {
1802
+ event.preventDefault()
1803
+ event.stopPropagation()
1804
+ set_context_bond_order(order)
1805
+ }}
1806
+ onkeydown={(event: KeyboardEvent) => {
1807
+ if (event.key !== `Enter` && event.key !== ` `) return
1808
+ event.preventDefault()
1809
+ event.stopPropagation()
1810
+ set_context_bond_order(order)
1811
+ }}
1812
+ >
1813
+ {label}
1814
+ </button>
1815
+ {/each}
1816
+ <button
1817
+ type="button"
1818
+ class="remove"
1819
+ onpointerdown={(event: PointerEvent) => {
1820
+ event.preventDefault()
1821
+ event.stopPropagation()
1822
+ remove_context_bond()
1823
+ }}
1824
+ onkeydown={(event: KeyboardEvent) => {
1825
+ if (event.key !== `Enter` && event.key !== ` `) return
1826
+ event.preventDefault()
1827
+ event.stopPropagation()
1828
+ remove_context_bond()
1829
+ }}
1830
+ >
1831
+ Remove
1832
+ </button>
1833
+ <button
1834
+ type="button"
1835
+ onpointerdown={(event: PointerEvent) => {
1836
+ event.preventDefault()
1837
+ event.stopPropagation()
1838
+ close_bond_context_menu()
1839
+ }}
1840
+ onkeydown={(event: KeyboardEvent) => {
1841
+ if (event.key !== `Enter` && event.key !== ` `) return
1842
+ event.preventDefault()
1843
+ event.stopPropagation()
1844
+ close_bond_context_menu()
1845
+ }}
1846
+ >
1847
+ Close
1848
+ </button>
1849
+ </div>
1850
+ </extras.HTML>
1851
+ {/if}
1852
+
1853
+ <!-- highlight hovered, active and selected sites -->
1854
+ {#each [
1855
+ {
1856
+ kind: `hover`,
1857
+ site: hovered_site,
1858
+ opacity: 0.28,
1859
+ color: `white`,
1860
+ site_idx: hovered_idx,
1861
+ },
1862
+ ...((selected_sites ?? []).map((idx) => ({
1863
+ kind: `selected`,
1864
+ site: structure?.sites?.[idx] ?? null,
1865
+ site_idx: idx,
1866
+ opacity: pulse_opacity,
1867
+ color: selection_highlight_color,
1868
+ }))),
1869
+ ...((active_sites ?? []).map((idx) => ({
1870
+ kind: `active`,
1871
+ site: structure?.sites?.[idx] ?? null,
1872
+ site_idx: idx,
1873
+ opacity: pulse_opacity, // Let it pulse freely
1874
+ color: active_highlight_color,
1875
+ }))),
1876
+ ] as
1877
+ entry
1878
+ (`${entry.kind}-${entry.site_idx}`)
1879
+ }
1880
+ {@const { site, opacity, color, kind, site_idx } = entry}
1881
+ {#if site}
1882
+ {@const xyz = site.xyz}
1883
+ {@const highlight_radius = site_idx !== null
1884
+ ? radius_by_site_idx.get(site_idx) ?? get_site_radius(site, site_idx)
1885
+ : get_site_radius(site, site_idx)}
1886
+ <T.Mesh
1887
+ position={xyz}
1888
+ scale={1.2 * highlight_radius}
1889
+ oncreate={disable_raycast}
1890
+ >
1891
+ <T.SphereGeometry args={[0.5, 22, 22]} />
1892
+ <T.MeshStandardMaterial
1893
+ {color}
1894
+ transparent
1895
+ {opacity}
1896
+ emissive={color}
1897
+ emissiveIntensity={kind === `selected` || kind === `active` ? 0.7 : 0.2}
1898
+ depthTest={false}
1899
+ depthWrite={false}
1900
+ />
1901
+ </T.Mesh>
1902
+ {/if}
1903
+ {/each}
1904
+
1905
+ <!-- selection order labels (1, 2, 3, ...) for measurements and bond editing -->
1906
+ {#if structure?.sites && (measured_sites?.length ?? 0) > 0 &&
1907
+ (measure_mode === `distance` || measure_mode === `angle` ||
1908
+ measure_mode === `edit-bonds`)}
1909
+ {#each measured_sites as site_index, loop_idx (site_index)}
1910
+ {@const site = structure.sites[site_index]}
1911
+ {#if site}
1912
+ <!-- shift selected site labels down to avoid overlapping regular site labels-->
1913
+ {@const selection_offset = math.add(site_label_offset, [0, -0.5, 0])}
1914
+ {@const pos = math.add(site.xyz, selection_offset) as Vec3}
1915
+ <extras.HTML center position={pos}>
1916
+ <span class="selection-label">{loop_idx + 1}</span>
1917
+ </extras.HTML>
1918
+ {/if}
1919
+ {/each}
1920
+ {/if}
1921
+
1922
+ <!-- hovered site tooltip -->
1923
+ {#if hovered_site && !camera_is_moving &&
1924
+ (atom_tooltip_active || active_sites.includes(hovered_idx ?? -1))}
1925
+ {@const abc = hovered_site.abc.map((val) => format_num(val, float_fmt)).join(
1926
+ `, `,
1927
+ )}
1928
+ {@const xyz = hovered_site.xyz.map((val) => format_num(val, float_fmt)).join(
1929
+ `, `,
1930
+ )}
1931
+ {@const bond_neighbors = (() => {
1932
+ if (hovered_idx == null || !structure?.sites) return []
1933
+ return filtered_bond_pairs
1934
+ .filter((bond) =>
1935
+ bond.site_idx_1 === hovered_idx || bond.site_idx_2 === hovered_idx
1936
+ )
1937
+ .map((bond) => {
1938
+ const neighbor_idx = bond.site_idx_1 === hovered_idx
1939
+ ? bond.site_idx_2
1940
+ : bond.site_idx_1
1941
+ return structure.sites[neighbor_idx]?.species[0]?.element ?? `?`
1942
+ })
1943
+ })()}
1944
+ {@const bond_summary = (() => {
1945
+ if (bond_neighbors.length === 0) return ``
1946
+ const counts: Record<string, number> = {}
1947
+ for (const elem of bond_neighbors) {
1948
+ counts[elem] = (counts[elem] ?? 0) + 1
1949
+ }
1950
+ const parts = Object.entries(counts)
1951
+ .sort(([a], [b]) => a.localeCompare(b))
1952
+ .map(([elem, count]) => `${elem}: ${count}`)
1953
+ return ` (${parts.join(`, `)})`
1954
+ })()}
1955
+ <CanvasTooltip position={hovered_site.xyz}>
1956
+ <!-- Element symbols with occupancies for disordered sites -->
1957
+ <div class="elements">
1958
+ {#each hovered_site.species ?? [] as
1959
+ { element, occu, oxidation_state: oxi_state },
1960
+ idx
1961
+ (`${element ?? ``}-${occu ?? ``}-${oxi_state ?? ``}-${idx}`)
1962
+ }
1963
+ {@const element_name = element_data.find((elem) =>
1964
+ elem.symbol === element
1965
+ )?.name ??
1966
+ ``}
1967
+ {#if idx > 0}&thinsp;{/if}
1968
+ {#if occu !== 1}<span class="occupancy">{
1969
+ format_num(occu, `.3~f`)
1970
+ }</span>{/if}
1971
+ <strong>
1972
+ {element}{#if oxi_state != null && oxi_state !== 0}<sup>{Math.abs(
1973
+ oxi_state,
1974
+ )}{oxi_state > 0 ? `+` : `−`}</sup>{/if}
1975
+ </strong>
1976
+ {#if element_name}<span class="elem-name">{element_name}</span>{/if}
1977
+ {/each}
1978
+ </div>
1979
+ <div class="coordinates fractional">abc: ({abc})</div>
1980
+ <div class="coordinates cartesian">xyz: ({xyz}) Å</div>
1981
+ {#if bond_neighbors.length > 0}
1982
+ <div class="coordinates">Bonds: {bond_neighbors.length}{bond_summary}</div>
1983
+ {/if}
1984
+ </CanvasTooltip>
1985
+ {/if}
1986
+
1987
+ {#if visual_lattice}
1988
+ <Lattice matrix={visual_lattice.matrix} {...lattice_props} />
1989
+ {/if}
1990
+
1991
+ <!-- TransformControls for editing atoms in edit-atoms mode -->
1992
+ {#if measure_mode === `edit-atoms` && selected_sites.length > 0 &&
1993
+ structure?.sites}
1994
+ {@const selected_atoms = selected_sites
1995
+ .map((idx) => structure?.sites?.[idx])
1996
+ .filter((site): site is Site => site != null)}
1997
+ {#if selected_atoms.length > 0}
1998
+ {@const avg = (dim: number) =>
1999
+ selected_atoms.reduce((sum, atom) => sum + atom.xyz[dim], 0) /
2000
+ selected_atoms.length}
2001
+ {@const centroid = [avg(0), avg(1), avg(2)] as Vec3}
2002
+ <!-- Invisible mesh at centroid for TransformControls to manipulate.
2003
+ During drag, use frozen_centroid so Svelte doesn't override TransformControls
2004
+ with the wrapped centroid (which jumps on PBC boundary crossings). -->
2005
+ <T.Mesh
2006
+ position={frozen_centroid ?? centroid}
2007
+ bind:ref={transform_object}
2008
+ >
2009
+ <T.SphereGeometry args={[0.01, 4, 4]} />
2010
+ <T.MeshBasicMaterial transparent opacity={0} />
2011
+ </T.Mesh>
2012
+ <extras.TransformControls
2013
+ object={transform_object}
2014
+ translationSnap={0.1}
2015
+ size={1.2}
2016
+ space="world"
2017
+ onobjectChange={() => {
2018
+ if (!transform_object?.position || !drag_start_centroid) return
2019
+ const { x: tx, y: ty, z: tz } = transform_object.position
2020
+ const delta: Vec3 = [
2021
+ tx - drag_start_centroid[0],
2022
+ ty - drag_start_centroid[1],
2023
+ tz - drag_start_centroid[2],
2024
+ ]
2025
+ // Update reference point so deltas are incremental, not cumulative.
2026
+ // Without this, each frame compounds: sites already moved by previous
2027
+ // delta get the full cumulative delta re-applied.
2028
+ drag_start_centroid = [tx, ty, tz]
2029
+ on_sites_moved?.(selected_sites, delta)
2030
+ }}
2031
+ onmouseDown={() => {
2032
+ dragging_atoms = true
2033
+ drag_start_centroid = frozen_centroid = [...centroid] as Vec3
2034
+ on_operation_start?.()
2035
+ }}
2036
+ onmouseUp={() => {
2037
+ dragging_atoms = false
2038
+ frozen_centroid = null
2039
+ drag_start_centroid = null
2040
+ }}
2041
+ />
2042
+ {/if}
2043
+ {/if}
2044
+
2045
+ <!-- Invisible plane for click-to-place atom in add-atom mode -->
2046
+ <!-- Uses onBeforeRender to orient normal toward camera so raycasts always hit -->
2047
+ {#if measure_mode === `edit-atoms` && add_atom_mode}
2048
+ {@const center = rotation_target ?? [0, 0, 0]}
2049
+ <T.Mesh
2050
+ position={center}
2051
+ onBeforeRender={(mesh: Mesh) => {
2052
+ if (camera) {
2053
+ mesh.lookAt(camera.position)
2054
+ }
2055
+ }}
2056
+ onclick={(event: { point: { x: number; y: number; z: number } }) => {
2057
+ const { x, y, z } = event.point
2058
+ on_add_atom?.([x, y, z] as Vec3, add_element as ElementSymbol)
2059
+ }}
2060
+ >
2061
+ <T.PlaneGeometry
2062
+ args={[
2063
+ Math.max(200, structure_size * 4),
2064
+ Math.max(200, structure_size * 4),
2065
+ ]}
2066
+ />
2067
+ <T.MeshBasicMaterial transparent opacity={0} side={2} depthWrite={false} />
2068
+ </T.Mesh>
2069
+ {/if}
2070
+
2071
+ <!-- Isosurface rendering from volumetric data (CHGCAR, .cube files) -->
2072
+ {#if volumetric_data && isosurface_settings}
2073
+ <Isosurface volume={volumetric_data} settings={isosurface_settings} />
2074
+ {/if}
2075
+
2076
+ <!-- Measurement overlays for measured sites -->
2077
+ {#if structure?.sites && (measured_sites?.length ?? 0) > 0}
2078
+ {#if measure_mode === `distance`}
2079
+ {#each measured_sites as idx_i, loop_idx (idx_i)}
2080
+ {#each measured_sites.slice(loop_idx + 1) as idx_j (idx_i + `-` + idx_j)}
2081
+ {@const site_i = structure.sites[idx_i]}
2082
+ {@const site_j = structure.sites[idx_j]}
2083
+ {@const pos_i = site_i.xyz}
2084
+ {@const pos_j = site_j.xyz}
2085
+ <Cylinder
2086
+ from={pos_i}
2087
+ to={pos_j}
2088
+ thickness={0.12}
2089
+ color={measure_line_color}
2090
+ />
2091
+ {@const midpoint = [
2092
+ (pos_i[0] + pos_j[0]) / 2,
2093
+ (pos_i[1] + pos_j[1]) / 2,
2094
+ (pos_i[2] + pos_j[2]) / 2,
2095
+ ] as Vec3}
2096
+ {@const direct = math.euclidean_dist(pos_i, pos_j)}
2097
+ {@const pbc = lattice
2098
+ ? measure.distance_pbc(pos_i, pos_j, lattice.matrix, undefined, lattice.pbc)
2099
+ : direct}
2100
+ {@const differ = lattice ? Math.abs(pbc - direct) > 1e-6 : false}
2101
+ <extras.HTML center position={midpoint}>
2102
+ <span class="measure-label">
2103
+ {#if differ}
2104
+ PBC: {format_num(pbc, float_fmt)} Å<br /><small>
2105
+ Direct: {format_num(direct, float_fmt)} Å</small>
2106
+ {:else}
2107
+ {format_num(pbc, float_fmt)} Å
2108
+ {/if}
2109
+ </span>
2110
+ </extras.HTML>
2111
+ {/each}
2112
+ {/each}
2113
+ {:else if measure_mode === `angle` && measured_sites.length >= 3}
2114
+ {#each measured_sites as idx_center (idx_center)}
2115
+ {@const center = structure.sites[idx_center]}
2116
+ {#each measured_sites.filter((idx) => idx !== idx_center) as
2117
+ idx_a,
2118
+ loop_idx
2119
+ (idx_center + `-` + idx_a)
2120
+ }
2121
+ {#each measured_sites.filter((idx) => idx !== idx_center).slice(loop_idx + 1) as
2122
+ idx_b
2123
+ (idx_center + `-` + idx_a + `-` + idx_b)
2124
+ }
2125
+ {@const site_a = structure.sites[idx_a]}
2126
+ {@const site_b = structure.sites[idx_b]}
2127
+ {@const disp = (to: Vec3) =>
2128
+ measure.displacement_pbc(center.xyz, to, lattice?.matrix, undefined, lattice?.pbc)}
2129
+ {@const v1 = disp(site_a.xyz)}
2130
+ {@const v2 = disp(site_b.xyz)}
2131
+ {@const n1 = Math.hypot(v1[0], v1[1], v1[2])}
2132
+ {@const n2 = Math.hypot(v2[0], v2[1], v2[2])}
2133
+ {@const angle_deg = measure.angle_between_vectors(v1, v2, `degrees`)}
2134
+ {#if n1 > math.EPS && n2 > math.EPS}
2135
+ <!-- draw rays from center to the two sites -->
2136
+ <Cylinder
2137
+ from={center.xyz}
2138
+ to={site_a.xyz}
2139
+ thickness={0.05}
2140
+ color={measure_line_color}
2141
+ />
2142
+ <Cylinder
2143
+ from={center.xyz}
2144
+ to={site_b.xyz}
2145
+ thickness={0.05}
2146
+ color={measure_line_color}
2147
+ />
2148
+ {@const bisector = math.add(math.scale(v1, 1 / n1), math.scale(v2, 1 / n2))}
2149
+ {@const bis_norm = Math.hypot(...bisector) || 1}
2150
+ {@const offset_dir = math.scale(bisector, 1 / bis_norm)}
2151
+ {@const label_pos = math.add(center.xyz, math.scale(offset_dir, 0.6))}
2152
+ <extras.HTML center position={label_pos}>
2153
+ <span class="measure-label">{format_num(angle_deg, float_fmt)}°</span>
2154
+ </extras.HTML>
2155
+ {/if}
2156
+ {/each}
2157
+ {/each}
2158
+ {/each}
2159
+ {/if}
2160
+ {/if}
2161
+ </T.Group>
2162
+ </T.Group>
2163
+ </T.Group>
2164
+
2165
+ <style>
2166
+ :global(.structure .responsive-gizmo) {
2167
+ width: clamp(70px, 18cqmin, 100px) !important;
2168
+ height: clamp(70px, 18cqmin, 100px) !important;
2169
+ }
2170
+ .atom-label {
2171
+ background: var(--struct-atom-label-bg, rgba(0, 0, 0, 0.1));
2172
+ border: 0;
2173
+ border-radius: var(--struct-atom-label-border-radius, var(--border-radius, 3pt));
2174
+ color: inherit;
2175
+ cursor: pointer;
2176
+ font: inherit;
2177
+ padding: var(--struct-atom-label-padding, 0 3px);
2178
+ white-space: nowrap;
2179
+ }
2180
+ .elements {
2181
+ margin-bottom: var(--canvas-tooltip-elements-margin);
2182
+ }
2183
+ .occupancy {
2184
+ font-size: var(--canvas-tooltip-occu-font-size);
2185
+ opacity: var(--canvas-tooltip-occu-opacity);
2186
+ margin-right: var(--canvas-tooltip-occu-margin);
2187
+ }
2188
+ .elem-name {
2189
+ font-size: var(--canvas-tooltip-elem-name-font-size, 0.85em);
2190
+ opacity: var(--canvas-tooltip-elem-name-opacity, 0.7);
2191
+ margin: var(--canvas-tooltip-elem-name-margin, 0 0 0 0.3em);
2192
+ font-weight: var(--canvas-tooltip-elem-name-font-weight, normal);
2193
+ }
2194
+ .coordinates {
2195
+ font-size: var(--canvas-tooltip-coords-font-size);
2196
+ margin: var(--canvas-tooltip-coords-margin);
2197
+ }
2198
+ .measure-label {
2199
+ background: var(--measure-label-bg, var(--surface-bg));
2200
+ color: var(--measure-label-color, var(--text-color));
2201
+ border-radius: var(--border-radius, 3pt);
2202
+ padding: 0 5px;
2203
+ user-select: none;
2204
+ white-space: pre;
2205
+ display: grid;
2206
+ place-items: center;
2207
+ line-height: 1.2;
2208
+ font-size: var(--canvas-tooltip-font-size, clamp(8pt, 2cqmin, 18pt));
2209
+ box-shadow: var(--measure-label-shadow, 0 1px 6px rgba(0, 0, 0, 0.2));
2210
+ }
2211
+ .bond-context-menu {
2212
+ display: grid;
2213
+ min-width: 8rem;
2214
+ gap: 2pt;
2215
+ padding: 3pt 5pt;
2216
+ border-radius: var(--border-radius, 3pt);
2217
+ background: var(--surface-bg, Canvas);
2218
+ color: var(--text-color, currentColor);
2219
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
2220
+ pointer-events: auto;
2221
+ strong {
2222
+ font-size: 0.85em;
2223
+ padding: 0 2pt 2pt;
2224
+ white-space: nowrap;
2225
+ }
2226
+ button {
2227
+ border: none;
2228
+ border-radius: var(--border-radius, 3pt);
2229
+ background: transparent;
2230
+ color: inherit;
2231
+ cursor: pointer;
2232
+ padding: 2pt 5pt;
2233
+ text-align: left;
2234
+ }
2235
+ button:hover {
2236
+ background: color-mix(in srgb, currentColor 10%, transparent);
2237
+ }
2238
+ button.remove {
2239
+ color: var(--error-color, #f44336);
2240
+ }
2241
+ }
2242
+ .selection-label {
2243
+ display: inline-flex;
2244
+ align-items: center;
2245
+ justify-content: center;
2246
+ min-width: 1.2em;
2247
+ height: 1.2em;
2248
+ padding: 0 0.25em;
2249
+ border-radius: 999px;
2250
+ background: var(--pane-btn-bg-hover);
2251
+ color: var(--struct-text-color);
2252
+ font-size: 0.85em;
2253
+ line-height: 1;
2254
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
2255
+ }
2256
+ </style>