matterviz 0.3.5 → 0.3.7
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/MillerIndexInput.svelte +5 -5
- package/dist/api/optimade.js +3 -3
- package/dist/brillouin/BrillouinZone.svelte +5 -2
- package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneExportPane.svelte +1 -3
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +1 -1
- package/dist/brillouin/BrillouinZoneScene.svelte +5 -5
- package/dist/brillouin/compute.js +21 -21
- package/dist/brillouin/index.d.ts +1 -1
- package/dist/brillouin/index.js +0 -1
- package/dist/brillouin/types.d.ts +8 -13
- package/dist/chempot-diagram/ChemPotDiagram.svelte +3 -3
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +3 -4
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +33 -34
- package/dist/chempot-diagram/compute.js +1 -7
- package/dist/chempot-diagram/temperature.d.ts +1 -1
- package/dist/chempot-diagram/temperature.js +1 -3
- package/dist/chempot-diagram/types.d.ts +4 -9
- package/dist/colors/index.js +5 -5
- package/dist/composition/Composition.svelte +2 -1
- package/dist/composition/Formula.svelte +7 -4
- package/dist/composition/FormulaFilter.svelte +1 -3
- package/dist/composition/format.js +4 -4
- package/dist/composition/parse.d.ts +2 -1
- package/dist/composition/parse.js +61 -46
- package/dist/convex-hull/ConvexHull2D.svelte +62 -51
- package/dist/convex-hull/ConvexHull3D.svelte +101 -90
- package/dist/convex-hull/ConvexHull4D.svelte +70 -58
- package/dist/convex-hull/ConvexHullControls.svelte +24 -35
- package/dist/convex-hull/ConvexHullInfoPane.svelte +8 -5
- package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +2 -0
- package/dist/convex-hull/ConvexHullStats.svelte +9 -2
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +2 -0
- package/dist/convex-hull/GasPressureControls.svelte +7 -7
- package/dist/convex-hull/StructurePopup.svelte +65 -30
- package/dist/convex-hull/StructurePopup.svelte.d.ts +6 -6
- package/dist/convex-hull/TemperatureSlider.svelte +8 -5
- package/dist/convex-hull/barycentric-coords.d.ts +2 -2
- package/dist/convex-hull/barycentric-coords.js +2 -2
- package/dist/convex-hull/gas-thermodynamics.js +2 -4
- package/dist/convex-hull/helpers.d.ts +13 -2
- package/dist/convex-hull/helpers.js +37 -16
- package/dist/convex-hull/index.d.ts +1 -0
- package/dist/convex-hull/index.js +1 -0
- package/dist/convex-hull/thermodynamics.d.ts +2 -1
- package/dist/convex-hull/thermodynamics.js +7 -7
- package/dist/convex-hull/types.d.ts +15 -15
- package/dist/effects.svelte.d.ts +12 -0
- package/dist/effects.svelte.js +37 -0
- package/dist/element/BohrAtom.svelte +4 -4
- package/dist/element/data.json.gz.d.ts +3 -1
- package/dist/element/index.d.ts +1 -1
- package/dist/element/index.js +0 -1
- package/dist/fermi-surface/FermiSurface.svelte +4 -4
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte +15 -19
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +8 -6
- package/dist/fermi-surface/compute.js +2 -2
- package/dist/fermi-surface/export.js +13 -26
- package/dist/fermi-surface/parse.js +8 -12
- package/dist/fermi-surface/types.d.ts +2 -5
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +21 -3
- package/dist/heatmap-matrix/index.js +6 -6
- package/dist/io/decompress.d.ts +2 -1
- package/dist/io/decompress.js +1 -1
- package/dist/io/export.js +1 -1
- package/dist/io/index.d.ts +1 -1
- package/dist/io/index.js +0 -1
- package/dist/io/url-drop.js +7 -1
- package/dist/isosurface/IsosurfaceControls.svelte +11 -25
- package/dist/isosurface/slice.js +1 -1
- package/dist/isosurface/types.js +12 -12
- package/dist/labels.d.ts +1 -1
- package/dist/labels.js +14 -11
- package/dist/layout/InfoTag.svelte +6 -4
- package/dist/layout/PropertyFilter.svelte +4 -2
- package/dist/layout/json-tree/JsonTree.svelte +22 -14
- package/dist/layout/json-tree/JsonValue.svelte +2 -2
- package/dist/layout/json-tree/types.d.ts +3 -2
- package/dist/layout/json-tree/types.js +0 -1
- package/dist/layout/json-tree/utils.d.ts +4 -4
- package/dist/layout/json-tree/utils.js +12 -20
- package/dist/marching-cubes.js +13 -15
- package/dist/math.d.ts +11 -1
- package/dist/math.js +15 -6
- package/dist/overlays/DragControlTab.svelte +98 -0
- package/dist/overlays/DragControlTab.svelte.d.ts +8 -0
- package/dist/overlays/DraggablePane.svelte +7 -84
- package/dist/overlays/index.d.ts +1 -0
- package/dist/overlays/index.js +1 -0
- package/dist/periodic-table/PeriodicTable.svelte +11 -11
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +4 -2
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramControls.svelte +4 -9
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +2 -10
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +2 -3
- package/dist/phase-diagram/TdbInfoPanel.svelte +3 -3
- package/dist/phase-diagram/build-diagram.js +11 -18
- package/dist/phase-diagram/diagram-input.d.ts +5 -9
- package/dist/phase-diagram/index.d.ts +2 -2
- package/dist/phase-diagram/index.js +0 -2
- package/dist/phase-diagram/parse.d.ts +2 -2
- package/dist/phase-diagram/parse.js +6 -10
- package/dist/phase-diagram/svg-to-diagram.js +15 -15
- package/dist/phase-diagram/types.d.ts +5 -11
- package/dist/phase-diagram/utils.d.ts +2 -2
- package/dist/phase-diagram/utils.js +9 -11
- package/dist/plot/BarPlot.svelte +162 -314
- package/dist/plot/BarPlot.svelte.d.ts +5 -4
- package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/BinnedScatterPlot.svelte +1114 -0
- package/dist/plot/BinnedScatterPlot.svelte.d.ts +66 -0
- package/dist/plot/ColorBar.svelte +19 -17
- package/dist/plot/ColorBar.svelte.d.ts +1 -1
- package/dist/plot/FillArea.svelte +2 -4
- package/dist/plot/FillArea.svelte.d.ts +1 -1
- package/dist/plot/Histogram.svelte +167 -281
- package/dist/plot/Histogram.svelte.d.ts +1 -1
- package/dist/plot/HistogramControls.svelte.d.ts +1 -1
- package/dist/plot/InteractiveAxisLabel.svelte +5 -3
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
- package/dist/plot/PlotAxis.svelte +169 -0
- package/dist/plot/PlotAxis.svelte.d.ts +24 -0
- package/dist/plot/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/ReferenceLine3D.svelte +53 -51
- package/dist/plot/ReferencePlane.svelte +39 -42
- package/dist/plot/ScatterPlot.svelte +300 -367
- package/dist/plot/ScatterPlot.svelte.d.ts +8 -5
- package/dist/plot/ScatterPlot3D.svelte +33 -6
- package/dist/plot/ScatterPlot3D.svelte.d.ts +3 -2
- package/dist/plot/ScatterPlot3DControls.svelte +9 -9
- package/dist/plot/ScatterPlotControls.svelte +3 -4
- package/dist/plot/ScatterPoint.svelte +18 -27
- package/dist/plot/ScatterPoint.svelte.d.ts +4 -3
- package/dist/plot/Surface3D.svelte +4 -7
- package/dist/plot/ZeroLines.svelte +2 -1
- package/dist/plot/ZeroLines.svelte.d.ts +2 -1
- package/dist/plot/ZoomRect.svelte +2 -2
- package/dist/plot/ZoomRect.svelte.d.ts +3 -3
- package/dist/plot/adaptive-density.d.ts +69 -0
- package/dist/plot/adaptive-density.js +191 -0
- package/dist/plot/auto-place.d.ts +43 -0
- package/dist/plot/auto-place.js +122 -0
- package/dist/plot/axis-utils.js +3 -5
- package/dist/plot/binned-scatter-types.d.ts +59 -0
- package/dist/plot/binned-scatter-types.js +1 -0
- package/dist/plot/data-cleaning.js +1 -1
- package/dist/plot/data-transform.js +1 -1
- package/dist/plot/fill-utils.d.ts +4 -9
- package/dist/plot/fill-utils.js +29 -44
- package/dist/plot/index.d.ts +4 -0
- package/dist/plot/index.js +2 -0
- package/dist/plot/interactions.d.ts +4 -4
- package/dist/plot/interactions.js +4 -3
- package/dist/plot/layout.d.ts +20 -2
- package/dist/plot/layout.js +59 -16
- package/dist/plot/reference-line.d.ts +1 -1
- package/dist/plot/reference-line.js +9 -11
- package/dist/plot/scales.d.ts +1 -1
- package/dist/plot/scales.js +20 -23
- package/dist/plot/types.d.ts +30 -58
- package/dist/plot/types.js +2 -6
- package/dist/plot/utils/label-placement.d.ts +24 -3
- package/dist/plot/utils/label-placement.js +82 -12
- package/dist/plot/utils/series-visibility.d.ts +8 -2
- package/dist/plot/utils/series-visibility.js +23 -5
- package/dist/rdf/RdfPlot.svelte +5 -5
- package/dist/rdf/calc-rdf.js +3 -3
- package/dist/sanitize.d.ts +2 -0
- package/dist/sanitize.js +2 -0
- package/dist/spectral/Bands.svelte +1 -1
- package/dist/spectral/BandsAndDos.svelte +22 -16
- package/dist/spectral/BrillouinBandsDos.svelte +20 -16
- package/dist/spectral/Dos.svelte +1 -1
- package/dist/spectral/helpers.d.ts +4 -2
- package/dist/spectral/helpers.js +44 -35
- package/dist/spectral/index.d.ts +1 -1
- package/dist/spectral/index.js +0 -1
- package/dist/structure/AtomLegend.svelte +23 -6
- package/dist/structure/AtomLegend.svelte.d.ts +1 -0
- package/dist/structure/CanvasTooltip.svelte +9 -9
- package/dist/structure/CanvasTooltip.svelte.d.ts +1 -1
- package/dist/structure/CellSelect.svelte +14 -16
- package/dist/structure/Structure.svelte +317 -68
- package/dist/structure/Structure.svelte.d.ts +4 -2
- package/dist/structure/StructureControls.svelte +20 -45
- package/dist/structure/StructureExportPane.svelte +2 -1
- package/dist/structure/StructureInfoPane.svelte +10 -8
- package/dist/structure/StructureScene.svelte +527 -177
- package/dist/structure/StructureScene.svelte.d.ts +5 -2
- package/dist/structure/atom-properties.js +4 -4
- package/dist/structure/bond-order-perception.js +115 -98
- package/dist/structure/bonding.d.ts +27 -1
- package/dist/structure/bonding.js +187 -16
- package/dist/structure/export.js +1 -1
- package/dist/structure/index.d.ts +3 -2
- package/dist/structure/index.js +0 -2
- package/dist/structure/parse.js +88 -59
- package/dist/symmetry/WyckoffTable.svelte +7 -0
- package/dist/symmetry/index.js +13 -14
- package/dist/table/HeatmapTable.svelte +45 -66
- package/dist/table/HeatmapTable.svelte.d.ts +1 -1
- package/dist/table/ToggleMenu.svelte +19 -10
- package/dist/theme/themes.mjs +12 -0
- package/dist/tooltip/index.d.ts +1 -1
- package/dist/tooltip/index.js +0 -1
- package/dist/trajectory/Trajectory.svelte +43 -15
- package/dist/trajectory/TrajectoryInfoPane.svelte +2 -2
- package/dist/trajectory/extract.js +1 -1
- package/dist/trajectory/frame-reader.js +4 -4
- package/dist/trajectory/helpers.d.ts +5 -4
- package/dist/trajectory/helpers.js +9 -17
- package/dist/trajectory/index.d.ts +2 -2
- package/dist/trajectory/index.js +2 -2
- package/dist/trajectory/parse/ase.js +4 -4
- package/dist/trajectory/parse/hdf5.js +1 -1
- package/dist/trajectory/parse/index.js +2 -3
- package/dist/trajectory/parse/lammps.js +1 -1
- package/dist/trajectory/parse/vasp.js +1 -1
- package/dist/trajectory/plotting.d.ts +1 -1
- package/dist/trajectory/plotting.js +38 -38
- package/dist/trajectory/types.d.ts +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +9 -0
- package/dist/xrd/calc-xrd.js +3 -4
- package/dist/xrd/parse.js +1 -1
- package/package.json +42 -22
|
@@ -16,12 +16,15 @@ export function compute_hull_stability(raw_distance, exclude_from_hull, tol = HU
|
|
|
16
16
|
const e_above_hull = Math.abs(raw_distance) < tol ? 0 : Math.max(0, raw_distance);
|
|
17
17
|
return { e_above_hull, is_stable: e_above_hull <= tol };
|
|
18
18
|
}
|
|
19
|
+
export const entry_is_stable = (entry, tol = HULL_STABILITY_TOL) => entry.is_stable === true ||
|
|
20
|
+
(entry.is_stable !== false && Math.abs(entry.e_above_hull ?? Infinity) <= tol);
|
|
19
21
|
// Check if entry is on the convex hull (stable or e_above_hull ≈ 0)
|
|
20
|
-
export const is_on_hull = (entry, tol = HULL_STABILITY_TOL) => !entry.exclude_from_hull &&
|
|
21
|
-
(entry.is_stable === true ||
|
|
22
|
-
(typeof entry.e_above_hull === `number` && entry.e_above_hull < tol));
|
|
22
|
+
export const is_on_hull = (entry, tol = HULL_STABILITY_TOL) => !entry.exclude_from_hull && entry_is_stable(entry, tol);
|
|
23
23
|
export const get_arity = (entry) => Object.values(entry.composition).filter((count) => count > 0).length;
|
|
24
24
|
export const is_unary_entry = (entry) => get_arity(entry) === 1;
|
|
25
|
+
export const entry_is_unstable = (entry) => !entry_is_stable(entry);
|
|
26
|
+
export const entry_is_visible = (entry, show_stable, show_unstable) => (entry_is_stable(entry) ? show_stable : show_unstable);
|
|
27
|
+
export const visible_entries = (entries, show_stable, show_unstable) => entries.filter((entry) => entry_is_visible(entry, show_stable, show_unstable));
|
|
25
28
|
// Energy color scale factory (shared)
|
|
26
29
|
export function get_energy_color_scale(color_mode, color_scale, plot_entries) {
|
|
27
30
|
if (color_mode !== `energy` || plot_entries.length === 0)
|
|
@@ -43,7 +46,7 @@ export function get_energy_color_scale(color_mode, color_scale, plot_entries) {
|
|
|
43
46
|
}
|
|
44
47
|
// Point color resolver (shared)
|
|
45
48
|
export function get_point_color_for_entry(entry, color_mode, colors, energy_scale) {
|
|
46
|
-
const is_stable =
|
|
49
|
+
const is_stable = entry_is_stable(entry);
|
|
47
50
|
if (color_mode === `stability`) {
|
|
48
51
|
return is_stable ? colors?.stable || `#0072B2` : colors?.unstable || `#E69F00`;
|
|
49
52
|
}
|
|
@@ -95,6 +98,28 @@ export function compute_auto_hull_dist_threshold(n_entries, max_hull_dist_in_dat
|
|
|
95
98
|
const t = (n_entries - LOW) / (HIGH - LOW);
|
|
96
99
|
return max_hull_dist_in_data * (1 - t) + static_default * t;
|
|
97
100
|
}
|
|
101
|
+
export function auto_threshold_reset(default_threshold) {
|
|
102
|
+
let source;
|
|
103
|
+
let auto_threshold = default_threshold;
|
|
104
|
+
let initialized = false;
|
|
105
|
+
return (next_source, current_threshold, next_auto_threshold) => {
|
|
106
|
+
if (initialized && next_source === source)
|
|
107
|
+
return undefined;
|
|
108
|
+
const user_changed = initialized && Math.abs(current_threshold - auto_threshold) > 0.001;
|
|
109
|
+
source = next_source;
|
|
110
|
+
auto_threshold = next_auto_threshold;
|
|
111
|
+
initialized = true;
|
|
112
|
+
return user_changed ? undefined : next_auto_threshold;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export function current_entry(entry, entries) {
|
|
116
|
+
if (!entry)
|
|
117
|
+
return null;
|
|
118
|
+
if (entry.entry_id) {
|
|
119
|
+
return entries.find((candidate) => candidate.entry_id === entry.entry_id) ?? null;
|
|
120
|
+
}
|
|
121
|
+
return entries.includes(entry) ? entry : null;
|
|
122
|
+
}
|
|
98
123
|
// Build a tooltip text for any phase entry (shared)
|
|
99
124
|
export function build_entry_tooltip_text(entry) {
|
|
100
125
|
const is_element = is_unary_entry(entry);
|
|
@@ -119,7 +144,7 @@ export function build_entry_tooltip_text(entry) {
|
|
|
119
144
|
text += `E<sub>above hull</sub>: ${e_hull_str} eV/atom\n`;
|
|
120
145
|
}
|
|
121
146
|
// Fallback to energy_per_atom if e_form_per_atom is absent
|
|
122
|
-
const e_form_display = entry.e_form_per_atom
|
|
147
|
+
const e_form_display = entry.e_form_per_atom ?? entry.energy_per_atom;
|
|
123
148
|
if (e_form_display !== undefined) {
|
|
124
149
|
const e_form_str = format_num(e_form_display, `.3~`);
|
|
125
150
|
text += `E<sub>form</sub>: ${e_form_str} eV/atom`;
|
|
@@ -137,11 +162,9 @@ export function find_hull_entry_at_mouse(canvas, event, plot_entries, project_po
|
|
|
137
162
|
const mouse_y = event.clientY - rect.top;
|
|
138
163
|
const container_scale = Math.min(canvas.clientWidth || 600, canvas.clientHeight || 600) / 600;
|
|
139
164
|
for (const entry of plot_entries) {
|
|
140
|
-
if (!entry.visible)
|
|
141
|
-
continue;
|
|
142
165
|
const projected = project_point(entry.x, entry.y, entry.z);
|
|
143
166
|
const distance = Math.hypot(mouse_x - projected.x, mouse_y - projected.y);
|
|
144
|
-
const base = entry.size ?? (entry
|
|
167
|
+
const base = entry.size ?? (entry_is_stable(entry) ? 6 : 4);
|
|
145
168
|
if (distance < base * container_scale + 5)
|
|
146
169
|
return entry;
|
|
147
170
|
}
|
|
@@ -421,10 +444,10 @@ export function draw_highlight_effect(ctx, projected, size, container_scale, pul
|
|
|
421
444
|
if (effect === `pulse`) {
|
|
422
445
|
// Smooth pulsating effect with moderate size and opacity changes
|
|
423
446
|
const pulse_val = 0.5 + 0.5 * Math.sin(pulse_time * pulse_speed);
|
|
424
|
-
const hl_size = size * (size_multiplier + 0.5 * pulse_val);
|
|
425
|
-
const hl_opacity = opacity * (0.5 + 0.5 * pulse_val);
|
|
447
|
+
const hl_size = size * (size_multiplier + 0.5 * pulse_val);
|
|
448
|
+
const hl_opacity = opacity * (0.5 + 0.5 * pulse_val);
|
|
426
449
|
// Draw pulsating ring
|
|
427
|
-
ctx.lineWidth = (1.5 +
|
|
450
|
+
ctx.lineWidth = (1.5 + pulse_val) * container_scale;
|
|
428
451
|
ctx.beginPath();
|
|
429
452
|
ctx.arc(projected.x, projected.y, hl_size, 0, 2 * Math.PI);
|
|
430
453
|
ctx.fillStyle = apply_alpha_to_color(hl_color, hl_opacity * 0.3);
|
|
@@ -522,9 +545,7 @@ function entry_has_temp_data(entry) {
|
|
|
522
545
|
temperatures.length === free_energies.length);
|
|
523
546
|
}
|
|
524
547
|
// Check if entry has data at exact temperature T
|
|
525
|
-
export
|
|
526
|
-
return entry_has_temp_data(entry) && (entry.temperatures?.includes(T) ?? false);
|
|
527
|
-
}
|
|
548
|
+
export const entry_has_temperature = (entry, T) => entry_has_temp_data(entry) && (entry.temperatures?.includes(T) ?? false);
|
|
528
549
|
// Get energy at temperature T (throws if T not found - validate with entry_has_temperature first)
|
|
529
550
|
export function get_energy_at_temperature(entry, T) {
|
|
530
551
|
const temps = entry.temperatures ?? [];
|
|
@@ -625,7 +646,7 @@ export function filter_entries_at_temperature(entries, T, options = {}) {
|
|
|
625
646
|
// Analyze entries for gas-dependent elements (safe wrapper with optional config)
|
|
626
647
|
// Returns information about which gases are relevant for the chemical system.
|
|
627
648
|
export function safe_analyze_gas_data(entries, config) {
|
|
628
|
-
if (!config
|
|
649
|
+
if (!config?.enabled_gases?.length) {
|
|
629
650
|
return {
|
|
630
651
|
has_gas_dependent_elements: false,
|
|
631
652
|
gas_elements: [],
|
|
@@ -638,7 +659,7 @@ export function safe_analyze_gas_data(entries, config) {
|
|
|
638
659
|
// This adjusts formation energies based on gas atmosphere conditions (T, P).
|
|
639
660
|
// Should be applied after temperature filtering.
|
|
640
661
|
export function safe_apply_gas_corrections(entries, config, T) {
|
|
641
|
-
if (!config
|
|
662
|
+
if (!config?.enabled_gases?.length)
|
|
642
663
|
return entries;
|
|
643
664
|
return _apply_gas_corrections(entries, config, T);
|
|
644
665
|
}
|
|
@@ -11,6 +11,7 @@ export { default as ConvexHullControls } from './ConvexHullControls.svelte';
|
|
|
11
11
|
export { default as ConvexHullInfoPane } from './ConvexHullInfoPane.svelte';
|
|
12
12
|
export { default as ConvexHullStats } from './ConvexHullStats.svelte';
|
|
13
13
|
export { default as ConvexHullTooltip } from './ConvexHullTooltip.svelte';
|
|
14
|
+
export { default as StructurePopup } from './StructurePopup.svelte';
|
|
14
15
|
export * from './demo-temperature';
|
|
15
16
|
export * from './gas-thermodynamics';
|
|
16
17
|
export { default as GasPressureControls } from './GasPressureControls.svelte';
|
|
@@ -7,6 +7,7 @@ export { default as ConvexHullControls } from './ConvexHullControls.svelte';
|
|
|
7
7
|
export { default as ConvexHullInfoPane } from './ConvexHullInfoPane.svelte';
|
|
8
8
|
export { default as ConvexHullStats } from './ConvexHullStats.svelte';
|
|
9
9
|
export { default as ConvexHullTooltip } from './ConvexHullTooltip.svelte';
|
|
10
|
+
export { default as StructurePopup } from './StructurePopup.svelte';
|
|
10
11
|
export * from './demo-temperature';
|
|
11
12
|
export * from './gas-thermodynamics';
|
|
12
13
|
export { default as GasPressureControls } from './GasPressureControls.svelte';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ElementSymbol } from '../element';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Point2D, Point3D } from '../math';
|
|
3
|
+
import type { ConvexHullEntry, ConvexHullTriangle, PhaseData, PhaseStats, ProcessedPhaseData } from './types';
|
|
3
4
|
export declare function normalize_hull_composition_keys(composition: Record<string, number>): Partial<Record<ElementSymbol, number>>;
|
|
4
5
|
export declare function process_hull_entries(entries: PhaseData[]): ProcessedPhaseData;
|
|
5
6
|
export declare function compute_e_form_per_atom(entry: PhaseData, el_refs: Record<string, PhaseData>): number | null;
|
|
@@ -4,6 +4,8 @@ import { barycentric_to_ternary_xyz, barycentric_to_tetrahedral, composition_to_
|
|
|
4
4
|
import { get_arity, HULL_STABILITY_TOL, is_on_hull, is_unary_entry } from './helpers';
|
|
5
5
|
// Track warned keys to avoid log spam on large datasets with repeated invalid keys
|
|
6
6
|
const warned_keys = new Set();
|
|
7
|
+
const cross_point_2d = (origin, point_a, point_b) => (point_a.x - origin.x) * (point_b.y - origin.y) -
|
|
8
|
+
(point_a.y - origin.y) * (point_b.x - origin.x);
|
|
7
9
|
// Normalize convex hull composition keys by stripping oxidation states (e.g. "V4+" -> "V")
|
|
8
10
|
// and merging amounts for keys that map to the same element. Filters non-positive amounts.
|
|
9
11
|
// Only extracts FIRST valid element from each key (e.g. "Fe2O3" -> "Fe", not both Fe and O).
|
|
@@ -22,7 +24,7 @@ export function normalize_hull_composition_keys(composition) {
|
|
|
22
24
|
}
|
|
23
25
|
continue;
|
|
24
26
|
}
|
|
25
|
-
normalized[elem] = (normalized[elem]
|
|
27
|
+
normalized[elem] = (normalized[elem] ?? 0) + amount;
|
|
26
28
|
}
|
|
27
29
|
return normalized;
|
|
28
30
|
}
|
|
@@ -153,7 +155,7 @@ export function calculate_e_above_hull(input, reference_entries) {
|
|
|
153
155
|
const total = count_atoms_in_composition(ref.composition);
|
|
154
156
|
if (total <= 0)
|
|
155
157
|
continue;
|
|
156
|
-
const x = (ref.composition[el2]
|
|
158
|
+
const x = (ref.composition[el2] ?? 0) / total;
|
|
157
159
|
const current = hull_input_map.get(x);
|
|
158
160
|
if (current === undefined || e_form < current) {
|
|
159
161
|
hull_input_map.set(x, e_form);
|
|
@@ -178,7 +180,7 @@ export function calculate_e_above_hull(input, reference_entries) {
|
|
|
178
180
|
results[id] = NaN;
|
|
179
181
|
continue;
|
|
180
182
|
}
|
|
181
|
-
const x = (entry.composition[el2]
|
|
183
|
+
const x = (entry.composition[el2] ?? 0) / total;
|
|
182
184
|
const y_hull = interpolate_hull_2d(lower_hull, x);
|
|
183
185
|
results[id] = y_hull === null ? NaN : Math.max(0, e_form - y_hull);
|
|
184
186
|
}
|
|
@@ -438,7 +440,6 @@ export function get_convex_hull_stats(processed_entries, elements, max_arity = 4
|
|
|
438
440
|
function to_hull_entry(entry) {
|
|
439
441
|
return {
|
|
440
442
|
...entry,
|
|
441
|
-
visible: true,
|
|
442
443
|
is_element: get_arity(entry) === 1,
|
|
443
444
|
x: 0,
|
|
444
445
|
y: 0,
|
|
@@ -501,10 +502,9 @@ export function compute_lower_hull_2d(points) {
|
|
|
501
502
|
// Sort by x then y
|
|
502
503
|
const sorted = [...points].sort((p1, p2) => p1.x - p2.x || p1.y - p2.y);
|
|
503
504
|
const lower = [];
|
|
504
|
-
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
|
505
505
|
for (const point of sorted) {
|
|
506
506
|
while (lower.length >= 2 &&
|
|
507
|
-
|
|
507
|
+
cross_point_2d(lower[lower.length - 2], lower[lower.length - 1], point) <= 0)
|
|
508
508
|
lower.pop();
|
|
509
509
|
lower.push(point);
|
|
510
510
|
}
|
|
@@ -838,7 +838,7 @@ export const compute_e_above_hull_for_points = (points, models) => points.map((p
|
|
|
838
838
|
const z_hull = e_hull_at_xy(models, point.x, point.y);
|
|
839
839
|
if (z_hull === null)
|
|
840
840
|
return 0;
|
|
841
|
-
return point.z - z_hull;
|
|
841
|
+
return Math.max(0, point.z - z_hull);
|
|
842
842
|
});
|
|
843
843
|
const subtract_4d = (pt1, pt2) => ({
|
|
844
844
|
x: pt1.x - pt2.x,
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import type { CompositionType } from '../composition';
|
|
2
2
|
import type { ShowControlsProp } from '../controls';
|
|
3
3
|
import type { ElementSymbol } from '../element';
|
|
4
|
-
import type { Vec3 } from '../math';
|
|
5
|
-
import type { Sides } from '../plot';
|
|
6
|
-
import type {
|
|
4
|
+
import type { Point2D, Point3D, Vec3 } from '../math';
|
|
5
|
+
import type { Rect, Sides } from '../plot/layout';
|
|
6
|
+
import type { AnyStructure } from '../structure';
|
|
7
|
+
export interface StructurePopupStats {
|
|
8
|
+
id?: string;
|
|
9
|
+
formula?: string;
|
|
10
|
+
e_above_hull?: number;
|
|
11
|
+
e_form?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface StructurePopupContext {
|
|
14
|
+
structure: AnyStructure;
|
|
15
|
+
stats?: StructurePopupStats;
|
|
16
|
+
formula_html: string;
|
|
17
|
+
}
|
|
7
18
|
export interface PhaseData {
|
|
8
19
|
composition: CompositionType;
|
|
9
20
|
energy: number;
|
|
@@ -33,13 +44,6 @@ export interface ProcessedPhaseData {
|
|
|
33
44
|
elements: ElementSymbol[];
|
|
34
45
|
el_refs: Record<string, PhaseData>;
|
|
35
46
|
}
|
|
36
|
-
export interface Point2D {
|
|
37
|
-
x: number;
|
|
38
|
-
y: number;
|
|
39
|
-
}
|
|
40
|
-
export interface Point3D extends Point2D {
|
|
41
|
-
z: number;
|
|
42
|
-
}
|
|
43
47
|
export type MarkerSymbol = // Marker symbol types for convex hull entries
|
|
44
48
|
`circle` | `star` | `triangle` | `cross` | `diamond` | `square` | `wye`;
|
|
45
49
|
export type HullFaceColorMode = `uniform` | `formation_energy` | `dominant_element` | `facet_index`;
|
|
@@ -47,7 +51,6 @@ export declare const HULL_FACE_COLOR_MODES: readonly HullFaceColorMode[];
|
|
|
47
51
|
export interface ConvexHullEntry extends PhaseData, Point3D {
|
|
48
52
|
is_element: boolean;
|
|
49
53
|
size?: number;
|
|
50
|
-
visible: boolean;
|
|
51
54
|
marker?: MarkerSymbol;
|
|
52
55
|
}
|
|
53
56
|
export interface ConvexHullConfig {
|
|
@@ -103,10 +106,7 @@ export interface LabelPlacement {
|
|
|
103
106
|
}
|
|
104
107
|
export interface HoverData3D<T = ConvexHullEntry> {
|
|
105
108
|
entry: T;
|
|
106
|
-
position:
|
|
107
|
-
x: number;
|
|
108
|
-
y: number;
|
|
109
|
-
};
|
|
109
|
+
position: Point2D;
|
|
110
110
|
}
|
|
111
111
|
export interface PhaseStats {
|
|
112
112
|
total: number;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type PulseAnimationOptions = {
|
|
2
|
+
step?: number;
|
|
3
|
+
frequency?: number;
|
|
4
|
+
on_tick?: () => void;
|
|
5
|
+
reset_when_inactive?: boolean;
|
|
6
|
+
};
|
|
7
|
+
type PulseAnimation = {
|
|
8
|
+
readonly time: number;
|
|
9
|
+
readonly unit: number;
|
|
10
|
+
};
|
|
11
|
+
export declare function create_pulse_animation(active: () => boolean, options?: PulseAnimationOptions): PulseAnimation;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export function create_pulse_animation(active, options = {}) {
|
|
2
|
+
let time = $state(0);
|
|
3
|
+
let frame_id = null;
|
|
4
|
+
const { step = 0.02, frequency = 4, on_tick, reset_when_inactive = true } = options;
|
|
5
|
+
const cancel_frame = () => {
|
|
6
|
+
if (frame_id == null)
|
|
7
|
+
return;
|
|
8
|
+
cancelAnimationFrame(frame_id);
|
|
9
|
+
frame_id = null;
|
|
10
|
+
};
|
|
11
|
+
const stop = () => {
|
|
12
|
+
cancel_frame();
|
|
13
|
+
if (reset_when_inactive)
|
|
14
|
+
time = 0;
|
|
15
|
+
};
|
|
16
|
+
$effect(() => {
|
|
17
|
+
if (!active())
|
|
18
|
+
return stop();
|
|
19
|
+
const animate = () => {
|
|
20
|
+
time += step;
|
|
21
|
+
on_tick?.();
|
|
22
|
+
if (!active())
|
|
23
|
+
return stop();
|
|
24
|
+
frame_id = requestAnimationFrame(animate);
|
|
25
|
+
};
|
|
26
|
+
frame_id = requestAnimationFrame(animate);
|
|
27
|
+
return cancel_frame;
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
get time() {
|
|
31
|
+
return time;
|
|
32
|
+
},
|
|
33
|
+
get unit() {
|
|
34
|
+
return 0.5 + 0.5 * Math.sin(time * frequency);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -84,10 +84,10 @@
|
|
|
84
84
|
|
|
85
85
|
<!-- electron orbitals -->
|
|
86
86
|
{#each shells as electrons, shell_idx (`${shell_idx}-${electrons}`)}
|
|
87
|
-
{@const
|
|
88
|
-
{@const shell_radius = nucleus_svg_props.r +
|
|
89
|
-
{@const active =
|
|
90
|
-
<g class="shell" style:animation-duration="{orbital_period *
|
|
87
|
+
{@const shell_number = shell_idx + 1}
|
|
88
|
+
{@const shell_radius = nucleus_svg_props.r + shell_number * shell_width}
|
|
89
|
+
{@const active = shell_number === highlight_shell}
|
|
90
|
+
<g class="shell" style:animation-duration="{orbital_period * shell_number ** 1.5}s">
|
|
91
91
|
<circle
|
|
92
92
|
r={shell_radius}
|
|
93
93
|
{...shell_svg_props}
|
package/dist/element/index.d.ts
CHANGED
package/dist/element/index.js
CHANGED
|
@@ -165,8 +165,8 @@
|
|
|
165
165
|
|
|
166
166
|
// Yield to browser so spinner can render before heavy computation
|
|
167
167
|
const tick = () =>
|
|
168
|
-
new Promise<void>((
|
|
169
|
-
requestAnimationFrame(() => requestAnimationFrame(() =>
|
|
168
|
+
new Promise<void>((resolve) =>
|
|
169
|
+
requestAnimationFrame(() => requestAnimationFrame(() => resolve()))
|
|
170
170
|
)
|
|
171
171
|
|
|
172
172
|
// Parse and load Fermi surface from content (async for UI responsiveness)
|
|
@@ -339,8 +339,8 @@
|
|
|
339
339
|
})
|
|
340
340
|
|
|
341
341
|
function handle_keydown(event: KeyboardEvent) {
|
|
342
|
-
const target = event.target
|
|
343
|
-
if ([`INPUT`, `TEXTAREA`].includes(target.tagName)) return
|
|
342
|
+
const target = event.target
|
|
343
|
+
if (target instanceof HTMLElement && [`INPUT`, `TEXTAREA`].includes(target.tagName)) return
|
|
344
344
|
// Only handle shortcuts when component is focused/hovered or contains focus
|
|
345
345
|
if (!wrapper?.contains(document.activeElement) && !hovered) return
|
|
346
346
|
|
|
@@ -78,6 +78,6 @@ type $$ComponentProps = {
|
|
|
78
78
|
}]> | FermiTooltipConfig;
|
|
79
79
|
on_hover?: (data: FermiHoverData | null) => void;
|
|
80
80
|
} & HTMLAttributes<HTMLDivElement>;
|
|
81
|
-
declare const FermiSurface: import("svelte").Component<$$ComponentProps, {}, "height" | "width" | "dragover" | "
|
|
81
|
+
declare const FermiSurface: import("svelte").Component<$$ComponentProps, {}, "height" | "width" | "dragover" | "loading" | "fullscreen" | "hovered" | "controls_open" | "camera_projection" | "color_scale" | "wrapper" | "error_msg" | "surface_opacity" | "show_vectors" | "bz_data" | "mu" | "selected_bands" | "interpolation_factor" | "fermi_data" | "band_data" | "color_property" | "representation" | "show_bz" | "bz_opacity" | "tile_bz" | "clip_enabled" | "clip_axis" | "clip_position" | "clip_flip">;
|
|
82
82
|
type FermiSurface = ReturnType<typeof FermiSurface>;
|
|
83
83
|
export default FermiSurface;
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import SettingsSection from '../layout/SettingsSection.svelte'
|
|
3
3
|
import DraggablePane from '../overlays/DraggablePane.svelte'
|
|
4
4
|
import type { CameraProjection } from '../settings'
|
|
5
|
-
import {
|
|
5
|
+
import { make_change_detector } from '../utils'
|
|
6
|
+
import type { Snippet } from 'svelte'
|
|
6
7
|
import type {
|
|
7
8
|
BandGridData,
|
|
8
9
|
ColorProperty,
|
|
@@ -80,9 +81,9 @@
|
|
|
80
81
|
{ value: `interpolateSpectral`, label: `Spectral` },
|
|
81
82
|
]
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
let has_custom_color = $derived(
|
|
85
|
+
Boolean(custom_property_label) ||
|
|
86
|
+
(fermi_data?.isosurfaces?.some((iso) => iso.properties?.length) ?? false),
|
|
86
87
|
)
|
|
87
88
|
|
|
88
89
|
// Get unique band indices from Fermi surface data
|
|
@@ -93,18 +94,14 @@
|
|
|
93
94
|
)
|
|
94
95
|
: [],
|
|
95
96
|
)
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
let available_bands_key = $derived(available_bands.join(`,`))
|
|
98
|
+
const available_bands_changed = make_change_detector()
|
|
98
99
|
|
|
99
|
-
// Reset selected_bands when available_bands changes (new file loaded)
|
|
100
|
-
// This ensures band selection doesn't persist across different files
|
|
101
100
|
$effect(() => {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
// Only reset selected_bands when there are bands to select
|
|
107
|
-
if (available_bands.length > 0) selected_bands = [...available_bands]
|
|
101
|
+
if (color_property === `custom` && !has_custom_color) color_property = `band`
|
|
102
|
+
const bands_changed = available_bands_changed(available_bands_key)
|
|
103
|
+
if (available_bands.length > 0 && (selected_bands === undefined || bands_changed)) {
|
|
104
|
+
selected_bands = [...available_bands]
|
|
108
105
|
}
|
|
109
106
|
})
|
|
110
107
|
|
|
@@ -121,9 +118,8 @@
|
|
|
121
118
|
}
|
|
122
119
|
}
|
|
123
120
|
|
|
124
|
-
function handle_mu_change(event: Event) {
|
|
125
|
-
const
|
|
126
|
-
const trimmed = target.value.trim()
|
|
121
|
+
function handle_mu_change(event: Event & { currentTarget: HTMLInputElement }) {
|
|
122
|
+
const trimmed = event.currentTarget.value.trim()
|
|
127
123
|
const parsed = parseFloat(trimmed)
|
|
128
124
|
// Only update mu when input is valid; keep last valid value during transient
|
|
129
125
|
// invalid states (e.g. empty string while user is typing a new value)
|
|
@@ -216,7 +212,7 @@
|
|
|
216
212
|
<option value="band">Band</option>
|
|
217
213
|
<option value="velocity">Velocity</option>
|
|
218
214
|
<option value="spin">Spin</option>
|
|
219
|
-
{#if
|
|
215
|
+
{#if has_custom_color}
|
|
220
216
|
<option value="custom">{custom_property_label ?? `Custom`}</option>
|
|
221
217
|
{/if}
|
|
222
218
|
</select>
|
|
@@ -334,7 +330,7 @@
|
|
|
334
330
|
<select
|
|
335
331
|
value={interpolation_factor}
|
|
336
332
|
onchange={(event) => {
|
|
337
|
-
const val = parseFloat(
|
|
333
|
+
const val = parseFloat(event.currentTarget.value)
|
|
338
334
|
if (!Number.isFinite(val)) return
|
|
339
335
|
interpolation_factor = val
|
|
340
336
|
on_interpolation_change?.(val)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CameraProjection } from '../settings';
|
|
2
|
-
import {
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { BandGridData, ColorProperty, FermiSurfaceData, RepresentationMode } from './types';
|
|
4
4
|
type $$ComponentProps = {
|
|
5
5
|
controls_open?: boolean;
|
|
@@ -307,8 +307,8 @@
|
|
|
307
307
|
const e2: Vec3 = math.subtract(v2, v0)
|
|
308
308
|
const normal = math.cross_3d(e1, e2)
|
|
309
309
|
const len = Math.hypot(...normal)
|
|
310
|
-
const
|
|
311
|
-
normals.push(...
|
|
310
|
+
const unit_normal = len > 1e-10 ? normal.map((coord) => coord / len) : [0, 0, 1]
|
|
311
|
+
normals.push(...unit_normal, ...unit_normal, ...unit_normal)
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
// Per-vertex colors for this triangle
|
|
@@ -394,7 +394,9 @@
|
|
|
394
394
|
|
|
395
395
|
const computed_camera_position = $derived.by(
|
|
396
396
|
() =>
|
|
397
|
-
camera_position || ([10, 3, 8].map((
|
|
397
|
+
camera_position || ([10, 3, 8].map((coord) =>
|
|
398
|
+
coord * Math.max(1, scene_size)
|
|
399
|
+
) as Vec3),
|
|
398
400
|
)
|
|
399
401
|
|
|
400
402
|
const gizmo_props = $derived({
|
|
@@ -463,7 +465,7 @@
|
|
|
463
465
|
const e2: Vec3 = math.subtract(v2, v0)
|
|
464
466
|
const normal_vec = math.cross_3d(e1, e2)
|
|
465
467
|
const len = Math.hypot(...normal_vec)
|
|
466
|
-
const norm = len > 1e-10 ? normal_vec.map((
|
|
468
|
+
const norm = len > 1e-10 ? normal_vec.map((coord) => coord / len) : [0, 0, 0]
|
|
467
469
|
normals.push(...norm, ...norm, ...norm)
|
|
468
470
|
}
|
|
469
471
|
}
|
|
@@ -656,8 +658,8 @@
|
|
|
656
658
|
<!-- Reciprocal lattice vectors -->
|
|
657
659
|
{#if show_vectors && fermi_data?.k_lattice}
|
|
658
660
|
{#each fermi_data.k_lattice as vec, idx (idx)}
|
|
659
|
-
{@const scaled_vec = vec.map((
|
|
660
|
-
{@const label_position = scaled_vec.map((
|
|
661
|
+
{@const scaled_vec = vec.map((coord) => coord * vector_scale) as Vec3}
|
|
662
|
+
{@const label_position = scaled_vec.map((coord) => coord * 1.15) as Vec3}
|
|
661
663
|
<Arrow
|
|
662
664
|
position={[0, 0, 0]}
|
|
663
665
|
vector={scaled_vec}
|
|
@@ -3,6 +3,7 @@ import * as math from '../math';
|
|
|
3
3
|
import { EPS } from '../math';
|
|
4
4
|
import { CLOSED_CONTOUR_TOLERANCE, IRREDUCIBLE_BZ_MIN_VERTICES, IRREDUCIBLE_BZ_TOLERANCE, SPANNING_THRESHOLD, } from './constants';
|
|
5
5
|
import { marching_cubes } from './marching-cubes';
|
|
6
|
+
const safe_mod = (val, dim) => ((val % dim) + dim) % dim;
|
|
6
7
|
// Precompute Catmull-Rom coefficients for a given t value
|
|
7
8
|
// Returns [c0, c1, c2, c3] where result = c0*p0 + c1*p1 + c2*p2 + c3*p3
|
|
8
9
|
function catmull_rom_coeffs(t) {
|
|
@@ -238,7 +239,6 @@ function trilinear_interpolate_vec3(grid, x, y, z) {
|
|
|
238
239
|
if (nx === 0 || ny === 0 || nz === 0)
|
|
239
240
|
return [0, 0, 0];
|
|
240
241
|
// Use safe modulo pattern to handle negative values from floating-point edge cases
|
|
241
|
-
const safe_mod = (val, n) => ((val % n) + n) % n;
|
|
242
242
|
const x0 = safe_mod(Math.floor(x), nx);
|
|
243
243
|
const y0 = safe_mod(Math.floor(y), ny);
|
|
244
244
|
const z0 = safe_mod(Math.floor(z), nz);
|
|
@@ -387,7 +387,7 @@ function slice_surface_with_plane(surface, plane_normal, plane_distance, in_plan
|
|
|
387
387
|
if (intersection) {
|
|
388
388
|
crossing_edges.push({ edge_key, intersection });
|
|
389
389
|
// Register this face with the edge
|
|
390
|
-
const faces = edge_to_faces.get(edge_key)
|
|
390
|
+
const faces = edge_to_faces.get(edge_key) ?? [];
|
|
391
391
|
faces.push(face_idx);
|
|
392
392
|
edge_to_faces.set(edge_key, faces);
|
|
393
393
|
}
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
// Dynamic imports for Three.js exporters (tree-shaking friendly)
|
|
2
|
-
async function get_stl_exporter() {
|
|
3
|
-
const { STLExporter } = await import(`three/addons/exporters/STLExporter.js`);
|
|
4
|
-
return new STLExporter();
|
|
5
|
-
}
|
|
6
|
-
async function get_obj_exporter() {
|
|
7
|
-
const { OBJExporter } = await import(`three/addons/exporters/OBJExporter.js`);
|
|
8
|
-
return new OBJExporter();
|
|
9
|
-
}
|
|
10
|
-
async function get_gltf_exporter() {
|
|
11
|
-
const { GLTFExporter } = await import(`three/addons/exporters/GLTFExporter.js`);
|
|
12
|
-
return new GLTFExporter();
|
|
13
|
-
}
|
|
14
1
|
// Helper to trigger file download
|
|
15
2
|
function download_file(content, filename, mime_type) {
|
|
16
3
|
const blob = new Blob([content], { type: mime_type });
|
|
@@ -25,7 +12,8 @@ function download_file(content, filename, mime_type) {
|
|
|
25
12
|
}
|
|
26
13
|
// Export scene to STL format (good for 3D printing)
|
|
27
14
|
export async function export_to_stl(scene, filename) {
|
|
28
|
-
const
|
|
15
|
+
const { STLExporter } = await import(`three/addons/exporters/STLExporter.js`);
|
|
16
|
+
const exporter = new STLExporter();
|
|
29
17
|
const result = exporter.parse(scene, { binary: true });
|
|
30
18
|
// Binary STL returns DataView, convert to ArrayBuffer for Blob
|
|
31
19
|
const buffer = result instanceof DataView ? result.buffer : result;
|
|
@@ -33,13 +21,15 @@ export async function export_to_stl(scene, filename) {
|
|
|
33
21
|
}
|
|
34
22
|
// Export scene to OBJ format (widely compatible)
|
|
35
23
|
export async function export_to_obj(scene, filename) {
|
|
36
|
-
const
|
|
24
|
+
const { OBJExporter } = await import(`three/addons/exporters/OBJExporter.js`);
|
|
25
|
+
const exporter = new OBJExporter();
|
|
37
26
|
const result = exporter.parse(scene);
|
|
38
27
|
download_file(result, `${filename}.obj`, `text/plain`);
|
|
39
28
|
}
|
|
40
29
|
// Export scene to GLTF format (modern web/AR standard)
|
|
41
30
|
export async function export_to_gltf(scene, filename) {
|
|
42
|
-
const
|
|
31
|
+
const { GLTFExporter } = await import(`three/addons/exporters/GLTFExporter.js`);
|
|
32
|
+
const exporter = new GLTFExporter();
|
|
43
33
|
return new Promise((resolve, reject) => {
|
|
44
34
|
exporter.parse(scene, (gltf) => {
|
|
45
35
|
const output = JSON.stringify(gltf, null, 2);
|
|
@@ -50,14 +40,11 @@ export async function export_to_gltf(scene, filename) {
|
|
|
50
40
|
}
|
|
51
41
|
// Main export function that dispatches to the appropriate format
|
|
52
42
|
export function export_scene(scene, format, filename) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
default:
|
|
61
|
-
return Promise.reject(new Error(`Unsupported export format: ${format}`));
|
|
62
|
-
}
|
|
43
|
+
if (format === `stl`)
|
|
44
|
+
return export_to_stl(scene, filename);
|
|
45
|
+
if (format === `obj`)
|
|
46
|
+
return export_to_obj(scene, filename);
|
|
47
|
+
if (format === `gltf`)
|
|
48
|
+
return export_to_gltf(scene, filename);
|
|
49
|
+
return Promise.reject(new Error(`Unsupported export format: ${format}`));
|
|
63
50
|
}
|