matterviz 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EmptyState.svelte +10 -2
- package/dist/FilePicker.svelte +123 -82
- package/dist/Icon.svelte +18 -12
- package/dist/MillerIndexInput.svelte +27 -21
- package/dist/api/optimade.js +6 -6
- package/dist/app.css +216 -207
- package/dist/brillouin/BrillouinZone.svelte +292 -149
- package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
- package/dist/brillouin/BrillouinZoneExportPane.svelte +69 -42
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
- package/dist/brillouin/BrillouinZoneScene.svelte +275 -163
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
- package/dist/brillouin/compute.js +11 -6
- package/dist/chempot-diagram/ChemPotDiagram.svelte +162 -27
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +451 -281
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2148 -1642
- package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -5
- package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
- package/dist/chempot-diagram/async-compute.svelte.js +77 -0
- package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
- package/dist/chempot-diagram/chempot-worker.js +11 -0
- package/dist/chempot-diagram/color.js +1 -2
- package/dist/chempot-diagram/compute.d.ts +10 -0
- package/dist/chempot-diagram/compute.js +250 -88
- package/dist/chempot-diagram/index.d.ts +2 -1
- package/dist/chempot-diagram/index.js +2 -1
- package/dist/chempot-diagram/temperature.js +8 -9
- package/dist/chempot-diagram/types.d.ts +3 -0
- package/dist/chempot-diagram/types.js +1 -0
- package/dist/colors/index.d.ts +1 -1
- package/dist/colors/index.js +5 -3
- package/dist/composition/BarChart.svelte +128 -55
- package/dist/composition/BubbleChart.svelte +102 -49
- package/dist/composition/Composition.svelte +100 -79
- package/dist/composition/Formula.svelte +108 -62
- package/dist/composition/FormulaFilter.svelte +665 -537
- package/dist/composition/PieChart.svelte +183 -108
- package/dist/composition/format.d.ts +5 -0
- package/dist/composition/format.js +20 -3
- package/dist/composition/parse.js +14 -9
- package/dist/convex-hull/ConvexHull.svelte +93 -40
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull2D.svelte +549 -360
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +1296 -827
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +1004 -688
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullControls.svelte +115 -28
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
- package/dist/convex-hull/ConvexHullStats.svelte +425 -328
- package/dist/convex-hull/ConvexHullTooltip.svelte +40 -16
- package/dist/convex-hull/GasPressureControls.svelte +104 -61
- package/dist/convex-hull/StructurePopup.svelte +25 -4
- package/dist/convex-hull/TemperatureSlider.svelte +45 -25
- package/dist/convex-hull/barycentric-coords.js +13 -7
- package/dist/convex-hull/demo-temperature.js +8 -4
- package/dist/convex-hull/gas-thermodynamics.js +17 -12
- package/dist/convex-hull/helpers.d.ts +9 -0
- package/dist/convex-hull/helpers.js +77 -34
- package/dist/convex-hull/thermodynamics.js +61 -56
- package/dist/convex-hull/types.d.ts +9 -14
- package/dist/convex-hull/types.js +0 -17
- package/dist/coordination/CoordinationBarPlot.svelte +227 -154
- package/dist/element/BohrAtom.svelte +55 -12
- package/dist/element/ElementHeading.svelte +7 -2
- package/dist/element/ElementPhoto.svelte +15 -9
- package/dist/element/ElementStats.svelte +10 -4
- package/dist/element/ElementTile.svelte +137 -73
- package/dist/element/Nucleus.svelte +39 -11
- package/dist/feedback/ClickFeedback.svelte +16 -5
- package/dist/feedback/DragOverlay.svelte +10 -2
- package/dist/feedback/Spinner.svelte +4 -2
- package/dist/feedback/StatusMessage.svelte +8 -2
- package/dist/fermi-surface/FermiSlice.svelte +118 -88
- package/dist/fermi-surface/FermiSurface.svelte +328 -187
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +535 -342
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
- package/dist/fermi-surface/compute.js +16 -20
- package/dist/fermi-surface/parse.js +24 -14
- package/dist/fermi-surface/symmetry.js +2 -7
- package/dist/fermi-surface/types.d.ts +3 -5
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +1019 -765
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +1 -1
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +76 -22
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +2 -3
- package/dist/icons.js +47 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/io/decompress.js +1 -1
- package/dist/io/export.d.ts +3 -0
- package/dist/io/export.js +129 -143
- package/dist/io/is-binary.js +2 -3
- package/dist/io/url-drop.js +1 -2
- package/dist/isosurface/Isosurface.svelte +202 -148
- package/dist/isosurface/IsosurfaceControls.svelte +46 -28
- package/dist/isosurface/parse.js +34 -29
- package/dist/isosurface/slice.js +5 -10
- package/dist/isosurface/types.d.ts +2 -1
- package/dist/isosurface/types.js +61 -12
- package/dist/labels.js +11 -8
- package/dist/layout/FullscreenToggle.svelte +11 -2
- package/dist/layout/InfoCard.svelte +38 -6
- package/dist/layout/InfoTag.svelte +63 -32
- package/dist/layout/PropertyFilter.svelte +82 -37
- package/dist/layout/SettingsSection.svelte +85 -55
- package/dist/layout/SubpageGrid.svelte +10 -2
- package/dist/layout/json-tree/JsonNode.svelte +183 -138
- package/dist/layout/json-tree/JsonTree.svelte +499 -413
- package/dist/layout/json-tree/JsonValue.svelte +127 -99
- package/dist/layout/json-tree/utils.js +4 -2
- package/dist/marching-cubes.js +25 -2
- package/dist/math.d.ts +13 -17
- package/dist/math.js +133 -67
- package/dist/overlays/ContextMenu.svelte +65 -40
- package/dist/overlays/DraggablePane.svelte +211 -139
- package/dist/periodic-table/PeriodicTable.svelte +278 -145
- package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
- package/dist/periodic-table/PropertySelect.svelte +25 -7
- package/dist/periodic-table/TableInset.svelte +8 -3
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +446 -309
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramControls.svelte +102 -43
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +63 -40
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +71 -28
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +158 -101
- package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
- package/dist/phase-diagram/build-diagram.js +9 -9
- package/dist/phase-diagram/colors.js +1 -3
- package/dist/phase-diagram/parse.js +10 -9
- package/dist/phase-diagram/svg-to-diagram.js +53 -49
- package/dist/phase-diagram/utils.d.ts +1 -0
- package/dist/phase-diagram/utils.js +80 -25
- package/dist/plot/AxisLabel.svelte +28 -3
- package/dist/plot/BarPlot.svelte +1182 -734
- package/dist/plot/BarPlot.svelte.d.ts +2 -2
- package/dist/plot/BarPlotControls.svelte +31 -5
- package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ColorBar.svelte +479 -329
- package/dist/plot/ColorScaleSelect.svelte +27 -6
- package/dist/plot/ElementScatter.svelte +36 -15
- package/dist/plot/FillArea.svelte +152 -95
- package/dist/plot/Histogram.svelte +934 -571
- package/dist/plot/Histogram.svelte.d.ts +1 -1
- package/dist/plot/HistogramControls.svelte +53 -9
- package/dist/plot/HistogramControls.svelte.d.ts +1 -1
- package/dist/plot/InteractiveAxisLabel.svelte +34 -11
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
- package/dist/plot/Line.svelte +63 -28
- package/dist/plot/PlotControls.svelte +157 -114
- package/dist/plot/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/PlotLegend.svelte +174 -91
- package/dist/plot/PlotTooltip.svelte +45 -6
- package/dist/plot/PortalSelect.svelte +175 -147
- package/dist/plot/ReferenceLine.svelte +76 -22
- package/dist/plot/ReferenceLine3D.svelte +132 -107
- package/dist/plot/ReferencePlane.svelte +146 -121
- package/dist/plot/ScatterPlot.svelte +1681 -1091
- package/dist/plot/ScatterPlot.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3D.svelte +256 -131
- package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3DControls.svelte +113 -63
- package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
- package/dist/plot/ScatterPlot3DScene.svelte +608 -403
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlotControls.svelte +65 -25
- package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ScatterPoint.svelte +98 -26
- package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
- package/dist/plot/SpacegroupBarPlot.svelte +142 -85
- package/dist/plot/Surface3D.svelte +159 -108
- package/dist/plot/ZeroLines.svelte +55 -3
- package/dist/plot/ZoomRect.svelte +4 -2
- package/dist/plot/axis-utils.js +1 -3
- package/dist/plot/data-cleaning.js +12 -28
- package/dist/plot/data-transform.js +2 -1
- package/dist/plot/fill-utils.js +2 -0
- package/dist/plot/layout.d.ts +4 -1
- package/dist/plot/layout.js +33 -14
- package/dist/plot/reference-line.d.ts +2 -2
- package/dist/plot/reference-line.js +7 -5
- package/dist/plot/scales.js +24 -36
- package/dist/plot/types.d.ts +11 -23
- package/dist/plot/types.js +6 -11
- package/dist/plot/utils/label-placement.d.ts +32 -15
- package/dist/plot/utils/label-placement.js +227 -66
- package/dist/plot/utils/series-visibility.js +2 -3
- package/dist/rdf/RdfPlot.svelte +143 -91
- package/dist/rdf/calc-rdf.js +4 -5
- package/dist/sanitize.d.ts +4 -0
- package/dist/sanitize.js +107 -0
- package/dist/settings.d.ts +18 -6
- package/dist/settings.js +46 -16
- package/dist/spectral/Bands.svelte +632 -453
- package/dist/spectral/BandsAndDos.svelte +90 -49
- package/dist/spectral/BrillouinBandsDos.svelte +151 -93
- package/dist/spectral/Dos.svelte +389 -258
- package/dist/spectral/helpers.js +55 -43
- package/dist/state.svelte.d.ts +1 -1
- package/dist/state.svelte.js +3 -2
- package/dist/structure/Arrow.svelte +59 -20
- package/dist/structure/AtomLegend.svelte +215 -134
- package/dist/structure/Bond.svelte +73 -47
- package/dist/structure/CanvasTooltip.svelte +10 -2
- package/dist/structure/CellSelect.svelte +72 -45
- package/dist/structure/Cylinder.svelte +33 -17
- package/dist/structure/Lattice.svelte +88 -33
- package/dist/structure/Structure.svelte +1063 -797
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureControls.svelte +349 -118
- package/dist/structure/StructureExportPane.svelte +124 -89
- package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +304 -237
- package/dist/structure/StructureScene.svelte +879 -443
- package/dist/structure/StructureScene.svelte.d.ts +15 -7
- package/dist/structure/atom-properties.js +8 -8
- package/dist/structure/bonding.js +6 -7
- package/dist/structure/export.js +14 -29
- package/dist/structure/ferrox-wasm.js +1 -1
- package/dist/structure/index.d.ts +13 -3
- package/dist/structure/index.js +83 -23
- package/dist/structure/measure.d.ts +2 -2
- package/dist/structure/measure.js +4 -44
- package/dist/structure/parse.js +113 -141
- package/dist/structure/partial-occupancy.js +7 -10
- package/dist/structure/pbc.d.ts +1 -0
- package/dist/structure/pbc.js +16 -6
- package/dist/structure/supercell.d.ts +2 -2
- package/dist/structure/supercell.js +12 -22
- package/dist/structure/validation.js +1 -2
- package/dist/symmetry/SymmetryStats.svelte +84 -41
- package/dist/symmetry/WyckoffTable.svelte +26 -6
- package/dist/symmetry/cell-transform.js +5 -3
- package/dist/symmetry/index.js +8 -7
- package/dist/symmetry/spacegroups.js +148 -148
- package/dist/table/HeatmapTable.svelte +790 -554
- package/dist/table/HeatmapTable.svelte.d.ts +1 -1
- package/dist/table/ToggleMenu.svelte +125 -92
- package/dist/table/index.js +2 -4
- package/dist/theme/ThemeControl.svelte +21 -12
- package/dist/time.js +4 -1
- package/dist/tooltip/TooltipContent.svelte +33 -8
- package/dist/trajectory/Trajectory.svelte +758 -558
- package/dist/trajectory/TrajectoryError.svelte +14 -3
- package/dist/trajectory/TrajectoryExportPane.svelte +137 -83
- package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
- package/dist/trajectory/extract.js +10 -26
- package/dist/trajectory/format-detect.js +5 -5
- package/dist/trajectory/frame-reader.d.ts +1 -1
- package/dist/trajectory/frame-reader.js +5 -12
- package/dist/trajectory/helpers.d.ts +0 -1
- package/dist/trajectory/helpers.js +2 -17
- package/dist/trajectory/index.js +14 -12
- package/dist/trajectory/parse/ase.js +5 -4
- package/dist/trajectory/parse/hdf5.js +26 -18
- package/dist/trajectory/parse/index.js +13 -18
- package/dist/trajectory/parse/lammps.js +17 -7
- package/dist/trajectory/parse/vasp.js +5 -2
- package/dist/trajectory/parse/xyz.js +8 -7
- package/dist/trajectory/plotting.js +13 -8
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +13 -0
- package/dist/xrd/XrdPlot.svelte +337 -247
- package/dist/xrd/broadening.js +14 -9
- package/dist/xrd/calc-xrd.js +12 -18
- package/dist/xrd/parse.d.ts +1 -1
- package/dist/xrd/parse.js +17 -17
- package/package.json +99 -103
- package/readme.md +1 -1
- /package/dist/theme/{themes.js → themes.mjs} +0 -0
package/dist/spectral/Dos.svelte
CHANGED
|
@@ -1,306 +1,437 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { PLOT_COLORS } from '../colors'
|
|
3
|
+
import EmptyState from '../EmptyState.svelte'
|
|
4
|
+
import { SettingsSection } from '../layout'
|
|
5
|
+
import ScatterPlot from '../plot/ScatterPlot.svelte'
|
|
6
|
+
import type { AxisConfig, DataSeries } from '../plot/types'
|
|
7
|
+
import type { ComponentProps } from 'svelte'
|
|
8
|
+
import { tooltip as attach_tooltip } from 'svelte-multiselect/attachments'
|
|
9
|
+
import {
|
|
10
|
+
apply_gaussian_smearing,
|
|
11
|
+
calculate_sigma_step,
|
|
12
|
+
convert_frequencies,
|
|
13
|
+
extract_efermi,
|
|
14
|
+
extract_pdos,
|
|
15
|
+
format_dos_tooltip,
|
|
16
|
+
format_sigma,
|
|
17
|
+
FREQUENCY_UNITS,
|
|
18
|
+
IMAGINARY_MODE_NOISE_THRESHOLD,
|
|
19
|
+
is_valid_range,
|
|
20
|
+
negative_fraction,
|
|
21
|
+
NORMALIZATION_MODES,
|
|
22
|
+
normalize_densities,
|
|
23
|
+
normalize_dos,
|
|
24
|
+
SPIN_MODES,
|
|
25
|
+
validate_sigma_range,
|
|
26
|
+
} from './helpers'
|
|
27
|
+
import type {
|
|
28
|
+
DosData,
|
|
29
|
+
DosInput,
|
|
30
|
+
FrequencyUnit,
|
|
31
|
+
NormalizationMode,
|
|
32
|
+
PdosType,
|
|
33
|
+
SpinMode,
|
|
34
|
+
StackedAreaData,
|
|
35
|
+
} from './types'
|
|
36
|
+
|
|
37
|
+
let {
|
|
38
|
+
doses,
|
|
39
|
+
stack = false,
|
|
40
|
+
sigma = $bindable(0),
|
|
41
|
+
units = $bindable(`THz`),
|
|
42
|
+
normalize = $bindable(null),
|
|
43
|
+
orientation = `vertical`,
|
|
44
|
+
show_legend = true,
|
|
45
|
+
x_axis = {},
|
|
46
|
+
y_axis = $bindable({}),
|
|
47
|
+
hovered_frequency = $bindable(null),
|
|
48
|
+
reference_frequency = null,
|
|
49
|
+
fermi_level = undefined,
|
|
50
|
+
spin_mode = $bindable(`mirror`),
|
|
51
|
+
pdos_type = null,
|
|
52
|
+
pdos_filter = undefined,
|
|
53
|
+
// Controls configuration
|
|
54
|
+
show_controls = true,
|
|
55
|
+
show_sigma_control = true,
|
|
56
|
+
show_normalize_control = false,
|
|
57
|
+
show_units_control = false,
|
|
58
|
+
sigma_range = undefined,
|
|
59
|
+
...rest
|
|
60
|
+
}: ComponentProps<typeof ScatterPlot> & {
|
|
61
|
+
doses: DosInput | Record<string, DosInput>
|
|
62
|
+
x_axis?: AxisConfig
|
|
63
|
+
y_axis?: AxisConfig
|
|
64
|
+
stack?: boolean
|
|
65
|
+
sigma?: number
|
|
66
|
+
units?: FrequencyUnit
|
|
67
|
+
normalize?: NormalizationMode
|
|
68
|
+
orientation?: `vertical` | `horizontal`
|
|
69
|
+
show_legend?: boolean
|
|
70
|
+
hovered_frequency?: number | null
|
|
71
|
+
reference_frequency?: number | null
|
|
72
|
+
fermi_level?: number // Fermi level for electronic DOS (auto-detected if not provided)
|
|
73
|
+
spin_mode?: SpinMode // How to display spin-polarized DOS: mirror (default), overlay, up_only, down_only, or null (auto)
|
|
74
|
+
pdos_type?: PdosType | null // Extract projected DOS: 'atom' for atom-resolved, 'orbital' for orbital-resolved (s, p, d)
|
|
75
|
+
pdos_filter?: string[] // Filter projected DOS to specific keys (e.g., ["Fe", "O"] for atoms or ["s", "p", "d"] for orbitals)
|
|
76
|
+
// Controls configuration
|
|
77
|
+
show_controls?: boolean // Show the controls pane
|
|
78
|
+
show_sigma_control?: boolean // Show sigma/smearing control
|
|
79
|
+
show_normalize_control?: boolean // Show normalization selector
|
|
80
|
+
show_units_control?: boolean // Show units selector (phonon DOS only)
|
|
81
|
+
sigma_range?: [number, number] // Min/max range for sigma slider (auto-detected if not provided)
|
|
82
|
+
} = $props()
|
|
83
|
+
|
|
84
|
+
const is_horizontal = $derived(orientation === `horizontal`)
|
|
85
|
+
|
|
86
|
+
// Normalize input to dict format - converts any DosInput format to DosData
|
|
87
|
+
// If pdos_type is set, extract projected DOS from the input instead
|
|
88
|
+
let doses_dict = $derived.by((): Record<string, DosData> => {
|
|
89
|
+
if (!doses) return {}
|
|
90
|
+
|
|
16
91
|
// If pdos_type is set, try to extract projected DOS
|
|
17
92
|
if (pdos_type) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
93
|
+
// Try extracting from the doses object directly (single CompleteDos)
|
|
94
|
+
const pdos = extract_pdos(doses, pdos_type, pdos_filter)
|
|
95
|
+
if (pdos) return pdos
|
|
96
|
+
|
|
97
|
+
// Try extracting from first entry if doses is a dict
|
|
98
|
+
if (typeof doses === `object` && !(`densities` in doses)) {
|
|
99
|
+
const first_dos = Object.values(doses)[0]
|
|
100
|
+
const pdos_from_first = extract_pdos(first_dos, pdos_type, pdos_filter)
|
|
101
|
+
if (pdos_from_first) return pdos_from_first
|
|
102
|
+
}
|
|
103
|
+
// PDOS extraction was requested but failed - warn and revert to normal processing
|
|
104
|
+
console.warn(
|
|
105
|
+
`PDOS extraction requested (pdos_type="${pdos_type}") but no projected DOS data found. ` +
|
|
106
|
+
`Falling back to total DOS. Ensure input has atom_dos (for atom) or spd_dos (for orbital) data.`,
|
|
107
|
+
)
|
|
32
108
|
}
|
|
109
|
+
|
|
33
110
|
if (`densities` in doses && (`frequencies` in doses || `energies` in doses)) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
111
|
+
// Single DOS
|
|
112
|
+
const normalized = normalize_dos(doses)
|
|
113
|
+
return normalized ? { '': normalized } : {}
|
|
37
114
|
}
|
|
115
|
+
|
|
38
116
|
// Already a dict - normalize each DOS
|
|
39
|
-
const result = {}
|
|
40
|
-
for (const [key, dos] of Object.entries(doses)) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
result[key] = normalized;
|
|
44
|
-
}
|
|
45
|
-
return result;
|
|
46
|
-
});
|
|
47
|
-
// Determine if this is phonon or electronic DOS using discriminated union
|
|
48
|
-
let is_phonon = $derived(Object.values(doses_dict)[0]?.type === `phonon`);
|
|
49
|
-
// Auto-detect Fermi level from electronic DOS data if not explicitly provided
|
|
50
|
-
let effective_fermi_level = $derived.by(() => {
|
|
51
|
-
if (fermi_level !== undefined)
|
|
52
|
-
return fermi_level;
|
|
53
|
-
if (is_phonon)
|
|
54
|
-
return undefined;
|
|
55
|
-
return extract_efermi(doses);
|
|
56
|
-
});
|
|
57
|
-
// Check if any DOS in the dict has spin-polarized data
|
|
58
|
-
let has_spin_polarized = $derived(Object.values(doses_dict).some((dos) => dos.type === `electronic` && dos.spin_down_densities?.length));
|
|
59
|
-
// Effective spin mode: null means auto-detect (mirror if spin data exists)
|
|
60
|
-
let effective_spin_mode = $derived.by(() => {
|
|
61
|
-
if (spin_mode !== null)
|
|
62
|
-
return spin_mode;
|
|
63
|
-
return has_spin_polarized ? `mirror` : null;
|
|
64
|
-
});
|
|
65
|
-
// Convert DOS data to scatter plot series and stacked area data
|
|
66
|
-
// Performance: Only recalculates when doses_dict, units, sigma, normalize, or spin_mode changes
|
|
67
|
-
let { series_data, stacked_areas } = $derived.by(() => {
|
|
68
|
-
if (Object.keys(doses_dict).length === 0) {
|
|
69
|
-
return { series_data: [], stacked_areas: [] };
|
|
117
|
+
const result: Record<string, DosData> = {}
|
|
118
|
+
for (const [key, dos] of Object.entries(doses as Record<string, DosInput>)) {
|
|
119
|
+
const normalized = normalize_dos(dos)
|
|
120
|
+
if (normalized) result[key] = normalized
|
|
70
121
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
122
|
+
return result
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// Determine if this is phonon or electronic DOS using discriminated union
|
|
126
|
+
let is_phonon = $derived(Object.values(doses_dict)[0]?.type === `phonon`)
|
|
127
|
+
|
|
128
|
+
// Auto-detect Fermi level from electronic DOS data if not explicitly provided
|
|
129
|
+
let effective_fermi_level = $derived.by((): number | undefined => {
|
|
130
|
+
if (fermi_level !== undefined) return fermi_level
|
|
131
|
+
if (is_phonon) return undefined
|
|
132
|
+
return extract_efermi(doses)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
// Check if any DOS in the dict has spin-polarized data
|
|
136
|
+
let has_spin_polarized = $derived(
|
|
137
|
+
Object.values(doses_dict).some(
|
|
138
|
+
(dos) => dos.type === `electronic` && dos.spin_down_densities?.length,
|
|
139
|
+
),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
// Effective spin mode: null means auto-detect (mirror if spin data exists)
|
|
143
|
+
let effective_spin_mode = $derived.by((): SpinMode => {
|
|
144
|
+
if (spin_mode !== null) return spin_mode
|
|
145
|
+
return has_spin_polarized ? `mirror` : null
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
// Convert DOS data to scatter plot series and stacked area data
|
|
149
|
+
// Performance: Only recalculates when doses_dict, units, sigma, normalize, or spin_mode changes
|
|
150
|
+
let { series_data, stacked_areas } = $derived.by(
|
|
151
|
+
(): { series_data: DataSeries[]; stacked_areas: StackedAreaData[] } => {
|
|
152
|
+
if (Object.keys(doses_dict).length === 0) {
|
|
153
|
+
return { series_data: [], stacked_areas: [] }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const all_series: DataSeries[] = []
|
|
157
|
+
const areas: StackedAreaData[] = []
|
|
158
|
+
const dos_entries = Object.entries(doses_dict)
|
|
159
|
+
// Separate cumulative trackers for spin-up and spin-down in overlay mode
|
|
160
|
+
// This prevents spin-down from incorrectly stacking on top of spin-up
|
|
161
|
+
let cumulative_spin_up: number[] | null = null
|
|
162
|
+
let cumulative_spin_down: number[] | null = null
|
|
163
|
+
|
|
164
|
+
for (let dos_idx = 0; dos_idx < dos_entries.length; dos_idx++) {
|
|
165
|
+
const [label, dos] = dos_entries[dos_idx]
|
|
166
|
+
const color = PLOT_COLORS[dos_idx % PLOT_COLORS.length]
|
|
167
|
+
|
|
81
168
|
// Get frequencies or energies using discriminated union type narrowing
|
|
82
|
-
let x_values = dos.type === `phonon` ? dos.frequencies : dos.energies
|
|
169
|
+
let x_values = dos.type === `phonon` ? dos.frequencies : dos.energies
|
|
170
|
+
|
|
83
171
|
// Convert units if needed
|
|
84
172
|
if (dos.type === `phonon` && units !== `THz`) {
|
|
85
|
-
|
|
173
|
+
x_values = convert_frequencies(x_values, units)
|
|
86
174
|
}
|
|
175
|
+
|
|
87
176
|
// Check for spin-down data (only for electronic DOS)
|
|
88
177
|
const has_spin_down = dos.type === `electronic` &&
|
|
89
|
-
|
|
90
|
-
const should_show_spin_up = effective_spin_mode !== `down_only
|
|
178
|
+
dos.spin_down_densities?.length === dos.densities.length
|
|
179
|
+
const should_show_spin_up = effective_spin_mode !== `down_only`
|
|
91
180
|
const should_show_spin_down = has_spin_down &&
|
|
92
|
-
|
|
181
|
+
effective_spin_mode !== `up_only` && effective_spin_mode !== null
|
|
182
|
+
|
|
93
183
|
// Process spin-up (or total) densities
|
|
94
184
|
if (should_show_spin_up) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
185
|
+
let densities_up = sigma > 0
|
|
186
|
+
? apply_gaussian_smearing(x_values, dos.densities, sigma)
|
|
187
|
+
: [...dos.densities]
|
|
188
|
+
|
|
189
|
+
densities_up = normalize_densities(densities_up, x_values, normalize)
|
|
190
|
+
|
|
191
|
+
// Store previous cumulative for area fill baseline
|
|
192
|
+
const prev_cumulative = cumulative_spin_up ? [...cumulative_spin_up] : null
|
|
193
|
+
|
|
194
|
+
// For stacked plots, accumulate densities (only if array lengths match)
|
|
195
|
+
if (stack && cumulative_spin_up?.length === densities_up.length) {
|
|
196
|
+
densities_up = densities_up.map((density, idx) =>
|
|
197
|
+
density + (cumulative_spin_up?.[idx] ?? 0)
|
|
198
|
+
)
|
|
199
|
+
} else if (stack && cumulative_spin_up) {
|
|
200
|
+
console.warn(`DOS stacking: length mismatch for "${label}"`)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Store stacked area data for rendering
|
|
204
|
+
if (stack) {
|
|
205
|
+
areas.push({
|
|
206
|
+
x_values: [...x_values],
|
|
207
|
+
upper_densities: [...densities_up],
|
|
208
|
+
lower_densities: prev_cumulative ?? x_values.map(() => 0),
|
|
209
|
+
color,
|
|
210
|
+
})
|
|
211
|
+
cumulative_spin_up = densities_up
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const spin_label = has_spin_down && effective_spin_mode
|
|
215
|
+
? `${label || `DOS ${dos_idx + 1}`} (↑)`
|
|
216
|
+
: (label || `DOS ${dos_idx + 1}`)
|
|
217
|
+
|
|
218
|
+
const series_up: DataSeries = {
|
|
219
|
+
x: is_horizontal ? densities_up : x_values,
|
|
220
|
+
y: is_horizontal ? x_values : densities_up,
|
|
221
|
+
markers: `line`,
|
|
222
|
+
label: spin_label,
|
|
223
|
+
line_style: { stroke: color, stroke_width: 1.5 },
|
|
224
|
+
point_style: { fill: stack ? color : undefined },
|
|
225
|
+
}
|
|
226
|
+
all_series.push(series_up)
|
|
130
227
|
}
|
|
228
|
+
|
|
131
229
|
// Process spin-down densities if available
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
color: PLOT_COLORS[(dos_idx * 2 + 1) % PLOT_COLORS.length],
|
|
160
|
-
});
|
|
161
|
-
cumulative_spin_down = densities_down;
|
|
230
|
+
if (
|
|
231
|
+
should_show_spin_down && dos.type === `electronic` &&
|
|
232
|
+
dos.spin_down_densities
|
|
233
|
+
) {
|
|
234
|
+
let densities_down = sigma > 0
|
|
235
|
+
? apply_gaussian_smearing(x_values, dos.spin_down_densities, sigma)
|
|
236
|
+
: [...dos.spin_down_densities]
|
|
237
|
+
|
|
238
|
+
densities_down = normalize_densities(densities_down, x_values, normalize)
|
|
239
|
+
|
|
240
|
+
// For mirror mode, negate the densities
|
|
241
|
+
if (effective_spin_mode === `mirror`) {
|
|
242
|
+
densities_down = densities_down.map((d) => -d)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// For stacked plots with overlay mode, use separate spin-down cumulative
|
|
246
|
+
// This prevents spin-down from stacking on top of spin-up within the same DOS
|
|
247
|
+
if (stack && effective_spin_mode === `overlay`) {
|
|
248
|
+
const prev_spin_down = cumulative_spin_down
|
|
249
|
+
? [...cumulative_spin_down]
|
|
250
|
+
: null
|
|
251
|
+
if (cumulative_spin_down?.length === densities_down.length) {
|
|
252
|
+
densities_down = densities_down.map((density, idx) =>
|
|
253
|
+
density + (cumulative_spin_down?.[idx] ?? 0)
|
|
254
|
+
)
|
|
255
|
+
} else if (cumulative_spin_down) {
|
|
256
|
+
console.warn(`DOS stacking (spin-down): length mismatch for "${label}"`)
|
|
162
257
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
258
|
+
|
|
259
|
+
// Store stacked area for spin-down
|
|
260
|
+
areas.push({
|
|
261
|
+
x_values: [...x_values],
|
|
262
|
+
upper_densities: [...densities_down],
|
|
263
|
+
lower_densities: prev_spin_down ?? x_values.map(() => 0),
|
|
264
|
+
color: PLOT_COLORS[(dos_idx * 2 + 1) % PLOT_COLORS.length],
|
|
265
|
+
})
|
|
266
|
+
cumulative_spin_down = densities_down
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Use a slightly different shade for spin-down in overlay mode
|
|
270
|
+
const spin_down_color = effective_spin_mode === `overlay`
|
|
271
|
+
? PLOT_COLORS[(dos_idx * 2 + 1) % PLOT_COLORS.length]
|
|
272
|
+
: color
|
|
273
|
+
|
|
274
|
+
const series_down: DataSeries = {
|
|
275
|
+
x: is_horizontal ? densities_down : x_values,
|
|
276
|
+
y: is_horizontal ? x_values : densities_down,
|
|
277
|
+
markers: `line`,
|
|
278
|
+
label: `${label || `DOS ${dos_idx + 1}`} (↓)`,
|
|
279
|
+
line_style: {
|
|
280
|
+
stroke: spin_down_color,
|
|
281
|
+
stroke_width: 1.5,
|
|
282
|
+
line_dash: effective_spin_mode === `overlay` ? `4,2` : undefined,
|
|
283
|
+
},
|
|
284
|
+
point_style: { fill: stack ? spin_down_color : undefined },
|
|
285
|
+
}
|
|
286
|
+
all_series.push(series_down)
|
|
180
287
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
let
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
let
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
288
|
+
}
|
|
289
|
+
return { series_data: all_series, stacked_areas: areas }
|
|
290
|
+
},
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
let all_freqs = $derived( // for clamping phonon noise
|
|
294
|
+
Object.values(doses_dict).flatMap((dos) =>
|
|
295
|
+
dos.type === `phonon` ? dos.frequencies : dos.energies
|
|
296
|
+
),
|
|
297
|
+
)
|
|
298
|
+
let clamp_to_zero = $derived(
|
|
299
|
+
is_phonon &&
|
|
300
|
+
all_freqs.length > 0 &&
|
|
301
|
+
Math.min(...all_freqs) < 0 &&
|
|
302
|
+
negative_fraction(all_freqs) < IMAGINARY_MODE_NOISE_THRESHOLD,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
// Check if we have mirrored spin-down data (negative densities)
|
|
306
|
+
let has_mirrored_spin = $derived(
|
|
307
|
+
effective_spin_mode === `mirror` && has_spin_polarized,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
let x_range = $derived.by((): [number, number] | undefined => {
|
|
311
|
+
if (!series_data.length) return undefined
|
|
312
|
+
const all_x = series_data.flatMap((srs) => srs.x)
|
|
313
|
+
const min_x = Math.min(...all_x), max_x = Math.max(...all_x)
|
|
197
314
|
// For horizontal orientation with mirror mode, allow negative values (mirrored densities)
|
|
198
|
-
if (is_horizontal && has_mirrored_spin)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const all_y = series_data.flatMap((srs) => srs.y);
|
|
208
|
-
const min_y = Math.min(...all_y), max_y = Math.max(...all_y);
|
|
315
|
+
if (is_horizontal && has_mirrored_spin) return [min_x, max_x]
|
|
316
|
+
if (is_horizontal || clamp_to_zero) return [0, max_x]
|
|
317
|
+
return [min_x, max_x]
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
let y_range = $derived.by((): [number, number] | undefined => {
|
|
321
|
+
if (!series_data.length) return undefined
|
|
322
|
+
const all_y = series_data.flatMap((srs) => srs.y)
|
|
323
|
+
const min_y = Math.min(...all_y), max_y = Math.max(...all_y)
|
|
209
324
|
// For vertical orientation with mirror mode, allow negative values (mirrored densities)
|
|
210
|
-
if (!is_horizontal && has_mirrored_spin)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
325
|
+
if (!is_horizontal && has_mirrored_spin) return [min_y, max_y]
|
|
326
|
+
if (!is_horizontal || clamp_to_zero) return [0, max_y]
|
|
327
|
+
return [min_y, max_y]
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
// Get axis labels based on orientation
|
|
331
|
+
let x_label = $derived(
|
|
332
|
+
is_horizontal
|
|
333
|
+
? `Density of States`
|
|
334
|
+
: is_phonon
|
|
335
|
+
? `Frequency (${units})`
|
|
336
|
+
: `Energy (eV)`,
|
|
337
|
+
)
|
|
338
|
+
let y_label = $derived(
|
|
339
|
+
is_horizontal
|
|
340
|
+
? (is_phonon ? `Frequency (${units})` : `Energy (eV)`)
|
|
341
|
+
: `Density of States`,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
// Compute final axis configurations with default labels
|
|
345
|
+
let final_x_axis = $derived({
|
|
227
346
|
label: x_label,
|
|
228
347
|
format: `.2f`,
|
|
229
348
|
range: x_range,
|
|
230
349
|
label_shift: { x: 0, y: -48 }, // Increase standoff from tick labels
|
|
231
350
|
...(is_horizontal && { ticks: 4 }),
|
|
232
351
|
...x_axis,
|
|
233
|
-
})
|
|
234
|
-
// Internal y_axis that ScatterPlot binds to - syncs zoom changes back to parent
|
|
235
|
-
let internal_y_axis = $derived({
|
|
352
|
+
})
|
|
353
|
+
// Internal y_axis that ScatterPlot binds to - syncs zoom changes back to parent
|
|
354
|
+
let internal_y_axis = $derived({
|
|
236
355
|
label: y_label,
|
|
237
356
|
format: `.2f`,
|
|
238
357
|
range: y_range,
|
|
239
358
|
...y_axis,
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
//
|
|
243
|
-
|
|
244
|
-
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
// Sync zoom changes from ScatterPlot back to parent via bindable y_axis
|
|
362
|
+
// Also clears parent range when internal range becomes invalid (auto-range reset)
|
|
363
|
+
$effect(() => {
|
|
364
|
+
const range = internal_y_axis.range
|
|
245
365
|
if (is_valid_range(range)) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
366
|
+
if (y_axis.range?.[0] !== range[0] || y_axis.range?.[1] !== range[1]) {
|
|
367
|
+
y_axis = { ...y_axis, range }
|
|
368
|
+
}
|
|
369
|
+
return
|
|
250
370
|
}
|
|
251
371
|
// Range became invalid - clear parent's range to propagate reset
|
|
252
372
|
if (`range` in y_axis) {
|
|
253
|
-
|
|
254
|
-
|
|
373
|
+
const { range: _omit, ...rest } = y_axis
|
|
374
|
+
y_axis = rest
|
|
255
375
|
}
|
|
256
|
-
})
|
|
257
|
-
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
let display = $state({
|
|
258
379
|
x_grid: true,
|
|
259
380
|
y_grid: true,
|
|
260
381
|
x_zero_line: true,
|
|
261
382
|
y_zero_line: true,
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (
|
|
270
|
-
|
|
271
|
-
const freq_range = Math.max(...all_freqs) - Math.min(...all_freqs)
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
// Determine if we have valid data to display
|
|
386
|
+
let has_valid_data = $derived(series_data.length > 0)
|
|
387
|
+
|
|
388
|
+
// Auto-detect sigma range based on frequency/energy range
|
|
389
|
+
let effective_sigma_range = $derived.by((): [number, number] => {
|
|
390
|
+
if (sigma_range) return sigma_range
|
|
391
|
+
if (!all_freqs.length) return [0, 1]
|
|
392
|
+
const freq_range = Math.max(...all_freqs) - Math.min(...all_freqs)
|
|
272
393
|
// Reasonable sigma range: 0 to ~5% of total range
|
|
273
|
-
const max_sigma = Math.max(0.1, freq_range * 0.05)
|
|
274
|
-
return [0, max_sigma]
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
let
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
394
|
+
const max_sigma = Math.max(0.1, freq_range * 0.05)
|
|
395
|
+
return [0, max_sigma]
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
// Computed values for sigma slider
|
|
399
|
+
let safe_sigma_range = $derived(validate_sigma_range(effective_sigma_range))
|
|
400
|
+
let sigma_step = $derived(calculate_sigma_step(effective_sigma_range))
|
|
401
|
+
|
|
402
|
+
// Build SVG path for stacked area fill between upper and lower density curves
|
|
403
|
+
function build_stacked_area_path(
|
|
404
|
+
area: StackedAreaData,
|
|
405
|
+
x_scale_fn: (val: number) => number,
|
|
406
|
+
y_scale_fn: (val: number) => number,
|
|
407
|
+
horizontal: boolean,
|
|
408
|
+
): string {
|
|
409
|
+
const pts = area.x_values.length
|
|
410
|
+
if (pts < 2) return ``
|
|
411
|
+
|
|
412
|
+
const upper_coords: string[] = []
|
|
413
|
+
const lower_coords: string[] = []
|
|
414
|
+
|
|
286
415
|
for (let idx = 0; idx < pts; idx++) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
416
|
+
const freq = area.x_values[idx]
|
|
417
|
+
const upper_dens = area.upper_densities[idx]
|
|
418
|
+
const lower_dens = area.lower_densities[idx]
|
|
419
|
+
// For vertical orientation: x = freq, y = density
|
|
420
|
+
// For horizontal orientation: x = density, y = freq
|
|
421
|
+
const [upper_x, upper_y] = horizontal
|
|
422
|
+
? [x_scale_fn(upper_dens), y_scale_fn(freq)]
|
|
423
|
+
: [x_scale_fn(freq), y_scale_fn(upper_dens)]
|
|
424
|
+
const [lower_x, lower_y] = horizontal
|
|
425
|
+
? [x_scale_fn(lower_dens), y_scale_fn(freq)]
|
|
426
|
+
: [x_scale_fn(freq), y_scale_fn(lower_dens)]
|
|
427
|
+
upper_coords.push(`${upper_x.toFixed(2)},${upper_y.toFixed(2)}`)
|
|
428
|
+
lower_coords.push(`${lower_x.toFixed(2)},${lower_y.toFixed(2)}`)
|
|
300
429
|
}
|
|
301
430
|
// Trace upper edge forward, lower edge backward, close path
|
|
302
|
-
return `M${upper_coords[0]} ${
|
|
303
|
-
}
|
|
431
|
+
return `M${upper_coords[0]} ${
|
|
432
|
+
upper_coords.slice(1).map((coord) => `L${coord}`).join(` `)
|
|
433
|
+
} ${lower_coords.toReversed().map((coord) => `L${coord}`).join(` `)} Z`
|
|
434
|
+
}
|
|
304
435
|
</script>
|
|
305
436
|
|
|
306
437
|
{#if has_valid_data}
|