matterviz 0.3.2 → 0.3.3
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/EmptyState.svelte +10 -2
- package/dist/FilePicker.svelte +123 -82
- package/dist/Icon.svelte +18 -12
- package/dist/MillerIndexInput.svelte +27 -21
- package/dist/api/optimade.js +6 -6
- package/dist/app.css +216 -207
- package/dist/brillouin/BrillouinZone.svelte +292 -149
- package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
- package/dist/brillouin/BrillouinZoneExportPane.svelte +69 -42
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
- package/dist/brillouin/BrillouinZoneScene.svelte +275 -163
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
- package/dist/brillouin/compute.js +11 -6
- package/dist/chempot-diagram/ChemPotDiagram.svelte +162 -27
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +451 -281
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2148 -1642
- package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -5
- package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
- package/dist/chempot-diagram/async-compute.svelte.js +77 -0
- package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
- package/dist/chempot-diagram/chempot-worker.js +11 -0
- package/dist/chempot-diagram/color.js +1 -2
- package/dist/chempot-diagram/compute.d.ts +10 -0
- package/dist/chempot-diagram/compute.js +250 -88
- package/dist/chempot-diagram/index.d.ts +2 -1
- package/dist/chempot-diagram/index.js +2 -1
- package/dist/chempot-diagram/temperature.js +8 -9
- package/dist/chempot-diagram/types.d.ts +3 -0
- package/dist/chempot-diagram/types.js +1 -0
- package/dist/colors/index.d.ts +1 -1
- package/dist/colors/index.js +5 -3
- package/dist/composition/BarChart.svelte +128 -55
- package/dist/composition/BubbleChart.svelte +102 -49
- package/dist/composition/Composition.svelte +100 -79
- package/dist/composition/Formula.svelte +108 -62
- package/dist/composition/FormulaFilter.svelte +665 -537
- package/dist/composition/PieChart.svelte +183 -108
- package/dist/composition/format.d.ts +5 -0
- package/dist/composition/format.js +20 -3
- package/dist/composition/parse.js +14 -9
- package/dist/convex-hull/ConvexHull.svelte +93 -40
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull2D.svelte +549 -360
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +1296 -827
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +1004 -688
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullControls.svelte +115 -28
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
- package/dist/convex-hull/ConvexHullStats.svelte +425 -328
- package/dist/convex-hull/ConvexHullTooltip.svelte +40 -16
- package/dist/convex-hull/GasPressureControls.svelte +104 -61
- package/dist/convex-hull/StructurePopup.svelte +25 -4
- package/dist/convex-hull/TemperatureSlider.svelte +45 -25
- package/dist/convex-hull/barycentric-coords.js +13 -7
- package/dist/convex-hull/demo-temperature.js +8 -4
- package/dist/convex-hull/gas-thermodynamics.js +17 -12
- package/dist/convex-hull/helpers.d.ts +9 -0
- package/dist/convex-hull/helpers.js +77 -34
- package/dist/convex-hull/thermodynamics.js +61 -56
- package/dist/convex-hull/types.d.ts +9 -14
- package/dist/convex-hull/types.js +0 -17
- package/dist/coordination/CoordinationBarPlot.svelte +227 -154
- package/dist/element/BohrAtom.svelte +55 -12
- package/dist/element/ElementHeading.svelte +7 -2
- package/dist/element/ElementPhoto.svelte +15 -9
- package/dist/element/ElementStats.svelte +10 -4
- package/dist/element/ElementTile.svelte +137 -73
- package/dist/element/Nucleus.svelte +39 -11
- package/dist/feedback/ClickFeedback.svelte +16 -5
- package/dist/feedback/DragOverlay.svelte +10 -2
- package/dist/feedback/Spinner.svelte +4 -2
- package/dist/feedback/StatusMessage.svelte +8 -2
- package/dist/fermi-surface/FermiSlice.svelte +118 -88
- package/dist/fermi-surface/FermiSurface.svelte +328 -187
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +535 -342
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
- package/dist/fermi-surface/compute.js +16 -20
- package/dist/fermi-surface/parse.js +24 -14
- package/dist/fermi-surface/symmetry.js +2 -7
- package/dist/fermi-surface/types.d.ts +3 -5
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +1019 -765
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +1 -1
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +76 -22
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +2 -3
- package/dist/icons.js +47 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/io/decompress.js +1 -1
- package/dist/io/export.d.ts +3 -0
- package/dist/io/export.js +129 -143
- package/dist/io/is-binary.js +2 -3
- package/dist/io/url-drop.js +1 -2
- package/dist/isosurface/Isosurface.svelte +202 -148
- package/dist/isosurface/IsosurfaceControls.svelte +46 -28
- package/dist/isosurface/parse.js +34 -29
- package/dist/isosurface/slice.js +5 -10
- package/dist/isosurface/types.d.ts +2 -1
- package/dist/isosurface/types.js +61 -12
- package/dist/labels.js +11 -8
- package/dist/layout/FullscreenToggle.svelte +11 -2
- package/dist/layout/InfoCard.svelte +38 -6
- package/dist/layout/InfoTag.svelte +63 -32
- package/dist/layout/PropertyFilter.svelte +82 -37
- package/dist/layout/SettingsSection.svelte +85 -55
- package/dist/layout/SubpageGrid.svelte +10 -2
- package/dist/layout/json-tree/JsonNode.svelte +183 -138
- package/dist/layout/json-tree/JsonTree.svelte +499 -413
- package/dist/layout/json-tree/JsonValue.svelte +127 -99
- package/dist/layout/json-tree/utils.js +4 -2
- package/dist/marching-cubes.js +25 -2
- package/dist/math.d.ts +13 -17
- package/dist/math.js +133 -67
- package/dist/overlays/ContextMenu.svelte +65 -40
- package/dist/overlays/DraggablePane.svelte +211 -139
- package/dist/periodic-table/PeriodicTable.svelte +278 -145
- package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
- package/dist/periodic-table/PropertySelect.svelte +25 -7
- package/dist/periodic-table/TableInset.svelte +8 -3
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +446 -309
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramControls.svelte +102 -43
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +63 -40
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +71 -28
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +158 -101
- package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
- package/dist/phase-diagram/build-diagram.js +9 -9
- package/dist/phase-diagram/colors.js +1 -3
- package/dist/phase-diagram/parse.js +10 -9
- package/dist/phase-diagram/svg-to-diagram.js +53 -49
- package/dist/phase-diagram/utils.d.ts +1 -0
- package/dist/phase-diagram/utils.js +80 -25
- package/dist/plot/AxisLabel.svelte +28 -3
- package/dist/plot/BarPlot.svelte +1182 -734
- package/dist/plot/BarPlot.svelte.d.ts +2 -2
- package/dist/plot/BarPlotControls.svelte +31 -5
- package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ColorBar.svelte +479 -329
- package/dist/plot/ColorScaleSelect.svelte +27 -6
- package/dist/plot/ElementScatter.svelte +36 -15
- package/dist/plot/FillArea.svelte +152 -95
- package/dist/plot/Histogram.svelte +934 -571
- package/dist/plot/Histogram.svelte.d.ts +1 -1
- package/dist/plot/HistogramControls.svelte +53 -9
- package/dist/plot/HistogramControls.svelte.d.ts +1 -1
- package/dist/plot/InteractiveAxisLabel.svelte +34 -11
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
- package/dist/plot/Line.svelte +63 -28
- package/dist/plot/PlotControls.svelte +157 -114
- package/dist/plot/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/PlotLegend.svelte +174 -91
- package/dist/plot/PlotTooltip.svelte +45 -6
- package/dist/plot/PortalSelect.svelte +175 -147
- package/dist/plot/ReferenceLine.svelte +76 -22
- package/dist/plot/ReferenceLine3D.svelte +132 -107
- package/dist/plot/ReferencePlane.svelte +146 -121
- package/dist/plot/ScatterPlot.svelte +1681 -1091
- package/dist/plot/ScatterPlot.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3D.svelte +256 -131
- package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3DControls.svelte +113 -63
- package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
- package/dist/plot/ScatterPlot3DScene.svelte +608 -403
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlotControls.svelte +65 -25
- package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ScatterPoint.svelte +98 -26
- package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
- package/dist/plot/SpacegroupBarPlot.svelte +142 -85
- package/dist/plot/Surface3D.svelte +159 -108
- package/dist/plot/ZeroLines.svelte +55 -3
- package/dist/plot/ZoomRect.svelte +4 -2
- package/dist/plot/axis-utils.js +1 -3
- package/dist/plot/data-cleaning.js +12 -28
- package/dist/plot/data-transform.js +2 -1
- package/dist/plot/fill-utils.js +2 -0
- package/dist/plot/layout.d.ts +4 -1
- package/dist/plot/layout.js +33 -14
- package/dist/plot/reference-line.d.ts +2 -2
- package/dist/plot/reference-line.js +7 -5
- package/dist/plot/scales.js +24 -36
- package/dist/plot/types.d.ts +11 -23
- package/dist/plot/types.js +6 -11
- package/dist/plot/utils/label-placement.d.ts +32 -15
- package/dist/plot/utils/label-placement.js +227 -66
- package/dist/plot/utils/series-visibility.js +2 -3
- package/dist/rdf/RdfPlot.svelte +143 -91
- package/dist/rdf/calc-rdf.js +4 -5
- package/dist/sanitize.d.ts +4 -0
- package/dist/sanitize.js +107 -0
- package/dist/settings.d.ts +18 -6
- package/dist/settings.js +46 -16
- package/dist/spectral/Bands.svelte +632 -453
- package/dist/spectral/BandsAndDos.svelte +90 -49
- package/dist/spectral/BrillouinBandsDos.svelte +151 -93
- package/dist/spectral/Dos.svelte +389 -258
- package/dist/spectral/helpers.js +55 -43
- package/dist/state.svelte.d.ts +1 -1
- package/dist/state.svelte.js +3 -2
- package/dist/structure/Arrow.svelte +59 -20
- package/dist/structure/AtomLegend.svelte +215 -134
- package/dist/structure/Bond.svelte +73 -47
- package/dist/structure/CanvasTooltip.svelte +10 -2
- package/dist/structure/CellSelect.svelte +72 -45
- package/dist/structure/Cylinder.svelte +33 -17
- package/dist/structure/Lattice.svelte +88 -33
- package/dist/structure/Structure.svelte +1063 -797
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureControls.svelte +349 -118
- package/dist/structure/StructureExportPane.svelte +124 -89
- package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +304 -237
- package/dist/structure/StructureScene.svelte +879 -443
- package/dist/structure/StructureScene.svelte.d.ts +15 -7
- package/dist/structure/atom-properties.js +8 -8
- package/dist/structure/bonding.js +6 -7
- package/dist/structure/export.js +14 -29
- package/dist/structure/ferrox-wasm.js +1 -1
- package/dist/structure/index.d.ts +13 -3
- package/dist/structure/index.js +83 -23
- package/dist/structure/measure.d.ts +2 -2
- package/dist/structure/measure.js +4 -44
- package/dist/structure/parse.js +113 -141
- package/dist/structure/partial-occupancy.js +7 -10
- package/dist/structure/pbc.d.ts +1 -0
- package/dist/structure/pbc.js +16 -6
- package/dist/structure/supercell.d.ts +2 -2
- package/dist/structure/supercell.js +12 -22
- package/dist/structure/validation.js +1 -2
- package/dist/symmetry/SymmetryStats.svelte +84 -41
- package/dist/symmetry/WyckoffTable.svelte +26 -6
- package/dist/symmetry/cell-transform.js +5 -3
- package/dist/symmetry/index.js +8 -7
- package/dist/symmetry/spacegroups.js +148 -148
- package/dist/table/HeatmapTable.svelte +790 -554
- package/dist/table/HeatmapTable.svelte.d.ts +1 -1
- package/dist/table/ToggleMenu.svelte +125 -92
- package/dist/table/index.js +2 -4
- package/dist/theme/ThemeControl.svelte +21 -12
- package/dist/time.js +4 -1
- package/dist/tooltip/TooltipContent.svelte +33 -8
- package/dist/trajectory/Trajectory.svelte +758 -558
- package/dist/trajectory/TrajectoryError.svelte +14 -3
- package/dist/trajectory/TrajectoryExportPane.svelte +137 -83
- package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
- package/dist/trajectory/extract.js +10 -26
- package/dist/trajectory/format-detect.js +5 -5
- package/dist/trajectory/frame-reader.d.ts +1 -1
- package/dist/trajectory/frame-reader.js +5 -12
- package/dist/trajectory/helpers.d.ts +0 -1
- package/dist/trajectory/helpers.js +2 -17
- package/dist/trajectory/index.js +14 -12
- package/dist/trajectory/parse/ase.js +5 -4
- package/dist/trajectory/parse/hdf5.js +26 -18
- package/dist/trajectory/parse/index.js +13 -18
- package/dist/trajectory/parse/lammps.js +17 -7
- package/dist/trajectory/parse/vasp.js +5 -2
- package/dist/trajectory/parse/xyz.js +8 -7
- package/dist/trajectory/plotting.js +13 -8
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +13 -0
- package/dist/xrd/XrdPlot.svelte +337 -247
- package/dist/xrd/broadening.js +14 -9
- package/dist/xrd/calc-xrd.js +12 -18
- package/dist/xrd/parse.d.ts +1 -1
- package/dist/xrd/parse.js +17 -17
- package/package.json +99 -103
- package/readme.md +1 -1
- /package/dist/theme/{themes.js → themes.mjs} +0 -0
|
@@ -1,178 +1,232 @@
|
|
|
1
|
-
<script lang="ts"
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Threlte component that renders isosurface meshes from volumetric data using marching cubes.
|
|
3
|
+
// Supports multiple layers at different isovalues with independent colors,
|
|
4
|
+
// plus positive/negative lobes and two-pass transparency.
|
|
5
|
+
import { marching_cubes } from '../marching-cubes'
|
|
6
|
+
import type { Matrix3x3, Vec3 } from '../math'
|
|
7
|
+
import { T } from '@threlte/core'
|
|
8
|
+
import {
|
|
9
|
+
BackSide,
|
|
10
|
+
BufferAttribute,
|
|
11
|
+
BufferGeometry,
|
|
12
|
+
DoubleSide,
|
|
13
|
+
FrontSide,
|
|
14
|
+
Uint32BufferAttribute,
|
|
15
|
+
} from 'three'
|
|
16
|
+
import type { IsosurfaceLayer, IsosurfaceSettings, VolumetricData } from './types'
|
|
17
|
+
import {
|
|
18
|
+
DEFAULT_ISOSURFACE_SETTINGS,
|
|
19
|
+
downsample_grid,
|
|
20
|
+
pad_periodic_grid,
|
|
21
|
+
} from './types'
|
|
22
|
+
|
|
23
|
+
let { volume, settings = DEFAULT_ISOSURFACE_SETTINGS }: {
|
|
24
|
+
volume: VolumetricData
|
|
25
|
+
settings?: IsosurfaceSettings
|
|
26
|
+
} = $props()
|
|
27
|
+
|
|
28
|
+
// Resolve layers: use explicit layers array if provided, else build from single-isovalue settings
|
|
29
|
+
let resolved_layers = $derived.by((): IsosurfaceLayer[] => {
|
|
30
|
+
if (settings.layers?.length) return settings.layers
|
|
13
31
|
return [{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
isovalue: settings.isovalue,
|
|
33
|
+
color: settings.positive_color,
|
|
34
|
+
opacity: settings.opacity,
|
|
35
|
+
visible: true,
|
|
36
|
+
show_negative: settings.show_negative,
|
|
37
|
+
negative_color: settings.negative_color,
|
|
38
|
+
}]
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Build indexed BufferGeometry from marching cubes output.
|
|
42
|
+
// Uses Three.js index buffer to avoid tripling vertex data, and
|
|
43
|
+
// computeVertexNormals() for fast GPU-friendly normals.
|
|
44
|
+
function build_geometry(
|
|
45
|
+
vertices: Vec3[],
|
|
46
|
+
faces: number[][],
|
|
47
|
+
): BufferGeometry | null {
|
|
48
|
+
if (vertices.length === 0 || faces.length === 0) return null
|
|
49
|
+
|
|
28
50
|
// Flatten vertices: Vec3[] → Float32Array
|
|
29
|
-
const positions = new Float32Array(vertices.length * 3)
|
|
51
|
+
const positions = new Float32Array(vertices.length * 3)
|
|
30
52
|
for (let idx = 0; idx < vertices.length; idx++) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
53
|
+
const vert = vertices[idx]
|
|
54
|
+
positions[idx * 3] = vert[0]
|
|
55
|
+
positions[idx * 3 + 1] = vert[1]
|
|
56
|
+
positions[idx * 3 + 2] = vert[2]
|
|
35
57
|
}
|
|
58
|
+
|
|
36
59
|
// Flatten face indices: number[][] → Uint32Array
|
|
37
|
-
const indices = new Uint32Array(faces.length * 3)
|
|
60
|
+
const indices = new Uint32Array(faces.length * 3)
|
|
38
61
|
for (let idx = 0; idx < faces.length; idx++) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
62
|
+
const face = faces[idx]
|
|
63
|
+
indices[idx * 3] = face[0]
|
|
64
|
+
indices[idx * 3 + 1] = face[1]
|
|
65
|
+
indices[idx * 3 + 2] = face[2]
|
|
43
66
|
}
|
|
44
|
-
|
|
45
|
-
geometry
|
|
46
|
-
geometry.
|
|
47
|
-
geometry.
|
|
48
|
-
geometry.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
|
|
68
|
+
const geometry = new BufferGeometry()
|
|
69
|
+
geometry.setAttribute(`position`, new BufferAttribute(positions, 3))
|
|
70
|
+
geometry.setIndex(new Uint32BufferAttribute(indices, 1))
|
|
71
|
+
geometry.computeVertexNormals()
|
|
72
|
+
geometry.computeBoundingSphere()
|
|
73
|
+
return geometry
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Downsample large grids once when volume changes to keep marching cubes interactive
|
|
77
|
+
let ds_result = $derived.by(() => {
|
|
78
|
+
if (!volume) return undefined
|
|
79
|
+
return downsample_grid(volume.grid, volume.grid_dims)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Run marching cubes at the given isovalue with pre-prepared grid/lattice/shift.
|
|
83
|
+
function extract_surface(
|
|
84
|
+
isovalue: number,
|
|
85
|
+
mc_grid: number[][][],
|
|
86
|
+
mc_lattice: Matrix3x3,
|
|
87
|
+
origin_shift: Vec3 | null,
|
|
88
|
+
): BufferGeometry | null {
|
|
89
|
+
if (isovalue === 0) return null
|
|
90
|
+
|
|
61
91
|
const result = marching_cubes(mc_grid, isovalue, mc_lattice, {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
})
|
|
92
|
+
periodic: false,
|
|
93
|
+
interpolate: true,
|
|
94
|
+
centered: false,
|
|
95
|
+
normals: false,
|
|
96
|
+
})
|
|
97
|
+
|
|
67
98
|
if (origin_shift) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
99
|
+
for (const vert of result.vertices) {
|
|
100
|
+
vert[0] += origin_shift[0]
|
|
101
|
+
vert[1] += origin_shift[1]
|
|
102
|
+
vert[2] += origin_shift[2]
|
|
103
|
+
}
|
|
73
104
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
105
|
+
|
|
106
|
+
return build_geometry(result.vertices, result.faces)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// === Multi-layer geometry management ===
|
|
110
|
+
// Each layer produces up to 2 geometries (positive + optional negative lobe).
|
|
111
|
+
// Keyed by "layer_idx:sign" for cache invalidation.
|
|
112
|
+
type GeoEntry = {
|
|
113
|
+
geometry: BufferGeometry
|
|
114
|
+
color: string
|
|
115
|
+
opacity: number
|
|
116
|
+
render_order: number
|
|
117
|
+
}
|
|
118
|
+
let active_geometries = $state<GeoEntry[]>([])
|
|
119
|
+
let raf_id = 0
|
|
120
|
+
let debounce_id = 0
|
|
121
|
+
|
|
122
|
+
// Dispose all current geometries
|
|
123
|
+
function dispose_all() {
|
|
124
|
+
for (const entry of active_geometries) entry.geometry.dispose()
|
|
125
|
+
active_geometries = []
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Dispose on unmount
|
|
129
|
+
$effect(() => () => dispose_all())
|
|
130
|
+
|
|
131
|
+
function rebuild_geometries(layers: IsosurfaceLayer[]) {
|
|
132
|
+
if (!volume || !ds_result) return
|
|
133
|
+
const old = active_geometries
|
|
134
|
+
const entries: GeoEntry[] = []
|
|
135
|
+
|
|
92
136
|
// Prepare grid/lattice/shift once for all layers.
|
|
93
137
|
// When halo > 0 for periodic volumes, the downsampled grid is padded with
|
|
94
138
|
// halo cells from the opposite face so isosurfaces extend beyond the unit
|
|
95
139
|
// cell and close into complete enclosed shapes around boundary atoms.
|
|
96
|
-
let mc_grid = ds_result.grid
|
|
97
|
-
let mc_lattice = volume.lattice
|
|
98
|
-
let origin_shift = null
|
|
140
|
+
let mc_grid = ds_result.grid
|
|
141
|
+
let mc_lattice: Matrix3x3 = volume.lattice
|
|
142
|
+
let origin_shift: Vec3 | null = null
|
|
143
|
+
|
|
99
144
|
if (settings.halo > 0 && volume.periodic) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
145
|
+
const padded = pad_periodic_grid(ds_result.grid, ds_result.dims, settings.halo)
|
|
146
|
+
mc_grid = padded.grid
|
|
147
|
+
// marching_cubes maps [0,1] fractional -> Cartesian via lattice.
|
|
148
|
+
// The padded grid covers a wider fractional range, so scale the lattice
|
|
149
|
+
// to match. Then shift all vertices by the fractional offset.
|
|
150
|
+
const [la, lb, lc] = volume.lattice
|
|
151
|
+
const sx = padded.dims[0] / ds_result.dims[0]
|
|
152
|
+
const sy = padded.dims[1] / ds_result.dims[1]
|
|
153
|
+
const sz = padded.dims[2] / ds_result.dims[2]
|
|
154
|
+
mc_lattice = [
|
|
155
|
+
[la[0] * sx, la[1] * sx, la[2] * sx],
|
|
156
|
+
[lb[0] * sy, lb[1] * sy, lb[2] * sy],
|
|
157
|
+
[lc[0] * sz, lc[1] * sz, lc[2] * sz],
|
|
158
|
+
]
|
|
159
|
+
const [ox, oy, oz] = padded.offset
|
|
160
|
+
origin_shift = [
|
|
161
|
+
ox * la[0] + oy * lb[0] + oz * lc[0],
|
|
162
|
+
ox * la[1] + oy * lb[1] + oz * lc[1],
|
|
163
|
+
ox * la[2] + oy * lb[2] + oz * lc[2],
|
|
164
|
+
]
|
|
120
165
|
}
|
|
121
|
-
|
|
166
|
+
|
|
167
|
+
const surface_at = (isovalue: number) =>
|
|
168
|
+
extract_surface(isovalue, mc_grid, mc_lattice, origin_shift)
|
|
169
|
+
|
|
122
170
|
// Render lower-isovalue (outer) shells earlier so per-layer back/front passes
|
|
123
171
|
// interleave back-to-front across shells and reduce transparency artefacts.
|
|
124
|
-
const layer_render_rank = new Map(
|
|
172
|
+
const layer_render_rank = new Map<number, number>(
|
|
173
|
+
layers
|
|
125
174
|
.map((layer, layer_idx) => ({ layer_idx, isovalue: layer.isovalue }))
|
|
126
175
|
.sort((layer_a, layer_b) => layer_a.isovalue - layer_b.isovalue)
|
|
127
|
-
.map(({ layer_idx }, rank) => [layer_idx, rank])
|
|
176
|
+
.map(({ layer_idx }, rank) => [layer_idx, rank]),
|
|
177
|
+
)
|
|
178
|
+
|
|
128
179
|
for (let layer_idx = 0; layer_idx < layers.length; layer_idx++) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
180
|
+
const layer = layers[layer_idx]
|
|
181
|
+
if (!layer.visible || layer.isovalue <= 0) continue
|
|
182
|
+
|
|
183
|
+
// Each layer gets 4 slots (positive back/front + negative back/front).
|
|
184
|
+
const base_order = (layer_render_rank.get(layer_idx) ?? layer_idx) * 4
|
|
185
|
+
|
|
186
|
+
const pos_geo = surface_at(layer.isovalue)
|
|
187
|
+
if (pos_geo) {
|
|
188
|
+
entries.push({
|
|
189
|
+
geometry: pos_geo,
|
|
190
|
+
color: layer.color,
|
|
191
|
+
opacity: layer.opacity,
|
|
192
|
+
render_order: base_order,
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (layer.show_negative) {
|
|
197
|
+
const neg_geo = surface_at(-layer.isovalue)
|
|
198
|
+
if (neg_geo) {
|
|
199
|
+
entries.push({
|
|
200
|
+
geometry: neg_geo,
|
|
201
|
+
color: layer.negative_color,
|
|
202
|
+
opacity: layer.opacity,
|
|
203
|
+
render_order: base_order + 2,
|
|
204
|
+
})
|
|
153
205
|
}
|
|
206
|
+
}
|
|
154
207
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
208
|
+
|
|
209
|
+
active_geometries = entries
|
|
210
|
+
for (const entry of old) entry.geometry.dispose()
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Rebuild all layer geometries when layers or volume change.
|
|
214
|
+
// Debounces rapid changes (e.g. slider drags) to avoid repeated expensive marching cubes.
|
|
215
|
+
$effect(() => {
|
|
216
|
+
const layers = resolved_layers
|
|
217
|
+
void settings.halo
|
|
164
218
|
if (!ds_result) {
|
|
165
|
-
|
|
166
|
-
|
|
219
|
+
dispose_all()
|
|
220
|
+
return
|
|
167
221
|
}
|
|
168
222
|
debounce_id = window.setTimeout(() => {
|
|
169
|
-
|
|
170
|
-
}, 50)
|
|
223
|
+
raf_id = requestAnimationFrame(() => rebuild_geometries(layers))
|
|
224
|
+
}, 50)
|
|
171
225
|
return () => {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
})
|
|
226
|
+
clearTimeout(debounce_id)
|
|
227
|
+
cancelAnimationFrame(raf_id)
|
|
228
|
+
}
|
|
229
|
+
})
|
|
176
230
|
</script>
|
|
177
231
|
|
|
178
232
|
<!-- Render each geometry entry with appropriate material -->
|
|
@@ -1,35 +1,53 @@
|
|
|
1
|
-
<script lang="ts"
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Controls panel for isosurface visualization settings (isovalue, opacity, colors, etc.)
|
|
3
|
+
// Supports both single-isovalue mode and multi-layer mode.
|
|
4
|
+
import { format_num } from '../labels'
|
|
5
|
+
import { SettingsSection } from '../layout'
|
|
6
|
+
import { tooltip } from 'svelte-multiselect/attachments'
|
|
7
|
+
import type { IsosurfaceLayer, IsosurfaceSettings, VolumetricData } from './types'
|
|
8
|
+
import { DEFAULT_ISOSURFACE_SETTINGS, generate_layers } from './types'
|
|
9
|
+
|
|
10
|
+
let {
|
|
11
|
+
settings = $bindable({ ...DEFAULT_ISOSURFACE_SETTINGS }),
|
|
12
|
+
volumes = [],
|
|
13
|
+
active_volume_idx = $bindable(0),
|
|
14
|
+
}: {
|
|
15
|
+
settings?: IsosurfaceSettings
|
|
16
|
+
volumes?: VolumetricData[]
|
|
17
|
+
active_volume_idx?: number
|
|
18
|
+
} = $props()
|
|
19
|
+
|
|
20
|
+
// Clamp active_volume_idx when volumes list changes (e.g. dataset swap)
|
|
21
|
+
$effect(() => {
|
|
10
22
|
if (volumes.length > 0 && active_volume_idx >= volumes.length) {
|
|
11
|
-
|
|
23
|
+
active_volume_idx = 0
|
|
12
24
|
}
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
let
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// Use precomputed data_range from the active volume
|
|
28
|
+
let data_range = $derived(
|
|
29
|
+
volumes[active_volume_idx]?.data_range ?? { min: 0, max: 1, abs_max: 1, mean: 0 },
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
let slider_max = $derived(Math.max(data_range.abs_max, 0.001))
|
|
33
|
+
let step = $derived(slider_max / 200)
|
|
34
|
+
let is_multi_layer = $derived((settings.layers?.length ?? 0) > 0)
|
|
35
|
+
let n_layers = $derived(settings.layers?.length ?? 1)
|
|
36
|
+
|
|
37
|
+
function set_layer_count(count: number) {
|
|
21
38
|
if (count <= 1) {
|
|
22
|
-
|
|
39
|
+
settings.layers = undefined
|
|
40
|
+
} else {
|
|
41
|
+
settings.layers = generate_layers(data_range, count)
|
|
23
42
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function update_layer(idx: number, updates: Partial<IsosurfaceLayer>) {
|
|
46
|
+
if (!settings.layers) return
|
|
47
|
+
settings.layers = settings.layers.map((layer, layer_idx) =>
|
|
48
|
+
layer_idx === idx ? { ...layer, ...updates } : layer
|
|
49
|
+
)
|
|
50
|
+
}
|
|
33
51
|
</script>
|
|
34
52
|
|
|
35
53
|
<SettingsSection
|
package/dist/isosurface/parse.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
// Parsers for volumetric data file formats (VASP CHGCAR, Gaussian .cube)
|
|
2
|
-
import { VASP_VOLUMETRIC_REGEX } from '../constants';
|
|
2
|
+
import { COMPRESSION_EXTENSIONS_REGEX, VASP_VOLUMETRIC_REGEX } from '../constants';
|
|
3
3
|
import { ELEM_SYMBOLS } from '../labels';
|
|
4
4
|
import * as math from '../math';
|
|
5
|
+
import { wrap_to_unit_cell } from '../structure/pbc';
|
|
5
6
|
// Bohr radius in Angstroms (for Gaussian .cube unit conversion)
|
|
6
7
|
const BOHR_TO_ANGSTROM = 0.529177249;
|
|
7
|
-
// Wrap a value to [0, 1) range for fractional coordinates
|
|
8
|
-
const wrap_frac = (val) => val - Math.floor(val);
|
|
9
8
|
// === Fast number parsing utilities ===
|
|
10
9
|
// Parse whitespace-separated numbers directly from a string, starting at `pos`.
|
|
11
10
|
// Writes into a pre-allocated Float64Array and returns { count, end_pos }.
|
|
@@ -87,11 +86,9 @@ function read_lines(text, pos, count) {
|
|
|
87
86
|
}
|
|
88
87
|
return { lines: result, next: pos };
|
|
89
88
|
}
|
|
90
|
-
function build_grid({ data, nx, ny, nz, divisor = 1, data_order = `z_fastest
|
|
89
|
+
function build_grid({ data, nx, ny, nz, divisor = 1, data_order = `z_fastest`, }) {
|
|
91
90
|
const grid = new Array(nx);
|
|
92
|
-
let min_val = Infinity;
|
|
93
|
-
let max_val = -Infinity;
|
|
94
|
-
let sum = 0;
|
|
91
|
+
let [min_val, max_val, sum] = [Infinity, -Infinity, 0];
|
|
95
92
|
const total = nx * ny * nz;
|
|
96
93
|
const data_len = Math.min(data.length, total);
|
|
97
94
|
if (data_len === 0) {
|
|
@@ -135,8 +132,7 @@ function build_grid({ data, nx, ny, nz, divisor = 1, data_order = `z_fastest` })
|
|
|
135
132
|
plane[iy] = new Array(nz).fill(0);
|
|
136
133
|
grid[ix] = plane;
|
|
137
134
|
}
|
|
138
|
-
let flat_idx = 0;
|
|
139
|
-
let data_exhausted = false;
|
|
135
|
+
let [flat_idx, data_exhausted] = [0, false];
|
|
140
136
|
for (let iz = 0; iz < nz; iz++) {
|
|
141
137
|
for (let iy = 0; iy < ny; iy++) {
|
|
142
138
|
for (let ix = 0; ix < nx; ix++) {
|
|
@@ -165,8 +161,8 @@ function build_grid({ data, nx, ny, nz, divisor = 1, data_order = `z_fastest` })
|
|
|
165
161
|
return { grid, data_range };
|
|
166
162
|
}
|
|
167
163
|
// === CHGCAR Parser ===
|
|
168
|
-
// Parse VASP CHGCAR/AECCAR/ELFCAR/LOCPOT file format.
|
|
169
|
-
// CHGCAR consists of a POSCAR header followed by volumetric data on a 3D grid.
|
|
164
|
+
// Parse VASP CHGCAR/AECCAR/ELFCAR/LOCPOT/PARCHG file format.
|
|
165
|
+
// CHGCAR/PARCHG consists of a POSCAR header followed by volumetric data on a 3D grid.
|
|
170
166
|
// Spin-polarized files contain two data blocks (total charge + magnetization).
|
|
171
167
|
export function parse_chgcar(content) {
|
|
172
168
|
// Strip leading whitespace
|
|
@@ -240,8 +236,16 @@ export function parse_chgcar(content) {
|
|
|
240
236
|
const is_direct = cur.line.trim().toUpperCase().startsWith(`D`);
|
|
241
237
|
pos = cur.next;
|
|
242
238
|
// Parse atomic positions
|
|
243
|
-
|
|
244
|
-
|
|
239
|
+
let cart_to_frac;
|
|
240
|
+
let frac_to_cart;
|
|
241
|
+
try {
|
|
242
|
+
;
|
|
243
|
+
({ cart_to_frac, frac_to_cart } = math.create_lattice_converters(lattice));
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
console.error(`CHGCAR: lattice matrix is singular; cannot convert coordinates`);
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
245
249
|
const sites = [];
|
|
246
250
|
let atom_idx = 0;
|
|
247
251
|
for (let elem_idx = 0; elem_idx < element_symbols.length; elem_idx++) {
|
|
@@ -259,13 +263,13 @@ export function parse_chgcar(content) {
|
|
|
259
263
|
let abc;
|
|
260
264
|
let xyz;
|
|
261
265
|
if (is_direct) {
|
|
262
|
-
abc =
|
|
263
|
-
xyz =
|
|
266
|
+
abc = wrap_to_unit_cell(coords);
|
|
267
|
+
xyz = frac_to_cart(abc);
|
|
264
268
|
}
|
|
265
269
|
else {
|
|
266
270
|
xyz = math.scale(coords, scale_factor);
|
|
267
|
-
const raw =
|
|
268
|
-
abc =
|
|
271
|
+
const raw = cart_to_frac(xyz);
|
|
272
|
+
abc = wrap_to_unit_cell(raw);
|
|
269
273
|
}
|
|
270
274
|
sites.push({
|
|
271
275
|
species: [{ element, occu: 1, oxidation_state: 0 }],
|
|
@@ -414,21 +418,22 @@ export function parse_cube(content, options = {}) {
|
|
|
414
418
|
const is_periodic = options.periodic ?? Math.hypot(...origin) < 1e-6;
|
|
415
419
|
// Parse atomic positions
|
|
416
420
|
const sites = [];
|
|
417
|
-
|
|
418
|
-
let lattice_inv;
|
|
421
|
+
let cube_cart_to_frac;
|
|
419
422
|
try {
|
|
420
|
-
|
|
423
|
+
cube_cart_to_frac = math.create_cart_to_frac(lattice);
|
|
421
424
|
}
|
|
422
425
|
catch {
|
|
423
426
|
// Non-periodic system (molecule), use identity
|
|
424
|
-
|
|
427
|
+
cube_cart_to_frac = (v) => [v[0], v[1], v[2]];
|
|
425
428
|
}
|
|
426
429
|
for (let atom_idx = 0; atom_idx < n_atoms; atom_idx++) {
|
|
427
430
|
const cur = read_line(content, pos);
|
|
428
431
|
const atom_line = cur.line.trim().split(/\s+/).map(Number);
|
|
429
432
|
pos = cur.next;
|
|
430
433
|
// Validate: need atomic_number, charge, x, y, z (5 tokens, indices 2-4 finite)
|
|
431
|
-
if (atom_line.length < 5 ||
|
|
434
|
+
if (atom_line.length < 5 ||
|
|
435
|
+
!isFinite(atom_line[2]) ||
|
|
436
|
+
!isFinite(atom_line[3]) ||
|
|
432
437
|
!isFinite(atom_line[4])) {
|
|
433
438
|
console.warn(`.cube atom ${atom_idx}: malformed line "${cur.line.trim()}", skipping`);
|
|
434
439
|
continue;
|
|
@@ -438,7 +443,7 @@ export function parse_cube(content, options = {}) {
|
|
|
438
443
|
// Convert Cartesian to fractional, accounting for origin offset.
|
|
439
444
|
// Store lattice-frame xyz (shifted) so abc and xyz stay consistent.
|
|
440
445
|
const xyz = math.subtract(raw_xyz, origin);
|
|
441
|
-
const abc =
|
|
446
|
+
const abc = cube_cart_to_frac(xyz);
|
|
442
447
|
const element = atomic_number_to_symbol(atom_line[0]);
|
|
443
448
|
sites.push({
|
|
444
449
|
species: [{ element, occu: 1, oxidation_state: 0 }],
|
|
@@ -481,7 +486,8 @@ export function parse_cube(content, options = {}) {
|
|
|
481
486
|
nz: n_grid[2],
|
|
482
487
|
data_order: `z_fastest`,
|
|
483
488
|
});
|
|
484
|
-
const volumes = [
|
|
489
|
+
const volumes = [
|
|
490
|
+
{
|
|
485
491
|
grid,
|
|
486
492
|
grid_dims: n_grid,
|
|
487
493
|
lattice,
|
|
@@ -490,21 +496,20 @@ export function parse_cube(content, options = {}) {
|
|
|
490
496
|
data_order: `z_fastest`,
|
|
491
497
|
periodic: is_periodic, // periodic systems wrap; molecular .cube files include both endpoints
|
|
492
498
|
label: `volumetric data`,
|
|
493
|
-
}
|
|
499
|
+
},
|
|
500
|
+
];
|
|
494
501
|
return { structure, volumes };
|
|
495
502
|
}
|
|
496
503
|
// Convert atomic number to element symbol using ELEM_SYMBOLS (1-indexed: H=1, He=2, ...)
|
|
497
504
|
function atomic_number_to_symbol(atomic_number) {
|
|
498
505
|
// ELEM_SYMBOLS is 0-indexed (H at index 0), atomic numbers are 1-indexed
|
|
499
506
|
const idx = atomic_number - 1;
|
|
500
|
-
return (idx >= 0 && idx < ELEM_SYMBOLS.length
|
|
501
|
-
? ELEM_SYMBOLS[idx]
|
|
502
|
-
: `H`);
|
|
507
|
+
return (idx >= 0 && idx < ELEM_SYMBOLS.length ? ELEM_SYMBOLS[idx] : `H`);
|
|
503
508
|
}
|
|
504
509
|
// Auto-detect and parse volumetric file format based on filename and content
|
|
505
510
|
export function parse_volumetric_file(content, filename) {
|
|
506
511
|
// Strip compression suffixes so "CHGCAR.gz" and "molecule.cube.bz2" match correctly
|
|
507
|
-
const lower_name = (filename ?? ``).toLowerCase().replace(
|
|
512
|
+
const lower_name = (filename ?? ``).toLowerCase().replace(COMPRESSION_EXTENSIONS_REGEX, ``);
|
|
508
513
|
// Extension-based detection
|
|
509
514
|
if (lower_name.endsWith(`.cube`))
|
|
510
515
|
return parse_cube(content);
|