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
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { type D3SymbolName, symbol_map } from '../labels'
|
|
3
3
|
import type { HoverStyle, LabelStyle, Point } from './'
|
|
4
|
-
import type { PointStyle,
|
|
4
|
+
import type { PointStyle, XyObj } from './types'
|
|
5
5
|
import { DEFAULTS } from '../settings'
|
|
6
6
|
import * as d3_symbols from 'd3-shape'
|
|
7
7
|
import { symbol } from 'd3-shape'
|
|
8
8
|
import { cubicOut } from 'svelte/easing'
|
|
9
9
|
import type { SVGAttributes } from 'svelte/elements'
|
|
10
|
-
import { Tween } from 'svelte/motion'
|
|
10
|
+
import { Tween, type TweenOptions } from 'svelte/motion'
|
|
11
11
|
|
|
12
12
|
let {
|
|
13
13
|
x,
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
origin = $bindable({ x: 0, y: 0 }),
|
|
21
21
|
is_hovered = false,
|
|
22
22
|
is_selected = false,
|
|
23
|
+
is_dimmed = false,
|
|
23
24
|
leader_line_threshold = 15,
|
|
24
25
|
...rest
|
|
25
26
|
}: Omit<SVGAttributes<SVGGElement>, `style` | `offset` | `origin` | `transform`> & {
|
|
@@ -29,10 +30,11 @@
|
|
|
29
30
|
hover?: HoverStyle
|
|
30
31
|
label?: LabelStyle
|
|
31
32
|
offset?: Point[`offset`]
|
|
32
|
-
point_tween?:
|
|
33
|
+
point_tween?: TweenOptions<XyObj>
|
|
33
34
|
origin?: XyObj
|
|
34
35
|
is_hovered?: boolean
|
|
35
36
|
is_selected?: boolean
|
|
37
|
+
is_dimmed?: boolean
|
|
36
38
|
leader_line_threshold?: number
|
|
37
39
|
} = $props()
|
|
38
40
|
|
|
@@ -46,7 +48,7 @@
|
|
|
46
48
|
|
|
47
49
|
let marker_path = $derived.by(get_symbol_path)
|
|
48
50
|
|
|
49
|
-
const default_tween_props:
|
|
51
|
+
const default_tween_props: TweenOptions<XyObj> = {
|
|
50
52
|
duration: 600,
|
|
51
53
|
easing: cubicOut,
|
|
52
54
|
}
|
|
@@ -92,6 +94,7 @@
|
|
|
92
94
|
fill="var(--point-fill-color, {style.fill ?? `black`})"
|
|
93
95
|
class="marker"
|
|
94
96
|
class:is-hovered={is_hovered && (hover.enabled ?? true)}
|
|
97
|
+
class:is-dimmed={is_dimmed}
|
|
95
98
|
style:cursor={style.cursor}
|
|
96
99
|
/>
|
|
97
100
|
{#if label.text}
|
|
@@ -154,6 +157,9 @@
|
|
|
154
157
|
stroke-width: var(--hover-stroke-width);
|
|
155
158
|
filter: brightness(var(--hover-brightness));
|
|
156
159
|
}
|
|
160
|
+
.marker.is-dimmed {
|
|
161
|
+
opacity: var(--scatter-point-dimmed-opacity, 0.25);
|
|
162
|
+
}
|
|
157
163
|
.effect-ring {
|
|
158
164
|
pointer-events: none;
|
|
159
165
|
animation: ring-pulse var(--effect-ring-duration, 1s) ease-in-out
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { HoverStyle, LabelStyle, Point } from './';
|
|
2
|
-
import type { PointStyle,
|
|
2
|
+
import type { PointStyle, XyObj } from './types';
|
|
3
3
|
import type { SVGAttributes } from 'svelte/elements';
|
|
4
|
+
import { type TweenOptions } from 'svelte/motion';
|
|
4
5
|
type $$ComponentProps = Omit<SVGAttributes<SVGGElement>, `style` | `offset` | `origin` | `transform`> & {
|
|
5
6
|
x: number;
|
|
6
7
|
y: number;
|
|
@@ -8,10 +9,11 @@ type $$ComponentProps = Omit<SVGAttributes<SVGGElement>, `style` | `offset` | `o
|
|
|
8
9
|
hover?: HoverStyle;
|
|
9
10
|
label?: LabelStyle;
|
|
10
11
|
offset?: Point[`offset`];
|
|
11
|
-
point_tween?:
|
|
12
|
+
point_tween?: TweenOptions<XyObj>;
|
|
12
13
|
origin?: XyObj;
|
|
13
14
|
is_hovered?: boolean;
|
|
14
15
|
is_selected?: boolean;
|
|
16
|
+
is_dimmed?: boolean;
|
|
15
17
|
leader_line_threshold?: number;
|
|
16
18
|
};
|
|
17
19
|
declare const ScatterPoint: import("svelte").Component<$$ComponentProps, {}, "origin" | "point_tween">;
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
let {
|
|
22
22
|
data,
|
|
23
23
|
show_counts = true,
|
|
24
|
+
show_legend = false,
|
|
24
25
|
orientation = `vertical`,
|
|
25
26
|
x_axis = {},
|
|
26
27
|
y_axis = {},
|
|
@@ -111,9 +112,9 @@
|
|
|
111
112
|
// Convert to BarSeries array, maintaining order of crystal systems
|
|
112
113
|
const result: BarSeries[] = []
|
|
113
114
|
for (const system of symmetry.CRYSTAL_SYSTEMS) {
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
116
|
-
const { x, y } =
|
|
115
|
+
const system_data = series_by_system.get(system)
|
|
116
|
+
if (system_data) {
|
|
117
|
+
const { x, y } = system_data
|
|
117
118
|
const color = symmetry.CRYSTAL_SYSTEM_COLORS[system]
|
|
118
119
|
result.push({ x, y, color, label: system, bar_width: 0.9, visible: true })
|
|
119
120
|
}
|
|
@@ -285,7 +286,7 @@
|
|
|
285
286
|
mode="overlay"
|
|
286
287
|
x_axis={x_axis_config}
|
|
287
288
|
y_axis={y_axis_config}
|
|
288
|
-
show_legend
|
|
289
|
+
{show_legend}
|
|
289
290
|
show_controls={false}
|
|
290
291
|
{tooltip}
|
|
291
292
|
{user_content}
|
|
@@ -15,7 +15,7 @@ export function compute_local_variance(values, window_size) {
|
|
|
15
15
|
if (len === 1)
|
|
16
16
|
return [0];
|
|
17
17
|
const half_window = Math.floor(window_size / 2);
|
|
18
|
-
const result =
|
|
18
|
+
const result = Array(len);
|
|
19
19
|
// Single pass for each index, no slice allocation (avoids O(n × window) allocations)
|
|
20
20
|
for (let idx = 0; idx < len; idx++) {
|
|
21
21
|
const start = Math.max(0, idx - half_window);
|
|
@@ -40,7 +40,7 @@ export function compute_local_variance(values, window_size) {
|
|
|
40
40
|
function compute_derivatives(values) {
|
|
41
41
|
if (values.length < 2)
|
|
42
42
|
return [];
|
|
43
|
-
const derivs =
|
|
43
|
+
const derivs = Array(values.length - 1);
|
|
44
44
|
for (let idx = 0; idx < values.length - 1; idx++) {
|
|
45
45
|
derivs[idx] = values[idx + 1] - values[idx];
|
|
46
46
|
}
|
|
@@ -216,7 +216,7 @@ export function detect_instability(x_values, y_values, config = {}) {
|
|
|
216
216
|
export function smooth_moving_average(values, window) {
|
|
217
217
|
if (values.length === 0 || window <= 1)
|
|
218
218
|
return [...values];
|
|
219
|
-
const result =
|
|
219
|
+
const result = Array(values.length);
|
|
220
220
|
const half_window = Math.floor(window / 2);
|
|
221
221
|
for (let idx = 0; idx < values.length; idx++) {
|
|
222
222
|
const start = Math.max(0, idx - half_window);
|
|
@@ -253,7 +253,7 @@ function compute_savgol_coefficients(window, order) {
|
|
|
253
253
|
const vtv_inv = invert_matrix(vtv);
|
|
254
254
|
if (!vtv_inv) {
|
|
255
255
|
// Fallback to uniform weights
|
|
256
|
-
return
|
|
256
|
+
return Array(size).fill(1 / size);
|
|
257
257
|
}
|
|
258
258
|
const vt = transpose(vandermonde);
|
|
259
259
|
const coeffs_matrix = multiply_matrices(vtv_inv, vt);
|
|
@@ -272,7 +272,7 @@ export function smooth_savitzky_golay(values, window, polynomial_order = DEFAULT
|
|
|
272
272
|
return [...values];
|
|
273
273
|
const coeffs = compute_savgol_coefficients(actual_window, polynomial_order);
|
|
274
274
|
const half = Math.floor(actual_window / 2);
|
|
275
|
-
const result =
|
|
275
|
+
const result = Array(values.length);
|
|
276
276
|
// Cache coefficient sum to avoid O(n × window) redundant reductions in loop
|
|
277
277
|
const coeffs_sum = coeffs.reduce((a, b) => a + b, 0);
|
|
278
278
|
for (let idx = 0; idx < values.length; idx++) {
|
|
@@ -355,7 +355,7 @@ export function remove_local_outliers(y_values, config = {}) {
|
|
|
355
355
|
iterations_used: 0,
|
|
356
356
|
};
|
|
357
357
|
}
|
|
358
|
-
let kept_mask =
|
|
358
|
+
let kept_mask = Array(len).fill(true);
|
|
359
359
|
let iterations_used = 0;
|
|
360
360
|
for (let iter = 0; iter < max_iterations; iter++) {
|
|
361
361
|
let removed_any = false;
|
|
@@ -592,7 +592,7 @@ export function clean_series(series, config = {}) {
|
|
|
592
592
|
}
|
|
593
593
|
}
|
|
594
594
|
// Build result series
|
|
595
|
-
const result_series =
|
|
595
|
+
const result_series = in_place ? series : { ...series };
|
|
596
596
|
result_series.x = x_arr;
|
|
597
597
|
result_series.y = y_arr;
|
|
598
598
|
if (metadata !== undefined)
|
|
@@ -791,7 +791,7 @@ function transpose(matrix) {
|
|
|
791
791
|
return [];
|
|
792
792
|
const rows = matrix.length;
|
|
793
793
|
const cols = matrix[0].length;
|
|
794
|
-
const result = Array.from({ length: cols }, () =>
|
|
794
|
+
const result = Array.from({ length: cols }, () => Array(rows).fill(0));
|
|
795
795
|
for (let row = 0; row < rows; row++) {
|
|
796
796
|
for (let col = 0; col < cols; col++) {
|
|
797
797
|
result[col][row] = matrix[row][col];
|
|
@@ -803,7 +803,7 @@ function multiply_matrices(a, b) {
|
|
|
803
803
|
const rows_a = a.length;
|
|
804
804
|
const cols_a = a[0]?.length ?? 0;
|
|
805
805
|
const cols_b = b[0]?.length ?? 0;
|
|
806
|
-
const result = Array.from({ length: rows_a }, () =>
|
|
806
|
+
const result = Array.from({ length: rows_a }, () => Array(cols_b).fill(0));
|
|
807
807
|
for (let row = 0; row < rows_a; row++) {
|
|
808
808
|
for (let col = 0; col < cols_b; col++) {
|
|
809
809
|
for (let k = 0; k < cols_a; k++) {
|
package/dist/plot/index.d.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
export interface TweenedOptions<T> {
|
|
2
|
-
delay?: number;
|
|
3
|
-
duration?: number | ((from: T, to: T) => number);
|
|
4
|
-
easing?: (t: number) => number;
|
|
5
|
-
interpolate?: (a: T, b: T) => (t: number) => T;
|
|
6
|
-
}
|
|
7
1
|
export { default as AxisLabel } from './AxisLabel.svelte';
|
|
8
2
|
export { default as BarPlot } from './BarPlot.svelte';
|
|
9
3
|
export { default as BarPlotControls } from './BarPlotControls.svelte';
|
package/dist/plot/scales.d.ts
CHANGED
|
@@ -16,8 +16,8 @@ export interface ArcsinhScale {
|
|
|
16
16
|
export type PlotScaleFn = ScaleContinuousNumeric<number, number> | ScaleTime<number, number> | ArcsinhScale;
|
|
17
17
|
export declare function scale_arcsinh(threshold?: number): ArcsinhScale;
|
|
18
18
|
export declare function generate_arcsinh_ticks(min: number, max: number, threshold?: number, count?: number): number[];
|
|
19
|
-
export declare function create_scale(scale_type: ScaleType, domain: [number, number],
|
|
20
|
-
export declare function create_time_scale(domain: [number, number],
|
|
19
|
+
export declare function create_scale(scale_type: ScaleType, domain: [number, number], output_range: [number, number]): ScaleContinuousNumeric<number, number> | ArcsinhScale;
|
|
20
|
+
export declare function create_time_scale(domain: [number, number], output_range: [number, number]): ScaleTime<number, number, never>;
|
|
21
21
|
export declare function generate_ticks(domain: [number, number], scale_type: ScaleType, ticks_option: TicksOption | undefined, scale_fn: PlotScaleFn, // D3 scale function with .ticks() method
|
|
22
22
|
options?: {
|
|
23
23
|
format?: string;
|
|
@@ -25,7 +25,7 @@ options?: {
|
|
|
25
25
|
interval_padding?: number;
|
|
26
26
|
}): number[];
|
|
27
27
|
export declare function calculate_domain(values: number[], scale_type?: ScaleType): [number, number];
|
|
28
|
-
export declare function get_nice_data_range(points: Point[], get_value: (p: Point) => number,
|
|
28
|
+
export declare function get_nice_data_range(points: Point[], get_value: (p: Point) => number, limits: [number | null, number | null], scale_type: ScaleType, padding_factor: number, is_time?: boolean): [number, number];
|
|
29
29
|
export declare function generate_log_ticks(min: number, max: number, ticks_option?: TicksOption): number[];
|
|
30
30
|
export declare function get_tick_label(tick_value: number, ticks_option: TicksOption | undefined): string | null;
|
|
31
31
|
export declare function create_color_scale(color_scale_config: {
|
package/dist/plot/scales.js
CHANGED
|
@@ -10,8 +10,8 @@ export function scale_arcsinh(threshold = 1) {
|
|
|
10
10
|
if (!Number.isFinite(threshold) || threshold <= 0) {
|
|
11
11
|
throw new Error(`arcsinh threshold must be a positive finite number, got ${threshold}`);
|
|
12
12
|
}
|
|
13
|
-
let
|
|
14
|
-
let
|
|
13
|
+
let current_domain = [0, 1];
|
|
14
|
+
let current_range = [0, 1];
|
|
15
15
|
// Forward transform: data value → arcsinh-space
|
|
16
16
|
const arcsinh_transform = (x) => Math.asinh(x / threshold);
|
|
17
17
|
// Inverse transform: arcsinh-space → data value
|
|
@@ -20,8 +20,8 @@ export function scale_arcsinh(threshold = 1) {
|
|
|
20
20
|
// Accepts Date for compatibility with D3 time scales
|
|
21
21
|
const scale = ((value) => {
|
|
22
22
|
const num_value = value instanceof Date ? value.getTime() : value;
|
|
23
|
-
const [d_min, d_max] =
|
|
24
|
-
const [r_min, r_max] =
|
|
23
|
+
const [d_min, d_max] = current_domain;
|
|
24
|
+
const [r_min, r_max] = current_range;
|
|
25
25
|
// Handle identical domain endpoints (degenerate case)
|
|
26
26
|
if (d_max === d_min)
|
|
27
27
|
return (r_min + r_max) / 2;
|
|
@@ -39,21 +39,21 @@ export function scale_arcsinh(threshold = 1) {
|
|
|
39
39
|
// Domain getter/setter
|
|
40
40
|
scale.domain = function (domain) {
|
|
41
41
|
if (domain === undefined)
|
|
42
|
-
return
|
|
43
|
-
|
|
42
|
+
return current_domain;
|
|
43
|
+
current_domain = domain;
|
|
44
44
|
return scale;
|
|
45
45
|
};
|
|
46
46
|
// Range getter/setter
|
|
47
|
-
scale.range = function (
|
|
48
|
-
if (
|
|
49
|
-
return
|
|
50
|
-
|
|
47
|
+
scale.range = function (output_range) {
|
|
48
|
+
if (output_range === undefined)
|
|
49
|
+
return current_range;
|
|
50
|
+
current_range = output_range;
|
|
51
51
|
return scale;
|
|
52
52
|
};
|
|
53
53
|
// Invert: screen position → data value
|
|
54
54
|
scale.invert = (value) => {
|
|
55
|
-
const [d_min, d_max] =
|
|
56
|
-
const [r_min, r_max] =
|
|
55
|
+
const [d_min, d_max] = current_domain;
|
|
56
|
+
const [r_min, r_max] = current_range;
|
|
57
57
|
// Handle identical domain endpoints (degenerate case)
|
|
58
58
|
if (d_max === d_min)
|
|
59
59
|
return (d_min + d_max) / 2;
|
|
@@ -71,13 +71,13 @@ export function scale_arcsinh(threshold = 1) {
|
|
|
71
71
|
// Copy the scale
|
|
72
72
|
scale.copy = () => {
|
|
73
73
|
const copy = scale_arcsinh(threshold);
|
|
74
|
-
copy.domain(
|
|
75
|
-
copy.range(
|
|
74
|
+
copy.domain(current_domain);
|
|
75
|
+
copy.range(current_range);
|
|
76
76
|
return copy;
|
|
77
77
|
};
|
|
78
78
|
// Generate nice ticks for arcsinh scale
|
|
79
79
|
scale.ticks = (count = 10) => {
|
|
80
|
-
return generate_arcsinh_ticks(
|
|
80
|
+
return generate_arcsinh_ticks(current_domain[0], current_domain[1], threshold, count);
|
|
81
81
|
};
|
|
82
82
|
scale.threshold = threshold;
|
|
83
83
|
return scale;
|
|
@@ -181,26 +181,26 @@ function generate_positive_arcsinh_ticks(min, max, threshold, count) {
|
|
|
181
181
|
// Note: Time scales are handled separately via create_time_scale() since ScaleTime
|
|
182
182
|
// has incompatible types (invert returns Date, not number). Use is_time_scale()
|
|
183
183
|
// to detect time mode and call create_time_scale() directly when needed.
|
|
184
|
-
export function create_scale(scale_type, domain,
|
|
184
|
+
export function create_scale(scale_type, domain, output_range) {
|
|
185
185
|
const [min_val, max_val] = domain;
|
|
186
186
|
const type_name = get_scale_type_name(scale_type);
|
|
187
187
|
if (type_name === `log`) {
|
|
188
188
|
return scaleLog()
|
|
189
189
|
.domain([Math.max(min_val, math.LOG_EPS), max_val])
|
|
190
|
-
.range(
|
|
190
|
+
.range(output_range);
|
|
191
191
|
}
|
|
192
192
|
if (type_name === `arcsinh`) {
|
|
193
193
|
const threshold = get_arcsinh_threshold(scale_type);
|
|
194
|
-
return scale_arcsinh(threshold).domain(domain).range(
|
|
194
|
+
return scale_arcsinh(threshold).domain(domain).range(output_range);
|
|
195
195
|
}
|
|
196
196
|
// For 'time' or 'linear', return linear scale (time scales need create_time_scale())
|
|
197
|
-
return scaleLinear().domain(domain).range(
|
|
197
|
+
return scaleLinear().domain(domain).range(output_range);
|
|
198
198
|
}
|
|
199
199
|
// Create a time scale for time-based data
|
|
200
|
-
export function create_time_scale(domain,
|
|
200
|
+
export function create_time_scale(domain, output_range) {
|
|
201
201
|
return scaleTime()
|
|
202
202
|
.domain([new Date(domain[0]), new Date(domain[1])])
|
|
203
|
-
.range(
|
|
203
|
+
.range(output_range);
|
|
204
204
|
}
|
|
205
205
|
// Unified tick generation function
|
|
206
206
|
export function generate_ticks(domain, scale_type, ticks_option, scale_fn, // D3 scale function with .ticks() method
|
|
@@ -276,8 +276,8 @@ export function calculate_domain(values, scale_type = `linear`) {
|
|
|
276
276
|
return type_name === `log` ? [Math.max(min_val, math.LOG_EPS), max_val] : [min_val, max_val];
|
|
277
277
|
}
|
|
278
278
|
// Advanced domain calculation with padding and nice boundaries (from ScatterPlot)
|
|
279
|
-
export function get_nice_data_range(points, get_value,
|
|
280
|
-
const [min, max] =
|
|
279
|
+
export function get_nice_data_range(points, get_value, limits, scale_type, padding_factor, is_time = false) {
|
|
280
|
+
const [min, max] = limits;
|
|
281
281
|
const [min_ext, max_ext] = extent(points, get_value);
|
|
282
282
|
let data_min = min ?? min_ext ?? 0;
|
|
283
283
|
let data_max = max ?? max_ext ?? 1;
|
|
@@ -422,14 +422,14 @@ export function create_color_scale(color_scale_config, auto_color_range) {
|
|
|
422
422
|
}
|
|
423
423
|
// Create an arcsinh-based color scale (custom sequential scale)
|
|
424
424
|
// Returns a D3-compatible scale with both getter and setter for domain
|
|
425
|
-
// Scale function reads from
|
|
425
|
+
// Scale function reads from closure state on each call for stable identity
|
|
426
426
|
function create_arcsinh_color_scale(interpolator, initial_domain, threshold) {
|
|
427
427
|
// Guard against extremely small thresholds that could cause precision issues
|
|
428
428
|
const safe_threshold = Math.max(threshold, Number.EPSILON);
|
|
429
|
-
let
|
|
430
|
-
// Single scale function that reads current
|
|
429
|
+
let current_domain = initial_domain;
|
|
430
|
+
// Single scale function that reads current domain on each call
|
|
431
431
|
const scale = ((value) => {
|
|
432
|
-
const [d_min, d_max] =
|
|
432
|
+
const [d_min, d_max] = current_domain;
|
|
433
433
|
// Handle identical domain endpoints - return middle of color range
|
|
434
434
|
if (d_max === d_min)
|
|
435
435
|
return interpolator(0.5);
|
|
@@ -443,8 +443,8 @@ function create_arcsinh_color_scale(interpolator, initial_domain, threshold) {
|
|
|
443
443
|
// Domain getter/setter for D3 compatibility - returns same scale instance
|
|
444
444
|
scale.domain = function (new_domain) {
|
|
445
445
|
if (new_domain === undefined)
|
|
446
|
-
return
|
|
447
|
-
|
|
446
|
+
return current_domain;
|
|
447
|
+
current_domain = new_domain;
|
|
448
448
|
return scale;
|
|
449
449
|
};
|
|
450
450
|
return scale;
|
package/dist/plot/types.d.ts
CHANGED
|
@@ -3,14 +3,10 @@ import type { Vec2, Vec3 } from '../math';
|
|
|
3
3
|
import type DraggablePane from '../overlays/DraggablePane.svelte';
|
|
4
4
|
import type { ComponentProps, Snippet } from 'svelte';
|
|
5
5
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
6
|
+
import type { TweenOptions } from 'svelte/motion';
|
|
7
|
+
export type { TweenOptions } from 'svelte/motion';
|
|
6
8
|
import type PlotLegend from './PlotLegend.svelte';
|
|
7
9
|
import type { TicksOption } from './scales';
|
|
8
|
-
export interface TweenedOptions<T> {
|
|
9
|
-
delay?: number;
|
|
10
|
-
duration?: number | ((from: T, to: T) => number);
|
|
11
|
-
easing?: (t: number) => number;
|
|
12
|
-
interpolate?: (a: T, b: T) => (t: number) => T;
|
|
13
|
-
}
|
|
14
10
|
export type XyObj = {
|
|
15
11
|
x: number;
|
|
16
12
|
y: number;
|
|
@@ -89,7 +85,7 @@ export interface PlotPoint<Metadata = Record<string, unknown>> extends Point<Met
|
|
|
89
85
|
point_hover?: HoverStyle;
|
|
90
86
|
point_label?: LabelStyle;
|
|
91
87
|
point_offset?: XyObj;
|
|
92
|
-
point_tween?:
|
|
88
|
+
point_tween?: TweenOptions<XyObj>;
|
|
93
89
|
}
|
|
94
90
|
export type Markers = `line` | `points` | `line+points` | `none`;
|
|
95
91
|
export interface DataSeries<Metadata = Record<string, unknown>> {
|
|
@@ -106,7 +102,7 @@ export interface DataSeries<Metadata = Record<string, unknown>> {
|
|
|
106
102
|
point_hover?: HoverStyle[] | HoverStyle;
|
|
107
103
|
point_label?: LabelStyle[] | LabelStyle;
|
|
108
104
|
point_offset?: XyObj[] | XyObj;
|
|
109
|
-
point_tween?:
|
|
105
|
+
point_tween?: TweenOptions<XyObj>;
|
|
110
106
|
visible?: boolean;
|
|
111
107
|
label?: string;
|
|
112
108
|
legend_group?: string;
|
|
@@ -217,7 +213,7 @@ export type HoverConfig = {
|
|
|
217
213
|
};
|
|
218
214
|
export type LegendConfig = Omit<ComponentProps<typeof PlotLegend>, `series_data` | `on_drag_start` | `on_drag` | `on_drag_end`> & {
|
|
219
215
|
margin?: number | Sides;
|
|
220
|
-
tween?:
|
|
216
|
+
tween?: TweenOptions<XyObj>;
|
|
221
217
|
responsive?: boolean;
|
|
222
218
|
draggable?: boolean;
|
|
223
219
|
axis_clearance?: number;
|
package/dist/rdf/calc-rdf.js
CHANGED
|
@@ -29,7 +29,7 @@ export function calculate_rdf(structure, options = {}) {
|
|
|
29
29
|
}
|
|
30
30
|
const bin_size = cutoff / n_bins;
|
|
31
31
|
const r = Array.from({ length: n_bins }, (_, idx) => (idx + 0.5) * bin_size);
|
|
32
|
-
const g_r =
|
|
32
|
+
const g_r = Array(n_bins).fill(0);
|
|
33
33
|
if (sites.length === 0)
|
|
34
34
|
return { r, g_r };
|
|
35
35
|
// Get occupancy weight for a site-species pair (supports mixed occupancy)
|
package/dist/sanitize.js
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
import DOMPurify from 'dompurify';
|
|
2
2
|
import { format_formula_html } from './phase-diagram/utils';
|
|
3
|
-
// SSR: provide a DOM for DOMPurify when no browser window exists (e.g. during vite build)
|
|
4
|
-
let ssr_window;
|
|
5
|
-
if (typeof globalThis.window === `undefined`) {
|
|
6
|
-
try {
|
|
7
|
-
const { Window } = await import(`happy-dom`);
|
|
8
|
-
ssr_window = new Window();
|
|
9
|
-
}
|
|
10
|
-
catch {
|
|
11
|
-
// happy-dom not available at runtime — get_purify() will fall back to pass-through
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
3
|
const SAFE_TAGS = [`a`, `b`, `i`, `em`, `strong`, `sub`, `sup`, `br`, `span`, `code`, `small`];
|
|
15
4
|
const SAFE_ATTRS = [`style`, `class`, `title`, `href`, `target`, `rel`];
|
|
16
5
|
// only allow safe CSS properties for text formatting
|
|
@@ -26,9 +15,9 @@ let purify;
|
|
|
26
15
|
function get_purify() {
|
|
27
16
|
if (purify !== undefined)
|
|
28
17
|
return purify;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
if (typeof globalThis.window === `undefined`)
|
|
19
|
+
return (purify = null);
|
|
20
|
+
const instance = DOMPurify();
|
|
32
21
|
if (typeof instance.sanitize !== `function`) {
|
|
33
22
|
purify = null;
|
|
34
23
|
return null;
|
|
@@ -68,12 +57,30 @@ function sanitize_svg_content(html, allowed_tags, allowed_attrs) {
|
|
|
68
57
|
return wrapped;
|
|
69
58
|
return wrapped.slice(open_end + 1, close_start);
|
|
70
59
|
}
|
|
60
|
+
const stringify_html_input = (html) => {
|
|
61
|
+
if (html == null)
|
|
62
|
+
return ``;
|
|
63
|
+
if (typeof html === `string`)
|
|
64
|
+
return html;
|
|
65
|
+
if (typeof html === `number`)
|
|
66
|
+
return Number.isNaN(html) ? `NaN` : `${html}`;
|
|
67
|
+
if (typeof html === `boolean` || typeof html === `bigint`)
|
|
68
|
+
return `${html}`;
|
|
69
|
+
if (typeof html !== `object`)
|
|
70
|
+
return ``;
|
|
71
|
+
try {
|
|
72
|
+
return JSON.stringify(html) ?? ``;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return ``;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
71
78
|
// Sanitize HTML string, allowing only safe formatting tags and links.
|
|
72
79
|
// Two-pass: happy-dom promotes dangerous children when a non-allowed parent is
|
|
73
80
|
// stripped (e.g. <div><script>…</script></div> → <script>…</script>). The first
|
|
74
81
|
// pass explicitly removes dangerous tags so they can't survive promotion.
|
|
75
82
|
export function sanitize_html(html) {
|
|
76
|
-
const str =
|
|
83
|
+
const str = stringify_html_input(html);
|
|
77
84
|
const dp = get_purify();
|
|
78
85
|
if (!dp)
|
|
79
86
|
return str;
|
package/dist/settings.d.ts
CHANGED
|
@@ -100,6 +100,8 @@ export interface SettingsConfig {
|
|
|
100
100
|
show_image_atoms: SettingType<boolean>;
|
|
101
101
|
sphere_segments: SettingType<number>;
|
|
102
102
|
bond_thickness: SettingType<number>;
|
|
103
|
+
auto_bond_order: SettingType<boolean>;
|
|
104
|
+
aromatic_display: SettingType<`aromatic` | `kekule`>;
|
|
103
105
|
show_bonds: SettingType<ShowBonds>;
|
|
104
106
|
bond_color: SettingType<string>;
|
|
105
107
|
bonding_strategy: SettingType<BondingStrategy>;
|
package/dist/settings.js
CHANGED
|
@@ -110,6 +110,15 @@ export const SETTINGS_CONFIG = {
|
|
|
110
110
|
minimum: 0.01,
|
|
111
111
|
maximum: 1.0,
|
|
112
112
|
},
|
|
113
|
+
auto_bond_order: {
|
|
114
|
+
value: false,
|
|
115
|
+
description: `Automatically perceive double/triple/aromatic bonds from geometry (main-group/organic; metals fall back to single)`,
|
|
116
|
+
},
|
|
117
|
+
aromatic_display: {
|
|
118
|
+
value: `aromatic`,
|
|
119
|
+
description: `How to render perceived aromatic rings`,
|
|
120
|
+
enum: { aromatic: `Aromatic (1.5)`, kekule: `Kekulé (alternating)` },
|
|
121
|
+
},
|
|
113
122
|
show_bonds: {
|
|
114
123
|
value: `always`,
|
|
115
124
|
description: `When to display bonds between atoms`,
|
|
@@ -243,11 +252,11 @@ export const SETTINGS_CONFIG = {
|
|
|
243
252
|
maximum: 5,
|
|
244
253
|
},
|
|
245
254
|
site_label_color: {
|
|
246
|
-
value: `#
|
|
255
|
+
value: `#111111`,
|
|
247
256
|
description: `Text color for atom labels`,
|
|
248
257
|
},
|
|
249
258
|
site_label_bg_color: {
|
|
250
|
-
value:
|
|
259
|
+
value: `color-mix(in srgb, #000000 0%, transparent)`,
|
|
251
260
|
description: `Background color for atom labels`,
|
|
252
261
|
},
|
|
253
262
|
site_label_padding: {
|
|
@@ -1111,7 +1120,7 @@ export const DEFAULTS = extract_values(SETTINGS_CONFIG);
|
|
|
1111
1120
|
// Helper to merge with defaults - handles nested structure
|
|
1112
1121
|
export const merge = (user) => ({
|
|
1113
1122
|
...DEFAULTS,
|
|
1114
|
-
...
|
|
1123
|
+
...user,
|
|
1115
1124
|
structure: merge_nested(DEFAULTS.structure, user?.structure),
|
|
1116
1125
|
trajectory: merge_nested(DEFAULTS.trajectory, user?.trajectory),
|
|
1117
1126
|
composition: merge_nested(DEFAULTS.composition, user?.composition),
|
|
@@ -234,17 +234,17 @@
|
|
|
234
234
|
|
|
235
235
|
// Collect all path segments across structures once (shared by strict checks and plotting)
|
|
236
236
|
let all_segments = $derived.by(() => {
|
|
237
|
-
const
|
|
237
|
+
const collected_segments: Record<string, [string, BaseBandStructure][]> = {}
|
|
238
238
|
for (const [label, bs] of Object.entries(band_structs_dict)) {
|
|
239
239
|
for (const branch of bs.branches) {
|
|
240
240
|
const start_label = bs.qpoints[branch.start_index]?.label ?? undefined
|
|
241
241
|
const end_label = bs.qpoints[branch.end_index]?.label ?? undefined
|
|
242
242
|
const segment_key = helpers.get_segment_key(start_label, end_label)
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
collected_segments[segment_key] ??= []
|
|
244
|
+
collected_segments[segment_key].push([label, bs])
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
|
-
return
|
|
247
|
+
return collected_segments
|
|
248
248
|
})
|
|
249
249
|
|
|
250
250
|
let num_structures = $derived(Object.keys(band_structs_dict).length)
|
|
@@ -626,8 +626,8 @@
|
|
|
626
626
|
}
|
|
627
627
|
// Range became invalid - clear parent's range to propagate reset
|
|
628
628
|
if (`range` in y_axis) {
|
|
629
|
-
const { range: _omit, ...
|
|
630
|
-
y_axis =
|
|
629
|
+
const { range: _omit, ...axis_without_range } = y_axis
|
|
630
|
+
y_axis = axis_without_range
|
|
631
631
|
}
|
|
632
632
|
})
|
|
633
633
|
|
|
@@ -69,10 +69,10 @@
|
|
|
69
69
|
// Only include range if it's valid (don't override child's auto-range with undefined)
|
|
70
70
|
bands_y_axis = shared_y_axis
|
|
71
71
|
? {
|
|
72
|
-
...
|
|
72
|
+
...bands_props.y_axis,
|
|
73
73
|
...(is_valid_range(base_range) && { range: base_range }),
|
|
74
74
|
}
|
|
75
|
-
: { ...
|
|
75
|
+
: { ...bands_props.y_axis }
|
|
76
76
|
})
|
|
77
77
|
|
|
78
78
|
// Propagate synced range to DOS y-axis (untrack current to avoid overwriting child zoom)
|
|
@@ -88,10 +88,10 @@
|
|
|
88
88
|
dos_y_axis = shared_y_axis
|
|
89
89
|
? {
|
|
90
90
|
label: ``,
|
|
91
|
-
...
|
|
91
|
+
...dos_props.y_axis,
|
|
92
92
|
...(is_valid_range(base_range) && { range: base_range }),
|
|
93
93
|
}
|
|
94
|
-
: { label: ``, ...
|
|
94
|
+
: { label: ``, ...dos_props.y_axis }
|
|
95
95
|
})
|
|
96
96
|
|
|
97
97
|
let hovered_frequency = $state<number | null>(null)
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
) return
|
|
132
132
|
// Only include range if it's valid (don't override child's auto-range with undefined)
|
|
133
133
|
bands_y_axis = {
|
|
134
|
-
...
|
|
134
|
+
...bands_props.y_axis,
|
|
135
135
|
...(helpers.is_valid_range(base_range) && { range: base_range }),
|
|
136
136
|
}
|
|
137
137
|
})
|
|
@@ -150,10 +150,10 @@
|
|
|
150
150
|
dos_y_axis = is_desktop
|
|
151
151
|
? {
|
|
152
152
|
label: ``,
|
|
153
|
-
...
|
|
153
|
+
...dos_props.y_axis,
|
|
154
154
|
...(helpers.is_valid_range(base_range) && { range: base_range }),
|
|
155
155
|
}
|
|
156
|
-
: { ...
|
|
156
|
+
: { ...dos_props.y_axis }
|
|
157
157
|
})
|
|
158
158
|
|
|
159
159
|
let hovered_frequency = $state<number | null>(null)
|
package/dist/spectral/Dos.svelte
CHANGED
|
@@ -370,8 +370,8 @@
|
|
|
370
370
|
}
|
|
371
371
|
// Range became invalid - clear parent's range to propagate reset
|
|
372
372
|
if (`range` in y_axis) {
|
|
373
|
-
const { range: _omit, ...
|
|
374
|
-
y_axis =
|
|
373
|
+
const { range: _omit, ...axis_without_range } = y_axis
|
|
374
|
+
y_axis = axis_without_range
|
|
375
375
|
}
|
|
376
376
|
})
|
|
377
377
|
|
package/dist/spectral/helpers.js
CHANGED
|
@@ -224,7 +224,7 @@ function apply_gaussian_smearing_core(freqs_or_energies, densities, sigma) {
|
|
|
224
224
|
const orig_sum = densities.reduce((acc, d) => acc + d, 0);
|
|
225
225
|
if (sigma <= 0 || orig_sum === 0)
|
|
226
226
|
return densities;
|
|
227
|
-
const smeared =
|
|
227
|
+
const smeared = Array(densities.length).fill(0);
|
|
228
228
|
const truncation_width = 4; // Truncate Gaussian at ±4σ (contribution < 0.01%)
|
|
229
229
|
for (let idx = 0; idx < freqs_or_energies.length; idx++) {
|
|
230
230
|
const energy = freqs_or_energies[idx];
|
|
@@ -176,8 +176,8 @@
|
|
|
176
176
|
function remap_element(from: ElementSymbol, to: ElementSymbol) {
|
|
177
177
|
if (from === to && element_mapping?.[from]) {
|
|
178
178
|
// Remove mapping if mapping back to original element
|
|
179
|
-
const { [from]:
|
|
180
|
-
element_mapping = Object.keys(
|
|
179
|
+
const { [from]: _removed_mapping, ...mapping } = element_mapping
|
|
180
|
+
element_mapping = Object.keys(mapping).length > 0 ? mapping : undefined
|
|
181
181
|
} else if (from !== to) {
|
|
182
182
|
element_mapping = { ...element_mapping, [from]: to }
|
|
183
183
|
}
|
|
@@ -202,8 +202,8 @@
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
function clear_element_radius(elem: ElementSymbol) {
|
|
205
|
-
const { [elem]:
|
|
206
|
-
element_radius_overrides =
|
|
205
|
+
const { [elem]: _removed_radius, ...radii } = element_radius_overrides ?? {}
|
|
206
|
+
element_radius_overrides = radii
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
const get_element_radius = (elem: ElementSymbol): number =>
|