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.
Files changed (131) hide show
  1. package/dist/BohrAtom.svelte +105 -0
  2. package/dist/BohrAtom.svelte.d.ts +21 -0
  3. package/dist/ControlPanel.svelte +158 -0
  4. package/dist/ControlPanel.svelte.d.ts +18 -0
  5. package/dist/Icon.svelte +23 -0
  6. package/dist/Icon.svelte.d.ts +8 -0
  7. package/dist/InfoCard.svelte +79 -0
  8. package/dist/InfoCard.svelte.d.ts +23 -0
  9. package/dist/Nucleus.svelte +64 -0
  10. package/dist/Nucleus.svelte.d.ts +16 -0
  11. package/dist/Spinner.svelte +44 -0
  12. package/dist/Spinner.svelte.d.ts +7 -0
  13. package/dist/api.d.ts +6 -0
  14. package/dist/api.js +30 -0
  15. package/dist/colors/alloy-colors.json +111 -0
  16. package/dist/colors/dark-mode-colors.json +111 -0
  17. package/dist/colors/index.d.ts +26 -0
  18. package/dist/colors/index.js +72 -0
  19. package/dist/colors/jmol-colors.json +111 -0
  20. package/dist/colors/muted-colors.json +111 -0
  21. package/dist/colors/pastel-colors.json +111 -0
  22. package/dist/colors/vesta-colors.json +111 -0
  23. package/dist/composition/BarChart.svelte +260 -0
  24. package/dist/composition/BarChart.svelte.d.ts +33 -0
  25. package/dist/composition/BubbleChart.svelte +166 -0
  26. package/dist/composition/BubbleChart.svelte.d.ts +30 -0
  27. package/dist/composition/Composition.svelte +73 -0
  28. package/dist/composition/Composition.svelte.d.ts +27 -0
  29. package/dist/composition/PieChart.svelte +236 -0
  30. package/dist/composition/PieChart.svelte.d.ts +36 -0
  31. package/dist/composition/index.d.ts +5 -0
  32. package/dist/composition/index.js +5 -0
  33. package/dist/composition/parse.d.ts +14 -0
  34. package/dist/composition/parse.js +307 -0
  35. package/dist/element/ElementHeading.svelte +21 -0
  36. package/dist/element/ElementHeading.svelte.d.ts +8 -0
  37. package/dist/element/ElementPhoto.svelte +56 -0
  38. package/dist/element/ElementPhoto.svelte.d.ts +9 -0
  39. package/dist/element/ElementStats.svelte +73 -0
  40. package/dist/element/ElementStats.svelte.d.ts +8 -0
  41. package/dist/element/ElementTile.svelte +449 -0
  42. package/dist/element/ElementTile.svelte.d.ts +25 -0
  43. package/dist/element/data.d.ts +4958 -0
  44. package/dist/element/data.js +5628 -0
  45. package/dist/element/index.d.ts +4 -0
  46. package/dist/element/index.js +4 -0
  47. package/dist/icons.d.ts +435 -0
  48. package/dist/icons.js +435 -0
  49. package/dist/index.d.ts +82 -0
  50. package/dist/index.js +43 -0
  51. package/dist/io/decompress.d.ts +16 -0
  52. package/dist/io/decompress.js +78 -0
  53. package/dist/io/export.d.ts +9 -0
  54. package/dist/io/export.js +205 -0
  55. package/dist/io/parse.d.ts +53 -0
  56. package/dist/io/parse.js +747 -0
  57. package/dist/labels.d.ts +31 -0
  58. package/dist/labels.js +209 -0
  59. package/dist/material/MaterialCard.svelte +135 -0
  60. package/dist/material/MaterialCard.svelte.d.ts +10 -0
  61. package/dist/material/SymmetryCard.svelte +23 -0
  62. package/dist/material/SymmetryCard.svelte.d.ts +9 -0
  63. package/dist/material/index.d.ts +2 -0
  64. package/dist/material/index.js +2 -0
  65. package/dist/math.d.ts +24 -0
  66. package/dist/math.js +216 -0
  67. package/dist/periodic-table/PeriodicTable.svelte +284 -0
  68. package/dist/periodic-table/PeriodicTable.svelte.d.ts +50 -0
  69. package/dist/periodic-table/PropertySelect.svelte +20 -0
  70. package/dist/periodic-table/PropertySelect.svelte.d.ts +13 -0
  71. package/dist/periodic-table/TableInset.svelte +18 -0
  72. package/dist/periodic-table/TableInset.svelte.d.ts +9 -0
  73. package/dist/periodic-table/index.d.ts +9 -0
  74. package/dist/periodic-table/index.js +3 -0
  75. package/dist/plot/ColorBar.svelte +414 -0
  76. package/dist/plot/ColorBar.svelte.d.ts +22 -0
  77. package/dist/plot/ColorScaleSelect.svelte +31 -0
  78. package/dist/plot/ColorScaleSelect.svelte.d.ts +15 -0
  79. package/dist/plot/ElementScatter.svelte +38 -0
  80. package/dist/plot/ElementScatter.svelte.d.ts +14 -0
  81. package/dist/plot/Line.svelte +42 -0
  82. package/dist/plot/Line.svelte.d.ts +15 -0
  83. package/dist/plot/PlotLegend.svelte +206 -0
  84. package/dist/plot/PlotLegend.svelte.d.ts +18 -0
  85. package/dist/plot/ScatterPlot.svelte +1753 -0
  86. package/dist/plot/ScatterPlot.svelte.d.ts +114 -0
  87. package/dist/plot/ScatterPlotControls.svelte +505 -0
  88. package/dist/plot/ScatterPlotControls.svelte.d.ts +33 -0
  89. package/dist/plot/ScatterPoint.svelte +72 -0
  90. package/dist/plot/ScatterPoint.svelte.d.ts +17 -0
  91. package/dist/plot/index.d.ts +168 -0
  92. package/dist/plot/index.js +46 -0
  93. package/dist/state.svelte.d.ts +12 -0
  94. package/dist/state.svelte.js +11 -0
  95. package/dist/structure/Bond.svelte +68 -0
  96. package/dist/structure/Bond.svelte.d.ts +13 -0
  97. package/dist/structure/Lattice.svelte +115 -0
  98. package/dist/structure/Lattice.svelte.d.ts +15 -0
  99. package/dist/structure/Structure.svelte +298 -0
  100. package/dist/structure/Structure.svelte.d.ts +28 -0
  101. package/dist/structure/StructureCard.svelte +26 -0
  102. package/dist/structure/StructureCard.svelte.d.ts +9 -0
  103. package/dist/structure/StructureControls.svelte +383 -0
  104. package/dist/structure/StructureControls.svelte.d.ts +23 -0
  105. package/dist/structure/StructureLegend.svelte +130 -0
  106. package/dist/structure/StructureLegend.svelte.d.ts +17 -0
  107. package/dist/structure/StructureScene.svelte +331 -0
  108. package/dist/structure/StructureScene.svelte.d.ts +47 -0
  109. package/dist/structure/bonding.d.ts +16 -0
  110. package/dist/structure/bonding.js +150 -0
  111. package/dist/structure/index.d.ts +98 -0
  112. package/dist/structure/index.js +114 -0
  113. package/dist/structure/pbc.d.ts +6 -0
  114. package/dist/structure/pbc.js +72 -0
  115. package/dist/trajectory/Sidebar.svelte +412 -0
  116. package/dist/trajectory/Sidebar.svelte.d.ts +14 -0
  117. package/dist/trajectory/Trajectory.svelte +1084 -0
  118. package/dist/trajectory/Trajectory.svelte.d.ts +49 -0
  119. package/dist/trajectory/TrajectoryError.svelte +120 -0
  120. package/dist/trajectory/TrajectoryError.svelte.d.ts +12 -0
  121. package/dist/trajectory/extract.d.ts +5 -0
  122. package/dist/trajectory/extract.js +157 -0
  123. package/dist/trajectory/index.d.ts +16 -0
  124. package/dist/trajectory/index.js +49 -0
  125. package/dist/trajectory/parse.d.ts +13 -0
  126. package/dist/trajectory/parse.js +1093 -0
  127. package/dist/trajectory/plotting.d.ts +12 -0
  128. package/dist/trajectory/plotting.js +148 -0
  129. package/license +21 -0
  130. package/package.json +131 -0
  131. 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(`&nbsp;`)}
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
+ &thinsp;
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;