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
@@ -0,0 +1,69 @@
1
+ <!-- Legend + per-kind visibility toggles for symmetry-element overlays. Renders one
2
+ checkbox per element kind present in `elements`, with a color swatch matching the
3
+ overlay render colors and the element count. Bind `show_kinds` and pass it through to
4
+ SymmetryElements (via symmetry_elements_props on the structure viewer). -->
5
+ <script lang="ts">
6
+ import type { ShowSymmetryKinds, SymmetryElement } from './symmetry-elements'
7
+ import {
8
+ count_symmetry_elements,
9
+ DEFAULT_SHOW_SYM_KINDS,
10
+ SYM_ELEM_KIND_INFO,
11
+ SYM_ELEM_KINDS,
12
+ } from './symmetry-elements'
13
+
14
+ let {
15
+ elements = [],
16
+ show_kinds = $bindable({ ...DEFAULT_SHOW_SYM_KINDS }),
17
+ ...rest
18
+ }: {
19
+ elements?: SymmetryElement[]
20
+ show_kinds?: ShowSymmetryKinds
21
+ [key: string]: unknown
22
+ } = $props()
23
+
24
+ const counts = $derived(count_symmetry_elements(elements))
25
+ const present_kinds = $derived(SYM_ELEM_KINDS.filter((kind) => counts[kind]))
26
+
27
+ function toggle_kind(kind: (typeof SYM_ELEM_KINDS)[number], checked: boolean) {
28
+ // Reassign (not mutate) so bound parents always see the change
29
+ show_kinds = { ...show_kinds, [kind]: checked }
30
+ }
31
+ </script>
32
+
33
+ {#if present_kinds.length > 0}
34
+ <div class="sym-elem-controls" {...rest}>
35
+ {#each present_kinds as kind (kind)}
36
+ <label>
37
+ <input
38
+ type="checkbox"
39
+ checked={show_kinds[kind] ?? false}
40
+ onchange={(evt) => toggle_kind(kind, evt.currentTarget.checked)}
41
+ />
42
+ <span class="swatch" style:background={SYM_ELEM_KIND_INFO[kind].color}></span>
43
+ {SYM_ELEM_KIND_INFO[kind].label} ({counts[kind]})
44
+ </label>
45
+ {/each}
46
+ </div>
47
+ {/if}
48
+
49
+ <style>
50
+ .sym-elem-controls {
51
+ display: flex;
52
+ flex-direction: column;
53
+ gap: 3pt;
54
+ }
55
+ label {
56
+ display: flex;
57
+ align-items: center;
58
+ gap: 6pt;
59
+ cursor: pointer;
60
+ font-size: 0.95em;
61
+ }
62
+ .swatch {
63
+ width: 0.9em;
64
+ height: 0.9em;
65
+ border-radius: 2px;
66
+ border: 1px solid var(--border-color, #ccc);
67
+ flex-shrink: 0;
68
+ }
69
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { ShowSymmetryKinds, SymmetryElement } from './symmetry-elements';
2
+ type $$ComponentProps = {
3
+ elements?: SymmetryElement[];
4
+ show_kinds?: ShowSymmetryKinds;
5
+ [key: string]: unknown;
6
+ };
7
+ declare const SymmetryElementControls: import("svelte").Component<$$ComponentProps, {}, "show_kinds">;
8
+ type SymmetryElementControls = ReturnType<typeof SymmetryElementControls>;
9
+ export default SymmetryElementControls;
@@ -0,0 +1,354 @@
1
+ <!-- Render crystallographic symmetry elements (rotation/screw axes, mirror/glide
2
+ planes, inversion centers, rotoinversion axes) inside a Threlte scene. Elements come
3
+ from symmetry_elements_from_ops and are expressed in fractional coordinates of the cell
4
+ described by `lattice` — make sure both refer to the SAME cell (moyo operations are in
5
+ the input-cell frame, so pass the original structure's lattice).
6
+
7
+ Visual conventions (loosely following ITA diagram conventions: translation-carrying
8
+ elements are dashed/striped):
9
+ - rotation axes: solid cylinders, colored by order
10
+ - screw axes: DASHED cylinders (short dashes, gaps narrower than dashes; same order
11
+ colors, slightly thinner)
12
+ - mirror planes: solid translucent fills with opaque outlines
13
+ - glide planes: STRIPED translucent fills (stripes run along the glide-translation
14
+ direction) with opaque outlines
15
+ - inversion centers / rotoinversion markers: small faceted octahedra — themselves
16
+ centrosymmetric, and clearly distinct from the smooth spheres used for atoms
17
+
18
+ For performance, geometries are merged per material group (one draw call per distinct
19
+ color/opacity instead of one mesh per element) and disposed on change/unmount. -->
20
+ <script lang="ts">
21
+ import type { Matrix3x3, Vec2, Vec3 } from '../math'
22
+ import * as math from '../math'
23
+ import type { ShowSymmetryKinds, SymmetryElement } from './symmetry-elements'
24
+ import {
25
+ clip_line_to_cell,
26
+ clip_plane_to_cell,
27
+ dash_segments,
28
+ DEFAULT_SHOW_SYM_KINDS,
29
+ frac_to_cart_direction,
30
+ } from './symmetry-elements'
31
+ import { T } from '@threlte/core'
32
+ import {
33
+ BufferAttribute,
34
+ BufferGeometry,
35
+ ClampToEdgeWrapping,
36
+ CylinderGeometry,
37
+ DataTexture,
38
+ DoubleSide,
39
+ LinearFilter,
40
+ Matrix4,
41
+ OctahedronGeometry,
42
+ Quaternion,
43
+ RepeatWrapping,
44
+ RGBAFormat,
45
+ Vector3,
46
+ } from 'three'
47
+ import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
48
+
49
+ let {
50
+ elements = [],
51
+ lattice,
52
+ // Per-kind visibility. Defaults to rotation axes ONLY: drawing every kind at once
53
+ // buries the structure under overlays for high-symmetry cells. Toggle additional
54
+ // kinds individually (e.g. via SymmetryElementControls).
55
+ show_kinds = DEFAULT_SHOW_SYM_KINDS,
56
+ // hide 2-fold axes that are sub-elements of higher-order axes on the same line
57
+ hide_redundant_axes = true,
58
+ axis_radius = 0.04,
59
+ screw_radius = 0.03,
60
+ // [dash, gap] in Å for dashed screw axes — gap narrower than dash so the line
61
+ // reads as continuous-but-broken rather than sparse
62
+ screw_dash = [0.25, 0.1] as Vec2,
63
+ inversion_radius = 0.12,
64
+ plane_opacity = 0.2,
65
+ glide_opacity = 0.15,
66
+ // opaque polygon outlines make overlapping translucent planes legible
67
+ show_plane_edges = true,
68
+ plane_edge_opacity = 0.9,
69
+ // stripe period in Å for glide-plane fills (stripes run along the glide direction)
70
+ glide_stripe_period = 0.7,
71
+ axis_colors = { 2: `#e63946`, 3: `#2a9d8f`, 4: `#3a6fb0`, 6: `#9c27b0` },
72
+ mirror_color = `#ffb703`,
73
+ glide_color = `#8ecae6`,
74
+ inversion_color = `#555555`,
75
+ }: {
76
+ elements?: SymmetryElement[]
77
+ lattice: Matrix3x3
78
+ show_kinds?: ShowSymmetryKinds
79
+ hide_redundant_axes?: boolean
80
+ axis_radius?: number
81
+ screw_radius?: number
82
+ screw_dash?: Vec2
83
+ inversion_radius?: number
84
+ plane_opacity?: number
85
+ glide_opacity?: number
86
+ show_plane_edges?: boolean
87
+ plane_edge_opacity?: number
88
+ glide_stripe_period?: number
89
+ axis_colors?: Record<number, string>
90
+ mirror_color?: string
91
+ glide_color?: string
92
+ inversion_color?: string
93
+ } = $props()
94
+
95
+ const UP = new Vector3(0, 1, 0)
96
+ const UNIT_SCALE = new Vector3(1, 1, 1)
97
+
98
+ // 1D stripe alpha texture for glide fills (three.js alphaMap samples the GREEN
99
+ // channel): ~55% full-alpha stripe, ~45% faint background so the plane stays
100
+ // contiguous between stripes. Repeats along U; V is constant.
101
+ const stripe_texture = (() => {
102
+ const width = 16
103
+ const data = new Uint8Array(width * 4)
104
+ for (let px = 0; px < width; px++) {
105
+ const val = px < 9 ? 255 : 56
106
+ data.set([val, val, val, 255], px * 4)
107
+ }
108
+ const tex = new DataTexture(data, width, 1, RGBAFormat)
109
+ tex.wrapS = RepeatWrapping
110
+ tex.wrapT = ClampToEdgeWrapping
111
+ tex.magFilter = LinearFilter
112
+ tex.minFilter = LinearFilter
113
+ tex.needsUpdate = true
114
+ return tex
115
+ })()
116
+
117
+ // Key identifying the geometric line of an axis element (direction + intercept)
118
+ const line_key = (elem: SymmetryElement): string => {
119
+ const axis = elem.axis
120
+ if (!axis) return ``
121
+ const lambda = math.dot(elem.point, axis) / math.dot(axis, axis)
122
+ const intercept = elem.point.map((val, idx) => val - lambda * axis[idx])
123
+ return `${axis.join(`,`)}|${intercept.map((val) => val.toFixed(4)).join(`,`)}`
124
+ }
125
+
126
+ type MaterialGroup = {
127
+ geometry: BufferGeometry
128
+ color: string
129
+ opacity?: number
130
+ striped?: boolean
131
+ }
132
+
133
+ // Cylinder of given radius/length centered at `center` pointing along unit `dir`
134
+ const oriented_cylinder = (
135
+ center: Vector3,
136
+ dir_unit: Vector3,
137
+ radius: number,
138
+ length: number,
139
+ ): CylinderGeometry =>
140
+ new CylinderGeometry(radius, radius, length, 12).applyMatrix4(
141
+ new Matrix4().compose(
142
+ center,
143
+ new Quaternion().setFromUnitVectors(UP, dir_unit),
144
+ UNIT_SCALE,
145
+ ),
146
+ )
147
+
148
+ // Axes (cylinders) + rotoinversion center markers (spheres), merged per color/radius.
149
+ // Pure rotations render solid; screw axes render DASHED so the two are
150
+ // distinguishable at a glance (translation-carrying elements are dashed, as in ITA
151
+ // diagrams).
152
+ const axis_groups: MaterialGroup[] = $derived.by(() => {
153
+ const axis_elements = elements.filter(
154
+ (elem) =>
155
+ (elem.kind === `rotation` || elem.kind === `screw` ||
156
+ elem.kind === `rotoinversion`) &&
157
+ show_kinds[elem.kind] && elem.axis,
158
+ )
159
+ // Drop 2-fold axes coincident with a higher-order axis (4 contains 2, 6 contains
160
+ // 2 and 3, -4 contains 2, …) to reduce visual clutter. Computed over the VISIBLE
161
+ // elements only, so 2-folds reappear when their enclosing higher-order kind is
162
+ // toggled off.
163
+ const max_order_by_line = new Map<string, number>()
164
+ for (const elem of axis_elements) {
165
+ const key = line_key(elem)
166
+ max_order_by_line.set(key, Math.max(max_order_by_line.get(key) ?? 0, elem.order))
167
+ }
168
+
169
+ const parts_by_group = new Map<string, BufferGeometry[]>()
170
+ for (const elem of axis_elements) {
171
+ if (
172
+ hide_redundant_axes && elem.order < (max_order_by_line.get(line_key(elem)) ?? 0)
173
+ ) continue
174
+ const clipped = clip_line_to_cell(elem.point, elem.axis as Vec3, lattice)
175
+ if (!clipped) continue
176
+ const [start, end] = clipped
177
+ const span = new Vector3(...math.subtract(end, start))
178
+ const length = span.length()
179
+ if (length < 1e-6) continue
180
+ const dir_unit = span.clone().normalize()
181
+ const start_vec = new Vector3(...start)
182
+
183
+ // Radius is baked into each geometry, so one merged group per color suffices
184
+ const color = axis_colors[elem.order] ?? `#777777`
185
+ const group = parts_by_group.get(color) ?? []
186
+
187
+ if (elem.kind === `screw`) {
188
+ // Dashed cylinder: segments along the axis, touching both cell faces
189
+ for (const dash of dash_segments(length, screw_dash[0], screw_dash[1])) {
190
+ const center = start_vec.clone().addScaledVector(dir_unit, dash.center)
191
+ group.push(oriented_cylinder(center, dir_unit, screw_radius, dash.length))
192
+ }
193
+ } else {
194
+ const center = start_vec.clone().addScaledVector(dir_unit, length / 2)
195
+ group.push(oriented_cylinder(center, dir_unit, axis_radius, length))
196
+ }
197
+ if (elem.kind === `rotoinversion`) {
198
+ const [cx, cy, cz] = frac_to_cart_direction(elem.point, lattice)
199
+ group.push(new OctahedronGeometry(inversion_radius * 0.8).translate(cx, cy, cz))
200
+ }
201
+ parts_by_group.set(color, group)
202
+ }
203
+
204
+ return [...parts_by_group.entries()].flatMap(([color, geometries]) => {
205
+ const merged = mergeGeometries(geometries)
206
+ geometries.forEach((geo) => geo.dispose())
207
+ return merged ? [{ geometry: merged, color }] : []
208
+ })
209
+ })
210
+
211
+ // Visible mirror/glide planes clipped to the cell — computed once, shared by the
212
+ // fill and outline groups below. stripe_dir is the unit Cartesian glide direction
213
+ // (null for mirrors and translation-less entries).
214
+ const visible_planes = $derived.by(() => {
215
+ const planes: {
216
+ polygon: Vec3[]
217
+ color: string
218
+ opacity: number
219
+ stripe_dir: Vec3 | null
220
+ }[] = []
221
+ for (const elem of elements) {
222
+ if ((elem.kind !== `mirror` && elem.kind !== `glide`) || !elem.axis) continue
223
+ if (!show_kinds[elem.kind]) continue
224
+ const polygon = clip_plane_to_cell(elem.point, elem.axis, lattice)
225
+ if (polygon.length < 3) continue
226
+ const is_mirror = elem.kind === `mirror`
227
+ planes.push({
228
+ polygon,
229
+ color: is_mirror ? mirror_color : glide_color,
230
+ opacity: is_mirror ? plane_opacity : glide_opacity,
231
+ stripe_dir: elem.translation
232
+ ? math.normalize_vec(frac_to_cart_direction(elem.translation, lattice))
233
+ : null,
234
+ })
235
+ }
236
+ return planes
237
+ })
238
+
239
+ // Plane FILLS: triangles concatenated per color+opacity into one geometry. Glide
240
+ // fills carry per-vertex UVs whose U coordinate measures Cartesian distance along
241
+ // the glide-translation direction, so the stripe alphaMap renders stripes running
242
+ // along the glide direction — the pattern shows both that the plane glides and
243
+ // where it translates.
244
+ const plane_groups: MaterialGroup[] = $derived.by(() => {
245
+ const groups = new Map<string, { positions: number[]; uvs: number[] }>()
246
+ for (const { polygon, color, opacity, stripe_dir } of visible_planes) {
247
+ const group_key = `${color}|${opacity}|${stripe_dir ? 1 : 0}`
248
+ const group = groups.get(group_key) ?? { positions: [], uvs: [] }
249
+ // Stripe coordinate: Cartesian distance along the glide direction / period
250
+ const stripe_u = (vert: Vec3): number =>
251
+ stripe_dir ? math.dot(vert, stripe_dir) / glide_stripe_period : 0
252
+ // Fan triangulation of the convex polygon
253
+ for (let idx = 1; idx < polygon.length - 1; idx++) {
254
+ for (const vert of [polygon[0], polygon[idx], polygon[idx + 1]]) {
255
+ group.positions.push(...vert)
256
+ group.uvs.push(stripe_u(vert), 0.5)
257
+ }
258
+ }
259
+ groups.set(group_key, group)
260
+ }
261
+ return [...groups.entries()].map(([group_key, { positions, uvs }]) => {
262
+ const geometry = new BufferGeometry()
263
+ geometry.setAttribute(`position`, new BufferAttribute(new Float32Array(positions), 3))
264
+ geometry.setAttribute(`uv`, new BufferAttribute(new Float32Array(uvs), 2))
265
+ geometry.computeVertexNormals()
266
+ const [color, opacity, striped] = group_key.split(`|`)
267
+ return { geometry, color, opacity: Number(opacity), striped: striped === `1` }
268
+ })
269
+ })
270
+
271
+ // Opaque plane OUTLINES (line segments per color): crisp borders keep overlapping
272
+ // translucent planes individually legible instead of blending into a single wash.
273
+ const plane_edge_groups: MaterialGroup[] = $derived.by(() => {
274
+ if (!show_plane_edges) return []
275
+ const segments_by_color = new Map<string, number[]>()
276
+ for (const { polygon, color } of visible_planes) {
277
+ const positions = segments_by_color.get(color) ?? []
278
+ for (let idx = 0; idx < polygon.length; idx++) {
279
+ positions.push(...polygon[idx], ...polygon[(idx + 1) % polygon.length])
280
+ }
281
+ segments_by_color.set(color, positions)
282
+ }
283
+ return [...segments_by_color.entries()].map(([color, positions]) => {
284
+ const geometry = new BufferGeometry()
285
+ geometry.setAttribute(`position`, new BufferAttribute(new Float32Array(positions), 3))
286
+ return { geometry, color }
287
+ })
288
+ })
289
+
290
+ // Inversion centers: faceted octahedra (centrosymmetric, unlike the smooth spheres
291
+ // used for atoms) merged into a single geometry
292
+ const inversion_group: MaterialGroup | null = $derived.by(() => {
293
+ if (!show_kinds.inversion) return null
294
+ const markers = elements
295
+ .filter((elem) => elem.kind === `inversion`)
296
+ .map((elem) => {
297
+ const [cx, cy, cz] = frac_to_cart_direction(elem.point, lattice)
298
+ return new OctahedronGeometry(inversion_radius).translate(cx, cy, cz)
299
+ })
300
+ if (markers.length === 0) return null
301
+ const merged = mergeGeometries(markers)
302
+ markers.forEach((geo) => geo.dispose())
303
+ return merged ? { geometry: merged, color: inversion_color } : null
304
+ })
305
+
306
+ // Dispose each group's merged geometries when that group recomputes or on unmount. One
307
+ // $effect per group (rather than a single combined one): the deriveds recompute
308
+ // independently — e.g. tweaking a plane-only prop rebuilds plane_groups but not
309
+ // axis_groups — and a combined effect would dispose the still-mounted axis geometry.
310
+ const dispose_geometries = (groups: { geometry: BufferGeometry }[]) => () =>
311
+ groups.forEach((group) => group.geometry.dispose())
312
+ $effect(() => dispose_geometries(axis_groups))
313
+ $effect(() => dispose_geometries(plane_groups))
314
+ $effect(() => dispose_geometries(plane_edge_groups))
315
+ $effect(() => dispose_geometries(inversion_group ? [inversion_group] : []))
316
+
317
+ // Dispose the (non-reactive) stripe texture on unmount
318
+ $effect(() => () => stripe_texture.dispose())
319
+ </script>
320
+
321
+ {#each axis_groups as group, idx (idx)}
322
+ <T.Mesh geometry={group.geometry}>
323
+ <T.MeshStandardMaterial color={group.color} />
324
+ </T.Mesh>
325
+ {/each}
326
+
327
+ {#each plane_groups as group, idx (idx)}
328
+ <T.Mesh geometry={group.geometry}>
329
+ <T.MeshStandardMaterial
330
+ color={group.color}
331
+ transparent
332
+ opacity={group.opacity}
333
+ alphaMap={group.striped ? stripe_texture : null}
334
+ side={DoubleSide}
335
+ depthWrite={false}
336
+ />
337
+ </T.Mesh>
338
+ {/each}
339
+
340
+ {#each plane_edge_groups as group, idx (idx)}
341
+ <T.LineSegments geometry={group.geometry}>
342
+ <T.LineBasicMaterial
343
+ color={group.color}
344
+ transparent={plane_edge_opacity < 1}
345
+ opacity={plane_edge_opacity}
346
+ />
347
+ </T.LineSegments>
348
+ {/each}
349
+
350
+ {#if inversion_group}
351
+ <T.Mesh geometry={inversion_group.geometry}>
352
+ <T.MeshStandardMaterial color={inversion_group.color} />
353
+ </T.Mesh>
354
+ {/if}
@@ -0,0 +1,24 @@
1
+ import type { Matrix3x3, Vec2 } from '../math';
2
+ import type { ShowSymmetryKinds, SymmetryElement } from './symmetry-elements';
3
+ type $$ComponentProps = {
4
+ elements?: SymmetryElement[];
5
+ lattice: Matrix3x3;
6
+ show_kinds?: ShowSymmetryKinds;
7
+ hide_redundant_axes?: boolean;
8
+ axis_radius?: number;
9
+ screw_radius?: number;
10
+ screw_dash?: Vec2;
11
+ inversion_radius?: number;
12
+ plane_opacity?: number;
13
+ glide_opacity?: number;
14
+ show_plane_edges?: boolean;
15
+ plane_edge_opacity?: number;
16
+ glide_stripe_period?: number;
17
+ axis_colors?: Record<number, string>;
18
+ mirror_color?: string;
19
+ glide_color?: string;
20
+ inversion_color?: string;
21
+ };
22
+ declare const SymmetryElements: import("svelte").Component<$$ComponentProps, {}, "">;
23
+ type SymmetryElements = ReturnType<typeof SymmetryElements>;
24
+ export default SymmetryElements;
@@ -5,7 +5,15 @@
5
5
  import type { HTMLAttributes } from 'svelte/elements'
6
6
  import { SETTINGS_CONFIG } from '../settings'
7
7
  import type { SymmetrySettings } from './index'
8
- import { default_sym_settings, wyckoff_positions_from_moyo } from './index'
8
+ import {
9
+ count_structure_free_params,
10
+ default_sym_settings,
11
+ enrich_wyckoff_rows,
12
+ spacegroup_settings,
13
+ spacegroup_wyckoff_positions,
14
+ wyckoff_positions_from_moyo,
15
+ wyckoff_sequence,
16
+ } from './index'
9
17
  import * as spg from './spacegroups'
10
18
 
11
19
  type SymmetrySnippet = Snippet<
@@ -29,10 +37,35 @@
29
37
  header?: SymmetrySnippet
30
38
  } = $props()
31
39
 
32
- const wyckoff_count = $derived(
33
- sym_data ? wyckoff_positions_from_moyo(sym_data).length : 0,
40
+ // Space-group Wyckoff database + all settings (empty when WASM is not initialized,
41
+ // e.g. SSR or unit tests — the stats below degrade gracefully)
42
+ const wyckoff_db = $derived(
43
+ sym_data ? spacegroup_wyckoff_positions(sym_data.hall_number) : [],
34
44
  )
45
+ const settings_entries = $derived(sym_data ? spacegroup_settings(sym_data.number) : [])
46
+ const current_setting = $derived(
47
+ settings_entries.find((entry) => entry.hall_number === sym_data?.hall_number),
48
+ )
49
+ const occupied_rows = $derived(
50
+ sym_data ? enrich_wyckoff_rows(wyckoff_positions_from_moyo(sym_data), wyckoff_db) : [],
51
+ )
52
+ const wyckoff_count = $derived(occupied_rows.length)
53
+ const wyckoff_seq = $derived(wyckoff_sequence(occupied_rows))
54
+ // Internal degrees of freedom (null when ITA coordinates are unavailable)
55
+ const free_params = $derived(count_structure_free_params(occupied_rows))
35
56
  const display_hm_symbol = $derived(sym_data?.hm_symbol?.replaceAll(/\s+/g, ``) ?? `?`)
57
+ // Crystal system, plus the lattice system in parens when it differs (e.g. trigonal
58
+ // space groups split into rhombohedral R-centered and hexagonal P lattices)
59
+ const crystal_system_label = $derived.by(() => {
60
+ if (!sym_data) return `?`
61
+ const crystal_sys = spg.spacegroup_to_crystal_sys(sym_data.number)
62
+ const lattice_sys = spg.spacegroup_num_to_lattice_system(sym_data.number)
63
+ if (!crystal_sys) return `?`
64
+ const suffix = lattice_sys && lattice_sys !== crystal_sys
65
+ ? ` (${lattice_sys} lattice)`
66
+ : ``
67
+ return `${crystal_sys}${suffix}`
68
+ })
36
69
 
37
70
  const sym_ops_counts = $derived.by(() => {
38
71
  const EPS = 1e-10
@@ -63,7 +96,7 @@
63
96
  space_group:
64
97
  `International Tables Space group number (1-230) - unique identifier for each space group. Higher numbers indicate more symmetries in the crystal.`,
65
98
  crystal_system:
66
- `Crystal system classification based on the unit cell symmetry. Seven systems: triclinic, monoclinic, orthorhombic, tetragonal, trigonal, hexagonal, and cubic.`,
99
+ `Crystal system classification based on the unit cell symmetry. Seven systems: triclinic, monoclinic, orthorhombic, tetragonal, trigonal, hexagonal, and cubic. For trigonal space groups, the lattice system (rhombohedral for R-centered groups, hexagonal otherwise) is shown in parentheses when it differs from the crystal system.`,
67
100
  hermann_mauguin:
68
101
  `Hermann-Mauguin symbol describes symmetry operations. Format: Lattice type + Point group symmetry. Example: P4/mmm = Primitive + 4-fold rotation + mirror planes`,
69
102
  hall_number:
@@ -74,6 +107,12 @@
74
107
  `Total symmetry operations that map the crystal structure onto itself. Includes rotations, translations, and combinations.`,
75
108
  distinct_orbits:
76
109
  `Number of unique Wyckoff positions (symmetry-equivalent atomic sites) in the crystal structure.`,
110
+ wyckoff_sequence:
111
+ `Wyckoff sequence: letters of all occupied Wyckoff positions in descending alphabetical order, with superscript counts for letters occupied by multiple orbits. A standard structure-type fingerprint (complements the Pearson symbol).`,
112
+ free_params:
113
+ `Internal degrees of freedom: number of free fractional-coordinate parameters (x, y, z in the ITA representative coordinates) summed over occupied Wyckoff orbits. 0 means all atomic positions are fully fixed by symmetry.`,
114
+ settings:
115
+ `All settings of this space group in the International Tables (origin choices, unique axes, cell choices, hexagonal vs rhombohedral axes). The setting detected for this structure is highlighted.`,
77
116
  translations: `Number of translations in the crystal structure.`,
78
117
  rotations: `Number of rotations in the crystal structure.`,
79
118
  roto_translations: `Number of roto-translations in the crystal structure.`,
@@ -151,10 +190,14 @@
151
190
  Space Group <strong>{sym_data.number} ({display_hm_symbol})</strong>
152
191
  </div>
153
192
  <div title={tooltips?.crystal_system} {@attach tooltip()}>
154
- Crystal System <strong>{spg.spacegroup_to_crystal_sys(sym_data.number)}</strong>
193
+ Crystal System <strong>{crystal_system_label}</strong>
155
194
  </div>
156
195
  <div title={tooltips?.hall_number} {@attach tooltip()}>
157
- Hall Number <strong>{sym_data.hall_number}</strong>
196
+ Hall Number <strong>
197
+ {sym_data.hall_number}{
198
+ current_setting ? ` (${current_setting.hall_symbol})` : ``
199
+ }
200
+ </strong>
158
201
  </div>
159
202
  <div title={tooltips?.pearson_symbol} {@attach tooltip()}>
160
203
  Pearson <strong>{sym_data.pearson_symbol}</strong>
@@ -162,6 +205,16 @@
162
205
  <div title={tooltips?.distinct_orbits} {@attach tooltip()}>
163
206
  Wyckoff Positions <strong>{wyckoff_count}</strong>
164
207
  </div>
208
+ {#if wyckoff_seq}
209
+ <div title={tooltips?.wyckoff_sequence} {@attach tooltip()}>
210
+ Wyckoff Sequence <strong>{wyckoff_seq}</strong>
211
+ </div>
212
+ {/if}
213
+ {#if free_params !== null}
214
+ <div title={tooltips?.free_params} {@attach tooltip()}>
215
+ Free Parameters <strong>{free_params}</strong>
216
+ </div>
217
+ {/if}
165
218
  <div
166
219
  class="sym-ops-summary"
167
220
  title="{sym_ops_counts.translations} translations + {sym_ops_counts.rotations} rotations + {sym_ops_counts.roto_translations} roto-translations"
@@ -173,6 +226,39 @@
173
226
  }RT)
174
227
  </div>
175
228
  </div>
229
+ {#if settings_entries.length > 1}
230
+ <details class="settings-explorer">
231
+ <summary title={tooltips?.settings} {@attach tooltip()}>
232
+ {settings_entries.length} settings of space group {sym_data.number}
233
+ </summary>
234
+ <table>
235
+ <thead>
236
+ <tr>
237
+ <th>Hall #</th>
238
+ <th>H-M full</th>
239
+ <th>Hall symbol</th>
240
+ <th>Setting</th>
241
+ <th>Centering</th>
242
+ </tr>
243
+ </thead>
244
+ <tbody>
245
+ {#each settings_entries as entry (entry.hall_number)}
246
+ {@const is_current = entry.hall_number === sym_data.hall_number}
247
+ <tr
248
+ class:current={is_current}
249
+ title={is_current ? `Setting detected for this structure` : null}
250
+ >
251
+ <td>{entry.hall_number}</td>
252
+ <td>{entry.hm_full}</td>
253
+ <td>{entry.hall_symbol}</td>
254
+ <td>{entry.setting || `—`}</td>
255
+ <td>{entry.centering}</td>
256
+ </tr>
257
+ {/each}
258
+ </tbody>
259
+ </table>
260
+ </details>
261
+ {/if}
176
262
  {:else}
177
263
  <div class="no-data">
178
264
  <p>No symmetry data available</p>
@@ -210,6 +296,25 @@
210
296
  .stats-grid strong {
211
297
  margin: var(--sym-stats-strong-margin, 0 0 0 3pt);
212
298
  }
299
+ .settings-explorer {
300
+ margin-block: var(--sym-stats-grid-margin-block, 1ex);
301
+ }
302
+ .settings-explorer summary {
303
+ cursor: pointer;
304
+ color: var(--text-muted, #666);
305
+ }
306
+ .settings-explorer table {
307
+ margin-top: 1ex;
308
+ border-collapse: collapse;
309
+ }
310
+ .settings-explorer :is(th, td) {
311
+ padding: 1px 8px;
312
+ text-align: left;
313
+ }
314
+ .settings-explorer tr.current {
315
+ background: color-mix(in srgb, var(--accent-color, #0066cc) 18%, transparent);
316
+ font-weight: bold;
317
+ }
213
318
  .no-data {
214
319
  display: flex;
215
320
  align-items: center;