matterviz 0.3.2 → 0.3.4

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 (281) hide show
  1. package/dist/EmptyState.svelte +10 -2
  2. package/dist/FilePicker.svelte +123 -82
  3. package/dist/Icon.svelte +18 -12
  4. package/dist/MillerIndexInput.svelte +27 -21
  5. package/dist/api/optimade.js +6 -6
  6. package/dist/app.css +216 -207
  7. package/dist/brillouin/BrillouinZone.svelte +292 -149
  8. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  9. package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
  10. package/dist/brillouin/BrillouinZoneExportPane.svelte +69 -42
  11. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
  12. package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
  13. package/dist/brillouin/BrillouinZoneScene.svelte +275 -163
  14. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  15. package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
  16. package/dist/brillouin/compute.js +11 -6
  17. package/dist/chempot-diagram/ChemPotDiagram.svelte +162 -27
  18. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +451 -281
  19. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2148 -1642
  20. package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -5
  21. package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
  22. package/dist/chempot-diagram/async-compute.svelte.js +77 -0
  23. package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
  24. package/dist/chempot-diagram/chempot-worker.js +11 -0
  25. package/dist/chempot-diagram/color.js +1 -2
  26. package/dist/chempot-diagram/compute.d.ts +10 -0
  27. package/dist/chempot-diagram/compute.js +250 -88
  28. package/dist/chempot-diagram/index.d.ts +2 -1
  29. package/dist/chempot-diagram/index.js +2 -1
  30. package/dist/chempot-diagram/temperature.js +8 -9
  31. package/dist/chempot-diagram/types.d.ts +3 -0
  32. package/dist/chempot-diagram/types.js +1 -0
  33. package/dist/colors/index.d.ts +1 -1
  34. package/dist/colors/index.js +5 -3
  35. package/dist/composition/BarChart.svelte +128 -55
  36. package/dist/composition/BubbleChart.svelte +102 -49
  37. package/dist/composition/Composition.svelte +100 -79
  38. package/dist/composition/Formula.svelte +108 -62
  39. package/dist/composition/FormulaFilter.svelte +665 -537
  40. package/dist/composition/PieChart.svelte +183 -108
  41. package/dist/composition/format.d.ts +5 -0
  42. package/dist/composition/format.js +20 -3
  43. package/dist/composition/parse.js +14 -9
  44. package/dist/convex-hull/ConvexHull.svelte +93 -40
  45. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  46. package/dist/convex-hull/ConvexHull2D.svelte +549 -360
  47. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  48. package/dist/convex-hull/ConvexHull3D.svelte +1296 -827
  49. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  50. package/dist/convex-hull/ConvexHull4D.svelte +1004 -688
  51. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  52. package/dist/convex-hull/ConvexHullControls.svelte +115 -28
  53. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
  54. package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
  55. package/dist/convex-hull/ConvexHullStats.svelte +425 -328
  56. package/dist/convex-hull/ConvexHullTooltip.svelte +40 -16
  57. package/dist/convex-hull/GasPressureControls.svelte +104 -61
  58. package/dist/convex-hull/StructurePopup.svelte +25 -4
  59. package/dist/convex-hull/TemperatureSlider.svelte +45 -25
  60. package/dist/convex-hull/barycentric-coords.js +13 -7
  61. package/dist/convex-hull/demo-temperature.js +8 -4
  62. package/dist/convex-hull/gas-thermodynamics.js +17 -12
  63. package/dist/convex-hull/helpers.d.ts +9 -0
  64. package/dist/convex-hull/helpers.js +77 -34
  65. package/dist/convex-hull/thermodynamics.js +61 -56
  66. package/dist/convex-hull/types.d.ts +9 -14
  67. package/dist/convex-hull/types.js +0 -17
  68. package/dist/coordination/CoordinationBarPlot.svelte +227 -154
  69. package/dist/element/BohrAtom.svelte +55 -12
  70. package/dist/element/ElementHeading.svelte +7 -2
  71. package/dist/element/ElementPhoto.svelte +15 -9
  72. package/dist/element/ElementStats.svelte +10 -4
  73. package/dist/element/ElementTile.svelte +137 -73
  74. package/dist/element/Nucleus.svelte +39 -11
  75. package/dist/element/data.js +1 -1
  76. package/dist/feedback/ClickFeedback.svelte +16 -5
  77. package/dist/feedback/DragOverlay.svelte +10 -2
  78. package/dist/feedback/Spinner.svelte +4 -2
  79. package/dist/feedback/StatusMessage.svelte +8 -2
  80. package/dist/fermi-surface/FermiSlice.svelte +118 -88
  81. package/dist/fermi-surface/FermiSurface.svelte +328 -187
  82. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  83. package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
  84. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  85. package/dist/fermi-surface/FermiSurfaceScene.svelte +535 -342
  86. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  87. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
  88. package/dist/fermi-surface/compute.js +16 -20
  89. package/dist/fermi-surface/parse.js +24 -14
  90. package/dist/fermi-surface/symmetry.js +2 -7
  91. package/dist/fermi-surface/types.d.ts +3 -5
  92. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1019 -765
  93. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +1 -1
  94. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +76 -22
  95. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +2 -3
  96. package/dist/icons.js +47 -0
  97. package/dist/index.d.ts +2 -1
  98. package/dist/index.js +2 -1
  99. package/dist/io/decompress.js +1 -1
  100. package/dist/io/export.d.ts +3 -0
  101. package/dist/io/export.js +129 -143
  102. package/dist/io/is-binary.js +2 -3
  103. package/dist/io/url-drop.js +1 -2
  104. package/dist/isosurface/Isosurface.svelte +202 -148
  105. package/dist/isosurface/IsosurfaceControls.svelte +46 -28
  106. package/dist/isosurface/parse.js +34 -29
  107. package/dist/isosurface/slice.js +5 -10
  108. package/dist/isosurface/types.d.ts +2 -1
  109. package/dist/isosurface/types.js +61 -12
  110. package/dist/labels.js +11 -8
  111. package/dist/layout/FullscreenToggle.svelte +11 -2
  112. package/dist/layout/InfoCard.svelte +38 -6
  113. package/dist/layout/InfoTag.svelte +63 -32
  114. package/dist/layout/PropertyFilter.svelte +82 -37
  115. package/dist/layout/SettingsSection.svelte +85 -55
  116. package/dist/layout/SubpageGrid.svelte +10 -2
  117. package/dist/layout/json-tree/JsonNode.svelte +183 -138
  118. package/dist/layout/json-tree/JsonTree.svelte +499 -413
  119. package/dist/layout/json-tree/JsonValue.svelte +127 -99
  120. package/dist/layout/json-tree/utils.js +4 -2
  121. package/dist/marching-cubes.js +25 -2
  122. package/dist/math.d.ts +13 -17
  123. package/dist/math.js +133 -67
  124. package/dist/overlays/ContextMenu.svelte +65 -40
  125. package/dist/overlays/DraggablePane.svelte +211 -139
  126. package/dist/periodic-table/PeriodicTable.svelte +278 -145
  127. package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
  128. package/dist/periodic-table/PropertySelect.svelte +25 -7
  129. package/dist/periodic-table/TableInset.svelte +8 -3
  130. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +446 -309
  131. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
  132. package/dist/phase-diagram/PhaseDiagramControls.svelte +102 -43
  133. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  134. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +63 -40
  135. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +71 -28
  136. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +1 -1
  137. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +158 -101
  138. package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
  139. package/dist/phase-diagram/build-diagram.js +9 -9
  140. package/dist/phase-diagram/colors.js +1 -3
  141. package/dist/phase-diagram/parse.js +10 -9
  142. package/dist/phase-diagram/svg-to-diagram.js +53 -49
  143. package/dist/phase-diagram/utils.d.ts +1 -0
  144. package/dist/phase-diagram/utils.js +80 -25
  145. package/dist/plot/AxisLabel.svelte +28 -3
  146. package/dist/plot/BarPlot.svelte +1182 -734
  147. package/dist/plot/BarPlot.svelte.d.ts +2 -2
  148. package/dist/plot/BarPlotControls.svelte +31 -5
  149. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  150. package/dist/plot/ColorBar.svelte +479 -329
  151. package/dist/plot/ColorScaleSelect.svelte +27 -6
  152. package/dist/plot/ElementScatter.svelte +36 -15
  153. package/dist/plot/FillArea.svelte +152 -95
  154. package/dist/plot/Histogram.svelte +934 -571
  155. package/dist/plot/Histogram.svelte.d.ts +1 -1
  156. package/dist/plot/HistogramControls.svelte +53 -9
  157. package/dist/plot/HistogramControls.svelte.d.ts +1 -1
  158. package/dist/plot/InteractiveAxisLabel.svelte +34 -11
  159. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
  160. package/dist/plot/Line.svelte +63 -28
  161. package/dist/plot/PlotControls.svelte +157 -114
  162. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  163. package/dist/plot/PlotLegend.svelte +174 -91
  164. package/dist/plot/PlotTooltip.svelte +45 -6
  165. package/dist/plot/PortalSelect.svelte +175 -147
  166. package/dist/plot/ReferenceLine.svelte +76 -22
  167. package/dist/plot/ReferenceLine3D.svelte +132 -107
  168. package/dist/plot/ReferencePlane.svelte +146 -121
  169. package/dist/plot/ScatterPlot.svelte +1681 -1091
  170. package/dist/plot/ScatterPlot.svelte.d.ts +2 -2
  171. package/dist/plot/ScatterPlot3D.svelte +256 -131
  172. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  173. package/dist/plot/ScatterPlot3DControls.svelte +113 -63
  174. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
  175. package/dist/plot/ScatterPlot3DScene.svelte +608 -403
  176. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  177. package/dist/plot/ScatterPlotControls.svelte +65 -25
  178. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  179. package/dist/plot/ScatterPoint.svelte +98 -26
  180. package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
  181. package/dist/plot/SpacegroupBarPlot.svelte +142 -85
  182. package/dist/plot/Surface3D.svelte +159 -108
  183. package/dist/plot/ZeroLines.svelte +55 -3
  184. package/dist/plot/ZoomRect.svelte +4 -2
  185. package/dist/plot/axis-utils.js +1 -3
  186. package/dist/plot/data-cleaning.js +12 -28
  187. package/dist/plot/data-transform.js +2 -1
  188. package/dist/plot/fill-utils.js +2 -0
  189. package/dist/plot/layout.d.ts +4 -1
  190. package/dist/plot/layout.js +33 -14
  191. package/dist/plot/reference-line.d.ts +2 -2
  192. package/dist/plot/reference-line.js +7 -5
  193. package/dist/plot/scales.js +24 -36
  194. package/dist/plot/types.d.ts +11 -23
  195. package/dist/plot/types.js +6 -11
  196. package/dist/plot/utils/label-placement.d.ts +32 -15
  197. package/dist/plot/utils/label-placement.js +227 -66
  198. package/dist/plot/utils/series-visibility.js +2 -3
  199. package/dist/rdf/RdfPlot.svelte +143 -91
  200. package/dist/rdf/calc-rdf.js +4 -5
  201. package/dist/sanitize.d.ts +4 -0
  202. package/dist/sanitize.js +107 -0
  203. package/dist/settings.d.ts +18 -6
  204. package/dist/settings.js +46 -16
  205. package/dist/spectral/Bands.svelte +632 -453
  206. package/dist/spectral/BandsAndDos.svelte +90 -49
  207. package/dist/spectral/BrillouinBandsDos.svelte +151 -93
  208. package/dist/spectral/Dos.svelte +389 -258
  209. package/dist/spectral/helpers.js +55 -43
  210. package/dist/state.svelte.d.ts +1 -1
  211. package/dist/state.svelte.js +3 -2
  212. package/dist/structure/Arrow.svelte +59 -20
  213. package/dist/structure/AtomLegend.svelte +215 -134
  214. package/dist/structure/Bond.svelte +73 -47
  215. package/dist/structure/CanvasTooltip.svelte +10 -2
  216. package/dist/structure/CellSelect.svelte +72 -45
  217. package/dist/structure/Cylinder.svelte +33 -17
  218. package/dist/structure/Lattice.svelte +88 -33
  219. package/dist/structure/Structure.svelte +1063 -797
  220. package/dist/structure/Structure.svelte.d.ts +1 -1
  221. package/dist/structure/StructureControls.svelte +349 -118
  222. package/dist/structure/StructureExportPane.svelte +124 -89
  223. package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
  224. package/dist/structure/StructureInfoPane.svelte +304 -237
  225. package/dist/structure/StructureScene.svelte +879 -443
  226. package/dist/structure/StructureScene.svelte.d.ts +15 -7
  227. package/dist/structure/atom-properties.js +8 -8
  228. package/dist/structure/bonding.js +6 -7
  229. package/dist/structure/export.js +14 -29
  230. package/dist/structure/ferrox-wasm.js +1 -1
  231. package/dist/structure/index.d.ts +13 -3
  232. package/dist/structure/index.js +83 -23
  233. package/dist/structure/measure.d.ts +2 -2
  234. package/dist/structure/measure.js +4 -44
  235. package/dist/structure/parse.js +113 -141
  236. package/dist/structure/partial-occupancy.js +7 -10
  237. package/dist/structure/pbc.d.ts +1 -0
  238. package/dist/structure/pbc.js +16 -6
  239. package/dist/structure/supercell.d.ts +2 -2
  240. package/dist/structure/supercell.js +12 -22
  241. package/dist/structure/validation.js +1 -2
  242. package/dist/symmetry/SymmetryStats.svelte +84 -41
  243. package/dist/symmetry/WyckoffTable.svelte +26 -6
  244. package/dist/symmetry/cell-transform.js +5 -3
  245. package/dist/symmetry/index.js +8 -7
  246. package/dist/symmetry/spacegroups.js +148 -148
  247. package/dist/table/HeatmapTable.svelte +790 -554
  248. package/dist/table/HeatmapTable.svelte.d.ts +1 -1
  249. package/dist/table/ToggleMenu.svelte +125 -92
  250. package/dist/table/index.js +2 -4
  251. package/dist/theme/ThemeControl.svelte +21 -12
  252. package/dist/time.js +4 -1
  253. package/dist/tooltip/TooltipContent.svelte +33 -8
  254. package/dist/trajectory/Trajectory.svelte +758 -558
  255. package/dist/trajectory/TrajectoryError.svelte +14 -3
  256. package/dist/trajectory/TrajectoryExportPane.svelte +137 -83
  257. package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
  258. package/dist/trajectory/extract.js +10 -26
  259. package/dist/trajectory/format-detect.js +5 -5
  260. package/dist/trajectory/frame-reader.d.ts +1 -1
  261. package/dist/trajectory/frame-reader.js +5 -12
  262. package/dist/trajectory/helpers.d.ts +0 -1
  263. package/dist/trajectory/helpers.js +2 -17
  264. package/dist/trajectory/index.js +14 -12
  265. package/dist/trajectory/parse/ase.js +5 -4
  266. package/dist/trajectory/parse/hdf5.js +26 -18
  267. package/dist/trajectory/parse/index.js +13 -18
  268. package/dist/trajectory/parse/lammps.js +17 -7
  269. package/dist/trajectory/parse/vasp.js +5 -2
  270. package/dist/trajectory/parse/xyz.js +8 -7
  271. package/dist/trajectory/plotting.js +13 -8
  272. package/dist/utils.d.ts +1 -0
  273. package/dist/utils.js +13 -0
  274. package/dist/xrd/XrdPlot.svelte +337 -247
  275. package/dist/xrd/broadening.js +14 -9
  276. package/dist/xrd/calc-xrd.js +12 -18
  277. package/dist/xrd/parse.d.ts +1 -1
  278. package/dist/xrd/parse.js +17 -17
  279. package/package.json +99 -103
  280. package/readme.md +1 -1
  281. /package/dist/theme/{themes.js → themes.mjs} +0 -0
@@ -1,137 +1,188 @@
1
- <script lang="ts">import { T } from '@threlte/core';
2
- import * as THREE from 'three';
3
- let { config, x_range = [0, 1], y_range = [0, 1], z_range = [0, 1], scene_x = 10, scene_y = 10, scene_z = 5, } = $props();
4
- // Normalize value to scene coordinates (centered around 0)
5
- function normalize(value, [min_val, max_val], scene_size) {
6
- return ((value - min_val) / (max_val - min_val || 1) - 0.5) * scene_size;
7
- }
8
- // Parse color to THREE.Color with fallback
9
- function parse_color(color) {
1
+ <script lang="ts">
2
+ import type { Surface3DConfig } from './types'
3
+ import { T } from '@threlte/core'
4
+ import * as THREE from 'three'
5
+
6
+ let {
7
+ config,
8
+ x_range = [0, 1],
9
+ y_range = [0, 1],
10
+ z_range = [0, 1],
11
+ scene_x = 10,
12
+ scene_y = 10,
13
+ scene_z = 5,
14
+ }: {
15
+ config: Surface3DConfig
16
+ x_range?: [number, number]
17
+ y_range?: [number, number]
18
+ z_range?: [number, number]
19
+ scene_x?: number
20
+ scene_y?: number
21
+ scene_z?: number
22
+ } = $props()
23
+
24
+ // Normalize value to scene coordinates (centered around 0)
25
+ function normalize(
26
+ value: number,
27
+ [min_val, max_val]: [number, number],
28
+ scene_size: number,
29
+ ): number {
30
+ return ((value - min_val) / (max_val - min_val || 1) - 0.5) * scene_size
31
+ }
32
+
33
+ // Parse color to THREE.Color with fallback
34
+ function parse_color(color: string): THREE.Color {
10
35
  try {
11
- return new THREE.Color(color);
36
+ return new THREE.Color(color)
37
+ } catch {
38
+ return new THREE.Color(0x4488ff)
12
39
  }
13
- catch {
14
- return new THREE.Color(0x4488ff);
15
- }
16
- }
17
- // Calculate vertex color based on config
18
- function get_vertex_color(x_val, y_val, z_val) {
19
- if (config.color_fn)
20
- return parse_color(config.color_fn(x_val, y_val, z_val));
21
- if (config.color)
22
- return parse_color(config.color);
40
+ }
41
+
42
+ // Calculate vertex color based on config
43
+ function get_vertex_color(
44
+ x_val: number,
45
+ y_val: number,
46
+ z_val: number,
47
+ ): THREE.Color {
48
+ if (config.color_fn) return parse_color(config.color_fn(x_val, y_val, z_val))
49
+ if (config.color) return parse_color(config.color)
23
50
  // Default: color by z value (blue to red gradient)
24
- const z_norm = (z_val - z_range[0]) / (z_range[1] - z_range[0] || 1);
25
- return new THREE.Color().setHSL(0.66 - z_norm * 0.66, 0.8, 0.5);
26
- }
27
- // Add vertex position (with Y/Z swap for Three.js) and color to arrays
28
- function add_vertex(positions, colors, x_val, y_val, z_val) {
29
- positions.push(normalize(x_val, x_range, scene_x), normalize(z_val, z_range, scene_z), // user Z → Three.js Y (vertical)
30
- normalize(y_val, y_range, scene_y));
31
- const color = get_vertex_color(x_val, y_val, z_val);
32
- colors.push(color.r, color.g, color.b);
33
- }
34
- // Build geometry from positions/colors arrays with optional grid indices
35
- function build_geometry(positions, colors, res_a, res_b, triangles) {
36
- const geom = new THREE.BufferGeometry();
37
- geom.setAttribute(`position`, new THREE.Float32BufferAttribute(positions, 3));
38
- geom.setAttribute(`color`, new THREE.Float32BufferAttribute(colors, 3));
51
+ const z_norm = (z_val - z_range[0]) / (z_range[1] - z_range[0] || 1)
52
+ return new THREE.Color().setHSL(0.66 - z_norm * 0.66, 0.8, 0.5)
53
+ }
54
+
55
+ // Add vertex position (with Y/Z swap for Three.js) and color to arrays
56
+ function add_vertex(
57
+ positions: number[],
58
+ colors: number[],
59
+ x_val: number,
60
+ y_val: number,
61
+ z_val: number,
62
+ ): void {
63
+ positions.push(
64
+ normalize(x_val, x_range, scene_x),
65
+ normalize(z_val, z_range, scene_z), // user Z → Three.js Y (vertical)
66
+ normalize(y_val, y_range, scene_y), // user Y → Three.js Z (depth)
67
+ )
68
+ const color = get_vertex_color(x_val, y_val, z_val)
69
+ colors.push(color.r, color.g, color.b)
70
+ }
71
+
72
+ // Build geometry from positions/colors arrays with optional grid indices
73
+ function build_geometry(
74
+ positions: number[],
75
+ colors: number[],
76
+ res_a?: number,
77
+ res_b?: number,
78
+ triangles?: number[][],
79
+ ): THREE.BufferGeometry {
80
+ const geom = new THREE.BufferGeometry()
81
+ geom.setAttribute(`position`, new THREE.Float32BufferAttribute(positions, 3))
82
+ geom.setAttribute(`color`, new THREE.Float32BufferAttribute(colors, 3))
39
83
  // Set indices: either from explicit triangles or generate grid
40
84
  if (triangles?.length) {
41
- geom.setIndex(triangles.flat());
42
- }
43
- else if (res_a && res_b && res_a >= 2 && res_b >= 2) {
44
- const indices = [];
45
- for (let ib = 0; ib < res_b - 1; ib++) {
46
- for (let ia = 0; ia < res_a - 1; ia++) {
47
- const tl = ib * res_a + ia;
48
- indices.push(tl, tl + res_a, tl + 1, tl + 1, tl + res_a, tl + res_a + 1);
49
- }
85
+ geom.setIndex(triangles.flat())
86
+ } else if (res_a && res_b && res_a >= 2 && res_b >= 2) {
87
+ const indices: number[] = []
88
+ for (let ib = 0; ib < res_b - 1; ib++) {
89
+ for (let ia = 0; ia < res_a - 1; ia++) {
90
+ const tl = ib * res_a + ia
91
+ indices.push(tl, tl + res_a, tl + 1, tl + 1, tl + res_a, tl + res_a + 1)
50
92
  }
51
- geom.setIndex(indices);
93
+ }
94
+ geom.setIndex(indices)
52
95
  }
53
- geom.computeVertexNormals();
54
- return geom;
55
- }
56
- // Parse resolution config into [res_a, res_b]
57
- function get_resolution() {
96
+ geom.computeVertexNormals()
97
+ return geom
98
+ }
99
+
100
+ // Parse resolution config into [res_a, res_b]
101
+ function get_resolution(): [number, number] {
58
102
  return Array.isArray(config.resolution)
59
- ? config.resolution
60
- : [config.resolution ?? 20, config.resolution ?? 20];
61
- }
62
- function create_geometry() {
63
- const [res_a, res_b] = get_resolution();
64
- const positions = [];
65
- const colors = [];
103
+ ? config.resolution
104
+ : [config.resolution ?? 20, config.resolution ?? 20]
105
+ }
106
+
107
+ function create_geometry(): THREE.BufferGeometry | null {
108
+ const [res_a, res_b] = get_resolution()
109
+ const positions: number[] = []
110
+ const colors: number[] = []
111
+
66
112
  if (config.type === `grid` && config.z_fn) {
67
- if (res_a < 2 || res_b < 2)
68
- return new THREE.BufferGeometry();
69
- const [x0, x1] = config.x_range ?? x_range;
70
- const [y0, y1] = config.y_range ?? y_range;
71
- const x_step = (x1 - x0) / (res_a - 1);
72
- const y_step = (y1 - y0) / (res_b - 1);
73
- for (let ib = 0; ib < res_b; ib++) {
74
- for (let ia = 0; ia < res_a; ia++) {
75
- const x_val = x0 + ia * x_step;
76
- const y_val = y0 + ib * y_step;
77
- add_vertex(positions, colors, x_val, y_val, config.z_fn(x_val, y_val));
78
- }
113
+ if (res_a < 2 || res_b < 2) return new THREE.BufferGeometry()
114
+ const [x0, x1] = config.x_range ?? x_range
115
+ const [y0, y1] = config.y_range ?? y_range
116
+ const x_step = (x1 - x0) / (res_a - 1)
117
+ const y_step = (y1 - y0) / (res_b - 1)
118
+ for (let ib = 0; ib < res_b; ib++) {
119
+ for (let ia = 0; ia < res_a; ia++) {
120
+ const x_val = x0 + ia * x_step
121
+ const y_val = y0 + ib * y_step
122
+ add_vertex(positions, colors, x_val, y_val, config.z_fn(x_val, y_val))
79
123
  }
80
- return build_geometry(positions, colors, res_a, res_b);
124
+ }
125
+ return build_geometry(positions, colors, res_a, res_b)
81
126
  }
127
+
82
128
  if (config.type === `parametric` && config.parametric_fn) {
83
- if (res_a < 2 || res_b < 2)
84
- return new THREE.BufferGeometry();
85
- const [u0, u1] = config.u_range ?? [0, 1];
86
- const [v0, v1] = config.v_range ?? [0, 1];
87
- const u_step = (u1 - u0) / (res_a - 1);
88
- const v_step = (v1 - v0) / (res_b - 1);
89
- for (let ib = 0; ib < res_b; ib++) {
90
- for (let ia = 0; ia < res_a; ia++) {
91
- const pt = config.parametric_fn(u0 + ia * u_step, v0 + ib * v_step);
92
- add_vertex(positions, colors, pt.x, pt.y, pt.z);
93
- }
129
+ if (res_a < 2 || res_b < 2) return new THREE.BufferGeometry()
130
+ const [u0, u1] = config.u_range ?? [0, 1]
131
+ const [v0, v1] = config.v_range ?? [0, 1]
132
+ const u_step = (u1 - u0) / (res_a - 1)
133
+ const v_step = (v1 - v0) / (res_b - 1)
134
+ for (let ib = 0; ib < res_b; ib++) {
135
+ for (let ia = 0; ia < res_a; ia++) {
136
+ const pt = config.parametric_fn(u0 + ia * u_step, v0 + ib * v_step)
137
+ add_vertex(positions, colors, pt.x, pt.y, pt.z)
94
138
  }
95
- return build_geometry(positions, colors, res_a, res_b);
139
+ }
140
+ return build_geometry(positions, colors, res_a, res_b)
96
141
  }
142
+
97
143
  if (config.type === `triangulated` && config.points?.length) {
98
- for (const pt of config.points) {
99
- add_vertex(positions, colors, pt.x, pt.y, pt.z);
100
- }
101
- return build_geometry(positions, colors, undefined, undefined, config.triangles);
144
+ for (const pt of config.points) {
145
+ add_vertex(positions, colors, pt.x, pt.y, pt.z)
146
+ }
147
+ return build_geometry(positions, colors, undefined, undefined, config.triangles)
102
148
  }
103
- return null;
104
- }
105
- // Geometry with proper disposal on change/unmount
106
- let geometry = $state(null);
107
- let wireframe_geometry = $state(null);
108
- $effect(() => {
109
- const new_geom = create_geometry();
110
- const new_wireframe = new_geom ? new THREE.WireframeGeometry(new_geom) : null;
111
- geometry = new_geom;
112
- wireframe_geometry = new_wireframe;
149
+
150
+ return null
151
+ }
152
+
153
+ // Geometry with proper disposal on change/unmount
154
+ let geometry: THREE.BufferGeometry | null = $state(null)
155
+ let wireframe_geometry: THREE.WireframeGeometry | null = $state(null)
156
+
157
+ $effect(() => {
158
+ const new_geom = create_geometry()
159
+ const new_wireframe = new_geom ? new THREE.WireframeGeometry(new_geom) : null
160
+ geometry = new_geom
161
+ wireframe_geometry = new_wireframe
113
162
  return () => {
114
- new_geom?.dispose();
115
- new_wireframe?.dispose();
116
- };
117
- });
118
- // Material properties
119
- let is_transparent = $derived((config.opacity ?? 1) < 1);
120
- let material_props = $derived({
163
+ new_geom?.dispose()
164
+ new_wireframe?.dispose()
165
+ }
166
+ })
167
+
168
+ // Material properties
169
+ let is_transparent = $derived((config.opacity ?? 1) < 1)
170
+ let material_props = $derived({
121
171
  transparent: is_transparent,
122
172
  opacity: config.opacity ?? 1,
123
173
  side: (config.double_sided ?? is_transparent)
124
- ? THREE.DoubleSide
125
- : THREE.FrontSide,
174
+ ? THREE.DoubleSide
175
+ : THREE.FrontSide,
126
176
  vertexColors: true,
127
177
  depthWrite: true,
128
- });
129
- let wireframe_props = $derived({
178
+ })
179
+
180
+ let wireframe_props = $derived({
130
181
  color: config.wireframe_color ?? `#333`,
131
182
  linewidth: config.wireframe_width ?? 1,
132
183
  transparent: true,
133
184
  opacity: 0.5,
134
- });
185
+ })
135
186
  </script>
136
187
 
137
188
  {#if geometry}
@@ -1,6 +1,58 @@
1
- <script lang="ts">import { get_scale_type_name } from './';
2
- let { display, x_scale_fn, x2_scale_fn, y_scale_fn, y2_scale_fn, x_range, x2_range, y_range, y2_range, x_scale_type, x2_scale_type, y_scale_type, y2_scale_type, x_is_time = false, x2_is_time = false, has_x2 = false, has_y2 = false, width, height, pad, } = $props();
3
- const spans_zero = (range) => Math.min(range[0], range[1]) <= 0 && Math.max(range[0], range[1]) >= 0;
1
+ <script lang="ts">
2
+ import type { Vec2 } from '../math'
3
+ import { get_scale_type_name, type ScaleType, type Sides } from './'
4
+
5
+ let {
6
+ display,
7
+ x_scale_fn,
8
+ x2_scale_fn,
9
+ y_scale_fn,
10
+ y2_scale_fn,
11
+ x_range,
12
+ x2_range,
13
+ y_range,
14
+ y2_range,
15
+ x_scale_type,
16
+ x2_scale_type,
17
+ y_scale_type,
18
+ y2_scale_type,
19
+ x_is_time = false,
20
+ x2_is_time = false,
21
+ has_x2 = false,
22
+ has_y2 = false,
23
+ width,
24
+ height,
25
+ pad,
26
+ }: {
27
+ display: {
28
+ x_zero_line?: boolean
29
+ x2_zero_line?: boolean
30
+ y_zero_line?: boolean
31
+ y2_zero_line?: boolean
32
+ }
33
+ x_scale_fn: (val: number) => number
34
+ x2_scale_fn?: (val: number) => number
35
+ y_scale_fn: (val: number) => number
36
+ y2_scale_fn?: (val: number) => number
37
+ x_range: Vec2
38
+ x2_range?: Vec2
39
+ y_range: Vec2
40
+ y2_range?: Vec2
41
+ x_scale_type?: ScaleType
42
+ x2_scale_type?: ScaleType
43
+ y_scale_type?: ScaleType
44
+ y2_scale_type?: ScaleType
45
+ x_is_time?: boolean
46
+ x2_is_time?: boolean
47
+ has_x2?: boolean
48
+ has_y2?: boolean
49
+ width: number
50
+ height: number
51
+ pad: Required<Sides>
52
+ } = $props()
53
+
54
+ const spans_zero = (range: Vec2): boolean =>
55
+ Math.min(range[0], range[1]) <= 0 && Math.max(range[0], range[1]) >= 0
4
56
  </script>
5
57
 
6
58
  {#if display.x_zero_line && get_scale_type_name(x_scale_type) !== `log` &&
@@ -1,5 +1,7 @@
1
- <script lang="ts">let { start, current } = $props();
2
- export {};
1
+ <script lang="ts">
2
+ import type { XyObj } from './'
3
+
4
+ let { start, current }: { start: XyObj | null; current: XyObj | null } = $props()
3
5
  </script>
4
6
 
5
7
  {#if start && current && isFinite(start.x) && isFinite(start.y) &&
@@ -10,9 +10,7 @@ export function merge_series_state(old_series, new_series) {
10
10
  }
11
11
  return new_series.map((new_srs, idx) => {
12
12
  // Match by id if available (string or number), otherwise fall back to index
13
- const old_srs = (new_srs.id !== undefined && new_srs.id !== ``
14
- ? by_id.get(new_srs.id)
15
- : undefined) ??
13
+ const old_srs = (new_srs.id !== undefined && new_srs.id !== `` ? by_id.get(new_srs.id) : undefined) ??
16
14
  old_series[idx];
17
15
  if (!old_srs) {
18
16
  return new_srs;
@@ -21,9 +21,7 @@ export function compute_local_variance(values, window_size) {
21
21
  const start = Math.max(0, idx - half_window);
22
22
  const end = Math.min(len, idx + half_window + 1);
23
23
  // Welford's online variance calculation
24
- let mean = 0;
25
- let m2 = 0;
26
- let count = 0;
24
+ let [mean, m2, count] = [0, 0, 0];
27
25
  for (let jdx = start; jdx < end; jdx++) {
28
26
  const val = values[jdx];
29
27
  if (!Number.isFinite(val))
@@ -170,13 +168,9 @@ export function detect_instability(x_values, y_values, config = {}) {
170
168
  }
171
169
  }
172
170
  if (valid_y.length < window_size * 2) {
173
- return {
174
- detected: false,
175
- onset_index: -1,
176
- onset_x: NaN,
177
- combined_score: 0,
178
- method_scores: { derivative_variance: 0, amplitude_growth: 0, sign_changes: 0 },
179
- };
171
+ const method_scores = { derivative_variance: 0, amplitude_growth: 0, sign_changes: 0 };
172
+ const detected = false;
173
+ return { detected, onset_index: -1, onset_x: NaN, combined_score: 0, method_scores };
180
174
  }
181
175
  // Run all three detection methods
182
176
  const deriv_result = detect_derivative_variance(valid_y, window_size, threshold);
@@ -188,12 +182,12 @@ export function detect_instability(x_values, y_values, config = {}) {
188
182
  sign_changes: sign_result.score,
189
183
  };
190
184
  // Compute weighted combined score
191
- const total_weight = weights.derivative_variance + weights.amplitude_growth +
192
- weights.sign_changes;
185
+ const total_weight = weights.derivative_variance + weights.amplitude_growth + weights.sign_changes;
193
186
  const combined_score = total_weight > 0
194
187
  ? (weights.derivative_variance * deriv_result.score +
195
188
  weights.amplitude_growth * amp_result.score +
196
- weights.sign_changes * sign_result.score) / total_weight
189
+ weights.sign_changes * sign_result.score) /
190
+ total_weight
197
191
  : 0;
198
192
  // Find earliest onset across all methods that exceeded threshold
199
193
  const onset_candidates = [
@@ -214,16 +208,8 @@ export function detect_instability(x_values, y_values, config = {}) {
214
208
  }
215
209
  }
216
210
  const detected = combined_score >= threshold || onset_index >= 0;
217
- const onset_x = onset_index >= 0 && onset_index < x_values.length
218
- ? x_values[onset_index]
219
- : NaN;
220
- return {
221
- detected,
222
- onset_index,
223
- onset_x,
224
- combined_score,
225
- method_scores,
226
- };
211
+ const onset_x = onset_index >= 0 && onset_index < x_values.length ? x_values[onset_index] : NaN;
212
+ return { detected, onset_index, onset_x, combined_score, method_scores };
227
213
  }
228
214
  // --- Smoothing Functions ---
229
215
  // Moving average - O(n)
@@ -235,8 +221,7 @@ export function smooth_moving_average(values, window) {
235
221
  for (let idx = 0; idx < values.length; idx++) {
236
222
  const start = Math.max(0, idx - half_window);
237
223
  const end = Math.min(values.length, idx + half_window + 1);
238
- let sum = 0;
239
- let count = 0;
224
+ let [sum, count] = [0, 0];
240
225
  for (let jdx = start; jdx < end; jdx++) {
241
226
  if (Number.isFinite(values[jdx])) {
242
227
  sum += values[jdx];
@@ -291,8 +276,7 @@ export function smooth_savitzky_golay(values, window, polynomial_order = DEFAULT
291
276
  // Cache coefficient sum to avoid O(n × window) redundant reductions in loop
292
277
  const coeffs_sum = coeffs.reduce((a, b) => a + b, 0);
293
278
  for (let idx = 0; idx < values.length; idx++) {
294
- let sum = 0;
295
- let weight_sum = 0;
279
+ let [sum, weight_sum] = [0, 0];
296
280
  for (let jdx = 0; jdx < actual_window; jdx++) {
297
281
  const data_idx = idx - half + jdx;
298
282
  if (data_idx >= 0 && data_idx < values.length && Number.isFinite(values[data_idx])) {
@@ -300,7 +284,7 @@ export function smooth_savitzky_golay(values, window, polynomial_order = DEFAULT
300
284
  weight_sum += coeffs[jdx];
301
285
  }
302
286
  }
303
- result[idx] = weight_sum !== 0 ? sum / weight_sum * coeffs_sum : values[idx];
287
+ result[idx] = weight_sum !== 0 ? (sum / weight_sum) * coeffs_sum : values[idx];
304
288
  }
305
289
  return result;
306
290
  }
@@ -17,6 +17,7 @@ export const prepare_legend_data = (series) => series.map((series_data, series_i
17
17
  label: series_data.label ?? `Series ${series_idx + 1}`,
18
18
  visible: series_data.visible ?? true,
19
19
  display_style: {
20
+ // Prefer the series’ symbol when present, falling back to settings
20
21
  symbol_type: !Array.isArray(series_data.point_style)
21
22
  ? (series_data.point_style?.symbol_type ?? DEFAULTS.scatter.symbol_type)
22
23
  : DEFAULTS.scatter.symbol_type,
@@ -38,7 +39,7 @@ export const create_data_points = (series, filter_fn) => series
38
39
  // If prop is a scalar, returns the scalar (applied to all indices).
39
40
  // Returns undefined if prop is null/undefined.
40
41
  export function process_prop(prop, idx) {
41
- if (prop === null || prop === undefined)
42
+ if (prop == null)
42
43
  return undefined;
43
44
  return Array.isArray(prop) ? prop[idx] : prop;
44
45
  }
@@ -113,6 +113,8 @@ export function resolve_boundary(boundary, series, x_values, domains) {
113
113
  case `function`:
114
114
  return x_values.map((curr_x) => boundary.fn(curr_x));
115
115
  case `data`:
116
+ if (boundary.values.length === 0)
117
+ return Array(x_values.length).fill(NaN);
116
118
  // If lengths match, use directly; otherwise interpolate
117
119
  if (boundary.values.length === x_values.length) {
118
120
  return [...boundary.values];
@@ -1,6 +1,6 @@
1
1
  import type { AxisConfig, Sides } from './';
2
2
  export declare const LABEL_GAP_DEFAULT = 30;
3
- export declare const filter_padding: <T extends Partial<Sides>>(padding: T | undefined | null, defaults: Required<Sides>) => Required<Sides>;
3
+ export declare const filter_padding: (padding: Partial<Sides> | undefined | null, defaults: Required<Sides>) => Required<Sides>;
4
4
  export declare function measure_text_width(text: string, font?: string): number;
5
5
  export interface AutoPaddingConfig {
6
6
  padding: Partial<Sides>;
@@ -34,6 +34,9 @@ export interface Rect {
34
34
  width: number;
35
35
  height: number;
36
36
  }
37
+ export declare const pad_rect: (rect: Rect, padding: number) => Rect;
38
+ export declare const centered_rect: (center_x: number, top_y: number, width: number, height: number) => Rect;
39
+ export declare const rect_within_rect: (rect: Rect, bounds: Rect) => boolean;
37
40
  export interface ElementPlacementConfig {
38
41
  plot_bounds: Rect;
39
42
  element_size: {
@@ -23,10 +23,12 @@ export function measure_text_width(text, font = `12px sans-serif`) {
23
23
  return ctx.measureText(text).width;
24
24
  }
25
25
  // Measure the widest formatted tick label. Used for auto-padding and label placement.
26
- export const measure_max_tick_width = (ticks, format = ``) => ticks.length === 0 ? 0 : Math.max(...ticks.map((tick) => {
27
- const label = typeof tick === `string` ? tick : format_value(tick, format);
28
- return measure_text_width(label, `12px sans-serif`);
29
- }));
26
+ export const measure_max_tick_width = (ticks, format = ``) => ticks.length === 0
27
+ ? 0
28
+ : Math.max(...ticks.map((tick) => {
29
+ const label = typeof tick === `string` ? tick : format_value(tick, format);
30
+ return measure_text_width(label, `12px sans-serif`);
31
+ }));
30
32
  // Estimated height of a single tick label line (font-size 0.8em ≈ 12px + leading)
31
33
  export const TICK_LABEL_HEIGHT = 16;
32
34
  // Estimated height of an axis label (font-size ~14px + margin)
@@ -68,18 +70,22 @@ export function constrain_tooltip_position(cursor_x, cursor_y, tooltip_width, to
68
70
  const abs_offset_y = Math.abs(offset_y);
69
71
  // Determine X position based on preferred side and flip state
70
72
  let raw_x;
71
- if (offset_x >= 0) { // Prefer right side: flip to left if overflows
73
+ if (offset_x >= 0) {
74
+ // Prefer right side: flip to left if overflows
72
75
  raw_x = flip_x ? cursor_x - abs_offset_x - tooltip_width : cursor_x + abs_offset_x;
73
76
  }
74
- else { // Prefer left side: flip to right if overflows
77
+ else {
78
+ // Prefer left side: flip to right if overflows
75
79
  raw_x = flip_x ? cursor_x + abs_offset_x : cursor_x - abs_offset_x - tooltip_width;
76
80
  }
77
81
  // Determine Y position based on preferred side and flip state
78
82
  let raw_y;
79
- if (offset_y >= 0) { // Prefer bottom: flip to top if overflows
83
+ if (offset_y >= 0) {
84
+ // Prefer bottom: flip to top if overflows
80
85
  raw_y = flip_y ? cursor_y - abs_offset_y - tooltip_height : cursor_y + abs_offset_y;
81
86
  }
82
- else { // Prefer top: flip to bottom if overflows
87
+ else {
88
+ // Prefer top: flip to bottom if overflows
83
89
  raw_y = flip_y ? cursor_y + abs_offset_y : cursor_y - abs_offset_y - tooltip_height;
84
90
  }
85
91
  // Clamp to viewport bounds
@@ -87,6 +93,22 @@ export function constrain_tooltip_position(cursor_x, cursor_y, tooltip_width, to
87
93
  const y_pos = Math.max(0, Math.min(raw_y, viewport_height - tooltip_height));
88
94
  return { x: x_pos, y: y_pos };
89
95
  }
96
+ export const pad_rect = (rect, padding) => ({
97
+ x: rect.x - padding,
98
+ y: rect.y - padding,
99
+ width: rect.width + 2 * padding,
100
+ height: rect.height + 2 * padding,
101
+ });
102
+ export const centered_rect = (center_x, top_y, width, height) => ({
103
+ x: center_x - width / 2,
104
+ y: top_y,
105
+ width,
106
+ height,
107
+ });
108
+ export const rect_within_rect = (rect, bounds) => rect.x >= bounds.x &&
109
+ rect.x + rect.width <= bounds.x + bounds.width &&
110
+ rect.y >= bounds.y &&
111
+ rect.y + rect.height <= bounds.y + bounds.height;
90
112
  // Scoring constants
91
113
  const EXCLUSION_PENALTY = 1000;
92
114
  const DISTANCE_WEIGHT = 0.001;
@@ -129,7 +151,7 @@ export function compute_element_placement(config) {
129
151
  const effective_y_max = Math.max(valid_y_min, valid_y_max);
130
152
  // Subsample points for performance
131
153
  const sampled_points = points.length > MAX_SAMPLE_POINTS
132
- ? Array.from({ length: MAX_SAMPLE_POINTS }, (_, idx) => points[Math.floor(idx * points.length / MAX_SAMPLE_POINTS)])
154
+ ? Array.from({ length: MAX_SAMPLE_POINTS }, (_, idx) => points[Math.floor((idx * points.length) / MAX_SAMPLE_POINTS)])
133
155
  : points;
134
156
  let best_result = {
135
157
  x: effective_x_min,
@@ -197,11 +219,8 @@ export function compute_element_placement(config) {
197
219
  euclidean_dist([cand_x, elem_bottom], [plot_left, plot_bottom]), // bottom-left
198
220
  euclidean_dist([elem_right, elem_bottom], [plot_right, plot_bottom]));
199
221
  // Higher bonus for positions closer to corners (0 = at corner, 1 = far from all)
200
- const corner_bonus = max_corner_dist > 0
201
- ? (1 - min_corner_dist / max_corner_dist) * CORNER_WEIGHT
202
- : 0;
203
- const score = -overlap_count + min_distance * DISTANCE_WEIGHT + corner_bonus -
204
- exclusion_penalty;
222
+ const corner_bonus = max_corner_dist > 0 ? (1 - min_corner_dist / max_corner_dist) * CORNER_WEIGHT : 0;
223
+ const score = -overlap_count + min_distance * DISTANCE_WEIGHT + corner_bonus - exclusion_penalty;
205
224
  if (score > best_result.score) {
206
225
  best_result = { x: cand_x, y: cand_y, score };
207
226
  }
@@ -12,12 +12,12 @@ export interface RefLinesByZIndex {
12
12
  export declare function group_ref_lines_by_z(lines: IndexedRefLine[]): RefLinesByZIndex;
13
13
  export declare function normalize_value(value: RefLineValue): number;
14
14
  export declare const normalize_point: (point: [RefLineValue, RefLineValue]) => [number, number];
15
- export declare function resolve_line_endpoints(ref_line: RefLine, { x_min, x_max, y_min, y_max }: {
15
+ export declare function resolve_line_endpoints(ref_line: RefLine, { x_min, x_max, y_min, y_max, }: {
16
16
  x_min: number;
17
17
  x_max: number;
18
18
  y_min: number;
19
19
  y_max: number;
20
- }, { x_scale, x2_scale, y_scale, y2_scale }: {
20
+ }, { x_scale, x2_scale, y_scale, y2_scale, }: {
21
21
  x_scale: (val: number) => number;
22
22
  x2_scale?: (val: number) => number;
23
23
  y_scale: (val: number) => number;