matterviz 0.3.7 → 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 (324) hide show
  1. package/dist/Icon.svelte +7 -4
  2. package/dist/MillerIndexInput.svelte +1 -1
  3. package/dist/api/optimade.js +32 -26
  4. package/dist/app.css +0 -3
  5. package/dist/brillouin/BrillouinZone.svelte +8 -3
  6. package/dist/brillouin/BrillouinZone.svelte.d.ts +2 -1
  7. package/dist/brillouin/BrillouinZoneScene.svelte +52 -6
  8. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -0
  9. package/dist/brillouin/BrillouinZoneTooltip.svelte +16 -25
  10. package/dist/brillouin/compute.js +10 -14
  11. package/dist/chempot-diagram/ChemPotDiagram.svelte +14 -13
  12. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +12 -15
  13. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +8 -10
  14. package/dist/chempot-diagram/async-compute.svelte.js +3 -1
  15. package/dist/chempot-diagram/chempot-worker.js +2 -1
  16. package/dist/chempot-diagram/compute.d.ts +1 -1
  17. package/dist/chempot-diagram/compute.js +17 -19
  18. package/dist/colors/index.js +6 -5
  19. package/dist/composition/FormulaFilter.svelte +12 -6
  20. package/dist/composition/PieChart.svelte +6 -5
  21. package/dist/composition/chem-sys.d.ts +8 -0
  22. package/dist/composition/chem-sys.js +85 -0
  23. package/dist/composition/format.js +4 -2
  24. package/dist/composition/index.d.ts +1 -0
  25. package/dist/composition/index.js +1 -0
  26. package/dist/composition/parse.js +25 -13
  27. package/dist/convex-hull/ConvexHull2D.svelte +12 -10
  28. package/dist/convex-hull/ConvexHull3D.svelte +5 -5
  29. package/dist/convex-hull/ConvexHull4D.svelte +5 -9
  30. package/dist/convex-hull/ConvexHullStats.svelte +12 -12
  31. package/dist/convex-hull/GasPressureControls.svelte +4 -4
  32. package/dist/convex-hull/TemperatureSlider.svelte +2 -2
  33. package/dist/convex-hull/demo-temperature.d.ts +1 -1
  34. package/dist/convex-hull/demo-temperature.js +20 -22
  35. package/dist/convex-hull/gas-thermodynamics.d.ts +2 -2
  36. package/dist/convex-hull/gas-thermodynamics.js +22 -30
  37. package/dist/convex-hull/helpers.d.ts +3 -0
  38. package/dist/convex-hull/helpers.js +17 -9
  39. package/dist/convex-hull/index.d.ts +1 -1
  40. package/dist/convex-hull/thermodynamics.js +83 -78
  41. package/dist/convex-hull/types.d.ts +1 -1
  42. package/dist/coordination/CoordinationBarPlot.svelte +23 -23
  43. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
  44. package/dist/element/ElementTile.svelte.d.ts +1 -1
  45. package/dist/fermi-surface/FermiSlice.svelte +13 -5
  46. package/dist/fermi-surface/FermiSurface.svelte +11 -5
  47. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  48. package/dist/fermi-surface/FermiSurfaceControls.svelte +1 -1
  49. package/dist/fermi-surface/FermiSurfaceScene.svelte +3 -0
  50. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +8 -34
  51. package/dist/fermi-surface/compute.js +59 -59
  52. package/dist/fermi-surface/export.js +3 -2
  53. package/dist/fermi-surface/parse.js +7 -4
  54. package/dist/fermi-surface/types.d.ts +1 -0
  55. package/dist/heatmap-matrix/HeatmapMatrix.svelte +23 -21
  56. package/dist/heatmap-matrix/index.js +1 -1
  57. package/dist/io/decompress.js +4 -2
  58. package/dist/io/export.d.ts +4 -4
  59. package/dist/io/export.js +47 -25
  60. package/dist/io/fetch.js +5 -1
  61. package/dist/io/file-drop.d.ts +1 -1
  62. package/dist/io/file-drop.js +35 -36
  63. package/dist/io/url-drop.js +64 -33
  64. package/dist/isosurface/parse.js +6 -7
  65. package/dist/isosurface/slice.js +5 -4
  66. package/dist/isosurface/types.js +1 -1
  67. package/dist/keyboard.d.ts +3 -0
  68. package/dist/keyboard.js +23 -0
  69. package/dist/labels.d.ts +1 -1
  70. package/dist/labels.js +8 -7
  71. package/dist/layout/PropertyFilter.svelte +3 -2
  72. package/dist/layout/SettingsSection.svelte +1 -1
  73. package/dist/layout/json-tree/JsonNode.svelte +1 -1
  74. package/dist/layout/json-tree/JsonTree.svelte +2 -2
  75. package/dist/layout/json-tree/utils.js +5 -4
  76. package/dist/marching-cubes.js +8 -13
  77. package/dist/math.d.ts +5 -1
  78. package/dist/math.js +24 -9
  79. package/dist/overlays/DraggablePane.svelte +4 -4
  80. package/dist/periodic-table/PeriodicTable.svelte +20 -9
  81. package/dist/periodic-table/PropertySelect.svelte +1 -0
  82. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +9 -3
  83. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
  84. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  85. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
  86. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +1 -1
  87. package/dist/phase-diagram/build-diagram.js +2 -2
  88. package/dist/phase-diagram/parse.js +6 -5
  89. package/dist/phase-diagram/types.d.ts +1 -1
  90. package/dist/phase-diagram/utils.d.ts +3 -3
  91. package/dist/phase-diagram/utils.js +8 -12
  92. package/dist/plot/{BarPlot.svelte → bar/BarPlot.svelte} +229 -587
  93. package/dist/plot/{BarPlot.svelte.d.ts → bar/BarPlot.svelte.d.ts} +5 -5
  94. package/dist/plot/{BarPlotControls.svelte → bar/BarPlotControls.svelte} +6 -5
  95. package/dist/plot/{BarPlotControls.svelte.d.ts → bar/BarPlotControls.svelte.d.ts} +3 -3
  96. package/dist/plot/{SpacegroupBarPlot.svelte → bar/SpacegroupBarPlot.svelte} +6 -6
  97. package/dist/plot/{SpacegroupBarPlot.svelte.d.ts → bar/SpacegroupBarPlot.svelte.d.ts} +1 -1
  98. package/dist/plot/bar/data.d.ts +40 -0
  99. package/dist/plot/bar/data.js +154 -0
  100. package/dist/plot/bar/geometry.d.ts +39 -0
  101. package/dist/plot/bar/geometry.js +60 -0
  102. package/dist/plot/bar/index.d.ts +3 -0
  103. package/dist/plot/bar/index.js +3 -0
  104. package/dist/plot/box/BoxPlot.svelte +1462 -0
  105. package/dist/plot/box/BoxPlot.svelte.d.ts +94 -0
  106. package/dist/plot/box/BoxPlotControls.svelte +109 -0
  107. package/dist/plot/box/BoxPlotControls.svelte.d.ts +19 -0
  108. package/dist/plot/box/Violin.svelte +14 -0
  109. package/dist/plot/box/Violin.svelte.d.ts +70 -0
  110. package/dist/plot/box/box-plot.d.ts +55 -0
  111. package/dist/plot/box/box-plot.js +126 -0
  112. package/dist/plot/box/index.d.ts +5 -0
  113. package/dist/plot/box/index.js +5 -0
  114. package/dist/plot/box/kde.d.ts +16 -0
  115. package/dist/plot/box/kde.js +160 -0
  116. package/dist/plot/box/quantile.d.ts +3 -0
  117. package/dist/plot/box/quantile.js +53 -0
  118. package/dist/plot/{auto-place.js → core/auto-place.js} +2 -2
  119. package/dist/plot/core/axis-utils.d.ts +46 -0
  120. package/dist/plot/core/axis-utils.js +110 -0
  121. package/dist/plot/{AxisLabel.svelte → core/components/AxisLabel.svelte} +2 -2
  122. package/dist/plot/{AxisLabel.svelte.d.ts → core/components/AxisLabel.svelte.d.ts} +1 -1
  123. package/dist/plot/{ColorBar.svelte → core/components/ColorBar.svelte} +36 -33
  124. package/dist/plot/{ColorBar.svelte.d.ts → core/components/ColorBar.svelte.d.ts} +2 -2
  125. package/dist/plot/{ColorScaleSelect.svelte → core/components/ColorScaleSelect.svelte} +4 -3
  126. package/dist/plot/{ColorScaleSelect.svelte.d.ts → core/components/ColorScaleSelect.svelte.d.ts} +2 -2
  127. package/dist/plot/core/components/ControlPane.svelte +46 -0
  128. package/dist/plot/core/components/ControlPane.svelte.d.ts +13 -0
  129. package/dist/plot/{FillArea.svelte → core/components/FillArea.svelte} +17 -6
  130. package/dist/plot/{FillArea.svelte.d.ts → core/components/FillArea.svelte.d.ts} +1 -1
  131. package/dist/plot/{InteractiveAxisLabel.svelte → core/components/InteractiveAxisLabel.svelte} +3 -3
  132. package/dist/plot/{InteractiveAxisLabel.svelte.d.ts → core/components/InteractiveAxisLabel.svelte.d.ts} +2 -2
  133. package/dist/plot/{Line.svelte → core/components/Line.svelte} +30 -13
  134. package/dist/plot/{PlotAxis.svelte → core/components/PlotAxis.svelte} +7 -5
  135. package/dist/plot/{PlotAxis.svelte.d.ts → core/components/PlotAxis.svelte.d.ts} +3 -2
  136. package/dist/plot/{PlotControls.svelte → core/components/PlotControls.svelte} +17 -29
  137. package/dist/plot/core/components/PlotControls.svelte.d.ts +4 -0
  138. package/dist/plot/{PlotLegend.svelte → core/components/PlotLegend.svelte} +21 -10
  139. package/dist/plot/{PlotLegend.svelte.d.ts → core/components/PlotLegend.svelte.d.ts} +3 -2
  140. package/dist/plot/{PlotTooltip.svelte → core/components/PlotTooltip.svelte} +17 -1
  141. package/dist/plot/{PlotTooltip.svelte.d.ts → core/components/PlotTooltip.svelte.d.ts} +8 -0
  142. package/dist/plot/{PortalSelect.svelte → core/components/PortalSelect.svelte} +11 -7
  143. package/dist/plot/{ReferenceLine.svelte → core/components/ReferenceLine.svelte} +3 -3
  144. package/dist/plot/{ReferenceLine.svelte.d.ts → core/components/ReferenceLine.svelte.d.ts} +1 -1
  145. package/dist/plot/{ReferenceLine3D.svelte → core/components/ReferenceLine3D.svelte} +4 -4
  146. package/dist/plot/{ReferenceLine3D.svelte.d.ts → core/components/ReferenceLine3D.svelte.d.ts} +2 -2
  147. package/dist/plot/{ReferencePlane.svelte → core/components/ReferencePlane.svelte} +7 -7
  148. package/dist/plot/{ReferencePlane.svelte.d.ts → core/components/ReferencePlane.svelte.d.ts} +2 -2
  149. package/dist/plot/{ZeroLines.svelte → core/components/ZeroLines.svelte} +3 -3
  150. package/dist/plot/{ZeroLines.svelte.d.ts → core/components/ZeroLines.svelte.d.ts} +3 -3
  151. package/dist/plot/{ZoomRect.svelte → core/components/ZoomRect.svelte} +1 -1
  152. package/dist/plot/{ZoomRect.svelte.d.ts → core/components/ZoomRect.svelte.d.ts} +1 -1
  153. package/dist/plot/core/components/index.d.ts +17 -0
  154. package/dist/plot/core/components/index.js +17 -0
  155. package/dist/plot/{data-cleaning.d.ts → core/data-cleaning.d.ts} +71 -1
  156. package/dist/plot/{data-cleaning.js → core/data-cleaning.js} +3 -5
  157. package/dist/plot/{data-transform.d.ts → core/data-transform.d.ts} +2 -2
  158. package/dist/plot/{data-transform.js → core/data-transform.js} +3 -3
  159. package/dist/plot/core/fill-utils.d.ts +33 -0
  160. package/dist/plot/core/fill-utils.js +388 -0
  161. package/dist/plot/{hover-lock.svelte.js → core/hover-lock.svelte.js} +5 -6
  162. package/dist/plot/core/index.d.ts +10 -0
  163. package/dist/plot/core/index.js +11 -0
  164. package/dist/plot/core/interactions.d.ts +35 -0
  165. package/dist/plot/core/interactions.js +195 -0
  166. package/dist/plot/{layout.d.ts → core/layout.d.ts} +1 -0
  167. package/dist/plot/{layout.js → core/layout.js} +16 -8
  168. package/dist/plot/{reference-line.d.ts → core/reference-line.d.ts} +1 -1
  169. package/dist/plot/{reference-line.js → core/reference-line.js} +23 -36
  170. package/dist/plot/{scales.d.ts → core/scales.d.ts} +2 -2
  171. package/dist/plot/{scales.js → core/scales.js} +84 -85
  172. package/dist/plot/core/svg.d.ts +2 -0
  173. package/dist/plot/core/svg.js +41 -0
  174. package/dist/plot/{types.d.ts → core/types.d.ts} +19 -79
  175. package/dist/plot/{types.js → core/types.js} +1 -1
  176. package/dist/plot/{utils → core/utils}/label-placement.d.ts +2 -2
  177. package/dist/plot/core/utils/series-visibility.d.ts +26 -0
  178. package/dist/plot/{utils → core/utils}/series-visibility.js +29 -2
  179. package/dist/plot/core/utils.d.ts +11 -0
  180. package/dist/plot/core/utils.js +27 -0
  181. package/dist/plot/{Histogram.svelte → histogram/Histogram.svelte} +154 -294
  182. package/dist/plot/{Histogram.svelte.d.ts → histogram/Histogram.svelte.d.ts} +2 -2
  183. package/dist/plot/{HistogramControls.svelte → histogram/HistogramControls.svelte} +6 -6
  184. package/dist/plot/{HistogramControls.svelte.d.ts → histogram/HistogramControls.svelte.d.ts} +4 -4
  185. package/dist/plot/histogram/index.d.ts +2 -0
  186. package/dist/plot/histogram/index.js +2 -0
  187. package/dist/plot/index.d.ts +8 -41
  188. package/dist/plot/index.js +10 -39
  189. package/dist/plot/sankey/Sankey.svelte +700 -0
  190. package/dist/plot/sankey/Sankey.svelte.d.ts +74 -0
  191. package/dist/plot/sankey/SankeyControls.svelte +98 -0
  192. package/dist/plot/sankey/SankeyControls.svelte.d.ts +19 -0
  193. package/dist/plot/sankey/index.d.ts +4 -0
  194. package/dist/plot/sankey/index.js +3 -0
  195. package/dist/plot/sankey/sankey-types.d.ts +42 -0
  196. package/dist/plot/sankey/sankey-types.js +4 -0
  197. package/dist/plot/sankey/sankey.d.ts +52 -0
  198. package/dist/plot/sankey/sankey.js +187 -0
  199. package/dist/plot/{BinnedScatterPlot.svelte → scatter/BinnedScatterPlot.svelte} +61 -59
  200. package/dist/plot/{BinnedScatterPlot.svelte.d.ts → scatter/BinnedScatterPlot.svelte.d.ts} +4 -4
  201. package/dist/plot/{ElementScatter.svelte → scatter/ElementScatter.svelte} +6 -6
  202. package/dist/plot/{ElementScatter.svelte.d.ts → scatter/ElementScatter.svelte.d.ts} +2 -2
  203. package/dist/plot/{ScatterPlot.svelte → scatter/ScatterPlot.svelte} +221 -642
  204. package/dist/plot/{ScatterPlot.svelte.d.ts → scatter/ScatterPlot.svelte.d.ts} +7 -7
  205. package/dist/plot/{ScatterPlotControls.svelte → scatter/ScatterPlotControls.svelte} +6 -5
  206. package/dist/plot/{ScatterPlotControls.svelte.d.ts → scatter/ScatterPlotControls.svelte.d.ts} +1 -1
  207. package/dist/plot/{ScatterPoint.svelte → scatter/ScatterPoint.svelte} +7 -7
  208. package/dist/plot/{ScatterPoint.svelte.d.ts → scatter/ScatterPoint.svelte.d.ts} +3 -3
  209. package/dist/plot/{adaptive-density.d.ts → scatter/adaptive-density.d.ts} +14 -4
  210. package/dist/plot/{adaptive-density.js → scatter/adaptive-density.js} +46 -20
  211. package/dist/plot/{binned-scatter-types.d.ts → scatter/binned-scatter-types.d.ts} +3 -3
  212. package/dist/plot/scatter/index.d.ts +7 -0
  213. package/dist/plot/scatter/index.js +5 -0
  214. package/dist/plot/scatter/scatter-data.d.ts +19 -0
  215. package/dist/plot/scatter/scatter-data.js +212 -0
  216. package/dist/plot/{ScatterPlot3D.svelte → scatter-3d/ScatterPlot3D.svelte} +12 -10
  217. package/dist/plot/{ScatterPlot3D.svelte.d.ts → scatter-3d/ScatterPlot3D.svelte.d.ts} +7 -7
  218. package/dist/plot/{ScatterPlot3DControls.svelte → scatter-3d/ScatterPlot3DControls.svelte} +5 -4
  219. package/dist/plot/{ScatterPlot3DControls.svelte.d.ts → scatter-3d/ScatterPlot3DControls.svelte.d.ts} +2 -2
  220. package/dist/plot/{ScatterPlot3DScene.svelte → scatter-3d/ScatterPlot3DScene.svelte} +11 -11
  221. package/dist/plot/{ScatterPlot3DScene.svelte.d.ts → scatter-3d/ScatterPlot3DScene.svelte.d.ts} +3 -3
  222. package/dist/plot/{Surface3D.svelte → scatter-3d/Surface3D.svelte} +1 -1
  223. package/dist/plot/{Surface3D.svelte.d.ts → scatter-3d/Surface3D.svelte.d.ts} +1 -1
  224. package/dist/plot/scatter-3d/index.d.ts +4 -0
  225. package/dist/plot/scatter-3d/index.js +4 -0
  226. package/dist/plot/sunburst/Sunburst.svelte +1045 -0
  227. package/dist/plot/sunburst/Sunburst.svelte.d.ts +96 -0
  228. package/dist/plot/sunburst/SunburstControls.svelte +200 -0
  229. package/dist/plot/sunburst/SunburstControls.svelte.d.ts +26 -0
  230. package/dist/plot/sunburst/index.d.ts +4 -0
  231. package/dist/plot/sunburst/index.js +4 -0
  232. package/dist/plot/sunburst/render.d.ts +34 -0
  233. package/dist/plot/sunburst/render.js +122 -0
  234. package/dist/plot/sunburst/sunburst.d.ts +62 -0
  235. package/dist/plot/sunburst/sunburst.js +266 -0
  236. package/dist/rdf/RdfPlot.svelte +2 -1
  237. package/dist/rdf/calc-rdf.js +11 -24
  238. package/dist/sanitize.js +1 -1
  239. package/dist/settings.d.ts +65 -1
  240. package/dist/settings.js +262 -0
  241. package/dist/spectral/Bands.svelte +39 -29
  242. package/dist/spectral/Bands.svelte.d.ts +3 -4
  243. package/dist/spectral/BandsAndDos.svelte +1 -1
  244. package/dist/spectral/BrillouinBandsDos.svelte +39 -27
  245. package/dist/spectral/Dos.svelte +10 -19
  246. package/dist/spectral/Dos.svelte.d.ts +2 -2
  247. package/dist/spectral/helpers.d.ts +3 -1
  248. package/dist/spectral/helpers.js +95 -29
  249. package/dist/structure/AtomLegend.svelte +8 -9
  250. package/dist/structure/CellSelect.svelte +1 -2
  251. package/dist/structure/Cylinder.svelte +12 -8
  252. package/dist/structure/Cylinder.svelte.d.ts +4 -1
  253. package/dist/structure/Structure.svelte +78 -72
  254. package/dist/structure/Structure.svelte.d.ts +1 -1
  255. package/dist/structure/StructureInfoPane.svelte +5 -6
  256. package/dist/structure/StructureScene.svelte +11 -10
  257. package/dist/structure/atom-properties.js +6 -6
  258. package/dist/structure/bond-order-perception.js +1 -1
  259. package/dist/structure/bonding.d.ts +1 -0
  260. package/dist/structure/bonding.js +43 -15
  261. package/dist/structure/export.js +27 -23
  262. package/dist/structure/index.d.ts +2 -4
  263. package/dist/structure/index.js +1 -3
  264. package/dist/structure/label-placement.js +4 -4
  265. package/dist/structure/measure.d.ts +3 -2
  266. package/dist/structure/measure.js +6 -5
  267. package/dist/structure/parse.js +121 -103
  268. package/dist/structure/pbc.js +4 -0
  269. package/dist/symmetry/SymmetryStats.svelte +2 -2
  270. package/dist/symmetry/index.d.ts +1 -1
  271. package/dist/symmetry/index.js +22 -24
  272. package/dist/symmetry/spacegroups.d.ts +7 -0
  273. package/dist/symmetry/spacegroups.js +48 -13
  274. package/dist/table/HeatmapTable.svelte +63 -11
  275. package/dist/table/HeatmapTable.svelte.d.ts +1 -1
  276. package/dist/table/index.d.ts +1 -3
  277. package/dist/table/index.js +1 -1
  278. package/dist/theme/index.js +8 -8
  279. package/dist/tooltip/KCoords.svelte +45 -0
  280. package/dist/tooltip/KCoords.svelte.d.ts +8 -0
  281. package/dist/tooltip/index.d.ts +1 -0
  282. package/dist/tooltip/index.js +1 -0
  283. package/dist/trajectory/Trajectory.svelte +66 -40
  284. package/dist/trajectory/Trajectory.svelte.d.ts +2 -1
  285. package/dist/trajectory/TrajectoryExportPane.svelte +2 -1
  286. package/dist/trajectory/TrajectoryInfoPane.svelte +2 -1
  287. package/dist/trajectory/format-detect.d.ts +1 -0
  288. package/dist/trajectory/format-detect.js +25 -11
  289. package/dist/trajectory/frame-reader.js +17 -50
  290. package/dist/trajectory/helpers.js +1 -1
  291. package/dist/trajectory/index.js +1 -1
  292. package/dist/trajectory/parse/hdf5.js +1 -1
  293. package/dist/trajectory/parse/index.js +14 -6
  294. package/dist/trajectory/parse/vasp.js +36 -17
  295. package/dist/trajectory/parse/xyz.d.ts +24 -0
  296. package/dist/trajectory/parse/xyz.js +102 -89
  297. package/dist/trajectory/plotting.d.ts +1 -1
  298. package/dist/trajectory/plotting.js +15 -15
  299. package/dist/utils.d.ts +1 -0
  300. package/dist/utils.js +6 -4
  301. package/dist/xrd/XrdPlot.svelte +2 -1
  302. package/dist/xrd/calc-xrd.js +15 -12
  303. package/dist/xrd/parse.js +2 -2
  304. package/package.json +22 -18
  305. package/dist/plot/PlotControls.svelte.d.ts +0 -4
  306. package/dist/plot/axis-utils.d.ts +0 -19
  307. package/dist/plot/axis-utils.js +0 -78
  308. package/dist/plot/defaults.d.ts +0 -19
  309. package/dist/plot/defaults.js +0 -9
  310. package/dist/plot/fill-utils.d.ts +0 -46
  311. package/dist/plot/fill-utils.js +0 -322
  312. package/dist/plot/interactions.d.ts +0 -12
  313. package/dist/plot/interactions.js +0 -101
  314. package/dist/plot/svg.d.ts +0 -1
  315. package/dist/plot/svg.js +0 -11
  316. package/dist/plot/utils/series-visibility.d.ts +0 -15
  317. package/dist/plot/utils.d.ts +0 -1
  318. package/dist/plot/utils.js +0 -14
  319. /package/dist/plot/{auto-place.d.ts → core/auto-place.d.ts} +0 -0
  320. /package/dist/plot/{Line.svelte.d.ts → core/components/Line.svelte.d.ts} +0 -0
  321. /package/dist/plot/{PortalSelect.svelte.d.ts → core/components/PortalSelect.svelte.d.ts} +0 -0
  322. /package/dist/plot/{hover-lock.svelte.d.ts → core/hover-lock.svelte.d.ts} +0 -0
  323. /package/dist/plot/{utils → core/utils}/label-placement.js +0 -0
  324. /package/dist/plot/{binned-scatter-types.js → scatter/binned-scatter-types.js} +0 -0
@@ -41,9 +41,9 @@ function parse_coordinate_line(line) {
41
41
  const sanitized = line
42
42
  .trim()
43
43
  // Add space when '-' follows a digit and precedes a digit or dot
44
- .replace(/(\d)-(?=[\d.])/g, `$1 -`)
44
+ .replaceAll(/(\d)-(?=[\d.])/g, `$1 -`)
45
45
  // Revert accidental spaces after exponent markers
46
- .replace(/([eE])\s-\s/g, `$1-`);
46
+ .replaceAll(/([eE])\s-\s/g, `$1-`);
47
47
  tokens = sanitized.split(/\s+/);
48
48
  }
49
49
  if (tokens.length < 3)
@@ -61,6 +61,30 @@ function validate_element_symbol(symbol, index) {
61
61
  console.warn(`Invalid element symbol '${symbol}', using fallback '${fallback}'`);
62
62
  return fallback;
63
63
  }
64
+ // Per OPTIMADE spec, species_at_sites holds species NAMES (e.g. 'Si1') resolved via the
65
+ // species list: highest-concentration entry in chemical_symbols wins, non-element entries
66
+ // like 'vacancy' are skipped, and unresolved names are treated as element symbols.
67
+ // Returns the chosen element plus its index into the species' chemical_symbols
68
+ // (sym_idx = -1 on fallback), so callers can read the matching mass/concentration entry.
69
+ function resolve_optimade_element(species_name, species_list, index) {
70
+ const spec = species_list?.find((entry) => entry.name === species_name);
71
+ let best;
72
+ for (const [sym_idx, symbol] of (spec?.chemical_symbols ?? []).entries()) {
73
+ if (!is_known_element_symbol(symbol))
74
+ continue;
75
+ const conc = spec?.concentration?.[sym_idx] ?? 0;
76
+ if (!best || conc > best.conc)
77
+ best = { symbol, conc, sym_idx };
78
+ }
79
+ if (best)
80
+ return { symbol: best.symbol, sym_idx: best.sym_idx };
81
+ // Fallback: the name may be an element with a trailing atom index (e.g. 'O1');
82
+ // element symbols never contain digits, so stripping them is safe
83
+ const stripped = species_name.replace(/\d+$/, ``);
84
+ if (is_known_element_symbol(stripped))
85
+ return { symbol: stripped, sym_idx: -1 };
86
+ return { symbol: validate_element_symbol(species_name, index), sym_idx: -1 };
87
+ }
64
88
  const try_create_cart_to_frac = (lattice_matrix) => {
65
89
  try {
66
90
  return math.create_cart_to_frac(lattice_matrix);
@@ -77,17 +101,21 @@ const approximate_cart_to_frac = (xyz, axis_lengths) => [
77
101
  // Parse VASP POSCAR file format
78
102
  export function parse_poscar(content) {
79
103
  try {
80
- const lines = content.replace(/^\s+/, ``).split(/\r?\n/);
104
+ // Strip only horizontal whitespace: a blank first (comment) line is valid POSCAR
105
+ const lines = content.replace(/^[ \t]+/, ``).split(/\r?\n/);
81
106
  if (lines.length < 8) {
82
107
  console.error(`POSCAR file too short`);
83
108
  return null;
84
109
  }
85
- // Parse scaling factor (line 2)
86
- let scale_factor = parseFloat(lines[1]);
110
+ // Scale line: one value (negative = target volume) or three per-axis Cartesian factors
111
+ const scale_tokens = lines[1].trim().split(/\s+/).map(parseFloat);
112
+ let scale_factor = scale_tokens[0];
87
113
  if (isNaN(scale_factor)) {
88
114
  console.error(`Invalid scaling factor in POSCAR`);
89
115
  return null;
90
116
  }
117
+ const scale_vec = scale_tokens.slice(0, 3);
118
+ const per_axis_scale = scale_vec.length === 3 && !scale_vec.some(isNaN) ? scale_vec : null;
91
119
  // Parse lattice vectors (lines 3-5)
92
120
  const parse_vector = (line, line_num) => {
93
121
  const coords = line.trim().split(/\s+/).map(parse_coordinate);
@@ -98,17 +126,15 @@ export function parse_poscar(content) {
98
126
  parse_vector(lines[3], 4),
99
127
  parse_vector(lines[4], 5),
100
128
  ];
101
- // Handle negative scale factor (volume-based scaling)
102
- if (scale_factor < 0) {
129
+ // Handle negative scale factor (volume-based scaling, single-factor form only)
130
+ if (!per_axis_scale && scale_factor < 0) {
103
131
  const volume = Math.abs(math.det_3x3(lattice_vecs));
104
- scale_factor = Math.pow(-scale_factor / volume, 1 / 3);
132
+ scale_factor = (-scale_factor / volume) ** (1 / 3);
105
133
  }
106
- // Scale lattice vectors
107
- const scaled_lattice = [
108
- math.scale(lattice_vecs[0], scale_factor),
109
- math.scale(lattice_vecs[1], scale_factor),
110
- math.scale(lattice_vecs[2], scale_factor),
111
- ];
134
+ // Scale lattice vectors (per-axis factors multiply Cartesian components)
135
+ const axis_scale = per_axis_scale ?? [scale_factor, scale_factor, scale_factor];
136
+ const apply_axis_scale = (vec) => vec.map((val, axis) => val * axis_scale[axis]);
137
+ const scaled_lattice = lattice_vecs.map(apply_axis_scale);
112
138
  // Parse element symbols and atom counts (may span multiple lines)
113
139
  let line_index = 5;
114
140
  let element_symbols = [];
@@ -116,7 +142,7 @@ export function parse_poscar(content) {
116
142
  // Detect if this is VASP 5+ format (has element symbols)
117
143
  // Try to parse the first token as a number - if it succeeds, it's VASP 4 format
118
144
  const first_token = lines[line_index].trim().split(/\s+/)[0];
119
- const first_token_as_number = parseInt(first_token);
145
+ const first_token_as_number = parseInt(first_token, 10);
120
146
  const has_element_symbols = isNaN(first_token_as_number);
121
147
  if (has_element_symbols) {
122
148
  // VASP 5+ format - parse element symbols (may span multiple lines)
@@ -126,7 +152,7 @@ export function parse_poscar(content) {
126
152
  if (line_index + lookahead_idx >= lines.length)
127
153
  break;
128
154
  const next_line_first_token = lines[line_index + lookahead_idx].trim().split(/\s+/)[0];
129
- const next_token_as_number = parseInt(next_line_first_token);
155
+ const next_token_as_number = parseInt(next_line_first_token, 10);
130
156
  if (!isNaN(next_token_as_number)) {
131
157
  symbol_lines = lookahead_idx;
132
158
  break;
@@ -213,28 +239,22 @@ export function parse_poscar(content) {
213
239
  selective_dynamics = [tokens[3] === `T`, tokens[4] === `T`, tokens[5] === `T`];
214
240
  }
215
241
  }
216
- let xyz;
217
- let abc;
218
- if (is_direct) {
219
- // Store fractional coordinates, wrapping to [0, 1) range
220
- abc = wrap_to_unit_cell(coords);
221
- xyz = poscar_frac_to_cart(abc);
222
- }
223
- else {
224
- // Already Cartesian, scale if needed
225
- xyz = math.scale(coords, scale_factor);
226
- const raw_abc = poscar_cart_to_frac
227
- ? poscar_cart_to_frac(xyz)
228
- : approximate_cart_to_frac(xyz, poscar_axis_lengths);
229
- // Wrap fractional coordinates to [0, 1) range
230
- abc = wrap_to_unit_cell(raw_abc);
231
- }
242
+ // Cartesian input is scaled then converted to fractional (axis-length fallback
243
+ // for singular lattices); abc wraps to [0, 1) and xyz is recomputed from it so
244
+ // both stay consistent (singular Cartesian keeps the scaled input as xyz)
245
+ const cart = is_direct ? null : apply_axis_scale(coords);
246
+ const raw_abc = cart
247
+ ? (poscar_cart_to_frac?.(cart) ??
248
+ approximate_cart_to_frac(cart, poscar_axis_lengths))
249
+ : coords;
250
+ const abc = wrap_to_unit_cell(raw_abc);
251
+ const xyz = cart && !poscar_cart_to_frac ? cart : poscar_frac_to_cart(abc);
232
252
  const site = {
233
253
  species: [{ element, occu: 1, oxidation_state: 0 }],
234
254
  abc,
235
255
  xyz,
236
256
  label: `${element}${atom_index + atom_count_idx + 1}`,
237
- properties: selective_dynamics ? { selective_dynamics: selective_dynamics } : {},
257
+ properties: selective_dynamics ? { selective_dynamics } : {},
238
258
  };
239
259
  sites.push(site);
240
260
  }
@@ -247,10 +267,8 @@ export function parse_poscar(content) {
247
267
  };
248
268
  return structure;
249
269
  }
250
- else {
251
- console.error(`Missing coordinate mode line in POSCAR`);
252
- return null;
253
- }
270
+ console.error(`Missing coordinate mode line in POSCAR`);
271
+ return null;
254
272
  }
255
273
  catch (error) {
256
274
  console.error(`Error parsing POSCAR file:`, error);
@@ -292,7 +310,7 @@ export function parse_xyz(content) {
292
310
  return null;
293
311
  }
294
312
  // Parse number of atoms (line 1)
295
- const num_atoms = parseInt(lines[0].trim());
313
+ const num_atoms = parseInt(lines[0].trim(), 10);
296
314
  if (isNaN(num_atoms) || num_atoms <= 0) {
297
315
  console.error(`Invalid number of atoms in XYZ file`);
298
316
  return null;
@@ -369,7 +387,7 @@ const parse_symmetry_expression = (expr_input) => {
369
387
  const coefficients = [0, 0, 0];
370
388
  let translation = 0;
371
389
  // Remove all whitespace
372
- const expr = expr_input.replace(/\s+/g, ``);
390
+ const expr = expr_input.replaceAll(/\s+/g, ``);
373
391
  if (!expr)
374
392
  return { coefficients, translation };
375
393
  // Tokenize: split into terms while preserving signs
@@ -480,13 +498,15 @@ const extract_cif_cell_parameters = (text, type, strict = true) => text
480
498
  .split(`\n`)
481
499
  .filter((line) => line.startsWith(`_${type}`))
482
500
  .map((line) => {
483
- const tokens = line.split(/\s+/).filter((token) => token.length > 0);
501
+ // Strip trailing comment (# after whitespace) and take the value right after the tag
502
+ const sans_comment = line.replace(/\s#.*$/, ``);
503
+ const tokens = sans_comment.split(/\s+/).filter(Boolean);
484
504
  if (tokens.length < 2) {
485
505
  if (strict)
486
506
  throw new Error(`Invalid CIF cell parameter line format: ${line}`);
487
507
  return null;
488
508
  }
489
- const value = parseFloat((tokens.at(-1) ?? ``).split(`(`)[0]);
509
+ const value = parseFloat(tokens[1].split(`(`)[0]);
490
510
  if (isNaN(value)) {
491
511
  if (strict)
492
512
  throw new Error(`Invalid CIF cell parameter in line: ${line}`);
@@ -541,11 +561,11 @@ const parse_cif_atom_data = (raw_data, indices, coords_type) => {
541
561
  const occu = occupancy >= 0 && raw_data[occupancy]
542
562
  ? parseFloat(raw_data[occupancy].split(`(`)[0]) || 1.0
543
563
  : 1.0;
544
- const element_symbol = (symbol >= 0 && /^([A-Z][a-z]*)/.exec(raw_data[symbol])?.[1]) ||
545
- raw_data[label]?.match(/([A-Z][a-z]*)/g)?.[0] ||
546
- (() => {
547
- throw new Error(`Could not extract element symbol from: ${raw_data.join(` `)}`);
548
- })();
564
+ const from_symbol = symbol >= 0 ? /^([A-Z][a-z]*)/.exec(raw_data[symbol])?.[1] : undefined;
565
+ const element_symbol = from_symbol ?? raw_data[label]?.match(/([A-Z][a-z]*)/g)?.[0];
566
+ if (!element_symbol) {
567
+ throw new Error(`Could not extract element symbol from: ${raw_data.join(` `)}`);
568
+ }
549
569
  return {
550
570
  id: raw_data[label],
551
571
  element: element_symbol,
@@ -617,7 +637,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
617
637
  if (line.startsWith(`;`)) {
618
638
  let multi_line_data = ``;
619
639
  while (jj < lines.length && !lines[jj].trim().endsWith(`;`)) {
620
- multi_line_data += lines[jj] + `\n`;
640
+ multi_line_data += `${lines[jj]}\n`;
621
641
  jj++;
622
642
  }
623
643
  multi_line_data += lines[jj];
@@ -632,7 +652,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
632
652
  if (atom_data_lines.length > 0)
633
653
  break;
634
654
  }
635
- if (!atom_headers.length || !atom_data_lines.length) {
655
+ if (atom_headers.length === 0 || atom_data_lines.length === 0) {
636
656
  console.error(`No valid atom site loop found in CIF file`);
637
657
  return null;
638
658
  }
@@ -660,8 +680,8 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
660
680
  .map((line) => {
661
681
  // Handle quoted multi-word values by splitting only on whitespace
662
682
  // that is not inside quotes.
663
- const tokens = line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || [];
664
- return tokens.map((token) => token.replace(/['"]/g, ``));
683
+ const tokens = line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? [];
684
+ return tokens.map((token) => token.replaceAll(/['"]/g, ``));
665
685
  })
666
686
  .filter((tokens) => {
667
687
  const { disorder } = header_indices;
@@ -679,7 +699,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
679
699
  }
680
700
  })
681
701
  .filter((atom) => atom !== null);
682
- if (!atoms.length) {
702
+ if (atoms.length === 0) {
683
703
  console.error(`No valid atoms found in CIF file`);
684
704
  return null;
685
705
  }
@@ -703,8 +723,8 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
703
723
  const all_sites = [];
704
724
  // Normalize symmetry operations (trim/strip quotes) but preserve duplicates; we deduplicate positions later
705
725
  const normalized_ops = symmetry_ops
706
- .map((op) => /['"]([^'"]+)['"]/.exec(op)?.[1] || op.trim())
707
- .map((op) => op.replace(/\s+/g, ``));
726
+ .map((op) => /['"]([^'"]+)['"]/.exec(op)?.[1] ?? op.trim())
727
+ .map((op) => op.replaceAll(/\s+/g, ``));
708
728
  // Rely on symmetry operations list for all centering/translations to avoid double-counting
709
729
  // TODO: Support conventional cells with centering by discovering centering from space group metadata
710
730
  // when present (e.g. P, I, F, C, R centering types)
@@ -734,12 +754,12 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
734
754
  lj++;
735
755
  continue;
736
756
  }
737
- const toks = (line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []).map((tok) => tok.replace(/['"]/g, ``));
757
+ const toks = (line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? []).map((tok) => tok.replaceAll(/['"]/g, ``));
738
758
  if (toks.length > Math.max(sym_idx, num_idx)) {
739
759
  // Normalize type symbol to bare element (e.g. 'Sn2+' -> 'Sn')
740
760
  const match = /^([A-Z][a-z]*)/.exec(toks[sym_idx]);
741
761
  const sym = match ? match[1] : toks[sym_idx];
742
- const num = parseInt(toks[num_idx]);
762
+ const num = parseInt(toks[num_idx], 10);
743
763
  if (sym && !Number.isNaN(num))
744
764
  map[sym] = num;
745
765
  }
@@ -887,10 +907,8 @@ export function parse_phonopy_yaml(content, cell_type) {
887
907
  const cell = get_phonopy_cell(data, cell_type);
888
908
  if (cell)
889
909
  return convert_phonopy_cell(cell);
890
- else {
891
- console.error(`Requested cell type '${cell_type}' not found in phonopy YAML`);
892
- return null;
893
- }
910
+ console.error(`Requested cell type '${cell_type}' not found in phonopy YAML`);
911
+ return null;
894
912
  }
895
913
  // Auto mode: return preferred structure in order of preference
896
914
  // 1. supercell (most detailed)
@@ -982,6 +1000,19 @@ export function normalize_fractional_coords(structure) {
982
1000
  });
983
1001
  return { ...structure, sites: normalized_sites };
984
1002
  }
1003
+ // Detect a structure inside already-stringified JSON (OPTIMADE or pymatgen/nested).
1004
+ // Throws if `content` isn't valid JSON; returns null if it holds no known structure.
1005
+ const detect_json_structure = (content) => {
1006
+ const parsed = JSON.parse(content);
1007
+ if (is_optimade_raw(parsed)) {
1008
+ const result = parse_optimade_from_raw(parsed);
1009
+ if (result)
1010
+ return result;
1011
+ }
1012
+ // Otherwise try parsing as pymatgen/nested structure JSON
1013
+ const structure = find_structure_in_json(parsed);
1014
+ return structure ? normalize_fractional_coords(structure) : null;
1015
+ };
985
1016
  // Auto-detect file format and parse accordingly
986
1017
  export function parse_structure_file(content, filename) {
987
1018
  // If a filename is provided, try to detect format by file extension first
@@ -998,27 +1029,18 @@ export function parse_structure_file(content, filename) {
998
1029
  // CIF files
999
1030
  if (ext === `cif`)
1000
1031
  return parse_cif(content);
1001
- // JSON files - try OPTIMADE JSON structure format first, then pymatgen
1032
+ // JSON files - extension is authoritative, so failures return null
1002
1033
  if (ext === `json`) {
1003
1034
  try {
1004
- // Parse once, reuse for detection and parsing
1005
- const parsed = JSON.parse(content);
1006
- if (is_optimade_raw(parsed)) {
1007
- const result = parse_optimade_from_raw(parsed);
1008
- if (result)
1009
- return result;
1010
- }
1011
- // Otherwise, try to parse as pymatgen/nested structure JSON
1012
- const structure = find_structure_in_json(parsed);
1013
- if (structure)
1014
- return normalize_fractional_coords(structure);
1035
+ const result = detect_json_structure(content);
1036
+ if (result)
1037
+ return result;
1015
1038
  console.error(`JSON file does not contain a valid structure format`);
1016
- return null;
1017
1039
  }
1018
1040
  catch (error) {
1019
1041
  console.error(`Error parsing JSON file:`, error);
1020
- return null;
1021
1042
  }
1043
+ return null;
1022
1044
  }
1023
1045
  // YAML files (phonopy)
1024
1046
  if (ext === `yaml` || ext === `yml`)
@@ -1028,31 +1050,24 @@ export function parse_structure_file(content, filename) {
1028
1050
  return parse_poscar(content);
1029
1051
  }
1030
1052
  }
1031
- // Try to auto-detect based on content
1032
- const lines = content.trim().split(/\r?\n/);
1033
- if (lines.length < 2) {
1034
- console.error(`File too short to determine format`);
1035
- return null;
1036
- }
1037
- // JSON format detection: try to parse as JSON first
1053
+ // Try to auto-detect based on content.
1054
+ // JSON detection must come before the line-count guard: minified JSON
1055
+ // (e.g. fetched via extensionless blob: object URLs) is a single line.
1038
1056
  try {
1039
- const parsed = JSON.parse(content);
1040
- if (is_optimade_raw(parsed)) {
1041
- const result = parse_optimade_from_raw(parsed);
1042
- if (result)
1043
- return result;
1044
- }
1045
- // Otherwise try parsing as regular JSON structure
1046
- const structure = find_structure_in_json(parsed);
1047
- if (structure) {
1048
- return normalize_fractional_coords(structure);
1049
- }
1057
+ const result = detect_json_structure(content);
1058
+ if (result)
1059
+ return result;
1050
1060
  }
1051
1061
  catch {
1052
1062
  // Not JSON, continue with other format detection
1053
1063
  }
1064
+ const lines = content.trim().split(/\r?\n/);
1065
+ if (lines.length < 2) {
1066
+ console.error(`File too short to determine format`);
1067
+ return null;
1068
+ }
1054
1069
  // XYZ format detection: first line should be a number, second line is comment
1055
- const first_line_number = parseInt(lines[0].trim());
1070
+ const first_line_number = parseInt(lines[0].trim(), 10);
1056
1071
  if (!isNaN(first_line_number) && first_line_number > 0) {
1057
1072
  // Check if this looks like XYZ format
1058
1073
  if (lines.length >= first_line_number + 2) {
@@ -1066,7 +1081,7 @@ export function parse_structure_file(content, filename) {
1066
1081
  const coords = parts.slice(1, 4);
1067
1082
  // Check if first token looks like an element symbol (not a number)
1068
1083
  // and the next 3 tokens look like coordinates (numbers)
1069
- const is_element_symbol = isNaN(parseInt(first_token)) && first_token.length <= 3;
1084
+ const is_element_symbol = isNaN(parseInt(first_token, 10)) && first_token.length <= 3;
1070
1085
  const are_coordinates = coords.every((coord) => !isNaN(parseFloat(coord)));
1071
1086
  if (is_element_symbol && are_coordinates) {
1072
1087
  // First token is likely an element symbol, likely XYZ
@@ -1190,6 +1205,7 @@ export function parse_optimade_from_raw(raw) {
1190
1205
  if (lattice_matrix && !optimade_exact_cart_to_frac) {
1191
1206
  console.warn(`Failed to create exact coordinate converter for OPTIMADE structure`);
1192
1207
  }
1208
+ const optimade_species = Array.isArray(attrs.species) ? attrs.species : undefined;
1193
1209
  const sites = [];
1194
1210
  for (let idx = 0; idx < positions.length; idx++) {
1195
1211
  const pos = positions[idx];
@@ -1202,7 +1218,7 @@ export function parse_optimade_from_raw(raw) {
1202
1218
  console.warn(`Invalid position data at site ${idx}: ${error}`);
1203
1219
  continue;
1204
1220
  }
1205
- const element = validate_element_symbol(element_symbol, idx);
1221
+ const { symbol: element } = resolve_optimade_element(element_symbol, optimade_species, idx);
1206
1222
  // Calculate fractional coordinates if lattice is available
1207
1223
  const abc = optimade_cart_to_frac ? optimade_cart_to_frac(xyz) : [0, 0, 0];
1208
1224
  const site = {
@@ -1282,24 +1298,26 @@ export function optimade_to_crystal(optimade_structure) {
1282
1298
  vec3_from_values(lattice_vectors[2], `OPTIMADE lattice vector 3`),
1283
1299
  ];
1284
1300
  const lattice_params = math.calc_lattice_params(lattice_matrix);
1285
- // Build species lookup for site properties (mass, concentration, etc.)
1286
- const species_map = new Map(species?.map((spec) => [spec.name, spec]));
1287
1301
  const crystal_cart_to_frac = try_create_cart_to_frac(lattice_matrix) ??
1288
1302
  ((xyz) => approximate_cart_to_frac(xyz, [lattice_params.a, lattice_params.b, lattice_params.c]));
1289
1303
  const sites = cartesian_site_positions.map((pos, idx) => {
1290
1304
  const element_symbol = species_at_sites[idx];
1291
1305
  if (!element_symbol)
1292
1306
  throw new Error(`Missing species for site ${idx}`);
1293
- const element = validate_element_symbol(element_symbol, idx);
1307
+ const { symbol: element, sym_idx } = resolve_optimade_element(element_symbol, species, idx);
1294
1308
  const xyz = vec3_from_values(pos, `OPTIMADE atom position ${idx + 1}`);
1295
1309
  const abc = crystal_cart_to_frac ? crystal_cart_to_frac(xyz) : [0, 0, 0];
1296
- // Extract mass/concentration from species data
1297
- const spec = species_map.get(element_symbol);
1310
+ // Extract mass/concentration for the chosen element. sym_idx indexes the (parallel)
1311
+ // chemical_symbols/mass/concentration arrays; -1 (name resolved directly, no
1312
+ // chemical_symbols) falls back to index 0 — the single-element entry.
1313
+ const spec = species?.find((entry) => entry.name === element_symbol);
1314
+ const spec_idx = Math.max(sym_idx, 0);
1298
1315
  const site_props = {};
1299
- if (spec?.mass?.[0] !== undefined)
1300
- site_props.mass = spec.mass[0];
1301
- if (spec?.concentration?.[0] !== undefined && spec.concentration[0] !== 1) {
1302
- site_props.concentration = spec.concentration[0];
1316
+ if (spec?.mass?.[spec_idx] !== undefined)
1317
+ site_props.mass = spec.mass[spec_idx];
1318
+ if (spec?.concentration?.[spec_idx] !== undefined &&
1319
+ spec.concentration[spec_idx] !== 1) {
1320
+ site_props.concentration = spec.concentration[spec_idx];
1303
1321
  }
1304
1322
  return {
1305
1323
  species: [{ element, occu: 1, oxidation_state: 0 }],
@@ -43,11 +43,15 @@ export function find_image_atoms(structure, { tolerance } = {}) {
43
43
  // but fall back to 0.05 fractional tolerance if it does
44
44
  return vec_len > 0 ? PHYSICAL_TOLERANCE / vec_len : 0.05;
45
45
  });
46
+ // Respect per-axis periodicity: no images across non-periodic (vacuum) directions
47
+ const pbc = structure.lattice.pbc ?? [true, true, true];
46
48
  for (const [idx, site] of structure.sites.entries()) {
47
49
  // Find edge dimensions and translation directions
48
50
  const edge_dims = [];
49
51
  // Find boundary dimensions
50
52
  for (let dim = 0; dim < 3; dim++) {
53
+ if (!pbc[dim])
54
+ continue;
51
55
  const coord = site.abc[dim];
52
56
  const dim_tolerance = tolerances[dim];
53
57
  if (Math.abs(coord) < dim_tolerance)
@@ -32,7 +32,7 @@
32
32
  const wyckoff_count = $derived(
33
33
  sym_data ? wyckoff_positions_from_moyo(sym_data).length : 0,
34
34
  )
35
- const display_hm_symbol = $derived(sym_data?.hm_symbol?.replace(/\s+/g, ``) ?? `?`)
35
+ const display_hm_symbol = $derived(sym_data?.hm_symbol?.replaceAll(/\s+/g, ``) ?? `?`)
36
36
 
37
37
  const sym_ops_counts = $derived.by(() => {
38
38
  const EPS = 1e-10
@@ -83,7 +83,7 @@
83
83
  function get_step_from_order_of_magnitude(value: number): number {
84
84
  if (!Number.isFinite(value) || value <= 0) return 1e-5
85
85
  const exponent = Math.floor(Math.log10(value))
86
- return Math.pow(10, exponent)
86
+ return 10 ** exponent
87
87
  }
88
88
 
89
89
  const symprec_step = $derived(get_step_from_order_of_magnitude(settings.symprec))
@@ -35,7 +35,7 @@ export type SymmetryDataset = MoyoDataset & {
35
35
  };
36
36
  export declare function ensure_moyo_wasm_ready(wasm_url?: string): Promise<void>;
37
37
  export declare function to_cell_json(structure: Crystal): string;
38
- export declare function map_std_to_orig_site_indices(std_positions: Vec3[], std_numbers: number[], input_positions: Vec3[], input_numbers: number[], orig_site_indices_by_input_idx: number[][]): number[][];
38
+ export declare const map_std_to_orig_site_indices: (std_positions: Vec3[], std_numbers: number[], input_positions: Vec3[], input_numbers: number[], orig_site_indices_by_input_idx: number[][]) => number[][];
39
39
  export declare function analyze_structure_symmetry(struct_or_mol: AnyStructure, settings: Partial<SymmetrySettings>): Promise<SymmetryDataset>;
40
40
  export declare function simplicity_score(vec: number[]): number;
41
41
  export declare function wyckoff_positions_from_moyo(sym_data: SymmetryDataset | null): WyckoffPos[];
@@ -93,25 +93,23 @@ export function to_cell_json(structure) {
93
93
  const fractional_sq_dist = (pos_1, pos_2) => (pos_1[0] - pos_2[0] - Math.round(pos_1[0] - pos_2[0])) ** 2 +
94
94
  (pos_1[1] - pos_2[1] - Math.round(pos_1[1] - pos_2[1])) ** 2 +
95
95
  (pos_1[2] - pos_2[2] - Math.round(pos_1[2] - pos_2[2])) ** 2;
96
- export function map_std_to_orig_site_indices(std_positions, std_numbers, input_positions, input_numbers, orig_site_indices_by_input_idx) {
97
- return std_positions.map((std_pos, std_idx) => {
98
- const std_number = std_numbers[std_idx];
99
- let nearest_input_idx = -1;
100
- let nearest_sq_dist = Infinity;
101
- for (let input_idx = 0; input_idx < input_positions.length; input_idx += 1) {
102
- if (input_numbers[input_idx] !== std_number)
103
- continue;
104
- const sq_dist = fractional_sq_dist(std_pos, input_positions[input_idx]);
105
- if (sq_dist < nearest_sq_dist) {
106
- nearest_sq_dist = sq_dist;
107
- nearest_input_idx = input_idx;
108
- }
96
+ export const map_std_to_orig_site_indices = (std_positions, std_numbers, input_positions, input_numbers, orig_site_indices_by_input_idx) => std_positions.map((std_pos, std_idx) => {
97
+ const std_number = std_numbers[std_idx];
98
+ let nearest_input_idx = -1;
99
+ let nearest_sq_dist = Infinity;
100
+ for (let input_idx = 0; input_idx < input_positions.length; input_idx += 1) {
101
+ if (input_numbers[input_idx] !== std_number)
102
+ continue;
103
+ const sq_dist = fractional_sq_dist(std_pos, input_positions[input_idx]);
104
+ if (sq_dist < nearest_sq_dist) {
105
+ nearest_sq_dist = sq_dist;
106
+ nearest_input_idx = input_idx;
109
107
  }
110
- if (nearest_input_idx === -1)
111
- return [];
112
- return orig_site_indices_by_input_idx[nearest_input_idx] ?? [];
113
- });
114
- }
108
+ }
109
+ if (nearest_input_idx === -1)
110
+ return [];
111
+ return orig_site_indices_by_input_idx[nearest_input_idx] ?? [];
112
+ });
115
113
  export async function analyze_structure_symmetry(struct_or_mol, settings) {
116
114
  await ensure_moyo_wasm_ready();
117
115
  if (!(`lattice` in struct_or_mol)) {
@@ -178,7 +176,7 @@ export function wyckoff_positions_from_moyo(sym_data) {
178
176
  };
179
177
  });
180
178
  rows.sort((w1, w2) => {
181
- const [w1_mult, w2_mult] = [parseInt(w1.wyckoff), parseInt(w2.wyckoff)];
179
+ const [w1_mult, w2_mult] = [parseInt(w1.wyckoff, 10), parseInt(w2.wyckoff, 10)];
182
180
  if (w1_mult !== w2_mult)
183
181
  return w1_mult - w2_mult;
184
182
  return w1.wyckoff.localeCompare(w2.wyckoff);
@@ -190,10 +188,10 @@ export function apply_symmetry_operations(position, operations, _tolerance = 1e-
190
188
  const seen = new Set();
191
189
  return operations
192
190
  .map(({ rotation, translation }) => {
193
- // Apply 3x3 rotation matrix and translation: new_pos = R * position + t
194
- const new_pos = [0, 1, 2].map((dim) => rotation[dim * 3] * position[0] +
195
- rotation[dim * 3 + 1] * position[1] +
196
- rotation[dim * 3 + 2] * position[2] +
191
+ // new_pos = W·position + t; moyo serializes W COLUMN-major: W[dim][j] = rotation[dim + 3j]
192
+ const new_pos = [0, 1, 2].map((dim) => rotation[dim] * position[0] +
193
+ rotation[dim + 3] * position[1] +
194
+ rotation[dim + 6] * position[2] +
197
195
  translation[dim]);
198
196
  return new_pos.map(to_unit);
199
197
  })
@@ -211,7 +209,7 @@ export function map_wyckoff_to_all_atoms(wyckoff_positions, displayed_structure,
211
209
  return wyckoff_positions;
212
210
  }
213
211
  return wyckoff_positions.map((wyckoff_pos) => {
214
- const indices = (wyckoff_pos.site_indices || [])
212
+ const indices = (wyckoff_pos.site_indices ?? [])
215
213
  .filter((idx) => idx < orig_structure.sites.length)
216
214
  .flatMap((orig_idx) => {
217
215
  const { abc: orig_abc, species } = orig_structure.sites[orig_idx];
@@ -1,3 +1,4 @@
1
+ import type { SunburstNode } from '../plot/core/types';
1
2
  export declare const CRYSTAL_SYSTEM_RANGES: Record<CrystalSystem, [number, number]>;
2
3
  export declare const CRYSTAL_SYSTEM_COLORS: Record<CrystalSystem, string>;
3
4
  export declare const CRYSTAL_SYSTEMS: readonly ["triclinic", "monoclinic", "orthorhombic", "tetragonal", "trigonal", "hexagonal", "cubic"];
@@ -7,3 +8,9 @@ export declare function spacegroup_to_crystal_sys(spacegroup: number | string):
7
8
  export declare function normalize_spacegroup(spacegroup: number | string): number | null;
8
9
  export declare const SPACEGROUP_SYMBOL_TO_NUM: Record<string, number>;
9
10
  export declare const SPACEGROUP_NUM_TO_SYMBOL: Record<number, string>;
11
+ export interface SpacegroupSunburstMetadata {
12
+ spacegroup: number;
13
+ crystal_system: CrystalSystem;
14
+ [key: string]: unknown;
15
+ }
16
+ export declare function spacegroup_sunburst_data(spacegroups: readonly (number | string)[]): SunburstNode<SpacegroupSunburstMetadata>[];