matterviz 0.4.0 → 0.4.1

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 (326) hide show
  1. package/dist/brillouin/BrillouinZone.svelte +68 -145
  2. package/dist/brillouin/BrillouinZone.svelte.d.ts +5 -14
  3. package/dist/brillouin/BrillouinZoneExportPane.svelte +43 -96
  4. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
  5. package/dist/brillouin/BrillouinZoneInfoPane.svelte +9 -32
  6. package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +2 -3
  7. package/dist/brillouin/BrillouinZoneScene.svelte +49 -203
  8. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +3 -23
  9. package/dist/brillouin/ReciprocalVectors.svelte +39 -0
  10. package/dist/brillouin/ReciprocalVectors.svelte.d.ts +9 -0
  11. package/dist/brillouin/compute.d.ts +2 -0
  12. package/dist/brillouin/compute.js +80 -77
  13. package/dist/brillouin/geometry.d.ts +8 -0
  14. package/dist/brillouin/geometry.js +57 -0
  15. package/dist/brillouin/index.d.ts +2 -0
  16. package/dist/brillouin/index.js +2 -0
  17. package/dist/brillouin/types.d.ts +2 -2
  18. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +1 -1
  19. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +100 -191
  20. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +4 -1
  21. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +176 -464
  22. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +7 -1
  23. package/dist/chempot-diagram/color.d.ts +3 -6
  24. package/dist/chempot-diagram/color.js +5 -5
  25. package/dist/chempot-diagram/compute.d.ts +3 -3
  26. package/dist/chempot-diagram/compute.js +3 -1
  27. package/dist/chempot-diagram/controls-state.svelte.d.ts +10 -0
  28. package/dist/chempot-diagram/controls-state.svelte.js +42 -0
  29. package/dist/chempot-diagram/export.d.ts +47 -0
  30. package/dist/chempot-diagram/export.js +133 -0
  31. package/dist/chempot-diagram/index.d.ts +1 -0
  32. package/dist/chempot-diagram/index.js +1 -0
  33. package/dist/chempot-diagram/pointer.d.ts +0 -10
  34. package/dist/chempot-diagram/pointer.js +4 -4
  35. package/dist/chempot-diagram/types.d.ts +3 -3
  36. package/dist/colors/index.js +2 -2
  37. package/dist/composition/FormulaFilter.svelte +6 -5
  38. package/dist/composition/PieChart.svelte +5 -5
  39. package/dist/composition/chem-sys.js +3 -2
  40. package/dist/composition/format.js +3 -2
  41. package/dist/composition/parse.d.ts +0 -1
  42. package/dist/composition/parse.js +17 -19
  43. package/dist/controls.d.ts +1 -0
  44. package/dist/controls.js +0 -1
  45. package/dist/convex-hull/ConvexHull.svelte +8 -10
  46. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -4
  47. package/dist/convex-hull/ConvexHull2D.svelte +94 -175
  48. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  49. package/dist/convex-hull/ConvexHull3D.svelte +176 -680
  50. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  51. package/dist/convex-hull/ConvexHull4D.svelte +180 -680
  52. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  53. package/dist/convex-hull/ConvexHullChrome.svelte +268 -0
  54. package/dist/convex-hull/ConvexHullChrome.svelte.d.ts +30 -0
  55. package/dist/convex-hull/ConvexHullControls.svelte +88 -7
  56. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +7 -6
  57. package/dist/convex-hull/ConvexHullInfoPane.svelte +18 -5
  58. package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +6 -5
  59. package/dist/convex-hull/ConvexHullStats.svelte +29 -168
  60. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +3 -1
  61. package/dist/convex-hull/ConvexHullTooltip.svelte +11 -2
  62. package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +2 -1
  63. package/dist/convex-hull/barycentric-coords.d.ts +2 -4
  64. package/dist/convex-hull/barycentric-coords.js +6 -33
  65. package/dist/convex-hull/canvas-interactions.svelte.d.ts +79 -0
  66. package/dist/convex-hull/canvas-interactions.svelte.js +278 -0
  67. package/dist/convex-hull/helpers.d.ts +39 -7
  68. package/dist/convex-hull/helpers.js +154 -69
  69. package/dist/convex-hull/hull-state.svelte.d.ts +44 -0
  70. package/dist/convex-hull/hull-state.svelte.js +124 -0
  71. package/dist/convex-hull/index.d.ts +9 -7
  72. package/dist/convex-hull/index.js +7 -2
  73. package/dist/convex-hull/thermodynamics.js +91 -920
  74. package/dist/convex-hull/types.d.ts +12 -4
  75. package/dist/convex-hull/types.js +12 -0
  76. package/dist/coordination/CoordinationBarPlot.svelte +4 -11
  77. package/dist/element/BohrAtom.svelte +2 -1
  78. package/dist/element/ElementTile.svelte.d.ts +1 -1
  79. package/dist/element/index.d.ts +4 -0
  80. package/dist/element/index.js +18 -0
  81. package/dist/feedback/DragOverlay.svelte +3 -1
  82. package/dist/feedback/DragOverlay.svelte.d.ts +1 -0
  83. package/dist/feedback/StatusMessage.svelte +13 -3
  84. package/dist/fermi-surface/FermiSurface.svelte +67 -146
  85. package/dist/fermi-surface/FermiSurface.svelte.d.ts +5 -14
  86. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  87. package/dist/fermi-surface/FermiSurfaceScene.svelte +72 -224
  88. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +3 -23
  89. package/dist/fermi-surface/compute.js +11 -10
  90. package/dist/fermi-surface/export.js +4 -15
  91. package/dist/fermi-surface/index.d.ts +0 -1
  92. package/dist/fermi-surface/index.js +0 -1
  93. package/dist/fermi-surface/parse.d.ts +1 -1
  94. package/dist/fermi-surface/parse.js +64 -75
  95. package/dist/fermi-surface/types.d.ts +2 -2
  96. package/dist/heatmap-matrix/HeatmapMatrix.svelte +55 -40
  97. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +4 -3
  98. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +3 -2
  99. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +5 -5
  100. package/dist/heatmap-matrix/index.d.ts +3 -2
  101. package/dist/index.d.ts +1 -0
  102. package/dist/index.js +1 -0
  103. package/dist/io/ExportPane.svelte +166 -0
  104. package/dist/io/ExportPane.svelte.d.ts +17 -0
  105. package/dist/io/decompress.js +1 -2
  106. package/dist/io/export.d.ts +5 -1
  107. package/dist/io/export.js +32 -28
  108. package/dist/io/fetch.d.ts +2 -1
  109. package/dist/io/file-drop.d.ts +7 -0
  110. package/dist/io/file-drop.js +13 -0
  111. package/dist/io/index.d.ts +2 -0
  112. package/dist/io/index.js +10 -0
  113. package/dist/io/types.d.ts +13 -0
  114. package/dist/isosurface/parse.js +46 -44
  115. package/dist/labels.js +1 -1
  116. package/dist/layout/FullscreenButton.svelte +33 -0
  117. package/dist/layout/FullscreenButton.svelte.d.ts +10 -0
  118. package/dist/layout/FullscreenToggle.svelte +8 -14
  119. package/dist/layout/ViewerChrome.svelte +116 -0
  120. package/dist/layout/ViewerChrome.svelte.d.ts +17 -0
  121. package/dist/layout/fullscreen.d.ts +4 -0
  122. package/dist/layout/fullscreen.svelte.d.ts +8 -0
  123. package/dist/layout/fullscreen.svelte.js +37 -0
  124. package/dist/layout/index.d.ts +3 -0
  125. package/dist/layout/index.js +3 -0
  126. package/dist/math.d.ts +7 -3
  127. package/dist/math.js +18 -21
  128. package/dist/overlays/index.d.ts +4 -0
  129. package/dist/periodic-table/PeriodicTable.svelte +9 -8
  130. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
  131. package/dist/phase-diagram/PhaseDiagramControls.svelte +3 -2
  132. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +4 -3
  133. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
  134. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +2 -3
  135. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +47 -132
  136. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +3 -4
  137. package/dist/phase-diagram/colors.js +1 -1
  138. package/dist/phase-diagram/parse.d.ts +2 -1
  139. package/dist/plot/bar/BarPlot.svelte +79 -316
  140. package/dist/plot/bar/BarPlot.svelte.d.ts +7 -15
  141. package/dist/plot/bar/BarPlotControls.svelte.d.ts +1 -1
  142. package/dist/plot/bar/SpacegroupBarPlot.svelte +2 -1
  143. package/dist/plot/box/BoxPlot.svelte +76 -246
  144. package/dist/plot/box/BoxPlot.svelte.d.ts +4 -3
  145. package/dist/plot/box/BoxPlotControls.svelte.d.ts +1 -1
  146. package/dist/plot/box/Violin.svelte.d.ts +1 -1
  147. package/dist/plot/box/box-plot.d.ts +3 -2
  148. package/dist/plot/box/box-plot.js +5 -2
  149. package/dist/plot/box/kde.d.ts +2 -1
  150. package/dist/plot/box/kde.js +4 -4
  151. package/dist/plot/core/auto-place.d.ts +1 -1
  152. package/dist/plot/core/auto-place.js +4 -1
  153. package/dist/plot/core/components/ColorBar.svelte +5 -5
  154. package/dist/plot/core/components/ColorBar.svelte.d.ts +5 -4
  155. package/dist/plot/core/components/Line.svelte +3 -2
  156. package/dist/plot/core/components/Line.svelte.d.ts +3 -2
  157. package/dist/plot/core/components/PlotAxis.svelte +2 -1
  158. package/dist/plot/core/components/PlotAxis.svelte.d.ts +2 -1
  159. package/dist/plot/core/components/PlotControls.svelte.d.ts +1 -1
  160. package/dist/plot/core/components/ReferenceLine3D.svelte +2 -2
  161. package/dist/plot/core/components/ReferenceLine3D.svelte.d.ts +4 -4
  162. package/dist/plot/core/components/ReferencePlane.svelte +2 -2
  163. package/dist/plot/core/components/ReferencePlane.svelte.d.ts +4 -4
  164. package/dist/plot/core/data-cleaning.js +18 -18
  165. package/dist/plot/core/fill-utils.d.ts +4 -3
  166. package/dist/plot/core/fill-utils.js +6 -3
  167. package/dist/plot/core/interactions.d.ts +5 -1
  168. package/dist/plot/core/interactions.js +14 -0
  169. package/dist/plot/core/pan-zoom.svelte.d.ts +35 -0
  170. package/dist/plot/core/pan-zoom.svelte.js +221 -0
  171. package/dist/plot/core/placed-tween.svelte.d.ts +21 -0
  172. package/dist/plot/core/placed-tween.svelte.js +68 -0
  173. package/dist/plot/core/reference-line.d.ts +10 -10
  174. package/dist/plot/core/reference-line.js +6 -6
  175. package/dist/plot/core/scales.d.ts +17 -25
  176. package/dist/plot/core/scales.js +10 -8
  177. package/dist/plot/core/svg.d.ts +2 -1
  178. package/dist/plot/core/types.d.ts +18 -7
  179. package/dist/plot/core/utils/label-placement.d.ts +1 -1
  180. package/dist/plot/core/utils/label-placement.js +3 -3
  181. package/dist/plot/core/utils.d.ts +2 -1
  182. package/dist/plot/histogram/Histogram.svelte +77 -314
  183. package/dist/plot/histogram/HistogramControls.svelte.d.ts +1 -1
  184. package/dist/plot/sankey/Sankey.svelte +2 -5
  185. package/dist/plot/sankey/Sankey.svelte.d.ts +1 -1
  186. package/dist/plot/sankey/sankey.js +3 -1
  187. package/dist/plot/scatter/BinnedScatterPlot.svelte +3 -5
  188. package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +4 -4
  189. package/dist/plot/scatter/ScatterPlot.svelte +160 -450
  190. package/dist/plot/scatter/ScatterPlot.svelte.d.ts +7 -15
  191. package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
  192. package/dist/plot/scatter/binned-scatter-types.d.ts +4 -11
  193. package/dist/plot/scatter/index.d.ts +1 -1
  194. package/dist/plot/scatter-3d/ScatterPlot3D.svelte +15 -26
  195. package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +6 -14
  196. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +9 -10
  197. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte.d.ts +5 -5
  198. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +122 -121
  199. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +5 -14
  200. package/dist/plot/scatter-3d/Surface3D.svelte +6 -5
  201. package/dist/plot/scatter-3d/Surface3D.svelte.d.ts +4 -3
  202. package/dist/plot/sunburst/Sunburst.svelte +16 -20
  203. package/dist/plot/sunburst/Sunburst.svelte.d.ts +4 -3
  204. package/dist/plot/sunburst/SunburstControls.svelte.d.ts +1 -1
  205. package/dist/plot/sunburst/sunburst.js +4 -1
  206. package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
  207. package/dist/sanitize.js +13 -2
  208. package/dist/scene/SceneCamera.svelte +62 -0
  209. package/dist/scene/SceneCamera.svelte.d.ts +19 -0
  210. package/dist/scene/bind-renderer.svelte.d.ts +2 -0
  211. package/dist/scene/bind-renderer.svelte.js +14 -0
  212. package/dist/scene/index.d.ts +4 -0
  213. package/dist/scene/index.js +5 -0
  214. package/dist/scene/props.js +52 -0
  215. package/dist/scene/types.d.ts +26 -0
  216. package/dist/scene/types.js +1 -0
  217. package/dist/settings.d.ts +14 -2
  218. package/dist/settings.js +59 -1
  219. package/dist/spectral/Bands.svelte +8 -7
  220. package/dist/spectral/Bands.svelte.d.ts +3 -2
  221. package/dist/spectral/BandsAndDos.svelte +22 -24
  222. package/dist/spectral/BrillouinBandsDos.svelte +3 -3
  223. package/dist/spectral/Dos.svelte +5 -4
  224. package/dist/spectral/Dos.svelte.d.ts +2 -1
  225. package/dist/spectral/helpers.d.ts +6 -6
  226. package/dist/spectral/helpers.js +43 -37
  227. package/dist/state.svelte.d.ts +0 -7
  228. package/dist/state.svelte.js +0 -6
  229. package/dist/structure/Arrow.svelte +2 -4
  230. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  231. package/dist/structure/CanvasTooltip.svelte +1 -0
  232. package/dist/structure/CellSelect.svelte +11 -3
  233. package/dist/structure/CellSelect.svelte.d.ts +2 -1
  234. package/dist/structure/Lattice.svelte +2 -2
  235. package/dist/structure/Structure.svelte +291 -355
  236. package/dist/structure/Structure.svelte.d.ts +5 -15
  237. package/dist/structure/StructureControls.svelte +217 -2
  238. package/dist/structure/StructureControls.svelte.d.ts +5 -3
  239. package/dist/structure/StructureExportPane.svelte +54 -156
  240. package/dist/structure/StructureExportPane.svelte.d.ts +4 -5
  241. package/dist/structure/StructureInfoPane.svelte +5 -3
  242. package/dist/structure/StructureInfoPane.svelte.d.ts +5 -5
  243. package/dist/structure/StructureScene.svelte +365 -198
  244. package/dist/structure/StructureScene.svelte.d.ts +22 -20
  245. package/dist/structure/{label-placement.d.ts → atom-label-placement.d.ts} +3 -3
  246. package/dist/structure/{label-placement.js → atom-label-placement.js} +12 -2
  247. package/dist/structure/atom-properties.d.ts +1 -1
  248. package/dist/structure/atom-properties.js +11 -16
  249. package/dist/structure/bond-order-perception.js +2 -4
  250. package/dist/structure/bonding.d.ts +3 -0
  251. package/dist/structure/bonding.js +91 -48
  252. package/dist/structure/export.d.ts +24 -4
  253. package/dist/structure/export.js +64 -122
  254. package/dist/structure/index.d.ts +2 -0
  255. package/dist/structure/index.js +2 -0
  256. package/dist/structure/parse.d.ts +3 -2
  257. package/dist/structure/parse.js +333 -370
  258. package/dist/structure/partial-occupancy.d.ts +0 -1
  259. package/dist/structure/partial-occupancy.js +1 -1
  260. package/dist/structure/pbc.d.ts +1 -1
  261. package/dist/structure/pbc.js +186 -13
  262. package/dist/structure/polyhedra.d.ts +41 -0
  263. package/dist/structure/polyhedra.js +602 -0
  264. package/dist/structure/site.d.ts +4 -0
  265. package/dist/structure/site.js +1 -0
  266. package/dist/structure/supercell.js +3 -2
  267. package/dist/structure/validation.js +5 -6
  268. package/dist/symmetry/SymmetryElementControls.svelte +69 -0
  269. package/dist/symmetry/SymmetryElementControls.svelte.d.ts +9 -0
  270. package/dist/symmetry/SymmetryElements.svelte +354 -0
  271. package/dist/symmetry/SymmetryElements.svelte.d.ts +24 -0
  272. package/dist/symmetry/SymmetryStats.svelte +111 -6
  273. package/dist/symmetry/WyckoffTable.svelte +68 -7
  274. package/dist/symmetry/WyckoffTable.svelte.d.ts +3 -0
  275. package/dist/symmetry/cell-transform.js +7 -14
  276. package/dist/symmetry/index.d.ts +14 -4
  277. package/dist/symmetry/index.js +301 -80
  278. package/dist/symmetry/spacegroups.d.ts +5 -1
  279. package/dist/symmetry/spacegroups.js +15 -1
  280. package/dist/symmetry/symmetry-elements.d.ts +33 -0
  281. package/dist/symmetry/symmetry-elements.js +521 -0
  282. package/dist/symmetry/wyckoff-db.d.ts +9 -0
  283. package/dist/symmetry/wyckoff-db.js +87 -0
  284. package/dist/table/HeatmapTable.svelte +4 -15
  285. package/dist/table/HeatmapTable.svelte.d.ts +1 -1
  286. package/dist/trajectory/Trajectory.svelte +58 -61
  287. package/dist/trajectory/Trajectory.svelte.d.ts +10 -22
  288. package/dist/trajectory/TrajectoryExportPane.svelte +15 -24
  289. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +4 -5
  290. package/dist/trajectory/TrajectoryInfoPane.svelte +3 -2
  291. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +3 -2
  292. package/dist/trajectory/constants.js +6 -2
  293. package/dist/trajectory/extract.js +17 -37
  294. package/dist/trajectory/format-detect.d.ts +0 -1
  295. package/dist/trajectory/format-detect.js +3 -9
  296. package/dist/trajectory/frame-reader.d.ts +0 -1
  297. package/dist/trajectory/frame-reader.js +62 -128
  298. package/dist/trajectory/helpers.d.ts +10 -2
  299. package/dist/trajectory/helpers.js +56 -36
  300. package/dist/trajectory/parse/ase.d.ts +9 -1
  301. package/dist/trajectory/parse/ase.js +47 -32
  302. package/dist/trajectory/parse/diagnostics.d.ts +3 -0
  303. package/dist/trajectory/parse/diagnostics.js +14 -0
  304. package/dist/trajectory/parse/index.d.ts +1 -1
  305. package/dist/trajectory/parse/index.js +54 -102
  306. package/dist/trajectory/parse/lammps.d.ts +0 -2
  307. package/dist/trajectory/parse/lammps.js +8 -6
  308. package/dist/trajectory/parse/pymatgen.d.ts +2 -0
  309. package/dist/trajectory/parse/pymatgen.js +74 -0
  310. package/dist/trajectory/parse/vasp.js +4 -3
  311. package/dist/trajectory/parse/xyz.d.ts +9 -21
  312. package/dist/trajectory/parse/xyz.js +28 -33
  313. package/dist/trajectory/plotting.d.ts +0 -1
  314. package/dist/trajectory/plotting.js +3 -100
  315. package/dist/utils.d.ts +1 -0
  316. package/dist/utils.js +1 -1
  317. package/dist/xrd/XrdPlot.svelte +14 -29
  318. package/dist/xrd/broadening.d.ts +2 -1
  319. package/dist/xrd/calc-xrd.js +6 -11
  320. package/dist/xrd/index.d.ts +2 -2
  321. package/package.json +29 -16
  322. package/dist/element/data.json +0 -11864
  323. package/dist/fermi-surface/marching-cubes.d.ts +0 -2
  324. package/dist/fermi-surface/marching-cubes.js +0 -2
  325. package/dist/plot/core/hover-lock.svelte.d.ts +0 -14
  326. package/dist/plot/core/hover-lock.svelte.js +0 -45
@@ -1,10 +1,17 @@
1
1
  import { ATOMIC_NUMBER_TO_SYMBOL, SYMBOL_TO_ATOMIC_NUMBER } from '../composition/parse';
2
+ import * as math from '../math';
2
3
  import { DEFAULTS } from '../settings';
3
4
  import { merge_split_partial_sites } from '../structure/partial-occupancy';
4
5
  import init, { analyze_cell } from '@spglib/moyo-wasm';
5
6
  import moyo_wasm_url from '@spglib/moyo-wasm/moyo_wasm_bg.wasm?url';
7
+ import { mat3_from_flat_col_major } from './symmetry-elements';
8
+ import { wyckoff_letter } from './wyckoff-db';
6
9
  export * from './cell-transform';
7
10
  export * from './spacegroups';
11
+ export * from './symmetry-elements';
12
+ export * from './wyckoff-db';
13
+ export { default as SymmetryElementControls } from './SymmetryElementControls.svelte';
14
+ export { default as SymmetryElements } from './SymmetryElements.svelte';
8
15
  export { default as SymmetryStats } from './SymmetryStats.svelte';
9
16
  export { default as WyckoffTable } from './WyckoffTable.svelte';
10
17
  // Keys are standard crystallographic symbols (P, I, F, A, B, C, R)
@@ -27,13 +34,37 @@ const to_unit = (value) => value - Math.floor(value);
27
34
  const near_zero = (value) => Math.min(value, 1 - value);
28
35
  const near_half = (value) => Math.abs(value - 0.5);
29
36
  const symmetry_position_key = (pos) => pos.map((coord) => to_unit(coord).toFixed(8)).join(`,`);
30
- const periodic_distance = (pos1, pos2) => Math.sqrt(pos1.reduce((sum, coord, idx) => {
31
- // Wrap delta into [-0.5, 0.5) using safe modulo
32
- const delta = coord - pos2[idx];
33
- const wrapped = ((((delta + 0.5) % 1) + 1) % 1) - 0.5;
34
- const distance = Math.abs(wrapped);
35
- return sum + distance * distance;
36
- }, 0));
37
+ const is_near_integer_vec = (vec, tol) => vec.every((coord) => Math.abs(coord - Math.round(coord)) < tol);
38
+ // Wrap fractional coordinates into [0, 1), snapping values within 1e-9 of 1 back to 0.
39
+ // NOTE on epsilon: 1e-9 is deliberately looser than wrap_frac_coord @1e-10
40
+ // [[src/lib/structure/pbc.ts:26]] because inputs here passed through moyo
41
+ // standardization plus a P⁻¹(x − p) matrix transform (make_frac_coord_mapper),
42
+ // accumulating more float error than freshly parsed coords. It is tighter than
43
+ // wrap_point @1e-8 [[src/lib/symmetry/symmetry-elements.ts:214]], which must
44
+ // keep dedup keys stable for fixed points solved from linear systems. Do not
45
+ // unify: each epsilon matches the noise level of its inputs.
46
+ const wrap_frac = (pos) => pos.map((coord) => {
47
+ const wrapped = coord - Math.floor(coord);
48
+ return wrapped > 1 - 1e-9 ? 0 : wrapped;
49
+ });
50
+ // Build a mapper from input-cell to standardized-cell fractional coordinates.
51
+ // moyo's (std_linear P, std_origin_shift p) follow the ITA convention for the
52
+ // transformation from the input cell to the standardized cell: x_std = P⁻¹ (x_input − p).
53
+ // Returns null if std_linear is absent or singular.
54
+ function make_frac_coord_mapper(linear_flat, origin_shift) {
55
+ if (linear_flat?.length !== 9)
56
+ return null;
57
+ try {
58
+ const linear = mat3_from_flat_col_major(linear_flat);
59
+ const linear_inv = math.matrix_inverse_3x3(linear);
60
+ const shift = (origin_shift ?? [0, 0, 0]);
61
+ const to_std = (pos) => math.mat3x3_vec3_multiply(linear_inv, math.subtract(pos, shift));
62
+ return { to_std, linear, linear_inv, shift };
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
37
68
  export async function ensure_moyo_wasm_ready(wasm_url) {
38
69
  if (initialized)
39
70
  return;
@@ -93,23 +124,42 @@ export function to_cell_json(structure) {
93
124
  const fractional_sq_dist = (pos_1, pos_2) => (pos_1[0] - pos_2[0] - Math.round(pos_1[0] - pos_2[0])) ** 2 +
94
125
  (pos_1[1] - pos_2[1] - Math.round(pos_1[1] - pos_2[1])) ** 2 +
95
126
  (pos_1[2] - pos_2[2] - Math.round(pos_1[2] - pos_2[2])) ** 2;
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;
127
+ // Map each standardized-cell site to the input-cell site it descends from, then to
128
+ // original structure indices. When moyo's (std_linear, std_origin_shift) transform is
129
+ // provided, std positions are first mapped into the input frame via x_in = P·x_std + p
130
+ // (the std and input cells use DIFFERENT bases in general, so comparing raw fractional
131
+ // coordinates across them is meaningless). Matches account for crystal translations of
132
+ // both lattices: a difference d is a translation if d ∈ ℤ³ (input lattice) or P⁻¹d ∈ ℤ³
133
+ // (standardized lattice expressed in the input frame).
134
+ export const map_std_to_orig_site_indices = (std_positions, std_numbers, input_positions, input_numbers, orig_site_indices_by_input_idx, transform, tol = 1e-4) => {
135
+ const mapper = make_frac_coord_mapper(transform?.std_linear, transform?.std_origin_shift);
136
+ return std_positions.map((std_pos, std_idx) => {
137
+ // Predicted input-frame position of this std site: x_in = P·x_std + p
138
+ const pred = mapper
139
+ ? math.add(math.mat3x3_vec3_multiply(mapper.linear, std_pos), mapper.shift)
140
+ : std_pos;
141
+ const std_number = std_numbers[std_idx];
142
+ let nearest_input_idx = -1;
143
+ let nearest_sq_dist = Infinity;
144
+ for (let input_idx = 0; input_idx < input_positions.length; input_idx += 1) {
145
+ if (input_numbers[input_idx] !== std_number)
146
+ continue;
147
+ const delta = math.subtract(pred, input_positions[input_idx]);
148
+ // Exact match modulo a translation of the standardized lattice
149
+ const is_std_translation = mapper && is_near_integer_vec(math.mat3x3_vec3_multiply(mapper.linear_inv, delta), tol);
150
+ const sq_dist = is_std_translation
151
+ ? 0
152
+ : fractional_sq_dist(pred, input_positions[input_idx]);
153
+ if (sq_dist < nearest_sq_dist) {
154
+ nearest_sq_dist = sq_dist;
155
+ nearest_input_idx = input_idx;
156
+ }
107
157
  }
108
- }
109
- if (nearest_input_idx === -1)
110
- return [];
111
- return orig_site_indices_by_input_idx[nearest_input_idx] ?? [];
112
- });
158
+ if (nearest_input_idx === -1)
159
+ return [];
160
+ return orig_site_indices_by_input_idx[nearest_input_idx] ?? [];
161
+ });
162
+ };
113
163
  export async function analyze_structure_symmetry(struct_or_mol, settings) {
114
164
  await ensure_moyo_wasm_ready();
115
165
  if (!(`lattice` in struct_or_mol)) {
@@ -121,8 +171,16 @@ export async function analyze_structure_symmetry(struct_or_mol, settings) {
121
171
  // Map "Moyo" to "Standard" for moyo-wasm
122
172
  const moyo_algo = algo === `Moyo` ? `Standard` : algo;
123
173
  const sym_data = analyze_cell(cell_json, symprec, moyo_algo);
124
- const orig_site_indices_by_std_idx = map_std_to_orig_site_indices(sym_data.std_cell.positions, sym_data.std_cell.numbers, moyo_input_cell.positions, moyo_input_cell.numbers, moyo_input_cell.orig_site_indices_by_input_idx);
125
- return { ...sym_data, orig_site_indices_by_std_idx };
174
+ const orig_site_indices_by_std_idx = map_std_to_orig_site_indices(sym_data.std_cell.positions, sym_data.std_cell.numbers, moyo_input_cell.positions, moyo_input_cell.numbers, moyo_input_cell.orig_site_indices_by_input_idx, { std_linear: sym_data.std_linear, std_origin_shift: sym_data.std_origin_shift }, Math.max(1e-5, symprec * 10));
175
+ return {
176
+ ...sym_data,
177
+ orig_site_indices_by_std_idx,
178
+ input_cell: {
179
+ positions: moyo_input_cell.positions,
180
+ numbers: moyo_input_cell.numbers,
181
+ },
182
+ orig_site_indices_by_input_idx: moyo_input_cell.orig_site_indices_by_input_idx,
183
+ };
126
184
  }
127
185
  // Helper function to score coordinate simplicity for Wyckoff table
128
186
  export function simplicity_score(vec) {
@@ -132,59 +190,67 @@ export function simplicity_score(vec) {
132
190
  near_zero(az) +
133
191
  0.5 * (near_half(ax) + near_half(ay) + near_half(az)));
134
192
  }
135
- // Generate Wyckoff table rows from symmetry data
136
- export function wyckoff_positions_from_moyo(sym_data) {
137
- if (!sym_data)
138
- return [];
139
- const { positions, numbers } = sym_data.std_cell;
140
- const { wyckoffs, orig_indices, orig_site_indices_by_std_idx } = sym_data;
141
- // Group sites by letter-element combination and track all indices
142
- const groups = new Map();
143
- // Process all atoms in the standardized cell
144
- // Note: wyckoffs array may be shorter than std_cell when moyo combines symmetry-equivalent sites
145
- for (let idx = 0; idx < numbers.length; idx++) {
146
- // Use wyckoff letter if available, otherwise mark as non-symmetric
147
- const full = idx < wyckoffs.length ? wyckoffs[idx] : null;
148
- const letter = full?.match(/[a-z]+$/)?.[0] ?? full ?? ``;
149
- const atomic_num = numbers[idx];
150
- const elem = ATOMIC_NUMBER_TO_SYMBOL[atomic_num] ?? `?`;
151
- const position = positions[idx];
152
- const key = letter ? `${letter}|${elem}` : `nosym|${elem}|${idx}`;
153
- const group = groups.get(key) ?? { letter, elem, indices: [], positions: [] };
154
- group.indices.push(idx);
155
- group.positions.push(position);
156
- groups.set(key, group);
157
- }
158
- const rows = Array.from(groups.values()).map(({ letter, elem, indices, positions: group_positions }) => {
159
- // Find the position with the best simplicity score to display
160
- const best_pos = group_positions.reduce((best, pos) => {
161
- const score = simplicity_score(pos);
162
- return score < best.score ? { pos, score } : best;
163
- }, { pos: group_positions[0], score: simplicity_score(group_positions[0]) }).pos;
164
- // Map standardized cell indices back to original structure indices
165
- const orig_site_indices = orig_site_indices_by_std_idx
166
- ? indices.flatMap((std_idx) => orig_site_indices_by_std_idx[std_idx] ?? [])
167
- : orig_indices
168
- ? indices.map((std_idx) => orig_indices[std_idx]).filter((idx) => idx !== undefined)
169
- : indices;
170
- const wyckoff = letter ? `${indices.length}${letter}` : `1`;
193
+ // Pick the representative coordinate with the lowest simplicity score (ties keep first)
194
+ const simplest_position = (positions) => positions.reduce((best, pos) => simplicity_score(pos) < simplicity_score(best) ? pos : best);
195
+ // Build Wyckoff rows from moyo's input-cell orbits. moyo's per-site arrays (wyckoffs,
196
+ // orbits, site_symmetry_symbols) are indexed by INPUT cell sites — NOT std_cell sites —
197
+ // so rows must be derived by grouping input sites into crystallographic orbits.
198
+ // Multiplicity in the conventional cell is the orbit size scaled by the std/input cell
199
+ // size ratio (e.g. a 1-atom primitive FCC input has orbit size 1 but multiplicity 4).
200
+ function wyckoff_rows_from_input_orbits(sym_data) {
201
+ const { input_cell, orbits, wyckoffs, site_symmetry_symbols, std_cell, orig_site_indices_by_input_idx, } = sym_data;
202
+ const n_input = input_cell?.positions.length ?? 0;
203
+ if (!input_cell || n_input === 0)
204
+ return null;
205
+ if (orbits?.length !== n_input || wyckoffs?.length !== n_input)
206
+ return null;
207
+ const mapper = make_frac_coord_mapper(sym_data.std_linear, sym_data.std_origin_shift);
208
+ if (!mapper)
209
+ return null;
210
+ // Group input-cell sites by crystallographic orbit (keyed by orbit representative).
211
+ // Distinct orbits sharing the same Wyckoff letter and element stay separate rows.
212
+ const orbit_members = new Map();
213
+ orbits.forEach((rep, idx) => {
214
+ const members = orbit_members.get(rep) ?? [];
215
+ members.push(idx);
216
+ orbit_members.set(rep, members);
217
+ });
218
+ const n_std = std_cell?.positions.length ?? n_input;
219
+ return [...orbit_members.entries()].map(([rep, members]) => {
220
+ const letter = wyckoff_letter(wyckoffs[rep] ?? ``);
221
+ const elem = ATOMIC_NUMBER_TO_SYMBOL[input_cell.numbers[rep]] ?? `?`;
222
+ const multiplicity = Math.round((members.length * n_std) / n_input);
223
+ // Representative coordinate in the standardized frame, simplest first
224
+ const best_pos = simplest_position(members.map((idx) => wrap_frac(mapper.to_std(input_cell.positions[idx]))));
225
+ const orig_site_indices = members.flatMap((idx) => orig_site_indices_by_input_idx?.[idx] ?? [idx]);
226
+ const site_symmetry = site_symmetry_symbols?.[rep];
171
227
  return {
172
- wyckoff,
228
+ wyckoff: letter ? `${multiplicity}${letter}` : `${multiplicity}`,
173
229
  elem,
174
230
  abc: best_pos,
175
231
  site_indices: [...new Set(orig_site_indices)].sort((idx_a, idx_b) => idx_a - idx_b),
232
+ ...(site_symmetry ? { site_symmetry } : {}),
176
233
  };
177
234
  });
178
- rows.sort((w1, w2) => {
235
+ }
236
+ // Generate Wyckoff table rows from symmetry data by grouping moyo's input-cell sites into
237
+ // crystallographic orbits. moyo's per-site arrays (wyckoffs, orbits, site_symmetry_symbols)
238
+ // always index the input cell and analyze_structure_symmetry always attaches input_cell, so
239
+ // the orbit grouping is the single source of truth for any input cell setting.
240
+ // Rows sort by ascending multiplicity, then Wyckoff label.
241
+ export function wyckoff_positions_from_moyo(sym_data) {
242
+ if (!sym_data)
243
+ return [];
244
+ const orbit_rows = wyckoff_rows_from_input_orbits(sym_data);
245
+ return (orbit_rows ?? []).sort((w1, w2) => {
179
246
  const [w1_mult, w2_mult] = [parseInt(w1.wyckoff, 10), parseInt(w2.wyckoff, 10)];
180
247
  if (w1_mult !== w2_mult)
181
248
  return w1_mult - w2_mult;
182
249
  return w1.wyckoff.localeCompare(w2.wyckoff);
183
250
  });
184
- return rows;
185
251
  }
186
252
  // Apply symmetry operations to find all equivalent positions for a given fractional coordinate
187
- export function apply_symmetry_operations(position, operations, _tolerance = 1e-6) {
253
+ export function apply_symmetry_operations(position, operations) {
188
254
  const seen = new Set();
189
255
  return operations
190
256
  .map(({ rotation, translation }) => {
@@ -203,24 +269,179 @@ export function apply_symmetry_operations(position, operations, _tolerance = 1e-
203
269
  return true;
204
270
  });
205
271
  }
206
- // Map Wyckoff positions to all equivalent atoms in the displayed structure (including image atoms)
272
+ // Build candidate frames for the displayed structure: original, conventional (moyo
273
+ // std_cell), and primitive (moyo prim_std_cell). The displayed structure's fractional
274
+ // coordinates only match symmetry-equivalent positions when expressed in the same frame.
275
+ function candidate_display_frames(orig_structure, sym_data) {
276
+ const frames = [
277
+ {
278
+ lattice: orig_structure.lattice.matrix,
279
+ map_equiv: (pos) => pos,
280
+ input_translation_check: null,
281
+ },
282
+ ];
283
+ const cells = [
284
+ { cell: sym_data.std_cell, linear: sym_data.std_linear, shift: sym_data.std_origin_shift },
285
+ {
286
+ cell: sym_data.prim_std_cell,
287
+ linear: sym_data.prim_std_linear,
288
+ shift: sym_data.prim_std_origin_shift,
289
+ },
290
+ ];
291
+ for (const { cell, linear, shift } of cells) {
292
+ const basis = cell?.lattice?.basis;
293
+ const mapper = make_frac_coord_mapper(linear, shift);
294
+ if (basis?.length !== 9 || !mapper)
295
+ continue;
296
+ // basis is row-major (each row a lattice vector); same reshape as moyo_cell_to_structure
297
+ frames.push({
298
+ lattice: math.vec9_to_mat3x3([...basis]),
299
+ map_equiv: mapper.to_std,
300
+ input_translation_check: mapper.linear,
301
+ });
302
+ }
303
+ return frames;
304
+ }
305
+ // Spatial hash over wrapped fractional coordinates for tolerance-based, mod-1 position
306
+ // lookups. Cell size is chosen ≥ tolerance so probing the ±1 neighbor cells (with
307
+ // wraparound) covers every point within tolerance of the query.
308
+ class WrappedPositionIndex {
309
+ tolerance;
310
+ buckets = new Map();
311
+ coords;
312
+ n_cells;
313
+ constructor(positions, tolerance) {
314
+ this.tolerance = tolerance;
315
+ this.n_cells = Math.min(64, Math.max(1, Math.floor(1 / Math.max(tolerance, 1e-9))));
316
+ this.coords = positions;
317
+ positions.forEach((pos, idx) => {
318
+ const key = this.cell_key(pos, 0, 0, 0);
319
+ const bucket = this.buckets.get(key);
320
+ if (bucket)
321
+ bucket.push(idx);
322
+ else
323
+ this.buckets.set(key, [idx]);
324
+ });
325
+ }
326
+ cell_key(pos, dx, dy, dz) {
327
+ const n_cells = this.n_cells;
328
+ const cell = (coord, offset) => {
329
+ const wrapped = coord - Math.floor(coord);
330
+ return (((Math.floor(wrapped * n_cells) + offset) % n_cells) + n_cells) % n_cells;
331
+ };
332
+ return `${cell(pos[0], dx)},${cell(pos[1], dy)},${cell(pos[2], dz)}`;
333
+ }
334
+ // Indices of stored positions within `tolerance` of `query` modulo ℤ³
335
+ query(query, out) {
336
+ const tol = this.tolerance;
337
+ for (let dx = -1; dx <= 1; dx++) {
338
+ for (let dy = -1; dy <= 1; dy++) {
339
+ for (let dz = -1; dz <= 1; dz++) {
340
+ const bucket = this.buckets.get(this.cell_key(query, dx, dy, dz));
341
+ if (!bucket)
342
+ continue;
343
+ for (const idx of bucket) {
344
+ const pos = this.coords[idx];
345
+ const d0 = pos[0] - query[0];
346
+ const d1 = pos[1] - query[1];
347
+ const d2 = pos[2] - query[2];
348
+ if (Math.abs(d0 - Math.round(d0)) < tol &&
349
+ Math.abs(d1 - Math.round(d1)) < tol &&
350
+ Math.abs(d2 - Math.round(d2)) < tol)
351
+ out.add(idx);
352
+ }
353
+ }
354
+ }
355
+ }
356
+ }
357
+ }
358
+ // Map Wyckoff positions to all equivalent atoms in the displayed structure (including
359
+ // image atoms). Handles displayed structures in the original frame as well as
360
+ // conventional/primitive cell transforms and integer supercells of any of those: the
361
+ // displayed lattice L_disp is matched against each candidate frame's lattice L_F via
362
+ // S = L_disp·L_F⁻¹ (S must be near-integer), displayed coords are converted into the
363
+ // frame via x_F = x_disp·S, and matches allow crystal translations of both the frame
364
+ // lattice (d ∈ ℤ³) and the input lattice (P·d ∈ ℤ³). Matching uses spatial hashing:
365
+ // O(N_disp + N_orig·N_ops) instead of O(N_orig·N_disp·N_ops).
207
366
  export function map_wyckoff_to_all_atoms(wyckoff_positions, displayed_structure, orig_structure, sym_data, tolerance = 1e-5) {
208
367
  if (!sym_data?.operations || !displayed_structure.sites || !orig_structure.sites) {
209
368
  return wyckoff_positions;
210
369
  }
211
- return wyckoff_positions.map((wyckoff_pos) => {
212
- const indices = (wyckoff_pos.site_indices ?? [])
213
- .filter((idx) => idx < orig_structure.sites.length)
214
- .flatMap((orig_idx) => {
215
- const { abc: orig_abc, species } = orig_structure.sites[orig_idx];
216
- const element = species[0]?.element;
217
- const equivalent_positions = apply_symmetry_operations(orig_abc, sym_data.operations, tolerance);
218
- return displayed_structure.sites
219
- .map((site, display_idx) => ({ site, display_idx }))
220
- .filter(({ site }) => site.species[0]?.element === element)
221
- .filter(({ site }) => equivalent_positions.some((equiv_pos) => periodic_distance(equiv_pos, site.abc) < tolerance))
222
- .map(({ display_idx }) => display_idx);
370
+ const map_in_frame = (frame) => {
371
+ // Supercell factor S = L_disp·L_F⁻¹ must be a near-integer matrix with |det| ≥ 1
372
+ let scaling;
373
+ try {
374
+ scaling = math.dot(displayed_structure.lattice.matrix, math.matrix_inverse_3x3(frame.lattice));
375
+ }
376
+ catch {
377
+ return null;
378
+ }
379
+ const is_integer_scaling = scaling.every((row) => row.every((val) => Math.abs(val - Math.round(val)) < tolerance));
380
+ if (!is_integer_scaling || Math.abs(math.det_3x3(scaling)) < 0.99)
381
+ return null;
382
+ // Displayed site coords expressed in frame-F fractional coordinates: x_F = x_disp·S
383
+ const scaling_transpose = math.transpose_3x3_matrix(scaling);
384
+ const displayed_frame_coords = displayed_structure.sites.map((site) => math.mat3x3_vec3_multiply(scaling_transpose, site.abc));
385
+ const displayed_elements = displayed_structure.sites.map((site) => site.species[0]?.element);
386
+ // Spatial hashes: one over the frame coords directly (matches d ∈ ℤ³), and one over
387
+ // P·x_F (matches input-lattice translations: d ∈ P⁻¹ℤ³ ⟺ P·d ∈ ℤ³)
388
+ const direct_index = new WrappedPositionIndex(displayed_frame_coords, tolerance);
389
+ const check = frame.input_translation_check;
390
+ const check_index = check
391
+ ? new WrappedPositionIndex(displayed_frame_coords.map((pos) => math.mat3x3_vec3_multiply(check, pos)), tolerance)
392
+ : null;
393
+ let any_matched = false;
394
+ const rows = wyckoff_positions.map((wyckoff_pos) => {
395
+ // Union the symmetry orbits of all original sites in this row, grouped by element.
396
+ // Sites whose (wrapped) position already appears in the accumulated orbit are
397
+ // skipped — orbit members generate identical orbits, so rows with many sites of
398
+ // one orbit (e.g. supercells) only pay for one full operation sweep
399
+ const equiv_by_element = new Map();
400
+ for (const orig_idx of wyckoff_pos.site_indices ?? []) {
401
+ if (orig_idx >= orig_structure.sites.length)
402
+ continue;
403
+ const { abc: orig_abc, species } = orig_structure.sites[orig_idx];
404
+ const element = species[0]?.element;
405
+ const equivalents = equiv_by_element.get(element) ?? new Map();
406
+ equiv_by_element.set(element, equivalents);
407
+ const member_key = symmetry_position_key(frame.map_equiv(orig_abc.map(to_unit)));
408
+ if (equivalents.has(member_key))
409
+ continue;
410
+ for (const equiv_pos of apply_symmetry_operations(orig_abc, sym_data.operations)) {
411
+ const frame_pos = frame.map_equiv(equiv_pos);
412
+ const key = symmetry_position_key(frame_pos);
413
+ if (!equivalents.has(key))
414
+ equivalents.set(key, frame_pos);
415
+ }
416
+ }
417
+ const matched = new Set();
418
+ for (const [element, equivalents] of equiv_by_element) {
419
+ const candidates = new Set();
420
+ for (const equiv_pos of equivalents.values()) {
421
+ direct_index.query(equiv_pos, candidates);
422
+ if (check && check_index) {
423
+ check_index.query(math.mat3x3_vec3_multiply(check, equiv_pos), candidates);
424
+ }
425
+ }
426
+ for (const display_idx of candidates) {
427
+ if (displayed_elements[display_idx] === element)
428
+ matched.add(display_idx);
429
+ }
430
+ }
431
+ if (matched.size > 0)
432
+ any_matched = true;
433
+ return { ...wyckoff_pos, site_indices: [...matched].sort((a, b) => a - b) };
223
434
  });
224
- return { ...wyckoff_pos, site_indices: [...new Set(indices)].sort((a, b) => a - b) };
225
- });
435
+ return any_matched || displayed_structure.sites.length === 0 ? rows : null;
436
+ };
437
+ // Try frames in order; accept the first whose lattice fits AND that matches any site
438
+ // (lattices can coincide across frames while origins differ, so a lattice match alone
439
+ // is not conclusive)
440
+ for (const frame of candidate_display_frames(orig_structure, sym_data)) {
441
+ const rows = map_in_frame(frame);
442
+ if (rows)
443
+ return rows;
444
+ }
445
+ // No frame fits — site indices into the displayed structure cannot be determined
446
+ return wyckoff_positions.map((pos) => ({ ...pos, site_indices: [] }));
226
447
  }
@@ -1,10 +1,14 @@
1
+ import type { Vec2 } from '../math';
1
2
  import type { SunburstNode } from '../plot/core/types';
2
- export declare const CRYSTAL_SYSTEM_RANGES: Record<CrystalSystem, [number, number]>;
3
+ export declare const CRYSTAL_SYSTEM_RANGES: Record<CrystalSystem, Vec2>;
3
4
  export declare const CRYSTAL_SYSTEM_COLORS: Record<CrystalSystem, string>;
4
5
  export declare const CRYSTAL_SYSTEMS: readonly ["triclinic", "monoclinic", "orthorhombic", "tetragonal", "trigonal", "hexagonal", "cubic"];
5
6
  export type CrystalSystem = (typeof CRYSTAL_SYSTEMS)[number];
6
7
  export declare function spacegroup_num_to_crystal_sys(spacegroup: number): CrystalSystem | null;
7
8
  export declare function spacegroup_to_crystal_sys(spacegroup: number | string): CrystalSystem | null;
9
+ export declare const RHOMBOHEDRAL_SPACEGROUPS: readonly number[];
10
+ export type LatticeSystem = Exclude<CrystalSystem, `trigonal`> | `rhombohedral`;
11
+ export declare function spacegroup_num_to_lattice_system(spacegroup: number): LatticeSystem | null;
8
12
  export declare function normalize_spacegroup(spacegroup: number | string): number | null;
9
13
  export declare const SPACEGROUP_SYMBOL_TO_NUM: Record<string, number>;
10
14
  export declare const SPACEGROUP_NUM_TO_SYMBOL: Record<number, string>;
@@ -43,6 +43,20 @@ export function spacegroup_to_crystal_sys(spacegroup) {
43
43
  const num = normalize_spacegroup(spacegroup);
44
44
  return num == null ? null : spacegroup_num_to_crystal_sys(num);
45
45
  }
46
+ // Trigonal space groups with rhombohedral (R-centered) Bravais lattices. All other
47
+ // trigonal groups have primitive hexagonal lattices.
48
+ export const RHOMBOHEDRAL_SPACEGROUPS = [146, 148, 155, 160, 161, 166, 167];
49
+ // Convert space group number to lattice system (classification of the Bravais lattice).
50
+ // Differs from the crystal system only for trigonal groups: R-centered ones (R3, R-3m, …)
51
+ // have rhombohedral lattices while P-trigonal ones (P3, P-3m1, …) have hexagonal lattices.
52
+ export function spacegroup_num_to_lattice_system(spacegroup) {
53
+ const crystal_sys = spacegroup_num_to_crystal_sys(spacegroup);
54
+ if (crystal_sys === null)
55
+ return null;
56
+ if (crystal_sys !== `trigonal`)
57
+ return crystal_sys;
58
+ return RHOMBOHEDRAL_SPACEGROUPS.includes(spacegroup) ? `rhombohedral` : `hexagonal`;
59
+ }
46
60
  // Normalize space group input (number, Hermann-Mauguin symbol, or numeric string
47
61
  // like "225") to a space group number in [1, 230], or null if invalid
48
62
  export function normalize_spacegroup(spacegroup) {
@@ -202,13 +216,13 @@ export const SPACEGROUP_SYMBOL_TO_NUM = {
202
216
  'I4/mcm': 140,
203
217
  'I4_1/amd': 141,
204
218
  'I4_1/acd': 142,
219
+ // Trigonal
205
220
  P3: 143,
206
221
  P3_1: 144,
207
222
  P3_2: 145,
208
223
  R3: 146,
209
224
  'P-3': 147,
210
225
  'R-3': 148,
211
- // Trigonal
212
226
  P312: 149,
213
227
  P321: 150,
214
228
  P3_112: 151,
@@ -0,0 +1,33 @@
1
+ import type { Matrix3x3, Vec3 } from '../math';
2
+ import type { MoyoDataset } from '@spglib/moyo-wasm';
3
+ export type SymmetryElementKind = `rotation` | `screw` | `mirror` | `glide` | `inversion` | `rotoinversion`;
4
+ export type SymmetryElement = {
5
+ kind: SymmetryElementKind;
6
+ order: number;
7
+ label: string;
8
+ axis: Vec3 | null;
9
+ point: Vec3;
10
+ translation: Vec3 | null;
11
+ };
12
+ export declare const SYM_ELEM_KINDS: readonly ["rotation", "screw", "rotoinversion", "mirror", "glide", "inversion"];
13
+ export type ShowSymmetryKinds = Partial<Record<SymmetryElementKind, boolean>>;
14
+ export declare const DEFAULT_SHOW_SYM_KINDS: ShowSymmetryKinds;
15
+ export declare const SYM_ELEM_KIND_INFO: Record<SymmetryElementKind, {
16
+ label: string;
17
+ color: string;
18
+ }>;
19
+ export declare function count_symmetry_elements(elements: readonly SymmetryElement[]): Partial<Record<SymmetryElementKind, number>>;
20
+ export declare const has_visible_symmetry_overlay: (elements: readonly SymmetryElement[], show_kinds?: ShowSymmetryKinds) => boolean;
21
+ export declare const mat3_from_flat_col_major: (flat: readonly number[]) => Matrix3x3;
22
+ export type ClassifiedOperation = Omit<SymmetryElement, `point`> & {
23
+ point: Vec3;
24
+ };
25
+ export declare function classify_symmetry_op(rotation: readonly number[], translation: readonly number[], centerings?: readonly Vec3[]): ClassifiedOperation | null;
26
+ export declare function symmetry_elements_from_ops(operations: MoyoDataset[`operations`]): SymmetryElement[];
27
+ export declare function dash_segments(length: number, dash: number, gap: number): {
28
+ center: number;
29
+ length: number;
30
+ }[];
31
+ export declare const frac_to_cart_direction: (frac: Vec3, lattice: Matrix3x3) => Vec3;
32
+ export declare function clip_line_to_cell(point: Vec3, direction: Vec3, lattice: Matrix3x3, eps?: number): [Vec3, Vec3] | null;
33
+ export declare function clip_plane_to_cell(point: Vec3, normal_frac: Vec3, lattice: Matrix3x3): Vec3[];