matterviz 0.1.0
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/BohrAtom.svelte +105 -0
- package/dist/BohrAtom.svelte.d.ts +21 -0
- package/dist/ControlPanel.svelte +158 -0
- package/dist/ControlPanel.svelte.d.ts +18 -0
- package/dist/Icon.svelte +23 -0
- package/dist/Icon.svelte.d.ts +8 -0
- package/dist/InfoCard.svelte +79 -0
- package/dist/InfoCard.svelte.d.ts +23 -0
- package/dist/Nucleus.svelte +64 -0
- package/dist/Nucleus.svelte.d.ts +16 -0
- package/dist/Spinner.svelte +44 -0
- package/dist/Spinner.svelte.d.ts +7 -0
- package/dist/api.d.ts +6 -0
- package/dist/api.js +30 -0
- package/dist/colors/alloy-colors.json +111 -0
- package/dist/colors/dark-mode-colors.json +111 -0
- package/dist/colors/index.d.ts +26 -0
- package/dist/colors/index.js +72 -0
- package/dist/colors/jmol-colors.json +111 -0
- package/dist/colors/muted-colors.json +111 -0
- package/dist/colors/pastel-colors.json +111 -0
- package/dist/colors/vesta-colors.json +111 -0
- package/dist/composition/BarChart.svelte +260 -0
- package/dist/composition/BarChart.svelte.d.ts +33 -0
- package/dist/composition/BubbleChart.svelte +166 -0
- package/dist/composition/BubbleChart.svelte.d.ts +30 -0
- package/dist/composition/Composition.svelte +73 -0
- package/dist/composition/Composition.svelte.d.ts +27 -0
- package/dist/composition/PieChart.svelte +236 -0
- package/dist/composition/PieChart.svelte.d.ts +36 -0
- package/dist/composition/index.d.ts +5 -0
- package/dist/composition/index.js +5 -0
- package/dist/composition/parse.d.ts +14 -0
- package/dist/composition/parse.js +307 -0
- package/dist/element/ElementHeading.svelte +21 -0
- package/dist/element/ElementHeading.svelte.d.ts +8 -0
- package/dist/element/ElementPhoto.svelte +56 -0
- package/dist/element/ElementPhoto.svelte.d.ts +9 -0
- package/dist/element/ElementStats.svelte +73 -0
- package/dist/element/ElementStats.svelte.d.ts +8 -0
- package/dist/element/ElementTile.svelte +449 -0
- package/dist/element/ElementTile.svelte.d.ts +25 -0
- package/dist/element/data.d.ts +4958 -0
- package/dist/element/data.js +5628 -0
- package/dist/element/index.d.ts +4 -0
- package/dist/element/index.js +4 -0
- package/dist/icons.d.ts +435 -0
- package/dist/icons.js +435 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.js +43 -0
- package/dist/io/decompress.d.ts +16 -0
- package/dist/io/decompress.js +78 -0
- package/dist/io/export.d.ts +9 -0
- package/dist/io/export.js +205 -0
- package/dist/io/parse.d.ts +53 -0
- package/dist/io/parse.js +747 -0
- package/dist/labels.d.ts +31 -0
- package/dist/labels.js +209 -0
- package/dist/material/MaterialCard.svelte +135 -0
- package/dist/material/MaterialCard.svelte.d.ts +10 -0
- package/dist/material/SymmetryCard.svelte +23 -0
- package/dist/material/SymmetryCard.svelte.d.ts +9 -0
- package/dist/material/index.d.ts +2 -0
- package/dist/material/index.js +2 -0
- package/dist/math.d.ts +24 -0
- package/dist/math.js +216 -0
- package/dist/periodic-table/PeriodicTable.svelte +284 -0
- package/dist/periodic-table/PeriodicTable.svelte.d.ts +50 -0
- package/dist/periodic-table/PropertySelect.svelte +20 -0
- package/dist/periodic-table/PropertySelect.svelte.d.ts +13 -0
- package/dist/periodic-table/TableInset.svelte +18 -0
- package/dist/periodic-table/TableInset.svelte.d.ts +9 -0
- package/dist/periodic-table/index.d.ts +9 -0
- package/dist/periodic-table/index.js +3 -0
- package/dist/plot/ColorBar.svelte +414 -0
- package/dist/plot/ColorBar.svelte.d.ts +22 -0
- package/dist/plot/ColorScaleSelect.svelte +31 -0
- package/dist/plot/ColorScaleSelect.svelte.d.ts +15 -0
- package/dist/plot/ElementScatter.svelte +38 -0
- package/dist/plot/ElementScatter.svelte.d.ts +14 -0
- package/dist/plot/Line.svelte +42 -0
- package/dist/plot/Line.svelte.d.ts +15 -0
- package/dist/plot/PlotLegend.svelte +206 -0
- package/dist/plot/PlotLegend.svelte.d.ts +18 -0
- package/dist/plot/ScatterPlot.svelte +1753 -0
- package/dist/plot/ScatterPlot.svelte.d.ts +114 -0
- package/dist/plot/ScatterPlotControls.svelte +505 -0
- package/dist/plot/ScatterPlotControls.svelte.d.ts +33 -0
- package/dist/plot/ScatterPoint.svelte +72 -0
- package/dist/plot/ScatterPoint.svelte.d.ts +17 -0
- package/dist/plot/index.d.ts +168 -0
- package/dist/plot/index.js +46 -0
- package/dist/state.svelte.d.ts +12 -0
- package/dist/state.svelte.js +11 -0
- package/dist/structure/Bond.svelte +68 -0
- package/dist/structure/Bond.svelte.d.ts +13 -0
- package/dist/structure/Lattice.svelte +115 -0
- package/dist/structure/Lattice.svelte.d.ts +15 -0
- package/dist/structure/Structure.svelte +298 -0
- package/dist/structure/Structure.svelte.d.ts +28 -0
- package/dist/structure/StructureCard.svelte +26 -0
- package/dist/structure/StructureCard.svelte.d.ts +9 -0
- package/dist/structure/StructureControls.svelte +383 -0
- package/dist/structure/StructureControls.svelte.d.ts +23 -0
- package/dist/structure/StructureLegend.svelte +130 -0
- package/dist/structure/StructureLegend.svelte.d.ts +17 -0
- package/dist/structure/StructureScene.svelte +331 -0
- package/dist/structure/StructureScene.svelte.d.ts +47 -0
- package/dist/structure/bonding.d.ts +16 -0
- package/dist/structure/bonding.js +150 -0
- package/dist/structure/index.d.ts +98 -0
- package/dist/structure/index.js +114 -0
- package/dist/structure/pbc.d.ts +6 -0
- package/dist/structure/pbc.js +72 -0
- package/dist/trajectory/Sidebar.svelte +412 -0
- package/dist/trajectory/Sidebar.svelte.d.ts +14 -0
- package/dist/trajectory/Trajectory.svelte +1084 -0
- package/dist/trajectory/Trajectory.svelte.d.ts +49 -0
- package/dist/trajectory/TrajectoryError.svelte +120 -0
- package/dist/trajectory/TrajectoryError.svelte.d.ts +12 -0
- package/dist/trajectory/extract.d.ts +5 -0
- package/dist/trajectory/extract.js +157 -0
- package/dist/trajectory/index.d.ts +16 -0
- package/dist/trajectory/index.js +49 -0
- package/dist/trajectory/parse.d.ts +13 -0
- package/dist/trajectory/parse.js +1093 -0
- package/dist/trajectory/plotting.d.ts +12 -0
- package/dist/trajectory/plotting.js +148 -0
- package/license +21 -0
- package/package.json +131 -0
- package/readme.md +95 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
<script lang="ts">import { add, atomic_radii, Bond, BOND_DEFAULTS, element_data, euclidean_dist, Lattice, pbc_dist, scale, } from '..';
|
|
2
|
+
import { format_num } from '../labels';
|
|
3
|
+
import { colors } from '../state.svelte';
|
|
4
|
+
import { T } from '@threlte/core';
|
|
5
|
+
import { Gizmo, HTML, interactivity, OrbitControls } from '@threlte/extras';
|
|
6
|
+
import {} from 'svelte';
|
|
7
|
+
import * as bonding_strategies from './bonding';
|
|
8
|
+
let { structure = undefined, atom_radius = 0.5,
|
|
9
|
+
// label_radius = 1,
|
|
10
|
+
same_size_atoms = false, camera_position = [0, 0, 0], rotation_damping = 0.1, max_zoom = undefined, min_zoom = undefined, zoom_speed = 0.3, pan_speed = 1, show_atoms = true, show_bonds = true, show_site_labels = false, gizmo = true, hovered_idx = $bindable(null), active_idx = $bindable(null), hovered_site = $bindable(null), active_site = $bindable(null), precision = `.3~f`, auto_rotate = 0, bond_thickness = BOND_DEFAULTS.thickness, bond_color = `#ffffff`, bonding_strategy = `nearest_neighbor`, bonding_options = {}, active_hovered_dist = { color: `green`, width: 0.1, opacity: 0.5 }, fov = 10, ambient_light = 1.8, directional_light = 2.5, sphere_segments = 20, lattice_props = {}, atom_label, camera_is_moving = $bindable(false), } = $props();
|
|
11
|
+
let bond_pairs = $state([]);
|
|
12
|
+
interactivity();
|
|
13
|
+
$effect.pre(() => {
|
|
14
|
+
hovered_site = structure?.sites?.[hovered_idx ?? -1] ?? null;
|
|
15
|
+
});
|
|
16
|
+
$effect.pre(() => {
|
|
17
|
+
active_site = structure?.sites?.[active_idx ?? -1] ?? null;
|
|
18
|
+
});
|
|
19
|
+
let lattice = $derived(structure && `lattice` in structure ? structure.lattice : null);
|
|
20
|
+
$effect.pre(() => {
|
|
21
|
+
if (camera_position.every((val) => val === 0) && structure) {
|
|
22
|
+
// Simple approach: use sum of lattice dimensions as size estimate
|
|
23
|
+
const size = lattice ? (lattice.a + lattice.b + lattice.c) / 2 : 10;
|
|
24
|
+
const distance = size * (65 / fov);
|
|
25
|
+
camera_position[0] = distance;
|
|
26
|
+
camera_position[1] = distance * 0.3;
|
|
27
|
+
camera_position[2] = distance * 0.8;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
$effect.pre(() => {
|
|
31
|
+
if (structure && show_bonds) {
|
|
32
|
+
bond_pairs = bonding_strategies[bonding_strategy](structure, bonding_options);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
// Create modest gizmo options with balanced colors
|
|
36
|
+
let gizmo_options = $derived.by(() => {
|
|
37
|
+
const axis_options = Object.fromEntries([
|
|
38
|
+
[`x`, `#d75555`, `#e66666`], // red
|
|
39
|
+
[`y`, `#55b855`, `#66c966`], // green
|
|
40
|
+
[`z`, `#5555d7`, `#6666e6`], // blue
|
|
41
|
+
[`nx`, `#b84444`, `#cc5555`], // darker red
|
|
42
|
+
[`ny`, `#44a044`, `#55b155`], // darker green
|
|
43
|
+
[`nz`, `#4444b8`, `#5555c9`], // darker blue
|
|
44
|
+
].map(([axis, color, hover_color]) => [
|
|
45
|
+
axis,
|
|
46
|
+
{
|
|
47
|
+
color,
|
|
48
|
+
labelColor: `#555555`,
|
|
49
|
+
opacity: axis.startsWith(`n`) ? 0.7 : 0.85,
|
|
50
|
+
hover: {
|
|
51
|
+
color: hover_color,
|
|
52
|
+
labelColor: `#222222`,
|
|
53
|
+
opacity: axis.startsWith(`n`) ? 0.85 : 0.95,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
]));
|
|
57
|
+
const default_options = {
|
|
58
|
+
size: 100,
|
|
59
|
+
background: { enabled: false },
|
|
60
|
+
...axis_options,
|
|
61
|
+
};
|
|
62
|
+
return { ...default_options, ...(typeof gizmo === `boolean` ? {} : gizmo) };
|
|
63
|
+
});
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<T.PerspectiveCamera makeDefault position={camera_position} {fov}>
|
|
67
|
+
<!-- fix the ugly target -->
|
|
68
|
+
<OrbitControls
|
|
69
|
+
position={[0, 0, 0]}
|
|
70
|
+
enableZoom={zoom_speed > 0}
|
|
71
|
+
zoomSpeed={zoom_speed}
|
|
72
|
+
enablePan={pan_speed > 0}
|
|
73
|
+
panSpeed={pan_speed}
|
|
74
|
+
target={lattice ? (scale(add(...lattice.matrix), 0.5) as Vector3) : [0, 0, 0]}
|
|
75
|
+
maxZoom={max_zoom}
|
|
76
|
+
minZoom={min_zoom}
|
|
77
|
+
autoRotate={Boolean(auto_rotate)}
|
|
78
|
+
autoRotateSpeed={auto_rotate}
|
|
79
|
+
enableDamping={Boolean(rotation_damping)}
|
|
80
|
+
dampingFactor={rotation_damping}
|
|
81
|
+
onstart={() => {
|
|
82
|
+
camera_is_moving = true
|
|
83
|
+
// Clear any existing hover state to immediately hide tooltips
|
|
84
|
+
hovered_idx = null
|
|
85
|
+
}}
|
|
86
|
+
onend={() => {
|
|
87
|
+
camera_is_moving = false
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
{#if gizmo}
|
|
91
|
+
<Gizmo {...gizmo_options} />
|
|
92
|
+
{/if}
|
|
93
|
+
</OrbitControls>
|
|
94
|
+
</T.PerspectiveCamera>
|
|
95
|
+
|
|
96
|
+
<T.DirectionalLight position={[3, 10, 10]} intensity={directional_light} />
|
|
97
|
+
<T.AmbientLight intensity={ambient_light} />
|
|
98
|
+
|
|
99
|
+
{#if show_atoms && structure?.sites}
|
|
100
|
+
{#each structure.sites as site, site_idx (JSON.stringify({ site, site_idx }))}
|
|
101
|
+
{@const { species, xyz } = site}
|
|
102
|
+
{@const site_radius = same_size_atoms ? atom_radius : species.reduce(
|
|
103
|
+
(sum, spec) => sum + spec.occu * (atomic_radii[spec.element] ?? 1),
|
|
104
|
+
0,
|
|
105
|
+
) * atom_radius}
|
|
106
|
+
{#each species as { element: elem, occu }, spec_idx ([elem, occu])}
|
|
107
|
+
{@const start_angle = species
|
|
108
|
+
.slice(0, spec_idx)
|
|
109
|
+
.reduce((total, spec) => total + spec.occu, 0)}
|
|
110
|
+
<T.Group
|
|
111
|
+
position={xyz}
|
|
112
|
+
scale={site_radius}
|
|
113
|
+
onpointerenter={(_event: PointerEvent) => {
|
|
114
|
+
hovered_idx = site_idx
|
|
115
|
+
}}
|
|
116
|
+
onpointerleave={(_event: PointerEvent) => {
|
|
117
|
+
hovered_idx = null
|
|
118
|
+
}}
|
|
119
|
+
onclick={(_event: MouseEvent) => {
|
|
120
|
+
if (active_idx == site_idx) active_idx = null
|
|
121
|
+
else active_idx = site_idx
|
|
122
|
+
}}
|
|
123
|
+
>
|
|
124
|
+
<!-- Main partial sphere -->
|
|
125
|
+
<T.Mesh>
|
|
126
|
+
<T.SphereGeometry
|
|
127
|
+
args={[
|
|
128
|
+
0.5,
|
|
129
|
+
sphere_segments,
|
|
130
|
+
sphere_segments,
|
|
131
|
+
2 * Math.PI * start_angle,
|
|
132
|
+
2 * Math.PI * occu,
|
|
133
|
+
]}
|
|
134
|
+
/>
|
|
135
|
+
<T.MeshStandardMaterial color={colors.element?.[elem]} />
|
|
136
|
+
</T.Mesh>
|
|
137
|
+
|
|
138
|
+
<!-- Cap the open surfaces with flat circles if partial occupancy -->
|
|
139
|
+
{#if occu < 1}
|
|
140
|
+
{@const start_phi = 2 * Math.PI * start_angle}
|
|
141
|
+
{@const end_phi = 2 * Math.PI * (start_angle + occu)}
|
|
142
|
+
|
|
143
|
+
<!-- Start cap - positioned at the start angle -->
|
|
144
|
+
<T.Mesh rotation={[0, start_phi, 0]}>
|
|
145
|
+
<T.CircleGeometry args={[0.5, sphere_segments]} />
|
|
146
|
+
<T.MeshStandardMaterial color={colors.element?.[elem]} side={2} />
|
|
147
|
+
</T.Mesh>
|
|
148
|
+
|
|
149
|
+
<!-- End cap - positioned at the end angle -->
|
|
150
|
+
<T.Mesh rotation={[0, end_phi, 0]}>
|
|
151
|
+
<T.CircleGeometry args={[0.5, sphere_segments]} />
|
|
152
|
+
<T.MeshStandardMaterial color={colors.element?.[elem]} side={2} />
|
|
153
|
+
</T.Mesh>
|
|
154
|
+
{/if}
|
|
155
|
+
</T.Group>
|
|
156
|
+
<!-- use polar coordinates + offset if site has partial occupancy to move the text to the side of the corresponding sphere slice -->
|
|
157
|
+
<!-- TODO fix render multiple labels for disordered sites
|
|
158
|
+
{@const phi = 2 * Math.PI * (start_angle + occu / 2)}
|
|
159
|
+
{@const pos = add(
|
|
160
|
+
xyz,
|
|
161
|
+
scale([Math.cos(phi), 0, Math.sin(phi)], label_radius * radius),
|
|
162
|
+
)} -->
|
|
163
|
+
{#if show_site_labels}
|
|
164
|
+
<HTML center position={xyz}>
|
|
165
|
+
{#if atom_label}
|
|
166
|
+
{@render atom_label(site)}
|
|
167
|
+
{:else}
|
|
168
|
+
<span class="atom-label">
|
|
169
|
+
{@html species.map((sp) => sp.element).join(` `)}
|
|
170
|
+
</span>
|
|
171
|
+
{/if}
|
|
172
|
+
</HTML>
|
|
173
|
+
{/if}
|
|
174
|
+
{/each}
|
|
175
|
+
{/each}
|
|
176
|
+
{/if}
|
|
177
|
+
|
|
178
|
+
{#if show_bonds}
|
|
179
|
+
{#each bond_pairs as [from, to, idx_a, idx_b] ([from, to, idx_a, idx_b])}
|
|
180
|
+
{@const site_a = structure?.sites[idx_a]}
|
|
181
|
+
{@const site_b = structure?.sites[idx_b]}
|
|
182
|
+
{@const get_majority_color = (site: typeof site_a) => {
|
|
183
|
+
if (!site?.species || site.species.length === 0) return bond_color
|
|
184
|
+
// Find species with highest occupancy
|
|
185
|
+
const majority_species = site.species.reduce((max, spec) =>
|
|
186
|
+
spec.occu > max.occu ? spec : max
|
|
187
|
+
)
|
|
188
|
+
return colors.element?.[majority_species.element] || bond_color
|
|
189
|
+
}}
|
|
190
|
+
{@const from_color = get_majority_color(site_a)}
|
|
191
|
+
{@const to_color = get_majority_color(site_b)}
|
|
192
|
+
<Bond
|
|
193
|
+
{from}
|
|
194
|
+
{to}
|
|
195
|
+
thickness={bond_thickness}
|
|
196
|
+
{from_color}
|
|
197
|
+
{to_color}
|
|
198
|
+
color={bond_color}
|
|
199
|
+
/>
|
|
200
|
+
{/each}
|
|
201
|
+
{/if}
|
|
202
|
+
|
|
203
|
+
<!-- highlight active and hovered sites -->
|
|
204
|
+
{#each [{ site: hovered_site, opacity: 0.2 }, { site: active_site, opacity: 0.3 }] as
|
|
205
|
+
{ site, opacity }
|
|
206
|
+
(opacity)
|
|
207
|
+
}
|
|
208
|
+
{#if site}
|
|
209
|
+
{@const { xyz, species } = site}
|
|
210
|
+
{@const highlight_radius = same_size_atoms ? atom_radius : species.reduce(
|
|
211
|
+
(sum, spec) => sum + spec.occu * (atomic_radii[spec.element] ?? 1),
|
|
212
|
+
0,
|
|
213
|
+
) * atom_radius}
|
|
214
|
+
<T.Mesh position={xyz} scale={1.02 * highlight_radius}>
|
|
215
|
+
<T.SphereGeometry args={[0.5, 20, 20]} />
|
|
216
|
+
<T.MeshStandardMaterial color="white" transparent {opacity} />
|
|
217
|
+
</T.Mesh>
|
|
218
|
+
{/if}
|
|
219
|
+
{/each}
|
|
220
|
+
|
|
221
|
+
<!-- cylinder between active and hovered site to indicate measured distance -->
|
|
222
|
+
{#if active_site && hovered_site && active_hovered_dist}
|
|
223
|
+
{@const { color, width } = active_hovered_dist}
|
|
224
|
+
<Bond from={active_site.xyz} to={hovered_site.xyz} thickness={width} {color} />
|
|
225
|
+
{/if}
|
|
226
|
+
|
|
227
|
+
<!-- hovered site tooltip -->
|
|
228
|
+
{#if hovered_site && !camera_is_moving}
|
|
229
|
+
<HTML position={hovered_site.xyz} pointerEvents="none">
|
|
230
|
+
<div class="tooltip">
|
|
231
|
+
<!-- Element names with occupancies for disordered sites -->
|
|
232
|
+
<div class="elements">
|
|
233
|
+
{#each hovered_site.species ?? [] as
|
|
234
|
+
{ element, occu, oxidation_state: oxi_state },
|
|
235
|
+
idx
|
|
236
|
+
([element, occu, oxi_state])
|
|
237
|
+
}
|
|
238
|
+
{@const oxi_str = oxi_state != null && oxi_state !== 0
|
|
239
|
+
? `<sup>${oxi_state}${oxi_state > 0 ? `+` : `-`}</sup>`
|
|
240
|
+
: ``}
|
|
241
|
+
{@const element_name = element_data.find((elem) =>
|
|
242
|
+
elem.symbol === element
|
|
243
|
+
)?.name ?? ``}
|
|
244
|
+
{#if idx > 0}
|
|
245
|
+
 
|
|
246
|
+
{/if}
|
|
247
|
+
{#if occu !== 1}
|
|
248
|
+
<span class="occupancy">{format_num(occu, `.3~f`)}</span>
|
|
249
|
+
{/if}
|
|
250
|
+
<strong>{element}{@html oxi_str}</strong>
|
|
251
|
+
{#if element_name}
|
|
252
|
+
<span class="elem-name">{element_name}</span>
|
|
253
|
+
{/if}
|
|
254
|
+
{/each}
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
<!-- Fractional coordinates -->
|
|
258
|
+
<div class="coordinates">
|
|
259
|
+
abc: ({hovered_site.abc.map((num) => format_num(num, precision)).join(`, `)})
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<!-- Cartesian coordinates -->
|
|
263
|
+
<div class="coordinates">
|
|
264
|
+
xyz: ({hovered_site.xyz.map((num) => format_num(num, precision)).join(`, `)}) Å
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<!-- distance from hovered to active site -->
|
|
268
|
+
{#if active_site && active_site != hovered_site && active_hovered_dist}
|
|
269
|
+
{@const direct_distance = euclidean_dist(hovered_site.xyz, active_site.xyz)}
|
|
270
|
+
{@const pbc_distance = lattice
|
|
271
|
+
? pbc_dist(hovered_site.xyz, active_site.xyz, lattice.matrix)
|
|
272
|
+
: direct_distance}
|
|
273
|
+
<div class="distance">
|
|
274
|
+
<strong>dist:</strong>
|
|
275
|
+
{format_num(pbc_distance, precision)} Å{lattice ? ` (PBC)` : ``}
|
|
276
|
+
{#if lattice && Math.abs(pbc_distance - direct_distance) > 0.1}
|
|
277
|
+
<small> | direct: {format_num(direct_distance, precision)} Å</small>
|
|
278
|
+
{/if}
|
|
279
|
+
</div>
|
|
280
|
+
{/if}
|
|
281
|
+
</div>
|
|
282
|
+
</HTML>
|
|
283
|
+
{/if}
|
|
284
|
+
|
|
285
|
+
{#if lattice}
|
|
286
|
+
<Lattice matrix={lattice.matrix} {...lattice_props} />
|
|
287
|
+
{/if}
|
|
288
|
+
|
|
289
|
+
<style>
|
|
290
|
+
div.tooltip {
|
|
291
|
+
width: max-content;
|
|
292
|
+
box-sizing: border-box;
|
|
293
|
+
border-radius: var(--struct-tooltip-border-radius, 5pt);
|
|
294
|
+
background: var(--struct-tooltip-bg, rgba(0, 0, 0, 0.5));
|
|
295
|
+
padding: var(--struct-tooltip-padding, 1pt 5pt);
|
|
296
|
+
text-align: left;
|
|
297
|
+
color: var(--struct-tooltip-text-color);
|
|
298
|
+
font-family: var(--struct-tooltip-font-family);
|
|
299
|
+
font-size: var(--struct-tooltip-font-size);
|
|
300
|
+
line-height: var(--struct-tooltip-line-height);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
div.tooltip .elements {
|
|
304
|
+
margin-bottom: var(--struct-tooltip-elements-margin);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
div.tooltip .occupancy {
|
|
308
|
+
font-size: var(--struct-tooltip-occu-font-size);
|
|
309
|
+
opacity: var(--struct-tooltip-occu-opacity);
|
|
310
|
+
margin-right: var(--struct-tooltip-occu-margin);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
div.tooltip .elem-name {
|
|
314
|
+
font-size: var(--struct-tooltip-elem-name-font-size, 0.85em);
|
|
315
|
+
opacity: var(--struct-tooltip-elem-name-opacity, 0.7);
|
|
316
|
+
margin: var(--struct-tooltip-elem-name-margin, 0 0 0 0.3em);
|
|
317
|
+
font-weight: var(--struct-tooltip-elem-name-font-weight, normal);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
div.tooltip .coordinates,
|
|
321
|
+
div.tooltip .distance {
|
|
322
|
+
font-size: var(--struct-tooltip-coords-font-size);
|
|
323
|
+
margin: var(--struct-tooltip-coords-margin);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.atom-label {
|
|
327
|
+
background: var(--struct-atom-label-bg, rgba(0, 0, 0, 0.1));
|
|
328
|
+
border-radius: var(--struct-atom-label-border-radius, 3pt);
|
|
329
|
+
padding: var(--struct-atom-label-padding, 0 3px);
|
|
330
|
+
}
|
|
331
|
+
</style>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { AnyStructure, Site, Vector3 } from '..';
|
|
2
|
+
import { Lattice } from '..';
|
|
3
|
+
import { Gizmo } from '@threlte/extras';
|
|
4
|
+
import type { ComponentProps } from 'svelte';
|
|
5
|
+
import { type Snippet } from 'svelte';
|
|
6
|
+
import * as bonding_strategies from './bonding';
|
|
7
|
+
type ActiveHoveredDist = {
|
|
8
|
+
color: string;
|
|
9
|
+
width: number;
|
|
10
|
+
opacity: number;
|
|
11
|
+
};
|
|
12
|
+
interface Props {
|
|
13
|
+
structure?: AnyStructure | undefined;
|
|
14
|
+
atom_radius?: number;
|
|
15
|
+
same_size_atoms?: boolean;
|
|
16
|
+
camera_position?: Vector3;
|
|
17
|
+
rotation_damping?: number;
|
|
18
|
+
max_zoom?: number | undefined;
|
|
19
|
+
min_zoom?: number | undefined;
|
|
20
|
+
zoom_speed?: number;
|
|
21
|
+
pan_speed?: number;
|
|
22
|
+
show_atoms?: boolean;
|
|
23
|
+
show_bonds?: boolean;
|
|
24
|
+
show_site_labels?: boolean;
|
|
25
|
+
gizmo?: boolean | ComponentProps<typeof Gizmo>;
|
|
26
|
+
hovered_idx?: number | null;
|
|
27
|
+
active_idx?: number | null;
|
|
28
|
+
hovered_site?: Site | null;
|
|
29
|
+
active_site?: Site | null;
|
|
30
|
+
precision?: string;
|
|
31
|
+
auto_rotate?: number;
|
|
32
|
+
bond_thickness?: number;
|
|
33
|
+
bond_color?: string;
|
|
34
|
+
bonding_strategy?: keyof typeof bonding_strategies;
|
|
35
|
+
bonding_options?: Record<string, unknown>;
|
|
36
|
+
active_hovered_dist?: ActiveHoveredDist | null;
|
|
37
|
+
fov?: number;
|
|
38
|
+
ambient_light?: number;
|
|
39
|
+
directional_light?: number;
|
|
40
|
+
sphere_segments?: number;
|
|
41
|
+
lattice_props?: Omit<ComponentProps<typeof Lattice>, `matrix`>;
|
|
42
|
+
atom_label?: Snippet<[Site]>;
|
|
43
|
+
camera_is_moving?: boolean;
|
|
44
|
+
}
|
|
45
|
+
declare const StructureScene: import("svelte").Component<Props, {}, "hovered_idx" | "active_idx" | "hovered_site" | "active_site" | "camera_is_moving">;
|
|
46
|
+
type StructureScene = ReturnType<typeof StructureScene>;
|
|
47
|
+
export default StructureScene;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AnyStructure, BondPair } from '..';
|
|
2
|
+
export type BondingAlgo = typeof max_dist | typeof nearest_neighbor | typeof vdw_radius_based;
|
|
3
|
+
export declare function max_dist(structure: AnyStructure, { max_bond_dist, min_bond_dist }?: {
|
|
4
|
+
max_bond_dist?: number | undefined;
|
|
5
|
+
min_bond_dist?: number | undefined;
|
|
6
|
+
}): BondPair[];
|
|
7
|
+
export declare function nearest_neighbor(structure: AnyStructure, { scaling_factor, min_bond_dist }?: {
|
|
8
|
+
scaling_factor?: number | undefined;
|
|
9
|
+
min_bond_dist?: number | undefined;
|
|
10
|
+
}): BondPair[];
|
|
11
|
+
export declare function vdw_radius_based(structure: AnyStructure, { tolerance, // Extra distance beyond sum of VdW radii
|
|
12
|
+
min_bond_dist, max_bond_dist, }?: {
|
|
13
|
+
tolerance?: number | undefined;
|
|
14
|
+
min_bond_dist?: number | undefined;
|
|
15
|
+
max_bond_dist?: number | undefined;
|
|
16
|
+
}): BondPair[];
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// Efficient distance-based bonding algorithm using squared distances and unique bond IDs.
|
|
2
|
+
// Finds all bonds within the specified distance range.
|
|
3
|
+
export function max_dist(structure, { max_bond_dist = 3, min_bond_dist = 0.4 } = {}) {
|
|
4
|
+
const bonds = [];
|
|
5
|
+
const max_dist_sq = max_bond_dist ** 2;
|
|
6
|
+
const min_dist_sq = min_bond_dist ** 2;
|
|
7
|
+
const sites = structure.sites;
|
|
8
|
+
if (sites.length < 2)
|
|
9
|
+
return bonds;
|
|
10
|
+
// Use nested loops with early termination optimizations
|
|
11
|
+
for (let idx_a = 0; idx_a < sites.length - 1; idx_a++) {
|
|
12
|
+
const site_a = sites[idx_a];
|
|
13
|
+
const [x1, y1, z1] = site_a.xyz;
|
|
14
|
+
for (let idx_b = idx_a + 1; idx_b < sites.length; idx_b++) {
|
|
15
|
+
const site_b = sites[idx_b];
|
|
16
|
+
const [x2, y2, z2] = site_b.xyz;
|
|
17
|
+
// Fast squared distance calculation
|
|
18
|
+
const dx = x2 - x1;
|
|
19
|
+
const dy = y2 - y1;
|
|
20
|
+
const dz = z2 - z1;
|
|
21
|
+
const dist_sq = dx * dx + dy * dy + dz * dz;
|
|
22
|
+
if (dist_sq >= min_dist_sq && dist_sq <= max_dist_sq) {
|
|
23
|
+
const distance = Math.sqrt(dist_sq);
|
|
24
|
+
bonds.push([site_a.xyz, site_b.xyz, idx_a, idx_b, distance]);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return bonds;
|
|
29
|
+
}
|
|
30
|
+
// Optimized nearest neighbor bonding with proper duplicate prevention.
|
|
31
|
+
// Uses spatial hashing for better O(n) performance on large structures.
|
|
32
|
+
export function nearest_neighbor(structure, { scaling_factor = 1.8, min_bond_dist = 0.4 } = {}) {
|
|
33
|
+
const sites = structure.sites;
|
|
34
|
+
const bonds = [];
|
|
35
|
+
if (sites.length < 2)
|
|
36
|
+
return bonds;
|
|
37
|
+
const min_dist_sq = min_bond_dist ** 2;
|
|
38
|
+
const nearest_dist_sq = new Float64Array(sites.length).fill(Infinity);
|
|
39
|
+
// First pass: find nearest neighbor distances efficiently
|
|
40
|
+
for (let idx_a = 0; idx_a < sites.length - 1; idx_a++) {
|
|
41
|
+
const [x1, y1, z1] = sites[idx_a].xyz;
|
|
42
|
+
for (let idx_b = idx_a + 1; idx_b < sites.length; idx_b++) {
|
|
43
|
+
const [x2, y2, z2] = sites[idx_b].xyz;
|
|
44
|
+
const dx = x2 - x1;
|
|
45
|
+
const dy = y2 - y1;
|
|
46
|
+
const dz = z2 - z1;
|
|
47
|
+
const dist_sq = dx * dx + dy * dy + dz * dz;
|
|
48
|
+
if (dist_sq >= min_dist_sq) {
|
|
49
|
+
if (dist_sq < nearest_dist_sq[idx_a])
|
|
50
|
+
nearest_dist_sq[idx_a] = dist_sq;
|
|
51
|
+
if (dist_sq < nearest_dist_sq[idx_b])
|
|
52
|
+
nearest_dist_sq[idx_b] = dist_sq;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Second pass: create bonds within scaled nearest neighbor distances
|
|
57
|
+
const scaling_sq = scaling_factor ** 2;
|
|
58
|
+
for (let idx_a = 0; idx_a < sites.length - 1; idx_a++) {
|
|
59
|
+
const site_a = sites[idx_a];
|
|
60
|
+
const [x1, y1, z1] = site_a.xyz;
|
|
61
|
+
const max_dist_sq = nearest_dist_sq[idx_a] * scaling_sq;
|
|
62
|
+
for (let idx_b = idx_a + 1; idx_b < sites.length; idx_b++) {
|
|
63
|
+
const site_b = sites[idx_b];
|
|
64
|
+
const [x2, y2, z2] = site_b.xyz;
|
|
65
|
+
const dx = x2 - x1;
|
|
66
|
+
const dy = y2 - y1;
|
|
67
|
+
const dz = z2 - z1;
|
|
68
|
+
const dist_sq = dx * dx + dy * dy + dz * dz;
|
|
69
|
+
if (dist_sq >= min_dist_sq && dist_sq <= max_dist_sq) {
|
|
70
|
+
const distance = Math.sqrt(dist_sq);
|
|
71
|
+
bonds.push([site_a.xyz, site_b.xyz, idx_a, idx_b, distance]);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return bonds;
|
|
76
|
+
}
|
|
77
|
+
// Van der Waals radius-based bonding algorithm.
|
|
78
|
+
// Uses atomic radii to determine reasonable bonding distances.
|
|
79
|
+
export function vdw_radius_based(structure, { tolerance = 0.3, // Extra distance beyond sum of VdW radii
|
|
80
|
+
min_bond_dist = 0.4, max_bond_dist = 4.0, } = {}) {
|
|
81
|
+
// Default VdW radii in Angstroms (simplified set)
|
|
82
|
+
const vdw_radii = {
|
|
83
|
+
H: 1.2,
|
|
84
|
+
He: 1.4,
|
|
85
|
+
Li: 1.82,
|
|
86
|
+
Be: 1.53,
|
|
87
|
+
B: 1.92,
|
|
88
|
+
C: 1.7,
|
|
89
|
+
N: 1.55,
|
|
90
|
+
O: 1.52,
|
|
91
|
+
F: 1.47,
|
|
92
|
+
Ne: 1.54,
|
|
93
|
+
Na: 2.27,
|
|
94
|
+
Mg: 1.73,
|
|
95
|
+
Al: 1.84,
|
|
96
|
+
Si: 2.1,
|
|
97
|
+
P: 1.8,
|
|
98
|
+
S: 1.8,
|
|
99
|
+
Cl: 1.75,
|
|
100
|
+
Ar: 1.88,
|
|
101
|
+
K: 2.75,
|
|
102
|
+
Ca: 2.31,
|
|
103
|
+
Fe: 2.04,
|
|
104
|
+
Co: 2.0,
|
|
105
|
+
Ni: 1.97,
|
|
106
|
+
Cu: 1.96,
|
|
107
|
+
Zn: 2.01,
|
|
108
|
+
Br: 1.85,
|
|
109
|
+
Kr: 2.02,
|
|
110
|
+
Ag: 2.11,
|
|
111
|
+
I: 1.98,
|
|
112
|
+
Xe: 2.16,
|
|
113
|
+
Au: 2.14,
|
|
114
|
+
Hg: 2.23,
|
|
115
|
+
Pb: 2.02,
|
|
116
|
+
};
|
|
117
|
+
const bonds = [];
|
|
118
|
+
const sites = structure.sites;
|
|
119
|
+
if (sites.length < 2)
|
|
120
|
+
return bonds;
|
|
121
|
+
const min_dist_sq = min_bond_dist ** 2;
|
|
122
|
+
const max_dist_sq = max_bond_dist ** 2;
|
|
123
|
+
for (let idx_a = 0; idx_a < sites.length - 1; idx_a++) {
|
|
124
|
+
const site_a = sites[idx_a];
|
|
125
|
+
const [x1, y1, z1] = site_a.xyz;
|
|
126
|
+
// Get primary element for site A
|
|
127
|
+
const elem_a = site_a.species?.[0]?.element || `C`;
|
|
128
|
+
const radius_a = vdw_radii[elem_a] || 1.7;
|
|
129
|
+
for (let idx_b = idx_a + 1; idx_b < sites.length; idx_b++) {
|
|
130
|
+
const site_b = sites[idx_b];
|
|
131
|
+
const [x2, y2, z2] = site_b.xyz;
|
|
132
|
+
// Get primary element for site B
|
|
133
|
+
const elem_b = site_b.species?.[0]?.element || `C`;
|
|
134
|
+
const radius_b = vdw_radii[elem_b] || 1.7;
|
|
135
|
+
const dx = x2 - x1;
|
|
136
|
+
const dy = y2 - y1;
|
|
137
|
+
const dz = z2 - z1;
|
|
138
|
+
const dist_sq = dx * dx + dy * dy + dz * dz;
|
|
139
|
+
const distance = Math.sqrt(dist_sq);
|
|
140
|
+
// Check if distance is reasonable for bonding
|
|
141
|
+
const expected_bond_dist = radius_a + radius_b + tolerance;
|
|
142
|
+
if (dist_sq >= min_dist_sq &&
|
|
143
|
+
dist_sq <= max_dist_sq &&
|
|
144
|
+
distance <= expected_bond_dist) {
|
|
145
|
+
bonds.push([site_a.xyz, site_b.xyz, idx_a, idx_b, distance]);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return bonds;
|
|
150
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { ElementSymbol, Vec3 } from '..';
|
|
2
|
+
import type { Matrix3x3 } from '../math';
|
|
3
|
+
export { default as Bond } from './Bond.svelte';
|
|
4
|
+
export * as bonding_strategies from './bonding';
|
|
5
|
+
export { default as Lattice } from './Lattice.svelte';
|
|
6
|
+
export * from './pbc';
|
|
7
|
+
export { default as Structure } from './Structure.svelte';
|
|
8
|
+
export { default as StructureCard } from './StructureCard.svelte';
|
|
9
|
+
export { default as StructureControls } from './StructureControls.svelte';
|
|
10
|
+
export { default as StructureLegend } from './StructureLegend.svelte';
|
|
11
|
+
export { default as StructureScene } from './StructureScene.svelte';
|
|
12
|
+
export declare const CELL_DEFAULTS: {
|
|
13
|
+
readonly edge_opacity: 0.4;
|
|
14
|
+
readonly surface_opacity: 0.05;
|
|
15
|
+
readonly color: "#ffffff";
|
|
16
|
+
readonly line_width: 1.5;
|
|
17
|
+
};
|
|
18
|
+
export declare const BOND_DEFAULTS: {
|
|
19
|
+
readonly thickness: 0.25;
|
|
20
|
+
readonly offset: 0;
|
|
21
|
+
readonly color: "white";
|
|
22
|
+
readonly from_color: "white";
|
|
23
|
+
readonly to_color: "white";
|
|
24
|
+
};
|
|
25
|
+
export type Species = {
|
|
26
|
+
element: ElementSymbol;
|
|
27
|
+
occu: number;
|
|
28
|
+
oxidation_state: number;
|
|
29
|
+
};
|
|
30
|
+
export type Site = {
|
|
31
|
+
species: Species[];
|
|
32
|
+
abc: Vec3;
|
|
33
|
+
xyz: Vec3;
|
|
34
|
+
label: string;
|
|
35
|
+
properties: Record<string, unknown>;
|
|
36
|
+
};
|
|
37
|
+
export declare const lattice_param_keys: readonly ["a", "b", "c", "alpha", "beta", "gamma"];
|
|
38
|
+
export type LatticeParams = {
|
|
39
|
+
[key in (typeof lattice_param_keys)[number]]: number;
|
|
40
|
+
};
|
|
41
|
+
export type PymatgenLattice = {
|
|
42
|
+
matrix: Matrix3x3;
|
|
43
|
+
pbc: [boolean, boolean, boolean];
|
|
44
|
+
volume: number;
|
|
45
|
+
} & LatticeParams;
|
|
46
|
+
export type PymatgenMolecule = {
|
|
47
|
+
sites: Site[];
|
|
48
|
+
charge?: number;
|
|
49
|
+
id?: string;
|
|
50
|
+
};
|
|
51
|
+
export type PymatgenStructure = PymatgenMolecule & {
|
|
52
|
+
lattice: PymatgenLattice;
|
|
53
|
+
};
|
|
54
|
+
export type Edge = {
|
|
55
|
+
to_jimage: [number, number, number];
|
|
56
|
+
id: number;
|
|
57
|
+
key: number;
|
|
58
|
+
};
|
|
59
|
+
export type Graph = {
|
|
60
|
+
directed: boolean;
|
|
61
|
+
multigraph: boolean;
|
|
62
|
+
graph: [
|
|
63
|
+
[
|
|
64
|
+
`edge_weight_name`,
|
|
65
|
+
null
|
|
66
|
+
] | [`edge_weight_units`, null] | [`name`, string]
|
|
67
|
+
];
|
|
68
|
+
nodes: {
|
|
69
|
+
id: number;
|
|
70
|
+
}[];
|
|
71
|
+
adjacency: Edge[][];
|
|
72
|
+
};
|
|
73
|
+
export type StructureGraph = {
|
|
74
|
+
'@module': string;
|
|
75
|
+
'@class': string;
|
|
76
|
+
structure: PymatgenStructure;
|
|
77
|
+
graphs: Graph[];
|
|
78
|
+
};
|
|
79
|
+
export type BondPair = [Vec3, Vec3, number, number, number];
|
|
80
|
+
export type IdStructure = PymatgenStructure & {
|
|
81
|
+
id: string;
|
|
82
|
+
};
|
|
83
|
+
export type StructureWithGraph = IdStructure & {
|
|
84
|
+
graph: Graph;
|
|
85
|
+
};
|
|
86
|
+
export type AnyStructure = PymatgenStructure | PymatgenMolecule;
|
|
87
|
+
export type AnyStructureGraph = AnyStructure & {
|
|
88
|
+
graph: Graph;
|
|
89
|
+
};
|
|
90
|
+
export declare function get_elem_amounts(structure: AnyStructure): Partial<Record<"K" | "H" | "He" | "Li" | "Be" | "B" | "C" | "N" | "O" | "F" | "Ne" | "Na" | "Mg" | "Al" | "Si" | "P" | "S" | "Cl" | "Ar" | "Ca" | "Sc" | "Ti" | "V" | "Cr" | "Mn" | "Fe" | "Co" | "Ni" | "Cu" | "Zn" | "Ga" | "Ge" | "As" | "Se" | "Br" | "Kr" | "Rb" | "Sr" | "Y" | "Zr" | "Nb" | "Mo" | "Tc" | "Ru" | "Rh" | "Pd" | "Ag" | "Cd" | "In" | "Sn" | "Sb" | "Te" | "I" | "Xe" | "Cs" | "Ba" | "La" | "Ce" | "Pr" | "Nd" | "Pm" | "Sm" | "Eu" | "Gd" | "Tb" | "Dy" | "Ho" | "Er" | "Tm" | "Yb" | "Lu" | "Hf" | "Ta" | "W" | "Re" | "Os" | "Ir" | "Pt" | "Au" | "Hg" | "Tl" | "Pb" | "Bi" | "Po" | "At" | "Rn" | "Fr" | "Ra" | "Ac" | "Th" | "Pa" | "U" | "Np" | "Pu" | "Am" | "Cm" | "Bk" | "Cf" | "Es" | "Fm" | "Md" | "No" | "Lr" | "Rf" | "Db" | "Sg" | "Bh" | "Hs" | "Mt" | "Ds" | "Rg" | "Cn" | "Nh" | "Fl" | "Mc" | "Lv" | "Ts" | "Og", number>>;
|
|
91
|
+
export declare function format_chemical_formula(structure: AnyStructure, sort_fn: (symbols: ElementSymbol[]) => ElementSymbol[]): string;
|
|
92
|
+
export declare function alphabetical_formula(structure: AnyStructure): string;
|
|
93
|
+
export declare function electro_neg_formula(structure: AnyStructure): string;
|
|
94
|
+
export declare const atomic_radii: Partial<Record<ElementSymbol, number>>;
|
|
95
|
+
export declare const atomic_weights: Partial<Record<ElementSymbol, number>>;
|
|
96
|
+
export declare function get_elements(structure: AnyStructure): ElementSymbol[];
|
|
97
|
+
export declare function density(structure: PymatgenStructure, prec?: string): string;
|
|
98
|
+
export declare function get_center_of_mass(struct_or_mol: AnyStructure): Vec3;
|