matterviz 0.3.1 → 0.3.3

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 (358) hide show
  1. package/dist/EmptyState.svelte +10 -2
  2. package/dist/FilePicker.svelte +154 -96
  3. package/dist/Icon.svelte +20 -14
  4. package/dist/MillerIndexInput.svelte +27 -21
  5. package/dist/api/optimade.js +6 -6
  6. package/dist/app.css +216 -178
  7. package/dist/brillouin/BrillouinZone.svelte +299 -198
  8. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  9. package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
  10. package/dist/brillouin/BrillouinZoneExportPane.svelte +74 -55
  11. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
  12. package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
  13. package/dist/brillouin/BrillouinZoneScene.svelte +277 -165
  14. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  15. package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
  16. package/dist/brillouin/compute.js +11 -6
  17. package/dist/chempot-diagram/ChemPotDiagram.svelte +327 -0
  18. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  19. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +847 -0
  20. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  21. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +3194 -0
  22. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  23. package/dist/chempot-diagram/ChemPotScene3D.svelte +11 -0
  24. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  25. package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
  26. package/dist/chempot-diagram/async-compute.svelte.js +77 -0
  27. package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
  28. package/dist/chempot-diagram/chempot-worker.js +11 -0
  29. package/dist/chempot-diagram/color.d.ts +10 -0
  30. package/dist/chempot-diagram/color.js +32 -0
  31. package/dist/chempot-diagram/compute.d.ts +48 -0
  32. package/dist/chempot-diagram/compute.js +812 -0
  33. package/dist/chempot-diagram/index.d.ts +6 -0
  34. package/dist/chempot-diagram/index.js +6 -0
  35. package/dist/chempot-diagram/pointer.d.ts +16 -0
  36. package/dist/chempot-diagram/pointer.js +40 -0
  37. package/dist/chempot-diagram/temperature.d.ts +15 -0
  38. package/dist/chempot-diagram/temperature.js +36 -0
  39. package/dist/chempot-diagram/types.d.ts +86 -0
  40. package/dist/chempot-diagram/types.js +28 -0
  41. package/dist/colors/index.d.ts +3 -1
  42. package/dist/colors/index.js +9 -3
  43. package/dist/composition/BarChart.svelte +141 -77
  44. package/dist/composition/BubbleChart.svelte +107 -52
  45. package/dist/composition/Composition.svelte +100 -79
  46. package/dist/composition/Formula.svelte +108 -62
  47. package/dist/composition/FormulaFilter.svelte +973 -353
  48. package/dist/composition/FormulaFilter.svelte.d.ts +35 -1
  49. package/dist/composition/PieChart.svelte +199 -99
  50. package/dist/composition/PieChart.svelte.d.ts +1 -1
  51. package/dist/composition/format.d.ts +5 -0
  52. package/dist/composition/format.js +20 -3
  53. package/dist/composition/parse.js +14 -9
  54. package/dist/convex-hull/ConvexHull.svelte +93 -38
  55. package/dist/convex-hull/ConvexHull2D.svelte +551 -393
  56. package/dist/convex-hull/ConvexHull3D.svelte +1303 -825
  57. package/dist/convex-hull/ConvexHull4D.svelte +1012 -686
  58. package/dist/convex-hull/ConvexHullControls.svelte +115 -28
  59. package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
  60. package/dist/convex-hull/ConvexHullStats.svelte +821 -249
  61. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
  62. package/dist/convex-hull/ConvexHullTooltip.svelte +41 -16
  63. package/dist/convex-hull/GasPressureControls.svelte +104 -61
  64. package/dist/convex-hull/StructurePopup.svelte +25 -4
  65. package/dist/convex-hull/TemperatureSlider.svelte +45 -25
  66. package/dist/convex-hull/barycentric-coords.js +13 -7
  67. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  68. package/dist/convex-hull/demo-temperature.js +40 -0
  69. package/dist/convex-hull/gas-thermodynamics.js +17 -12
  70. package/dist/convex-hull/helpers.d.ts +10 -1
  71. package/dist/convex-hull/helpers.js +79 -38
  72. package/dist/convex-hull/index.d.ts +1 -0
  73. package/dist/convex-hull/index.js +1 -0
  74. package/dist/convex-hull/thermodynamics.d.ts +8 -21
  75. package/dist/convex-hull/thermodynamics.js +163 -69
  76. package/dist/convex-hull/types.d.ts +12 -12
  77. package/dist/convex-hull/types.js +0 -12
  78. package/dist/coordination/CoordinationBarPlot.svelte +232 -176
  79. package/dist/element/BohrAtom.svelte +56 -13
  80. package/dist/element/ElementHeading.svelte +7 -2
  81. package/dist/element/ElementPhoto.svelte +15 -9
  82. package/dist/element/ElementStats.svelte +10 -4
  83. package/dist/element/ElementTile.svelte +137 -73
  84. package/dist/element/Nucleus.svelte +39 -11
  85. package/dist/element/data.js +2 -14
  86. package/dist/element/data.json.gz +0 -0
  87. package/dist/element/types.d.ts +1 -0
  88. package/dist/feedback/ClickFeedback.svelte +16 -5
  89. package/dist/feedback/DragOverlay.svelte +10 -2
  90. package/dist/feedback/Spinner.svelte +4 -2
  91. package/dist/feedback/StatusMessage.svelte +8 -2
  92. package/dist/fermi-surface/FermiSlice.svelte +118 -88
  93. package/dist/fermi-surface/FermiSurface.svelte +336 -239
  94. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  95. package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
  96. package/dist/fermi-surface/FermiSurfaceScene.svelte +536 -343
  97. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  98. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
  99. package/dist/fermi-surface/compute.js +16 -20
  100. package/dist/fermi-surface/parse.js +37 -33
  101. package/dist/fermi-surface/symmetry.js +2 -7
  102. package/dist/fermi-surface/types.d.ts +3 -5
  103. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1527 -0
  104. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  105. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
  106. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +30 -0
  107. package/dist/heatmap-matrix/index.d.ts +53 -0
  108. package/dist/heatmap-matrix/index.js +100 -0
  109. package/dist/heatmap-matrix/shared.d.ts +2 -0
  110. package/dist/heatmap-matrix/shared.js +4 -0
  111. package/dist/icons.d.ts +111 -0
  112. package/dist/icons.js +158 -0
  113. package/dist/index.d.ts +5 -2
  114. package/dist/index.js +5 -2
  115. package/dist/io/decompress.js +1 -1
  116. package/dist/io/export.d.ts +3 -0
  117. package/dist/io/export.js +138 -140
  118. package/dist/io/file-drop.d.ts +7 -0
  119. package/dist/io/file-drop.js +43 -0
  120. package/dist/io/index.d.ts +2 -2
  121. package/dist/io/index.js +2 -112
  122. package/dist/io/is-binary.js +2 -3
  123. package/dist/io/types.d.ts +1 -0
  124. package/dist/io/url-drop.d.ts +2 -0
  125. package/dist/io/url-drop.js +117 -0
  126. package/dist/isosurface/Isosurface.svelte +220 -110
  127. package/dist/isosurface/IsosurfaceControls.svelte +65 -28
  128. package/dist/isosurface/parse.js +104 -56
  129. package/dist/isosurface/slice.d.ts +2 -1
  130. package/dist/isosurface/slice.js +8 -13
  131. package/dist/isosurface/types.d.ts +14 -1
  132. package/dist/isosurface/types.js +152 -5
  133. package/dist/labels.d.ts +2 -1
  134. package/dist/labels.js +12 -8
  135. package/dist/layout/FullscreenToggle.svelte +11 -2
  136. package/dist/layout/InfoCard.svelte +38 -6
  137. package/dist/layout/InfoTag.svelte +125 -94
  138. package/dist/layout/PropertyFilter.svelte +82 -37
  139. package/dist/layout/SettingsSection.svelte +85 -55
  140. package/dist/layout/SubpageGrid.svelte +82 -0
  141. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  142. package/dist/layout/index.d.ts +1 -0
  143. package/dist/layout/index.js +1 -0
  144. package/dist/layout/json-tree/JsonNode.svelte +266 -223
  145. package/dist/layout/json-tree/JsonTree.svelte +516 -429
  146. package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
  147. package/dist/layout/json-tree/JsonValue.svelte +281 -173
  148. package/dist/layout/json-tree/types.d.ts +10 -2
  149. package/dist/layout/json-tree/utils.d.ts +2 -0
  150. package/dist/layout/json-tree/utils.js +37 -2
  151. package/dist/marching-cubes.js +25 -2
  152. package/dist/math.d.ts +20 -17
  153. package/dist/math.js +474 -57
  154. package/dist/overlays/ContextMenu.svelte +66 -40
  155. package/dist/overlays/DraggablePane.svelte +331 -154
  156. package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
  157. package/dist/periodic-table/PeriodicTable.svelte +278 -145
  158. package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
  159. package/dist/periodic-table/PropertySelect.svelte +25 -7
  160. package/dist/periodic-table/TableInset.svelte +8 -3
  161. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +559 -267
  162. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
  163. package/dist/phase-diagram/PhaseDiagramControls.svelte +131 -51
  164. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
  165. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +126 -0
  166. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  167. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +160 -110
  168. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +8 -1
  169. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +217 -86
  170. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
  171. package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
  172. package/dist/phase-diagram/build-diagram.js +9 -9
  173. package/dist/phase-diagram/colors.js +1 -3
  174. package/dist/phase-diagram/index.d.ts +2 -0
  175. package/dist/phase-diagram/index.js +2 -0
  176. package/dist/phase-diagram/parse.js +10 -9
  177. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  178. package/dist/phase-diagram/svg-to-diagram.js +869 -0
  179. package/dist/phase-diagram/types.d.ts +10 -0
  180. package/dist/phase-diagram/utils.d.ts +8 -4
  181. package/dist/phase-diagram/utils.js +219 -74
  182. package/dist/plot/AxisLabel.svelte +51 -0
  183. package/dist/plot/AxisLabel.svelte.d.ts +16 -0
  184. package/dist/plot/BarPlot.svelte +1461 -768
  185. package/dist/plot/BarPlot.svelte.d.ts +3 -3
  186. package/dist/plot/BarPlotControls.svelte +33 -6
  187. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  188. package/dist/plot/ColorBar.svelte +533 -383
  189. package/dist/plot/ColorBar.svelte.d.ts +1 -1
  190. package/dist/plot/ColorScaleSelect.svelte +28 -7
  191. package/dist/plot/ElementScatter.svelte +38 -16
  192. package/dist/plot/FillArea.svelte +152 -92
  193. package/dist/plot/Histogram.svelte +1162 -709
  194. package/dist/plot/Histogram.svelte.d.ts +1 -1
  195. package/dist/plot/HistogramControls.svelte +81 -18
  196. package/dist/plot/HistogramControls.svelte.d.ts +6 -2
  197. package/dist/plot/InteractiveAxisLabel.svelte +34 -11
  198. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
  199. package/dist/plot/Line.svelte +63 -28
  200. package/dist/plot/PlotControls.svelte +221 -96
  201. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  202. package/dist/plot/PlotLegend.svelte +174 -91
  203. package/dist/plot/PlotTooltip.svelte +45 -6
  204. package/dist/plot/PortalSelect.svelte +175 -146
  205. package/dist/plot/ReferenceLine.svelte +77 -22
  206. package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
  207. package/dist/plot/ReferenceLine3D.svelte +132 -107
  208. package/dist/plot/ReferencePlane.svelte +146 -123
  209. package/dist/plot/ScatterPlot.svelte +1880 -1156
  210. package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
  211. package/dist/plot/ScatterPlot3D.svelte +256 -131
  212. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  213. package/dist/plot/ScatterPlot3DControls.svelte +300 -297
  214. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
  215. package/dist/plot/ScatterPlot3DScene.svelte +608 -406
  216. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  217. package/dist/plot/ScatterPlotControls.svelte +150 -70
  218. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  219. package/dist/plot/ScatterPoint.svelte +98 -26
  220. package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
  221. package/dist/plot/SpacegroupBarPlot.svelte +142 -85
  222. package/dist/plot/Surface3D.svelte +159 -108
  223. package/dist/plot/ZeroLines.svelte +96 -0
  224. package/dist/plot/ZeroLines.svelte.d.ts +32 -0
  225. package/dist/plot/ZoomRect.svelte +23 -0
  226. package/dist/plot/ZoomRect.svelte.d.ts +8 -0
  227. package/dist/plot/axis-utils.d.ts +1 -1
  228. package/dist/plot/axis-utils.js +1 -3
  229. package/dist/plot/data-cleaning.js +12 -28
  230. package/dist/plot/data-transform.js +2 -1
  231. package/dist/plot/fill-utils.js +2 -0
  232. package/dist/plot/index.d.ts +6 -2
  233. package/dist/plot/index.js +6 -2
  234. package/dist/plot/interactions.d.ts +8 -10
  235. package/dist/plot/interactions.js +2 -3
  236. package/dist/plot/layout.d.ts +11 -2
  237. package/dist/plot/layout.js +44 -17
  238. package/dist/plot/reference-line.d.ts +5 -22
  239. package/dist/plot/reference-line.js +12 -84
  240. package/dist/plot/scales.js +24 -36
  241. package/dist/plot/types.d.ts +53 -40
  242. package/dist/plot/types.js +12 -7
  243. package/dist/plot/utils/label-placement.d.ts +32 -15
  244. package/dist/plot/utils/label-placement.js +227 -63
  245. package/dist/plot/utils/series-visibility.js +2 -3
  246. package/dist/plot/utils.d.ts +1 -0
  247. package/dist/plot/utils.js +14 -0
  248. package/dist/rdf/RdfPlot.svelte +173 -132
  249. package/dist/rdf/calc-rdf.js +4 -5
  250. package/dist/sanitize.d.ts +4 -0
  251. package/dist/sanitize.js +107 -0
  252. package/dist/settings.d.ts +21 -6
  253. package/dist/settings.js +63 -19
  254. package/dist/spectral/Bands.svelte +963 -412
  255. package/dist/spectral/Bands.svelte.d.ts +22 -2
  256. package/dist/spectral/BandsAndDos.svelte +90 -49
  257. package/dist/spectral/BrillouinBandsDos.svelte +151 -93
  258. package/dist/spectral/Dos.svelte +389 -258
  259. package/dist/spectral/helpers.d.ts +23 -1
  260. package/dist/spectral/helpers.js +119 -51
  261. package/dist/spectral/types.d.ts +2 -0
  262. package/dist/state.svelte.d.ts +1 -1
  263. package/dist/state.svelte.js +3 -2
  264. package/dist/structure/Arrow.svelte +59 -20
  265. package/dist/structure/AtomLegend.svelte +231 -129
  266. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  267. package/dist/structure/Bond.svelte +73 -47
  268. package/dist/structure/CanvasTooltip.svelte +10 -2
  269. package/dist/structure/CellSelect.svelte +148 -51
  270. package/dist/structure/Cylinder.svelte +33 -17
  271. package/dist/structure/Lattice.svelte +88 -33
  272. package/dist/structure/Structure.svelte +1077 -821
  273. package/dist/structure/Structure.svelte.d.ts +1 -1
  274. package/dist/structure/StructureControls.svelte +373 -139
  275. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  276. package/dist/structure/StructureExportPane.svelte +124 -89
  277. package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
  278. package/dist/structure/StructureInfoPane.svelte +304 -231
  279. package/dist/structure/StructureScene.svelte +919 -445
  280. package/dist/structure/StructureScene.svelte.d.ts +16 -7
  281. package/dist/structure/atom-properties.d.ts +6 -2
  282. package/dist/structure/atom-properties.js +42 -29
  283. package/dist/structure/bonding.js +6 -7
  284. package/dist/structure/export.js +22 -34
  285. package/dist/structure/ferrox-wasm-types.d.ts +3 -2
  286. package/dist/structure/ferrox-wasm-types.js +0 -3
  287. package/dist/structure/ferrox-wasm.d.ts +3 -2
  288. package/dist/structure/ferrox-wasm.js +2 -3
  289. package/dist/structure/index.d.ts +16 -0
  290. package/dist/structure/index.js +88 -6
  291. package/dist/structure/measure.d.ts +2 -2
  292. package/dist/structure/measure.js +4 -44
  293. package/dist/structure/parse.js +130 -155
  294. package/dist/structure/partial-occupancy.d.ts +25 -0
  295. package/dist/structure/partial-occupancy.js +99 -0
  296. package/dist/structure/pbc.d.ts +1 -0
  297. package/dist/structure/pbc.js +16 -6
  298. package/dist/structure/supercell.d.ts +2 -2
  299. package/dist/structure/supercell.js +12 -22
  300. package/dist/structure/validation.js +5 -3
  301. package/dist/symmetry/SymmetryStats.svelte +94 -37
  302. package/dist/symmetry/WyckoffTable.svelte +42 -14
  303. package/dist/symmetry/cell-transform.js +5 -3
  304. package/dist/symmetry/index.d.ts +7 -4
  305. package/dist/symmetry/index.js +87 -21
  306. package/dist/symmetry/spacegroups.js +148 -148
  307. package/dist/table/HeatmapTable.svelte +1112 -516
  308. package/dist/table/HeatmapTable.svelte.d.ts +12 -1
  309. package/dist/table/ToggleMenu.svelte +125 -90
  310. package/dist/table/index.d.ts +2 -0
  311. package/dist/table/index.js +2 -4
  312. package/dist/theme/ThemeControl.svelte +21 -12
  313. package/dist/time.js +4 -1
  314. package/dist/tooltip/TooltipContent.svelte +33 -8
  315. package/dist/trajectory/Trajectory.svelte +889 -687
  316. package/dist/trajectory/TrajectoryError.svelte +14 -3
  317. package/dist/trajectory/TrajectoryExportPane.svelte +148 -90
  318. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
  319. package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
  320. package/dist/trajectory/constants.d.ts +6 -0
  321. package/dist/trajectory/constants.js +7 -0
  322. package/dist/trajectory/extract.js +13 -31
  323. package/dist/trajectory/format-detect.d.ts +9 -0
  324. package/dist/trajectory/format-detect.js +76 -0
  325. package/dist/trajectory/frame-reader.d.ts +17 -0
  326. package/dist/trajectory/frame-reader.js +332 -0
  327. package/dist/trajectory/helpers.d.ts +14 -0
  328. package/dist/trajectory/helpers.js +172 -0
  329. package/dist/trajectory/index.d.ts +1 -0
  330. package/dist/trajectory/index.js +23 -14
  331. package/dist/trajectory/parse/ase.d.ts +2 -0
  332. package/dist/trajectory/parse/ase.js +77 -0
  333. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  334. package/dist/trajectory/parse/hdf5.js +129 -0
  335. package/dist/trajectory/parse/index.d.ts +12 -0
  336. package/dist/trajectory/parse/index.js +299 -0
  337. package/dist/trajectory/parse/lammps.d.ts +5 -0
  338. package/dist/trajectory/parse/lammps.js +179 -0
  339. package/dist/trajectory/parse/vasp.d.ts +2 -0
  340. package/dist/trajectory/parse/vasp.js +68 -0
  341. package/dist/trajectory/parse/xyz.d.ts +2 -0
  342. package/dist/trajectory/parse/xyz.js +110 -0
  343. package/dist/trajectory/plotting.js +13 -8
  344. package/dist/trajectory/types.d.ts +11 -0
  345. package/dist/trajectory/types.js +1 -0
  346. package/dist/utils.d.ts +3 -0
  347. package/dist/utils.js +17 -0
  348. package/dist/xrd/XrdPlot.svelte +337 -245
  349. package/dist/xrd/broadening.js +14 -9
  350. package/dist/xrd/calc-xrd.js +12 -19
  351. package/dist/xrd/parse.d.ts +1 -1
  352. package/dist/xrd/parse.js +17 -17
  353. package/package.json +103 -101
  354. package/readme.md +4 -4
  355. package/dist/trajectory/parse.d.ts +0 -42
  356. package/dist/trajectory/parse.js +0 -1267
  357. /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
  358. /package/dist/theme/{themes.js → themes.mjs} +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,7 @@ 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;
118
+ export declare function compute_x_domain(x_range: [number | null, number | null] | undefined, data: PhaseDiagramData | null): Vec2;
@@ -7,17 +7,9 @@ export function convert_temp(value, from, to) {
7
7
  if (from === to)
8
8
  return value;
9
9
  // Convert to Kelvin first
10
- const kelvin = from === `°C`
11
- ? value + 273.15
12
- : from === `°F`
13
- ? (value - 32) * (5 / 9) + 273.15
14
- : value;
10
+ const kelvin = from === `°C` ? value + 273.15 : from === `°F` ? (value - 32) * (5 / 9) + 273.15 : value;
15
11
  // Convert from Kelvin to target
16
- return to === `K`
17
- ? kelvin
18
- : to === `°C`
19
- ? kelvin - 273.15
20
- : (kelvin - 273.15) * (9 / 5) + 32;
12
+ return to === `K` ? kelvin : to === `°C` ? kelvin - 273.15 : (kelvin - 273.15) * (9 / 5) + 32;
21
13
  }
22
14
  // Centralized defaults for phase diagram configuration (single source of truth)
23
15
  export const PHASE_DIAGRAM_DEFAULTS = Object.freeze({
@@ -49,7 +41,7 @@ export const PHASE_DIAGRAM_DEFAULTS = Object.freeze({
49
41
  special_point: `#d32f2f`,
50
42
  }),
51
43
  // Margins
52
- margin: Object.freeze({ t: 25, r: 15, b: 50, l: 60 }),
44
+ margin: Object.freeze({ t: 25, r: 25, b: 50, l: 60 }),
53
45
  // Export
54
46
  png_dpi: 150,
55
47
  });
@@ -58,8 +50,7 @@ export function merge_phase_diagram_config(config) {
58
50
  return {
59
51
  margin: { ...PHASE_DIAGRAM_DEFAULTS.margin, ...config.margin },
60
52
  font_size: config.font_size ?? PHASE_DIAGRAM_DEFAULTS.font_size,
61
- special_point_radius: config.special_point_radius ??
62
- PHASE_DIAGRAM_DEFAULTS.special_point_radius,
53
+ special_point_radius: config.special_point_radius ?? PHASE_DIAGRAM_DEFAULTS.special_point_radius,
63
54
  tie_line: { ...PHASE_DIAGRAM_DEFAULTS.tie_line, ...config.tie_line },
64
55
  colors: { ...PHASE_DIAGRAM_DEFAULTS.colors, ...config.colors },
65
56
  };
@@ -94,7 +85,7 @@ export const PHASE_COLOR_RGB = Object.freeze(Object.fromEntries(Object.entries(P
94
85
  const PHASE_ALPHA = { two_phase: 0.5, default: 0.5, tie_line: 1 };
95
86
  export const PHASE_COLORS = Object.freeze(Object.fromEntries(Object.entries(PHASE_COLOR_HEX).map(([key, hex]) => [
96
87
  key,
97
- add_alpha(hex, PHASE_ALPHA[key] ?? 0.6),
88
+ add_alpha(hex, key in PHASE_ALPHA ? PHASE_ALPHA[key] : 0.6),
98
89
  ])));
99
90
  // Phase pattern matching rules: [substrings to match, color key, optional prefix check]
100
91
  // Order matters: theta before eta (since "theta" contains "eta" as substring)
@@ -127,7 +118,7 @@ export function get_phase_color_key(name) {
127
118
  }
128
119
  // Get phase color - returns rgba() by default, or RGB string if format='rgb'
129
120
  export function get_phase_color(name, format = `rgba`) {
130
- const lower = name.toLowerCase();
121
+ const lower = name.toLowerCase().trim();
131
122
  const key = lower.includes(`+`) ? `two_phase` : get_phase_color_key(name);
132
123
  return format === `rgb` ? PHASE_COLOR_RGB[key] : PHASE_COLORS[key];
133
124
  }
@@ -136,7 +127,10 @@ export function get_phase_color(name, format = `rgba`) {
136
127
  export function get_multi_phase_gradient(name) {
137
128
  if (!name.includes(`+`))
138
129
  return null;
139
- const phases = name.split(`+`).map((s) => s.trim()).filter(Boolean);
130
+ const phases = name
131
+ .split(`+`)
132
+ .map((s) => s.trim())
133
+ .filter(Boolean);
140
134
  if (phases.length < 2)
141
135
  return null;
142
136
  // Create evenly spaced gradient stops (phases.length >= 2 guaranteed by early return)
@@ -150,17 +144,20 @@ export function find_phase_at_point(composition, temperature, data) {
150
144
  // Search regions in reverse order so later-defined regions take precedence
151
145
  for (let idx = data.regions.length - 1; idx >= 0; idx--) {
152
146
  const region = data.regions[idx];
153
- if (point_in_polygon(composition, temperature, region.vertices))
147
+ if (point_in_polygon(composition, temperature, region.vertices)) {
154
148
  return region;
149
+ }
155
150
  }
156
151
  return null;
157
152
  }
158
153
  // SVG path generator using d3-shape
159
- const path_line = line().x((d) => d[0]).y((d) => d[1]);
154
+ const path_line = line()
155
+ .x((d) => d[0])
156
+ .y((d) => d[1]);
160
157
  // Generate closed SVG path for polygon regions (min 3 points)
161
158
  export const generate_region_path = (vertices) => vertices.length < 3 ? `` : `${path_line(vertices)} Z`;
162
159
  // Generate open SVG path for boundary curves (min 2 points)
163
- export const generate_boundary_path = (points) => points.length < 2 ? `` : path_line(points) ?? ``;
160
+ export const generate_boundary_path = (points) => points.length < 2 ? `` : (path_line(points) ?? ``);
164
161
  // Compute label properties (rotation, wrapping, scale) to fit within region bounds
165
162
  export function compute_label_properties(label, bounds, font_size) {
166
163
  if (bounds.width <= 0 || bounds.height <= 0 || !label || font_size <= 0) {
@@ -199,7 +196,7 @@ export function compute_label_properties(label, bounds, font_size) {
199
196
  }
200
197
  // Scale down as last resort (min 70%)
201
198
  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;
199
+ const rotation = is_tall ? -90 : 0;
203
200
  return { rotation, lines: [label], scale };
204
201
  }
205
202
  // Wrap text into multiple lines at delimiter boundaries
@@ -210,7 +207,7 @@ function wrap_text(text, max_chars) {
210
207
  const lines = [];
211
208
  let current_line = ``;
212
209
  for (const word of words) {
213
- const candidate = current_line ? `${current_line}_${word}` : word;
210
+ const candidate = current_line ? `${current_line} ${word}` : word;
214
211
  if (candidate.length <= max_chars) {
215
212
  current_line = candidate;
216
213
  }
@@ -222,7 +219,7 @@ function wrap_text(text, max_chars) {
222
219
  }
223
220
  if (current_line)
224
221
  lines.push(current_line);
225
- return lines.length > 0 ? lines : [text];
222
+ return lines;
226
223
  }
227
224
  // Transform data coordinates to SVG coordinates using scale functions
228
225
  export function transform_vertices(vertices, x_scale, y_scale) {
@@ -231,78 +228,158 @@ export function transform_vertices(vertices, x_scale, y_scale) {
231
228
  // Format composition value for display
232
229
  export function format_composition(value, unit = `at%`, include_unit = true) {
233
230
  if (unit === `fraction`)
234
- return format_num(value, `.3f`);
235
- const formatted = format_num(value * 100, `.3`);
231
+ return format_num(value, `.3~f`);
232
+ const formatted = format_num(value * 100, `.3~`);
236
233
  return include_unit ? `${formatted} ${unit}` : formatted;
237
234
  }
238
235
  // Format temperature value for display
239
236
  export function format_temperature(value, unit = `K`) {
240
237
  return `${format_num(value, `.0f`)} ${unit}`;
241
238
  }
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)
239
+ // Parse a two-phase region name into its two phase names
240
+ // Returns null if the region is not exactly a two-phase region
241
+ function parse_two_phases(name) {
242
+ if (!name.includes(`+`))
251
243
  return null;
252
- // Find horizontal intersections with polygon edges at this temperature
244
+ const parts = name
245
+ .trim()
246
+ .split(/\s*\+\s*/)
247
+ .filter(Boolean);
248
+ return parts.length === 2 ? [parts[0], parts[1]] : null;
249
+ }
250
+ // Find polygon edge intersections along a scan line (horizontal or vertical)
251
+ // For horizontal: fixed_val = temperature, returns x-intersections
252
+ // For vertical: fixed_val = composition, returns y-intersections
253
+ function find_polygon_intersections(vertices, fixed_val, axis) {
254
+ const other = axis === 0 ? 1 : 0;
253
255
  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));
256
+ for (let idx = 0; idx < vertices.length; idx++) {
257
+ const v1 = vertices[idx];
258
+ const v2 = vertices[(idx + 1) % vertices.length];
259
+ if ((v1[axis] <= fixed_val && v2[axis] > fixed_val) ||
260
+ (v2[axis] <= fixed_val && v1[axis] > fixed_val)) {
261
+ intersections.push(v1[other] + ((fixed_val - v1[axis]) / (v2[axis] - v1[axis])) * (v2[other] - v1[other]));
260
262
  }
261
263
  }
264
+ return intersections.toSorted((a, b) => a - b);
265
+ }
266
+ function pick_bracketing_intersection_pair(intersections, position) {
262
267
  if (intersections.length < 2)
263
268
  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)
269
+ const unique_intersections = [];
270
+ const dedup_tol = 1e-9;
271
+ for (const value of intersections) {
272
+ const prev_value = unique_intersections.at(-1);
273
+ if (prev_value === undefined || Math.abs(value - prev_value) > dedup_tol) {
274
+ unique_intersections.push(value);
275
+ }
276
+ }
277
+ if (unique_intersections.length < 2)
278
+ return null;
279
+ const bound_tol = 1e-10;
280
+ for (let pair_idx = 0; pair_idx + 1 < unique_intersections.length; pair_idx += 2) {
281
+ const low_bound = unique_intersections[pair_idx];
282
+ const high_bound = unique_intersections[pair_idx + 1];
283
+ if (position >= low_bound - bound_tol && position <= high_bound + bound_tol) {
284
+ return [low_bound, high_bound];
285
+ }
286
+ }
287
+ // Fallback for numerical edge cases where even-odd pairing fails:
288
+ // pick nearest enclosing neighbors around the hovered point.
289
+ let left_idx = -1;
290
+ for (let idx = 0; idx < unique_intersections.length; idx++) {
291
+ if (unique_intersections[idx] <= position + bound_tol)
292
+ left_idx = idx;
293
+ }
294
+ if (left_idx < 0 || left_idx + 1 >= unique_intersections.length)
295
+ return null;
296
+ const left_bound = unique_intersections[left_idx];
297
+ const right_bound = unique_intersections[left_idx + 1];
298
+ return right_bound - left_bound > bound_tol ? [left_bound, right_bound] : null;
299
+ }
300
+ // Shared core for lever rule calculations (horizontal and vertical)
301
+ // Parses phases, finds intersections along the scan axis, validates bounds,
302
+ // and computes the fractional position within the two-phase region.
303
+ function lever_rule_core(region, position, scan_val, axis) {
304
+ const phases = parse_two_phases(region.name);
305
+ if (!phases)
306
+ return null;
307
+ const intersections = find_polygon_intersections(region.vertices, scan_val, axis);
308
+ const bounds = pick_bracketing_intersection_pair(intersections, position);
309
+ if (!bounds)
310
+ return null;
311
+ const [lo, hi] = bounds;
312
+ const span = hi - lo;
313
+ if (span < 1e-10)
314
+ return null;
315
+ return { phases, lo, hi, fraction_hi: (position - lo) / span };
316
+ }
317
+ // Calculate lever rule for a point in a two-phase region
318
+ // Returns null if the region is not exactly a two-phase region or calculation fails
319
+ // Note: Lever rule is thermodynamically defined only for two-phase equilibria
320
+ export function calculate_lever_rule(region, composition, temperature) {
321
+ // Horizontal scan: fixed temperature, find composition intersections
322
+ const core = lever_rule_core(region, composition, temperature, 1);
323
+ if (!core)
268
324
  return null;
269
- const total_width = right_composition - left_composition;
270
- if (total_width < 1e-10)
325
+ return {
326
+ left_phase: core.phases[0],
327
+ right_phase: core.phases[1],
328
+ left_composition: core.lo,
329
+ right_composition: core.hi,
330
+ fraction_left: 1 - core.fraction_hi,
331
+ fraction_right: core.fraction_hi,
332
+ };
333
+ }
334
+ // Calculate vertical lever rule for a point in a two-phase region
335
+ // Uses constant composition (vertical line) to find temperature boundaries
336
+ export function calculate_vertical_lever_rule(region, composition, temperature) {
337
+ // Vertical scan: fixed composition, find temperature intersections
338
+ const core = lever_rule_core(region, temperature, composition, 0);
339
+ if (!core)
271
340
  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
341
  return {
279
- left_phase,
280
- right_phase,
281
- left_composition,
282
- right_composition,
283
- fraction_left,
284
- fraction_right,
342
+ bottom_phase: core.phases[0],
343
+ top_phase: core.phases[1],
344
+ bottom_temperature: core.lo,
345
+ top_temperature: core.hi,
346
+ fraction_bottom: 1 - core.fraction_hi,
347
+ fraction_top: core.fraction_hi,
285
348
  };
286
349
  }
287
350
  // 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`) {
351
+ // Only includes lever rule data for the active mode to match tooltip display
352
+ 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`) {
353
+ // Convert temperature from data unit to display unit
354
+ const to_display = (temp) => convert_temp(temp, data_temp_unit, temp_unit);
289
355
  const lines = [
290
356
  `Phase: ${info.region.name}`,
291
- `Temperature: ${format_temperature(info.temperature, temp_unit)}`,
357
+ `Temperature: ${format_temperature(to_display(info.temperature), temp_unit)}`,
292
358
  `Composition: ${format_composition(info.composition, comp_unit)} ${component_b} (${format_composition(1 - info.composition, comp_unit)} ${component_a})`,
293
359
  ];
294
- if (info.lever_rule) {
360
+ if (lever_rule_mode === `horizontal` && info.lever_rule) {
295
361
  const lr = info.lever_rule;
296
362
  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
363
  }
364
+ if (lever_rule_mode === `vertical` && info.vertical_lever_rule) {
365
+ const vlr = info.vertical_lever_rule;
366
+ 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)})`);
367
+ }
298
368
  return lines.join(`\n`);
299
369
  }
300
370
  // Calculate temperature stability range for a phase at given composition
301
371
  export function get_phase_stability_range(region) {
302
372
  if (!region.vertices?.length)
303
373
  return null;
304
- const temps = region.vertices.map(([, temp]) => temp);
305
- return { t_min: Math.min(...temps), t_max: Math.max(...temps) };
374
+ let t_min = Infinity;
375
+ let t_max = -Infinity;
376
+ for (const [, temp] of region.vertices) {
377
+ if (temp < t_min)
378
+ t_min = temp;
379
+ if (temp > t_max)
380
+ t_max = temp;
381
+ }
382
+ return { t_min, t_max };
306
383
  }
307
384
  // Extract reference/citation from TDB comments
308
385
  export function extract_tdb_reference(comments) {
@@ -325,8 +402,8 @@ export function summarize_models(phases) {
325
402
  counts.set(phase.sublattice_count, (counts.get(phase.sublattice_count) ?? 0) + 1);
326
403
  }
327
404
  return [...counts.entries()]
328
- .sort(([a], [b]) => a - b)
329
- .map(([n, c]) => `${c}×${n}-SL`)
405
+ .toSorted(([sl_a], [sl_b]) => sl_a - sl_b)
406
+ .map(([sublattices, count]) => `${count}×${sublattices}-SL`)
330
407
  .join(`, `);
331
408
  }
332
409
  // Check if a component name is a compound (vs single element)
@@ -342,13 +419,7 @@ export function is_compound(name) {
342
419
  // Single element pattern: one uppercase followed by optional lowercase (Fe, Ca, He, C)
343
420
  if (/^[A-Z][a-z]?$/.test(name))
344
421
  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;
422
+ return (name.match(/[A-Z]/g)?.length ?? 0) >= 2;
352
423
  }
353
424
  // Tokenize a chemical formula for rendering with subscripts/superscripts
354
425
  // Examples:
@@ -413,7 +484,7 @@ export function tokenize_formula(formula) {
413
484
  // Any other character (lowercase, hyphen, etc.) - collect as text
414
485
  let text = char;
415
486
  idx++;
416
- while (idx < formula.length && !/[A-Z\d\-]/.test(formula[idx])) {
487
+ while (idx < formula.length && !/[A-Z\d-]/.test(formula[idx])) {
417
488
  text += formula[idx];
418
489
  idx++;
419
490
  }
@@ -447,10 +518,29 @@ export function format_formula_svg(formula, use_subscripts = true) {
447
518
  offset += dy;
448
519
  }
449
520
  }
521
+ // Reset baseline after trailing subscript/superscript using a zero-width space
522
+ // (empty tspans may not apply dy in all SVG renderers)
450
523
  if (offset)
451
- result += `<tspan dy="${-offset}em"></tspan>`;
524
+ result += `<tspan dy="${-offset}em">\u200B</tspan>`;
452
525
  return result;
453
526
  }
527
+ // Split a multi-phase label on " + " and format each part with the given formatter
528
+ function format_label_parts(label, use_subscripts, formatter) {
529
+ if (!use_subscripts)
530
+ return label;
531
+ return label
532
+ .split(/(\s*\+\s*)/)
533
+ .map((part) => {
534
+ if (part.trim() === `+`)
535
+ return part;
536
+ return formatter(part.trim(), use_subscripts);
537
+ })
538
+ .join(``);
539
+ }
540
+ // Format a phase region label (e.g. "La2NiO4 + NiO") as SVG with subscripts
541
+ export const format_label_svg = (label, use_subscripts = true) => format_label_parts(label, use_subscripts, format_formula_svg);
542
+ // Format a phase region label as HTML with subscripts (splits on " + ")
543
+ export const format_label_html = (label, use_subscripts = true) => format_label_parts(label, use_subscripts, format_formula_html);
454
544
  // Format chemical formula as HTML with <sub> and <sup> tags
455
545
  export function format_formula_html(formula, use_subscripts = true) {
456
546
  if (!use_subscripts || !is_compound(formula))
@@ -459,3 +549,58 @@ export function format_formula_html(formula, use_subscripts = true) {
459
549
  .map((token) => token.text ?? (token.sub ? `<sub>${token.sub}</sub>` : `<sup>${token.sup}</sup>`))
460
550
  .join(``);
461
551
  }
552
+ // Compute the x-axis domain for a binary phase diagram.
553
+ // Uses explicit range if fully specified, otherwise derives from data extent
554
+ // and auto-extends to 0/1 when edge regions contain pure components.
555
+ export function compute_x_domain(x_range, data) {
556
+ const lo = x_range?.[0];
557
+ const hi = x_range?.[1];
558
+ if (lo != null && hi != null)
559
+ return [lo, hi];
560
+ if (data) {
561
+ let data_min = Infinity;
562
+ let data_max = -Infinity;
563
+ const update = (val) => {
564
+ if (val < data_min)
565
+ data_min = val;
566
+ if (val > data_max)
567
+ data_max = val;
568
+ };
569
+ for (const region of data.regions) {
570
+ for (const vertex of region.vertices)
571
+ update(vertex[0]);
572
+ }
573
+ for (const boundary of data.boundaries) {
574
+ for (const point of boundary.points)
575
+ update(point[0]);
576
+ }
577
+ for (const special_point of data.special_points ?? []) {
578
+ update(special_point.position[0]);
579
+ }
580
+ if (data_min <= data_max) {
581
+ let x_min = lo ?? data_min;
582
+ let x_max = hi ?? data_max;
583
+ // Auto-extend to 0/1 when edge regions contain a pure component AND the
584
+ // data already nearly reaches the boundary
585
+ const comp_at_edge = (comp, x_val) => {
586
+ const re = new RegExp(`\\b${comp.replaceAll(/[.*+?^${}()|[\]\\]/g, `\\$&`)}\\b`);
587
+ return data.regions.some((region) => re.test(region.name) &&
588
+ region.vertices.some((vertex) => Math.abs(vertex[0] - x_val) < 1e-6));
589
+ };
590
+ if (lo == null &&
591
+ x_min < 0.05 &&
592
+ data.components[0] &&
593
+ comp_at_edge(data.components[0], x_min)) {
594
+ x_min = 0;
595
+ }
596
+ if (hi == null &&
597
+ x_max > 0.95 &&
598
+ data.components[1] &&
599
+ comp_at_edge(data.components[1], x_max)) {
600
+ x_max = 1;
601
+ }
602
+ return [x_min, x_max];
603
+ }
604
+ }
605
+ return [lo ?? 0, hi ?? 1];
606
+ }
@@ -0,0 +1,51 @@
1
+ <script lang="ts">
2
+ import { AXIS_LABEL_CONTAINER } from './axis-utils'
3
+ import type { AxisOption } from './types'
4
+ import InteractiveAxisLabel from './InteractiveAxisLabel.svelte'
5
+
6
+ let {
7
+ x,
8
+ y,
9
+ rotate = false,
10
+ label = ``,
11
+ options,
12
+ selected_key,
13
+ color,
14
+ loading = false,
15
+ axis_type,
16
+ on_select,
17
+ }: {
18
+ x: number
19
+ y: number
20
+ rotate?: boolean
21
+ label?: string
22
+ options?: AxisOption[]
23
+ selected_key?: string
24
+ color?: string | null
25
+ loading?: boolean
26
+ axis_type: `x` | `x2` | `y` | `y2`
27
+ on_select?: (key: string) => void
28
+ } = $props()
29
+ </script>
30
+
31
+ <foreignObject
32
+ x={x - AXIS_LABEL_CONTAINER.x_offset}
33
+ y={y - AXIS_LABEL_CONTAINER.y_offset}
34
+ width={AXIS_LABEL_CONTAINER.width}
35
+ height={AXIS_LABEL_CONTAINER.height}
36
+ style="overflow: visible; pointer-events: none"
37
+ transform={rotate ? `rotate(-90, ${x}, ${y})` : undefined}
38
+ >
39
+ <div xmlns="http://www.w3.org/1999/xhtml" style="pointer-events: auto">
40
+ <InteractiveAxisLabel
41
+ {label}
42
+ {options}
43
+ {selected_key}
44
+ {loading}
45
+ {axis_type}
46
+ {color}
47
+ {on_select}
48
+ class="axis-label {axis_type}-label"
49
+ />
50
+ </div>
51
+ </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;