matterviz 0.3.2 → 0.3.4

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 (281) hide show
  1. package/dist/EmptyState.svelte +10 -2
  2. package/dist/FilePicker.svelte +123 -82
  3. package/dist/Icon.svelte +18 -12
  4. package/dist/MillerIndexInput.svelte +27 -21
  5. package/dist/api/optimade.js +6 -6
  6. package/dist/app.css +216 -207
  7. package/dist/brillouin/BrillouinZone.svelte +292 -149
  8. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  9. package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
  10. package/dist/brillouin/BrillouinZoneExportPane.svelte +69 -42
  11. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
  12. package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
  13. package/dist/brillouin/BrillouinZoneScene.svelte +275 -163
  14. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  15. package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
  16. package/dist/brillouin/compute.js +11 -6
  17. package/dist/chempot-diagram/ChemPotDiagram.svelte +162 -27
  18. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +451 -281
  19. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2148 -1642
  20. package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -5
  21. package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
  22. package/dist/chempot-diagram/async-compute.svelte.js +77 -0
  23. package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
  24. package/dist/chempot-diagram/chempot-worker.js +11 -0
  25. package/dist/chempot-diagram/color.js +1 -2
  26. package/dist/chempot-diagram/compute.d.ts +10 -0
  27. package/dist/chempot-diagram/compute.js +250 -88
  28. package/dist/chempot-diagram/index.d.ts +2 -1
  29. package/dist/chempot-diagram/index.js +2 -1
  30. package/dist/chempot-diagram/temperature.js +8 -9
  31. package/dist/chempot-diagram/types.d.ts +3 -0
  32. package/dist/chempot-diagram/types.js +1 -0
  33. package/dist/colors/index.d.ts +1 -1
  34. package/dist/colors/index.js +5 -3
  35. package/dist/composition/BarChart.svelte +128 -55
  36. package/dist/composition/BubbleChart.svelte +102 -49
  37. package/dist/composition/Composition.svelte +100 -79
  38. package/dist/composition/Formula.svelte +108 -62
  39. package/dist/composition/FormulaFilter.svelte +665 -537
  40. package/dist/composition/PieChart.svelte +183 -108
  41. package/dist/composition/format.d.ts +5 -0
  42. package/dist/composition/format.js +20 -3
  43. package/dist/composition/parse.js +14 -9
  44. package/dist/convex-hull/ConvexHull.svelte +93 -40
  45. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  46. package/dist/convex-hull/ConvexHull2D.svelte +549 -360
  47. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  48. package/dist/convex-hull/ConvexHull3D.svelte +1296 -827
  49. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  50. package/dist/convex-hull/ConvexHull4D.svelte +1004 -688
  51. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  52. package/dist/convex-hull/ConvexHullControls.svelte +115 -28
  53. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
  54. package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
  55. package/dist/convex-hull/ConvexHullStats.svelte +425 -328
  56. package/dist/convex-hull/ConvexHullTooltip.svelte +40 -16
  57. package/dist/convex-hull/GasPressureControls.svelte +104 -61
  58. package/dist/convex-hull/StructurePopup.svelte +25 -4
  59. package/dist/convex-hull/TemperatureSlider.svelte +45 -25
  60. package/dist/convex-hull/barycentric-coords.js +13 -7
  61. package/dist/convex-hull/demo-temperature.js +8 -4
  62. package/dist/convex-hull/gas-thermodynamics.js +17 -12
  63. package/dist/convex-hull/helpers.d.ts +9 -0
  64. package/dist/convex-hull/helpers.js +77 -34
  65. package/dist/convex-hull/thermodynamics.js +61 -56
  66. package/dist/convex-hull/types.d.ts +9 -14
  67. package/dist/convex-hull/types.js +0 -17
  68. package/dist/coordination/CoordinationBarPlot.svelte +227 -154
  69. package/dist/element/BohrAtom.svelte +55 -12
  70. package/dist/element/ElementHeading.svelte +7 -2
  71. package/dist/element/ElementPhoto.svelte +15 -9
  72. package/dist/element/ElementStats.svelte +10 -4
  73. package/dist/element/ElementTile.svelte +137 -73
  74. package/dist/element/Nucleus.svelte +39 -11
  75. package/dist/element/data.js +1 -1
  76. package/dist/feedback/ClickFeedback.svelte +16 -5
  77. package/dist/feedback/DragOverlay.svelte +10 -2
  78. package/dist/feedback/Spinner.svelte +4 -2
  79. package/dist/feedback/StatusMessage.svelte +8 -2
  80. package/dist/fermi-surface/FermiSlice.svelte +118 -88
  81. package/dist/fermi-surface/FermiSurface.svelte +328 -187
  82. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  83. package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
  84. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  85. package/dist/fermi-surface/FermiSurfaceScene.svelte +535 -342
  86. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  87. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
  88. package/dist/fermi-surface/compute.js +16 -20
  89. package/dist/fermi-surface/parse.js +24 -14
  90. package/dist/fermi-surface/symmetry.js +2 -7
  91. package/dist/fermi-surface/types.d.ts +3 -5
  92. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1019 -765
  93. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +1 -1
  94. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +76 -22
  95. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +2 -3
  96. package/dist/icons.js +47 -0
  97. package/dist/index.d.ts +2 -1
  98. package/dist/index.js +2 -1
  99. package/dist/io/decompress.js +1 -1
  100. package/dist/io/export.d.ts +3 -0
  101. package/dist/io/export.js +129 -143
  102. package/dist/io/is-binary.js +2 -3
  103. package/dist/io/url-drop.js +1 -2
  104. package/dist/isosurface/Isosurface.svelte +202 -148
  105. package/dist/isosurface/IsosurfaceControls.svelte +46 -28
  106. package/dist/isosurface/parse.js +34 -29
  107. package/dist/isosurface/slice.js +5 -10
  108. package/dist/isosurface/types.d.ts +2 -1
  109. package/dist/isosurface/types.js +61 -12
  110. package/dist/labels.js +11 -8
  111. package/dist/layout/FullscreenToggle.svelte +11 -2
  112. package/dist/layout/InfoCard.svelte +38 -6
  113. package/dist/layout/InfoTag.svelte +63 -32
  114. package/dist/layout/PropertyFilter.svelte +82 -37
  115. package/dist/layout/SettingsSection.svelte +85 -55
  116. package/dist/layout/SubpageGrid.svelte +10 -2
  117. package/dist/layout/json-tree/JsonNode.svelte +183 -138
  118. package/dist/layout/json-tree/JsonTree.svelte +499 -413
  119. package/dist/layout/json-tree/JsonValue.svelte +127 -99
  120. package/dist/layout/json-tree/utils.js +4 -2
  121. package/dist/marching-cubes.js +25 -2
  122. package/dist/math.d.ts +13 -17
  123. package/dist/math.js +133 -67
  124. package/dist/overlays/ContextMenu.svelte +65 -40
  125. package/dist/overlays/DraggablePane.svelte +211 -139
  126. package/dist/periodic-table/PeriodicTable.svelte +278 -145
  127. package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
  128. package/dist/periodic-table/PropertySelect.svelte +25 -7
  129. package/dist/periodic-table/TableInset.svelte +8 -3
  130. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +446 -309
  131. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
  132. package/dist/phase-diagram/PhaseDiagramControls.svelte +102 -43
  133. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  134. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +63 -40
  135. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +71 -28
  136. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +1 -1
  137. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +158 -101
  138. package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
  139. package/dist/phase-diagram/build-diagram.js +9 -9
  140. package/dist/phase-diagram/colors.js +1 -3
  141. package/dist/phase-diagram/parse.js +10 -9
  142. package/dist/phase-diagram/svg-to-diagram.js +53 -49
  143. package/dist/phase-diagram/utils.d.ts +1 -0
  144. package/dist/phase-diagram/utils.js +80 -25
  145. package/dist/plot/AxisLabel.svelte +28 -3
  146. package/dist/plot/BarPlot.svelte +1182 -734
  147. package/dist/plot/BarPlot.svelte.d.ts +2 -2
  148. package/dist/plot/BarPlotControls.svelte +31 -5
  149. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  150. package/dist/plot/ColorBar.svelte +479 -329
  151. package/dist/plot/ColorScaleSelect.svelte +27 -6
  152. package/dist/plot/ElementScatter.svelte +36 -15
  153. package/dist/plot/FillArea.svelte +152 -95
  154. package/dist/plot/Histogram.svelte +934 -571
  155. package/dist/plot/Histogram.svelte.d.ts +1 -1
  156. package/dist/plot/HistogramControls.svelte +53 -9
  157. package/dist/plot/HistogramControls.svelte.d.ts +1 -1
  158. package/dist/plot/InteractiveAxisLabel.svelte +34 -11
  159. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
  160. package/dist/plot/Line.svelte +63 -28
  161. package/dist/plot/PlotControls.svelte +157 -114
  162. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  163. package/dist/plot/PlotLegend.svelte +174 -91
  164. package/dist/plot/PlotTooltip.svelte +45 -6
  165. package/dist/plot/PortalSelect.svelte +175 -147
  166. package/dist/plot/ReferenceLine.svelte +76 -22
  167. package/dist/plot/ReferenceLine3D.svelte +132 -107
  168. package/dist/plot/ReferencePlane.svelte +146 -121
  169. package/dist/plot/ScatterPlot.svelte +1681 -1091
  170. package/dist/plot/ScatterPlot.svelte.d.ts +2 -2
  171. package/dist/plot/ScatterPlot3D.svelte +256 -131
  172. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  173. package/dist/plot/ScatterPlot3DControls.svelte +113 -63
  174. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
  175. package/dist/plot/ScatterPlot3DScene.svelte +608 -403
  176. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  177. package/dist/plot/ScatterPlotControls.svelte +65 -25
  178. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  179. package/dist/plot/ScatterPoint.svelte +98 -26
  180. package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
  181. package/dist/plot/SpacegroupBarPlot.svelte +142 -85
  182. package/dist/plot/Surface3D.svelte +159 -108
  183. package/dist/plot/ZeroLines.svelte +55 -3
  184. package/dist/plot/ZoomRect.svelte +4 -2
  185. package/dist/plot/axis-utils.js +1 -3
  186. package/dist/plot/data-cleaning.js +12 -28
  187. package/dist/plot/data-transform.js +2 -1
  188. package/dist/plot/fill-utils.js +2 -0
  189. package/dist/plot/layout.d.ts +4 -1
  190. package/dist/plot/layout.js +33 -14
  191. package/dist/plot/reference-line.d.ts +2 -2
  192. package/dist/plot/reference-line.js +7 -5
  193. package/dist/plot/scales.js +24 -36
  194. package/dist/plot/types.d.ts +11 -23
  195. package/dist/plot/types.js +6 -11
  196. package/dist/plot/utils/label-placement.d.ts +32 -15
  197. package/dist/plot/utils/label-placement.js +227 -66
  198. package/dist/plot/utils/series-visibility.js +2 -3
  199. package/dist/rdf/RdfPlot.svelte +143 -91
  200. package/dist/rdf/calc-rdf.js +4 -5
  201. package/dist/sanitize.d.ts +4 -0
  202. package/dist/sanitize.js +107 -0
  203. package/dist/settings.d.ts +18 -6
  204. package/dist/settings.js +46 -16
  205. package/dist/spectral/Bands.svelte +632 -453
  206. package/dist/spectral/BandsAndDos.svelte +90 -49
  207. package/dist/spectral/BrillouinBandsDos.svelte +151 -93
  208. package/dist/spectral/Dos.svelte +389 -258
  209. package/dist/spectral/helpers.js +55 -43
  210. package/dist/state.svelte.d.ts +1 -1
  211. package/dist/state.svelte.js +3 -2
  212. package/dist/structure/Arrow.svelte +59 -20
  213. package/dist/structure/AtomLegend.svelte +215 -134
  214. package/dist/structure/Bond.svelte +73 -47
  215. package/dist/structure/CanvasTooltip.svelte +10 -2
  216. package/dist/structure/CellSelect.svelte +72 -45
  217. package/dist/structure/Cylinder.svelte +33 -17
  218. package/dist/structure/Lattice.svelte +88 -33
  219. package/dist/structure/Structure.svelte +1063 -797
  220. package/dist/structure/Structure.svelte.d.ts +1 -1
  221. package/dist/structure/StructureControls.svelte +349 -118
  222. package/dist/structure/StructureExportPane.svelte +124 -89
  223. package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
  224. package/dist/structure/StructureInfoPane.svelte +304 -237
  225. package/dist/structure/StructureScene.svelte +879 -443
  226. package/dist/structure/StructureScene.svelte.d.ts +15 -7
  227. package/dist/structure/atom-properties.js +8 -8
  228. package/dist/structure/bonding.js +6 -7
  229. package/dist/structure/export.js +14 -29
  230. package/dist/structure/ferrox-wasm.js +1 -1
  231. package/dist/structure/index.d.ts +13 -3
  232. package/dist/structure/index.js +83 -23
  233. package/dist/structure/measure.d.ts +2 -2
  234. package/dist/structure/measure.js +4 -44
  235. package/dist/structure/parse.js +113 -141
  236. package/dist/structure/partial-occupancy.js +7 -10
  237. package/dist/structure/pbc.d.ts +1 -0
  238. package/dist/structure/pbc.js +16 -6
  239. package/dist/structure/supercell.d.ts +2 -2
  240. package/dist/structure/supercell.js +12 -22
  241. package/dist/structure/validation.js +1 -2
  242. package/dist/symmetry/SymmetryStats.svelte +84 -41
  243. package/dist/symmetry/WyckoffTable.svelte +26 -6
  244. package/dist/symmetry/cell-transform.js +5 -3
  245. package/dist/symmetry/index.js +8 -7
  246. package/dist/symmetry/spacegroups.js +148 -148
  247. package/dist/table/HeatmapTable.svelte +790 -554
  248. package/dist/table/HeatmapTable.svelte.d.ts +1 -1
  249. package/dist/table/ToggleMenu.svelte +125 -92
  250. package/dist/table/index.js +2 -4
  251. package/dist/theme/ThemeControl.svelte +21 -12
  252. package/dist/time.js +4 -1
  253. package/dist/tooltip/TooltipContent.svelte +33 -8
  254. package/dist/trajectory/Trajectory.svelte +758 -558
  255. package/dist/trajectory/TrajectoryError.svelte +14 -3
  256. package/dist/trajectory/TrajectoryExportPane.svelte +137 -83
  257. package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
  258. package/dist/trajectory/extract.js +10 -26
  259. package/dist/trajectory/format-detect.js +5 -5
  260. package/dist/trajectory/frame-reader.d.ts +1 -1
  261. package/dist/trajectory/frame-reader.js +5 -12
  262. package/dist/trajectory/helpers.d.ts +0 -1
  263. package/dist/trajectory/helpers.js +2 -17
  264. package/dist/trajectory/index.js +14 -12
  265. package/dist/trajectory/parse/ase.js +5 -4
  266. package/dist/trajectory/parse/hdf5.js +26 -18
  267. package/dist/trajectory/parse/index.js +13 -18
  268. package/dist/trajectory/parse/lammps.js +17 -7
  269. package/dist/trajectory/parse/vasp.js +5 -2
  270. package/dist/trajectory/parse/xyz.js +8 -7
  271. package/dist/trajectory/plotting.js +13 -8
  272. package/dist/utils.d.ts +1 -0
  273. package/dist/utils.js +13 -0
  274. package/dist/xrd/XrdPlot.svelte +337 -247
  275. package/dist/xrd/broadening.js +14 -9
  276. package/dist/xrd/calc-xrd.js +12 -18
  277. package/dist/xrd/parse.d.ts +1 -1
  278. package/dist/xrd/parse.js +17 -17
  279. package/package.json +99 -103
  280. package/readme.md +1 -1
  281. /package/dist/theme/{themes.js → themes.mjs} +0 -0
package/dist/math.js CHANGED
@@ -14,6 +14,7 @@ export const LOG_EPS = 1e-9;
14
14
  export const EPS = 1e-10;
15
15
  export const RAD_TO_DEG = 180 / Math.PI;
16
16
  export const DEG_TO_RAD = Math.PI / 180;
17
+ const MAX_MIN_IMAGE_CANDIDATES = 100_000;
17
18
  export const to_degrees = (radians) => radians * RAD_TO_DEG;
18
19
  export const to_radians = (degrees) => degrees * DEG_TO_RAD;
19
20
  // Calculate all lattice parameters in a single efficient pass
@@ -44,21 +45,70 @@ export const euclidean_dist = (vec1, vec2) => {
44
45
  }
45
46
  return Math.hypot(...vec1.map((x, idx) => x - vec2[idx]));
46
47
  };
48
+ const vec3_norm_sq = (vec) => vec[0] ** 2 + vec[1] ** 2 + vec[2] ** 2;
49
+ // Exact minimum-image displacement for row-vector lattices.
50
+ // Rounded fractional wrapping is only approximate for highly skewed cells, so
51
+ // we use it as a starting guess and then search the small set of shifts that
52
+ // can still beat that Cartesian radius.
53
+ export function min_image_displacement(from, to, lattice_matrix, converters, pbc = [true, true, true]) {
54
+ const { cart_to_frac, frac_to_cart, reciprocal_axis_norms } = converters ?? create_lattice_converters(lattice_matrix);
55
+ const frac_from = cart_to_frac(from);
56
+ const frac_to = cart_to_frac(to);
57
+ const frac_diff = [
58
+ frac_to[0] - frac_from[0],
59
+ frac_to[1] - frac_from[1],
60
+ frac_to[2] - frac_from[2],
61
+ ];
62
+ const wrapped_frac_diff = [
63
+ pbc[0] ? frac_diff[0] - Math.round(frac_diff[0]) : frac_diff[0],
64
+ pbc[1] ? frac_diff[1] - Math.round(frac_diff[1]) : frac_diff[1],
65
+ pbc[2] ? frac_diff[2] - Math.round(frac_diff[2]) : frac_diff[2],
66
+ ];
67
+ let best_displacement = frac_to_cart(wrapped_frac_diff);
68
+ let best_dist_sq = vec3_norm_sq(best_displacement);
69
+ const search_radius = Math.sqrt(best_dist_sq) + EPS;
70
+ const candidate_shift_ranges = [0, 1, 2].map((axis_idx) => {
71
+ if (!pbc[axis_idx])
72
+ return [0, 0];
73
+ const axis_bound = reciprocal_axis_norms[axis_idx] * search_radius;
74
+ return [
75
+ Math.ceil(-frac_diff[axis_idx] - axis_bound),
76
+ Math.floor(-frac_diff[axis_idx] + axis_bound),
77
+ ];
78
+ });
79
+ let candidate_count = 1;
80
+ for (const [shift_min, shift_max] of candidate_shift_ranges) {
81
+ candidate_count *= shift_max - shift_min + 1;
82
+ if (candidate_count > MAX_MIN_IMAGE_CANDIDATES) {
83
+ throw new Error(`Minimum-image search would test >${MAX_MIN_IMAGE_CANDIDATES} candidates ` +
84
+ `for lattice ${JSON.stringify(lattice_matrix)}; reciprocal norms=` +
85
+ `${JSON.stringify(reciprocal_axis_norms)} ranges=${JSON.stringify(candidate_shift_ranges)}`);
86
+ }
87
+ }
88
+ const [[i_min, i_max], [j_min, j_max], [k_min, k_max]] = candidate_shift_ranges;
89
+ // Only test integer shifts that reciprocal-space bounds say could still win.
90
+ for (let ii = i_min; ii <= i_max; ii++) {
91
+ for (let jj = j_min; jj <= j_max; jj++) {
92
+ for (let kk = k_min; kk <= k_max; kk++) {
93
+ const candidate_frac_diff = [
94
+ frac_diff[0] + ii,
95
+ frac_diff[1] + jj,
96
+ frac_diff[2] + kk,
97
+ ];
98
+ const candidate_displacement = frac_to_cart(candidate_frac_diff);
99
+ const candidate_dist_sq = vec3_norm_sq(candidate_displacement);
100
+ if (candidate_dist_sq < best_dist_sq) {
101
+ best_dist_sq = candidate_dist_sq;
102
+ best_displacement = candidate_displacement;
103
+ }
104
+ }
105
+ }
106
+ }
107
+ return best_displacement;
108
+ }
47
109
  // Calculate the minimum distance between two points considering periodic boundary conditions.
48
- export function pbc_dist(pos1, // First position vector (Cartesian coordinates)
49
- pos2, // Second position vector (Cartesian coordinates)
50
- lattice_matrix, // 3x3 lattice matrix where each row is a lattice vector
51
- lattice_inv, // Optional pre-computed inverse matrix for optimization (since lattice is usually constant and repeatedly inverting matrix is expensive)
52
- pbc = [true, true, true]) {
53
- const inv_matrix = lattice_inv ?? matrix_inverse_3x3(lattice_matrix);
54
- // Convert Cartesian coordinates to fractional coordinates
55
- const [fx1, fy1, fz1] = mat3x3_vec3_multiply(inv_matrix, pos1);
56
- const [fx2, fy2, fz2] = mat3x3_vec3_multiply(inv_matrix, pos2);
57
- // Apply minimum image convention only for periodic axes
58
- const wrapped_frac_diff = [fx1 - fx2, fy1 - fy2, fz1 - fz2].map((diff, idx) => pbc[idx] ? diff - Math.round(diff) : diff);
59
- // Convert back to Cartesian coordinates
60
- const cart_diff = mat3x3_vec3_multiply(lattice_matrix, wrapped_frac_diff);
61
- return Math.hypot(...cart_diff);
110
+ export function pbc_dist(pos1, pos2, lattice_matrix, converters, pbc = [true, true, true]) {
111
+ return Math.hypot(...min_image_displacement(pos1, pos2, lattice_matrix, converters, pbc));
62
112
  }
63
113
  export function matrix_inverse_3x3(matrix) {
64
114
  const [[m11, m12, m13], [m21, m22, m23], [m31, m32, m33]] = matrix;
@@ -106,7 +156,7 @@ export function add(...vecs) {
106
156
  throw new Error(`All vectors must have the same length`);
107
157
  }
108
158
  }
109
- const result = new Array(length).fill(0);
159
+ const result = Array.from({ length }).fill(0);
110
160
  for (const vec of vecs) {
111
161
  for (let idx = 0; idx < length; idx++) {
112
162
  result[idx] += vec[idx];
@@ -192,7 +242,11 @@ export function from_voigt(voigt) {
192
242
  throw new Error(`Expected 6-element Voigt vector, got ${voigt.length} elements`);
193
243
  }
194
244
  const [v1, v2, v3, v4, v5, v6] = voigt;
195
- return [[v1, v6, v5], [v6, v2, v4], [v5, v4, v3]];
245
+ return [
246
+ [v1, v6, v5],
247
+ [v6, v2, v4],
248
+ [v5, v4, v3],
249
+ ];
196
250
  }
197
251
  // Convert flat 9-element array to 3x3 tensor (row-major order)
198
252
  export function vec9_to_mat3x3(flat_array) {
@@ -200,7 +254,11 @@ export function vec9_to_mat3x3(flat_array) {
200
254
  throw new Error(`Expected 9-element array, got ${flat_array.length} elements`);
201
255
  }
202
256
  const [a1, a2, a3, a4, a5, a6, a7, a8, a9] = flat_array;
203
- return [[a1, a2, a3], [a4, a5, a6], [a7, a8, a9]];
257
+ return [
258
+ [a1, a2, a3],
259
+ [a4, a5, a6],
260
+ [a7, a8, a9],
261
+ ];
204
262
  }
205
263
  // Convert 3x3 tensor to flat 9-element array (row-major order)
206
264
  export function tensor_to_flat_array(tensor) {
@@ -216,6 +274,18 @@ export const transpose_3x3_matrix = (matrix) => [
216
274
  [matrix[0][1], matrix[1][1], matrix[2][1]],
217
275
  [matrix[0][2], matrix[1][2], matrix[2][2]],
218
276
  ];
277
+ // Scale each row of a 3x3 matrix by the corresponding element of a Vec3.
278
+ // Used to scale lattice vectors by supercell factors.
279
+ export function scale_lattice_matrix(orig_matrix, scaling_factors) {
280
+ const [nx, ny, nz] = scaling_factors;
281
+ const [a, b, c] = orig_matrix;
282
+ return [
283
+ [a[0] * nx, a[1] * nx, a[2] * nx],
284
+ [b[0] * ny, b[1] * ny, b[2] * ny],
285
+ [c[0] * nz, c[1] * nz, c[2] * nz],
286
+ ];
287
+ }
288
+ const create_cart_to_frac_matrix = (lattice) => matrix_inverse_3x3(transpose_3x3_matrix(lattice));
219
289
  // Curried fractional→Cartesian converter (caches transposed matrix)
220
290
  export const create_frac_to_cart = (lattice) => {
221
291
  const transposed = transpose_3x3_matrix(lattice);
@@ -223,8 +293,16 @@ export const create_frac_to_cart = (lattice) => {
223
293
  };
224
294
  // Curried Cartesian→fractional converter (caches inverse transpose)
225
295
  export const create_cart_to_frac = (lattice) => {
226
- const inv_transposed = matrix_inverse_3x3(transpose_3x3_matrix(lattice));
227
- return (cart) => mat3x3_vec3_multiply(inv_transposed, cart);
296
+ const cart_to_frac_mat = create_cart_to_frac_matrix(lattice);
297
+ return (cart) => mat3x3_vec3_multiply(cart_to_frac_mat, cart);
298
+ };
299
+ export const create_lattice_converters = (lattice) => {
300
+ const cart_to_frac_mat = create_cart_to_frac_matrix(lattice);
301
+ return {
302
+ cart_to_frac: (cart) => mat3x3_vec3_multiply(cart_to_frac_mat, cart),
303
+ frac_to_cart: create_frac_to_cart(lattice),
304
+ reciprocal_axis_norms: cart_to_frac_mat.map((row) => Math.hypot(...row)),
305
+ };
228
306
  };
229
307
  // Convert unit cell parameters to lattice matrix (crystallographic convention)
230
308
  export function cell_to_lattice_matrix(a, b, c, alpha, beta, gamma) {
@@ -237,11 +315,7 @@ export function cell_to_lattice_matrix(a, b, c, alpha, beta, gamma) {
237
315
  const cos_gamma = Math.cos(gamma_rad);
238
316
  const sin_gamma = Math.sin(gamma_rad);
239
317
  // Calculate volume factor for triclinic system
240
- const vol_factor = Math.sqrt(1 -
241
- cos_alpha ** 2 -
242
- cos_beta ** 2 -
243
- cos_gamma ** 2 +
244
- 2 * cos_alpha * cos_beta * cos_gamma);
318
+ const vol_factor = Math.sqrt(1 - cos_alpha ** 2 - cos_beta ** 2 - cos_gamma ** 2 + 2 * cos_alpha * cos_beta * cos_gamma);
245
319
  // Standard crystallographic lattice vectors
246
320
  const c_x = c * cos_beta;
247
321
  const c_y = (c * (cos_alpha - cos_beta * cos_gamma)) / sin_gamma;
@@ -256,7 +330,8 @@ export function det_3x3(matrix) {
256
330
  // |A| = a(ei − fh) − b(di − fg) + c(dh − eg)
257
331
  // where matrix = [[a, b, c], [d, e, f], [g, h, i]]
258
332
  const [[m00, m01, m02], [m10, m11, m12], [m20, m21, m22]] = matrix;
259
- return (m00 * (m11 * m22 - m12 * m21) - m01 * (m10 * m22 - m12 * m20) +
333
+ return (m00 * (m11 * m22 - m12 * m21) -
334
+ m01 * (m10 * m22 - m12 * m20) +
260
335
  m02 * (m10 * m21 - m11 * m20));
261
336
  }
262
337
  export function get_coefficient_of_variation(values) {
@@ -264,9 +339,7 @@ export function get_coefficient_of_variation(values) {
264
339
  return 0;
265
340
  const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
266
341
  const variance = values.reduce((sum, val) => sum + (val - mean) ** 2, 0) / values.length;
267
- return Math.abs(mean) > 1e-10
268
- ? Math.sqrt(variance) / Math.abs(mean)
269
- : Math.sqrt(variance);
342
+ return Math.abs(mean) > 1e-10 ? Math.sqrt(variance) / Math.abs(mean) : Math.sqrt(variance);
270
343
  }
271
344
  // Compute 4x4 determinant (used for 4D barycentric coordinates)
272
345
  export function det_4x4(matrix) {
@@ -275,42 +348,38 @@ export function det_4x4(matrix) {
275
348
  const [b0, b1, b2, b3] = b_row;
276
349
  const [c0, c1, c2, c3] = c_row;
277
350
  const [d0, d1, d2, d3] = d_row;
278
- return (a0 *
279
- (b1 * (c2 * d3 - c3 * d2) - b2 * (c1 * d3 - c3 * d1) + b3 * (c1 * d2 - c2 * d1)) -
280
- a1 *
281
- (b0 * (c2 * d3 - c3 * d2) - b2 * (c0 * d3 - c3 * d0) + b3 * (c0 * d2 - c2 * d0)) +
282
- a2 *
283
- (b0 * (c1 * d3 - c3 * d1) - b1 * (c0 * d3 - c3 * d0) + b3 * (c0 * d1 - c1 * d0)) -
351
+ return (a0 * (b1 * (c2 * d3 - c3 * d2) - b2 * (c1 * d3 - c3 * d1) + b3 * (c1 * d2 - c2 * d1)) -
352
+ a1 * (b0 * (c2 * d3 - c3 * d2) - b2 * (c0 * d3 - c3 * d0) + b3 * (c0 * d2 - c2 * d0)) +
353
+ a2 * (b0 * (c1 * d3 - c3 * d1) - b1 * (c0 * d3 - c3 * d0) + b3 * (c0 * d1 - c1 * d0)) -
284
354
  a3 * (b0 * (c1 * d2 - c2 * d1) - b1 * (c0 * d2 - c2 * d0) + b2 * (c0 * d1 - c1 * d0)));
285
355
  }
286
356
  // Compute NxN determinant using LU decomposition with partial pivoting
287
357
  // More numerically stable than cofactor expansion for N > 4
288
358
  // Returns 0 for singular/near-singular matrices (pivot < EPS ≈ 1e-10)
289
359
  export function det_nxn(matrix) {
290
- const n = matrix.length;
291
- if (n === 0)
360
+ const mat_size = matrix.length;
361
+ if (mat_size === 0)
292
362
  return 1;
293
- if (!matrix.every((row) => row.length === n)) {
363
+ if (!matrix.every((row) => row.length === mat_size)) {
294
364
  throw new Error(`det_nxn requires a square matrix`);
295
365
  }
296
366
  // Fast paths for small matrices
297
- if (n === 1)
367
+ if (mat_size === 1)
298
368
  return matrix[0][0];
299
- if (n === 2)
369
+ if (mat_size === 2)
300
370
  return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
301
- if (n === 3)
371
+ if (mat_size === 3)
302
372
  return det_3x3(matrix);
303
- if (n === 4)
373
+ if (mat_size === 4)
304
374
  return det_4x4(matrix);
305
375
  // LU decomposition with partial pivoting
306
376
  // Create a working copy to avoid mutating input
307
377
  const lu = matrix.map((row) => [...row]);
308
378
  let swaps = 0;
309
- for (let col = 0; col < n; col++) {
379
+ for (let col = 0; col < mat_size; col++) {
310
380
  // Find pivot (largest absolute value in column)
311
- let max_row = col;
312
- let max_val = Math.abs(lu[col][col]);
313
- for (let row = col + 1; row < n; row++) {
381
+ let [max_row, max_val] = [col, Math.abs(lu[col][col])];
382
+ for (let row = col + 1; row < mat_size; row++) {
314
383
  const val = Math.abs(lu[row][col]);
315
384
  if (val > max_val) {
316
385
  max_val = val;
@@ -328,17 +397,17 @@ export function det_nxn(matrix) {
328
397
  }
329
398
  // Eliminate below pivot
330
399
  const pivot = lu[col][col];
331
- for (let row = col + 1; row < n; row++) {
400
+ for (let row = col + 1; row < mat_size; row++) {
332
401
  const factor = lu[row][col] / pivot;
333
402
  lu[row][col] = 0;
334
- for (let k = col + 1; k < n; k++) {
403
+ for (let k = col + 1; k < mat_size; k++) {
335
404
  lu[row][k] -= factor * lu[col][k];
336
405
  }
337
406
  }
338
407
  }
339
408
  // Determinant is product of diagonal elements × (-1)^swaps
340
409
  let det = swaps % 2 === 0 ? 1 : -1;
341
- for (let idx = 0; idx < n; idx++) {
410
+ for (let idx = 0; idx < mat_size; idx++) {
342
411
  det *= lu[idx][idx];
343
412
  }
344
413
  return det;
@@ -370,8 +439,12 @@ export const centered_frac = (val) => {
370
439
  };
371
440
  // Element-wise equality check for two optional Vec3s.
372
441
  // Returns true if both are the same reference, or both are defined with equal components.
373
- export const vecs_equal = (vec_a, vec_b) => vec_a === vec_b || (!!vec_a && !!vec_b &&
374
- vec_a[0] === vec_b[0] && vec_a[1] === vec_b[1] && vec_a[2] === vec_b[2]);
442
+ export const vecs_equal = (vec_a, vec_b) => vec_a === vec_b ||
443
+ (!!vec_a &&
444
+ !!vec_b &&
445
+ vec_a[0] === vec_b[0] &&
446
+ vec_a[1] === vec_b[1] &&
447
+ vec_a[2] === vec_b[2]);
375
448
  // Normalize a Vec3 to unit length, returns zero vector if input is zero
376
449
  export function normalize_vec3(vec, fallback) {
377
450
  const len = Math.hypot(vec[0], vec[1], vec[2]);
@@ -393,7 +466,7 @@ export function compute_in_plane_basis(normal) {
393
466
  ];
394
467
  const u_vec = normalize_vec3(u_raw, [0, 1, 0]);
395
468
  const v_vec = cross_3d(normal, u_vec);
396
- return [u_vec, v_vec];
469
+ return [u_vec, v_vec]; // u, v basis vectors
397
470
  }
398
471
  // Check whether N 3D points all lie on the same plane within tolerance.
399
472
  // Fewer than 3 points are trivially coplanar.
@@ -539,8 +612,7 @@ export function merge_coplanar_triangles(positions, tolerance = 1e-4) {
539
612
  if (pa.degenerate || pb.degenerate)
540
613
  continue;
541
614
  // Check coplanarity: same canonical normal direction AND same plane distance
542
- const normal_dot = pa.normal[0] * pb.normal[0] + pa.normal[1] * pb.normal[1] +
543
- pa.normal[2] * pb.normal[2];
615
+ const normal_dot = pa.normal[0] * pb.normal[0] + pa.normal[1] * pb.normal[1] + pa.normal[2] * pb.normal[2];
544
616
  if (Math.abs(normal_dot) < 1 - tolerance)
545
617
  continue;
546
618
  if (Math.abs(pa.plane_d - pb.plane_d) > tolerance)
@@ -591,7 +663,7 @@ export function merge_coplanar_triangles(positions, tolerance = 1e-4) {
591
663
  }
592
664
  // Project to 2D using in-plane basis
593
665
  const [u_vec, v_vec] = compute_in_plane_basis(normal);
594
- const pts_2d = unique_verts.map((vert) => [dot(u_vec, vert), dot(v_vec, vert)]);
666
+ const pts_2d = unique_verts.map((vertex) => [dot(u_vec, vertex), dot(v_vec, vertex)]);
595
667
  const hull = convex_hull_2d(pts_2d);
596
668
  if (hull.length < 3) {
597
669
  emit_original(members);
@@ -661,7 +733,7 @@ export function point_in_polygon(point_x, point_y, vertices) {
661
733
  const [x_i, y_i] = vertices[idx];
662
734
  const [x_j, y_j] = vertices[prev_idx];
663
735
  // Check if horizontal ray from point crosses this edge
664
- if (y_i !== y_j && (y_i > point_y) !== (y_j > point_y)) {
736
+ if (y_i !== y_j && y_i > point_y !== y_j > point_y) {
665
737
  const x_intersect = ((x_j - x_i) * (point_y - y_i)) / (y_j - y_i) + x_i;
666
738
  if (point_x < x_intersect)
667
739
  inside = !inside;
@@ -733,10 +805,7 @@ b) {
733
805
  const det = A[0][0] * A[1][1] - A[0][1] * A[1][0];
734
806
  if (Math.abs(det) < EPS)
735
807
  return null;
736
- return [
737
- (b[0] * A[1][1] - b[1] * A[0][1]) / det,
738
- (A[0][0] * b[1] - A[1][0] * b[0]) / det,
739
- ];
808
+ return [(b[0] * A[1][1] - b[1] * A[0][1]) / det, (A[0][0] * b[1] - A[1][0] * b[0]) / det];
740
809
  }
741
810
  // 3x3 fast path via matrix inverse
742
811
  if (n === 3) {
@@ -751,14 +820,11 @@ b) {
751
820
  const perm = Array.from({ length: n }, (_, idx) => idx);
752
821
  for (let col = 0; col < n; col++) {
753
822
  // Find pivot
754
- let max_row = col;
755
- let max_val = Math.abs(lu[col][col]);
823
+ let [max_row, max_val] = [col, Math.abs(lu[col][col])];
756
824
  for (let row = col + 1; row < n; row++) {
757
825
  const val = Math.abs(lu[row][col]);
758
- if (val > max_val) {
759
- max_val = val;
760
- max_row = row;
761
- }
826
+ if (val > max_val)
827
+ [max_val, max_row] = [val, row];
762
828
  }
763
829
  if (max_val < EPS)
764
830
  return null; // singular
@@ -787,7 +853,7 @@ b) {
787
853
  }
788
854
  }
789
855
  // Back substitution (Ux = y)
790
- const x = new Array(n).fill(0);
856
+ const x = Array.from({ length: n }).fill(0);
791
857
  for (let row = n - 1; row >= 0; row--) {
792
858
  let sum = pb[row];
793
859
  for (let col = row + 1; col < n; col++) {
@@ -802,7 +868,7 @@ b) {
802
868
  export function convex_hull_2d(points) {
803
869
  if (points.length < 3)
804
870
  return [...points];
805
- const sorted = [...points].sort((a, b) => (a[0] - b[0]) || (a[1] - b[1]));
871
+ const sorted = points.toSorted((a, b) => a[0] - b[0] || a[1] - b[1]);
806
872
  const cross = (o, a, b) => (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
807
873
  // Lower hull
808
874
  const lower = [];
@@ -1,47 +1,72 @@
1
- <script lang="ts">import Icon from '../Icon.svelte';
2
- let { sections, selected_values = {}, on_select, position, visible, on_close, menu_element, ...rest } = $props();
3
- // Calculate smart position that keeps menu in viewport
4
- function get_smart_position() {
5
- if (!menu_element)
6
- return position;
7
- const rect = menu_element.getBoundingClientRect();
8
- let { x, y } = position;
9
- if (x + rect.width > window.innerWidth)
10
- x = position.x - rect.width;
11
- if (y + rect.height > window.innerHeight)
12
- y = position.y - rect.height;
13
- return { x: Math.max(0, x), y: Math.max(0, y) };
14
- }
15
- // Handle click outside to close
16
- function handle_click_outside(event) {
17
- const target = event.target;
1
+ <script lang="ts">
2
+ import Icon from '../Icon.svelte'
3
+ import type { IconName } from '../icons'
4
+ import type { HTMLAttributes } from 'svelte/elements'
5
+
6
+ interface MenuOption {
7
+ value: string
8
+ icon?: string
9
+ label?: string
10
+ disabled?: boolean
11
+ }
12
+ let {
13
+ sections,
14
+ selected_values = {},
15
+ on_select,
16
+ position,
17
+ visible,
18
+ on_close,
19
+ menu_element,
20
+ ...rest
21
+ }: HTMLAttributes<HTMLDivElement> & {
22
+ sections: Readonly<{ title: string; options: readonly MenuOption[] }[]>
23
+ selected_values?: Record<string, string>
24
+ on_select?: (section_title: string, option: MenuOption) => void
25
+ position: { x: number; y: number }
26
+ visible: boolean
27
+ on_close?: () => void
28
+ menu_element?: HTMLDivElement
29
+ } = $props()
30
+
31
+ // Calculate smart position that keeps menu in viewport
32
+ function get_smart_position() {
33
+ if (!menu_element) return position
34
+ const rect = menu_element.getBoundingClientRect()
35
+ let { x, y } = position
36
+ if (x + rect.width > window.innerWidth) x = position.x - rect.width
37
+ if (y + rect.height > window.innerHeight) y = position.y - rect.height
38
+ return { x: Math.max(0, x), y: Math.max(0, y) }
39
+ }
40
+
41
+ // Handle click outside to close
42
+ function handle_click_outside(event: MouseEvent) {
43
+ const target = event.target
18
44
  if (target instanceof Element && visible) {
19
- const menu = target.closest(`.context-menu`);
20
- if (!menu)
21
- on_close?.();
45
+ const menu = target.closest(`.context-menu`)
46
+ if (!menu) on_close?.()
22
47
  }
23
- }
24
- // Handle right-click outside to close
25
- function handle_right_click_outside(event) {
26
- if (!visible)
27
- return;
28
- const target = event.target;
29
- const menu = target instanceof Element ? target.closest(`.context-menu`) : null;
48
+ }
49
+
50
+ // Handle right-click outside to close
51
+ function handle_right_click_outside(event: MouseEvent) {
52
+ if (!visible) return
53
+ const target = event.target
54
+ const menu = target instanceof Element ? target.closest(`.context-menu`) : null
30
55
  if (!menu) {
31
- event.preventDefault();
32
- on_close?.();
56
+ event.preventDefault()
57
+ on_close?.()
33
58
  }
34
- }
35
- // Handle keyboard shortcuts
36
- function handle_keydown(event) {
37
- if (event.key === `Escape` && visible)
38
- on_close?.();
39
- }
40
- // Handle option selection
41
- function handle_option_click(section_title, option) {
42
- if (!option.disabled)
43
- on_select?.(section_title, option);
44
- }
59
+ }
60
+
61
+ // Handle keyboard shortcuts
62
+ function handle_keydown(event: KeyboardEvent) {
63
+ if (event.key === `Escape` && visible) on_close?.()
64
+ }
65
+
66
+ // Handle option selection
67
+ function handle_option_click(section_title: string, option: MenuOption) {
68
+ if (!option.disabled) on_select?.(section_title, option)
69
+ }
45
70
  </script>
46
71
 
47
72
  <svelte:document