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/math.js
CHANGED
|
@@ -14,6 +14,7 @@ export const LOG_EPS = 1e-9;
|
|
|
14
14
|
export const EPS = 1e-10;
|
|
15
15
|
export const RAD_TO_DEG = 180 / Math.PI;
|
|
16
16
|
export const DEG_TO_RAD = Math.PI / 180;
|
|
17
|
+
const MAX_MIN_IMAGE_CANDIDATES = 100_000;
|
|
17
18
|
export const to_degrees = (radians) => radians * RAD_TO_DEG;
|
|
18
19
|
export const to_radians = (degrees) => degrees * DEG_TO_RAD;
|
|
19
20
|
// Calculate all lattice parameters in a single efficient pass
|
|
@@ -44,21 +45,70 @@ export const euclidean_dist = (vec1, vec2) => {
|
|
|
44
45
|
}
|
|
45
46
|
return Math.hypot(...vec1.map((x, idx) => x - vec2[idx]));
|
|
46
47
|
};
|
|
48
|
+
const vec3_norm_sq = (vec) => vec[0] ** 2 + vec[1] ** 2 + vec[2] ** 2;
|
|
49
|
+
// Exact minimum-image displacement for row-vector lattices.
|
|
50
|
+
// Rounded fractional wrapping is only approximate for highly skewed cells, so
|
|
51
|
+
// we use it as a starting guess and then search the small set of shifts that
|
|
52
|
+
// can still beat that Cartesian radius.
|
|
53
|
+
export function min_image_displacement(from, to, lattice_matrix, converters, pbc = [true, true, true]) {
|
|
54
|
+
const { cart_to_frac, frac_to_cart, reciprocal_axis_norms } = converters ?? create_lattice_converters(lattice_matrix);
|
|
55
|
+
const frac_from = cart_to_frac(from);
|
|
56
|
+
const frac_to = cart_to_frac(to);
|
|
57
|
+
const frac_diff = [
|
|
58
|
+
frac_to[0] - frac_from[0],
|
|
59
|
+
frac_to[1] - frac_from[1],
|
|
60
|
+
frac_to[2] - frac_from[2],
|
|
61
|
+
];
|
|
62
|
+
const wrapped_frac_diff = [
|
|
63
|
+
pbc[0] ? frac_diff[0] - Math.round(frac_diff[0]) : frac_diff[0],
|
|
64
|
+
pbc[1] ? frac_diff[1] - Math.round(frac_diff[1]) : frac_diff[1],
|
|
65
|
+
pbc[2] ? frac_diff[2] - Math.round(frac_diff[2]) : frac_diff[2],
|
|
66
|
+
];
|
|
67
|
+
let best_displacement = frac_to_cart(wrapped_frac_diff);
|
|
68
|
+
let best_dist_sq = vec3_norm_sq(best_displacement);
|
|
69
|
+
const search_radius = Math.sqrt(best_dist_sq) + EPS;
|
|
70
|
+
const candidate_shift_ranges = [0, 1, 2].map((axis_idx) => {
|
|
71
|
+
if (!pbc[axis_idx])
|
|
72
|
+
return [0, 0];
|
|
73
|
+
const axis_bound = reciprocal_axis_norms[axis_idx] * search_radius;
|
|
74
|
+
return [
|
|
75
|
+
Math.ceil(-frac_diff[axis_idx] - axis_bound),
|
|
76
|
+
Math.floor(-frac_diff[axis_idx] + axis_bound),
|
|
77
|
+
];
|
|
78
|
+
});
|
|
79
|
+
let candidate_count = 1;
|
|
80
|
+
for (const [shift_min, shift_max] of candidate_shift_ranges) {
|
|
81
|
+
candidate_count *= shift_max - shift_min + 1;
|
|
82
|
+
if (candidate_count > MAX_MIN_IMAGE_CANDIDATES) {
|
|
83
|
+
throw new Error(`Minimum-image search would test >${MAX_MIN_IMAGE_CANDIDATES} candidates ` +
|
|
84
|
+
`for lattice ${JSON.stringify(lattice_matrix)}; reciprocal norms=` +
|
|
85
|
+
`${JSON.stringify(reciprocal_axis_norms)} ranges=${JSON.stringify(candidate_shift_ranges)}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const [[i_min, i_max], [j_min, j_max], [k_min, k_max]] = candidate_shift_ranges;
|
|
89
|
+
// Only test integer shifts that reciprocal-space bounds say could still win.
|
|
90
|
+
for (let ii = i_min; ii <= i_max; ii++) {
|
|
91
|
+
for (let jj = j_min; jj <= j_max; jj++) {
|
|
92
|
+
for (let kk = k_min; kk <= k_max; kk++) {
|
|
93
|
+
const candidate_frac_diff = [
|
|
94
|
+
frac_diff[0] + ii,
|
|
95
|
+
frac_diff[1] + jj,
|
|
96
|
+
frac_diff[2] + kk,
|
|
97
|
+
];
|
|
98
|
+
const candidate_displacement = frac_to_cart(candidate_frac_diff);
|
|
99
|
+
const candidate_dist_sq = vec3_norm_sq(candidate_displacement);
|
|
100
|
+
if (candidate_dist_sq < best_dist_sq) {
|
|
101
|
+
best_dist_sq = candidate_dist_sq;
|
|
102
|
+
best_displacement = candidate_displacement;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return best_displacement;
|
|
108
|
+
}
|
|
47
109
|
// Calculate the minimum distance between two points considering periodic boundary conditions.
|
|
48
|
-
export function pbc_dist(pos1,
|
|
49
|
-
pos2,
|
|
50
|
-
lattice_matrix, // 3x3 lattice matrix where each row is a lattice vector
|
|
51
|
-
lattice_inv, // Optional pre-computed inverse matrix for optimization (since lattice is usually constant and repeatedly inverting matrix is expensive)
|
|
52
|
-
pbc = [true, true, true]) {
|
|
53
|
-
const inv_matrix = lattice_inv ?? matrix_inverse_3x3(lattice_matrix);
|
|
54
|
-
// Convert Cartesian coordinates to fractional coordinates
|
|
55
|
-
const [fx1, fy1, fz1] = mat3x3_vec3_multiply(inv_matrix, pos1);
|
|
56
|
-
const [fx2, fy2, fz2] = mat3x3_vec3_multiply(inv_matrix, pos2);
|
|
57
|
-
// Apply minimum image convention only for periodic axes
|
|
58
|
-
const wrapped_frac_diff = [fx1 - fx2, fy1 - fy2, fz1 - fz2].map((diff, idx) => pbc[idx] ? diff - Math.round(diff) : diff);
|
|
59
|
-
// Convert back to Cartesian coordinates
|
|
60
|
-
const cart_diff = mat3x3_vec3_multiply(lattice_matrix, wrapped_frac_diff);
|
|
61
|
-
return Math.hypot(...cart_diff);
|
|
110
|
+
export function pbc_dist(pos1, pos2, lattice_matrix, converters, pbc = [true, true, true]) {
|
|
111
|
+
return Math.hypot(...min_image_displacement(pos1, pos2, lattice_matrix, converters, pbc));
|
|
62
112
|
}
|
|
63
113
|
export function matrix_inverse_3x3(matrix) {
|
|
64
114
|
const [[m11, m12, m13], [m21, m22, m23], [m31, m32, m33]] = matrix;
|
|
@@ -106,7 +156,7 @@ export function add(...vecs) {
|
|
|
106
156
|
throw new Error(`All vectors must have the same length`);
|
|
107
157
|
}
|
|
108
158
|
}
|
|
109
|
-
const result =
|
|
159
|
+
const result = Array.from({ length }).fill(0);
|
|
110
160
|
for (const vec of vecs) {
|
|
111
161
|
for (let idx = 0; idx < length; idx++) {
|
|
112
162
|
result[idx] += vec[idx];
|
|
@@ -192,7 +242,11 @@ export function from_voigt(voigt) {
|
|
|
192
242
|
throw new Error(`Expected 6-element Voigt vector, got ${voigt.length} elements`);
|
|
193
243
|
}
|
|
194
244
|
const [v1, v2, v3, v4, v5, v6] = voigt;
|
|
195
|
-
return [
|
|
245
|
+
return [
|
|
246
|
+
[v1, v6, v5],
|
|
247
|
+
[v6, v2, v4],
|
|
248
|
+
[v5, v4, v3],
|
|
249
|
+
];
|
|
196
250
|
}
|
|
197
251
|
// Convert flat 9-element array to 3x3 tensor (row-major order)
|
|
198
252
|
export function vec9_to_mat3x3(flat_array) {
|
|
@@ -200,7 +254,11 @@ export function vec9_to_mat3x3(flat_array) {
|
|
|
200
254
|
throw new Error(`Expected 9-element array, got ${flat_array.length} elements`);
|
|
201
255
|
}
|
|
202
256
|
const [a1, a2, a3, a4, a5, a6, a7, a8, a9] = flat_array;
|
|
203
|
-
return [
|
|
257
|
+
return [
|
|
258
|
+
[a1, a2, a3],
|
|
259
|
+
[a4, a5, a6],
|
|
260
|
+
[a7, a8, a9],
|
|
261
|
+
];
|
|
204
262
|
}
|
|
205
263
|
// Convert 3x3 tensor to flat 9-element array (row-major order)
|
|
206
264
|
export function tensor_to_flat_array(tensor) {
|
|
@@ -216,6 +274,18 @@ export const transpose_3x3_matrix = (matrix) => [
|
|
|
216
274
|
[matrix[0][1], matrix[1][1], matrix[2][1]],
|
|
217
275
|
[matrix[0][2], matrix[1][2], matrix[2][2]],
|
|
218
276
|
];
|
|
277
|
+
// Scale each row of a 3x3 matrix by the corresponding element of a Vec3.
|
|
278
|
+
// Used to scale lattice vectors by supercell factors.
|
|
279
|
+
export function scale_lattice_matrix(orig_matrix, scaling_factors) {
|
|
280
|
+
const [nx, ny, nz] = scaling_factors;
|
|
281
|
+
const [a, b, c] = orig_matrix;
|
|
282
|
+
return [
|
|
283
|
+
[a[0] * nx, a[1] * nx, a[2] * nx],
|
|
284
|
+
[b[0] * ny, b[1] * ny, b[2] * ny],
|
|
285
|
+
[c[0] * nz, c[1] * nz, c[2] * nz],
|
|
286
|
+
];
|
|
287
|
+
}
|
|
288
|
+
const create_cart_to_frac_matrix = (lattice) => matrix_inverse_3x3(transpose_3x3_matrix(lattice));
|
|
219
289
|
// Curried fractional→Cartesian converter (caches transposed matrix)
|
|
220
290
|
export const create_frac_to_cart = (lattice) => {
|
|
221
291
|
const transposed = transpose_3x3_matrix(lattice);
|
|
@@ -223,8 +293,16 @@ export const create_frac_to_cart = (lattice) => {
|
|
|
223
293
|
};
|
|
224
294
|
// Curried Cartesian→fractional converter (caches inverse transpose)
|
|
225
295
|
export const create_cart_to_frac = (lattice) => {
|
|
226
|
-
const
|
|
227
|
-
return (cart) => mat3x3_vec3_multiply(
|
|
296
|
+
const cart_to_frac_mat = create_cart_to_frac_matrix(lattice);
|
|
297
|
+
return (cart) => mat3x3_vec3_multiply(cart_to_frac_mat, cart);
|
|
298
|
+
};
|
|
299
|
+
export const create_lattice_converters = (lattice) => {
|
|
300
|
+
const cart_to_frac_mat = create_cart_to_frac_matrix(lattice);
|
|
301
|
+
return {
|
|
302
|
+
cart_to_frac: (cart) => mat3x3_vec3_multiply(cart_to_frac_mat, cart),
|
|
303
|
+
frac_to_cart: create_frac_to_cart(lattice),
|
|
304
|
+
reciprocal_axis_norms: cart_to_frac_mat.map((row) => Math.hypot(...row)),
|
|
305
|
+
};
|
|
228
306
|
};
|
|
229
307
|
// Convert unit cell parameters to lattice matrix (crystallographic convention)
|
|
230
308
|
export function cell_to_lattice_matrix(a, b, c, alpha, beta, gamma) {
|
|
@@ -237,11 +315,7 @@ export function cell_to_lattice_matrix(a, b, c, alpha, beta, gamma) {
|
|
|
237
315
|
const cos_gamma = Math.cos(gamma_rad);
|
|
238
316
|
const sin_gamma = Math.sin(gamma_rad);
|
|
239
317
|
// Calculate volume factor for triclinic system
|
|
240
|
-
const vol_factor = Math.sqrt(1 -
|
|
241
|
-
cos_alpha ** 2 -
|
|
242
|
-
cos_beta ** 2 -
|
|
243
|
-
cos_gamma ** 2 +
|
|
244
|
-
2 * cos_alpha * cos_beta * cos_gamma);
|
|
318
|
+
const vol_factor = Math.sqrt(1 - cos_alpha ** 2 - cos_beta ** 2 - cos_gamma ** 2 + 2 * cos_alpha * cos_beta * cos_gamma);
|
|
245
319
|
// Standard crystallographic lattice vectors
|
|
246
320
|
const c_x = c * cos_beta;
|
|
247
321
|
const c_y = (c * (cos_alpha - cos_beta * cos_gamma)) / sin_gamma;
|
|
@@ -256,7 +330,8 @@ export function det_3x3(matrix) {
|
|
|
256
330
|
// |A| = a(ei − fh) − b(di − fg) + c(dh − eg)
|
|
257
331
|
// where matrix = [[a, b, c], [d, e, f], [g, h, i]]
|
|
258
332
|
const [[m00, m01, m02], [m10, m11, m12], [m20, m21, m22]] = matrix;
|
|
259
|
-
return (m00 * (m11 * m22 - m12 * m21) -
|
|
333
|
+
return (m00 * (m11 * m22 - m12 * m21) -
|
|
334
|
+
m01 * (m10 * m22 - m12 * m20) +
|
|
260
335
|
m02 * (m10 * m21 - m11 * m20));
|
|
261
336
|
}
|
|
262
337
|
export function get_coefficient_of_variation(values) {
|
|
@@ -264,9 +339,7 @@ export function get_coefficient_of_variation(values) {
|
|
|
264
339
|
return 0;
|
|
265
340
|
const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
|
|
266
341
|
const variance = values.reduce((sum, val) => sum + (val - mean) ** 2, 0) / values.length;
|
|
267
|
-
return Math.abs(mean) > 1e-10
|
|
268
|
-
? Math.sqrt(variance) / Math.abs(mean)
|
|
269
|
-
: Math.sqrt(variance);
|
|
342
|
+
return Math.abs(mean) > 1e-10 ? Math.sqrt(variance) / Math.abs(mean) : Math.sqrt(variance);
|
|
270
343
|
}
|
|
271
344
|
// Compute 4x4 determinant (used for 4D barycentric coordinates)
|
|
272
345
|
export function det_4x4(matrix) {
|
|
@@ -275,42 +348,38 @@ export function det_4x4(matrix) {
|
|
|
275
348
|
const [b0, b1, b2, b3] = b_row;
|
|
276
349
|
const [c0, c1, c2, c3] = c_row;
|
|
277
350
|
const [d0, d1, d2, d3] = d_row;
|
|
278
|
-
return (a0 *
|
|
279
|
-
(
|
|
280
|
-
|
|
281
|
-
(b0 * (c2 * d3 - c3 * d2) - b2 * (c0 * d3 - c3 * d0) + b3 * (c0 * d2 - c2 * d0)) +
|
|
282
|
-
a2 *
|
|
283
|
-
(b0 * (c1 * d3 - c3 * d1) - b1 * (c0 * d3 - c3 * d0) + b3 * (c0 * d1 - c1 * d0)) -
|
|
351
|
+
return (a0 * (b1 * (c2 * d3 - c3 * d2) - b2 * (c1 * d3 - c3 * d1) + b3 * (c1 * d2 - c2 * d1)) -
|
|
352
|
+
a1 * (b0 * (c2 * d3 - c3 * d2) - b2 * (c0 * d3 - c3 * d0) + b3 * (c0 * d2 - c2 * d0)) +
|
|
353
|
+
a2 * (b0 * (c1 * d3 - c3 * d1) - b1 * (c0 * d3 - c3 * d0) + b3 * (c0 * d1 - c1 * d0)) -
|
|
284
354
|
a3 * (b0 * (c1 * d2 - c2 * d1) - b1 * (c0 * d2 - c2 * d0) + b2 * (c0 * d1 - c1 * d0)));
|
|
285
355
|
}
|
|
286
356
|
// Compute NxN determinant using LU decomposition with partial pivoting
|
|
287
357
|
// More numerically stable than cofactor expansion for N > 4
|
|
288
358
|
// Returns 0 for singular/near-singular matrices (pivot < EPS ≈ 1e-10)
|
|
289
359
|
export function det_nxn(matrix) {
|
|
290
|
-
const
|
|
291
|
-
if (
|
|
360
|
+
const mat_size = matrix.length;
|
|
361
|
+
if (mat_size === 0)
|
|
292
362
|
return 1;
|
|
293
|
-
if (!matrix.every((row) => row.length ===
|
|
363
|
+
if (!matrix.every((row) => row.length === mat_size)) {
|
|
294
364
|
throw new Error(`det_nxn requires a square matrix`);
|
|
295
365
|
}
|
|
296
366
|
// Fast paths for small matrices
|
|
297
|
-
if (
|
|
367
|
+
if (mat_size === 1)
|
|
298
368
|
return matrix[0][0];
|
|
299
|
-
if (
|
|
369
|
+
if (mat_size === 2)
|
|
300
370
|
return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
|
|
301
|
-
if (
|
|
371
|
+
if (mat_size === 3)
|
|
302
372
|
return det_3x3(matrix);
|
|
303
|
-
if (
|
|
373
|
+
if (mat_size === 4)
|
|
304
374
|
return det_4x4(matrix);
|
|
305
375
|
// LU decomposition with partial pivoting
|
|
306
376
|
// Create a working copy to avoid mutating input
|
|
307
377
|
const lu = matrix.map((row) => [...row]);
|
|
308
378
|
let swaps = 0;
|
|
309
|
-
for (let col = 0; col <
|
|
379
|
+
for (let col = 0; col < mat_size; col++) {
|
|
310
380
|
// Find pivot (largest absolute value in column)
|
|
311
|
-
let max_row = col;
|
|
312
|
-
let
|
|
313
|
-
for (let row = col + 1; row < n; row++) {
|
|
381
|
+
let [max_row, max_val] = [col, Math.abs(lu[col][col])];
|
|
382
|
+
for (let row = col + 1; row < mat_size; row++) {
|
|
314
383
|
const val = Math.abs(lu[row][col]);
|
|
315
384
|
if (val > max_val) {
|
|
316
385
|
max_val = val;
|
|
@@ -328,17 +397,17 @@ export function det_nxn(matrix) {
|
|
|
328
397
|
}
|
|
329
398
|
// Eliminate below pivot
|
|
330
399
|
const pivot = lu[col][col];
|
|
331
|
-
for (let row = col + 1; row <
|
|
400
|
+
for (let row = col + 1; row < mat_size; row++) {
|
|
332
401
|
const factor = lu[row][col] / pivot;
|
|
333
402
|
lu[row][col] = 0;
|
|
334
|
-
for (let k = col + 1; k <
|
|
403
|
+
for (let k = col + 1; k < mat_size; k++) {
|
|
335
404
|
lu[row][k] -= factor * lu[col][k];
|
|
336
405
|
}
|
|
337
406
|
}
|
|
338
407
|
}
|
|
339
408
|
// Determinant is product of diagonal elements × (-1)^swaps
|
|
340
409
|
let det = swaps % 2 === 0 ? 1 : -1;
|
|
341
|
-
for (let idx = 0; idx <
|
|
410
|
+
for (let idx = 0; idx < mat_size; idx++) {
|
|
342
411
|
det *= lu[idx][idx];
|
|
343
412
|
}
|
|
344
413
|
return det;
|
|
@@ -370,8 +439,12 @@ export const centered_frac = (val) => {
|
|
|
370
439
|
};
|
|
371
440
|
// Element-wise equality check for two optional Vec3s.
|
|
372
441
|
// Returns true if both are the same reference, or both are defined with equal components.
|
|
373
|
-
export const vecs_equal = (vec_a, vec_b) => vec_a === vec_b ||
|
|
374
|
-
vec_a
|
|
442
|
+
export const vecs_equal = (vec_a, vec_b) => vec_a === vec_b ||
|
|
443
|
+
(!!vec_a &&
|
|
444
|
+
!!vec_b &&
|
|
445
|
+
vec_a[0] === vec_b[0] &&
|
|
446
|
+
vec_a[1] === vec_b[1] &&
|
|
447
|
+
vec_a[2] === vec_b[2]);
|
|
375
448
|
// Normalize a Vec3 to unit length, returns zero vector if input is zero
|
|
376
449
|
export function normalize_vec3(vec, fallback) {
|
|
377
450
|
const len = Math.hypot(vec[0], vec[1], vec[2]);
|
|
@@ -393,7 +466,7 @@ export function compute_in_plane_basis(normal) {
|
|
|
393
466
|
];
|
|
394
467
|
const u_vec = normalize_vec3(u_raw, [0, 1, 0]);
|
|
395
468
|
const v_vec = cross_3d(normal, u_vec);
|
|
396
|
-
return [u_vec, v_vec];
|
|
469
|
+
return [u_vec, v_vec]; // u, v basis vectors
|
|
397
470
|
}
|
|
398
471
|
// Check whether N 3D points all lie on the same plane within tolerance.
|
|
399
472
|
// Fewer than 3 points are trivially coplanar.
|
|
@@ -539,8 +612,7 @@ export function merge_coplanar_triangles(positions, tolerance = 1e-4) {
|
|
|
539
612
|
if (pa.degenerate || pb.degenerate)
|
|
540
613
|
continue;
|
|
541
614
|
// Check coplanarity: same canonical normal direction AND same plane distance
|
|
542
|
-
const normal_dot = pa.normal[0] * pb.normal[0] + pa.normal[1] * pb.normal[1] +
|
|
543
|
-
pa.normal[2] * pb.normal[2];
|
|
615
|
+
const normal_dot = pa.normal[0] * pb.normal[0] + pa.normal[1] * pb.normal[1] + pa.normal[2] * pb.normal[2];
|
|
544
616
|
if (Math.abs(normal_dot) < 1 - tolerance)
|
|
545
617
|
continue;
|
|
546
618
|
if (Math.abs(pa.plane_d - pb.plane_d) > tolerance)
|
|
@@ -591,7 +663,7 @@ export function merge_coplanar_triangles(positions, tolerance = 1e-4) {
|
|
|
591
663
|
}
|
|
592
664
|
// Project to 2D using in-plane basis
|
|
593
665
|
const [u_vec, v_vec] = compute_in_plane_basis(normal);
|
|
594
|
-
const pts_2d = unique_verts.map((
|
|
666
|
+
const pts_2d = unique_verts.map((vertex) => [dot(u_vec, vertex), dot(v_vec, vertex)]);
|
|
595
667
|
const hull = convex_hull_2d(pts_2d);
|
|
596
668
|
if (hull.length < 3) {
|
|
597
669
|
emit_original(members);
|
|
@@ -661,7 +733,7 @@ export function point_in_polygon(point_x, point_y, vertices) {
|
|
|
661
733
|
const [x_i, y_i] = vertices[idx];
|
|
662
734
|
const [x_j, y_j] = vertices[prev_idx];
|
|
663
735
|
// Check if horizontal ray from point crosses this edge
|
|
664
|
-
if (y_i !== y_j &&
|
|
736
|
+
if (y_i !== y_j && y_i > point_y !== y_j > point_y) {
|
|
665
737
|
const x_intersect = ((x_j - x_i) * (point_y - y_i)) / (y_j - y_i) + x_i;
|
|
666
738
|
if (point_x < x_intersect)
|
|
667
739
|
inside = !inside;
|
|
@@ -733,10 +805,7 @@ b) {
|
|
|
733
805
|
const det = A[0][0] * A[1][1] - A[0][1] * A[1][0];
|
|
734
806
|
if (Math.abs(det) < EPS)
|
|
735
807
|
return null;
|
|
736
|
-
return [
|
|
737
|
-
(b[0] * A[1][1] - b[1] * A[0][1]) / det,
|
|
738
|
-
(A[0][0] * b[1] - A[1][0] * b[0]) / det,
|
|
739
|
-
];
|
|
808
|
+
return [(b[0] * A[1][1] - b[1] * A[0][1]) / det, (A[0][0] * b[1] - A[1][0] * b[0]) / det];
|
|
740
809
|
}
|
|
741
810
|
// 3x3 fast path via matrix inverse
|
|
742
811
|
if (n === 3) {
|
|
@@ -751,14 +820,11 @@ b) {
|
|
|
751
820
|
const perm = Array.from({ length: n }, (_, idx) => idx);
|
|
752
821
|
for (let col = 0; col < n; col++) {
|
|
753
822
|
// Find pivot
|
|
754
|
-
let max_row = col;
|
|
755
|
-
let max_val = Math.abs(lu[col][col]);
|
|
823
|
+
let [max_row, max_val] = [col, Math.abs(lu[col][col])];
|
|
756
824
|
for (let row = col + 1; row < n; row++) {
|
|
757
825
|
const val = Math.abs(lu[row][col]);
|
|
758
|
-
if (val > max_val)
|
|
759
|
-
max_val = val;
|
|
760
|
-
max_row = row;
|
|
761
|
-
}
|
|
826
|
+
if (val > max_val)
|
|
827
|
+
[max_val, max_row] = [val, row];
|
|
762
828
|
}
|
|
763
829
|
if (max_val < EPS)
|
|
764
830
|
return null; // singular
|
|
@@ -787,7 +853,7 @@ b) {
|
|
|
787
853
|
}
|
|
788
854
|
}
|
|
789
855
|
// Back substitution (Ux = y)
|
|
790
|
-
const x =
|
|
856
|
+
const x = Array.from({ length: n }).fill(0);
|
|
791
857
|
for (let row = n - 1; row >= 0; row--) {
|
|
792
858
|
let sum = pb[row];
|
|
793
859
|
for (let col = row + 1; col < n; col++) {
|
|
@@ -802,7 +868,7 @@ b) {
|
|
|
802
868
|
export function convex_hull_2d(points) {
|
|
803
869
|
if (points.length < 3)
|
|
804
870
|
return [...points];
|
|
805
|
-
const sorted =
|
|
871
|
+
const sorted = points.toSorted((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
806
872
|
const cross = (o, a, b) => (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
|
|
807
873
|
// Lower hull
|
|
808
874
|
const lower = [];
|
|
@@ -1,47 +1,72 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '../Icon.svelte'
|
|
3
|
+
import type { IconName } from '../icons'
|
|
4
|
+
import type { HTMLAttributes } from 'svelte/elements'
|
|
5
|
+
|
|
6
|
+
interface MenuOption {
|
|
7
|
+
value: string
|
|
8
|
+
icon?: string
|
|
9
|
+
label?: string
|
|
10
|
+
disabled?: boolean
|
|
11
|
+
}
|
|
12
|
+
let {
|
|
13
|
+
sections,
|
|
14
|
+
selected_values = {},
|
|
15
|
+
on_select,
|
|
16
|
+
position,
|
|
17
|
+
visible,
|
|
18
|
+
on_close,
|
|
19
|
+
menu_element,
|
|
20
|
+
...rest
|
|
21
|
+
}: HTMLAttributes<HTMLDivElement> & {
|
|
22
|
+
sections: Readonly<{ title: string; options: readonly MenuOption[] }[]>
|
|
23
|
+
selected_values?: Record<string, string>
|
|
24
|
+
on_select?: (section_title: string, option: MenuOption) => void
|
|
25
|
+
position: { x: number; y: number }
|
|
26
|
+
visible: boolean
|
|
27
|
+
on_close?: () => void
|
|
28
|
+
menu_element?: HTMLDivElement
|
|
29
|
+
} = $props()
|
|
30
|
+
|
|
31
|
+
// Calculate smart position that keeps menu in viewport
|
|
32
|
+
function get_smart_position() {
|
|
33
|
+
if (!menu_element) return position
|
|
34
|
+
const rect = menu_element.getBoundingClientRect()
|
|
35
|
+
let { x, y } = position
|
|
36
|
+
if (x + rect.width > window.innerWidth) x = position.x - rect.width
|
|
37
|
+
if (y + rect.height > window.innerHeight) y = position.y - rect.height
|
|
38
|
+
return { x: Math.max(0, x), y: Math.max(0, y) }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Handle click outside to close
|
|
42
|
+
function handle_click_outside(event: MouseEvent) {
|
|
43
|
+
const target = event.target
|
|
18
44
|
if (target instanceof Element && visible) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
on_close?.();
|
|
45
|
+
const menu = target.closest(`.context-menu`)
|
|
46
|
+
if (!menu) on_close?.()
|
|
22
47
|
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const target = event.target
|
|
29
|
-
const menu = target instanceof Element ? target.closest(`.context-menu`) : null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Handle right-click outside to close
|
|
51
|
+
function handle_right_click_outside(event: MouseEvent) {
|
|
52
|
+
if (!visible) return
|
|
53
|
+
const target = event.target
|
|
54
|
+
const menu = target instanceof Element ? target.closest(`.context-menu`) : null
|
|
30
55
|
if (!menu) {
|
|
31
|
-
|
|
32
|
-
|
|
56
|
+
event.preventDefault()
|
|
57
|
+
on_close?.()
|
|
33
58
|
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Handle keyboard shortcuts
|
|
62
|
+
function handle_keydown(event: KeyboardEvent) {
|
|
63
|
+
if (event.key === `Escape` && visible) on_close?.()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle option selection
|
|
67
|
+
function handle_option_click(section_title: string, option: MenuOption) {
|
|
68
|
+
if (!option.disabled) on_select?.(section_title, option)
|
|
69
|
+
}
|
|
45
70
|
</script>
|
|
46
71
|
|
|
47
72
|
<svelte:document
|