matterviz 0.4.0 → 0.4.1
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/brillouin/BrillouinZone.svelte +68 -145
- package/dist/brillouin/BrillouinZone.svelte.d.ts +5 -14
- package/dist/brillouin/BrillouinZoneExportPane.svelte +43 -96
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +9 -32
- package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +2 -3
- package/dist/brillouin/BrillouinZoneScene.svelte +49 -203
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +3 -23
- package/dist/brillouin/ReciprocalVectors.svelte +39 -0
- package/dist/brillouin/ReciprocalVectors.svelte.d.ts +9 -0
- package/dist/brillouin/compute.d.ts +2 -0
- package/dist/brillouin/compute.js +80 -77
- package/dist/brillouin/geometry.d.ts +8 -0
- package/dist/brillouin/geometry.js +57 -0
- package/dist/brillouin/index.d.ts +2 -0
- package/dist/brillouin/index.js +2 -0
- package/dist/brillouin/types.d.ts +2 -2
- package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +1 -1
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +100 -191
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +4 -1
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +176 -464
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +7 -1
- package/dist/chempot-diagram/color.d.ts +3 -6
- package/dist/chempot-diagram/color.js +5 -5
- package/dist/chempot-diagram/compute.d.ts +3 -3
- package/dist/chempot-diagram/compute.js +3 -1
- package/dist/chempot-diagram/controls-state.svelte.d.ts +10 -0
- package/dist/chempot-diagram/controls-state.svelte.js +42 -0
- package/dist/chempot-diagram/export.d.ts +47 -0
- package/dist/chempot-diagram/export.js +133 -0
- package/dist/chempot-diagram/index.d.ts +1 -0
- package/dist/chempot-diagram/index.js +1 -0
- package/dist/chempot-diagram/pointer.d.ts +0 -10
- package/dist/chempot-diagram/pointer.js +4 -4
- package/dist/chempot-diagram/types.d.ts +3 -3
- package/dist/colors/index.js +2 -2
- package/dist/composition/FormulaFilter.svelte +6 -5
- package/dist/composition/PieChart.svelte +5 -5
- package/dist/composition/chem-sys.js +3 -2
- package/dist/composition/format.js +3 -2
- package/dist/composition/parse.d.ts +0 -1
- package/dist/composition/parse.js +17 -19
- package/dist/controls.d.ts +1 -0
- package/dist/controls.js +0 -1
- package/dist/convex-hull/ConvexHull.svelte +8 -10
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -4
- package/dist/convex-hull/ConvexHull2D.svelte +94 -175
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +176 -680
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +180 -680
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullChrome.svelte +268 -0
- package/dist/convex-hull/ConvexHullChrome.svelte.d.ts +30 -0
- package/dist/convex-hull/ConvexHullControls.svelte +88 -7
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +7 -6
- package/dist/convex-hull/ConvexHullInfoPane.svelte +18 -5
- package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +6 -5
- package/dist/convex-hull/ConvexHullStats.svelte +29 -168
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +3 -1
- package/dist/convex-hull/ConvexHullTooltip.svelte +11 -2
- package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +2 -1
- package/dist/convex-hull/barycentric-coords.d.ts +2 -4
- package/dist/convex-hull/barycentric-coords.js +6 -33
- package/dist/convex-hull/canvas-interactions.svelte.d.ts +79 -0
- package/dist/convex-hull/canvas-interactions.svelte.js +278 -0
- package/dist/convex-hull/helpers.d.ts +39 -7
- package/dist/convex-hull/helpers.js +154 -69
- package/dist/convex-hull/hull-state.svelte.d.ts +44 -0
- package/dist/convex-hull/hull-state.svelte.js +124 -0
- package/dist/convex-hull/index.d.ts +9 -7
- package/dist/convex-hull/index.js +7 -2
- package/dist/convex-hull/thermodynamics.js +91 -920
- package/dist/convex-hull/types.d.ts +12 -4
- package/dist/convex-hull/types.js +12 -0
- package/dist/coordination/CoordinationBarPlot.svelte +4 -11
- package/dist/element/BohrAtom.svelte +2 -1
- package/dist/element/ElementTile.svelte.d.ts +1 -1
- package/dist/element/index.d.ts +4 -0
- package/dist/element/index.js +18 -0
- package/dist/feedback/DragOverlay.svelte +3 -1
- package/dist/feedback/DragOverlay.svelte.d.ts +1 -0
- package/dist/feedback/StatusMessage.svelte +13 -3
- package/dist/fermi-surface/FermiSurface.svelte +67 -146
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +5 -14
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +72 -224
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +3 -23
- package/dist/fermi-surface/compute.js +11 -10
- package/dist/fermi-surface/export.js +4 -15
- package/dist/fermi-surface/index.d.ts +0 -1
- package/dist/fermi-surface/index.js +0 -1
- package/dist/fermi-surface/parse.d.ts +1 -1
- package/dist/fermi-surface/parse.js +64 -75
- package/dist/fermi-surface/types.d.ts +2 -2
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +55 -40
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +4 -3
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +3 -2
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +5 -5
- package/dist/heatmap-matrix/index.d.ts +3 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/io/ExportPane.svelte +166 -0
- package/dist/io/ExportPane.svelte.d.ts +17 -0
- package/dist/io/decompress.js +1 -2
- package/dist/io/export.d.ts +5 -1
- package/dist/io/export.js +32 -28
- package/dist/io/fetch.d.ts +2 -1
- package/dist/io/file-drop.d.ts +7 -0
- package/dist/io/file-drop.js +13 -0
- package/dist/io/index.d.ts +2 -0
- package/dist/io/index.js +10 -0
- package/dist/io/types.d.ts +13 -0
- package/dist/isosurface/parse.js +46 -44
- package/dist/labels.js +1 -1
- package/dist/layout/FullscreenButton.svelte +33 -0
- package/dist/layout/FullscreenButton.svelte.d.ts +10 -0
- package/dist/layout/FullscreenToggle.svelte +8 -14
- package/dist/layout/ViewerChrome.svelte +116 -0
- package/dist/layout/ViewerChrome.svelte.d.ts +17 -0
- package/dist/layout/fullscreen.d.ts +4 -0
- package/dist/layout/fullscreen.svelte.d.ts +8 -0
- package/dist/layout/fullscreen.svelte.js +37 -0
- package/dist/layout/index.d.ts +3 -0
- package/dist/layout/index.js +3 -0
- package/dist/math.d.ts +7 -3
- package/dist/math.js +18 -21
- package/dist/overlays/index.d.ts +4 -0
- package/dist/periodic-table/PeriodicTable.svelte +9 -8
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramControls.svelte +3 -2
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +4 -3
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +2 -3
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +47 -132
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +3 -4
- package/dist/phase-diagram/colors.js +1 -1
- package/dist/phase-diagram/parse.d.ts +2 -1
- package/dist/plot/bar/BarPlot.svelte +79 -316
- package/dist/plot/bar/BarPlot.svelte.d.ts +7 -15
- package/dist/plot/bar/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/bar/SpacegroupBarPlot.svelte +2 -1
- package/dist/plot/box/BoxPlot.svelte +76 -246
- package/dist/plot/box/BoxPlot.svelte.d.ts +4 -3
- package/dist/plot/box/BoxPlotControls.svelte.d.ts +1 -1
- package/dist/plot/box/Violin.svelte.d.ts +1 -1
- package/dist/plot/box/box-plot.d.ts +3 -2
- package/dist/plot/box/box-plot.js +5 -2
- package/dist/plot/box/kde.d.ts +2 -1
- package/dist/plot/box/kde.js +4 -4
- package/dist/plot/core/auto-place.d.ts +1 -1
- package/dist/plot/core/auto-place.js +4 -1
- package/dist/plot/core/components/ColorBar.svelte +5 -5
- package/dist/plot/core/components/ColorBar.svelte.d.ts +5 -4
- package/dist/plot/core/components/Line.svelte +3 -2
- package/dist/plot/core/components/Line.svelte.d.ts +3 -2
- package/dist/plot/core/components/PlotAxis.svelte +2 -1
- package/dist/plot/core/components/PlotAxis.svelte.d.ts +2 -1
- package/dist/plot/core/components/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/core/components/ReferenceLine3D.svelte +2 -2
- package/dist/plot/core/components/ReferenceLine3D.svelte.d.ts +4 -4
- package/dist/plot/core/components/ReferencePlane.svelte +2 -2
- package/dist/plot/core/components/ReferencePlane.svelte.d.ts +4 -4
- package/dist/plot/core/data-cleaning.js +18 -18
- package/dist/plot/core/fill-utils.d.ts +4 -3
- package/dist/plot/core/fill-utils.js +6 -3
- package/dist/plot/core/interactions.d.ts +5 -1
- package/dist/plot/core/interactions.js +14 -0
- package/dist/plot/core/pan-zoom.svelte.d.ts +35 -0
- package/dist/plot/core/pan-zoom.svelte.js +221 -0
- package/dist/plot/core/placed-tween.svelte.d.ts +21 -0
- package/dist/plot/core/placed-tween.svelte.js +68 -0
- package/dist/plot/core/reference-line.d.ts +10 -10
- package/dist/plot/core/reference-line.js +6 -6
- package/dist/plot/core/scales.d.ts +17 -25
- package/dist/plot/core/scales.js +10 -8
- package/dist/plot/core/svg.d.ts +2 -1
- package/dist/plot/core/types.d.ts +18 -7
- package/dist/plot/core/utils/label-placement.d.ts +1 -1
- package/dist/plot/core/utils/label-placement.js +3 -3
- package/dist/plot/core/utils.d.ts +2 -1
- package/dist/plot/histogram/Histogram.svelte +77 -314
- package/dist/plot/histogram/HistogramControls.svelte.d.ts +1 -1
- package/dist/plot/sankey/Sankey.svelte +2 -5
- package/dist/plot/sankey/Sankey.svelte.d.ts +1 -1
- package/dist/plot/sankey/sankey.js +3 -1
- package/dist/plot/scatter/BinnedScatterPlot.svelte +3 -5
- package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +4 -4
- package/dist/plot/scatter/ScatterPlot.svelte +160 -450
- package/dist/plot/scatter/ScatterPlot.svelte.d.ts +7 -15
- package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/scatter/binned-scatter-types.d.ts +4 -11
- package/dist/plot/scatter/index.d.ts +1 -1
- package/dist/plot/scatter-3d/ScatterPlot3D.svelte +15 -26
- package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +6 -14
- package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +9 -10
- package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte.d.ts +5 -5
- package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +122 -121
- package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +5 -14
- package/dist/plot/scatter-3d/Surface3D.svelte +6 -5
- package/dist/plot/scatter-3d/Surface3D.svelte.d.ts +4 -3
- package/dist/plot/sunburst/Sunburst.svelte +16 -20
- package/dist/plot/sunburst/Sunburst.svelte.d.ts +4 -3
- package/dist/plot/sunburst/SunburstControls.svelte.d.ts +1 -1
- package/dist/plot/sunburst/sunburst.js +4 -1
- package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
- package/dist/sanitize.js +13 -2
- package/dist/scene/SceneCamera.svelte +62 -0
- package/dist/scene/SceneCamera.svelte.d.ts +19 -0
- package/dist/scene/bind-renderer.svelte.d.ts +2 -0
- package/dist/scene/bind-renderer.svelte.js +14 -0
- package/dist/scene/index.d.ts +4 -0
- package/dist/scene/index.js +5 -0
- package/dist/scene/props.js +52 -0
- package/dist/scene/types.d.ts +26 -0
- package/dist/scene/types.js +1 -0
- package/dist/settings.d.ts +14 -2
- package/dist/settings.js +59 -1
- package/dist/spectral/Bands.svelte +8 -7
- package/dist/spectral/Bands.svelte.d.ts +3 -2
- package/dist/spectral/BandsAndDos.svelte +22 -24
- package/dist/spectral/BrillouinBandsDos.svelte +3 -3
- package/dist/spectral/Dos.svelte +5 -4
- package/dist/spectral/Dos.svelte.d.ts +2 -1
- package/dist/spectral/helpers.d.ts +6 -6
- package/dist/spectral/helpers.js +43 -37
- package/dist/state.svelte.d.ts +0 -7
- package/dist/state.svelte.js +0 -6
- package/dist/structure/Arrow.svelte +2 -4
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/CanvasTooltip.svelte +1 -0
- package/dist/structure/CellSelect.svelte +11 -3
- package/dist/structure/CellSelect.svelte.d.ts +2 -1
- package/dist/structure/Lattice.svelte +2 -2
- package/dist/structure/Structure.svelte +291 -355
- package/dist/structure/Structure.svelte.d.ts +5 -15
- package/dist/structure/StructureControls.svelte +217 -2
- package/dist/structure/StructureControls.svelte.d.ts +5 -3
- package/dist/structure/StructureExportPane.svelte +54 -156
- package/dist/structure/StructureExportPane.svelte.d.ts +4 -5
- package/dist/structure/StructureInfoPane.svelte +5 -3
- package/dist/structure/StructureInfoPane.svelte.d.ts +5 -5
- package/dist/structure/StructureScene.svelte +365 -198
- package/dist/structure/StructureScene.svelte.d.ts +22 -20
- package/dist/structure/{label-placement.d.ts → atom-label-placement.d.ts} +3 -3
- package/dist/structure/{label-placement.js → atom-label-placement.js} +12 -2
- package/dist/structure/atom-properties.d.ts +1 -1
- package/dist/structure/atom-properties.js +11 -16
- package/dist/structure/bond-order-perception.js +2 -4
- package/dist/structure/bonding.d.ts +3 -0
- package/dist/structure/bonding.js +91 -48
- package/dist/structure/export.d.ts +24 -4
- package/dist/structure/export.js +64 -122
- package/dist/structure/index.d.ts +2 -0
- package/dist/structure/index.js +2 -0
- package/dist/structure/parse.d.ts +3 -2
- package/dist/structure/parse.js +333 -370
- package/dist/structure/partial-occupancy.d.ts +0 -1
- package/dist/structure/partial-occupancy.js +1 -1
- package/dist/structure/pbc.d.ts +1 -1
- package/dist/structure/pbc.js +186 -13
- package/dist/structure/polyhedra.d.ts +41 -0
- package/dist/structure/polyhedra.js +602 -0
- package/dist/structure/site.d.ts +4 -0
- package/dist/structure/site.js +1 -0
- package/dist/structure/supercell.js +3 -2
- package/dist/structure/validation.js +5 -6
- package/dist/symmetry/SymmetryElementControls.svelte +69 -0
- package/dist/symmetry/SymmetryElementControls.svelte.d.ts +9 -0
- package/dist/symmetry/SymmetryElements.svelte +354 -0
- package/dist/symmetry/SymmetryElements.svelte.d.ts +24 -0
- package/dist/symmetry/SymmetryStats.svelte +111 -6
- package/dist/symmetry/WyckoffTable.svelte +68 -7
- package/dist/symmetry/WyckoffTable.svelte.d.ts +3 -0
- package/dist/symmetry/cell-transform.js +7 -14
- package/dist/symmetry/index.d.ts +14 -4
- package/dist/symmetry/index.js +301 -80
- package/dist/symmetry/spacegroups.d.ts +5 -1
- package/dist/symmetry/spacegroups.js +15 -1
- package/dist/symmetry/symmetry-elements.d.ts +33 -0
- package/dist/symmetry/symmetry-elements.js +521 -0
- package/dist/symmetry/wyckoff-db.d.ts +9 -0
- package/dist/symmetry/wyckoff-db.js +87 -0
- package/dist/table/HeatmapTable.svelte +4 -15
- package/dist/table/HeatmapTable.svelte.d.ts +1 -1
- package/dist/trajectory/Trajectory.svelte +58 -61
- package/dist/trajectory/Trajectory.svelte.d.ts +10 -22
- package/dist/trajectory/TrajectoryExportPane.svelte +15 -24
- package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +4 -5
- package/dist/trajectory/TrajectoryInfoPane.svelte +3 -2
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +3 -2
- package/dist/trajectory/constants.js +6 -2
- package/dist/trajectory/extract.js +17 -37
- package/dist/trajectory/format-detect.d.ts +0 -1
- package/dist/trajectory/format-detect.js +3 -9
- package/dist/trajectory/frame-reader.d.ts +0 -1
- package/dist/trajectory/frame-reader.js +62 -128
- package/dist/trajectory/helpers.d.ts +10 -2
- package/dist/trajectory/helpers.js +56 -36
- package/dist/trajectory/parse/ase.d.ts +9 -1
- package/dist/trajectory/parse/ase.js +47 -32
- package/dist/trajectory/parse/diagnostics.d.ts +3 -0
- package/dist/trajectory/parse/diagnostics.js +14 -0
- package/dist/trajectory/parse/index.d.ts +1 -1
- package/dist/trajectory/parse/index.js +54 -102
- package/dist/trajectory/parse/lammps.d.ts +0 -2
- package/dist/trajectory/parse/lammps.js +8 -6
- package/dist/trajectory/parse/pymatgen.d.ts +2 -0
- package/dist/trajectory/parse/pymatgen.js +74 -0
- package/dist/trajectory/parse/vasp.js +4 -3
- package/dist/trajectory/parse/xyz.d.ts +9 -21
- package/dist/trajectory/parse/xyz.js +28 -33
- package/dist/trajectory/plotting.d.ts +0 -1
- package/dist/trajectory/plotting.js +3 -100
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +1 -1
- package/dist/xrd/XrdPlot.svelte +14 -29
- package/dist/xrd/broadening.d.ts +2 -1
- package/dist/xrd/calc-xrd.js +6 -11
- package/dist/xrd/index.d.ts +2 -2
- package/package.json +29 -16
- package/dist/element/data.json +0 -11864
- package/dist/fermi-surface/marching-cubes.d.ts +0 -2
- package/dist/fermi-surface/marching-cubes.js +0 -2
- package/dist/plot/core/hover-lock.svelte.d.ts +0 -14
- package/dist/plot/core/hover-lock.svelte.js +0 -45
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { PaneProps, PaneToggleProps } from '../overlays'
|
|
3
|
+
import type { ExportItem, ExportSection } from './types'
|
|
4
|
+
import DraggablePane from '../overlays/DraggablePane.svelte'
|
|
5
|
+
import { sanitize_html } from '../sanitize'
|
|
6
|
+
import { type ComponentProps, onDestroy, type Snippet } from 'svelte'
|
|
7
|
+
import { tooltip } from 'svelte-multiselect/attachments'
|
|
8
|
+
import type { HTMLAttributes } from 'svelte/elements'
|
|
9
|
+
|
|
10
|
+
let {
|
|
11
|
+
export_pane_open = $bindable(false),
|
|
12
|
+
sections = [],
|
|
13
|
+
png_dpi = $bindable(150),
|
|
14
|
+
dpi_range = [50, 600],
|
|
15
|
+
icon_style = ``,
|
|
16
|
+
toggle_props = {},
|
|
17
|
+
pane_props = {},
|
|
18
|
+
children = undefined,
|
|
19
|
+
...rest
|
|
20
|
+
}: HTMLAttributes<HTMLDivElement> & {
|
|
21
|
+
export_pane_open?: boolean
|
|
22
|
+
sections?: ExportSection[]
|
|
23
|
+
png_dpi?: number
|
|
24
|
+
dpi_range?: readonly [number, number]
|
|
25
|
+
icon_style?: string
|
|
26
|
+
toggle_props?: PaneToggleProps
|
|
27
|
+
pane_props?: PaneProps
|
|
28
|
+
// Pane-specific extras rendered below the sections (e.g. video export controls)
|
|
29
|
+
children?: Snippet
|
|
30
|
+
} = $props()
|
|
31
|
+
|
|
32
|
+
// Clamp DPI into dpi_range on input change (fires on blur, before any download click)
|
|
33
|
+
function clamp_dpi(): void {
|
|
34
|
+
const [min_dpi, max_dpi] = dpi_range
|
|
35
|
+
if (typeof png_dpi !== `number` || !Number.isFinite(png_dpi)) png_dpi = 150
|
|
36
|
+
else png_dpi = Math.round(Math.min(max_dpi, Math.max(min_dpi, png_dpi)))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Copy-to-clipboard with temporary ✅ feedback; copy_text runs only on click
|
|
40
|
+
let copied_key = $state<string | null>(null)
|
|
41
|
+
let copied_timeout: ReturnType<typeof setTimeout> | undefined
|
|
42
|
+
async function handle_copy(item: ExportItem, key: string): Promise<void> {
|
|
43
|
+
if (item.disabled) return
|
|
44
|
+
const text = item.copy_text?.()
|
|
45
|
+
if (!text) return
|
|
46
|
+
try {
|
|
47
|
+
await navigator.clipboard.writeText(text)
|
|
48
|
+
copied_key = key
|
|
49
|
+
clearTimeout(copied_timeout)
|
|
50
|
+
copied_timeout = setTimeout(() => (copied_key = null), 1000)
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error(`Failed to copy ${item.label} to clipboard`, error)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
onDestroy(() => clearTimeout(copied_timeout))
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<DraggablePane
|
|
59
|
+
bind:show={export_pane_open}
|
|
60
|
+
open_icon="Cross"
|
|
61
|
+
closed_icon="Export"
|
|
62
|
+
{icon_style}
|
|
63
|
+
pane_props={{
|
|
64
|
+
...rest,
|
|
65
|
+
...pane_props,
|
|
66
|
+
class: `export-pane ${rest.class ?? ``} ${pane_props?.class ?? ``}`.trim(),
|
|
67
|
+
}}
|
|
68
|
+
{toggle_props}
|
|
69
|
+
>
|
|
70
|
+
{#each sections as section, sec_idx (section.title ?? sec_idx)}
|
|
71
|
+
{#if section.title}
|
|
72
|
+
<h4
|
|
73
|
+
{@attach section.tooltip
|
|
74
|
+
? tooltip({ allow_html: true, content: sanitize_html(section.tooltip) })
|
|
75
|
+
: () => {}}
|
|
76
|
+
>{section.title}</h4>
|
|
77
|
+
{/if}
|
|
78
|
+
<div class="export-grid">
|
|
79
|
+
{#each section.items as item, item_idx (item.label)}
|
|
80
|
+
{@const copy_key = `${sec_idx}-${item_idx}`}
|
|
81
|
+
<!-- not a <label>: it would forward label-text clicks to the first (download) button -->
|
|
82
|
+
<span class="export-item">
|
|
83
|
+
{#if item.hint}
|
|
84
|
+
<span
|
|
85
|
+
{@attach tooltip({ allow_html: true, content: sanitize_html(item.hint) })}
|
|
86
|
+
>{item.label}</span>
|
|
87
|
+
{:else}
|
|
88
|
+
{item.label}
|
|
89
|
+
{/if}
|
|
90
|
+
{#if item.on_download}
|
|
91
|
+
<button
|
|
92
|
+
type="button"
|
|
93
|
+
onclick={item.on_download}
|
|
94
|
+
disabled={item.disabled ?? false}
|
|
95
|
+
aria-label={`Download ${item.label}`}
|
|
96
|
+
title={`Download ${item.label}${item.show_dpi ? ` (${png_dpi} DPI)` : ``}`}
|
|
97
|
+
>
|
|
98
|
+
⬇
|
|
99
|
+
</button>
|
|
100
|
+
{/if}
|
|
101
|
+
{#if item.copy_text}
|
|
102
|
+
<button
|
|
103
|
+
type="button"
|
|
104
|
+
onclick={() => handle_copy(item, copy_key)}
|
|
105
|
+
disabled={item.disabled ?? false}
|
|
106
|
+
aria-label="Copy {item.label} to clipboard"
|
|
107
|
+
title="Copy {item.label} to clipboard"
|
|
108
|
+
>
|
|
109
|
+
{copied_key === copy_key ? `✅` : `📋`}
|
|
110
|
+
</button>
|
|
111
|
+
{/if}
|
|
112
|
+
{#if item.show_dpi}
|
|
113
|
+
<span class="dpi-input">(DPI: <input
|
|
114
|
+
type="number"
|
|
115
|
+
min={dpi_range[0]}
|
|
116
|
+
max={dpi_range[1]}
|
|
117
|
+
bind:value={png_dpi}
|
|
118
|
+
onchange={clamp_dpi}
|
|
119
|
+
title="Export resolution in dots per inch"
|
|
120
|
+
/>)</span>
|
|
121
|
+
{/if}
|
|
122
|
+
</span>
|
|
123
|
+
{/each}
|
|
124
|
+
</div>
|
|
125
|
+
{/each}
|
|
126
|
+
{@render children?.()}
|
|
127
|
+
</DraggablePane>
|
|
128
|
+
|
|
129
|
+
<style>
|
|
130
|
+
h4 {
|
|
131
|
+
display: flex;
|
|
132
|
+
align-items: center;
|
|
133
|
+
margin: 0;
|
|
134
|
+
}
|
|
135
|
+
.export-grid {
|
|
136
|
+
display: flex;
|
|
137
|
+
flex-wrap: wrap;
|
|
138
|
+
align-items: center;
|
|
139
|
+
gap: 4pt 10pt;
|
|
140
|
+
font-size: 0.95em;
|
|
141
|
+
}
|
|
142
|
+
.export-item {
|
|
143
|
+
display: flex;
|
|
144
|
+
align-items: center;
|
|
145
|
+
gap: 4pt;
|
|
146
|
+
white-space: nowrap;
|
|
147
|
+
}
|
|
148
|
+
.export-grid button {
|
|
149
|
+
min-width: 1.9em;
|
|
150
|
+
height: 1.6em;
|
|
151
|
+
padding: 0 4pt;
|
|
152
|
+
box-sizing: border-box;
|
|
153
|
+
display: inline-flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
justify-content: center;
|
|
156
|
+
}
|
|
157
|
+
.export-grid input[type='number'] {
|
|
158
|
+
width: 3.5em;
|
|
159
|
+
}
|
|
160
|
+
.dpi-input {
|
|
161
|
+
display: inline-flex;
|
|
162
|
+
align-items: center;
|
|
163
|
+
gap: 2pt;
|
|
164
|
+
white-space: nowrap;
|
|
165
|
+
}
|
|
166
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PaneProps, PaneToggleProps } from '../overlays';
|
|
2
|
+
import type { ExportSection } from './types';
|
|
3
|
+
import { type Snippet } from 'svelte';
|
|
4
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
5
|
+
type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
|
|
6
|
+
export_pane_open?: boolean;
|
|
7
|
+
sections?: ExportSection[];
|
|
8
|
+
png_dpi?: number;
|
|
9
|
+
dpi_range?: readonly [number, number];
|
|
10
|
+
icon_style?: string;
|
|
11
|
+
toggle_props?: PaneToggleProps;
|
|
12
|
+
pane_props?: PaneProps;
|
|
13
|
+
children?: Snippet;
|
|
14
|
+
};
|
|
15
|
+
declare const ExportPane: import("svelte").Component<$$ComponentProps, {}, "export_pane_open" | "png_dpi">;
|
|
16
|
+
type ExportPane = ReturnType<typeof ExportPane>;
|
|
17
|
+
export default ExportPane;
|
package/dist/io/decompress.js
CHANGED
|
@@ -16,7 +16,6 @@ export async function decompress_data(data, format) {
|
|
|
16
16
|
// Decompress data and return as ArrayBuffer (for binary files like .brml.gz)
|
|
17
17
|
export async function decompress_data_binary(data, format) {
|
|
18
18
|
try {
|
|
19
|
-
// Handle unsupported formats
|
|
20
19
|
if (format === `zip` || format === `xz` || format === `bz2`) {
|
|
21
20
|
throw new Error(`${format.toUpperCase()} decompression is not supported in the browser. ` +
|
|
22
21
|
`Please extract the ${format.toUpperCase()} file first.`);
|
|
@@ -40,7 +39,7 @@ export async function decompress_data_binary(data, format) {
|
|
|
40
39
|
}
|
|
41
40
|
export function decompress_file(file) {
|
|
42
41
|
const format = detect_compression_format(file.name);
|
|
43
|
-
const is_supported =
|
|
42
|
+
const is_supported = format !== null && format !== `zip` && format !== `xz` && format !== `bz2`;
|
|
44
43
|
return new Promise((resolve, reject) => {
|
|
45
44
|
const reader = new FileReader();
|
|
46
45
|
reader.addEventListener(`load`, () => {
|
package/dist/io/export.d.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import type { AnyStructure } from '../structure';
|
|
2
|
-
import { type Camera, type Scene } from 'three';
|
|
2
|
+
import { type Camera, type Scene, type WebGLRenderer } from 'three';
|
|
3
|
+
export declare const renderer_registry: WeakMap<HTMLCanvasElement, WebGLRenderer>;
|
|
4
|
+
export declare const dpi_to_scale: (png_dpi: number) => number;
|
|
3
5
|
export declare function canvas_to_png_blob(canvas: HTMLCanvasElement, png_dpi?: number, scene?: Scene | null, camera?: Camera | null): Promise<Blob>;
|
|
4
6
|
export declare function export_canvas_as_png(canvas: HTMLCanvasElement | null, structure_or_filename: AnyStructure | string | undefined, png_dpi?: number, scene?: Scene | null, camera?: Camera | null): void;
|
|
5
7
|
export declare function svg_to_svg_string(svg_element: SVGElement, inline_styles?: readonly string[]): string;
|
|
6
8
|
export declare function export_svg_as_svg(svg_element: SVGElement | null, filename: string, inline_styles?: readonly string[]): void;
|
|
7
9
|
export declare function svg_to_png_blob(svg_element: SVGElement, png_dpi?: number, inline_styles?: readonly string[]): Promise<Blob>;
|
|
8
10
|
export declare function export_svg_as_png(svg_element: SVGElement | null, filename: string, png_dpi?: number, inline_styles?: readonly string[]): void;
|
|
11
|
+
export declare function observe_canvas_presence(wrapper: HTMLElement | undefined, set: (has_canvas: boolean) => void): (() => void) | undefined;
|
|
12
|
+
export declare const estimate_video_bitrate: (pixel_count: number, fps: number) => number;
|
|
9
13
|
export declare function get_ffmpeg_conversion_command(input_filename: string): string;
|
|
10
14
|
export declare function export_trajectory_video(canvas: HTMLCanvasElement | null, filename: string, options?: {
|
|
11
15
|
fps?: number;
|
package/dist/io/export.js
CHANGED
|
@@ -2,29 +2,20 @@ import { download } from './fetch';
|
|
|
2
2
|
import { create_structure_filename } from '../structure/export';
|
|
3
3
|
import { to_error } from '../utils';
|
|
4
4
|
import { Vector2 } from 'three';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
typeof renderer_obj.setPixelRatio === `function` &&
|
|
12
|
-
typeof renderer_obj.getSize === `function` &&
|
|
13
|
-
typeof renderer_obj.setSize === `function`);
|
|
14
|
-
}
|
|
15
|
-
function get_canvas_renderer(canvas) {
|
|
16
|
-
// oxlint-disable-next-line no-underscore-dangle -- three.js stores its renderer here
|
|
17
|
-
const renderer_val = canvas.__renderer;
|
|
18
|
-
return is_webgl_renderer_like(renderer_val) ? renderer_val : undefined;
|
|
19
|
-
}
|
|
5
|
+
// Maps a Threlte canvas to its WebGLRenderer so PNG export can look up the renderer for a
|
|
6
|
+
// given canvas without mutating the DOM element. Populated by bind_renderer (scene/).
|
|
7
|
+
export const renderer_registry = new WeakMap();
|
|
8
|
+
// PNG DPI -> render scale relative to the 72 DPI baseline, capped at 10x. DPI floors
|
|
9
|
+
// at 1 (non-finite -> 72) so bad inputs can't yield 0x0 canvases or NaN pixel ratios.
|
|
10
|
+
export const dpi_to_scale = (png_dpi) => Math.min(Math.max(1, Number.isFinite(png_dpi) ? png_dpi : 72) / 72, 10);
|
|
20
11
|
// Capture a WebGL canvas as a PNG Blob at the given DPI.
|
|
21
12
|
// Temporarily adjusts renderer pixel ratio for high-res capture, then restores.
|
|
22
13
|
// Returns data directly (no browser download), suitable for programmatic capture
|
|
23
14
|
// in test suites, server-side rendering, or Python widget integration via anywidget.
|
|
24
15
|
// DPI is converted to a resolution multiplier relative to 72 DPI baseline, capped at 10x.
|
|
25
16
|
export function canvas_to_png_blob(canvas, png_dpi = 150, scene = null, camera = null) {
|
|
26
|
-
const resolution_multiplier =
|
|
27
|
-
const renderer =
|
|
17
|
+
const resolution_multiplier = dpi_to_scale(png_dpi);
|
|
18
|
+
const renderer = renderer_registry.get(canvas);
|
|
28
19
|
if (resolution_multiplier <= 1.1 || !renderer) {
|
|
29
20
|
if (renderer && scene && camera)
|
|
30
21
|
renderer.render(scene, camera);
|
|
@@ -168,9 +159,10 @@ export function svg_to_png_blob(svg_element, png_dpi = 150, inline_styles = [])
|
|
|
168
159
|
if (!Number.isFinite(png_dpi) || png_dpi <= 0) {
|
|
169
160
|
return Promise.reject(new Error(`Invalid PNG DPI for export`));
|
|
170
161
|
}
|
|
171
|
-
const resolution_multiplier =
|
|
172
|
-
|
|
173
|
-
const
|
|
162
|
+
const resolution_multiplier = dpi_to_scale(png_dpi);
|
|
163
|
+
// Floor at 1px: small viewBoxes at low DPI round to 0 and make toBlob fail confusingly
|
|
164
|
+
const pixel_width = Math.max(1, Math.round(width * resolution_multiplier));
|
|
165
|
+
const pixel_height = Math.max(1, Math.round(height * resolution_multiplier));
|
|
174
166
|
const canvas = document.createElement(`canvas`);
|
|
175
167
|
const ctx = canvas.getContext(`2d`);
|
|
176
168
|
if (!ctx)
|
|
@@ -217,6 +209,23 @@ export function export_svg_as_png(svg_element, filename, png_dpi = 150, inline_s
|
|
|
217
209
|
.then((blob) => download(blob, filename, `image/png`))
|
|
218
210
|
.catch((error) => console.error(`Error exporting PNG:`, error));
|
|
219
211
|
}
|
|
212
|
+
// Watch a wrapper element for <canvas> insertion/removal: calls set(bool) immediately
|
|
213
|
+
// and on every DOM mutation. Returns a cleanup that disconnects the observer (or
|
|
214
|
+
// undefined when no wrapper is given). Used by export panes to enable canvas exports.
|
|
215
|
+
export function observe_canvas_presence(wrapper, set) {
|
|
216
|
+
if (!wrapper) {
|
|
217
|
+
set(false);
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
const check = () => set(Boolean(wrapper.querySelector(`canvas`)));
|
|
221
|
+
check();
|
|
222
|
+
const observer = new MutationObserver(check);
|
|
223
|
+
observer.observe(wrapper, { childList: true, subtree: true });
|
|
224
|
+
return () => observer.disconnect();
|
|
225
|
+
}
|
|
226
|
+
// Estimate VP9 video bitrate (bits/s) from pixel count and frame rate.
|
|
227
|
+
// VP9 needs ~0.1 bits per pixel per frame for good quality; clamped to [1, 200] Mbps.
|
|
228
|
+
export const estimate_video_bitrate = (pixel_count, fps) => Math.max(1_000_000, Math.min(pixel_count * fps * 0.1, 200_000_000));
|
|
220
229
|
// Generate FFmpeg command for WebM to MP4 conversion
|
|
221
230
|
export function get_ffmpeg_conversion_command(input_filename) {
|
|
222
231
|
const output = input_filename.replace(/\.webm$/i, `.mp4`);
|
|
@@ -230,7 +239,7 @@ export async function export_trajectory_video(canvas, filename, options = {}) {
|
|
|
230
239
|
typeof MediaRecorder === `undefined` ||
|
|
231
240
|
!MediaRecorder.isTypeSupported(`video/webm;codecs=vp9`))
|
|
232
241
|
throw new Error(`WebM video recording not supported in this browser`);
|
|
233
|
-
const renderer =
|
|
242
|
+
const renderer = renderer_registry.get(canvas);
|
|
234
243
|
// Store original renderer settings if changing resolution
|
|
235
244
|
let orig_pixel_ratio;
|
|
236
245
|
let orig_size;
|
|
@@ -242,13 +251,8 @@ export async function export_trajectory_video(canvas, filename, options = {}) {
|
|
|
242
251
|
renderer.setSize(orig_size.width, orig_size.height, false);
|
|
243
252
|
}
|
|
244
253
|
// Calculate bitrate based on actual video dimensions
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
const pixels_per_frame = canvas.width * canvas.height;
|
|
248
|
-
const bits_per_pixel_per_frame = 0.1; // Good quality for VP9
|
|
249
|
-
// Clamp bitrate to reasonable bounds (1 Mbps min, 200 Mbps max)
|
|
250
|
-
const calculated_bitrate = pixels_per_frame * fps * bits_per_pixel_per_frame;
|
|
251
|
-
const bitrate = Math.max(1_000_000, Math.min(calculated_bitrate, 200_000_000));
|
|
254
|
+
// (canvas dimensions include device pixel ratio and any resolution_multiplier)
|
|
255
|
+
const bitrate = estimate_video_bitrate(canvas.width * canvas.height, fps);
|
|
252
256
|
const stream = canvas.captureStream(0);
|
|
253
257
|
const chunks = [];
|
|
254
258
|
const recorder = new MediaRecorder(stream, {
|
package/dist/io/fetch.d.ts
CHANGED
|
@@ -2,4 +2,5 @@ export declare const to_query: (params: Record<string, string | number | undefin
|
|
|
2
2
|
export declare function fetch_zipped<T>(url: string, { unzip }?: {
|
|
3
3
|
unzip?: boolean | undefined;
|
|
4
4
|
}): Promise<T | null>;
|
|
5
|
-
export
|
|
5
|
+
export type DownloadData = string | Blob | ArrayBuffer | ArrayBufferView<ArrayBuffer>;
|
|
6
|
+
export declare function download(data: DownloadData, filename: string, type: string): void;
|
package/dist/io/file-drop.d.ts
CHANGED
|
@@ -4,4 +4,11 @@ export interface FileDropOptions {
|
|
|
4
4
|
on_error?: (msg: string) => void;
|
|
5
5
|
set_loading?: (loading: boolean) => void;
|
|
6
6
|
}
|
|
7
|
+
export declare const drag_over_handlers: (opts: {
|
|
8
|
+
allow?: () => boolean;
|
|
9
|
+
set_dragover: (over: boolean) => void;
|
|
10
|
+
}) => {
|
|
11
|
+
ondragover: (event: DragEvent) => void;
|
|
12
|
+
ondragleave: () => void;
|
|
13
|
+
};
|
|
7
14
|
export declare const create_file_drop_handler: (opts: FileDropOptions) => ((event: DragEvent) => Promise<void>);
|
package/dist/io/file-drop.js
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
import { decompress_file } from './decompress';
|
|
3
3
|
import { handle_url_drop } from './url-drop';
|
|
4
4
|
import { to_error } from '../utils';
|
|
5
|
+
// Drag-over visual-state handlers for file-drop zones; spread onto the drop target
|
|
6
|
+
// alongside `ondrop` from create_file_drop_handler
|
|
7
|
+
export const drag_over_handlers = (opts) => ({
|
|
8
|
+
// preventDefault on dragover marks the element as a valid drop target; dragleave
|
|
9
|
+
// has no default action to cancel, so it only clears the visual state
|
|
10
|
+
ondragover: (event) => {
|
|
11
|
+
event.preventDefault();
|
|
12
|
+
if (opts.allow && !opts.allow())
|
|
13
|
+
return;
|
|
14
|
+
opts.set_dragover(true);
|
|
15
|
+
},
|
|
16
|
+
ondragleave: () => opts.set_dragover(false),
|
|
17
|
+
});
|
|
5
18
|
// Handles URL drops (from FilePicker), direct file drops with decompression,
|
|
6
19
|
// loading state, and error reporting.
|
|
7
20
|
export const create_file_drop_handler = (opts) => async (event) => {
|
package/dist/io/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { default as ExportPane } from './ExportPane.svelte';
|
|
1
2
|
export * from './decompress';
|
|
2
3
|
export * from './export';
|
|
3
4
|
export * from './fetch';
|
|
@@ -5,3 +6,4 @@ export * from './file-drop';
|
|
|
5
6
|
export * from './is-binary';
|
|
6
7
|
export type * from './types';
|
|
7
8
|
export * from './url-drop';
|
|
9
|
+
export declare function strip_compression_extensions(filename: string): string;
|
package/dist/io/index.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
import { COMPRESSION_EXTENSIONS_REGEX } from '../constants';
|
|
2
|
+
export { default as ExportPane } from './ExportPane.svelte';
|
|
1
3
|
export * from './decompress';
|
|
2
4
|
export * from './export';
|
|
3
5
|
export * from './fetch';
|
|
4
6
|
export * from './file-drop';
|
|
5
7
|
export * from './is-binary';
|
|
6
8
|
export * from './url-drop';
|
|
9
|
+
// Lowercase a filename and strip all trailing compression extensions (.gz, .zip, ...)
|
|
10
|
+
export function strip_compression_extensions(filename) {
|
|
11
|
+
let base_name = filename.toLowerCase();
|
|
12
|
+
while (COMPRESSION_EXTENSIONS_REGEX.test(base_name)) {
|
|
13
|
+
base_name = base_name.replace(COMPRESSION_EXTENSIONS_REGEX, ``);
|
|
14
|
+
}
|
|
15
|
+
return base_name;
|
|
16
|
+
}
|
package/dist/io/types.d.ts
CHANGED
|
@@ -6,3 +6,16 @@ export interface FileInfo {
|
|
|
6
6
|
category?: string;
|
|
7
7
|
category_icon?: string;
|
|
8
8
|
}
|
|
9
|
+
export interface ExportItem {
|
|
10
|
+
label: string;
|
|
11
|
+
hint?: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
on_download?: () => void;
|
|
14
|
+
copy_text?: () => string | null;
|
|
15
|
+
show_dpi?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface ExportSection {
|
|
18
|
+
title?: string;
|
|
19
|
+
tooltip?: string;
|
|
20
|
+
items: ExportItem[];
|
|
21
|
+
}
|
package/dist/isosurface/parse.js
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
// Parsers for volumetric data file formats (VASP CHGCAR, Gaussian .cube)
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { ATOMIC_NUMBER_TO_SYMBOL } from '../composition/parse';
|
|
3
|
+
import { VASP_VOLUMETRIC_REGEX } from '../constants';
|
|
4
|
+
import { coerce_elem_symbol, FALLBACK_ELEMENTS } from '../element';
|
|
5
|
+
import { strip_compression_extensions } from '../io';
|
|
4
6
|
import * as math from '../math';
|
|
5
7
|
import { wrap_to_unit_cell } from '../structure/pbc';
|
|
8
|
+
import { make_site } from '../structure/site';
|
|
6
9
|
// Bohr radius in Angstroms (for Gaussian .cube unit conversion)
|
|
7
10
|
const BOHR_TO_ANGSTROM = 0.529177249;
|
|
11
|
+
// === Parse error contract ===
|
|
12
|
+
// parse_chgcar/parse_cube return null and record reasons here (mirrored to console.error).
|
|
13
|
+
// parse_volumetric_file resets per call and throws when the FILENAME identifies a volumetric
|
|
14
|
+
// format that fails to parse, but returns null when content doesn't look volumetric at all
|
|
15
|
+
// (probe semantics — callers then fall back to structure parsing).
|
|
16
|
+
let vol_parse_errors = [];
|
|
17
|
+
const vol_error = (message) => {
|
|
18
|
+
vol_parse_errors.push(message);
|
|
19
|
+
console.error(message);
|
|
20
|
+
};
|
|
8
21
|
// === Fast number parsing utilities ===
|
|
9
22
|
// Parse whitespace-separated numbers directly from a string, starting at `pos`.
|
|
10
23
|
// Writes into a pre-allocated Float64Array and returns { count, end_pos }.
|
|
@@ -177,7 +190,7 @@ export function parse_chgcar(content) {
|
|
|
177
190
|
cur = read_line(content, pos);
|
|
178
191
|
const scale_factor = parseFloat(cur.line);
|
|
179
192
|
if (isNaN(scale_factor)) {
|
|
180
|
-
|
|
193
|
+
vol_error(`Invalid scaling factor in CHGCAR`);
|
|
181
194
|
return null;
|
|
182
195
|
}
|
|
183
196
|
pos = cur.next;
|
|
@@ -195,7 +208,7 @@ export function parse_chgcar(content) {
|
|
|
195
208
|
let atom_counts = [];
|
|
196
209
|
cur = read_line(content, pos);
|
|
197
210
|
if (pos >= content.length) {
|
|
198
|
-
|
|
211
|
+
vol_error(`CHGCAR: file ends before element/count lines`);
|
|
199
212
|
return null;
|
|
200
213
|
}
|
|
201
214
|
// Detect VASP 5+ format (has element symbols before counts)
|
|
@@ -206,19 +219,18 @@ export function parse_chgcar(content) {
|
|
|
206
219
|
pos = cur.next;
|
|
207
220
|
cur = read_line(content, pos);
|
|
208
221
|
if (pos >= content.length) {
|
|
209
|
-
|
|
222
|
+
vol_error(`CHGCAR: file ends before atom counts line`);
|
|
210
223
|
return null;
|
|
211
224
|
}
|
|
212
225
|
atom_counts = cur.line.trim().split(/\s+/).map(Number);
|
|
213
226
|
}
|
|
214
227
|
else {
|
|
215
228
|
atom_counts = cur.line.trim().split(/\s+/).map(Number);
|
|
216
|
-
|
|
217
|
-
element_symbols = atom_counts.map((_count, idx) => fallback_elements[idx % fallback_elements.length]);
|
|
229
|
+
element_symbols = atom_counts.map((_count, idx) => FALLBACK_ELEMENTS[idx % FALLBACK_ELEMENTS.length]);
|
|
218
230
|
}
|
|
219
231
|
pos = cur.next;
|
|
220
232
|
if (pos >= content.length) {
|
|
221
|
-
|
|
233
|
+
vol_error(`CHGCAR: file ends before coordinate mode line`);
|
|
222
234
|
return null;
|
|
223
235
|
}
|
|
224
236
|
// Check for selective dynamics line
|
|
@@ -228,7 +240,7 @@ export function parse_chgcar(content) {
|
|
|
228
240
|
cur = read_line(content, pos);
|
|
229
241
|
}
|
|
230
242
|
if (pos >= content.length) {
|
|
231
|
-
|
|
243
|
+
vol_error(`CHGCAR: file ends before coordinate mode line`);
|
|
232
244
|
return null;
|
|
233
245
|
}
|
|
234
246
|
// Coordinate mode line
|
|
@@ -242,18 +254,18 @@ export function parse_chgcar(content) {
|
|
|
242
254
|
({ cart_to_frac, frac_to_cart } = math.create_lattice_converters(lattice));
|
|
243
255
|
}
|
|
244
256
|
catch {
|
|
245
|
-
|
|
257
|
+
vol_error(`CHGCAR: lattice matrix is singular; cannot convert coordinates`);
|
|
246
258
|
return null;
|
|
247
259
|
}
|
|
248
260
|
const sites = [];
|
|
249
261
|
let atom_idx = 0;
|
|
250
262
|
for (let elem_idx = 0; elem_idx < element_symbols.length; elem_idx++) {
|
|
251
263
|
const symbol = element_symbols[elem_idx].split(/[_/]/)[0];
|
|
252
|
-
const element = (
|
|
264
|
+
const element = coerce_elem_symbol(symbol) ?? `H`;
|
|
253
265
|
const count = atom_counts[elem_idx];
|
|
254
266
|
for (let count_idx = 0; count_idx < count; count_idx++) {
|
|
255
267
|
if (pos >= content.length) {
|
|
256
|
-
|
|
268
|
+
vol_error(`CHGCAR: file ends before all atom coordinates are read`);
|
|
257
269
|
return null;
|
|
258
270
|
}
|
|
259
271
|
cur = read_line(content, pos);
|
|
@@ -270,13 +282,7 @@ export function parse_chgcar(content) {
|
|
|
270
282
|
const raw = cart_to_frac(xyz);
|
|
271
283
|
abc = wrap_to_unit_cell(raw);
|
|
272
284
|
}
|
|
273
|
-
sites.push({
|
|
274
|
-
species: [{ element, occu: 1, oxidation_state: 0 }],
|
|
275
|
-
abc,
|
|
276
|
-
xyz,
|
|
277
|
-
label: `${element}${atom_idx + count_idx + 1}`,
|
|
278
|
-
properties: {},
|
|
279
|
-
});
|
|
285
|
+
sites.push(make_site(element, abc, xyz, `${element}${atom_idx + count_idx + 1}`));
|
|
280
286
|
}
|
|
281
287
|
atom_idx += count;
|
|
282
288
|
}
|
|
@@ -348,7 +354,7 @@ export function parse_chgcar(content) {
|
|
|
348
354
|
}
|
|
349
355
|
}
|
|
350
356
|
if (volumes.length === 0) {
|
|
351
|
-
|
|
357
|
+
vol_error(`No volumetric data found in CHGCAR`);
|
|
352
358
|
return null;
|
|
353
359
|
}
|
|
354
360
|
return { structure, volumes };
|
|
@@ -365,7 +371,7 @@ export function parse_cube(content, options = {}) {
|
|
|
365
371
|
line_count++;
|
|
366
372
|
}
|
|
367
373
|
if (line_count < 6) {
|
|
368
|
-
|
|
374
|
+
vol_error(`.cube file too short`);
|
|
369
375
|
return null;
|
|
370
376
|
}
|
|
371
377
|
// Parse header (first 6 lines + atom lines)
|
|
@@ -376,7 +382,7 @@ export function parse_cube(content, options = {}) {
|
|
|
376
382
|
// (negative n_atoms indicates orbital data with extra header line)
|
|
377
383
|
const line2 = header.lines[2].split(/\s+/).map(Number);
|
|
378
384
|
if (line2.length < 4 || line2.some(isNaN)) {
|
|
379
|
-
|
|
385
|
+
vol_error(`.cube header line 3 malformed: expected 4 numbers`);
|
|
380
386
|
return null;
|
|
381
387
|
}
|
|
382
388
|
const n_atoms = Math.abs(line2[0]);
|
|
@@ -390,7 +396,7 @@ export function parse_cube(content, options = {}) {
|
|
|
390
396
|
header.lines[5].split(/\s+/).map(Number),
|
|
391
397
|
];
|
|
392
398
|
if (voxel_lines.some((line) => line.length < 4 || line.some(isNaN))) {
|
|
393
|
-
|
|
399
|
+
vol_error(`.cube voxel lines malformed: expected 4 numbers per line`);
|
|
394
400
|
return null;
|
|
395
401
|
}
|
|
396
402
|
const n_grid = [
|
|
@@ -423,7 +429,7 @@ export function parse_cube(content, options = {}) {
|
|
|
423
429
|
}
|
|
424
430
|
catch {
|
|
425
431
|
// Non-periodic system (molecule), use identity
|
|
426
|
-
cube_cart_to_frac = (
|
|
432
|
+
cube_cart_to_frac = (vec) => [vec[0], vec[1], vec[2]];
|
|
427
433
|
}
|
|
428
434
|
for (let atom_idx = 0; atom_idx < n_atoms; atom_idx++) {
|
|
429
435
|
const cur = read_line(content, pos);
|
|
@@ -444,13 +450,7 @@ export function parse_cube(content, options = {}) {
|
|
|
444
450
|
const xyz = math.subtract(raw_xyz, origin);
|
|
445
451
|
const abc = cube_cart_to_frac(xyz);
|
|
446
452
|
const element = atomic_number_to_symbol(atom_line[0]);
|
|
447
|
-
sites.push({
|
|
448
|
-
species: [{ element, occu: 1, oxidation_state: 0 }],
|
|
449
|
-
abc,
|
|
450
|
-
xyz,
|
|
451
|
-
label: `${element}${atom_idx + 1}`,
|
|
452
|
-
properties: {},
|
|
453
|
-
});
|
|
453
|
+
sites.push(make_site(element, abc, xyz, `${element}${atom_idx + 1}`));
|
|
454
454
|
}
|
|
455
455
|
// Build structure
|
|
456
456
|
const lattice_params = math.calc_lattice_params(lattice);
|
|
@@ -474,7 +474,7 @@ export function parse_cube(content, options = {}) {
|
|
|
474
474
|
if (parsed_count < total_points) {
|
|
475
475
|
console.warn(`.cube: expected ${total_points} data values, got ${parsed_count}`);
|
|
476
476
|
if (parsed_count === 0) {
|
|
477
|
-
|
|
477
|
+
vol_error(`No volumetric data found in .cube file`);
|
|
478
478
|
return null;
|
|
479
479
|
}
|
|
480
480
|
}
|
|
@@ -499,22 +499,24 @@ export function parse_cube(content, options = {}) {
|
|
|
499
499
|
];
|
|
500
500
|
return { structure, volumes };
|
|
501
501
|
}
|
|
502
|
-
// Convert atomic number to element symbol
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
const idx = atomic_number - 1;
|
|
506
|
-
return idx >= 0 && idx < ELEM_SYMBOLS.length ? ELEM_SYMBOLS[idx] : `H`;
|
|
507
|
-
}
|
|
508
|
-
// Auto-detect and parse volumetric file format based on filename and content
|
|
502
|
+
// Convert atomic number to element symbol (falls back to H for unknown numbers)
|
|
503
|
+
const atomic_number_to_symbol = (atomic_number) => ATOMIC_NUMBER_TO_SYMBOL[atomic_number] ?? `H`;
|
|
504
|
+
// Auto-detect and parse volumetric file by filename + content (see parse error contract at top)
|
|
509
505
|
export function parse_volumetric_file(content, filename) {
|
|
506
|
+
vol_parse_errors = [];
|
|
507
|
+
const fail = (format) => {
|
|
508
|
+
const detail = vol_parse_errors.length ? `: ${vol_parse_errors.join(`; `)}` : ``;
|
|
509
|
+
throw new Error(`Failed to parse ${format} file${filename ? ` '${filename}'` : ``}${detail}`);
|
|
510
|
+
};
|
|
510
511
|
// Strip compression suffixes so "CHGCAR.gz" and "molecule.cube.bz2" match correctly
|
|
511
|
-
const lower_name = (filename ?? ``)
|
|
512
|
-
// Extension-based detection
|
|
512
|
+
const lower_name = strip_compression_extensions(filename ?? ``);
|
|
513
|
+
// Extension-based detection (filename is authoritative: parse failure throws)
|
|
513
514
|
if (lower_name.endsWith(`.cube`))
|
|
514
|
-
return parse_cube(content);
|
|
515
|
+
return parse_cube(content) ?? fail(`.cube`);
|
|
515
516
|
// VASP volumetric file detection by filename
|
|
516
|
-
if (VASP_VOLUMETRIC_REGEX.test(lower_name))
|
|
517
|
-
return parse_chgcar(content);
|
|
517
|
+
if (VASP_VOLUMETRIC_REGEX.test(lower_name)) {
|
|
518
|
+
return parse_chgcar(content) ?? fail(`VASP volumetric (CHGCAR-like)`);
|
|
519
|
+
}
|
|
518
520
|
// Content-based detection (only parse first few lines, not the whole file)
|
|
519
521
|
// Find enough lines for detection without splitting the entire string
|
|
520
522
|
const detection_end = find_line_offset(content, 10);
|
package/dist/labels.js
CHANGED
|
@@ -16,7 +16,7 @@ export const symbol_names = [
|
|
|
16
16
|
...new Set([...d3_symbols.symbolsFill, ...d3_symbols.symbolsStroke]),
|
|
17
17
|
]
|
|
18
18
|
.map(name_for_symbol)
|
|
19
|
-
.filter((
|
|
19
|
+
.filter((name) => name !== null);
|
|
20
20
|
export const symbol_map = Object.fromEntries(
|
|
21
21
|
// Symbol lookup from d3-shape
|
|
22
22
|
symbol_names.map((name) => [name, d3_symbols[`symbol${name}`]]));
|