matterviz 0.3.1 → 0.3.2

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 (257) hide show
  1. package/dist/FilePicker.svelte +37 -20
  2. package/dist/Icon.svelte +2 -2
  3. package/dist/app.css +29 -0
  4. package/dist/brillouin/BrillouinZone.svelte +19 -61
  5. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  6. package/dist/brillouin/BrillouinZoneExportPane.svelte +12 -20
  7. package/dist/brillouin/BrillouinZoneScene.svelte +2 -2
  8. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  9. package/dist/chempot-diagram/ChemPotDiagram.svelte +192 -0
  10. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  11. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +677 -0
  12. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  13. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2688 -0
  14. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  15. package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -0
  16. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  17. package/dist/chempot-diagram/color.d.ts +10 -0
  18. package/dist/chempot-diagram/color.js +33 -0
  19. package/dist/chempot-diagram/compute.d.ts +38 -0
  20. package/dist/chempot-diagram/compute.js +650 -0
  21. package/dist/chempot-diagram/index.d.ts +5 -0
  22. package/dist/chempot-diagram/index.js +5 -0
  23. package/dist/chempot-diagram/pointer.d.ts +16 -0
  24. package/dist/chempot-diagram/pointer.js +40 -0
  25. package/dist/chempot-diagram/temperature.d.ts +15 -0
  26. package/dist/chempot-diagram/temperature.js +37 -0
  27. package/dist/chempot-diagram/types.d.ts +83 -0
  28. package/dist/chempot-diagram/types.js +27 -0
  29. package/dist/colors/index.d.ts +3 -1
  30. package/dist/colors/index.js +4 -0
  31. package/dist/composition/BarChart.svelte +13 -22
  32. package/dist/composition/BubbleChart.svelte +5 -3
  33. package/dist/composition/FormulaFilter.svelte +586 -94
  34. package/dist/composition/FormulaFilter.svelte.d.ts +35 -1
  35. package/dist/composition/PieChart.svelte +43 -18
  36. package/dist/composition/PieChart.svelte.d.ts +1 -1
  37. package/dist/convex-hull/ConvexHull.svelte +4 -2
  38. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  39. package/dist/convex-hull/ConvexHull2D.svelte +13 -44
  40. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  41. package/dist/convex-hull/ConvexHull3D.svelte +16 -7
  42. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  43. package/dist/convex-hull/ConvexHull4D.svelte +17 -7
  44. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  45. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
  46. package/dist/convex-hull/ConvexHullStats.svelte +701 -226
  47. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
  48. package/dist/convex-hull/ConvexHullTooltip.svelte +1 -0
  49. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  50. package/dist/convex-hull/demo-temperature.js +36 -0
  51. package/dist/convex-hull/helpers.d.ts +1 -1
  52. package/dist/convex-hull/helpers.js +2 -4
  53. package/dist/convex-hull/index.d.ts +1 -0
  54. package/dist/convex-hull/index.js +1 -0
  55. package/dist/convex-hull/thermodynamics.d.ts +8 -21
  56. package/dist/convex-hull/thermodynamics.js +106 -17
  57. package/dist/convex-hull/types.d.ts +5 -0
  58. package/dist/convex-hull/types.js +5 -0
  59. package/dist/coordination/CoordinationBarPlot.svelte +29 -46
  60. package/dist/element/BohrAtom.svelte +1 -1
  61. package/dist/element/data.js +2 -14
  62. package/dist/element/data.json.gz +0 -0
  63. package/dist/element/types.d.ts +1 -0
  64. package/dist/fermi-surface/FermiSurface.svelte +20 -64
  65. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  66. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  67. package/dist/fermi-surface/FermiSurfaceScene.svelte +1 -1
  68. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  69. package/dist/fermi-surface/parse.js +16 -22
  70. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1273 -0
  71. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  72. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +171 -0
  73. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +31 -0
  74. package/dist/heatmap-matrix/index.d.ts +53 -0
  75. package/dist/heatmap-matrix/index.js +100 -0
  76. package/dist/heatmap-matrix/shared.d.ts +2 -0
  77. package/dist/heatmap-matrix/shared.js +4 -0
  78. package/dist/icons.d.ts +111 -0
  79. package/dist/icons.js +111 -0
  80. package/dist/index.d.ts +3 -1
  81. package/dist/index.js +3 -1
  82. package/dist/io/export.js +15 -3
  83. package/dist/io/file-drop.d.ts +7 -0
  84. package/dist/io/file-drop.js +43 -0
  85. package/dist/io/index.d.ts +2 -2
  86. package/dist/io/index.js +2 -112
  87. package/dist/io/types.d.ts +1 -0
  88. package/dist/io/url-drop.d.ts +2 -0
  89. package/dist/io/url-drop.js +118 -0
  90. package/dist/isosurface/Isosurface.svelte +101 -45
  91. package/dist/isosurface/IsosurfaceControls.svelte +19 -0
  92. package/dist/isosurface/parse.js +73 -30
  93. package/dist/isosurface/slice.d.ts +2 -1
  94. package/dist/isosurface/slice.js +3 -3
  95. package/dist/isosurface/types.d.ts +13 -1
  96. package/dist/isosurface/types.js +98 -0
  97. package/dist/labels.d.ts +2 -1
  98. package/dist/labels.js +1 -0
  99. package/dist/layout/InfoTag.svelte +62 -62
  100. package/dist/layout/SubpageGrid.svelte +74 -0
  101. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  102. package/dist/layout/index.d.ts +1 -0
  103. package/dist/layout/index.js +1 -0
  104. package/dist/layout/json-tree/JsonNode.svelte +83 -85
  105. package/dist/layout/json-tree/JsonTree.svelte +20 -19
  106. package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
  107. package/dist/layout/json-tree/JsonValue.svelte +196 -116
  108. package/dist/layout/json-tree/types.d.ts +10 -2
  109. package/dist/layout/json-tree/utils.d.ts +2 -0
  110. package/dist/layout/json-tree/utils.js +33 -0
  111. package/dist/math.d.ts +7 -0
  112. package/dist/math.js +358 -7
  113. package/dist/overlays/ContextMenu.svelte +3 -2
  114. package/dist/overlays/DraggablePane.svelte +163 -58
  115. package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
  116. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +232 -77
  117. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
  118. package/dist/phase-diagram/PhaseDiagramControls.svelte +32 -11
  119. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
  120. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +103 -0
  121. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  122. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +102 -95
  123. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +7 -0
  124. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +100 -26
  125. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
  126. package/dist/phase-diagram/index.d.ts +2 -0
  127. package/dist/phase-diagram/index.js +2 -0
  128. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  129. package/dist/phase-diagram/svg-to-diagram.js +865 -0
  130. package/dist/phase-diagram/types.d.ts +10 -0
  131. package/dist/phase-diagram/utils.d.ts +7 -4
  132. package/dist/phase-diagram/utils.js +149 -59
  133. package/dist/plot/AxisLabel.svelte +26 -0
  134. package/dist/plot/AxisLabel.svelte.d.ts +16 -0
  135. package/dist/plot/BarPlot.svelte +473 -228
  136. package/dist/plot/BarPlot.svelte.d.ts +3 -3
  137. package/dist/plot/BarPlotControls.svelte +3 -2
  138. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  139. package/dist/plot/ColorBar.svelte +54 -54
  140. package/dist/plot/ColorBar.svelte.d.ts +1 -1
  141. package/dist/plot/ColorScaleSelect.svelte +1 -1
  142. package/dist/plot/ElementScatter.svelte +3 -2
  143. package/dist/plot/FillArea.svelte +4 -1
  144. package/dist/plot/Histogram.svelte +320 -230
  145. package/dist/plot/Histogram.svelte.d.ts +2 -2
  146. package/dist/plot/HistogramControls.svelte +29 -10
  147. package/dist/plot/HistogramControls.svelte.d.ts +6 -2
  148. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +2 -2
  149. package/dist/plot/PlotControls.svelte +109 -27
  150. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  151. package/dist/plot/PlotLegend.svelte +1 -1
  152. package/dist/plot/PortalSelect.svelte +2 -1
  153. package/dist/plot/ReferenceLine.svelte +2 -1
  154. package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
  155. package/dist/plot/ReferencePlane.svelte +1 -3
  156. package/dist/plot/ScatterPlot.svelte +343 -209
  157. package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
  158. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  159. package/dist/plot/ScatterPlot3DControls.svelte +203 -250
  160. package/dist/plot/ScatterPlot3DScene.svelte +4 -7
  161. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  162. package/dist/plot/ScatterPlotControls.svelte +95 -55
  163. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  164. package/dist/plot/ZeroLines.svelte +44 -0
  165. package/dist/plot/ZeroLines.svelte.d.ts +32 -0
  166. package/dist/plot/ZoomRect.svelte +21 -0
  167. package/dist/plot/ZoomRect.svelte.d.ts +8 -0
  168. package/dist/plot/axis-utils.d.ts +1 -1
  169. package/dist/plot/index.d.ts +6 -2
  170. package/dist/plot/index.js +6 -2
  171. package/dist/plot/interactions.d.ts +8 -10
  172. package/dist/plot/interactions.js +2 -3
  173. package/dist/plot/layout.d.ts +7 -1
  174. package/dist/plot/layout.js +12 -4
  175. package/dist/plot/reference-line.d.ts +4 -21
  176. package/dist/plot/reference-line.js +7 -81
  177. package/dist/plot/types.d.ts +42 -17
  178. package/dist/plot/types.js +10 -0
  179. package/dist/plot/utils/label-placement.js +13 -10
  180. package/dist/plot/utils.d.ts +1 -0
  181. package/dist/plot/utils.js +14 -0
  182. package/dist/rdf/RdfPlot.svelte +55 -66
  183. package/dist/settings.d.ts +3 -0
  184. package/dist/settings.js +17 -3
  185. package/dist/spectral/Bands.svelte +515 -143
  186. package/dist/spectral/Bands.svelte.d.ts +22 -2
  187. package/dist/spectral/helpers.d.ts +23 -1
  188. package/dist/spectral/helpers.js +65 -9
  189. package/dist/spectral/types.d.ts +2 -0
  190. package/dist/structure/AtomLegend.svelte +29 -8
  191. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  192. package/dist/structure/CellSelect.svelte +92 -22
  193. package/dist/structure/Structure.svelte +108 -118
  194. package/dist/structure/Structure.svelte.d.ts +1 -1
  195. package/dist/structure/StructureControls.svelte +25 -22
  196. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  197. package/dist/structure/StructureInfoPane.svelte +7 -1
  198. package/dist/structure/StructureScene.svelte +104 -66
  199. package/dist/structure/StructureScene.svelte.d.ts +2 -1
  200. package/dist/structure/atom-properties.d.ts +6 -2
  201. package/dist/structure/atom-properties.js +38 -25
  202. package/dist/structure/export.js +10 -7
  203. package/dist/structure/ferrox-wasm-types.d.ts +3 -2
  204. package/dist/structure/ferrox-wasm-types.js +0 -3
  205. package/dist/structure/ferrox-wasm.d.ts +3 -2
  206. package/dist/structure/ferrox-wasm.js +1 -2
  207. package/dist/structure/index.d.ts +6 -0
  208. package/dist/structure/index.js +22 -0
  209. package/dist/structure/parse.js +19 -16
  210. package/dist/structure/partial-occupancy.d.ts +25 -0
  211. package/dist/structure/partial-occupancy.js +102 -0
  212. package/dist/structure/validation.js +6 -3
  213. package/dist/symmetry/SymmetryStats.svelte +18 -4
  214. package/dist/symmetry/WyckoffTable.svelte +18 -10
  215. package/dist/symmetry/index.d.ts +7 -4
  216. package/dist/symmetry/index.js +83 -18
  217. package/dist/table/HeatmapTable.svelte +425 -65
  218. package/dist/table/HeatmapTable.svelte.d.ts +12 -1
  219. package/dist/table/ToggleMenu.svelte +2 -0
  220. package/dist/table/index.d.ts +2 -0
  221. package/dist/trajectory/Trajectory.svelte +147 -145
  222. package/dist/trajectory/TrajectoryExportPane.svelte +13 -9
  223. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
  224. package/dist/trajectory/constants.d.ts +6 -0
  225. package/dist/trajectory/constants.js +7 -0
  226. package/dist/trajectory/extract.js +3 -5
  227. package/dist/trajectory/format-detect.d.ts +9 -0
  228. package/dist/trajectory/format-detect.js +76 -0
  229. package/dist/trajectory/frame-reader.d.ts +17 -0
  230. package/dist/trajectory/frame-reader.js +339 -0
  231. package/dist/trajectory/helpers.d.ts +15 -0
  232. package/dist/trajectory/helpers.js +187 -0
  233. package/dist/trajectory/index.d.ts +1 -0
  234. package/dist/trajectory/index.js +11 -4
  235. package/dist/trajectory/parse/ase.d.ts +2 -0
  236. package/dist/trajectory/parse/ase.js +76 -0
  237. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  238. package/dist/trajectory/parse/hdf5.js +121 -0
  239. package/dist/trajectory/parse/index.d.ts +12 -0
  240. package/dist/trajectory/parse/index.js +304 -0
  241. package/dist/trajectory/parse/lammps.d.ts +5 -0
  242. package/dist/trajectory/parse/lammps.js +169 -0
  243. package/dist/trajectory/parse/vasp.d.ts +2 -0
  244. package/dist/trajectory/parse/vasp.js +65 -0
  245. package/dist/trajectory/parse/xyz.d.ts +2 -0
  246. package/dist/trajectory/parse/xyz.js +109 -0
  247. package/dist/trajectory/types.d.ts +11 -0
  248. package/dist/trajectory/types.js +1 -0
  249. package/dist/utils.d.ts +2 -0
  250. package/dist/utils.js +4 -0
  251. package/dist/xrd/XrdPlot.svelte +6 -4
  252. package/dist/xrd/calc-xrd.js +0 -1
  253. package/package.json +30 -24
  254. package/readme.md +4 -4
  255. package/dist/trajectory/parse.d.ts +0 -42
  256. package/dist/trajectory/parse.js +0 -1267
  257. /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
@@ -6,9 +6,10 @@ import { format_num } from '../labels';
6
6
  import * as math from '../math';
7
7
  import { DEFAULTS } from '../settings';
8
8
  import { colors } from '../state.svelte';
9
- import { Arrow, atomic_radii, Cylinder, get_center_of_mass, Lattice, } from './';
9
+ import { Arrow, atomic_radii, Cylinder, get_center_of_mass, get_site_vector_info, Lattice, } from './';
10
10
  import { get_orig_site_idx, get_property_colors, } from './atom-properties';
11
11
  import * as measure from './measure';
12
+ import { compute_slice_geometry, merge_split_partial_sites, PARTIAL_OCCUPANCY_CAP_ARC, } from './partial-occupancy';
12
13
  import { T, useThrelte } from '@threlte/core';
13
14
  import * as extras from '@threlte/extras';
14
15
  import { untrack } from 'svelte';
@@ -35,7 +36,7 @@ $effect(() => {
35
36
  frame_id = requestAnimationFrame(animate);
36
37
  return () => cancelAnimationFrame(frame_id);
37
38
  });
38
- let { structure = undefined, base_structure = undefined, atom_radius = DEFAULTS.structure.atom_radius, same_size_atoms = false, camera_position = DEFAULTS.structure.camera_position, camera_projection = DEFAULTS.structure.camera_projection, rotation_damping = DEFAULTS.structure.rotation_damping, max_zoom = DEFAULTS.structure.max_zoom, min_zoom = DEFAULTS.structure.min_zoom, rotate_speed = DEFAULTS.structure.rotate_speed, zoom_speed = DEFAULTS.structure.zoom_speed, pan_speed = DEFAULTS.structure.pan_speed, zoom_to_cursor = DEFAULTS.structure.zoom_to_cursor, show_atoms = DEFAULTS.structure.show_atoms, show_bonds = DEFAULTS.structure.show_bonds, show_site_labels = DEFAULTS.structure.show_site_labels, show_site_indices = DEFAULTS.structure.show_site_indices, site_label_size = DEFAULTS.structure.site_label_size, site_label_offset = $bindable(DEFAULTS.structure.site_label_offset), site_label_bg_color = `color-mix(in srgb, #000000 0%, transparent)`, site_label_color = `#ffffff`, site_label_padding = 3, show_force_vectors = DEFAULTS.structure.show_force_vectors, force_scale = DEFAULTS.structure.force_scale, force_color = DEFAULTS.structure.force_color, gizmo = DEFAULTS.structure.show_gizmo, hovered_idx = $bindable(null), hovered_site = $bindable(null), float_fmt = `.3~f`, auto_rotate = DEFAULTS.structure.auto_rotate, bond_thickness = DEFAULTS.structure.bond_thickness, bond_color = DEFAULTS.structure.bond_color, bonding_strategy = DEFAULTS.structure.bonding_strategy, bonding_options = {}, fov = DEFAULTS.structure.fov, initial_zoom = DEFAULTS.structure.initial_zoom, ambient_light = DEFAULTS.structure.ambient_light, directional_light = DEFAULTS.structure.directional_light, sphere_segments = DEFAULTS.structure.sphere_segments, lattice_props = {}, atom_label, camera_is_moving = $bindable(false), width = 0, height = 0, measure_mode = `distance`, selected_sites = $bindable([]), measured_sites = $bindable([]), added_bonds = $bindable([]), removed_bonds = $bindable([]), selection_highlight_color = `#6cf0ff`,
39
+ let { structure = undefined, base_structure = undefined, atom_radius = DEFAULTS.structure.atom_radius, same_size_atoms = false, camera_position = DEFAULTS.structure.camera_position, camera_target = undefined, camera_projection = DEFAULTS.structure.camera_projection, rotation_damping = DEFAULTS.structure.rotation_damping, max_zoom = DEFAULTS.structure.max_zoom, min_zoom = DEFAULTS.structure.min_zoom, rotate_speed = DEFAULTS.structure.rotate_speed, zoom_speed = DEFAULTS.structure.zoom_speed, pan_speed = DEFAULTS.structure.pan_speed, zoom_to_cursor = DEFAULTS.structure.zoom_to_cursor, show_atoms = DEFAULTS.structure.show_atoms, show_bonds = DEFAULTS.structure.show_bonds, show_site_labels = DEFAULTS.structure.show_site_labels, show_site_indices = DEFAULTS.structure.show_site_indices, site_label_size = DEFAULTS.structure.site_label_size, site_label_offset = $bindable(DEFAULTS.structure.site_label_offset), site_label_bg_color = `color-mix(in srgb, #000000 0%, transparent)`, site_label_color = `#ffffff`, site_label_padding = 3, show_force_vectors = DEFAULTS.structure.show_force_vectors, force_scale = DEFAULTS.structure.force_scale, force_color = DEFAULTS.structure.force_color, gizmo = DEFAULTS.structure.show_gizmo, hovered_idx = $bindable(null), hovered_site = $bindable(null), float_fmt = `.3~f`, auto_rotate = DEFAULTS.structure.auto_rotate, bond_thickness = DEFAULTS.structure.bond_thickness, bond_color = DEFAULTS.structure.bond_color, bonding_strategy = DEFAULTS.structure.bonding_strategy, bonding_options = {}, fov = DEFAULTS.structure.fov, initial_zoom = DEFAULTS.structure.initial_zoom, ambient_light = DEFAULTS.structure.ambient_light, directional_light = DEFAULTS.structure.directional_light, sphere_segments = DEFAULTS.structure.sphere_segments, lattice_props = {}, atom_label, camera_is_moving = $bindable(false), width = 0, height = 0, measure_mode = `distance`, selected_sites = $bindable([]), measured_sites = $bindable([]), added_bonds = $bindable([]), removed_bonds = $bindable([]), selection_highlight_color = `#6cf0ff`,
39
40
  // Active highlight group with different color
40
41
  active_sites = $bindable([]), active_highlight_color = `var(--struct-active-highlight-color, #2563eb)`, rotation = DEFAULTS.structure.rotation, scene = $bindable(), camera = $bindable(), orbit_controls = $bindable(), rotation_target_ref = $bindable(), initial_computed_zoom = $bindable(), hidden_elements = $bindable(new SvelteSet()), hidden_prop_vals = $bindable(new SvelteSet()), element_radius_overrides = $bindable({}), site_radius_overrides = $bindable(new SvelteMap()), atom_color_config = {
41
42
  mode: DEFAULTS.structure.atom_color_mode,
@@ -49,9 +50,7 @@ $effect(() => {
49
50
  scene = threlte.scene;
50
51
  camera = threlte.camera.current;
51
52
  if (threlte.renderer) {
52
- Object.assign(threlte.renderer.domElement, {
53
- __renderer: threlte.renderer,
54
- });
53
+ Object.assign(threlte.renderer.domElement, { __renderer: threlte.renderer });
55
54
  }
56
55
  });
57
56
  // Expose rotation target for external reset
@@ -119,12 +118,10 @@ function toggle_bond(site_1, site_2) {
119
118
  }
120
119
  // bond_pairs may not be sorted, so use get_bond_key for comparison
121
120
  const key = `${idx_i}-${idx_j}`;
122
- if (bond_pairs.some((bond) => get_bond_key(bond.site_idx_1, bond.site_idx_2) === key)) {
121
+ if (bond_pairs.some((bond) => get_bond_key(bond.site_idx_1, bond.site_idx_2) === key))
123
122
  removed_bonds = [...removed_bonds, [idx_i, idx_j]];
124
- }
125
- else {
123
+ else
126
124
  added_bonds = [...added_bonds, [idx_i, idx_j]];
127
- }
128
125
  }
129
126
  // Deduplicate clicks: when a highlight sphere and the underlying atom both
130
127
  // intercept the same native click, only the first intersection should fire.
@@ -132,11 +129,13 @@ function toggle_bond(site_1, site_2) {
132
129
  let last_native_event = null;
133
130
  function toggle_selection(site_index, evt) {
134
131
  evt?.stopPropagation?.();
135
- const native = evt?.nativeEvent;
136
- if (native && native === last_native_event)
137
- return;
138
- if (native)
139
- last_native_event = native;
132
+ const native_event = evt
133
+ ?.nativeEvent;
134
+ if (native_event instanceof Event) {
135
+ if (native_event === last_native_event)
136
+ return;
137
+ last_native_event = native_event;
138
+ }
140
139
  if (measure_mode === `edit-bonds`) {
141
140
  // In edit-bonds mode, select atoms to add/remove bonds between them
142
141
  const new_sites = measured_sites.includes(site_index)
@@ -232,7 +231,7 @@ $effect(() => {
232
231
  computed_zoom = new_zoom;
233
232
  });
234
233
  $effect.pre(() => {
235
- if (camera_position.every((v) => v === 0) && structure) {
234
+ if (camera_position.every((val) => val === 0) && structure) {
236
235
  const distance = Math.max(1, structure_size) * (60 / fov);
237
236
  camera_position = [distance, distance * 0.3, distance * 0.8];
238
237
  }
@@ -268,7 +267,8 @@ const calc_weighted_radius = (site) => {
268
267
  let atom_data = $derived.by(() => {
269
268
  if (!show_atoms || !structure?.sites)
270
269
  return [];
271
- return structure.sites.flatMap((site, site_idx) => {
270
+ const render_sites = merge_split_partial_sites(structure.sites, hidden_elements);
271
+ return render_sites.flatMap(({ site_idx, site, is_image_atom }) => {
272
272
  const orig_idx = get_orig_site_idx(site, site_idx);
273
273
  // Skip sites with hidden property values
274
274
  const prop_val = property_colors?.values[orig_idx];
@@ -283,23 +283,25 @@ let atom_data = $derived.by(() => {
283
283
  // Use property color if available (e.g. coordination number, Wyckoff position)
284
284
  // Otherwise, each species gets its own element color (important for disordered sites)
285
285
  const site_property_color = property_colors?.colors[orig_idx];
286
- // Detect image atoms by presence of orig_site_idx property (set by get_pbc_image_sites)
287
- const is_image_atom = site.properties?.orig_site_idx != null;
288
- let start_angle = 0;
289
- return site.species
290
- .filter(({ element }) => !hidden_elements.has(element))
291
- .map(({ element, occu }) => ({
292
- site_idx,
293
- element,
294
- occupancy: occu,
295
- position: site.xyz,
296
- radius,
297
- color: site_property_color ?? colors.element?.[element],
298
- has_partial_occupancy: occu < 1,
299
- start_phi: 2 * Math.PI * start_angle,
300
- end_phi: 2 * Math.PI * (start_angle += occu),
301
- is_image_atom,
302
- }));
286
+ const visible_species = site.species.filter(({ element }) => !hidden_elements.has(element));
287
+ const slice_geometry = compute_slice_geometry(visible_species);
288
+ return slice_geometry.map((slice_data) => {
289
+ return {
290
+ site_idx,
291
+ element: slice_data.element,
292
+ occupancy: slice_data.occupancy,
293
+ position: site.xyz,
294
+ radius,
295
+ color: site_property_color ?? colors.element?.[slice_data.element],
296
+ has_partial_occupancy: slice_data.occupancy < 1,
297
+ start_phi: slice_data.start_phi,
298
+ end_phi: slice_data.end_phi,
299
+ phi_length: slice_data.phi_length,
300
+ render_start_cap: slice_data.render_start_cap,
301
+ render_end_cap: slice_data.render_end_cap,
302
+ is_image_atom,
303
+ };
304
+ });
303
305
  });
304
306
  });
305
307
  let filtered_bond_pairs = $derived.by(() => {
@@ -389,21 +391,47 @@ const get_site_radius = (site, site_idx) => {
389
391
  const base_radius = same_size_atoms ? 1 : override ?? calc_weighted_radius(site);
390
392
  return base_radius * atom_radius;
391
393
  };
392
- let force_data = $derived.by(() => show_force_vectors && structure?.sites
393
- ? structure?.sites
394
+ // Interpolate between spin-down (#3498db blue) and spin-up (#e74c3c red)
395
+ // based on the z-component direction of a magnetic vector
396
+ function spin_direction_color(vec) {
397
+ const mag = Math.hypot(...vec);
398
+ const z_frac = mag > 1e-10 ? (vec[2] / mag + 1) / 2 : 0.5; // 0=down, 1=up
399
+ const red = Math.round(52 + (231 - 52) * z_frac);
400
+ const grn = Math.round(152 + (76 - 152) * z_frac);
401
+ const blu = Math.round(219 + (60 - 219) * z_frac);
402
+ return `#${red.toString(16).padStart(2, `0`)}${grn.toString(16).padStart(2, `0`)}${blu.toString(16).padStart(2, `0`)}`;
403
+ }
404
+ // Extract per-site vectors from force, magmom, or spin properties.
405
+ // For magmom/spin, color interpolates between spin-up (red) and spin-down (blue).
406
+ let force_data = $derived.by(() => {
407
+ if (!show_force_vectors || !structure?.sites)
408
+ return [];
409
+ return structure.sites
394
410
  .map((site) => {
395
- if (!site.properties?.force || !Array.isArray(site.properties.force))
411
+ const info = get_site_vector_info(site);
412
+ if (!info)
396
413
  return null;
397
- const majority_element = site.species.reduce((max, spec) => spec.occu > max.occu ? spec : max).element;
414
+ let arrow_color;
415
+ if (info.key !== `force`) {
416
+ arrow_color = spin_direction_color(info.vec);
417
+ }
418
+ else {
419
+ const majority_element = site.species.length > 0
420
+ ? site.species.reduce((max, spec) => spec.occu > max.occu ? spec : max)
421
+ .element
422
+ : undefined;
423
+ arrow_color = (majority_element && colors.element?.[majority_element]) ||
424
+ force_color;
425
+ }
398
426
  return {
399
427
  position: site.xyz,
400
- vector: site.properties.force,
428
+ vector: info.vec,
401
429
  scale: force_scale,
402
- color: colors.element?.[majority_element] || force_color,
430
+ color: arrow_color,
403
431
  };
404
432
  })
405
- .filter((item) => item !== null)
406
- : []);
433
+ .filter((item) => item !== null);
434
+ });
407
435
  let instanced_atom_groups = $derived(Object.values(atom_data
408
436
  .filter((atom) => !atom.has_partial_occupancy)
409
437
  .reduce((groups, atom) => {
@@ -452,7 +480,7 @@ let orbit_controls_props = $derived({
452
480
  zoomToCursor: zoom_to_cursor,
453
481
  enablePan: pan_speed > 0,
454
482
  panSpeed: pan_speed,
455
- target: rotation_target,
483
+ target: camera_target ?? rotation_target,
456
484
  maxZoom: max_zoom,
457
485
  minZoom: min_zoom,
458
486
  autoRotate: Boolean(auto_rotate),
@@ -498,27 +526,17 @@ let measure_line_color = $derived.by(() => {
498
526
  style:padding="{site_label_padding}px"
499
527
  style:color={site_label_color}
500
528
  >
501
- {#if show_site_labels && show_site_indices}
529
+ {#if show_site_labels}
502
530
  {#if site.species.length === 1}
503
- {site.species[0].element}-{site_idx + 1}
531
+ {site.species[0].element}{#if show_site_indices}-{site_idx + 1}{/if}
504
532
  {:else}
505
533
  {@html site.species.map((spec) =>
506
534
  `${spec.element}<sub>${
507
535
  format_num(spec.occu, `.3~`).replace(`0.`, `.`)
508
536
  }</sub>`
509
- ).join(``)}-{
510
- site_idx + 1
511
- }
512
- {/if}
513
- {:else if show_site_labels}
514
- {#if site.species.length === 1}
515
- {site.species[0].element}
516
- {:else}
517
- {@html site.species.map((spec) =>
518
- `${spec.element}<sub>${
519
- format_num(spec.occu, `.3~`).replace(`0.`, `.`)
520
- }</sub>`
521
- ).join(``)}
537
+ ).join(``)}{#if show_site_indices}-{
538
+ site_idx + 1
539
+ }{/if}
522
540
  {/if}
523
541
  {:else if show_site_indices}
524
542
  {site_idx + 1}
@@ -637,7 +655,7 @@ let measure_line_color = $derived.by(() => {
637
655
  sphere_segments,
638
656
  sphere_segments,
639
657
  atom.start_phi,
640
- 2 * Math.PI * atom.occupancy,
658
+ atom.phi_length,
641
659
  ]}
642
660
  />
643
661
  <T.MeshStandardMaterial
@@ -647,9 +665,16 @@ let measure_line_color = $derived.by(() => {
647
665
  />
648
666
  </T.Mesh>
649
667
 
650
- {#if atom.has_partial_occupancy}
668
+ {#if atom.has_partial_occupancy && atom.render_start_cap}
651
669
  <T.Mesh rotation={[0, atom.start_phi, 0]}>
652
- <T.CircleGeometry args={[0.5, sphere_segments]} />
670
+ <T.CircleGeometry
671
+ args={[
672
+ 0.5,
673
+ sphere_segments,
674
+ PARTIAL_OCCUPANCY_CAP_ARC.start_cap_arc_start,
675
+ PARTIAL_OCCUPANCY_CAP_ARC.arc_length,
676
+ ]}
677
+ />
653
678
  <T.MeshStandardMaterial
654
679
  color={partial_color}
655
680
  side={2}
@@ -657,8 +682,17 @@ let measure_line_color = $derived.by(() => {
657
682
  transparent={partial_edit_image}
658
683
  />
659
684
  </T.Mesh>
685
+ {/if}
686
+ {#if atom.has_partial_occupancy && atom.render_end_cap}
660
687
  <T.Mesh rotation={[0, atom.end_phi, 0]}>
661
- <T.CircleGeometry args={[0.5, sphere_segments]} />
688
+ <T.CircleGeometry
689
+ args={[
690
+ 0.5,
691
+ sphere_segments,
692
+ PARTIAL_OCCUPANCY_CAP_ARC.end_cap_arc_start,
693
+ PARTIAL_OCCUPANCY_CAP_ARC.arc_length,
694
+ ]}
695
+ />
662
696
  <T.MeshStandardMaterial
663
697
  color={partial_color}
664
698
  side={2}
@@ -806,8 +840,12 @@ let measure_line_color = $derived.by(() => {
806
840
 
807
841
  <!-- hovered site tooltip -->
808
842
  {#if hovered_site && !camera_is_moving && active_tooltip === `atom`}
809
- {@const abc = hovered_site.abc.map((x) => format_num(x, float_fmt)).join(`, `)}
810
- {@const xyz = hovered_site.xyz.map((x) => format_num(x, float_fmt)).join(`, `)}
843
+ {@const abc = hovered_site.abc.map((val) => format_num(val, float_fmt)).join(
844
+ `, `,
845
+ )}
846
+ {@const xyz = hovered_site.xyz.map((val) => format_num(val, float_fmt)).join(
847
+ `, `,
848
+ )}
811
849
  {@const bond_neighbors = (() => {
812
850
  if (hovered_idx == null || !structure?.sites) return []
813
851
  return filtered_bond_pairs
@@ -874,7 +912,7 @@ let measure_line_color = $derived.by(() => {
874
912
  structure?.sites}
875
913
  {@const selected_atoms = selected_sites
876
914
  .map((idx) => structure?.sites?.[idx])
877
- .filter(Boolean) as Site[]}
915
+ .filter((site): site is Site => site != null)}
878
916
  {#if selected_atoms.length > 0}
879
917
  {@const avg = (dim: number) =>
880
918
  selected_atoms.reduce((sum, atom) => sum + atom.xyz[dim], 0) /
@@ -994,12 +1032,12 @@ let measure_line_color = $derived.by(() => {
994
1032
  {:else if measure_mode === `angle` && measured_sites.length >= 3}
995
1033
  {#each measured_sites as idx_center (idx_center)}
996
1034
  {@const center = structure.sites[idx_center]}
997
- {#each measured_sites.filter((x) => x !== idx_center) as
1035
+ {#each measured_sites.filter((idx) => idx !== idx_center) as
998
1036
  idx_a,
999
1037
  loop_idx
1000
1038
  (idx_center + `-` + idx_a)
1001
1039
  }
1002
- {#each measured_sites.filter((x) => x !== idx_center).slice(loop_idx + 1) as
1040
+ {#each measured_sites.filter((idx) => idx !== idx_center).slice(loop_idx + 1) as
1003
1041
  idx_b
1004
1042
  (idx_center + `-` + idx_a + `-` + idx_b)
1005
1043
  }
@@ -18,6 +18,7 @@ type $$ComponentProps = {
18
18
  atom_radius?: number;
19
19
  same_size_atoms?: boolean;
20
20
  camera_position?: [x: number, y: number, z: number];
21
+ camera_target?: Vec3;
21
22
  camera_projection?: CameraProjection;
22
23
  rotation_damping?: number;
23
24
  max_zoom?: number;
@@ -90,6 +91,6 @@ type $$ComponentProps = {
90
91
  volumetric_data?: VolumetricData;
91
92
  isosurface_settings?: IsosurfaceSettings;
92
93
  };
93
- declare const StructureScene: import("svelte").Component<$$ComponentProps, {}, "cursor" | "site_label_offset" | "hidden_elements" | "hidden_prop_vals" | "element_radius_overrides" | "site_radius_overrides" | "selected_sites" | "hovered_idx" | "hovered_site" | "camera_is_moving" | "measured_sites" | "added_bonds" | "removed_bonds" | "active_sites" | "scene" | "camera" | "orbit_controls" | "rotation_target_ref" | "initial_computed_zoom" | "add_atom_mode" | "add_element" | "dragging_atoms">;
94
+ declare const StructureScene: import("svelte").Component<$$ComponentProps, {}, "cursor" | "site_label_offset" | "scene" | "camera" | "orbit_controls" | "hidden_elements" | "hidden_prop_vals" | "element_radius_overrides" | "site_radius_overrides" | "selected_sites" | "hovered_idx" | "hovered_site" | "camera_is_moving" | "measured_sites" | "added_bonds" | "removed_bonds" | "active_sites" | "rotation_target_ref" | "initial_computed_zoom" | "add_atom_mode" | "add_element" | "dragging_atoms">;
94
95
  type StructureScene = ReturnType<typeof StructureScene>;
95
96
  export default StructureScene;
@@ -1,6 +1,6 @@
1
- import type { AnyStructure, Site } from './';
2
1
  import type { ColorScaleType, D3InterpolateName } from '../colors';
3
2
  import type { AtomColorMode } from '../settings';
3
+ import type { AnyStructure, Site } from './';
4
4
  import type { BondingStrategy } from './bonding';
5
5
  import type { MoyoDataset } from '@spglib/moyo-wasm';
6
6
  export interface AtomColorConfig {
@@ -16,6 +16,9 @@ export interface AtomPropertyColors {
16
16
  max_value?: number;
17
17
  unique_values?: (number | string)[];
18
18
  }
19
+ type SymmetryDataWithOrigMap = MoyoDataset & {
20
+ orig_site_indices_by_std_idx?: number[][];
21
+ };
19
22
  export declare const get_d3_color_scales: () => string[];
20
23
  export declare function apply_color_scale(vals: number[], scale?: string, type?: ColorScaleType): {
21
24
  colors: string[];
@@ -27,7 +30,8 @@ export declare const apply_categorical_color_scale: (vals: string[], scale?: str
27
30
  };
28
31
  export declare const get_orig_site_idx: (site: Site | undefined, site_idx: number) => number;
29
32
  export declare function get_coordination_colors(structure: AnyStructure, strategy?: BondingStrategy, scale?: string, type?: ColorScaleType): AtomPropertyColors;
30
- export declare function get_wyckoff_colors(structure: AnyStructure, sym_data: MoyoDataset | null, scale?: string): AtomPropertyColors;
33
+ export declare function get_wyckoff_colors(structure: AnyStructure, sym_data: SymmetryDataWithOrigMap | null, scale?: string): AtomPropertyColors;
31
34
  export declare function get_custom_colors(structure: AnyStructure, fn: (site: Site, idx: number) => number | string, scale?: string, type?: ColorScaleType): AtomPropertyColors;
32
35
  export declare function get_atom_colors(structure: AnyStructure, config: Partial<AtomColorConfig>, bonding_strategy?: BondingStrategy, sym_data?: MoyoDataset | null): AtomPropertyColors;
33
36
  export declare function get_property_colors(structure: AnyStructure | undefined, config: Partial<AtomColorConfig>, bonding_strategy: BondingStrategy, sym_data: MoyoDataset | null): AtomPropertyColors | null;
37
+ export {};
@@ -15,6 +15,25 @@ const get_interpolator = (scale) => {
15
15
  return interp_fn;
16
16
  };
17
17
  const to_hex = (interp_fn, t) => rgb(interp_fn(t)).formatHex();
18
+ const build_image_site = (site, lattice_T, offset, orig_idx) => {
19
+ const img_abc = [
20
+ site.abc[0] + offset[0],
21
+ site.abc[1] + offset[1],
22
+ site.abc[2] + offset[2],
23
+ ];
24
+ return {
25
+ ...site,
26
+ abc: img_abc,
27
+ xyz: math.mat3x3_vec3_multiply(lattice_T, img_abc),
28
+ properties: { ...site.properties, orig_site_idx: orig_idx },
29
+ };
30
+ };
31
+ const get_all_offsets = (pbc) => [-1, 0, 1]
32
+ .flatMap((dx) => [-1, 0, 1].flatMap((dy) => [-1, 0, 1].map((dz) => [dx, dy, dz])))
33
+ .filter(([dx, dy, dz]) => !(dx === 0 && dy === 0 && dz === 0) &&
34
+ (pbc[0] || dx === 0) &&
35
+ (pbc[1] || dy === 0) &&
36
+ (pbc[2] || dz === 0));
18
37
  const make_categorical = (vals, scale, sort_fn) => {
19
38
  const interp_fn = get_interpolator(scale);
20
39
  const uniq = sort_fn ? [...new Set(vals)].sort(sort_fn) : [...new Set(vals)].sort();
@@ -68,24 +87,10 @@ function expand_structure_for_pbc(structure) {
68
87
  const { sites, lattice } = structure;
69
88
  const lattice_T = math.transpose_3x3_matrix(lattice.matrix);
70
89
  const pbc = lattice.pbc ?? [true, true, true];
71
- // All valid image offsets respecting PBC
72
- const all_offsets = [-1, 0, 1]
73
- .flatMap((dx) => [-1, 0, 1].flatMap((dy) => [-1, 0, 1].map((dz) => [dx, dy, dz])))
74
- .filter(([dx, dy, dz]) => !(dx === 0 && dy === 0 && dz === 0) &&
75
- (pbc[0] || dx === 0) &&
76
- (pbc[1] || dy === 0) &&
77
- (pbc[2] || dz === 0));
90
+ const all_offsets = get_all_offsets(pbc);
78
91
  // Small structures: expand all atoms
79
92
  if (sites.length < 20 || !pbc.some((periodic) => periodic)) {
80
- const image_sites = sites.flatMap((site, orig_idx) => all_offsets.map(([dx, dy, dz]) => {
81
- const img_abc = [site.abc[0] + dx, site.abc[1] + dy, site.abc[2] + dz];
82
- return {
83
- ...site,
84
- abc: img_abc,
85
- xyz: math.mat3x3_vec3_multiply(lattice_T, img_abc),
86
- properties: { ...site.properties, orig_site_idx: orig_idx },
87
- };
88
- }));
93
+ const image_sites = sites.flatMap((site, orig_idx) => all_offsets.map((offset) => build_image_site(site, lattice_T, offset, orig_idx)));
89
94
  return { ...structure, sites: [...sites, ...image_sites] };
90
95
  }
91
96
  // Large structures: only expand atoms near boundaries (within 5Å bond distance)
@@ -96,15 +101,7 @@ function expand_structure_for_pbc(structure) {
96
101
  .filter(([dx, dy, dz]) => (dx === 0 || (dx === -1 ? norm[0] <= cutoff[0] : norm[0] >= 1 - cutoff[0])) &&
97
102
  (dy === 0 || (dy === -1 ? norm[1] <= cutoff[1] : norm[1] >= 1 - cutoff[1])) &&
98
103
  (dz === 0 || (dz === -1 ? norm[2] <= cutoff[2] : norm[2] >= 1 - cutoff[2])))
99
- .map(([dx, dy, dz]) => {
100
- const img_abc = [site.abc[0] + dx, site.abc[1] + dy, site.abc[2] + dz];
101
- return {
102
- ...site,
103
- abc: img_abc,
104
- xyz: math.mat3x3_vec3_multiply(lattice_T, img_abc),
105
- properties: { ...site.properties, orig_site_idx: orig_idx },
106
- };
107
- });
104
+ .map((offset) => build_image_site(site, lattice_T, offset, orig_idx));
108
105
  });
109
106
  return { ...structure, sites: [...sites, ...image_sites] };
110
107
  }
@@ -135,9 +132,25 @@ export function get_wyckoff_colors(structure, sym_data, scale = DEFAULT_COLOR_SC
135
132
  unique_values: [`unknown`],
136
133
  };
137
134
  }
135
+ const wyckoff_by_orig_idx = new Map();
136
+ const mapping_by_std_idx = sym_data.orig_site_indices_by_std_idx;
137
+ if (mapping_by_std_idx) {
138
+ for (let std_idx = 0; std_idx < sym_data.wyckoffs.length; std_idx += 1) {
139
+ const wyckoff = sym_data.wyckoffs[std_idx];
140
+ for (const orig_idx of mapping_by_std_idx[std_idx] ?? []) {
141
+ if (!wyckoff_by_orig_idx.has(orig_idx))
142
+ wyckoff_by_orig_idx.set(orig_idx, wyckoff);
143
+ }
144
+ }
145
+ }
138
146
  // Create unique orbit identifiers: Wyckoff position + element symbol
139
147
  const orbit_ids = structure.sites.map((site, idx) => {
140
148
  const sym_idx = get_orig_site_idx(site, idx);
149
+ const mapped_wyckoff = wyckoff_by_orig_idx.get(sym_idx);
150
+ if (mapped_wyckoff !== undefined) {
151
+ const element = site.species[0]?.element ?? `?`;
152
+ return mapped_wyckoff ? `${mapped_wyckoff}|${element}` : `unknown`;
153
+ }
141
154
  if (sym_idx >= sym_data.wyckoffs.length) {
142
155
  console.error(`[get_wyckoff_colors] Site ${idx} (maps to ${sym_idx}) has no Wyckoff data. ` +
143
156
  `Structure has ${n} sites but symmetry data only has ${sym_data.wyckoffs.length}.`);
@@ -10,13 +10,16 @@ export function has_color_property(mat) {
10
10
  if (!(`color` in mat))
11
11
  return false;
12
12
  const color = mat.color;
13
- // Check for Color-like object with r, g, b properties (duck typing)
14
- return (color !== null &&
15
- typeof color === `object` &&
16
- `r` in color &&
17
- `g` in color &&
18
- `b` in color &&
19
- typeof color.r === `number`);
13
+ if (!color || typeof color !== `object`)
14
+ return false;
15
+ // Check for Color-like object with numeric r/g/b channels (duck typing)
16
+ const color_obj = color;
17
+ const red_channel = color_obj.r;
18
+ const green_channel = color_obj.g;
19
+ const blue_channel = color_obj.b;
20
+ return typeof red_channel === `number` &&
21
+ typeof green_channel === `number` &&
22
+ typeof blue_channel === `number`;
20
23
  }
21
24
  // Extract color from a ShaderMaterial by checking common color uniform patterns
22
25
  function extract_shader_color(shader_mat) {
@@ -1,3 +1,4 @@
1
+ import type { Vec3 } from '../math';
1
2
  export type WasmResult<T> = {
2
3
  ok: T;
3
4
  } | {
@@ -14,7 +15,7 @@ export declare function unwrap_or<T>(result: WasmResult<T>, default_value: T): T
14
15
  export interface NeighborListResult {
15
16
  center_indices: number[];
16
17
  neighbor_indices: number[];
17
- image_offsets: [number, number, number][];
18
+ image_offsets: Vec3[];
18
19
  distances: number[];
19
20
  }
20
21
  export interface MatcherOptions {
@@ -28,7 +29,7 @@ export interface MatcherOptions {
28
29
  export type StructureFormat = `cif` | `poscar` | `json`;
29
30
  export type ReductionAlgorithm = `niggli` | `lll`;
30
31
  export interface HklInfo {
31
- hkl: [number, number, number];
32
+ hkl: Vec3;
32
33
  multiplicity: number;
33
34
  }
34
35
  export interface XrdPattern {
@@ -1,6 +1,3 @@
1
- // Pure type definitions and utility functions for ferrox-wasm results.
2
- // This module has no WASM side effects, making it safe to import in tests
3
- // without triggering WASM resolution.
4
1
  // Type guard to check if result is successful
5
2
  export function is_ok(result) {
6
3
  return `ok` in result;
@@ -1,3 +1,4 @@
1
+ import type { Vec3 } from '../math';
1
2
  import type { Crystal } from './';
2
3
  import type { MatcherOptions, NeighborListResult, ReductionAlgorithm, StructureFormat, WasmResult, XrdOptions, XrdPattern } from './ferrox-wasm-types';
3
4
  export * from './ferrox-wasm-types';
@@ -81,8 +82,8 @@ export declare function interpolate_structures(start: Crystal, end: Crystal, n_i
81
82
  }): Promise<WasmResult<Crystal[]>>;
82
83
  export declare function copy_structure(structure: Crystal, sanitize?: boolean): Promise<WasmResult<Crystal>>;
83
84
  export declare function wrap_to_unit_cell(structure: Crystal): Promise<WasmResult<Crystal>>;
84
- export declare function create_supercell_matrix(structure: Crystal, matrix: [[number, number, number], [number, number, number], [number, number, number]]): Promise<WasmResult<Crystal>>;
85
- export declare function translate_sites(structure: Crystal, indices: number[], vector: [number, number, number], frac_coords?: boolean): Promise<WasmResult<Crystal>>;
85
+ export declare function create_supercell_matrix(structure: Crystal, matrix: [Vec3, Vec3, Vec3]): Promise<WasmResult<Crystal>>;
86
+ export declare function translate_sites(structure: Crystal, indices: number[], vector: Vec3, frac_coords?: boolean): Promise<WasmResult<Crystal>>;
86
87
  export declare function perturb_structure(structure: Crystal, distance: number, options?: {
87
88
  min_distance?: number;
88
89
  seed?: number;
@@ -25,8 +25,7 @@ export function ensure_ferrox_wasm_ready() {
25
25
  init_promise = (async () => {
26
26
  try {
27
27
  // Dynamic import to avoid loading WASM until needed
28
- const { default: init } = (await import(
29
- /* @vite-ignore */ `matterviz-wasm`));
28
+ const { default: init } = (await import(`matterviz-wasm`));
30
29
  // Initialize WASM module (the package handles WASM loading internally)
31
30
  wasm_module = await init();
32
31
  return wasm_module;
@@ -67,6 +67,11 @@ export declare function format_formula_by_electronegativity(structure: AnyStruct
67
67
  export declare const atomic_radii: CompositionType;
68
68
  export declare function get_density(structure: Crystal): number;
69
69
  export declare function get_center_of_mass(structure: AnyStructure): Vec3;
70
+ export declare function get_site_vector_info(site: Site): {
71
+ vec: Vec3;
72
+ key: string;
73
+ } | null;
74
+ export declare function get_site_vector(site: Site): Vec3 | null;
70
75
  export interface StructureHandlerData {
71
76
  structure?: AnyStructure;
72
77
  filename?: string;
@@ -75,6 +80,7 @@ export interface StructureHandlerData {
75
80
  error_msg?: string;
76
81
  fullscreen?: boolean;
77
82
  camera_position?: Vec3;
83
+ camera_target?: Vec3;
78
84
  camera_has_moved?: boolean;
79
85
  color_scheme?: string;
80
86
  performance_mode?: `quality` | `speed`;
@@ -84,3 +84,25 @@ export function get_center_of_mass(structure) {
84
84
  }
85
85
  return math.scale(center, 1 / total_weight);
86
86
  }
87
+ // Property keys checked for per-site vector data (force, magnetic moment, spin)
88
+ const VECTOR_PROPERTY_KEYS = [`force`, `magmom`, `spin`];
89
+ // Extract a vector and its source key from a site's properties. Checks force, magmom,
90
+ // and spin in priority order. Scalar values are converted to z-directed vectors [0, 0, val].
91
+ export function get_site_vector_info(site) {
92
+ const props = site.properties;
93
+ if (!props)
94
+ return null;
95
+ for (const key of VECTOR_PROPERTY_KEYS) {
96
+ const val = props[key];
97
+ if (Array.isArray(val) && val.length === 3 &&
98
+ val.every((elem) => typeof elem === `number` && isFinite(elem)))
99
+ return { vec: val, key };
100
+ if (typeof val === `number` && isFinite(val))
101
+ return { vec: [0, 0, val], key };
102
+ }
103
+ return null;
104
+ }
105
+ // Convenience wrapper returning just the vector (preserves existing API)
106
+ export function get_site_vector(site) {
107
+ return get_site_vector_info(site)?.vec ?? null;
108
+ }