matterviz 0.4.2 → 0.4.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 (244) hide show
  1. package/dist/FilePicker.svelte +40 -50
  2. package/dist/api/optimade.js +10 -7
  3. package/dist/app.css +15 -0
  4. package/dist/brillouin/BrillouinZone.svelte +18 -9
  5. package/dist/brillouin/BrillouinZoneScene.svelte +20 -29
  6. package/dist/brillouin/compute.js +10 -16
  7. package/dist/brillouin/geometry.js +2 -2
  8. package/dist/chempot-diagram/ChemPotDiagram.svelte +18 -15
  9. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +17 -113
  10. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +104 -264
  11. package/dist/chempot-diagram/async-compute.svelte.js +12 -13
  12. package/dist/chempot-diagram/color.d.ts +21 -1
  13. package/dist/chempot-diagram/color.js +70 -1
  14. package/dist/chempot-diagram/compute.d.ts +7 -0
  15. package/dist/chempot-diagram/compute.js +85 -26
  16. package/dist/chempot-diagram/export.js +1 -6
  17. package/dist/chempot-diagram/temperature.js +9 -10
  18. package/dist/composition/BarChart.svelte +7 -9
  19. package/dist/composition/Composition.svelte +3 -8
  20. package/dist/composition/Formula.svelte +2 -2
  21. package/dist/composition/FormulaFilter.svelte +27 -93
  22. package/dist/composition/format.js +6 -8
  23. package/dist/composition/parse.js +50 -105
  24. package/dist/constants.d.ts +0 -1
  25. package/dist/constants.js +6 -17
  26. package/dist/convex-hull/ConvexHull2D.svelte +0 -2
  27. package/dist/convex-hull/ConvexHull3D.svelte +19 -47
  28. package/dist/convex-hull/ConvexHull4D.svelte +65 -95
  29. package/dist/convex-hull/StructurePopup.svelte +1 -1
  30. package/dist/convex-hull/gas-thermodynamics.js +21 -53
  31. package/dist/convex-hull/helpers.js +1 -7
  32. package/dist/convex-hull/thermodynamics.js +19 -27
  33. package/dist/coordination/CoordinationBarPlot.svelte +12 -21
  34. package/dist/element/BohrAtom.svelte +0 -1
  35. package/dist/element/ElementTile.svelte +26 -44
  36. package/dist/element/data.d.ts +2 -0
  37. package/dist/element/data.js +5 -1
  38. package/dist/element/index.d.ts +1 -1
  39. package/dist/element/index.js +1 -1
  40. package/dist/fermi-surface/FermiSurface.svelte +22 -23
  41. package/dist/fermi-surface/FermiSurface.svelte.d.ts +2 -6
  42. package/dist/fermi-surface/FermiSurfaceScene.svelte +32 -77
  43. package/dist/fermi-surface/compute.js +22 -55
  44. package/dist/fermi-surface/parse.js +38 -70
  45. package/dist/file-viewer/JsonBrowser.svelte +1043 -0
  46. package/dist/file-viewer/JsonBrowser.svelte.d.ts +9 -0
  47. package/dist/file-viewer/PlotPanel.svelte +350 -0
  48. package/dist/file-viewer/PlotPanel.svelte.d.ts +9 -0
  49. package/dist/file-viewer/TrajectoryWithDos.svelte +30 -0
  50. package/dist/file-viewer/TrajectoryWithDos.svelte.d.ts +10 -0
  51. package/dist/file-viewer/detect.d.ts +11 -0
  52. package/dist/file-viewer/detect.js +381 -0
  53. package/dist/file-viewer/eligibility.d.ts +4 -0
  54. package/dist/file-viewer/eligibility.js +53 -0
  55. package/dist/file-viewer/host-protocol.d.ts +50 -0
  56. package/dist/file-viewer/host-transfer.d.ts +25 -0
  57. package/dist/file-viewer/host-transfer.js +42 -0
  58. package/dist/file-viewer/index.d.ts +5 -0
  59. package/dist/file-viewer/index.js +5 -0
  60. package/dist/file-viewer/main.d.ts +44 -0
  61. package/dist/file-viewer/main.js +535 -0
  62. package/dist/file-viewer/parse.d.ts +9 -0
  63. package/dist/file-viewer/parse.js +170 -0
  64. package/dist/file-viewer/plot-utils.d.ts +24 -0
  65. package/dist/file-viewer/plot-utils.js +191 -0
  66. package/dist/file-viewer/types.d.ts +4 -0
  67. package/dist/file-viewer/types.js +5 -0
  68. package/dist/heatmap-matrix/HeatmapMatrix.svelte +57 -84
  69. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
  70. package/dist/index.d.ts +2 -2
  71. package/dist/index.js +1 -1
  72. package/dist/io/ExportPane.svelte +1 -1
  73. package/dist/io/decompress.d.ts +2 -0
  74. package/dist/io/decompress.js +3 -2
  75. package/dist/io/export.d.ts +7 -4
  76. package/dist/io/export.js +70 -18
  77. package/dist/io/file-drop.js +60 -35
  78. package/dist/io/url-drop.d.ts +2 -0
  79. package/dist/io/url-drop.js +28 -16
  80. package/dist/isosurface/Isosurface.svelte +687 -196
  81. package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
  82. package/dist/isosurface/IsosurfaceControls.svelte +564 -85
  83. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +1 -1
  84. package/dist/isosurface/VolumeSlice.svelte +224 -0
  85. package/dist/isosurface/VolumeSlice.svelte.d.ts +28 -0
  86. package/dist/isosurface/coloring.d.ts +20 -0
  87. package/dist/isosurface/coloring.js +118 -0
  88. package/dist/isosurface/geometry-worker-types.d.ts +38 -0
  89. package/dist/isosurface/geometry.worker.d.ts +1 -0
  90. package/dist/isosurface/geometry.worker.js +59 -0
  91. package/dist/isosurface/grid.d.ts +10 -0
  92. package/dist/isosurface/grid.js +31 -0
  93. package/dist/isosurface/index.d.ts +4 -0
  94. package/dist/isosurface/index.js +4 -0
  95. package/dist/isosurface/profile.d.ts +12 -0
  96. package/dist/isosurface/profile.js +19 -0
  97. package/dist/isosurface/sampling.d.ts +34 -0
  98. package/dist/isosurface/sampling.js +408 -0
  99. package/dist/isosurface/slice-rendering.d.ts +13 -0
  100. package/dist/isosurface/slice-rendering.js +79 -0
  101. package/dist/isosurface/slice.d.ts +22 -2
  102. package/dist/isosurface/slice.js +200 -116
  103. package/dist/isosurface/types.d.ts +24 -1
  104. package/dist/isosurface/types.js +120 -8
  105. package/dist/json-path.d.ts +4 -0
  106. package/dist/json-path.js +89 -0
  107. package/dist/labels.js +1 -1
  108. package/dist/layout/InfoCard.svelte +1 -3
  109. package/dist/layout/SettingsSection.svelte +52 -61
  110. package/dist/layout/SettingsSection.svelte.d.ts +6 -7
  111. package/dist/layout/ViewerChrome.svelte +7 -1
  112. package/dist/layout/json-tree/JsonNode.svelte +14 -48
  113. package/dist/layout/json-tree/JsonTree.svelte +48 -95
  114. package/dist/layout/json-tree/JsonValue.svelte +3 -18
  115. package/dist/layout/json-tree/utils.d.ts +0 -3
  116. package/dist/layout/json-tree/utils.js +50 -179
  117. package/dist/marching-cubes.d.ts +8 -1
  118. package/dist/marching-cubes.js +115 -48
  119. package/dist/math.d.ts +3 -2
  120. package/dist/math.js +29 -38
  121. package/dist/overlays/ContextMenu.svelte +15 -32
  122. package/dist/overlays/DraggablePane.svelte +39 -58
  123. package/dist/overlays/DraggablePane.svelte.d.ts +1 -1
  124. package/dist/overlays/GlassChip.svelte +3 -1
  125. package/dist/periodic-table/PeriodicTableControls.svelte +26 -52
  126. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +18 -34
  127. package/dist/phase-diagram/build-diagram.js +1 -6
  128. package/dist/phase-diagram/svg-to-diagram.js +9 -14
  129. package/dist/plot/bar/BarPlot.svelte +38 -42
  130. package/dist/plot/bar/SpacegroupBarPlot.svelte +18 -42
  131. package/dist/plot/box/BoxPlot.svelte +27 -23
  132. package/dist/plot/core/auto-place.d.ts +1 -1
  133. package/dist/plot/core/auto-place.js +1 -1
  134. package/dist/plot/core/data-transform.d.ts +1 -2
  135. package/dist/plot/core/data-transform.js +0 -10
  136. package/dist/plot/core/layout.d.ts +12 -5
  137. package/dist/plot/core/layout.js +37 -27
  138. package/dist/plot/core/pan-zoom.svelte.d.ts +2 -4
  139. package/dist/plot/core/pan-zoom.svelte.js +34 -17
  140. package/dist/plot/core/placed-tween.svelte.d.ts +3 -0
  141. package/dist/plot/core/placed-tween.svelte.js +68 -21
  142. package/dist/plot/core/svg.js +0 -1
  143. package/dist/plot/core/utils/hierarchy-chart.d.ts +1 -0
  144. package/dist/plot/core/utils/hierarchy-chart.js +12 -0
  145. package/dist/plot/core/utils/hierarchy-labels.js +3 -2
  146. package/dist/plot/histogram/Histogram.svelte +21 -26
  147. package/dist/plot/sankey/sankey.js +13 -9
  148. package/dist/plot/scatter/BinnedScatterPlot.svelte +59 -43
  149. package/dist/plot/scatter/ScatterPlot.svelte +67 -52
  150. package/dist/plot/scatter/adaptive-density.d.ts +1 -1
  151. package/dist/plot/scatter/adaptive-density.js +36 -20
  152. package/dist/plot/sunburst/Sunburst.svelte +20 -20
  153. package/dist/plot/treemap/Treemap.svelte +175 -60
  154. package/dist/plot/treemap/Treemap.svelte.d.ts +6 -0
  155. package/dist/plot/treemap/index.d.ts +1 -0
  156. package/dist/plot/treemap/labels.d.ts +38 -0
  157. package/dist/plot/treemap/labels.js +80 -0
  158. package/dist/plot/treemap/treemap.d.ts +4 -2
  159. package/dist/plot/treemap/treemap.js +5 -2
  160. package/dist/rdf/RdfPlot.svelte +17 -35
  161. package/dist/rdf/calc-rdf.js +59 -44
  162. package/dist/rdf/index.d.ts +0 -1
  163. package/dist/sanitize.js +84 -26
  164. package/dist/scene/SceneCamera.svelte +1 -1
  165. package/dist/scene/SceneCamera.svelte.d.ts +1 -1
  166. package/dist/scene/index.d.ts +1 -2
  167. package/dist/scene/index.js +1 -1
  168. package/dist/scene/{props.js → props.svelte.js} +15 -1
  169. package/dist/settings.js +159 -482
  170. package/dist/spectral/Bands.svelte +151 -158
  171. package/dist/spectral/BrillouinBandsDos.svelte +4 -19
  172. package/dist/spectral/Dos.svelte +26 -24
  173. package/dist/spectral/helpers.js +40 -105
  174. package/dist/structure/AtomLegend.svelte +8 -13
  175. package/dist/structure/AtomLegend.svelte.d.ts +2 -3
  176. package/dist/structure/CellSelect.svelte +35 -10
  177. package/dist/structure/Structure.svelte +177 -99
  178. package/dist/structure/Structure.svelte.d.ts +1 -1
  179. package/dist/structure/StructureCarousel.svelte +10 -3
  180. package/dist/structure/StructureControls.svelte +8 -26
  181. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  182. package/dist/structure/StructureInfoPane.svelte +65 -76
  183. package/dist/structure/StructureScene.svelte +75 -109
  184. package/dist/structure/StructureScene.svelte.d.ts +3 -1
  185. package/dist/structure/StructureViewport.svelte +120 -55
  186. package/dist/structure/StructureViewport.svelte.d.ts +3 -1
  187. package/dist/structure/atom-properties.d.ts +1 -1
  188. package/dist/structure/atom-properties.js +4 -3
  189. package/dist/structure/bond-order-perception.js +3 -8
  190. package/dist/structure/bonding.d.ts +0 -1
  191. package/dist/structure/bonding.js +16 -23
  192. package/dist/structure/export.js +50 -74
  193. package/dist/structure/format-detect.js +5 -7
  194. package/dist/structure/index.js +6 -12
  195. package/dist/structure/parse.js +38 -100
  196. package/dist/structure/partial-occupancy.js +7 -4
  197. package/dist/structure/pbc.js +8 -10
  198. package/dist/structure/polyhedra.js +5 -4
  199. package/dist/structure/prop-groups.d.ts +85 -0
  200. package/dist/structure/prop-groups.js +18 -0
  201. package/dist/symmetry/SymmetryStats.svelte +0 -4
  202. package/dist/symmetry/WyckoffTable.svelte +5 -4
  203. package/dist/symmetry/cell-transform.js +5 -14
  204. package/dist/symmetry/index.js +6 -9
  205. package/dist/table/HeatmapTable.svelte +61 -103
  206. package/dist/table/ToggleMenu.svelte +166 -44
  207. package/dist/table/ToggleMenu.svelte.d.ts +1 -1
  208. package/dist/theme/embedded.d.ts +18 -0
  209. package/dist/theme/embedded.js +206 -0
  210. package/dist/theme/index.d.ts +4 -0
  211. package/dist/theme/index.js +1 -2
  212. package/dist/theme/themes.mjs +31 -60
  213. package/dist/tooltip/TooltipContent.svelte +1 -1
  214. package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
  215. package/dist/tooltip/index.d.ts +8 -1
  216. package/dist/trajectory/Trajectory.svelte +140 -120
  217. package/dist/trajectory/TrajectoryError.svelte +1 -66
  218. package/dist/trajectory/TrajectoryExportPane.svelte +0 -1
  219. package/dist/trajectory/TrajectoryInfoPane.svelte +55 -80
  220. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
  221. package/dist/trajectory/extract.js +19 -29
  222. package/dist/trajectory/format-detect.d.ts +2 -0
  223. package/dist/trajectory/format-detect.js +7 -5
  224. package/dist/trajectory/frame-reader.js +3 -4
  225. package/dist/trajectory/helpers.js +16 -49
  226. package/dist/trajectory/index.js +2 -8
  227. package/dist/trajectory/parse/ase.js +1 -1
  228. package/dist/trajectory/parse/index.d.ts +4 -3
  229. package/dist/trajectory/parse/index.js +12 -17
  230. package/dist/trajectory/parse/vaspout-h5.d.ts +0 -1
  231. package/dist/trajectory/parse/vaspout-h5.js +1 -4
  232. package/dist/trajectory/parse/xyz.js +48 -2
  233. package/dist/trajectory/plotting.js +14 -37
  234. package/dist/xrd/XrdPlot.svelte +43 -68
  235. package/dist/xrd/calc-xrd.js +22 -58
  236. package/dist/xrd/parse.js +13 -22
  237. package/package.json +40 -8
  238. package/readme.md +6 -5
  239. package/dist/scene/types.d.ts +0 -26
  240. package/dist/tooltip/types.d.ts +0 -8
  241. package/dist/trajectory/constants.d.ts +0 -6
  242. package/dist/trajectory/constants.js +0 -11
  243. /package/dist/{scene/types.js → file-viewer/host-protocol.js} +0 -0
  244. /package/dist/{tooltip/types.js → isosurface/geometry-worker-types.js} +0 -0
@@ -1,6 +1,6 @@
1
1
  export type * from './types';
2
2
  export { default as BohrAtom } from './BohrAtom.svelte';
3
- export { default as element_data } from './data';
3
+ export { default as element_data, element_by_symbol } from './data';
4
4
  export { default as ElementHeading } from './ElementHeading.svelte';
5
5
  export { default as ElementPhoto } from './ElementPhoto.svelte';
6
6
  export { default as ElementStats } from './ElementStats.svelte';
@@ -1,5 +1,5 @@
1
1
  export { default as BohrAtom } from './BohrAtom.svelte';
2
- export { default as element_data } from './data';
2
+ export { default as element_data, element_by_symbol } from './data';
3
3
  export { default as ElementHeading } from './ElementHeading.svelte';
4
4
  export { default as ElementPhoto } from './ElementPhoto.svelte';
5
5
  export { default as ElementStats } from './ElementStats.svelte';
@@ -5,7 +5,7 @@
5
5
  import EmptyState from '../EmptyState.svelte'
6
6
  import { StatusMessage } from '../feedback'
7
7
  import Spinner from '../feedback/Spinner.svelte'
8
- import { create_file_drop_handler, drag_over_handlers, load_from_url } from '../io'
8
+ import * as io from '../io'
9
9
  import { type FullscreenToggleProp, toggle_fullscreen, ViewerChrome } from '../layout'
10
10
  import { sync_fullscreen } from '../layout/fullscreen.svelte'
11
11
  import { PlotTooltip } from '../plot'
@@ -34,13 +34,9 @@
34
34
  RepresentationMode,
35
35
  } from './types'
36
36
 
37
- type FermiHandlerData = {
37
+ type FermiFullscreenData = {
38
38
  fermi_data?: FermiSurfaceData
39
- band_data?: BandGridData
40
39
  bz_data?: BrillouinZoneData
41
- filename?: string
42
- file_size?: number
43
- error_msg?: string
44
40
  fullscreen?: boolean
45
41
  }
46
42
 
@@ -140,7 +136,7 @@
140
136
  on_file_drop?: (filename: string) => void
141
137
  on_file_load?: (data: FermiFileLoadData) => void
142
138
  on_error?: (data: FermiErrorData) => void
143
- on_fullscreen_change?: (data: FermiHandlerData) => void
139
+ on_fullscreen_change?: (data: FermiFullscreenData) => void
144
140
  on_mu_change?: (mu: number) => void
145
141
  tooltip_config?: Snippet<[{ hover_data: FermiHoverData }]> | FermiTooltipConfig
146
142
  on_point_hover?: (data: FermiHoverData | null) => void
@@ -303,23 +299,26 @@
303
299
  // Load from URL (with race condition protection for rapid URL changes)
304
300
  let load_id = 0
305
301
  $effect(() => {
306
- if (data_url && !fermi_data && !band_data) {
307
- const current_load_id = ++load_id
308
- loading = true
309
- error_msg = undefined
310
- load_from_url(data_url, safe_parse)
311
- .catch((err) => {
312
- if (current_load_id !== load_id) return // stale request
313
- error_msg = to_error(err).message
314
- on_error?.({ error_msg, filename: data_url })
315
- })
316
- .finally(() => {
317
- if (current_load_id === load_id) loading = false
318
- })
319
- }
302
+ const requested_url = data_url
303
+ if (!requested_url || fermi_data || band_data) return
304
+ const current_load_id = ++load_id
305
+ loading = true
306
+ error_msg = undefined
307
+ io.load_from_url(requested_url, (content, filename) => {
308
+ if (current_load_id !== load_id) return
309
+ return safe_parse(content, filename)
310
+ })
311
+ .catch((err) => {
312
+ if (current_load_id !== load_id) return
313
+ error_msg = to_error(err).message
314
+ on_error?.({ error_msg, filename: io.basename_from_url(requested_url) })
315
+ })
316
+ .finally(() => {
317
+ if (current_load_id === load_id) loading = false
318
+ })
320
319
  })
321
320
 
322
- const handle_file_drop = create_file_drop_handler({
321
+ const handle_file_drop = io.create_file_drop_handler({
323
322
  allow: () => allow_file_drop,
324
323
  on_drop: async (content, filename) => {
325
324
  on_file_drop?.(filename)
@@ -368,7 +367,7 @@
368
367
  onmouseenter={() => (hovered = true)}
369
368
  onmouseleave={() => (hovered = false)}
370
369
  ondrop={handle_file_drop}
371
- {...drag_over_handlers({
370
+ {...io.drag_over_handlers({
372
371
  allow: () => allow_file_drop,
373
372
  set_dragover: (over) => (dragover = over),
374
373
  })}
@@ -7,13 +7,9 @@ import type { Crystal } from '../structure';
7
7
  import type { ComponentProps, Snippet } from 'svelte';
8
8
  import type { HTMLAttributes } from 'svelte/elements';
9
9
  import type { BandGridData, ColorProperty, FermiErrorData, FermiFileLoadData, FermiHoverData, FermiSurfaceData, FermiTooltipConfig, RepresentationMode } from './types';
10
- type FermiHandlerData = {
10
+ type FermiFullscreenData = {
11
11
  fermi_data?: FermiSurfaceData;
12
- band_data?: BandGridData;
13
12
  bz_data?: BrillouinZoneData;
14
- filename?: string;
15
- file_size?: number;
16
- error_msg?: string;
17
13
  fullscreen?: boolean;
18
14
  };
19
15
  type $$ComponentProps = {
@@ -59,7 +55,7 @@ type $$ComponentProps = {
59
55
  on_file_drop?: (filename: string) => void;
60
56
  on_file_load?: (data: FermiFileLoadData) => void;
61
57
  on_error?: (data: FermiErrorData) => void;
62
- on_fullscreen_change?: (data: FermiHandlerData) => void;
58
+ on_fullscreen_change?: (data: FermiFullscreenData) => void;
63
59
  on_mu_change?: (mu: number) => void;
64
60
  tooltip_config?: Snippet<[{
65
61
  hover_data: FermiHoverData;
@@ -172,21 +172,11 @@
172
172
 
173
173
  // Map from surface to its render order based on size (inner surfaces first)
174
174
  let surface_render_orders = $derived.by((): Map<Isosurface, number> => {
175
- const order_map = new SvelteMap<Isosurface, number>()
176
- if (visible_surfaces.length === 0) return order_map
177
-
178
- // Compute radius for each surface and sort by it
179
- const surfaces_with_radius = visible_surfaces.map((surface) => ({
180
- surface,
181
- radius: compute_surface_radius(surface),
182
- }))
183
- surfaces_with_radius.sort((a, b) => a.radius - b.radius)
184
-
185
- // Assign render order: smaller radius (inner) = lower order = rendered first
186
- for (let idx = 0; idx < surfaces_with_radius.length; idx++) {
187
- order_map.set(surfaces_with_radius[idx].surface, idx)
188
- }
189
- return order_map
175
+ const by_radius = visible_surfaces
176
+ .map((surface) => ({ surface, radius: compute_surface_radius(surface) }))
177
+ .toSorted((surf_a, surf_b) => surf_a.radius - surf_b.radius)
178
+ // Smaller radius (inner) = lower order = rendered first
179
+ return new SvelteMap(by_radius.map(({ surface }, idx) => [surface, idx]))
190
180
  })
191
181
 
192
182
  // Compute property range for color scaling
@@ -538,79 +528,44 @@
538
528
 
539
529
  {#if geo_data}
540
530
  {#each symmetry_ops as sym_matrix, sym_idx (`sym-${sym_idx}`)}
541
- {#if representation === `wireframe`}
531
+ {#snippet mesh_pass(order: number, pass: `wireframe` | `front` | `back`)}
542
532
  <T.Mesh
543
533
  geometry={geo_data.geometry}
544
534
  matrix={sym_matrix}
545
535
  matrixAutoUpdate={false}
546
- {renderOrder}
536
+ renderOrder={order}
547
537
  onpointermove={(event: ThreltePointerEvent) =>
548
538
  handle_pointer_move(event, surface, surface_color, sym_idx, sym_matrix)}
549
539
  onpointerleave={clear_hover}
550
540
  >
551
- <T.MeshBasicMaterial
552
- color={surface_color}
553
- wireframe
554
- transparent={surface_opacity < 1}
555
- opacity={surface_opacity}
556
- depthWrite={true}
557
- depthTest={true}
558
- />
541
+ {#if pass === `wireframe`}
542
+ <T.MeshBasicMaterial
543
+ color={surface_color}
544
+ wireframe
545
+ transparent={surface_opacity < 1}
546
+ opacity={surface_opacity}
547
+ depthWrite={true}
548
+ depthTest={true}
549
+ />
550
+ {:else}
551
+ <T.MeshStandardMaterial
552
+ {...get_material_props(surface_color, use_vertex_colors, surface_idx, pass)}
553
+ metalness={0.1}
554
+ roughness={0.6}
555
+ flatShading={false}
556
+ />
557
+ {/if}
559
558
  </T.Mesh>
559
+ {/snippet}
560
+
561
+ {#if representation === `wireframe`}
562
+ {@render mesh_pass(renderOrder, `wireframe`)}
560
563
  {:else if surface_opacity < 1}
561
- <!-- Two-pass rendering for transparent surfaces -->
562
- <!-- Pass 1: Back faces (rendered first, lower renderOrder) -->
563
- <T.Mesh
564
- geometry={geo_data.geometry}
565
- matrix={sym_matrix}
566
- matrixAutoUpdate={false}
567
- renderOrder={renderOrder * 2}
568
- onpointermove={(event: ThreltePointerEvent) =>
569
- handle_pointer_move(event, surface, surface_color, sym_idx, sym_matrix)}
570
- onpointerleave={clear_hover}
571
- >
572
- <T.MeshStandardMaterial
573
- {...get_material_props(surface_color, use_vertex_colors, surface_idx, `back`)}
574
- metalness={0.1}
575
- roughness={0.6}
576
- flatShading={false}
577
- />
578
- </T.Mesh>
579
- <!-- Pass 2: Front faces (rendered second, higher renderOrder) -->
580
- <T.Mesh
581
- geometry={geo_data.geometry}
582
- matrix={sym_matrix}
583
- matrixAutoUpdate={false}
584
- renderOrder={renderOrder * 2 + 1}
585
- onpointermove={(event: ThreltePointerEvent) =>
586
- handle_pointer_move(event, surface, surface_color, sym_idx, sym_matrix)}
587
- onpointerleave={clear_hover}
588
- >
589
- <T.MeshStandardMaterial
590
- {...get_material_props(surface_color, use_vertex_colors, surface_idx, `front`)}
591
- metalness={0.1}
592
- roughness={0.6}
593
- flatShading={false}
594
- />
595
- </T.Mesh>
564
+ <!-- Two-pass transparent rendering: back faces first, front faces on top -->
565
+ {@render mesh_pass(renderOrder * 2, `back`)}
566
+ {@render mesh_pass(renderOrder * 2 + 1, `front`)}
596
567
  {:else}
597
- <!-- Single pass for opaque surfaces -->
598
- <T.Mesh
599
- geometry={geo_data.geometry}
600
- matrix={sym_matrix}
601
- matrixAutoUpdate={false}
602
- {renderOrder}
603
- onpointermove={(event: ThreltePointerEvent) =>
604
- handle_pointer_move(event, surface, surface_color, sym_idx, sym_matrix)}
605
- onpointerleave={clear_hover}
606
- >
607
- <T.MeshStandardMaterial
608
- {...get_material_props(surface_color, use_vertex_colors, surface_idx, `front`)}
609
- metalness={0.1}
610
- roughness={0.6}
611
- flatShading={false}
612
- />
613
- </T.Mesh>
568
+ {@render mesh_pass(renderOrder, `front`)}
614
569
  {/if}
615
570
  {/each}
616
571
  {/if}
@@ -26,18 +26,15 @@ function tricubic_interpolate(grid, fx, fy, fz, px, py, pz) {
26
26
  const iy = Math.floor(fy);
27
27
  const iz = Math.floor(fz);
28
28
  // Precompute Catmull-Rom coefficients
29
- const [cx0, cx1, cx2, cx3] = catmull_rom_coeffs(fx - ix);
30
- const [cy0, cy1, cy2, cy3] = catmull_rom_coeffs(fy - iy);
31
- const [cz0, cz1, cz2, cz3] = catmull_rom_coeffs(fz - iz);
29
+ const cx = catmull_rom_coeffs(fx - ix);
30
+ const cy = catmull_rom_coeffs(fy - iy);
31
+ const cz = catmull_rom_coeffs(fz - iz);
32
32
  // Wrapped stencil indices (modulo hoisted out of the inner loop); a zero period
33
33
  // (single-point axis) has no neighbours, so collapse the stencil onto index 0
34
34
  const stencil = (idx, period) => [idx - 1, idx, idx + 1, idx + 2].map((stencil_idx) => period > 0 ? ((stencil_idx % period) + period) % period : 0);
35
35
  const [wx, wy, wz] = [stencil(ix, px), stencil(iy, py), stencil(iz, pz)];
36
36
  // Interpolate along z, then y, then x (fully inlined)
37
37
  let result = 0;
38
- const cx = [cx0, cx1, cx2, cx3];
39
- const cy = [cy0, cy1, cy2, cy3];
40
- const cz = [cz0, cz1, cz2, cz3];
41
38
  for (let xi = 0; xi < 4; xi++) {
42
39
  const row = grid[wx[xi]];
43
40
  let y_sum = 0;
@@ -286,36 +283,26 @@ function analyze_surface_topology(surface, k_lattice) {
286
283
  spans[axis_idx] = surface_size > SPANNING_THRESHOLD * bz_size;
287
284
  }
288
285
  const n_spanning = spans.filter(Boolean).length;
289
- if (n_spanning === 0) {
290
- // Closed surface, no spanning directions
286
+ // 0 spanning = closed 3D pocket; 3 spanning = complex warped network
287
+ if (n_spanning === 0)
291
288
  return { dimensionality: `3D`, orientation: null };
292
- }
293
- else if (n_spanning === 1) {
294
- // 2D sheet spanning one direction
295
- const axis_idx = spans.indexOf(true);
296
- const orientation = [0, 0, 0];
297
- orientation[axis_idx] = 1;
298
- return { dimensionality: `2D`, orientation };
299
- }
300
- else if (n_spanning === 2) {
301
- // 1D noodle-like (tube spanning 2 directions, bounded in 1)
302
- const axis_idx = spans.indexOf(false);
303
- const orientation = [0, 0, 0];
304
- orientation[axis_idx] = 1;
305
- return { dimensionality: `1D`, orientation };
306
- }
307
- // Spans all 3 directions - complex warped network
308
- return { dimensionality: `quasi-2D`, orientation: null };
289
+ if (n_spanning === 3)
290
+ return { dimensionality: `quasi-2D`, orientation: null };
291
+ // 1 spanning = 2D sheet (oriented along the spanning axis);
292
+ // 2 spanning = 1D noodle-like tube (oriented along the bounded axis)
293
+ const axis_idx = n_spanning === 1 ? spans.indexOf(true) : spans.indexOf(false);
294
+ const orientation = [0, 0, 0];
295
+ orientation[axis_idx] = 1;
296
+ return { dimensionality: n_spanning === 1 ? `2D` : `1D`, orientation };
309
297
  }
310
298
  // Compute 2D Fermi slice along a specified plane
311
299
  export function compute_fermi_slice(fermi_data, options = {}) {
312
300
  const { miller_indices = [0, 0, 1], distance = 0 } = options;
313
- // Validate miller indices are not all zero
314
- if (miller_indices[0] === 0 && miller_indices[1] === 0 && miller_indices[2] === 0) {
301
+ if (miller_indices.every((miller_idx) => miller_idx === 0)) {
315
302
  throw new Error(`Invalid miller indices [0, 0, 0]: at least one index must be non-zero to define a plane`);
316
303
  }
317
304
  // Compute plane normal in Cartesian coordinates
318
- const plane_normal = math.add(math.add(math.scale(fermi_data.k_lattice[0], miller_indices[0]), math.scale(fermi_data.k_lattice[1], miller_indices[1])), math.scale(fermi_data.k_lattice[2], miller_indices[2]));
305
+ const plane_normal = math.add(math.scale(fermi_data.k_lattice[0], miller_indices[0]), math.scale(fermi_data.k_lattice[1], miller_indices[1]), math.scale(fermi_data.k_lattice[2], miller_indices[2]));
319
306
  const normal_len = Math.hypot(...plane_normal);
320
307
  if (normal_len < EPS) {
321
308
  throw new Error(`Degenerate plane normal: k_lattice vectors produce a zero-length normal for miller indices [${miller_indices.join(`, `)}]`);
@@ -324,11 +311,7 @@ export function compute_fermi_slice(fermi_data, options = {}) {
324
311
  // Compute in-plane basis vectors
325
312
  const [in_plane_u, in_plane_v] = math.compute_in_plane_basis(unit_normal);
326
313
  // Slice each isosurface
327
- const isolines = [];
328
- for (const surface of fermi_data.isosurfaces) {
329
- const lines = slice_surface_with_plane(surface, unit_normal, distance, in_plane_u, in_plane_v);
330
- isolines.push(...lines);
331
- }
314
+ const isolines = fermi_data.isosurfaces.flatMap((surface) => slice_surface_with_plane(surface, unit_normal, distance, in_plane_u, in_plane_v));
332
315
  return {
333
316
  isolines,
334
317
  plane_normal: unit_normal,
@@ -340,8 +323,6 @@ export function compute_fermi_slice(fermi_data, options = {}) {
340
323
  },
341
324
  };
342
325
  }
343
- // Helper to create edge key (sorted vertex indices)
344
- const make_edge_key = (v0_idx, v1_idx) => `${Math.min(v0_idx, v1_idx)},${Math.max(v0_idx, v1_idx)}`;
345
326
  // Compute intersection point on an edge
346
327
  function compute_edge_intersection(surface, vertex_distances, v0_idx, v1_idx) {
347
328
  const d0 = vertex_distances[v0_idx];
@@ -383,7 +364,7 @@ function slice_surface_with_plane(surface, plane_normal, plane_distance, in_plan
383
364
  for (let edge_idx = 0; edge_idx < face.length; edge_idx++) {
384
365
  const v0_idx = face[edge_idx];
385
366
  const v1_idx = face[(edge_idx + 1) % face.length];
386
- const edge_key = make_edge_key(v0_idx, v1_idx);
367
+ const edge_key = `${Math.min(v0_idx, v1_idx)},${Math.max(v0_idx, v1_idx)}`;
387
368
  const intersection = compute_edge_intersection(surface, vertex_distances, v0_idx, v1_idx);
388
369
  if (intersection) {
389
370
  crossing_edges.push({ edge_key, intersection });
@@ -516,24 +497,10 @@ function slice_surface_with_plane(surface, plane_normal, plane_distance, in_plan
516
497
  // For cubic Oh symmetry, this is the region where all vertices are in the first octant
517
498
  // (x >= 0, y >= 0, z >= 0) with some tolerance. Such data needs tiling to show the full BZ.
518
499
  export function detect_irreducible_bz(fermi_data) {
519
- if (fermi_data.isosurfaces.length === 0)
500
+ const vertex_count = fermi_data.isosurfaces.reduce((sum, surface) => sum + surface.vertices.length, 0);
501
+ // Only consider it irreducible if we have significant data and all vertices are
502
+ // in the positive octant (with small tolerance for numerical error)
503
+ if (vertex_count <= IRREDUCIBLE_BZ_MIN_VERTICES)
520
504
  return false;
521
- // Check if all vertices are in the positive octant (with small tolerance for numerical error)
522
- let all_positive = true;
523
- let vertex_count = 0;
524
- for (const surface of fermi_data.isosurfaces) {
525
- for (const vertex of surface.vertices) {
526
- vertex_count++;
527
- if (vertex[0] < -IRREDUCIBLE_BZ_TOLERANCE ||
528
- vertex[1] < -IRREDUCIBLE_BZ_TOLERANCE ||
529
- vertex[2] < -IRREDUCIBLE_BZ_TOLERANCE) {
530
- all_positive = false;
531
- break;
532
- }
533
- }
534
- if (!all_positive)
535
- break;
536
- }
537
- // Only consider it irreducible if we have significant data and all positive
538
- return all_positive && vertex_count > IRREDUCIBLE_BZ_MIN_VERTICES;
505
+ return fermi_data.isosurfaces.every((surface) => surface.vertices.every((vertex) => vertex.every((coord) => coord >= -IRREDUCIBLE_BZ_TOLERANCE)));
539
506
  }
@@ -95,18 +95,11 @@ function parse_bxsf(content) {
95
95
  if (energy_values.length < total_points) {
96
96
  throw new Error(`Band ${band_idx}: expected ${total_points} values, got ${energy_values.length}`);
97
97
  }
98
- // Reshape into 3D grid [kx][ky][kz] (preallocated for speed)
99
- const band_grid = Array(nx);
100
- let val_idx = 0;
101
- for (let ix = 0; ix < nx; ix++) {
102
- const iy_arr = Array(ny);
103
- for (let iy = 0; iy < ny; iy++) {
104
- const iz_arr = energy_values.slice(val_idx, val_idx + nz);
105
- val_idx += nz;
106
- iy_arr[iy] = iz_arr;
107
- }
108
- band_grid[ix] = iy_arr;
109
- }
98
+ // Reshape into 3D grid [kx][ky][kz]
99
+ const band_grid = Array.from({ length: nx }, (_x_row, ix) => Array.from({ length: ny }, (_y_row, iy) => {
100
+ const offset = (ix * ny + iy) * nz;
101
+ return energy_values.slice(offset, offset + nz);
102
+ }));
110
103
  energies[0].push(band_grid);
111
104
  }
112
105
  // Try to find Fermi energy (often in a comment or as FERMI_ENERGY keyword)
@@ -159,58 +152,40 @@ function parse_frmsf(content) {
159
152
  if (isNaN(n_bands) || n_bands <= 0) {
160
153
  throw new Error(`FRMSF: Invalid number of bands`);
161
154
  }
162
- // FRMSF format has single spin channel (no spin-polarized support in standard format)
163
- const n_spins = 1;
164
155
  // Lines 4-6: reciprocal lattice vectors (in Bohr^-1, convert to Å^-1)
165
156
  const inv_bohr = 1 / constants.BOHR_TO_ANGSTROM;
166
- const k_lattice = [
167
- parse_floats(lines[line_idx++])
168
- .slice(0, 3)
169
- .map((val) => val * inv_bohr),
170
- parse_floats(lines[line_idx++])
171
- .slice(0, 3)
172
- .map((val) => val * inv_bohr),
173
- parse_floats(lines[line_idx++])
174
- .slice(0, 3)
175
- .map((val) => val * inv_bohr),
176
- ];
157
+ const parse_k_vector = () => parse_floats(lines[line_idx++])
158
+ .slice(0, 3)
159
+ .map((val) => val * inv_bohr);
160
+ const k_lattice = [parse_k_vector(), parse_k_vector(), parse_k_vector()];
177
161
  const [nx, ny, nz] = k_grid;
178
162
  const total_points = nx * ny * nz;
179
- // Parse band energies for each spin and band
180
- // FRMSF format: one energy value per line per grid point. Any additional columns
181
- // (e.g. auxiliary color/velocity data) are ignored to prevent grid corruption.
182
- const energies = [];
183
- for (let spin_idx = 0; spin_idx < n_spins; spin_idx++) {
184
- energies[spin_idx] = [];
185
- for (let band_idx = 0; band_idx < n_bands; band_idx++) {
186
- const energy_values = [];
187
- // Read energy values (first value per line only, ignore auxiliary columns;
188
- // normalize Fortran D-exponents like parse_floats does)
189
- while (energy_values.length < total_points && line_idx < lines.length) {
190
- const energy = parse_leading_num(normalize_scientific_notation(lines[line_idx] ?? ``));
191
- if (isNaN(energy))
192
- break;
193
- energy_values.push(energy);
194
- line_idx++;
195
- }
196
- if (energy_values.length < total_points) {
197
- throw new Error(`FRMSF spin ${spin_idx} band ${band_idx}: expected ${total_points} values, got ${energy_values.length}`);
198
- }
199
- // Reshape into 3D grid [kx][ky][kz]
200
- const band_grid = [];
201
- let val_idx = 0;
202
- for (let ix = 0; ix < nx; ix++) {
203
- band_grid[ix] = [];
204
- for (let iy = 0; iy < ny; iy++) {
205
- band_grid[ix][iy] = [];
206
- for (let iz = 0; iz < nz; iz++) {
207
- // FRMSF uses Hartree, convert to eV
208
- band_grid[ix][iy][iz] = energy_values[val_idx++] * constants.HARTREE_TO_EV;
209
- }
210
- }
211
- }
212
- energies[spin_idx].push(band_grid);
163
+ // Parse band energies (FRMSF has a single spin channel — no spin-polarized support
164
+ // in the standard format). One energy value per line per grid point; any additional
165
+ // columns (e.g. auxiliary color/velocity data) are ignored to prevent grid corruption.
166
+ const energies = [[]];
167
+ for (let band_idx = 0; band_idx < n_bands; band_idx++) {
168
+ const energy_values = [];
169
+ // Read energy values (first value per line only, ignore auxiliary columns;
170
+ // normalize Fortran D-exponents like parse_floats does)
171
+ while (energy_values.length < total_points && line_idx < lines.length) {
172
+ const energy = parse_leading_num(normalize_scientific_notation(lines[line_idx] ?? ``));
173
+ if (isNaN(energy))
174
+ break;
175
+ energy_values.push(energy);
176
+ line_idx++;
177
+ }
178
+ if (energy_values.length < total_points) {
179
+ throw new Error(`FRMSF band ${band_idx}: expected ${total_points} values, got ${energy_values.length}`);
213
180
  }
181
+ // Reshape into 3D grid [kx][ky][kz], converting Hartree to eV
182
+ const band_grid = Array.from({ length: nx }, (_x_row, ix) => Array.from({ length: ny }, (_y_row, iy) => {
183
+ const offset = (ix * ny + iy) * nz;
184
+ return energy_values
185
+ .slice(offset, offset + nz)
186
+ .map((energy) => energy * constants.HARTREE_TO_EV);
187
+ }));
188
+ energies[0].push(band_grid);
214
189
  }
215
190
  return {
216
191
  energies,
@@ -218,7 +193,7 @@ function parse_frmsf(content) {
218
193
  k_lattice,
219
194
  fermi_energy: 0, // FRMSF typically expects Fermi level at 0
220
195
  n_bands,
221
- n_spins,
196
+ n_spins: 1,
222
197
  periodic: true, // FRMSF stores k=i/n with no duplicated endpoint (unlike BXSF)
223
198
  };
224
199
  }
@@ -357,16 +332,9 @@ function parse_ifermi_surface(data) {
357
332
  const faces = ifermi_iso.faces;
358
333
  // Compute vertex normals from faces
359
334
  const normals = compute_vertex_normals(vertices, faces);
360
- // Extract properties if available
361
- let properties;
362
- if (ifermi_iso.properties) {
363
- // IFermi stores properties like fermi_velocity, spin, etc.
364
- const prop_keys = Object.keys(ifermi_iso.properties);
365
- if (prop_keys.length > 0) {
366
- // Use first available property as vertex colors
367
- properties = ifermi_iso.properties[prop_keys[0]];
368
- }
369
- }
335
+ // IFermi stores properties like fermi_velocity, spin, etc.
336
+ // Use first available property as vertex colors
337
+ const properties = Object.values(ifermi_iso.properties ?? {})[0];
370
338
  // Parse dimensionality
371
339
  let dimensionality;
372
340
  if (ifermi_iso.dimensionality) {