matterviz 0.4.1 → 0.4.2

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 (395) hide show
  1. package/dist/EmptyState.svelte +10 -4
  2. package/dist/FilePicker.svelte +12 -14
  3. package/dist/Icon.svelte +7 -4
  4. package/dist/MillerIndexInput.svelte +2 -2
  5. package/dist/app.css +53 -0
  6. package/dist/brillouin/BrillouinZone.svelte +75 -85
  7. package/dist/brillouin/BrillouinZone.svelte.d.ts +5 -7
  8. package/dist/brillouin/BrillouinZoneControls.svelte +3 -6
  9. package/dist/brillouin/BrillouinZoneExportPane.svelte +18 -14
  10. package/dist/brillouin/BrillouinZoneInfoPane.svelte +6 -10
  11. package/dist/brillouin/BrillouinZoneScene.svelte +26 -39
  12. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  13. package/dist/chempot-diagram/ChemPotDiagram.svelte +33 -38
  14. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +62 -74
  15. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +1 -1
  16. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +511 -547
  17. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +1 -1
  18. package/dist/chempot-diagram/async-compute.svelte.js +1 -1
  19. package/dist/chempot-diagram/controls-state.svelte.js +1 -1
  20. package/dist/colors/index.d.ts +0 -4
  21. package/dist/colors/index.js +19 -20
  22. package/dist/composition/BarChart.svelte +37 -31
  23. package/dist/composition/BubbleChart.svelte +17 -17
  24. package/dist/composition/Composition.svelte +7 -11
  25. package/dist/composition/Formula.svelte +18 -25
  26. package/dist/composition/FormulaFilter.svelte +92 -95
  27. package/dist/composition/PieChart.svelte +55 -41
  28. package/dist/composition/format.js +1 -1
  29. package/dist/composition/index.d.ts +2 -0
  30. package/dist/composition/index.js +14 -0
  31. package/dist/composition/parse.d.ts +2 -2
  32. package/dist/composition/parse.js +11 -10
  33. package/dist/constants.js +11 -9
  34. package/dist/convex-hull/ConvexHull.svelte +12 -15
  35. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  36. package/dist/convex-hull/ConvexHull2D.svelte +93 -92
  37. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  38. package/dist/convex-hull/ConvexHull3D.svelte +109 -140
  39. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  40. package/dist/convex-hull/ConvexHull4D.svelte +75 -94
  41. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  42. package/dist/convex-hull/ConvexHullChrome.svelte +36 -40
  43. package/dist/convex-hull/ConvexHullControls.svelte +46 -48
  44. package/dist/convex-hull/ConvexHullStats.svelte +193 -195
  45. package/dist/convex-hull/ConvexHullTooltip.svelte +5 -12
  46. package/dist/convex-hull/GasPressureControls.svelte +5 -12
  47. package/dist/convex-hull/StructurePopup.svelte +17 -21
  48. package/dist/convex-hull/StructurePopup.svelte.d.ts +2 -0
  49. package/dist/convex-hull/TemperatureSlider.svelte +4 -10
  50. package/dist/convex-hull/canvas-interactions.svelte.js +2 -1
  51. package/dist/convex-hull/helpers.d.ts +8 -10
  52. package/dist/convex-hull/helpers.js +36 -36
  53. package/dist/convex-hull/hull-state.svelte.d.ts +1 -1
  54. package/dist/convex-hull/thermodynamics.js +18 -17
  55. package/dist/coordination/CoordinationBarPlot.svelte +38 -33
  56. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
  57. package/dist/coordination/calc-coordination.d.ts +1 -1
  58. package/dist/coordination/calc-coordination.js +28 -25
  59. package/dist/element/BohrAtom.svelte +5 -8
  60. package/dist/element/ElementHeading.svelte +4 -1
  61. package/dist/element/ElementPhoto.svelte +11 -9
  62. package/dist/element/ElementStats.svelte +12 -10
  63. package/dist/element/ElementTile.svelte +24 -29
  64. package/dist/element/Nucleus.svelte +4 -2
  65. package/dist/element/data.d.ts +1 -2
  66. package/dist/element/helpers.d.ts +4 -0
  67. package/dist/element/helpers.js +18 -0
  68. package/dist/element/index.d.ts +1 -4
  69. package/dist/element/index.js +3 -18
  70. package/dist/feedback/DragOverlay.svelte +5 -5
  71. package/dist/feedback/DragOverlay.svelte.d.ts +2 -2
  72. package/dist/feedback/StatusMessage.svelte +7 -4
  73. package/dist/fermi-surface/FermiSlice.svelte +21 -18
  74. package/dist/fermi-surface/FermiSurface.svelte +14 -22
  75. package/dist/fermi-surface/FermiSurface.svelte.d.ts +5 -7
  76. package/dist/fermi-surface/FermiSurfaceControls.svelte +6 -20
  77. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  78. package/dist/fermi-surface/FermiSurfaceScene.svelte +37 -64
  79. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  80. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +9 -6
  81. package/dist/fermi-surface/compute.js +3 -3
  82. package/dist/fermi-surface/parse.js +22 -24
  83. package/dist/heatmap-matrix/HeatmapMatrix.svelte +109 -148
  84. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +2 -2
  85. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +9 -7
  86. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
  87. package/dist/heatmap-matrix/index.d.ts +5 -0
  88. package/dist/icons.d.ts +4 -0
  89. package/dist/icons.js +4 -0
  90. package/dist/io/ExportPane.svelte +10 -6
  91. package/dist/io/decompress.d.ts +2 -1
  92. package/dist/io/decompress.js +22 -35
  93. package/dist/io/export.d.ts +4 -0
  94. package/dist/io/export.js +5 -1
  95. package/dist/io/fetch.d.ts +0 -3
  96. package/dist/io/fetch.js +0 -11
  97. package/dist/io/index.d.ts +0 -1
  98. package/dist/io/index.js +2 -9
  99. package/dist/io/is-binary.d.ts +9 -0
  100. package/dist/io/is-binary.js +45 -1
  101. package/dist/io/url-drop.js +13 -25
  102. package/dist/isosurface/Isosurface.svelte +17 -23
  103. package/dist/isosurface/IsosurfaceControls.svelte +37 -42
  104. package/dist/isosurface/parse-vaspwave.d.ts +3 -0
  105. package/dist/isosurface/parse-vaspwave.js +138 -0
  106. package/dist/isosurface/parse.js +6 -6
  107. package/dist/labels.d.ts +6 -2
  108. package/dist/labels.js +43 -13
  109. package/dist/layout/FullscreenButton.svelte +2 -1
  110. package/dist/layout/FullscreenToggle.svelte +10 -4
  111. package/dist/layout/InfoCard.svelte +7 -18
  112. package/dist/layout/InfoTag.svelte +1 -3
  113. package/dist/layout/NumberRangeInput.svelte +46 -0
  114. package/dist/layout/NumberRangeInput.svelte.d.ts +12 -0
  115. package/dist/layout/PropertyFilter.svelte +2 -6
  116. package/dist/layout/SettingsSection.svelte +21 -17
  117. package/dist/layout/SubpageGrid.svelte +3 -1
  118. package/dist/layout/ViewerChrome.svelte +9 -10
  119. package/dist/layout/ViewerChrome.svelte.d.ts +2 -1
  120. package/dist/layout/fullscreen.js +3 -5
  121. package/dist/layout/fullscreen.svelte.js +1 -2
  122. package/dist/layout/index.d.ts +1 -0
  123. package/dist/layout/index.js +1 -0
  124. package/dist/layout/json-tree/JsonNode.svelte +8 -11
  125. package/dist/layout/json-tree/JsonTree.svelte +53 -77
  126. package/dist/layout/json-tree/JsonValue.svelte +7 -15
  127. package/dist/layout/json-tree/utils.js +14 -23
  128. package/dist/math.d.ts +2 -0
  129. package/dist/math.js +19 -0
  130. package/dist/overlays/ContextMenu.svelte +4 -2
  131. package/dist/overlays/CopyButton.svelte +11 -5
  132. package/dist/overlays/DragControlTab.svelte +6 -1
  133. package/dist/overlays/DraggablePane.svelte +81 -26
  134. package/dist/overlays/DraggablePane.svelte.d.ts +1 -0
  135. package/dist/overlays/GlassChip.svelte +30 -0
  136. package/dist/overlays/GlassChip.svelte.d.ts +8 -0
  137. package/dist/overlays/InfoPaneCards.svelte +11 -23
  138. package/dist/overlays/index.d.ts +7 -0
  139. package/dist/overlays/index.js +26 -0
  140. package/dist/overlays/portal.d.ts +2 -0
  141. package/dist/overlays/portal.js +14 -0
  142. package/dist/periodic-table/PeriodicTable.svelte +113 -145
  143. package/dist/periodic-table/PeriodicTable.svelte.d.ts +5 -3
  144. package/dist/periodic-table/PeriodicTableControls.svelte +11 -63
  145. package/dist/periodic-table/TableInset.svelte +9 -4
  146. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +50 -71
  147. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -8
  148. package/dist/phase-diagram/PhaseDiagramControls.svelte +70 -94
  149. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  150. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
  151. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +12 -12
  152. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +54 -58
  153. package/dist/phase-diagram/TdbInfoPanel.svelte +21 -15
  154. package/dist/phase-diagram/build-diagram.js +3 -3
  155. package/dist/phase-diagram/diagram-input.d.ts +1 -2
  156. package/dist/phase-diagram/parse.js +15 -15
  157. package/dist/phase-diagram/svg-to-diagram.js +21 -15
  158. package/dist/phase-diagram/utils.js +2 -3
  159. package/dist/plot/bar/BarPlot.svelte +430 -391
  160. package/dist/plot/bar/BarPlot.svelte.d.ts +2 -0
  161. package/dist/plot/bar/BarPlotControls.svelte +1 -3
  162. package/dist/plot/bar/BarPlotControls.svelte.d.ts +4 -6
  163. package/dist/plot/bar/SpacegroupBarPlot.svelte +29 -36
  164. package/dist/plot/bar/data.d.ts +5 -4
  165. package/dist/plot/bar/data.js +2 -1
  166. package/dist/plot/box/BoxPlot.svelte +301 -209
  167. package/dist/plot/box/BoxPlot.svelte.d.ts +2 -0
  168. package/dist/plot/box/Violin.svelte.d.ts +1 -0
  169. package/dist/plot/box/box-plot.js +1 -1
  170. package/dist/plot/core/auto-place.js +1 -1
  171. package/dist/plot/core/axis-utils.d.ts +12 -7
  172. package/dist/plot/core/axis-utils.js +48 -48
  173. package/dist/plot/core/components/AxisLabel.svelte +82 -20
  174. package/dist/plot/core/components/AxisLabel.svelte.d.ts +1 -0
  175. package/dist/plot/core/components/ColorBar.svelte +101 -106
  176. package/dist/plot/core/components/ColorScaleSelect.svelte +1 -1
  177. package/dist/plot/core/components/FillArea.svelte +10 -17
  178. package/dist/plot/core/components/HierarchyControls.svelte +249 -0
  179. package/dist/plot/{sunburst/SunburstControls.svelte.d.ts → core/components/HierarchyControls.svelte.d.ts} +12 -8
  180. package/dist/plot/core/components/InteractiveAxisLabel.svelte +1 -1
  181. package/dist/plot/core/components/InteractiveAxisLabel.svelte.d.ts +1 -1
  182. package/dist/plot/core/components/Line.svelte +18 -9
  183. package/dist/plot/core/components/Line.svelte.d.ts +2 -0
  184. package/dist/plot/core/components/PlotAxis.svelte +40 -17
  185. package/dist/plot/core/components/PlotControls.svelte +65 -41
  186. package/dist/plot/core/components/PlotLegend.svelte +31 -35
  187. package/dist/plot/core/components/PlotMarginals.svelte +616 -0
  188. package/dist/plot/core/components/PlotMarginals.svelte.d.ts +15 -0
  189. package/dist/plot/core/components/PlotTooltip.svelte +1 -1
  190. package/dist/plot/core/components/PortalSelect.svelte +4 -8
  191. package/dist/plot/core/components/ReferenceLine.svelte +27 -24
  192. package/dist/plot/core/components/ReferenceLine3D.svelte +54 -57
  193. package/dist/plot/core/components/ReferencePlane.svelte +7 -8
  194. package/dist/plot/core/components/ZeroLines.svelte +4 -8
  195. package/dist/plot/core/components/ZoomRect.svelte +1 -2
  196. package/dist/plot/core/components/index.d.ts +1 -0
  197. package/dist/plot/core/components/index.js +1 -0
  198. package/dist/plot/core/data-cleaning-signal.d.ts +46 -0
  199. package/dist/plot/core/data-cleaning-signal.js +467 -0
  200. package/dist/plot/core/data-cleaning.d.ts +2 -42
  201. package/dist/plot/core/data-cleaning.js +27 -474
  202. package/dist/plot/core/data-transform.js +9 -5
  203. package/dist/plot/core/fill-utils.d.ts +3 -1
  204. package/dist/plot/core/fill-utils.js +28 -6
  205. package/dist/plot/core/index.d.ts +1 -0
  206. package/dist/plot/core/index.js +1 -0
  207. package/dist/plot/core/interactions.js +0 -2
  208. package/dist/plot/core/layout.d.ts +3 -1
  209. package/dist/plot/core/layout.js +22 -13
  210. package/dist/plot/core/marginals.d.ts +170 -0
  211. package/dist/plot/core/marginals.js +436 -0
  212. package/dist/plot/core/reference-line.js +1 -1
  213. package/dist/plot/core/scales.d.ts +23 -0
  214. package/dist/plot/core/scales.js +39 -13
  215. package/dist/plot/core/types/fills.d.ts +101 -0
  216. package/dist/plot/core/types/fills.js +15 -0
  217. package/dist/plot/core/types/plot-3d.d.ts +87 -0
  218. package/dist/plot/core/types/plot-3d.js +4 -0
  219. package/dist/plot/core/types/reference-lines.d.ts +140 -0
  220. package/dist/plot/core/types/reference-lines.js +9 -0
  221. package/dist/plot/core/types.d.ts +29 -324
  222. package/dist/plot/core/types.js +23 -20
  223. package/dist/plot/core/utils/hierarchy-chart.d.ts +51 -0
  224. package/dist/plot/core/utils/hierarchy-chart.js +179 -0
  225. package/dist/plot/core/utils/hierarchy-labels.d.ts +20 -0
  226. package/dist/plot/core/utils/hierarchy-labels.js +99 -0
  227. package/dist/plot/core/utils/label-placement.js +2 -2
  228. package/dist/plot/histogram/Histogram.svelte +304 -325
  229. package/dist/plot/histogram/Histogram.svelte.d.ts +7 -7
  230. package/dist/plot/histogram/HistogramControls.svelte +30 -28
  231. package/dist/plot/histogram/histogram.d.ts +31 -0
  232. package/dist/plot/histogram/histogram.js +70 -0
  233. package/dist/plot/histogram/index.d.ts +1 -0
  234. package/dist/plot/histogram/index.js +1 -0
  235. package/dist/plot/index.d.ts +1 -0
  236. package/dist/plot/index.js +1 -0
  237. package/dist/plot/sankey/Sankey.svelte +78 -69
  238. package/dist/plot/sankey/Sankey.svelte.d.ts +2 -2
  239. package/dist/plot/sankey/SankeyControls.svelte +22 -16
  240. package/dist/plot/scatter/BinnedScatterPlot.svelte +263 -109
  241. package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +7 -0
  242. package/dist/plot/scatter/ElementScatter.svelte +4 -1
  243. package/dist/plot/scatter/ScatterPlot.svelte +477 -385
  244. package/dist/plot/scatter/ScatterPlot.svelte.d.ts +2 -0
  245. package/dist/plot/scatter/ScatterPlotControls.svelte +18 -19
  246. package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
  247. package/dist/plot/scatter/ScatterPoint.svelte +16 -9
  248. package/dist/plot/scatter/binned-scatter-types.d.ts +3 -2
  249. package/dist/plot/scatter/binned-scatter-types.js +6 -1
  250. package/dist/plot/scatter/scatter-data.d.ts +1 -1
  251. package/dist/plot/scatter/scatter-data.js +8 -7
  252. package/dist/plot/scatter-3d/ScatterPlot3D.svelte +25 -34
  253. package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +2 -2
  254. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +8 -5
  255. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +113 -116
  256. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +2 -2
  257. package/dist/plot/scatter-3d/Surface3D.svelte +4 -13
  258. package/dist/plot/sunburst/Sunburst.svelte +251 -329
  259. package/dist/plot/sunburst/Sunburst.svelte.d.ts +2 -2
  260. package/dist/plot/sunburst/index.d.ts +1 -1
  261. package/dist/plot/sunburst/index.js +2 -1
  262. package/dist/plot/sunburst/render.d.ts +2 -2
  263. package/dist/plot/sunburst/render.js +47 -22
  264. package/dist/plot/sunburst/sunburst.d.ts +5 -1
  265. package/dist/plot/sunburst/sunburst.js +10 -3
  266. package/dist/plot/treemap/Treemap.svelte +907 -0
  267. package/dist/plot/treemap/Treemap.svelte.d.ts +84 -0
  268. package/dist/plot/treemap/index.d.ts +3 -0
  269. package/dist/plot/treemap/index.js +4 -0
  270. package/dist/plot/treemap/treemap.d.ts +26 -0
  271. package/dist/plot/treemap/treemap.js +99 -0
  272. package/dist/rdf/RdfPlot.svelte +22 -26
  273. package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
  274. package/dist/rdf/calc-rdf.js +4 -8
  275. package/dist/sanitize.js +1 -1
  276. package/dist/scene/bind-renderer.svelte.js +14 -6
  277. package/dist/settings.d.ts +19 -9
  278. package/dist/settings.js +77 -50
  279. package/dist/spectral/Bands.svelte +223 -230
  280. package/dist/spectral/BandsAndDos.svelte +11 -9
  281. package/dist/spectral/BrillouinBandsDos.svelte +19 -27
  282. package/dist/spectral/Dos.svelte +70 -73
  283. package/dist/spectral/helpers.d.ts +3 -3
  284. package/dist/spectral/helpers.js +25 -35
  285. package/dist/state.svelte.js +5 -18
  286. package/dist/structure/Arrow.svelte +5 -18
  287. package/dist/structure/ArrowInstances.svelte +160 -0
  288. package/dist/structure/ArrowInstances.svelte.d.ts +16 -0
  289. package/dist/structure/AtomLegend.svelte +115 -75
  290. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  291. package/dist/structure/Bond.svelte +31 -23
  292. package/dist/structure/CanvasTooltip.svelte +15 -3
  293. package/dist/structure/CellSelect.svelte +11 -26
  294. package/dist/structure/Cylinder.svelte +24 -40
  295. package/dist/structure/InstancedAtoms.svelte +114 -0
  296. package/dist/structure/InstancedAtoms.svelte.d.ts +15 -0
  297. package/dist/structure/Lattice.svelte +85 -149
  298. package/dist/structure/SiteLabels.svelte +122 -0
  299. package/dist/structure/SiteLabels.svelte.d.ts +18 -0
  300. package/dist/structure/Structure.svelte +473 -419
  301. package/dist/structure/Structure.svelte.d.ts +4 -2
  302. package/dist/structure/StructureCarousel.svelte +630 -0
  303. package/dist/structure/StructureCarousel.svelte.d.ts +19 -0
  304. package/dist/structure/StructureControls.svelte +157 -399
  305. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  306. package/dist/structure/StructureExportPane.svelte +17 -21
  307. package/dist/structure/StructureInfoPane.svelte +72 -66
  308. package/dist/structure/StructureInfoPane.svelte.d.ts +1 -1
  309. package/dist/structure/StructureScene.svelte +467 -476
  310. package/dist/structure/StructureScene.svelte.d.ts +3 -1
  311. package/dist/structure/StructureViewport.svelte +386 -0
  312. package/dist/structure/StructureViewport.svelte.d.ts +63 -0
  313. package/dist/structure/atom-label-placement.d.ts +16 -12
  314. package/dist/structure/atom-label-placement.js +70 -37
  315. package/dist/structure/atom-properties.d.ts +2 -0
  316. package/dist/structure/atom-properties.js +104 -38
  317. package/dist/structure/bonding.d.ts +10 -3
  318. package/dist/structure/bonding.js +84 -28
  319. package/dist/structure/edit-history.d.ts +6 -0
  320. package/dist/structure/edit-history.js +27 -0
  321. package/dist/structure/export.d.ts +1 -0
  322. package/dist/structure/export.js +39 -31
  323. package/dist/structure/format-detect.d.ts +1 -0
  324. package/dist/structure/format-detect.js +22 -0
  325. package/dist/structure/geometry.d.ts +9 -0
  326. package/dist/structure/geometry.js +27 -0
  327. package/dist/structure/index.d.ts +16 -1
  328. package/dist/structure/index.js +13 -2
  329. package/dist/structure/measure.d.ts +1 -1
  330. package/dist/structure/measure.js +6 -12
  331. package/dist/structure/parse.d.ts +2 -1
  332. package/dist/structure/parse.js +243 -222
  333. package/dist/structure/partial-occupancy.js +4 -2
  334. package/dist/structure/pbc.js +78 -98
  335. package/dist/structure/polyhedra.d.ts +0 -1
  336. package/dist/structure/polyhedra.js +3 -18
  337. package/dist/structure/supercell.d.ts +1 -2
  338. package/dist/structure/supercell.js +7 -13
  339. package/dist/symmetry/SymmetryElements.svelte +11 -15
  340. package/dist/symmetry/SymmetryStats.svelte +56 -68
  341. package/dist/symmetry/WyckoffTable.svelte +5 -24
  342. package/dist/symmetry/cell-transform.js +1 -0
  343. package/dist/symmetry/index.d.ts +1 -0
  344. package/dist/symmetry/index.js +8 -2
  345. package/dist/symmetry/spacegroups.js +3 -2
  346. package/dist/table/HeatmapTable.svelte +1102 -365
  347. package/dist/table/HeatmapTable.svelte.d.ts +12 -2
  348. package/dist/table/ToggleMenu.svelte +5 -12
  349. package/dist/table/index.d.ts +21 -6
  350. package/dist/table/index.js +34 -21
  351. package/dist/theme/ThemeControl.svelte +16 -8
  352. package/dist/theme/index.d.ts +1 -5
  353. package/dist/theme/index.js +4 -10
  354. package/dist/theme/themes.mjs +14 -7
  355. package/dist/tooltip/KCoords.svelte +4 -1
  356. package/dist/tooltip/TooltipContent.svelte +2 -5
  357. package/dist/trajectory/Trajectory.svelte +515 -308
  358. package/dist/trajectory/Trajectory.svelte.d.ts +1 -1
  359. package/dist/trajectory/TrajectoryError.svelte +11 -8
  360. package/dist/trajectory/TrajectoryExportPane.svelte +14 -31
  361. package/dist/trajectory/TrajectoryInfoPane.svelte +85 -97
  362. package/dist/trajectory/extract.js +56 -34
  363. package/dist/trajectory/format-detect.js +11 -9
  364. package/dist/trajectory/frame-reader.d.ts +2 -0
  365. package/dist/trajectory/frame-reader.js +33 -29
  366. package/dist/trajectory/helpers.d.ts +2 -0
  367. package/dist/trajectory/helpers.js +18 -7
  368. package/dist/trajectory/index.d.ts +1 -0
  369. package/dist/trajectory/index.js +3 -0
  370. package/dist/trajectory/parse/h5-utils.d.ts +13 -0
  371. package/dist/trajectory/parse/h5-utils.js +109 -0
  372. package/dist/trajectory/parse/hdf5.d.ts +1 -1
  373. package/dist/trajectory/parse/hdf5.js +116 -106
  374. package/dist/trajectory/parse/index.d.ts +2 -3
  375. package/dist/trajectory/parse/index.js +17 -20
  376. package/dist/trajectory/parse/lammps.js +8 -12
  377. package/dist/trajectory/parse/vasp.js +10 -7
  378. package/dist/trajectory/parse/vaspout-electronic.d.ts +13 -0
  379. package/dist/trajectory/parse/vaspout-electronic.js +207 -0
  380. package/dist/trajectory/parse/vaspout-h5.d.ts +5 -0
  381. package/dist/trajectory/parse/vaspout-h5.js +284 -0
  382. package/dist/trajectory/parse/xyz.js +11 -7
  383. package/dist/trajectory/plotting.d.ts +8 -9
  384. package/dist/trajectory/plotting.js +99 -27
  385. package/dist/utils.d.ts +3 -0
  386. package/dist/utils.js +7 -0
  387. package/dist/xrd/XrdPlot.svelte +47 -61
  388. package/dist/xrd/broadening.js +2 -1
  389. package/dist/xrd/calc-xrd.js +2 -4
  390. package/dist/xrd/parse.d.ts +0 -1
  391. package/dist/xrd/parse.js +56 -73
  392. package/package.json +61 -33
  393. package/dist/api/mp.d.ts +0 -6
  394. package/dist/api/mp.js +0 -22
  395. package/dist/plot/sunburst/SunburstControls.svelte +0 -200
@@ -1,12 +1,21 @@
1
1
  // Utility functions for computing atom properties and applying color scales
2
2
  import { get_d3_interpolator } from '../colors';
3
- import { calc_coordination_nums } from '../coordination';
3
+ import { calc_coordination_nums, } from '../coordination/calc-coordination';
4
4
  import * as math from '../math';
5
- import { wrap_to_unit_cell } from './pbc';
5
+ import { element_lookup, get_majority_element } from './bonding';
6
+ import { wrap_frac_coord } from './pbc';
6
7
  import { rgb } from 'd3-color';
7
8
  import * as d3_sc from 'd3-scale-chromatic';
8
9
  const GRAY = `#808080`;
9
10
  const DEFAULT_COLOR_SCALE = `interpolateViridis`;
11
+ // Cap on periodic image shells per axis when expanding for coordination. Guards
12
+ // against image explosion in very thin / highly oblique cells (coordination is ~O(n²)).
13
+ const MAX_IMAGE_SHELLS = 3;
14
+ // Max bond distance each strategy can form, mirroring the defaults in bonding.ts
15
+ // (electroneg_ratio.max_distance_ratio, solid_angle.max_distance). Used to size PBC
16
+ // image expansion just tightly enough that coordination never misses a bonded neighbor.
17
+ const ELECTRONEG_MAX_RATIO = 2;
18
+ const SOLID_ANGLE_MAX_DIST = 5; // Å
10
19
  export const get_d3_color_scales = () => Object.keys(d3_sc).filter((key) => key.startsWith(`interpolate`));
11
20
  const to_hex = (interp_fn, frac) => rgb(interp_fn(frac)).formatHex();
12
21
  const build_image_site = (site, frac_to_cart, offset, orig_idx) => {
@@ -22,12 +31,6 @@ const build_image_site = (site, frac_to_cart, offset, orig_idx) => {
22
31
  properties: { ...site.properties, orig_site_idx: orig_idx },
23
32
  };
24
33
  };
25
- const get_all_offsets = (pbc) => [-1, 0, 1]
26
- .flatMap((dx) => [-1, 0, 1].flatMap((dy) => [-1, 0, 1].map((dz) => [dx, dy, dz])))
27
- .filter(([dx, dy, dz]) => !(dx === 0 && dy === 0 && dz === 0) &&
28
- (pbc[0] || dx === 0) &&
29
- (pbc[1] || dy === 0) &&
30
- (pbc[2] || dz === 0));
31
34
  const make_categorical = (vals, scale, sort_fn) => {
32
35
  const interp_fn = get_d3_interpolator(scale);
33
36
  const uniq = sort_fn
@@ -75,46 +78,109 @@ export const get_orig_site_idx = (site, site_idx) => typeof site?.properties?.or
75
78
  : typeof site?.properties?.orig_site_idx === `number`
76
79
  ? site.properties.orig_site_idx
77
80
  : site_idx;
78
- // Expand structure with PBC images - use minimal expansion based on atom positions
79
- function expand_structure_for_pbc(structure) {
81
+ // Expand a periodic structure with the neighbor images needed for correct
82
+ // coordination. Each atom's `reach` (the largest bond `strategy` can form for it —
83
+ // electroneg_ratio's (r + r_max)·ratio or solid_angle's flat cap) sizes how many whole
84
+ // cells to image per periodic axis, measured over the perpendicular cell height (not
85
+ // the lattice vector length, so oblique cells work), keeping only images within `reach`
86
+ // of the [0,1] cell and capping at MAX_IMAGE_SHELLS/axis (warns + may undercount beyond
87
+ // that — near-degenerate cells only). Smaller atoms image less; atoms with no covalent
88
+ // radius form no bonds and get no images.
89
+ function expand_structure_for_pbc(structure, strategy) {
80
90
  if (!(`lattice` in structure) || !structure.lattice || structure.sites.length === 0) {
81
91
  return structure;
82
92
  }
83
93
  const { sites, lattice } = structure;
84
- const frac_to_cart = math.create_frac_to_cart(lattice.matrix);
85
94
  const pbc = lattice.pbc ?? [true, true, true];
86
- const all_offsets = get_all_offsets(pbc);
87
- // Small structures: expand all atoms
88
- if (sites.length < 20 || !pbc.some(Boolean)) {
89
- const image_sites = sites.flatMap((site, orig_idx) => all_offsets.map((offset) => build_image_site(site, frac_to_cart, offset, orig_idx)));
90
- return { ...structure, sites: [...sites, ...image_sites] };
91
- }
92
- // Large structures: only expand atoms near boundaries (within 5Å bond distance)
93
- const cutoff = [5.0 / lattice.a, 5.0 / lattice.b, 5.0 / lattice.c];
94
- const image_sites = sites.flatMap((site, orig_idx) => {
95
- const norm = wrap_to_unit_cell(site.abc);
96
- return all_offsets
97
- .filter(([dx, dy, dz]) => (dx === 0 || (dx === -1 ? norm[0] <= cutoff[0] : norm[0] >= 1 - cutoff[0])) &&
98
- (dy === 0 || (dy === -1 ? norm[1] <= cutoff[1] : norm[1] >= 1 - cutoff[1])) &&
99
- (dz === 0 || (dz === -1 ? norm[2] <= cutoff[2] : norm[2] >= 1 - cutoff[2])))
100
- .map((offset) => build_image_site(site, frac_to_cart, offset, orig_idx));
95
+ if (!pbc.some(Boolean))
96
+ return structure;
97
+ const frac_to_cart = math.create_frac_to_cart(lattice.matrix);
98
+ // Wrap into [0,1) along PERIODIC axes only so the near-cell filter and image
99
+ // building share one position (else boundary atoms image on the wrong side and lose
100
+ // neighbors). Vacuum axes keep their real coord — wrapping would fold apart atoms
101
+ // together and invent bonds.
102
+ const cell_sites = sites.map((site) => {
103
+ const abc = site.abc.map((coord, axis) => pbc[axis] ? wrap_frac_coord(coord) : coord);
104
+ return { ...site, abc, xyz: frac_to_cart(abc) };
101
105
  });
102
- return { ...structure, sites: [...sites, ...image_sites] };
106
+ // Covalent radius per atom (0 = unknown → forms no bonds → needs no images)
107
+ const radii = cell_sites.map((site) => {
108
+ const elem = get_majority_element(site);
109
+ return (elem ? element_lookup.get(elem)?.covalent_radius : undefined) ?? 0;
110
+ });
111
+ let max_radius = 0;
112
+ for (const radius of radii)
113
+ if (radius > max_radius)
114
+ max_radius = radius;
115
+ const reach_of = (radius) => strategy === `solid_angle`
116
+ ? SOLID_ANGLE_MAX_DIST
117
+ : (radius + max_radius) * ELECTRONEG_MAX_RATIO;
118
+ const heights = math.cell_heights(lattice.matrix);
119
+ // Axes we image along: periodic and non-degenerate (finite height). Non-live axes
120
+ // (vacuum or degenerate) contribute no images — 0 shells + ∞ cutoff so their only
121
+ // copy (shift 0) always passes the near-cell filter. Loop-invariant, so hoisted out.
122
+ const live_axis = heights.map((height, axis) => pbc[axis] && Number.isFinite(height));
123
+ const image_near_cell = (frac, shift, axis_cutoff) => {
124
+ const shifted = frac + shift;
125
+ return shifted >= -axis_cutoff && shifted <= 1 + axis_cutoff;
126
+ };
127
+ let capped = false;
128
+ const image_sites = [];
129
+ for (const [orig_idx, site] of cell_sites.entries()) {
130
+ const radius = radii[orig_idx];
131
+ if (radius === 0)
132
+ continue; // no covalent radius → no bonds → no images
133
+ const reach = reach_of(radius);
134
+ // `cutoff` = fractional reach for the near-cell filter (keep an image shifted by `s`
135
+ // iff abc+s lands within `cutoff` of [0,1]); `n_shells` bounds the loop so no
136
+ // in-reach copy is missed.
137
+ const cutoff = heights.map((height, axis) => (live_axis[axis] ? reach / height : Infinity));
138
+ const n_shells = heights.map((height, axis) => {
139
+ if (!live_axis[axis])
140
+ return 0;
141
+ const shells = Math.floor(1 + reach / height);
142
+ if (shells > MAX_IMAGE_SHELLS)
143
+ capped = true;
144
+ return Math.min(MAX_IMAGE_SHELLS, shells);
145
+ });
146
+ const [frac_a, frac_b, frac_c] = site.abc; // periodic axes wrapped into [0, 1)
147
+ for (let dx = -n_shells[0]; dx <= n_shells[0]; dx++) {
148
+ if (!image_near_cell(frac_a, dx, cutoff[0]))
149
+ continue;
150
+ for (let dy = -n_shells[1]; dy <= n_shells[1]; dy++) {
151
+ if (!image_near_cell(frac_b, dy, cutoff[1]))
152
+ continue;
153
+ for (let dz = -n_shells[2]; dz <= n_shells[2]; dz++) {
154
+ if (dx === 0 && dy === 0 && dz === 0)
155
+ continue;
156
+ if (!image_near_cell(frac_c, dz, cutoff[2]))
157
+ continue;
158
+ image_sites.push(build_image_site(site, frac_to_cart, [dx, dy, dz], orig_idx));
159
+ }
160
+ }
161
+ }
162
+ }
163
+ if (capped) {
164
+ console.warn(`[coordination] cell is very thin or oblique relative to bond reach; capping ` +
165
+ `PBC images at ${MAX_IMAGE_SHELLS} shells/axis, coordination near cell ` +
166
+ `boundaries may be undercounted`);
167
+ }
168
+ return { ...structure, sites: [...cell_sites, ...image_sites] };
103
169
  }
104
- export function get_coordination_colors(structure, strategy = `electroneg_ratio`, scale = DEFAULT_COLOR_SCALE, type = `continuous`) {
105
- const orig_site_count = structure.sites.length;
106
- // Check if structure has periodic boundary conditions
170
+ // PBC-aware coordination: expand periodic cells with image atoms (so boundary atoms get
171
+ // full coordination), counting only original atoms as centers. Shared by the 3D viewer and
172
+ // CoordinationBarPlot so both report identical numbers.
173
+ export function calc_structure_coordination(structure, strategy = `electroneg_ratio`) {
107
174
  const has_lattice = `lattice` in structure && structure.lattice !== undefined;
108
175
  const pbc = has_lattice ? structure.lattice.pbc : undefined;
109
176
  const has_pbc = has_lattice && (pbc === undefined || pbc.some(Boolean));
110
- // For PBC structures, expand with images from neighboring cells for accurate coordination
111
- const coord_structure = has_pbc ? expand_structure_for_pbc(structure) : structure;
112
- // Calculate coordination numbers on the (potentially expanded) structure
113
- const all_coord_data = calc_coordination_nums(coord_structure, strategy);
114
- // Extract coordination numbers only for the original sites (not image atoms)
115
- const coord_nums = all_coord_data.sites
116
- .slice(0, orig_site_count)
117
- .map((site) => site.coordination_num);
177
+ // Image atoms still count as neighbors but aren't iterated as centers (big speedup)
178
+ const coord_structure = has_pbc ? expand_structure_for_pbc(structure, strategy) : structure;
179
+ return calc_coordination_nums(coord_structure, strategy, structure.sites.length);
180
+ }
181
+ export function get_coordination_colors(structure, strategy = `electroneg_ratio`, scale = DEFAULT_COLOR_SCALE, type = `continuous`) {
182
+ // sites is already limited to the original atoms (calc_coordination_nums center_count)
183
+ const coord_nums = calc_structure_coordination(structure, strategy).sites.map((site) => site.coordination_num);
118
184
  const { colors, unique_values } = apply_color_scale(coord_nums, scale, type);
119
185
  return build_prop_colors(coord_nums, colors, unique_values);
120
186
  }
@@ -1,8 +1,10 @@
1
1
  import type { ElementSymbol } from '../element';
2
2
  import type { Vec3 } from '../math';
3
3
  import type { AnyStructure, BondOrder, BondPair, Site, StructureBond } from './';
4
- export declare const element_lookup: Map<"S" | "K" | "B" | "H" | "He" | "Li" | "Be" | "C" | "N" | "O" | "F" | "Ne" | "Na" | "Mg" | "Al" | "Si" | "P" | "Cl" | "Ar" | "Ca" | "Sc" | "Ti" | "V" | "Cr" | "Mn" | "Fe" | "Co" | "Ni" | "Cu" | "Zn" | "Ga" | "Ge" | "As" | "Se" | "Br" | "Kr" | "Rb" | "Sr" | "Y" | "Zr" | "Nb" | "Mo" | "Tc" | "Ru" | "Rh" | "Pd" | "Ag" | "Cd" | "In" | "Sn" | "Sb" | "Te" | "I" | "Xe" | "Cs" | "Ba" | "La" | "Ce" | "Pr" | "Nd" | "Pm" | "Sm" | "Eu" | "Gd" | "Tb" | "Dy" | "Ho" | "Er" | "Tm" | "Yb" | "Lu" | "Hf" | "Ta" | "W" | "Re" | "Os" | "Ir" | "Pt" | "Au" | "Hg" | "Tl" | "Pb" | "Bi" | "Po" | "At" | "Rn" | "Fr" | "Ra" | "Ac" | "Th" | "Pa" | "U" | "Np" | "Pu" | "Am" | "Cm" | "Bk" | "Cf" | "Es" | "Fm" | "Md" | "No" | "Lr" | "Rf" | "Db" | "Sg" | "Bh" | "Hs" | "Mt" | "Ds" | "Rg" | "Cn" | "Nh" | "Fl" | "Mc" | "Lv" | "Ts" | "Og", import("../element").ChemicalElement>;
4
+ export declare const element_lookup: Map<"Ac" | "Ag" | "Al" | "Am" | "Ar" | "As" | "At" | "Au" | "B" | "Ba" | "Be" | "Bh" | "Bi" | "Bk" | "Br" | "C" | "Ca" | "Cd" | "Ce" | "Cf" | "Cl" | "Cm" | "Co" | "Cr" | "Cs" | "Cu" | "Db" | "Dy" | "Er" | "Es" | "Eu" | "F" | "Fe" | "Fm" | "Fr" | "Ga" | "Gd" | "Ge" | "H" | "He" | "Hf" | "Hg" | "Ho" | "Hs" | "I" | "In" | "Ir" | "K" | "Kr" | "La" | "Li" | "Lr" | "Lu" | "Md" | "Mg" | "Mn" | "Mo" | "Mt" | "N" | "Na" | "Nb" | "Nd" | "Ne" | "Ni" | "No" | "Np" | "O" | "Os" | "P" | "Pa" | "Pb" | "Pd" | "Pm" | "Po" | "Pr" | "Pt" | "Pu" | "Ra" | "Rb" | "Re" | "Rf" | "Rh" | "Rn" | "Ru" | "S" | "Sb" | "Sc" | "Se" | "Sg" | "Si" | "Sm" | "Sn" | "Sr" | "Ta" | "Tb" | "Tc" | "Te" | "Th" | "Ti" | "Tl" | "Tm" | "U" | "V" | "W" | "Xe" | "Y" | "Yb" | "Zn" | "Zr" | "Ds" | "Rg" | "Cn" | "Nh" | "Fl" | "Mc" | "Lv" | "Ts" | "Og", import("../element").ChemicalElement>;
5
5
  export declare const get_majority_element: (site: Site | undefined) => ElementSymbol | null;
6
+ export declare const is_spectator_center: (element: string) => boolean;
7
+ export declare function has_framework_potential(elements: Iterable<string>): boolean;
6
8
  export declare const normalize_structure_bond: (site_idx_1: number, site_idx_2: number, order: BondOrder, cell_shift?: Vec3) => StructureBond;
7
9
  export declare const get_bond_key: (idx_1: number, idx_2: number, cell_shift?: Vec3) => string;
8
10
  export declare function remap_bonds_after_deletion(bonds: readonly StructureBond[], deleted_indices: ReadonlySet<number>): StructureBond[];
@@ -39,12 +41,14 @@ export declare function apply_explicit_bond_metadata(structure: AnyStructure, bo
39
41
  export declare function scale_and_offset_bond_matrix(transform_matrix: Float32Array, offset: number, radius_scale: number): Float32Array;
40
42
  export declare function get_bond_render_matrices(bond: BondPair, bond_thickness: number): Float32Array[];
41
43
  export declare function compute_bond_transform(pos_1: Vec3, pos_2: Vec3): Float32Array;
44
+ export declare const pack_cell_key: (x: number, y: number, z: number) => number;
42
45
  export declare const BONDING_STRATEGIES: {
43
46
  readonly electroneg_ratio: typeof electroneg_ratio;
44
47
  readonly solid_angle: typeof solid_angle;
45
48
  };
46
49
  export type BondingStrategy = keyof typeof BONDING_STRATEGIES;
47
50
  export type BondingAlgo = (typeof BONDING_STRATEGIES)[BondingStrategy];
51
+ export declare function compute_bonds(structure: AnyStructure, strategy: BondingStrategy, options?: Record<string, unknown>): BondPair[];
48
52
  export declare function electroneg_ratio(structure: AnyStructure, { electronegativity_threshold, // Max electronegativity difference for bonding
49
53
  max_distance_ratio, // Max distance as multiple of sum of covalent radii
50
54
  min_bond_dist, // Minimum bond distance in Angstroms
@@ -52,7 +56,8 @@ metal_metal_penalty, // Strength penalty for metal-metal bonds
52
56
  metal_nonmetal_bonus, // Strength bonus for metal-nonmetal bonds
53
57
  similar_electronegativity_bonus, // Bonus for similar electronegativity
54
58
  same_species_penalty, // Penalty for bonds between same element
55
- strength_threshold, }?: {
59
+ strength_threshold, // Minimum bond strength to include in results
60
+ center_count, }?: {
56
61
  electronegativity_threshold?: number | undefined;
57
62
  max_distance_ratio?: number | undefined;
58
63
  min_bond_dist?: number | undefined;
@@ -61,12 +66,14 @@ strength_threshold, }?: {
61
66
  similar_electronegativity_bonus?: number | undefined;
62
67
  same_species_penalty?: number | undefined;
63
68
  strength_threshold?: number | undefined;
69
+ center_count?: number | undefined;
64
70
  }): BondPair[];
65
- export declare function solid_angle(structure: AnyStructure, { min_solid_angle, min_face_area, max_distance, min_bond_dist, strength_threshold, }?: {
71
+ export declare function solid_angle(structure: AnyStructure, { min_solid_angle, min_face_area, max_distance, min_bond_dist, strength_threshold, center_count, }?: {
66
72
  min_solid_angle?: number | undefined;
67
73
  min_face_area?: number | undefined;
68
74
  max_distance?: number | undefined;
69
75
  min_bond_dist?: number | undefined;
70
76
  strength_threshold?: number | undefined;
77
+ center_count?: number | undefined;
71
78
  }): BondPair[];
72
79
  export {};
@@ -1,5 +1,5 @@
1
1
  // Bonding algorithms for structure visualization
2
- import { element_data } from '../element';
2
+ import { default as element_data } from '../element/data';
3
3
  import * as math from '../math';
4
4
  // Shared per-symbol element data lookup (also used by pbc.ts and polyhedra.ts)
5
5
  export const element_lookup = new Map(element_data.map((el) => [el.symbol, el]));
@@ -10,6 +10,39 @@ export const get_majority_element = (site) => {
10
10
  return null;
11
11
  return site.species.reduce((max, spec) => (spec.occu > max.occu ? spec : max)).element;
12
12
  };
13
+ // Large low-valent A-site cations whose coordination polyhedra (CN 8-12) tend to
14
+ // obscure the structural framework. VESTA-style figures draw the framework
15
+ // (e.g. TiO6 in BaTiO3, FeO6/PO4 in LiFePO4) and leave these as plain spheres.
16
+ // They still get polyhedra when they are the only qualifying cations (e.g. NaCl)
17
+ // or when force-included via `included_center_elements`. Shared by polyhedra.ts
18
+ // (vertex/center selection) and pbc.ts (phase-2 boundary completion) so the bond
19
+ // graph and the polyhedra it feeds stay consistent.
20
+ const SPECTATOR_CATEGORIES = new Set([`alkali metal`]);
21
+ const HEAVY_ALKALINE_EARTHS = new Set([`Ca`, `Sr`, `Ba`, `Ra`]);
22
+ export const is_spectator_center = (element) => SPECTATOR_CATEGORIES.has(element_lookup.get(element)?.category ?? ``) ||
23
+ HEAVY_ALKALINE_EARTHS.has(element);
24
+ // True if the composition contains a framework cation: a non-spectator element
25
+ // strictly less electronegative than the most electronegative element present
26
+ // (i.e. one that can coordinate the anions). When true, spectator A-site cations
27
+ // are hidden from coordination polyhedra (compute_polyhedra) and skipped by phase-2
28
+ // boundary completion (find_image_atoms) - sharing this keeps the bond graph and
29
+ // the polyhedra it feeds consistent. Purely ionic binaries (NaCl, Li2O) return
30
+ // false, so the spectator IS the framework and keeps its polyhedra/completions.
31
+ export function has_framework_potential(elements) {
32
+ const els = [...new Set(elements)]; // dedupe so callers can pass per-site element lists
33
+ let max_en = -Infinity;
34
+ for (const el of els) {
35
+ const en = element_lookup.get(el)?.electronegativity;
36
+ if (en != null && en > max_en)
37
+ max_en = en;
38
+ }
39
+ return els.some((el) => {
40
+ if (is_spectator_center(el))
41
+ return false;
42
+ const en = element_lookup.get(el)?.electronegativity;
43
+ return en != null && en < max_en;
44
+ });
45
+ }
13
46
  const is_zero_cell_shift = (cell_shift) => cell_shift === undefined || cell_shift.every((val) => val === 0);
14
47
  const format_cell_shift = (cell_shift) => {
15
48
  if (cell_shift === undefined || is_zero_cell_shift(cell_shift))
@@ -501,11 +534,22 @@ export function compute_bond_transform(pos_1, pos_2) {
501
534
  1,
502
535
  ]);
503
536
  }
537
+ // Build a BondPair between two sites (shared by electroneg_ratio and solid_angle)
538
+ const make_bond = (sites, idx_1, idx_2, bond_length, strength) => ({
539
+ pos_1: sites[idx_1].xyz,
540
+ pos_2: sites[idx_2].xyz,
541
+ site_idx_1: idx_1,
542
+ site_idx_2: idx_2,
543
+ bond_length,
544
+ strength,
545
+ transform_matrix: compute_bond_transform(sites[idx_1].xyz, sites[idx_2].xyz),
546
+ });
504
547
  // Pack quantized cell coordinates into one integer key (exact for cell coords in
505
548
  // [-512, 511], i.e. structures up to ~1000 cells per axis - far beyond any real
506
549
  // case). Integer Map keys avoid per-lookup string building in the hot pair loop.
550
+ // Also used by pbc.ts for its phase-2 boundary-completion grid.
507
551
  const CELL_OFFSET = 512;
508
- const pack_cell_key = (x, y, z) => (x + CELL_OFFSET) * 1048576 + (y + CELL_OFFSET) * 1024 + (z + CELL_OFFSET);
552
+ export const pack_cell_key = (x, y, z) => (x + CELL_OFFSET) * 1048576 + (y + CELL_OFFSET) * 1024 + (z + CELL_OFFSET);
509
553
  // Build spatial grid by dividing 3D space into cubic cells.
510
554
  function build_spatial_grid(sites, cell_size) {
511
555
  const grid = new Map();
@@ -520,24 +564,28 @@ function build_spatial_grid(sites, cell_size) {
520
564
  return grid;
521
565
  }
522
566
  // Get all site indices in 3x3x3 cube of cells around position.
567
+ // Fills and returns a REUSED module-level array (valid until the next call):
568
+ // this runs once per bond center, so per-call allocations would dominate GC
569
+ // pressure when computing bonds for large supercells.
570
+ const scratch_neighbors = [];
523
571
  function get_neighbors_from_grid(pos, grid, cell_size) {
524
572
  const [cx, cy, cz] = [
525
573
  Math.floor(pos[0] / cell_size),
526
574
  Math.floor(pos[1] / cell_size),
527
575
  Math.floor(pos[2] / cell_size),
528
576
  ];
529
- const neighbors = [];
577
+ scratch_neighbors.length = 0;
530
578
  for (let dx = -1; dx <= 1; dx++) {
531
579
  for (let dy = -1; dy <= 1; dy++) {
532
580
  for (let dz = -1; dz <= 1; dz++) {
533
581
  const cell = grid.get(pack_cell_key(cx + dx, cy + dy, cz + dz));
534
582
  if (cell)
535
- for (const idx of cell)
536
- neighbors.push(idx);
583
+ for (const site_idx of cell)
584
+ scratch_neighbors.push(site_idx);
537
585
  }
538
586
  }
539
587
  }
540
- return neighbors;
588
+ return scratch_neighbors;
541
589
  }
542
590
  // Setup spatial decomposition for structures with >50 atoms.
543
591
  function setup_spatial_grid(sites, cutoff) {
@@ -549,6 +597,23 @@ const get_candidates = (pos, sites, spatial) => spatial
549
597
  ? get_neighbors_from_grid(pos, spatial.grid, spatial.cell_size)
550
598
  : Array.from({ length: sites.length }, (_, idx) => idx);
551
599
  export const BONDING_STRATEGIES = { electroneg_ratio, solid_angle };
600
+ // Memo for the costly neighbor search: WeakMap keyed by structure (GC'd with it), each holding a
601
+ // per-signature (strategy + JSON options) map of results. The multi-side view's 4 panes share one
602
+ // search (identical inputs, one flush); the per-signature map also lets alternating
603
+ // strategies/options on the same structure reuse earlier results instead of thrashing one slot.
604
+ const bond_memo = new WeakMap();
605
+ export function compute_bonds(structure, strategy, options = {}) {
606
+ const sig = `${strategy}:${JSON.stringify(options)}`;
607
+ let by_sig = bond_memo.get(structure);
608
+ const cached = by_sig?.get(sig);
609
+ if (cached)
610
+ return cached;
611
+ const bonds = BONDING_STRATEGIES[strategy](structure, options);
612
+ if (!by_sig)
613
+ bond_memo.set(structure, (by_sig = new Map()));
614
+ by_sig.set(sig, bonds);
615
+ return bonds;
616
+ }
552
617
  // Electronegativity-based bonding with chemical preferences.
553
618
  // This algorithm considers electronegativity differences between atoms, metal/nonmetal
554
619
  // properties, and distance to determine bond strength. Bonds are only created if the
@@ -561,7 +626,11 @@ metal_nonmetal_bonus = 1.5, // Strength bonus for metal-nonmetal bonds
561
626
  similar_electronegativity_bonus = 1.2, // Bonus for similar electronegativity
562
627
  same_species_penalty = 0.5, // Penalty for bonds between same element
563
628
  strength_threshold = 0.3, // Minimum bond strength to include in results
564
- } = {}) {
629
+ // Only iterate the first `center_count` sites as bond centers (default: all).
630
+ // Coordination coloring sets this to the original-atom count so appended PBC image
631
+ // atoms are used as neighbors but not iterated as centers — identical coordination
632
+ // for the originals (by translational symmetry), far cheaper when images dominate.
633
+ center_count = Infinity, } = {}) {
565
634
  const { sites } = structure;
566
635
  if (sites.length < 2)
567
636
  return [];
@@ -571,6 +640,7 @@ strength_threshold = 0.3, // Minimum bond strength to include in results
571
640
  // millions of candidate pairs in large supercells, so object property chains
572
641
  // and Map lookups are replaced with indexed array reads.
573
642
  const n_sites = sites.length;
643
+ const n_center = Math.min(center_count, n_sites - 1);
574
644
  const electronegs = new Float64Array(n_sites);
575
645
  const radii = new Float64Array(n_sites); // 0 = no covalent radius known
576
646
  const metal_flags = new Uint8Array(n_sites);
@@ -608,7 +678,7 @@ strength_threshold = 0.3, // Minimum bond strength to include in results
608
678
  const max_cutoff = max_radius * 2 * max_distance_ratio;
609
679
  const spatial = setup_spatial_grid(sites, max_cutoff);
610
680
  const potential_bonds = [];
611
- for (let idx_a = 0; idx_a < sites.length - 1; idx_a++) {
681
+ for (let idx_a = 0; idx_a < n_center; idx_a++) {
612
682
  const radius_a = radii[idx_a];
613
683
  if (radius_a === 0)
614
684
  continue; // no covalent radius -> no pairs (symmetric: idx_b skips too)
@@ -700,15 +770,7 @@ strength_threshold = 0.3, // Minimum bond strength to include in results
700
770
  strength *= Math.exp(-(norm_dist / closest_dist_b - 1) / 0.5);
701
771
  }
702
772
  if (strength > strength_threshold) {
703
- bonds.push({
704
- pos_1: sites[site_idx_1].xyz,
705
- pos_2: sites[site_idx_2].xyz,
706
- site_idx_1,
707
- site_idx_2,
708
- bond_length: dist,
709
- strength,
710
- transform_matrix: compute_bond_transform(sites[site_idx_1].xyz, sites[site_idx_2].xyz),
711
- });
773
+ bonds.push(make_bond(sites, site_idx_1, site_idx_2, dist, strength));
712
774
  }
713
775
  }
714
776
  return apply_explicit_bond_metadata(structure, bonds);
@@ -718,7 +780,8 @@ strength_threshold = 0.3, // Minimum bond strength to include in results
718
780
  // This algorithm computes bond strength based on the solid angle subtended by atoms
719
781
  // and their distance penalty. Bonds are only created if the computed strength exceeds
720
782
  // the strength_threshold parameter.
721
- export function solid_angle(structure, { min_solid_angle = 0.01, min_face_area = 0.05, max_distance = 5.0, min_bond_dist = 0.4, strength_threshold = 0.05, } = {}) {
783
+ export function solid_angle(structure, { min_solid_angle = 0.01, min_face_area = 0.05, max_distance = 5.0, min_bond_dist = 0.4, strength_threshold = 0.05, center_count = Infinity, // see electroneg_ratio: limit bond centers to first N sites
784
+ } = {}) {
722
785
  const { sites } = structure;
723
786
  if (sites.length < 2)
724
787
  return [];
@@ -726,7 +789,8 @@ export function solid_angle(structure, { min_solid_angle = 0.01, min_face_area =
726
789
  const min_dist_sq = min_bond_dist ** 2;
727
790
  const max_dist_sq = max_distance ** 2;
728
791
  const spatial = setup_spatial_grid(sites, max_distance);
729
- for (let idx_a = 0; idx_a < sites.length - 1; idx_a++) {
792
+ const n_center = Math.min(center_count, sites.length - 1);
793
+ for (let idx_a = 0; idx_a < n_center; idx_a++) {
730
794
  const [x1, y1, z1] = sites[idx_a].xyz;
731
795
  const elem_a = get_majority_element(sites[idx_a]);
732
796
  const radius_a = elem_a ? covalent_radii.get(elem_a) : undefined;
@@ -751,15 +815,7 @@ export function solid_angle(structure, { min_solid_angle = 0.01, min_face_area =
751
815
  const angle_strength = Math.min(bond_solid_angle / (4.0 * Math.PI), 1.0);
752
816
  const strength = angle_strength * dist_penalty;
753
817
  if (strength > strength_threshold) {
754
- bonds.push({
755
- pos_1: sites[idx_a].xyz,
756
- pos_2: sites[idx_b].xyz,
757
- site_idx_1: idx_a,
758
- site_idx_2: idx_b,
759
- bond_length: dist,
760
- strength,
761
- transform_matrix: compute_bond_transform(sites[idx_a].xyz, sites[idx_b].xyz),
762
- });
818
+ bonds.push(make_bond(sites, idx_a, idx_b, dist, strength));
763
819
  }
764
820
  }
765
821
  }
@@ -0,0 +1,6 @@
1
+ export type HistoryStacks<T> = [undo_stack: T[], redo_stack: T[]];
2
+ export declare function push_edit<T>([undo_stack]: HistoryStacks<T>, snapshot: T, max_history: number): HistoryStacks<T>;
3
+ export declare function step_history<T>([undo_stack, redo_stack]: HistoryStacks<T>, direction: `undo` | `redo`, current: T): {
4
+ stacks: HistoryStacks<T>;
5
+ restored: T;
6
+ } | null;
@@ -0,0 +1,27 @@
1
+ // Pure undo/redo stack mechanics for Structure.svelte's edit-atoms history.
2
+ // Stacks are treated as immutable (updates return new arrays) so they work with
3
+ // $state.raw holders where only reassignment is reactive. Kept free of
4
+ // component state so the real logic (not a test-local mirror) is unit-testable.
5
+ // Record a new edit: push `snapshot` onto the undo stack (trimming to
6
+ // `max_history` entries) and invalidate the redo stack.
7
+ export function push_edit([undo_stack], snapshot, max_history) {
8
+ const trimmed = undo_stack.length >= max_history
9
+ ? undo_stack.slice(undo_stack.length - max_history + 1)
10
+ : undo_stack;
11
+ return [[...trimmed, snapshot], []];
12
+ }
13
+ // Step the history in `direction`: pop that stack's top as the state to
14
+ // restore, pushing `current` onto the opposite stack. Returns null when there
15
+ // is nothing to step to (callers should snapshot `current` lazily by checking
16
+ // stack length first if snapshots are expensive).
17
+ export function step_history([undo_stack, redo_stack], direction, current) {
18
+ const source = direction === `undo` ? undo_stack : redo_stack;
19
+ const restored = source.at(-1);
20
+ if (restored === undefined)
21
+ return null;
22
+ const remaining = source.slice(0, -1);
23
+ const stacks = direction === `undo`
24
+ ? [remaining, [...redo_stack, current]]
25
+ : [[...undo_stack, current], remaining];
26
+ return { stacks, restored };
27
+ }
@@ -7,6 +7,7 @@ export declare function has_color_property(mat: Material): mat is Material & {
7
7
  export declare function extract_bond_color_for_instance(geometry: BufferGeometry, instance_idx: number): Color | null;
8
8
  export declare function clean_geometry_for_export(geometry: BufferGeometry): void;
9
9
  export declare function generate_mtl_content(scene: Scene): string;
10
+ export declare function convert_instanced_meshes_to_regular(scene: Scene): Scene;
10
11
  export declare function create_structure_filename(structure: AnyStructure | undefined, extension: string): string;
11
12
  export declare function structure_to_xyz_str(structure?: AnyStructure): string;
12
13
  export declare function structure_to_cif_str(structure?: AnyStructure): string;
@@ -123,9 +123,10 @@ export function generate_mtl_content(scene) {
123
123
  // Get diffuse color (main color)
124
124
  if (has_color_property(mat)) {
125
125
  const color = mat.color;
126
- lines.push(`Kd ${color.r.toFixed(6)} ${color.g.toFixed(6)} ${color.b.toFixed(6)}`);
126
+ const fmt = (val) => val.toFixed(6);
127
+ lines.push(`Kd ${fmt(color.r)} ${fmt(color.g)} ${fmt(color.b)}`);
127
128
  // Ambient is typically a fraction of diffuse
128
- lines.push(`Ka ${(color.r * 0.2).toFixed(6)} ${(color.g * 0.2).toFixed(6)} ${(color.b * 0.2).toFixed(6)}`);
129
+ lines.push(`Ka ${fmt(color.r * 0.2)} ${fmt(color.g * 0.2)} ${fmt(color.b * 0.2)}`);
129
130
  }
130
131
  else {
131
132
  // Default white if no color
@@ -157,7 +158,28 @@ function extract_material_color(mat) {
157
158
  // Note: Threlte's InstancedMesh sets isInstancedMesh=true but type remains "Mesh"
158
159
  // Type guard for InstancedMesh (Three.js uses isInstancedMesh property, not exposed in Object3D type)
159
160
  const is_instanced_mesh = (obj) => obj.isInstancedMesh || obj.type === `InstancedMesh`;
160
- function convert_instanced_meshes_to_regular(scene) {
161
+ // Resolve the export color for one instance of an InstancedMesh. Precedence:
162
+ // gradient bond midpoints (shader bonds), then per-instance colors on meshes
163
+ // flagged userData.per_instance_color (atoms/arrows keep a white base material
164
+ // and store real colors in the instanceColor buffer), then the shared material
165
+ // color (legacy threlte-instanced meshes carry an all-white instanceColor that
166
+ // must NOT win), then instanceColor as a last resort. null = keep default.
167
+ function resolve_instance_material_color(instanced_mesh, instance_idx, bond_colors, stored_color) {
168
+ const bond_color = bond_colors?.get(instance_idx);
169
+ if (bond_color)
170
+ return bond_color;
171
+ const { instanceColor } = instanced_mesh;
172
+ const per_instance = instanceColor
173
+ ? new Color(instanceColor.getX(instance_idx), instanceColor.getY(instance_idx), instanceColor.getZ(instance_idx))
174
+ : null;
175
+ if (instanced_mesh.userData.per_instance_color && per_instance)
176
+ return per_instance;
177
+ if (stored_color)
178
+ return new Color(stored_color.r, stored_color.g, stored_color.b);
179
+ return per_instance;
180
+ }
181
+ // @internal exported only for tests - not part of the public API.
182
+ export function convert_instanced_meshes_to_regular(scene) {
161
183
  // STEP 1: Collect material colors from ORIGINAL scene BEFORE cloning
162
184
  // This is crucial because scene.clone() may not properly preserve Threlte's material colors
163
185
  const material_colors = new Map();
@@ -244,24 +266,10 @@ function convert_instanced_meshes_to_regular(scene) {
244
266
  roughness: 0.5,
245
267
  });
246
268
  new_material.name = `material_${mesh_idx}_${idx}`;
247
- // Apply the correct color
248
- const bond_color = stored_bond_colors?.get(idx);
249
- if (bond_color) {
250
- // Bond with gradient color - use stored midpoint color
251
- new_material.color.copy(bond_color);
252
- }
253
- else if (stored_color) {
254
- // Atom with shared material color
255
- new_material.color.setRGB(stored_color.r, stored_color.g, stored_color.b);
256
- }
257
- else if (instanced_mesh.instanceColor) {
258
- // Fallback: per-instance colors from instanceColor attribute
259
- const color_r = instanced_mesh.instanceColor.getX(idx);
260
- const color_g = instanced_mesh.instanceColor.getY(idx);
261
- const color_b = instanced_mesh.instanceColor.getZ(idx);
262
- new_material.color.setRGB(color_r, color_g, color_b);
263
- }
264
- // If no color found, material stays default white
269
+ // Apply the correct color (null = no color found, material stays default white)
270
+ const resolved_color = resolve_instance_material_color(instanced_mesh, idx, stored_bond_colors, stored_color);
271
+ if (resolved_color)
272
+ new_material.color.copy(resolved_color);
265
273
  const mesh = new Mesh(cloned_geometry, new_material);
266
274
  // Combine base transform with instance transform
267
275
  const combined_matrix = new Matrix4();
@@ -372,9 +380,12 @@ export function structure_to_xyz_str(structure) {
372
380
  const formula = get_electro_neg_formula(structure, true);
373
381
  if (formula && formula !== `Unknown`)
374
382
  comment_parts.push(formula);
383
+ const lattice_matrix = `lattice` in structure && structure.lattice?.matrix?.length === 3
384
+ ? structure.lattice.matrix
385
+ : null;
375
386
  // Include extended XYZ lattice information when available so round-trips preserve lattice
376
- if (`lattice` in structure && structure.lattice?.matrix?.length === 3) {
377
- const lattice_values = structure.lattice.matrix
387
+ if (lattice_matrix) {
388
+ const lattice_values = lattice_matrix
378
389
  .flatMap((row) => row.map((value) => (Number.isFinite(value) ? value : 0).toFixed(8)))
379
390
  .join(` `);
380
391
  comment_parts.push(`Lattice="${lattice_values}"`);
@@ -382,9 +393,7 @@ export function structure_to_xyz_str(structure) {
382
393
  const comment = comment_parts.length > 0 ? comment_parts.join(` `) : `Generated from structure`;
383
394
  lines.push(comment);
384
395
  // Cache converter for fractional→Cartesian (if lattice available)
385
- const frac_to_cart = `lattice` in structure && structure.lattice?.matrix?.length === 3
386
- ? math.create_frac_to_cart(structure.lattice.matrix)
387
- : null;
396
+ const frac_to_cart = lattice_matrix ? math.create_frac_to_cart(lattice_matrix) : null;
388
397
  // Atom lines: element symbol followed by x, y, z coordinates
389
398
  for (const site of structure.sites) {
390
399
  const element_symbol = site_element(site);
@@ -536,11 +545,10 @@ export function structure_to_poscar_str(structure) {
536
545
  lines.push(`1.0`); // Scale factor (1.0 for direct coordinates)
537
546
  const lattice = structure.lattice;
538
547
  if (lattice.matrix && Array.isArray(lattice.matrix) && lattice.matrix.length >= 3) {
539
- // Convert 3x3 matrix to 3 vectors
540
- const matrix = lattice.matrix;
541
- lines.push(`${matrix[0][0].toFixed(8)} ${matrix[0][1].toFixed(8)} ${matrix[0][2].toFixed(8)}`);
542
- lines.push(`${matrix[1][0].toFixed(8)} ${matrix[1][1].toFixed(8)} ${matrix[1][2].toFixed(8)}`);
543
- lines.push(`${matrix[2][0].toFixed(8)} ${matrix[2][1].toFixed(8)} ${matrix[2][2].toFixed(8)}`);
548
+ // One line per lattice vector, exactly 3 components at 8-decimal fixed precision
549
+ for (const vec of lattice.matrix.slice(0, 3)) {
550
+ lines.push([vec[0], vec[1], vec[2]].map((coord) => coord.toFixed(8)).join(` `));
551
+ }
544
552
  }
545
553
  else {
546
554
  throw new Error(`No valid lattice matrix for POSCAR export`);
@@ -0,0 +1 @@
1
+ export declare function is_structure_file(filename: string): boolean;
@@ -0,0 +1,22 @@
1
+ import { CONFIG_DIRS_REGEX, STRUCT_KEYWORDS_REGEX, STRUCT_KEYWORDS_STRICT_REGEX, STRUCTURE_EXTENSIONS_REGEX, TRAJ_KEYWORDS_REGEX, VASP_FILES_REGEX, } from '../constants';
2
+ import { strip_compression_extensions } from '../io/decompress';
3
+ // Filename-only detection lives apart from the parsers so lightweight callers
4
+ // (desktop recents, file pickers) do not load YAML, structure math, and element
5
+ // data just to choose an icon.
6
+ export function is_structure_file(filename) {
7
+ const name = strip_compression_extensions(filename);
8
+ if (/\.(?:traj|xtc|h5|hdf5)$/i.test(name) || /xdatcar/i.test(name))
9
+ return false;
10
+ if (STRUCTURE_EXTENSIONS_REGEX.test(name) || VASP_FILES_REGEX.test(name))
11
+ return true;
12
+ if (/\.(?:xyz|extxyz)$/i.test(name))
13
+ return !TRAJ_KEYWORDS_REGEX.test(name);
14
+ if (/\.(?:yaml|yml|xml)$/i.test(name) && STRUCT_KEYWORDS_REGEX.test(name))
15
+ return true;
16
+ if (/\.json$/i.test(name) &&
17
+ STRUCT_KEYWORDS_STRICT_REGEX.test(name) &&
18
+ !TRAJ_KEYWORDS_REGEX.test(name) &&
19
+ !CONFIG_DIRS_REGEX.test(name))
20
+ return true;
21
+ return false;
22
+ }