matterviz 0.3.2 → 0.3.4
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/element/data.js +1 -1
- 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,268 +1,426 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import * as
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { BrillouinZoneData } from '../brillouin'
|
|
3
|
+
import { AXIS_COLORS, NEG_AXIS_COLORS } from '../colors'
|
|
4
|
+
import type { Matrix4Tuple, Vec3 } from '../math'
|
|
5
|
+
import * as math from '../math'
|
|
6
|
+
import type { CameraProjection } from '../settings'
|
|
7
|
+
import { DEFAULTS } from '../settings'
|
|
8
|
+
import { Arrow, Cylinder } from '../structure'
|
|
9
|
+
import { T, useThrelte } from '@threlte/core'
|
|
10
|
+
import * as extras from '@threlte/extras'
|
|
11
|
+
import * as d3_sc from 'd3-scale-chromatic'
|
|
12
|
+
import type { ComponentProps } from 'svelte'
|
|
13
|
+
import { SvelteMap } from 'svelte/reactivity'
|
|
14
|
+
import type { Camera, Scene } from 'three'
|
|
15
|
+
import {
|
|
16
|
+
BackSide,
|
|
17
|
+
BufferAttribute,
|
|
18
|
+
BufferGeometry,
|
|
19
|
+
Color,
|
|
20
|
+
DoubleSide,
|
|
21
|
+
FrontSide,
|
|
22
|
+
Matrix4,
|
|
23
|
+
Plane,
|
|
24
|
+
Vector3,
|
|
25
|
+
} from 'three'
|
|
26
|
+
import * as constants from './constants'
|
|
27
|
+
import { IDENTITY_4x4, OH_SYMMETRY_MATRICES } from './symmetry'
|
|
28
|
+
import type {
|
|
29
|
+
ColorProperty,
|
|
30
|
+
FermiHoverData,
|
|
31
|
+
FermiSurfaceData,
|
|
32
|
+
Isosurface,
|
|
33
|
+
RepresentationMode,
|
|
34
|
+
} from './types'
|
|
35
|
+
|
|
36
|
+
// Threlte pointer event type for mesh interactions
|
|
37
|
+
type ThreltePointerEvent = { point: Vector3; nativeEvent: PointerEvent }
|
|
38
|
+
|
|
39
|
+
let {
|
|
40
|
+
fermi_data = $bindable(),
|
|
41
|
+
bz_data = $bindable(),
|
|
42
|
+
camera_position = $bindable(),
|
|
43
|
+
camera_projection = $bindable(`perspective`),
|
|
44
|
+
// Fermi surface styling
|
|
45
|
+
color_property = `band`,
|
|
46
|
+
color_scale = `interpolateViridis`,
|
|
47
|
+
representation = `solid`,
|
|
48
|
+
surface_opacity = $bindable(0.8),
|
|
49
|
+
selected_bands,
|
|
50
|
+
// BZ styling
|
|
51
|
+
show_bz = true,
|
|
52
|
+
bz_color = `#888888`,
|
|
53
|
+
bz_opacity = 0.1,
|
|
54
|
+
bz_edge_color = `#333333`,
|
|
55
|
+
bz_edge_width = 0.02,
|
|
56
|
+
show_vectors = true,
|
|
57
|
+
tile_bz = false,
|
|
58
|
+
// Clipping plane
|
|
59
|
+
clip_enabled = false,
|
|
60
|
+
clip_axis = `z`,
|
|
61
|
+
clip_position = 0,
|
|
62
|
+
clip_flip = false,
|
|
63
|
+
vector_scale = 1.0,
|
|
64
|
+
// Camera controls
|
|
65
|
+
rotation_damping = DEFAULTS.structure.rotation_damping,
|
|
66
|
+
max_zoom = DEFAULTS.structure.max_zoom,
|
|
67
|
+
min_zoom = DEFAULTS.structure.min_zoom,
|
|
68
|
+
rotate_speed = DEFAULTS.structure.rotate_speed,
|
|
69
|
+
zoom_speed = DEFAULTS.structure.zoom_speed,
|
|
70
|
+
pan_speed = DEFAULTS.structure.pan_speed,
|
|
71
|
+
zoom_to_cursor = DEFAULTS.structure.zoom_to_cursor,
|
|
72
|
+
fov = DEFAULTS.structure.fov,
|
|
73
|
+
initial_zoom = DEFAULTS.structure.initial_zoom,
|
|
74
|
+
ambient_light = DEFAULTS.structure.ambient_light,
|
|
75
|
+
directional_light = DEFAULTS.structure.directional_light,
|
|
76
|
+
gizmo = DEFAULTS.structure.show_gizmo,
|
|
77
|
+
auto_rotate = DEFAULTS.structure.auto_rotate,
|
|
78
|
+
camera_is_moving = $bindable(false),
|
|
79
|
+
scene = $bindable(),
|
|
80
|
+
camera = $bindable(),
|
|
81
|
+
hover_data = $bindable<FermiHoverData | null>(null),
|
|
82
|
+
}: {
|
|
83
|
+
fermi_data?: FermiSurfaceData
|
|
84
|
+
bz_data?: BrillouinZoneData
|
|
85
|
+
camera_position?: Vec3 | undefined
|
|
86
|
+
camera_projection?: CameraProjection
|
|
87
|
+
color_property?: ColorProperty
|
|
88
|
+
color_scale?: string
|
|
89
|
+
representation?: RepresentationMode
|
|
90
|
+
surface_opacity?: number
|
|
91
|
+
selected_bands?: number[]
|
|
92
|
+
show_bz?: boolean
|
|
93
|
+
bz_color?: string
|
|
94
|
+
bz_opacity?: number
|
|
95
|
+
bz_edge_color?: string
|
|
96
|
+
bz_edge_width?: number
|
|
97
|
+
show_vectors?: boolean
|
|
98
|
+
tile_bz?: boolean
|
|
99
|
+
clip_enabled?: boolean
|
|
100
|
+
clip_axis?: `x` | `y` | `z`
|
|
101
|
+
clip_position?: number
|
|
102
|
+
clip_flip?: boolean
|
|
103
|
+
vector_scale?: number
|
|
104
|
+
rotation_damping?: number
|
|
105
|
+
max_zoom?: number
|
|
106
|
+
min_zoom?: number
|
|
107
|
+
rotate_speed?: number
|
|
108
|
+
zoom_speed?: number
|
|
109
|
+
pan_speed?: number
|
|
110
|
+
zoom_to_cursor?: boolean
|
|
111
|
+
fov?: number
|
|
112
|
+
initial_zoom?: number
|
|
113
|
+
ambient_light?: number
|
|
114
|
+
directional_light?: number
|
|
115
|
+
gizmo?: boolean | ComponentProps<typeof extras.Gizmo>
|
|
116
|
+
auto_rotate?: number
|
|
117
|
+
camera_is_moving?: boolean
|
|
118
|
+
scene?: Scene
|
|
119
|
+
camera?: Camera
|
|
120
|
+
hover_data?: FermiHoverData | null
|
|
121
|
+
} = $props()
|
|
122
|
+
|
|
123
|
+
const threlte = useThrelte()
|
|
124
|
+
|
|
125
|
+
// Compute scene size for clipping (also used for camera positioning later)
|
|
126
|
+
function get_scene_size(): number {
|
|
127
|
+
if (!fermi_data?.k_lattice) return 10
|
|
128
|
+
const mags = fermi_data.k_lattice.map((vec) => Math.hypot(...vec))
|
|
129
|
+
return mags.reduce((sum, mag) => sum + mag, 0) / 3
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Compute clipping plane based on axis and position
|
|
133
|
+
// Plane equation: dot(normal, point) + constant >= 0 means point is visible
|
|
134
|
+
const clip_plane = $derived.by(() => {
|
|
135
|
+
if (!clip_enabled) return null
|
|
136
|
+
|
|
137
|
+
const axis_idx = { x: 0, y: 1, z: 2 }[clip_axis]
|
|
138
|
+
const normal_arr: Vec3 = [0, 0, 0]
|
|
139
|
+
normal_arr[axis_idx] = clip_flip ? -1 : 1
|
|
140
|
+
|
|
141
|
+
const scaled_position = clip_position * get_scene_size()
|
|
38
142
|
// constant = -position for normal case (keep points >= position)
|
|
39
143
|
// constant = +position for flipped case (keep points <= position)
|
|
40
|
-
const constant = clip_flip ? scaled_position : -scaled_position
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
144
|
+
const constant = clip_flip ? scaled_position : -scaled_position
|
|
145
|
+
|
|
146
|
+
return new Plane(new Vector3(...normal_arr), constant)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// Apply clipping plane to renderer
|
|
150
|
+
$effect(() => {
|
|
151
|
+
if (!threlte.renderer) return
|
|
152
|
+
|
|
47
153
|
if (clip_plane) {
|
|
48
|
-
|
|
49
|
-
|
|
154
|
+
threlte.renderer.clippingPlanes = [clip_plane]
|
|
155
|
+
threlte.renderer.localClippingEnabled = true
|
|
156
|
+
} else {
|
|
157
|
+
threlte.renderer.clippingPlanes = []
|
|
158
|
+
threlte.renderer.localClippingEnabled = false
|
|
50
159
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
$effect(() => {
|
|
57
|
-
scene = threlte.scene;
|
|
58
|
-
camera = threlte.camera.current;
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
$effect(() => {
|
|
163
|
+
scene = threlte.scene
|
|
164
|
+
camera = threlte.camera.current
|
|
59
165
|
if (threlte.renderer) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
166
|
+
// Enable object sorting for proper depth ordering of transparent surfaces
|
|
167
|
+
threlte.renderer.sortObjects = true
|
|
168
|
+
Object.assign(threlte.renderer.domElement, { __renderer: threlte.renderer })
|
|
63
169
|
}
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
extras.interactivity()
|
|
173
|
+
|
|
174
|
+
// Get color interpolator from d3
|
|
175
|
+
const get_interpolator = (name: string): (t: number) => string => {
|
|
176
|
+
const fn = d3_sc[name as keyof typeof d3_sc]
|
|
177
|
+
return typeof fn === `function` ? fn : d3_sc.interpolateViridis
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Filter surfaces based on selected bands
|
|
181
|
+
let visible_surfaces = $derived(
|
|
182
|
+
fermi_data?.isosurfaces.filter((surface) =>
|
|
183
|
+
selected_bands === undefined || selected_bands.includes(surface.band_index)
|
|
184
|
+
) ?? [],
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
// Compute average vertex distance from origin for each surface (used for render ordering)
|
|
188
|
+
// Smaller distance = inner surface = render first (lower renderOrder)
|
|
189
|
+
// Larger distance = outer surface = render last (higher renderOrder)
|
|
190
|
+
function compute_surface_radius(surface: Isosurface): number {
|
|
191
|
+
if (surface.vertices.length === 0) return 0
|
|
192
|
+
let sum = 0
|
|
80
193
|
for (const vertex of surface.vertices) {
|
|
81
|
-
|
|
194
|
+
sum += Math.hypot(vertex[0], vertex[1], vertex[2])
|
|
82
195
|
}
|
|
83
|
-
return sum / surface.vertices.length
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
196
|
+
return sum / surface.vertices.length
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Map from surface to its render order based on size (inner surfaces first)
|
|
200
|
+
let surface_render_orders = $derived.by((): Map<Isosurface, number> => {
|
|
201
|
+
const order_map = new SvelteMap<Isosurface, number>()
|
|
202
|
+
if (visible_surfaces.length === 0) return order_map
|
|
203
|
+
|
|
90
204
|
// Compute radius for each surface and sort by it
|
|
91
205
|
const surfaces_with_radius = visible_surfaces.map((surface) => ({
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}))
|
|
95
|
-
surfaces_with_radius.sort((a, b) => a.radius - b.radius)
|
|
206
|
+
surface,
|
|
207
|
+
radius: compute_surface_radius(surface),
|
|
208
|
+
}))
|
|
209
|
+
surfaces_with_radius.sort((a, b) => a.radius - b.radius)
|
|
210
|
+
|
|
96
211
|
// Assign render order: smaller radius (inner) = lower order = rendered first
|
|
97
212
|
for (let idx = 0; idx < surfaces_with_radius.length; idx++) {
|
|
98
|
-
|
|
213
|
+
order_map.set(surfaces_with_radius[idx].surface, idx)
|
|
99
214
|
}
|
|
100
|
-
return order_map
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
|
|
215
|
+
return order_map
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
// Compute property range for color scaling
|
|
219
|
+
let property_range = $derived.by((): [number, number] => {
|
|
104
220
|
if (color_property !== `velocity` && color_property !== `custom`) {
|
|
105
|
-
|
|
221
|
+
return [0, 1]
|
|
106
222
|
}
|
|
107
|
-
let min_val = Infinity
|
|
108
|
-
let max_val = -Infinity;
|
|
223
|
+
let [min_val, max_val] = [Infinity, -Infinity]
|
|
109
224
|
for (const surface of visible_surfaces) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (prop > max_val)
|
|
116
|
-
max_val = prop;
|
|
117
|
-
}
|
|
225
|
+
if (!surface.properties) continue
|
|
226
|
+
for (const prop of surface.properties) {
|
|
227
|
+
if (prop < min_val) min_val = prop
|
|
228
|
+
if (prop > max_val) max_val = prop
|
|
229
|
+
}
|
|
118
230
|
}
|
|
119
|
-
if (min_val === Infinity)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// Get color for a surface/vertex
|
|
124
|
-
function get_surface_color(surface, vertex_idx) {
|
|
231
|
+
if (min_val === Infinity) return [0, 1]
|
|
232
|
+
return [min_val, max_val]
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
// Get color for a surface/vertex
|
|
236
|
+
function get_surface_color(surface: Isosurface, vertex_idx?: number): string {
|
|
125
237
|
if (color_property === `band`) {
|
|
126
|
-
|
|
238
|
+
return constants.BAND_COLORS[surface.band_index % constants.BAND_COLORS.length]
|
|
127
239
|
}
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
240
|
+
if (
|
|
241
|
+
(color_property === `velocity` || color_property === `custom`) &&
|
|
242
|
+
surface.properties &&
|
|
243
|
+
vertex_idx !== undefined
|
|
244
|
+
) {
|
|
245
|
+
const prop = surface.properties[vertex_idx]
|
|
246
|
+
const [min_val, max_val] = property_range
|
|
247
|
+
const normalized = max_val > min_val
|
|
248
|
+
? (prop - min_val) / (max_val - min_val)
|
|
249
|
+
: 0.5
|
|
250
|
+
return get_interpolator(color_scale)(normalized)
|
|
137
251
|
}
|
|
138
252
|
// Spin coloring
|
|
139
253
|
if (color_property === `spin` && surface.spin) {
|
|
140
|
-
|
|
254
|
+
return surface.spin === `up` ? `#e41a1c` : `#377eb8`
|
|
141
255
|
}
|
|
142
|
-
return constants.BAND_COLORS[surface.band_index % constants.BAND_COLORS.length]
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
256
|
+
return constants.BAND_COLORS[surface.band_index % constants.BAND_COLORS.length]
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Create geometry for an isosurface
|
|
260
|
+
function create_isosurface_geometry(
|
|
261
|
+
surface: Isosurface,
|
|
262
|
+
): { geometry: BufferGeometry; dispose: () => void } | null {
|
|
263
|
+
if (surface.vertices.length === 0 || surface.faces.length === 0) return null
|
|
264
|
+
|
|
265
|
+
const positions: number[] = []
|
|
266
|
+
const normals: number[] = []
|
|
267
|
+
const colors: number[] = []
|
|
268
|
+
|
|
151
269
|
const use_vertex_colors = color_property === `velocity` ||
|
|
152
|
-
|
|
153
|
-
|
|
270
|
+
color_property === `custom`
|
|
271
|
+
|
|
272
|
+
const n_vertices = surface.vertices.length
|
|
273
|
+
|
|
154
274
|
for (const face of surface.faces) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const color = new Color(color_str);
|
|
193
|
-
colors.push(color.r, color.g, color.b);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
275
|
+
if (face.length < 3) continue
|
|
276
|
+
|
|
277
|
+
// Fan triangulation: for polygon with N vertices, create N-2 triangles
|
|
278
|
+
// e.g. quad [0,1,2,3] becomes triangles [0,1,2] and [0,2,3]
|
|
279
|
+
for (let fan_idx = 1; fan_idx < face.length - 1; fan_idx++) {
|
|
280
|
+
const idx0 = face[0]
|
|
281
|
+
const idx1 = face[fan_idx]
|
|
282
|
+
const idx2 = face[fan_idx + 1]
|
|
283
|
+
|
|
284
|
+
// Validate face indices are within bounds (protects against malformed JSON files)
|
|
285
|
+
if (
|
|
286
|
+
idx0 < 0 || idx0 >= n_vertices ||
|
|
287
|
+
idx1 < 0 || idx1 >= n_vertices ||
|
|
288
|
+
idx2 < 0 || idx2 >= n_vertices
|
|
289
|
+
) {
|
|
290
|
+
continue
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const v0 = surface.vertices[idx0]
|
|
294
|
+
const v1 = surface.vertices[idx1]
|
|
295
|
+
const v2 = surface.vertices[idx2]
|
|
296
|
+
|
|
297
|
+
positions.push(...v0, ...v1, ...v2)
|
|
298
|
+
|
|
299
|
+
// Use per-vertex normals if available, otherwise compute face normal
|
|
300
|
+
if (surface.normals && surface.normals.length > 0) {
|
|
301
|
+
const n0 = surface.normals[idx0] ?? [0, 0, 1]
|
|
302
|
+
const n1 = surface.normals[idx1] ?? [0, 0, 1]
|
|
303
|
+
const n2 = surface.normals[idx2] ?? [0, 0, 1]
|
|
304
|
+
normals.push(...n0, ...n1, ...n2)
|
|
305
|
+
} else {
|
|
306
|
+
const e1: Vec3 = math.subtract(v1, v0)
|
|
307
|
+
const e2: Vec3 = math.subtract(v2, v0)
|
|
308
|
+
const normal = math.cross_3d(e1, e2)
|
|
309
|
+
const len = Math.hypot(...normal)
|
|
310
|
+
const n = len > 1e-10 ? normal.map((x) => x / len) : [0, 0, 1]
|
|
311
|
+
normals.push(...n, ...n, ...n)
|
|
196
312
|
}
|
|
313
|
+
|
|
314
|
+
// Per-vertex colors for this triangle
|
|
315
|
+
if (use_vertex_colors) {
|
|
316
|
+
for (const vert_idx of [idx0, idx1, idx2]) {
|
|
317
|
+
const color_str = get_surface_color(surface, vert_idx)
|
|
318
|
+
const color = new Color(color_str)
|
|
319
|
+
colors.push(color.r, color.g, color.b)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
197
323
|
}
|
|
198
|
-
|
|
199
|
-
geometry
|
|
200
|
-
geometry.setAttribute(
|
|
324
|
+
|
|
325
|
+
const geometry = new BufferGeometry()
|
|
326
|
+
geometry.setAttribute(
|
|
327
|
+
`position`,
|
|
328
|
+
new BufferAttribute(new Float32Array(positions), 3),
|
|
329
|
+
)
|
|
330
|
+
geometry.setAttribute(`normal`, new BufferAttribute(new Float32Array(normals), 3))
|
|
331
|
+
|
|
201
332
|
if (use_vertex_colors) {
|
|
202
|
-
|
|
333
|
+
geometry.setAttribute(`color`, new BufferAttribute(new Float32Array(colors), 3))
|
|
203
334
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
335
|
+
|
|
336
|
+
geometry.computeBoundingSphere()
|
|
337
|
+
|
|
338
|
+
return { geometry, dispose: () => geometry.dispose() }
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Memoized geometry cache - pre-compute geometries to avoid recomputation on every render
|
|
342
|
+
type GeometryData = { geometry: BufferGeometry; dispose: () => void }
|
|
343
|
+
let geometry_cache = $derived.by((): Map<string, GeometryData | null> => {
|
|
344
|
+
const cache = new SvelteMap<string, GeometryData | null>()
|
|
209
345
|
for (let idx = 0; idx < visible_surfaces.length; idx++) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
346
|
+
const surface = visible_surfaces[idx]
|
|
347
|
+
const key = `${surface.band_index}-${surface.spin}-${idx}`
|
|
348
|
+
cache.set(key, create_isosurface_geometry(surface))
|
|
213
349
|
}
|
|
214
|
-
return cache
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
350
|
+
return cache
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
// Cleanup geometries when cache changes
|
|
354
|
+
$effect(() => {
|
|
355
|
+
const current_cache = geometry_cache
|
|
219
356
|
return () => {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
let
|
|
228
|
-
|
|
229
|
-
|
|
357
|
+
for (const geo_data of current_cache.values()) {
|
|
358
|
+
geo_data?.dispose()
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
// Count total triangles and auto-disable tiling for very large surfaces
|
|
364
|
+
let total_triangles = $derived(
|
|
365
|
+
visible_surfaces.reduce((sum, surface) => sum + surface.faces.length, 0),
|
|
366
|
+
)
|
|
367
|
+
let effective_tile_bz = $derived(
|
|
368
|
+
tile_bz && total_triangles < constants.MAX_TRIANGLES_FOR_TILING,
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
// Warn user when tiling is auto-disabled
|
|
372
|
+
$effect(() => {
|
|
230
373
|
if (tile_bz && !effective_tile_bz && total_triangles > 0) {
|
|
231
|
-
|
|
374
|
+
console.warn(
|
|
375
|
+
`Fermi surface has ${total_triangles} triangles, auto-disabled BZ tiling for performance`,
|
|
376
|
+
)
|
|
232
377
|
}
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
// Compute rotation target from surfaces or BZ
|
|
381
|
+
const rotation_target = $derived.by((): Vec3 => {
|
|
236
382
|
if (bz_data?.vertices && bz_data.vertices.length > 0) {
|
|
237
|
-
|
|
238
|
-
|
|
383
|
+
const sum = bz_data.vertices.reduce(
|
|
384
|
+
(acc, vert) => math.add(acc, vert),
|
|
385
|
+
[0, 0, 0] as Vec3,
|
|
386
|
+
)
|
|
387
|
+
return math.scale(sum, 1 / bz_data.vertices.length)
|
|
239
388
|
}
|
|
240
|
-
return [0, 0, 0]
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const
|
|
245
|
-
|
|
389
|
+
return [0, 0, 0]
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
// Scene size for camera positioning (uses helper function defined earlier)
|
|
393
|
+
const scene_size = $derived(get_scene_size())
|
|
394
|
+
|
|
395
|
+
const computed_camera_position = $derived.by(
|
|
396
|
+
() =>
|
|
397
|
+
camera_position || ([10, 3, 8].map((x) => x * Math.max(1, scene_size)) as Vec3),
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
const gizmo_props = $derived({
|
|
246
401
|
background: { enabled: false },
|
|
247
402
|
className: `responsive-gizmo`,
|
|
248
|
-
...Object.fromEntries(
|
|
403
|
+
...Object.fromEntries(
|
|
404
|
+
[...AXIS_COLORS, ...NEG_AXIS_COLORS].map(([axis, color, hover]) => [
|
|
249
405
|
axis,
|
|
250
406
|
{
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
407
|
+
color,
|
|
408
|
+
labelColor: `#111`,
|
|
409
|
+
opacity: axis.startsWith(`n`) ? 0.9 : 0.8,
|
|
410
|
+
hover: {
|
|
411
|
+
color: hover,
|
|
412
|
+
labelColor: `#222`,
|
|
413
|
+
opacity: axis.startsWith(`n`) ? 1 : 0.9,
|
|
414
|
+
},
|
|
259
415
|
},
|
|
260
|
-
|
|
416
|
+
]),
|
|
417
|
+
),
|
|
261
418
|
...(typeof gizmo === `object` ? gizmo : {}),
|
|
262
419
|
offset: { left: 5, bottom: 5 },
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
const
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
const is_ortho = $derived(camera_projection === `orthographic`)
|
|
423
|
+
const orbit_controls_props = $derived({
|
|
266
424
|
position: [0, 0, 0],
|
|
267
425
|
target: rotation_target,
|
|
268
426
|
enableRotate: rotate_speed > 0,
|
|
@@ -280,141 +438,176 @@ const orbit_controls_props = $derived({
|
|
|
280
438
|
dampingFactor: rotation_damping,
|
|
281
439
|
onstart: () => (camera_is_moving = true),
|
|
282
440
|
onend: () => (camera_is_moving = false),
|
|
283
|
-
})
|
|
284
|
-
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
const vector_colors = [`red`, `green`, `blue`]
|
|
444
|
+
const vector_labels = [`b₁`, `b₂`, `b₃`]
|
|
445
|
+
|
|
446
|
+
// Create BZ geometry
|
|
447
|
+
const bz_geometry = $derived.by(() => {
|
|
448
|
+
if (!bz_data || bz_data.faces.length === 0) return null
|
|
449
|
+
|
|
450
|
+
const positions: number[] = []
|
|
451
|
+
const normals: number[] = []
|
|
452
|
+
|
|
292
453
|
for (const face of bz_data.faces) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
454
|
+
if (face.length < 3) continue
|
|
455
|
+
|
|
456
|
+
for (let face_idx = 1; face_idx < face.length - 1; face_idx++) {
|
|
457
|
+
const indices = [face[0], face[face_idx], face[face_idx + 1]]
|
|
458
|
+
if (indices.some((idx) => idx < 0 || idx >= bz_data.vertices.length)) continue
|
|
459
|
+
const [v0, v1, v2] = indices.map((idx) => bz_data.vertices[idx])
|
|
460
|
+
positions.push(...v0, ...v1, ...v2)
|
|
461
|
+
|
|
462
|
+
const e1: Vec3 = math.subtract(v1, v0)
|
|
463
|
+
const e2: Vec3 = math.subtract(v2, v0)
|
|
464
|
+
const normal_vec = math.cross_3d(e1, e2)
|
|
465
|
+
const len = Math.hypot(...normal_vec)
|
|
466
|
+
const norm = len > 1e-10 ? normal_vec.map((x) => x / len) : [0, 0, 0]
|
|
467
|
+
normals.push(...norm, ...norm, ...norm)
|
|
468
|
+
}
|
|
308
469
|
}
|
|
309
|
-
|
|
310
|
-
geometry
|
|
311
|
-
geometry.setAttribute(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
return
|
|
318
|
-
})
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
470
|
+
|
|
471
|
+
const geometry = new BufferGeometry()
|
|
472
|
+
geometry.setAttribute(
|
|
473
|
+
`position`,
|
|
474
|
+
new BufferAttribute(new Float32Array(positions), 3),
|
|
475
|
+
)
|
|
476
|
+
geometry.setAttribute(`normal`, new BufferAttribute(new Float32Array(normals), 3))
|
|
477
|
+
geometry.computeBoundingSphere()
|
|
478
|
+
return geometry
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
$effect(() => {
|
|
482
|
+
const prev_geometry = bz_geometry
|
|
483
|
+
return () => prev_geometry?.dispose()
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
// Get material props for two-pass transparent rendering
|
|
487
|
+
// Pass 1 (back faces): renders interior/back of surfaces first
|
|
488
|
+
// Pass 2 (front faces): renders exterior/front of surfaces on top
|
|
489
|
+
// This avoids z-fighting while showing both sides correctly
|
|
490
|
+
const get_material_props = (
|
|
491
|
+
surface_color: string,
|
|
492
|
+
use_vertex_colors: boolean,
|
|
493
|
+
surface_idx: number,
|
|
494
|
+
pass: `front` | `back`,
|
|
495
|
+
) => {
|
|
496
|
+
const is_transparent = surface_opacity < 1
|
|
325
497
|
const base = {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
498
|
+
transparent: is_transparent,
|
|
499
|
+
opacity: surface_opacity,
|
|
500
|
+
// Two-pass: back faces first (pass=back), front faces second (pass=front)
|
|
501
|
+
// For opaque: just use DoubleSide
|
|
502
|
+
side: is_transparent ? (pass === `back` ? BackSide : FrontSide) : DoubleSide,
|
|
503
|
+
depthWrite: true,
|
|
504
|
+
depthTest: true,
|
|
505
|
+
// Polygon offset helps separate overlapping geometry
|
|
506
|
+
polygonOffset: true,
|
|
507
|
+
polygonOffsetFactor: 1 + surface_idx * 0.5,
|
|
508
|
+
polygonOffsetUnits: 1 + surface_idx * 0.5,
|
|
509
|
+
}
|
|
510
|
+
|
|
338
511
|
if (use_vertex_colors) {
|
|
339
|
-
|
|
512
|
+
return { ...base, vertexColors: true }
|
|
340
513
|
}
|
|
341
|
-
return { ...base, color: surface_color }
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
514
|
+
return { ...base, color: surface_color }
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Compute inverse of k_lattice for Cartesian->fractional conversion (cached)
|
|
518
|
+
const k_lattice_inv = $derived.by(() => {
|
|
519
|
+
if (!fermi_data?.k_lattice) return null
|
|
347
520
|
try {
|
|
348
|
-
|
|
521
|
+
return math.matrix_inverse_3x3(fermi_data.k_lattice)
|
|
522
|
+
} catch {
|
|
523
|
+
return null
|
|
349
524
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
//
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
let min_dist = Infinity;
|
|
366
|
-
let nearest_idx = 0;
|
|
525
|
+
})
|
|
526
|
+
|
|
527
|
+
// Throttle state for pointer move events to avoid O(n) vertex lookups causing jank
|
|
528
|
+
let last_hover_time = 0
|
|
529
|
+
|
|
530
|
+
// Convert Cartesian k-coordinates to fractional (reciprocal lattice units)
|
|
531
|
+
// Returns null if k_lattice is unavailable or inversion failed
|
|
532
|
+
function cartesian_to_fractional(cart: Vec3): Vec3 | null {
|
|
533
|
+
if (!k_lattice_inv) return null
|
|
534
|
+
return math.mat3x3_vec3_multiply(k_lattice_inv, cart)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Find index of nearest vertex to a point in a surface
|
|
538
|
+
function find_nearest_vertex(surface: Isosurface, point: Vec3): number {
|
|
539
|
+
let [min_dist, nearest_idx] = [Infinity, 0]
|
|
367
540
|
for (let idx = 0; idx < surface.vertices.length; idx++) {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
541
|
+
const vertex = surface.vertices[idx]
|
|
542
|
+
const dist = Math.hypot(
|
|
543
|
+
point[0] - vertex[0],
|
|
544
|
+
point[1] - vertex[1],
|
|
545
|
+
point[2] - vertex[2],
|
|
546
|
+
)
|
|
547
|
+
if (dist < min_dist) {
|
|
548
|
+
min_dist = dist
|
|
549
|
+
nearest_idx = idx
|
|
550
|
+
}
|
|
374
551
|
}
|
|
375
|
-
return nearest_idx
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
|
|
552
|
+
return nearest_idx
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Create hover data from pointer event on a surface
|
|
556
|
+
function create_hover_data(
|
|
557
|
+
event: ThreltePointerEvent,
|
|
558
|
+
surface: Isosurface,
|
|
559
|
+
surface_color: string,
|
|
560
|
+
sym_idx: number,
|
|
561
|
+
sym_matrix: Matrix4Tuple,
|
|
562
|
+
): FermiHoverData {
|
|
379
563
|
// event.point is in world space (after sym_matrix transformation)
|
|
380
|
-
const position_cartesian = [event.point.x, event.point.y, event.point.z]
|
|
381
|
-
const position_fractional = cartesian_to_fractional(position_cartesian)
|
|
564
|
+
const position_cartesian: Vec3 = [event.point.x, event.point.y, event.point.z]
|
|
565
|
+
const position_fractional = cartesian_to_fractional(position_cartesian)
|
|
566
|
+
|
|
382
567
|
// Transform world-space point to local space for nearest-vertex lookup
|
|
383
568
|
// surface.vertices are in local space (raw geometry before sym_matrix)
|
|
384
|
-
const local_point = event.point.clone()
|
|
385
|
-
const inv_matrix = new Matrix4().fromArray(sym_matrix).invert()
|
|
386
|
-
local_point.applyMatrix4(inv_matrix)
|
|
387
|
-
const local_position = [local_point.x, local_point.y, local_point.z]
|
|
569
|
+
const local_point = event.point.clone()
|
|
570
|
+
const inv_matrix = new Matrix4().fromArray(sym_matrix).invert()
|
|
571
|
+
local_point.applyMatrix4(inv_matrix)
|
|
572
|
+
const local_position: Vec3 = [local_point.x, local_point.y, local_point.z]
|
|
573
|
+
|
|
388
574
|
// Find nearest vertex for property lookup (in local space)
|
|
389
|
-
const nearest_idx = find_nearest_vertex(surface, local_position)
|
|
390
|
-
const property_value = surface.properties?.[nearest_idx]
|
|
391
|
-
const has_velocities = fermi_data?.metadata?.has_velocities
|
|
575
|
+
const nearest_idx = find_nearest_vertex(surface, local_position)
|
|
576
|
+
const property_value = surface.properties?.[nearest_idx]
|
|
577
|
+
const has_velocities = fermi_data?.metadata?.has_velocities
|
|
392
578
|
const property_name = property_value != null
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
579
|
+
? (has_velocities ? `velocity` : `custom`)
|
|
580
|
+
: undefined
|
|
581
|
+
|
|
582
|
+
const { clientX, clientY } = event.nativeEvent
|
|
396
583
|
return {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
//
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
584
|
+
band_index: surface.band_index,
|
|
585
|
+
spin: surface.spin,
|
|
586
|
+
position_cartesian,
|
|
587
|
+
position_fractional,
|
|
588
|
+
screen_position: { x: clientX, y: clientY },
|
|
589
|
+
surface_color,
|
|
590
|
+
property_value,
|
|
591
|
+
property_name,
|
|
592
|
+
is_tiled: effective_tile_bz,
|
|
593
|
+
symmetry_index: sym_idx,
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Throttled handler for pointer move events
|
|
598
|
+
// Skips expensive nearest-vertex lookups if called too frequently
|
|
599
|
+
function handle_pointer_move(
|
|
600
|
+
event: ThreltePointerEvent,
|
|
601
|
+
surface: Isosurface,
|
|
602
|
+
surface_color: string,
|
|
603
|
+
sym_idx: number,
|
|
604
|
+
sym_matrix: Matrix4Tuple,
|
|
605
|
+
): void {
|
|
606
|
+
const now = performance.now()
|
|
607
|
+
if (now - last_hover_time < constants.HOVER_THROTTLE_MS) return
|
|
608
|
+
last_hover_time = now
|
|
609
|
+
hover_data = create_hover_data(event, surface, surface_color, sym_idx, sym_matrix)
|
|
610
|
+
}
|
|
418
611
|
</script>
|
|
419
612
|
|
|
420
613
|
{#if camera_projection === `perspective`}
|