matterviz 0.3.2 → 0.3.3

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 (280) 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/feedback/ClickFeedback.svelte +16 -5
  76. package/dist/feedback/DragOverlay.svelte +10 -2
  77. package/dist/feedback/Spinner.svelte +4 -2
  78. package/dist/feedback/StatusMessage.svelte +8 -2
  79. package/dist/fermi-surface/FermiSlice.svelte +118 -88
  80. package/dist/fermi-surface/FermiSurface.svelte +328 -187
  81. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  82. package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
  83. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  84. package/dist/fermi-surface/FermiSurfaceScene.svelte +535 -342
  85. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  86. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
  87. package/dist/fermi-surface/compute.js +16 -20
  88. package/dist/fermi-surface/parse.js +24 -14
  89. package/dist/fermi-surface/symmetry.js +2 -7
  90. package/dist/fermi-surface/types.d.ts +3 -5
  91. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1019 -765
  92. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +1 -1
  93. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +76 -22
  94. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +2 -3
  95. package/dist/icons.js +47 -0
  96. package/dist/index.d.ts +2 -1
  97. package/dist/index.js +2 -1
  98. package/dist/io/decompress.js +1 -1
  99. package/dist/io/export.d.ts +3 -0
  100. package/dist/io/export.js +129 -143
  101. package/dist/io/is-binary.js +2 -3
  102. package/dist/io/url-drop.js +1 -2
  103. package/dist/isosurface/Isosurface.svelte +202 -148
  104. package/dist/isosurface/IsosurfaceControls.svelte +46 -28
  105. package/dist/isosurface/parse.js +34 -29
  106. package/dist/isosurface/slice.js +5 -10
  107. package/dist/isosurface/types.d.ts +2 -1
  108. package/dist/isosurface/types.js +61 -12
  109. package/dist/labels.js +11 -8
  110. package/dist/layout/FullscreenToggle.svelte +11 -2
  111. package/dist/layout/InfoCard.svelte +38 -6
  112. package/dist/layout/InfoTag.svelte +63 -32
  113. package/dist/layout/PropertyFilter.svelte +82 -37
  114. package/dist/layout/SettingsSection.svelte +85 -55
  115. package/dist/layout/SubpageGrid.svelte +10 -2
  116. package/dist/layout/json-tree/JsonNode.svelte +183 -138
  117. package/dist/layout/json-tree/JsonTree.svelte +499 -413
  118. package/dist/layout/json-tree/JsonValue.svelte +127 -99
  119. package/dist/layout/json-tree/utils.js +4 -2
  120. package/dist/marching-cubes.js +25 -2
  121. package/dist/math.d.ts +13 -17
  122. package/dist/math.js +133 -67
  123. package/dist/overlays/ContextMenu.svelte +65 -40
  124. package/dist/overlays/DraggablePane.svelte +211 -139
  125. package/dist/periodic-table/PeriodicTable.svelte +278 -145
  126. package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
  127. package/dist/periodic-table/PropertySelect.svelte +25 -7
  128. package/dist/periodic-table/TableInset.svelte +8 -3
  129. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +446 -309
  130. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
  131. package/dist/phase-diagram/PhaseDiagramControls.svelte +102 -43
  132. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  133. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +63 -40
  134. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +71 -28
  135. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +1 -1
  136. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +158 -101
  137. package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
  138. package/dist/phase-diagram/build-diagram.js +9 -9
  139. package/dist/phase-diagram/colors.js +1 -3
  140. package/dist/phase-diagram/parse.js +10 -9
  141. package/dist/phase-diagram/svg-to-diagram.js +53 -49
  142. package/dist/phase-diagram/utils.d.ts +1 -0
  143. package/dist/phase-diagram/utils.js +80 -25
  144. package/dist/plot/AxisLabel.svelte +28 -3
  145. package/dist/plot/BarPlot.svelte +1182 -734
  146. package/dist/plot/BarPlot.svelte.d.ts +2 -2
  147. package/dist/plot/BarPlotControls.svelte +31 -5
  148. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  149. package/dist/plot/ColorBar.svelte +479 -329
  150. package/dist/plot/ColorScaleSelect.svelte +27 -6
  151. package/dist/plot/ElementScatter.svelte +36 -15
  152. package/dist/plot/FillArea.svelte +152 -95
  153. package/dist/plot/Histogram.svelte +934 -571
  154. package/dist/plot/Histogram.svelte.d.ts +1 -1
  155. package/dist/plot/HistogramControls.svelte +53 -9
  156. package/dist/plot/HistogramControls.svelte.d.ts +1 -1
  157. package/dist/plot/InteractiveAxisLabel.svelte +34 -11
  158. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
  159. package/dist/plot/Line.svelte +63 -28
  160. package/dist/plot/PlotControls.svelte +157 -114
  161. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  162. package/dist/plot/PlotLegend.svelte +174 -91
  163. package/dist/plot/PlotTooltip.svelte +45 -6
  164. package/dist/plot/PortalSelect.svelte +175 -147
  165. package/dist/plot/ReferenceLine.svelte +76 -22
  166. package/dist/plot/ReferenceLine3D.svelte +132 -107
  167. package/dist/plot/ReferencePlane.svelte +146 -121
  168. package/dist/plot/ScatterPlot.svelte +1681 -1091
  169. package/dist/plot/ScatterPlot.svelte.d.ts +2 -2
  170. package/dist/plot/ScatterPlot3D.svelte +256 -131
  171. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  172. package/dist/plot/ScatterPlot3DControls.svelte +113 -63
  173. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
  174. package/dist/plot/ScatterPlot3DScene.svelte +608 -403
  175. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  176. package/dist/plot/ScatterPlotControls.svelte +65 -25
  177. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  178. package/dist/plot/ScatterPoint.svelte +98 -26
  179. package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
  180. package/dist/plot/SpacegroupBarPlot.svelte +142 -85
  181. package/dist/plot/Surface3D.svelte +159 -108
  182. package/dist/plot/ZeroLines.svelte +55 -3
  183. package/dist/plot/ZoomRect.svelte +4 -2
  184. package/dist/plot/axis-utils.js +1 -3
  185. package/dist/plot/data-cleaning.js +12 -28
  186. package/dist/plot/data-transform.js +2 -1
  187. package/dist/plot/fill-utils.js +2 -0
  188. package/dist/plot/layout.d.ts +4 -1
  189. package/dist/plot/layout.js +33 -14
  190. package/dist/plot/reference-line.d.ts +2 -2
  191. package/dist/plot/reference-line.js +7 -5
  192. package/dist/plot/scales.js +24 -36
  193. package/dist/plot/types.d.ts +11 -23
  194. package/dist/plot/types.js +6 -11
  195. package/dist/plot/utils/label-placement.d.ts +32 -15
  196. package/dist/plot/utils/label-placement.js +227 -66
  197. package/dist/plot/utils/series-visibility.js +2 -3
  198. package/dist/rdf/RdfPlot.svelte +143 -91
  199. package/dist/rdf/calc-rdf.js +4 -5
  200. package/dist/sanitize.d.ts +4 -0
  201. package/dist/sanitize.js +107 -0
  202. package/dist/settings.d.ts +18 -6
  203. package/dist/settings.js +46 -16
  204. package/dist/spectral/Bands.svelte +632 -453
  205. package/dist/spectral/BandsAndDos.svelte +90 -49
  206. package/dist/spectral/BrillouinBandsDos.svelte +151 -93
  207. package/dist/spectral/Dos.svelte +389 -258
  208. package/dist/spectral/helpers.js +55 -43
  209. package/dist/state.svelte.d.ts +1 -1
  210. package/dist/state.svelte.js +3 -2
  211. package/dist/structure/Arrow.svelte +59 -20
  212. package/dist/structure/AtomLegend.svelte +215 -134
  213. package/dist/structure/Bond.svelte +73 -47
  214. package/dist/structure/CanvasTooltip.svelte +10 -2
  215. package/dist/structure/CellSelect.svelte +72 -45
  216. package/dist/structure/Cylinder.svelte +33 -17
  217. package/dist/structure/Lattice.svelte +88 -33
  218. package/dist/structure/Structure.svelte +1063 -797
  219. package/dist/structure/Structure.svelte.d.ts +1 -1
  220. package/dist/structure/StructureControls.svelte +349 -118
  221. package/dist/structure/StructureExportPane.svelte +124 -89
  222. package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
  223. package/dist/structure/StructureInfoPane.svelte +304 -237
  224. package/dist/structure/StructureScene.svelte +879 -443
  225. package/dist/structure/StructureScene.svelte.d.ts +15 -7
  226. package/dist/structure/atom-properties.js +8 -8
  227. package/dist/structure/bonding.js +6 -7
  228. package/dist/structure/export.js +14 -29
  229. package/dist/structure/ferrox-wasm.js +1 -1
  230. package/dist/structure/index.d.ts +13 -3
  231. package/dist/structure/index.js +83 -23
  232. package/dist/structure/measure.d.ts +2 -2
  233. package/dist/structure/measure.js +4 -44
  234. package/dist/structure/parse.js +113 -141
  235. package/dist/structure/partial-occupancy.js +7 -10
  236. package/dist/structure/pbc.d.ts +1 -0
  237. package/dist/structure/pbc.js +16 -6
  238. package/dist/structure/supercell.d.ts +2 -2
  239. package/dist/structure/supercell.js +12 -22
  240. package/dist/structure/validation.js +1 -2
  241. package/dist/symmetry/SymmetryStats.svelte +84 -41
  242. package/dist/symmetry/WyckoffTable.svelte +26 -6
  243. package/dist/symmetry/cell-transform.js +5 -3
  244. package/dist/symmetry/index.js +8 -7
  245. package/dist/symmetry/spacegroups.js +148 -148
  246. package/dist/table/HeatmapTable.svelte +790 -554
  247. package/dist/table/HeatmapTable.svelte.d.ts +1 -1
  248. package/dist/table/ToggleMenu.svelte +125 -92
  249. package/dist/table/index.js +2 -4
  250. package/dist/theme/ThemeControl.svelte +21 -12
  251. package/dist/time.js +4 -1
  252. package/dist/tooltip/TooltipContent.svelte +33 -8
  253. package/dist/trajectory/Trajectory.svelte +758 -558
  254. package/dist/trajectory/TrajectoryError.svelte +14 -3
  255. package/dist/trajectory/TrajectoryExportPane.svelte +137 -83
  256. package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
  257. package/dist/trajectory/extract.js +10 -26
  258. package/dist/trajectory/format-detect.js +5 -5
  259. package/dist/trajectory/frame-reader.d.ts +1 -1
  260. package/dist/trajectory/frame-reader.js +5 -12
  261. package/dist/trajectory/helpers.d.ts +0 -1
  262. package/dist/trajectory/helpers.js +2 -17
  263. package/dist/trajectory/index.js +14 -12
  264. package/dist/trajectory/parse/ase.js +5 -4
  265. package/dist/trajectory/parse/hdf5.js +26 -18
  266. package/dist/trajectory/parse/index.js +13 -18
  267. package/dist/trajectory/parse/lammps.js +17 -7
  268. package/dist/trajectory/parse/vasp.js +5 -2
  269. package/dist/trajectory/parse/xyz.js +8 -7
  270. package/dist/trajectory/plotting.js +13 -8
  271. package/dist/utils.d.ts +1 -0
  272. package/dist/utils.js +13 -0
  273. package/dist/xrd/XrdPlot.svelte +337 -247
  274. package/dist/xrd/broadening.js +14 -9
  275. package/dist/xrd/calc-xrd.js +12 -18
  276. package/dist/xrd/parse.d.ts +1 -1
  277. package/dist/xrd/parse.js +17 -17
  278. package/package.json +99 -103
  279. package/readme.md +1 -1
  280. /package/dist/theme/{themes.js → themes.mjs} +0 -0
@@ -74,9 +74,7 @@ export function sample_hkl_slice(volume, miller_indices, distance, n_points) {
74
74
  const unit_normal = math.normalize_vec3(plane_normal);
75
75
  // In-plane basis vectors
76
76
  const [u_vec, v_vec] = math.compute_in_plane_basis(unit_normal);
77
- // Compute lattice inverse for Cartesian → fractional conversion.
78
- // lattice rows are vectors [a, b, c], so cart = lattice^T * frac → frac = inv(lattice^T) * cart
79
- const lattice_inv = math.matrix_inverse_3x3(math.transpose_3x3_matrix(lattice));
77
+ const cart_to_frac = math.create_cart_to_frac(lattice);
80
78
  // Project all 8 unit cell corners onto the (u, v) plane to find sampling bounds.
81
79
  // Corners are at fractional coords (0 or 1) for each axis.
82
80
  let u_min = Infinity;
@@ -86,9 +84,9 @@ export function sample_hkl_slice(volume, miller_indices, distance, n_points) {
86
84
  let normal_min = Infinity;
87
85
  let normal_max = -Infinity;
88
86
  for (let ci = 0; ci < 8; ci++) {
89
- const fi = (ci & 1) ? 1 : 0;
90
- const fj = (ci & 2) ? 1 : 0;
91
- const fk = (ci & 4) ? 1 : 0;
87
+ const fi = ci & 1 ? 1 : 0;
88
+ const fj = ci & 2 ? 1 : 0;
89
+ const fk = ci & 4 ? 1 : 0;
92
90
  // Corner in Cartesian: frac * lattice
93
91
  const corner = [
94
92
  fi * lattice[0][0] + fj * lattice[1][0] + fk * lattice[2][0],
@@ -129,10 +127,7 @@ export function sample_hkl_slice(volume, miller_indices, distance, n_points) {
129
127
  const px = d_cartesian * unit_normal[0] + u_val * u_vec[0] + v_val * v_vec[0];
130
128
  const py = d_cartesian * unit_normal[1] + u_val * u_vec[1] + v_val * v_vec[1];
131
129
  const pz = d_cartesian * unit_normal[2] + u_val * u_vec[2] + v_val * v_vec[2];
132
- // Convert to fractional coordinates: frac = lattice_inv * p
133
- const fx = lattice_inv[0][0] * px + lattice_inv[0][1] * py + lattice_inv[0][2] * pz;
134
- const fy = lattice_inv[1][0] * px + lattice_inv[1][1] * py + lattice_inv[1][2] * pz;
135
- const fz = lattice_inv[2][0] * px + lattice_inv[2][1] * py + lattice_inv[2][2] * pz;
130
+ const [fx, fy, fz] = cart_to_frac([px, py, pz]);
136
131
  const val = trilinear_interpolate(grid, fx, fy, fz, periodic);
137
132
  data[row * width + col] = val;
138
133
  if (val < data_min)
@@ -45,7 +45,7 @@ export declare function pad_periodic_grid(grid: number[][][], dims: Vec3, pad_fr
45
45
  dims: Vec3;
46
46
  offset: Vec3;
47
47
  };
48
- export declare function downsample_grid(grid: number[][][], dims: Vec3): {
48
+ export declare function downsample_grid(grid: number[][][], dims: Vec3, max_points?: number): {
49
49
  grid: number[][][];
50
50
  dims: Vec3;
51
51
  factor: number;
@@ -53,3 +53,4 @@ export declare function downsample_grid(grid: number[][][], dims: Vec3): {
53
53
  export declare const DEFAULT_ISOSURFACE_SETTINGS: IsosurfaceSettings;
54
54
  export declare function auto_isosurface_settings(data_range: DataRange): IsosurfaceSettings;
55
55
  export declare function generate_layers(data_range: DataRange, n_layers: number): IsosurfaceLayer[];
56
+ export declare function tile_volumetric_data(volume: VolumetricData, scaling: Vec3): VolumetricData;
@@ -1,3 +1,4 @@
1
+ import { scale_lattice_matrix } from '../math';
1
2
  // Categorical palette for auto-coloring isosurface layers (Tailwind-inspired)
2
3
  export const LAYER_COLORS = [
3
4
  `#3b82f6`, // blue
@@ -15,8 +16,7 @@ export function grid_data_range(grid) {
15
16
  if (!grid.length || !grid[0]?.length || !grid[0][0]?.length) {
16
17
  return { min: 0, max: 0, abs_max: 0, mean: 0 };
17
18
  }
18
- let min_val = Infinity;
19
- let max_val = -Infinity;
19
+ let [min_val, max_val] = [Infinity, -Infinity];
20
20
  let sum = 0;
21
21
  let count = 0;
22
22
  for (const plane of grid) {
@@ -71,35 +71,41 @@ export function pad_periodic_grid(grid, dims, pad_fraction) {
71
71
  // Max total grid points before downsampling is applied for isosurface extraction.
72
72
  // 500K balances visual quality with interactive performance (<200ms marching cubes).
73
73
  const MAX_GRID_POINTS = 500_000;
74
- // Downsample a 3D volumetric grid to keep total point count under MAX_GRID_POINTS.
74
+ // Downsample a 3D volumetric grid to keep total point count under a budget.
75
75
  // Uses block averaging to preserve data fidelity while reducing grid dimensions.
76
76
  // Returns original grid/dims if already within budget.
77
- export function downsample_grid(grid, dims) {
77
+ export function downsample_grid(grid, dims, max_points = MAX_GRID_POINTS) {
78
78
  const [nx, ny, nz] = dims;
79
79
  const total = nx * ny * nz;
80
- if (total <= MAX_GRID_POINTS)
80
+ if (total <= max_points)
81
81
  return { grid, dims, factor: 1 };
82
+ // Floor at 1 to avoid Infinity in cbrt(total/0)
83
+ max_points = Math.max(1, max_points);
82
84
  // Increase factor until the clamped output fits within budget.
83
85
  // A single cbrt step can overshoot for anisotropic grids where max(2,...)
84
86
  // clamping prevents a small axis from shrinking below 2.
85
87
  // clamp_dim: returns 1 for single-cell axes, otherwise clamps to [2, src]
86
88
  const clamp_dim = (src, fac) => Math.min(src, Math.max(2, Math.ceil(src / fac)));
87
- let factor = Math.ceil(Math.cbrt(total / MAX_GRID_POINTS));
89
+ let factor = Math.ceil(Math.cbrt(total / max_points));
88
90
  let new_nx = clamp_dim(nx, factor);
89
91
  let new_ny = clamp_dim(ny, factor);
90
92
  let new_nz = clamp_dim(nz, factor);
91
- while (new_nx * new_ny * new_nz > MAX_GRID_POINTS) {
93
+ while (new_nx * new_ny * new_nz > max_points) {
92
94
  factor++;
95
+ const prev_total = new_nx * new_ny * new_nz;
93
96
  new_nx = clamp_dim(nx, factor);
94
97
  new_ny = clamp_dim(ny, factor);
95
98
  new_nz = clamp_dim(nz, factor);
99
+ // dims hit their floor (2 per axis or 1 for single-cell) — stop to avoid infinite loop
100
+ if (new_nx * new_ny * new_nz === prev_total)
101
+ break;
96
102
  }
97
103
  // Proportional partitioning: evenly divides [0, n) into new_n non-empty blocks.
98
104
  // Unlike fixed-stride (ix * factor), this is safe when max(2,...) clamping
99
105
  // produces more output cells than ceil(n/factor) would — no empty blocks.
100
106
  const partition = (n_out, n_src) => Array.from({ length: n_out }, (_, idx) => [
101
- Math.round(idx * n_src / n_out),
102
- Math.round((idx + 1) * n_src / n_out),
107
+ Math.round((idx * n_src) / n_out),
108
+ Math.round(((idx + 1) * n_src) / n_out),
103
109
  ]);
104
110
  const x_ranges = partition(new_nx, nx);
105
111
  const y_ranges = partition(new_ny, ny);
@@ -149,9 +155,7 @@ export function auto_isosurface_settings(data_range) {
149
155
  return {
150
156
  ...DEFAULT_ISOSURFACE_SETTINGS,
151
157
  // Fall back to default isovalue for all-zero grids to keep controls usable
152
- isovalue: data_range.abs_max > 0
153
- ? data_range.abs_max * 0.2
154
- : DEFAULT_ISOSURFACE_SETTINGS.isovalue,
158
+ isovalue: data_range.abs_max > 0 ? data_range.abs_max * 0.2 : DEFAULT_ISOSURFACE_SETTINGS.isovalue,
155
159
  show_negative: has_negatives,
156
160
  };
157
161
  }
@@ -176,3 +180,48 @@ export function generate_layers(data_range, n_layers) {
176
180
  };
177
181
  });
178
182
  }
183
+ // Tile (repeat) volumetric data to fill a supercell.
184
+ // Pre-downsamples the source grid when the tiled result would exceed MAX_GRID_POINTS
185
+ // to avoid large temporary allocations. Returns the original volume unchanged for
186
+ // [1,1,1] scaling.
187
+ export function tile_volumetric_data(volume, scaling) {
188
+ const [sx, sy, sz] = scaling;
189
+ if (sx === 1 && sy === 1 && sz === 1)
190
+ return volume;
191
+ const total_cells = sx * sy * sz;
192
+ let src_grid = volume.grid;
193
+ let [nx, ny, nz] = volume.grid_dims;
194
+ // Pre-downsample source grid so the tiled result stays within budget.
195
+ // Clamp budget to 8 (minimum downsample output = 2^3) to prevent infinite
196
+ // loops in downsample_grid when total_cells is very large.
197
+ if (nx * ny * nz * total_cells > MAX_GRID_POINTS) {
198
+ const budget = Math.max(8, Math.floor(MAX_GRID_POINTS / total_cells));
199
+ const ds = downsample_grid(src_grid, [nx, ny, nz], budget);
200
+ src_grid = ds.grid;
201
+ [nx, ny, nz] = ds.dims;
202
+ }
203
+ const new_nx = nx * sx;
204
+ const new_ny = ny * sy;
205
+ const new_nz = nz * sz;
206
+ const new_grid = new Array(new_nx);
207
+ for (let ix = 0; ix < new_nx; ix++) {
208
+ const plane = new Array(new_ny);
209
+ const src_x = ix % nx;
210
+ for (let iy = 0; iy < new_ny; iy++) {
211
+ const row = new Array(new_nz);
212
+ const src_y = iy % ny;
213
+ const src_row = src_grid[src_x][src_y];
214
+ for (let iz = 0; iz < new_nz; iz++) {
215
+ row[iz] = src_row[iz % nz];
216
+ }
217
+ plane[iy] = row;
218
+ }
219
+ new_grid[ix] = plane;
220
+ }
221
+ return {
222
+ ...volume,
223
+ grid: new_grid,
224
+ grid_dims: [new_nx, new_ny, new_nz],
225
+ lattice: scale_lattice_matrix(volume.lattice, scaling),
226
+ };
227
+ }
package/dist/labels.js CHANGED
@@ -10,9 +10,13 @@ function name_for_symbol(sym) {
10
10
  }
11
11
  return null;
12
12
  }
13
- export const symbol_names = ([...new Set([...d3_symbols.symbolsFill, ...d3_symbols.symbolsStroke])]
14
- .map(name_for_symbol).filter((n) => n !== null));
15
- export const symbol_map = Object.fromEntries(// Symbol lookup from d3-shape
13
+ export const symbol_names = [
14
+ ...new Set([...d3_symbols.symbolsFill, ...d3_symbols.symbolsStroke]),
15
+ ]
16
+ .map(name_for_symbol)
17
+ .filter((n) => n !== null);
18
+ export const symbol_map = Object.fromEntries(
19
+ // Symbol lookup from d3-shape
16
20
  symbol_names.map((name) => [name, d3_symbols[`symbol${name}`]]));
17
21
  // Format a value for display with optional time formatting
18
22
  export function format_value(value, formatter) {
@@ -74,8 +78,7 @@ export const ELEM_HEATMAP_KEYS = [
74
78
  `melting_point`,
75
79
  `first_ionization`,
76
80
  ];
77
- export const ELEM_HEATMAP_LABELS = Object
78
- .fromEntries(ELEM_HEATMAP_KEYS.map((key) => {
81
+ export const ELEM_HEATMAP_LABELS = Object.fromEntries(ELEM_HEATMAP_KEYS.map((key) => {
79
82
  const [label, unit] = ELEM_PROPERTY_LABELS[key] ?? [];
80
83
  if (!label)
81
84
  throw `Unexpected missing label ${label}`;
@@ -142,7 +145,7 @@ export function format_fractional(value) {
142
145
  return glyph;
143
146
  }
144
147
  for (const [target, glyph] of FRACTION_GLYPHS) {
145
- if (target !== 0 && Math.abs((1 - x) - target) < eps)
148
+ if (target !== 0 && Math.abs(1 - x - target) < eps)
146
149
  return glyph;
147
150
  }
148
151
  return format_num(value, `.4~`);
@@ -154,7 +157,7 @@ export function parse_si_float(value) {
154
157
  // Remove whitespace and commas
155
158
  const cleaned = value.trim().replace(/(\d),(\d)/g, `$1$2`);
156
159
  // Check if the value is a SI-formatted number (e.g. "1.23k", "4.56M", "789µ", "12n")
157
- const match = cleaned.match(/^([-+]?\d*\.?\d+)\s*([yzafpnµmkMGTPEZY])?$/i);
160
+ const match = /^([-+]?\d*\.?\d+)\s*([yzafpnµmkMGTPEZY])?$/i.exec(cleaned);
158
161
  if (match) {
159
162
  const [, num_part, suffix] = match;
160
163
  let multiplier = 1;
@@ -197,7 +200,7 @@ export const ELEMENT_CATEGORIES = [
197
200
  `post-transition metal`,
198
201
  `transition metal`,
199
202
  ];
200
- // deno-fmt-ignore-next-line
203
+ // oxfmt-ignore
201
204
  export const ELEM_SYMBOLS = [`H`, `He`, `Li`, `Be`, `B`, `C`, `N`, `O`, `F`, `Ne`, `Na`, `Mg`, `Al`, `Si`, `P`, `S`, `Cl`, `Ar`, `K`, `Ca`, `Sc`, `Ti`, `V`, `Cr`, `Mn`, `Fe`, `Co`, `Ni`, `Cu`, `Zn`, `Ga`, `Ge`, `As`, `Se`, `Br`, `Kr`, `Rb`, `Sr`, `Y`, `Zr`, `Nb`, `Mo`, `Tc`, `Ru`, `Rh`, `Pd`, `Ag`, `Cd`, `In`, `Sn`, `Sb`, `Te`, `I`, `Xe`, `Cs`, `Ba`, `La`, `Ce`, `Pr`, `Nd`, `Pm`, `Sm`, `Eu`, `Gd`, `Tb`, `Dy`, `Ho`, `Er`, `Tm`, `Yb`, `Lu`, `Hf`, `Ta`, `W`, `Re`, `Os`, `Ir`, `Pt`, `Au`, `Hg`, `Tl`, `Pb`, `Bi`, `Po`, `At`, `Rn`, `Fr`, `Ra`, `Ac`, `Th`, `Pa`, `U`, `Np`, `Pu`, `Am`, `Cm`, `Bk`, `Cf`, `Es`, `Fm`, `Md`, `No`, `Lr`, `Rf`, `Db`, `Sg`, `Bh`, `Hs`, `Mt`, `Ds`, `Rg`, `Cn`, `Nh`, `Fl`, `Mc`, `Lv`, `Ts`, `Og`];
202
205
  export const SUPERSCRIPT_MAP = {
203
206
  '0': `⁰`,
@@ -1,5 +1,14 @@
1
- <script lang="ts">import Icon from '../Icon.svelte';
2
- let { fullscreen = $bindable(false), class: className, ...rest } = $props();
1
+ <script lang="ts">
2
+ import Icon from '../Icon.svelte'
3
+ import type { HTMLButtonAttributes } from 'svelte/elements'
4
+
5
+ let {
6
+ fullscreen = $bindable(false),
7
+ class: className,
8
+ ...rest
9
+ }: HTMLButtonAttributes & {
10
+ fullscreen?: boolean
11
+ } = $props()
3
12
  </script>
4
13
 
5
14
  <button
@@ -1,13 +1,45 @@
1
- <script lang="ts">import { format_num } from '../labels';
2
- let { data = [], title = ``, fallback = ``, fmt = `.2f`, as = `section`, title_snippet, fallback_snippet, ...rest } = $props();
3
- let default_fmt = $derived(fmt); // rename fmt to default_fmt for internal use
1
+ <script lang="ts">
2
+ import { format_num } from '../labels'
3
+ import { sanitize_html } from '../sanitize'
4
+ import type { Snippet } from 'svelte'
5
+ import type { HTMLAttributes } from 'svelte/elements'
6
+
7
+ interface Props<T extends keyof HTMLElementTagNameMap = `section`>
8
+ extends HTMLAttributes<HTMLElementTagNameMap[T]> {
9
+ data?: {
10
+ title: string
11
+ value?: string | number | number[] | null
12
+ unit?: string
13
+ fmt?: string
14
+ condition?: boolean | number | null
15
+ tooltip?: string
16
+ }[]
17
+ title?: string
18
+ fallback?: string
19
+ fmt?: string
20
+ as?: T
21
+ title_snippet?: Snippet
22
+ fallback_snippet?: Snippet
23
+ }
24
+ let {
25
+ data = [],
26
+ title = ``,
27
+ fallback = ``,
28
+ fmt = `.2f`,
29
+ as = `section`,
30
+ title_snippet,
31
+ fallback_snippet,
32
+ ...rest
33
+ }: Props = $props()
34
+
35
+ let default_fmt = $derived(fmt) // rename fmt to default_fmt for internal use
4
36
  </script>
5
37
 
6
38
  <svelte:element this={as} {...rest} class="info-card {rest.class ?? ``}">
7
39
  {#if title || title_snippet}
8
40
  <h2>
9
41
  {#if title_snippet}{@render title_snippet()}{:else}
10
- {@html title}
42
+ {@html sanitize_html(title)}
11
43
  {/if}
12
44
  </h2>
13
45
  {/if}
@@ -20,10 +52,10 @@ let default_fmt = $derived(fmt); // rename fmt to default_fmt for internal use
20
52
  }
21
53
  <div>
22
54
  <span class="title" {title}>
23
- {@html title}
55
+ {@html sanitize_html(title)}
24
56
  </span>
25
57
  <strong title={tooltip ?? null}>
26
- {@html typeof value == `number` ? format_num(value, fmt) : value}
58
+ {@html sanitize_html(typeof value == `number` ? format_num(value, fmt) : value)}
27
59
  {#if unit}
28
60
  <small>{unit}</small>
29
61
  {/if}
@@ -1,33 +1,64 @@
1
- <script lang="ts">import Icon from '../Icon.svelte';
2
- import { tooltip } from 'svelte-multiselect';
3
- let { label, value, copy_value, title, variant = `default`, size = `md`, removable = false, disabled = false, onclick, onremove, children, ...rest } = $props();
4
- let just_copied = $state(false);
5
- async function copy_to_clipboard() {
6
- const to_copy = copy_value ?? value;
7
- if (to_copy === undefined)
8
- return;
9
- await navigator.clipboard.writeText(String(to_copy));
10
- just_copied = true;
11
- setTimeout(() => (just_copied = false), 1000);
12
- }
13
- function handle_click(event) {
14
- if (disabled)
15
- return;
16
- if (onclick)
17
- onclick(event);
18
- else
19
- void copy_to_clipboard();
20
- }
21
- function handle_keydown(event) {
22
- if (disabled || (event.key !== `Enter` && event.key !== ` `))
23
- return;
24
- event.preventDefault();
25
- event.currentTarget?.click();
26
- }
27
- function handle_remove(event) {
28
- event.stopPropagation();
29
- onremove?.();
30
- }
1
+ <script lang="ts">
2
+ import Icon from '../Icon.svelte'
3
+ import { sanitize_html } from '../sanitize'
4
+ import type { Snippet } from 'svelte'
5
+ import { tooltip } from 'svelte-multiselect'
6
+ import type { HTMLAttributes } from 'svelte/elements'
7
+ import type { InfoTagSize, InfoTagVariant } from './index'
8
+
9
+ let {
10
+ label,
11
+ value,
12
+ copy_value,
13
+ title,
14
+ variant = `default`,
15
+ size = `md`,
16
+ removable = false,
17
+ disabled = false,
18
+ onclick,
19
+ onremove,
20
+ children,
21
+ ...rest
22
+ }: {
23
+ label: string // Label text (supports HTML)
24
+ value: string | number | undefined // Value to display (supports HTML)
25
+ copy_value?: string | number // Value to copy to clipboard (defaults to value)
26
+ title?: string // Tooltip text
27
+ variant?: InfoTagVariant // Visual variant for semantic meaning
28
+ size?: InfoTagSize // Size variant
29
+ removable?: boolean // Show remove/close button
30
+ disabled?: boolean // Disable interactions
31
+ onclick?: (event: MouseEvent) => void // Custom click handler (overrides copy-to-clipboard default)
32
+ onremove?: () => void // Callback when remove button is clicked
33
+ children?: Snippet<[]> // Additional content to render inside the tag
34
+ } & Omit<HTMLAttributes<HTMLSpanElement>, `onclick` | `onkeydown`> = $props()
35
+
36
+ let just_copied = $state(false)
37
+
38
+ async function copy_to_clipboard(): Promise<void> {
39
+ const to_copy = copy_value ?? value
40
+ if (to_copy === undefined) return
41
+ await navigator.clipboard.writeText(String(to_copy))
42
+ just_copied = true
43
+ setTimeout(() => (just_copied = false), 1000)
44
+ }
45
+
46
+ function handle_click(event: MouseEvent): void {
47
+ if (disabled) return
48
+ if (onclick) onclick(event)
49
+ else void copy_to_clipboard()
50
+ }
51
+
52
+ function handle_keydown(event: KeyboardEvent): void {
53
+ if (disabled || (event.key !== `Enter` && event.key !== ` `)) return
54
+ event.preventDefault()
55
+ ;(event.currentTarget as HTMLElement)?.click()
56
+ }
57
+
58
+ function handle_remove(event: MouseEvent): void {
59
+ event.stopPropagation()
60
+ onremove?.()
61
+ }
31
62
  </script>
32
63
 
33
64
  <span
@@ -42,8 +73,8 @@ function handle_remove(event) {
42
73
  aria-disabled={disabled}
43
74
  {...rest}
44
75
  >
45
- {@html label}
46
- <em>{@html value}</em>
76
+ {@html sanitize_html(label)}
77
+ <em>{@html sanitize_html(value)}</em>
47
78
  {#if just_copied}
48
79
  <Icon
49
80
  icon="Check"
@@ -1,50 +1,95 @@
1
- <script lang="ts">import Icon from '../Icon.svelte';
2
- import { Histogram } from '../plot';
3
- let { label, min_value = $bindable(), max_value = $bindable(), placeholders = {}, title, histogram_data, histogram_height = 30, histogram_color = `rgba(0, 0, 0, 0.2)`, histogram_position = `top`, log = false, disabled = false, unit, show_clear_button = true, onchange, onclear, ...rest } = $props();
4
- let show_histogram = $derived(histogram_position !== `none` && histogram_data?.length);
5
- // Active when either bound is set (undefined = unbounded)
6
- let active = $derived(min_value !== undefined || max_value !== undefined);
7
- let plain_label = $derived(label.replace(/<[^>]*>/g, ``));
8
- let filtered_data = $derived.by(() => {
9
- if (!histogram_data)
10
- return [];
1
+ <script lang="ts">
2
+ import Icon from '../Icon.svelte'
3
+ import { sanitize_html } from '../sanitize'
4
+ import type { AxisConfig, DataSeries } from '../plot'
5
+ import { Histogram } from '../plot'
6
+ import type { HTMLAttributes } from 'svelte/elements'
7
+
8
+ let {
9
+ label,
10
+ min_value = $bindable(),
11
+ max_value = $bindable(),
12
+ placeholders = {},
13
+ title,
14
+ histogram_data,
15
+ histogram_height = 30,
16
+ histogram_color = `rgba(0, 0, 0, 0.2)`,
17
+ histogram_position = `top`,
18
+ log = false,
19
+ disabled = false,
20
+ unit,
21
+ show_clear_button = true,
22
+ onchange,
23
+ onclear,
24
+ ...rest
25
+ }: {
26
+ label: string // Label text (supports HTML)
27
+ min_value?: number // Minimum value for filtering (undefined = unbounded)
28
+ max_value?: number // Maximum value for filtering (undefined = unbounded)
29
+ placeholders?: { min?: string; max?: string } // Placeholder text for min/max inputs
30
+ title?: string // Tooltip title for the label
31
+ histogram_data?: number[] // Data array for histogram visualization
32
+ histogram_height?: number // Height of histogram in pixels
33
+ histogram_color?: string // Color for histogram bars (unfilled data)
34
+ histogram_position?: `top` | `bottom` | `none` // Position of histogram relative to inputs, or 'none' to hide
35
+ log?: boolean // Use logarithmic scale for histogram y-axis
36
+ disabled?: boolean // Disable all inputs
37
+ unit?: string // Unit label to display after inputs
38
+ show_clear_button?: boolean // Show clear button when filters are active
39
+ onchange?: (min: number | undefined, max: number | undefined) => void // Callback when filter values change
40
+ onclear?: () => void // Callback when clear button is clicked (fires before onchange)
41
+ } & HTMLAttributes<HTMLDivElement> = $props()
42
+
43
+ let show_histogram = $derived(
44
+ histogram_position !== `none` && histogram_data?.length,
45
+ )
46
+
47
+ // Active when either bound is set (undefined = unbounded)
48
+ let active = $derived(min_value !== undefined || max_value !== undefined)
49
+ let plain_label = $derived(label.replace(/<[^>]*>/g, ``))
50
+
51
+ let filtered_data = $derived.by(() => {
52
+ if (!histogram_data) return []
11
53
  // undefined means unbounded (-Infinity for min, +Infinity for max)
12
- const min = min_value ?? -Infinity;
13
- const max = max_value ?? Infinity;
14
- return histogram_data.filter((val) => val >= min && val <= max);
15
- });
16
- function onkeydown(event) {
54
+ const min = min_value ?? -Infinity
55
+ const max = max_value ?? Infinity
56
+ return histogram_data.filter((val) => val >= min && val <= max)
57
+ })
58
+
59
+ function onkeydown(event: KeyboardEvent): void {
17
60
  if (event.key === `Enter`) {
18
- event.preventDefault();
19
- event.target.blur();
20
- }
21
- else if (event.key === `Escape` && active) {
22
- event.preventDefault();
23
- clear_filter();
61
+ event.preventDefault()
62
+ ;(event.target as HTMLInputElement).blur()
63
+ } else if (event.key === `Escape` && active) {
64
+ event.preventDefault()
65
+ clear_filter()
24
66
  }
25
- }
26
- function clear_filter() {
27
- min_value = max_value = undefined;
28
- onclear?.();
29
- onchange?.(undefined, undefined);
30
- }
31
- const axis_config = {
67
+ }
68
+
69
+ function clear_filter(): void {
70
+ min_value = max_value = undefined
71
+ onclear?.()
72
+ onchange?.(undefined, undefined)
73
+ }
74
+
75
+ const axis_config: AxisConfig = {
32
76
  ticks: 0,
33
77
  label: ``,
34
78
  grid_style: { style: `opacity: 0` },
35
79
  tick: { label: { inside: true } },
36
80
  color: `color-mix(in srgb, currentColor 60%, transparent)`,
37
- };
38
- // x: [] satisfies DataSeries type requirement; Histogram bins on y values only
39
- const series = $derived([
81
+ }
82
+
83
+ // x: [] satisfies DataSeries type requirement; Histogram bins on y values only
84
+ const series: DataSeries[] = $derived([
40
85
  { y: histogram_data ?? [], color: histogram_color, label: `All`, x: [] },
41
86
  {
42
- y: filtered_data,
43
- color: `var(--accent-color, #228be6)`,
44
- label: `Filtered`,
45
- x: [],
87
+ y: filtered_data,
88
+ color: `var(--accent-color, #228be6)`,
89
+ label: `Filtered`,
90
+ x: [],
46
91
  },
47
- ]);
92
+ ])
48
93
  </script>
49
94
 
50
95
  {#snippet histogram_snippet()}
@@ -76,7 +121,7 @@ const series = $derived([
76
121
  {@render histogram_snippet()}
77
122
  {/if}
78
123
  <div class="filter-row">
79
- <span {title} class="filter-label">{@html label}</span>
124
+ <span {title} class="filter-label">{@html sanitize_html(label)}</span>
80
125
  <div class="filter-inputs">
81
126
  <input
82
127
  bind:value={min_value}