matterviz 0.3.4 → 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 +5 -4
- 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/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 +22 -37
- 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/icons.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ export declare const ICON_DATA: {
|
|
|
60
60
|
readonly path: "M13 3a9 9 0 0 0-9 9H1l4 4l4-4H6c0-3.87 3.13-7 7-7s7 3.13 7 7s-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.95 8.95 0 0 0 13 21a9 9 0 0 0 0-18m-1 5v5l4.25 2.52l.77-1.28l-3.52-2.09V8z";
|
|
61
61
|
};
|
|
62
62
|
readonly Info: {
|
|
63
|
-
readonly viewBox: "
|
|
63
|
+
readonly viewBox: "1.5 1.5 21 21";
|
|
64
64
|
readonly path: "M11 9h2V7h-2m1 13c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8m0-18A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m-1 15h2v-6h-2z";
|
|
65
65
|
};
|
|
66
66
|
readonly Edit: {
|
|
@@ -244,12 +244,12 @@ export declare const ICON_DATA: {
|
|
|
244
244
|
readonly path: "<g fill=\"none\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"20\" r=\"3\"/><circle cx=\"6\" cy=\"6\" r=\"3\"/><circle cx=\"18\" cy=\"6\" r=\"3\"/><path d=\"M18 9v2c0 .6-.4 1-1 1H7c-.6 0-1-.4-1-1V9\"/><path d=\"M12 12v5\"/></g>";
|
|
245
245
|
};
|
|
246
246
|
readonly Fullscreen: {
|
|
247
|
-
readonly viewBox: "
|
|
248
|
-
readonly path: "
|
|
247
|
+
readonly viewBox: "1.25 1.25 21.5 21.5";
|
|
248
|
+
readonly path: "M3 3h7v2.5H5.5V10H3zm0 11h2.5v4.5H10V21H3zm11 7v-2.5h4.5V14H21v7zm4.5-11V5.5H14V3h7v7z";
|
|
249
249
|
};
|
|
250
250
|
readonly ExitFullscreen: {
|
|
251
|
-
readonly viewBox: "
|
|
252
|
-
readonly path: "M10
|
|
251
|
+
readonly viewBox: "1.25 1.25 21.5 21.5";
|
|
252
|
+
readonly path: "M10 4H7.5v4.5H4V11h6zm-2.5 16H10v-6H4v2.5h3.5zM20 14h-6v6h2.5v-3.5H20zm0-5.5h-3.5V4H14v7h6z";
|
|
253
253
|
};
|
|
254
254
|
readonly Weight: {
|
|
255
255
|
readonly viewBox: "0 0 24 24";
|
|
@@ -297,7 +297,7 @@ export declare const ICON_DATA: {
|
|
|
297
297
|
};
|
|
298
298
|
readonly Reset: {
|
|
299
299
|
readonly viewBox: "0 0 15 15";
|
|
300
|
-
readonly path: "M4.
|
|
300
|
+
readonly path: "M4.94 2.06a.62.62 0 0 1 0 .88L4.13 3.75H9a4.75 4.75 0 1 1 0 9.5H5a.62.62 0 0 1 0-1.24h4a3.51 3.51 0 1 0 0-7.02H4.13l.81.81a.62.62 0 1 1-.88.88l-2-2a.62.62 0 0 1 0-.88l2-2a.62.62 0 0 1 .88 0";
|
|
301
301
|
};
|
|
302
302
|
readonly Undo: {
|
|
303
303
|
readonly viewBox: "0 0 24 24";
|
package/dist/icons.js
CHANGED
|
@@ -61,7 +61,7 @@ export const ICON_DATA = {
|
|
|
61
61
|
path: `M13 3a9 9 0 0 0-9 9H1l4 4l4-4H6c0-3.87 3.13-7 7-7s7 3.13 7 7s-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.95 8.95 0 0 0 13 21a9 9 0 0 0 0-18m-1 5v5l4.25 2.52l.77-1.28l-3.52-2.09V8z`,
|
|
62
62
|
},
|
|
63
63
|
Info: {
|
|
64
|
-
viewBox: `
|
|
64
|
+
viewBox: `1.5 1.5 21 21`,
|
|
65
65
|
path: `M11 9h2V7h-2m1 13c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8m0-18A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m-1 15h2v-6h-2z`,
|
|
66
66
|
},
|
|
67
67
|
Edit: {
|
|
@@ -283,12 +283,12 @@ export const ICON_DATA = {
|
|
|
283
283
|
},
|
|
284
284
|
Fullscreen: {
|
|
285
285
|
// from BoxIcons by Atisa
|
|
286
|
-
viewBox: `
|
|
287
|
-
path: `
|
|
286
|
+
viewBox: `1.25 1.25 21.5 21.5`,
|
|
287
|
+
path: `M3 3h7v2.5H5.5V10H3zm0 11h2.5v4.5H10V21H3zm11 7v-2.5h4.5V14H21v7zm4.5-11V5.5H14V3h7v7z`,
|
|
288
288
|
},
|
|
289
289
|
ExitFullscreen: {
|
|
290
|
-
viewBox: `
|
|
291
|
-
path: `M10
|
|
290
|
+
viewBox: `1.25 1.25 21.5 21.5`,
|
|
291
|
+
path: `M10 4H7.5v4.5H4V11h6zm-2.5 16H10v-6H4v2.5h3.5zM20 14h-6v6h2.5v-3.5H20zm0-5.5h-3.5V4H14v7h6z`,
|
|
292
292
|
},
|
|
293
293
|
Weight: {
|
|
294
294
|
viewBox: `0 0 24 24`,
|
|
@@ -336,7 +336,7 @@ export const ICON_DATA = {
|
|
|
336
336
|
},
|
|
337
337
|
Reset: {
|
|
338
338
|
viewBox: `0 0 15 15`,
|
|
339
|
-
path: `M4.
|
|
339
|
+
path: `M4.94 2.06a.62.62 0 0 1 0 .88L4.13 3.75H9a4.75 4.75 0 1 1 0 9.5H5a.62.62 0 0 1 0-1.24h4a3.51 3.51 0 1 0 0-7.02H4.13l.81.81a.62.62 0 1 1-.88.88l-2-2a.62.62 0 0 1 0-.88l2-2a.62.62 0 0 1 .88 0`,
|
|
340
340
|
},
|
|
341
341
|
Undo: {
|
|
342
342
|
viewBox: `0 0 24 24`,
|
package/dist/io/decompress.js
CHANGED
|
@@ -42,25 +42,30 @@ export function decompress_file(file) {
|
|
|
42
42
|
const is_supported = Boolean(format && ![`zip`, `xz`, `bz2`].includes(format));
|
|
43
43
|
return new Promise((resolve, reject) => {
|
|
44
44
|
const reader = new FileReader();
|
|
45
|
-
reader.
|
|
45
|
+
reader.addEventListener(`load`, () => {
|
|
46
46
|
try {
|
|
47
|
-
const result =
|
|
47
|
+
const result = reader.result;
|
|
48
48
|
if (!result)
|
|
49
49
|
throw new Error(`Failed to read file`);
|
|
50
50
|
if (is_supported && format) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
if (!(result instanceof ArrayBuffer))
|
|
52
|
+
throw new Error(`Expected binary file data`);
|
|
53
|
+
decompress_data(result, format).then((content) => {
|
|
54
|
+
const filename = file.name.replace(COMPRESSION_EXTENSIONS_REGEX, ``);
|
|
55
|
+
resolve({ content, filename });
|
|
56
|
+
}, reject);
|
|
54
57
|
}
|
|
55
58
|
else {
|
|
59
|
+
if (typeof result !== `string`)
|
|
60
|
+
throw new Error(`Expected text file data`);
|
|
56
61
|
resolve({ content: result, filename: file.name });
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
64
|
catch (error) {
|
|
60
65
|
reject(error);
|
|
61
66
|
}
|
|
62
|
-
};
|
|
63
|
-
reader.
|
|
67
|
+
}, { once: true });
|
|
68
|
+
reader.addEventListener(`error`, () => reject(new Error(`Failed to read file ${file.name}`)));
|
|
64
69
|
if (is_supported)
|
|
65
70
|
reader.readAsArrayBuffer(file);
|
|
66
71
|
else
|
package/dist/io/export.js
CHANGED
|
@@ -11,6 +11,10 @@ function is_webgl_renderer_like(value) {
|
|
|
11
11
|
typeof renderer_obj.getSize === `function` &&
|
|
12
12
|
typeof renderer_obj.setSize === `function`);
|
|
13
13
|
}
|
|
14
|
+
function get_canvas_renderer(canvas) {
|
|
15
|
+
const renderer_val = canvas[`__renderer`];
|
|
16
|
+
return is_webgl_renderer_like(renderer_val) ? renderer_val : undefined;
|
|
17
|
+
}
|
|
14
18
|
// Capture a WebGL canvas as a PNG Blob at the given DPI.
|
|
15
19
|
// Temporarily adjusts renderer pixel ratio for high-res capture, then restores.
|
|
16
20
|
// Returns data directly (no browser download), suitable for programmatic capture
|
|
@@ -18,8 +22,7 @@ function is_webgl_renderer_like(value) {
|
|
|
18
22
|
// DPI is converted to a resolution multiplier relative to 72 DPI baseline, capped at 10x.
|
|
19
23
|
export function canvas_to_png_blob(canvas, png_dpi = 150, scene = null, camera = null) {
|
|
20
24
|
const resolution_multiplier = Math.min(png_dpi / 72, 10);
|
|
21
|
-
const
|
|
22
|
-
const renderer = is_webgl_renderer_like(renderer_val) ? renderer_val : undefined;
|
|
25
|
+
const renderer = get_canvas_renderer(canvas);
|
|
23
26
|
if (resolution_multiplier <= 1.1 || !renderer) {
|
|
24
27
|
if (renderer && scene && camera)
|
|
25
28
|
renderer.render(scene, camera);
|
|
@@ -157,7 +160,7 @@ export function svg_to_png_blob(svg_element, png_dpi = 150) {
|
|
|
157
160
|
const svg_data_url = URL.createObjectURL(svg_blob);
|
|
158
161
|
return new Promise((resolve, reject) => {
|
|
159
162
|
const img = new Image();
|
|
160
|
-
img.
|
|
163
|
+
img.addEventListener(`load`, () => {
|
|
161
164
|
try {
|
|
162
165
|
ctx.clearRect(0, 0, pixel_width, pixel_height);
|
|
163
166
|
ctx.drawImage(img, 0, 0, pixel_width, pixel_height);
|
|
@@ -174,11 +177,11 @@ export function svg_to_png_blob(svg_element, png_dpi = 150) {
|
|
|
174
177
|
finally {
|
|
175
178
|
URL.revokeObjectURL(svg_data_url);
|
|
176
179
|
}
|
|
177
|
-
};
|
|
178
|
-
img.
|
|
180
|
+
});
|
|
181
|
+
img.addEventListener(`error`, () => {
|
|
179
182
|
URL.revokeObjectURL(svg_data_url);
|
|
180
183
|
reject(new Error(`Failed to load SVG for PNG export`));
|
|
181
|
-
};
|
|
184
|
+
});
|
|
182
185
|
img.src = svg_data_url;
|
|
183
186
|
});
|
|
184
187
|
}
|
|
@@ -205,8 +208,7 @@ export async function export_trajectory_video(canvas, filename, options = {}) {
|
|
|
205
208
|
typeof MediaRecorder === `undefined` ||
|
|
206
209
|
!MediaRecorder.isTypeSupported(`video/webm;codecs=vp9`))
|
|
207
210
|
throw new Error(`WebM video recording not supported in this browser`);
|
|
208
|
-
const
|
|
209
|
-
const renderer = is_webgl_renderer_like(renderer_val) ? renderer_val : undefined;
|
|
211
|
+
const renderer = get_canvas_renderer(canvas);
|
|
210
212
|
// Store original renderer settings if changing resolution
|
|
211
213
|
let orig_pixel_ratio;
|
|
212
214
|
let orig_size;
|
|
@@ -231,10 +233,10 @@ export async function export_trajectory_video(canvas, filename, options = {}) {
|
|
|
231
233
|
mimeType: `video/webm;codecs=vp9`,
|
|
232
234
|
videoBitsPerSecond: bitrate,
|
|
233
235
|
});
|
|
234
|
-
recorder.
|
|
236
|
+
recorder.addEventListener(`dataavailable`, (event) => {
|
|
235
237
|
if (event.data.size > 0)
|
|
236
238
|
chunks.push(event.data);
|
|
237
|
-
};
|
|
239
|
+
});
|
|
238
240
|
const track = stream.getVideoTracks()[0];
|
|
239
241
|
// Start recording
|
|
240
242
|
recorder.start();
|
|
@@ -269,7 +271,7 @@ export async function export_trajectory_video(canvas, filename, options = {}) {
|
|
|
269
271
|
// Finalize recording
|
|
270
272
|
return new Promise((resolve, reject) => {
|
|
271
273
|
let is_resolved = false;
|
|
272
|
-
recorder.
|
|
274
|
+
recorder.addEventListener(`stop`, () => {
|
|
273
275
|
if (is_resolved)
|
|
274
276
|
return;
|
|
275
277
|
is_resolved = true;
|
|
@@ -283,14 +285,16 @@ export async function export_trajectory_video(canvas, filename, options = {}) {
|
|
|
283
285
|
catch (error) {
|
|
284
286
|
reject(error);
|
|
285
287
|
}
|
|
286
|
-
};
|
|
287
|
-
recorder.
|
|
288
|
+
});
|
|
289
|
+
recorder.addEventListener(`error`, (event) => {
|
|
288
290
|
if (is_resolved)
|
|
289
291
|
return;
|
|
290
292
|
is_resolved = true;
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
293
|
+
const error_msg = event instanceof ErrorEvent && event.error instanceof Error
|
|
294
|
+
? event.error.message
|
|
295
|
+
: event.type;
|
|
296
|
+
reject(new Error(`MediaRecorder error: ${error_msg}`));
|
|
297
|
+
});
|
|
294
298
|
// Stop recording with safety timeout
|
|
295
299
|
try {
|
|
296
300
|
recorder.stop();
|
package/dist/io/is-binary.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
// Simplified binary detection
|
|
2
|
-
export const is_binary = (content) =>
|
|
3
|
-
(content
|
|
4
|
-
|
|
5
|
-
(content.
|
|
2
|
+
export const is_binary = (content) => {
|
|
3
|
+
if (!content)
|
|
4
|
+
return false;
|
|
5
|
+
if (content.includes(`\0`))
|
|
6
|
+
return true;
|
|
7
|
+
let binary_char_count = 0;
|
|
8
|
+
let printable_ascii_count = 0;
|
|
9
|
+
for (let char_idx = 0; char_idx < content.length; char_idx += 1) {
|
|
10
|
+
const char_code = content.charCodeAt(char_idx);
|
|
11
|
+
if (char_code <= 8 ||
|
|
12
|
+
(char_code >= 14 && char_code <= 31) ||
|
|
13
|
+
(char_code >= 127 && char_code <= 255)) {
|
|
14
|
+
binary_char_count += 1;
|
|
15
|
+
}
|
|
16
|
+
if (char_code >= 32 && char_code <= 126)
|
|
17
|
+
printable_ascii_count += 1;
|
|
18
|
+
}
|
|
19
|
+
return (binary_char_count / content.length > 0.1 || printable_ascii_count / content.length < 0.7);
|
|
20
|
+
};
|
package/dist/isosurface/parse.js
CHANGED
|
@@ -87,16 +87,16 @@ function read_lines(text, pos, count) {
|
|
|
87
87
|
return { lines: result, next: pos };
|
|
88
88
|
}
|
|
89
89
|
function build_grid({ data, nx, ny, nz, divisor = 1, data_order = `z_fastest`, }) {
|
|
90
|
-
const grid =
|
|
90
|
+
const grid = Array(nx);
|
|
91
91
|
let [min_val, max_val, sum] = [Infinity, -Infinity, 0];
|
|
92
92
|
const total = nx * ny * nz;
|
|
93
93
|
const data_len = Math.min(data.length, total);
|
|
94
94
|
if (data_len === 0) {
|
|
95
95
|
// Empty data: return zeroed grid with neutral data_range
|
|
96
96
|
for (let ix = 0; ix < nx; ix++) {
|
|
97
|
-
const plane =
|
|
97
|
+
const plane = Array(ny);
|
|
98
98
|
for (let iy = 0; iy < ny; iy++)
|
|
99
|
-
plane[iy] =
|
|
99
|
+
plane[iy] = Array(nz).fill(0);
|
|
100
100
|
grid[ix] = plane;
|
|
101
101
|
}
|
|
102
102
|
return { grid, data_range: { min: 0, max: 0, abs_max: 0, mean: 0 } };
|
|
@@ -105,9 +105,9 @@ function build_grid({ data, nx, ny, nz, divisor = 1, data_order = `z_fastest`, }
|
|
|
105
105
|
// .cube convention: z varies fastest, then y, then x.
|
|
106
106
|
const ny_nz = ny * nz;
|
|
107
107
|
for (let ix = 0; ix < nx; ix++) {
|
|
108
|
-
const plane =
|
|
108
|
+
const plane = Array(ny);
|
|
109
109
|
for (let iy = 0; iy < ny; iy++) {
|
|
110
|
-
const row =
|
|
110
|
+
const row = Array(nz).fill(0);
|
|
111
111
|
const base = ix * ny_nz + iy * nz;
|
|
112
112
|
const row_end = Math.min(base + nz, data_len);
|
|
113
113
|
for (let flat_idx = base; flat_idx < row_end; flat_idx++) {
|
|
@@ -127,9 +127,9 @@ function build_grid({ data, nx, ny, nz, divisor = 1, data_order = `z_fastest`, }
|
|
|
127
127
|
else {
|
|
128
128
|
// VASP CHGCAR/ELFCAR/LOCPOT convention: x varies fastest, then y, then z.
|
|
129
129
|
for (let ix = 0; ix < nx; ix++) {
|
|
130
|
-
const plane =
|
|
130
|
+
const plane = Array(ny);
|
|
131
131
|
for (let iy = 0; iy < ny; iy++)
|
|
132
|
-
plane[iy] =
|
|
132
|
+
plane[iy] = Array(nz).fill(0);
|
|
133
133
|
grid[ix] = plane;
|
|
134
134
|
}
|
|
135
135
|
let [flat_idx, data_exhausted] = [0, false];
|
|
@@ -504,7 +504,7 @@ export function parse_cube(content, options = {}) {
|
|
|
504
504
|
function atomic_number_to_symbol(atomic_number) {
|
|
505
505
|
// ELEM_SYMBOLS is 0-indexed (H at index 0), atomic numbers are 1-indexed
|
|
506
506
|
const idx = atomic_number - 1;
|
|
507
|
-
return
|
|
507
|
+
return idx >= 0 && idx < ELEM_SYMBOLS.length ? ELEM_SYMBOLS[idx] : `H`;
|
|
508
508
|
}
|
|
509
509
|
// Auto-detect and parse volumetric file format based on filename and content
|
|
510
510
|
export function parse_volumetric_file(content, filename) {
|
package/dist/isosurface/types.js
CHANGED
|
@@ -50,12 +50,12 @@ export function pad_periodic_grid(grid, dims, pad_fraction) {
|
|
|
50
50
|
const out_ny = ny + 2 * py;
|
|
51
51
|
const out_nz = nz + 2 * pz;
|
|
52
52
|
const wrap = (val, size) => ((val % size) + size) % size;
|
|
53
|
-
const out =
|
|
53
|
+
const out = Array(out_nx);
|
|
54
54
|
for (let ix = 0; ix < out_nx; ix++) {
|
|
55
|
-
const plane =
|
|
55
|
+
const plane = Array(out_ny);
|
|
56
56
|
const src_x = wrap(ix - px, nx);
|
|
57
57
|
for (let iy = 0; iy < out_ny; iy++) {
|
|
58
|
-
const row =
|
|
58
|
+
const row = Array(out_nz);
|
|
59
59
|
const src_y = wrap(iy - py, ny);
|
|
60
60
|
for (let iz = 0; iz < out_nz; iz++) {
|
|
61
61
|
row[iz] = grid[src_x][src_y][wrap(iz - pz, nz)];
|
|
@@ -110,12 +110,12 @@ export function downsample_grid(grid, dims, max_points = MAX_GRID_POINTS) {
|
|
|
110
110
|
const x_ranges = partition(new_nx, nx);
|
|
111
111
|
const y_ranges = partition(new_ny, ny);
|
|
112
112
|
const z_ranges = partition(new_nz, nz);
|
|
113
|
-
const out =
|
|
113
|
+
const out = Array(new_nx);
|
|
114
114
|
for (let ix = 0; ix < new_nx; ix++) {
|
|
115
|
-
const plane =
|
|
115
|
+
const plane = Array(new_ny);
|
|
116
116
|
const [sx_start, sx_end] = x_ranges[ix];
|
|
117
117
|
for (let iy = 0; iy < new_ny; iy++) {
|
|
118
|
-
const row =
|
|
118
|
+
const row = Array(new_nz);
|
|
119
119
|
const [sy_start, sy_end] = y_ranges[iy];
|
|
120
120
|
for (let iz = 0; iz < new_nz; iz++) {
|
|
121
121
|
let sum = 0;
|
|
@@ -203,12 +203,12 @@ export function tile_volumetric_data(volume, scaling) {
|
|
|
203
203
|
const new_nx = nx * sx;
|
|
204
204
|
const new_ny = ny * sy;
|
|
205
205
|
const new_nz = nz * sz;
|
|
206
|
-
const new_grid =
|
|
206
|
+
const new_grid = Array(new_nx);
|
|
207
207
|
for (let ix = 0; ix < new_nx; ix++) {
|
|
208
|
-
const plane =
|
|
208
|
+
const plane = Array(new_ny);
|
|
209
209
|
const src_x = ix % nx;
|
|
210
210
|
for (let iy = 0; iy < new_ny; iy++) {
|
|
211
|
-
const row =
|
|
211
|
+
const row = Array(new_nz);
|
|
212
212
|
const src_y = iy % ny;
|
|
213
213
|
const src_row = src_grid[src_x][src_y];
|
|
214
214
|
for (let iz = 0; iz < new_nz; iz++) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import Icon from '../Icon.svelte'
|
|
3
3
|
import { sanitize_html } from '../sanitize'
|
|
4
4
|
import type { Snippet } from 'svelte'
|
|
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 { InfoTagSize, InfoTagVariant } from './index'
|
|
8
8
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Icon from '../../Icon.svelte'
|
|
3
3
|
import { getContext, onMount } from 'svelte'
|
|
4
|
+
// oxlint-disable-next-line import/no-self-import -- recursive Svelte component
|
|
4
5
|
import JsonNode from './JsonNode.svelte'
|
|
5
6
|
import JsonValue from './JsonValue.svelte'
|
|
6
7
|
import type { JsonTreeContext } from './types'
|
|
@@ -273,7 +273,8 @@ export function find_matching_paths(value, query, current_path = ``, current_key
|
|
|
273
273
|
for_each_child(value, type, (child_value, key, map_key) => {
|
|
274
274
|
const child_path = build_path(current_path, key);
|
|
275
275
|
// Also check if Map key matches
|
|
276
|
-
if (map_key !== undefined &&
|
|
276
|
+
if (map_key !== undefined &&
|
|
277
|
+
serialize_for_copy(map_key).toLowerCase().includes(lower_query)) {
|
|
277
278
|
matches.add(child_path);
|
|
278
279
|
}
|
|
279
280
|
for (const match of find_matching_paths(child_value, query, child_path, key, seen)) {
|
package/dist/marching-cubes.js
CHANGED
|
@@ -432,7 +432,7 @@ export function marching_cubes(grid, iso_value, k_lattice, options = {}) {
|
|
|
432
432
|
const max_y = periodic ? ny : ny - 1;
|
|
433
433
|
const max_z = periodic ? nz : nz - 1;
|
|
434
434
|
// Preallocate cube_values array (reuse across iterations)
|
|
435
|
-
const cube_values =
|
|
435
|
+
const cube_values = Array(8);
|
|
436
436
|
for (let ix = 0; ix < max_x; ix++) {
|
|
437
437
|
const ix_row = grid[ix];
|
|
438
438
|
const ix1_row = grid[(ix + 1) % nx];
|
package/dist/math.js
CHANGED
|
@@ -222,7 +222,7 @@ export function dot(vec1, vec2) {
|
|
|
222
222
|
if (mat1_cols !== mat2.length) {
|
|
223
223
|
throw new Error(`First matrix columns must equal second matrix rows`);
|
|
224
224
|
}
|
|
225
|
-
return mat1.map((
|
|
225
|
+
return mat1.map((_row, ii) => Array.from({ length: mat2_cols }, (_col, jj) => mat1[ii].reduce((sum, _val, kk) => sum + mat1[ii][kk] * mat2[kk][jj], 0)));
|
|
226
226
|
}
|
|
227
227
|
throw new Error(`Unsupported input types for dot product`);
|
|
228
228
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '../Icon.svelte'
|
|
3
|
+
import type { HTMLButtonAttributes } from 'svelte/elements'
|
|
4
|
+
|
|
5
|
+
let { copied = false, label, title = label, onclick, ...rest }: Omit<
|
|
6
|
+
HTMLButtonAttributes,
|
|
7
|
+
`type` | `aria-label`
|
|
8
|
+
> & { copied?: boolean; label: string } = $props()
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<button
|
|
12
|
+
{...rest}
|
|
13
|
+
type="button"
|
|
14
|
+
class="copy-button {rest.class ?? ``}"
|
|
15
|
+
aria-label={label}
|
|
16
|
+
{title}
|
|
17
|
+
{onclick}
|
|
18
|
+
>
|
|
19
|
+
<Icon icon={copied ? `Check` : `Copy`} />
|
|
20
|
+
</button>
|
|
21
|
+
|
|
22
|
+
<style>
|
|
23
|
+
.copy-button {
|
|
24
|
+
display: inline-grid;
|
|
25
|
+
place-items: center;
|
|
26
|
+
flex: 0 0 auto;
|
|
27
|
+
width: 1.6em;
|
|
28
|
+
height: 1.6em;
|
|
29
|
+
border: 0;
|
|
30
|
+
border-radius: var(--border-radius, 3pt);
|
|
31
|
+
background: color-mix(in srgb, currentColor 8%, transparent);
|
|
32
|
+
color: inherit;
|
|
33
|
+
cursor: pointer;
|
|
34
|
+
opacity: 0.75;
|
|
35
|
+
padding: 0;
|
|
36
|
+
&:is(:hover, :focus-visible) {
|
|
37
|
+
opacity: 1;
|
|
38
|
+
background: color-mix(in srgb, currentColor 14%, transparent);
|
|
39
|
+
}
|
|
40
|
+
:global(svg) {
|
|
41
|
+
width: 0.9em;
|
|
42
|
+
height: 0.9em;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
</style>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
2
|
+
type $$ComponentProps = Omit<HTMLButtonAttributes, `type` | `aria-label`> & {
|
|
3
|
+
copied?: boolean;
|
|
4
|
+
label: string;
|
|
5
|
+
};
|
|
6
|
+
declare const CopyButton: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
|
+
type CopyButton = ReturnType<typeof CopyButton>;
|
|
8
|
+
export default CopyButton;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { sanitize_html } from '../sanitize'
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements'
|
|
4
|
+
import { SvelteSet } from 'svelte/reactivity'
|
|
5
|
+
import CopyButton from './CopyButton.svelte'
|
|
6
|
+
|
|
7
|
+
type InfoPaneRow = {
|
|
8
|
+
label: string
|
|
9
|
+
value: string | number
|
|
10
|
+
key?: string
|
|
11
|
+
tooltip?: string
|
|
12
|
+
}
|
|
13
|
+
type InfoPaneCard = {
|
|
14
|
+
title: string
|
|
15
|
+
rows: InfoPaneRow[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
cards,
|
|
20
|
+
filter_placeholder,
|
|
21
|
+
empty_label,
|
|
22
|
+
show_filter = true,
|
|
23
|
+
heading_level = 4,
|
|
24
|
+
row_label_min = `5em`,
|
|
25
|
+
...rest
|
|
26
|
+
}: HTMLAttributes<HTMLDivElement> & {
|
|
27
|
+
cards: InfoPaneCard[]
|
|
28
|
+
filter_placeholder: string
|
|
29
|
+
empty_label: string
|
|
30
|
+
show_filter?: boolean
|
|
31
|
+
heading_level?: 4 | 5
|
|
32
|
+
row_label_min?: string
|
|
33
|
+
} = $props()
|
|
34
|
+
|
|
35
|
+
let info_filter = $state(``)
|
|
36
|
+
const copied_items = new SvelteSet<string>()
|
|
37
|
+
const row_key = (card_title: string, row: InfoPaneRow, row_idx: number): string =>
|
|
38
|
+
row.key ?? `${card_title}:${row.label}:${row.value}:${row_idx}`
|
|
39
|
+
|
|
40
|
+
let filtered_cards = $derived.by(() => {
|
|
41
|
+
const filter = info_filter.trim().toLowerCase()
|
|
42
|
+
if (!filter) return cards
|
|
43
|
+
return cards
|
|
44
|
+
.map((card) => ({
|
|
45
|
+
...card,
|
|
46
|
+
rows: card.rows.filter(({ label, value }) =>
|
|
47
|
+
`${card.title} ${label} ${value}`.toLowerCase().includes(filter)
|
|
48
|
+
),
|
|
49
|
+
}))
|
|
50
|
+
.filter(({ rows }) => rows.length > 0)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
async function copy_row(
|
|
54
|
+
card_title: string,
|
|
55
|
+
row: InfoPaneRow,
|
|
56
|
+
row_idx: number,
|
|
57
|
+
): Promise<void> {
|
|
58
|
+
const key = row_key(card_title, row, row_idx)
|
|
59
|
+
try {
|
|
60
|
+
await navigator.clipboard.writeText(`${row.label}: ${row.value}`)
|
|
61
|
+
copied_items.add(key)
|
|
62
|
+
setTimeout(() => copied_items.delete(key), 1000)
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(`Failed to copy to clipboard:`, error)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
</script>
|
|
68
|
+
{#if show_filter}
|
|
69
|
+
<input
|
|
70
|
+
class="info-filter"
|
|
71
|
+
type="search"
|
|
72
|
+
bind:value={info_filter}
|
|
73
|
+
placeholder={filter_placeholder}
|
|
74
|
+
aria-label={filter_placeholder}
|
|
75
|
+
/>
|
|
76
|
+
{/if}
|
|
77
|
+
|
|
78
|
+
{#if filtered_cards.length === 0}
|
|
79
|
+
<p class="empty-filter">No {empty_label} matches "{info_filter}".</p>
|
|
80
|
+
{:else}
|
|
81
|
+
<div {...rest} class="info-cards {rest.class ?? ``}" style:--row-label-min={row_label_min}>
|
|
82
|
+
{#each filtered_cards as card (card.title)}
|
|
83
|
+
<section class="info-card">
|
|
84
|
+
<svelte:element this={`h${heading_level}`}>{card.title}</svelte:element>
|
|
85
|
+
{#each card.rows as row, row_idx (row_key(card.title, row, row_idx))}
|
|
86
|
+
<div class="info-row" data-testid={row.key}>
|
|
87
|
+
<span>{@html sanitize_html(row.label)}</span>
|
|
88
|
+
<span title={row.tooltip}>{@html sanitize_html(row.value)}</span>
|
|
89
|
+
<CopyButton
|
|
90
|
+
label="Copy {row.label}: {row.value}"
|
|
91
|
+
title="Copy {row.label}"
|
|
92
|
+
copied={copied_items.has(row_key(card.title, row, row_idx))}
|
|
93
|
+
onclick={() => copy_row(card.title, row, row_idx)}
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
{/each}
|
|
97
|
+
</section>
|
|
98
|
+
{/each}
|
|
99
|
+
</div>
|
|
100
|
+
{/if}
|
|
101
|
+
|
|
102
|
+
<style>
|
|
103
|
+
.info-filter {
|
|
104
|
+
box-sizing: border-box;
|
|
105
|
+
width: 100%;
|
|
106
|
+
margin-bottom: 5pt;
|
|
107
|
+
padding: 4pt 6pt;
|
|
108
|
+
border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
|
|
109
|
+
border-radius: var(--border-radius, 3pt);
|
|
110
|
+
background: color-mix(in srgb, var(--pane-bg, Canvas) 88%, currentColor);
|
|
111
|
+
color: inherit;
|
|
112
|
+
}
|
|
113
|
+
.empty-filter {
|
|
114
|
+
margin: 0.25em 0;
|
|
115
|
+
opacity: 0.75;
|
|
116
|
+
}
|
|
117
|
+
.info-cards {
|
|
118
|
+
display: grid;
|
|
119
|
+
gap: 5pt;
|
|
120
|
+
}
|
|
121
|
+
.info-card {
|
|
122
|
+
padding: var(--info-card-padding, 5pt);
|
|
123
|
+
border-left: 3px solid var(--accent-color, currentColor);
|
|
124
|
+
border-radius: var(--border-radius, 3pt);
|
|
125
|
+
background: var(--info-card-bg, color-mix(in srgb, currentColor 4%, transparent));
|
|
126
|
+
:is(h4, h5) {
|
|
127
|
+
margin: 0 0 var(--info-card-heading-gap, 3pt);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
.info-row {
|
|
131
|
+
display: grid;
|
|
132
|
+
grid-template-columns: minmax(var(--row-label-min), var(--row-label-max, 0.8fr)) minmax(
|
|
133
|
+
0,
|
|
134
|
+
1fr
|
|
135
|
+
) auto;
|
|
136
|
+
align-items: center;
|
|
137
|
+
gap: 5pt;
|
|
138
|
+
padding: var(--info-row-padding, 1pt 0);
|
|
139
|
+
line-height: 1.5;
|
|
140
|
+
span:first-child {
|
|
141
|
+
color: var(--info-row-label-color);
|
|
142
|
+
}
|
|
143
|
+
span:nth-child(2) {
|
|
144
|
+
overflow: hidden;
|
|
145
|
+
text-overflow: ellipsis;
|
|
146
|
+
white-space: nowrap;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
type InfoPaneRow = {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string | number;
|
|
5
|
+
key?: string;
|
|
6
|
+
tooltip?: string;
|
|
7
|
+
};
|
|
8
|
+
type InfoPaneCard = {
|
|
9
|
+
title: string;
|
|
10
|
+
rows: InfoPaneRow[];
|
|
11
|
+
};
|
|
12
|
+
type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
|
|
13
|
+
cards: InfoPaneCard[];
|
|
14
|
+
filter_placeholder: string;
|
|
15
|
+
empty_label: string;
|
|
16
|
+
show_filter?: boolean;
|
|
17
|
+
heading_level?: 4 | 5;
|
|
18
|
+
row_label_min?: string;
|
|
19
|
+
};
|
|
20
|
+
declare const InfoPaneCards: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
21
|
+
type InfoPaneCards = ReturnType<typeof InfoPaneCards>;
|
|
22
|
+
export default InfoPaneCards;
|