matterviz 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (395) hide show
  1. package/dist/EmptyState.svelte +10 -4
  2. package/dist/FilePicker.svelte +12 -14
  3. package/dist/Icon.svelte +7 -4
  4. package/dist/MillerIndexInput.svelte +2 -2
  5. package/dist/app.css +53 -0
  6. package/dist/brillouin/BrillouinZone.svelte +75 -85
  7. package/dist/brillouin/BrillouinZone.svelte.d.ts +5 -7
  8. package/dist/brillouin/BrillouinZoneControls.svelte +3 -6
  9. package/dist/brillouin/BrillouinZoneExportPane.svelte +18 -14
  10. package/dist/brillouin/BrillouinZoneInfoPane.svelte +6 -10
  11. package/dist/brillouin/BrillouinZoneScene.svelte +26 -39
  12. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  13. package/dist/chempot-diagram/ChemPotDiagram.svelte +33 -38
  14. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +62 -74
  15. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +1 -1
  16. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +511 -547
  17. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +1 -1
  18. package/dist/chempot-diagram/async-compute.svelte.js +1 -1
  19. package/dist/chempot-diagram/controls-state.svelte.js +1 -1
  20. package/dist/colors/index.d.ts +0 -4
  21. package/dist/colors/index.js +19 -20
  22. package/dist/composition/BarChart.svelte +37 -31
  23. package/dist/composition/BubbleChart.svelte +17 -17
  24. package/dist/composition/Composition.svelte +7 -11
  25. package/dist/composition/Formula.svelte +18 -25
  26. package/dist/composition/FormulaFilter.svelte +92 -95
  27. package/dist/composition/PieChart.svelte +55 -41
  28. package/dist/composition/format.js +1 -1
  29. package/dist/composition/index.d.ts +2 -0
  30. package/dist/composition/index.js +14 -0
  31. package/dist/composition/parse.d.ts +2 -2
  32. package/dist/composition/parse.js +11 -10
  33. package/dist/constants.js +11 -9
  34. package/dist/convex-hull/ConvexHull.svelte +12 -15
  35. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  36. package/dist/convex-hull/ConvexHull2D.svelte +93 -92
  37. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  38. package/dist/convex-hull/ConvexHull3D.svelte +109 -140
  39. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  40. package/dist/convex-hull/ConvexHull4D.svelte +75 -94
  41. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  42. package/dist/convex-hull/ConvexHullChrome.svelte +36 -40
  43. package/dist/convex-hull/ConvexHullControls.svelte +46 -48
  44. package/dist/convex-hull/ConvexHullStats.svelte +193 -195
  45. package/dist/convex-hull/ConvexHullTooltip.svelte +5 -12
  46. package/dist/convex-hull/GasPressureControls.svelte +5 -12
  47. package/dist/convex-hull/StructurePopup.svelte +17 -21
  48. package/dist/convex-hull/StructurePopup.svelte.d.ts +2 -0
  49. package/dist/convex-hull/TemperatureSlider.svelte +4 -10
  50. package/dist/convex-hull/canvas-interactions.svelte.js +2 -1
  51. package/dist/convex-hull/helpers.d.ts +8 -10
  52. package/dist/convex-hull/helpers.js +36 -36
  53. package/dist/convex-hull/hull-state.svelte.d.ts +1 -1
  54. package/dist/convex-hull/thermodynamics.js +18 -17
  55. package/dist/coordination/CoordinationBarPlot.svelte +38 -33
  56. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
  57. package/dist/coordination/calc-coordination.d.ts +1 -1
  58. package/dist/coordination/calc-coordination.js +28 -25
  59. package/dist/element/BohrAtom.svelte +5 -8
  60. package/dist/element/ElementHeading.svelte +4 -1
  61. package/dist/element/ElementPhoto.svelte +11 -9
  62. package/dist/element/ElementStats.svelte +12 -10
  63. package/dist/element/ElementTile.svelte +24 -29
  64. package/dist/element/Nucleus.svelte +4 -2
  65. package/dist/element/data.d.ts +1 -2
  66. package/dist/element/helpers.d.ts +4 -0
  67. package/dist/element/helpers.js +18 -0
  68. package/dist/element/index.d.ts +1 -4
  69. package/dist/element/index.js +3 -18
  70. package/dist/feedback/DragOverlay.svelte +5 -5
  71. package/dist/feedback/DragOverlay.svelte.d.ts +2 -2
  72. package/dist/feedback/StatusMessage.svelte +7 -4
  73. package/dist/fermi-surface/FermiSlice.svelte +21 -18
  74. package/dist/fermi-surface/FermiSurface.svelte +14 -22
  75. package/dist/fermi-surface/FermiSurface.svelte.d.ts +5 -7
  76. package/dist/fermi-surface/FermiSurfaceControls.svelte +6 -20
  77. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  78. package/dist/fermi-surface/FermiSurfaceScene.svelte +37 -64
  79. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  80. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +9 -6
  81. package/dist/fermi-surface/compute.js +3 -3
  82. package/dist/fermi-surface/parse.js +22 -24
  83. package/dist/heatmap-matrix/HeatmapMatrix.svelte +109 -148
  84. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +2 -2
  85. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +9 -7
  86. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
  87. package/dist/heatmap-matrix/index.d.ts +5 -0
  88. package/dist/icons.d.ts +4 -0
  89. package/dist/icons.js +4 -0
  90. package/dist/io/ExportPane.svelte +10 -6
  91. package/dist/io/decompress.d.ts +2 -1
  92. package/dist/io/decompress.js +22 -35
  93. package/dist/io/export.d.ts +4 -0
  94. package/dist/io/export.js +5 -1
  95. package/dist/io/fetch.d.ts +0 -3
  96. package/dist/io/fetch.js +0 -11
  97. package/dist/io/index.d.ts +0 -1
  98. package/dist/io/index.js +2 -9
  99. package/dist/io/is-binary.d.ts +9 -0
  100. package/dist/io/is-binary.js +45 -1
  101. package/dist/io/url-drop.js +13 -25
  102. package/dist/isosurface/Isosurface.svelte +17 -23
  103. package/dist/isosurface/IsosurfaceControls.svelte +37 -42
  104. package/dist/isosurface/parse-vaspwave.d.ts +3 -0
  105. package/dist/isosurface/parse-vaspwave.js +138 -0
  106. package/dist/isosurface/parse.js +6 -6
  107. package/dist/labels.d.ts +6 -2
  108. package/dist/labels.js +43 -13
  109. package/dist/layout/FullscreenButton.svelte +2 -1
  110. package/dist/layout/FullscreenToggle.svelte +10 -4
  111. package/dist/layout/InfoCard.svelte +7 -18
  112. package/dist/layout/InfoTag.svelte +1 -3
  113. package/dist/layout/NumberRangeInput.svelte +46 -0
  114. package/dist/layout/NumberRangeInput.svelte.d.ts +12 -0
  115. package/dist/layout/PropertyFilter.svelte +2 -6
  116. package/dist/layout/SettingsSection.svelte +21 -17
  117. package/dist/layout/SubpageGrid.svelte +3 -1
  118. package/dist/layout/ViewerChrome.svelte +9 -10
  119. package/dist/layout/ViewerChrome.svelte.d.ts +2 -1
  120. package/dist/layout/fullscreen.js +3 -5
  121. package/dist/layout/fullscreen.svelte.js +1 -2
  122. package/dist/layout/index.d.ts +1 -0
  123. package/dist/layout/index.js +1 -0
  124. package/dist/layout/json-tree/JsonNode.svelte +8 -11
  125. package/dist/layout/json-tree/JsonTree.svelte +53 -77
  126. package/dist/layout/json-tree/JsonValue.svelte +7 -15
  127. package/dist/layout/json-tree/utils.js +14 -23
  128. package/dist/math.d.ts +2 -0
  129. package/dist/math.js +19 -0
  130. package/dist/overlays/ContextMenu.svelte +4 -2
  131. package/dist/overlays/CopyButton.svelte +11 -5
  132. package/dist/overlays/DragControlTab.svelte +6 -1
  133. package/dist/overlays/DraggablePane.svelte +81 -26
  134. package/dist/overlays/DraggablePane.svelte.d.ts +1 -0
  135. package/dist/overlays/GlassChip.svelte +30 -0
  136. package/dist/overlays/GlassChip.svelte.d.ts +8 -0
  137. package/dist/overlays/InfoPaneCards.svelte +11 -23
  138. package/dist/overlays/index.d.ts +7 -0
  139. package/dist/overlays/index.js +26 -0
  140. package/dist/overlays/portal.d.ts +2 -0
  141. package/dist/overlays/portal.js +14 -0
  142. package/dist/periodic-table/PeriodicTable.svelte +113 -145
  143. package/dist/periodic-table/PeriodicTable.svelte.d.ts +5 -3
  144. package/dist/periodic-table/PeriodicTableControls.svelte +11 -63
  145. package/dist/periodic-table/TableInset.svelte +9 -4
  146. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +50 -71
  147. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -8
  148. package/dist/phase-diagram/PhaseDiagramControls.svelte +70 -94
  149. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  150. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
  151. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +12 -12
  152. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +54 -58
  153. package/dist/phase-diagram/TdbInfoPanel.svelte +21 -15
  154. package/dist/phase-diagram/build-diagram.js +3 -3
  155. package/dist/phase-diagram/diagram-input.d.ts +1 -2
  156. package/dist/phase-diagram/parse.js +15 -15
  157. package/dist/phase-diagram/svg-to-diagram.js +21 -15
  158. package/dist/phase-diagram/utils.js +2 -3
  159. package/dist/plot/bar/BarPlot.svelte +430 -391
  160. package/dist/plot/bar/BarPlot.svelte.d.ts +2 -0
  161. package/dist/plot/bar/BarPlotControls.svelte +1 -3
  162. package/dist/plot/bar/BarPlotControls.svelte.d.ts +4 -6
  163. package/dist/plot/bar/SpacegroupBarPlot.svelte +29 -36
  164. package/dist/plot/bar/data.d.ts +5 -4
  165. package/dist/plot/bar/data.js +2 -1
  166. package/dist/plot/box/BoxPlot.svelte +301 -209
  167. package/dist/plot/box/BoxPlot.svelte.d.ts +2 -0
  168. package/dist/plot/box/Violin.svelte.d.ts +1 -0
  169. package/dist/plot/box/box-plot.js +1 -1
  170. package/dist/plot/core/auto-place.js +1 -1
  171. package/dist/plot/core/axis-utils.d.ts +12 -7
  172. package/dist/plot/core/axis-utils.js +48 -48
  173. package/dist/plot/core/components/AxisLabel.svelte +82 -20
  174. package/dist/plot/core/components/AxisLabel.svelte.d.ts +1 -0
  175. package/dist/plot/core/components/ColorBar.svelte +101 -106
  176. package/dist/plot/core/components/ColorScaleSelect.svelte +1 -1
  177. package/dist/plot/core/components/FillArea.svelte +10 -17
  178. package/dist/plot/core/components/HierarchyControls.svelte +249 -0
  179. package/dist/plot/{sunburst/SunburstControls.svelte.d.ts → core/components/HierarchyControls.svelte.d.ts} +12 -8
  180. package/dist/plot/core/components/InteractiveAxisLabel.svelte +1 -1
  181. package/dist/plot/core/components/InteractiveAxisLabel.svelte.d.ts +1 -1
  182. package/dist/plot/core/components/Line.svelte +18 -9
  183. package/dist/plot/core/components/Line.svelte.d.ts +2 -0
  184. package/dist/plot/core/components/PlotAxis.svelte +40 -17
  185. package/dist/plot/core/components/PlotControls.svelte +65 -41
  186. package/dist/plot/core/components/PlotLegend.svelte +31 -35
  187. package/dist/plot/core/components/PlotMarginals.svelte +616 -0
  188. package/dist/plot/core/components/PlotMarginals.svelte.d.ts +15 -0
  189. package/dist/plot/core/components/PlotTooltip.svelte +1 -1
  190. package/dist/plot/core/components/PortalSelect.svelte +4 -8
  191. package/dist/plot/core/components/ReferenceLine.svelte +27 -24
  192. package/dist/plot/core/components/ReferenceLine3D.svelte +54 -57
  193. package/dist/plot/core/components/ReferencePlane.svelte +7 -8
  194. package/dist/plot/core/components/ZeroLines.svelte +4 -8
  195. package/dist/plot/core/components/ZoomRect.svelte +1 -2
  196. package/dist/plot/core/components/index.d.ts +1 -0
  197. package/dist/plot/core/components/index.js +1 -0
  198. package/dist/plot/core/data-cleaning-signal.d.ts +46 -0
  199. package/dist/plot/core/data-cleaning-signal.js +467 -0
  200. package/dist/plot/core/data-cleaning.d.ts +2 -42
  201. package/dist/plot/core/data-cleaning.js +27 -474
  202. package/dist/plot/core/data-transform.js +9 -5
  203. package/dist/plot/core/fill-utils.d.ts +3 -1
  204. package/dist/plot/core/fill-utils.js +28 -6
  205. package/dist/plot/core/index.d.ts +1 -0
  206. package/dist/plot/core/index.js +1 -0
  207. package/dist/plot/core/interactions.js +0 -2
  208. package/dist/plot/core/layout.d.ts +3 -1
  209. package/dist/plot/core/layout.js +22 -13
  210. package/dist/plot/core/marginals.d.ts +170 -0
  211. package/dist/plot/core/marginals.js +436 -0
  212. package/dist/plot/core/reference-line.js +1 -1
  213. package/dist/plot/core/scales.d.ts +23 -0
  214. package/dist/plot/core/scales.js +39 -13
  215. package/dist/plot/core/types/fills.d.ts +101 -0
  216. package/dist/plot/core/types/fills.js +15 -0
  217. package/dist/plot/core/types/plot-3d.d.ts +87 -0
  218. package/dist/plot/core/types/plot-3d.js +4 -0
  219. package/dist/plot/core/types/reference-lines.d.ts +140 -0
  220. package/dist/plot/core/types/reference-lines.js +9 -0
  221. package/dist/plot/core/types.d.ts +29 -324
  222. package/dist/plot/core/types.js +23 -20
  223. package/dist/plot/core/utils/hierarchy-chart.d.ts +51 -0
  224. package/dist/plot/core/utils/hierarchy-chart.js +179 -0
  225. package/dist/plot/core/utils/hierarchy-labels.d.ts +20 -0
  226. package/dist/plot/core/utils/hierarchy-labels.js +99 -0
  227. package/dist/plot/core/utils/label-placement.js +2 -2
  228. package/dist/plot/histogram/Histogram.svelte +304 -325
  229. package/dist/plot/histogram/Histogram.svelte.d.ts +7 -7
  230. package/dist/plot/histogram/HistogramControls.svelte +30 -28
  231. package/dist/plot/histogram/histogram.d.ts +31 -0
  232. package/dist/plot/histogram/histogram.js +70 -0
  233. package/dist/plot/histogram/index.d.ts +1 -0
  234. package/dist/plot/histogram/index.js +1 -0
  235. package/dist/plot/index.d.ts +1 -0
  236. package/dist/plot/index.js +1 -0
  237. package/dist/plot/sankey/Sankey.svelte +78 -69
  238. package/dist/plot/sankey/Sankey.svelte.d.ts +2 -2
  239. package/dist/plot/sankey/SankeyControls.svelte +22 -16
  240. package/dist/plot/scatter/BinnedScatterPlot.svelte +263 -109
  241. package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +7 -0
  242. package/dist/plot/scatter/ElementScatter.svelte +4 -1
  243. package/dist/plot/scatter/ScatterPlot.svelte +477 -385
  244. package/dist/plot/scatter/ScatterPlot.svelte.d.ts +2 -0
  245. package/dist/plot/scatter/ScatterPlotControls.svelte +18 -19
  246. package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
  247. package/dist/plot/scatter/ScatterPoint.svelte +16 -9
  248. package/dist/plot/scatter/binned-scatter-types.d.ts +3 -2
  249. package/dist/plot/scatter/binned-scatter-types.js +6 -1
  250. package/dist/plot/scatter/scatter-data.d.ts +1 -1
  251. package/dist/plot/scatter/scatter-data.js +8 -7
  252. package/dist/plot/scatter-3d/ScatterPlot3D.svelte +25 -34
  253. package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +2 -2
  254. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +8 -5
  255. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +113 -116
  256. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +2 -2
  257. package/dist/plot/scatter-3d/Surface3D.svelte +4 -13
  258. package/dist/plot/sunburst/Sunburst.svelte +251 -329
  259. package/dist/plot/sunburst/Sunburst.svelte.d.ts +2 -2
  260. package/dist/plot/sunburst/index.d.ts +1 -1
  261. package/dist/plot/sunburst/index.js +2 -1
  262. package/dist/plot/sunburst/render.d.ts +2 -2
  263. package/dist/plot/sunburst/render.js +47 -22
  264. package/dist/plot/sunburst/sunburst.d.ts +5 -1
  265. package/dist/plot/sunburst/sunburst.js +10 -3
  266. package/dist/plot/treemap/Treemap.svelte +907 -0
  267. package/dist/plot/treemap/Treemap.svelte.d.ts +84 -0
  268. package/dist/plot/treemap/index.d.ts +3 -0
  269. package/dist/plot/treemap/index.js +4 -0
  270. package/dist/plot/treemap/treemap.d.ts +26 -0
  271. package/dist/plot/treemap/treemap.js +99 -0
  272. package/dist/rdf/RdfPlot.svelte +22 -26
  273. package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
  274. package/dist/rdf/calc-rdf.js +4 -8
  275. package/dist/sanitize.js +1 -1
  276. package/dist/scene/bind-renderer.svelte.js +14 -6
  277. package/dist/settings.d.ts +19 -9
  278. package/dist/settings.js +77 -50
  279. package/dist/spectral/Bands.svelte +223 -230
  280. package/dist/spectral/BandsAndDos.svelte +11 -9
  281. package/dist/spectral/BrillouinBandsDos.svelte +19 -27
  282. package/dist/spectral/Dos.svelte +70 -73
  283. package/dist/spectral/helpers.d.ts +3 -3
  284. package/dist/spectral/helpers.js +25 -35
  285. package/dist/state.svelte.js +5 -18
  286. package/dist/structure/Arrow.svelte +5 -18
  287. package/dist/structure/ArrowInstances.svelte +160 -0
  288. package/dist/structure/ArrowInstances.svelte.d.ts +16 -0
  289. package/dist/structure/AtomLegend.svelte +115 -75
  290. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  291. package/dist/structure/Bond.svelte +31 -23
  292. package/dist/structure/CanvasTooltip.svelte +15 -3
  293. package/dist/structure/CellSelect.svelte +11 -26
  294. package/dist/structure/Cylinder.svelte +24 -40
  295. package/dist/structure/InstancedAtoms.svelte +114 -0
  296. package/dist/structure/InstancedAtoms.svelte.d.ts +15 -0
  297. package/dist/structure/Lattice.svelte +85 -149
  298. package/dist/structure/SiteLabels.svelte +122 -0
  299. package/dist/structure/SiteLabels.svelte.d.ts +18 -0
  300. package/dist/structure/Structure.svelte +473 -419
  301. package/dist/structure/Structure.svelte.d.ts +4 -2
  302. package/dist/structure/StructureCarousel.svelte +630 -0
  303. package/dist/structure/StructureCarousel.svelte.d.ts +19 -0
  304. package/dist/structure/StructureControls.svelte +157 -399
  305. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  306. package/dist/structure/StructureExportPane.svelte +17 -21
  307. package/dist/structure/StructureInfoPane.svelte +72 -66
  308. package/dist/structure/StructureInfoPane.svelte.d.ts +1 -1
  309. package/dist/structure/StructureScene.svelte +467 -476
  310. package/dist/structure/StructureScene.svelte.d.ts +3 -1
  311. package/dist/structure/StructureViewport.svelte +386 -0
  312. package/dist/structure/StructureViewport.svelte.d.ts +63 -0
  313. package/dist/structure/atom-label-placement.d.ts +16 -12
  314. package/dist/structure/atom-label-placement.js +70 -37
  315. package/dist/structure/atom-properties.d.ts +2 -0
  316. package/dist/structure/atom-properties.js +104 -38
  317. package/dist/structure/bonding.d.ts +10 -3
  318. package/dist/structure/bonding.js +84 -28
  319. package/dist/structure/edit-history.d.ts +6 -0
  320. package/dist/structure/edit-history.js +27 -0
  321. package/dist/structure/export.d.ts +1 -0
  322. package/dist/structure/export.js +39 -31
  323. package/dist/structure/format-detect.d.ts +1 -0
  324. package/dist/structure/format-detect.js +22 -0
  325. package/dist/structure/geometry.d.ts +9 -0
  326. package/dist/structure/geometry.js +27 -0
  327. package/dist/structure/index.d.ts +16 -1
  328. package/dist/structure/index.js +13 -2
  329. package/dist/structure/measure.d.ts +1 -1
  330. package/dist/structure/measure.js +6 -12
  331. package/dist/structure/parse.d.ts +2 -1
  332. package/dist/structure/parse.js +243 -222
  333. package/dist/structure/partial-occupancy.js +4 -2
  334. package/dist/structure/pbc.js +78 -98
  335. package/dist/structure/polyhedra.d.ts +0 -1
  336. package/dist/structure/polyhedra.js +3 -18
  337. package/dist/structure/supercell.d.ts +1 -2
  338. package/dist/structure/supercell.js +7 -13
  339. package/dist/symmetry/SymmetryElements.svelte +11 -15
  340. package/dist/symmetry/SymmetryStats.svelte +56 -68
  341. package/dist/symmetry/WyckoffTable.svelte +5 -24
  342. package/dist/symmetry/cell-transform.js +1 -0
  343. package/dist/symmetry/index.d.ts +1 -0
  344. package/dist/symmetry/index.js +8 -2
  345. package/dist/symmetry/spacegroups.js +3 -2
  346. package/dist/table/HeatmapTable.svelte +1102 -365
  347. package/dist/table/HeatmapTable.svelte.d.ts +12 -2
  348. package/dist/table/ToggleMenu.svelte +5 -12
  349. package/dist/table/index.d.ts +21 -6
  350. package/dist/table/index.js +34 -21
  351. package/dist/theme/ThemeControl.svelte +16 -8
  352. package/dist/theme/index.d.ts +1 -5
  353. package/dist/theme/index.js +4 -10
  354. package/dist/theme/themes.mjs +14 -7
  355. package/dist/tooltip/KCoords.svelte +4 -1
  356. package/dist/tooltip/TooltipContent.svelte +2 -5
  357. package/dist/trajectory/Trajectory.svelte +515 -308
  358. package/dist/trajectory/Trajectory.svelte.d.ts +1 -1
  359. package/dist/trajectory/TrajectoryError.svelte +11 -8
  360. package/dist/trajectory/TrajectoryExportPane.svelte +14 -31
  361. package/dist/trajectory/TrajectoryInfoPane.svelte +85 -97
  362. package/dist/trajectory/extract.js +56 -34
  363. package/dist/trajectory/format-detect.js +11 -9
  364. package/dist/trajectory/frame-reader.d.ts +2 -0
  365. package/dist/trajectory/frame-reader.js +33 -29
  366. package/dist/trajectory/helpers.d.ts +2 -0
  367. package/dist/trajectory/helpers.js +18 -7
  368. package/dist/trajectory/index.d.ts +1 -0
  369. package/dist/trajectory/index.js +3 -0
  370. package/dist/trajectory/parse/h5-utils.d.ts +13 -0
  371. package/dist/trajectory/parse/h5-utils.js +109 -0
  372. package/dist/trajectory/parse/hdf5.d.ts +1 -1
  373. package/dist/trajectory/parse/hdf5.js +116 -106
  374. package/dist/trajectory/parse/index.d.ts +2 -3
  375. package/dist/trajectory/parse/index.js +17 -20
  376. package/dist/trajectory/parse/lammps.js +8 -12
  377. package/dist/trajectory/parse/vasp.js +10 -7
  378. package/dist/trajectory/parse/vaspout-electronic.d.ts +13 -0
  379. package/dist/trajectory/parse/vaspout-electronic.js +207 -0
  380. package/dist/trajectory/parse/vaspout-h5.d.ts +5 -0
  381. package/dist/trajectory/parse/vaspout-h5.js +284 -0
  382. package/dist/trajectory/parse/xyz.js +11 -7
  383. package/dist/trajectory/plotting.d.ts +8 -9
  384. package/dist/trajectory/plotting.js +99 -27
  385. package/dist/utils.d.ts +3 -0
  386. package/dist/utils.js +7 -0
  387. package/dist/xrd/XrdPlot.svelte +47 -61
  388. package/dist/xrd/broadening.js +2 -1
  389. package/dist/xrd/calc-xrd.js +2 -4
  390. package/dist/xrd/parse.d.ts +0 -1
  391. package/dist/xrd/parse.js +56 -73
  392. package/package.json +61 -33
  393. package/dist/api/mp.d.ts +0 -6
  394. package/dist/api/mp.js +0 -22
  395. package/dist/plot/sunburst/SunburstControls.svelte +0 -200
@@ -2,7 +2,7 @@
2
2
  lang="ts"
3
3
  generics="Metadata extends Record<string, unknown> = Record<string, unknown>"
4
4
  >
5
- import { format_value } from '../../labels'
5
+ import { format_value_or_num } from '../../labels'
6
6
  import { FullscreenToggle, set_fullscreen_bg } from '../../layout'
7
7
  import type { Vec2 } from '../../math'
8
8
  import type {
@@ -28,8 +28,17 @@
28
28
  compute_element_placement,
29
29
  PlotAxis,
30
30
  PlotLegend,
31
+ PlotMarginals,
31
32
  ReferenceLine,
32
33
  } from '..'
34
+ import type { MarginalSeriesInput, MarginalsProp } from '../core/marginals'
35
+ import {
36
+ add_sides,
37
+ marginal_axis,
38
+ marginal_axis_presence,
39
+ normalize_marginals,
40
+ reserve_marginal_pad,
41
+ } from '../core/marginals'
33
42
  import {
34
43
  build_obstacles_norm,
35
44
  clip_bar,
@@ -49,7 +58,9 @@
49
58
  resolve_axis_ranges,
50
59
  } from '../core/interactions'
51
60
  import {
61
+ AXIS_TITLE_OFFSET,
52
62
  calc_auto_padding,
63
+ DEFAULT_PLOT_PADDING,
53
64
  filter_padding,
54
65
  LABEL_GAP_DEFAULT,
55
66
  y2_axis_label_x,
@@ -59,7 +70,7 @@
59
70
  import type { IndexedRefLine } from '../core/reference-line'
60
71
  import { group_ref_lines_by_z, index_ref_lines } from '../core/reference-line'
61
72
  import {
62
- create_scale,
73
+ create_axis_scales,
63
74
  generate_ticks,
64
75
  get_nice_data_range,
65
76
  get_tick_label,
@@ -114,8 +125,8 @@
114
125
  y_axis = $bindable({}),
115
126
  y2_axis: y2_axis_prop = $bindable({}),
116
127
  display = $bindable(DEFAULTS.box.display),
117
- range_padding = 0.05,
118
- padding = { t: 20, b: 60, l: 60, r: 20 },
128
+ range_padding = 0,
129
+ padding = DEFAULT_PLOT_PADDING,
119
130
  legend = {},
120
131
  show_legend,
121
132
  box = {},
@@ -129,7 +140,7 @@
129
140
  show_mean = $bindable(DEFAULTS.box.show_mean),
130
141
  show_value_labels = false,
131
142
  value_label_stat = `median`,
132
- value_label_format = `.3~s`,
143
+ value_label_format = ``,
133
144
  kind = $bindable(DEFAULTS.box.kind),
134
145
  side = $bindable(DEFAULTS.box.side),
135
146
  bandwidth = DEFAULTS.box.bandwidth,
@@ -158,51 +169,55 @@
158
169
  header_controls,
159
170
  controls_extra,
160
171
  pan = {},
172
+ marginals = false,
161
173
  ...rest
162
- }: HTMLAttributes<HTMLDivElement> & BasePlotProps & PlotConfig & {
163
- series?: BoxPlotSeries<Metadata>[]
164
- orientation?: Orientation
165
- legend?: LegendConfig | null
166
- show_legend?: boolean
167
- box?: BoxStyle
168
- whisker?: WhiskerStyle
169
- median_style?: BoxLineStyle
170
- outlier_style?: OutlierStyle
171
- whisker_mode?: WhiskerMode
172
- whisker_range?: number
173
- whisker_percentiles?: Vec2
174
- show_outliers?: boolean
175
- show_mean?: boolean
176
- show_value_labels?: boolean
177
- value_label_stat?: `median` | `mean`
178
- value_label_format?: string
179
- kind?: ViolinKind
180
- side?: ViolinSide
181
- bandwidth?: BandwidthOption
182
- violin_width?: number
183
- violin_style?: ViolinStyle
184
- kde_points?: number
185
- kde_cut?: number
186
- kde_max_samples?: number
187
- kde_clip?: [number | null, number | null]
188
- tooltip?: Snippet<[BoxHandlerProps<Metadata>]>
189
- user_content?: Snippet<[UserContentProps]>
190
- header_controls?: Snippet<[{ height: number; width: number; fullscreen: boolean }]>
191
- controls_extra?: Snippet<[{ orientation: Orientation } & Required<PlotConfig>]>
192
- change?: (data: BoxHandlerProps<Metadata> | null) => void
193
- on_box_click?: (
194
- data: BoxHandlerProps<Metadata> & { event: MouseEvent | KeyboardEvent },
195
- ) => void
196
- on_box_hover?: (
197
- data:
198
- | (BoxHandlerProps<Metadata> & { event: MouseEvent | FocusEvent | KeyboardEvent })
199
- | null,
200
- ) => void
201
- ref_lines?: RefLine[]
202
- on_ref_line_click?: (event: RefLineEvent) => void
203
- on_ref_line_hover?: (event: RefLineEvent | null) => void
204
- pan?: PanConfig
205
- } = $props()
174
+ }: HTMLAttributes<HTMLDivElement> &
175
+ BasePlotProps &
176
+ PlotConfig & {
177
+ series?: BoxPlotSeries<Metadata>[]
178
+ orientation?: Orientation
179
+ legend?: LegendConfig | null
180
+ show_legend?: boolean
181
+ box?: BoxStyle
182
+ whisker?: WhiskerStyle
183
+ median_style?: BoxLineStyle
184
+ outlier_style?: OutlierStyle
185
+ whisker_mode?: WhiskerMode
186
+ whisker_range?: number
187
+ whisker_percentiles?: Vec2
188
+ show_outliers?: boolean
189
+ show_mean?: boolean
190
+ show_value_labels?: boolean
191
+ value_label_stat?: `median` | `mean`
192
+ value_label_format?: string
193
+ kind?: ViolinKind
194
+ side?: ViolinSide
195
+ bandwidth?: BandwidthOption
196
+ violin_width?: number
197
+ violin_style?: ViolinStyle
198
+ kde_points?: number
199
+ kde_cut?: number
200
+ kde_max_samples?: number
201
+ kde_clip?: [number | null, number | null]
202
+ tooltip?: Snippet<[BoxHandlerProps<Metadata>]>
203
+ user_content?: Snippet<[UserContentProps]>
204
+ header_controls?: Snippet<[{ height: number; width: number; fullscreen: boolean }]>
205
+ controls_extra?: Snippet<[{ orientation: Orientation } & Required<PlotConfig>]>
206
+ change?: (data: BoxHandlerProps<Metadata> | null) => void
207
+ on_box_click?: (
208
+ data: BoxHandlerProps<Metadata> & { event: MouseEvent | KeyboardEvent },
209
+ ) => void
210
+ on_box_hover?: (
211
+ data:
212
+ | (BoxHandlerProps<Metadata> & { event: MouseEvent | FocusEvent | KeyboardEvent })
213
+ | null,
214
+ ) => void
215
+ ref_lines?: RefLine[]
216
+ on_ref_line_click?: (event: RefLineEvent) => void
217
+ on_ref_line_hover?: (event: RefLineEvent | null) => void
218
+ pan?: PanConfig
219
+ marginals?: MarginalsProp
220
+ } = $props()
206
221
 
207
222
  let box_state = $derived({ ...DEFAULTS.box.box, ...box })
208
223
  let whisker_state = $derived({ ...DEFAULTS.box.whisker, ...whisker })
@@ -212,24 +227,20 @@
212
227
 
213
228
  // Merge secondary-axis defaults as deriveds instead of assigning back into the
214
229
  // $bindable props (which would push library defaults into the parent's bound state)
215
- let y2_axis = $derived(
216
- {
217
- format: ``,
218
- scale_type: `linear`,
219
- ticks: 5,
220
- range: [null, null],
221
- ...y2_axis_prop,
222
- } as typeof y2_axis_prop,
223
- )
224
- let x2_axis = $derived(
225
- {
226
- format: ``,
227
- scale_type: `linear`,
228
- ticks: 5,
229
- range: [null, null],
230
- ...x2_axis_prop,
231
- } as typeof x2_axis_prop,
232
- )
230
+ let y2_axis = $derived({
231
+ format: ``,
232
+ scale_type: `linear`,
233
+ ticks: 5,
234
+ range: [null, null],
235
+ ...y2_axis_prop,
236
+ } as typeof y2_axis_prop)
237
+ let x2_axis = $derived({
238
+ format: ``,
239
+ scale_type: `linear`,
240
+ ticks: 5,
241
+ range: [null, null],
242
+ ...x2_axis_prop,
243
+ } as typeof x2_axis_prop)
233
244
 
234
245
  let [width, height] = $state([0, 0])
235
246
  let wrapper: HTMLDivElement | undefined = $state()
@@ -256,7 +267,7 @@
256
267
  whisker_range: srs.whisker_range ?? whisker_range,
257
268
  whisker_percentiles: srs.whisker_percentiles ?? whisker_percentiles,
258
269
  collect_outliers: show_outliers && draws_box(srs) && (srs.visible ?? true),
259
- })
270
+ }),
260
271
  ),
261
272
  )
262
273
 
@@ -309,9 +320,8 @@
309
320
  // KDE per visible violin series, keyed by series index (bandwidth from the full sample)
310
321
  let violin_kdes = $derived.by(() => {
311
322
  const map = new SvelteMap<number, KdeResult>()
312
- const [val_axis, val_axis2] = orientation === `vertical`
313
- ? [y_axis, y2_axis]
314
- : [x_axis, x2_axis]
323
+ const [val_axis, val_axis2] =
324
+ orientation === `vertical` ? [y_axis, y2_axis] : [x_axis, x2_axis]
315
325
  for (const box_item of visible_boxes) {
316
326
  if (!draws_violin(box_item.series)) continue
317
327
  const samples = box_item.series.y ?? []
@@ -319,7 +329,10 @@
319
329
  // On a log value axis the KDE grid tail (data_min - cut*bandwidth) is usually <= 0 →
320
330
  // NaN pixels + LOG_EPS range pollution. Clamp the grid to the smallest positive sample.
321
331
  if ((is_secondary(box_item.series) ? val_axis2 : val_axis).scale_type === `log`) {
322
- const min_pos = samples.reduce((min, val) => (val > 0 && val < min ? val : min), Infinity)
332
+ const min_pos = samples.reduce(
333
+ (min, val) => (val > 0 && val < min ? val : min),
334
+ Infinity,
335
+ )
323
336
  // Guard: no positive samples → min_pos is Infinity; leave clip unchanged so the KDE
324
337
  // never receives a non-finite lower bound
325
338
  if (Number.isFinite(min_pos)) {
@@ -343,7 +356,11 @@
343
356
  // The horizontal category pixel axis is inverted, so flip the half-violin side to keep
344
357
  // `positive` meaning "above the center line" (vertical/`both` pass through unchanged)
345
358
  const to_screen_side = (eff_side: ViolinSide, vertical: boolean): ViolinSide =>
346
- vertical || eff_side === `both` ? eff_side : eff_side === `positive` ? `negative` : `positive`
359
+ vertical || eff_side === `both`
360
+ ? eff_side
361
+ : eff_side === `positive`
362
+ ? `negative`
363
+ : `positive`
347
364
 
348
365
  // Peak density per violin, computed once on data change (avoids spreading kde.density into
349
366
  // Math.max — unsafe for large kde_points — and re-deriving it on every render/hover).
@@ -360,8 +377,15 @@
360
377
  // Which boxes live on the secondary value axis (y2 for vertical, x2 for horizontal)
361
378
  const is_secondary = (srs: BoxPlotSeries<Metadata>): boolean =>
362
379
  orientation === `vertical` ? srs.y_axis === `y2` : srs.x_axis === `x2`
363
- let secondary_boxes = $derived(visible_boxes.filter((box_item) => is_secondary(box_item.series)))
380
+ let secondary_boxes = $derived(
381
+ visible_boxes.filter((box_item) => is_secondary(box_item.series)),
382
+ )
364
383
  let has_secondary = $derived(secondary_boxes.length > 0)
384
+ // The secondary value axis renders transposed by orientation: x2 (top) when horizontal, y2
385
+ // (right) when vertical. Derive once so axis rendering, ticks, range writes, point picking, and
386
+ // marginal placement all stay provably in sync.
387
+ let show_x2 = $derived(has_secondary && orientation === `horizontal`)
388
+ let show_y2 = $derived(has_secondary && orientation === `vertical`)
365
389
 
366
390
  // Collect value-axis points (whiskers, quartiles, outliers, KDE tails) for auto-range
367
391
  const value_points = (boxes: Box[]): { x: number; y: number }[] =>
@@ -407,8 +431,8 @@
407
431
  )
408
432
 
409
433
  return vertical
410
- ? ({ x: cat_range, x2: [0, 1] as Vec2, y: value_primary, y2: value_secondary })
411
- : ({ x: value_primary, x2: value_secondary, y: cat_range, y2: [0, 1] as Vec2 })
434
+ ? { x: cat_range, x2: [0, 1] as Vec2, y: value_primary, y2: value_secondary }
435
+ : { x: value_primary, x2: value_secondary, y: cat_range, y2: [0, 1] as Vec2 }
412
436
  })
413
437
 
414
438
  let ranges = $state<{
@@ -419,10 +443,14 @@
419
443
  current: { x: [0, 1], x2: [0, 1], y: [0, 1], y2: [0, 1] },
420
444
  })
421
445
 
422
- $effect(() => { // sync ranges from axis.range overrides / auto ranges
446
+ $effect(() => {
447
+ // sync ranges from axis.range overrides / auto ranges
423
448
  // resolve_axis_ranges returns null for transient non-finite bounds (skip: writing
424
449
  // NaN breaks scales and, since NaN !== NaN, loops the effect)
425
- const next = resolve_axis_ranges({ x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis }, auto_ranges)
450
+ const next = resolve_axis_ranges(
451
+ { x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis },
452
+ auto_ranges,
453
+ )
426
454
  if (!next) return
427
455
  // untrack the read of `ranges` so the assignment can't re-trigger this effect
428
456
  // (reading + writing the same state otherwise causes effect_update_depth_exceeded).
@@ -432,34 +460,40 @@
432
460
  }
433
461
  })
434
462
 
435
- const default_padding = { t: 20, b: 60, l: 60, r: 20 }
436
- let base_pad = $derived(filter_padding(padding, default_padding))
437
-
438
- $effect(() => { // dynamic padding from tick label widths
439
- const new_pad = width && height && ticks.y.length > 0
440
- ? calc_auto_padding({
441
- padding,
442
- default_padding,
443
- x2_axis: { ...x2_axis, tick_values: ticks.x2 },
444
- y_axis: { ...y_axis, tick_values: ticks.y },
445
- y2_axis: { ...y2_axis, tick_values: ticks.y2 },
446
- })
447
- : filter_padding(padding, default_padding)
448
- if (
449
- width && height && orientation === `vertical` && has_secondary && ticks.y2.length > 0
450
- ) {
463
+ let base_pad = $derived(filter_padding(padding, DEFAULT_PLOT_PADDING))
464
+
465
+ $effect(() => {
466
+ // dynamic padding from tick label widths
467
+ const new_pad =
468
+ width && height && ticks.y.length > 0
469
+ ? calc_auto_padding({
470
+ padding,
471
+ default_padding: DEFAULT_PLOT_PADDING,
472
+ x2_axis: { ...x2_axis, tick_values: ticks.x2 },
473
+ y_axis: { ...y_axis, tick_values: ticks.y },
474
+ y2_axis: { ...y2_axis, tick_values: ticks.y2 },
475
+ })
476
+ : filter_padding(padding, DEFAULT_PLOT_PADDING)
477
+ if (width && height && show_y2 && ticks.y2.length > 0) {
451
478
  const inside = y2_axis.tick?.label?.inside ?? false
452
479
  const tick_shift = inside ? 0 : (y2_axis.tick?.label?.shift?.x ?? 0) + 8
453
480
  const tick_width_contribution = inside ? 0 : tick_label_widths.y2_max
454
481
  const label_space = y2_axis.label ? 20 : 0
455
482
  new_pad.r = Math.max(new_pad.r, tick_shift + tick_width_contribution + 30 + label_space)
456
483
  }
457
- if (base_pad.t !== new_pad.t || base_pad.b !== new_pad.b ||
458
- base_pad.l !== new_pad.l || base_pad.r !== new_pad.r) base_pad = new_pad
484
+ if (
485
+ base_pad.t !== new_pad.t ||
486
+ base_pad.b !== new_pad.b ||
487
+ base_pad.l !== new_pad.l ||
488
+ base_pad.r !== new_pad.r
489
+ )
490
+ base_pad = new_pad
459
491
  })
460
492
 
461
493
  let legend_element = $state<HTMLDivElement | undefined>()
462
- const legend_footprint = $derived(measured_footprint(legend_element, { width: 120, height: 60 }))
494
+ const legend_footprint = $derived(
495
+ measured_footprint(legend_element, { width: 120, height: 60 }),
496
+ )
463
497
  const legend_has_explicit_pos = $derived(has_explicit_position(legend?.style))
464
498
 
465
499
  // Obstacle field in normalized [0,1] coords: each box modeled as a whisker-spanning segment
@@ -476,8 +510,12 @@
476
510
  const secondary = is_secondary(box_item.series)
477
511
  const cat_rng = vertical ? ranges.current.x : ranges.current.y
478
512
  const val_rng = vertical
479
- ? (secondary ? ranges.current.y2 : ranges.current.y)
480
- : (secondary ? ranges.current.x2 : ranges.current.x)
513
+ ? secondary
514
+ ? ranges.current.y2
515
+ : ranges.current.y
516
+ : secondary
517
+ ? ranges.current.x2
518
+ : ranges.current.x
481
519
  const cat_span = cat_rng[1] - cat_rng[0]
482
520
  const val_span = val_rng[1] - val_rng[0]
483
521
  if (cat_span === 0 || val_span === 0) continue
@@ -499,37 +537,70 @@
499
537
  width,
500
538
  height,
501
539
  obstacles_norm,
502
- legend: legend != null && should_show_legend && legend_element != null &&
503
- !legend_has_explicit_pos
504
- ? { footprint: legend_footprint, clearance: legend?.axis_clearance }
505
- : null,
506
- })
540
+ legend:
541
+ legend != null &&
542
+ should_show_legend &&
543
+ legend_element != null &&
544
+ !legend_has_explicit_pos
545
+ ? { footprint: legend_footprint, clearance: legend?.axis_clearance }
546
+ : null,
547
+ }),
548
+ )
549
+ // Marginals are opt-in (default prop `false`) and bind to the VALUE axis, pooling each box's
550
+ // raw samples. The default side follows orientation (value axis = y when vertical, x when
551
+ // horizontal) so the `marginals` boolean / type-string shorthand land on a meaningful side.
552
+ // Each box is tagged with its value axis so a primary-axis marginal ignores secondary boxes.
553
+ const marginal_vertical = $derived(orientation === `vertical`)
554
+ const resolved_marginals = $derived(
555
+ normalize_marginals(marginals, marginal_vertical ? { right: true } : { top: true }),
507
556
  )
508
- const pad = $derived(decor.pad)
557
+ const pad = $derived(add_sides(decor.pad, reserve_marginal_pad(resolved_marginals)))
558
+ const marginal_series = $derived<MarginalSeriesInput[]>(
559
+ visible_boxes.map((box_item) => {
560
+ const secondary = is_secondary(box_item.series)
561
+ return {
562
+ x: marginal_vertical ? undefined : (box_item.series.y ?? []),
563
+ y: marginal_vertical ? (box_item.series.y ?? []) : undefined,
564
+ color: box_color(box_item.idx),
565
+ label: box_item.series.label,
566
+ visible: true,
567
+ x_axis: marginal_vertical ? `x1` : secondary ? `x2` : `x1`,
568
+ y_axis: marginal_vertical ? (secondary ? `y2` : `y1`) : `y1`,
569
+ }
570
+ }),
571
+ )
572
+ const marginal_has_axis = $derived(marginal_axis_presence(show_x2, show_y2))
509
573
  const legend_auto_outside = $derived(decor.legend_outside)
510
574
  const legend_outside_x = $derived(decor.legend_pos.x)
511
575
  const legend_outside_y = $derived(decor.legend_pos.y)
512
576
  const chart_width = $derived(Math.max(1, width - pad.l - pad.r))
513
577
  const chart_height = $derived(Math.max(1, height - pad.t - pad.b))
514
578
 
515
- let scales = $derived({
516
- x: create_scale(x_axis.scale_type ?? `linear`, ranges.current.x, [pad.l, width - pad.r]),
517
- x2: create_scale(x2_axis.scale_type ?? `linear`, ranges.current.x2, [pad.l, width - pad.r]),
518
- y: create_scale(y_axis.scale_type ?? `linear`, ranges.current.y, [height - pad.b, pad.t]),
519
- y2: create_scale(y2_axis.scale_type ?? `linear`, ranges.current.y2, [height - pad.b, pad.t]),
520
- })
579
+ let scales = $derived(
580
+ create_axis_scales(
581
+ { x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis },
582
+ ranges.current,
583
+ pad,
584
+ width,
585
+ height,
586
+ ),
587
+ )
521
588
 
522
589
  // Value scale for a box (vertical -> y/y2, horizontal -> x/x2), made log-safe: on a
523
590
  // log value axis, stats at values <= 0 (whisker_low is often exactly 0; negative
524
591
  // outliers) have no finite pixel. Clamp to LOG_EPS so whiskers/boxes/labels draw
525
592
  // toward the plot edge (the clip group crops the overshoot) instead of NaN coords.
526
- const box_val_scale = (srs: BoxPlotSeries<Metadata>): (val: number) => number => {
593
+ const box_val_scale = (srs: BoxPlotSeries<Metadata>): ((val: number) => number) => {
527
594
  const vertical = orientation === `vertical`
528
595
  const secondary = is_secondary(srs)
529
596
  const scale = vertical
530
- ? (secondary ? scales.y2 : scales.y)
531
- : (secondary ? scales.x2 : scales.x)
532
- const axis = vertical ? (secondary ? y2_axis : y_axis) : (secondary ? x2_axis : x_axis)
597
+ ? secondary
598
+ ? scales.y2
599
+ : scales.y
600
+ : secondary
601
+ ? scales.x2
602
+ : scales.x
603
+ const axis = vertical ? (secondary ? y2_axis : y_axis) : secondary ? x2_axis : x_axis
533
604
  return axis.scale_type === `log` ? (val) => scale(Math.max(val, LOG_EPS)) : scale
534
605
  }
535
606
 
@@ -543,35 +614,32 @@
543
614
  return Object.fromEntries(slot_list.map((cat, idx) => [idx, cat]))
544
615
  })
545
616
 
546
- let ticks = $derived({
547
- x: width && height
548
- ? (cat_axis === `x` ? slot_indices : generate_ticks(
549
- ranges.current.x,
550
- x_axis.scale_type ?? `linear`,
551
- x_axis.ticks,
552
- scales.x,
553
- { default_count: 8 },
554
- ))
555
- : [],
556
- y: width && height
557
- ? (cat_axis === `y` ? slot_indices : generate_ticks(
558
- ranges.current.y,
559
- y_axis.scale_type ?? `linear`,
560
- y_axis.ticks,
561
- scales.y,
562
- { default_count: 6 },
563
- ))
564
- : [],
565
- y2: width && height && has_secondary && orientation === `vertical`
566
- ? generate_ticks(ranges.current.y2, y2_axis.scale_type ?? `linear`, y2_axis.ticks, scales.y2, {
567
- default_count: 6,
568
- })
569
- : [],
570
- x2: width && height && has_secondary && orientation === `horizontal`
571
- ? generate_ticks(ranges.current.x2, x2_axis.scale_type ?? `linear`, x2_axis.ticks, scales.x2, {
572
- default_count: 8,
573
- })
574
- : [],
617
+ let ticks = $derived.by(() => {
618
+ const axis_ticks = (
619
+ axis: typeof x_axis,
620
+ range: Vec2,
621
+ scale: typeof scales.x,
622
+ default_count: number,
623
+ show = true,
624
+ ) =>
625
+ width && height && show
626
+ ? generate_ticks(range, axis.scale_type ?? `linear`, axis.ticks, scale, {
627
+ default_count,
628
+ })
629
+ : []
630
+ // categorical axes show one tick per slot instead of generated numeric ticks
631
+ return {
632
+ x:
633
+ cat_axis === `x` && width && height
634
+ ? slot_indices
635
+ : axis_ticks(x_axis, ranges.current.x, scales.x, 8),
636
+ y:
637
+ cat_axis === `y` && width && height
638
+ ? slot_indices
639
+ : axis_ticks(y_axis, ranges.current.y, scales.y, 6),
640
+ y2: axis_ticks(y2_axis, ranges.current.y2, scales.y2, 6, show_y2),
641
+ x2: axis_ticks(x2_axis, ranges.current.x2, scales.x2, 8, show_x2),
642
+ }
575
643
  })
576
644
 
577
645
  let tick_label_widths = $derived({
@@ -583,7 +651,8 @@
583
651
  // Shared pan/zoom/touch/drag-rect interaction controller
584
652
  const pan_zoom = create_pan_zoom({
585
653
  ranges: () => ranges.current,
586
- scale_type: (axis) => ({ x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis })[axis].scale_type,
654
+ scale_type: (axis) =>
655
+ ({ x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis })[axis].scale_type,
587
656
  plot_dims: () => ({ width: chart_width, height: chart_height }),
588
657
  pan: () => pan,
589
658
  set_range: (axis, range) => (ranges.current[axis] = range),
@@ -595,15 +664,11 @@
595
664
  // the secondary value axis is x2 only in horizontal mode, y2 only in vertical
596
665
  // (is_secondary keys off orientation); writing the off-orientation axis would
597
666
  // store a phantom range from its [0, 1] sentinel scale into the bound prop
598
- const next_x2 = has_secondary && orientation === `horizontal`
599
- ? invert_rect_range(scales.x2, start.x, current.x)
600
- : null
667
+ const next_x2 = show_x2 ? invert_rect_range(scales.x2, start.x, current.x) : null
601
668
  if (next_x2) x2_axis_prop = { ...x2_axis_prop, range: next_x2 }
602
669
  const next_y = invert_rect_range(scales.y, start.y, current.y)
603
670
  if (next_y) y_axis = { ...y_axis, range: next_y }
604
- const next_y2 = has_secondary && orientation === `vertical`
605
- ? invert_rect_range(scales.y2, start.y, current.y)
606
- : null
671
+ const next_y2 = show_y2 ? invert_rect_range(scales.y2, start.y, current.y) : null
607
672
  if (next_y2) y2_axis_prop = { ...y2_axis_prop, range: next_y2 }
608
673
  },
609
674
  on_reset: () => {
@@ -632,7 +697,10 @@
632
697
  })),
633
698
  )
634
699
 
635
- const legend_vis = create_legend_visibility(() => series, (next) => (series = next))
700
+ const legend_vis = create_legend_visibility(
701
+ () => series,
702
+ (next) => (series = next),
703
+ )
636
704
 
637
705
  let box_points_for_placement = $derived.by(() => {
638
706
  if (!width || !height || visible_boxes.length === 0) return []
@@ -725,16 +793,13 @@
725
793
 
726
794
  // Value label helper
727
795
  const value_label_for = (stats: Box[`stats`]): string =>
728
- format_value(value_label_stat === `mean` ? stats.mean : stats.median, value_label_format)
796
+ format_value_or_num(
797
+ value_label_stat === `mean` ? stats.mean : stats.median,
798
+ value_label_format,
799
+ )
729
800
  </script>
730
801
 
731
- {#snippet seg(
732
- p1: Vec2,
733
- p2: Vec2,
734
- stroke: string,
735
- sw: number,
736
- dash?: string,
737
- )}
802
+ {#snippet seg(p1: Vec2, p2: Vec2, stroke: string, sw: number, dash?: string)}
738
803
  <line
739
804
  x1={p1[0]}
740
805
  y1={p1[1]}
@@ -790,7 +855,7 @@
790
855
  bind:clientWidth={width}
791
856
  bind:clientHeight={height}
792
857
  {...rest}
793
- class="box-plot {rest.class ?? ``}"
858
+ class={[`box-plot`, rest.class]}
794
859
  class:fullscreen
795
860
  >
796
861
  {#if width && height}
@@ -806,7 +871,7 @@
806
871
  bind:this={svg_element}
807
872
  role="application"
808
873
  aria-label={rest[`aria-label`] ??
809
- ([x_axis.label, y_axis.label].filter(Boolean).join(` vs `) || `Box plot`)}
874
+ ([x_axis.label, y_axis.label].filter(Boolean).join(` vs `) || `Box plot`)}
810
875
  tabindex="0"
811
876
  onfocusin={() => pan_zoom.set_focused(true)}
812
877
  onfocusout={() => pan_zoom.set_focused(false)}
@@ -847,65 +912,67 @@
847
912
 
848
913
  <PlotAxis
849
914
  side="x"
850
- ticks={ticks.x as number[]}
915
+ ticks={ticks.x}
851
916
  place={scales.x}
852
917
  axis={x_axis}
853
- domain={ranges.current.x as Vec2}
918
+ domain={ranges.current.x}
854
919
  {pad}
855
920
  {width}
856
921
  {height}
857
922
  show_grid={display.x_grid}
858
923
  tick_label={(tick) =>
859
- get_tick_label(tick, cat_axis === `x` ? effective_cat_ticks : x_axis.ticks)}
924
+ get_tick_label(tick, cat_axis === `x` ? effective_cat_ticks : x_axis.ticks)}
860
925
  tick_color={cat_axis === `x` ? (tick) => slot_colors.get(tick) : undefined}
861
926
  label_x={pad.l + chart_width / 2 + (x_axis.label_shift?.x ?? 0)}
862
- label_y={height - pad.b / 3 + (x_axis.label_shift?.y ?? 0)}
927
+ label_y={height - pad.b + AXIS_TITLE_OFFSET + (x_axis.label_shift?.y ?? 0)}
863
928
  />
864
929
 
865
- {#if has_secondary && orientation === `horizontal`}
930
+ {#if show_x2}
866
931
  <PlotAxis
867
932
  side="x2"
868
- ticks={ticks.x2 as number[]}
933
+ ticks={ticks.x2}
869
934
  place={scales.x2}
870
935
  axis={x2_axis}
871
- domain={ranges.current.x2 as Vec2}
936
+ domain={ranges.current.x2}
872
937
  {pad}
873
938
  {width}
874
939
  {height}
875
940
  show_grid={display.x2_grid}
876
941
  tick_label={(tick) => get_tick_label(tick, x2_axis.ticks)}
877
942
  label_x={pad.l + chart_width / 2 + (x2_axis.label_shift?.x ?? 0)}
878
- label_y={Math.max(12, pad.t - (x2_axis.label_shift?.y ?? 40))}
943
+ label_y={Math.max(12, pad.t - (x2_axis.label_shift?.y ?? AXIS_TITLE_OFFSET))}
879
944
  />
880
945
  {/if}
881
946
 
882
947
  <PlotAxis
883
948
  side="y"
884
- ticks={ticks.y as number[]}
949
+ ticks={ticks.y}
885
950
  place={scales.y}
886
951
  axis={y_axis}
887
- domain={ranges.current.y as Vec2}
952
+ domain={ranges.current.y}
888
953
  {pad}
889
954
  {width}
890
955
  {height}
891
956
  show_grid={display.y_grid}
892
957
  tick_label={(tick) =>
893
- get_tick_label(tick, cat_axis === `y` ? effective_cat_ticks : y_axis.ticks)}
958
+ get_tick_label(tick, cat_axis === `y` ? effective_cat_ticks : y_axis.ticks)}
894
959
  tick_color={cat_axis === `y` ? (tick) => slot_colors.get(tick) : undefined}
895
960
  label_x={Math.max(
896
961
  12,
897
- pad.l - (y_axis.tick?.label?.inside ? 0 : tick_label_widths.y_max) - LABEL_GAP_DEFAULT,
962
+ pad.l -
963
+ (y_axis.tick?.label?.inside ? 0 : tick_label_widths.y_max) -
964
+ LABEL_GAP_DEFAULT,
898
965
  ) + (y_axis.label_shift?.x ?? 0)}
899
966
  label_y={pad.t + chart_height / 2 + (y_axis.label_shift?.y ?? 0)}
900
967
  />
901
968
 
902
- {#if has_secondary && orientation === `vertical`}
969
+ {#if show_y2}
903
970
  <PlotAxis
904
971
  side="y2"
905
- ticks={ticks.y2 as number[]}
972
+ ticks={ticks.y2}
906
973
  place={scales.y2}
907
974
  axis={y2_axis}
908
- domain={ranges.current.y2 as Vec2}
975
+ domain={ranges.current.y2}
909
976
  {pad}
910
977
  {width}
911
978
  {height}
@@ -941,8 +1008,8 @@
941
1008
  x2_scale_type={x2_axis.scale_type}
942
1009
  y_scale_type={y_axis.scale_type}
943
1010
  y2_scale_type={y2_axis.scale_type}
944
- has_x2={has_secondary && orientation === `horizontal`}
945
- has_y2={has_secondary && orientation === `vertical`}
1011
+ has_x2={show_x2}
1012
+ has_y2={show_y2}
946
1013
  {width}
947
1014
  {height}
948
1015
  {pad}
@@ -963,12 +1030,13 @@
963
1030
  {@const draw_box = draws_box(box_item.series)}
964
1031
  {@const kde = violin_kdes.get(box_item.idx)}
965
1032
  {@const eff_side = box_item.series.side ?? side}
966
- {@const bw = box_item.series.box_width ??
967
- (kde ? DEFAULTS.box.violin_box_width : DEFAULTS.box.box_width)}
1033
+ {@const bw =
1034
+ box_item.series.box_width ??
1035
+ (kde ? DEFAULTS.box.violin_box_width : DEFAULTS.box.box_width)}
968
1036
  {@const c_lo = cat_scale(box_item.slot - bw / 2)}
969
1037
  {@const c_hi = cat_scale(box_item.slot + bw / 2)}
970
1038
  {@const c_center = cat_scale(box_item.slot)}
971
- {@const cap = Math.abs(c_hi - c_lo) * (whisker_state.cap_fraction ?? 0.5) / 2}
1039
+ {@const cap = (Math.abs(c_hi - c_lo) * (whisker_state.cap_fraction ?? 0.5)) / 2}
972
1040
  {@const cap_lo = c_center - cap}
973
1041
  {@const cap_hi = c_center + cap}
974
1042
  {@const v_q1 = val_scale(stats.q1)}
@@ -978,7 +1046,7 @@
978
1046
  {@const v_wh = val_scale(stats.whisker_high)}
979
1047
  {@const v_mean = val_scale(stats.mean)}
980
1048
  {@const pt = (cross: number, val: number): Vec2 =>
981
- vertical ? [cross, val] : [val, cross]}
1049
+ vertical ? [cross, val] : [val, cross]}
982
1050
  {@const [q1x, q1y] = pt(c_lo, v_q1)}
983
1051
  {@const [q3x, q3y] = pt(c_hi, v_q3)}
984
1052
  {@const [wlx, wly] = pt(c_lo, v_wl)}
@@ -992,12 +1060,12 @@
992
1060
  {@const hit_w = Math.abs(whx - wlx)}
993
1061
  {@const hit_h = Math.abs(why - wly)}
994
1062
  {@const [label_x, label_y] = vertical
995
- ? [c_center, Math.min(v_wh, v_wl) - 6]
996
- : [Math.max(v_wh, v_wl) + 6, c_center]}
1063
+ ? [c_center, Math.min(v_wh, v_wl) - 6]
1064
+ : [Math.max(v_wh, v_wl) + 6, c_center]}
997
1065
  {@const violin_half = Math.abs(
998
- cat_scale(box_item.slot + (box_item.series.violin_width ?? violin_width) / 2) -
999
- c_center,
1000
- )}
1066
+ cat_scale(box_item.slot + (box_item.series.violin_width ?? violin_width) / 2) -
1067
+ c_center,
1068
+ )}
1001
1069
  {@const max_density = kde ? (violin_max_density.get(box_item.idx) ?? 0) : 0}
1002
1070
  <!-- svelte-ignore a11y_no_static_element_interactions a11y_click_events_have_key_events -->
1003
1071
  <g
@@ -1008,7 +1076,7 @@
1008
1076
  aria-label={`box ${box_item.idx + 1}: ${box_item.series.label ?? ``}`}
1009
1077
  style:cursor={on_box_click ? `pointer` : undefined}
1010
1078
  opacity={hovered_legend_series_idx !== null &&
1011
- hovered_legend_series_idx !== box_item.idx
1079
+ hovered_legend_series_idx !== box_item.idx
1012
1080
  ? 0.25
1013
1081
  : 1}
1014
1082
  onmousemove={handle_box_hover(box_item, color)}
@@ -1017,7 +1085,8 @@
1017
1085
  change(null)
1018
1086
  on_box_hover?.(null)
1019
1087
  }}
1020
- onclick={(evt) => on_box_click?.({ ...get_box_data(box_item, color), event: evt })}
1088
+ onclick={(evt) =>
1089
+ on_box_click?.({ ...get_box_data(box_item, color), event: evt })}
1021
1090
  onkeydown={(evt) => {
1022
1091
  if (evt.key === `Enter` || evt.key === ` `) {
1023
1092
  evt.preventDefault()
@@ -1064,7 +1133,12 @@
1064
1133
  stroke-width={box_state.stroke_width}
1065
1134
  />
1066
1135
  <!-- median (solid) and mean (dashed) -->
1067
- {@render seg(pt(c_lo, v_med), pt(c_hi, v_med), median_state.color, median_state.width)}
1136
+ {@render seg(
1137
+ pt(c_lo, v_med),
1138
+ pt(c_hi, v_med),
1139
+ median_state.color,
1140
+ median_state.width,
1141
+ )}
1068
1142
  {#if show_mean}
1069
1143
  {@render seg(
1070
1144
  pt(c_lo, v_mean),
@@ -1120,16 +1194,33 @@
1120
1194
 
1121
1195
  {@render ref_lines_layer(ref_lines_by_z.below_points)}
1122
1196
  {@render ref_lines_layer(ref_lines_by_z.above_all)}
1197
+
1198
+ <!-- Marginal distribution strips -->
1199
+ <PlotMarginals
1200
+ marginals={resolved_marginals}
1201
+ series={marginal_series}
1202
+ {width}
1203
+ {height}
1204
+ {pad}
1205
+ has_axis={marginal_has_axis}
1206
+ axes={{
1207
+ x1: marginal_axis(scales.x, ranges.current.x, x_axis),
1208
+ x2: marginal_axis(scales.x2, ranges.current.x2, x2_axis),
1209
+ y1: marginal_axis(scales.y, ranges.current.y, y_axis),
1210
+ y2: marginal_axis(scales.y2, ranges.current.y2, y2_axis),
1211
+ }}
1212
+ id={clip_path_id}
1213
+ />
1123
1214
  </svg>
1124
1215
 
1125
1216
  {#if legend && should_show_legend}
1126
1217
  {@const legend_pos = placed_coords(
1127
- legend_auto_outside,
1128
- { x: legend_outside_x, y: legend_outside_y },
1129
- legend_placement,
1130
- legend_tween.coords.current,
1131
- { x: pad.l + 10, y: pad.t + 10 },
1132
- )}
1218
+ legend_auto_outside,
1219
+ { x: legend_outside_x, y: legend_outside_y },
1220
+ legend_placement,
1221
+ legend_tween.coords.current,
1222
+ { x: pad.l + 10, y: pad.t + 10 },
1223
+ )}
1133
1224
  <PlotLegend
1134
1225
  bind:root_element={legend_element}
1135
1226
  {...legend}
@@ -1139,9 +1230,8 @@
1139
1230
  on_double_click={legend?.on_double_click ?? legend_vis.on_double_click}
1140
1231
  on_hover_change={legend_tween.set_locked}
1141
1232
  on_item_hover={(item) =>
1142
- (hovered_legend_series_idx = item != null && item.series_idx >= 0
1143
- ? item.series_idx
1144
- : null)}
1233
+ (hovered_legend_series_idx =
1234
+ item != null && item.series_idx >= 0 ? item.series_idx : null)}
1145
1235
  active_series_idx={hover_info?.series_idx ?? hovered_legend_series_idx}
1146
1236
  style={`position: absolute; left: ${legend_pos.x}px; top: ${legend_pos.y}px; pointer-events: auto; ${
1147
1237
  legend?.style || ``
@@ -1161,7 +1251,7 @@
1161
1251
  {#if tooltip}
1162
1252
  {@render tooltip({ ...hover_info, fullscreen })}
1163
1253
  {:else}
1164
- {@const fmt = (orientation === `vertical` ? y_axis.format : x_axis.format) || `.3~s`}
1254
+ {@const fmt = orientation === `vertical` ? y_axis.format : x_axis.format}
1165
1255
  {@const stat = hover_info.stats}
1166
1256
  {@const rows = [
1167
1257
  [`max`, stat.whisker_high],
@@ -1175,7 +1265,7 @@
1175
1265
  <div><strong>{hover_info.category_label}</strong></div>
1176
1266
  {/if}
1177
1267
  {#each rows as [label, value] (label)}
1178
- <div>{label}: {format_value(value, fmt)}</div>
1268
+ <div>{label}: {format_value_or_num(value, fmt)}</div>
1179
1269
  {/each}
1180
1270
  {#if show_outliers && stat.outliers.length > 0}
1181
1271
  <div>outliers: {stat.outliers.length}</div>
@@ -1206,12 +1296,12 @@
1206
1296
  bind:y_axis
1207
1297
  bind:y2_axis={y2_axis_prop}
1208
1298
  bind:display
1209
- auto_x_range={auto_ranges.x as Vec2}
1210
- auto_x2_range={auto_ranges.x2 as Vec2}
1211
- auto_y_range={auto_ranges.y as Vec2}
1212
- auto_y2_range={auto_ranges.y2 as Vec2}
1213
- has_x2_points={has_secondary && orientation === `horizontal`}
1214
- has_y2_points={has_secondary && orientation === `vertical`}
1299
+ auto_x_range={auto_ranges.x}
1300
+ auto_x2_range={auto_ranges.x2}
1301
+ auto_y_range={auto_ranges.y}
1302
+ auto_y2_range={auto_ranges.y2}
1303
+ has_x2_points={show_x2}
1304
+ has_y2_points={show_y2}
1215
1305
  children={controls_extra}
1216
1306
  />
1217
1307
  {/if}
@@ -1268,7 +1358,9 @@
1268
1358
  .box-plot :global(.pane-toggle),
1269
1359
  .box-plot .header-controls {
1270
1360
  opacity: 0;
1271
- transition: opacity 0.2s, background-color 0.2s;
1361
+ transition:
1362
+ opacity 0.2s,
1363
+ background-color 0.2s;
1272
1364
  }
1273
1365
  .box-plot:hover :global(.pane-toggle),
1274
1366
  .box-plot:hover .header-controls,