matterviz 0.1.12 → 0.1.13

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 (195) hide show
  1. package/dist/ContextMenu.svelte +1 -1
  2. package/dist/ContextMenu.svelte.d.ts +3 -3
  3. package/dist/DraggablePane.svelte +10 -5
  4. package/dist/DraggablePane.svelte.d.ts +3 -3
  5. package/dist/FilePicker.svelte.d.ts +3 -3
  6. package/dist/Icon.svelte.d.ts +3 -3
  7. package/dist/Nav.svelte +76 -31
  8. package/dist/SettingsSection.svelte.d.ts +3 -3
  9. package/dist/Spinner.svelte +18 -29
  10. package/dist/Spinner.svelte.d.ts +3 -3
  11. package/dist/app.css +4 -0
  12. package/dist/bands/Bands.svelte +216 -0
  13. package/dist/bands/Bands.svelte.d.ts +16 -0
  14. package/dist/bands/BandsAndDos.svelte +26 -0
  15. package/dist/bands/BandsAndDos.svelte.d.ts +16 -0
  16. package/dist/bands/Dos.svelte +124 -0
  17. package/dist/bands/Dos.svelte.d.ts +18 -0
  18. package/dist/bands/helpers.d.ts +11 -0
  19. package/dist/bands/helpers.js +195 -0
  20. package/dist/bands/index.d.ts +5 -0
  21. package/dist/bands/index.js +6 -0
  22. package/dist/bands/types.d.ts +54 -0
  23. package/dist/bands/types.js +2 -0
  24. package/dist/brillouin/BrillouinZone.svelte +373 -0
  25. package/dist/brillouin/BrillouinZone.svelte.d.ts +57 -0
  26. package/dist/brillouin/BrillouinZoneControls.svelte +88 -0
  27. package/dist/brillouin/BrillouinZoneControls.svelte.d.ts +14 -0
  28. package/dist/brillouin/BrillouinZoneExportPane.svelte +129 -0
  29. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +15 -0
  30. package/dist/brillouin/BrillouinZoneInfoPane.svelte +115 -0
  31. package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +13 -0
  32. package/dist/brillouin/BrillouinZoneScene.svelte +180 -0
  33. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +34 -0
  34. package/dist/brillouin/compute.d.ts +7 -0
  35. package/dist/brillouin/compute.js +215 -0
  36. package/dist/brillouin/index.d.ts +7 -0
  37. package/dist/brillouin/index.js +7 -0
  38. package/dist/brillouin/types.d.ts +23 -0
  39. package/dist/brillouin/types.js +1 -0
  40. package/dist/composition/BarChart.svelte.d.ts +3 -3
  41. package/dist/composition/BubbleChart.svelte +7 -6
  42. package/dist/composition/BubbleChart.svelte.d.ts +3 -3
  43. package/dist/composition/Composition.svelte.d.ts +3 -3
  44. package/dist/composition/PieChart.svelte +1 -1
  45. package/dist/composition/PieChart.svelte.d.ts +3 -3
  46. package/dist/composition/parse.d.ts +3 -3
  47. package/dist/coordination/CoordinationBarPlot.svelte +241 -0
  48. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +29 -0
  49. package/dist/coordination/calc-coordination.d.ts +15 -0
  50. package/dist/coordination/calc-coordination.js +66 -0
  51. package/dist/coordination/index.d.ts +8 -0
  52. package/dist/coordination/index.js +7 -0
  53. package/dist/element/BohrAtom.svelte.d.ts +3 -3
  54. package/dist/element/ElementHeading.svelte.d.ts +3 -3
  55. package/dist/element/ElementPhoto.svelte.d.ts +3 -3
  56. package/dist/element/ElementStats.svelte.d.ts +3 -3
  57. package/dist/element/ElementTile.svelte +1 -1
  58. package/dist/element/ElementTile.svelte.d.ts +4 -4
  59. package/dist/element/Nucleus.svelte.d.ts +3 -3
  60. package/dist/icons.d.ts +4 -0
  61. package/dist/icons.js +4 -0
  62. package/dist/index.d.ts +5 -1
  63. package/dist/index.js +5 -1
  64. package/dist/io/export.d.ts +1 -6
  65. package/dist/io/export.js +12 -12
  66. package/dist/labels.d.ts +19 -0
  67. package/dist/labels.js +59 -0
  68. package/dist/math.d.ts +7 -2
  69. package/dist/math.js +24 -12
  70. package/dist/periodic-table/PeriodicTable.svelte.d.ts +3 -3
  71. package/dist/periodic-table/PeriodicTableControls.svelte.d.ts +3 -3
  72. package/dist/periodic-table/PropertySelect.svelte +1 -1
  73. package/dist/periodic-table/PropertySelect.svelte.d.ts +5 -4
  74. package/dist/periodic-table/TableInset.svelte.d.ts +3 -3
  75. package/dist/phase-diagram/PhaseDiagram2D.svelte +44 -107
  76. package/dist/phase-diagram/PhaseDiagram2D.svelte.d.ts +7 -27
  77. package/dist/phase-diagram/PhaseDiagram3D.svelte +67 -170
  78. package/dist/phase-diagram/PhaseDiagram3D.svelte.d.ts +7 -31
  79. package/dist/phase-diagram/PhaseDiagram4D.svelte +87 -145
  80. package/dist/phase-diagram/PhaseDiagram4D.svelte.d.ts +5 -28
  81. package/dist/phase-diagram/PhaseDiagramControls.svelte +9 -9
  82. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +6 -6
  83. package/dist/phase-diagram/PhaseDiagramInfoPane.svelte +4 -4
  84. package/dist/phase-diagram/PhaseDiagramInfoPane.svelte.d.ts +5 -5
  85. package/dist/phase-diagram/PhaseDiagramStats.svelte +9 -16
  86. package/dist/phase-diagram/PhaseDiagramStats.svelte.d.ts +3 -3
  87. package/dist/phase-diagram/StructurePopup.svelte.d.ts +3 -3
  88. package/dist/phase-diagram/barycentric-coords.js +13 -24
  89. package/dist/phase-diagram/helpers.d.ts +12 -14
  90. package/dist/phase-diagram/helpers.js +70 -23
  91. package/dist/phase-diagram/index.d.ts +58 -0
  92. package/dist/phase-diagram/index.js +44 -0
  93. package/dist/phase-diagram/thermodynamics.js +2 -3
  94. package/dist/phase-diagram/types.d.ts +3 -8
  95. package/dist/plot/BarPlot.svelte +474 -240
  96. package/dist/plot/BarPlot.svelte.d.ts +16 -37
  97. package/dist/plot/BarPlotControls.svelte +33 -201
  98. package/dist/plot/BarPlotControls.svelte.d.ts +11 -22
  99. package/dist/plot/ColorBar.svelte.d.ts +3 -3
  100. package/dist/plot/ColorScaleSelect.svelte +1 -1
  101. package/dist/plot/ColorScaleSelect.svelte.d.ts +3 -3
  102. package/dist/plot/ElementScatter.svelte +5 -5
  103. package/dist/plot/ElementScatter.svelte.d.ts +8 -8
  104. package/dist/plot/Histogram.svelte +318 -170
  105. package/dist/plot/Histogram.svelte.d.ts +21 -41
  106. package/dist/plot/HistogramControls.svelte +115 -347
  107. package/dist/plot/HistogramControls.svelte.d.ts +13 -28
  108. package/dist/plot/Line.svelte +1 -1
  109. package/dist/plot/Line.svelte.d.ts +4 -4
  110. package/dist/plot/PlotControls.svelte +280 -0
  111. package/dist/plot/PlotControls.svelte.d.ts +4 -0
  112. package/dist/plot/PlotLegend.svelte +9 -24
  113. package/dist/plot/PlotLegend.svelte.d.ts +3 -3
  114. package/dist/plot/ScatterPlot.svelte +536 -571
  115. package/dist/plot/ScatterPlot.svelte.d.ts +18 -90
  116. package/dist/plot/ScatterPlotControls.svelte +108 -316
  117. package/dist/plot/ScatterPlotControls.svelte.d.ts +11 -38
  118. package/dist/plot/ScatterPoint.svelte +1 -1
  119. package/dist/plot/ScatterPoint.svelte.d.ts +5 -5
  120. package/dist/plot/SpacegroupBarPlot.svelte +235 -0
  121. package/dist/plot/SpacegroupBarPlot.svelte.d.ts +9 -0
  122. package/dist/plot/data-transform.d.ts +2 -2
  123. package/dist/plot/data-transform.js +1 -5
  124. package/dist/plot/index.d.ts +12 -200
  125. package/dist/plot/index.js +14 -6
  126. package/dist/plot/layout.d.ts +10 -16
  127. package/dist/plot/layout.js +40 -25
  128. package/dist/plot/scales.d.ts +7 -8
  129. package/dist/plot/scales.js +22 -7
  130. package/dist/plot/types.d.ts +303 -0
  131. package/dist/plot/types.js +1 -0
  132. package/dist/rdf/RdfPlot.svelte +156 -0
  133. package/dist/rdf/RdfPlot.svelte.d.ts +20 -0
  134. package/dist/rdf/calc-rdf.d.ts +4 -0
  135. package/dist/rdf/calc-rdf.js +107 -0
  136. package/dist/rdf/index.d.ts +22 -0
  137. package/dist/rdf/index.js +2 -0
  138. package/dist/settings.d.ts +86 -75
  139. package/dist/settings.js +257 -155
  140. package/dist/structure/Bond.svelte +126 -110
  141. package/dist/structure/Bond.svelte.d.ts +7 -20
  142. package/dist/structure/CanvasTooltip.svelte.d.ts +4 -4
  143. package/dist/structure/Cylinder.svelte +29 -0
  144. package/dist/structure/Cylinder.svelte.d.ts +10 -0
  145. package/dist/structure/Lattice.svelte +1 -2
  146. package/dist/structure/Lattice.svelte.d.ts +3 -3
  147. package/dist/structure/Structure.svelte +23 -8
  148. package/dist/structure/Structure.svelte.d.ts +10 -7
  149. package/dist/structure/StructureControls.svelte +25 -134
  150. package/dist/structure/StructureControls.svelte.d.ts +4 -9
  151. package/dist/structure/StructureExportPane.svelte +188 -0
  152. package/dist/structure/StructureExportPane.svelte.d.ts +17 -0
  153. package/dist/structure/StructureInfoPane.svelte.d.ts +3 -3
  154. package/dist/structure/StructureLegend.svelte +91 -27
  155. package/dist/structure/StructureLegend.svelte.d.ts +5 -4
  156. package/dist/structure/StructureScene.svelte +93 -100
  157. package/dist/structure/StructureScene.svelte.d.ts +15 -12
  158. package/dist/structure/Vector.svelte +19 -25
  159. package/dist/structure/Vector.svelte.d.ts +3 -3
  160. package/dist/structure/bonding.d.ts +16 -14
  161. package/dist/structure/bonding.js +207 -216
  162. package/dist/structure/export.d.ts +3 -0
  163. package/dist/structure/export.js +120 -0
  164. package/dist/structure/index.d.ts +15 -1
  165. package/dist/structure/index.js +2 -0
  166. package/dist/symmetry/WyckoffTable.svelte.d.ts +3 -3
  167. package/dist/symmetry/index.d.ts +1 -0
  168. package/dist/symmetry/index.js +1 -0
  169. package/dist/symmetry/spacegroups.d.ts +9 -0
  170. package/dist/symmetry/spacegroups.js +397 -0
  171. package/dist/theme/ThemeControl.svelte.d.ts +3 -3
  172. package/dist/theme/themes.js +2 -16
  173. package/dist/trajectory/Trajectory.svelte +52 -78
  174. package/dist/trajectory/Trajectory.svelte.d.ts +5 -5
  175. package/dist/trajectory/TrajectoryError.svelte.d.ts +3 -3
  176. package/dist/trajectory/TrajectoryExportPane.svelte +1 -1
  177. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +5 -5
  178. package/dist/trajectory/TrajectoryInfoPane.svelte +1 -1
  179. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +3 -3
  180. package/dist/trajectory/extract.js +2 -1
  181. package/dist/trajectory/index.d.ts +1 -1
  182. package/dist/trajectory/index.js +1 -1
  183. package/dist/trajectory/parse.js +21 -14
  184. package/dist/utils.d.ts +1 -0
  185. package/dist/utils.js +17 -0
  186. package/dist/xrd/XrdPlot.svelte +14 -11
  187. package/dist/xrd/XrdPlot.svelte.d.ts +6 -5
  188. package/package.json +20 -12
  189. package/readme.md +1 -1
  190. package/dist/material/MaterialCard.svelte +0 -123
  191. package/dist/material/MaterialCard.svelte.d.ts +0 -9
  192. package/dist/material/index.d.ts +0 -1
  193. package/dist/material/index.js +0 -1
  194. package/dist/plot/formatting.d.ts +0 -7
  195. package/dist/plot/formatting.js +0 -53
@@ -1,4 +1,4 @@
1
- <script lang="ts">import { Icon, Spinner, Structure, toggle_fullscreen } from '..';
1
+ <script lang="ts">import { Icon, Spinner, Structure, toggle_fullscreen, } from '..';
2
2
  import { handle_url_drop, load_from_url } from '../io';
3
3
  import { format_num, trajectory_property_config } from '../labels';
4
4
  import { Histogram, ScatterPlot } from '../plot';
@@ -9,7 +9,7 @@ import { tooltip } from 'svelte-multiselect/attachments';
9
9
  import { full_data_extractor } from './extract';
10
10
  import { TrajectoryError, TrajectoryExportPane, TrajectoryInfoPane } from './index';
11
11
  import { create_frame_loader, get_unsupported_format_message, MAX_BIN_FILE_SIZE, MAX_TEXT_FILE_SIZE, parse_trajectory_async, } from './parse';
12
- import { generate_axis_labels, generate_plot_series, generate_streaming_plot_series, should_hide_plot, toggle_series_visibility, } from './plotting';
12
+ import { generate_axis_labels, generate_plot_series, generate_streaming_plot_series, should_hide_plot, } from './plotting';
13
13
  let { trajectory = $bindable(undefined), data_url, current_step_idx = $bindable(0), data_extractor = full_data_extractor, allow_file_drop = true, layout = `auto`, structure_props = {}, scatter_props = {}, histogram_props = {}, spinner_props = {}, trajectory_controls, error_snippet, show_controls = true, fullscreen_toggle = DEFAULTS.trajectory.fullscreen_toggle, auto_play = false, display_mode = $bindable(`structure+scatter`), step_labels = 5, on_play, on_pause, on_step_change, on_end, on_loop, on_frame_rate_change, on_display_mode_change, on_fullscreen_change, on_file_load, on_error, fps_range = DEFAULTS.trajectory.fps_range, fps = $bindable(5), loading_options = {}, plot_skimming = true, ...rest } = $props();
14
14
  let dragover = $state(false);
15
15
  let loading = $state(false);
@@ -96,20 +96,8 @@ async function load_frame_on_demand(frame_idx) {
96
96
  }
97
97
  // Current frame structure for display
98
98
  let current_structure = $derived(current_frame?.structure);
99
- // Dynamically hide bonds during playback to improve FPS - bond computation is expensive
100
- // Maybe revisit this in future if we find much more efficient bonding algo
101
- let final_structure_props = $derived.by(() => {
102
- if (is_playing) { // Hide bonds during playback
103
- const { scene_props = {} } = structure_props;
104
- if (scene_props.show_bonds !== `never`) {
105
- const show_bonds = `never`;
106
- return { ...structure_props, scene_props: { ...scene_props, show_bonds } };
107
- }
108
- }
109
- // default show_image_atoms to false since usually undesired if new image atoms
110
- // pup-up/disappear when moving near cell edge during the trajectory
111
- return { show_image_atoms: false, ...structure_props };
112
- });
99
+ // Track hidden elements (persists across frame changes)
100
+ let hidden_elements = $state(new Set());
113
101
  let step_label_positions = $derived.by(() => {
114
102
  if (!step_labels || total_frames <= 1)
115
103
  return [];
@@ -149,6 +137,23 @@ let plot_series = $derived.by(() => {
149
137
  })
150
138
  : [];
151
139
  });
140
+ let x_axis = $derived({
141
+ label: `Step`,
142
+ format: `.3~s`,
143
+ ticks: step_label_positions,
144
+ });
145
+ // Generate axis labels based on first visible series on each axis
146
+ let y_axis_labels = $derived(generate_axis_labels(plot_series));
147
+ let y_axis = $derived({
148
+ label: y_axis_labels.y1,
149
+ format: `.2~s`,
150
+ label_shift: { y: 20 },
151
+ });
152
+ let y2_axis = $derived({
153
+ label: y_axis_labels.y2,
154
+ format: `.2~s`,
155
+ label_shift: { y: 80 },
156
+ });
152
157
  // Helper function to get current frame data for callbacks
153
158
  function get_current_frame_data() {
154
159
  return {
@@ -161,8 +166,6 @@ let show_plot = $derived(display_mode !== `structure` && !should_hide_plot(traje
161
166
  // Determine what to show based on display mode
162
167
  let show_structure = $derived(![`scatter`, `histogram`].includes(display_mode));
163
168
  let actual_show_plot = $derived(display_mode !== `structure` && show_plot);
164
- // Generate axis labels based on first visible series on each axis
165
- let y_axis_labels = $derived(generate_axis_labels(plot_series));
166
169
  // Check if there are any Y2 series to determine padding
167
170
  let has_y2_series = $derived(plot_series.some((s) => s.y_axis === `y2` && s.visible));
168
171
  // Step navigation functions
@@ -218,10 +221,6 @@ function handle_plot_change(data) {
218
221
  go_to_step(Math.round(data.x));
219
222
  }
220
223
  }
221
- // Handle legend toggling
222
- function handle_legend_toggle(series_idx) {
223
- plot_series = toggle_series_visibility(plot_series, series_idx);
224
- }
225
224
  // Helper function to read file content
226
225
  async function read_file_content(file) {
227
226
  return new Promise((resolve, reject) => {
@@ -236,16 +235,6 @@ async function read_file_content(file) {
236
235
  reader.readAsArrayBuffer(file);
237
236
  });
238
237
  }
239
- // Legend configuration
240
- let legend_config = $derived({
241
- responsive: true,
242
- layout: `horizontal`,
243
- layout_tracks: 3,
244
- item_gap: 0,
245
- padding: { t: 5, b: 5, l: 5, r: 5 },
246
- ...scatter_props?.legend,
247
- on_toggle: scatter_props?.legend?.on_toggle ?? handle_legend_toggle,
248
- });
249
238
  // Play/pause functionality
250
239
  function toggle_play() {
251
240
  if (is_playing)
@@ -490,7 +479,6 @@ async function load_with_indexing(data, filename) {
490
479
  original_data = data;
491
480
  // @ts-expect-error - dynamically adding frame_loader for indexed trajectories
492
481
  trajectory.frame_loader = create_frame_loader(filename);
493
- console.log(`Loaded trajectory with indexing: ${trajectory.total_frames || trajectory.frames.length} frames`);
494
482
  }
495
483
  catch (error) {
496
484
  console.error(`Indexed loading failed:`, error);
@@ -596,12 +584,11 @@ function onkeydown(event) {
596
584
  go_to_step(Math.floor((parseInt(event.key, 10) / 10) * (total_frames - 1)));
597
585
  }
598
586
  }
599
- let panes_open = $state({
600
- structure_info: false,
601
- structure_controls: false,
602
- plot_controls: false,
603
- export_pane: false,
604
- });
587
+ // Separate state variables for each pane to match component prop types
588
+ let structure_info_open = $state(false);
589
+ let structure_controls_open = $state(false);
590
+ let scatter_controls = $state({ open: false });
591
+ let trajectory_export_open = $state(false);
605
592
  let fullscreen = $state(false);
606
593
  </script>
607
594
 
@@ -614,8 +601,8 @@ let fullscreen = $state(false);
614
601
 
615
602
  <div
616
603
  class:dragover
617
- class:active={is_playing || panes_open.structure_info || panes_open.structure_controls ||
618
- panes_open.plot_controls || panes_open.export_pane || info_pane_open}
604
+ class:active={is_playing || structure_info_open || structure_controls_open ||
605
+ scatter_controls.open || trajectory_export_open || info_pane_open}
619
606
  bind:this={wrapper}
620
607
  bind:clientWidth={viewport.width}
621
608
  bind:clientHeight={viewport.height}
@@ -791,7 +778,7 @@ let fullscreen = $state(false);
791
778
  {/if}
792
779
  <!-- Trajectory Export Pane -->
793
780
  <TrajectoryExportPane
794
- bind:export_pane_open={panes_open.export_pane}
781
+ bind:export_pane_open={trajectory_export_open}
795
782
  {trajectory}
796
783
  {wrapper}
797
784
  filename={current_filename || `trajectory`}
@@ -895,10 +882,13 @@ let fullscreen = $state(false);
895
882
  structure={current_structure}
896
883
  allow_file_drop={false}
897
884
  style="height: 100%; min-height: 0; z-index: 3; border-radius: 0"
898
- enable_tips={false}
899
- {...final_structure_props}
900
- bind:controls_open={panes_open.structure_controls}
901
- bind:info_pane_open={panes_open.structure_info}
885
+ {...{
886
+ show_image_atoms: false, // Default to false to avoid atoms popping in/out at cell edges
887
+ ...structure_props,
888
+ }}
889
+ bind:controls_open={structure_controls_open}
890
+ bind:info_pane_open={structure_info_open}
891
+ bind:hidden_elements
902
892
  />
903
893
  {/if}
904
894
 
@@ -906,23 +896,16 @@ let fullscreen = $state(false);
906
896
  {#if display_mode === `scatter` || display_mode === `structure+scatter`}
907
897
  <ScatterPlot
908
898
  series={plot_series}
909
- x_label="Step"
910
- y_label={y_axis_labels.y1}
911
- y_label_shift={{ y: 20 }}
912
- y_format=".2~s"
913
- y2_format=".2~s"
914
- y2_label={y_axis_labels.y2}
915
- y2_label_shift={{ y: 80 }}
899
+ bind:x_axis
900
+ bind:y_axis
901
+ bind:y2_axis
916
902
  current_x_value={current_step_idx}
917
903
  change={plot_skimming ? handle_plot_change : undefined}
918
- markers="line"
919
- x_format=".3~s"
920
- x_ticks={step_label_positions}
921
- bind:controls_open={panes_open.plot_controls}
904
+ bind:controls={scatter_controls}
922
905
  padding={{ t: 20, b: 60, l: 100, r: has_y2_series ? 100 : 20 }}
923
906
  range_padding={0}
924
907
  style="height: 100%"
925
- legend={legend_config}
908
+ legend={scatter_props?.legend}
926
909
  {...scatter_props}
927
910
  class="plot {scatter_props.class ?? ``}"
928
911
  >
@@ -939,26 +922,14 @@ let fullscreen = $state(false);
939
922
  {:else if display_mode === `histogram` || display_mode === `structure+histogram`}
940
923
  <Histogram
941
924
  series={plot_series}
942
- x_label={String(histogram_props.x_label ?? y_axis_labels.y1)}
943
- y_label={(`y_label` in histogram_props) ? histogram_props.y_label as string : `Count`}
944
- x_format=".3~s"
945
- y_format=".3~s"
946
- mode={(`mode` in histogram_props)
947
- ? histogram_props.mode as `overlay` | `single`
948
- : `overlay`}
949
- show_legend={(`show_legend` in histogram_props)
950
- ? histogram_props.show_legend as boolean
951
- : (plot_series.length > 1)}
952
- legend={{
953
- responsive: true,
954
- layout: `horizontal`,
955
- layout_tracks: 3,
956
- item_gap: 0,
957
- padding: { t: 5, b: 5, l: 5, r: 5 },
958
- on_toggle: handle_legend_toggle,
959
- series_data: [],
960
- ...(histogram_props.legend || {}),
925
+ x_axis={{
926
+ label: String(histogram_props.x_axis?.label ?? y_axis_labels.y1),
927
+ format: `.3~s`,
961
928
  }}
929
+ y_axis={{ label: histogram_props.y_axis?.label ?? `Count`, format: `.3~s` }}
930
+ mode={histogram_props.mode ?? `overlay`}
931
+ show_legend={histogram_props.show_legend ?? plot_series.length > 1}
932
+ legend={histogram_props.legend}
962
933
  style="height: 100%"
963
934
  {...histogram_props}
964
935
  class="plot {histogram_props.class ?? ``}"
@@ -1139,13 +1110,16 @@ let fullscreen = $state(false);
1139
1110
  button.filename {
1140
1111
  align-items: center;
1141
1112
  white-space: nowrap;
1142
- padding: var(--trajectory-filename-padding, 2pt 4pt);
1113
+ padding: var(--trajectory-filename-padding, 3pt 4pt);
1143
1114
  border-radius: var(--trajectory-filename-border-radius, 2px);
1144
1115
  max-width: clamp(150px, 20cqw, 250px);
1145
1116
  overflow: hidden;
1146
1117
  text-overflow: ellipsis;
1147
1118
  display: inline-block;
1148
1119
  position: relative;
1120
+ font-family: monospace;
1121
+ font-size: 0.9em;
1122
+ background: var(--code-bg, rgba(0, 0, 0, 0.1));
1149
1123
  }
1150
1124
  @keyframes fade-in {
1151
1125
  from {
@@ -17,8 +17,8 @@ type EventHandlers = {
17
17
  on_file_load?: (data: TrajHandlerData) => void;
18
18
  on_error?: (data: TrajHandlerData) => void;
19
19
  };
20
- interface Props extends EventHandlers, HTMLAttributes<HTMLDivElement> {
21
- trajectory?: TrajectoryType | undefined;
20
+ type $$ComponentProps = EventHandlers & HTMLAttributes<HTMLDivElement> & {
21
+ trajectory?: TrajectoryType;
22
22
  data_url?: string;
23
23
  current_step_idx?: number;
24
24
  data_extractor?: TrajectoryDataExtractor;
@@ -44,7 +44,7 @@ interface Props extends EventHandlers, HTMLAttributes<HTMLDivElement> {
44
44
  fullscreen_toggle?: Snippet<[]> | boolean;
45
45
  auto_play?: boolean;
46
46
  display_mode?: `structure+scatter` | `structure` | `scatter` | `histogram` | `structure+histogram`;
47
- step_labels?: number | number[] | undefined;
47
+ step_labels?: number | number[];
48
48
  property_labels?: Record<string, string>;
49
49
  units?: {
50
50
  energy?: string;
@@ -66,7 +66,7 @@ interface Props extends EventHandlers, HTMLAttributes<HTMLDivElement> {
66
66
  fps?: number;
67
67
  loading_options?: LoadingOptions;
68
68
  plot_skimming?: boolean;
69
- }
70
- declare const Trajectory: import("svelte").Component<Props, {}, "trajectory" | "fps" | "display_mode" | "current_step_idx">;
69
+ };
70
+ declare const Trajectory: import("svelte").Component<$$ComponentProps, {}, "trajectory" | "fps" | "display_mode" | "current_step_idx">;
71
71
  type Trajectory = ReturnType<typeof Trajectory>;
72
72
  export default Trajectory;
@@ -1,13 +1,13 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
- interface Props extends HTMLAttributes<HTMLDivElement> {
3
+ type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
4
4
  error_msg: string;
5
5
  on_dismiss: () => void;
6
6
  error_snippet?: Snippet<[{
7
7
  error_msg: string;
8
8
  on_dismiss: () => void;
9
9
  }]>;
10
- }
11
- declare const TrajectoryError: import("svelte").Component<Props, {}, "">;
10
+ };
11
+ declare const TrajectoryError: import("svelte").Component<$$ComponentProps, {}, "">;
12
12
  type TrajectoryError = ReturnType<typeof TrajectoryError>;
13
13
  export default TrajectoryError;
@@ -101,7 +101,7 @@ $effect(() => {
101
101
  closed_icon="Export"
102
102
  pane_props={{ ...pane_props, class: `export-pane ${pane_props?.class ?? ``}` }}
103
103
  toggle_props={{
104
- title: `${export_pane_open ? `Close` : `Open`} export controls`,
104
+ title: export_pane_open ? `` : `Export Trajectory`,
105
105
  ...toggle_props,
106
106
  class: `trajectory-export-toggle ${toggle_props?.class ?? ``}`,
107
107
  }}
@@ -1,17 +1,17 @@
1
1
  import { DraggablePane } from '..';
2
2
  import type { TrajectoryType } from './';
3
3
  import type { ComponentProps } from 'svelte';
4
- interface Props {
4
+ type $$ComponentProps = {
5
5
  export_pane_open?: boolean;
6
- trajectory?: TrajectoryType | undefined;
7
- wrapper?: HTMLDivElement | undefined;
6
+ trajectory?: TrajectoryType;
7
+ wrapper?: HTMLDivElement;
8
8
  filename?: string;
9
9
  video_fps?: number;
10
10
  resolution_multiplier?: number;
11
11
  on_step_change?: (step_idx: number) => Promise<void> | void;
12
12
  pane_props?: ComponentProps<typeof DraggablePane>[`pane_props`];
13
13
  toggle_props?: ComponentProps<typeof DraggablePane>[`toggle_props`];
14
- }
15
- declare const TrajectoryExportPane: import("svelte").Component<Props, {}, "toggle_props" | "pane_props" | "export_pane_open" | "video_fps" | "resolution_multiplier">;
14
+ };
15
+ declare const TrajectoryExportPane: import("svelte").Component<$$ComponentProps, {}, "toggle_props" | "pane_props" | "export_pane_open" | "video_fps" | "resolution_multiplier">;
16
16
  type TrajectoryExportPane = ReturnType<typeof TrajectoryExportPane>;
17
17
  export default TrajectoryExportPane;
@@ -174,7 +174,7 @@ let info_pane_data = $derived.by(() => {
174
174
  bind:show={pane_open}
175
175
  max_width="24em"
176
176
  toggle_props={{
177
- title: `${pane_open ? `Close` : `Open`} trajectory info`,
177
+ title: pane_open ? `` : `Trajectory info`,
178
178
  ...toggle_props,
179
179
  class: `trajectory-info-toggle ${toggle_props?.class ?? ``}`,
180
180
  }}
@@ -1,7 +1,7 @@
1
1
  import { DraggablePane } from '..';
2
2
  import type { ComponentProps } from 'svelte';
3
3
  import type { TrajectoryType } from './index';
4
- interface Props extends Omit<ComponentProps<typeof DraggablePane>, `children`> {
4
+ type $$ComponentProps = Omit<ComponentProps<typeof DraggablePane>, `children`> & {
5
5
  trajectory: TrajectoryType;
6
6
  current_step_idx: number;
7
7
  current_filename?: string | null;
@@ -11,7 +11,7 @@ interface Props extends Omit<ComponentProps<typeof DraggablePane>, `children`> {
11
11
  pane_open?: boolean;
12
12
  toggle_props?: ComponentProps<typeof DraggablePane>[`toggle_props`];
13
13
  pane_props?: ComponentProps<typeof DraggablePane>[`pane_props`];
14
- }
15
- declare const TrajectoryInfoPane: import("svelte").Component<Props, {}, "pane_open">;
14
+ };
15
+ declare const TrajectoryInfoPane: import("svelte").Component<$$ComponentProps, {}, "pane_open">;
16
16
  type TrajectoryInfoPane = ReturnType<typeof TrajectoryInfoPane>;
17
17
  export default TrajectoryInfoPane;
@@ -33,8 +33,9 @@ export const force_stress_data_extractor = (frame, _trajectory) => {
33
33
  if (frame.metadata.forces && Array.isArray(frame.metadata.forces)) {
34
34
  const forces = frame.metadata.forces;
35
35
  if (forces.length > 0) {
36
- const force_magnitudes = forces.map((force) => Math.sqrt(force[0] ** 2 + force[1] ** 2 + force[2] ** 2));
36
+ const force_magnitudes = forces.map((force) => Math.hypot(...force));
37
37
  data.force_max = Math.max(...force_magnitudes);
38
+ // Calculate RMS (root mean square) of force magnitudes
38
39
  data.force_norm = Math.sqrt(force_magnitudes.reduce((sum, f) => sum + f ** 2, 0) /
39
40
  force_magnitudes.length);
40
41
  }
@@ -1,4 +1,4 @@
1
- import type { AnyStructure } from '..';
1
+ import type { AnyStructure, Trajectory } from '..';
2
2
  import type { ComponentProps } from 'svelte';
3
3
  export { default as Trajectory } from './Trajectory.svelte';
4
4
  export { default as TrajectoryError } from './TrajectoryError.svelte';
@@ -76,7 +76,7 @@ export function get_trajectory_stats(trajectory) {
76
76
  is_indexed: trajectory.is_indexed || false,
77
77
  };
78
78
  if (frames.length > 0) {
79
- const [first_frame, last_frame] = [frames[0], frames.at(-1)];
79
+ const [first_frame, last_frame] = [frames[0], frames.at(-1) ?? frames[0]];
80
80
  const max_sample = 100;
81
81
  const sampled = frames.length <= max_sample ? frames : (() => {
82
82
  const interval = Math.floor(frames.length / max_sample);
@@ -411,9 +411,10 @@ const parse_xyz_trajectory = (content) => {
411
411
  }
412
412
  if (forces.length > 0) {
413
413
  metadata.forces = forces;
414
- const magnitudes = forces.map((f) => Math.sqrt(f[0] ** 2 + f[1] ** 2 + f[2] ** 2));
414
+ const magnitudes = forces.map((force) => Math.hypot(...force));
415
415
  metadata.force_max = Math.max(...magnitudes);
416
- metadata.force_norm = Math.sqrt(magnitudes.reduce((sum, f) => sum + f ** 2, 0) / magnitudes.length);
416
+ // Calculate RMS (root mean square) of force magnitudes
417
+ metadata.force_norm = Math.sqrt(magnitudes.reduce((sum, mag) => sum + mag ** 2, 0) / magnitudes.length);
417
418
  }
418
419
  frames.push(create_trajectory_frame(positions, elements, lattice_matrix, lattice_matrix ? [true, true, true] : undefined, step, metadata));
419
420
  line_idx++;
@@ -896,23 +897,29 @@ export async function parse_trajectory_data(data, filename) {
896
897
  // Calculate force statistics for forces
897
898
  if (key === `forces` && Array.isArray(array_obj.data)) {
898
899
  const forces = array_obj.data;
899
- const force_magnitudes = forces.map((force) => Math.sqrt(force.reduce((sum, f) => sum + f * f, 0)));
900
+ const force_magnitudes = forces.map((force) => Math.hypot(...force));
900
901
  processed_properties.force_max = Math.max(...force_magnitudes);
901
- processed_properties.force_norm = Math.sqrt(force_magnitudes.reduce((sum, f) => sum + f * f, 0));
902
+ processed_properties.force_norm = Math.sqrt(force_magnitudes.reduce((sum, f) => sum + f ** 2, 0) /
903
+ force_magnitudes.length);
902
904
  }
903
905
  // Calculate stress statistics for stress tensor
904
906
  if (key === `stress` && Array.isArray(array_obj.data)) {
905
907
  const stress_tensor = array_obj.data;
906
- // Calculate stress components (diagonal elements represent normal stresses)
907
- const normal_stresses = [
908
- stress_tensor[0][0],
909
- stress_tensor[1][1],
910
- stress_tensor[2][2],
911
- ];
912
- processed_properties.stress_max = Math.max(...normal_stresses.map(Math.abs));
913
- // Calculate hydrostatic pressure (negative of mean normal stress)
914
- processed_properties.pressure =
915
- -(normal_stresses[0] + normal_stresses[1] + normal_stresses[2]) / 3;
908
+ if (!math.is_square_matrix(stress_tensor, 3)) {
909
+ console.warn(`Invalid stress tensor structure in frame ${idx}`);
910
+ }
911
+ else {
912
+ // Calculate stress components (diagonal elements represent normal stresses)
913
+ const normal_stresses = [
914
+ stress_tensor[0][0],
915
+ stress_tensor[1][1],
916
+ stress_tensor[2][2],
917
+ ];
918
+ processed_properties.stress_max = Math.max(...normal_stresses.map(Math.abs));
919
+ // Calculate hydrostatic pressure (negative of mean normal stress)
920
+ processed_properties.pressure =
921
+ -(normal_stresses[0] + normal_stresses[1] + normal_stresses[2]) / 3;
922
+ }
916
923
  }
917
924
  }
918
925
  else {
@@ -0,0 +1 @@
1
+ export declare function merge_nested<T extends Record<string, unknown>>(obj1: T, obj2?: Partial<T>): T;
package/dist/utils.js ADDED
@@ -0,0 +1,17 @@
1
+ // Merge nested objects (1 level deep).
2
+ export function merge_nested(obj1, obj2) {
3
+ const result = { ...obj1, ...(obj2 || {}) };
4
+ for (const key in obj1) {
5
+ if (typeof obj1[key] === `object` &&
6
+ obj1[key] !== null &&
7
+ !Array.isArray(obj1[key])) {
8
+ // Only deep-merge if obj2 value is also a plain object
9
+ if (obj2?.[key] &&
10
+ typeof obj2[key] === `object` &&
11
+ !Array.isArray(obj2[key]))
12
+ result[key] = { ...obj1[key], ...obj2[key] };
13
+ // Otherwise keep the top-level override (already applied above)
14
+ }
15
+ }
16
+ return result;
17
+ }
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">import { plot_colors } from '../colors';
2
2
  import { decompress_file, handle_url_drop } from '../io';
3
+ import { format_value } from '../labels';
3
4
  import { BarPlot } from '../plot';
4
- import { format_value } from '../plot/formatting';
5
5
  import { parse_any_structure } from '../structure/parse';
6
6
  import { compute_xrd_pattern } from './calc-xrd';
7
7
  function format_hkl(hkl, format) {
@@ -20,7 +20,7 @@ function format_hkl(hkl, format) {
20
20
  return `(${hkl.join(`, `)})`;
21
21
  return ``;
22
22
  }
23
- let { patterns, peak_width = 0.5, annotate_peaks = 5, hkl_format = `compact`, show_angles = null, orientation = `vertical`, wavelength = null, x_label = `2θ (degrees)`, y_label = `Intensity (a.u.)`, allow_file_drop = true, on_file_drop, loading = $bindable(false), error_msg = $bindable(undefined), children, ...rest } = $props();
23
+ let { patterns, peak_width = 0.5, annotate_peaks = 5, hkl_format = `compact`, show_angles = null, orientation = `vertical`, wavelength = null, x_axis = $bindable({ label: `2θ (degrees)` }), y_axis = $bindable({ label: `Intensity (a.u.)` }), allow_file_drop = true, on_file_drop, loading = $bindable(false), error_msg = $bindable(undefined), children, ...rest } = $props();
24
24
  let dragover = $state(false);
25
25
  // Patterns created from dropped structures
26
26
  let dropped_entries = $state([]);
@@ -191,9 +191,8 @@ async function handle_file_drop(event) {
191
191
  ? hkls.map((h) => format_hkl(h, hkl_format)).join(`, `)
192
192
  : ``}
193
193
  {@const d_text = d != null ? `${format_value(d, `.3f`)} Å` : ``}
194
- {@const series_label = info.metadata?.label ? `${String(info.metadata.label)} — ` : ``}
195
- {@html series_label}2θ: {angle_text}
196
- <br />
194
+ {@html info.metadata?.label ?? ``}<br />
195
+ 2θ: {angle_text}<br />
197
196
  Intensity: {intensity_text}
198
197
  {#if hkl_text}<br />hkl: {hkl_text}{/if}
199
198
  {#if d_text}<br />d: {d_text}{/if}
@@ -203,13 +202,17 @@ async function handle_file_drop(event) {
203
202
  {...rest}
204
203
  series={bar_series}
205
204
  bind:orientation
206
- x_label={orientation === `horizontal` ? y_label : x_label}
207
- y_label={orientation === `horizontal` ? x_label : y_label}
208
- x_label_shift={{ y: 20 }}
209
- y_label_shift={{ x: 2 }}
205
+ x_axis={{
206
+ label_shift: { y: 20 },
207
+ range: orientation === `horizontal` ? intensity_range : angle_range,
208
+ ...(orientation === `horizontal` ? y_axis : x_axis),
209
+ }}
210
+ y_axis={{
211
+ label_shift: { x: 2 },
212
+ range: orientation === `horizontal` ? angle_range : intensity_range,
213
+ ...(orientation === `horizontal` ? x_axis : y_axis),
214
+ }}
210
215
  {tooltip}
211
- x_range={orientation === `horizontal` ? intensity_range : angle_range}
212
- y_range={orientation === `horizontal` ? angle_range : intensity_range}
213
216
  ondrop={handle_file_drop}
214
217
  ondragover={(event) => {
215
218
  event.preventDefault()
@@ -1,7 +1,8 @@
1
+ import type { AxisConfig } from '../plot';
1
2
  import { BarPlot } from '../plot';
2
3
  import type { ComponentProps, Snippet } from 'svelte';
3
4
  import type { HklFormat, PatternEntry, XrdPattern } from './index';
4
- interface Props extends ComponentProps<typeof BarPlot> {
5
+ type $$ComponentProps = ComponentProps<typeof BarPlot> & {
5
6
  patterns: XrdPattern | Record<string, XrdPattern | {
6
7
  pattern: XrdPattern;
7
8
  color?: string;
@@ -11,14 +12,14 @@ interface Props extends ComponentProps<typeof BarPlot> {
11
12
  hkl_format?: HklFormat;
12
13
  show_angles?: boolean | null;
13
14
  wavelength?: number | null;
14
- x_label?: string;
15
- y_label?: string;
15
+ x_axis?: AxisConfig;
16
+ y_axis?: AxisConfig;
16
17
  allow_file_drop?: boolean;
17
18
  on_file_drop?: (content: string | ArrayBuffer, filename: string) => void;
18
19
  loading?: boolean;
19
20
  error_msg?: string;
20
21
  children?: Snippet<[]>;
21
- }
22
- declare const XrdPlot: import("svelte").Component<Props, {}, "loading" | "error_msg">;
22
+ };
23
+ declare const XrdPlot: import("svelte").Component<$$ComponentProps, {}, "loading" | "y_axis" | "x_axis" | "error_msg">;
23
24
  type XrdPlot = ReturnType<typeof XrdPlot>;
24
25
  export default XrdPlot;
package/package.json CHANGED
@@ -5,15 +5,15 @@
5
5
  "homepage": "https://janosh.github.io/matterviz",
6
6
  "repository": "https://github.com/janosh/matterviz",
7
7
  "license": "MIT",
8
- "version": "0.1.12",
8
+ "version": "0.1.13",
9
9
  "type": "module",
10
10
  "svelte": "./dist/index.js",
11
11
  "bugs": "https://github.com/janosh/matterviz/issues",
12
12
  "dependencies": {
13
13
  "@spglib/moyo-wasm": "^0.6.0",
14
- "@sveltejs/kit": "^2.43.8",
15
- "@threlte/core": "^8.1.5",
16
- "@threlte/extras": "^9.5.4",
14
+ "@sveltejs/kit": "^2.47.0",
15
+ "@threlte/core": "^8.1.7",
16
+ "@threlte/extras": "^9.5.5",
17
17
  "@types/d3-force": "^3.0.10",
18
18
  "@types/d3-hierarchy": "^3.1.7",
19
19
  "@types/js-yaml": "^4.0.9",
@@ -27,15 +27,15 @@
27
27
  "d3-scale": "^4.0.2",
28
28
  "d3-scale-chromatic": "^3.1.0",
29
29
  "d3-shape": "^3.2.0",
30
- "h5wasm": "^0.8.6",
30
+ "h5wasm": "^0.8.7",
31
31
  "highlight.js": "^11.11.1",
32
32
  "js-yaml": "^4.1.0",
33
- "svelte": "^5.39.8",
33
+ "svelte": "^5.40.1",
34
34
  "svelte-multiselect": "11.2.4",
35
35
  "three": "^0.180.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@playwright/test": "^1.55.1",
38
+ "@playwright/test": "^1.56.0",
39
39
  "@rollup/plugin-yaml": "^4.1.2",
40
40
  "@stylistic/eslint-plugin": "^5.4.0",
41
41
  "@sveltejs/adapter-static": "3.0.10",
@@ -54,7 +54,7 @@
54
54
  "d3-time-format": "^4.1.0",
55
55
  "eslint": "^9.37.0",
56
56
  "eslint-plugin-svelte": "^3.12.4",
57
- "happy-dom": "^19.0.2",
57
+ "happy-dom": "^20.0.2",
58
58
  "hastscript": "^9.0.1",
59
59
  "mdsvex": "^0.12.6",
60
60
  "mdsvexamples": "^0.5.0",
@@ -62,12 +62,12 @@
62
62
  "rehype-katex": "^7.0.1",
63
63
  "rehype-slug": "^6.0.0",
64
64
  "remark-math": "3.0.1",
65
- "svelte-check": "^4.3.2",
65
+ "svelte-check": "^4.3.3",
66
66
  "svelte-preprocess": "^6.0.3",
67
- "svelte2tsx": "^0.7.44",
67
+ "svelte2tsx": "^0.7.45",
68
68
  "typescript": "5.9.3",
69
- "typescript-eslint": "^8.45.0",
70
- "vite": "^7.1.9",
69
+ "typescript-eslint": "^8.46.1",
70
+ "vite": "^7.1.10",
71
71
  "vitest": "^3.2.4"
72
72
  },
73
73
  "keywords": [
@@ -95,6 +95,10 @@
95
95
  "types": "./dist/app.css.d.ts",
96
96
  "default": "./dist/app.css"
97
97
  },
98
+ "./bands": {
99
+ "types": "./dist/bands/index.d.ts",
100
+ "default": "./dist/bands/index.js"
101
+ },
98
102
  "./composition": {
99
103
  "types": "./dist/composition.d.ts",
100
104
  "default": "./dist/composition.js"
@@ -147,6 +151,10 @@
147
151
  "types": "./dist/trajectory/index.d.ts",
148
152
  "default": "./dist/trajectory/index.js"
149
153
  },
154
+ "./rdf": {
155
+ "types": "./dist/rdf/index.d.ts",
156
+ "default": "./dist/rdf/index.js"
157
+ },
150
158
  "./xrd": {
151
159
  "types": "./dist/xrd/index.d.ts",
152
160
  "default": "./dist/xrd/index.js"
package/readme.md CHANGED
@@ -116,7 +116,7 @@ npm install --dev matterviz
116
116
  - Thanks to [Images of Elements](https://images-of-elements.com) for providing photos of elemental crystals and glowing excited gases.
117
117
  - Thanks to [@kadinzhang](https://github.com/kadinzhang) and their [Periodicity project](https://ptable.netlify.app) [[code](https://github.com/kadinzhang/Periodicity)] for the idea to display animated Bohr model atoms and inset a scatter plot into the periodic table to visualize the periodic nature of elemental properties.
118
118
  - Big thanks to all sources of element images. See [`fetch-elem-images.ts`](https://github.com/janosh/matterviz/blob/-/src/fetch-elem-images.ts) and [`static/elements`](https://github.com/janosh/matterviz/tree/main/static/elements).
119
- - Thanks to [@ixxie](https://github.com/ixxie) ([shenhav.fyi](https://shenhav.fyi)) for a lot of great suggestions, UX ideas, helping me learn [`threlte`](https://threlte.xyz) and contributing the [`Bond.svelte`](https://github.com/janosh/matterviz/blob/-/src/lib/structure/Bond.svelte) component.
119
+ - Thanks to [@ixxie](https://github.com/ixxie) ([shenhav.fyi](https://shenhav.fyi)) for great suggestions.
120
120
 
121
121
  This project would not have been possible as a one-person side project without many fine open-source projects. 🙏 To name just a few:
122
122