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
@@ -5,81 +5,33 @@ import { format_fractional, format_num } from '../labels';
5
5
  import { ColorBar } from '../plot';
6
6
  import { SvelteMap } from 'svelte/reactivity';
7
7
  import { get_ternary_3d_coordinates, get_triangle_centroid, get_triangle_edges, get_triangle_vertical_edges, TRIANGLE_VERTICES, } from './barycentric-coords';
8
- import { build_entry_tooltip_text, compute_max_energy_threshold, default_controls, find_pd_entry_at_mouse, get_energy_color_scale, get_point_color_for_entry, parse_pd_entries_from_drop, PD_STYLE, } from './helpers';
8
+ import * as helpers from './helpers';
9
+ import { default_controls, default_pd_config, PD_STYLE } from './index';
9
10
  import PhaseDiagramControls from './PhaseDiagramControls.svelte';
10
11
  import PhaseDiagramInfoPane from './PhaseDiagramInfoPane.svelte';
11
12
  import StructurePopup from './StructurePopup.svelte';
12
- import { build_lower_hull_model, compute_e_above_hull_for_points, compute_e_form_per_atom, compute_lower_hull_triangles, find_lowest_energy_unary_refs, get_phase_diagram_stats, process_pd_entries, } from './thermodynamics';
13
- let { entries, controls = {}, config = {}, on_point_click, on_point_hover, fullscreen = $bindable(false), enable_fullscreen = true, enable_info_pane = true, wrapper = $bindable(undefined), label_threshold = 50, show_stable = $bindable(true), show_unstable = $bindable(true), show_hull_faces = $bindable(true), hull_face_opacity = $bindable(0.3), color_mode = $bindable(`energy`), color_scale = $bindable(`interpolateViridis`), info_pane_open = $bindable(false), legend_pane_open = $bindable(false), energy_threshold = $bindable(0.5), // eV/atom above hull for showing entries
14
- on_file_drop, enable_structure_preview = true, energy_source_mode = $bindable(`precomputed`), phase_stats = $bindable(null), stable_entries_out = $bindable([]), unstable_entries_out = $bindable([]), ...rest } = $props();
15
- const merged_controls = $derived({
16
- ...default_controls,
17
- ...controls,
18
- });
19
- const default_config = {
20
- margin: { top: 40, right: 40, bottom: 60, left: 60 },
21
- width: 600,
22
- height: 600,
23
- unstable_threshold: 0.2,
24
- show_labels: true,
25
- show_hull: true,
26
- point_size: 8,
27
- line_width: 2,
28
- font_size: 12,
29
- colors: {
30
- stable: `#0072B2`,
31
- unstable: `#E69F00`,
32
- hull_line: `var(--accent-color, #1976D2)`,
33
- background: `transparent`,
34
- text: `var(--text-color, #212121)`,
35
- edge: `var(--text-color, #212121)`,
36
- tooltip_bg: `var(--tooltip-bg, rgba(0, 0, 0, 0.85))`,
37
- tooltip_text: `var(--tooltip-text, white)`,
38
- annotation: `var(--text-color, #212121)`,
39
- },
40
- };
13
+ import * as thermo from './thermodynamics';
14
+ let { entries, controls = {}, config = {}, on_point_click, on_point_hover, fullscreen = $bindable(false), enable_fullscreen = true, enable_info_pane = true, wrapper = $bindable(undefined), label_threshold = 50, show_stable = $bindable(true), show_unstable = $bindable(true), show_hull_faces = $bindable(true), hull_face_opacity = $bindable(0.3), color_mode = $bindable(`energy`), color_scale = $bindable(`interpolateViridis`), info_pane_open = $bindable(false), legend_pane_open = $bindable(false), max_hull_dist_show_phases = $bindable(0.5), // eV/atom above hull for showing entries
15
+ on_file_drop, enable_structure_preview = true, energy_source_mode = $bindable(`precomputed`), phase_stats = $bindable(null), stable_entries = $bindable([]), unstable_entries = $bindable([]), ...rest } = $props();
16
+ const merged_controls = $derived({ ...default_controls, ...controls });
41
17
  const merged_config = $derived({
42
- ...default_config,
18
+ ...default_pd_config,
43
19
  ...config,
44
- margin: { ...default_config.margin, ...(config.margin || {}) },
45
- });
46
- // Decide which energy source to use per entry
47
- const has_precomputed_e_form = $derived(entries.length > 0 && entries.every((e) => typeof e.e_form_per_atom === `number`));
48
- const has_precomputed_hull = $derived(entries.length > 0 && entries.every((e) => typeof e.e_above_hull === `number`));
49
- // Build unary references once from entries; inexpensive and used for capability checks
50
- const unary_refs = $derived.by(() => find_lowest_energy_unary_refs(entries));
51
- const can_compute_e_form = $derived.by(() => {
52
- const elements_in_entries = Array.from(new Set(entries.flatMap((e) => Object.keys(e.composition))));
53
- return elements_in_entries.every((el) => Boolean(unary_refs[el]));
54
- });
55
- const can_compute_hull = $derived(can_compute_e_form);
56
- // Resolve mode to avoid inconsistent states:
57
- // - If full precomputed available, honor user toggle
58
- // - Else if we can compute both, use on-the-fly automatically
59
- // - Else fall back to precomputed (best-effort)
60
- const energy_mode = $derived((has_precomputed_e_form && has_precomputed_hull)
61
- ? energy_source_mode
62
- : ((can_compute_e_form && can_compute_hull) ? `on-the-fly` : `precomputed`));
63
- const effective_entries = $derived.by(() => {
64
- if (energy_mode === `precomputed`)
65
- return entries;
66
- return entries.map((entry) => {
67
- const e_form = compute_e_form_per_atom(entry, unary_refs);
68
- if (e_form == null)
69
- return entry;
70
- return { ...entry, e_form_per_atom: e_form };
71
- });
20
+ colors: { ...default_pd_config.colors, ...(config.colors || {}) },
21
+ margin: { t: 40, r: 40, b: 60, l: 60, ...(config.margin || {}) },
72
22
  });
23
+ let { // Compute energy mode information
24
+ has_precomputed_e_form, has_precomputed_hull, can_compute_e_form, can_compute_hull, energy_mode, unary_refs, } = $derived(helpers.compute_energy_mode_info(entries, thermo.find_lowest_energy_unary_refs, energy_source_mode));
25
+ const effective_entries = $derived(helpers.get_effective_entries(entries, energy_mode, unary_refs, thermo.compute_e_form_per_atom));
73
26
  // Process phase diagram data with unified PhaseEntry interface using effective entries
74
27
  const processed_entries = $derived(effective_entries);
75
- const pd_data = $derived(process_pd_entries(processed_entries));
28
+ const pd_data = $derived(thermo.process_pd_entries(processed_entries));
76
29
  const elements = $derived.by(() => {
77
- const all_elements = pd_data.elements;
78
- if (all_elements.length > 3) {
79
- console.error(`PhaseDiagram3D: Dataset contains ${all_elements.length} elements, but ternary diagrams require exactly 3. Found: [${all_elements.join(`, `)}]`);
30
+ if (pd_data.elements.length > 3) {
31
+ console.error(`PhaseDiagram3D: Dataset contains ${pd_data.elements.length} elements, but ternary diagrams require exactly 3. Found: [${pd_data.elements.join(`, `)}]`);
80
32
  return [];
81
33
  }
82
- return all_elements;
34
+ return pd_data.elements;
83
35
  });
84
36
  // 1) Raw 3D coordinates (formation-energy z), independent of hull state
85
37
  const coords_entries = $derived.by(() => {
@@ -103,12 +55,12 @@ const plot_entries = $derived.by(() => {
103
55
  const enriched = (() => {
104
56
  if (energy_mode === `on-the-fly`) {
105
57
  const pts = coords_entries.map((e) => ({ x: e.x, y: e.y, z: e.z }));
106
- const e_hulls = compute_e_above_hull_for_points(pts, hull_model);
58
+ const e_hulls = thermo.compute_e_above_hull_for_points(pts, hull_model);
107
59
  return coords_entries.map((e, idx) => ({ ...e, e_above_hull: e_hulls[idx] }));
108
60
  }
109
61
  return coords_entries;
110
62
  })();
111
- const energy_filtered = enriched.filter((entry) => (entry.e_above_hull ?? 0) <= energy_threshold);
63
+ const energy_filtered = enriched.filter((entry) => (entry.e_above_hull ?? 0) <= max_hull_dist_show_phases);
112
64
  return energy_filtered.map((entry) => {
113
65
  const is_stable = entry.is_stable || entry.e_above_hull === 0;
114
66
  return {
@@ -117,16 +69,18 @@ const plot_entries = $derived.by(() => {
117
69
  };
118
70
  });
119
71
  });
120
- const stable_entries = $derived(plot_entries.filter((entry) => entry.is_stable || entry.e_above_hull === 0));
121
- const unstable_entries = $derived(plot_entries.filter((entry) => typeof entry.e_above_hull === `number` && entry.e_above_hull > 0 &&
122
- !entry.is_stable));
72
+ $effect(() => {
73
+ stable_entries = plot_entries.filter((entry) => entry.is_stable || entry.e_above_hull === 0);
74
+ unstable_entries = plot_entries.filter((entry) => typeof entry.e_above_hull === `number` && entry.e_above_hull > 0 &&
75
+ !entry.is_stable);
76
+ });
123
77
  const hull_faces = $derived.by(() => {
124
78
  if (coords_entries.length === 0) {
125
79
  return [];
126
80
  }
127
81
  const points = coords_entries.map((e) => ({ x: e.x, y: e.y, z: e.z }));
128
82
  try {
129
- return compute_lower_hull_triangles(points);
83
+ return thermo.compute_lower_hull_triangles(points);
130
84
  }
131
85
  catch (error) {
132
86
  console.error(`Error computing convex hull:`, error);
@@ -134,7 +88,7 @@ const hull_faces = $derived.by(() => {
134
88
  }
135
89
  });
136
90
  // Cached hull model for e_above_hull queries; recompute only when faces change
137
- let hull_model = $derived.by(() => build_lower_hull_model(hull_faces));
91
+ let hull_model = $derived.by(() => thermo.build_lower_hull_model(hull_faces));
138
92
  // Total counts based on hull-enriched entries
139
93
  const total_unstable_count = $derived(plot_entries.filter((entry) => typeof entry.e_above_hull === `number` && entry.e_above_hull > 0 &&
140
94
  !entry.is_stable).length);
@@ -203,32 +157,18 @@ $effect(() => {
203
157
  $effect(() => {
204
158
  // deno-fmt-ignore
205
159
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
206
- [show_stable, show_unstable, show_hull_faces, color_mode, color_scale, energy_threshold, show_stable_labels, show_unstable_labels, label_energy_threshold, camera.elevation, camera.azimuth, camera.zoom, camera.center_x, camera.center_y, plot_entries, hull_face_color, hull_face_opacity];
160
+ [show_stable, show_unstable, show_hull_faces, color_mode, color_scale, max_hull_dist_show_phases, show_stable_labels, show_unstable_labels, max_hull_dist_show_labels, camera.elevation, camera.azimuth, camera.zoom, camera.center_x, camera.center_y, plot_entries, hull_face_color, hull_face_opacity];
207
161
  render_once();
208
162
  });
209
163
  // Label controls with smart defaults based on entry count
210
164
  let show_stable_labels = $state(true);
211
165
  let show_unstable_labels = $state(false);
212
- let label_energy_threshold = $state(0.1); // eV/atom above hull for showing labels
213
- // Smart label defaults - hide labels if too many entries
214
- $effect(() => {
215
- const total_entries = processed_entries.length;
216
- if (total_entries > label_threshold) {
217
- show_stable_labels = false;
218
- show_unstable_labels = false;
219
- }
220
- else {
221
- // For smaller datasets, show stable labels by default
222
- show_stable_labels = true;
223
- show_unstable_labels = false;
224
- }
225
- });
166
+ let max_hull_dist_show_labels = $state(0.1); // eV/atom above hull for showing labels
226
167
  // Function to extract structure data from a phase diagram entry
227
168
  function extract_structure_from_entry(entry) {
228
169
  const original_entry = entries.find((orig_entry) => orig_entry.entry_id === entry.entry_id);
229
170
  return original_entry?.structure || null;
230
171
  }
231
- const get_tooltip_text = (entry) => build_entry_tooltip_text(entry);
232
172
  const reset_camera = () => Object.assign(camera, camera_default);
233
173
  function reset_all() {
234
174
  reset_camera();
@@ -241,8 +181,8 @@ function reset_all() {
241
181
  show_unstable = PD_DEFAULTS.ternary.show_unstable;
242
182
  show_stable_labels = PD_DEFAULTS.ternary.show_stable_labels;
243
183
  show_unstable_labels = PD_DEFAULTS.ternary.show_unstable_labels;
244
- label_energy_threshold = PD_DEFAULTS.ternary.label_energy_threshold;
245
- energy_threshold = PD_DEFAULTS.ternary.energy_threshold;
184
+ max_hull_dist_show_labels = PD_DEFAULTS.ternary.max_hull_dist_show_labels;
185
+ max_hull_dist_show_phases = PD_DEFAULTS.ternary.max_hull_dist_show_phases;
246
186
  show_hull_faces = PD_DEFAULTS.ternary.show_hull_faces;
247
187
  hull_face_color = PD_DEFAULTS.ternary.hull_face_color;
248
188
  hull_face_opacity = PD_DEFAULTS.ternary.hull_face_opacity;
@@ -262,7 +202,7 @@ const handle_keydown = (event) => {
262
202
  };
263
203
  async function handle_file_drop(event) {
264
204
  drag_over = false;
265
- const data = await parse_pd_entries_from_drop(event);
205
+ const data = await helpers.parse_pd_entries_from_drop(event);
266
206
  if (data)
267
207
  on_file_drop?.(data);
268
208
  }
@@ -272,20 +212,13 @@ async function copy_to_clipboard(text, position) {
272
212
  copy_feedback_visible = true;
273
213
  setTimeout(() => copy_feedback_visible = false, 1500);
274
214
  }
275
- function get_point_color(entry) {
276
- return get_point_color_for_entry(entry, color_mode, merged_config.colors, energy_color_scale);
277
- }
215
+ const get_point_color = (entry) => helpers.get_point_color_for_entry(entry, color_mode, merged_config.colors, energy_color_scale);
278
216
  // Cache energy color scale per frame/setting
279
- const energy_color_scale = $derived.by(() => get_energy_color_scale(color_mode, color_scale, plot_entries));
280
- const max_energy_threshold = $derived(compute_max_energy_threshold(processed_entries));
217
+ const energy_color_scale = $derived.by(() => helpers.get_energy_color_scale(color_mode, color_scale, plot_entries));
218
+ const max_hull_dist_in_data = $derived(helpers.calc_max_hull_dist_in_data(processed_entries));
281
219
  // Phase diagram statistics - compute internally and expose via bindable prop
282
220
  $effect(() => {
283
- phase_stats = get_phase_diagram_stats(processed_entries, elements, 3);
284
- });
285
- // Sync stable and unstable entries to bindable props
286
- $effect(() => {
287
- stable_entries_out = stable_entries;
288
- unstable_entries_out = unstable_entries;
221
+ phase_stats = thermo.get_phase_diagram_stats(processed_entries, elements, 3);
289
222
  });
290
223
  // 3D to 2D projection for ternary diagrams
291
224
  function project_3d_point(x, y, z) {
@@ -294,7 +227,7 @@ function project_3d_point(x, y, z) {
294
227
  // Center coordinates around the volumetric center of the phase diagram
295
228
  const triangle_centroid = get_triangle_centroid();
296
229
  // Calculate the energy center (middle of formation energy range)
297
- const formation_energies = plot_entries.map((e) => e.formation_energy);
230
+ const formation_energies = plot_entries.map((e) => e.e_form);
298
231
  const e_form_min = Math.min(0, ...formation_energies);
299
232
  const e_form_max = Math.max(0, ...formation_energies);
300
233
  const energy_center = (e_form_min + e_form_max) / 2;
@@ -349,7 +282,7 @@ function draw_triangle_structure() {
349
282
  if (!ctx)
350
283
  return;
351
284
  // Get formation energy range for vertical edges
352
- const formation_energies = plot_entries.map((e) => e.formation_energy);
285
+ const formation_energies = plot_entries.map((e) => e.e_form);
353
286
  const e_form_min = Math.min(0, ...formation_energies); // Include 0 for elemental references
354
287
  const e_form_max = Math.max(0, ...formation_energies); // Include 0 for elemental references
355
288
  // Draw base triangle edges (top triangle at formation energy = 0)
@@ -418,7 +351,7 @@ function draw_convex_hull_faces() {
418
351
  if (!ctx || !show_hull_faces || hull_faces.length === 0)
419
352
  return;
420
353
  // Normalize alpha by formation energy: 0 eV -> 0 alpha, min E_form -> hull_face_opacity
421
- const formation_energies = plot_entries.map((e) => e.formation_energy);
354
+ const formation_energies = plot_entries.map((e) => e.e_form);
422
355
  const min_fe = Math.min(0, ...formation_energies);
423
356
  const norm_alpha = (z) => {
424
357
  const t = Math.max(0, Math.min(1, (0 - z) / Math.max(1e-6, 0 - min_fe)));
@@ -504,7 +437,7 @@ function draw_convex_hull_faces() {
504
437
  }
505
438
  // Formation energy color bar helpers
506
439
  const e_form_range = $derived.by(() => {
507
- const energies = plot_entries.map((e) => e.formation_energy);
440
+ const energies = plot_entries.map((e) => e.e_form);
508
441
  const min_fe = energies.length ? Math.min(0, ...energies) : -1;
509
442
  return [min_fe, 0];
510
443
  });
@@ -604,36 +537,22 @@ function draw_hull_labels() {
604
537
  const can_label_stable = is_stable_point && show_stable_labels;
605
538
  const can_label_unstable = !is_stable_point && show_unstable_labels &&
606
539
  (typeof entry.e_above_hull === `number` &&
607
- entry.e_above_hull <= label_energy_threshold);
540
+ entry.e_above_hull <= max_hull_dist_show_labels);
608
541
  if (!(can_label_stable || can_label_unstable))
609
542
  continue;
610
543
  const projected = project_3d_point(entry.x, entry.y, entry.z);
611
- // Generate simplified formula from composition
544
+ // Generate label from composition
612
545
  let formula = entry.reduced_formula || entry.name || ``;
613
546
  if (!formula) {
614
- // Generate reduced formula from composition
615
- const composition_elements = Object.entries(entry.composition)
547
+ // Format composition as element fractions
548
+ formula = Object.entries(entry.composition)
616
549
  .filter(([, amt]) => amt > 0)
617
550
  .sort(([el1], [el2]) => elements.indexOf(el1) -
618
- elements.indexOf(el2));
619
- // Find GCD to reduce the formula
620
- const amounts = composition_elements.map(([, amt]) => Math.round(amt * 1000) / 1000);
621
- const gcd_val = amounts.reduce((a, b) => {
622
- // Simple GCD for fractional numbers (scaled by 1000)
623
- const scale = 1000;
624
- let a_int = Math.round(a * scale);
625
- let b_int = Math.round(b * scale);
626
- while (b_int !== 0) {
627
- const temp = b_int;
628
- b_int = a_int % b_int;
629
- a_int = temp;
630
- }
631
- return a_int / scale;
632
- });
633
- formula = composition_elements
551
+ elements.indexOf(el2))
634
552
  .map(([element, amount]) => {
635
- const reduced_amount = Math.round((amount / gcd_val) * 100) / 100;
636
- return reduced_amount === 1 ? element : `${element}${reduced_amount}`;
553
+ if (Math.abs(amount - 1) < 1e-6)
554
+ return element;
555
+ return `${element}${format_num(amount, `.2~`)}`;
637
556
  })
638
557
  .join(``);
639
558
  }
@@ -710,7 +629,7 @@ const handle_hover = (event) => {
710
629
  : null;
711
630
  on_point_hover?.(hover_data);
712
631
  };
713
- const find_entry_at_mouse = (event) => find_pd_entry_at_mouse(canvas, event, plot_entries, (x, y, z) => {
632
+ const find_entry_at_mouse = (event) => helpers.find_pd_entry_at_mouse(canvas, event, plot_entries, (x, y, z) => {
714
633
  const pt = project_3d_point(x, y, z);
715
634
  return { x: pt.x, y: pt.y };
716
635
  });
@@ -729,7 +648,7 @@ const handle_click = (event) => {
729
648
  if (structure) {
730
649
  selected_structure = structure;
731
650
  selected_entry = entry;
732
- calculate_modal_side();
651
+ modal_place_right = helpers.calculate_modal_side(wrapper);
733
652
  modal_open = true;
734
653
  }
735
654
  }
@@ -737,16 +656,6 @@ const handle_click = (event) => {
737
656
  else if (modal_open)
738
657
  close_structure_popup();
739
658
  };
740
- // Decide side based on available viewport room; modal is absolutely positioned
741
- function calculate_modal_side() {
742
- if (!wrapper)
743
- return;
744
- const rect = wrapper.getBoundingClientRect();
745
- const viewport_width = globalThis.innerWidth;
746
- const space_on_right = viewport_width - rect.right;
747
- const space_on_left = rect.left;
748
- modal_place_right = space_on_right >= space_on_left;
749
- }
750
659
  function close_structure_popup() {
751
660
  modal_open = false;
752
661
  selected_structure = null;
@@ -755,7 +664,7 @@ function close_structure_popup() {
755
664
  const handle_double_click = (event) => {
756
665
  const entry = find_entry_at_mouse(event);
757
666
  if (entry) {
758
- copy_to_clipboard(get_tooltip_text(entry), {
667
+ copy_to_clipboard(helpers.build_entry_tooltip_text(entry), {
759
668
  x: event.clientX,
760
669
  y: event.clientY,
761
670
  });
@@ -819,20 +728,14 @@ $effect(() => {
819
728
  // Fullscreen handling with camera reset on transitions
820
729
  let was_fullscreen = $state(fullscreen);
821
730
  $effect(() => {
822
- if (typeof window === `undefined`)
823
- return;
824
- if (fullscreen && !document.fullscreenElement && wrapper?.isConnected) {
825
- wrapper.requestFullscreen().catch(console.error);
826
- }
827
- else if (!fullscreen && document.fullscreenElement) {
828
- document.exitFullscreen();
829
- }
830
- // Reset camera only on fullscreen transitions
831
- if (fullscreen !== was_fullscreen) {
832
- camera.center_x = 0;
833
- camera.center_y = -50;
834
- was_fullscreen = fullscreen;
835
- }
731
+ helpers.setup_fullscreen_effect(fullscreen, wrapper, (entering_fullscreen) => {
732
+ // Reset camera only on fullscreen transitions
733
+ if (entering_fullscreen !== was_fullscreen) {
734
+ camera.center_x = 0;
735
+ camera.center_y = -50;
736
+ was_fullscreen = entering_fullscreen;
737
+ }
738
+ });
836
739
  });
837
740
  let style = $derived(`--pd-stable-color:${merged_config.colors?.stable || `#0072B2`};
838
741
  --pd-unstable-color:${merged_config.colors?.unstable || `#E69F00`};
@@ -882,13 +785,11 @@ let style = $derived(`--pd-stable-color:${merged_config.colors?.stable || `#0072
882
785
 
883
786
  <!-- Formation Energy Color Bar (bottom-left corner) -->
884
787
  {#if color_mode === `energy` && plot_entries.length > 0}
885
- {@const formation_energies = plot_entries
788
+ {@const hull_distances = plot_entries
886
789
  .map((e) => e.e_above_hull)
887
790
  .filter((v): v is number => typeof v === `number`)}
888
- {@const min_energy = formation_energies.length > 0 ? Math.min(...formation_energies) : 0}
889
- {@const max_energy = formation_energies.length > 0
890
- ? Math.max(...formation_energies, 0.1)
891
- : 0.1}
791
+ {@const min_energy = hull_distances.length > 0 ? Math.min(...hull_distances) : 0}
792
+ {@const max_energy = hull_distances.length > 0 ? Math.max(...hull_distances, 0.1) : 0.1}
892
793
  <ColorBar
893
794
  title="Energy above hull (eV/atom)"
894
795
  range={[min_energy, max_energy]}
@@ -930,12 +831,10 @@ let style = $derived(`--pd-stable-color:${merged_config.colors?.stable || `#0072
930
831
  {phase_stats}
931
832
  {stable_entries}
932
833
  {unstable_entries}
933
- {energy_threshold}
934
- {label_energy_threshold}
834
+ {max_hull_dist_show_phases}
835
+ {max_hull_dist_show_labels}
935
836
  {label_threshold}
936
- toggle_props={{
937
- class: `info-btn`,
938
- }}
837
+ toggle_props={{ class: `info-btn` }}
939
838
  />
940
839
  {/if}
941
840
 
@@ -959,17 +858,15 @@ let style = $derived(`--pd-stable-color:${merged_config.colors?.stable || `#0072
959
858
  bind:show_unstable
960
859
  bind:show_stable_labels
961
860
  bind:show_unstable_labels
962
- bind:energy_threshold
963
- bind:label_energy_threshold
964
- {max_energy_threshold}
861
+ bind:max_hull_dist_show_phases
862
+ bind:max_hull_dist_show_labels
863
+ {max_hull_dist_in_data}
965
864
  {stable_entries}
966
865
  {unstable_entries}
967
866
  {total_unstable_count}
968
867
  {camera}
969
868
  {merged_controls}
970
- toggle_props={{
971
- class: `legend-controls-btn`,
972
- }}
869
+ toggle_props={{ class: `legend-controls-btn` }}
973
870
  {show_hull_faces}
974
871
  on_hull_faces_change={(value) => show_hull_faces = value}
975
872
  {hull_face_color}
@@ -1,33 +1,9 @@
1
- import type { D3InterpolateName } from '../colors';
2
- import type { HTMLAttributes } from 'svelte/elements';
3
- import type { HoverData3D, PDControlsType, PhaseDiagramConfig, PhaseEntry, PhaseStats, TernaryPlotEntry } from './types';
4
- interface Props extends HTMLAttributes<HTMLDivElement> {
5
- entries: PhaseEntry[];
6
- controls?: Partial<PDControlsType>;
7
- config?: Partial<PhaseDiagramConfig>;
8
- on_point_click?: (entry: TernaryPlotEntry) => void;
9
- on_point_hover?: (data: HoverData3D<TernaryPlotEntry> | null) => void;
10
- fullscreen?: boolean;
11
- enable_fullscreen?: boolean;
12
- enable_info_pane?: boolean;
13
- wrapper?: HTMLDivElement;
14
- label_threshold?: number;
15
- show_stable?: boolean;
16
- show_unstable?: boolean;
17
- show_hull_faces?: boolean;
18
- hull_face_opacity?: number;
19
- color_mode?: `stability` | `energy`;
20
- color_scale?: D3InterpolateName;
21
- info_pane_open?: boolean;
22
- legend_pane_open?: boolean;
23
- energy_threshold?: number;
24
- on_file_drop?: (entries: PhaseEntry[]) => void;
25
- enable_structure_preview?: boolean;
26
- energy_source_mode?: `precomputed` | `on-the-fly`;
27
- phase_stats?: PhaseStats | null;
28
- stable_entries_out?: TernaryPlotEntry[];
29
- unstable_entries_out?: TernaryPlotEntry[];
30
- }
31
- declare const PhaseDiagram3D: import("svelte").Component<Props, {}, "color_mode" | "color_scale" | "show_stable" | "show_unstable" | "energy_threshold" | "fullscreen" | "info_pane_open" | "legend_pane_open" | "show_hull_faces" | "hull_face_opacity" | "wrapper" | "energy_source_mode" | "phase_stats" | "stable_entries_out" | "unstable_entries_out">;
1
+ import type { BasePhaseDiagramProps, Hull3DProps } from './index';
2
+ import type { TernaryPlotEntry } from './types';
3
+ type $$ComponentProps = BasePhaseDiagramProps<TernaryPlotEntry> & Hull3DProps & {
4
+ stable_entries?: TernaryPlotEntry[];
5
+ unstable_entries?: TernaryPlotEntry[];
6
+ };
7
+ declare const PhaseDiagram3D: import("svelte").Component<$$ComponentProps, {}, "show_hull_faces" | "hull_face_opacity" | "color_mode" | "color_scale" | "show_stable" | "show_unstable" | "max_hull_dist_show_phases" | "fullscreen" | "info_pane_open" | "legend_pane_open" | "wrapper" | "energy_source_mode" | "stable_entries" | "unstable_entries" | "phase_stats">;
32
8
  type PhaseDiagram3D = ReturnType<typeof PhaseDiagram3D>;
33
9
  export default PhaseDiagram3D;