matterviz 0.1.12 → 0.1.13

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 (195) hide show
  1. package/dist/ContextMenu.svelte +1 -1
  2. package/dist/ContextMenu.svelte.d.ts +3 -3
  3. package/dist/DraggablePane.svelte +10 -5
  4. package/dist/DraggablePane.svelte.d.ts +3 -3
  5. package/dist/FilePicker.svelte.d.ts +3 -3
  6. package/dist/Icon.svelte.d.ts +3 -3
  7. package/dist/Nav.svelte +76 -31
  8. package/dist/SettingsSection.svelte.d.ts +3 -3
  9. package/dist/Spinner.svelte +18 -29
  10. package/dist/Spinner.svelte.d.ts +3 -3
  11. package/dist/app.css +4 -0
  12. package/dist/bands/Bands.svelte +216 -0
  13. package/dist/bands/Bands.svelte.d.ts +16 -0
  14. package/dist/bands/BandsAndDos.svelte +26 -0
  15. package/dist/bands/BandsAndDos.svelte.d.ts +16 -0
  16. package/dist/bands/Dos.svelte +124 -0
  17. package/dist/bands/Dos.svelte.d.ts +18 -0
  18. package/dist/bands/helpers.d.ts +11 -0
  19. package/dist/bands/helpers.js +195 -0
  20. package/dist/bands/index.d.ts +5 -0
  21. package/dist/bands/index.js +6 -0
  22. package/dist/bands/types.d.ts +54 -0
  23. package/dist/bands/types.js +2 -0
  24. package/dist/brillouin/BrillouinZone.svelte +373 -0
  25. package/dist/brillouin/BrillouinZone.svelte.d.ts +57 -0
  26. package/dist/brillouin/BrillouinZoneControls.svelte +88 -0
  27. package/dist/brillouin/BrillouinZoneControls.svelte.d.ts +14 -0
  28. package/dist/brillouin/BrillouinZoneExportPane.svelte +129 -0
  29. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +15 -0
  30. package/dist/brillouin/BrillouinZoneInfoPane.svelte +115 -0
  31. package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +13 -0
  32. package/dist/brillouin/BrillouinZoneScene.svelte +180 -0
  33. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +34 -0
  34. package/dist/brillouin/compute.d.ts +7 -0
  35. package/dist/brillouin/compute.js +215 -0
  36. package/dist/brillouin/index.d.ts +7 -0
  37. package/dist/brillouin/index.js +7 -0
  38. package/dist/brillouin/types.d.ts +23 -0
  39. package/dist/brillouin/types.js +1 -0
  40. package/dist/composition/BarChart.svelte.d.ts +3 -3
  41. package/dist/composition/BubbleChart.svelte +7 -6
  42. package/dist/composition/BubbleChart.svelte.d.ts +3 -3
  43. package/dist/composition/Composition.svelte.d.ts +3 -3
  44. package/dist/composition/PieChart.svelte +1 -1
  45. package/dist/composition/PieChart.svelte.d.ts +3 -3
  46. package/dist/composition/parse.d.ts +3 -3
  47. package/dist/coordination/CoordinationBarPlot.svelte +241 -0
  48. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +29 -0
  49. package/dist/coordination/calc-coordination.d.ts +15 -0
  50. package/dist/coordination/calc-coordination.js +66 -0
  51. package/dist/coordination/index.d.ts +8 -0
  52. package/dist/coordination/index.js +7 -0
  53. package/dist/element/BohrAtom.svelte.d.ts +3 -3
  54. package/dist/element/ElementHeading.svelte.d.ts +3 -3
  55. package/dist/element/ElementPhoto.svelte.d.ts +3 -3
  56. package/dist/element/ElementStats.svelte.d.ts +3 -3
  57. package/dist/element/ElementTile.svelte +1 -1
  58. package/dist/element/ElementTile.svelte.d.ts +4 -4
  59. package/dist/element/Nucleus.svelte.d.ts +3 -3
  60. package/dist/icons.d.ts +4 -0
  61. package/dist/icons.js +4 -0
  62. package/dist/index.d.ts +5 -1
  63. package/dist/index.js +5 -1
  64. package/dist/io/export.d.ts +1 -6
  65. package/dist/io/export.js +12 -12
  66. package/dist/labels.d.ts +19 -0
  67. package/dist/labels.js +59 -0
  68. package/dist/math.d.ts +7 -2
  69. package/dist/math.js +24 -12
  70. package/dist/periodic-table/PeriodicTable.svelte.d.ts +3 -3
  71. package/dist/periodic-table/PeriodicTableControls.svelte.d.ts +3 -3
  72. package/dist/periodic-table/PropertySelect.svelte +1 -1
  73. package/dist/periodic-table/PropertySelect.svelte.d.ts +5 -4
  74. package/dist/periodic-table/TableInset.svelte.d.ts +3 -3
  75. package/dist/phase-diagram/PhaseDiagram2D.svelte +44 -107
  76. package/dist/phase-diagram/PhaseDiagram2D.svelte.d.ts +7 -27
  77. package/dist/phase-diagram/PhaseDiagram3D.svelte +67 -170
  78. package/dist/phase-diagram/PhaseDiagram3D.svelte.d.ts +7 -31
  79. package/dist/phase-diagram/PhaseDiagram4D.svelte +87 -145
  80. package/dist/phase-diagram/PhaseDiagram4D.svelte.d.ts +5 -28
  81. package/dist/phase-diagram/PhaseDiagramControls.svelte +9 -9
  82. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +6 -6
  83. package/dist/phase-diagram/PhaseDiagramInfoPane.svelte +4 -4
  84. package/dist/phase-diagram/PhaseDiagramInfoPane.svelte.d.ts +5 -5
  85. package/dist/phase-diagram/PhaseDiagramStats.svelte +9 -16
  86. package/dist/phase-diagram/PhaseDiagramStats.svelte.d.ts +3 -3
  87. package/dist/phase-diagram/StructurePopup.svelte.d.ts +3 -3
  88. package/dist/phase-diagram/barycentric-coords.js +13 -24
  89. package/dist/phase-diagram/helpers.d.ts +12 -14
  90. package/dist/phase-diagram/helpers.js +70 -23
  91. package/dist/phase-diagram/index.d.ts +58 -0
  92. package/dist/phase-diagram/index.js +44 -0
  93. package/dist/phase-diagram/thermodynamics.js +2 -3
  94. package/dist/phase-diagram/types.d.ts +3 -8
  95. package/dist/plot/BarPlot.svelte +474 -240
  96. package/dist/plot/BarPlot.svelte.d.ts +16 -37
  97. package/dist/plot/BarPlotControls.svelte +33 -201
  98. package/dist/plot/BarPlotControls.svelte.d.ts +11 -22
  99. package/dist/plot/ColorBar.svelte.d.ts +3 -3
  100. package/dist/plot/ColorScaleSelect.svelte +1 -1
  101. package/dist/plot/ColorScaleSelect.svelte.d.ts +3 -3
  102. package/dist/plot/ElementScatter.svelte +5 -5
  103. package/dist/plot/ElementScatter.svelte.d.ts +8 -8
  104. package/dist/plot/Histogram.svelte +318 -170
  105. package/dist/plot/Histogram.svelte.d.ts +21 -41
  106. package/dist/plot/HistogramControls.svelte +115 -347
  107. package/dist/plot/HistogramControls.svelte.d.ts +13 -28
  108. package/dist/plot/Line.svelte +1 -1
  109. package/dist/plot/Line.svelte.d.ts +4 -4
  110. package/dist/plot/PlotControls.svelte +280 -0
  111. package/dist/plot/PlotControls.svelte.d.ts +4 -0
  112. package/dist/plot/PlotLegend.svelte +9 -24
  113. package/dist/plot/PlotLegend.svelte.d.ts +3 -3
  114. package/dist/plot/ScatterPlot.svelte +536 -571
  115. package/dist/plot/ScatterPlot.svelte.d.ts +18 -90
  116. package/dist/plot/ScatterPlotControls.svelte +108 -316
  117. package/dist/plot/ScatterPlotControls.svelte.d.ts +11 -38
  118. package/dist/plot/ScatterPoint.svelte +1 -1
  119. package/dist/plot/ScatterPoint.svelte.d.ts +5 -5
  120. package/dist/plot/SpacegroupBarPlot.svelte +235 -0
  121. package/dist/plot/SpacegroupBarPlot.svelte.d.ts +9 -0
  122. package/dist/plot/data-transform.d.ts +2 -2
  123. package/dist/plot/data-transform.js +1 -5
  124. package/dist/plot/index.d.ts +12 -200
  125. package/dist/plot/index.js +14 -6
  126. package/dist/plot/layout.d.ts +10 -16
  127. package/dist/plot/layout.js +40 -25
  128. package/dist/plot/scales.d.ts +7 -8
  129. package/dist/plot/scales.js +22 -7
  130. package/dist/plot/types.d.ts +303 -0
  131. package/dist/plot/types.js +1 -0
  132. package/dist/rdf/RdfPlot.svelte +156 -0
  133. package/dist/rdf/RdfPlot.svelte.d.ts +20 -0
  134. package/dist/rdf/calc-rdf.d.ts +4 -0
  135. package/dist/rdf/calc-rdf.js +107 -0
  136. package/dist/rdf/index.d.ts +22 -0
  137. package/dist/rdf/index.js +2 -0
  138. package/dist/settings.d.ts +86 -75
  139. package/dist/settings.js +257 -155
  140. package/dist/structure/Bond.svelte +126 -110
  141. package/dist/structure/Bond.svelte.d.ts +7 -20
  142. package/dist/structure/CanvasTooltip.svelte.d.ts +4 -4
  143. package/dist/structure/Cylinder.svelte +29 -0
  144. package/dist/structure/Cylinder.svelte.d.ts +10 -0
  145. package/dist/structure/Lattice.svelte +1 -2
  146. package/dist/structure/Lattice.svelte.d.ts +3 -3
  147. package/dist/structure/Structure.svelte +23 -8
  148. package/dist/structure/Structure.svelte.d.ts +10 -7
  149. package/dist/structure/StructureControls.svelte +25 -134
  150. package/dist/structure/StructureControls.svelte.d.ts +4 -9
  151. package/dist/structure/StructureExportPane.svelte +188 -0
  152. package/dist/structure/StructureExportPane.svelte.d.ts +17 -0
  153. package/dist/structure/StructureInfoPane.svelte.d.ts +3 -3
  154. package/dist/structure/StructureLegend.svelte +91 -27
  155. package/dist/structure/StructureLegend.svelte.d.ts +5 -4
  156. package/dist/structure/StructureScene.svelte +93 -100
  157. package/dist/structure/StructureScene.svelte.d.ts +15 -12
  158. package/dist/structure/Vector.svelte +19 -25
  159. package/dist/structure/Vector.svelte.d.ts +3 -3
  160. package/dist/structure/bonding.d.ts +16 -14
  161. package/dist/structure/bonding.js +207 -216
  162. package/dist/structure/export.d.ts +3 -0
  163. package/dist/structure/export.js +120 -0
  164. package/dist/structure/index.d.ts +15 -1
  165. package/dist/structure/index.js +2 -0
  166. package/dist/symmetry/WyckoffTable.svelte.d.ts +3 -3
  167. package/dist/symmetry/index.d.ts +1 -0
  168. package/dist/symmetry/index.js +1 -0
  169. package/dist/symmetry/spacegroups.d.ts +9 -0
  170. package/dist/symmetry/spacegroups.js +397 -0
  171. package/dist/theme/ThemeControl.svelte.d.ts +3 -3
  172. package/dist/theme/themes.js +2 -16
  173. package/dist/trajectory/Trajectory.svelte +52 -78
  174. package/dist/trajectory/Trajectory.svelte.d.ts +5 -5
  175. package/dist/trajectory/TrajectoryError.svelte.d.ts +3 -3
  176. package/dist/trajectory/TrajectoryExportPane.svelte +1 -1
  177. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +5 -5
  178. package/dist/trajectory/TrajectoryInfoPane.svelte +1 -1
  179. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +3 -3
  180. package/dist/trajectory/extract.js +2 -1
  181. package/dist/trajectory/index.d.ts +1 -1
  182. package/dist/trajectory/index.js +1 -1
  183. package/dist/trajectory/parse.js +21 -14
  184. package/dist/utils.d.ts +1 -0
  185. package/dist/utils.js +17 -0
  186. package/dist/xrd/XrdPlot.svelte +14 -11
  187. package/dist/xrd/XrdPlot.svelte.d.ts +6 -5
  188. package/package.json +20 -12
  189. package/readme.md +1 -1
  190. package/dist/material/MaterialCard.svelte +0 -123
  191. package/dist/material/MaterialCard.svelte.d.ts +0 -9
  192. package/dist/material/index.d.ts +0 -1
  193. package/dist/material/index.js +0 -1
  194. package/dist/plot/formatting.d.ts +0 -7
  195. package/dist/plot/formatting.js +0 -53
@@ -0,0 +1,373 @@
1
+ <script lang="ts">import { Icon, Spinner, toggle_fullscreen } from '..';
2
+ import { decompress_file, handle_url_drop, load_from_url } from '../io';
3
+ import { DEFAULTS } from '../settings';
4
+ import { parse_any_structure } from '../structure/parse';
5
+ import { Canvas } from '@threlte/core';
6
+ import { untrack } from 'svelte';
7
+ import { tooltip } from 'svelte-multiselect/attachments';
8
+ import BrillouinZoneControls from './BrillouinZoneControls.svelte';
9
+ import BrillouinZoneExportPane from './BrillouinZoneExportPane.svelte';
10
+ import BrillouinZoneInfoPane from './BrillouinZoneInfoPane.svelte';
11
+ import BrillouinZoneScene from './BrillouinZoneScene.svelte';
12
+ import { compute_brillouin_zone, reciprocal_lattice } from './compute';
13
+ let { structure = $bindable(undefined), bz_order = $bindable(1), bz_data = $bindable(undefined), controls_open = $bindable(false), info_pane_open = $bindable(false), surface_color = $bindable(`#4488ff`), surface_opacity = $bindable(0.3), edge_color = $bindable(`#000000`), edge_width = $bindable(0.05), show_vectors = $bindable(true), vector_scale = $bindable(1.0), camera_projection = $bindable(`perspective`), show_controls = 0, fullscreen = false, wrapper = $bindable(undefined), width = $bindable(0), height = $bindable(0), hovered = $bindable(false), dragover = $bindable(false), allow_file_drop = true, png_dpi = $bindable(150), fullscreen_toggle = DEFAULTS.structure.fullscreen_toggle, data_url, structure_string, on_file_drop, spinner_props = {}, loading = $bindable(false), error_msg = $bindable(undefined), children, on_file_load, on_error, on_fullscreen_change, ...rest } = $props();
14
+ let scene = $state(undefined);
15
+ let camera = $state(undefined);
16
+ let export_pane_open = $state(false);
17
+ let current_filename = $state(undefined);
18
+ let visible_buttons = $derived(show_controls === true ||
19
+ (typeof show_controls === `number` && width > show_controls));
20
+ // Parse and load structure from content
21
+ function parse_structure(content, filename) {
22
+ const text = content instanceof ArrayBuffer
23
+ ? new TextDecoder().decode(content)
24
+ : content;
25
+ const parsed = parse_any_structure(text, filename);
26
+ if (!parsed)
27
+ throw new Error(`Failed to parse structure from ${filename}`);
28
+ structure = parsed;
29
+ current_filename = filename;
30
+ const file_size = new Blob([content]).size;
31
+ on_file_load?.({ structure, bz_data, bz_order, filename, file_size });
32
+ }
33
+ // Load with error handling
34
+ function safe_parse(content, filename) {
35
+ try {
36
+ parse_structure(content, filename);
37
+ }
38
+ catch (err) {
39
+ error_msg = `Failed to parse ${filename}: ${err instanceof Error ? err.message : err}`;
40
+ on_error?.({ error_msg, filename });
41
+ }
42
+ }
43
+ // Compute BZ when structure/order changes
44
+ $effect(() => {
45
+ if (!structure || !(`lattice` in structure) || !structure.lattice) {
46
+ bz_data = undefined;
47
+ return;
48
+ }
49
+ try {
50
+ const k_lattice = reciprocal_lattice(structure.lattice.matrix);
51
+ bz_data = compute_brillouin_zone(k_lattice, bz_order);
52
+ }
53
+ catch (err) {
54
+ const msg = err instanceof Error ? err.message : String(err);
55
+ error_msg = `BZ computation failed: ${msg}`;
56
+ bz_data = undefined;
57
+ untrack(() => on_error?.({ error_msg, structure, bz_order }));
58
+ }
59
+ });
60
+ // Load structure from URL or string
61
+ $effect(() => {
62
+ const handle_error = (err, source) => {
63
+ error_msg = err instanceof Error ? err.message : String(err);
64
+ on_error?.({ error_msg, filename: source });
65
+ };
66
+ if (data_url && !structure) {
67
+ loading = true;
68
+ error_msg = undefined;
69
+ load_from_url(data_url, (content, filename) => on_file_drop
70
+ ? on_file_drop(content, filename)
71
+ : safe_parse(content, filename))
72
+ .catch((err) => handle_error(err, data_url))
73
+ .finally(() => (loading = false));
74
+ }
75
+ else if (structure_string && !data_url) {
76
+ loading = true;
77
+ error_msg = undefined;
78
+ try {
79
+ safe_parse(structure_string, `string`);
80
+ }
81
+ catch (err) {
82
+ handle_error(err, `string`);
83
+ }
84
+ finally {
85
+ loading = false;
86
+ }
87
+ }
88
+ });
89
+ async function handle_file_drop(event) {
90
+ event.preventDefault();
91
+ dragover = false;
92
+ if (!allow_file_drop)
93
+ return;
94
+ loading = true;
95
+ error_msg = undefined;
96
+ try {
97
+ const handler = on_file_drop || safe_parse;
98
+ const handled = await handle_url_drop(event, handler).catch(() => false);
99
+ if (handled)
100
+ return;
101
+ const file = event.dataTransfer?.files[0];
102
+ if (file) {
103
+ const { content, filename } = await decompress_file(file);
104
+ if (content)
105
+ handler(content, filename);
106
+ }
107
+ }
108
+ catch (err) {
109
+ error_msg = `File drop failed: ${err}`;
110
+ on_error?.({ error_msg });
111
+ }
112
+ finally {
113
+ loading = false;
114
+ }
115
+ }
116
+ function onkeydown(event) {
117
+ const target = event.target;
118
+ if (target.tagName === `INPUT` || target.tagName === `TEXTAREA`)
119
+ return;
120
+ if (event.key === `f` && fullscreen_toggle)
121
+ toggle_fullscreen(wrapper);
122
+ else if (event.key === `i`)
123
+ info_pane_open = !info_pane_open;
124
+ else if (event.key === `Escape`) {
125
+ if (info_pane_open)
126
+ info_pane_open = false;
127
+ else
128
+ controls_open = false;
129
+ }
130
+ }
131
+ $effect(() => {
132
+ if (typeof window === `undefined`)
133
+ return;
134
+ const fs_el = document.fullscreenElement;
135
+ if (fullscreen && fs_el !== wrapper && wrapper) {
136
+ wrapper.requestFullscreen().catch(console.error);
137
+ }
138
+ else if (!fullscreen && fs_el === wrapper)
139
+ document.exitFullscreen();
140
+ });
141
+ </script>
142
+
143
+ <svelte:document
144
+ onfullscreenchange={() => {
145
+ fullscreen = Boolean(document.fullscreenElement)
146
+ on_fullscreen_change?.({ structure, bz_data, bz_order, is_fullscreen: fullscreen })
147
+ }}
148
+ />
149
+
150
+ <div
151
+ class:dragover
152
+ class:active={info_pane_open || controls_open || export_pane_open}
153
+ role="region"
154
+ aria-label="Brillouin zone viewer"
155
+ bind:this={wrapper}
156
+ bind:clientWidth={width}
157
+ bind:clientHeight={height}
158
+ onmouseenter={() => (hovered = true)}
159
+ onmouseleave={() => (hovered = false)}
160
+ ondrop={handle_file_drop}
161
+ ondragover={(event) => {
162
+ event.preventDefault()
163
+ if (!allow_file_drop) return
164
+ dragover = true
165
+ }}
166
+ ondragleave={(event) => {
167
+ event.preventDefault()
168
+ dragover = false
169
+ }}
170
+ {onkeydown}
171
+ {...rest}
172
+ class="brillouin-zone {rest.class ?? ``}"
173
+ >
174
+ {@render children?.({ structure, bz_data })}
175
+ {#if loading}
176
+ <Spinner text="Loading structure..." {...spinner_props} />
177
+ {:else if error_msg}
178
+ <div class="error-state">
179
+ <p class="error">{error_msg}</p>
180
+ <button onclick={() => (error_msg = undefined)}>Dismiss</button>
181
+ </div>
182
+ {:else if structure && `lattice` in structure}
183
+ <section class:visible={visible_buttons} class="control-buttons">
184
+ {#if visible_buttons}
185
+ {#if current_filename}
186
+ <span class="filename">{current_filename}</span>
187
+ {/if}
188
+
189
+ {#if fullscreen_toggle}
190
+ <button
191
+ type="button"
192
+ onclick={() => fullscreen_toggle && toggle_fullscreen(wrapper)}
193
+ title="{fullscreen ? `Exit` : `Enter`} fullscreen"
194
+ aria-pressed={fullscreen}
195
+ class="fullscreen-toggle"
196
+ style="padding: 0"
197
+ {@attach tooltip()}
198
+ >
199
+ {#if typeof fullscreen_toggle === `function`}
200
+ {@render fullscreen_toggle()}
201
+ {:else}
202
+ <Icon icon="{fullscreen ? `Exit` : ``}Fullscreen" />
203
+ {/if}
204
+ </button>
205
+ {/if}
206
+
207
+ <BrillouinZoneInfoPane {structure} {bz_data} bind:pane_open={info_pane_open} />
208
+
209
+ <BrillouinZoneExportPane
210
+ bind:export_pane_open
211
+ {bz_data}
212
+ {wrapper}
213
+ {scene}
214
+ {camera}
215
+ bind:png_dpi
216
+ filename={current_filename || `brillouin-zone`}
217
+ />
218
+
219
+ <BrillouinZoneControls
220
+ bind:controls_open
221
+ bind:bz_order
222
+ bind:surface_color
223
+ bind:surface_opacity
224
+ bind:edge_color
225
+ bind:edge_width
226
+ bind:show_vectors
227
+ bind:vector_scale
228
+ bind:camera_projection
229
+ />
230
+ {/if}
231
+ </section>
232
+
233
+ {#if typeof WebGLRenderingContext !== `undefined`}
234
+ <div style="overflow: hidden; height: 100%">
235
+ <Canvas>
236
+ <BrillouinZoneScene
237
+ {bz_data}
238
+ {surface_color}
239
+ {surface_opacity}
240
+ {edge_color}
241
+ {edge_width}
242
+ {show_vectors}
243
+ {vector_scale}
244
+ {camera_projection}
245
+ bind:scene
246
+ bind:camera
247
+ />
248
+ </Canvas>
249
+ </div>
250
+ {/if}
251
+ {:else if structure}
252
+ <p class="warn">Structure must have a lattice to compute Brillouin zone</p>
253
+ {:else}
254
+ <div class="empty-state">
255
+ <h3>Drop Structure File</h3>
256
+ <p>
257
+ Supports CIF, POSCAR, JSON, (ext)XYZ, (+ .gz)
258
+ </p>
259
+ </div>
260
+ {/if}
261
+ </div>
262
+
263
+ <style>
264
+ .brillouin-zone {
265
+ position: relative;
266
+ container-type: size;
267
+ height: var(--bz-height, 500px);
268
+ width: var(--bz-width, 100%);
269
+ max-width: var(--bz-max-width, 100%);
270
+ min-width: var(--bz-min-width, 300px);
271
+ border-radius: var(--bz-border-radius, 3pt);
272
+ background: var(--bz-bg, var(--surface-bg));
273
+ color: var(--bz-text-color, var(--text-color));
274
+ }
275
+ .brillouin-zone.active {
276
+ z-index: var(--bz-active-z-index, 2);
277
+ }
278
+ .brillouin-zone:fullscreen {
279
+ background: var(--bz-bg-fullscreen, var(--surface-bg));
280
+ }
281
+ .brillouin-zone:fullscreen :global(canvas) {
282
+ height: 100vh !important;
283
+ width: 100vw !important;
284
+ }
285
+ .brillouin-zone.dragover {
286
+ background: var(--bz-dragover-bg, var(--dragover-bg));
287
+ border: var(--bz-dragover-border, var(--dragover-border));
288
+ }
289
+ .brillouin-zone :global(canvas) {
290
+ user-select: none;
291
+ }
292
+ section.control-buttons {
293
+ position: absolute;
294
+ display: flex;
295
+ top: var(--bz-buttons-top, var(--ctrl-btn-top, 1ex));
296
+ right: var(--bz-buttons-right, var(--ctrl-btn-right, 1ex));
297
+ gap: clamp(6pt, 1cqmin, 9pt);
298
+ z-index: var(--bz-buttons-z-index, 100000000);
299
+ opacity: 0;
300
+ pointer-events: none;
301
+ transition: opacity 0.2s ease;
302
+ align-items: center;
303
+ }
304
+ section.control-buttons.visible {
305
+ opacity: 1;
306
+ pointer-events: auto;
307
+ }
308
+ section.control-buttons > :global(button) {
309
+ background-color: transparent;
310
+ display: flex;
311
+ padding: 0;
312
+ font-size: clamp(1em, 2cqmin, 2.5em);
313
+ }
314
+ section.control-buttons :global(button:hover) {
315
+ background-color: var(--pane-btn-bg-hover);
316
+ }
317
+ .filename {
318
+ font-family: monospace;
319
+ font-size: 0.9em;
320
+ background: var(--code-bg, rgba(0, 0, 0, 0.1));
321
+ padding: 3pt 6pt;
322
+ border-radius: 3pt;
323
+ max-width: 200px;
324
+ overflow: hidden;
325
+ text-overflow: ellipsis;
326
+ white-space: nowrap;
327
+ }
328
+ p.warn {
329
+ text-align: center;
330
+ padding: 2rem;
331
+ }
332
+ .error-state {
333
+ display: flex;
334
+ flex-direction: column;
335
+ align-items: center;
336
+ justify-content: center;
337
+ height: 100%;
338
+ padding: 2rem;
339
+ text-align: center;
340
+ box-sizing: border-box;
341
+ }
342
+ .error-state p {
343
+ color: var(--error-color, #ff6b6b);
344
+ margin: 0 0 1rem;
345
+ }
346
+ .error-state button {
347
+ padding: 0.5rem 1rem;
348
+ background: var(--error-color, #ff6b6b);
349
+ color: white;
350
+ border: none;
351
+ border-radius: 4px;
352
+ cursor: pointer;
353
+ }
354
+ .error-state button:hover {
355
+ background: var(--error-color-hover, #ff5252);
356
+ }
357
+ .empty-state {
358
+ display: flex;
359
+ flex-direction: column;
360
+ align-items: center;
361
+ justify-content: center;
362
+ height: 100%;
363
+ text-align: center;
364
+ }
365
+ .empty-state h3 {
366
+ margin: 0 0 0.5em;
367
+ font-size: 1.5em;
368
+ }
369
+ .empty-state p {
370
+ color: var(--text-color-muted);
371
+ margin: 0;
372
+ }
373
+ </style>
@@ -0,0 +1,57 @@
1
+ import { Spinner } from '..';
2
+ import { type CameraProjection } from '../settings';
3
+ import type { PymatgenStructure } from '../structure';
4
+ import type { ComponentProps, Snippet } from 'svelte';
5
+ import type { HTMLAttributes } from 'svelte/elements';
6
+ import type { BrillouinZoneData } from './types';
7
+ type BZHandlerData = {
8
+ structure?: PymatgenStructure;
9
+ bz_data?: BrillouinZoneData;
10
+ bz_order?: number;
11
+ filename?: string;
12
+ file_size?: number;
13
+ error_msg?: string;
14
+ is_fullscreen?: boolean;
15
+ };
16
+ type $$ComponentProps = {
17
+ structure?: PymatgenStructure;
18
+ bz_order?: number;
19
+ bz_data?: BrillouinZoneData;
20
+ controls_open?: boolean;
21
+ info_pane_open?: boolean;
22
+ surface_color?: string;
23
+ surface_opacity?: number;
24
+ edge_color?: string;
25
+ edge_width?: number;
26
+ show_vectors?: boolean;
27
+ vector_scale?: number;
28
+ camera_projection?: CameraProjection;
29
+ show_controls?: boolean | number;
30
+ fullscreen?: boolean;
31
+ width?: number;
32
+ height?: number;
33
+ wrapper?: HTMLDivElement;
34
+ png_dpi?: number;
35
+ hovered?: boolean;
36
+ dragover?: boolean;
37
+ allow_file_drop?: boolean;
38
+ fullscreen_toggle?: Snippet<[]> | boolean;
39
+ data_url?: string;
40
+ on_file_drop?: (content: string | ArrayBuffer, filename: string) => void;
41
+ spinner_props?: ComponentProps<typeof Spinner>;
42
+ loading?: boolean;
43
+ error_msg?: string;
44
+ structure_string?: string;
45
+ children?: Snippet<[
46
+ {
47
+ structure?: PymatgenStructure;
48
+ bz_data?: BrillouinZoneData;
49
+ }
50
+ ]>;
51
+ on_file_load?: (data: BZHandlerData) => void;
52
+ on_error?: (data: BZHandlerData) => void;
53
+ on_fullscreen_change?: (data: BZHandlerData) => void;
54
+ } & HTMLAttributes<HTMLDivElement>;
55
+ declare const BrillouinZone: import("svelte").Component<$$ComponentProps, {}, "height" | "width" | "loading" | "dragover" | "structure" | "camera_projection" | "info_pane_open" | "hovered" | "controls_open" | "wrapper" | "png_dpi" | "error_msg" | "bz_order" | "surface_color" | "surface_opacity" | "edge_color" | "edge_width" | "show_vectors" | "bz_data" | "vector_scale">;
56
+ type BrillouinZone = ReturnType<typeof BrillouinZone>;
57
+ export default BrillouinZone;
@@ -0,0 +1,88 @@
1
+ <script lang="ts">import { DraggablePane, SettingsSection } from '..';
2
+ let { controls_open = $bindable(false), bz_order = $bindable(1), surface_color = $bindable(`#4488ff`), surface_opacity = $bindable(0.3), edge_color = $bindable(`#000000`), edge_width = $bindable(0.05), show_vectors = $bindable(true), camera_projection = $bindable(`perspective`), } = $props();
3
+ </script>
4
+
5
+ <DraggablePane
6
+ bind:show={controls_open}
7
+ open_icon="Cross"
8
+ closed_icon="Settings"
9
+ pane_props={{ class: `bz-controls` }}
10
+ toggle_props={{ class: `controls-toggle`, title: `Brillouin zone controls` }}
11
+ >
12
+ <SettingsSection
13
+ title="Brillouin Zone Controls"
14
+ current_values={{ bz_order, show_vectors }}
15
+ on_reset={() => {
16
+ bz_order = 1
17
+ show_vectors = true
18
+ }}
19
+ style="display: flex; gap: 2ex; flex-wrap: wrap"
20
+ >
21
+ <label>
22
+ <span>Order:</span>
23
+ <select bind:value={bz_order}>
24
+ <option value={1}>1st BZ</option>
25
+ <option value={2}>2nd BZ</option>
26
+ <option value={3}>3rd BZ</option>
27
+ </select>
28
+ </label>
29
+ <label>
30
+ <span>Show Vectors:</span>
31
+ <input type="checkbox" bind:checked={show_vectors} />
32
+ </label>
33
+ </SettingsSection>
34
+
35
+ <SettingsSection
36
+ title="Surface"
37
+ current_values={{ surface_color, surface_opacity }}
38
+ on_reset={() => {
39
+ surface_color = `#4488ff`
40
+ surface_opacity = 0.3
41
+ }}
42
+ >
43
+ <label>
44
+ <span>Color:</span>
45
+ <input type="color" bind:value={surface_color} />
46
+ </label>
47
+ <label>
48
+ <span>Opacity:</span>
49
+ <input type="range" min="0" max="1" step="0.01" bind:value={surface_opacity} />
50
+ <span class="value">{surface_opacity.toFixed(2)}</span>
51
+ </label>
52
+ </SettingsSection>
53
+
54
+ <SettingsSection
55
+ title="Edges"
56
+ current_values={{ edge_color, edge_width }}
57
+ on_reset={() => {
58
+ edge_color = `#000000`
59
+ edge_width = 0.05
60
+ }}
61
+ >
62
+ <label>
63
+ <span>Color:</span>
64
+ <input type="color" bind:value={edge_color} />
65
+ </label>
66
+ <label>
67
+ <span>Width:</span>
68
+ <input type="range" min="0.01" max="0.2" step="0.01" bind:value={edge_width} />
69
+ <span class="value">{edge_width.toFixed(2)}</span>
70
+ </label>
71
+ </SettingsSection>
72
+
73
+ <SettingsSection
74
+ title="Camera"
75
+ current_values={{ camera_projection }}
76
+ on_reset={() => {
77
+ camera_projection = `perspective`
78
+ }}
79
+ >
80
+ <label>
81
+ <span>Projection:</span>
82
+ <select bind:value={camera_projection}>
83
+ <option value="perspective">Perspective</option>
84
+ <option value="orthographic">Orthographic</option>
85
+ </select>
86
+ </label>
87
+ </SettingsSection>
88
+ </DraggablePane>
@@ -0,0 +1,14 @@
1
+ import type { CameraProjection } from '../settings';
2
+ type $$ComponentProps = {
3
+ controls_open?: boolean;
4
+ bz_order?: number;
5
+ surface_color?: string;
6
+ surface_opacity?: number;
7
+ edge_color?: string;
8
+ edge_width?: number;
9
+ show_vectors?: boolean;
10
+ camera_projection?: CameraProjection;
11
+ };
12
+ declare const BrillouinZoneControls: import("svelte").Component<$$ComponentProps, {}, "camera_projection" | "controls_open" | "bz_order" | "surface_color" | "surface_opacity" | "edge_color" | "edge_width" | "show_vectors">;
13
+ type BrillouinZoneControls = ReturnType<typeof BrillouinZoneControls>;
14
+ export default BrillouinZoneControls;
@@ -0,0 +1,129 @@
1
+ <script lang="ts">import { DraggablePane } from '..';
2
+ import { export_canvas_as_png } from '../io/export';
3
+ import { tooltip } from 'svelte-multiselect/attachments';
4
+ let { export_pane_open = $bindable(false), bz_data, wrapper, scene, camera, filename = `brillouin-zone`, png_dpi = $bindable(150), ...rest } = $props();
5
+ let copy_status = $state(false);
6
+ const copy_confirm = `✅`;
7
+ function export_as_png() {
8
+ const canvas = wrapper?.querySelector(`canvas`);
9
+ if (!canvas || !scene || !camera)
10
+ return;
11
+ const dpi = Math.max(50, Math.min(600, Math.trunc(png_dpi)));
12
+ const png_name = `${filename}-${bz_data?.order ?? `1`}.png`;
13
+ export_canvas_as_png(canvas, png_name, dpi, scene, camera);
14
+ }
15
+ function export_as_json() {
16
+ const json_data = get_json_data();
17
+ if (!json_data || !bz_data)
18
+ return;
19
+ const blob = new Blob([JSON.stringify(json_data, null, 2)], {
20
+ type: `application/json`,
21
+ });
22
+ const url = URL.createObjectURL(blob);
23
+ const link = document.createElement(`a`);
24
+ link.href = url;
25
+ link.download = `${filename}-bz-order-${bz_data.order}.json`;
26
+ link.click();
27
+ URL.revokeObjectURL(url);
28
+ }
29
+ function get_json_data() {
30
+ if (!bz_data)
31
+ return null;
32
+ return {
33
+ order: bz_data.order,
34
+ volume: bz_data.volume,
35
+ vertices: bz_data.vertices,
36
+ faces: bz_data.faces,
37
+ edges: bz_data.edges,
38
+ reciprocal_lattice: bz_data.k_lattice,
39
+ };
40
+ }
41
+ async function copy_json() {
42
+ const json_data = get_json_data();
43
+ if (!json_data)
44
+ return;
45
+ await navigator.clipboard.writeText(JSON.stringify(json_data, null, 2));
46
+ copy_status = true;
47
+ setTimeout(() => {
48
+ copy_status = false;
49
+ }, 1000);
50
+ }
51
+ </script>
52
+
53
+ <DraggablePane
54
+ bind:show={export_pane_open}
55
+ open_icon="Cross"
56
+ closed_icon="Export"
57
+ pane_props={{ ...rest, class: `export-pane ${rest.class ?? ``}` }}
58
+ toggle_props={{
59
+ class: `bz-export-toggle`,
60
+ title: export_pane_open ? `` : `Export Brillouin zone`,
61
+ }}
62
+ >
63
+ <h4>Export as image</h4>
64
+ <label>
65
+ PNG
66
+ <button
67
+ type="button"
68
+ onclick={export_as_png}
69
+ disabled={!scene || !camera}
70
+ title="PNG ({png_dpi} DPI)"
71
+ >
72
+
73
+ </button>
74
+ &nbsp;(DPI: <input
75
+ type="number"
76
+ min={72}
77
+ max={600}
78
+ bind:value={png_dpi}
79
+ title="Export resolution in dots per inch"
80
+ />)
81
+ </label>
82
+
83
+ <h4
84
+ {@attach tooltip({
85
+ content: `Includes vertices, faces, edges, and reciprocal lattice vectors`,
86
+ })}
87
+ >
88
+ Export as data
89
+ </h4>
90
+ <label>
91
+ JSON
92
+ <button
93
+ type="button"
94
+ onclick={export_as_json}
95
+ disabled={!bz_data}
96
+ title="Download JSON"
97
+ >
98
+
99
+ </button>
100
+ <button
101
+ type="button"
102
+ onclick={copy_json}
103
+ disabled={!bz_data}
104
+ title="Copy JSON to clipboard"
105
+ >
106
+ {copy_status ? copy_confirm : `📋`}
107
+ </button>
108
+ </label>
109
+ </DraggablePane>
110
+
111
+ <style>
112
+ label {
113
+ display: flex;
114
+ flex-wrap: wrap;
115
+ align-items: center;
116
+ gap: 4pt;
117
+ font-size: 0.95em;
118
+ }
119
+ button {
120
+ width: 1.9em;
121
+ height: 1.6em;
122
+ padding: 0 6pt;
123
+ margin: 0 0 0 4pt;
124
+ box-sizing: border-box;
125
+ }
126
+ input {
127
+ margin: 0 0 0 2pt;
128
+ }
129
+ </style>
@@ -0,0 +1,15 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import type { Camera, Scene } from 'three';
3
+ import type { BrillouinZoneData } from './types';
4
+ type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
5
+ export_pane_open?: boolean;
6
+ bz_data?: BrillouinZoneData;
7
+ wrapper?: HTMLDivElement;
8
+ scene?: Scene;
9
+ camera?: Camera;
10
+ filename?: string;
11
+ png_dpi?: number;
12
+ };
13
+ declare const BrillouinZoneExportPane: import("svelte").Component<$$ComponentProps, {}, "png_dpi" | "export_pane_open">;
14
+ type BrillouinZoneExportPane = ReturnType<typeof BrillouinZoneExportPane>;
15
+ export default BrillouinZoneExportPane;