matterviz 0.3.0 → 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 (286) hide show
  1. package/dist/FilePicker.svelte +37 -20
  2. package/dist/Icon.svelte +2 -2
  3. package/dist/MillerIndexInput.svelte +60 -0
  4. package/dist/MillerIndexInput.svelte.d.ts +7 -0
  5. package/dist/app.css +38 -2
  6. package/dist/brillouin/BrillouinZone.svelte +20 -62
  7. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  8. package/dist/brillouin/BrillouinZoneExportPane.svelte +12 -20
  9. package/dist/brillouin/BrillouinZoneScene.svelte +2 -2
  10. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  11. package/dist/chempot-diagram/ChemPotDiagram.svelte +192 -0
  12. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  13. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +677 -0
  14. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  15. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2688 -0
  16. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  17. package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -0
  18. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  19. package/dist/chempot-diagram/color.d.ts +10 -0
  20. package/dist/chempot-diagram/color.js +33 -0
  21. package/dist/chempot-diagram/compute.d.ts +38 -0
  22. package/dist/chempot-diagram/compute.js +650 -0
  23. package/dist/chempot-diagram/index.d.ts +5 -0
  24. package/dist/chempot-diagram/index.js +5 -0
  25. package/dist/chempot-diagram/pointer.d.ts +16 -0
  26. package/dist/chempot-diagram/pointer.js +40 -0
  27. package/dist/chempot-diagram/temperature.d.ts +15 -0
  28. package/dist/chempot-diagram/temperature.js +37 -0
  29. package/dist/chempot-diagram/types.d.ts +83 -0
  30. package/dist/chempot-diagram/types.js +27 -0
  31. package/dist/colors/index.d.ts +3 -1
  32. package/dist/colors/index.js +4 -0
  33. package/dist/composition/BarChart.svelte +13 -22
  34. package/dist/composition/BubbleChart.svelte +5 -3
  35. package/dist/composition/FormulaFilter.svelte +770 -90
  36. package/dist/composition/FormulaFilter.svelte.d.ts +37 -1
  37. package/dist/composition/PieChart.svelte +43 -18
  38. package/dist/composition/PieChart.svelte.d.ts +1 -1
  39. package/dist/constants.d.ts +1 -0
  40. package/dist/constants.js +2 -0
  41. package/dist/convex-hull/ConvexHull.svelte +14 -1
  42. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  43. package/dist/convex-hull/ConvexHull2D.svelte +14 -45
  44. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  45. package/dist/convex-hull/ConvexHull3D.svelte +396 -134
  46. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  47. package/dist/convex-hull/ConvexHull4D.svelte +93 -42
  48. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  49. package/dist/convex-hull/ConvexHullControls.svelte +94 -31
  50. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +4 -2
  51. package/dist/convex-hull/ConvexHullStats.svelte +697 -128
  52. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
  53. package/dist/convex-hull/ConvexHullTooltip.svelte +1 -0
  54. package/dist/convex-hull/GasPressureControls.svelte +72 -38
  55. package/dist/convex-hull/GasPressureControls.svelte.d.ts +2 -1
  56. package/dist/convex-hull/TemperatureSlider.svelte +46 -19
  57. package/dist/convex-hull/TemperatureSlider.svelte.d.ts +2 -1
  58. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  59. package/dist/convex-hull/demo-temperature.js +36 -0
  60. package/dist/convex-hull/gas-thermodynamics.js +16 -5
  61. package/dist/convex-hull/helpers.d.ts +7 -1
  62. package/dist/convex-hull/helpers.js +45 -15
  63. package/dist/convex-hull/index.d.ts +15 -1
  64. package/dist/convex-hull/index.js +1 -0
  65. package/dist/convex-hull/thermodynamics.d.ts +8 -21
  66. package/dist/convex-hull/thermodynamics.js +106 -17
  67. package/dist/convex-hull/types.d.ts +7 -0
  68. package/dist/convex-hull/types.js +11 -0
  69. package/dist/coordination/CoordinationBarPlot.svelte +29 -46
  70. package/dist/element/BohrAtom.svelte +1 -1
  71. package/dist/element/data.js +2 -14
  72. package/dist/element/data.json.gz +0 -0
  73. package/dist/element/index.d.ts +1 -1
  74. package/dist/element/index.js +1 -0
  75. package/dist/element/types.d.ts +1 -0
  76. package/dist/fermi-surface/FermiSurface.svelte +21 -65
  77. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  78. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  79. package/dist/fermi-surface/FermiSurfaceScene.svelte +1 -1
  80. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  81. package/dist/fermi-surface/compute.js +1 -21
  82. package/dist/fermi-surface/marching-cubes.d.ts +2 -13
  83. package/dist/fermi-surface/marching-cubes.js +2 -519
  84. package/dist/fermi-surface/parse.js +17 -23
  85. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1273 -0
  86. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  87. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +171 -0
  88. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +31 -0
  89. package/dist/heatmap-matrix/index.d.ts +53 -0
  90. package/dist/heatmap-matrix/index.js +100 -0
  91. package/dist/heatmap-matrix/shared.d.ts +2 -0
  92. package/dist/heatmap-matrix/shared.js +4 -0
  93. package/dist/icons.d.ts +119 -0
  94. package/dist/icons.js +119 -0
  95. package/dist/index.d.ts +6 -1
  96. package/dist/index.js +6 -1
  97. package/dist/io/export.js +15 -3
  98. package/dist/io/file-drop.d.ts +7 -0
  99. package/dist/io/file-drop.js +43 -0
  100. package/dist/io/index.d.ts +2 -2
  101. package/dist/io/index.js +2 -112
  102. package/dist/io/types.d.ts +1 -0
  103. package/dist/io/url-drop.d.ts +2 -0
  104. package/dist/io/url-drop.js +118 -0
  105. package/dist/isosurface/Isosurface.svelte +231 -0
  106. package/dist/isosurface/Isosurface.svelte.d.ts +8 -0
  107. package/dist/isosurface/IsosurfaceControls.svelte +273 -0
  108. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +9 -0
  109. package/dist/isosurface/index.d.ts +5 -0
  110. package/dist/isosurface/index.js +6 -0
  111. package/dist/isosurface/parse.d.ts +6 -0
  112. package/dist/isosurface/parse.js +548 -0
  113. package/dist/isosurface/slice.d.ts +11 -0
  114. package/dist/isosurface/slice.js +145 -0
  115. package/dist/isosurface/types.d.ts +55 -0
  116. package/dist/isosurface/types.js +178 -0
  117. package/dist/labels.d.ts +2 -1
  118. package/dist/labels.js +1 -0
  119. package/dist/layout/InfoTag.svelte +62 -62
  120. package/dist/layout/SubpageGrid.svelte +74 -0
  121. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  122. package/dist/layout/index.d.ts +1 -0
  123. package/dist/layout/index.js +1 -0
  124. package/dist/layout/json-tree/JsonNode.svelte +226 -53
  125. package/dist/layout/json-tree/JsonTree.svelte +425 -51
  126. package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
  127. package/dist/layout/json-tree/JsonValue.svelte +218 -97
  128. package/dist/layout/json-tree/types.d.ts +27 -2
  129. package/dist/layout/json-tree/utils.d.ts +14 -1
  130. package/dist/layout/json-tree/utils.js +254 -0
  131. package/dist/marching-cubes.d.ts +14 -0
  132. package/dist/marching-cubes.js +519 -0
  133. package/dist/math.d.ts +8 -0
  134. package/dist/math.js +374 -7
  135. package/dist/overlays/ContextMenu.svelte +3 -2
  136. package/dist/overlays/DraggablePane.svelte +163 -58
  137. package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
  138. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +232 -77
  139. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
  140. package/dist/phase-diagram/PhaseDiagramControls.svelte +32 -11
  141. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
  142. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +103 -0
  143. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  144. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +102 -95
  145. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +7 -0
  146. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +100 -26
  147. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
  148. package/dist/phase-diagram/index.d.ts +2 -0
  149. package/dist/phase-diagram/index.js +2 -0
  150. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  151. package/dist/phase-diagram/svg-to-diagram.js +865 -0
  152. package/dist/phase-diagram/types.d.ts +10 -0
  153. package/dist/phase-diagram/utils.d.ts +7 -4
  154. package/dist/phase-diagram/utils.js +149 -59
  155. package/dist/plot/AxisLabel.svelte +26 -0
  156. package/dist/plot/AxisLabel.svelte.d.ts +16 -0
  157. package/dist/plot/BarPlot.svelte +473 -228
  158. package/dist/plot/BarPlot.svelte.d.ts +3 -3
  159. package/dist/plot/BarPlotControls.svelte +3 -2
  160. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  161. package/dist/plot/ColorBar.svelte +54 -54
  162. package/dist/plot/ColorBar.svelte.d.ts +1 -1
  163. package/dist/plot/ElementScatter.svelte +4 -3
  164. package/dist/plot/FillArea.svelte +4 -1
  165. package/dist/plot/Histogram.svelte +320 -230
  166. package/dist/plot/Histogram.svelte.d.ts +2 -2
  167. package/dist/plot/HistogramControls.svelte +29 -10
  168. package/dist/plot/HistogramControls.svelte.d.ts +6 -2
  169. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +2 -2
  170. package/dist/plot/PlotControls.svelte +109 -27
  171. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  172. package/dist/plot/PlotLegend.svelte +1 -1
  173. package/dist/plot/PortalSelect.svelte +2 -1
  174. package/dist/plot/ReferenceLine.svelte +2 -1
  175. package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
  176. package/dist/plot/ReferencePlane.svelte +1 -3
  177. package/dist/plot/ScatterPlot.svelte +343 -209
  178. package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
  179. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  180. package/dist/plot/ScatterPlot3DControls.svelte +203 -250
  181. package/dist/plot/ScatterPlot3DScene.svelte +4 -7
  182. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  183. package/dist/plot/ScatterPlotControls.svelte +95 -55
  184. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  185. package/dist/plot/ZeroLines.svelte +44 -0
  186. package/dist/plot/ZeroLines.svelte.d.ts +32 -0
  187. package/dist/plot/ZoomRect.svelte +21 -0
  188. package/dist/plot/ZoomRect.svelte.d.ts +8 -0
  189. package/dist/plot/axis-utils.d.ts +1 -1
  190. package/dist/plot/data-cleaning.js +1 -5
  191. package/dist/plot/index.d.ts +6 -2
  192. package/dist/plot/index.js +6 -2
  193. package/dist/plot/interactions.d.ts +8 -10
  194. package/dist/plot/interactions.js +10 -19
  195. package/dist/plot/layout.d.ts +7 -1
  196. package/dist/plot/layout.js +12 -4
  197. package/dist/plot/reference-line.d.ts +4 -21
  198. package/dist/plot/reference-line.js +7 -81
  199. package/dist/plot/types.d.ts +42 -17
  200. package/dist/plot/types.js +10 -0
  201. package/dist/plot/utils/label-placement.js +14 -11
  202. package/dist/plot/utils.d.ts +1 -0
  203. package/dist/plot/utils.js +14 -0
  204. package/dist/rdf/RdfPlot.svelte +55 -66
  205. package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
  206. package/dist/rdf/index.d.ts +1 -1
  207. package/dist/rdf/index.js +1 -1
  208. package/dist/settings.d.ts +5 -0
  209. package/dist/settings.js +37 -3
  210. package/dist/spectral/Bands.svelte +515 -143
  211. package/dist/spectral/Bands.svelte.d.ts +22 -2
  212. package/dist/spectral/helpers.d.ts +23 -1
  213. package/dist/spectral/helpers.js +65 -9
  214. package/dist/spectral/types.d.ts +2 -0
  215. package/dist/structure/AtomLegend.svelte +31 -10
  216. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  217. package/dist/structure/CellSelect.svelte +92 -22
  218. package/dist/structure/Lattice.svelte +2 -0
  219. package/dist/structure/Structure.svelte +716 -173
  220. package/dist/structure/Structure.svelte.d.ts +7 -2
  221. package/dist/structure/StructureControls.svelte +26 -14
  222. package/dist/structure/StructureControls.svelte.d.ts +5 -1
  223. package/dist/structure/StructureInfoPane.svelte +7 -1
  224. package/dist/structure/StructureScene.svelte +386 -95
  225. package/dist/structure/StructureScene.svelte.d.ts +15 -4
  226. package/dist/structure/atom-properties.d.ts +6 -2
  227. package/dist/structure/atom-properties.js +38 -25
  228. package/dist/structure/export.js +10 -7
  229. package/dist/structure/ferrox-wasm-types.d.ts +3 -2
  230. package/dist/structure/ferrox-wasm-types.js +0 -3
  231. package/dist/structure/ferrox-wasm.d.ts +3 -2
  232. package/dist/structure/ferrox-wasm.js +1 -2
  233. package/dist/structure/index.d.ts +7 -0
  234. package/dist/structure/index.js +22 -0
  235. package/dist/structure/parse.js +19 -16
  236. package/dist/structure/partial-occupancy.d.ts +25 -0
  237. package/dist/structure/partial-occupancy.js +102 -0
  238. package/dist/structure/validation.js +6 -3
  239. package/dist/symmetry/SymmetryStats.svelte +18 -4
  240. package/dist/symmetry/WyckoffTable.svelte +18 -10
  241. package/dist/symmetry/index.d.ts +7 -4
  242. package/dist/symmetry/index.js +83 -18
  243. package/dist/table/HeatmapTable.svelte +468 -69
  244. package/dist/table/HeatmapTable.svelte.d.ts +13 -1
  245. package/dist/table/ToggleMenu.svelte +291 -44
  246. package/dist/table/ToggleMenu.svelte.d.ts +4 -1
  247. package/dist/table/index.d.ts +3 -0
  248. package/dist/tooltip/index.d.ts +1 -1
  249. package/dist/tooltip/index.js +1 -0
  250. package/dist/trajectory/Trajectory.svelte +147 -145
  251. package/dist/trajectory/TrajectoryExportPane.svelte +13 -9
  252. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
  253. package/dist/trajectory/constants.d.ts +6 -0
  254. package/dist/trajectory/constants.js +7 -0
  255. package/dist/trajectory/extract.js +3 -5
  256. package/dist/trajectory/format-detect.d.ts +9 -0
  257. package/dist/trajectory/format-detect.js +76 -0
  258. package/dist/trajectory/frame-reader.d.ts +17 -0
  259. package/dist/trajectory/frame-reader.js +339 -0
  260. package/dist/trajectory/helpers.d.ts +15 -0
  261. package/dist/trajectory/helpers.js +187 -0
  262. package/dist/trajectory/index.d.ts +1 -0
  263. package/dist/trajectory/index.js +11 -4
  264. package/dist/trajectory/parse/ase.d.ts +2 -0
  265. package/dist/trajectory/parse/ase.js +76 -0
  266. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  267. package/dist/trajectory/parse/hdf5.js +121 -0
  268. package/dist/trajectory/parse/index.d.ts +12 -0
  269. package/dist/trajectory/parse/index.js +304 -0
  270. package/dist/trajectory/parse/lammps.d.ts +5 -0
  271. package/dist/trajectory/parse/lammps.js +169 -0
  272. package/dist/trajectory/parse/vasp.d.ts +2 -0
  273. package/dist/trajectory/parse/vasp.js +65 -0
  274. package/dist/trajectory/parse/xyz.d.ts +2 -0
  275. package/dist/trajectory/parse/xyz.js +109 -0
  276. package/dist/trajectory/types.d.ts +11 -0
  277. package/dist/trajectory/types.js +1 -0
  278. package/dist/utils.d.ts +2 -0
  279. package/dist/utils.js +4 -0
  280. package/dist/xrd/XrdPlot.svelte +6 -4
  281. package/dist/xrd/calc-xrd.js +0 -1
  282. package/package.json +33 -23
  283. package/readme.md +4 -4
  284. package/dist/trajectory/parse.d.ts +0 -42
  285. package/dist/trajectory/parse.js +0 -1267
  286. /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
@@ -74,6 +74,15 @@ export interface LeverRuleResult {
74
74
  fraction_left: number;
75
75
  fraction_right: number;
76
76
  }
77
+ export interface VerticalLeverRuleResult {
78
+ bottom_phase: string;
79
+ top_phase: string;
80
+ bottom_temperature: number;
81
+ top_temperature: number;
82
+ fraction_bottom: number;
83
+ fraction_top: number;
84
+ }
85
+ export type LeverRuleMode = `horizontal` | `vertical`;
77
86
  export interface PhaseHoverInfo {
78
87
  region: PhaseRegion;
79
88
  composition: number;
@@ -83,6 +92,7 @@ export interface PhaseHoverInfo {
83
92
  y: number;
84
93
  };
85
94
  lever_rule?: LeverRuleResult;
95
+ vertical_lever_rule?: VerticalLeverRuleResult;
86
96
  special_point?: SpecialPoint;
87
97
  }
88
98
  export type PhaseDiagramTooltipConfig = TooltipConfig<PhaseHoverInfo>;
@@ -1,6 +1,6 @@
1
1
  import { type Vec2 } from '../math';
2
2
  import type { Sides } from '../plot';
3
- import type { CompUnit, LeverRuleResult, PhaseDiagramConfig, PhaseDiagramData, PhaseHoverInfo, PhaseRegion, TempUnit } from './types';
3
+ import type { CompUnit, LeverRuleMode, LeverRuleResult, PhaseDiagramConfig, PhaseDiagramData, PhaseHoverInfo, PhaseRegion, TempUnit, VerticalLeverRuleResult } from './types';
4
4
  export declare function convert_temp(value: number, from: TempUnit, to: TempUnit): number;
5
5
  export declare const PHASE_DIAGRAM_DEFAULTS: Readonly<{
6
6
  show_boundaries: true;
@@ -90,10 +90,11 @@ export declare function compute_label_properties(label: string, bounds: {
90
90
  scale: number;
91
91
  };
92
92
  export declare function transform_vertices(vertices: Vec2[], x_scale: (val: number) => number, y_scale: (val: number) => number): Vec2[];
93
- export declare function format_composition(value: number, unit?: CompUnit | string, include_unit?: boolean): string;
94
- export declare function format_temperature(value: number, unit?: TempUnit | string): string;
93
+ export declare function format_composition(value: number, unit?: CompUnit, include_unit?: boolean): string;
94
+ export declare function format_temperature(value: number, unit?: TempUnit): string;
95
95
  export declare function calculate_lever_rule(region: PhaseRegion, composition: number, temperature: number): LeverRuleResult | null;
96
- export declare function format_hover_info_text(info: PhaseHoverInfo, temp_unit?: string, comp_unit?: string, component_a?: string, component_b?: string): string;
96
+ export declare function calculate_vertical_lever_rule(region: PhaseRegion, composition: number, temperature: number): VerticalLeverRuleResult | null;
97
+ export declare function format_hover_info_text(info: PhaseHoverInfo, temp_unit?: TempUnit, comp_unit?: CompUnit, component_a?: string, component_b?: string, data_temp_unit?: TempUnit, lever_rule_mode?: LeverRuleMode): string;
97
98
  export declare function get_phase_stability_range(region: PhaseRegion): {
98
99
  t_min: number;
99
100
  t_max: number;
@@ -111,4 +112,6 @@ export interface FormulaToken {
111
112
  export declare function is_compound(name: string): boolean;
112
113
  export declare function tokenize_formula(formula: string): FormulaToken[];
113
114
  export declare function format_formula_svg(formula: string, use_subscripts?: boolean): string;
115
+ export declare const format_label_svg: (label: string, use_subscripts?: boolean) => string;
116
+ export declare const format_label_html: (label: string, use_subscripts?: boolean) => string;
114
117
  export declare function format_formula_html(formula: string, use_subscripts?: boolean): string;
@@ -49,7 +49,7 @@ export const PHASE_DIAGRAM_DEFAULTS = Object.freeze({
49
49
  special_point: `#d32f2f`,
50
50
  }),
51
51
  // Margins
52
- margin: Object.freeze({ t: 25, r: 15, b: 50, l: 60 }),
52
+ margin: Object.freeze({ t: 25, r: 25, b: 50, l: 60 }),
53
53
  // Export
54
54
  png_dpi: 150,
55
55
  });
@@ -94,7 +94,7 @@ export const PHASE_COLOR_RGB = Object.freeze(Object.fromEntries(Object.entries(P
94
94
  const PHASE_ALPHA = { two_phase: 0.5, default: 0.5, tie_line: 1 };
95
95
  export const PHASE_COLORS = Object.freeze(Object.fromEntries(Object.entries(PHASE_COLOR_HEX).map(([key, hex]) => [
96
96
  key,
97
- add_alpha(hex, PHASE_ALPHA[key] ?? 0.6),
97
+ add_alpha(hex, key in PHASE_ALPHA ? PHASE_ALPHA[key] : 0.6),
98
98
  ])));
99
99
  // Phase pattern matching rules: [substrings to match, color key, optional prefix check]
100
100
  // Order matters: theta before eta (since "theta" contains "eta" as substring)
@@ -127,7 +127,7 @@ export function get_phase_color_key(name) {
127
127
  }
128
128
  // Get phase color - returns rgba() by default, or RGB string if format='rgb'
129
129
  export function get_phase_color(name, format = `rgba`) {
130
- const lower = name.toLowerCase();
130
+ const lower = name.toLowerCase().trim();
131
131
  const key = lower.includes(`+`) ? `two_phase` : get_phase_color_key(name);
132
132
  return format === `rgb` ? PHASE_COLOR_RGB[key] : PHASE_COLORS[key];
133
133
  }
@@ -150,8 +150,9 @@ export function find_phase_at_point(composition, temperature, data) {
150
150
  // Search regions in reverse order so later-defined regions take precedence
151
151
  for (let idx = data.regions.length - 1; idx >= 0; idx--) {
152
152
  const region = data.regions[idx];
153
- if (point_in_polygon(composition, temperature, region.vertices))
153
+ if (point_in_polygon(composition, temperature, region.vertices)) {
154
154
  return region;
155
+ }
155
156
  }
156
157
  return null;
157
158
  }
@@ -199,7 +200,7 @@ export function compute_label_properties(label, bounds, font_size) {
199
200
  }
200
201
  // Scale down as last resort (min 70%)
201
202
  const scale = Math.max(0.7, Math.min(avail_w / label_width, avail_h / line_height, 1));
202
- const rotation = is_tall && avail_h / label_width > avail_w / label_width ? -90 : 0;
203
+ const rotation = is_tall ? -90 : 0;
203
204
  return { rotation, lines: [label], scale };
204
205
  }
205
206
  // Wrap text into multiple lines at delimiter boundaries
@@ -210,7 +211,7 @@ function wrap_text(text, max_chars) {
210
211
  const lines = [];
211
212
  let current_line = ``;
212
213
  for (const word of words) {
213
- const candidate = current_line ? `${current_line}_${word}` : word;
214
+ const candidate = current_line ? `${current_line} ${word}` : word;
214
215
  if (candidate.length <= max_chars) {
215
216
  current_line = candidate;
216
217
  }
@@ -222,7 +223,7 @@ function wrap_text(text, max_chars) {
222
223
  }
223
224
  if (current_line)
224
225
  lines.push(current_line);
225
- return lines.length > 0 ? lines : [text];
226
+ return lines;
226
227
  }
227
228
  // Transform data coordinates to SVG coordinates using scale functions
228
229
  export function transform_vertices(vertices, x_scale, y_scale) {
@@ -231,78 +232,156 @@ export function transform_vertices(vertices, x_scale, y_scale) {
231
232
  // Format composition value for display
232
233
  export function format_composition(value, unit = `at%`, include_unit = true) {
233
234
  if (unit === `fraction`)
234
- return format_num(value, `.3f`);
235
- const formatted = format_num(value * 100, `.3`);
235
+ return format_num(value, `.3~f`);
236
+ const formatted = format_num(value * 100, `.3~`);
236
237
  return include_unit ? `${formatted} ${unit}` : formatted;
237
238
  }
238
239
  // Format temperature value for display
239
240
  export function format_temperature(value, unit = `K`) {
240
241
  return `${format_num(value, `.0f`)} ${unit}`;
241
242
  }
242
- // Calculate lever rule for a point in a two-phase region
243
- // Returns null if the region is not exactly a two-phase region or calculation fails
244
- // Note: Lever rule is thermodynamically defined only for two-phase equilibria
245
- export function calculate_lever_rule(region, composition, temperature) {
246
- // Only works for exactly two-phase regions (lever rule undefined for 3+ phases)
247
- if (!region.name.includes(`+`))
248
- return null;
249
- const phase_count = region.name.split(`+`).filter((s) => s.trim()).length;
250
- if (phase_count !== 2)
243
+ // Parse a two-phase region name into its two phase names
244
+ // Returns null if the region is not exactly a two-phase region
245
+ function parse_two_phases(name) {
246
+ if (!name.includes(`+`))
251
247
  return null;
252
- // Find horizontal intersections with polygon edges at this temperature
248
+ const parts = name.trim().split(/\s*\+\s*/).filter(Boolean);
249
+ return parts.length === 2 ? [parts[0], parts[1]] : null;
250
+ }
251
+ // Find polygon edge intersections along a scan line (horizontal or vertical)
252
+ // For horizontal: fixed_val = temperature, returns x-intersections
253
+ // For vertical: fixed_val = composition, returns y-intersections
254
+ function find_polygon_intersections(vertices, fixed_val, axis) {
255
+ const other = axis === 0 ? 1 : 0;
253
256
  const intersections = [];
254
- const n = region.vertices.length;
255
- for (let idx = 0; idx < n; idx++) {
256
- const [x1, y1] = region.vertices[idx];
257
- const [x2, y2] = region.vertices[(idx + 1) % n];
258
- if ((y1 <= temperature && y2 > temperature) || (y2 <= temperature && y1 > temperature)) {
259
- intersections.push(x1 + ((temperature - y1) / (y2 - y1)) * (x2 - x1));
257
+ for (let idx = 0; idx < vertices.length; idx++) {
258
+ const v1 = vertices[idx];
259
+ const v2 = vertices[(idx + 1) % vertices.length];
260
+ if ((v1[axis] <= fixed_val && v2[axis] > fixed_val) ||
261
+ (v2[axis] <= fixed_val && v1[axis] > fixed_val)) {
262
+ intersections.push(v1[other] +
263
+ ((fixed_val - v1[axis]) / (v2[axis] - v1[axis])) * (v2[other] - v1[other]));
260
264
  }
261
265
  }
266
+ return intersections.sort((a, b) => a - b);
267
+ }
268
+ function pick_bracketing_intersection_pair(intersections, position) {
262
269
  if (intersections.length < 2)
263
270
  return null;
264
- intersections.sort((a, b) => a - b);
265
- const left_composition = intersections[0];
266
- const right_composition = intersections[intersections.length - 1];
267
- if (composition < left_composition || composition > right_composition)
271
+ const unique_intersections = [];
272
+ const dedup_tol = 1e-9;
273
+ for (const value of intersections) {
274
+ const prev_value = unique_intersections.at(-1);
275
+ if (prev_value === undefined || Math.abs(value - prev_value) > dedup_tol) {
276
+ unique_intersections.push(value);
277
+ }
278
+ }
279
+ if (unique_intersections.length < 2)
280
+ return null;
281
+ const bound_tol = 1e-10;
282
+ for (let pair_idx = 0; pair_idx + 1 < unique_intersections.length; pair_idx += 2) {
283
+ const low_bound = unique_intersections[pair_idx];
284
+ const high_bound = unique_intersections[pair_idx + 1];
285
+ if (position >= low_bound - bound_tol && position <= high_bound + bound_tol) {
286
+ return [low_bound, high_bound];
287
+ }
288
+ }
289
+ // Fallback for numerical edge cases where even-odd pairing fails:
290
+ // pick nearest enclosing neighbors around the hovered point.
291
+ let left_idx = -1;
292
+ for (let idx = 0; idx < unique_intersections.length; idx++) {
293
+ if (unique_intersections[idx] <= position + bound_tol)
294
+ left_idx = idx;
295
+ }
296
+ if (left_idx < 0 || left_idx + 1 >= unique_intersections.length)
268
297
  return null;
269
- const total_width = right_composition - left_composition;
270
- if (total_width < 1e-10)
298
+ const left_bound = unique_intersections[left_idx];
299
+ const right_bound = unique_intersections[left_idx + 1];
300
+ return right_bound - left_bound > bound_tol ? [left_bound, right_bound] : null;
301
+ }
302
+ // Shared core for lever rule calculations (horizontal and vertical)
303
+ // Parses phases, finds intersections along the scan axis, validates bounds,
304
+ // and computes the fractional position within the two-phase region.
305
+ function lever_rule_core(region, position, scan_val, axis) {
306
+ const phases = parse_two_phases(region.name);
307
+ if (!phases)
308
+ return null;
309
+ const intersections = find_polygon_intersections(region.vertices, scan_val, axis);
310
+ const bounds = pick_bracketing_intersection_pair(intersections, position);
311
+ if (!bounds)
312
+ return null;
313
+ const [lo, hi] = bounds;
314
+ const span = hi - lo;
315
+ if (span < 1e-10)
316
+ return null;
317
+ return { phases, lo, hi, fraction_hi: (position - lo) / span };
318
+ }
319
+ // Calculate lever rule for a point in a two-phase region
320
+ // Returns null if the region is not exactly a two-phase region or calculation fails
321
+ // Note: Lever rule is thermodynamically defined only for two-phase equilibria
322
+ export function calculate_lever_rule(region, composition, temperature) {
323
+ // Horizontal scan: fixed temperature, find composition intersections
324
+ const core = lever_rule_core(region, composition, temperature, 1);
325
+ if (!core)
271
326
  return null;
272
- const fraction_right = (composition - left_composition) / total_width;
273
- const fraction_left = 1 - fraction_right;
274
- // Parse phase names from "α + β" format
275
- const parts = region.name.split(/\s*\+\s*/);
276
- const left_phase = parts[0]?.trim() || `Phase 1`;
277
- const right_phase = parts[1]?.trim() || `Phase 2`;
278
327
  return {
279
- left_phase,
280
- right_phase,
281
- left_composition,
282
- right_composition,
283
- fraction_left,
284
- fraction_right,
328
+ left_phase: core.phases[0],
329
+ right_phase: core.phases[1],
330
+ left_composition: core.lo,
331
+ right_composition: core.hi,
332
+ fraction_left: 1 - core.fraction_hi,
333
+ fraction_right: core.fraction_hi,
334
+ };
335
+ }
336
+ // Calculate vertical lever rule for a point in a two-phase region
337
+ // Uses constant composition (vertical line) to find temperature boundaries
338
+ export function calculate_vertical_lever_rule(region, composition, temperature) {
339
+ // Vertical scan: fixed composition, find temperature intersections
340
+ const core = lever_rule_core(region, temperature, composition, 0);
341
+ if (!core)
342
+ return null;
343
+ return {
344
+ bottom_phase: core.phases[0],
345
+ top_phase: core.phases[1],
346
+ bottom_temperature: core.lo,
347
+ top_temperature: core.hi,
348
+ fraction_bottom: 1 - core.fraction_hi,
349
+ fraction_top: core.fraction_hi,
285
350
  };
286
351
  }
287
352
  // Format hover info as copyable text for clipboard
288
- export function format_hover_info_text(info, temp_unit = `K`, comp_unit = `at%`, component_a = `A`, component_b = `B`) {
353
+ // Only includes lever rule data for the active mode to match tooltip display
354
+ export function format_hover_info_text(info, temp_unit = `K`, comp_unit = `at%`, component_a = `A`, component_b = `B`, data_temp_unit = temp_unit, lever_rule_mode = `horizontal`) {
355
+ // Convert temperature from data unit to display unit
356
+ const to_display = (temp) => convert_temp(temp, data_temp_unit, temp_unit);
289
357
  const lines = [
290
358
  `Phase: ${info.region.name}`,
291
- `Temperature: ${format_temperature(info.temperature, temp_unit)}`,
359
+ `Temperature: ${format_temperature(to_display(info.temperature), temp_unit)}`,
292
360
  `Composition: ${format_composition(info.composition, comp_unit)} ${component_b} (${format_composition(1 - info.composition, comp_unit)} ${component_a})`,
293
361
  ];
294
- if (info.lever_rule) {
362
+ if (lever_rule_mode === `horizontal` && info.lever_rule) {
295
363
  const lr = info.lever_rule;
296
364
  lines.push(``, `Lever Rule:`, ` ${lr.left_phase}: ${format_num(lr.fraction_left * 100, `.1f`)}% (at ${format_composition(lr.left_composition, comp_unit)})`, ` ${lr.right_phase}: ${format_num(lr.fraction_right * 100, `.1f`)}% (at ${format_composition(lr.right_composition, comp_unit)})`);
297
365
  }
366
+ if (lever_rule_mode === `vertical` && info.vertical_lever_rule) {
367
+ const vlr = info.vertical_lever_rule;
368
+ lines.push(``, `Vertical Lever Rule:`, ` ${vlr.bottom_phase}: ${format_num(vlr.fraction_bottom * 100, `.1f`)}% (at ${format_temperature(to_display(vlr.bottom_temperature), temp_unit)})`, ` ${vlr.top_phase}: ${format_num(vlr.fraction_top * 100, `.1f`)}% (at ${format_temperature(to_display(vlr.top_temperature), temp_unit)})`);
369
+ }
298
370
  return lines.join(`\n`);
299
371
  }
300
372
  // Calculate temperature stability range for a phase at given composition
301
373
  export function get_phase_stability_range(region) {
302
374
  if (!region.vertices?.length)
303
375
  return null;
304
- const temps = region.vertices.map(([, temp]) => temp);
305
- return { t_min: Math.min(...temps), t_max: Math.max(...temps) };
376
+ let t_min = Infinity;
377
+ let t_max = -Infinity;
378
+ for (const [, temp] of region.vertices) {
379
+ if (temp < t_min)
380
+ t_min = temp;
381
+ if (temp > t_max)
382
+ t_max = temp;
383
+ }
384
+ return { t_min, t_max };
306
385
  }
307
386
  // Extract reference/citation from TDB comments
308
387
  export function extract_tdb_reference(comments) {
@@ -325,8 +404,8 @@ export function summarize_models(phases) {
325
404
  counts.set(phase.sublattice_count, (counts.get(phase.sublattice_count) ?? 0) + 1);
326
405
  }
327
406
  return [...counts.entries()]
328
- .sort(([a], [b]) => a - b)
329
- .map(([n, c]) => `${c}×${n}-SL`)
407
+ .sort(([sl_a], [sl_b]) => sl_a - sl_b)
408
+ .map(([sublattices, count]) => `${count}×${sublattices}-SL`)
330
409
  .join(`, `);
331
410
  }
332
411
  // Check if a component name is a compound (vs single element)
@@ -342,13 +421,7 @@ export function is_compound(name) {
342
421
  // Single element pattern: one uppercase followed by optional lowercase (Fe, Ca, He, C)
343
422
  if (/^[A-Z][a-z]?$/.test(name))
344
423
  return false;
345
- // Count uppercase letters without array allocation
346
- let uppercase_count = 0;
347
- for (const char of name) {
348
- if (char >= `A` && char <= `Z`)
349
- uppercase_count++;
350
- }
351
- return uppercase_count >= 2;
424
+ return (name.match(/[A-Z]/g)?.length ?? 0) >= 2;
352
425
  }
353
426
  // Tokenize a chemical formula for rendering with subscripts/superscripts
354
427
  // Examples:
@@ -447,15 +520,32 @@ export function format_formula_svg(formula, use_subscripts = true) {
447
520
  offset += dy;
448
521
  }
449
522
  }
523
+ // Reset baseline after trailing subscript/superscript using a zero-width space
524
+ // (empty tspans may not apply dy in all SVG renderers)
450
525
  if (offset)
451
- result += `<tspan dy="${-offset}em"></tspan>`;
526
+ result += `<tspan dy="${-offset}em">\u200B</tspan>`;
452
527
  return result;
453
528
  }
529
+ // Split a multi-phase label on " + " and format each part with the given formatter
530
+ function format_label_parts(label, use_subscripts, formatter) {
531
+ if (!use_subscripts)
532
+ return label;
533
+ return label.split(/(\s*\+\s*)/).map((part) => {
534
+ if (part.trim() === `+`)
535
+ return part;
536
+ return formatter(part.trim(), use_subscripts);
537
+ }).join(``);
538
+ }
539
+ // Format a phase region label (e.g. "La2NiO4 + NiO") as SVG with subscripts
540
+ export const format_label_svg = (label, use_subscripts = true) => format_label_parts(label, use_subscripts, format_formula_svg);
541
+ // Format a phase region label as HTML with subscripts (splits on " + ")
542
+ export const format_label_html = (label, use_subscripts = true) => format_label_parts(label, use_subscripts, format_formula_html);
454
543
  // Format chemical formula as HTML with <sub> and <sup> tags
455
544
  export function format_formula_html(formula, use_subscripts = true) {
456
545
  if (!use_subscripts || !is_compound(formula))
457
546
  return formula;
458
547
  return tokenize_formula(formula)
459
- .map((token) => token.text ?? (token.sub ? `<sub>${token.sub}</sub>` : `<sup>${token.sup}</sup>`))
548
+ .map((token) => token.text ??
549
+ (token.sub ? `<sub>${token.sub}</sub>` : `<sup>${token.sup}</sup>`))
460
550
  .join(``);
461
551
  }
@@ -0,0 +1,26 @@
1
+ <script lang="ts">import { AXIS_LABEL_CONTAINER } from './axis-utils';
2
+ import InteractiveAxisLabel from './InteractiveAxisLabel.svelte';
3
+ let { x, y, rotate = false, label = ``, options, selected_key, color, loading = false, axis_type, on_select, } = $props();
4
+ </script>
5
+
6
+ <foreignObject
7
+ x={x - AXIS_LABEL_CONTAINER.x_offset}
8
+ y={y - AXIS_LABEL_CONTAINER.y_offset}
9
+ width={AXIS_LABEL_CONTAINER.width}
10
+ height={AXIS_LABEL_CONTAINER.height}
11
+ style="overflow: visible; pointer-events: none"
12
+ transform={rotate ? `rotate(-90, ${x}, ${y})` : undefined}
13
+ >
14
+ <div xmlns="http://www.w3.org/1999/xhtml" style="pointer-events: auto">
15
+ <InteractiveAxisLabel
16
+ {label}
17
+ {options}
18
+ {selected_key}
19
+ {loading}
20
+ {axis_type}
21
+ {color}
22
+ {on_select}
23
+ class="axis-label {axis_type}-label"
24
+ />
25
+ </div>
26
+ </foreignObject>
@@ -0,0 +1,16 @@
1
+ import type { AxisOption } from './types';
2
+ type $$ComponentProps = {
3
+ x: number;
4
+ y: number;
5
+ rotate?: boolean;
6
+ label?: string;
7
+ options?: AxisOption[];
8
+ selected_key?: string;
9
+ color?: string | null;
10
+ loading?: boolean;
11
+ axis_type: `x` | `x2` | `y` | `y2`;
12
+ on_select?: (key: string) => void;
13
+ };
14
+ declare const AxisLabel: import("svelte").Component<$$ComponentProps, {}, "">;
15
+ type AxisLabel = ReturnType<typeof AxisLabel>;
16
+ export default AxisLabel;