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,298 @@
1
+ <script lang="ts">import { get_elem_amounts, get_pbc_image_sites, Icon } from '..';
2
+ import { element_color_schemes } from '../colors';
3
+ import { decompress_file } from '../io/decompress';
4
+ import * as exports from '../io/export';
5
+ import { colors } from '../state.svelte';
6
+ import { Canvas } from '@threlte/core';
7
+ import { WebGLRenderer } from 'three';
8
+ import { BOND_DEFAULTS, CELL_DEFAULTS, StructureControls, StructureLegend, StructureScene, } from './index';
9
+ let { structure = $bindable(undefined), scene_props = $bindable({ atom_radius: 1, show_atoms: true, auto_rotate: 0 }), lattice_props = $bindable({
10
+ cell_edge_opacity: CELL_DEFAULTS.edge_opacity,
11
+ cell_surface_opacity: CELL_DEFAULTS.surface_opacity,
12
+ cell_edge_color: CELL_DEFAULTS.color,
13
+ cell_surface_color: CELL_DEFAULTS.color,
14
+ cell_line_width: CELL_DEFAULTS.line_width,
15
+ show_vectors: true,
16
+ }), controls_open = $bindable(false), background_color = $bindable(`#ffffff`), background_opacity = $bindable(0.1), reveal_buttons = 500, fullscreen = false, wrapper = $bindable(undefined), width = $bindable(0), height = $bindable(0), reset_text = `Reset camera`, color_scheme = $bindable(`Vesta`), hovered = $bindable(false), dragover = $bindable(false), allow_file_drop = true, tips_modal = $bindable(undefined), enable_tips = true, save_json_btn_text = `⬇ Save as JSON`, save_png_btn_text = `✎ Save as PNG`, save_xyz_btn_text = `📄 Save as XYZ`, png_dpi = $bindable(150), show_site_labels = $bindable(false), show_image_atoms = $bindable(true), show_full_controls = $bindable(false), tips_icon, fullscreen_toggle = true, bottom_left, on_file_drop, max_text_size = 5 * 1024 * 1024, // 5 MB default
17
+ ...rest } = $props();
18
+ // Ensure scene_props always has some defaults merged in
19
+ $effect.pre(() => {
20
+ scene_props = {
21
+ atom_radius: 1,
22
+ show_atoms: true,
23
+ auto_rotate: 0,
24
+ bond_thickness: BOND_DEFAULTS.thickness,
25
+ ...scene_props,
26
+ };
27
+ });
28
+ $effect.pre(() => {
29
+ colors.element = element_color_schemes[color_scheme];
30
+ });
31
+ let visible_buttons = $derived(reveal_buttons == true ||
32
+ (typeof reveal_buttons == `number` && reveal_buttons < width));
33
+ // only updates when structure or show_image_atoms change
34
+ let scene_structure = $derived(show_image_atoms && structure && `lattice` in structure
35
+ ? get_pbc_image_sites(structure)
36
+ : structure);
37
+ // Track if camera has ever been moved from initial position
38
+ let camera_has_moved = $state(false);
39
+ let camera_is_moving = $state(false);
40
+ // Reset tracking when structure changes
41
+ $effect(() => {
42
+ if (structure)
43
+ camera_has_moved = false;
44
+ });
45
+ // Set camera_has_moved to true when camera starts moving
46
+ $effect(() => {
47
+ if (camera_is_moving)
48
+ camera_has_moved = true;
49
+ });
50
+ function reset_camera() {
51
+ // Reset camera position to trigger automatic positioning
52
+ scene_props.camera_position = [0, 0, 0];
53
+ camera_has_moved = false;
54
+ }
55
+ async function handle_file_drop(event) {
56
+ event.preventDefault();
57
+ dragover = false;
58
+ if (!allow_file_drop)
59
+ return;
60
+ // Check for our custom internal file format first
61
+ const internal_data = event.dataTransfer?.getData(`application/x-matterviz-file`);
62
+ if (internal_data) {
63
+ try {
64
+ const file_info = JSON.parse(internal_data);
65
+ if (file_info.content && file_info.content.length > max_text_size) {
66
+ console.warn(`Internal file data too large: ${file_info.content.length} bytes`);
67
+ return;
68
+ }
69
+ try {
70
+ on_file_drop?.(file_info.content, file_info.name);
71
+ }
72
+ catch (error) {
73
+ console.error(`Failed to process internal file data:`, error);
74
+ }
75
+ return;
76
+ }
77
+ catch (error) {
78
+ console.warn(`Failed to parse internal file data:`, error);
79
+ }
80
+ }
81
+ // Check for plain text data (fallback)
82
+ const text_data = event.dataTransfer?.getData(`text/plain`);
83
+ if (text_data) {
84
+ if (text_data.length > max_text_size) {
85
+ console.warn(`Text data too large: ${text_data.length} bytes (max: ${max_text_size})`);
86
+ return;
87
+ }
88
+ try {
89
+ on_file_drop?.(text_data, `structure.json`);
90
+ }
91
+ catch (error) {
92
+ console.error(`Failed to process text data:`, error);
93
+ }
94
+ return;
95
+ }
96
+ // Handle actual file drops from file system
97
+ const file = event.dataTransfer?.files[0];
98
+ if (!file)
99
+ return;
100
+ try {
101
+ const { content, filename } = await decompress_file(file);
102
+ if (content)
103
+ on_file_drop?.(content, filename);
104
+ }
105
+ catch (error) {
106
+ console.error(`Failed to read file:`, error);
107
+ }
108
+ }
109
+ export function toggle_fullscreen() {
110
+ if (!document.fullscreenElement && wrapper) {
111
+ wrapper.requestFullscreen().catch(console.error);
112
+ }
113
+ else {
114
+ document.exitFullscreen();
115
+ }
116
+ }
117
+ // set --struct-bg to background_color
118
+ $effect(() => {
119
+ if (typeof window !== `undefined` && wrapper && background_color) {
120
+ // Convert opacity (0-1) to hex alpha value (00-FF)
121
+ const alpha_hex = Math.round(background_opacity * 255)
122
+ .toString(16)
123
+ .padStart(2, `0`);
124
+ wrapper.style.setProperty(`--struct-bg`, `${background_color}${alpha_hex}`);
125
+ }
126
+ });
127
+ // react to changes in the 'fullscreen' property
128
+ $effect(() => {
129
+ if (typeof window !== `undefined`) {
130
+ // react to changes in the 'fullscreen' property
131
+ if (fullscreen && !document.fullscreenElement && wrapper) {
132
+ wrapper.requestFullscreen().catch(console.error);
133
+ }
134
+ else if (!fullscreen && document.fullscreenElement) {
135
+ document.exitFullscreen();
136
+ }
137
+ }
138
+ });
139
+ </script>
140
+
141
+ {#if structure?.sites}
142
+ <div
143
+ class="structure"
144
+ class:dragover
145
+ role="region"
146
+ bind:this={wrapper}
147
+ bind:clientWidth={width}
148
+ bind:clientHeight={height}
149
+ onmouseenter={() => (hovered = true)}
150
+ onmouseleave={() => (hovered = false)}
151
+ ondrop={handle_file_drop}
152
+ ondragover={(event) => {
153
+ event.preventDefault()
154
+ if (!allow_file_drop) return
155
+ dragover = true
156
+ }}
157
+ ondragleave={(event) => {
158
+ event.preventDefault()
159
+ dragover = false
160
+ }}
161
+ {...rest}
162
+ >
163
+ <section class:visible={visible_buttons}>
164
+ {#if camera_has_moved}
165
+ <button class="reset-camera" onclick={reset_camera} title={reset_text}>
166
+ <!-- Target/Focus icon for reset camera -->
167
+ <Icon icon="Reset" />
168
+ </button>
169
+ {/if}
170
+ {#if enable_tips}
171
+ <button class="info-icon" onclick={() => tips_modal?.showModal()}>
172
+ {#if tips_icon}{@render tips_icon()}{:else}<Icon icon="Info" />{/if}
173
+ </button>
174
+ {/if}
175
+ <button
176
+ onclick={toggle_fullscreen}
177
+ class="fullscreen-toggle"
178
+ title="Toggle fullscreen"
179
+ >
180
+ {#if typeof fullscreen_toggle === `function`}
181
+ {@render fullscreen_toggle()}
182
+ {:else if fullscreen_toggle}
183
+ <Icon icon="Fullscreen" style="transform: scale(0.9)" />
184
+ {/if}
185
+ </button>
186
+
187
+ <StructureControls
188
+ bind:controls_open
189
+ bind:scene_props
190
+ bind:lattice_props
191
+ bind:show_image_atoms
192
+ bind:show_site_labels
193
+ bind:show_full_controls
194
+ bind:background_color
195
+ bind:background_opacity
196
+ bind:color_scheme
197
+ bind:png_dpi
198
+ {structure}
199
+ {wrapper}
200
+ {save_json_btn_text}
201
+ {save_png_btn_text}
202
+ {save_xyz_btn_text}
203
+ />
204
+ </section>
205
+
206
+ <StructureLegend elements={get_elem_amounts(structure)} bind:tips_modal />
207
+
208
+ <Canvas
209
+ createRenderer={(canvas) => {
210
+ const renderer = new WebGLRenderer({
211
+ canvas,
212
+ preserveDrawingBuffer: true,
213
+ antialias: true,
214
+ alpha: true,
215
+ }) // Store renderer reference for high-res export
216
+ ;(canvas as exports.CanvasWithRenderer).__customRenderer = renderer
217
+ return renderer
218
+ }}
219
+ >
220
+ <StructureScene
221
+ structure={scene_structure}
222
+ {...scene_props}
223
+ {show_site_labels}
224
+ {lattice_props}
225
+ bind:camera_is_moving
226
+ />
227
+ </Canvas>
228
+
229
+ <div class="bottom-left">
230
+ {@render bottom_left?.({ structure })}
231
+ </div>
232
+ </div>
233
+ {:else if structure}
234
+ <p class="warn">No sites found in structure</p>
235
+ {:else}
236
+ <p class="warn">No structure provided</p>
237
+ {/if}
238
+
239
+ <style>
240
+ .structure {
241
+ position: relative;
242
+ container-type: size;
243
+ height: var(--struct-height, 500px);
244
+ width: var(--struct-width, 100%);
245
+ max-width: var(--struct-max-width, 100%);
246
+ min-width: var(--struct-min-width, 300px);
247
+ border-radius: var(--struct-border-radius, 3pt);
248
+ background: var(--struct-bg, rgba(255, 255, 255, 0.1));
249
+ --struct-controls-transition-duration: 0.3s;
250
+ overflow: var(--struct-overflow, visible);
251
+ color: var(--struct-text-color);
252
+ }
253
+ .structure:fullscreen :global(canvas) {
254
+ height: 100vh !important;
255
+ width: 100vw !important;
256
+ }
257
+ .structure.dragover {
258
+ background: var(--struct-dragover-bg, rgba(0, 0, 0, 0.7));
259
+ }
260
+ div.bottom-left {
261
+ position: absolute;
262
+ bottom: 0;
263
+ left: 0;
264
+ font-size: var(--struct-bottom-left-font-size, 1.2em);
265
+ padding: var(--struct-bottom-left-padding, 1pt 5pt);
266
+ }
267
+ button {
268
+ background-color: transparent;
269
+ }
270
+ button:hover {
271
+ background-color: transparent !important;
272
+ }
273
+ section {
274
+ position: absolute;
275
+ display: flex;
276
+ justify-content: end;
277
+ top: var(--struct-buttons-top, 1ex);
278
+ right: var(--struct-buttons-right, 1ex);
279
+ gap: var(--struct-buttons-gap, 3pt);
280
+ z-index: 2;
281
+ }
282
+ section button {
283
+ pointer-events: auto;
284
+ font-size: 1em;
285
+ }
286
+ section :global(.controls-toggle) {
287
+ background-color: transparent;
288
+ }
289
+ section :global(.controls-toggle):hover {
290
+ background-color: transparent !important;
291
+ }
292
+ .structure :global(canvas) {
293
+ pointer-events: auto;
294
+ }
295
+ p.warn {
296
+ text-align: center;
297
+ }
298
+ </style>
@@ -0,0 +1,28 @@
1
+ import type { AnyStructure } from '..';
2
+ import type { Snippet } from 'svelte';
3
+ import type { Props as ControlProps } from './StructureControls.svelte';
4
+ interface Props extends ControlProps {
5
+ reveal_buttons?: boolean | number;
6
+ fullscreen?: boolean;
7
+ width?: number;
8
+ height?: number;
9
+ reset_text?: string;
10
+ hovered?: boolean;
11
+ dragover?: boolean;
12
+ allow_file_drop?: boolean;
13
+ tips_modal?: HTMLDialogElement | undefined;
14
+ enable_tips?: boolean;
15
+ tips_icon?: Snippet<[]>;
16
+ fullscreen_toggle?: Snippet<[]> | boolean;
17
+ bottom_left?: Snippet<[{
18
+ structure: AnyStructure;
19
+ }]>;
20
+ on_file_drop?: (content: string, filename: string) => void;
21
+ max_text_size?: number;
22
+ [key: string]: unknown;
23
+ }
24
+ declare const Structure: import("svelte").Component<Props, {
25
+ toggle_fullscreen: () => void;
26
+ }, "height" | "width" | "dragover" | "color_scheme" | "structure" | "controls_open" | "scene_props" | "lattice_props" | "show_image_atoms" | "show_site_labels" | "show_full_controls" | "background_color" | "background_opacity" | "wrapper" | "png_dpi" | "hovered" | "tips_modal">;
27
+ type Structure = ReturnType<typeof Structure>;
28
+ export default Structure;
@@ -0,0 +1,26 @@
1
+ <script lang="ts">import { format_num, InfoCard } from '..';
2
+ import { density, electro_neg_formula } from './index';
3
+ let { structure, title = `Structure`, ...rest } = $props();
4
+ let { volume, a, b, c, alpha, beta, gamma } = $derived(structure?.lattice ?? {});
5
+ </script>
6
+
7
+ <InfoCard
8
+ data={[
9
+ { title: `Formula`, value: electro_neg_formula(structure) },
10
+ { title: `Number of atoms`, value: structure?.sites?.length, fmt: `.0f` },
11
+ { title: `Volume`, value: volume, unit: `ų` },
12
+ { title: `Density`, value: density(structure), unit: `g/cm³` },
13
+ {
14
+ title: `Lattice lengths a, b, c`,
15
+ value: [a, b, c].map(format_num).join(`, `),
16
+ unit: `Å`,
17
+ },
18
+ {
19
+ title: `Lattice angles α, β, γ`,
20
+ value: [alpha, beta, gamma].map(format_num).join(`°, `) + `°`,
21
+ },
22
+ // { title: 'Charge', value: structure?.charge },
23
+ ]}
24
+ {...rest}
25
+ {title}
26
+ />
@@ -0,0 +1,9 @@
1
+ import { type PymatgenStructure } from './index';
2
+ interface Props {
3
+ structure: PymatgenStructure;
4
+ title?: string;
5
+ [key: string]: unknown;
6
+ }
7
+ declare const StructureCard: import("svelte").Component<Props, {}, "">;
8
+ type StructureCard = ReturnType<typeof StructureCard>;
9
+ export default StructureCard;