matterviz 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/dist/FilePicker.svelte +37 -20
  2. package/dist/Icon.svelte +2 -2
  3. package/dist/app.css +29 -0
  4. package/dist/brillouin/BrillouinZone.svelte +19 -61
  5. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  6. package/dist/brillouin/BrillouinZoneExportPane.svelte +12 -20
  7. package/dist/brillouin/BrillouinZoneScene.svelte +2 -2
  8. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  9. package/dist/chempot-diagram/ChemPotDiagram.svelte +192 -0
  10. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  11. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +677 -0
  12. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  13. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2688 -0
  14. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  15. package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -0
  16. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  17. package/dist/chempot-diagram/color.d.ts +10 -0
  18. package/dist/chempot-diagram/color.js +33 -0
  19. package/dist/chempot-diagram/compute.d.ts +38 -0
  20. package/dist/chempot-diagram/compute.js +650 -0
  21. package/dist/chempot-diagram/index.d.ts +5 -0
  22. package/dist/chempot-diagram/index.js +5 -0
  23. package/dist/chempot-diagram/pointer.d.ts +16 -0
  24. package/dist/chempot-diagram/pointer.js +40 -0
  25. package/dist/chempot-diagram/temperature.d.ts +15 -0
  26. package/dist/chempot-diagram/temperature.js +37 -0
  27. package/dist/chempot-diagram/types.d.ts +83 -0
  28. package/dist/chempot-diagram/types.js +27 -0
  29. package/dist/colors/index.d.ts +3 -1
  30. package/dist/colors/index.js +4 -0
  31. package/dist/composition/BarChart.svelte +13 -22
  32. package/dist/composition/BubbleChart.svelte +5 -3
  33. package/dist/composition/FormulaFilter.svelte +586 -94
  34. package/dist/composition/FormulaFilter.svelte.d.ts +35 -1
  35. package/dist/composition/PieChart.svelte +43 -18
  36. package/dist/composition/PieChart.svelte.d.ts +1 -1
  37. package/dist/convex-hull/ConvexHull.svelte +4 -2
  38. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  39. package/dist/convex-hull/ConvexHull2D.svelte +13 -44
  40. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  41. package/dist/convex-hull/ConvexHull3D.svelte +16 -7
  42. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  43. package/dist/convex-hull/ConvexHull4D.svelte +17 -7
  44. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  45. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
  46. package/dist/convex-hull/ConvexHullStats.svelte +701 -226
  47. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
  48. package/dist/convex-hull/ConvexHullTooltip.svelte +1 -0
  49. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  50. package/dist/convex-hull/demo-temperature.js +36 -0
  51. package/dist/convex-hull/helpers.d.ts +1 -1
  52. package/dist/convex-hull/helpers.js +2 -4
  53. package/dist/convex-hull/index.d.ts +1 -0
  54. package/dist/convex-hull/index.js +1 -0
  55. package/dist/convex-hull/thermodynamics.d.ts +8 -21
  56. package/dist/convex-hull/thermodynamics.js +106 -17
  57. package/dist/convex-hull/types.d.ts +5 -0
  58. package/dist/convex-hull/types.js +5 -0
  59. package/dist/coordination/CoordinationBarPlot.svelte +29 -46
  60. package/dist/element/BohrAtom.svelte +1 -1
  61. package/dist/element/data.js +2 -14
  62. package/dist/element/data.json.gz +0 -0
  63. package/dist/element/types.d.ts +1 -0
  64. package/dist/fermi-surface/FermiSurface.svelte +20 -64
  65. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  66. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  67. package/dist/fermi-surface/FermiSurfaceScene.svelte +1 -1
  68. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  69. package/dist/fermi-surface/parse.js +16 -22
  70. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1273 -0
  71. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  72. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +171 -0
  73. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +31 -0
  74. package/dist/heatmap-matrix/index.d.ts +53 -0
  75. package/dist/heatmap-matrix/index.js +100 -0
  76. package/dist/heatmap-matrix/shared.d.ts +2 -0
  77. package/dist/heatmap-matrix/shared.js +4 -0
  78. package/dist/icons.d.ts +111 -0
  79. package/dist/icons.js +111 -0
  80. package/dist/index.d.ts +3 -1
  81. package/dist/index.js +3 -1
  82. package/dist/io/export.js +15 -3
  83. package/dist/io/file-drop.d.ts +7 -0
  84. package/dist/io/file-drop.js +43 -0
  85. package/dist/io/index.d.ts +2 -2
  86. package/dist/io/index.js +2 -112
  87. package/dist/io/types.d.ts +1 -0
  88. package/dist/io/url-drop.d.ts +2 -0
  89. package/dist/io/url-drop.js +118 -0
  90. package/dist/isosurface/Isosurface.svelte +101 -45
  91. package/dist/isosurface/IsosurfaceControls.svelte +19 -0
  92. package/dist/isosurface/parse.js +73 -30
  93. package/dist/isosurface/slice.d.ts +2 -1
  94. package/dist/isosurface/slice.js +3 -3
  95. package/dist/isosurface/types.d.ts +13 -1
  96. package/dist/isosurface/types.js +98 -0
  97. package/dist/labels.d.ts +2 -1
  98. package/dist/labels.js +1 -0
  99. package/dist/layout/InfoTag.svelte +62 -62
  100. package/dist/layout/SubpageGrid.svelte +74 -0
  101. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  102. package/dist/layout/index.d.ts +1 -0
  103. package/dist/layout/index.js +1 -0
  104. package/dist/layout/json-tree/JsonNode.svelte +83 -85
  105. package/dist/layout/json-tree/JsonTree.svelte +20 -19
  106. package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
  107. package/dist/layout/json-tree/JsonValue.svelte +196 -116
  108. package/dist/layout/json-tree/types.d.ts +10 -2
  109. package/dist/layout/json-tree/utils.d.ts +2 -0
  110. package/dist/layout/json-tree/utils.js +33 -0
  111. package/dist/math.d.ts +7 -0
  112. package/dist/math.js +358 -7
  113. package/dist/overlays/ContextMenu.svelte +3 -2
  114. package/dist/overlays/DraggablePane.svelte +163 -58
  115. package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
  116. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +232 -77
  117. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
  118. package/dist/phase-diagram/PhaseDiagramControls.svelte +32 -11
  119. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
  120. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +103 -0
  121. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  122. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +102 -95
  123. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +7 -0
  124. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +100 -26
  125. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
  126. package/dist/phase-diagram/index.d.ts +2 -0
  127. package/dist/phase-diagram/index.js +2 -0
  128. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  129. package/dist/phase-diagram/svg-to-diagram.js +865 -0
  130. package/dist/phase-diagram/types.d.ts +10 -0
  131. package/dist/phase-diagram/utils.d.ts +7 -4
  132. package/dist/phase-diagram/utils.js +149 -59
  133. package/dist/plot/AxisLabel.svelte +26 -0
  134. package/dist/plot/AxisLabel.svelte.d.ts +16 -0
  135. package/dist/plot/BarPlot.svelte +473 -228
  136. package/dist/plot/BarPlot.svelte.d.ts +3 -3
  137. package/dist/plot/BarPlotControls.svelte +3 -2
  138. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  139. package/dist/plot/ColorBar.svelte +54 -54
  140. package/dist/plot/ColorBar.svelte.d.ts +1 -1
  141. package/dist/plot/ColorScaleSelect.svelte +1 -1
  142. package/dist/plot/ElementScatter.svelte +3 -2
  143. package/dist/plot/FillArea.svelte +4 -1
  144. package/dist/plot/Histogram.svelte +320 -230
  145. package/dist/plot/Histogram.svelte.d.ts +2 -2
  146. package/dist/plot/HistogramControls.svelte +29 -10
  147. package/dist/plot/HistogramControls.svelte.d.ts +6 -2
  148. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +2 -2
  149. package/dist/plot/PlotControls.svelte +109 -27
  150. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  151. package/dist/plot/PlotLegend.svelte +1 -1
  152. package/dist/plot/PortalSelect.svelte +2 -1
  153. package/dist/plot/ReferenceLine.svelte +2 -1
  154. package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
  155. package/dist/plot/ReferencePlane.svelte +1 -3
  156. package/dist/plot/ScatterPlot.svelte +343 -209
  157. package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
  158. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  159. package/dist/plot/ScatterPlot3DControls.svelte +203 -250
  160. package/dist/plot/ScatterPlot3DScene.svelte +4 -7
  161. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  162. package/dist/plot/ScatterPlotControls.svelte +95 -55
  163. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  164. package/dist/plot/ZeroLines.svelte +44 -0
  165. package/dist/plot/ZeroLines.svelte.d.ts +32 -0
  166. package/dist/plot/ZoomRect.svelte +21 -0
  167. package/dist/plot/ZoomRect.svelte.d.ts +8 -0
  168. package/dist/plot/axis-utils.d.ts +1 -1
  169. package/dist/plot/index.d.ts +6 -2
  170. package/dist/plot/index.js +6 -2
  171. package/dist/plot/interactions.d.ts +8 -10
  172. package/dist/plot/interactions.js +2 -3
  173. package/dist/plot/layout.d.ts +7 -1
  174. package/dist/plot/layout.js +12 -4
  175. package/dist/plot/reference-line.d.ts +4 -21
  176. package/dist/plot/reference-line.js +7 -81
  177. package/dist/plot/types.d.ts +42 -17
  178. package/dist/plot/types.js +10 -0
  179. package/dist/plot/utils/label-placement.js +13 -10
  180. package/dist/plot/utils.d.ts +1 -0
  181. package/dist/plot/utils.js +14 -0
  182. package/dist/rdf/RdfPlot.svelte +55 -66
  183. package/dist/settings.d.ts +3 -0
  184. package/dist/settings.js +17 -3
  185. package/dist/spectral/Bands.svelte +515 -143
  186. package/dist/spectral/Bands.svelte.d.ts +22 -2
  187. package/dist/spectral/helpers.d.ts +23 -1
  188. package/dist/spectral/helpers.js +65 -9
  189. package/dist/spectral/types.d.ts +2 -0
  190. package/dist/structure/AtomLegend.svelte +29 -8
  191. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  192. package/dist/structure/CellSelect.svelte +92 -22
  193. package/dist/structure/Structure.svelte +108 -118
  194. package/dist/structure/Structure.svelte.d.ts +1 -1
  195. package/dist/structure/StructureControls.svelte +25 -22
  196. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  197. package/dist/structure/StructureInfoPane.svelte +7 -1
  198. package/dist/structure/StructureScene.svelte +104 -66
  199. package/dist/structure/StructureScene.svelte.d.ts +2 -1
  200. package/dist/structure/atom-properties.d.ts +6 -2
  201. package/dist/structure/atom-properties.js +38 -25
  202. package/dist/structure/export.js +10 -7
  203. package/dist/structure/ferrox-wasm-types.d.ts +3 -2
  204. package/dist/structure/ferrox-wasm-types.js +0 -3
  205. package/dist/structure/ferrox-wasm.d.ts +3 -2
  206. package/dist/structure/ferrox-wasm.js +1 -2
  207. package/dist/structure/index.d.ts +6 -0
  208. package/dist/structure/index.js +22 -0
  209. package/dist/structure/parse.js +19 -16
  210. package/dist/structure/partial-occupancy.d.ts +25 -0
  211. package/dist/structure/partial-occupancy.js +102 -0
  212. package/dist/structure/validation.js +6 -3
  213. package/dist/symmetry/SymmetryStats.svelte +18 -4
  214. package/dist/symmetry/WyckoffTable.svelte +18 -10
  215. package/dist/symmetry/index.d.ts +7 -4
  216. package/dist/symmetry/index.js +83 -18
  217. package/dist/table/HeatmapTable.svelte +425 -65
  218. package/dist/table/HeatmapTable.svelte.d.ts +12 -1
  219. package/dist/table/ToggleMenu.svelte +2 -0
  220. package/dist/table/index.d.ts +2 -0
  221. package/dist/trajectory/Trajectory.svelte +147 -145
  222. package/dist/trajectory/TrajectoryExportPane.svelte +13 -9
  223. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
  224. package/dist/trajectory/constants.d.ts +6 -0
  225. package/dist/trajectory/constants.js +7 -0
  226. package/dist/trajectory/extract.js +3 -5
  227. package/dist/trajectory/format-detect.d.ts +9 -0
  228. package/dist/trajectory/format-detect.js +76 -0
  229. package/dist/trajectory/frame-reader.d.ts +17 -0
  230. package/dist/trajectory/frame-reader.js +339 -0
  231. package/dist/trajectory/helpers.d.ts +15 -0
  232. package/dist/trajectory/helpers.js +187 -0
  233. package/dist/trajectory/index.d.ts +1 -0
  234. package/dist/trajectory/index.js +11 -4
  235. package/dist/trajectory/parse/ase.d.ts +2 -0
  236. package/dist/trajectory/parse/ase.js +76 -0
  237. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  238. package/dist/trajectory/parse/hdf5.js +121 -0
  239. package/dist/trajectory/parse/index.d.ts +12 -0
  240. package/dist/trajectory/parse/index.js +304 -0
  241. package/dist/trajectory/parse/lammps.d.ts +5 -0
  242. package/dist/trajectory/parse/lammps.js +169 -0
  243. package/dist/trajectory/parse/vasp.d.ts +2 -0
  244. package/dist/trajectory/parse/vasp.js +65 -0
  245. package/dist/trajectory/parse/xyz.d.ts +2 -0
  246. package/dist/trajectory/parse/xyz.js +109 -0
  247. package/dist/trajectory/types.d.ts +11 -0
  248. package/dist/trajectory/types.js +1 -0
  249. package/dist/utils.d.ts +2 -0
  250. package/dist/utils.js +4 -0
  251. package/dist/xrd/XrdPlot.svelte +6 -4
  252. package/dist/xrd/calc-xrd.js +0 -1
  253. package/package.json +30 -24
  254. package/readme.md +4 -4
  255. package/dist/trajectory/parse.d.ts +0 -42
  256. package/dist/trajectory/parse.js +0 -1267
  257. /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
@@ -0,0 +1,677 @@
1
+ <script lang="ts">import {} from '../colors';
2
+ import { get_hill_formula } from '../composition/format';
3
+ import { extract_formula_elements } from '../composition/parse';
4
+ import TemperatureSlider from '../convex-hull/TemperatureSlider.svelte';
5
+ import { export_svg_as_png, export_svg_as_svg } from '../io/export';
6
+ import { download } from '../io/fetch';
7
+ import DraggablePane from '../overlays/DraggablePane.svelte';
8
+ import { ColorBar, ScatterPlot } from '../plot';
9
+ import { onDestroy } from 'svelte';
10
+ import { SvelteMap } from 'svelte/reactivity';
11
+ import { get_chempot_color_bar_config, make_chempot_color_scale } from './color';
12
+ import { apply_element_padding, best_form_energy_for_formula, build_axis_ranges, compute_chempot_diagram, formula_key_from_composition, get_energy_per_atom, get_min_entries_and_el_refs, orthonormal_2d, pad_domain_points, } from './compute';
13
+ import { with_hover_pointer } from './pointer';
14
+ import { get_temp_filter_payload, get_valid_temperature } from './temperature';
15
+ import { CHEMPOT_DEFAULTS } from './types';
16
+ let { entries = [], config = {}, width = $bindable(800), height = $bindable(600),
17
+ // Auto-corrected to a valid available temperature when needed.
18
+ temperature = $bindable(undefined), interpolate_temperature = CHEMPOT_DEFAULTS.interpolate_temperature, max_interpolation_gap = CHEMPOT_DEFAULTS.max_interpolation_gap, hover_info = $bindable(null), render_local_tooltip = true, } = $props();
19
+ let container_width = $state(0);
20
+ const base_aspect_ratio = $derived(height > 0 && width > 0 ? height / width : 1);
21
+ const render_width = $derived(container_width > 0 ? container_width : width);
22
+ const render_height = $derived(Math.round(render_width * base_aspect_ratio));
23
+ // === Control overrides ===
24
+ let formal_chempots_override = $state(null);
25
+ let label_stable_override = $state(null);
26
+ let element_padding_override = $state(null);
27
+ let default_min_limit_override = $state(null);
28
+ const formal_chempots = $derived(formal_chempots_override ??
29
+ (config.formal_chempots ?? CHEMPOT_DEFAULTS.formal_chempots));
30
+ const label_stable = $derived(label_stable_override ?? (config.label_stable ?? CHEMPOT_DEFAULTS.label_stable));
31
+ const element_padding = $derived(element_padding_override ??
32
+ (config.element_padding ?? CHEMPOT_DEFAULTS.element_padding));
33
+ const default_min_limit = $derived(default_min_limit_override ??
34
+ (config.default_min_limit ?? CHEMPOT_DEFAULTS.default_min_limit));
35
+ let color_mode_override = $state(null);
36
+ let color_scale_override = $state(null);
37
+ let reverse_color_scale_override = $state(null);
38
+ const color_mode = $derived(color_mode_override ?? (config.color_mode ?? CHEMPOT_DEFAULTS.color_mode));
39
+ const color_scale = $derived(color_scale_override ?? (config.color_scale ?? CHEMPOT_DEFAULTS.color_scale));
40
+ const reverse_color_scale = $derived(reverse_color_scale_override ??
41
+ (config.reverse_color_scale ?? CHEMPOT_DEFAULTS.reverse_color_scale));
42
+ const arity_colors = [`#3498db`, `#2ecc71`, `#e67e22`, `#9b59b6`];
43
+ const show_tooltip = $derived(config.show_tooltip ?? CHEMPOT_DEFAULTS.show_tooltip);
44
+ const effective_config = $derived({
45
+ ...config,
46
+ formal_chempots,
47
+ label_stable,
48
+ element_padding,
49
+ default_min_limit,
50
+ });
51
+ const { has_temp_data, available_temperatures, temp_filtered_entries } = $derived(get_temp_filter_payload(entries, temperature, config, {
52
+ interpolate_temperature,
53
+ max_interpolation_gap,
54
+ }));
55
+ $effect(() => {
56
+ // Keep bound temperature aligned with available data points.
57
+ const next_temperature = get_valid_temperature(temperature, has_temp_data, available_temperatures);
58
+ if (next_temperature !== temperature)
59
+ temperature = next_temperature;
60
+ });
61
+ const show_temperature_slider = $derived(has_temp_data && available_temperatures.length > 0);
62
+ function reset_controls() {
63
+ formal_chempots_override = null;
64
+ label_stable_override = null;
65
+ element_padding_override = null;
66
+ default_min_limit_override = null;
67
+ color_mode_override = null;
68
+ color_scale_override = null;
69
+ reverse_color_scale_override = null;
70
+ }
71
+ // === Diagram computation ===
72
+ const diagram_data = $derived.by(() => {
73
+ if (temp_filtered_entries.length < 2)
74
+ return null;
75
+ try {
76
+ return compute_chempot_diagram(temp_filtered_entries, effective_config);
77
+ }
78
+ catch (err) {
79
+ console.error(`ChemPotDiagram2D:`, err);
80
+ return null;
81
+ }
82
+ });
83
+ const plot_elements = $derived((diagram_data?.elements ?? config.elements ?? []).slice(0, 2));
84
+ const draw_domains = $derived.by(() => {
85
+ if (!diagram_data || plot_elements.length < 2)
86
+ return {};
87
+ const indices = [0, 1];
88
+ if (element_padding <= 0) {
89
+ return Object.fromEntries(Object.entries(diagram_data.domains).filter(([, pts]) => pts.length > 0));
90
+ }
91
+ const new_lims = apply_element_padding(diagram_data.domains, indices, element_padding, default_min_limit);
92
+ const result = {};
93
+ for (const [formula, pts] of Object.entries(diagram_data.domains)) {
94
+ const padded = pad_domain_points(pts, indices, new_lims, default_min_limit, element_padding);
95
+ if (padded.length > 0)
96
+ result[formula] = padded;
97
+ }
98
+ return result;
99
+ });
100
+ const domain_entries = $derived(Object.entries(draw_domains));
101
+ const domain_formulas = $derived(Object.keys(draw_domains));
102
+ const raw_el_refs = $derived(get_min_entries_and_el_refs(temp_filtered_entries).el_refs);
103
+ const entry_energy_stats_by_formula = $derived.by(() => {
104
+ const stats = new SvelteMap();
105
+ for (const entry of temp_filtered_entries) {
106
+ const formula_key = formula_key_from_composition(entry.composition);
107
+ const epa = get_energy_per_atom(entry);
108
+ const prev_stats = stats.get(formula_key);
109
+ if (!prev_stats) {
110
+ stats.set(formula_key, {
111
+ matching_entry_count: 1,
112
+ min_energy_per_atom: epa,
113
+ });
114
+ continue;
115
+ }
116
+ stats.set(formula_key, {
117
+ matching_entry_count: prev_stats.matching_entry_count + 1,
118
+ min_energy_per_atom: Math.min(prev_stats.min_energy_per_atom ?? epa, epa),
119
+ });
120
+ }
121
+ return stats;
122
+ });
123
+ const color_mode_labels = {
124
+ energy: `Energy per atom (eV)`,
125
+ formation_energy: `Formation energy (eV/atom)`,
126
+ entries: `Entry count`,
127
+ };
128
+ function get_numeric_color_value(formula, active_color_mode) {
129
+ if (active_color_mode === `energy`) {
130
+ return entry_energy_stats_by_formula.get(formula)?.min_energy_per_atom ?? null;
131
+ }
132
+ if (active_color_mode === `formation_energy`) {
133
+ return best_form_energy_for_formula(temp_filtered_entries, formula, raw_el_refs) ?? null;
134
+ }
135
+ return entry_energy_stats_by_formula.get(formula)?.matching_entry_count ?? 0;
136
+ }
137
+ const domain_color_values = $derived.by(() => {
138
+ if (color_mode === `none` || color_mode === `arity`)
139
+ return null;
140
+ const active_color_mode = color_mode;
141
+ const value_by_formula = new SvelteMap();
142
+ const values = [];
143
+ for (const formula of domain_formulas) {
144
+ const value = get_numeric_color_value(formula, active_color_mode);
145
+ if (value == null || !Number.isFinite(value))
146
+ continue;
147
+ values.push(value);
148
+ value_by_formula.set(formula, value);
149
+ }
150
+ return { value_by_formula, values };
151
+ });
152
+ const domain_colors = $derived.by(() => {
153
+ const colors = new SvelteMap();
154
+ if (color_mode === `none`)
155
+ return colors;
156
+ if (color_mode === `arity`) {
157
+ for (const formula of domain_formulas) {
158
+ const n_elements = extract_formula_elements(formula).length;
159
+ const color_idx = Math.min(n_elements, arity_colors.length) - 1;
160
+ colors.set(formula, arity_colors[Math.max(0, color_idx)]);
161
+ }
162
+ return colors;
163
+ }
164
+ const values_payload = domain_color_values;
165
+ const scale = make_chempot_color_scale(values_payload?.values ?? [], color_scale, reverse_color_scale);
166
+ for (const formula of domain_formulas) {
167
+ const color_val = values_payload?.value_by_formula.get(formula);
168
+ colors.set(formula, color_val != null && scale ? scale(color_val) : `#999`);
169
+ }
170
+ return colors;
171
+ });
172
+ const color_range = $derived.by(() => {
173
+ const values = domain_color_values?.values ?? [];
174
+ if (values.length === 0)
175
+ return null;
176
+ let min_val = values[0], max_val = values[0];
177
+ for (let idx = 1; idx < values.length; idx++) {
178
+ if (values[idx] < min_val)
179
+ min_val = values[idx];
180
+ if (values[idx] > max_val)
181
+ max_val = values[idx];
182
+ }
183
+ return {
184
+ min: min_val,
185
+ max: Math.max(max_val, min_val + 1e-6),
186
+ label: color_mode === `none` || color_mode === `arity`
187
+ ? ``
188
+ : color_mode_labels[color_mode],
189
+ };
190
+ });
191
+ // === Convert domains to ScatterPlot DataSeries ===
192
+ const series = $derived(domain_entries.map(([formula, pts]) => ({
193
+ id: formula,
194
+ label: formula,
195
+ x: pts.map((pt) => pt[0]),
196
+ y: pts.map((pt) => pt[1]),
197
+ markers: `line+points`,
198
+ line_style: { stroke: domain_colors.get(formula) ?? `black`, stroke_width: 3 },
199
+ point_style: { fill: domain_colors.get(formula) ?? `black`, radius: 3 },
200
+ })));
201
+ // Axis label text
202
+ function axis_label(element) {
203
+ if (formal_chempots)
204
+ return `\u0394\u03BC(${element}) (eV)`;
205
+ return `\u03BC(${element}) (eV)`;
206
+ }
207
+ let x_axis = $state({ label: `` });
208
+ let y_axis = $state({ label: `` });
209
+ $effect(() => {
210
+ const next_x_label = axis_label(plot_elements[0] ?? ``);
211
+ const next_y_label = axis_label(plot_elements[1] ?? ``);
212
+ if (x_axis.label !== next_x_label)
213
+ x_axis = { ...x_axis, label: next_x_label };
214
+ if (y_axis.label !== next_y_label)
215
+ y_axis = { ...y_axis, label: next_y_label };
216
+ });
217
+ // === Domain label annotations (in data coordinates) ===
218
+ const annotations = $derived.by(() => {
219
+ if (!label_stable)
220
+ return [];
221
+ const result = [];
222
+ for (const [formula, pts] of Object.entries(draw_domains)) {
223
+ if (pts.length === 0)
224
+ continue;
225
+ const center_x = pts.reduce((s, p) => s + p[0], 0) / pts.length;
226
+ const center_y = pts.reduce((s, p) => s + p[1], 0) / pts.length;
227
+ let offset_x = 0;
228
+ let offset_y = 0;
229
+ if (pts.length >= 2) {
230
+ const [nx, ny] = orthonormal_2d(pts);
231
+ offset_x = nx * 0.25;
232
+ offset_y = ny * 0.25;
233
+ }
234
+ result.push({
235
+ formula,
236
+ data_x: center_x + offset_x,
237
+ data_y: center_y + offset_y,
238
+ });
239
+ }
240
+ return result;
241
+ });
242
+ // === Hover info for external consumers ===
243
+ let locked_hover_formula = $state(null);
244
+ function set_hover_info(formula, pts, event) {
245
+ const bounds = scatter_wrapper?.getBoundingClientRect();
246
+ hover_info = with_hover_pointer({
247
+ formula,
248
+ view: `2d`,
249
+ n_points: pts.length,
250
+ axis_ranges: build_axis_ranges(pts, plot_elements),
251
+ }, event, bounds);
252
+ }
253
+ function clear_hover_lock() {
254
+ locked_hover_formula = null;
255
+ hover_info = null;
256
+ }
257
+ function handle_hover(data) {
258
+ if (!data) {
259
+ if (!locked_hover_formula)
260
+ hover_info = null;
261
+ return;
262
+ }
263
+ const entry = domain_entries[data.point.series_idx];
264
+ if (!entry)
265
+ return;
266
+ const [formula, pts] = entry;
267
+ if (locked_hover_formula && locked_hover_formula !== formula)
268
+ return;
269
+ set_hover_info(formula, pts, data.event);
270
+ }
271
+ function handle_click(data) {
272
+ const entry = domain_entries[data.point.series_idx];
273
+ if (!entry)
274
+ return;
275
+ const [formula, pts] = entry;
276
+ if (locked_hover_formula === formula) {
277
+ clear_hover_lock();
278
+ return;
279
+ }
280
+ locked_hover_formula = formula;
281
+ set_hover_info(formula, pts, data.event);
282
+ }
283
+ // === Export ===
284
+ let scatter_wrapper = $state();
285
+ let export_pane_open = $state(false);
286
+ let copy_status = $state(false);
287
+ let copy_timeout_id = null;
288
+ function get_svg_element() {
289
+ return scatter_wrapper?.querySelector(`svg`) ?? null;
290
+ }
291
+ function get_json_string() {
292
+ return JSON.stringify({
293
+ elements: diagram_data?.elements ?? [],
294
+ domains: draw_domains,
295
+ lims: diagram_data?.lims ?? [],
296
+ }, null, 2);
297
+ }
298
+ function export_json_file() {
299
+ download(get_json_string(), `chempot-${plot_elements.join(`-`)}.json`, `application/json`);
300
+ }
301
+ async function copy_json() {
302
+ try {
303
+ await navigator.clipboard.writeText(get_json_string());
304
+ copy_status = true;
305
+ }
306
+ catch (err) {
307
+ copy_status = false;
308
+ console.error(`Failed to copy JSON to clipboard:`, err);
309
+ }
310
+ if (copy_timeout_id !== null)
311
+ clearTimeout(copy_timeout_id);
312
+ copy_timeout_id = setTimeout(() => {
313
+ copy_status = false;
314
+ copy_timeout_id = null;
315
+ }, 1000);
316
+ }
317
+ onDestroy(() => {
318
+ if (copy_timeout_id !== null)
319
+ clearTimeout(copy_timeout_id);
320
+ });
321
+ </script>
322
+
323
+ {#snippet domain_labels(props: UserContentProps)}
324
+ {#each annotations as { formula, data_x, data_y } (formula)}
325
+ <text
326
+ x={props.x_scale_fn(data_x)}
327
+ y={props.y_scale_fn(data_y)}
328
+ text-anchor="middle"
329
+ class="domain-label"
330
+ >
331
+ {get_hill_formula(formula, true, ``)}
332
+ </text>
333
+ {/each}
334
+ {/snippet}
335
+
336
+ {#snippet export_toggle()}
337
+ <DraggablePane
338
+ bind:show={export_pane_open}
339
+ open_icon="Cross"
340
+ closed_icon="Export"
341
+ pane_props={{ class: `chempot-export-pane` }}
342
+ toggle_props={{
343
+ class: `chempot-export-toggle`,
344
+ title: `Export chemical potential diagram`,
345
+ style:
346
+ `position: absolute; top: var(--ctrl-btn-top, 5pt); right: 36px; z-index: 10`,
347
+ }}
348
+ >
349
+ <h4 id="export-image">Export Image</h4>
350
+ <div class="export-row">
351
+ <label>
352
+ SVG
353
+ <button
354
+ type="button"
355
+ onclick={() => {
356
+ const svg = get_svg_element()
357
+ if (svg) {
358
+ export_svg_as_svg(svg, `chempot-${plot_elements.join(`-`)}.svg`)
359
+ }
360
+ }}
361
+ aria-label="Download SVG"
362
+ >
363
+
364
+ </button>
365
+ </label>
366
+ <label>
367
+ PNG
368
+ <button
369
+ type="button"
370
+ onclick={() => {
371
+ const svg = get_svg_element()
372
+ if (svg) {
373
+ export_svg_as_png(svg, `chempot-${plot_elements.join(`-`)}.png`)
374
+ }
375
+ }}
376
+ aria-label="Download PNG"
377
+ >
378
+
379
+ </button>
380
+ </label>
381
+ </div>
382
+ <h4 id="export-data">Export Data</h4>
383
+ <div class="export-row">
384
+ <label>
385
+ JSON
386
+ <button type="button" onclick={export_json_file} aria-label="Download JSON">
387
+
388
+ </button>
389
+ <button
390
+ type="button"
391
+ onclick={copy_json}
392
+ aria-label="Copy JSON to clipboard"
393
+ >
394
+ {copy_status ? `✅` : `📋`}
395
+ </button>
396
+ </label>
397
+ </div>
398
+ </DraggablePane>
399
+ {/snippet}
400
+
401
+ {#snippet chempot_controls(_props: unknown)}
402
+ <h4 id="chempot">ChemPot</h4>
403
+ <label>
404
+ <span>Formal chempots:</span>
405
+ <input
406
+ type="checkbox"
407
+ checked={formal_chempots}
408
+ onchange={() => {
409
+ formal_chempots_override = !formal_chempots
410
+ }}
411
+ />
412
+ </label>
413
+ <label>
414
+ <span>Label stable:</span>
415
+ <input
416
+ type="checkbox"
417
+ checked={label_stable}
418
+ onchange={() => {
419
+ label_stable_override = !label_stable
420
+ }}
421
+ />
422
+ </label>
423
+ <label>
424
+ <span>Element padding (eV):</span>
425
+ <input
426
+ type="number"
427
+ min="0"
428
+ step="0.1"
429
+ value={element_padding}
430
+ oninput={(event) => {
431
+ element_padding_override = Number(event.currentTarget.value) || 0
432
+ }}
433
+ />
434
+ </label>
435
+ <label>
436
+ <span>Default min limit (eV):</span>
437
+ <input
438
+ type="number"
439
+ max="0"
440
+ step="1"
441
+ value={default_min_limit}
442
+ oninput={(event) => {
443
+ const raw = event.currentTarget.value
444
+ const parsed = parseFloat(raw)
445
+ default_min_limit_override = raw === `` || isNaN(parsed)
446
+ ? default_min_limit
447
+ : parsed
448
+ }}
449
+ />
450
+ </label>
451
+ <label>
452
+ <span>Color mode:</span>
453
+ <select
454
+ value={color_mode}
455
+ onchange={(event) => {
456
+ color_mode_override = event.currentTarget.value as typeof color_mode
457
+ }}
458
+ >
459
+ <option value="none">None</option>
460
+ <option value="energy">Energy/atom</option>
461
+ <option value="formation_energy">Formation energy</option>
462
+ <option value="arity">Element count</option>
463
+ <option value="entries">Entry count</option>
464
+ </select>
465
+ </label>
466
+ {#if color_mode !== `none` && color_mode !== `arity`}
467
+ <label>
468
+ <span>Color scale:</span>
469
+ <select
470
+ value={color_scale}
471
+ onchange={(event) => {
472
+ color_scale_override = event.currentTarget.value as D3InterpolateName
473
+ }}
474
+ >
475
+ <option value="interpolateViridis">Viridis</option>
476
+ <option value="interpolatePlasma">Plasma</option>
477
+ <option value="interpolateInferno">Inferno</option>
478
+ <option value="interpolateMagma">Magma</option>
479
+ <option value="interpolateCividis">Cividis</option>
480
+ <option value="interpolateTurbo">Turbo</option>
481
+ <option value="interpolateRdYlBu">RdYlBu</option>
482
+ <option value="interpolateSpectral">Spectral</option>
483
+ </select>
484
+ <span class="reverse-scale-toggle">
485
+ <span>Reverse:</span>
486
+ <input
487
+ type="checkbox"
488
+ checked={reverse_color_scale}
489
+ onchange={() => {
490
+ reverse_color_scale_override = !reverse_color_scale
491
+ }}
492
+ />
493
+ </span>
494
+ </label>
495
+ {/if}
496
+ <button type="button" onclick={reset_controls}>Reset defaults</button>
497
+ {/snippet}
498
+
499
+ {#if !diagram_data}
500
+ <div class="error-state" role="alert" aria-live="polite">
501
+ <p>Cannot compute chemical potential diagram.</p>
502
+ <p>Need at least 2 elements with elemental reference entries.</p>
503
+ </div>
504
+ {:else}
505
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
506
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
507
+ <div
508
+ class="chempot-diagram-2d"
509
+ bind:clientWidth={container_width}
510
+ role="application"
511
+ tabindex="0"
512
+ onkeydown={(event) => {
513
+ if (event.key === `Escape`) clear_hover_lock()
514
+ }}
515
+ onpointerdown={(event) => {
516
+ const target = event.target
517
+ if (!locked_hover_formula) return
518
+ const is_background_click = target === scatter_wrapper ||
519
+ (target instanceof SVGElement &&
520
+ target.closest(`g[data-series-id]`) === null)
521
+ if (is_background_click) {
522
+ clear_hover_lock()
523
+ }
524
+ }}
525
+ >
526
+ {@render export_toggle()}
527
+ <ScatterPlot
528
+ bind:wrapper={scatter_wrapper}
529
+ {series}
530
+ bind:x_axis
531
+ bind:y_axis
532
+ legend={null}
533
+ controls={{ show: true }}
534
+ controls_extra={chempot_controls}
535
+ user_content={domain_labels}
536
+ on_point_hover={handle_hover}
537
+ on_point_click={handle_click}
538
+ style="--scatter-width: 100%; --scatter-height: {render_height}px; --fullscreen-btn-offset: 68px"
539
+ />
540
+ {#if color_mode !== `none` && color_mode !== `arity` && color_range}
541
+ {@const color_bar_config = get_chempot_color_bar_config(
542
+ color_scale,
543
+ reverse_color_scale,
544
+ )}
545
+ <ColorBar
546
+ title={color_range.label}
547
+ range={[color_range.min, color_range.max]}
548
+ color_scale_fn={color_bar_config.color_scale_fn}
549
+ color_scale_domain={color_bar_config.color_scale_domain}
550
+ wrapper_style="position: absolute; bottom: 70px; left: 50px; width: 180px; z-index: 10;"
551
+ bar_style="height: 10px;"
552
+ title_style="margin-bottom: 3px;"
553
+ />
554
+ {/if}
555
+ {#if color_mode === `arity`}
556
+ <div class="arity-legend">
557
+ {#each [`Unary`, `Binary`, `Ternary`, `4+`] as label_text, color_idx (label_text)}
558
+ <span>
559
+ <span style:background={arity_colors[color_idx]}></span>
560
+ {label_text}
561
+ </span>
562
+ {/each}
563
+ </div>
564
+ {/if}
565
+ {#if render_local_tooltip && show_tooltip && hover_info?.view === `2d`}
566
+ <aside
567
+ class="tooltip"
568
+ style:left="{hover_info.pointer?.x ?? 4}px"
569
+ style:top="{hover_info.pointer?.y ?? 4}px"
570
+ >
571
+ <strong>{@html get_hill_formula(hover_info.formula, false, ``)}</strong>
572
+ {#if locked_hover_formula === hover_info.formula}
573
+ <div>Pinned · Press Esc to unlock</div>
574
+ {/if}
575
+ </aside>
576
+ {/if}
577
+ {#if show_temperature_slider && temperature !== undefined}
578
+ <TemperatureSlider
579
+ class="chempot-temp-slider"
580
+ {available_temperatures}
581
+ bind:temperature
582
+ />
583
+ {/if}
584
+ </div>
585
+ {/if}
586
+
587
+ <style>
588
+ .chempot-diagram-2d {
589
+ position: relative;
590
+ width: 100%;
591
+ }
592
+ .chempot-diagram-2d > :global(.pane-toggle) {
593
+ opacity: 0;
594
+ transition: opacity 0.2s, background-color 0.2s;
595
+ }
596
+ .chempot-diagram-2d:hover > :global(.pane-toggle),
597
+ .chempot-diagram-2d > :global(.pane-toggle:focus-visible),
598
+ .chempot-diagram-2d > :global(.pane-toggle[aria-expanded='true']) {
599
+ opacity: 1;
600
+ }
601
+ .chempot-diagram-2d :global(.draggable-pane label) {
602
+ display: flex;
603
+ align-items: center;
604
+ gap: 6pt;
605
+ margin: 4pt 0;
606
+ font-size: 0.95em;
607
+ }
608
+ .chempot-diagram-2d :global(.export-row) {
609
+ display: flex;
610
+ flex-wrap: wrap;
611
+ gap: 4pt 10pt;
612
+ margin: 0 0 4pt;
613
+ }
614
+ .chempot-diagram-2d :global(.export-row > label) {
615
+ margin: 0;
616
+ }
617
+ .chempot-diagram-2d :global(.chempot-temp-slider) {
618
+ top: var(--chempot-temp-slider-top, calc(1ex + 108px));
619
+ right: 4px;
620
+ z-index: 11;
621
+ }
622
+ .chempot-diagram-2d :global(.reverse-scale-toggle) {
623
+ display: flex;
624
+ align-items: center;
625
+ gap: 4pt;
626
+ margin-left: 4pt;
627
+ }
628
+ .error-state {
629
+ display: flex;
630
+ flex-direction: column;
631
+ align-items: center;
632
+ justify-content: center;
633
+ height: 100%;
634
+ color: var(--text-color, #666);
635
+ }
636
+ .domain-label {
637
+ font-size: 12px;
638
+ fill: var(--text-color, currentColor);
639
+ opacity: 0.7;
640
+ pointer-events: none;
641
+ }
642
+ .tooltip {
643
+ position: absolute;
644
+ background: var(
645
+ --tooltip-bg,
646
+ light-dark(rgba(255, 255, 255, 0.95), rgba(0, 0, 0, 0.9))
647
+ );
648
+ color: var(--tooltip-text, var(--text-color, #fff));
649
+ padding: 4px 8px;
650
+ border-radius: 4px;
651
+ font-size: 12px;
652
+ pointer-events: none;
653
+ white-space: nowrap;
654
+ z-index: 10;
655
+ }
656
+ .arity-legend {
657
+ position: absolute;
658
+ bottom: 52px;
659
+ left: 24px;
660
+ display: flex;
661
+ gap: 10px;
662
+ font-size: 12px;
663
+ z-index: 10;
664
+ pointer-events: none;
665
+ }
666
+ .arity-legend > span {
667
+ display: flex;
668
+ align-items: center;
669
+ gap: 4px;
670
+ }
671
+ .arity-legend > span > span {
672
+ width: 10px;
673
+ height: 10px;
674
+ border-radius: 50%;
675
+ flex-shrink: 0;
676
+ }
677
+ </style>
@@ -0,0 +1,16 @@
1
+ import type { PhaseData } from '../convex-hull/types';
2
+ import type { ChemPotDiagramConfig, ChemPotHoverInfo } from './types';
3
+ type $$ComponentProps = {
4
+ entries: PhaseData[];
5
+ config?: ChemPotDiagramConfig;
6
+ width?: number;
7
+ height?: number;
8
+ temperature?: number;
9
+ interpolate_temperature?: boolean;
10
+ max_interpolation_gap?: number;
11
+ hover_info?: ChemPotHoverInfo | null;
12
+ render_local_tooltip?: boolean;
13
+ };
14
+ declare const ChemPotDiagram2D: import("svelte").Component<$$ComponentProps, {}, "temperature" | "height" | "width" | "hover_info">;
15
+ type ChemPotDiagram2D = ReturnType<typeof ChemPotDiagram2D>;
16
+ export default ChemPotDiagram2D;