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
@@ -1,10 +1,8 @@
1
1
  <script lang="ts">import { DraggablePane, format_num, Lattice, SettingsSection } from '..';
2
2
  import { axis_colors, element_color_schemes } from '../colors';
3
- import { export_canvas_as_png } from '../io/export';
4
3
  import { to_degrees, to_radians } from '../math';
5
4
  import { DEFAULTS, SETTINGS_CONFIG } from '../settings';
6
5
  import { StructureScene } from './';
7
- import * as exports from './export';
8
6
  import { is_valid_supercell_input } from './supercell';
9
7
  import Select from 'svelte-multiselect';
10
8
  import { tooltip } from 'svelte-multiselect/attachments';
@@ -15,7 +13,7 @@ let { controls_open = $bindable(false), scene_props = $bindable({}), lattice_pro
15
13
  cell_surface_color: DEFAULTS.structure.cell_surface_color,
16
14
  cell_surface_opacity: DEFAULTS.structure.cell_surface_opacity,
17
15
  cell_edge_width: DEFAULTS.structure.cell_edge_width,
18
- }), show_image_atoms = $bindable(DEFAULTS.structure.show_image_atoms), supercell_scaling = $bindable(`1x1x1`), background_color = $bindable(undefined), background_opacity = $bindable(DEFAULTS.background_opacity), color_scheme = $bindable(DEFAULTS.color_scheme), structure = undefined, wrapper = undefined, png_dpi = $bindable(150), scene = undefined, camera = undefined, pane_props = $bindable({}), toggle_props = $bindable({}), ...rest } = $props();
16
+ }), show_image_atoms = $bindable(DEFAULTS.structure.show_image_atoms), supercell_scaling = $bindable(`1x1x1`), background_color = $bindable(undefined), background_opacity = $bindable(DEFAULTS.background_opacity), color_scheme = $bindable(DEFAULTS.color_scheme), structure = undefined, pane_props = $bindable({}), toggle_props = $bindable({}), ...rest } = $props();
19
17
  // Color scheme selection state
20
18
  let color_scheme_selected = $state([color_scheme]);
21
19
  $effect(() => {
@@ -34,16 +32,6 @@ $effect(() => {
34
32
  });
35
33
  // Ensure site_label_offset is always available
36
34
  scene_props.site_label_offset ??= [...DEFAULTS.structure.site_label_offset];
37
- // Copy button feedback state
38
- let copy_status = $state({ json: false, xyz: false, cif: false, poscar: false });
39
- // Dynamic button text based on copy status
40
- const copy_confirm = `✅`;
41
- const export_formats = [
42
- { label: `JSON`, format: `json` },
43
- { label: `XYZ`, format: `xyz` },
44
- { label: `CIF`, format: `cif` },
45
- { label: `POSCAR`, format: `poscar` },
46
- ];
47
35
  // Detect if structure has force data
48
36
  let has_forces = $derived(structure?.sites?.some((site) => site.properties?.force && Array.isArray(site.properties.force)) ?? false);
49
37
  // Detect if structure has lattice (can create supercells)
@@ -80,55 +68,13 @@ function get_representative_colors(scheme_name) {
80
68
  .map((el) => scheme[el] || scheme.H || `#cccccc`)
81
69
  .filter(Boolean);
82
70
  }
83
- // Helper function to export structure to file
84
- function export_structure(format) {
85
- if (!structure)
86
- return;
87
- const export_fns = {
88
- json: exports.export_structure_as_json,
89
- xyz: exports.export_structure_as_xyz,
90
- cif: exports.export_structure_as_cif,
91
- poscar: exports.export_structure_as_poscar,
92
- };
93
- export_fns[format](structure);
94
- }
95
- // Handle clipboard copy with user feedback
96
- async function handle_copy(format) {
97
- if (!structure) {
98
- console.warn(`No structure available for copying`);
99
- return;
100
- }
101
- try {
102
- let content;
103
- if (format === `json`)
104
- content = exports.structure_to_json_str(structure);
105
- else if (format === `xyz`)
106
- content = exports.structure_to_xyz_str(structure);
107
- else if (format === `cif`)
108
- content = exports.structure_to_cif_str(structure);
109
- else if (format === `poscar`) {
110
- content = exports.structure_to_poscar_str(structure);
111
- }
112
- else
113
- throw new Error(`Invalid format: ${format}`);
114
- await navigator.clipboard.writeText(content);
115
- // Show temporary feedback in button text
116
- copy_status[format] = true;
117
- setTimeout(() => {
118
- copy_status[format] = false;
119
- }, 1000);
120
- }
121
- catch (error) {
122
- console.error(`Failed to copy ${format.toUpperCase()} to clipboard`, error);
123
- }
124
- }
125
71
  </script>
126
72
 
127
73
  <DraggablePane
128
74
  bind:show={controls_open}
129
75
  pane_props={{ ...pane_props, class: `controls-pane ${pane_props?.class ?? ``}` }}
130
76
  toggle_props={{
131
- title: `${controls_open ? `Close` : `Open`} structure controls`,
77
+ title: controls_open ? `` : `Structure controls`,
132
78
  ...toggle_props,
133
79
  class: `structure-controls-toggle ${toggle_props?.class ?? ``}`,
134
80
  }}
@@ -207,66 +153,16 @@ async function handle_copy(format) {
207
153
  >
208
154
  Bonds:
209
155
  <select bind:value={scene_props.show_bonds}>
210
- {#each SETTINGS_CONFIG.structure.show_bonds.enum ?? [] as option (option)}
211
- <option value={option}>{option}</option>
156
+ {#each Object.entries(SETTINGS_CONFIG.structure.show_bonds.enum ?? {}) as
157
+ [value, label]
158
+ (value)
159
+ }
160
+ <option {value}>{label}</option>
212
161
  {/each}
213
162
  </select>
214
163
  </label>
215
164
  </SettingsSection>
216
165
 
217
- <h4>Export</h4>
218
- <div class="export-buttons">
219
- {#each export_formats as { label, format } (format)}
220
- <div style="display: flex; align-items: center; gap: 4pt">
221
- {label}
222
- <button
223
- type="button"
224
- onclick={() => export_structure(format)}
225
- title="Download {label}"
226
- >
227
-
228
- </button>
229
- <button
230
- type="button"
231
- onclick={() => handle_copy(format)}
232
- title="Copy {label} to clipboard"
233
- >
234
- {copy_status[format] ? copy_confirm : `📋`}
235
- </button>
236
- </div>
237
- {/each}
238
- <label>
239
- PNG
240
- <button
241
- type="button"
242
- disabled={!wrapper?.querySelector(`canvas`)}
243
- onclick={() => {
244
- const canvas = wrapper?.querySelector(`canvas`) as HTMLCanvasElement
245
- if (canvas) {
246
- export_canvas_as_png(
247
- canvas,
248
- structure,
249
- png_dpi,
250
- scene,
251
- camera,
252
- )
253
- } else console.warn(`Canvas element not found for PNG export`)
254
- }}
255
- title="PNG ({png_dpi} DPI)"
256
- >
257
-
258
- </button>
259
- &nbsp;(DPI: <input
260
- type="number"
261
- min={50}
262
- max={500}
263
- bind:value={png_dpi}
264
- title="Export resolution in dots per inch"
265
- style="margin: 0 0 0 2pt"
266
- />)
267
- </label>
268
- </div>
269
-
270
166
  <SettingsSection
271
167
  title="Camera"
272
168
  current_values={{
@@ -297,8 +193,13 @@ async function handle_copy(format) {
297
193
  Projection
298
194
  </span>
299
195
  <select bind:value={scene_props.camera_projection}>
300
- {#each SETTINGS_CONFIG.structure.camera_projection.enum ?? [] as option (option)}
301
- <option value={option}>{option}</option>
196
+ {#each Object.entries(
197
+ SETTINGS_CONFIG.structure.camera_projection.enum ?? {},
198
+ ) as
199
+ [value, label]
200
+ (value)
201
+ }
202
+ <option {value}>{label}</option>
302
203
  {/each}
303
204
  </select>
304
205
  </label>
@@ -830,19 +731,22 @@ async function handle_copy(format) {
830
731
  }}
831
732
  >
832
733
  <label>
833
- Bonding strategy
834
- <select bind:value={scene_props.bonding_strategy}>
835
- <option value="electroneg_ratio">Electronegativity Ratio</option>
836
- <option value="max_dist">Max Distance</option>
837
- <option value="nearest_neighbor">Nearest Neighbor</option>
734
+ Strategy <select bind:value={scene_props.bonding_strategy}>
735
+ {#each Object.entries(
736
+ SETTINGS_CONFIG.structure.bonding_strategy.enum ?? {},
737
+ ) as
738
+ [value, label]
739
+ (value)
740
+ }
741
+ <option {value}>{label}</option>
742
+ {/each}
838
743
  </select>
839
744
  </label>
840
745
  <label>
841
- Bond color
842
- <input type="color" bind:value={scene_props.bond_color} />
746
+ Color <input type="color" bind:value={scene_props.bond_color} />
843
747
  </label>
844
748
  <label>
845
- Bond thickness
749
+ Thickness
846
750
  <input
847
751
  type="number"
848
752
  min={0.05}
@@ -863,19 +767,6 @@ async function handle_copy(format) {
863
767
  </DraggablePane>
864
768
 
865
769
  <style>
866
- .export-buttons {
867
- display: flex;
868
- flex-wrap: wrap;
869
- gap: 1ex;
870
- font-size: 0.95em;
871
- }
872
- .export-buttons button {
873
- width: 1.6em;
874
- height: 1.6em;
875
- display: grid;
876
- place-items: center;
877
- padding: 0;
878
- }
879
770
  .rotation-axes {
880
771
  display: flex;
881
772
  gap: 9pt;
@@ -2,8 +2,7 @@ import type { AnyStructure } from '..';
2
2
  import { DraggablePane, Lattice } from '..';
3
3
  import { StructureScene } from './';
4
4
  import type { ComponentProps } from 'svelte';
5
- import type { Camera, Scene } from 'three';
6
- export interface Props extends Omit<ComponentProps<typeof DraggablePane>, `children`> {
5
+ type $$ComponentProps = Omit<ComponentProps<typeof DraggablePane>, `children`> & {
7
6
  controls_open?: boolean;
8
7
  scene_props?: ComponentProps<typeof StructureScene>;
9
8
  lattice_props?: ComponentProps<typeof Lattice>;
@@ -12,14 +11,10 @@ export interface Props extends Omit<ComponentProps<typeof DraggablePane>, `child
12
11
  background_color?: string;
13
12
  background_opacity?: number;
14
13
  color_scheme?: string;
15
- structure?: AnyStructure | undefined;
16
- wrapper?: HTMLDivElement;
17
- png_dpi?: number;
18
- scene?: Scene;
19
- camera?: Camera;
14
+ structure?: AnyStructure;
20
15
  pane_props?: ComponentProps<typeof DraggablePane>[`pane_props`];
21
16
  toggle_props?: ComponentProps<typeof DraggablePane>[`toggle_props`];
22
- }
23
- declare const StructureControls: import("svelte").Component<Props, {}, "color_scheme" | "background_color" | "background_opacity" | "show_image_atoms" | "scene_props" | "lattice_props" | "controls_open" | "png_dpi" | "supercell_scaling" | "toggle_props" | "pane_props">;
17
+ };
18
+ declare const StructureControls: import("svelte").Component<$$ComponentProps, {}, "color_scheme" | "background_color" | "background_opacity" | "show_image_atoms" | "controls_open" | "toggle_props" | "pane_props" | "scene_props" | "lattice_props" | "supercell_scaling">;
24
19
  type StructureControls = ReturnType<typeof StructureControls>;
25
20
  export default StructureControls;
@@ -0,0 +1,188 @@
1
+ <script lang="ts">import { DraggablePane } from '..';
2
+ import { export_canvas_as_png } from '../io/export';
3
+ import * as exports from './export';
4
+ import { tooltip } from 'svelte-multiselect/attachments';
5
+ let { export_pane_open = $bindable(false), structure = undefined, wrapper = undefined, scene = undefined, camera = undefined, png_dpi = $bindable(150), pane_props = $bindable({}), toggle_props = $bindable({}), ...rest } = $props();
6
+ // Copy button feedback state
7
+ let copy_status = $state({ json: false, xyz: false, cif: false, poscar: false });
8
+ // Dynamic button text based on copy status
9
+ const copy_confirm = `✅`;
10
+ const text_export_formats = [
11
+ { label: `JSON`, format: `json` },
12
+ { label: `XYZ`, format: `xyz` },
13
+ { label: `CIF`, format: `cif` },
14
+ { label: `POSCAR`, format: `poscar` },
15
+ ];
16
+ const model_3d_formats = [
17
+ {
18
+ label: `GLB`,
19
+ format: `glb`,
20
+ hint: `Export as GLB (binary GLTF) - preserves element colors and materials, ideal for visualization in Blender, Unity, web viewers`,
21
+ },
22
+ {
23
+ label: `OBJ`,
24
+ format: `obj`,
25
+ hint: `Export as OBJ (Wavefront Object) - widely supported 3D format with material references, works in most 3D applications`,
26
+ },
27
+ ];
28
+ // Helper function to export structure to file
29
+ function export_structure(format) {
30
+ if (!structure)
31
+ return;
32
+ const export_fns = {
33
+ json: exports.export_structure_as_json,
34
+ xyz: exports.export_structure_as_xyz,
35
+ cif: exports.export_structure_as_cif,
36
+ poscar: exports.export_structure_as_poscar,
37
+ };
38
+ export_fns[format](structure);
39
+ }
40
+ // Handle clipboard copy with user feedback
41
+ async function handle_copy(format) {
42
+ if (!structure) {
43
+ console.warn(`No structure available for copying`);
44
+ return;
45
+ }
46
+ try {
47
+ let content;
48
+ if (format === `json`)
49
+ content = exports.structure_to_json_str(structure);
50
+ else if (format === `xyz`)
51
+ content = exports.structure_to_xyz_str(structure);
52
+ else if (format === `cif`)
53
+ content = exports.structure_to_cif_str(structure);
54
+ else if (format === `poscar`) {
55
+ content = exports.structure_to_poscar_str(structure);
56
+ }
57
+ else
58
+ throw new Error(`Invalid format: ${format}`);
59
+ await navigator.clipboard.writeText(content);
60
+ // Show temporary feedback in button text
61
+ copy_status[format] = true;
62
+ setTimeout(() => {
63
+ copy_status[format] = false;
64
+ }, 1000);
65
+ }
66
+ catch (error) {
67
+ console.error(`Failed to copy ${format.toUpperCase()} to clipboard`, error);
68
+ }
69
+ }
70
+ function handle_3d_export(format) {
71
+ if (!scene) {
72
+ console.warn(`No scene available for ${format.toUpperCase()} export`);
73
+ return;
74
+ }
75
+ try {
76
+ if (format === `glb`)
77
+ exports.export_structure_as_glb(scene, structure);
78
+ else if (format === `obj`)
79
+ exports.export_structure_as_obj(scene, structure);
80
+ }
81
+ catch (error) {
82
+ console.error(`Failed to export ${format.toUpperCase()}:`, error);
83
+ }
84
+ }
85
+ </script>
86
+
87
+ <DraggablePane
88
+ bind:show={export_pane_open}
89
+ open_icon="Cross"
90
+ closed_icon="Export"
91
+ pane_props={{ ...pane_props, class: `export-pane ${pane_props?.class ?? ``}` }}
92
+ toggle_props={{
93
+ title: export_pane_open ? `` : `Export Structure`,
94
+ ...toggle_props,
95
+ class: `structure-export-toggle ${toggle_props?.class ?? ``}`,
96
+ }}
97
+ {...rest}
98
+ >
99
+ <h4>Export as text</h4>
100
+ <div class="export-buttons">
101
+ {#each text_export_formats as { label, format } (format)}
102
+ <div style="display: flex; align-items: center; gap: 4pt">
103
+ {label}
104
+ <button
105
+ type="button"
106
+ onclick={() => export_structure(format)}
107
+ title="Download {label}"
108
+ >
109
+
110
+ </button>
111
+ <button
112
+ type="button"
113
+ onclick={() => handle_copy(format)}
114
+ title="Copy {label} to clipboard"
115
+ >
116
+ {copy_status[format] ? copy_confirm : `📋`}
117
+ </button>
118
+ </div>
119
+ {/each}
120
+ </div>
121
+
122
+ <h4>Export as image</h4>
123
+ <div class="export-buttons">
124
+ <label>
125
+ PNG
126
+ <button
127
+ type="button"
128
+ disabled={!wrapper?.querySelector(`canvas`)}
129
+ onclick={() => {
130
+ const canvas = wrapper?.querySelector(`canvas`) as HTMLCanvasElement
131
+ if (canvas) {
132
+ export_canvas_as_png(
133
+ canvas,
134
+ structure,
135
+ png_dpi,
136
+ scene,
137
+ camera,
138
+ )
139
+ } else console.warn(`Canvas element not found for PNG export`)
140
+ }}
141
+ title="PNG ({png_dpi} DPI)"
142
+ >
143
+
144
+ </button>
145
+ &nbsp;(DPI: <input
146
+ type="number"
147
+ min={50}
148
+ max={500}
149
+ bind:value={png_dpi}
150
+ title="Export resolution in dots per inch"
151
+ style="margin: 0 0 0 2pt"
152
+ />)
153
+ </label>
154
+ </div>
155
+
156
+ <h4>Export as 3D model</h4>
157
+ <div class="export-buttons">
158
+ {#each model_3d_formats as { label, format, hint } (format)}
159
+ <div style="display: flex; align-items: center; gap: 4pt">
160
+ {label}
161
+ <button
162
+ type="button"
163
+ onclick={() => handle_3d_export(format)}
164
+ disabled={!scene}
165
+ {@attach tooltip({ content: hint })}
166
+ >
167
+
168
+ </button>
169
+ </div>
170
+ {/each}
171
+ </div>
172
+ </DraggablePane>
173
+
174
+ <style>
175
+ .export-buttons {
176
+ display: flex;
177
+ flex-wrap: wrap;
178
+ gap: 9pt;
179
+ font-size: 0.95em;
180
+ }
181
+ .export-buttons button {
182
+ width: 1.9em;
183
+ height: 1.6em;
184
+ padding: 0;
185
+ margin: 0 0 0 4pt;
186
+ box-sizing: border-box;
187
+ }
188
+ </style>
@@ -0,0 +1,17 @@
1
+ import type { AnyStructure } from '..';
2
+ import { DraggablePane } from '..';
3
+ import type { ComponentProps } from 'svelte';
4
+ import type { Camera, Scene } from 'three';
5
+ type $$ComponentProps = {
6
+ export_pane_open?: boolean;
7
+ structure?: AnyStructure;
8
+ wrapper?: HTMLDivElement;
9
+ scene?: Scene;
10
+ camera?: Camera;
11
+ png_dpi?: number;
12
+ pane_props?: ComponentProps<typeof DraggablePane>[`pane_props`];
13
+ toggle_props?: ComponentProps<typeof DraggablePane>[`toggle_props`];
14
+ };
15
+ declare const StructureExportPane: import("svelte").Component<$$ComponentProps, {}, "toggle_props" | "pane_props" | "png_dpi" | "export_pane_open">;
16
+ type StructureExportPane = ReturnType<typeof StructureExportPane>;
17
+ export default StructureExportPane;
@@ -2,7 +2,7 @@ import type { AnyStructure } from '..';
2
2
  import { DraggablePane } from '..';
3
3
  import type { ComponentProps } from 'svelte';
4
4
  import type { HTMLAttributes } from 'svelte/elements';
5
- interface Props extends Omit<HTMLAttributes<HTMLDivElement>, `onclose`> {
5
+ type $$ComponentProps = Omit<HTMLAttributes<HTMLDivElement>, `onclose`> & {
6
6
  structure: AnyStructure;
7
7
  pane_open?: boolean;
8
8
  atom_count_thresholds?: [number, number];
@@ -10,7 +10,7 @@ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, `onclose`> {
10
10
  pane_props?: ComponentProps<typeof DraggablePane>[`pane_props`];
11
11
  highlighted_sites?: number[];
12
12
  selected_sites?: number[];
13
- }
14
- declare const StructureInfoPane: import("svelte").Component<Props, {}, "selected_sites" | "toggle_props" | "pane_props" | "pane_open" | "highlighted_sites">;
13
+ };
14
+ declare const StructureInfoPane: import("svelte").Component<$$ComponentProps, {}, "toggle_props" | "pane_props" | "selected_sites" | "pane_open" | "highlighted_sites">;
15
15
  type StructureInfoPane = ReturnType<typeof StructureInfoPane>;
16
16
  export default StructureInfoPane;
@@ -2,36 +2,62 @@
2
2
  import { contrast_color, default_element_colors } from '../colors';
3
3
  import { colors } from '../state.svelte';
4
4
  import { tooltip } from 'svelte-multiselect/attachments';
5
- let { elements, elem_color_picker_title = `Double click to reset color`, labels = $bindable([]), amount_format = `.3~f`, show_amounts = true, get_element_label, ...rest } = $props();
5
+ import { SvelteSet } from 'svelte/reactivity';
6
+ let { elements, elem_color_picker_title = `Double click to reset color`, labels = $bindable([]), amount_format = `.3~f`, show_amounts = true, get_element_label, hidden_elements = $bindable(new Set()), ...rest } = $props();
7
+ function toggle_element_visibility(element, event) {
8
+ event.preventDefault();
9
+ event.stopPropagation();
10
+ event.stopImmediatePropagation();
11
+ const new_hidden = new SvelteSet(hidden_elements);
12
+ if (new_hidden.has(element))
13
+ new_hidden.delete(element);
14
+ else
15
+ new_hidden.add(element);
16
+ hidden_elements = new_hidden;
17
+ }
6
18
  </script>
7
19
 
8
20
  <div {...rest} class="structure-legend {rest.class ?? ``}">
9
21
  {#each Object.entries(elements) as [elem, amt], idx (elem + amt)}
10
- <label
11
- bind:this={labels[idx]}
12
- title={element_data.find((el) => el.symbol == elem)?.name}
13
- {@attach tooltip()}
14
- style:background-color={colors.element[elem]}
15
- ondblclick={(event) => {
16
- event.preventDefault()
17
- colors.element[elem] = default_element_colors[elem]
18
- }}
19
- {@attach contrast_color()}
20
- >
21
- {#if get_element_label}
22
- {get_element_label(elem, amt)}
23
- {:else}
24
- {elem}
25
- {#if show_amounts}
26
- <sub>{format_num(amt, amount_format)}</sub>
22
+ {@const is_hidden = hidden_elements.has(elem as ElementSymbol)}
23
+ <div class="legend-item">
24
+ <label
25
+ bind:this={labels[idx]}
26
+ title={element_data.find((el) => el.symbol == elem)?.name}
27
+ {@attach tooltip()}
28
+ style:background-color={colors.element[elem]}
29
+ class:hidden={is_hidden}
30
+ ondblclick={(event) => {
31
+ event.preventDefault()
32
+ colors.element[elem] = default_element_colors[elem]
33
+ }}
34
+ {@attach contrast_color()}
35
+ >
36
+ {#if get_element_label}
37
+ {get_element_label(elem, amt)}
38
+ {:else}
39
+ {elem}
40
+ {#if show_amounts}
41
+ <sub>{format_num(amt, amount_format)}</sub>
42
+ {/if}
27
43
  {/if}
28
- {/if}
29
- <input
30
- type="color"
31
- bind:value={colors.element[elem]}
32
- title={elem_color_picker_title}
33
- />
34
- </label>
44
+ <input
45
+ type="color"
46
+ bind:value={colors.element[elem]}
47
+ title={elem_color_picker_title}
48
+ />
49
+ </label>
50
+ <button
51
+ class="toggle-visibility"
52
+ class:visible={is_hidden}
53
+ onclick={(event) => toggle_element_visibility(elem as ElementSymbol, event)}
54
+ title={is_hidden ? `Show ${elem} atoms` : `Hide ${elem} atoms`}
55
+ {@attach tooltip({ placement: `top` })}
56
+ type="button"
57
+ >
58
+ ×
59
+ </button>
60
+ </div>
35
61
  {/each}
36
62
  </div>
37
63
 
@@ -41,22 +67,29 @@ let { elements, elem_color_picker_title = `Double click to reset color`, labels
41
67
  position: absolute;
42
68
  bottom: var(--struct-legend-bottom, clamp(4pt, 3cqmin, 8pt));
43
69
  right: var(--struct-legend-right, clamp(4pt, 3cqmin, 8pt));
44
- gap: var(--struct-legend-gap, clamp(2pt, 2cqmin, 5pt));
70
+ gap: var(--struct-legend-gap, clamp(3pt, 2cqmin, 7pt));
45
71
  font-size: var(--struct-legend-font, clamp(8pt, 3cqmin, 14pt));
46
72
  filter: var(--legend-filter, grayscale(10%) brightness(0.95) saturate(0.9));
47
73
  z-index: var(--struct-legend-z-index, 1);
48
74
  pointer-events: auto;
49
75
  visibility: visible;
50
76
  }
77
+ .structure-legend .legend-item {
78
+ position: relative;
79
+ display: inline-block;
80
+ }
51
81
  .structure-legend label {
52
82
  padding: var(--struct-legend-padding, 0 4pt);
53
83
  border-radius: var(--struct-legend-radius, 3pt);
54
84
  line-height: var(--struct-legend-line-height, 1.3);
55
- position: relative;
56
85
  display: inline-block;
57
86
  cursor: pointer;
58
87
  visibility: visible;
59
88
  white-space: nowrap;
89
+ transition: opacity 0.2s ease;
90
+ }
91
+ .structure-legend label.hidden {
92
+ opacity: 0.4;
60
93
  }
61
94
  .structure-legend label input[type='color'] {
62
95
  z-index: var(--struct-legend-input-z, 1);
@@ -66,6 +99,37 @@ let { elements, elem_color_picker_title = `Double click to reset color`, labels
66
99
  top: 7pt;
67
100
  left: 0;
68
101
  }
102
+ .structure-legend button.toggle-visibility {
103
+ position: absolute;
104
+ top: -3px;
105
+ right: -7px;
106
+ width: 1em;
107
+ height: 1em;
108
+ padding: 0;
109
+ margin: 0;
110
+ border: none;
111
+ background: rgba(0, 0, 0, 0.5);
112
+ color: white;
113
+ border-radius: 50%;
114
+ font-size: 0.9em;
115
+ line-height: 0.9;
116
+ cursor: pointer;
117
+ display: flex;
118
+ align-items: center;
119
+ justify-content: center;
120
+ opacity: 0;
121
+ transition: opacity 0.2s ease, background 0.2s ease, transform 0.1s ease;
122
+ z-index: 2;
123
+ pointer-events: auto;
124
+ }
125
+ .structure-legend button.toggle-visibility.visible,
126
+ .structure-legend .legend-item:hover button.toggle-visibility {
127
+ opacity: 1;
128
+ }
129
+ .structure-legend button.toggle-visibility:hover {
130
+ background: rgba(0, 0, 0, 0.8);
131
+ transform: scale(1.15);
132
+ }
69
133
  .structure-legend sub {
70
134
  font-size: 0.85em;
71
135
  margin: 0 0 0 -4px;
@@ -1,13 +1,14 @@
1
- import type { CompositionType } from '..';
1
+ import type { CompositionType, ElementSymbol } from '..';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
- interface Props extends HTMLAttributes<HTMLDivElement> {
3
+ type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
4
4
  elements: CompositionType;
5
5
  elem_color_picker_title?: string;
6
6
  labels?: HTMLLabelElement[];
7
7
  amount_format?: string;
8
8
  show_amounts?: boolean;
9
9
  get_element_label?: (element: string, amount: number) => string;
10
- }
11
- declare const StructureLegend: import("svelte").Component<Props, {}, "labels">;
10
+ hidden_elements?: Set<ElementSymbol>;
11
+ };
12
+ declare const StructureLegend: import("svelte").Component<$$ComponentProps, {}, "labels" | "hidden_elements">;
12
13
  type StructureLegend = ReturnType<typeof StructureLegend>;
13
14
  export default StructureLegend;