matterviz 0.3.3 → 0.3.5
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 +1 -1
- package/dist/app.css +7 -0
- package/dist/brillouin/BrillouinZone.svelte +5 -2
- package/dist/brillouin/compute.js +8 -4
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +6 -6
- package/dist/chempot-diagram/async-compute.svelte.js +6 -5
- package/dist/chempot-diagram/chempot-worker.js +2 -2
- package/dist/chempot-diagram/compute.js +16 -16
- package/dist/composition/FormulaFilter.svelte +3 -3
- package/dist/constants.js +2 -8
- package/dist/convex-hull/ConvexHull.svelte +2 -2
- package/dist/convex-hull/ConvexHull2D.svelte +11 -10
- package/dist/convex-hull/ConvexHull3D.svelte +16 -14
- package/dist/convex-hull/ConvexHull4D.svelte +26 -14
- package/dist/convex-hull/ConvexHullControls.svelte +1 -1
- package/dist/convex-hull/ConvexHullInfoPane.svelte +68 -61
- package/dist/convex-hull/ConvexHullStats.svelte +23 -6
- package/dist/convex-hull/GasPressureControls.svelte +3 -3
- package/dist/convex-hull/TemperatureSlider.svelte +1 -1
- package/dist/convex-hull/barycentric-coords.js +2 -2
- package/dist/convex-hull/helpers.js +45 -27
- package/dist/convex-hull/thermodynamics.js +2 -2
- package/dist/element/BohrAtom.svelte +25 -27
- package/dist/element/BohrAtom.svelte.d.ts +2 -2
- package/dist/element/data.d.ts +2 -3
- package/dist/element/data.js +1 -1
- package/dist/fermi-surface/FermiSurface.svelte +5 -2
- package/dist/fermi-surface/compute.js +3 -3
- package/dist/fermi-surface/parse.js +2 -2
- package/dist/fermi-surface/symmetry.js +1 -1
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +8 -8
- package/dist/icons.d.ts +6 -6
- package/dist/icons.js +6 -6
- package/dist/io/decompress.js +12 -7
- package/dist/io/export.js +20 -16
- package/dist/io/is-binary.js +19 -4
- package/dist/isosurface/parse.js +8 -8
- package/dist/isosurface/types.js +9 -9
- package/dist/layout/InfoTag.svelte +1 -1
- package/dist/layout/json-tree/JsonNode.svelte +1 -0
- package/dist/layout/json-tree/utils.js +2 -1
- package/dist/marching-cubes.js +1 -1
- package/dist/math.js +1 -1
- package/dist/overlays/CopyButton.svelte +45 -0
- package/dist/overlays/CopyButton.svelte.d.ts +8 -0
- package/dist/overlays/InfoPaneCards.svelte +149 -0
- package/dist/overlays/InfoPaneCards.svelte.d.ts +22 -0
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +33 -35
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +2 -2
- package/dist/phase-diagram/PhaseDiagramControls.svelte +27 -29
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +2 -2
- package/dist/phase-diagram/parse.js +3 -3
- package/dist/phase-diagram/svg-to-diagram.js +10 -12
- package/dist/plot/BarPlot.svelte +24 -15
- package/dist/plot/BarPlot.svelte.d.ts +3 -2
- package/dist/plot/FillArea.svelte +2 -3
- package/dist/plot/FillArea.svelte.d.ts +3 -2
- package/dist/plot/Histogram.svelte +37 -19
- package/dist/plot/Line.svelte +2 -3
- package/dist/plot/Line.svelte.d.ts +2 -2
- package/dist/plot/PlotLegend.svelte +79 -8
- package/dist/plot/PlotLegend.svelte.d.ts +4 -0
- package/dist/plot/PortalSelect.svelte +5 -5
- package/dist/plot/ScatterPlot.svelte +47 -33
- package/dist/plot/ScatterPlot.svelte.d.ts +5 -4
- package/dist/plot/ScatterPlot3D.svelte +6 -3
- package/dist/plot/ScatterPoint.svelte +10 -4
- package/dist/plot/ScatterPoint.svelte.d.ts +4 -2
- package/dist/plot/SpacegroupBarPlot.svelte +5 -4
- package/dist/plot/data-cleaning.js +9 -9
- package/dist/plot/index.d.ts +0 -6
- package/dist/plot/scales.d.ts +3 -3
- package/dist/plot/scales.js +29 -29
- package/dist/plot/types.d.ts +5 -9
- package/dist/rdf/calc-rdf.js +1 -1
- package/dist/sanitize.js +22 -15
- package/dist/settings.d.ts +2 -0
- package/dist/settings.js +12 -3
- package/dist/spectral/Bands.svelte +6 -6
- package/dist/spectral/BandsAndDos.svelte +4 -4
- package/dist/spectral/BrillouinBandsDos.svelte +3 -3
- package/dist/spectral/Dos.svelte +2 -2
- package/dist/spectral/helpers.js +1 -1
- package/dist/structure/AtomLegend.svelte +4 -4
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/Cylinder.svelte +7 -7
- package/dist/structure/Structure.svelte +169 -27
- package/dist/structure/Structure.svelte.d.ts +6 -2
- package/dist/structure/StructureControls.svelte +130 -16
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +519 -218
- package/dist/structure/StructureInfoPane.svelte.d.ts +2 -1
- package/dist/structure/StructureScene.svelte +399 -68
- package/dist/structure/StructureScene.svelte.d.ts +8 -4
- package/dist/structure/atom-properties.js +3 -1
- package/dist/structure/bond-order-perception.d.ts +13 -0
- package/dist/structure/bond-order-perception.js +367 -0
- package/dist/structure/bonding.d.ts +10 -1
- package/dist/structure/bonding.js +232 -11
- package/dist/structure/export.js +6 -4
- package/dist/structure/index.d.ts +19 -4
- package/dist/structure/index.js +3 -0
- package/dist/structure/label-placement.d.ts +14 -0
- package/dist/structure/label-placement.js +72 -0
- package/dist/structure/parse.d.ts +2 -1
- package/dist/structure/parse.js +25 -36
- package/dist/structure/supercell.js +35 -2
- package/dist/symmetry/SymmetryStats.svelte +1 -1
- package/dist/symmetry/cell-transform.js +15 -1
- package/dist/symmetry/index.js +3 -3
- package/dist/table/HeatmapTable.svelte +3 -3
- package/dist/table/ToggleMenu.svelte +1 -1
- package/dist/trajectory/Trajectory.svelte +2 -2
- package/dist/trajectory/TrajectoryInfoPane.svelte +14 -88
- package/dist/trajectory/extract.js +4 -4
- package/dist/trajectory/frame-reader.js +2 -2
- package/dist/trajectory/parse/ase.js +2 -6
- package/dist/trajectory/parse/hdf5.js +1 -3
- package/dist/trajectory/plotting.js +1 -1
- package/dist/utils.js +1 -1
- package/dist/xrd/calc-xrd.js +1 -1
- package/package.json +23 -38
- package/dist/structure/ferrox-wasm-types.d.ts +0 -46
- package/dist/structure/ferrox-wasm-types.js +0 -18
- package/dist/structure/ferrox-wasm.d.ts +0 -94
- package/dist/structure/ferrox-wasm.js +0 -249
package/dist/FilePicker.svelte
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { FileInfo } from './io'
|
|
3
|
-
import { tooltip } from 'svelte-multiselect'
|
|
3
|
+
import { tooltip } from 'svelte-multiselect/attachments'
|
|
4
4
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
5
5
|
|
|
6
6
|
// Delay for distinguishing click from double-click (ms)
|
package/dist/app.css
CHANGED
|
@@ -20,6 +20,13 @@
|
|
|
20
20
|
--sms-li-active-bg: light-dark(rgba(100, 149, 237, 0.25), cornflowerblue);
|
|
21
21
|
--sms-selected-bg: light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.15));
|
|
22
22
|
--border-radius: 3pt;
|
|
23
|
+
|
|
24
|
+
/* App-wide overlay stack: controls < fullscreen/nav < dialogs < floating options.
|
|
25
|
+
These must exceed Threlte HTML overlays, which use very high z-index values. */
|
|
26
|
+
--z-index-overlay-controls: 100000000;
|
|
27
|
+
--z-index-overlay-nav: 100000001;
|
|
28
|
+
--z-index-overlay-dialog: 100000002;
|
|
29
|
+
--z-index-overlay-options: 100000003;
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
/* App-shell styles: page layout, typography, headings, navigation.
|
|
@@ -444,7 +444,7 @@
|
|
|
444
444
|
y={hover_data.screen_position.y}
|
|
445
445
|
bg_color={hover_data.is_ibz ? ibz_color : surface_color}
|
|
446
446
|
fixed
|
|
447
|
-
style="z-index: calc(var(--bz-buttons-z-index, 100000000) + 1); backdrop-filter: blur(4px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3)"
|
|
447
|
+
style="z-index: calc(var(--bz-buttons-z-index, var(--z-index-overlay-controls, 100000000)) + 1); backdrop-filter: blur(4px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3)"
|
|
448
448
|
>
|
|
449
449
|
<BrillouinZoneTooltip {hover_data} tooltip={tooltip_config} />
|
|
450
450
|
</PlotTooltip>
|
|
@@ -497,7 +497,10 @@
|
|
|
497
497
|
top: var(--bz-buttons-top, var(--ctrl-btn-top, 1ex));
|
|
498
498
|
right: var(--bz-buttons-right, var(--ctrl-btn-right, 1ex));
|
|
499
499
|
gap: clamp(6pt, 1cqmin, 9pt);
|
|
500
|
-
z-index: var(
|
|
500
|
+
z-index: var(
|
|
501
|
+
--bz-buttons-z-index,
|
|
502
|
+
var(--z-index-overlay-controls, 100000000)
|
|
503
|
+
);
|
|
501
504
|
opacity: 0;
|
|
502
505
|
pointer-events: none;
|
|
503
506
|
transition: opacity 0.2s ease;
|
|
@@ -205,7 +205,7 @@ export function compute_convex_hull(vertices, edge_sharp_angle_deg = 5) {
|
|
|
205
205
|
}
|
|
206
206
|
const geometry = new ConvexGeometry(vertices.map((cert) => new Vector3(...cert)));
|
|
207
207
|
const pos = geometry.getAttribute(`position`);
|
|
208
|
-
const
|
|
208
|
+
const geometry_index = geometry.index;
|
|
209
209
|
// Deduplicate vertices from Three.js geometry
|
|
210
210
|
const unique_verts = [];
|
|
211
211
|
const vert_map = new Map();
|
|
@@ -218,10 +218,14 @@ export function compute_convex_hull(vertices, edge_sharp_angle_deg = 5) {
|
|
|
218
218
|
}
|
|
219
219
|
// Build faces with deduplicated vertex indices
|
|
220
220
|
const faces = [];
|
|
221
|
-
const n_faces =
|
|
221
|
+
const n_faces = geometry_index ? geometry_index.count / 3 : pos.count / 3;
|
|
222
222
|
for (let idx_face = 0; idx_face < n_faces; idx_face++) {
|
|
223
|
-
const tri =
|
|
224
|
-
? [
|
|
223
|
+
const tri = geometry_index
|
|
224
|
+
? [
|
|
225
|
+
geometry_index.getX(idx_face * 3),
|
|
226
|
+
geometry_index.getX(idx_face * 3 + 1),
|
|
227
|
+
geometry_index.getX(idx_face * 3 + 2),
|
|
228
|
+
]
|
|
225
229
|
: [idx_face * 3, idx_face * 3 + 1, idx_face * 3 + 2];
|
|
226
230
|
faces.push(tri.map((j) => {
|
|
227
231
|
const mapped = vert_map.get(j);
|
|
@@ -1931,8 +1931,8 @@
|
|
|
1931
1931
|
const export_basename = $derived(`chempot-${plot_elements.join(`-`)}`)
|
|
1932
1932
|
|
|
1933
1933
|
function get_view_settings(): Record<string, unknown> {
|
|
1934
|
-
const
|
|
1935
|
-
const
|
|
1934
|
+
const view_camera_position = orbit_controls_ref?.object?.position
|
|
1935
|
+
const view_camera_target = orbit_controls_ref?.target
|
|
1936
1936
|
return {
|
|
1937
1937
|
elements: plot_elements,
|
|
1938
1938
|
camera_projection,
|
|
@@ -1940,11 +1940,11 @@
|
|
|
1940
1940
|
color_mode,
|
|
1941
1941
|
color_scale,
|
|
1942
1942
|
reverse_color_scale,
|
|
1943
|
-
camera_position:
|
|
1944
|
-
? [
|
|
1943
|
+
camera_position: view_camera_position
|
|
1944
|
+
? [view_camera_position.x, view_camera_position.y, view_camera_position.z]
|
|
1945
1945
|
: null,
|
|
1946
|
-
camera_target:
|
|
1947
|
-
? [
|
|
1946
|
+
camera_target: view_camera_target
|
|
1947
|
+
? [view_camera_target.x, view_camera_target.y, view_camera_target.z]
|
|
1948
1948
|
: null,
|
|
1949
1949
|
}
|
|
1950
1950
|
}
|
|
@@ -28,8 +28,8 @@ function get_worker() {
|
|
|
28
28
|
if (typeof Worker === `undefined`)
|
|
29
29
|
return null;
|
|
30
30
|
if (!worker) {
|
|
31
|
-
worker = new Worker(new URL(`./chempot-worker.
|
|
32
|
-
worker.
|
|
31
|
+
worker = new Worker(new URL(`./chempot-worker.js`, import.meta.url), { type: `module` });
|
|
32
|
+
worker.addEventListener(`message`, ({ data: { id, result, error } }) => {
|
|
33
33
|
const req = pending.get(id);
|
|
34
34
|
if (!req)
|
|
35
35
|
return;
|
|
@@ -38,15 +38,15 @@ function get_worker() {
|
|
|
38
38
|
req.reject(new Error(error ?? `Worker returned null`));
|
|
39
39
|
else
|
|
40
40
|
req.resolve(result);
|
|
41
|
-
};
|
|
42
|
-
worker.
|
|
41
|
+
});
|
|
42
|
+
worker.addEventListener(`error`, (event) => {
|
|
43
43
|
event.preventDefault();
|
|
44
44
|
const err = new Error(event.message || `Worker initialization error`);
|
|
45
45
|
for (const req of pending.values())
|
|
46
46
|
req.reject(err);
|
|
47
47
|
pending.clear();
|
|
48
48
|
worker = null;
|
|
49
|
-
};
|
|
49
|
+
});
|
|
50
50
|
}
|
|
51
51
|
return worker;
|
|
52
52
|
}
|
|
@@ -66,6 +66,7 @@ export function compute_chempot_async(entries, config = {}) {
|
|
|
66
66
|
pending.set(id, { resolve, reject });
|
|
67
67
|
try {
|
|
68
68
|
// $state.snapshot strips Svelte $state proxies (not structured-cloneable)
|
|
69
|
+
// eslint-disable-next-line unicorn/require-post-message-target-origin
|
|
69
70
|
wkr.postMessage($state.snapshot({ id, entries, config }));
|
|
70
71
|
}
|
|
71
72
|
catch (err) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { compute_chempot_diagram } from './compute';
|
|
2
|
-
self.
|
|
2
|
+
self.addEventListener(`message`, (event) => {
|
|
3
3
|
const { id, entries, config } = event.data;
|
|
4
4
|
try {
|
|
5
5
|
const result = compute_chempot_diagram(entries, config);
|
|
@@ -8,4 +8,4 @@ self.onmessage = (event) => {
|
|
|
8
8
|
catch (err) {
|
|
9
9
|
postMessage({ id, result: null, error: err instanceof Error ? err.message : String(err) });
|
|
10
10
|
}
|
|
11
|
-
};
|
|
11
|
+
});
|
|
@@ -149,7 +149,7 @@ export function build_hyperplanes(min_entries, el_refs, elements) {
|
|
|
149
149
|
const atom_count = count_atoms_in_composition(entry.composition);
|
|
150
150
|
const composition = entry.composition;
|
|
151
151
|
const energy_per_atom = get_energy_per_atom(entry);
|
|
152
|
-
const row =
|
|
152
|
+
const row = Array(n_elems + 1).fill(0);
|
|
153
153
|
let ref_energy = 0;
|
|
154
154
|
for (let elem_idx = 0; elem_idx < n_elems; elem_idx++) {
|
|
155
155
|
const element = elements[elem_idx];
|
|
@@ -180,12 +180,12 @@ export function build_border_hyperplanes(lims) {
|
|
|
180
180
|
const borders = [];
|
|
181
181
|
for (let idx = 0; idx < dim; idx++) {
|
|
182
182
|
// Lower bound: -mu_i + lo <= 0 → [-1, 0, ..., lo]
|
|
183
|
-
const lower =
|
|
183
|
+
const lower = Array(dim + 1).fill(0);
|
|
184
184
|
lower[idx] = -1;
|
|
185
185
|
lower[dim] = lims[idx][0];
|
|
186
186
|
borders.push(lower);
|
|
187
187
|
// Upper bound: mu_i - hi <= 0 → [1, 0, ..., -hi]
|
|
188
|
-
const upper =
|
|
188
|
+
const upper = Array(dim + 1).fill(0);
|
|
189
189
|
upper[idx] = 1;
|
|
190
190
|
upper[dim] = -lims[idx][1];
|
|
191
191
|
borders.push(upper);
|
|
@@ -246,12 +246,12 @@ export function compute_domains(hyperplanes, border_hyperplanes, hyperplane_entr
|
|
|
246
246
|
domains[formula] = [];
|
|
247
247
|
}
|
|
248
248
|
// Pre-allocate reusable buffers to avoid GC pressure in the combo loop
|
|
249
|
-
const mu =
|
|
250
|
-
const offsets =
|
|
249
|
+
const mu = Array(dim).fill(0);
|
|
250
|
+
const offsets = Array(dim).fill(0);
|
|
251
251
|
// For dim <= 3, use inline solvers; for larger dims, build A on the fly
|
|
252
|
-
const A_rows = dim > 3 ? Array.from({ length: dim }, () =>
|
|
252
|
+
const A_rows = dim > 3 ? Array.from({ length: dim }, () => Array(dim).fill(0)) : [];
|
|
253
253
|
// Generate all combinations of dim indices from n_total halfspaces
|
|
254
|
-
const combo =
|
|
254
|
+
const combo = Array(dim).fill(0);
|
|
255
255
|
for (let idx = 0; idx < dim; idx++)
|
|
256
256
|
combo[idx] = idx;
|
|
257
257
|
function advance_combo() {
|
|
@@ -434,7 +434,7 @@ export function simple_pca(data, k = 2) {
|
|
|
434
434
|
return { scores: [], eigenvectors: [] };
|
|
435
435
|
}
|
|
436
436
|
// Center the data
|
|
437
|
-
const means =
|
|
437
|
+
const means = Array(n_cols).fill(0);
|
|
438
438
|
for (const row of data) {
|
|
439
439
|
for (let col = 0; col < n_cols; col++)
|
|
440
440
|
means[col] += row[col];
|
|
@@ -443,7 +443,7 @@ export function simple_pca(data, k = 2) {
|
|
|
443
443
|
means[col] /= n_rows;
|
|
444
444
|
const centered = data.map((row) => row.map((val, col) => val - means[col]));
|
|
445
445
|
// Covariance matrix
|
|
446
|
-
const cov = Array.from({ length: n_cols }, () =>
|
|
446
|
+
const cov = Array.from({ length: n_cols }, () => Array(n_cols).fill(0));
|
|
447
447
|
for (const row of centered) {
|
|
448
448
|
for (let idx = 0; idx < n_cols; idx++) {
|
|
449
449
|
for (let jdx = idx; jdx < n_cols; jdx++) {
|
|
@@ -462,11 +462,11 @@ export function simple_pca(data, k = 2) {
|
|
|
462
462
|
const eigenvectors = [];
|
|
463
463
|
const work_cov = cov.map((row) => [...row]);
|
|
464
464
|
for (let comp = 0; comp < k; comp++) {
|
|
465
|
-
let vec =
|
|
465
|
+
let vec = Array(n_cols).fill(0);
|
|
466
466
|
vec[comp % n_cols] = 1; // initial guess
|
|
467
467
|
for (let iter = 0; iter < 100; iter++) {
|
|
468
468
|
// Matrix-vector multiply
|
|
469
|
-
const new_vec =
|
|
469
|
+
const new_vec = Array(n_cols).fill(0);
|
|
470
470
|
for (let idx = 0; idx < n_cols; idx++) {
|
|
471
471
|
for (let jdx = 0; jdx < n_cols; jdx++) {
|
|
472
472
|
new_vec[idx] += work_cov[idx][jdx] * vec[jdx];
|
|
@@ -548,8 +548,8 @@ export function get_3d_domain_simplexes_and_ann_loc(points_3d) {
|
|
|
548
548
|
const mean_3d = Array.from({ length: n_dims }, (_, dim) => unique.reduce((sum, pt) => sum + pt[dim], 0) / unique.length);
|
|
549
549
|
const centroid_x = centroid[0] ?? 0;
|
|
550
550
|
const centroid_y = centroid[1] ?? 0;
|
|
551
|
-
const first_eigenvector = eigenvectors[0] ??
|
|
552
|
-
const second_eigenvector = eigenvectors[1] ??
|
|
551
|
+
const first_eigenvector = eigenvectors[0] ?? Array(n_dims).fill(0);
|
|
552
|
+
const second_eigenvector = eigenvectors[1] ?? Array(n_dims).fill(0);
|
|
553
553
|
const ann_loc = mean_3d.map((m, dim) => m + centroid_x * first_eigenvector[dim] + centroid_y * second_eigenvector[dim]);
|
|
554
554
|
// Map hull vertices back to original point indices using nearest projected
|
|
555
555
|
// vertex instead of stringified coordinates to avoid precision aliasing.
|
|
@@ -675,7 +675,7 @@ export function get_ternary_combinations(elements) {
|
|
|
675
675
|
}
|
|
676
676
|
return combos;
|
|
677
677
|
}
|
|
678
|
-
let
|
|
678
|
+
let nd_cache = null;
|
|
679
679
|
// Content-based fingerprint for N-D result caching. Uses sorted formula keys
|
|
680
680
|
// so deserialized Web Worker copies match and different compositions never collide.
|
|
681
681
|
export function make_nd_cache_key(entries, formal_chempots, default_min_limit, limits) {
|
|
@@ -723,7 +723,7 @@ export function compute_chempot_diagram(entries, config = {}) {
|
|
|
723
723
|
const cache_key = is_projection
|
|
724
724
|
? make_nd_cache_key(entries, formal_chempots, default_min_limit, limits)
|
|
725
725
|
: ``;
|
|
726
|
-
let nd_result = is_projection &&
|
|
726
|
+
let nd_result = is_projection && nd_cache?.key === cache_key ? nd_cache.result : null;
|
|
727
727
|
if (!nd_result) {
|
|
728
728
|
// In subsystem mode, filter entries to only those within the element set
|
|
729
729
|
let working_entries = entries;
|
|
@@ -779,7 +779,7 @@ export function compute_chempot_diagram(entries, config = {}) {
|
|
|
779
779
|
};
|
|
780
780
|
// Cache for projection mode reuse
|
|
781
781
|
if (is_projection) {
|
|
782
|
-
|
|
782
|
+
nd_cache = { key: cache_key, result: nd_result };
|
|
783
783
|
}
|
|
784
784
|
}
|
|
785
785
|
let domains = nd_result.domains;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import Icon from '../Icon.svelte'
|
|
3
3
|
import { get_alphabetical_formula } from './format'
|
|
4
4
|
import { ELEM_SYMBOLS } from '../labels'
|
|
5
|
-
import { tooltip } from 'svelte-multiselect'
|
|
5
|
+
import { tooltip } from 'svelte-multiselect/attachments'
|
|
6
6
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
7
7
|
import type { FormulaSearchMode } from './index'
|
|
8
8
|
import {
|
|
@@ -398,11 +398,11 @@
|
|
|
398
398
|
const operator = token.startsWith(`-`) || token.startsWith(`!`)
|
|
399
399
|
? `exclude`
|
|
400
400
|
: `include`
|
|
401
|
-
const
|
|
401
|
+
const stripped_value =
|
|
402
402
|
token.startsWith(`+`) || token.startsWith(`-`) || token.startsWith(`!`)
|
|
403
403
|
? token.slice(1)
|
|
404
404
|
: token
|
|
405
|
-
return { operator, value }
|
|
405
|
+
return { operator, value: stripped_value }
|
|
406
406
|
}
|
|
407
407
|
|
|
408
408
|
function serialize_token(
|
package/dist/constants.js
CHANGED
|
@@ -9,9 +9,7 @@ export const COMPRESSION_FORMATS = {
|
|
|
9
9
|
bz2: [`.bz2`], // Browser DecompressionStream doesn't support BZ2
|
|
10
10
|
};
|
|
11
11
|
// All detectable compression extensions
|
|
12
|
-
export const COMPRESSION_EXTENSIONS = Object.freeze(
|
|
13
|
-
...Object.values(COMPRESSION_FORMATS).flat(),
|
|
14
|
-
]);
|
|
12
|
+
export const COMPRESSION_EXTENSIONS = Object.freeze(Object.values(COMPRESSION_FORMATS).flat());
|
|
15
13
|
// Keywords that indicate a file is likely a trajectory file
|
|
16
14
|
export const TRAJ_KEYWORDS = Object.freeze([
|
|
17
15
|
`trajectory`,
|
|
@@ -64,11 +62,7 @@ export const STRUCT_KEYWORDS_REGEX = new RegExp(`(${STRUCT_KEYWORDS.join(`|`)})`
|
|
|
64
62
|
export const STRUCT_KEYWORDS_STRICT_REGEX = new RegExp(`(${STRUCT_KEYWORDS_STRICT.join(`|`)})`, `i`);
|
|
65
63
|
export const TRAJ_KEYWORDS_SIMPLE_REGEX = new RegExp(`(${TRAJ_KEYWORDS.join(`|`)})`, `i`);
|
|
66
64
|
// File extensions for different file types
|
|
67
|
-
export const TRAJ_EXTENSIONS = Object.freeze([
|
|
68
|
-
`.traj`,
|
|
69
|
-
`.xtc`,
|
|
70
|
-
`.lammpstrj`,
|
|
71
|
-
]);
|
|
65
|
+
export const TRAJ_EXTENSIONS = Object.freeze([`.traj`, `.xtc`, `.lammpstrj`]);
|
|
72
66
|
export const TRAJ_EXTENSIONS_REGEX = new RegExp(`\\.(${TRAJ_EXTENSIONS.map((ext) => ext.slice(1)).join(`|`)})$`, `i`);
|
|
73
67
|
export const STRUCTURE_EXTENSIONS = Object.freeze([
|
|
74
68
|
`.cif`,
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
// Lightweight element extraction - count unique elements, stripping oxidation states
|
|
56
56
|
// (e.g. "V4+" -> "V") to avoid counting the same element multiple times
|
|
57
57
|
function extract_unique_elements(
|
|
58
|
-
|
|
58
|
+
hull_entries: { composition: Record<string, number> }[],
|
|
59
59
|
): string[] {
|
|
60
60
|
const elements = new SvelteSet<string>()
|
|
61
|
-
for (const entry of
|
|
61
|
+
for (const entry of hull_entries) {
|
|
62
62
|
for (const key of Object.keys(entry.composition)) {
|
|
63
63
|
// Extract valid element symbols, stripping oxidation states
|
|
64
64
|
for (const elem of extract_formula_elements(key, { unique: false })) {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { set_fullscreen_bg, setup_fullscreen_effect } from '../layout'
|
|
12
12
|
import type {
|
|
13
13
|
AxisConfig,
|
|
14
|
+
PointStyle,
|
|
14
15
|
ScatterHandlerEvent,
|
|
15
16
|
ScatterHandlerProps,
|
|
16
17
|
UserContentProps,
|
|
@@ -99,8 +100,8 @@
|
|
|
99
100
|
...default_hull_config,
|
|
100
101
|
point_size: 6, // Binary diagrams use slightly smaller points
|
|
101
102
|
...config,
|
|
102
|
-
colors: { ...default_hull_config.colors, ...
|
|
103
|
-
margin: { t: 40, r: 40, b: 60, l: 60, ...
|
|
103
|
+
colors: { ...default_hull_config.colors, ...config.colors },
|
|
104
|
+
margin: { t: 40, r: 40, b: 60, l: 60, ...config.margin },
|
|
104
105
|
})
|
|
105
106
|
|
|
106
107
|
// Merge highlight style with defaults (consistent with 3D/4D)
|
|
@@ -274,16 +275,16 @@
|
|
|
274
275
|
x: x_coord,
|
|
275
276
|
y: min_y,
|
|
276
277
|
}))
|
|
277
|
-
const
|
|
278
|
+
const computed_hull_points = thermo.compute_lower_hull_2d(hull_input)
|
|
278
279
|
|
|
279
|
-
const
|
|
280
|
-
const y_hull = thermo.interpolate_hull_2d(
|
|
280
|
+
const enriched_entries = coords_entries.map((entry) => {
|
|
281
|
+
const y_hull = thermo.interpolate_hull_2d(computed_hull_points, entry.x)
|
|
281
282
|
const raw_dist = y_hull == null ? 0 : entry.y - y_hull
|
|
282
283
|
return {
|
|
283
284
|
...entry, ...compute_hull_stability(raw_dist, entry.exclude_from_hull), visible: true,
|
|
284
285
|
}
|
|
285
286
|
})
|
|
286
|
-
return { all_enriched_entries, hull_points }
|
|
287
|
+
return { all_enriched_entries: enriched_entries, hull_points: computed_hull_points }
|
|
287
288
|
})
|
|
288
289
|
|
|
289
290
|
// Auto threshold: show all for few entries, use default for many, interpolate between
|
|
@@ -377,10 +378,10 @@
|
|
|
377
378
|
const count = visible_entries.length
|
|
378
379
|
|
|
379
380
|
// Single pass: extract x, y, color_values, and point_style simultaneously
|
|
380
|
-
const x_vals: number[] =
|
|
381
|
-
const y_vals: number[] =
|
|
382
|
-
const color_values: number[] = is_energy_mode ?
|
|
383
|
-
const point_style =
|
|
381
|
+
const x_vals: number[] = Array(count)
|
|
382
|
+
const y_vals: number[] = Array(count)
|
|
383
|
+
const color_values: number[] = is_energy_mode ? Array(count) : []
|
|
384
|
+
const point_style: PointStyle[] = Array(count)
|
|
384
385
|
|
|
385
386
|
for (let idx = 0; idx < count; idx++) {
|
|
386
387
|
const entry = visible_entries[idx]
|
|
@@ -130,8 +130,8 @@
|
|
|
130
130
|
const merged_config = $derived({
|
|
131
131
|
...default_hull_config,
|
|
132
132
|
...config,
|
|
133
|
-
colors: { ...default_hull_config.colors, ...
|
|
134
|
-
margin: { t: 40, r: 40, b: 60, l: 60, ...
|
|
133
|
+
colors: { ...default_hull_config.colors, ...config.colors },
|
|
134
|
+
margin: { t: 40, r: 40, b: 60, l: 60, ...config.margin },
|
|
135
135
|
})
|
|
136
136
|
|
|
137
137
|
// Temperature-dependent free energy support
|
|
@@ -306,7 +306,7 @@
|
|
|
306
306
|
})
|
|
307
307
|
|
|
308
308
|
// Canvas rendering
|
|
309
|
-
let canvas: HTMLCanvasElement
|
|
309
|
+
let canvas: HTMLCanvasElement | undefined = undefined
|
|
310
310
|
let ctx: CanvasRenderingContext2D | null = null
|
|
311
311
|
|
|
312
312
|
// Performance optimization
|
|
@@ -632,7 +632,7 @@
|
|
|
632
632
|
}
|
|
633
633
|
|
|
634
634
|
function draw_structure_outline(): void {
|
|
635
|
-
if (!ctx) return
|
|
635
|
+
if (!ctx || !canvas) return
|
|
636
636
|
|
|
637
637
|
// Set consistent style for all triangle structure lines
|
|
638
638
|
ctx.strokeStyle = CONVEX_HULL_STYLE.structure_line.color
|
|
@@ -644,12 +644,11 @@
|
|
|
644
644
|
}
|
|
645
645
|
|
|
646
646
|
function draw_triangle_structure(): void {
|
|
647
|
-
if (!ctx) return
|
|
647
|
+
if (!ctx || !canvas) return
|
|
648
648
|
|
|
649
649
|
// Get formation energy range for vertical edges
|
|
650
|
-
const
|
|
651
|
-
const
|
|
652
|
-
const e_form_max = Math.max(0, ...formation_energies) // Include 0 for elemental references
|
|
650
|
+
const e_form_min = energy_range.min // Includes 0 for elemental references
|
|
651
|
+
const e_form_max = energy_range.max // Includes 0 for elemental references
|
|
653
652
|
|
|
654
653
|
// Draw base triangle edges (top triangle at formation energy = 0)
|
|
655
654
|
const triangle_edges = get_triangle_edges()
|
|
@@ -799,8 +798,7 @@
|
|
|
799
798
|
// Lazy computation for uniform mode: normalize alpha by formation energy
|
|
800
799
|
let norm_alpha: ((z: number) => number) | null = null
|
|
801
800
|
if (hull_face_color_mode === `uniform`) {
|
|
802
|
-
const
|
|
803
|
-
const min_fe = Math.min(0, ...formation_energies)
|
|
801
|
+
const min_fe = energy_range.min
|
|
804
802
|
norm_alpha = (z: number) => {
|
|
805
803
|
const t = Math.max(0, Math.min(1, (0 - z) / Math.max(1e-6, 0 - min_fe)))
|
|
806
804
|
return t * hull_face_opacity
|
|
@@ -955,8 +953,7 @@
|
|
|
955
953
|
|
|
956
954
|
// Formation energy color bar helpers
|
|
957
955
|
const e_form_range = $derived.by((): [number, number] => {
|
|
958
|
-
const
|
|
959
|
-
const min_fe = energies.length ? Math.min(0, ...energies) : -1
|
|
956
|
+
const min_fe = plot_entries.length ? energy_range.min : -1
|
|
960
957
|
return [min_fe, 0]
|
|
961
958
|
})
|
|
962
959
|
|
|
@@ -1387,8 +1384,13 @@
|
|
|
1387
1384
|
// Performance: Cache canvas dimensions and formation energy range
|
|
1388
1385
|
let canvas_dims = $state({ width: 600, height: 600, scale: 1 })
|
|
1389
1386
|
const energy_range = $derived.by(() => {
|
|
1390
|
-
|
|
1391
|
-
|
|
1387
|
+
let min = 0
|
|
1388
|
+
let max = 0
|
|
1389
|
+
for (const entry of plot_entries) {
|
|
1390
|
+
const energy = entry.e_form_per_atom ?? 0
|
|
1391
|
+
min = Math.min(min, energy)
|
|
1392
|
+
max = Math.max(max, energy)
|
|
1393
|
+
}
|
|
1392
1394
|
const z_scale = 0.75 / Math.max(max - min, 0.001)
|
|
1393
1395
|
return { min, max, center: (min + max) / 2, z_scale }
|
|
1394
1396
|
})
|
|
@@ -107,8 +107,8 @@
|
|
|
107
107
|
const merged_config = $derived({
|
|
108
108
|
...default_hull_config,
|
|
109
109
|
...config,
|
|
110
|
-
colors: { ...default_hull_config.colors, ...
|
|
111
|
-
margin: { t: 60, r: 60, b: 60, l: 60, ...
|
|
110
|
+
colors: { ...default_hull_config.colors, ...config.colors },
|
|
111
|
+
margin: { t: 60, r: 60, b: 60, l: 60, ...config.margin },
|
|
112
112
|
})
|
|
113
113
|
|
|
114
114
|
// Reactive dark mode detection for canvas text color
|
|
@@ -311,7 +311,7 @@
|
|
|
311
311
|
)
|
|
312
312
|
})
|
|
313
313
|
|
|
314
|
-
let canvas: HTMLCanvasElement
|
|
314
|
+
let canvas: HTMLCanvasElement | undefined = undefined
|
|
315
315
|
let ctx: CanvasRenderingContext2D | null = null
|
|
316
316
|
let frame_id = 0 // Performance optimization
|
|
317
317
|
|
|
@@ -554,7 +554,7 @@
|
|
|
554
554
|
}
|
|
555
555
|
|
|
556
556
|
function draw_structure_outline(): void {
|
|
557
|
-
if (!ctx) return
|
|
557
|
+
if (!ctx || !canvas) return
|
|
558
558
|
|
|
559
559
|
const styles = getComputedStyle(canvas)
|
|
560
560
|
// Match gray dashed structure lines used in 3D
|
|
@@ -732,10 +732,11 @@
|
|
|
732
732
|
// Lazy computation for uniform mode: normalize alpha by formation energy
|
|
733
733
|
let norm_alpha: ((w: number) => number) | null = null
|
|
734
734
|
if (hull_face_color_mode === `uniform`) {
|
|
735
|
-
const formation_energies = plot_entries.map((e) => e.e_form_per_atom ?? 0)
|
|
736
|
-
const min_fe = Math.min(0, ...formation_energies)
|
|
737
735
|
norm_alpha = (energy: number) => {
|
|
738
|
-
const t = Math.max(
|
|
736
|
+
const t = Math.max(
|
|
737
|
+
0,
|
|
738
|
+
Math.min(1, (0 - energy) / Math.max(1e-6, 0 - formation_energy_min)),
|
|
739
|
+
)
|
|
739
740
|
return t * hull_face_opacity
|
|
740
741
|
}
|
|
741
742
|
}
|
|
@@ -1032,15 +1033,19 @@
|
|
|
1032
1033
|
const rect = container?.getBoundingClientRect()
|
|
1033
1034
|
const [width, height] = rect ? [rect.width, rect.height] : [400, 400]
|
|
1034
1035
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1036
|
+
const new_width = Math.max(0, Math.round(width * dpr))
|
|
1037
|
+
const new_height = Math.max(0, Math.round(height * dpr))
|
|
1037
1038
|
canvas_dims = { width, height, scale: Math.min(width, height) / 600 }
|
|
1038
1039
|
|
|
1039
|
-
ctx
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
ctx
|
|
1043
|
-
ctx
|
|
1040
|
+
if (!ctx || canvas.width !== new_width || canvas.height !== new_height) {
|
|
1041
|
+
canvas.width = new_width
|
|
1042
|
+
canvas.height = new_height
|
|
1043
|
+
ctx = canvas.getContext(`2d`)
|
|
1044
|
+
if (ctx) {
|
|
1045
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
|
|
1046
|
+
ctx.imageSmoothingEnabled = true
|
|
1047
|
+
ctx.imageSmoothingQuality = `high`
|
|
1048
|
+
}
|
|
1044
1049
|
}
|
|
1045
1050
|
render_once()
|
|
1046
1051
|
}
|
|
@@ -1078,6 +1083,13 @@
|
|
|
1078
1083
|
|
|
1079
1084
|
// Performance: Cache canvas dimensions and pre-compute sorted point projections
|
|
1080
1085
|
let canvas_dims = $state({ width: 600, height: 600, scale: 1 })
|
|
1086
|
+
const formation_energy_min = $derived.by(() => {
|
|
1087
|
+
let min_energy = 0
|
|
1088
|
+
for (const entry of plot_entries) {
|
|
1089
|
+
min_energy = Math.min(min_energy, entry.e_form_per_atom ?? 0)
|
|
1090
|
+
}
|
|
1091
|
+
return min_energy
|
|
1092
|
+
})
|
|
1081
1093
|
const sorted_points_cache = $derived.by(() => {
|
|
1082
1094
|
if (!canvas || plot_entries.length === 0) return []
|
|
1083
1095
|
return plot_entries
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { sanitize_html } from '../sanitize'
|
|
6
6
|
import { ColorScaleSelect } from '../plot'
|
|
7
7
|
import type { ComponentProps } from 'svelte'
|
|
8
|
-
import { tooltip } from 'svelte-multiselect'
|
|
8
|
+
import { tooltip } from 'svelte-multiselect/attachments'
|
|
9
9
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
10
10
|
import type {
|
|
11
11
|
ConvexHullControlsType,
|