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
@@ -1,4 +1,5 @@
1
1
  export declare function strip_compression_extensions(filename: string): string;
2
+ export declare function ext_hint(filename: string | undefined, ext_regex: RegExp): boolean | null;
2
3
  export declare const FORMAT_PATTERNS: {
3
4
  readonly ase: (data: unknown, filename?: string) => boolean;
4
5
  readonly hdf5: (data: unknown, filename?: string) => boolean;
@@ -8,26 +8,42 @@ export function strip_compression_extensions(filename) {
8
8
  }
9
9
  return base_name;
10
10
  }
11
- // Unified format detection
11
+ // Extensions that explicitly identify a format — when present, format detection trusts
12
+ // the extension instead of sniffing content
13
+ const KNOWN_FORMAT_EXT_REGEX = /\.(xyz|extxyz|traj|h5|hdf5|lammpstrj|json|cif|poscar|vasp|yaml|yml|xml|csv)$/;
14
+ // Classify the filename hint for a format whose extensions match ext_regex:
15
+ // true = filename matches, false = filename names a different known format,
16
+ // null = no usable hint (missing filename or unrecognized extension, e.g. the UUID
17
+ // basenames of blob: object URLs) — callers should fall back to content detection
18
+ export function ext_hint(filename, ext_regex) {
19
+ if (!filename)
20
+ return null;
21
+ const base = strip_compression_extensions(filename);
22
+ if (ext_regex.test(base))
23
+ return true;
24
+ return KNOWN_FORMAT_EXT_REGEX.test(base) ? false : null;
25
+ }
26
+ // Unified format detection. Each pattern trusts a matching file extension when present
27
+ // but falls back to content/magic-byte detection when the filename gives no hint
28
+ // (e.g. blob: object URLs, extensionless API endpoints).
12
29
  export const FORMAT_PATTERNS = {
13
30
  ase: (data, filename) => {
14
- const base_name = filename ? strip_compression_extensions(filename) : undefined;
15
- if (!base_name?.endsWith(`.traj`) || !(data instanceof ArrayBuffer)) {
31
+ if (ext_hint(filename, /\.traj$/) === false || !(data instanceof ArrayBuffer)) {
16
32
  return false;
17
33
  }
18
34
  const view = new Uint8Array(data.slice(0, 24));
19
35
  return [0x2d, 0x20, 0x6f, 0x66, 0x20, 0x55, 0x6c, 0x6d].every((byte, idx) => view[idx] === byte);
20
36
  },
21
37
  hdf5: (data, filename) => {
22
- const base_name = filename ? strip_compression_extensions(filename) : undefined;
23
- const has_ext = base_name?.match(/\.(h5|hdf5)$/);
24
- if (!has_ext || !(data instanceof ArrayBuffer) || data.byteLength < 8)
38
+ if (ext_hint(filename, /\.(h5|hdf5)$/) === false)
39
+ return false;
40
+ if (!(data instanceof ArrayBuffer) || data.byteLength < 8)
25
41
  return false;
26
42
  const signature = new Uint8Array(data.slice(0, 8));
27
43
  return [0x89, 0x48, 0x44, 0x46, 0x0d, 0x0a, 0x1a, 0x0a].every((b, idx) => signature[idx] === b);
28
44
  },
29
45
  vasp: (data, filename) => {
30
- const basename = filename?.toLowerCase().split(`/`).pop() || ``;
46
+ const basename = filename?.toLowerCase().split(`/`).pop() ?? ``;
31
47
  if (basename === `xdatcar` || basename.startsWith(`xdatcar`))
32
48
  return true;
33
49
  const lines = data.trim().split(/\r?\n/);
@@ -37,14 +53,12 @@ export const FORMAT_PATTERNS = {
37
53
  lines.slice(2, 5).every((line) => line.trim().split(/\s+/).length === 3));
38
54
  },
39
55
  xyz_multi: (data, filename) => {
40
- const base = filename ? strip_compression_extensions(filename) : ``;
41
- if (!/\.(xyz|extxyz)$/.test(base))
56
+ if (ext_hint(filename, /\.(xyz|extxyz)$/) === false)
42
57
  return false;
43
58
  return count_xyz_frames(data) >= 2;
44
59
  },
45
60
  lammpstrj: (data, filename) => {
46
- const base = filename ? strip_compression_extensions(filename) : ``;
47
- if (!base.endsWith(`.lammpstrj`))
61
+ if (ext_hint(filename, /\.lammpstrj$/) === false)
48
62
  return false;
49
63
  return data.includes(`ITEM: TIMESTEP`) && data.includes(`ITEM: ATOMS`);
50
64
  },
@@ -1,7 +1,9 @@
1
+ // Unified frame loader for XYZ and ASE trajectories (large file indexing)
1
2
  import * as math from '../math';
2
3
  import { MAX_METADATA_SIZE } from './constants';
3
- import { coerce_element_symbol, convert_atomic_numbers, count_xyz_frames, create_trajectory_frame, read_ndarray_from_view, validate_3x3_matrix, } from './helpers';
4
+ import { convert_atomic_numbers, count_xyz_frames, create_trajectory_frame, read_ndarray_from_view, validate_3x3_matrix, } from './helpers';
4
5
  import { strip_compression_extensions } from './format-detect';
6
+ import { parse_extxyz_lattice, parse_xyz_atom_lines, parse_xyz_comment_metadata, } from './parse/xyz';
5
7
  export class TrajFrameReader {
6
8
  format;
7
9
  global_numbers;
@@ -185,16 +187,12 @@ export class TrajFrameReader {
185
187
  const lines = data.trim().split(/\r?\n/);
186
188
  let [current_frame, line_idx] = [0, 0];
187
189
  while (line_idx < lines.length && current_frame < frame_number) {
188
- if (!lines[line_idx]?.trim()) {
189
- line_idx++;
190
+ const skip_atoms = parseInt(lines[line_idx].trim(), 10);
191
+ if (isNaN(skip_atoms) || skip_atoms <= 0) {
192
+ line_idx++; // skip blank/invalid lines until the next frame's atom-count line
190
193
  continue;
191
194
  }
192
- const num_atoms = parseInt(lines[line_idx].trim(), 10);
193
- if (isNaN(num_atoms) || num_atoms <= 0) {
194
- line_idx++;
195
- continue;
196
- }
197
- line_idx += 2 + num_atoms;
195
+ line_idx += 2 + skip_atoms;
198
196
  current_frame++;
199
197
  }
200
198
  if (line_idx >= lines.length)
@@ -203,32 +201,14 @@ export class TrajFrameReader {
203
201
  if (isNaN(num_atoms) || line_idx + num_atoms + 1 >= lines.length)
204
202
  return null;
205
203
  const comment = lines[line_idx + 1] || ``;
206
- const positions = [];
207
- const elements = [];
208
- for (let idx = 0; idx < num_atoms; idx++) {
209
- const parts = lines[line_idx + 2 + idx]?.trim().split(/\s+/);
210
- if (parts?.length >= 4) {
211
- const x_coord = parseFloat(parts[1]);
212
- const y_coord = parseFloat(parts[2]);
213
- const z_coord = parseFloat(parts[3]);
214
- if (!Number.isFinite(x_coord) ||
215
- !Number.isFinite(y_coord) ||
216
- !Number.isFinite(z_coord)) {
217
- console.warn(`Skipping XYZ atom with invalid coordinates in indexed frame ${frame_number} at line ${line_idx + 2 + idx}`);
218
- continue;
219
- }
220
- const raw_symbol = parts[0];
221
- const element_symbol = coerce_element_symbol(raw_symbol);
222
- if (!element_symbol) {
223
- console.warn(`Skipping XYZ atom with unknown element symbol "${raw_symbol}" in indexed frame ${frame_number}`);
224
- continue;
225
- }
226
- elements.push(element_symbol);
227
- positions.push([x_coord, y_coord, z_coord]);
228
- }
229
- }
230
- const metadata = this.parse_xyz_metadata(comment, frame_number);
231
- return create_trajectory_frame(positions, elements, undefined, undefined, metadata.step, metadata.properties);
204
+ const lattice_matrix = parse_extxyz_lattice(comment);
205
+ const { elements, positions, force_stats } = parse_xyz_atom_lines(lines, line_idx + 2, num_atoms, comment, `indexed frame ${frame_number}`);
206
+ const { step, properties } = this.parse_xyz_metadata(comment, frame_number);
207
+ const metadata = { ...properties, ...force_stats };
208
+ // Derive volume from the lattice (parity with the eager parse_xyz_trajectory parser)
209
+ if (lattice_matrix)
210
+ metadata.volume = math.calc_lattice_params(lattice_matrix).volume;
211
+ return create_trajectory_frame(positions, elements, lattice_matrix, lattice_matrix ? [true, true, true] : undefined, step, metadata);
232
212
  }
233
213
  load_ase_frame(data, frame_number) {
234
214
  try {
@@ -274,21 +254,8 @@ export class TrajFrameReader {
274
254
  }
275
255
  }
276
256
  parse_xyz_metadata(comment, frame_number) {
277
- const properties = {};
278
- const patterns = {
279
- energy: /(?:energy|E|etot)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
280
- volume: /(?:volume|vol|V)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
281
- pressure: /(?:pressure|press|P)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
282
- force_max: /(?:max_force|fmax)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
283
- };
284
- Object.entries(patterns).forEach(([key, pattern]) => {
285
- const match = pattern.exec(comment);
286
- if (match)
287
- properties[key] = parseFloat(match[1]);
288
- });
289
- const step_match = /(?:step|frame)\s*[=:]?\s*(\d+)/i.exec(comment);
290
- const step = step_match ? parseInt(step_match[1]) : frame_number;
291
- return { frame_number, step, properties };
257
+ const { step, properties } = parse_xyz_comment_metadata(comment);
258
+ return { frame_number, step: step ?? frame_number, properties };
292
259
  }
293
260
  parse_ase_metadata(frame_data, frame_number) {
294
261
  const properties = {};
@@ -147,7 +147,7 @@ export function count_xyz_frames(data) {
147
147
  let valid_coords = 0;
148
148
  for (let idx = 0; idx < Math.min(num_atoms, 3); idx++) {
149
149
  const parts = lines[line_idx + 2 + idx]?.trim().split(/\s+/);
150
- if (parts?.length >= 4 && isNaN(parseInt(parts[0])) && parts[0].length <= 3) {
150
+ if (parts?.length >= 4 && isNaN(parseInt(parts[0], 10)) && parts[0].length <= 3) {
151
151
  if (parts.slice(1, 4).every((coord) => !isNaN(parseFloat(coord))))
152
152
  valid_coords++;
153
153
  }
@@ -77,7 +77,7 @@ export function validate_trajectory(trajectory) {
77
77
  }
78
78
  export function get_trajectory_stats(trajectory) {
79
79
  const { frames, total_frames, indexed_frames, plot_metadata } = trajectory;
80
- const frame_count = total_frames || frames.length;
80
+ const frame_count = total_frames ?? frames.length;
81
81
  const stats = {
82
82
  frame_count,
83
83
  is_indexed: trajectory.is_indexed ?? false,
@@ -9,7 +9,7 @@ export async function parse_torch_sim_hdf5(buffer, filename) {
9
9
  const file_basename = filename
10
10
  ?.split(`/`)
11
11
  .at(-1)
12
- ?.replace(/[^\w.-]/g, `_`) || `temp`;
12
+ ?.replaceAll(/[^\w.-]/g, `_`) ?? `temp`;
13
13
  const unique_suffix = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
14
14
  const temp_filename = `${file_basename}-${unique_suffix}.h5`;
15
15
  FS.writeFile(temp_filename, new Uint8Array(buffer));
@@ -2,9 +2,9 @@ import { is_binary } from '../../io/is-binary';
2
2
  import * as math from '../../math';
3
3
  import { parse_xyz } from '../../structure/parse';
4
4
  import { INDEX_SAMPLE_RATE, LARGE_FILE_THRESHOLD } from '../constants';
5
- import { FORMAT_PATTERNS, is_trajectory_file, strip_compression_extensions, } from '../format-detect';
5
+ import { ext_hint, FORMAT_PATTERNS, is_trajectory_file, strip_compression_extensions, } from '../format-detect';
6
6
  import { TrajFrameReader } from '../frame-reader';
7
- import { create_trajectory_frame, validate_3x3_matrix } from '../helpers';
7
+ import { count_xyz_frames, create_trajectory_frame, validate_3x3_matrix, } from '../helpers';
8
8
  import { parse_ase_trajectory } from './ase';
9
9
  import { parse_torch_sim_hdf5 } from './hdf5';
10
10
  import { parse_lammps_trajectory } from './lammps';
@@ -33,8 +33,10 @@ export async function parse_trajectory_data(data, filename, atom_type_mapping) {
33
33
  if (FORMAT_PATTERNS.lammpstrj(content, filename)) {
34
34
  return parse_lammps_trajectory(content, filename, atom_type_mapping);
35
35
  }
36
- // Single XYZ fallback
37
- if (filename?.toLowerCase().match(/\.(?:xyz|extxyz)$/)) {
36
+ // Single XYZ fallback (content-sniffed when the filename gives no format hint,
37
+ // e.g. blob: object URLs whose basenames are UUIDs)
38
+ const xyz_hint = ext_hint(filename, /\.(xyz|extxyz)$/);
39
+ if (xyz_hint || (xyz_hint === null && count_xyz_frames(content) === 1)) {
38
40
  try {
39
41
  const structure = parse_xyz(content);
40
42
  if (structure) {
@@ -65,7 +67,7 @@ export async function parse_trajectory_data(data, filename, atom_type_mapping) {
65
67
  const frame_obj = frame_data;
66
68
  const frame_step = frame_obj.step;
67
69
  return {
68
- structure: (frame_obj.structure || frame_obj),
70
+ structure: (frame_obj.structure ?? frame_obj),
69
71
  step: typeof frame_step === `number` ? frame_step : idx,
70
72
  metadata: frame_obj.metadata || {},
71
73
  };
@@ -198,8 +200,14 @@ export async function parse_trajectory_async(data, filename, on_progress, option
198
200
  update_progress(5, `Large file detected (${Math.round(data_size / 1024 / 1024)}MB)`);
199
201
  }
200
202
  // Use indexed loading for supported large files (including compressed names).
203
+ // When the filename gives no format hint (e.g. blob: URLs), sniff a content
204
+ // prefix for XYZ frames so large extensionless files still get indexed.
201
205
  const base_filename = strip_compression_extensions(filename);
202
- if (should_use_indexing && /\.(xyz|extxyz|traj)$/.test(base_filename)) {
206
+ const can_index = /\.(xyz|extxyz|traj)$/.test(base_filename) ||
207
+ (typeof data === `string` &&
208
+ ext_hint(filename, /\.(xyz|extxyz)$/) === null &&
209
+ count_xyz_frames(data.slice(0, 2 ** 20)) >= 1);
210
+ if (should_use_indexing && can_index) {
203
211
  return await parse_with_unified_loader(data, filename, {
204
212
  index_sample_rate,
205
213
  extract_plot_metadata,
@@ -1,42 +1,61 @@
1
1
  import * as math from '../../math';
2
2
  import { create_trajectory_frame, is_valid_element_symbol, validate_3x3_matrix, } from '../helpers';
3
+ // Parse the 7-line XDATCAR header at lines[start]: title, scale factor, 3 lattice rows
4
+ // (multiplied by scale), element names, element counts
5
+ function parse_xdatcar_header(lines, start) {
6
+ const scale = parseFloat(lines[start + 1]);
7
+ const rows = lines.slice(start + 2, start + 5).map((line) => line
8
+ .trim()
9
+ .split(/\s+/)
10
+ .map((val) => parseFloat(val) * scale));
11
+ const names = lines[start + 5].trim().split(/\s+/);
12
+ const counts = lines[start + 6].trim().split(/\s+/).map(Number);
13
+ return { scale, rows, names, counts };
14
+ }
3
15
  export function parse_vasp_xdatcar(content, filename) {
4
16
  const lines = content.trim().split(/\r?\n/);
5
17
  if (lines.length < 10)
6
18
  throw new Error(`XDATCAR file too short`);
7
- const scale = parseFloat(lines[1]);
8
- if (isNaN(scale))
19
+ const header = parse_xdatcar_header(lines, 0);
20
+ const { names: element_names, counts: element_counts } = header;
21
+ if (isNaN(header.scale))
9
22
  throw new Error(`Invalid scale factor`);
10
- const lattice_matrix = validate_3x3_matrix(lines.slice(2, 5).map((line) => line
11
- .trim()
12
- .split(/\s+/)
13
- .map((component) => parseFloat(component) * scale)));
14
- const element_names = lines[5].trim().split(/\s+/);
15
- const element_counts = lines[6].trim().split(/\s+/).map(Number);
23
+ let lattice_matrix = validate_3x3_matrix(header.rows);
16
24
  if (element_names.length !== element_counts.length) {
17
25
  throw new Error(`XDATCAR element names/counts mismatch: names=${element_names.length}, counts=${element_counts.length}`);
18
26
  }
19
27
  if (element_counts.some((count) => !Number.isFinite(count) || !Number.isInteger(count) || count <= 0)) {
20
28
  throw new Error(`XDATCAR contains invalid element counts: expected finite positive integers`);
21
29
  }
22
- const validated_element_names = element_names.map((name) => {
23
- if (!is_valid_element_symbol(name)) {
24
- throw new Error(`Invalid element symbol in XDATCAR: ${name}`);
25
- }
26
- return name;
27
- });
28
- const elements = validated_element_names.flatMap((name, idx) => Array(element_counts[idx]).fill(name));
30
+ const bad_element = element_names.find((name) => !is_valid_element_symbol(name));
31
+ if (bad_element)
32
+ throw new Error(`Invalid element symbol in XDATCAR: ${bad_element}`);
33
+ let elements = element_names.flatMap((name, idx) => Array(element_counts[idx]).fill(name));
29
34
  const frames = [];
30
35
  let line_idx = 7;
31
- const frac_to_cart = math.create_frac_to_cart(lattice_matrix);
36
+ let frac_to_cart = math.create_frac_to_cart(lattice_matrix);
32
37
  while (line_idx < lines.length) {
33
38
  const config_idx = lines.findIndex((line, idx) => idx >= line_idx && line.includes(`Direct configuration=`));
34
39
  if (config_idx === -1)
35
40
  break;
41
+ // Variable-cell runs (NPT/ISIF=3) repeat the full 7-line header before each configuration
42
+ if (config_idx - line_idx >= 7) {
43
+ const hdr = parse_xdatcar_header(lines, config_idx - 7);
44
+ if (Number.isFinite(hdr.scale) &&
45
+ hdr.rows.every((row) => row.length === 3 && row.every(Number.isFinite))) {
46
+ lattice_matrix = validate_3x3_matrix(hdr.rows);
47
+ frac_to_cart = math.create_frac_to_cart(lattice_matrix);
48
+ if (hdr.names.length === hdr.counts.length &&
49
+ hdr.names.every(is_valid_element_symbol) &&
50
+ hdr.counts.every((count) => Number.isInteger(count) && count > 0)) {
51
+ elements = hdr.names.flatMap((name, idx) => Array(hdr.counts[idx]).fill(name));
52
+ }
53
+ }
54
+ }
36
55
  const config_line = lines[config_idx];
37
56
  line_idx = config_idx + 1;
38
57
  const step_match = /configuration=\s*(\d+)/.exec(config_line);
39
- const step = step_match ? parseInt(step_match[1]) : frames.length + 1;
58
+ const step = step_match ? parseInt(step_match[1], 10) : frames.length + 1;
40
59
  const positions = [];
41
60
  for (let idx = 0; idx < elements.length && line_idx < lines.length; idx++) {
42
61
  const coords = lines[line_idx].trim().split(/\s+/).slice(0, 3).map(Number);
@@ -1,2 +1,26 @@
1
+ import type { ElementSymbol } from '../../element/types';
2
+ import * as math from '../../math';
1
3
  import type { TrajectoryType } from '../index';
4
+ export declare function parse_extxyz_columns(comment: string): {
5
+ species_col: number;
6
+ pos_col: number;
7
+ forces_col: number;
8
+ min_cols: number;
9
+ };
10
+ export declare function parse_extxyz_lattice(comment: string): math.Matrix3x3 | undefined;
11
+ export declare function parse_xyz_comment_metadata(comment: string): {
12
+ step?: number;
13
+ properties: Record<string, number>;
14
+ };
15
+ type ForceStats = {
16
+ forces: number[][];
17
+ force_max: number;
18
+ force_norm: number;
19
+ };
20
+ export declare function parse_xyz_atom_lines(lines: string[], start: number, num_atoms: number, comment: string, frame_label: string): {
21
+ elements: ElementSymbol[];
22
+ positions: number[][];
23
+ force_stats: ForceStats | null;
24
+ };
2
25
  export declare function parse_xyz_trajectory(content: string): TrajectoryType;
26
+ export {};
@@ -1,103 +1,116 @@
1
1
  import * as math from '../../math';
2
2
  import { coerce_element_symbol, create_trajectory_frame } from '../helpers';
3
+ // Resolve species/pos/forces column offsets from an extxyz Properties string of
4
+ // name:type:ncols triples (e.g. "species:S:1:pos:R:3:forces:R:3"), falling back
5
+ // to the conventional "symbol x y z" layout when absent or malformed
6
+ export function parse_extxyz_columns(comment) {
7
+ const fields = /Properties\s*=\s*"?([^"\s]+)"?/i.exec(comment)?.[1].split(`:`) ?? [];
8
+ // Well-formed Properties is name:type:ncols triples; a non-multiple of 3 is malformed,
9
+ // so bail to the conventional default rather than trusting a partial layout
10
+ let layout = fields.length % 3 === 0 ? {} : null;
11
+ for (let idx = 0, offset = 0; layout && idx + 3 <= fields.length; idx += 3) {
12
+ const ncols = parseInt(fields[idx + 2], 10);
13
+ if (Number.isInteger(ncols) && ncols > 0) {
14
+ layout[fields[idx].toLowerCase()] = { offset, ncols };
15
+ offset += ncols;
16
+ }
17
+ else
18
+ layout = null;
19
+ }
20
+ const species_col = layout?.species?.offset ?? 0;
21
+ const pos_col = layout?.pos?.offset ?? 1;
22
+ const forces_col = layout?.forces && layout.forces.ncols >= 3 ? layout.forces.offset : -1;
23
+ return { species_col, pos_col, forces_col, min_cols: Math.max(pos_col + 3, species_col + 1) };
24
+ }
25
+ // Parse Lattice="ax ay az bx by bz cx cy cz" from an extxyz comment line
26
+ export function parse_extxyz_lattice(comment) {
27
+ const vals = /Lattice\s*=\s*"([^"]+)"/i.exec(comment)?.[1].trim().split(/\s+/).map(Number);
28
+ if (vals?.length !== 9 || !vals.every(Number.isFinite))
29
+ return undefined;
30
+ return [vals.slice(0, 3), vals.slice(3, 6), vals.slice(6, 9)];
31
+ }
32
+ // Keys anchored at ^|\s and followed by [=:] so single-letter keys (E/V/P/T) don't match mid-word
33
+ const make_pattern = (keys) => new RegExp(`(?:^|\\s)(?:${keys})\\s*[=:]\\s*([-+]?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?)`, `i`);
34
+ const METADATA_PATTERNS = {
35
+ energy: make_pattern(`energy|E|etot|total_energy`),
36
+ volume: make_pattern(`volume|vol|V`),
37
+ pressure: make_pattern(`pressure|press|P`),
38
+ temperature: make_pattern(`temperature|temp|T`),
39
+ force_max: make_pattern(`max_force|force_max|fmax`),
40
+ bandgap: make_pattern(`bandgap|E_gap|gap`),
41
+ };
42
+ // Extract step number and scalar properties from an (ext)XYZ comment line
43
+ export function parse_xyz_comment_metadata(comment) {
44
+ const properties = {};
45
+ for (const [key, pattern] of Object.entries(METADATA_PATTERNS)) {
46
+ const match = pattern.exec(comment);
47
+ if (match)
48
+ properties[key] = parseFloat(match[1]);
49
+ }
50
+ const step = /(?:^|\s)(?:step|frame|ionic_step)\s*[=:]?\s*(\d+)/i.exec(comment)?.[1];
51
+ return { step: step ? parseInt(step, 10) : undefined, properties };
52
+ }
53
+ // Parse num_atoms atom lines starting at lines[start], reading species/pos/forces from
54
+ // their Properties-declared column offsets; invalid atoms are skipped with a warning.
55
+ // force_stats holds raw forces plus max and RMS force magnitudes when forces are present.
56
+ export function parse_xyz_atom_lines(lines, start, num_atoms, comment, frame_label) {
57
+ const { species_col, pos_col, forces_col, min_cols } = parse_extxyz_columns(comment);
58
+ const elements = [];
59
+ const positions = [];
60
+ const forces = [];
61
+ for (let idx = 0; idx < num_atoms; idx++) {
62
+ const parts = lines[start + idx]?.trim().split(/\s+/) ?? [];
63
+ if (parts.length < min_cols)
64
+ continue;
65
+ const pos = parts.slice(pos_col, pos_col + 3).map(parseFloat);
66
+ if (!pos.every(Number.isFinite)) {
67
+ console.warn(`Skipping XYZ atom with invalid coordinates in ${frame_label} at line ${start + idx + 1}`);
68
+ continue;
69
+ }
70
+ const symbol = parts[species_col];
71
+ const element_symbol = coerce_element_symbol(symbol);
72
+ if (!element_symbol) {
73
+ console.warn(`Skipping XYZ atom with unknown element symbol "${symbol}" in ${frame_label}`);
74
+ continue;
75
+ }
76
+ elements.push(element_symbol);
77
+ positions.push(pos);
78
+ if (forces_col >= 0 && parts.length >= forces_col + 3) {
79
+ const force_vec = parts.slice(forces_col, forces_col + 3).map(parseFloat);
80
+ if (force_vec.every(Number.isFinite))
81
+ forces.push(force_vec);
82
+ }
83
+ }
84
+ if (forces.length === 0)
85
+ return { elements, positions, force_stats: null };
86
+ const mags = forces.map((force) => Math.hypot(...force));
87
+ const force_norm = Math.sqrt(mags.reduce((sum, mag) => sum + mag ** 2, 0) / mags.length);
88
+ return {
89
+ elements,
90
+ positions,
91
+ force_stats: { forces, force_max: Math.max(...mags), force_norm },
92
+ };
93
+ }
3
94
  export function parse_xyz_trajectory(content) {
4
95
  const lines = content.trim().split(/\r?\n/);
5
96
  const frames = [];
6
97
  let line_idx = 0;
7
98
  while (line_idx < lines.length) {
8
- if (!lines[line_idx]?.trim()) {
9
- line_idx++;
10
- continue;
11
- }
12
99
  const num_atoms = parseInt(lines[line_idx].trim(), 10);
13
100
  if (isNaN(num_atoms) || num_atoms <= 0 || line_idx + num_atoms + 1 >= lines.length) {
14
- line_idx++;
101
+ line_idx++; // skip blank/invalid lines until the next frame's atom-count line
15
102
  continue;
16
103
  }
17
- const comment = lines[++line_idx] || ``;
18
- const metadata = {};
19
- // Extract properties efficiently
20
- const extractors = {
21
- step: /(?:step|frame|ionic_step)\s*[=:]?\s*(\d+)/i,
22
- energy: /(?:energy|E|etot|total_energy)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
23
- volume: /(?:volume|vol|V)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
24
- pressure: /(?:pressure|press|P)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
25
- temperature: /(?:temperature|temp|T)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
26
- force_max: /(?:max_force|force_max|fmax)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
27
- bandgap: /(?:bandgap|E_gap|gap)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
28
- };
29
- const step_match = extractors.step.exec(comment);
30
- const step = step_match?.[1] ? parseInt(step_match[1]) : frames.length;
31
- Object.entries(extractors).forEach(([key, pattern]) => {
32
- if (key === `step`)
33
- return;
34
- const match = pattern.exec(comment);
35
- if (match)
36
- metadata[key] = parseFloat(match[1]);
37
- });
38
- // Extract lattice matrix
39
- const lattice_match = /Lattice\s*=\s*"([^"]+)"/i.exec(comment);
40
- let lattice_matrix;
41
- if (lattice_match) {
42
- const values = lattice_match[1].split(/\s+/).map(Number);
43
- if (values.length === 9 && values.every((value) => Number.isFinite(value))) {
44
- lattice_matrix = [
45
- [values[0], values[1], values[2]],
46
- [values[3], values[4], values[5]],
47
- [values[6], values[7], values[8]],
48
- ];
49
- metadata.volume = math.calc_lattice_params(lattice_matrix).volume;
50
- }
51
- }
52
- // Parse atoms
53
- const positions = [];
54
- const elements = [];
55
- const forces = [];
56
- const has_forces = comment.includes(`forces:R:3`);
57
- for (let idx = 0; idx < num_atoms; idx++) {
58
- line_idx++;
59
- if (line_idx >= lines.length)
60
- break;
61
- const parts = lines[line_idx].trim().split(/\s+/);
62
- if (parts.length >= 4) {
63
- const x_coord = parseFloat(parts[1]);
64
- const y_coord = parseFloat(parts[2]);
65
- const z_coord = parseFloat(parts[3]);
66
- if (!Number.isFinite(x_coord) ||
67
- !Number.isFinite(y_coord) ||
68
- !Number.isFinite(z_coord)) {
69
- console.warn(`Skipping XYZ atom with invalid coordinates in frame ${frames.length} at line ${line_idx + 1}`);
70
- continue;
71
- }
72
- const raw_symbol = parts[0];
73
- const element_symbol = coerce_element_symbol(raw_symbol);
74
- if (!element_symbol) {
75
- console.warn(`Skipping XYZ atom with unknown element symbol "${raw_symbol}" in frame ${frames.length}`);
76
- continue;
77
- }
78
- elements.push(element_symbol);
79
- positions.push([x_coord, y_coord, z_coord]);
80
- if (has_forces && parts.length >= 7) {
81
- const force_x = parseFloat(parts[4]);
82
- const force_y = parseFloat(parts[5]);
83
- const force_z = parseFloat(parts[6]);
84
- if (Number.isFinite(force_x) &&
85
- Number.isFinite(force_y) &&
86
- Number.isFinite(force_z)) {
87
- forces.push([force_x, force_y, force_z]);
88
- }
89
- }
90
- }
91
- }
92
- if (forces.length > 0) {
93
- metadata.forces = forces;
94
- const magnitudes = forces.map((force) => Math.hypot(...force));
95
- metadata.force_max = Math.max(...magnitudes);
96
- // Calculate RMS (root mean square) of force magnitudes
97
- metadata.force_norm = Math.sqrt(magnitudes.reduce((sum, mag) => sum + mag ** 2, 0) / magnitudes.length);
98
- }
99
- frames.push(create_trajectory_frame(positions, elements, lattice_matrix, lattice_matrix ? [true, true, true] : undefined, step, metadata));
100
- line_idx++;
104
+ const comment = lines[line_idx + 1] || ``;
105
+ const { step, properties } = parse_xyz_comment_metadata(comment);
106
+ const metadata = { ...properties };
107
+ const lattice_matrix = parse_extxyz_lattice(comment);
108
+ if (lattice_matrix)
109
+ metadata.volume = math.calc_lattice_params(lattice_matrix).volume;
110
+ const { elements, positions, force_stats } = parse_xyz_atom_lines(lines, line_idx + 2, num_atoms, comment, `frame ${frames.length}`);
111
+ Object.assign(metadata, force_stats);
112
+ frames.push(create_trajectory_frame(positions, elements, lattice_matrix, lattice_matrix ? [true, true, true] : undefined, step ?? frames.length, metadata));
113
+ line_idx += num_atoms + 2;
101
114
  }
102
115
  return {
103
116
  frames,
@@ -1,4 +1,4 @@
1
- import type { DataSeries } from '../plot/types';
1
+ import type { DataSeries } from '../plot/core/types';
2
2
  import type { TrajectoryDataExtractor, TrajectoryMetadata, TrajectoryType } from './index';
3
3
  export interface PlotSeriesOptions {
4
4
  property_config?: Record<string, {