matterviz 0.4.1 → 0.4.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 (499) hide show
  1. package/dist/EmptyState.svelte +10 -4
  2. package/dist/FilePicker.svelte +48 -60
  3. package/dist/Icon.svelte +7 -4
  4. package/dist/MillerIndexInput.svelte +2 -2
  5. package/dist/api/optimade.js +10 -7
  6. package/dist/app.css +68 -0
  7. package/dist/brillouin/BrillouinZone.svelte +90 -91
  8. package/dist/brillouin/BrillouinZone.svelte.d.ts +5 -7
  9. package/dist/brillouin/BrillouinZoneControls.svelte +3 -6
  10. package/dist/brillouin/BrillouinZoneExportPane.svelte +18 -14
  11. package/dist/brillouin/BrillouinZoneInfoPane.svelte +6 -10
  12. package/dist/brillouin/BrillouinZoneScene.svelte +46 -68
  13. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  14. package/dist/brillouin/compute.js +10 -16
  15. package/dist/brillouin/geometry.js +2 -2
  16. package/dist/chempot-diagram/ChemPotDiagram.svelte +48 -50
  17. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +61 -169
  18. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +1 -1
  19. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +559 -755
  20. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +1 -1
  21. package/dist/chempot-diagram/async-compute.svelte.js +13 -14
  22. package/dist/chempot-diagram/color.d.ts +21 -1
  23. package/dist/chempot-diagram/color.js +70 -1
  24. package/dist/chempot-diagram/compute.d.ts +7 -0
  25. package/dist/chempot-diagram/compute.js +85 -26
  26. package/dist/chempot-diagram/controls-state.svelte.js +1 -1
  27. package/dist/chempot-diagram/export.js +1 -6
  28. package/dist/chempot-diagram/temperature.js +9 -10
  29. package/dist/colors/index.d.ts +0 -4
  30. package/dist/colors/index.js +19 -20
  31. package/dist/composition/BarChart.svelte +30 -26
  32. package/dist/composition/BubbleChart.svelte +17 -17
  33. package/dist/composition/Composition.svelte +10 -19
  34. package/dist/composition/Formula.svelte +20 -27
  35. package/dist/composition/FormulaFilter.svelte +115 -184
  36. package/dist/composition/PieChart.svelte +55 -41
  37. package/dist/composition/format.js +7 -9
  38. package/dist/composition/index.d.ts +2 -0
  39. package/dist/composition/index.js +14 -0
  40. package/dist/composition/parse.d.ts +2 -2
  41. package/dist/composition/parse.js +61 -115
  42. package/dist/constants.d.ts +0 -1
  43. package/dist/constants.js +17 -26
  44. package/dist/convex-hull/ConvexHull.svelte +12 -15
  45. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  46. package/dist/convex-hull/ConvexHull2D.svelte +93 -94
  47. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  48. package/dist/convex-hull/ConvexHull3D.svelte +127 -186
  49. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  50. package/dist/convex-hull/ConvexHull4D.svelte +131 -180
  51. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  52. package/dist/convex-hull/ConvexHullChrome.svelte +36 -40
  53. package/dist/convex-hull/ConvexHullControls.svelte +46 -48
  54. package/dist/convex-hull/ConvexHullStats.svelte +193 -195
  55. package/dist/convex-hull/ConvexHullTooltip.svelte +5 -12
  56. package/dist/convex-hull/GasPressureControls.svelte +5 -12
  57. package/dist/convex-hull/StructurePopup.svelte +18 -22
  58. package/dist/convex-hull/StructurePopup.svelte.d.ts +2 -0
  59. package/dist/convex-hull/TemperatureSlider.svelte +4 -10
  60. package/dist/convex-hull/canvas-interactions.svelte.js +2 -1
  61. package/dist/convex-hull/gas-thermodynamics.js +21 -53
  62. package/dist/convex-hull/helpers.d.ts +8 -10
  63. package/dist/convex-hull/helpers.js +37 -43
  64. package/dist/convex-hull/hull-state.svelte.d.ts +1 -1
  65. package/dist/convex-hull/thermodynamics.js +37 -44
  66. package/dist/coordination/CoordinationBarPlot.svelte +49 -53
  67. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
  68. package/dist/coordination/calc-coordination.d.ts +1 -1
  69. package/dist/coordination/calc-coordination.js +28 -25
  70. package/dist/element/BohrAtom.svelte +5 -9
  71. package/dist/element/ElementHeading.svelte +4 -1
  72. package/dist/element/ElementPhoto.svelte +11 -9
  73. package/dist/element/ElementStats.svelte +12 -10
  74. package/dist/element/ElementTile.svelte +44 -67
  75. package/dist/element/Nucleus.svelte +4 -2
  76. package/dist/element/data.d.ts +3 -2
  77. package/dist/element/data.js +5 -1
  78. package/dist/element/helpers.d.ts +4 -0
  79. package/dist/element/helpers.js +18 -0
  80. package/dist/element/index.d.ts +2 -5
  81. package/dist/element/index.js +4 -19
  82. package/dist/feedback/DragOverlay.svelte +5 -5
  83. package/dist/feedback/DragOverlay.svelte.d.ts +2 -2
  84. package/dist/feedback/StatusMessage.svelte +7 -4
  85. package/dist/fermi-surface/FermiSlice.svelte +21 -18
  86. package/dist/fermi-surface/FermiSurface.svelte +35 -44
  87. package/dist/fermi-surface/FermiSurface.svelte.d.ts +7 -13
  88. package/dist/fermi-surface/FermiSurfaceControls.svelte +6 -20
  89. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  90. package/dist/fermi-surface/FermiSurfaceScene.svelte +64 -136
  91. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  92. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +9 -6
  93. package/dist/fermi-surface/compute.js +25 -58
  94. package/dist/fermi-surface/parse.js +54 -88
  95. package/dist/file-viewer/JsonBrowser.svelte +1043 -0
  96. package/dist/file-viewer/JsonBrowser.svelte.d.ts +9 -0
  97. package/dist/file-viewer/PlotPanel.svelte +350 -0
  98. package/dist/file-viewer/PlotPanel.svelte.d.ts +9 -0
  99. package/dist/file-viewer/TrajectoryWithDos.svelte +30 -0
  100. package/dist/file-viewer/TrajectoryWithDos.svelte.d.ts +10 -0
  101. package/dist/file-viewer/detect.d.ts +11 -0
  102. package/dist/file-viewer/detect.js +381 -0
  103. package/dist/file-viewer/eligibility.d.ts +4 -0
  104. package/dist/file-viewer/eligibility.js +53 -0
  105. package/dist/file-viewer/host-protocol.d.ts +50 -0
  106. package/dist/file-viewer/host-transfer.d.ts +25 -0
  107. package/dist/file-viewer/host-transfer.js +42 -0
  108. package/dist/file-viewer/index.d.ts +5 -0
  109. package/dist/file-viewer/index.js +5 -0
  110. package/dist/file-viewer/main.d.ts +44 -0
  111. package/dist/file-viewer/main.js +535 -0
  112. package/dist/file-viewer/parse.d.ts +9 -0
  113. package/dist/file-viewer/parse.js +170 -0
  114. package/dist/file-viewer/plot-utils.d.ts +24 -0
  115. package/dist/file-viewer/plot-utils.js +191 -0
  116. package/dist/file-viewer/types.d.ts +4 -0
  117. package/dist/file-viewer/types.js +5 -0
  118. package/dist/heatmap-matrix/HeatmapMatrix.svelte +153 -219
  119. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +2 -2
  120. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +9 -7
  121. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
  122. package/dist/heatmap-matrix/index.d.ts +5 -0
  123. package/dist/icons.d.ts +4 -0
  124. package/dist/icons.js +4 -0
  125. package/dist/index.d.ts +2 -2
  126. package/dist/index.js +1 -1
  127. package/dist/io/ExportPane.svelte +11 -7
  128. package/dist/io/decompress.d.ts +4 -1
  129. package/dist/io/decompress.js +24 -36
  130. package/dist/io/export.d.ts +11 -4
  131. package/dist/io/export.js +75 -19
  132. package/dist/io/fetch.d.ts +0 -3
  133. package/dist/io/fetch.js +0 -11
  134. package/dist/io/file-drop.js +60 -35
  135. package/dist/io/index.d.ts +0 -1
  136. package/dist/io/index.js +2 -9
  137. package/dist/io/is-binary.d.ts +9 -0
  138. package/dist/io/is-binary.js +45 -1
  139. package/dist/io/url-drop.d.ts +2 -0
  140. package/dist/io/url-drop.js +40 -40
  141. package/dist/isosurface/Isosurface.svelte +700 -215
  142. package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
  143. package/dist/isosurface/IsosurfaceControls.svelte +590 -116
  144. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +1 -1
  145. package/dist/isosurface/VolumeSlice.svelte +224 -0
  146. package/dist/isosurface/VolumeSlice.svelte.d.ts +28 -0
  147. package/dist/isosurface/coloring.d.ts +20 -0
  148. package/dist/isosurface/coloring.js +118 -0
  149. package/dist/isosurface/geometry-worker-types.d.ts +38 -0
  150. package/dist/isosurface/geometry.worker.d.ts +1 -0
  151. package/dist/isosurface/geometry.worker.js +59 -0
  152. package/dist/isosurface/grid.d.ts +10 -0
  153. package/dist/isosurface/grid.js +31 -0
  154. package/dist/isosurface/index.d.ts +4 -0
  155. package/dist/isosurface/index.js +4 -0
  156. package/dist/isosurface/parse-vaspwave.d.ts +3 -0
  157. package/dist/isosurface/parse-vaspwave.js +138 -0
  158. package/dist/isosurface/parse.js +6 -6
  159. package/dist/isosurface/profile.d.ts +12 -0
  160. package/dist/isosurface/profile.js +19 -0
  161. package/dist/isosurface/sampling.d.ts +34 -0
  162. package/dist/isosurface/sampling.js +408 -0
  163. package/dist/isosurface/slice-rendering.d.ts +13 -0
  164. package/dist/isosurface/slice-rendering.js +79 -0
  165. package/dist/isosurface/slice.d.ts +22 -2
  166. package/dist/isosurface/slice.js +200 -116
  167. package/dist/isosurface/types.d.ts +24 -1
  168. package/dist/isosurface/types.js +120 -8
  169. package/dist/json-path.d.ts +4 -0
  170. package/dist/json-path.js +89 -0
  171. package/dist/labels.d.ts +6 -2
  172. package/dist/labels.js +44 -14
  173. package/dist/layout/FullscreenButton.svelte +2 -1
  174. package/dist/layout/FullscreenToggle.svelte +10 -4
  175. package/dist/layout/InfoCard.svelte +8 -21
  176. package/dist/layout/InfoTag.svelte +1 -3
  177. package/dist/layout/NumberRangeInput.svelte +46 -0
  178. package/dist/layout/NumberRangeInput.svelte.d.ts +12 -0
  179. package/dist/layout/PropertyFilter.svelte +2 -6
  180. package/dist/layout/SettingsSection.svelte +57 -62
  181. package/dist/layout/SettingsSection.svelte.d.ts +6 -7
  182. package/dist/layout/SubpageGrid.svelte +3 -1
  183. package/dist/layout/ViewerChrome.svelte +16 -11
  184. package/dist/layout/ViewerChrome.svelte.d.ts +2 -1
  185. package/dist/layout/fullscreen.js +3 -5
  186. package/dist/layout/fullscreen.svelte.js +1 -2
  187. package/dist/layout/index.d.ts +1 -0
  188. package/dist/layout/index.js +1 -0
  189. package/dist/layout/json-tree/JsonNode.svelte +22 -59
  190. package/dist/layout/json-tree/JsonTree.svelte +101 -172
  191. package/dist/layout/json-tree/JsonValue.svelte +9 -32
  192. package/dist/layout/json-tree/utils.d.ts +0 -3
  193. package/dist/layout/json-tree/utils.js +50 -188
  194. package/dist/marching-cubes.d.ts +8 -1
  195. package/dist/marching-cubes.js +115 -48
  196. package/dist/math.d.ts +5 -2
  197. package/dist/math.js +48 -38
  198. package/dist/overlays/ContextMenu.svelte +19 -34
  199. package/dist/overlays/CopyButton.svelte +11 -5
  200. package/dist/overlays/DragControlTab.svelte +6 -1
  201. package/dist/overlays/DraggablePane.svelte +110 -74
  202. package/dist/overlays/DraggablePane.svelte.d.ts +2 -1
  203. package/dist/overlays/GlassChip.svelte +32 -0
  204. package/dist/overlays/GlassChip.svelte.d.ts +8 -0
  205. package/dist/overlays/InfoPaneCards.svelte +11 -23
  206. package/dist/overlays/index.d.ts +7 -0
  207. package/dist/overlays/index.js +26 -0
  208. package/dist/overlays/portal.d.ts +2 -0
  209. package/dist/overlays/portal.js +14 -0
  210. package/dist/periodic-table/PeriodicTable.svelte +113 -145
  211. package/dist/periodic-table/PeriodicTable.svelte.d.ts +5 -3
  212. package/dist/periodic-table/PeriodicTableControls.svelte +37 -115
  213. package/dist/periodic-table/TableInset.svelte +9 -4
  214. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +68 -105
  215. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -8
  216. package/dist/phase-diagram/PhaseDiagramControls.svelte +70 -94
  217. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  218. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
  219. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +12 -12
  220. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +54 -58
  221. package/dist/phase-diagram/TdbInfoPanel.svelte +21 -15
  222. package/dist/phase-diagram/build-diagram.js +4 -9
  223. package/dist/phase-diagram/diagram-input.d.ts +1 -2
  224. package/dist/phase-diagram/parse.js +15 -15
  225. package/dist/phase-diagram/svg-to-diagram.js +28 -27
  226. package/dist/phase-diagram/utils.js +2 -3
  227. package/dist/plot/bar/BarPlot.svelte +461 -426
  228. package/dist/plot/bar/BarPlot.svelte.d.ts +2 -0
  229. package/dist/plot/bar/BarPlotControls.svelte +1 -3
  230. package/dist/plot/bar/BarPlotControls.svelte.d.ts +4 -6
  231. package/dist/plot/bar/SpacegroupBarPlot.svelte +46 -77
  232. package/dist/plot/bar/data.d.ts +5 -4
  233. package/dist/plot/bar/data.js +2 -1
  234. package/dist/plot/box/BoxPlot.svelte +322 -226
  235. package/dist/plot/box/BoxPlot.svelte.d.ts +2 -0
  236. package/dist/plot/box/Violin.svelte.d.ts +1 -0
  237. package/dist/plot/box/box-plot.js +1 -1
  238. package/dist/plot/core/auto-place.d.ts +1 -1
  239. package/dist/plot/core/auto-place.js +2 -2
  240. package/dist/plot/core/axis-utils.d.ts +12 -7
  241. package/dist/plot/core/axis-utils.js +48 -48
  242. package/dist/plot/core/components/AxisLabel.svelte +82 -20
  243. package/dist/plot/core/components/AxisLabel.svelte.d.ts +1 -0
  244. package/dist/plot/core/components/ColorBar.svelte +101 -106
  245. package/dist/plot/core/components/ColorScaleSelect.svelte +1 -1
  246. package/dist/plot/core/components/FillArea.svelte +10 -17
  247. package/dist/plot/core/components/HierarchyControls.svelte +249 -0
  248. package/dist/plot/{sunburst/SunburstControls.svelte.d.ts → core/components/HierarchyControls.svelte.d.ts} +12 -8
  249. package/dist/plot/core/components/InteractiveAxisLabel.svelte +1 -1
  250. package/dist/plot/core/components/InteractiveAxisLabel.svelte.d.ts +1 -1
  251. package/dist/plot/core/components/Line.svelte +18 -9
  252. package/dist/plot/core/components/Line.svelte.d.ts +2 -0
  253. package/dist/plot/core/components/PlotAxis.svelte +40 -17
  254. package/dist/plot/core/components/PlotControls.svelte +65 -41
  255. package/dist/plot/core/components/PlotLegend.svelte +31 -35
  256. package/dist/plot/core/components/PlotMarginals.svelte +616 -0
  257. package/dist/plot/core/components/PlotMarginals.svelte.d.ts +15 -0
  258. package/dist/plot/core/components/PlotTooltip.svelte +1 -1
  259. package/dist/plot/core/components/PortalSelect.svelte +4 -8
  260. package/dist/plot/core/components/ReferenceLine.svelte +27 -24
  261. package/dist/plot/core/components/ReferenceLine3D.svelte +54 -57
  262. package/dist/plot/core/components/ReferencePlane.svelte +7 -8
  263. package/dist/plot/core/components/ZeroLines.svelte +4 -8
  264. package/dist/plot/core/components/ZoomRect.svelte +1 -2
  265. package/dist/plot/core/components/index.d.ts +1 -0
  266. package/dist/plot/core/components/index.js +1 -0
  267. package/dist/plot/core/data-cleaning-signal.d.ts +46 -0
  268. package/dist/plot/core/data-cleaning-signal.js +467 -0
  269. package/dist/plot/core/data-cleaning.d.ts +2 -42
  270. package/dist/plot/core/data-cleaning.js +27 -474
  271. package/dist/plot/core/data-transform.d.ts +1 -2
  272. package/dist/plot/core/data-transform.js +9 -15
  273. package/dist/plot/core/fill-utils.d.ts +3 -1
  274. package/dist/plot/core/fill-utils.js +28 -6
  275. package/dist/plot/core/index.d.ts +1 -0
  276. package/dist/plot/core/index.js +1 -0
  277. package/dist/plot/core/interactions.js +0 -2
  278. package/dist/plot/core/layout.d.ts +15 -6
  279. package/dist/plot/core/layout.js +57 -38
  280. package/dist/plot/core/marginals.d.ts +170 -0
  281. package/dist/plot/core/marginals.js +436 -0
  282. package/dist/plot/core/pan-zoom.svelte.d.ts +2 -4
  283. package/dist/plot/core/pan-zoom.svelte.js +34 -17
  284. package/dist/plot/core/placed-tween.svelte.d.ts +3 -0
  285. package/dist/plot/core/placed-tween.svelte.js +68 -21
  286. package/dist/plot/core/reference-line.js +1 -1
  287. package/dist/plot/core/scales.d.ts +23 -0
  288. package/dist/plot/core/scales.js +39 -13
  289. package/dist/plot/core/svg.js +0 -1
  290. package/dist/plot/core/types/fills.d.ts +101 -0
  291. package/dist/plot/core/types/fills.js +15 -0
  292. package/dist/plot/core/types/plot-3d.d.ts +87 -0
  293. package/dist/plot/core/types/plot-3d.js +4 -0
  294. package/dist/plot/core/types/reference-lines.d.ts +140 -0
  295. package/dist/plot/core/types/reference-lines.js +9 -0
  296. package/dist/plot/core/types.d.ts +29 -324
  297. package/dist/plot/core/types.js +23 -20
  298. package/dist/plot/core/utils/hierarchy-chart.d.ts +52 -0
  299. package/dist/plot/core/utils/hierarchy-chart.js +191 -0
  300. package/dist/plot/core/utils/hierarchy-labels.d.ts +20 -0
  301. package/dist/plot/core/utils/hierarchy-labels.js +100 -0
  302. package/dist/plot/core/utils/label-placement.js +2 -2
  303. package/dist/plot/histogram/Histogram.svelte +314 -340
  304. package/dist/plot/histogram/Histogram.svelte.d.ts +7 -7
  305. package/dist/plot/histogram/HistogramControls.svelte +30 -28
  306. package/dist/plot/histogram/histogram.d.ts +31 -0
  307. package/dist/plot/histogram/histogram.js +70 -0
  308. package/dist/plot/histogram/index.d.ts +1 -0
  309. package/dist/plot/histogram/index.js +1 -0
  310. package/dist/plot/index.d.ts +1 -0
  311. package/dist/plot/index.js +1 -0
  312. package/dist/plot/sankey/Sankey.svelte +78 -69
  313. package/dist/plot/sankey/Sankey.svelte.d.ts +2 -2
  314. package/dist/plot/sankey/SankeyControls.svelte +22 -16
  315. package/dist/plot/sankey/sankey.js +13 -9
  316. package/dist/plot/scatter/BinnedScatterPlot.svelte +311 -141
  317. package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +7 -0
  318. package/dist/plot/scatter/ElementScatter.svelte +4 -1
  319. package/dist/plot/scatter/ScatterPlot.svelte +517 -410
  320. package/dist/plot/scatter/ScatterPlot.svelte.d.ts +2 -0
  321. package/dist/plot/scatter/ScatterPlotControls.svelte +18 -19
  322. package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
  323. package/dist/plot/scatter/ScatterPoint.svelte +16 -9
  324. package/dist/plot/scatter/adaptive-density.d.ts +1 -1
  325. package/dist/plot/scatter/adaptive-density.js +36 -20
  326. package/dist/plot/scatter/binned-scatter-types.d.ts +3 -2
  327. package/dist/plot/scatter/binned-scatter-types.js +6 -1
  328. package/dist/plot/scatter/scatter-data.d.ts +1 -1
  329. package/dist/plot/scatter/scatter-data.js +8 -7
  330. package/dist/plot/scatter-3d/ScatterPlot3D.svelte +25 -34
  331. package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +2 -2
  332. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +8 -5
  333. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +113 -116
  334. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +2 -2
  335. package/dist/plot/scatter-3d/Surface3D.svelte +4 -13
  336. package/dist/plot/sunburst/Sunburst.svelte +269 -347
  337. package/dist/plot/sunburst/Sunburst.svelte.d.ts +2 -2
  338. package/dist/plot/sunburst/index.d.ts +1 -1
  339. package/dist/plot/sunburst/index.js +2 -1
  340. package/dist/plot/sunburst/render.d.ts +2 -2
  341. package/dist/plot/sunburst/render.js +47 -22
  342. package/dist/plot/sunburst/sunburst.d.ts +5 -1
  343. package/dist/plot/sunburst/sunburst.js +10 -3
  344. package/dist/plot/treemap/Treemap.svelte +1022 -0
  345. package/dist/plot/treemap/Treemap.svelte.d.ts +90 -0
  346. package/dist/plot/treemap/index.d.ts +4 -0
  347. package/dist/plot/treemap/index.js +4 -0
  348. package/dist/plot/treemap/labels.d.ts +38 -0
  349. package/dist/plot/treemap/labels.js +80 -0
  350. package/dist/plot/treemap/treemap.d.ts +28 -0
  351. package/dist/plot/treemap/treemap.js +102 -0
  352. package/dist/rdf/RdfPlot.svelte +37 -59
  353. package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
  354. package/dist/rdf/calc-rdf.js +63 -52
  355. package/dist/rdf/index.d.ts +0 -1
  356. package/dist/sanitize.js +85 -27
  357. package/dist/scene/SceneCamera.svelte +1 -1
  358. package/dist/scene/SceneCamera.svelte.d.ts +1 -1
  359. package/dist/scene/bind-renderer.svelte.js +14 -6
  360. package/dist/scene/index.d.ts +1 -2
  361. package/dist/scene/index.js +1 -1
  362. package/dist/scene/{props.js → props.svelte.js} +15 -1
  363. package/dist/settings.d.ts +19 -9
  364. package/dist/settings.js +229 -525
  365. package/dist/spectral/Bands.svelte +207 -221
  366. package/dist/spectral/BandsAndDos.svelte +11 -9
  367. package/dist/spectral/BrillouinBandsDos.svelte +19 -42
  368. package/dist/spectral/Dos.svelte +92 -93
  369. package/dist/spectral/helpers.d.ts +3 -3
  370. package/dist/spectral/helpers.js +65 -140
  371. package/dist/state.svelte.js +5 -18
  372. package/dist/structure/Arrow.svelte +5 -18
  373. package/dist/structure/ArrowInstances.svelte +160 -0
  374. package/dist/structure/ArrowInstances.svelte.d.ts +16 -0
  375. package/dist/structure/AtomLegend.svelte +120 -85
  376. package/dist/structure/AtomLegend.svelte.d.ts +2 -3
  377. package/dist/structure/Bond.svelte +31 -23
  378. package/dist/structure/CanvasTooltip.svelte +15 -3
  379. package/dist/structure/CellSelect.svelte +45 -35
  380. package/dist/structure/Cylinder.svelte +24 -40
  381. package/dist/structure/InstancedAtoms.svelte +114 -0
  382. package/dist/structure/InstancedAtoms.svelte.d.ts +15 -0
  383. package/dist/structure/Lattice.svelte +85 -149
  384. package/dist/structure/SiteLabels.svelte +122 -0
  385. package/dist/structure/SiteLabels.svelte.d.ts +18 -0
  386. package/dist/structure/Structure.svelte +636 -504
  387. package/dist/structure/Structure.svelte.d.ts +4 -2
  388. package/dist/structure/StructureCarousel.svelte +637 -0
  389. package/dist/structure/StructureCarousel.svelte.d.ts +19 -0
  390. package/dist/structure/StructureControls.svelte +165 -425
  391. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  392. package/dist/structure/StructureExportPane.svelte +17 -21
  393. package/dist/structure/StructureInfoPane.svelte +128 -133
  394. package/dist/structure/StructureInfoPane.svelte.d.ts +1 -1
  395. package/dist/structure/StructureScene.svelte +535 -578
  396. package/dist/structure/StructureScene.svelte.d.ts +6 -2
  397. package/dist/structure/StructureViewport.svelte +451 -0
  398. package/dist/structure/StructureViewport.svelte.d.ts +65 -0
  399. package/dist/structure/atom-label-placement.d.ts +16 -12
  400. package/dist/structure/atom-label-placement.js +70 -37
  401. package/dist/structure/atom-properties.d.ts +2 -0
  402. package/dist/structure/atom-properties.js +105 -38
  403. package/dist/structure/bond-order-perception.js +3 -8
  404. package/dist/structure/bonding.d.ts +9 -3
  405. package/dist/structure/bonding.js +95 -46
  406. package/dist/structure/edit-history.d.ts +6 -0
  407. package/dist/structure/edit-history.js +27 -0
  408. package/dist/structure/export.d.ts +1 -0
  409. package/dist/structure/export.js +89 -105
  410. package/dist/structure/format-detect.d.ts +1 -0
  411. package/dist/structure/format-detect.js +20 -0
  412. package/dist/structure/geometry.d.ts +9 -0
  413. package/dist/structure/geometry.js +27 -0
  414. package/dist/structure/index.d.ts +16 -1
  415. package/dist/structure/index.js +15 -10
  416. package/dist/structure/measure.d.ts +1 -1
  417. package/dist/structure/measure.js +6 -12
  418. package/dist/structure/parse.d.ts +2 -1
  419. package/dist/structure/parse.js +272 -313
  420. package/dist/structure/partial-occupancy.js +10 -5
  421. package/dist/structure/pbc.js +85 -107
  422. package/dist/structure/polyhedra.d.ts +0 -1
  423. package/dist/structure/polyhedra.js +7 -21
  424. package/dist/structure/prop-groups.d.ts +85 -0
  425. package/dist/structure/prop-groups.js +18 -0
  426. package/dist/structure/supercell.d.ts +1 -2
  427. package/dist/structure/supercell.js +7 -13
  428. package/dist/symmetry/SymmetryElements.svelte +11 -15
  429. package/dist/symmetry/SymmetryStats.svelte +55 -71
  430. package/dist/symmetry/WyckoffTable.svelte +10 -28
  431. package/dist/symmetry/cell-transform.js +6 -14
  432. package/dist/symmetry/index.d.ts +1 -0
  433. package/dist/symmetry/index.js +13 -10
  434. package/dist/symmetry/spacegroups.js +3 -2
  435. package/dist/table/HeatmapTable.svelte +1142 -447
  436. package/dist/table/HeatmapTable.svelte.d.ts +12 -2
  437. package/dist/table/ToggleMenu.svelte +168 -53
  438. package/dist/table/ToggleMenu.svelte.d.ts +1 -1
  439. package/dist/table/index.d.ts +21 -6
  440. package/dist/table/index.js +34 -21
  441. package/dist/theme/ThemeControl.svelte +16 -8
  442. package/dist/theme/embedded.d.ts +18 -0
  443. package/dist/theme/embedded.js +206 -0
  444. package/dist/theme/index.d.ts +5 -5
  445. package/dist/theme/index.js +5 -12
  446. package/dist/theme/themes.mjs +44 -66
  447. package/dist/tooltip/KCoords.svelte +4 -1
  448. package/dist/tooltip/TooltipContent.svelte +3 -6
  449. package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
  450. package/dist/tooltip/index.d.ts +8 -1
  451. package/dist/trajectory/Trajectory.svelte +613 -386
  452. package/dist/trajectory/Trajectory.svelte.d.ts +1 -1
  453. package/dist/trajectory/TrajectoryError.svelte +12 -74
  454. package/dist/trajectory/TrajectoryExportPane.svelte +14 -32
  455. package/dist/trajectory/TrajectoryInfoPane.svelte +127 -164
  456. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
  457. package/dist/trajectory/extract.js +61 -49
  458. package/dist/trajectory/format-detect.d.ts +2 -0
  459. package/dist/trajectory/format-detect.js +17 -13
  460. package/dist/trajectory/frame-reader.d.ts +2 -0
  461. package/dist/trajectory/frame-reader.js +35 -32
  462. package/dist/trajectory/helpers.d.ts +2 -0
  463. package/dist/trajectory/helpers.js +34 -56
  464. package/dist/trajectory/index.d.ts +1 -0
  465. package/dist/trajectory/index.js +5 -8
  466. package/dist/trajectory/parse/ase.js +1 -1
  467. package/dist/trajectory/parse/h5-utils.d.ts +13 -0
  468. package/dist/trajectory/parse/h5-utils.js +109 -0
  469. package/dist/trajectory/parse/hdf5.d.ts +1 -1
  470. package/dist/trajectory/parse/hdf5.js +116 -106
  471. package/dist/trajectory/parse/index.d.ts +6 -6
  472. package/dist/trajectory/parse/index.js +25 -33
  473. package/dist/trajectory/parse/lammps.js +8 -12
  474. package/dist/trajectory/parse/vasp.js +10 -7
  475. package/dist/trajectory/parse/vaspout-electronic.d.ts +13 -0
  476. package/dist/trajectory/parse/vaspout-electronic.js +207 -0
  477. package/dist/trajectory/parse/vaspout-h5.d.ts +4 -0
  478. package/dist/trajectory/parse/vaspout-h5.js +281 -0
  479. package/dist/trajectory/parse/xyz.js +59 -9
  480. package/dist/trajectory/plotting.d.ts +8 -9
  481. package/dist/trajectory/plotting.js +113 -64
  482. package/dist/utils.d.ts +3 -0
  483. package/dist/utils.js +7 -0
  484. package/dist/xrd/XrdPlot.svelte +79 -118
  485. package/dist/xrd/broadening.js +2 -1
  486. package/dist/xrd/calc-xrd.js +24 -62
  487. package/dist/xrd/parse.d.ts +0 -1
  488. package/dist/xrd/parse.js +69 -95
  489. package/package.json +94 -34
  490. package/readme.md +6 -5
  491. package/dist/api/mp.d.ts +0 -6
  492. package/dist/api/mp.js +0 -22
  493. package/dist/plot/sunburst/SunburstControls.svelte +0 -200
  494. package/dist/scene/types.d.ts +0 -26
  495. package/dist/tooltip/types.d.ts +0 -8
  496. package/dist/trajectory/constants.d.ts +0 -6
  497. package/dist/trajectory/constants.js +0 -11
  498. /package/dist/{scene/types.js → file-viewer/host-protocol.js} +0 -0
  499. /package/dist/{tooltip/types.js → isosurface/geometry-worker-types.js} +0 -0
@@ -1,15 +1,30 @@
1
1
  <script lang="ts">
2
2
  // Controls panel for isosurface visualization settings (isovalue, opacity, colors, etc.)
3
- // Supports both single-isovalue mode and multi-layer mode.
3
+ // Single-isovalue mode preserves the classic UI; explicit-layers mode groups
4
+ // surfaces under their geometry-source volume and exposes cross-volume scalar
5
+ // coloring (color source, colormap, value range) per surface.
6
+ import Icon from '../Icon.svelte'
7
+ import type { D3InterpolateName } from '../colors'
4
8
  import { format_num } from '../labels'
5
9
  import { SettingsSection } from '../layout'
10
+ import type { Vec2 } from '../math'
11
+ import { ColorScaleSelect } from '../plot'
6
12
  import { tooltip } from 'svelte-multiselect/attachments'
13
+ import { auto_color_config, DEFAULT_ISO_COLORMAP, ISO_COLORMAPS } from './coloring'
14
+ import type { DisplayRange } from './sampling'
15
+ import { compare_volume_grids } from './sampling'
7
16
  import type { IsosurfaceLayer, IsosurfaceSettings, VolumetricData } from './types'
8
- import { DEFAULT_ISOSURFACE_SETTINGS, generate_layers } from './types'
17
+ import {
18
+ auto_volume_layer,
19
+ DEFAULT_ISOSURFACE_SETTINGS,
20
+ generate_layers,
21
+ materialize_layers,
22
+ remove_volume,
23
+ } from './types'
9
24
 
10
25
  let {
11
26
  settings = $bindable({ ...DEFAULT_ISOSURFACE_SETTINGS }),
12
- volumes = [],
27
+ volumes = $bindable([]),
13
28
  active_volume_idx = $bindable(0),
14
29
  }: {
15
30
  settings?: IsosurfaceSettings
@@ -31,25 +46,185 @@
31
46
 
32
47
  let slider_max = $derived(Math.max(data_range.abs_max, 0.001))
33
48
  let step = $derived(slider_max / 200)
34
- let is_multi_layer = $derived((settings.layers?.length ?? 0) > 0)
49
+ // Explicit layers mode is active whenever layers is set an empty array means
50
+ // "zero surfaces" (all removed), NOT a fallback to implicit single-surface mode
51
+ let is_multi_layer = $derived(settings.layers != null)
35
52
  let n_layers = $derived(settings.layers?.length ?? 1)
36
53
 
54
+ const vol_label = (idx: number): string => volumes[idx]?.label ?? `Volume ${idx + 1}`
55
+
56
+ // Resolve a layer's geometry volume the same way for grouping, warnings, and
57
+ // data ranges (out-of-range indices clamp so stale layers stay editable/removable)
58
+ const resolve_geo_idx = (layer: IsosurfaceLayer): number =>
59
+ Math.min(Math.max(layer.volume_idx ?? active_volume_idx, 0), volumes.length - 1)
60
+
61
+ // The layer's scalar-color-source volume, if any
62
+ const color_vol_of = (layer: IsosurfaceLayer): VolumetricData | undefined =>
63
+ layer.color_volume_idx != null ? volumes[layer.color_volume_idx] : undefined
64
+
37
65
  function set_layer_count(count: number) {
38
- if (count <= 1) {
39
- settings.layers = undefined
40
- } else {
41
- settings.layers = generate_layers(data_range, count)
42
- }
66
+ settings.layers = count <= 1 ? undefined : generate_layers(data_range, count)
43
67
  }
44
68
 
45
69
  function update_layer(idx: number, updates: Partial<IsosurfaceLayer>) {
46
70
  if (!settings.layers) return
47
71
  settings.layers = settings.layers.map((layer, layer_idx) =>
48
- layer_idx === idx ? { ...layer, ...updates } : layer
72
+ layer_idx === idx ? { ...layer, ...updates } : layer,
49
73
  )
50
74
  }
75
+
76
+ function remove_layer(idx: number) {
77
+ if (settings.layers)
78
+ settings.layers = settings.layers.filter((_layer, layer_idx) => layer_idx !== idx)
79
+ }
80
+
81
+ function add_surface(vol_idx: number) {
82
+ const vol = volumes[vol_idx]
83
+ if (!vol) return
84
+ const layers = materialize_layers(settings, active_volume_idx)
85
+ layers.push(auto_volume_layer(vol, vol_idx, layers.length))
86
+ settings.layers = layers
87
+ active_volume_idx = vol_idx
88
+ }
89
+
90
+ function handle_remove_volume(vol_idx: number) {
91
+ const layers = materialize_layers(settings, active_volume_idx)
92
+ const result = remove_volume(volumes, layers, vol_idx, active_volume_idx)
93
+ volumes = result.volumes
94
+ settings.layers = result.layers
95
+ // Keep the active volume pointing at the same physical volume (indices shift)
96
+ if (active_volume_idx > vol_idx) active_volume_idx -= 1
97
+ if (active_volume_idx >= result.volumes.length) active_volume_idx = 0
98
+ }
99
+
100
+ // Set (or clear) a layer's scalar-color source. The colormap is auto-picked
101
+ // from the color volume's data; color_range stays unset so the renderer fits
102
+ // it to the scalar values actually present on the surface.
103
+ function set_color_source(layer_idx: number, color_idx: number | null) {
104
+ const color_vol = color_idx === null ? undefined : volumes[color_idx]
105
+ if (color_idx !== null && !color_vol) return
106
+ update_layer(layer_idx, {
107
+ color_volume_idx: color_idx ?? undefined,
108
+ colormap: color_vol ? auto_color_config(color_vol.data_range).colormap : undefined,
109
+ color_range: undefined,
110
+ })
111
+ }
112
+
113
+ // Picking a color source in single-isovalue mode materializes explicit layers
114
+ function set_single_color_source(color_idx: number | null) {
115
+ if (color_idx === null) return
116
+ settings.layers = materialize_layers(settings, active_volume_idx)
117
+ set_color_source(0, color_idx)
118
+ }
119
+
120
+ // Update one bound of a layer's color range. An empty input resets the whole
121
+ // range to auto-fit (renderer fits it to the values sampled on the surface).
122
+ // Typing into an auto range seeds the other bound from the color volume's
123
+ // full data range as a starting point.
124
+ function update_color_range(layer_idx: number, bound: 0 | 1, raw_value: string) {
125
+ const layer = settings.layers?.[layer_idx]
126
+ if (!layer) return
127
+ if (raw_value.trim() === ``) {
128
+ update_layer(layer_idx, { color_range: undefined })
129
+ return
130
+ }
131
+ const value = Number(raw_value)
132
+ if (Number.isNaN(value)) return
133
+ const color_vol = color_vol_of(layer)
134
+ const current: Vec2 =
135
+ layer.color_range ??
136
+ (color_vol ? auto_color_config(color_vol.data_range).color_range : [0, 1])
137
+ const next: Vec2 = bound === 0 ? [value, current[1]] : [current[0], value]
138
+ update_layer(layer_idx, { color_range: next })
139
+ }
140
+
141
+ // Grid-compatibility note for a surface/color volume pair. Strictly matching
142
+ // grids sample exactly; otherwise values are resampled in shared coordinates.
143
+ function compat_warning(layer: IsosurfaceLayer): string | null {
144
+ const geo_vol = volumes[resolve_geo_idx(layer)]
145
+ const color_vol = color_vol_of(layer)
146
+ if (!geo_vol || !color_vol || geo_vol === color_vol) return null
147
+ const compat = compare_volume_grids(geo_vol, color_vol)
148
+ return compat.ok ? null : (compat.reason ?? `grids differ`)
149
+ }
150
+
151
+ // Layers grouped by geometry-source volume for the tree-style multi-layer UI
152
+ let grouped_layers = $derived.by(() => {
153
+ const groups = volumes.map((_vol, vol_idx) => ({
154
+ vol_idx,
155
+ entries: [] as { layer: IsosurfaceLayer; layer_idx: number }[],
156
+ }))
157
+ for (const [layer_idx, layer] of (settings.layers ?? []).entries()) {
158
+ const vol_idx = resolve_geo_idx(layer)
159
+ if (vol_idx >= 0) groups[vol_idx].entries.push({ layer, layer_idx })
160
+ }
161
+ return groups
162
+ })
163
+
164
+ // Halo pads geometry volumes only, so gate on volumes that actually render
165
+ // surfaces (color-source-only volumes don't count)
166
+ let any_periodic = $derived(
167
+ settings.layers
168
+ ? settings.layers.some((layer) => volumes[resolve_geo_idx(layer)]?.periodic)
169
+ : (volumes[active_volume_idx]?.periodic ?? false),
170
+ )
171
+
172
+ // Update one bound of the fractional display range. Clearing an input resets
173
+ // that bound to its default (0 or 1); a fully default range unsets display_range
174
+ // so surfaces follow the structure's integer supercell again.
175
+ function update_display_range(axis: number, bound: 0 | 1, raw_value: string) {
176
+ const range = (settings.display_range?.map((pair) => [...pair]) ?? [
177
+ [0, 1],
178
+ [0, 1],
179
+ [0, 1],
180
+ ]) as DisplayRange
181
+ const value = raw_value.trim() === `` ? bound : Number(raw_value)
182
+ if (Number.isNaN(value)) return
183
+ range[axis][bound] = value
184
+ const is_default = range.every(([lo, hi]) => lo === 0 && hi === 1)
185
+ settings.display_range = is_default ? undefined : range
186
+ }
51
187
  </script>
52
188
 
189
+ {#snippet color_source_select(
190
+ selected: number,
191
+ on_pick: (idx: number | null) => void,
192
+ tip: string,
193
+ )}
194
+ <label {@attach tooltip({ content: tip })}>
195
+ <span>Color by:</span>
196
+ <select
197
+ value={selected}
198
+ onchange={(event) => {
199
+ const idx = Number(event.currentTarget.value)
200
+ on_pick(idx < 0 ? null : idx)
201
+ }}
202
+ >
203
+ <option value={-1}>None (solid)</option>
204
+ {#each volumes as _color_vol, color_idx (color_idx)}
205
+ <option value={color_idx}>{vol_label(color_idx)}</option>
206
+ {/each}
207
+ </select>
208
+ </label>
209
+ {/snippet}
210
+
211
+ {#snippet range_bound_input(layer_idx: number, bound: 0 | 1, explicit_range?: Vec2)}
212
+ <input
213
+ type="number"
214
+ class="range-input"
215
+ step="any"
216
+ placeholder="auto"
217
+ value={explicit_range ? Number(explicit_range[bound].toPrecision(4)) : ``}
218
+ onchange={(event) => update_color_range(layer_idx, bound, event.currentTarget.value)}
219
+ aria-label={bound === 0 ? `Color range minimum` : `Color range maximum`}
220
+ {@attach tooltip({
221
+ content: `Value mapped to the colormap ${
222
+ bound === 0 ? `start` : `end`
223
+ } (empty = auto-fit to surface values)`,
224
+ })}
225
+ />
226
+ {/snippet}
227
+
53
228
  <SettingsSection
54
229
  title="Isosurface"
55
230
  current_values={{
@@ -59,50 +234,58 @@
59
234
  wireframe: settings.wireframe,
60
235
  halo: settings.halo,
61
236
  layers: n_layers,
237
+ display_range: settings.display_range?.flat().join(`,`) ?? ``,
62
238
  }}
63
239
  on_reset={() => {
64
240
  settings = { ...DEFAULT_ISOSURFACE_SETTINGS }
65
241
  }}
66
242
  >
67
- <!-- Top row: volume selector (if multi-volume) + layer count + toggles -->
243
+ <!-- Top row: volume selector (single-layer multi-volume) + layer count + toggles -->
68
244
  <div class="pane-row compact-row">
69
- {#if volumes.length > 1}
245
+ {#if !is_multi_layer && volumes.length > 1}
70
246
  <label
71
- {@attach tooltip({ content: `Select which volume to display (e.g. charge vs magnetization)` })}
247
+ {@attach tooltip({
248
+ content: `Select which volume to display (e.g. charge vs magnetization)`,
249
+ })}
72
250
  >
73
251
  <span>Volume:</span>
74
252
  <select bind:value={active_volume_idx}>
75
- {#each volumes as vol, idx (idx)}
76
- <option value={idx}>{vol.label ?? `Volume ${idx + 1}`}</option>
253
+ {#each volumes as _vol, idx (idx)}
254
+ <option value={idx}>{vol_label(idx)}</option>
77
255
  {/each}
78
256
  </select>
79
257
  </label>
80
258
  {/if}
81
- <label
82
- {@attach tooltip({ content: `Number of isosurface shells at different density thresholds` })}
83
- >
84
- <span>Layers:</span>
85
- <select
86
- value={n_layers}
87
- onchange={(event) =>
88
- set_layer_count(Number(event.currentTarget.value))}
259
+ {#if !is_multi_layer}
260
+ <label
261
+ {@attach tooltip({
262
+ content: `Number of isosurface shells at different density thresholds`,
263
+ })}
89
264
  >
90
- {#each [1, 2, 3, 4, 5] as count (count)}
91
- <option value={count}>{count}</option>
92
- {/each}
93
- </select>
94
- </label>
265
+ <span>Layers:</span>
266
+ <select
267
+ value={n_layers}
268
+ onchange={(event) => set_layer_count(Number(event.currentTarget.value))}
269
+ >
270
+ {#each [1, 2, 3, 4, 5] as count (count)}
271
+ <option value={count}>{count}</option>
272
+ {/each}
273
+ </select>
274
+ </label>
275
+ {/if}
95
276
  <!-- Sync both settings.show_negative (single-layer fallback) and all layer entries
96
277
  so the toggle works consistently regardless of which mode is active -->
97
278
  <label
98
- {@attach tooltip({ content: `Show negative lobe at −isovalue (for orbitals, ESP, magnetization)` })}
279
+ {@attach tooltip({
280
+ content: `Show negative lobe at −isovalue (for orbitals, ESP, magnetization)`,
281
+ })}
99
282
  >
100
283
  <span>Neg. lobe</span>
101
284
  <input
102
285
  type="checkbox"
103
286
  checked={is_multi_layer
104
- ? settings.layers?.some((layer) => layer.show_negative) ?? false
105
- : settings.show_negative}
287
+ ? (settings.layers?.some((layer) => layer.show_negative) ?? false)
288
+ : settings.show_negative}
106
289
  onchange={(event) => {
107
290
  const checked = event.currentTarget.checked
108
291
  settings.show_negative = checked
@@ -115,77 +298,187 @@
115
298
  }}
116
299
  />
117
300
  </label>
118
- <label
119
- {@attach tooltip({ content: `Render as wireframe mesh instead of solid surface` })}
120
- >
301
+ <label {@attach tooltip({ content: `Render as wireframe mesh instead of solid surface` })}>
121
302
  <span>Wireframe</span>
122
303
  <input type="checkbox" bind:checked={settings.wireframe} />
123
304
  </label>
124
305
  </div>
125
306
 
126
307
  {#if is_multi_layer && settings.layers}
127
- <!-- Multi-layer controls: each row has visibility, color, isovalue slider, opacity -->
128
- {#each settings.layers as layer, idx (idx)}
129
- <div class="layer-row">
130
- <input
131
- type="checkbox"
132
- checked={layer.visible}
133
- onchange={() => update_layer(idx, { visible: !layer.visible })}
134
- />
135
- <input
136
- type="color"
137
- value={layer.color}
138
- onchange={(event) =>
139
- update_layer(idx, { color: event.currentTarget.value })}
140
- />
141
- <input
142
- type="range"
143
- min={step}
144
- max={slider_max}
145
- {step}
146
- value={layer.isovalue}
147
- oninput={(event) =>
148
- update_layer(idx, {
149
- isovalue: Number(event.currentTarget.value),
150
- })}
151
- style="flex: 1; min-width: 60px"
152
- />
153
- <span class="layer-value">{format_num(layer.isovalue, `.3~g`)}</span>
154
- <input
155
- type="range"
156
- min={0.1}
157
- max={1}
158
- step={0.05}
159
- value={layer.opacity}
160
- oninput={(event) =>
161
- update_layer(idx, {
162
- opacity: Number(event.currentTarget.value),
163
- })}
164
- style="width: 50px"
165
- title="Opacity: {format_num(layer.opacity, `.2f`)}"
166
- />
308
+ <!-- Multi-layer mode: surfaces grouped under their geometry-source volume -->
309
+ {#each grouped_layers as { vol_idx, entries } (vol_idx)}
310
+ {@const vol = volumes[vol_idx]}
311
+ <div class="volume-group">
312
+ <div class="volume-header">
313
+ <span class="volume-label" title={vol_label(vol_idx)}>{vol_label(vol_idx)}</span>
314
+ <span class="volume-dims">{vol.grid_dims.join(`×`)}</span>
315
+ {#if entries.length === 0}
316
+ <span
317
+ class="volume-note"
318
+ {@attach tooltip({
319
+ content: `No surfaces — this volume is still available as a color source for other surfaces`,
320
+ })}>color source only</span
321
+ >
322
+ {/if}
323
+ <button
324
+ type="button"
325
+ class="icon-btn"
326
+ onclick={() => add_surface(vol_idx)}
327
+ aria-label="Add surface for {vol_label(vol_idx)}"
328
+ {@attach tooltip({ content: `Add an isosurface from this volume` })}>+</button
329
+ >
330
+ {#if volumes.length > 1}
331
+ <button
332
+ type="button"
333
+ class="icon-btn"
334
+ onclick={() => handle_remove_volume(vol_idx)}
335
+ aria-label="Remove volume {vol_label(vol_idx)}"
336
+ {@attach tooltip({ content: `Remove this volume and its surfaces` })}>×</button
337
+ >
338
+ {/if}
339
+ </div>
340
+
341
+ {#each entries as { layer, layer_idx } (layer_idx)}
342
+ {@const layer_abs_max = Math.max(vol.data_range.abs_max, 0.001)}
343
+ {@const layer_step = layer_abs_max / 200}
344
+ {@const warning = compat_warning(layer)}
345
+ {@const color_vol = color_vol_of(layer)}
346
+ <div class="layer-row">
347
+ <input
348
+ type="checkbox"
349
+ checked={layer.visible}
350
+ onchange={() => update_layer(layer_idx, { visible: !layer.visible })}
351
+ {@attach tooltip({ content: `Toggle surface visibility` })}
352
+ />
353
+ <input
354
+ type="color"
355
+ value={layer.color}
356
+ onchange={(event) =>
357
+ update_layer(layer_idx, { color: event.currentTarget.value })}
358
+ {@attach tooltip({
359
+ content:
360
+ layer.color_volume_idx != null
361
+ ? `Fallback color (surface uses colormap)`
362
+ : `Surface color`,
363
+ })}
364
+ />
365
+ <label
366
+ class="slider-field"
367
+ {@attach tooltip({
368
+ content: `Density threshold — surface is drawn where grid values equal this`,
369
+ })}
370
+ >
371
+ <span>Iso</span>
372
+ <input
373
+ type="range"
374
+ class="isovalue-slider"
375
+ min={layer_step}
376
+ max={layer_abs_max}
377
+ step={layer_step}
378
+ value={layer.isovalue}
379
+ oninput={(event) =>
380
+ update_layer(layer_idx, { isovalue: Number(event.currentTarget.value) })}
381
+ aria-label="Isovalue"
382
+ />
383
+ <span class="layer-value">{format_num(layer.isovalue, `.3~g`)}</span>
384
+ </label>
385
+ <label class="slider-field" {@attach tooltip({ content: `Surface transparency` })}>
386
+ <span>Op</span>
387
+ <input
388
+ type="range"
389
+ class="opacity-slider"
390
+ min={0.1}
391
+ max={1}
392
+ step={0.05}
393
+ value={layer.opacity}
394
+ oninput={(event) =>
395
+ update_layer(layer_idx, { opacity: Number(event.currentTarget.value) })}
396
+ aria-label="Opacity"
397
+ />
398
+ <span class="layer-value">{format_num(layer.opacity, `.2f`)}</span>
399
+ </label>
400
+ <button
401
+ type="button"
402
+ class="icon-btn"
403
+ onclick={() => remove_layer(layer_idx)}
404
+ aria-label="Remove surface"
405
+ {@attach tooltip({ content: `Remove this surface` })}>×</button
406
+ >
407
+ </div>
408
+ <div class="color-row">
409
+ {@render color_source_select(
410
+ layer.color_volume_idx ?? -1,
411
+ (idx) => set_color_source(layer_idx, idx),
412
+ `Color surface by another volume's values`,
413
+ )}
414
+ {#if color_vol}
415
+ {@const explicit_range = layer.color_range}
416
+ {@const auto_colormap = auto_color_config(color_vol.data_range).colormap}
417
+ <ColorScaleSelect
418
+ options={[...ISO_COLORMAPS]}
419
+ value={layer.colormap ?? DEFAULT_ISO_COLORMAP}
420
+ selected={[layer.colormap ?? DEFAULT_ISO_COLORMAP]}
421
+ onadd={({ option }) =>
422
+ update_layer(layer_idx, {
423
+ colormap: option as D3InterpolateName,
424
+ })}
425
+ colorbar={{
426
+ bar_style: `height: 8px`,
427
+ title_style: `width: 4em; font-size: 1em;`,
428
+ }}
429
+ liSelectedStyle="width: 100%; margin: 0; padding: 0; background: transparent;"
430
+ aria-label="Colormap for sampled values"
431
+ {@attach tooltip({ content: `Colormap for sampled values` })}
432
+ />
433
+ <div class="color-range" aria-label="Colormap value range">
434
+ <span>Range:</span>
435
+ {@render range_bound_input(layer_idx, 0, explicit_range)}
436
+ <span aria-hidden="true">&ndash;</span>
437
+ {@render range_bound_input(layer_idx, 1, explicit_range)}
438
+ </div>
439
+ {#if explicit_range || (layer.colormap ?? DEFAULT_ISO_COLORMAP) !== auto_colormap}
440
+ {@const reset_color_label = `Reset colormap + range to auto-fit`}
441
+ <button
442
+ type="button"
443
+ class="icon-btn"
444
+ onclick={() => set_color_source(layer_idx, layer.color_volume_idx ?? null)}
445
+ aria-label={reset_color_label}
446
+ {@attach tooltip({ content: reset_color_label })}
447
+ >
448
+ <Icon icon="Reset" aria-hidden="true" style="--icon-size: 12px" />
449
+ </button>
450
+ {/if}
451
+ {#if warning}
452
+ <span
453
+ class="compat-warning"
454
+ {@attach tooltip({
455
+ content: `Grids differ (${warning}) — values are resampled by trilinear interpolation in shared coordinates`,
456
+ })}>⚠</span
457
+ >
458
+ {/if}
459
+ {/if}
460
+ </div>
461
+ {/each}
167
462
  </div>
168
463
  {/each}
169
464
  {:else}
170
465
  <!-- Single-layer: isovalue slider full width -->
171
466
  <label
172
- {@attach tooltip({ content: `Density threshold — surface is drawn where grid values equal this` })}
467
+ {@attach tooltip({
468
+ content: `Density threshold — surface is drawn where grid values equal this`,
469
+ })}
173
470
  >
174
471
  <span>Isovalue:</span>
175
- <input
176
- type="range"
177
- min={step}
178
- max={slider_max}
179
- {step}
180
- bind:value={settings.isovalue}
181
- />
182
- <span class="value">{format_num(settings.isovalue, `.4~g`)}</span>
472
+ <input type="range" min={step} max={slider_max} {step} bind:value={settings.isovalue} />
473
+ <span class="value">{format_num(settings.isovalue, `.3~g`)}</span>
183
474
  </label>
184
475
 
185
476
  <!-- Opacity + colors on one row -->
186
477
  <div class="pane-row compact-row">
187
478
  <label
188
- {@attach tooltip({ content: `Surface transparency — lower values reveal inner structure` })}
479
+ {@attach tooltip({
480
+ content: `Surface transparency — lower values reveal inner structure`,
481
+ })}
189
482
  >
190
483
  <span>Opacity:</span>
191
484
  <input
@@ -203,41 +496,114 @@
203
496
  <input type="color" bind:value={settings.positive_color} />
204
497
  </label>
205
498
  {#if settings.show_negative}
206
- <label
207
- {@attach tooltip({ content: `Color for the negative (−isovalue) surface` })}
208
- >
499
+ <label {@attach tooltip({ content: `Color for the negative (−isovalue) surface` })}>
209
500
  <span>&minus; Color</span>
210
501
  <input type="color" bind:value={settings.negative_color} />
211
502
  </label>
212
503
  {/if}
504
+ {#if volumes.length > 1}
505
+ {@render color_source_select(
506
+ -1,
507
+ set_single_color_source,
508
+ `Color this surface by another volume's values (e.g. density by ESP)`,
509
+ )}
510
+ {/if}
213
511
  </div>
214
512
  {/if}
215
513
 
216
- {#if volumes?.[active_volume_idx]?.periodic}
514
+ {#if any_periodic}
217
515
  <label
218
- {@attach tooltip({ content: `Extend isosurface beyond cell boundaries to close partial spheres (fraction of cell)` })}
516
+ {@attach tooltip({
517
+ content: `Extend isosurface beyond cell boundaries to close partial spheres (fraction of cell)`,
518
+ })}
219
519
  >
220
520
  Halo: {format_num(settings.halo, `.2f`)}
221
- <input
222
- type="range"
223
- min={0}
224
- max={0.5}
225
- step={0.01}
226
- bind:value={settings.halo}
227
- />
521
+ <input type="range" min={0} max={0.5} step={0.01} bind:value={settings.halo} />
228
522
  </label>
523
+ <div class="pane-row compact-row display-range">
524
+ <span
525
+ {@attach tooltip({
526
+ content: `Fractional display range per lattice vector (VESTA-style): repeats periodic surfaces and clips them exactly at these bounds, e.g. -0.15 to 2.15. Empty = follow the structure supercell.`,
527
+ })}>Range:</span
528
+ >
529
+ {#each [`a`, `b`, `c`] as axis_label, axis (axis)}
530
+ <label class="range-axis">
531
+ {axis_label}
532
+ <input
533
+ type="number"
534
+ step="0.05"
535
+ placeholder="0"
536
+ value={settings.display_range ? settings.display_range[axis][0] : ``}
537
+ onchange={(event) => update_display_range(axis, 0, event.currentTarget.value)}
538
+ />
539
+ <input
540
+ type="number"
541
+ step="0.05"
542
+ placeholder="1"
543
+ value={settings.display_range ? settings.display_range[axis][1] : ``}
544
+ onchange={(event) => update_display_range(axis, 1, event.currentTarget.value)}
545
+ />
546
+ </label>
547
+ {/each}
548
+ {#if settings.display_range}
549
+ <button
550
+ type="button"
551
+ class="icon-btn"
552
+ onclick={() => (settings.display_range = undefined)}
553
+ aria-label="Reset display range"
554
+ {@attach tooltip({ content: `Follow the structure supercell again` })}
555
+ >
556
+ <Icon icon="Reset" aria-hidden="true" style="--icon-size: 12px" />
557
+ </button>
558
+ {/if}
559
+ </div>
229
560
  {/if}
230
561
 
231
- {#if volumes[active_volume_idx]}
562
+ {#if !is_multi_layer && volumes[active_volume_idx]}
232
563
  <div class="grid-info">
233
- {volumes[active_volume_idx].grid_dims.join(` × `)} grid &nbsp;|&nbsp; [{
234
- format_num(data_range.min, `.3~g`)
235
- }, {format_num(data_range.max, `.3~g`)}]
564
+ {volumes[active_volume_idx].grid_dims.join(` × `)} grid &nbsp;|&nbsp; [{format_num(
565
+ data_range.min,
566
+ `.3~g`,
567
+ )}, {format_num(data_range.max, `.3~g`)}]
236
568
  </div>
237
569
  {/if}
238
570
  </SettingsSection>
239
571
 
240
572
  <style>
573
+ /* Shared chrome for native selects/number/color + ColorScaleSelect (--sms-*) */
574
+ :is(.compact-row, .volume-group, .display-range) {
575
+ --iso-ctrl-h: 22px;
576
+ --iso-ctrl-radius: 3px;
577
+ --iso-ctrl-border: 1px solid var(--border-color, light-dark(#b8bec8, #555));
578
+ --iso-ctrl-bg: light-dark(#fff, #1e1e1e);
579
+ --sms-min-height: var(--iso-ctrl-h);
580
+ --sms-border: var(--iso-ctrl-border);
581
+ --sms-border-radius: var(--iso-ctrl-radius);
582
+ --sms-bg: var(--iso-ctrl-bg);
583
+ font-size: 0.95em;
584
+
585
+ :is(select, input[type='number'], input[type='color']) {
586
+ box-sizing: border-box;
587
+ margin: 0; /* beat DraggablePane margins */
588
+ border: var(--iso-ctrl-border);
589
+ border-radius: var(--iso-ctrl-radius);
590
+ }
591
+ :is(select, input[type='number']) {
592
+ height: var(--iso-ctrl-h);
593
+ padding: 0 4px;
594
+ background: var(--iso-ctrl-bg);
595
+ color: inherit;
596
+ font: inherit;
597
+ line-height: 1.2;
598
+ }
599
+ input[type='color'] {
600
+ width: var(--iso-ctrl-h);
601
+ height: var(--iso-ctrl-h);
602
+ padding: 0;
603
+ cursor: pointer;
604
+ background: transparent;
605
+ }
606
+ }
241
607
  .compact-row {
242
608
  flex-wrap: wrap;
243
609
  gap: 4pt 14pt;
@@ -250,28 +616,136 @@
250
616
  opacity: 0.7;
251
617
  padding: 2px 0;
252
618
  }
253
- .layer-row {
619
+ .volume-group {
620
+ display: flex;
621
+ flex-direction: column;
622
+ gap: 3px;
623
+ min-width: 0;
624
+ padding: 3px 0;
625
+ border-top: 1px solid light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.12));
626
+ }
627
+ .volume-header {
628
+ display: flex;
629
+ flex-wrap: wrap;
630
+ align-items: center;
631
+ gap: 0.4em;
632
+ min-width: 0;
633
+ }
634
+ .volume-label {
635
+ font-weight: 600;
636
+ overflow: hidden;
637
+ text-overflow: ellipsis;
638
+ white-space: nowrap;
639
+ min-width: 0;
640
+ max-width: 14em;
641
+ }
642
+ .volume-dims,
643
+ .volume-note {
644
+ opacity: 0.6;
645
+ white-space: nowrap;
646
+ }
647
+ .volume-header .icon-btn:first-of-type {
648
+ margin-left: auto;
649
+ }
650
+ .icon-btn {
651
+ display: inline-grid;
652
+ place-items: center;
653
+ width: var(--iso-ctrl-h);
654
+ height: var(--iso-ctrl-h);
655
+ padding: 0;
656
+ border: none;
657
+ border-radius: var(--iso-ctrl-radius);
658
+ background: transparent;
659
+ color: inherit;
660
+ font-size: 0.875rem;
661
+ line-height: 1;
662
+ opacity: 0.7;
663
+ cursor: pointer;
664
+ }
665
+ .icon-btn:hover {
666
+ opacity: 1;
667
+ background: light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.15));
668
+ }
669
+ .layer-row,
670
+ .color-row {
254
671
  display: flex;
672
+ flex-wrap: wrap;
255
673
  align-items: center;
256
674
  gap: 0.3em;
257
- font-size: 0.85em;
258
- input[type='color'] {
259
- width: 24px;
260
- height: 20px;
261
- padding: 0;
262
- border: 1px solid var(--border-color, #ccc);
263
- border-radius: 3px;
264
- box-sizing: border-box;
265
- cursor: pointer;
266
- }
675
+ min-width: 0;
267
676
  input[type='checkbox'] {
268
677
  margin: 0;
678
+ flex-shrink: 0;
679
+ }
680
+ }
681
+ .layer-row {
682
+ .slider-field {
683
+ display: inline-flex;
684
+ align-items: center;
685
+ gap: 2px;
686
+ min-width: 0;
687
+ }
688
+ .slider-field + .slider-field {
689
+ margin-inline-start: 0.7em;
690
+ }
691
+ .slider-field:has(.isovalue-slider) {
692
+ flex: 1 1 6em;
693
+ }
694
+ .isovalue-slider {
695
+ flex: 1 1 4em;
696
+ min-width: 3em;
697
+ margin: 0;
698
+ }
699
+ .opacity-slider {
700
+ width: 40px;
701
+ min-width: 0;
702
+ margin: 0;
703
+ }
704
+ .layer-value {
705
+ font-family: monospace;
706
+ font-variant-numeric: tabular-nums;
707
+ }
708
+ }
709
+ .color-row {
710
+ padding-left: 1.4em;
711
+ label {
712
+ display: flex;
713
+ align-items: center;
714
+ gap: 4pt;
715
+ min-width: 0;
716
+ }
717
+ select {
718
+ max-width: min(9em, 100%);
719
+ }
720
+ :global(.multiselect) {
721
+ flex: 1 1 10em;
722
+ min-width: 0;
723
+ font: inherit;
724
+ color: inherit;
725
+ }
726
+ }
727
+ .range-input {
728
+ width: 4.5em;
729
+ }
730
+ .color-range {
731
+ display: flex;
732
+ flex-wrap: wrap;
733
+ align-items: center;
734
+ gap: 3px;
735
+ }
736
+ .display-range {
737
+ gap: 4pt 8pt;
738
+ .range-axis {
739
+ display: flex;
740
+ align-items: center;
741
+ gap: 3pt;
742
+ input {
743
+ width: 3.8em;
744
+ }
269
745
  }
270
746
  }
271
- .layer-value {
272
- font-family: monospace;
273
- font-size: 0.85em;
274
- min-width: 3.5em;
275
- text-align: right;
747
+ .compat-warning {
748
+ cursor: help;
749
+ color: light-dark(#b45309, #fbbf24);
276
750
  }
277
751
  </style>