matterviz 0.3.2 → 0.3.3

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 (280) hide show
  1. package/dist/EmptyState.svelte +10 -2
  2. package/dist/FilePicker.svelte +123 -82
  3. package/dist/Icon.svelte +18 -12
  4. package/dist/MillerIndexInput.svelte +27 -21
  5. package/dist/api/optimade.js +6 -6
  6. package/dist/app.css +216 -207
  7. package/dist/brillouin/BrillouinZone.svelte +292 -149
  8. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  9. package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
  10. package/dist/brillouin/BrillouinZoneExportPane.svelte +69 -42
  11. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
  12. package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
  13. package/dist/brillouin/BrillouinZoneScene.svelte +275 -163
  14. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  15. package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
  16. package/dist/brillouin/compute.js +11 -6
  17. package/dist/chempot-diagram/ChemPotDiagram.svelte +162 -27
  18. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +451 -281
  19. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2148 -1642
  20. package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -5
  21. package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
  22. package/dist/chempot-diagram/async-compute.svelte.js +77 -0
  23. package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
  24. package/dist/chempot-diagram/chempot-worker.js +11 -0
  25. package/dist/chempot-diagram/color.js +1 -2
  26. package/dist/chempot-diagram/compute.d.ts +10 -0
  27. package/dist/chempot-diagram/compute.js +250 -88
  28. package/dist/chempot-diagram/index.d.ts +2 -1
  29. package/dist/chempot-diagram/index.js +2 -1
  30. package/dist/chempot-diagram/temperature.js +8 -9
  31. package/dist/chempot-diagram/types.d.ts +3 -0
  32. package/dist/chempot-diagram/types.js +1 -0
  33. package/dist/colors/index.d.ts +1 -1
  34. package/dist/colors/index.js +5 -3
  35. package/dist/composition/BarChart.svelte +128 -55
  36. package/dist/composition/BubbleChart.svelte +102 -49
  37. package/dist/composition/Composition.svelte +100 -79
  38. package/dist/composition/Formula.svelte +108 -62
  39. package/dist/composition/FormulaFilter.svelte +665 -537
  40. package/dist/composition/PieChart.svelte +183 -108
  41. package/dist/composition/format.d.ts +5 -0
  42. package/dist/composition/format.js +20 -3
  43. package/dist/composition/parse.js +14 -9
  44. package/dist/convex-hull/ConvexHull.svelte +93 -40
  45. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  46. package/dist/convex-hull/ConvexHull2D.svelte +549 -360
  47. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  48. package/dist/convex-hull/ConvexHull3D.svelte +1296 -827
  49. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  50. package/dist/convex-hull/ConvexHull4D.svelte +1004 -688
  51. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  52. package/dist/convex-hull/ConvexHullControls.svelte +115 -28
  53. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
  54. package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
  55. package/dist/convex-hull/ConvexHullStats.svelte +425 -328
  56. package/dist/convex-hull/ConvexHullTooltip.svelte +40 -16
  57. package/dist/convex-hull/GasPressureControls.svelte +104 -61
  58. package/dist/convex-hull/StructurePopup.svelte +25 -4
  59. package/dist/convex-hull/TemperatureSlider.svelte +45 -25
  60. package/dist/convex-hull/barycentric-coords.js +13 -7
  61. package/dist/convex-hull/demo-temperature.js +8 -4
  62. package/dist/convex-hull/gas-thermodynamics.js +17 -12
  63. package/dist/convex-hull/helpers.d.ts +9 -0
  64. package/dist/convex-hull/helpers.js +77 -34
  65. package/dist/convex-hull/thermodynamics.js +61 -56
  66. package/dist/convex-hull/types.d.ts +9 -14
  67. package/dist/convex-hull/types.js +0 -17
  68. package/dist/coordination/CoordinationBarPlot.svelte +227 -154
  69. package/dist/element/BohrAtom.svelte +55 -12
  70. package/dist/element/ElementHeading.svelte +7 -2
  71. package/dist/element/ElementPhoto.svelte +15 -9
  72. package/dist/element/ElementStats.svelte +10 -4
  73. package/dist/element/ElementTile.svelte +137 -73
  74. package/dist/element/Nucleus.svelte +39 -11
  75. package/dist/feedback/ClickFeedback.svelte +16 -5
  76. package/dist/feedback/DragOverlay.svelte +10 -2
  77. package/dist/feedback/Spinner.svelte +4 -2
  78. package/dist/feedback/StatusMessage.svelte +8 -2
  79. package/dist/fermi-surface/FermiSlice.svelte +118 -88
  80. package/dist/fermi-surface/FermiSurface.svelte +328 -187
  81. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  82. package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
  83. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  84. package/dist/fermi-surface/FermiSurfaceScene.svelte +535 -342
  85. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  86. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
  87. package/dist/fermi-surface/compute.js +16 -20
  88. package/dist/fermi-surface/parse.js +24 -14
  89. package/dist/fermi-surface/symmetry.js +2 -7
  90. package/dist/fermi-surface/types.d.ts +3 -5
  91. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1019 -765
  92. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +1 -1
  93. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +76 -22
  94. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +2 -3
  95. package/dist/icons.js +47 -0
  96. package/dist/index.d.ts +2 -1
  97. package/dist/index.js +2 -1
  98. package/dist/io/decompress.js +1 -1
  99. package/dist/io/export.d.ts +3 -0
  100. package/dist/io/export.js +129 -143
  101. package/dist/io/is-binary.js +2 -3
  102. package/dist/io/url-drop.js +1 -2
  103. package/dist/isosurface/Isosurface.svelte +202 -148
  104. package/dist/isosurface/IsosurfaceControls.svelte +46 -28
  105. package/dist/isosurface/parse.js +34 -29
  106. package/dist/isosurface/slice.js +5 -10
  107. package/dist/isosurface/types.d.ts +2 -1
  108. package/dist/isosurface/types.js +61 -12
  109. package/dist/labels.js +11 -8
  110. package/dist/layout/FullscreenToggle.svelte +11 -2
  111. package/dist/layout/InfoCard.svelte +38 -6
  112. package/dist/layout/InfoTag.svelte +63 -32
  113. package/dist/layout/PropertyFilter.svelte +82 -37
  114. package/dist/layout/SettingsSection.svelte +85 -55
  115. package/dist/layout/SubpageGrid.svelte +10 -2
  116. package/dist/layout/json-tree/JsonNode.svelte +183 -138
  117. package/dist/layout/json-tree/JsonTree.svelte +499 -413
  118. package/dist/layout/json-tree/JsonValue.svelte +127 -99
  119. package/dist/layout/json-tree/utils.js +4 -2
  120. package/dist/marching-cubes.js +25 -2
  121. package/dist/math.d.ts +13 -17
  122. package/dist/math.js +133 -67
  123. package/dist/overlays/ContextMenu.svelte +65 -40
  124. package/dist/overlays/DraggablePane.svelte +211 -139
  125. package/dist/periodic-table/PeriodicTable.svelte +278 -145
  126. package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
  127. package/dist/periodic-table/PropertySelect.svelte +25 -7
  128. package/dist/periodic-table/TableInset.svelte +8 -3
  129. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +446 -309
  130. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
  131. package/dist/phase-diagram/PhaseDiagramControls.svelte +102 -43
  132. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  133. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +63 -40
  134. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +71 -28
  135. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +1 -1
  136. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +158 -101
  137. package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
  138. package/dist/phase-diagram/build-diagram.js +9 -9
  139. package/dist/phase-diagram/colors.js +1 -3
  140. package/dist/phase-diagram/parse.js +10 -9
  141. package/dist/phase-diagram/svg-to-diagram.js +53 -49
  142. package/dist/phase-diagram/utils.d.ts +1 -0
  143. package/dist/phase-diagram/utils.js +80 -25
  144. package/dist/plot/AxisLabel.svelte +28 -3
  145. package/dist/plot/BarPlot.svelte +1182 -734
  146. package/dist/plot/BarPlot.svelte.d.ts +2 -2
  147. package/dist/plot/BarPlotControls.svelte +31 -5
  148. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  149. package/dist/plot/ColorBar.svelte +479 -329
  150. package/dist/plot/ColorScaleSelect.svelte +27 -6
  151. package/dist/plot/ElementScatter.svelte +36 -15
  152. package/dist/plot/FillArea.svelte +152 -95
  153. package/dist/plot/Histogram.svelte +934 -571
  154. package/dist/plot/Histogram.svelte.d.ts +1 -1
  155. package/dist/plot/HistogramControls.svelte +53 -9
  156. package/dist/plot/HistogramControls.svelte.d.ts +1 -1
  157. package/dist/plot/InteractiveAxisLabel.svelte +34 -11
  158. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
  159. package/dist/plot/Line.svelte +63 -28
  160. package/dist/plot/PlotControls.svelte +157 -114
  161. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  162. package/dist/plot/PlotLegend.svelte +174 -91
  163. package/dist/plot/PlotTooltip.svelte +45 -6
  164. package/dist/plot/PortalSelect.svelte +175 -147
  165. package/dist/plot/ReferenceLine.svelte +76 -22
  166. package/dist/plot/ReferenceLine3D.svelte +132 -107
  167. package/dist/plot/ReferencePlane.svelte +146 -121
  168. package/dist/plot/ScatterPlot.svelte +1681 -1091
  169. package/dist/plot/ScatterPlot.svelte.d.ts +2 -2
  170. package/dist/plot/ScatterPlot3D.svelte +256 -131
  171. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  172. package/dist/plot/ScatterPlot3DControls.svelte +113 -63
  173. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
  174. package/dist/plot/ScatterPlot3DScene.svelte +608 -403
  175. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  176. package/dist/plot/ScatterPlotControls.svelte +65 -25
  177. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  178. package/dist/plot/ScatterPoint.svelte +98 -26
  179. package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
  180. package/dist/plot/SpacegroupBarPlot.svelte +142 -85
  181. package/dist/plot/Surface3D.svelte +159 -108
  182. package/dist/plot/ZeroLines.svelte +55 -3
  183. package/dist/plot/ZoomRect.svelte +4 -2
  184. package/dist/plot/axis-utils.js +1 -3
  185. package/dist/plot/data-cleaning.js +12 -28
  186. package/dist/plot/data-transform.js +2 -1
  187. package/dist/plot/fill-utils.js +2 -0
  188. package/dist/plot/layout.d.ts +4 -1
  189. package/dist/plot/layout.js +33 -14
  190. package/dist/plot/reference-line.d.ts +2 -2
  191. package/dist/plot/reference-line.js +7 -5
  192. package/dist/plot/scales.js +24 -36
  193. package/dist/plot/types.d.ts +11 -23
  194. package/dist/plot/types.js +6 -11
  195. package/dist/plot/utils/label-placement.d.ts +32 -15
  196. package/dist/plot/utils/label-placement.js +227 -66
  197. package/dist/plot/utils/series-visibility.js +2 -3
  198. package/dist/rdf/RdfPlot.svelte +143 -91
  199. package/dist/rdf/calc-rdf.js +4 -5
  200. package/dist/sanitize.d.ts +4 -0
  201. package/dist/sanitize.js +107 -0
  202. package/dist/settings.d.ts +18 -6
  203. package/dist/settings.js +46 -16
  204. package/dist/spectral/Bands.svelte +632 -453
  205. package/dist/spectral/BandsAndDos.svelte +90 -49
  206. package/dist/spectral/BrillouinBandsDos.svelte +151 -93
  207. package/dist/spectral/Dos.svelte +389 -258
  208. package/dist/spectral/helpers.js +55 -43
  209. package/dist/state.svelte.d.ts +1 -1
  210. package/dist/state.svelte.js +3 -2
  211. package/dist/structure/Arrow.svelte +59 -20
  212. package/dist/structure/AtomLegend.svelte +215 -134
  213. package/dist/structure/Bond.svelte +73 -47
  214. package/dist/structure/CanvasTooltip.svelte +10 -2
  215. package/dist/structure/CellSelect.svelte +72 -45
  216. package/dist/structure/Cylinder.svelte +33 -17
  217. package/dist/structure/Lattice.svelte +88 -33
  218. package/dist/structure/Structure.svelte +1063 -797
  219. package/dist/structure/Structure.svelte.d.ts +1 -1
  220. package/dist/structure/StructureControls.svelte +349 -118
  221. package/dist/structure/StructureExportPane.svelte +124 -89
  222. package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
  223. package/dist/structure/StructureInfoPane.svelte +304 -237
  224. package/dist/structure/StructureScene.svelte +879 -443
  225. package/dist/structure/StructureScene.svelte.d.ts +15 -7
  226. package/dist/structure/atom-properties.js +8 -8
  227. package/dist/structure/bonding.js +6 -7
  228. package/dist/structure/export.js +14 -29
  229. package/dist/structure/ferrox-wasm.js +1 -1
  230. package/dist/structure/index.d.ts +13 -3
  231. package/dist/structure/index.js +83 -23
  232. package/dist/structure/measure.d.ts +2 -2
  233. package/dist/structure/measure.js +4 -44
  234. package/dist/structure/parse.js +113 -141
  235. package/dist/structure/partial-occupancy.js +7 -10
  236. package/dist/structure/pbc.d.ts +1 -0
  237. package/dist/structure/pbc.js +16 -6
  238. package/dist/structure/supercell.d.ts +2 -2
  239. package/dist/structure/supercell.js +12 -22
  240. package/dist/structure/validation.js +1 -2
  241. package/dist/symmetry/SymmetryStats.svelte +84 -41
  242. package/dist/symmetry/WyckoffTable.svelte +26 -6
  243. package/dist/symmetry/cell-transform.js +5 -3
  244. package/dist/symmetry/index.js +8 -7
  245. package/dist/symmetry/spacegroups.js +148 -148
  246. package/dist/table/HeatmapTable.svelte +790 -554
  247. package/dist/table/HeatmapTable.svelte.d.ts +1 -1
  248. package/dist/table/ToggleMenu.svelte +125 -92
  249. package/dist/table/index.js +2 -4
  250. package/dist/theme/ThemeControl.svelte +21 -12
  251. package/dist/time.js +4 -1
  252. package/dist/tooltip/TooltipContent.svelte +33 -8
  253. package/dist/trajectory/Trajectory.svelte +758 -558
  254. package/dist/trajectory/TrajectoryError.svelte +14 -3
  255. package/dist/trajectory/TrajectoryExportPane.svelte +137 -83
  256. package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
  257. package/dist/trajectory/extract.js +10 -26
  258. package/dist/trajectory/format-detect.js +5 -5
  259. package/dist/trajectory/frame-reader.d.ts +1 -1
  260. package/dist/trajectory/frame-reader.js +5 -12
  261. package/dist/trajectory/helpers.d.ts +0 -1
  262. package/dist/trajectory/helpers.js +2 -17
  263. package/dist/trajectory/index.js +14 -12
  264. package/dist/trajectory/parse/ase.js +5 -4
  265. package/dist/trajectory/parse/hdf5.js +26 -18
  266. package/dist/trajectory/parse/index.js +13 -18
  267. package/dist/trajectory/parse/lammps.js +17 -7
  268. package/dist/trajectory/parse/vasp.js +5 -2
  269. package/dist/trajectory/parse/xyz.js +8 -7
  270. package/dist/trajectory/plotting.js +13 -8
  271. package/dist/utils.d.ts +1 -0
  272. package/dist/utils.js +13 -0
  273. package/dist/xrd/XrdPlot.svelte +337 -247
  274. package/dist/xrd/broadening.js +14 -9
  275. package/dist/xrd/calc-xrd.js +12 -18
  276. package/dist/xrd/parse.d.ts +1 -1
  277. package/dist/xrd/parse.js +17 -17
  278. package/package.json +99 -103
  279. package/readme.md +1 -1
  280. /package/dist/theme/{themes.js → themes.mjs} +0 -0
package/dist/io/export.js CHANGED
@@ -1,182 +1,161 @@
1
1
  import { download } from './fetch';
2
2
  import { create_structure_filename } from '../structure/export';
3
- import { Vector2, WebGLRenderer } from 'three';
3
+ import { Vector2 } from 'three';
4
4
  function is_webgl_renderer_like(value) {
5
5
  if (typeof value !== `object` || !value)
6
6
  return false;
7
7
  const renderer_obj = value;
8
- return typeof renderer_obj.render === `function` &&
8
+ return (typeof renderer_obj.render === `function` &&
9
9
  typeof renderer_obj.getPixelRatio === `function` &&
10
10
  typeof renderer_obj.setPixelRatio === `function` &&
11
11
  typeof renderer_obj.getSize === `function` &&
12
- typeof renderer_obj.setSize === `function`;
12
+ typeof renderer_obj.setSize === `function`);
13
13
  }
14
- // Export structure as PNG image from canvas
15
- export function export_canvas_as_png(canvas, structure_or_filename, png_dpi = 150, scene = null, camera = null) {
16
- try {
17
- if (!canvas) {
18
- if (typeof window !== `undefined`) {
19
- console.warn(`Canvas not found for PNG export`);
20
- }
21
- return;
22
- }
23
- // Determine filename from either structure or direct filename
24
- let filename = typeof structure_or_filename === `string`
25
- ? structure_or_filename
26
- : create_structure_filename(structure_or_filename, `png`);
27
- // Inject DPI into filename
28
- const suffix = `-${Math.round(png_dpi)}dpi`;
29
- if (filename.toLowerCase().endsWith(`.png`)) {
30
- filename = filename.replace(/\.png$/i, `${suffix}.png`);
31
- }
32
- else {
33
- filename = `${filename}${suffix}.png`;
34
- }
35
- // Convert DPI to multiplier (72 DPI is baseline web resolution)
36
- // Cap to a reasonable upper bound to avoid excessive memory use
37
- const resolution_multiplier = Math.min(png_dpi / 72, 10);
38
- const renderer_val = canvas.__renderer;
39
- const renderer = is_webgl_renderer_like(renderer_val) ? renderer_val : undefined;
40
- // Force render to populate buffer
14
+ // Capture a WebGL canvas as a PNG Blob at the given DPI.
15
+ // Temporarily adjusts renderer pixel ratio for high-res capture, then restores.
16
+ // Returns data directly (no browser download), suitable for programmatic capture
17
+ // in test suites, server-side rendering, or Python widget integration via anywidget.
18
+ // DPI is converted to a resolution multiplier relative to 72 DPI baseline, capped at 10x.
19
+ export function canvas_to_png_blob(canvas, png_dpi = 150, scene = null, camera = null) {
20
+ const resolution_multiplier = Math.min(png_dpi / 72, 10);
21
+ const renderer_val = canvas.__renderer;
22
+ const renderer = is_webgl_renderer_like(renderer_val) ? renderer_val : undefined;
23
+ if (resolution_multiplier <= 1.1 || !renderer) {
41
24
  if (renderer && scene && camera)
42
25
  renderer.render(scene, camera);
43
- if (resolution_multiplier <= 1.1 || !renderer) {
44
- // Direct capture at current resolution (if DPI is close to 72 or renderer not available)
26
+ return new Promise((resolve, reject) => {
45
27
  try {
46
28
  canvas.toBlob((blob) => {
47
- if (blob) {
48
- download(blob, filename, `image/png`);
49
- }
50
- else {
51
- if (typeof window !== `undefined`) {
52
- console.warn(`Failed to generate PNG - canvas may be empty`);
53
- }
54
- }
29
+ if (blob)
30
+ resolve(blob);
31
+ else
32
+ reject(new Error(`Failed to generate PNG - canvas may be empty`));
55
33
  }, `image/png`);
56
34
  }
57
35
  catch (error) {
58
- console.error(`Error during PNG export:`, error);
36
+ reject(error);
59
37
  }
60
- return;
61
- }
62
- // Temporarily modify the renderer's pixel ratio for high-res capture
63
- const orig_pixel_ratio = renderer.getPixelRatio();
64
- const orig_size = renderer.getSize(new Vector2());
38
+ });
39
+ }
40
+ // Temporarily modify the renderer's pixel ratio for high-res capture
41
+ const orig_pixel_ratio = renderer.getPixelRatio();
42
+ const orig_size = renderer.getSize(new Vector2());
43
+ const restore = () => {
44
+ renderer.setPixelRatio(orig_pixel_ratio);
45
+ renderer.setSize(orig_size.width, orig_size.height, false);
46
+ };
47
+ renderer.setPixelRatio(resolution_multiplier);
48
+ renderer.setSize(orig_size.width, orig_size.height, false);
49
+ if (scene && camera)
50
+ renderer.render(scene, camera);
51
+ return new Promise((resolve, reject) => {
65
52
  try {
66
- // Set higher pixel ratio to increase rendering resolution
67
- renderer.setPixelRatio(resolution_multiplier);
68
- // Force the canvas to update its resolution
69
- renderer.setSize(orig_size.width, orig_size.height, false);
70
- if (scene && camera) {
71
- renderer.render(scene, camera);
72
- }
73
- // Capture the high-resolution render after paint completion
74
53
  canvas.toBlob((blob) => {
75
- // Restore original settings immediately
76
- renderer.setPixelRatio(orig_pixel_ratio);
77
- renderer.setSize(orig_size.width, orig_size.height, false);
78
- if (blob) {
79
- download(blob, filename, `image/png`);
80
- }
81
- else {
82
- if (typeof window !== `undefined`) {
83
- console.warn(`Failed to generate high-resolution PNG`);
84
- }
85
- }
54
+ restore();
55
+ if (blob)
56
+ resolve(blob);
57
+ else
58
+ reject(new Error(`Failed to generate high-resolution PNG`));
86
59
  }, `image/png`);
87
60
  }
88
61
  catch (error) {
89
- console.error(`Error during high-res rendering:`, error);
90
- // Restore original settings
91
- renderer.setPixelRatio(orig_pixel_ratio);
92
- renderer.setSize(orig_size.width, orig_size.height, false);
93
- if (typeof window !== `undefined`) {
94
- console.warn(`Failed to render at high resolution: ${error instanceof Error ? error.message : String(error)}`);
95
- }
62
+ restore();
63
+ reject(error);
96
64
  }
65
+ });
66
+ }
67
+ // Export structure as PNG image from canvas (triggers browser download)
68
+ export function export_canvas_as_png(canvas, structure_or_filename, png_dpi = 150, scene = null, camera = null) {
69
+ if (!canvas) {
70
+ if (typeof window !== `undefined`)
71
+ console.warn(`Canvas not found for PNG export`);
72
+ return;
97
73
  }
98
- catch (error) {
99
- console.error(`Error exporting PNG:`, error);
74
+ let filename = typeof structure_or_filename === `string`
75
+ ? structure_or_filename
76
+ : create_structure_filename(structure_or_filename, `png`);
77
+ const suffix = `-${Math.round(png_dpi)}dpi`;
78
+ if (filename.toLowerCase().endsWith(`.png`)) {
79
+ filename = filename.replace(/\.png$/i, `${suffix}.png`);
80
+ }
81
+ else {
82
+ filename = `${filename}${suffix}.png`;
100
83
  }
84
+ canvas_to_png_blob(canvas, png_dpi, scene, camera)
85
+ .then((blob) => download(blob, filename, `image/png`))
86
+ .catch((error) => console.error(`Error exporting PNG:`, error));
101
87
  }
102
88
  // Helper to ensure font-family is set on SVG root
103
89
  function set_svg_font_family(svg) {
104
90
  const style = svg.getAttribute(`style`) || ``;
105
91
  if (!/font-family/.test(style)) {
106
- svg.setAttribute(`style`, `${style};font-family:sans-serif;`);
92
+ svg.setAttribute(`style`, `${style}${style ? `;` : ``}font-family:sans-serif;`);
107
93
  }
108
94
  // Also set as attribute for extra robustness
109
95
  svg.setAttribute(`font-family`, `sans-serif`);
110
96
  }
111
- // Export SVG element as SVG file
97
+ // Serialize an SVG element to a standalone SVG string with proper XML headers.
98
+ // Clones the element to avoid mutation, sets font-family/xmlns, and
99
+ // prepends XML declaration + SVG DOCTYPE. Returns a complete SVG document string
100
+ // suitable for saving to file or further processing.
101
+ export function svg_to_svg_string(svg_element) {
102
+ const cloned_svg = svg_element.cloneNode(true);
103
+ set_svg_font_family(cloned_svg);
104
+ if (!cloned_svg.hasAttribute(`xmlns`)) {
105
+ cloned_svg.setAttribute(`xmlns`, `http://www.w3.org/2000/svg`);
106
+ }
107
+ const svg_string = new XMLSerializer().serializeToString(cloned_svg);
108
+ return `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n${svg_string}`;
109
+ }
110
+ // Export SVG element as SVG file (triggers browser download)
112
111
  export function export_svg_as_svg(svg_element, filename) {
112
+ if (!svg_element) {
113
+ console.warn(`SVG element not found for export`);
114
+ return;
115
+ }
113
116
  try {
114
- if (!svg_element) {
115
- console.warn(`SVG element not found for export`);
116
- return;
117
- }
118
- // Clone the SVG to avoid modifying the original
119
- const cloned_svg = svg_element.cloneNode(true);
120
- // Ensure the SVG has proper dimensions and viewBox
121
- const viewBox = svg_element.getAttribute(`viewBox`);
122
- if (viewBox)
123
- cloned_svg.setAttribute(`viewBox`, viewBox);
124
- // Ensure font-family is set
125
- set_svg_font_family(cloned_svg);
126
- // Ensure xmlns is set
127
- if (!cloned_svg.hasAttribute(`xmlns`)) {
128
- cloned_svg.setAttribute(`xmlns`, `http://www.w3.org/2000/svg`);
129
- }
130
- // Convert SVG to string
131
- const svg_string = new XMLSerializer().serializeToString(cloned_svg);
132
- // Add XML declaration and DOCTYPE for proper SVG format
133
- const svg_content = `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n${svg_string}`;
117
+ const svg_content = svg_to_svg_string(svg_element);
134
118
  download(svg_content, filename, `image/svg+xml;charset=utf-8`);
135
119
  }
136
120
  catch (error) {
137
121
  console.error(`Error exporting SVG:`, error);
138
122
  }
139
123
  }
140
- // Export SVG element as PNG by converting to canvas
141
- export function export_svg_as_png(svg_element, filename, png_dpi = 150) {
142
- try {
143
- if (!svg_element) {
144
- console.warn(`SVG element not found for PNG export`);
145
- return;
146
- }
147
- // Get SVG dimensions
148
- const viewBox = svg_element.getAttribute(`viewBox`);
149
- if (!viewBox) {
150
- console.warn(`SVG viewBox not found for PNG export`);
151
- return;
152
- }
153
- const [, , width, height] = viewBox.split(` `).map(Number);
154
- if (!width || !height) {
155
- console.warn(`Invalid SVG dimensions for PNG export`);
156
- return;
157
- }
158
- // Convert DPI to pixel dimensions
159
- const resolution_multiplier = Math.min(png_dpi / 72, 10);
160
- const pixel_width = Math.round(width * resolution_multiplier);
161
- const pixel_height = Math.round(height * resolution_multiplier);
162
- // Create a canvas for rendering
163
- const canvas = document.createElement(`canvas`);
164
- const ctx = canvas.getContext(`2d`);
165
- if (!ctx) {
166
- console.warn(`Canvas 2D context not available for PNG export`);
167
- return;
168
- }
169
- // Set canvas dimensions
170
- canvas.width = pixel_width;
171
- canvas.height = pixel_height;
172
- // Clone and patch SVG for font-family
173
- const cloned_svg = svg_element.cloneNode(true);
174
- set_svg_font_family(cloned_svg);
175
- // Create an object URL from SVG Blob
176
- const svg_string = new XMLSerializer().serializeToString(cloned_svg);
177
- const svg_blob = new Blob([svg_string], { type: `image/svg+xml;charset=utf-8` });
178
- const svg_data_url = URL.createObjectURL(svg_blob);
179
- // Create an image element to load the SVG
124
+ // Rasterize an SVG element to a PNG Blob at the given DPI.
125
+ // Creates an offscreen canvas at the scaled resolution, draws the SVG via an
126
+ // Image element, and returns the resulting PNG Blob. Rejects if viewBox is
127
+ // missing or dimensions are invalid (zero width/height).
128
+ // DPI is converted to a resolution multiplier relative to 72 DPI baseline, capped at 10x.
129
+ export function svg_to_png_blob(svg_element, png_dpi = 150) {
130
+ const viewBox = svg_element.getAttribute(`viewBox`)?.trim();
131
+ if (!viewBox)
132
+ return Promise.reject(new Error(`SVG viewBox not found for PNG export`));
133
+ const parts = viewBox.split(/[\s,]+/).map(Number);
134
+ if (parts.length < 4 || !parts.every(Number.isFinite)) {
135
+ return Promise.reject(new Error(`Invalid SVG dimensions for PNG export`));
136
+ }
137
+ const [, , width, height] = parts;
138
+ if (!(width > 0) || !(height > 0)) {
139
+ return Promise.reject(new Error(`Invalid SVG dimensions for PNG export`));
140
+ }
141
+ if (!Number.isFinite(png_dpi) || png_dpi <= 0) {
142
+ return Promise.reject(new Error(`Invalid PNG DPI for export`));
143
+ }
144
+ const resolution_multiplier = Math.min(png_dpi / 72, 10);
145
+ const pixel_width = Math.round(width * resolution_multiplier);
146
+ const pixel_height = Math.round(height * resolution_multiplier);
147
+ const canvas = document.createElement(`canvas`);
148
+ const ctx = canvas.getContext(`2d`);
149
+ if (!ctx)
150
+ return Promise.reject(new Error(`Canvas 2D context not available`));
151
+ canvas.width = pixel_width;
152
+ canvas.height = pixel_height;
153
+ const cloned_svg = svg_element.cloneNode(true);
154
+ set_svg_font_family(cloned_svg);
155
+ const serialized = new XMLSerializer().serializeToString(cloned_svg);
156
+ const svg_blob = new Blob([serialized], { type: `image/svg+xml;charset=utf-8` });
157
+ const svg_data_url = URL.createObjectURL(svg_blob);
158
+ return new Promise((resolve, reject) => {
180
159
  const img = new Image();
181
160
  img.onload = () => {
182
161
  try {
@@ -184,27 +163,34 @@ export function export_svg_as_png(svg_element, filename, png_dpi = 150) {
184
163
  ctx.drawImage(img, 0, 0, pixel_width, pixel_height);
185
164
  canvas.toBlob((blob) => {
186
165
  if (blob)
187
- download(blob, filename, `image/png`);
166
+ resolve(blob);
188
167
  else
189
- console.warn(`Failed to generate PNG blob`);
168
+ reject(new Error(`Failed to generate PNG blob`));
190
169
  }, `image/png`, 1);
191
170
  }
192
171
  catch (error) {
193
- console.error(`Error during PNG generation:`, error);
172
+ reject(error);
194
173
  }
195
174
  finally {
196
175
  URL.revokeObjectURL(svg_data_url);
197
176
  }
198
177
  };
199
178
  img.onerror = () => {
200
- console.error(`Failed to load SVG for PNG export`);
201
179
  URL.revokeObjectURL(svg_data_url);
180
+ reject(new Error(`Failed to load SVG for PNG export`));
202
181
  };
203
182
  img.src = svg_data_url;
183
+ });
184
+ }
185
+ // Export SVG element as PNG (triggers browser download)
186
+ export function export_svg_as_png(svg_element, filename, png_dpi = 150) {
187
+ if (!svg_element) {
188
+ console.warn(`SVG element not found for PNG export`);
189
+ return;
204
190
  }
205
- catch (error) {
206
- console.error(`Error exporting PNG:`, error);
207
- }
191
+ svg_to_png_blob(svg_element, png_dpi)
192
+ .then((blob) => download(blob, filename, `image/png`))
193
+ .catch((error) => console.error(`Error exporting PNG:`, error));
208
194
  }
209
195
  // Generate FFmpeg command for WebM to MP4 conversion
210
196
  export function get_ffmpeg_conversion_command(input_filename) {
@@ -1,6 +1,5 @@
1
1
  // Simplified binary detection
2
2
  export const is_binary = (content) => content.includes(`\0`) ||
3
- // deno-lint-ignore no-control-regex
4
- (content.match(/[\u0000-\u0008\u000E-\u001F\u007F-\u00FF]/g) || []).length /
5
- content.length > 0.1 ||
3
+ (content.match(/[\u0000-\u0008\u000E-\u001F\u007F-\u00FF]/g) || []).length / content.length >
4
+ 0.1 ||
6
5
  (content.match(/[\u0020-\u007E]/g) || []).length / content.length < 0.7;
@@ -97,8 +97,7 @@ export async function load_from_url(url, callback) {
97
97
  if (head.ok) {
98
98
  const buf = new Uint8Array(await head.arrayBuffer());
99
99
  const is_gzip = buf[0] === 0x1f && buf[1] === 0x8b;
100
- const is_hdf5 = buf[0] === 0x89 && buf[1] === 0x48 && buf[2] === 0x44 &&
101
- buf[3] === 0x46;
100
+ const is_hdf5 = buf[0] === 0x89 && buf[1] === 0x48 && buf[2] === 0x44 && buf[3] === 0x46;
102
101
  if (is_gzip || is_hdf5) {
103
102
  const resp = await fetch(url);
104
103
  if (!resp.ok)