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
@@ -1,17 +1,51 @@
1
1
  import type { HTMLAttributes } from 'svelte/elements';
2
2
  import type { FormulaSearchMode } from './index';
3
+ type SearchExampleCategory = {
4
+ label: string;
5
+ description: string;
6
+ examples: string[];
7
+ };
8
+ export type FormulaFilterToken = {
9
+ raw: string;
10
+ element: string;
11
+ operator: `include` | `exclude`;
12
+ constraint: string | null;
13
+ is_wildcard: boolean;
14
+ is_valid: boolean;
15
+ };
16
+ export type FormulaFilterParseResult = {
17
+ value: string;
18
+ normalized_value: string;
19
+ search_mode: FormulaSearchMode;
20
+ tokens: FormulaFilterToken[];
21
+ has_wildcards: boolean;
22
+ is_valid: boolean;
23
+ error_message: string | null;
24
+ };
25
+ export type FormulaFilterValidation = {
26
+ state: `valid` | `warning` | `invalid`;
27
+ message: string | null;
28
+ };
3
29
  type $$ComponentProps = {
4
30
  value: string;
5
31
  search_mode?: FormulaSearchMode;
6
32
  input_element?: HTMLInputElement | null;
7
33
  show_clear_button?: boolean;
8
34
  show_examples?: boolean;
35
+ show_mode_lock?: boolean;
36
+ show_chip_editor?: boolean;
37
+ normalize_exact?: boolean;
38
+ examples?: SearchExampleCategory[];
9
39
  disabled?: boolean;
40
+ mode_locked?: boolean;
10
41
  max_history?: number;
11
42
  history_key?: string;
43
+ validate?: (value: string, search_mode: FormulaSearchMode, parsed: FormulaFilterParseResult) => FormulaFilterValidation | null;
44
+ onparse?: (parsed: FormulaFilterParseResult) => void;
45
+ on_validation?: (validation: FormulaFilterValidation) => void;
12
46
  onchange?: (value: string, search_mode: FormulaSearchMode) => void;
13
47
  onclear?: () => void;
14
48
  } & HTMLAttributes<HTMLDivElement>;
15
- declare const FormulaFilter: import("svelte").Component<$$ComponentProps, {}, "value" | "search_mode" | "input_element">;
49
+ declare const FormulaFilter: import("svelte").Component<$$ComponentProps, {}, "value" | "search_mode" | "input_element" | "mode_locked">;
16
50
  type FormulaFilter = ReturnType<typeof FormulaFilter>;
17
51
  export default FormulaFilter;
@@ -1,5 +1,5 @@
1
- <script lang="ts">import { format_num } from '../labels';
2
- import { ELEMENT_COLOR_SCHEMES, pick_contrast_color } from '../colors';
1
+ <script lang="ts">import { ELEMENT_COLOR_SCHEMES, pick_contrast_color } from '../colors';
2
+ import { format_num } from '../labels';
3
3
  import { get_chart_font_scale } from './index';
4
4
  import { count_atoms_in_composition, fractional_composition } from './parse';
5
5
  // Constants for pie chart calculations
@@ -21,8 +21,34 @@ let segments = $derived.by(() => {
21
21
  .map(([element_key, amount]) => {
22
22
  const element = element_key;
23
23
  const fraction = fractions[element] || 0;
24
- // use 359.99° to avoid 360° for single-element compositions which cause SVG arc issues
25
- const angle_span = fraction === 1 ? 359.99 : fraction * 360;
24
+ const color = element_colors[element] || `#cccccc`;
25
+ // Single element: full circle with no radial stroke line, label at center
26
+ if (fraction === 1) {
27
+ const r = outer_radius;
28
+ const ir = inner_radius_adjusted;
29
+ // Two semicircular arcs to form a full circle (avoids SVG 360° arc bug)
30
+ const outer_arc = `M ${center} ${center - r} A ${r} ${r} 0 1 1 ${center} ${center + r} A ${r} ${r} 0 1 1 ${center} ${center - r} Z`;
31
+ const path = ir > 0
32
+ ? `${outer_arc} M ${center} ${center - ir} A ${ir} ${ir} 0 1 0 ${center} ${center + ir} A ${ir} ${ir} 0 1 0 ${center} ${center - ir} Z`
33
+ : outer_arc;
34
+ const label_text = element + (show_amounts ? String(amount) : ``) +
35
+ (show_percentages ? `${format_num(fraction, `.1~%`)}` : ``);
36
+ return {
37
+ element,
38
+ amount,
39
+ fraction,
40
+ color,
41
+ start_angle: -90,
42
+ end_angle: 270,
43
+ path,
44
+ label_x: center,
45
+ label_y: center,
46
+ is_outside_slice: false,
47
+ font_scale: get_chart_font_scale(2.6, label_text, r * 2),
48
+ text_color: pick_contrast_color({ bg_color: color }),
49
+ };
50
+ }
51
+ const angle_span = fraction * 360;
26
52
  const start_angle = current_angle;
27
53
  const end_angle = current_angle + angle_span;
28
54
  current_angle = end_angle;
@@ -78,7 +104,6 @@ let segments = $derived.by(() => {
78
104
  : Math.min(outer_radius - inner_radius_adjusted, // Radial space
79
105
  (angle_span * Math.PI / 180) * label_radius * 0.8);
80
106
  const font_scale = get_chart_font_scale(base_scale, label_text, available_space);
81
- const color = element_colors[element] || `#cccccc`;
82
107
  return {
83
108
  element,
84
109
  amount,
@@ -102,6 +127,7 @@ let hovered_element = $state(null);
102
127
 
103
128
  <svg
104
129
  viewBox="0 0 {size} {size}"
130
+ style:max-width="{size}px"
105
131
  {...rest}
106
132
  class="pie-chart {rest.class ?? ``}"
107
133
  bind:this={svg_node}
@@ -111,7 +137,11 @@ let hovered_element = $state(null);
111
137
  d={segment.path}
112
138
  fill={segment.color}
113
139
  stroke="white"
114
- stroke-width={hovered_element === segment.element ? stroke_width + 1 : stroke_width}
140
+ stroke-width={segments.length === 1
141
+ ? 0
142
+ : hovered_element === segment.element
143
+ ? stroke_width + 1
144
+ : stroke_width}
115
145
  class="pie-segment"
116
146
  class:interactive
117
147
  class:hovered={hovered_element === segment.element}
@@ -154,14 +184,12 @@ let hovered_element = $state(null);
154
184
  <span class="element-symbol" style:font-size="{14 * segment.font_scale}px">{
155
185
  segment.element
156
186
  }</span>
157
- {#if show_amounts}
158
- <sub class="amount" style:font-size="{10 * segment.font_scale}px">
159
- {segment.amount}
160
- </sub>{/if}
161
- {#if show_percentages}
162
- <sub class="percentage" style:font-size="{11 * segment.font_scale}px">
163
- {format_num(segment.fraction, `.1~%`)}
164
- </sub>
187
+ {#if show_amounts || show_percentages}
188
+ <sub class="amount" style:font-size="{8 * segment.font_scale}px">{
189
+ show_amounts ? segment.amount : ``
190
+ }{show_amounts && show_percentages ? `=` : ``}{
191
+ show_percentages ? format_num(segment.fraction, `.1~%`) : ``
192
+ }</sub>
165
193
  {/if}
166
194
  </div>
167
195
  </foreignObject>
@@ -215,9 +243,6 @@ let hovered_element = $state(null);
215
243
  }
216
244
  .amount {
217
245
  margin-left: 1px;
218
- transform: translateY(9pt);
219
- }
220
- .percentage {
221
- transform: translateY(4px);
246
+ transform: translateY(5pt);
222
247
  }
223
248
  </style>
@@ -1,6 +1,6 @@
1
+ import type { ColorSchemeName } from '../colors';
1
2
  import type { CompositionType } from './';
2
3
  import type { ElementSymbol } from '../element';
3
- import type { ColorSchemeName } from '../colors';
4
4
  import type { Snippet } from 'svelte';
5
5
  import type { SVGAttributes } from 'svelte/elements';
6
6
  import { type ChartSegmentData } from './index';
@@ -25,13 +25,15 @@ function extract_unique_elements(entries) {
25
25
  const elements = $derived(extract_unique_elements(entries));
26
26
  const element_count = $derived(elements.length);
27
27
  // Resolve hull face opacity: use caller's value if provided,
28
- // otherwise pick the right default for the dimensionality (ternary=30%, quaternary=3%)
29
- // Writable $derived handles forward sync (prop→local), back-sync $effect handles local→prop
28
+ // otherwise pick the right default for the dimensionality (ternary=30%, quaternary=3%).
29
+ // Use writable derived so child bind updates can sync back to parent.
30
30
  const default_opacity = $derived(element_count === 4
31
31
  ? DEFAULTS.convex_hull.quaternary.hull_face_opacity
32
32
  : DEFAULTS.convex_hull.ternary.hull_face_opacity);
33
33
  let hull_face_opacity = $derived(hull_face_opacity_prop ?? default_opacity);
34
34
  $effect(() => {
35
+ if (hull_face_opacity_prop === hull_face_opacity)
36
+ return;
35
37
  hull_face_opacity_prop = hull_face_opacity;
36
38
  });
37
39
  // Map element count to corresponding component
@@ -8,6 +8,6 @@ type ConvexHullProps = BaseConvexHullProps & Hull3DProps & {
8
8
  gas_config?: GasThermodynamicsConfig;
9
9
  gas_pressures?: Partial<Record<GasSpecies, number>>;
10
10
  };
11
- declare const ConvexHull: Component<ConvexHullProps, {}, "temperature" | "display" | "fullscreen" | "show_hull_faces" | "hull_face_opacity" | "color_mode" | "color_scale" | "show_stable" | "show_unstable" | "show_stable_labels" | "show_unstable_labels" | "max_hull_dist_show_phases" | "max_hull_dist_show_labels" | "info_pane_open" | "legend_pane_open" | "wrapper" | "energy_source_mode" | "stable_entries" | "unstable_entries" | "phase_stats" | "highlighted_entries" | "selected_entry" | "gas_pressures">;
11
+ declare const ConvexHull: Component<ConvexHullProps, {}, "temperature" | "display" | "color_scale" | "fullscreen" | "show_hull_faces" | "hull_face_opacity" | "color_mode" | "show_stable" | "show_unstable" | "show_stable_labels" | "show_unstable_labels" | "max_hull_dist_show_phases" | "max_hull_dist_show_labels" | "info_pane_open" | "legend_pane_open" | "wrapper" | "energy_source_mode" | "stable_entries" | "unstable_entries" | "phase_stats" | "highlighted_entries" | "selected_entry" | "gas_pressures">;
12
12
  type ConvexHull = ReturnType<typeof ConvexHull>;
13
13
  export default ConvexHull;
@@ -5,6 +5,7 @@ import { symbol_map } from '../labels';
5
5
  import { set_fullscreen_bg, setup_fullscreen_effect } from '../layout';
6
6
  import { ScatterPlot } from '../plot';
7
7
  import { DEFAULTS } from '../settings';
8
+ import { SvelteMap } from 'svelte/reactivity';
8
9
  import ConvexHullControls from './ConvexHullControls.svelte';
9
10
  import ConvexHullInfoPane from './ConvexHullInfoPane.svelte';
10
11
  import ConvexHullTooltip from './ConvexHullTooltip.svelte';
@@ -14,7 +15,7 @@ import { CONVEX_HULL_STYLE, default_controls, default_hull_config } from './inde
14
15
  import StructurePopup from './StructurePopup.svelte';
15
16
  import TemperatureSlider from './TemperatureSlider.svelte';
16
17
  import * as thermo from './thermodynamics';
17
- import { is_unary_entry } from './types';
18
+ import { HULL_STABILITY_TOL, is_unary_entry } from './types';
18
19
  // Binary convex hull rendered as energy vs composition (x in [0, 1])
19
20
  let { entries = [], controls = {}, config = {}, on_point_click, on_point_hover, fullscreen = $bindable(DEFAULTS.convex_hull.binary.fullscreen), enable_info_pane = true, wrapper = $bindable(), label_threshold = 50, show_stable = $bindable(DEFAULTS.convex_hull.binary.show_stable), show_unstable = $bindable(DEFAULTS.convex_hull.binary.show_unstable), color_mode = $bindable(DEFAULTS.convex_hull.binary.color_mode), color_scale = $bindable(DEFAULTS.convex_hull.binary.color_scale), info_pane_open = $bindable(DEFAULTS.convex_hull.binary.info_pane_open), legend_pane_open = $bindable(DEFAULTS.convex_hull.binary.legend_pane_open), max_hull_dist_show_phases = $bindable(DEFAULTS.convex_hull.binary.max_hull_dist_show_phases), max_hull_dist_show_labels = $bindable(DEFAULTS.convex_hull.binary.max_hull_dist_show_labels), show_stable_labels = $bindable(DEFAULTS.convex_hull.binary.show_stable_labels), show_unstable_labels = $bindable(DEFAULTS.convex_hull.binary.show_unstable_labels), on_file_drop, enable_click_selection = true, enable_structure_preview = true, energy_source_mode = $bindable(`precomputed`), phase_stats = $bindable(null), display = $bindable({ x_grid: false, y_grid: false }), stable_entries = $bindable([]), unstable_entries = $bindable([]), highlighted_entries = $bindable([]), highlight_style = {}, x_axis = {}, y_axis = {}, selected_entry = $bindable(null), temperature = $bindable(), interpolate_temperature = true, max_interpolation_gap = 500, gas_config, gas_pressures = $bindable({}), children, tooltip: custom_tooltip, ...rest } = $props();
20
21
  const merged_controls = $derived({ ...default_controls, ...controls });
@@ -106,36 +107,6 @@ function compute_binary_coordinates(raw_entries, elems) {
106
107
  }
107
108
  return coords;
108
109
  }
109
- function compute_lower_hull(points) {
110
- // Andrew's monotone chain for lower hull
111
- const sorted = [...points].sort((p1, p2) => (p1.x - p2.x) || (p1.y - p2.y));
112
- const lower = [];
113
- const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
114
- for (const p of sorted) {
115
- while (lower.length >= 2 &&
116
- cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0)
117
- lower.pop();
118
- lower.push(p);
119
- }
120
- return lower;
121
- }
122
- function interpolate_on_hull(hull, x) {
123
- if (hull.length < 2)
124
- return null;
125
- if (x <= hull[0].x)
126
- return hull[0].y;
127
- if (x >= hull[hull.length - 1].x)
128
- return hull[hull.length - 1].y;
129
- for (let i = 0; i < hull.length - 1; i++) {
130
- const p1 = hull[i];
131
- const p2 = hull[i + 1];
132
- if (x >= p1.x && x <= p2.x) {
133
- const t = (x - p1.x) / Math.max(1e-12, p2.x - p1.x);
134
- return p1.y * (1 - t) + p2.y * t;
135
- }
136
- }
137
- return null;
138
- }
139
110
  const coords_entries = $derived.by(() => {
140
111
  if (elements.length !== 2)
141
112
  return [];
@@ -152,28 +123,26 @@ const { all_enriched_entries, hull_points } = $derived.by(() => {
152
123
  if (coords_entries.length === 0) {
153
124
  return { all_enriched_entries: [], hull_points: [] };
154
125
  }
155
- // Build lower hull: group by x, use lowest energy per x
156
- // eslint-disable-next-line svelte/prefer-svelte-reactivity -- local var in derived
157
- const by_x = new Map();
126
+ // Build lower hull input: one minimum-energy point per composition x.
127
+ const min_y_by_x = new SvelteMap();
158
128
  for (const entry of coords_entries) {
159
- const group = by_x.get(entry.x);
160
- if (group)
161
- group.push(entry);
162
- else
163
- by_x.set(entry.x, [entry]);
129
+ const current_min_y = min_y_by_x.get(entry.x);
130
+ if (current_min_y === undefined || entry.y < current_min_y) {
131
+ min_y_by_x.set(entry.x, entry.y);
132
+ }
164
133
  }
165
- const hull_input = [...by_x].map(([x_coord, entries]) => ({
134
+ const hull_input = [...min_y_by_x].map(([x_coord, min_y]) => ({
166
135
  x: x_coord,
167
- y: Math.min(...entries.map((e) => e.y)),
136
+ y: min_y,
168
137
  }));
169
- const hull_points = compute_lower_hull(hull_input);
138
+ const hull_points = thermo.compute_lower_hull_2d(hull_input);
170
139
  const all_enriched_entries = coords_entries.map((entry) => {
171
- const y_hull = interpolate_on_hull(hull_points, entry.x);
140
+ const y_hull = thermo.interpolate_hull_2d(hull_points, entry.x);
172
141
  const e_above_hull = y_hull == null ? 0 : Math.max(0, entry.y - y_hull);
173
142
  return {
174
143
  ...entry,
175
144
  e_above_hull,
176
- is_stable: e_above_hull <= 1e-9,
145
+ is_stable: e_above_hull <= HULL_STABILITY_TOL,
177
146
  visible: true,
178
147
  };
179
148
  });
@@ -6,6 +6,6 @@ type $$ComponentProps = BaseConvexHullProps<ConvexHullEntry> & {
6
6
  x_axis?: AxisConfig;
7
7
  y_axis?: AxisConfig;
8
8
  };
9
- declare const ConvexHull2D: import("svelte").Component<$$ComponentProps, {}, "temperature" | "display" | "fullscreen" | "color_mode" | "color_scale" | "show_stable" | "show_unstable" | "show_stable_labels" | "show_unstable_labels" | "max_hull_dist_show_phases" | "max_hull_dist_show_labels" | "info_pane_open" | "legend_pane_open" | "wrapper" | "energy_source_mode" | "stable_entries" | "unstable_entries" | "phase_stats" | "highlighted_entries" | "selected_entry" | "gas_pressures">;
9
+ declare const ConvexHull2D: import("svelte").Component<$$ComponentProps, {}, "temperature" | "display" | "color_scale" | "fullscreen" | "color_mode" | "show_stable" | "show_unstable" | "show_stable_labels" | "show_unstable_labels" | "max_hull_dist_show_phases" | "max_hull_dist_show_labels" | "info_pane_open" | "legend_pane_open" | "wrapper" | "energy_source_mode" | "stable_entries" | "unstable_entries" | "phase_stats" | "highlighted_entries" | "selected_entry" | "gas_pressures">;
10
10
  type ConvexHull2D = ReturnType<typeof ConvexHull2D>;
11
11
  export default ConvexHull2D;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">import { add_alpha, AXIS_COLORS, is_dark_mode, NEG_AXIS_COLORS, PLOT_COLORS, vesta_hex, watch_dark_mode, } from '../colors';
2
2
  import { normalize_show_controls } from '../controls';
3
- import { ClickFeedback, DragOverlay } from '../feedback';
3
+ import { ClickFeedback, DragOverlay, Spinner } from '../feedback';
4
4
  import Icon from '../Icon.svelte';
5
5
  import { format_num } from '../labels';
6
6
  import { set_fullscreen_bg, setup_fullscreen_effect, toggle_fullscreen, } from '../layout';
@@ -146,7 +146,7 @@ let camera = $state({ ...camera_default });
146
146
  const GIZMO_CAM_DIST = 5;
147
147
  const MIN_ELEV_FOR_Z_AXIS = 5; // degrees — below this, z-axis ticks collapse to a point
148
148
  let gizmo_cam_ref = $state();
149
- let gizmo_orbit_ref = $state();
149
+ let gizmo_orbit_ref = $state(undefined);
150
150
  let gizmo_active = $state(false);
151
151
  // Convert elevation/azimuth (degrees) to Three.js camera position + up vector.
152
152
  function gizmo_camera(elev_deg, azim_deg) {
@@ -753,11 +753,13 @@ function render_frame() {
753
753
  ctx.fillStyle = `transparent`;
754
754
  ctx.fillRect(0, 0, display_width, display_height);
755
755
  if (elements.length !== 3) {
756
- ctx.fillStyle = text_color;
757
- ctx.font = `16px Arial`;
758
- ctx.textAlign = `center`;
759
- ctx.textBaseline = `middle`;
760
- ctx.fillText(`Ternary convex hull requires exactly 3 elements (got ${pd_data.elements.length})`, display_width / 2, display_height / 2);
756
+ if (elements.length > 0) {
757
+ ctx.fillStyle = text_color;
758
+ ctx.font = `16px Arial`;
759
+ ctx.textAlign = `center`;
760
+ ctx.textBaseline = `middle`;
761
+ ctx.fillText(`Ternary convex hull requires exactly 3 elements (got ${elements.length})`, display_width / 2, display_height / 2);
762
+ }
761
763
  return;
762
764
  }
763
765
  draw_structure_outline();
@@ -996,6 +998,13 @@ let style = $derived(`--hull-stable-color:${merged_config.colors?.stable || `#00
996
998
  onwheel={handle_wheel}
997
999
  ></canvas>
998
1000
 
1001
+ {#if entries.length === 0}
1002
+ <Spinner
1003
+ text="Loading data..."
1004
+ style="position: absolute; inset: 0; display: flex; align-items: center; justify-content: center"
1005
+ />
1006
+ {/if}
1007
+
999
1008
  <!-- Formation Energy Color Bar (bottom-left corner) -->
1000
1009
  {#if color_mode === `energy` && plot_entries.length > 0}
1001
1010
  {@const hull_distances = plot_entries
@@ -3,6 +3,6 @@ import type { ConvexHullEntry, HighlightStyle } from './types';
3
3
  type $$ComponentProps = BaseConvexHullProps<ConvexHullEntry> & Hull3DProps & {
4
4
  highlight_style?: HighlightStyle;
5
5
  };
6
- declare const ConvexHull3D: import("svelte").Component<$$ComponentProps, {}, "temperature" | "fullscreen" | "show_hull_faces" | "hull_face_opacity" | "hull_face_color_mode" | "color_mode" | "color_scale" | "show_stable" | "show_unstable" | "show_stable_labels" | "show_unstable_labels" | "max_hull_dist_show_phases" | "max_hull_dist_show_labels" | "info_pane_open" | "legend_pane_open" | "wrapper" | "energy_source_mode" | "stable_entries" | "unstable_entries" | "phase_stats" | "highlighted_entries" | "selected_entry" | "gas_pressures">;
6
+ declare const ConvexHull3D: import("svelte").Component<$$ComponentProps, {}, "temperature" | "color_scale" | "fullscreen" | "show_hull_faces" | "hull_face_opacity" | "hull_face_color_mode" | "color_mode" | "show_stable" | "show_unstable" | "show_stable_labels" | "show_unstable_labels" | "max_hull_dist_show_phases" | "max_hull_dist_show_labels" | "info_pane_open" | "legend_pane_open" | "wrapper" | "energy_source_mode" | "stable_entries" | "unstable_entries" | "phase_stats" | "highlighted_entries" | "selected_entry" | "gas_pressures">;
7
7
  type ConvexHull3D = ReturnType<typeof ConvexHull3D>;
8
8
  export default ConvexHull3D;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">import { add_alpha, is_dark_mode, PLOT_COLORS, vesta_hex, watch_dark_mode, } from '../colors';
2
2
  import { normalize_show_controls } from '../controls';
3
- import { ClickFeedback, DragOverlay } from '../feedback';
3
+ import { ClickFeedback, DragOverlay, Spinner } from '../feedback';
4
4
  import Icon from '../Icon.svelte';
5
5
  import { set_fullscreen_bg, setup_fullscreen_effect, toggle_fullscreen, } from '../layout';
6
6
  import { ColorBar, PlotTooltip } from '../plot';
@@ -616,11 +616,13 @@ function render_frame() {
616
616
  ctx.fillStyle = `transparent`; // Set background - use transparent to inherit from container
617
617
  ctx.fillRect(0, 0, display_width, display_height);
618
618
  if (elements.length !== 4) {
619
- ctx.fillStyle = text_color;
620
- ctx.font = `16px Arial`;
621
- ctx.textAlign = `center`;
622
- ctx.textBaseline = `middle`;
623
- ctx.fillText(`Quaternary convex hull requires exactly 4 elements (got ${pd_data.elements.length})`, display_width / 2, display_height / 2);
619
+ if (elements.length > 0) {
620
+ ctx.fillStyle = text_color;
621
+ ctx.font = `16px Arial`;
622
+ ctx.textAlign = `center`;
623
+ ctx.textBaseline = `middle`;
624
+ ctx.fillText(`Quaternary convex hull requires exactly 4 elements (got ${elements.length})`, display_width / 2, display_height / 2);
625
+ }
624
626
  return;
625
627
  }
626
628
  draw_structure_outline(); // Draw tetrahedron outline
@@ -648,7 +650,7 @@ const handle_mouse_move = (event) => {
648
650
  }
649
651
  else {
650
652
  camera.rotation_y += dx * 0.005;
651
- camera.rotation_x = Math.max(-Math.PI / 3, Math.min(Math.PI / 3, camera.rotation_x + dy * 0.005));
653
+ camera.rotation_x = Math.max(-Math.PI / 3, Math.min(Math.PI / 3, camera.rotation_x - dy * 0.005));
652
654
  }
653
655
  last_mouse = { x: event.clientX, y: event.clientY };
654
656
  };
@@ -797,6 +799,7 @@ let style = $derived(`--hull-stable-color:${merged_config.colors?.stable || `#00
797
799
  data-has-selection={selected_entry !== null}
798
800
  data-has-hover={hover_data !== null}
799
801
  data-is-dragging={is_dragging}
802
+ data-rotation-x={camera.rotation_x.toFixed(4)}
800
803
  data-rotation-y={camera.rotation_y.toFixed(4)}
801
804
  bind:this={wrapper}
802
805
  role="application"
@@ -835,6 +838,13 @@ let style = $derived(`--hull-stable-color:${merged_config.colors?.stable || `#00
835
838
  onwheel={handle_wheel}
836
839
  ></canvas>
837
840
 
841
+ {#if entries.length === 0}
842
+ <Spinner
843
+ text="Loading data..."
844
+ style="position: absolute; inset: 0; display: flex; align-items: center; justify-content: center"
845
+ />
846
+ {/if}
847
+
838
848
  <!-- Energy above hull Color Bar -->
839
849
  {#if color_mode === `energy` && plot_entries.length > 0}
840
850
  {@const hull_distances = plot_entries
@@ -3,6 +3,6 @@ import type { ConvexHullEntry, HighlightStyle } from './types';
3
3
  type $$ComponentProps = BaseConvexHullProps<ConvexHullEntry> & Hull3DProps & {
4
4
  highlight_style?: HighlightStyle;
5
5
  };
6
- declare const ConvexHull4D: import("svelte").Component<$$ComponentProps, {}, "temperature" | "fullscreen" | "show_hull_faces" | "hull_face_opacity" | "hull_face_color_mode" | "color_mode" | "color_scale" | "show_stable" | "show_unstable" | "show_stable_labels" | "show_unstable_labels" | "max_hull_dist_show_phases" | "max_hull_dist_show_labels" | "info_pane_open" | "legend_pane_open" | "wrapper" | "energy_source_mode" | "stable_entries" | "unstable_entries" | "phase_stats" | "highlighted_entries" | "selected_entry" | "gas_pressures">;
6
+ declare const ConvexHull4D: import("svelte").Component<$$ComponentProps, {}, "temperature" | "color_scale" | "fullscreen" | "show_hull_faces" | "hull_face_opacity" | "hull_face_color_mode" | "color_mode" | "show_stable" | "show_unstable" | "show_stable_labels" | "show_unstable_labels" | "max_hull_dist_show_phases" | "max_hull_dist_show_labels" | "info_pane_open" | "legend_pane_open" | "wrapper" | "energy_source_mode" | "stable_entries" | "unstable_entries" | "phase_stats" | "highlighted_entries" | "selected_entry" | "gas_pressures">;
7
7
  type ConvexHull4D = ReturnType<typeof ConvexHull4D>;
8
8
  export default ConvexHull4D;
@@ -43,6 +43,6 @@ type $$ComponentProps = Omit<HTMLAttributes<HTMLDivElement>, `onclose`> & {
43
43
  toggle_props?: ComponentProps<typeof DraggablePane>[`toggle_props`];
44
44
  pane_props?: ComponentProps<typeof DraggablePane>[`pane_props`];
45
45
  };
46
- declare const ConvexHullControls: import("svelte").Component<$$ComponentProps, {}, "controls_open" | "hull_face_opacity" | "color_mode" | "color_scale" | "show_stable" | "show_unstable" | "show_stable_labels" | "show_unstable_labels" | "max_hull_dist_show_phases" | "max_hull_dist_show_labels" | "energy_source_mode">;
46
+ declare const ConvexHullControls: import("svelte").Component<$$ComponentProps, {}, "color_scale" | "controls_open" | "hull_face_opacity" | "color_mode" | "show_stable" | "show_unstable" | "show_stable_labels" | "show_unstable_labels" | "max_hull_dist_show_phases" | "max_hull_dist_show_labels" | "energy_source_mode">;
47
47
  type ConvexHullControls = ReturnType<typeof ConvexHullControls>;
48
48
  export default ConvexHullControls;