matterviz 0.4.2 → 0.4.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/FilePicker.svelte +40 -50
- package/dist/api/optimade.js +10 -7
- package/dist/app.css +15 -0
- package/dist/brillouin/BrillouinZone.svelte +18 -9
- package/dist/brillouin/BrillouinZoneScene.svelte +20 -29
- package/dist/brillouin/compute.js +10 -16
- package/dist/brillouin/geometry.js +2 -2
- package/dist/chempot-diagram/ChemPotDiagram.svelte +18 -15
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +17 -113
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +104 -264
- package/dist/chempot-diagram/async-compute.svelte.js +12 -13
- package/dist/chempot-diagram/color.d.ts +21 -1
- package/dist/chempot-diagram/color.js +70 -1
- package/dist/chempot-diagram/compute.d.ts +7 -0
- package/dist/chempot-diagram/compute.js +85 -26
- package/dist/chempot-diagram/export.js +1 -6
- package/dist/chempot-diagram/temperature.js +9 -10
- package/dist/composition/BarChart.svelte +7 -9
- package/dist/composition/Composition.svelte +3 -8
- package/dist/composition/Formula.svelte +2 -2
- package/dist/composition/FormulaFilter.svelte +27 -93
- package/dist/composition/format.js +6 -8
- package/dist/composition/parse.js +50 -105
- package/dist/constants.d.ts +0 -1
- package/dist/constants.js +6 -17
- package/dist/convex-hull/ConvexHull2D.svelte +0 -2
- package/dist/convex-hull/ConvexHull3D.svelte +19 -47
- package/dist/convex-hull/ConvexHull4D.svelte +65 -95
- package/dist/convex-hull/StructurePopup.svelte +1 -1
- package/dist/convex-hull/gas-thermodynamics.js +21 -53
- package/dist/convex-hull/helpers.js +1 -7
- package/dist/convex-hull/thermodynamics.js +19 -27
- package/dist/coordination/CoordinationBarPlot.svelte +12 -21
- package/dist/element/BohrAtom.svelte +0 -1
- package/dist/element/ElementTile.svelte +26 -44
- package/dist/element/data.d.ts +2 -0
- package/dist/element/data.js +5 -1
- package/dist/element/index.d.ts +1 -1
- package/dist/element/index.js +1 -1
- package/dist/fermi-surface/FermiSurface.svelte +22 -23
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +2 -6
- package/dist/fermi-surface/FermiSurfaceScene.svelte +32 -77
- package/dist/fermi-surface/compute.js +22 -55
- package/dist/fermi-surface/parse.js +38 -70
- package/dist/file-viewer/JsonBrowser.svelte +1043 -0
- package/dist/file-viewer/JsonBrowser.svelte.d.ts +9 -0
- package/dist/file-viewer/PlotPanel.svelte +350 -0
- package/dist/file-viewer/PlotPanel.svelte.d.ts +9 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte +30 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte.d.ts +10 -0
- package/dist/file-viewer/detect.d.ts +11 -0
- package/dist/file-viewer/detect.js +381 -0
- package/dist/file-viewer/eligibility.d.ts +4 -0
- package/dist/file-viewer/eligibility.js +53 -0
- package/dist/file-viewer/host-protocol.d.ts +50 -0
- package/dist/file-viewer/host-transfer.d.ts +25 -0
- package/dist/file-viewer/host-transfer.js +42 -0
- package/dist/file-viewer/index.d.ts +5 -0
- package/dist/file-viewer/index.js +5 -0
- package/dist/file-viewer/main.d.ts +44 -0
- package/dist/file-viewer/main.js +535 -0
- package/dist/file-viewer/parse.d.ts +9 -0
- package/dist/file-viewer/parse.js +170 -0
- package/dist/file-viewer/plot-utils.d.ts +24 -0
- package/dist/file-viewer/plot-utils.js +191 -0
- package/dist/file-viewer/types.d.ts +4 -0
- package/dist/file-viewer/types.js +5 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +57 -84
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/io/ExportPane.svelte +1 -1
- package/dist/io/decompress.d.ts +2 -0
- package/dist/io/decompress.js +3 -2
- package/dist/io/export.d.ts +7 -4
- package/dist/io/export.js +70 -18
- package/dist/io/file-drop.js +60 -35
- package/dist/io/url-drop.d.ts +2 -0
- package/dist/io/url-drop.js +28 -16
- package/dist/isosurface/Isosurface.svelte +687 -196
- package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
- package/dist/isosurface/IsosurfaceControls.svelte +564 -85
- package/dist/isosurface/IsosurfaceControls.svelte.d.ts +1 -1
- package/dist/isosurface/VolumeSlice.svelte +224 -0
- package/dist/isosurface/VolumeSlice.svelte.d.ts +28 -0
- package/dist/isosurface/coloring.d.ts +20 -0
- package/dist/isosurface/coloring.js +118 -0
- package/dist/isosurface/geometry-worker-types.d.ts +38 -0
- package/dist/isosurface/geometry.worker.d.ts +1 -0
- package/dist/isosurface/geometry.worker.js +59 -0
- package/dist/isosurface/grid.d.ts +10 -0
- package/dist/isosurface/grid.js +31 -0
- package/dist/isosurface/index.d.ts +4 -0
- package/dist/isosurface/index.js +4 -0
- package/dist/isosurface/profile.d.ts +12 -0
- package/dist/isosurface/profile.js +19 -0
- package/dist/isosurface/sampling.d.ts +34 -0
- package/dist/isosurface/sampling.js +408 -0
- package/dist/isosurface/slice-rendering.d.ts +13 -0
- package/dist/isosurface/slice-rendering.js +79 -0
- package/dist/isosurface/slice.d.ts +22 -2
- package/dist/isosurface/slice.js +200 -116
- package/dist/isosurface/types.d.ts +24 -1
- package/dist/isosurface/types.js +120 -8
- package/dist/json-path.d.ts +4 -0
- package/dist/json-path.js +89 -0
- package/dist/labels.js +1 -1
- package/dist/layout/InfoCard.svelte +1 -3
- package/dist/layout/SettingsSection.svelte +52 -61
- package/dist/layout/SettingsSection.svelte.d.ts +6 -7
- package/dist/layout/ViewerChrome.svelte +7 -1
- package/dist/layout/json-tree/JsonNode.svelte +14 -48
- package/dist/layout/json-tree/JsonTree.svelte +48 -95
- package/dist/layout/json-tree/JsonValue.svelte +3 -18
- package/dist/layout/json-tree/utils.d.ts +0 -3
- package/dist/layout/json-tree/utils.js +50 -179
- package/dist/marching-cubes.d.ts +8 -1
- package/dist/marching-cubes.js +115 -48
- package/dist/math.d.ts +3 -2
- package/dist/math.js +29 -38
- package/dist/overlays/ContextMenu.svelte +15 -32
- package/dist/overlays/DraggablePane.svelte +39 -58
- package/dist/overlays/DraggablePane.svelte.d.ts +1 -1
- package/dist/overlays/GlassChip.svelte +3 -1
- package/dist/periodic-table/PeriodicTableControls.svelte +26 -52
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +18 -34
- package/dist/phase-diagram/build-diagram.js +1 -6
- package/dist/phase-diagram/svg-to-diagram.js +9 -14
- package/dist/plot/bar/BarPlot.svelte +38 -42
- package/dist/plot/bar/SpacegroupBarPlot.svelte +18 -42
- package/dist/plot/box/BoxPlot.svelte +27 -23
- package/dist/plot/core/auto-place.d.ts +1 -1
- package/dist/plot/core/auto-place.js +1 -1
- package/dist/plot/core/data-transform.d.ts +1 -2
- package/dist/plot/core/data-transform.js +0 -10
- package/dist/plot/core/layout.d.ts +12 -5
- package/dist/plot/core/layout.js +37 -27
- package/dist/plot/core/pan-zoom.svelte.d.ts +2 -4
- package/dist/plot/core/pan-zoom.svelte.js +34 -17
- package/dist/plot/core/placed-tween.svelte.d.ts +3 -0
- package/dist/plot/core/placed-tween.svelte.js +68 -21
- package/dist/plot/core/svg.js +0 -1
- package/dist/plot/core/utils/hierarchy-chart.d.ts +1 -0
- package/dist/plot/core/utils/hierarchy-chart.js +12 -0
- package/dist/plot/core/utils/hierarchy-labels.js +3 -2
- package/dist/plot/histogram/Histogram.svelte +21 -26
- package/dist/plot/sankey/sankey.js +13 -9
- package/dist/plot/scatter/BinnedScatterPlot.svelte +59 -43
- package/dist/plot/scatter/ScatterPlot.svelte +67 -52
- package/dist/plot/scatter/adaptive-density.d.ts +1 -1
- package/dist/plot/scatter/adaptive-density.js +36 -20
- package/dist/plot/sunburst/Sunburst.svelte +20 -20
- package/dist/plot/treemap/Treemap.svelte +175 -60
- package/dist/plot/treemap/Treemap.svelte.d.ts +6 -0
- package/dist/plot/treemap/index.d.ts +1 -0
- package/dist/plot/treemap/labels.d.ts +38 -0
- package/dist/plot/treemap/labels.js +80 -0
- package/dist/plot/treemap/treemap.d.ts +4 -2
- package/dist/plot/treemap/treemap.js +5 -2
- package/dist/rdf/RdfPlot.svelte +17 -35
- package/dist/rdf/calc-rdf.js +59 -44
- package/dist/rdf/index.d.ts +0 -1
- package/dist/sanitize.js +84 -26
- package/dist/scene/SceneCamera.svelte +1 -1
- package/dist/scene/SceneCamera.svelte.d.ts +1 -1
- package/dist/scene/index.d.ts +1 -2
- package/dist/scene/index.js +1 -1
- package/dist/scene/{props.js → props.svelte.js} +15 -1
- package/dist/settings.js +159 -482
- package/dist/spectral/Bands.svelte +151 -158
- package/dist/spectral/BrillouinBandsDos.svelte +4 -19
- package/dist/spectral/Dos.svelte +26 -24
- package/dist/spectral/helpers.js +40 -105
- package/dist/structure/AtomLegend.svelte +8 -13
- package/dist/structure/AtomLegend.svelte.d.ts +2 -3
- package/dist/structure/CellSelect.svelte +35 -10
- package/dist/structure/Structure.svelte +177 -99
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureCarousel.svelte +10 -3
- package/dist/structure/StructureControls.svelte +8 -26
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +65 -76
- package/dist/structure/StructureScene.svelte +75 -109
- package/dist/structure/StructureScene.svelte.d.ts +3 -1
- package/dist/structure/StructureViewport.svelte +120 -55
- package/dist/structure/StructureViewport.svelte.d.ts +3 -1
- package/dist/structure/atom-properties.d.ts +1 -1
- package/dist/structure/atom-properties.js +4 -3
- package/dist/structure/bond-order-perception.js +3 -8
- package/dist/structure/bonding.d.ts +0 -1
- package/dist/structure/bonding.js +16 -23
- package/dist/structure/export.js +50 -74
- package/dist/structure/format-detect.js +5 -7
- package/dist/structure/index.js +6 -12
- package/dist/structure/parse.js +38 -100
- package/dist/structure/partial-occupancy.js +7 -4
- package/dist/structure/pbc.js +8 -10
- package/dist/structure/polyhedra.js +5 -4
- package/dist/structure/prop-groups.d.ts +85 -0
- package/dist/structure/prop-groups.js +18 -0
- package/dist/symmetry/SymmetryStats.svelte +0 -4
- package/dist/symmetry/WyckoffTable.svelte +5 -4
- package/dist/symmetry/cell-transform.js +5 -14
- package/dist/symmetry/index.js +6 -9
- package/dist/table/HeatmapTable.svelte +61 -103
- package/dist/table/ToggleMenu.svelte +166 -44
- package/dist/table/ToggleMenu.svelte.d.ts +1 -1
- package/dist/theme/embedded.d.ts +18 -0
- package/dist/theme/embedded.js +206 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +1 -2
- package/dist/theme/themes.mjs +31 -60
- package/dist/tooltip/TooltipContent.svelte +1 -1
- package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
- package/dist/tooltip/index.d.ts +8 -1
- package/dist/trajectory/Trajectory.svelte +140 -120
- package/dist/trajectory/TrajectoryError.svelte +1 -66
- package/dist/trajectory/TrajectoryExportPane.svelte +0 -1
- package/dist/trajectory/TrajectoryInfoPane.svelte +55 -80
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
- package/dist/trajectory/extract.js +19 -29
- package/dist/trajectory/format-detect.d.ts +2 -0
- package/dist/trajectory/format-detect.js +7 -5
- package/dist/trajectory/frame-reader.js +3 -4
- package/dist/trajectory/helpers.js +16 -49
- package/dist/trajectory/index.js +2 -8
- package/dist/trajectory/parse/ase.js +1 -1
- package/dist/trajectory/parse/index.d.ts +4 -3
- package/dist/trajectory/parse/index.js +12 -17
- package/dist/trajectory/parse/vaspout-h5.d.ts +0 -1
- package/dist/trajectory/parse/vaspout-h5.js +1 -4
- package/dist/trajectory/parse/xyz.js +48 -2
- package/dist/trajectory/plotting.js +14 -37
- package/dist/xrd/XrdPlot.svelte +43 -68
- package/dist/xrd/calc-xrd.js +22 -58
- package/dist/xrd/parse.js +13 -22
- package/package.json +40 -8
- package/readme.md +6 -5
- package/dist/scene/types.d.ts +0 -26
- package/dist/tooltip/types.d.ts +0 -8
- package/dist/trajectory/constants.d.ts +0 -6
- package/dist/trajectory/constants.js +0 -11
- /package/dist/{scene/types.js → file-viewer/host-protocol.js} +0 -0
- /package/dist/{tooltip/types.js → isosurface/geometry-worker-types.js} +0 -0
package/dist/rdf/calc-rdf.js
CHANGED
|
@@ -1,61 +1,77 @@
|
|
|
1
1
|
import { calc_lattice_params, create_lattice_converters, euclidean_dist, pbc_dist, } from '../math';
|
|
2
|
-
import { make_supercell } from '../structure/supercell';
|
|
3
2
|
const get_occu = (site, elem) => elem ? (site.species.find((spec) => spec.element === elem)?.occu ?? 0) : 1;
|
|
4
3
|
const has_species = (site, elem) => !elem || site.species.some((spec) => spec.element === elem);
|
|
5
4
|
const sum_occu = (sites, elem) => sites.reduce((sum, site) => sum + get_occu(site, elem), 0);
|
|
5
|
+
// Symmetric ± lattice images on expanded PBC axes. Extents use reciprocal-axis norms
|
|
6
|
+
// with search radius cutoff + cell diagonal (length-based ceil undercounts skewed cells).
|
|
7
|
+
function build_rdf_neighbor_sites(structure, pbc, cutoff, auto_expand) {
|
|
8
|
+
const dist_lattice = structure.lattice.matrix;
|
|
9
|
+
if (!auto_expand)
|
|
10
|
+
return { sites: structure.sites, dist_pbc: pbc, dist_lattice };
|
|
11
|
+
const [[ax, ay, az], [bx, by, bz], [cx, cy, cz]] = dist_lattice;
|
|
12
|
+
let cell_diag = 0;
|
|
13
|
+
for (let bits = 0; bits < 8; bits++) {
|
|
14
|
+
const [ua, ub, uc] = [bits & 1, (bits >> 1) & 1, (bits >> 2) & 1];
|
|
15
|
+
cell_diag = Math.max(cell_diag, Math.hypot(ua * ax + ub * bx + uc * cx, ua * ay + ub * by + uc * cy, ua * az + ub * bz + uc * cz));
|
|
16
|
+
}
|
|
17
|
+
const { reciprocal_axis_norms } = create_lattice_converters(dist_lattice);
|
|
18
|
+
const search_radius = cutoff + cell_diag;
|
|
19
|
+
const extents = [0, 1, 2].map((axis) => pbc[axis]
|
|
20
|
+
? Math.max(0, Math.ceil(search_radius * reciprocal_axis_norms[axis] - 1e-12))
|
|
21
|
+
: 0);
|
|
22
|
+
if (extents.every((extent) => extent === 0)) {
|
|
23
|
+
return { sites: structure.sites, dist_pbc: pbc, dist_lattice };
|
|
24
|
+
}
|
|
25
|
+
const sites = [];
|
|
26
|
+
for (let ia = -extents[0]; ia <= extents[0]; ia++) {
|
|
27
|
+
for (let ib = -extents[1]; ib <= extents[1]; ib++) {
|
|
28
|
+
for (let ic = -extents[2]; ic <= extents[2]; ic++) {
|
|
29
|
+
const tx = ia * ax + ib * bx + ic * cx;
|
|
30
|
+
const ty = ia * ay + ib * by + ic * cy;
|
|
31
|
+
const tz = ia * az + ib * bz + ic * cz;
|
|
32
|
+
for (const site of structure.sites) {
|
|
33
|
+
sites.push({ ...site, xyz: [site.xyz[0] + tx, site.xyz[1] + ty, site.xyz[2] + tz] });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const dist_pbc = [
|
|
39
|
+
pbc[0] && extents[0] === 0,
|
|
40
|
+
pbc[1] && extents[1] === 0,
|
|
41
|
+
pbc[2] && extents[2] === 0,
|
|
42
|
+
];
|
|
43
|
+
return { sites, dist_pbc, dist_lattice };
|
|
44
|
+
}
|
|
6
45
|
// Calculate radial distribution function
|
|
7
46
|
export function calculate_rdf(structure, options = {}) {
|
|
8
|
-
const { center_species, neighbor_species, cutoff = 15, n_bins = 75, auto_expand = true,
|
|
9
|
-
const
|
|
47
|
+
const { center_species, neighbor_species, cutoff = 15, n_bins = 75, auto_expand = true, } = options;
|
|
48
|
+
const pbc = options.pbc ?? structure.lattice?.pbc ?? [true, true, true];
|
|
10
49
|
if (cutoff <= 0 || n_bins <= 0) {
|
|
11
50
|
throw new Error(`cutoff and n_bins must be positive`);
|
|
12
51
|
}
|
|
13
|
-
// Validate structure has lattice
|
|
14
52
|
if (!structure.lattice?.matrix) {
|
|
15
53
|
throw new Error(`Crystal must have a lattice for RDF calculation`);
|
|
16
54
|
}
|
|
17
|
-
|
|
18
|
-
let { sites } = structure;
|
|
19
|
-
let center_sites = sites;
|
|
20
|
-
// Expand structure if needed to ensure shortest lattice vector is expansion_factor× the cutoff
|
|
21
|
-
// This prevents artificial close contacts at cell boundaries when using PBC
|
|
22
|
-
// Standard practice uses 2.0-2.5× to eliminate finite-size effects
|
|
23
|
-
if (auto_expand) {
|
|
24
|
-
const { a, b, c } = calc_lattice_params(lattice);
|
|
25
|
-
const min_size = cutoff * expansion_factor;
|
|
26
|
-
const [n_a, n_b, n_c] = [a, b, c].map((len) => Math.ceil(min_size / len));
|
|
27
|
-
if (n_a > 1 || n_b > 1 || n_c > 1) {
|
|
28
|
-
const expanded_structure = make_supercell(structure, [n_a, n_b, n_c], false);
|
|
29
|
-
sites = expanded_structure.sites;
|
|
30
|
-
lattice = expanded_structure.lattice.matrix;
|
|
31
|
-
// Keep PBC: min-image is exact once every lattice vector ≥ 2× cutoff (disabling PBC
|
|
32
|
-
// starves boundary atoms and biases g(r) low). Under full PBC all periodic copies are
|
|
33
|
-
// equivalent, so restrict centers to the first copy (make_supercell emits (0,0,0) first)
|
|
34
|
-
center_sites = pbc.every(Boolean) ? sites.slice(0, structure.sites.length) : sites;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
55
|
+
const { sites: neighbor_sites, dist_pbc, dist_lattice, } = build_rdf_neighbor_sites(structure, pbc, cutoff, auto_expand);
|
|
37
56
|
const bin_size = cutoff / n_bins;
|
|
38
57
|
const r = Array.from({ length: n_bins }, (_, idx) => (idx + 0.5) * bin_size);
|
|
39
58
|
const g_r = Array(n_bins).fill(0);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
59
|
+
// Centers stay in the original cell; neighbor_sites may include periodic images
|
|
60
|
+
const centers = structure.sites.filter((site) => has_species(site, center_species));
|
|
61
|
+
const neighbors = neighbor_sites.filter((site) => has_species(site, neighbor_species));
|
|
62
|
+
// Normalization density uses the original cell (not the image cloud)
|
|
63
|
+
const norm_neighbors = structure.sites.filter((site) => has_species(site, neighbor_species));
|
|
45
64
|
const element_pair = center_species && neighbor_species
|
|
46
65
|
? [center_species, neighbor_species]
|
|
47
66
|
: undefined;
|
|
48
67
|
if (centers.length === 0 || neighbors.length === 0)
|
|
49
68
|
return { r, g_r, element_pair };
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
const converters = use_pbc ? create_lattice_converters(lattice) : undefined;
|
|
69
|
+
const use_pbc = dist_pbc.some(Boolean);
|
|
70
|
+
const converters = use_pbc ? create_lattice_converters(dist_lattice) : undefined;
|
|
53
71
|
for (const center of centers) {
|
|
54
72
|
for (const neighbor of neighbors) {
|
|
55
|
-
if (center === neighbor)
|
|
56
|
-
continue;
|
|
57
73
|
const dist = use_pbc
|
|
58
|
-
? pbc_dist(center.xyz, neighbor.xyz,
|
|
74
|
+
? pbc_dist(center.xyz, neighbor.xyz, dist_lattice, converters, dist_pbc)
|
|
59
75
|
: euclidean_dist(center.xyz, neighbor.xyz);
|
|
60
76
|
if (dist > 0 && dist < cutoff) {
|
|
61
77
|
// Weight by product of occupancies for the species pair
|
|
@@ -64,17 +80,16 @@ export function calculate_rdf(structure, options = {}) {
|
|
|
64
80
|
}
|
|
65
81
|
}
|
|
66
82
|
}
|
|
67
|
-
//
|
|
83
|
+
// Ideal-gas normalization with original-cell density. Do not subtract self-pairs:
|
|
84
|
+
// dist > 0 already drops the true self term, while periodic images of the same atom
|
|
85
|
+
// are valid neighbors (critical for 1-atom cells).
|
|
68
86
|
const center_weight = sum_occu(centers, center_species);
|
|
69
|
-
const neighbor_weight = sum_occu(
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
: 0;
|
|
73
|
-
const n_pairs = center_weight * neighbor_weight - self_weight;
|
|
74
|
-
if (n_pairs > 0) {
|
|
75
|
-
const volume = calc_lattice_params(lattice).volume;
|
|
87
|
+
const neighbor_weight = sum_occu(norm_neighbors, neighbor_species);
|
|
88
|
+
const volume = calc_lattice_params(structure.lattice.matrix).volume;
|
|
89
|
+
if (center_weight > 0 && neighbor_weight > 0 && volume > 0) {
|
|
76
90
|
for (let idx = 0; idx < n_bins; idx++) {
|
|
77
|
-
g_r[idx] /=
|
|
91
|
+
g_r[idx] /=
|
|
92
|
+
center_weight * neighbor_weight * ((4 * Math.PI * r[idx] ** 2 * bin_size) / volume);
|
|
78
93
|
}
|
|
79
94
|
}
|
|
80
95
|
return { r, g_r, element_pair };
|
package/dist/rdf/index.d.ts
CHANGED
package/dist/sanitize.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import DOMPurify from 'dompurify';
|
|
2
2
|
import { format_formula_html } from './phase-diagram/utils';
|
|
3
|
+
import { escape_html } from './utils';
|
|
3
4
|
const SAFE_TAGS = [`a`, `b`, `i`, `em`, `strong`, `sub`, `sup`, `br`, `span`, `code`, `small`];
|
|
4
5
|
const SAFE_ATTRS = [`style`, `class`, `title`, `href`, `target`, `rel`];
|
|
6
|
+
const SAFE_TAG_SET = new Set(SAFE_TAGS);
|
|
7
|
+
const SAFE_ATTR_SET = new Set(SAFE_ATTRS);
|
|
5
8
|
// only allow safe CSS properties for text formatting
|
|
6
9
|
const SAFE_STYLE_RE = /^\s*(?:color|font-weight|font-style|font-size|text-decoration|vertical-align)\s*:/;
|
|
7
|
-
// Add a token to a space-separated string if not already present
|
|
8
10
|
const ensure_token = (value, token) => {
|
|
9
11
|
const tokens = new Set(value.split(/\s+/).filter(Boolean));
|
|
10
12
|
tokens.add(token);
|
|
@@ -18,10 +20,8 @@ function get_purify() {
|
|
|
18
20
|
if (typeof globalThis.window === `undefined`)
|
|
19
21
|
return (purify = null);
|
|
20
22
|
const instance = DOMPurify();
|
|
21
|
-
if (typeof instance.sanitize !== `function`)
|
|
22
|
-
purify = null;
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
23
|
+
if (typeof instance.sanitize !== `function`)
|
|
24
|
+
return (purify = null);
|
|
25
25
|
purify = instance;
|
|
26
26
|
instance.addHook(`uponSanitizeAttribute`, (node, data) => {
|
|
27
27
|
if (data.attrName === `style`) {
|
|
@@ -35,21 +35,83 @@ function get_purify() {
|
|
|
35
35
|
if (data.attrName === `href`) {
|
|
36
36
|
node.setAttribute(`rel`, ensure_token(node.getAttribute(`rel`) ?? ``, `noopener`));
|
|
37
37
|
}
|
|
38
|
-
if (data.attrName === `rel`)
|
|
38
|
+
if (data.attrName === `rel`)
|
|
39
39
|
data.attrValue = ensure_token(data.attrValue, `noopener`);
|
|
40
|
-
}
|
|
41
40
|
});
|
|
42
41
|
return instance;
|
|
43
42
|
}
|
|
43
|
+
// Strip void / raw-text blocks that must not leak content when tags are removed
|
|
44
|
+
const DANGEROUS_BLOCK_RE = /<(?<block>script|style|iframe|object|embed|textarea|noscript|template)\b[^>]*>[\s\S]*?<\/\k<block>\s*>/gi;
|
|
45
|
+
const ATTR_RE = /(?<name>[^\s=]+)(?:\s*=\s*(?:"(?<dq>[^"]*)"|'(?<sq>[^']*)'|(?<bare>[^\s"'=<>`]+)))?/g;
|
|
46
|
+
const SAFE_HREF_RE = /^(?:\/|#|https?:|mailto:)/i;
|
|
47
|
+
function filter_attrs(attr_str, allowed, tag) {
|
|
48
|
+
let out = ``;
|
|
49
|
+
let rel = ``;
|
|
50
|
+
let has_href = false;
|
|
51
|
+
ATTR_RE.lastIndex = 0;
|
|
52
|
+
for (const match of attr_str.matchAll(ATTR_RE)) {
|
|
53
|
+
const name = (match.groups?.name ?? ``).toLowerCase();
|
|
54
|
+
if (name.startsWith(`on`) || !allowed.has(name))
|
|
55
|
+
continue;
|
|
56
|
+
const raw = match.groups?.dq ?? match.groups?.sq ?? match.groups?.bare ?? ``;
|
|
57
|
+
if (name === `rel`) {
|
|
58
|
+
rel = raw;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (name === `href`) {
|
|
62
|
+
if (!SAFE_HREF_RE.test(raw.trim()))
|
|
63
|
+
continue;
|
|
64
|
+
has_href = true;
|
|
65
|
+
}
|
|
66
|
+
else if (name === `style`) {
|
|
67
|
+
const rules = raw.split(`;`).filter((rule) => SAFE_STYLE_RE.test(rule));
|
|
68
|
+
if (rules.length === 0)
|
|
69
|
+
continue;
|
|
70
|
+
out += ` style="${escape_html(rules.join(`;`))}"`;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
out += ` ${name}="${escape_html(raw)}"`;
|
|
74
|
+
}
|
|
75
|
+
if (tag === `a` && (has_href || rel)) {
|
|
76
|
+
out += ` rel="${escape_html(ensure_token(rel, `noopener`))}"`;
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
// DOM-free allowlist sanitizer for SSR / no-window contexts. Never returns raw input.
|
|
81
|
+
function sanitize_allowlist_ssr(html, tags, attrs) {
|
|
82
|
+
const without_blocks = html
|
|
83
|
+
.replace(DANGEROUS_BLOCK_RE, ``)
|
|
84
|
+
.replaceAll(/<!--[\s\S]*?-->/g, ``);
|
|
85
|
+
const tag_re = /<\/?(?<tag>[A-Za-z][\w:-]*)\b(?<attrs>[^>]*)\/?>/g;
|
|
86
|
+
let out = ``;
|
|
87
|
+
let last = 0;
|
|
88
|
+
for (const match of without_blocks.matchAll(tag_re)) {
|
|
89
|
+
out += without_blocks.slice(last, match.index);
|
|
90
|
+
last = match.index + match[0].length;
|
|
91
|
+
const name = (match.groups?.tag ?? ``).toLowerCase();
|
|
92
|
+
if (!tags.has(name))
|
|
93
|
+
continue;
|
|
94
|
+
if (match[0].startsWith(`</`)) {
|
|
95
|
+
out += `</${name}>`;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const filtered = filter_attrs(match.groups?.attrs ?? ``, attrs, name);
|
|
99
|
+
out +=
|
|
100
|
+
match[0].endsWith(`/>`) || name === `br`
|
|
101
|
+
? `<${name}${filtered} />`
|
|
102
|
+
: `<${name}${filtered}>`;
|
|
103
|
+
}
|
|
104
|
+
return out + without_blocks.slice(last);
|
|
105
|
+
}
|
|
44
106
|
// Wrap in <svg>, sanitize with allowlist, then unwrap. Required because DOMPurify
|
|
45
107
|
// needs the <svg> parent to parse children in the SVG namespace.
|
|
46
|
-
function sanitize_svg_content(html,
|
|
108
|
+
function sanitize_svg_content(html, tags, attrs) {
|
|
47
109
|
const dp = get_purify();
|
|
48
110
|
if (!dp)
|
|
49
|
-
return html;
|
|
111
|
+
return sanitize_allowlist_ssr(html, tags, new Set(attrs));
|
|
50
112
|
const wrapped = dp.sanitize(`<svg>${html}</svg>`, {
|
|
51
|
-
ALLOWED_TAGS: [...
|
|
52
|
-
ALLOWED_ATTR:
|
|
113
|
+
ALLOWED_TAGS: [...tags, `svg`],
|
|
114
|
+
ALLOWED_ATTR: [...attrs],
|
|
53
115
|
});
|
|
54
116
|
const open_end = wrapped.indexOf(`>`);
|
|
55
117
|
const close_start = wrapped.lastIndexOf(`</svg>`);
|
|
@@ -75,10 +137,13 @@ const stringify_html_input = (html) => {
|
|
|
75
137
|
return ``;
|
|
76
138
|
}
|
|
77
139
|
};
|
|
78
|
-
// Memoize by input: two DOMPurify passes per call are costly when a component re-sanitizes
|
|
79
|
-
// many cells on every render (e.g. HeatmapTable). Sanitization is deterministic for the
|
|
80
|
-
// fixed config, so caching is output-identical.
|
|
81
140
|
const sanitize_cache = new Map();
|
|
141
|
+
const cache_sanitize = (key, result) => {
|
|
142
|
+
if (sanitize_cache.size >= 4096)
|
|
143
|
+
sanitize_cache.clear();
|
|
144
|
+
sanitize_cache.set(key, result);
|
|
145
|
+
return result;
|
|
146
|
+
};
|
|
82
147
|
// Sanitize HTML string, allowing only safe formatting tags and links.
|
|
83
148
|
// Two-pass: happy-dom promotes dangerous children when a non-allowed parent is
|
|
84
149
|
// stripped (e.g. <div><script>…</script></div> → <script>…</script>). The first
|
|
@@ -90,32 +155,26 @@ export function sanitize_html(html) {
|
|
|
90
155
|
return cached;
|
|
91
156
|
const dp = get_purify();
|
|
92
157
|
if (!dp)
|
|
93
|
-
return str
|
|
158
|
+
return cache_sanitize(str, sanitize_allowlist_ssr(str, SAFE_TAG_SET, SAFE_ATTR_SET));
|
|
94
159
|
// oxfmt-ignore
|
|
95
160
|
const safe = dp.sanitize(str, { ADD_ATTR: [`target`], FORBID_TAGS: [
|
|
96
161
|
`script`, `style`, `iframe`, `object`, `embed`, `form`, `input`, `textarea`,
|
|
97
162
|
`select`, `button`, `meta`, `link`, `base`, `template`, `noscript`,
|
|
98
163
|
] });
|
|
99
|
-
|
|
100
|
-
if (sanitize_cache.size >= 4096)
|
|
101
|
-
sanitize_cache.clear(); // bound memory (rarely hit)
|
|
102
|
-
sanitize_cache.set(str, result);
|
|
103
|
-
return result;
|
|
164
|
+
return cache_sanitize(str, dp.sanitize(safe, { ALLOWED_TAGS: SAFE_TAGS, ALLOWED_ATTR: SAFE_ATTRS }));
|
|
104
165
|
}
|
|
105
166
|
export const compact_formula = (formula) => formula.replaceAll(/\s+/g, ``);
|
|
106
|
-
// Sanitize a chemical formula with optional subscript formatting
|
|
107
167
|
export const sanitize_formula = (formula, use_subscripts = true) => sanitize_html(format_formula_html(formula, use_subscripts));
|
|
108
168
|
export const sanitize_compact_formula = (formula, use_subscripts = true) => sanitize_formula(compact_formula(formula), use_subscripts);
|
|
109
|
-
const SVG_TEXT_TAGS = [`tspan`, `title`];
|
|
169
|
+
const SVG_TEXT_TAGS = new Set([`tspan`, `title`]);
|
|
110
170
|
// oxfmt-ignore
|
|
111
171
|
const SVG_TEXT_ATTRS = [`dx`, `dy`, `x`, `y`, `fill`, `font-size`, `font-weight`, `baseline-shift`];
|
|
112
|
-
// Sanitize HTML intended for SVG text contexts (tspan, title)
|
|
113
172
|
export const sanitize_svg = (html) => sanitize_svg_content(html, SVG_TEXT_TAGS, SVG_TEXT_ATTRS);
|
|
114
173
|
// oxfmt-ignore
|
|
115
|
-
const SVG_ICON_TAGS = [
|
|
174
|
+
const SVG_ICON_TAGS = new Set([
|
|
116
175
|
`path`, `circle`, `rect`, `line`, `polyline`, `polygon`, `g`, `ellipse`,
|
|
117
176
|
`clipPath`, `defs`, `mask`, `use`, `title`,
|
|
118
|
-
];
|
|
177
|
+
]);
|
|
119
178
|
// oxfmt-ignore
|
|
120
179
|
const SVG_ICON_ATTRS = [
|
|
121
180
|
`d`, `fill`, `stroke`, `stroke-width`, `stroke-linecap`, `stroke-linejoin`,
|
|
@@ -123,5 +182,4 @@ const SVG_ICON_ATTRS = [
|
|
|
123
182
|
`width`, `height`, `viewBox`, `points`, `transform`, `opacity`,
|
|
124
183
|
`clip-path`, `clip-rule`, `fill-rule`, `id`, `class`,
|
|
125
184
|
];
|
|
126
|
-
// Sanitize inline SVG markup (path, circle, rect, etc.) for icon rendering
|
|
127
185
|
export const sanitize_icon_svg = (html) => sanitize_svg_content(html, SVG_ICON_TAGS, SVG_ICON_ATTRS);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { T } from '@threlte/core'
|
|
6
6
|
import * as extras from '@threlte/extras'
|
|
7
7
|
import type { ComponentProps } from 'svelte'
|
|
8
|
-
import { build_gizmo_props, build_orbit_props } from './props'
|
|
8
|
+
import { build_gizmo_props, build_orbit_props } from './props.svelte'
|
|
9
9
|
|
|
10
10
|
let {
|
|
11
11
|
camera_projection = `perspective`,
|
|
@@ -2,7 +2,7 @@ import type { Vec3 } from '../math';
|
|
|
2
2
|
import { type CameraProjection } from '../settings';
|
|
3
3
|
import * as extras from '@threlte/extras';
|
|
4
4
|
import type { ComponentProps } from 'svelte';
|
|
5
|
-
import { build_orbit_props } from './props';
|
|
5
|
+
import { build_orbit_props } from './props.svelte';
|
|
6
6
|
type $$ComponentProps = {
|
|
7
7
|
camera_projection?: CameraProjection;
|
|
8
8
|
position: Vec3;
|
package/dist/scene/index.d.ts
CHANGED
package/dist/scene/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Shared Three.js/Threlte scene boilerplate (cameras, orbit controls, gizmo,
|
|
2
2
|
// renderer binding) used by BrillouinZoneScene, FermiSurfaceScene and StructureScene.
|
|
3
3
|
export * from './bind-renderer.svelte';
|
|
4
|
-
export * from './props';
|
|
4
|
+
export * from './props.svelte';
|
|
5
5
|
export { default as SceneCamera } from './SceneCamera.svelte';
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { AXIS_COLORS, NEG_AXIS_COLORS } from '../colors';
|
|
2
|
+
// Reactive page visibility — pause auto-rotation while the tab/window is
|
|
3
|
+
// hidden. Desktop embedders like Tauri often disable background throttling,
|
|
4
|
+
// which would otherwise leave every auto-rotating WebGL canvas burning GPU.
|
|
5
|
+
export const page_visibility = $state({
|
|
6
|
+
visible: typeof document === `undefined` || document.visibilityState === `visible`,
|
|
7
|
+
});
|
|
8
|
+
if (typeof document !== `undefined`) {
|
|
9
|
+
document.addEventListener(`visibilitychange`, () => {
|
|
10
|
+
page_visibility.visible = document.visibilityState === `visible`;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
2
13
|
// ScatterPlot3DScene keeps its own gizmo/orbit props on purpose: its gizmo offset is
|
|
3
14
|
// ColorBar-aware (build_gizmo_props' fixed offset would clobber it) and its orbit controls
|
|
4
15
|
// differ by design (no zoom-to-cursor / ortho zoom-doubling / camera-moving tracking).
|
|
@@ -39,7 +50,10 @@ export function build_orbit_props(opts) {
|
|
|
39
50
|
panSpeed: opts.pan_speed,
|
|
40
51
|
maxZoom: opts.max_zoom,
|
|
41
52
|
minZoom: opts.min_zoom,
|
|
42
|
-
|
|
53
|
+
// pause auto-rotation while the page is hidden: callers build these props
|
|
54
|
+
// in $derived, so the visibility flip re-runs them and stops the per-frame
|
|
55
|
+
// OrbitControls task (threlte only runs it while autoRotate/damping is on)
|
|
56
|
+
autoRotate: Boolean(opts.auto_rotate) && page_visibility.visible,
|
|
43
57
|
autoRotateSpeed: opts.auto_rotate,
|
|
44
58
|
enableDamping: Boolean(opts.rotation_damping),
|
|
45
59
|
dampingFactor: opts.rotation_damping,
|