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.
- 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/element/data.js +1 -1
- 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/xrd/XrdPlot.svelte
CHANGED
|
@@ -1,185 +1,265 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { add_alpha, PLOT_COLORS } from '../colors'
|
|
3
|
+
import EmptyState from '../EmptyState.svelte'
|
|
4
|
+
import StatusMessage from '../feedback/StatusMessage.svelte'
|
|
5
|
+
import {
|
|
6
|
+
decompress_data_binary,
|
|
7
|
+
decompress_file,
|
|
8
|
+
detect_compression_format,
|
|
9
|
+
handle_url_drop,
|
|
10
|
+
} from '../io'
|
|
11
|
+
import { format_value } from '../labels'
|
|
12
|
+
import { sanitize_html } from '../sanitize'
|
|
13
|
+
import SettingsSection from '../layout/SettingsSection.svelte'
|
|
14
|
+
import type { Vec2 } from '../math'
|
|
15
|
+
import type {
|
|
16
|
+
AxisConfig,
|
|
17
|
+
BarHandlerProps,
|
|
18
|
+
BarSeries,
|
|
19
|
+
ControlsConfig,
|
|
20
|
+
DataSeries,
|
|
21
|
+
ScatterHandlerProps,
|
|
22
|
+
} from '../plot'
|
|
23
|
+
import { BarPlot, ScatterPlot } from '../plot'
|
|
24
|
+
import { add_xrd_pattern } from './calc-xrd'
|
|
25
|
+
import type { ComponentProps } from 'svelte'
|
|
26
|
+
import type { BroadeningParams } from './broadening'
|
|
27
|
+
import { compute_broadened_pattern, DEFAULT_BROADENING } from './broadening'
|
|
28
|
+
import type { Hkl, HklFormat, PatternEntry, XrdPattern } from './index'
|
|
29
|
+
|
|
30
|
+
function is_xrd_pattern(obj: unknown): obj is XrdPattern {
|
|
31
|
+
if (!obj || typeof obj !== `object`) return false
|
|
32
|
+
const pattern_obj = obj as { x?: unknown; y?: unknown }
|
|
33
|
+
const x = pattern_obj.x
|
|
34
|
+
const y = pattern_obj.y
|
|
35
|
+
return (
|
|
36
|
+
Array.isArray(x) &&
|
|
37
|
+
Array.isArray(y) &&
|
|
38
|
+
x.length === y.length
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function format_hkl(hkl: Hkl, format: HklFormat): string {
|
|
21
43
|
if (format === `compact`) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
// Use crystallographic overbar notation for negative indices (e.g. 1̄ instead of -1)
|
|
45
|
+
// Note: Requires font support for Unicode combining characters (U+0305)
|
|
46
|
+
return hkl
|
|
47
|
+
.map((val) => {
|
|
48
|
+
// Use combining overline character (U+0305) for negative values
|
|
49
|
+
// Apply overbar to each digit for multi-digit numbers
|
|
50
|
+
if (val < 0) {
|
|
51
|
+
const digits = String(Math.abs(val))
|
|
52
|
+
return digits
|
|
53
|
+
.split(``)
|
|
54
|
+
.map((digit) => `${digit}\u0305`)
|
|
55
|
+
.join(``)
|
|
56
|
+
}
|
|
57
|
+
return `${val}`
|
|
36
58
|
})
|
|
37
|
-
|
|
59
|
+
.join(``)
|
|
38
60
|
}
|
|
39
|
-
if (format === `full`)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
let {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
if (format === `full`) return `(${hkl.join(`, `)})`
|
|
62
|
+
return ``
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let {
|
|
66
|
+
patterns,
|
|
67
|
+
peak_width = 0.5,
|
|
68
|
+
annotate_peaks = 5,
|
|
69
|
+
hkl_format = `compact`,
|
|
70
|
+
show_angles = null,
|
|
71
|
+
orientation = `vertical`,
|
|
72
|
+
wavelength = null,
|
|
73
|
+
x_axis = {},
|
|
74
|
+
y_axis = {},
|
|
75
|
+
allow_file_drop = true,
|
|
76
|
+
on_file_drop,
|
|
77
|
+
loading = $bindable(false),
|
|
78
|
+
error_msg = $bindable(),
|
|
79
|
+
broadening_enabled = $bindable(false),
|
|
80
|
+
broadening_params = $bindable({ ...DEFAULT_BROADENING }),
|
|
81
|
+
controls = {},
|
|
82
|
+
...rest
|
|
83
|
+
}:
|
|
84
|
+
& ComponentProps<typeof BarPlot>
|
|
85
|
+
& ComponentProps<typeof ScatterPlot>
|
|
86
|
+
& {
|
|
87
|
+
patterns:
|
|
88
|
+
| XrdPattern
|
|
89
|
+
| Record<string, XrdPattern | { pattern: XrdPattern; color?: string }>
|
|
90
|
+
| PatternEntry[]
|
|
91
|
+
peak_width?: number
|
|
92
|
+
annotate_peaks?: number // int => top-k, float in (0,1) => threshold of max
|
|
93
|
+
hkl_format?: HklFormat
|
|
94
|
+
show_angles?: boolean | null
|
|
95
|
+
wavelength?: number | null
|
|
96
|
+
x_axis?: AxisConfig
|
|
97
|
+
y_axis?: AxisConfig
|
|
98
|
+
allow_file_drop?: boolean
|
|
99
|
+
on_file_drop?: (content: string | ArrayBuffer, filename: string) => void
|
|
100
|
+
loading?: boolean
|
|
101
|
+
error_msg?: string
|
|
102
|
+
broadening_enabled?: boolean
|
|
103
|
+
broadening_params?: BroadeningParams
|
|
104
|
+
controls?: ControlsConfig
|
|
105
|
+
} = $props()
|
|
106
|
+
|
|
107
|
+
let dragover = $state(false)
|
|
108
|
+
let dropped_entries = $state<PatternEntry[]>([])
|
|
109
|
+
|
|
110
|
+
// Normalize various input shapes to a consistent array of { label, pattern, color }
|
|
111
|
+
const pattern_entries = $derived.by<PatternEntry[]>(() => {
|
|
112
|
+
if (!patterns) return []
|
|
50
113
|
const base_entries = Array.isArray(patterns)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
114
|
+
? (patterns as PatternEntry[])
|
|
115
|
+
: is_xrd_pattern(patterns)
|
|
116
|
+
? [{ label: `XRD Pattern`, pattern: patterns as XrdPattern }]
|
|
117
|
+
: Object.entries(
|
|
118
|
+
patterns as Record<
|
|
119
|
+
string,
|
|
120
|
+
XrdPattern | { pattern: XrdPattern; color?: string }
|
|
121
|
+
>,
|
|
122
|
+
).map(([label, value]) =>
|
|
123
|
+
`pattern` in value
|
|
124
|
+
? { label, ...value }
|
|
125
|
+
: { label, pattern: value as XrdPattern }
|
|
126
|
+
)
|
|
57
127
|
// Merge user-provided patterns with any dropped-on-the-fly entries
|
|
58
|
-
return [...base_entries, ...dropped_entries]
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
128
|
+
return [...base_entries, ...dropped_entries]
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// Decide default show_angles
|
|
132
|
+
const actual_show_angles = $derived(
|
|
133
|
+
show_angles ?? pattern_entries.length <= 2,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
// Compute global max intensity for normalization (as in pymatviz xrd_pattern)
|
|
137
|
+
const global_max_intensity = $derived.by(() => {
|
|
138
|
+
let max_val = 0
|
|
65
139
|
for (const entry of pattern_entries) {
|
|
66
|
-
|
|
67
|
-
if (y > max_val)
|
|
68
|
-
max_val = y;
|
|
140
|
+
for (const y of entry.pattern.y) if (y > max_val) max_val = y
|
|
69
141
|
}
|
|
70
|
-
return max_val || 1
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
let max_x = 0
|
|
78
|
-
for (const entry of
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (entry_max > max_x)
|
|
84
|
-
max_x = entry_max;
|
|
142
|
+
return max_val || 1
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// Compute overall 2θ domain (degrees)
|
|
146
|
+
const angle_range = $derived.by((): Vec2 => {
|
|
147
|
+
const valid = pattern_entries.filter((entry) => entry.pattern.x.length > 0)
|
|
148
|
+
if (valid.length === 0) return [0, 90]
|
|
149
|
+
let [min_x, max_x] = [Infinity, 0]
|
|
150
|
+
for (const entry of valid) {
|
|
151
|
+
const entry_min = Math.min(...entry.pattern.x)
|
|
152
|
+
const entry_max = Math.max(...entry.pattern.x)
|
|
153
|
+
if (entry_min < min_x) min_x = entry_min
|
|
154
|
+
if (entry_max > max_x) max_x = entry_max
|
|
85
155
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// Scaled intensities are normalized to 0..100, add 10% top padding for peak labels
|
|
91
|
-
const intensity_range = [0, 110]
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
156
|
+
const x_min = min_x > 10 ? Math.floor(min_x) : 0
|
|
157
|
+
return [x_min, Math.ceil(max_x)]
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// Scaled intensities are normalized to 0..100, add 10% top padding for peak labels
|
|
161
|
+
const intensity_range: [number, number] = [0, 110]
|
|
162
|
+
|
|
163
|
+
// Build BarPlot series from entries (for Discrete/Stick view)
|
|
164
|
+
const bar_series = $derived.by<BarSeries[]>(() => {
|
|
165
|
+
if (broadening_enabled) return [] // Optimization: skip if not used
|
|
166
|
+
|
|
167
|
+
const include_name = pattern_entries.length > 1
|
|
97
168
|
// Add transparency when multiple series overlap
|
|
98
|
-
const alpha = pattern_entries.length > 1 ? 0.6 : 1
|
|
99
|
-
const scale = (y) => (y / global_max_intensity) * 100
|
|
169
|
+
const alpha = pattern_entries.length > 1 ? 0.6 : 1
|
|
170
|
+
const scale = (y: number) => (y / global_max_intensity) * 100
|
|
100
171
|
return pattern_entries.map((entry, entry_idx) => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
// Filter out overlapping labels: keep higher intensity peaks when x values are close
|
|
124
|
-
// Min spacing as fraction of x-range to avoid overlaps (roughly 3% of range)
|
|
125
|
-
const x_range = Math.max(...xs) - Math.min(...xs);
|
|
126
|
-
const min_spacing = x_range * 0.03;
|
|
127
|
-
// Sort by intensity descending so we keep highest peaks first
|
|
128
|
-
candidates.sort((a, b) => b.y_val - a.y_val);
|
|
129
|
-
const kept = [];
|
|
130
|
-
for (const cand of candidates) {
|
|
131
|
-
const cand_x = xs[cand.idx];
|
|
132
|
-
// Check if any already-kept peak is too close
|
|
133
|
-
const too_close = kept.some((kept_peak) => Math.abs(xs[kept_peak.idx] - cand_x) < min_spacing);
|
|
134
|
-
if (!too_close)
|
|
135
|
-
kept.push(cand);
|
|
136
|
-
}
|
|
137
|
-
selected_indices = kept.map((kept_peak) => kept_peak.idx);
|
|
172
|
+
const xs = entry.pattern.x
|
|
173
|
+
const ys = entry.pattern.y.map((val) => scale(val || 0))
|
|
174
|
+
const metadata: Record<string, unknown>[] = []
|
|
175
|
+
const labels: (string | null)[] = []
|
|
176
|
+
|
|
177
|
+
// Determine which peaks to annotate
|
|
178
|
+
const intens = ys
|
|
179
|
+
let selected_indices: number[] = []
|
|
180
|
+
if (annotate_peaks && annotate_peaks > 0) {
|
|
181
|
+
let candidates: { idx: number; y_val: number }[] = []
|
|
182
|
+
if (annotate_peaks > 0 && annotate_peaks < 1) {
|
|
183
|
+
const thresh = annotate_peaks * 100
|
|
184
|
+
candidates = intens
|
|
185
|
+
.map((y_val, idx) => ({ y_val, idx }))
|
|
186
|
+
.filter(({ y_val }) => y_val > thresh)
|
|
187
|
+
} else {
|
|
188
|
+
const max_peaks = Math.min(intens.length, Math.floor(annotate_peaks))
|
|
189
|
+
candidates = intens
|
|
190
|
+
.map((y_val, idx) => ({ y_val, idx }))
|
|
191
|
+
.sort((a, b) => b.y_val - a.y_val)
|
|
192
|
+
.slice(0, max_peaks)
|
|
138
193
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const separator = hkl_text && angle_text ? ` @ ` : ``;
|
|
155
|
-
const text = [hkl_text, angle_text].filter(Boolean).join(separator);
|
|
156
|
-
labels.push(text);
|
|
157
|
-
}
|
|
158
|
-
else
|
|
159
|
-
labels.push(null);
|
|
194
|
+
|
|
195
|
+
// Filter out overlapping labels: keep higher intensity peaks when x values are close
|
|
196
|
+
// Min spacing as fraction of x-range to avoid overlaps (roughly 3% of range)
|
|
197
|
+
const x_range = Math.max(...xs) - Math.min(...xs)
|
|
198
|
+
const min_spacing = x_range * 0.03
|
|
199
|
+
// Sort by intensity descending so we keep highest peaks first
|
|
200
|
+
candidates.sort((a, b) => b.y_val - a.y_val)
|
|
201
|
+
const kept: { idx: number; y_val: number }[] = []
|
|
202
|
+
for (const cand of candidates) {
|
|
203
|
+
const cand_x = xs[cand.idx]
|
|
204
|
+
// Check if any already-kept peak is too close
|
|
205
|
+
const too_close = kept.some(
|
|
206
|
+
(kept_peak) => Math.abs(xs[kept_peak.idx] - cand_x) < min_spacing,
|
|
207
|
+
)
|
|
208
|
+
if (!too_close) kept.push(cand)
|
|
160
209
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
210
|
+
selected_indices = kept.map((kept_peak) => kept_peak.idx)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
for (let idx = 0; idx < xs.length; idx++) {
|
|
214
|
+
const hkls_objs = entry.pattern.hkls?.[idx] ?? []
|
|
215
|
+
const hkls: Hkl[] = hkls_objs
|
|
216
|
+
.map((hkl_obj) => (Array.isArray(hkl_obj?.hkl) ? hkl_obj.hkl : null))
|
|
217
|
+
.filter((hkl_val): hkl_val is Hkl => Array.isArray(hkl_val) && hkl_val.length === 3)
|
|
218
|
+
const d_hkl = entry.pattern.d_hkls?.[idx]
|
|
219
|
+
metadata.push({ hkls, d: d_hkl, label: entry.label })
|
|
220
|
+
|
|
221
|
+
if (selected_indices.includes(idx)) {
|
|
222
|
+
const angle_text = actual_show_angles
|
|
223
|
+
? `${format_value(xs[idx], `.2f`)}°`
|
|
224
|
+
: ``
|
|
225
|
+
const hkl_text = hkls && hkl_format
|
|
226
|
+
? hkls.map((hkl_val) => format_hkl(hkl_val, hkl_format)).join(`, `)
|
|
227
|
+
: ``
|
|
228
|
+
// Use @ separator between hkl and angle for better clarity
|
|
229
|
+
const separator = hkl_text && angle_text ? ` @ ` : ``
|
|
230
|
+
const text = [hkl_text, angle_text].filter(Boolean).join(separator)
|
|
231
|
+
labels.push(text)
|
|
232
|
+
} else labels.push(null)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const base_color = entry.color ?? PLOT_COLORS[entry_idx % PLOT_COLORS.length]
|
|
236
|
+
return {
|
|
237
|
+
x: xs,
|
|
238
|
+
y: ys,
|
|
239
|
+
label: include_name ? entry.label : ``,
|
|
240
|
+
color: add_alpha(base_color, alpha),
|
|
241
|
+
bar_width: Math.max(peak_width, 0.8),
|
|
242
|
+
visible: true,
|
|
243
|
+
metadata,
|
|
244
|
+
labels,
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
// Build ScatterPlot series (for Broadened Profile view)
|
|
250
|
+
const scatter_series = $derived.by<DataSeries[]>(() => {
|
|
251
|
+
if (!broadening_enabled) return []
|
|
252
|
+
|
|
253
|
+
const include_name = pattern_entries.length > 1
|
|
179
254
|
// We rescale after broadening so that max peak is 100, matching stick view scale
|
|
180
255
|
return pattern_entries
|
|
181
|
-
|
|
182
|
-
const broadened = compute_broadened_pattern(
|
|
256
|
+
.map((entry, entry_idx) => {
|
|
257
|
+
const broadened = compute_broadened_pattern(
|
|
258
|
+
entry.pattern,
|
|
259
|
+
broadening_params,
|
|
260
|
+
angle_range,
|
|
261
|
+
)
|
|
262
|
+
|
|
183
263
|
// Normalize broadened profile relative to GLOBAL max intensity of stick patterns?
|
|
184
264
|
// Or normalize to 100 for *this* profile?
|
|
185
265
|
// Usually we want relative intensities between patterns to be preserved if possible.
|
|
@@ -187,91 +267,101 @@ const scatter_series = $derived.by(() => {
|
|
|
187
267
|
// If we normalize each profile to 100 independently, we lose relative scaling between patterns.
|
|
188
268
|
// If we normalize by global_max_intensity (from sticks), broadened peaks will be tiny.
|
|
189
269
|
// Let's normalize such that the highest peak across ALL broadened patterns is 100.
|
|
270
|
+
|
|
190
271
|
return {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
272
|
+
broadened,
|
|
273
|
+
entry,
|
|
274
|
+
entry_idx,
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
.map(({ broadened, entry, entry_idx }, _, all_processed) => {
|
|
197
278
|
// Find global max of all broadened patterns to normalize
|
|
198
|
-
const all_ys = all_processed.flatMap((
|
|
199
|
-
const max_y = Math.max(...all_ys, 1)
|
|
200
|
-
|
|
201
|
-
const
|
|
279
|
+
const all_ys = all_processed.flatMap((processed) => processed.broadened.y)
|
|
280
|
+
const max_y = Math.max(...all_ys, 1) // Avoid div by zero
|
|
281
|
+
|
|
282
|
+
const scale = (y: number) => (y / max_y) * 100
|
|
283
|
+
const base_color = entry.color ?? PLOT_COLORS[entry_idx % PLOT_COLORS.length]
|
|
202
284
|
// Add transparency when multiple series overlap
|
|
203
|
-
const alpha = all_processed.length > 1 ? 0.6 : 1
|
|
285
|
+
const alpha = all_processed.length > 1 ? 0.6 : 1
|
|
286
|
+
|
|
204
287
|
return {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
loading = true
|
|
221
|
-
error_msg = undefined
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
288
|
+
x: broadened.x,
|
|
289
|
+
y: broadened.y.map(scale),
|
|
290
|
+
label: include_name ? entry.label : ``,
|
|
291
|
+
color: add_alpha(base_color, alpha),
|
|
292
|
+
markers: `line`, // Only line for profile
|
|
293
|
+
line_style: { stroke_width: 2 },
|
|
294
|
+
visible: true,
|
|
295
|
+
} as DataSeries
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
async function handle_file_drop(event: DragEvent) {
|
|
300
|
+
event.preventDefault()
|
|
301
|
+
dragover = false
|
|
302
|
+
if (!allow_file_drop) return
|
|
303
|
+
loading = true
|
|
304
|
+
error_msg = undefined
|
|
305
|
+
|
|
306
|
+
const compute_and_add = async (
|
|
307
|
+
content: string | ArrayBuffer,
|
|
308
|
+
filename: string,
|
|
309
|
+
) => {
|
|
310
|
+
const result = await add_xrd_pattern(content, filename, wavelength)
|
|
311
|
+
if (result.error) {
|
|
312
|
+
error_msg = result.error
|
|
313
|
+
} else if (result.pattern) {
|
|
314
|
+
dropped_entries = [result.pattern, ...dropped_entries]
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
231
318
|
try {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
await (on_file_drop || compute_and_add)(buffer, output_name);
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
// Text-based formats (.xy, .xye, .xrdml) - decompress_file handles .gz
|
|
260
|
-
const { content, filename } = await decompress_file(file);
|
|
261
|
-
if (content)
|
|
262
|
-
await (on_file_drop || compute_and_add)(content, filename);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
catch (exc) {
|
|
266
|
-
error_msg = `Failed to load file ${file.name}: ${exc instanceof Error ? exc.message : String(exc)}`;
|
|
319
|
+
// Handle URL-based drops
|
|
320
|
+
const handled = await handle_url_drop(
|
|
321
|
+
event,
|
|
322
|
+
on_file_drop || compute_and_add,
|
|
323
|
+
).catch(() => false)
|
|
324
|
+
if (handled) return
|
|
325
|
+
|
|
326
|
+
const file = event.dataTransfer?.files?.[0]
|
|
327
|
+
if (file) {
|
|
328
|
+
try {
|
|
329
|
+
const lower_name = file.name.toLowerCase()
|
|
330
|
+
const compression_format = detect_compression_format(lower_name)
|
|
331
|
+
// Get base filename without compression extension
|
|
332
|
+
const base_name = compression_format
|
|
333
|
+
? lower_name.replace(/\.(gz|gzip)$/i, ``)
|
|
334
|
+
: lower_name
|
|
335
|
+
const base_ext = base_name.split(`.`).pop()
|
|
336
|
+
|
|
337
|
+
// Handle .brml files (ZIP archives) - both plain and gzipped
|
|
338
|
+
if (base_ext === `brml`) {
|
|
339
|
+
let buffer = await file.arrayBuffer()
|
|
340
|
+
// Decompress if gzipped
|
|
341
|
+
if (compression_format === `gzip`) {
|
|
342
|
+
buffer = await decompress_data_binary(buffer, `gzip`)
|
|
267
343
|
}
|
|
344
|
+
const output_name = base_name.endsWith(`.brml`)
|
|
345
|
+
? base_name
|
|
346
|
+
: file.name.replace(/\.gz$/i, ``)
|
|
347
|
+
await (on_file_drop || compute_and_add)(buffer, output_name)
|
|
348
|
+
} else {
|
|
349
|
+
// Text-based formats (.xy, .xye, .xrdml) - decompress_file handles .gz
|
|
350
|
+
const { content, filename } = await decompress_file(file)
|
|
351
|
+
if (content) await (on_file_drop || compute_and_add)(content, filename)
|
|
352
|
+
}
|
|
353
|
+
} catch (exc) {
|
|
354
|
+
error_msg = `Failed to load file ${file.name}: ${
|
|
355
|
+
exc instanceof Error ? exc.message : String(exc)
|
|
356
|
+
}`
|
|
268
357
|
}
|
|
358
|
+
}
|
|
359
|
+
} finally {
|
|
360
|
+
loading = false
|
|
269
361
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
274
|
-
const [angle_label, intensity_label] = [`2θ (degrees)`, `Intensity (a.u.)`];
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const [angle_label, intensity_label] = [`2θ (degrees)`, `Intensity (a.u.)`]
|
|
275
365
|
</script>
|
|
276
366
|
|
|
277
367
|
{#snippet broadening_controls_snippet()}
|
|
@@ -363,7 +453,7 @@ const [angle_label, intensity_label] = [`2θ (degrees)`, `Intensity (a.u.)`];
|
|
|
363
453
|
{#snippet tooltip(info: ScatterHandlerProps)}
|
|
364
454
|
{@const angle_text = `${format_value(info.x, `.2f`)}°`}
|
|
365
455
|
{@const intensity_text = `${format_value(info.y, `.1f`)}`}
|
|
366
|
-
{@html info.label ?? ``}<br />
|
|
456
|
+
{@html sanitize_html(info.label ?? ``)}<br />
|
|
367
457
|
2θ: {angle_text}<br />
|
|
368
458
|
Intensity: {intensity_text}
|
|
369
459
|
{/snippet}
|
|
@@ -410,7 +500,7 @@ const [angle_label, intensity_label] = [`2θ (degrees)`, `Intensity (a.u.)`];
|
|
|
410
500
|
? hkls.map((hkl: Hkl) => format_hkl(hkl, hkl_format)).join(`, `)
|
|
411
501
|
: ``}
|
|
412
502
|
{@const d_text = d != null ? `${format_value(d, `.3f`)} Å` : ``}
|
|
413
|
-
{@html info.metadata?.label ?? ``}<br />
|
|
503
|
+
{@html sanitize_html(info.metadata?.label ?? ``)}<br />
|
|
414
504
|
2θ: {angle_text}<br />
|
|
415
505
|
Intensity: {intensity_text}
|
|
416
506
|
{#if hkl_text}<br />hkl: {hkl_text}{/if}
|