react-cheminfo 0.66.0 → 0.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -11
- package/lib/download/ui/FigureDownloadPanel.d.ts +7 -1
- package/lib/download/ui/FigureDownloadPanel.d.ts.map +1 -1
- package/lib/download/ui/FigureDownloadPanel.js +2 -2
- package/lib/download/ui/FigureDownloadPanel.js.map +1 -1
- package/lib/ecosystem/core/sites.js +3 -3
- package/lib/ecosystem/core/sites.js.map +1 -1
- package/lib/ecosystem/core/types.d.ts +1 -1
- package/lib/ecosystem/core/types.d.ts.map +1 -1
- package/lib/ecosystem/ui/glyphs.js +1 -1
- package/lib/ecosystem/ui/glyphs.js.map +1 -1
- package/lib/i18n/core/chromeCatalog.d.ts +1 -1
- package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
- package/lib/i18n/core/messageCatalog.d.ts +2 -2
- package/lib/i18n/vite/localeCatalog.d.ts +1 -1
- package/lib/ir/core/index.d.ts +2 -2
- package/lib/ir/core/index.d.ts.map +1 -1
- package/lib/ir/core/index.js.map +1 -1
- package/lib/ir/core/irEditorApi.d.ts +12 -0
- package/lib/ir/core/irEditorApi.d.ts.map +1 -1
- package/lib/ir/core/irReducer.d.ts +2 -2
- package/lib/ir/core/irReducer.d.ts.map +1 -1
- package/lib/ir/core/irSidePanels.d.ts +7 -1
- package/lib/ir/core/irSidePanels.d.ts.map +1 -1
- package/lib/ir/core/irState.d.ts +8 -5
- package/lib/ir/core/irState.d.ts.map +1 -1
- package/lib/ir/core/irState.js.map +1 -1
- package/lib/ir/ui/IrCanvas.d.ts +7 -0
- package/lib/ir/ui/IrCanvas.d.ts.map +1 -1
- package/lib/ir/ui/IrCanvas.js +8 -3
- package/lib/ir/ui/IrCanvas.js.map +1 -1
- package/lib/ir/ui/IrChart.d.ts.map +1 -1
- package/lib/ir/ui/IrChart.js +5 -1
- package/lib/ir/ui/IrChart.js.map +1 -1
- package/lib/ir/ui/IrEditor.d.ts +22 -4
- package/lib/ir/ui/IrEditor.d.ts.map +1 -1
- package/lib/ir/ui/IrEditor.js.map +1 -1
- package/lib/ir/ui/IrShell.d.ts +9 -1
- package/lib/ir/ui/IrShell.d.ts.map +1 -1
- package/lib/ir/ui/IrShell.js +4 -19
- package/lib/ir/ui/IrShell.js.map +1 -1
- package/lib/ir/ui/IrSidePanel.d.ts +11 -0
- package/lib/ir/ui/IrSidePanel.d.ts.map +1 -1
- package/lib/ir/ui/IrSidePanel.js +12 -3
- package/lib/ir/ui/IrSidePanel.js.map +1 -1
- package/lib/ir/ui/RamanEditor.d.ts +22 -4
- package/lib/ir/ui/RamanEditor.d.ts.map +1 -1
- package/lib/ir/ui/RamanEditor.js.map +1 -1
- package/lib/ir/ui/VibrationalEditor.d.ts +18 -4
- package/lib/ir/ui/VibrationalEditor.d.ts.map +1 -1
- package/lib/ir/ui/VibrationalEditor.js +4 -2
- package/lib/ir/ui/VibrationalEditor.js.map +1 -1
- package/lib/ir/ui/index.d.ts +1 -0
- package/lib/ir/ui/index.d.ts.map +1 -1
- package/lib/ir/ui/index.js.map +1 -1
- package/lib/ir/ui/irActions.d.ts +3 -3
- package/lib/ir/ui/irActions.d.ts.map +1 -1
- package/lib/ir/ui/irChartProps.d.ts +7 -0
- package/lib/ir/ui/irChartProps.d.ts.map +1 -1
- package/lib/ir/ui/irHostPanels.d.ts +53 -0
- package/lib/ir/ui/irHostPanels.d.ts.map +1 -0
- package/lib/ir/ui/irHostPanels.js +41 -0
- package/lib/ir/ui/irHostPanels.js.map +1 -0
- package/lib/ir/ui/useIrSide.d.ts +27 -0
- package/lib/ir/ui/useIrSide.d.ts.map +1 -0
- package/lib/ir/ui/useIrSide.js +42 -0
- package/lib/ir/ui/useIrSide.js.map +1 -0
- package/lib/ir/ui/useIrZoom.d.ts +8 -0
- package/lib/ir/ui/useIrZoom.d.ts.map +1 -1
- package/lib/ir/ui/useIrZoom.js +2 -1
- package/lib/ir/ui/useIrZoom.js.map +1 -1
- package/lib/locales/de.json +9 -1
- package/lib/locales/en.json +9 -1
- package/lib/locales/es.json +9 -1
- package/lib/locales/fr.json +9 -1
- package/lib/locales/it.json +9 -1
- package/lib/molecule3d/core/animatedPng.d.ts +25 -0
- package/lib/molecule3d/core/animatedPng.d.ts.map +1 -0
- package/lib/molecule3d/core/animatedPng.js +59 -0
- package/lib/molecule3d/core/animatedPng.js.map +1 -0
- package/lib/molecule3d/core/file.d.ts +24 -0
- package/lib/molecule3d/core/file.d.ts.map +1 -0
- package/lib/molecule3d/core/file.js +2 -0
- package/lib/molecule3d/core/file.js.map +1 -0
- package/lib/molecule3d/core/index.d.ts +4 -1
- package/lib/molecule3d/core/index.d.ts.map +1 -1
- package/lib/molecule3d/core/index.js +1 -0
- package/lib/molecule3d/core/index.js.map +1 -1
- package/lib/molecule3d/core/settings.d.ts +0 -7
- package/lib/molecule3d/core/settings.d.ts.map +1 -1
- package/lib/molecule3d/core/settings.js.map +1 -1
- package/lib/molecule3d/ui/Molecule3DExport.d.ts +10 -2
- package/lib/molecule3d/ui/Molecule3DExport.d.ts.map +1 -1
- package/lib/molecule3d/ui/Molecule3DExport.js +15 -2
- package/lib/molecule3d/ui/Molecule3DExport.js.map +1 -1
- package/lib/molecule3d/ui/Molecule3DToolbar.js +9 -2
- package/lib/molecule3d/ui/Molecule3DToolbar.js.map +1 -1
- package/lib/molecule3d/ui/MoleculeCanvas3D.d.ts.map +1 -1
- package/lib/molecule3d/ui/MoleculeCanvas3D.js +23 -41
- package/lib/molecule3d/ui/MoleculeCanvas3D.js.map +1 -1
- package/lib/molecule3d/ui/drawScene.d.ts +2 -1
- package/lib/molecule3d/ui/drawScene.d.ts.map +1 -1
- package/lib/molecule3d/ui/drawScene.js.map +1 -1
- package/lib/molecule3d/ui/exportMoleculeImage.d.ts +15 -1
- package/lib/molecule3d/ui/exportMoleculeImage.d.ts.map +1 -1
- package/lib/molecule3d/ui/exportMoleculeImage.js +14 -2
- package/lib/molecule3d/ui/exportMoleculeImage.js.map +1 -1
- package/lib/molecule3d/ui/moleculeFrames.d.ts +47 -0
- package/lib/molecule3d/ui/moleculeFrames.d.ts.map +1 -0
- package/lib/molecule3d/ui/moleculeFrames.js +65 -0
- package/lib/molecule3d/ui/moleculeFrames.js.map +1 -0
- package/lib/molecule3d/ui/moleculeViewer3DProps.d.ts +14 -1
- package/lib/molecule3d/ui/moleculeViewer3DProps.d.ts.map +1 -1
- package/lib/molecule3d/ui/renderMolecule.d.ts +3 -3
- package/lib/molecule3d/ui/renderMolecule.d.ts.map +1 -1
- package/lib/molecule3d/ui/renderMolecule.js +2 -1
- package/lib/molecule3d/ui/renderMolecule.js.map +1 -1
- package/lib/molecule3d/ui/sceneDrawer.d.ts +2 -1
- package/lib/molecule3d/ui/sceneDrawer.d.ts.map +1 -1
- package/lib/molecule3d/ui/sceneDrawer.js +9 -1
- package/lib/molecule3d/ui/sceneDrawer.js.map +1 -1
- package/lib/molecule3d/ui/useImageExport.d.ts +8 -3
- package/lib/molecule3d/ui/useImageExport.d.ts.map +1 -1
- package/lib/molecule3d/ui/useImageExport.js +13 -4
- package/lib/molecule3d/ui/useImageExport.js.map +1 -1
- package/lib/molecule3d/ui/useMoleculeScene.d.ts +2 -1
- package/lib/molecule3d/ui/useMoleculeScene.d.ts.map +1 -1
- package/lib/molecule3d/ui/useMoleculeScene.js.map +1 -1
- package/lib/molecule3d/ui/useMoleculeViewer.d.ts +16 -0
- package/lib/molecule3d/ui/useMoleculeViewer.d.ts.map +1 -0
- package/lib/molecule3d/ui/useMoleculeViewer.js +37 -0
- package/lib/molecule3d/ui/useMoleculeViewer.js.map +1 -0
- package/lib/molecule3d/ui/usePolarSurfaceArea.d.ts +3 -3
- package/lib/molecule3d/ui/usePolarSurfaceArea.d.ts.map +1 -1
- package/lib/molecule3d/ui/usePolarSurfaceArea.js +4 -3
- package/lib/molecule3d/ui/usePolarSurfaceArea.js.map +1 -1
- package/lib/molecule3d/ui/viewer.d.ts +4 -1
- package/lib/molecule3d/ui/viewer.d.ts.map +1 -1
- package/lib/molecule3d/ui/viewer.js +21 -19
- package/lib/molecule3d/ui/viewer.js.map +1 -1
- package/lib/molecule3d/ui/viewerTypes.d.ts +6 -0
- package/lib/molecule3d/ui/viewerTypes.d.ts.map +1 -1
- package/lib/molstar/core/captureScene.d.ts +19 -1
- package/lib/molstar/core/captureScene.d.ts.map +1 -1
- package/lib/molstar/core/captureScene.js +57 -2
- package/lib/molstar/core/captureScene.js.map +1 -1
- package/lib/molstar/core/index.d.ts +5 -2
- package/lib/molstar/core/index.d.ts.map +1 -1
- package/lib/molstar/core/index.js +4 -2
- package/lib/molstar/core/index.js.map +1 -1
- package/lib/molstar/core/trajectory.d.ts +56 -0
- package/lib/molstar/core/trajectory.d.ts.map +1 -0
- package/lib/molstar/core/trajectory.js +96 -0
- package/lib/molstar/core/trajectory.js.map +1 -0
- package/lib/panel/ui/PanelRail.d.ts.map +1 -1
- package/lib/panel/ui/PanelRail.js +1 -1
- package/lib/panel/ui/PanelRail.js.map +1 -1
- package/lib/raman.d.ts +2 -1
- package/lib/raman.d.ts.map +1 -1
- package/lib/raman.js.map +1 -1
- package/lib/router/core/basePath.d.ts +2 -2
- package/lib/router/core/basePath.js +2 -2
- package/lib/router/core/siteAddresses.d.ts +1 -1
- package/lib/router/core/siteAddresses.js +1 -1
- package/lib/seo/core/siteFiles.js +1 -1
- package/lib/seo/core/siteFiles.js.map +1 -1
- package/lib/server/answerAddresses.d.ts +3 -3
- package/lib/server/answerAddresses.js +3 -3
- package/lib/structure/core/index.d.ts +2 -0
- package/lib/structure/core/index.d.ts.map +1 -1
- package/lib/structure/core/index.js +1 -0
- package/lib/structure/core/index.js.map +1 -1
- package/lib/structure/core/structureTransfer.d.ts +29 -0
- package/lib/structure/core/structureTransfer.d.ts.map +1 -0
- package/lib/structure/core/structureTransfer.js +26 -0
- package/lib/structure/core/structureTransfer.js.map +1 -0
- package/lib/structure/ui/EditorImportButton.d.ts +20 -0
- package/lib/structure/ui/EditorImportButton.d.ts.map +1 -0
- package/lib/structure/ui/EditorImportButton.js +24 -0
- package/lib/structure/ui/EditorImportButton.js.map +1 -0
- package/lib/structure/ui/EditorImportProblem.d.ts +17 -0
- package/lib/structure/ui/EditorImportProblem.d.ts.map +1 -0
- package/lib/structure/ui/EditorImportProblem.js +32 -0
- package/lib/structure/ui/EditorImportProblem.js.map +1 -0
- package/lib/structure/ui/StructureDrop.d.ts +39 -0
- package/lib/structure/ui/StructureDrop.d.ts.map +1 -0
- package/lib/structure/ui/StructureDrop.js +19 -0
- package/lib/structure/ui/StructureDrop.js.map +1 -0
- package/lib/structure/ui/StructureDropOverlay.d.ts +16 -0
- package/lib/structure/ui/StructureDropOverlay.d.ts.map +1 -0
- package/lib/structure/ui/StructureDropOverlay.js +29 -0
- package/lib/structure/ui/StructureDropOverlay.js.map +1 -0
- package/lib/structure/ui/StructureEditor.d.ts +3 -80
- package/lib/structure/ui/StructureEditor.d.ts.map +1 -1
- package/lib/structure/ui/StructureEditor.js +21 -33
- package/lib/structure/ui/StructureEditor.js.map +1 -1
- package/lib/structure/ui/importedStructure.d.ts +25 -0
- package/lib/structure/ui/importedStructure.d.ts.map +1 -0
- package/lib/structure/ui/importedStructure.js +74 -0
- package/lib/structure/ui/importedStructure.js.map +1 -0
- package/lib/structure/ui/index.d.ts +2 -0
- package/lib/structure/ui/index.d.ts.map +1 -1
- package/lib/structure/ui/index.js +1 -0
- package/lib/structure/ui/index.js.map +1 -1
- package/lib/structure/ui/structureEditorProps.d.ts +98 -0
- package/lib/structure/ui/structureEditorProps.d.ts.map +1 -0
- package/lib/structure/ui/structureEditorProps.js +2 -0
- package/lib/structure/ui/structureEditorProps.js.map +1 -0
- package/lib/structure/ui/useDebouncedChange.d.ts +16 -0
- package/lib/structure/ui/useDebouncedChange.d.ts.map +1 -0
- package/lib/structure/ui/useDebouncedChange.js +30 -0
- package/lib/structure/ui/useDebouncedChange.js.map +1 -0
- package/lib/structure/ui/useStructureDrop.d.ts +30 -0
- package/lib/structure/ui/useStructureDrop.d.ts.map +1 -0
- package/lib/structure/ui/useStructureDrop.js +49 -0
- package/lib/structure/ui/useStructureDrop.js.map +1 -0
- package/lib/structure/ui/useStructureImport.d.ts +45 -0
- package/lib/structure/ui/useStructureImport.d.ts.map +1 -0
- package/lib/structure/ui/useStructureImport.js +62 -0
- package/lib/structure/ui/useStructureImport.js.map +1 -0
- package/package.json +4 -1
- package/src/download/ui/FigureDownloadPanel.tsx +9 -2
- package/src/ecosystem/core/sites.ts +3 -3
- package/src/ecosystem/core/types.ts +1 -1
- package/src/ecosystem/ui/glyphs.tsx +1 -1
- package/src/i18n/core/messageCatalog.ts +2 -2
- package/src/i18n/vite/localeCatalog.ts +1 -1
- package/src/ir/core/index.ts +6 -2
- package/src/ir/core/irEditorApi.ts +13 -0
- package/src/ir/core/irReducer.ts +2 -2
- package/src/ir/core/irSidePanels.ts +8 -1
- package/src/ir/core/irState.ts +8 -5
- package/src/ir/ui/IrCanvas.tsx +27 -2
- package/src/ir/ui/IrChart.tsx +11 -0
- package/src/ir/ui/IrEditor.tsx +22 -4
- package/src/ir/ui/IrShell.tsx +21 -22
- package/src/ir/ui/IrSidePanel.tsx +36 -10
- package/src/ir/ui/RamanEditor.tsx +22 -4
- package/src/ir/ui/VibrationalEditor.tsx +24 -5
- package/src/ir/ui/index.ts +1 -0
- package/src/ir/ui/irActions.ts +3 -3
- package/src/ir/ui/irChartProps.ts +7 -0
- package/src/ir/ui/irHostPanels.ts +76 -0
- package/src/ir/ui/useIrSide.ts +60 -0
- package/src/ir/ui/useIrZoom.ts +10 -0
- package/src/locales/de.json +9 -1
- package/src/locales/en.json +9 -1
- package/src/locales/es.json +9 -1
- package/src/locales/fr.json +9 -1
- package/src/locales/it.json +9 -1
- package/src/molecule3d/core/animatedPng.ts +79 -0
- package/src/molecule3d/core/file.ts +23 -0
- package/src/molecule3d/core/index.ts +3 -1
- package/src/molecule3d/core/settings.ts +0 -8
- package/src/molecule3d/ui/Molecule3DExport.tsx +43 -5
- package/src/molecule3d/ui/Molecule3DToolbar.tsx +9 -2
- package/src/molecule3d/ui/MoleculeCanvas3D.tsx +24 -38
- package/src/molecule3d/ui/drawScene.ts +2 -1
- package/src/molecule3d/ui/exportMoleculeImage.ts +28 -3
- package/src/molecule3d/ui/moleculeFrames.ts +88 -0
- package/src/molecule3d/ui/moleculeViewer3DProps.ts +14 -5
- package/src/molecule3d/ui/renderMolecule.ts +5 -3
- package/src/molecule3d/ui/sceneDrawer.ts +14 -2
- package/src/molecule3d/ui/useImageExport.ts +22 -5
- package/src/molecule3d/ui/useMoleculeScene.ts +2 -1
- package/src/molecule3d/ui/useMoleculeViewer.ts +46 -0
- package/src/molecule3d/ui/usePolarSurfaceArea.ts +6 -4
- package/src/molecule3d/ui/viewer.ts +34 -27
- package/src/molecule3d/ui/viewerTypes.ts +6 -0
- package/src/molstar/core/captureScene.ts +85 -2
- package/src/molstar/core/index.ts +13 -2
- package/src/molstar/core/trajectory.ts +117 -0
- package/src/panel/ui/PanelRail.tsx +3 -0
- package/src/raman.ts +2 -1
- package/src/router/core/basePath.ts +2 -2
- package/src/router/core/siteAddresses.ts +1 -1
- package/src/seo/core/siteFiles.ts +1 -1
- package/src/server/answerAddresses.ts +3 -3
- package/src/structure/core/index.ts +8 -0
- package/src/structure/core/structureTransfer.ts +37 -0
- package/src/structure/ui/EditorImportButton.tsx +59 -0
- package/src/structure/ui/EditorImportProblem.tsx +63 -0
- package/src/structure/ui/StructureDrop.tsx +66 -0
- package/src/structure/ui/StructureDropOverlay.tsx +50 -0
- package/src/structure/ui/StructureEditor.tsx +49 -134
- package/src/structure/ui/importedStructure.ts +83 -0
- package/src/structure/ui/index.ts +2 -0
- package/src/structure/ui/structureEditorProps.ts +103 -0
- package/src/structure/ui/useDebouncedChange.ts +41 -0
- package/src/structure/ui/useStructureDrop.ts +76 -0
- package/src/structure/ui/useStructureImport.ts +122 -0
package/src/locales/de.json
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"site.nmrium.tagline": "NMR-Spektren im Browser verarbeiten und zuordnen.",
|
|
35
35
|
"site.metabo.tagline": "Eine Reihe von NMR-Spektren in eine Karte Ihrer Proben verwandeln.",
|
|
36
36
|
"site.derepflow.tagline": "Die Bestandteile eines Gemischs aus seinem ¹³C-Spektrum bestimmen.",
|
|
37
|
-
"site.
|
|
37
|
+
"site.isomers.tagline": "Alle Konstitutionsisomere einer Summenformel.",
|
|
38
38
|
"site.tex.tagline": "LaTeX-Formeln, aus einer URL als SVG oder PNG gerendert.",
|
|
39
39
|
"site.lcao.tagline": "Atomorbitale zu Molekülorbitalen kombinieren.",
|
|
40
40
|
"site.regexp.tagline": "Reguläre Ausdrücke in einer interaktiven Spielwiese lernen.",
|
|
@@ -493,6 +493,10 @@
|
|
|
493
493
|
"structure.tool.reactionOnly": "Nur beim Zeichnen einer Reaktion.",
|
|
494
494
|
"structure.tool.unavailable": "In diesem Editor nicht verfügbar.",
|
|
495
495
|
"structure.loadingEditor": "Der Editor wird geladen…",
|
|
496
|
+
"structure.import.open": "Eine Strukturdatei öffnen",
|
|
497
|
+
"structure.import.drop": "Ablegen, um die Struktur zu laden",
|
|
498
|
+
"structure.import.unreadableFile": "{name} enthält weder Molfile noch SMILES noch idCode, die Zeichnung bleibt erhalten.",
|
|
499
|
+
"structure.import.unreadableText": "Dieser Text ist weder ein Molfile noch ein SMILES noch ein idCode, die Zeichnung bleibt erhalten.",
|
|
496
500
|
"structure.export.open": "Struktur exportieren",
|
|
497
501
|
"structure.export.glyph": "Struktur kopieren oder speichern: SMILES, Molfile",
|
|
498
502
|
"structure.export.title": "Struktur exportieren",
|
|
@@ -522,6 +526,10 @@
|
|
|
522
526
|
"molecule3d.molecularSurface": "Moleküloberfläche",
|
|
523
527
|
"molecule3d.displayOptions": "Anzeigeoptionen",
|
|
524
528
|
"molecule3d.exportImage": "Bild exportieren",
|
|
529
|
+
"molecule3d.frames": "Bilder",
|
|
530
|
+
"molecule3d.framesHelp": "Ein animiertes PNG enthält alle Bilder und spielt sie in einer Schleife ab. Browser spielen es ab; ein Programm, das es nicht kann, zeigt das erste Bild.",
|
|
531
|
+
"molecule3d.frames.one": "Dieses",
|
|
532
|
+
"molecule3d.frames.all": "Alle {count}, animiert",
|
|
525
533
|
"molecule3d.fullScreen": "Vollbild",
|
|
526
534
|
"molecule3d.measure": "Messen",
|
|
527
535
|
"molecule3d.clearMeasurements": "Alle Messungen entfernen",
|
package/src/locales/en.json
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"site.nmrium.tagline": "Process and assign NMR spectra in the browser.",
|
|
35
35
|
"site.metabo.tagline": "Turn a batch of NMR spectra into a map of your samples.",
|
|
36
36
|
"site.derepflow.tagline": "Identify the constituents of a mixture from its ¹³C spectrum.",
|
|
37
|
-
"site.
|
|
37
|
+
"site.isomers.tagline": "Every constitutional isomer of a molecular formula.",
|
|
38
38
|
"site.tex.tagline": "LaTeX formulas rendered to SVG or PNG from a URL.",
|
|
39
39
|
"site.lcao.tagline": "Combine atomic orbitals into molecular ones.",
|
|
40
40
|
"site.regexp.tagline": "Learn regular expressions in a live playground.",
|
|
@@ -437,6 +437,10 @@
|
|
|
437
437
|
"structure.tool.reactionOnly": "Only when drawing a reaction.",
|
|
438
438
|
"structure.tool.unavailable": "Not available in this editor.",
|
|
439
439
|
"structure.loadingEditor": "Loading the editor…",
|
|
440
|
+
"structure.import.open": "Open a structure file",
|
|
441
|
+
"structure.import.drop": "Drop to load the structure",
|
|
442
|
+
"structure.import.unreadableFile": "{name} holds no molfile, SMILES or idCode, so the drawing was kept.",
|
|
443
|
+
"structure.import.unreadableText": "That text is not a molfile, a SMILES or an idCode, so the drawing was kept.",
|
|
440
444
|
"structure.export.open": "Export the structure",
|
|
441
445
|
"structure.export.glyph": "Copy or save the structure: SMILES, molfile",
|
|
442
446
|
"structure.export.title": "Export the structure",
|
|
@@ -498,6 +502,10 @@
|
|
|
498
502
|
"molecule3d.molecularSurface": "Molecular surface",
|
|
499
503
|
"molecule3d.displayOptions": "Display options",
|
|
500
504
|
"molecule3d.exportImage": "Export image",
|
|
505
|
+
"molecule3d.frames": "Frames",
|
|
506
|
+
"molecule3d.framesHelp": "An animated PNG holds every frame and plays them in a loop. Browsers play it; a program that cannot shows its first frame.",
|
|
507
|
+
"molecule3d.frames.one": "This one",
|
|
508
|
+
"molecule3d.frames.all": "All {count}, animated",
|
|
501
509
|
"molecule3d.fullScreen": "Full screen",
|
|
502
510
|
"molecule3d.measure": "Measure",
|
|
503
511
|
"molecule3d.clearMeasurements": "Remove all measurements",
|
package/src/locales/es.json
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"site.nmrium.tagline": "Procesar y asignar espectros de RMN en el navegador.",
|
|
35
35
|
"site.metabo.tagline": "Convertir un lote de espectros de RMN en un mapa de sus muestras.",
|
|
36
36
|
"site.derepflow.tagline": "Identificar los constituyentes de una mezcla a partir de su espectro de ¹³C.",
|
|
37
|
-
"site.
|
|
37
|
+
"site.isomers.tagline": "Todos los isómeros constitucionales de una fórmula molecular.",
|
|
38
38
|
"site.tex.tagline": "Fórmulas LaTeX representadas en SVG o PNG desde una URL.",
|
|
39
39
|
"site.lcao.tagline": "Combinar orbitales atómicos en orbitales moleculares.",
|
|
40
40
|
"site.regexp.tagline": "Aprender expresiones regulares en un entorno interactivo.",
|
|
@@ -493,6 +493,10 @@
|
|
|
493
493
|
"structure.tool.reactionOnly": "Solo al dibujar una reacción.",
|
|
494
494
|
"structure.tool.unavailable": "No disponible en este editor.",
|
|
495
495
|
"structure.loadingEditor": "Cargando el editor…",
|
|
496
|
+
"structure.import.open": "Abrir un archivo de estructura",
|
|
497
|
+
"structure.import.drop": "Suelta para cargar la estructura",
|
|
498
|
+
"structure.import.unreadableFile": "{name} no contiene ningún molfile, SMILES ni idCode, así que se conserva el dibujo.",
|
|
499
|
+
"structure.import.unreadableText": "Ese texto no es un molfile, un SMILES ni un idCode, así que se conserva el dibujo.",
|
|
496
500
|
"structure.export.open": "Exportar la estructura",
|
|
497
501
|
"structure.export.glyph": "Copiar o guardar la estructura: SMILES, molfile",
|
|
498
502
|
"structure.export.title": "Exportar la estructura",
|
|
@@ -522,6 +526,10 @@
|
|
|
522
526
|
"molecule3d.molecularSurface": "Superficie molecular",
|
|
523
527
|
"molecule3d.displayOptions": "Opciones de presentación",
|
|
524
528
|
"molecule3d.exportImage": "Exportar la imagen",
|
|
529
|
+
"molecule3d.frames": "Fotogramas",
|
|
530
|
+
"molecule3d.framesHelp": "Un PNG animado contiene todos los fotogramas y los reproduce en bucle. Los navegadores lo reproducen; un programa que no puede muestra el primer fotograma.",
|
|
531
|
+
"molecule3d.frames.one": "Este",
|
|
532
|
+
"molecule3d.frames.all": "Los {count}, animados",
|
|
525
533
|
"molecule3d.fullScreen": "Pantalla completa",
|
|
526
534
|
"molecule3d.measure": "Medir",
|
|
527
535
|
"molecule3d.clearMeasurements": "Quitar todas las medidas",
|
package/src/locales/fr.json
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"site.nmrium.tagline": "Traiter et attribuer des spectres RMN dans le navigateur.",
|
|
35
35
|
"site.metabo.tagline": "Transformer un lot de spectres RMN en une carte de vos échantillons.",
|
|
36
36
|
"site.derepflow.tagline": "Identifier les constituants d'un mélange à partir de son spectre ¹³C.",
|
|
37
|
-
"site.
|
|
37
|
+
"site.isomers.tagline": "Tous les isomères de constitution d'une formule moléculaire.",
|
|
38
38
|
"site.tex.tagline": "Des formules LaTeX rendues en SVG ou PNG depuis une URL.",
|
|
39
39
|
"site.lcao.tagline": "Combiner des orbitales atomiques en orbitales moléculaires.",
|
|
40
40
|
"site.regexp.tagline": "Apprendre les expressions régulières dans un bac à sable interactif.",
|
|
@@ -493,6 +493,10 @@
|
|
|
493
493
|
"structure.tool.reactionOnly": "Seulement lors du dessin d'une réaction.",
|
|
494
494
|
"structure.tool.unavailable": "Indisponible dans cet éditeur.",
|
|
495
495
|
"structure.loadingEditor": "Chargement de l'éditeur…",
|
|
496
|
+
"structure.import.open": "Ouvrir un fichier de structure",
|
|
497
|
+
"structure.import.drop": "Déposer pour charger la structure",
|
|
498
|
+
"structure.import.unreadableFile": "{name} ne contient ni molfile, ni SMILES, ni idCode : le dessin est conservé.",
|
|
499
|
+
"structure.import.unreadableText": "Ce texte n'est ni un molfile, ni un SMILES, ni un idCode : le dessin est conservé.",
|
|
496
500
|
"structure.export.open": "Exporter la structure",
|
|
497
501
|
"structure.export.glyph": "Copier ou enregistrer la structure : SMILES, molfile",
|
|
498
502
|
"structure.export.title": "Exporter la structure",
|
|
@@ -522,6 +526,10 @@
|
|
|
522
526
|
"molecule3d.molecularSurface": "Surface moléculaire",
|
|
523
527
|
"molecule3d.displayOptions": "Options d'affichage",
|
|
524
528
|
"molecule3d.exportImage": "Exporter l'image",
|
|
529
|
+
"molecule3d.frames": "Images",
|
|
530
|
+
"molecule3d.framesHelp": "Un PNG animé contient toutes les images et les joue en boucle. Les navigateurs le jouent ; un programme qui ne le peut pas montre la première image.",
|
|
531
|
+
"molecule3d.frames.one": "Celle-ci",
|
|
532
|
+
"molecule3d.frames.all": "Les {count}, animées",
|
|
525
533
|
"molecule3d.fullScreen": "Plein écran",
|
|
526
534
|
"molecule3d.measure": "Mesurer",
|
|
527
535
|
"molecule3d.clearMeasurements": "Retirer toutes les mesures",
|
package/src/locales/it.json
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"site.nmrium.tagline": "Elaborare e assegnare spettri NMR nel browser.",
|
|
35
35
|
"site.metabo.tagline": "Trasformare una serie di spettri NMR in una mappa dei propri campioni.",
|
|
36
36
|
"site.derepflow.tagline": "Identificare i costituenti di una miscela dal suo spettro ¹³C.",
|
|
37
|
-
"site.
|
|
37
|
+
"site.isomers.tagline": "Tutti gli isomeri costituzionali di una formula molecolare.",
|
|
38
38
|
"site.tex.tagline": "Formule LaTeX rese in SVG o PNG a partire da un URL.",
|
|
39
39
|
"site.lcao.tagline": "Combinare orbitali atomici in orbitali molecolari.",
|
|
40
40
|
"site.regexp.tagline": "Imparare le espressioni regolari in un ambiente interattivo.",
|
|
@@ -437,6 +437,10 @@
|
|
|
437
437
|
"structure.tool.reactionOnly": "Solo quando si disegna una reazione.",
|
|
438
438
|
"structure.tool.unavailable": "Non disponibile in questo editor.",
|
|
439
439
|
"structure.loadingEditor": "Caricamento dell'editor…",
|
|
440
|
+
"structure.import.open": "Apri un file di struttura",
|
|
441
|
+
"structure.import.drop": "Rilascia per caricare la struttura",
|
|
442
|
+
"structure.import.unreadableFile": "{name} non contiene né molfile, né SMILES, né idCode, quindi il disegno resta com'era.",
|
|
443
|
+
"structure.import.unreadableText": "Questo testo non è né un molfile, né uno SMILES, né un idCode, quindi il disegno resta com'era.",
|
|
440
444
|
"structure.export.open": "Esporta la struttura",
|
|
441
445
|
"structure.export.glyph": "Copia o salva la struttura: SMILES, molfile",
|
|
442
446
|
"structure.export.title": "Esporta la struttura",
|
|
@@ -498,6 +502,10 @@
|
|
|
498
502
|
"molecule3d.molecularSurface": "Superficie molecolare",
|
|
499
503
|
"molecule3d.displayOptions": "Opzioni di visualizzazione",
|
|
500
504
|
"molecule3d.exportImage": "Esporta l'immagine",
|
|
505
|
+
"molecule3d.frames": "Fotogrammi",
|
|
506
|
+
"molecule3d.framesHelp": "Un PNG animato contiene tutti i fotogrammi e li riproduce in ciclo. I browser lo riproducono; un programma che non può mostra il primo fotogramma.",
|
|
507
|
+
"molecule3d.frames.one": "Questo",
|
|
508
|
+
"molecule3d.frames.all": "Tutti i {count}, animati",
|
|
501
509
|
"molecule3d.fullScreen": "Schermo intero",
|
|
502
510
|
"molecule3d.measure": "Misura",
|
|
503
511
|
"molecule3d.clearMeasurements": "Togli tutte le misure",
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frames of a scene as one animated PNG, which loops forever.
|
|
3
|
+
*
|
|
4
|
+
* An APNG is a PNG whose frames every browser plays, and whose first frame
|
|
5
|
+
* any program that does not play them still shows. It keeps every colour of a
|
|
6
|
+
* shaded molecule, which a GIF's 256 cannot.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type * as Upng from '@pdf-lib/upng';
|
|
10
|
+
|
|
11
|
+
/** How many frames a second are played, and saved, when nobody says. */
|
|
12
|
+
export const DEFAULT_FRAMES_PER_SECOND = 25;
|
|
13
|
+
|
|
14
|
+
/** One frame: its pixels as RGBA bytes, row by row from the top. */
|
|
15
|
+
export interface RgbaFrame {
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
data: Uint8ClampedArray | Uint8Array;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Encode frames as one animated PNG, every frame shown for the same time, the
|
|
23
|
+
* whole looping forever. The encoder is loaded on first use.
|
|
24
|
+
* @param frames - The frames, in order, all the same size.
|
|
25
|
+
* @param framesPerSecond - How many are shown per second.
|
|
26
|
+
* @returns The PNG file.
|
|
27
|
+
* @throws {Error} When there is no frame, or the frames differ in size.
|
|
28
|
+
*/
|
|
29
|
+
export async function encodeAnimatedPng(
|
|
30
|
+
frames: readonly RgbaFrame[],
|
|
31
|
+
framesPerSecond: number,
|
|
32
|
+
): Promise<Uint8Array<ArrayBuffer>> {
|
|
33
|
+
const first = frames[0];
|
|
34
|
+
if (first === undefined) throw new Error('There is no frame to encode.');
|
|
35
|
+
const { width, height } = first;
|
|
36
|
+
const buffers: ArrayBuffer[] = [];
|
|
37
|
+
const delays: number[] = [];
|
|
38
|
+
const delay = Math.max(1, Math.round(1000 / framesPerSecond));
|
|
39
|
+
for (const frame of frames) {
|
|
40
|
+
if (frame.width !== width || frame.height !== height) {
|
|
41
|
+
throw new Error('Every frame of an animated PNG must be the same size.');
|
|
42
|
+
}
|
|
43
|
+
buffers.push(rgbaBuffer(frame));
|
|
44
|
+
delays.push(delay);
|
|
45
|
+
}
|
|
46
|
+
const upng = await loadEncoder();
|
|
47
|
+
// 0 colours: lossless.
|
|
48
|
+
return new Uint8Array(upng.encode(buffers, width, height, 0, delays));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The bytes of a frame in an ArrayBuffer of their own size, which the encoder
|
|
53
|
+
* reads whole.
|
|
54
|
+
* @param frame - The frame.
|
|
55
|
+
* @returns Its RGBA bytes.
|
|
56
|
+
*/
|
|
57
|
+
function rgbaBuffer(frame: RgbaFrame): ArrayBuffer {
|
|
58
|
+
const { data } = frame;
|
|
59
|
+
const { buffer, byteOffset, byteLength } = data;
|
|
60
|
+
if (
|
|
61
|
+
buffer instanceof ArrayBuffer &&
|
|
62
|
+
byteOffset === 0 &&
|
|
63
|
+
byteLength === buffer.byteLength
|
|
64
|
+
) {
|
|
65
|
+
return buffer;
|
|
66
|
+
}
|
|
67
|
+
return new Uint8Array(data).buffer;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type Encoder = Pick<typeof Upng, 'encode'>;
|
|
71
|
+
|
|
72
|
+
async function loadEncoder(): Promise<Encoder> {
|
|
73
|
+
const { default: loaded } = (await import('@pdf-lib/upng')) as {
|
|
74
|
+
default: Encoder | { default: Encoder };
|
|
75
|
+
};
|
|
76
|
+
// A bundler hands the ES build's one default object; Node's own import hands
|
|
77
|
+
// the exports of the CommonJS build, whose default is that object.
|
|
78
|
+
return 'encode' in loaded ? loaded : loaded.default;
|
|
79
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A molfile for the viewer, with the parser molstar has to read it with.
|
|
3
|
+
*
|
|
4
|
+
* A file of several models — an SDF of several records, an XYZ of several
|
|
5
|
+
* frames — is a series of frames of one molecule, which the viewer can play
|
|
6
|
+
* and export as an animated PNG.
|
|
7
|
+
*/
|
|
8
|
+
export interface Molecule3DFile {
|
|
9
|
+
/**
|
|
10
|
+
* molstar's format id: `mol` reads V2000, `sdf` reads V2000 and V3000, `xyz`
|
|
11
|
+
* reads bare coordinates and infers the bonds from the distances.
|
|
12
|
+
*/
|
|
13
|
+
format: 'mol' | 'sdf' | 'xyz';
|
|
14
|
+
/** The file text. It must carry 3D coordinates. */
|
|
15
|
+
data: string;
|
|
16
|
+
/**
|
|
17
|
+
* Names the molecule the file draws, when several files draw one — one per
|
|
18
|
+
* normal mode, say. Replacing a file by another of the same name keeps the
|
|
19
|
+
* camera where the reader left it, instead of framing a new molecule.
|
|
20
|
+
* @default undefined — every other file is another molecule
|
|
21
|
+
*/
|
|
22
|
+
moleculeId?: string;
|
|
23
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export type { RgbaFrame } from './animatedPng.ts';
|
|
2
|
+
export { DEFAULT_FRAMES_PER_SECOND, encodeAnimatedPng } from './animatedPng.ts';
|
|
1
3
|
export type {
|
|
2
4
|
Molecule3DCamera,
|
|
3
5
|
Molecule3DRotation,
|
|
@@ -33,8 +35,8 @@ export {
|
|
|
33
35
|
MEASUREMENT_LABELS,
|
|
34
36
|
sameMeasurements,
|
|
35
37
|
} from './measurement.ts';
|
|
38
|
+
export type { Molecule3DFile } from './file.ts';
|
|
36
39
|
export type {
|
|
37
|
-
Molecule3DFile,
|
|
38
40
|
Molecule3DSettings,
|
|
39
41
|
Molecule3DTools,
|
|
40
42
|
RepresentationId,
|
|
@@ -3,14 +3,6 @@
|
|
|
3
3
|
* free of React and molstar so a site can persist and validate them.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
/** A molfile for the viewer, with the parser molstar has to read it with. */
|
|
7
|
-
export interface Molecule3DFile {
|
|
8
|
-
/** molstar's format id: `mol` reads V2000, `sdf` reads V2000 and V3000. */
|
|
9
|
-
format: 'mol' | 'sdf';
|
|
10
|
-
/** The molfile text. It must carry 3D coordinates. */
|
|
11
|
-
data: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
6
|
/** Ways the model can be drawn, in the order the picker offers them. */
|
|
15
7
|
export const REPRESENTATIONS = [
|
|
16
8
|
'ball-and-stick',
|
|
@@ -16,15 +16,28 @@ import {
|
|
|
16
16
|
import { FigureDownloadPanel } from '../../download/ui/FigureDownloadPanel.tsx';
|
|
17
17
|
import { useFigureTask } from '../../download/ui/useFigureTask.ts';
|
|
18
18
|
import { useChromeT } from '../../i18n/ui/useT.ts';
|
|
19
|
+
import { OverlaySegmented } from '../../overlay/ui/OverlaySegmented.tsx';
|
|
19
20
|
|
|
20
21
|
/** Props of {@link Molecule3DExport}. */
|
|
21
22
|
export interface Molecule3DExportProps {
|
|
22
23
|
/** Reads the size of the canvas on screen, when the popover opens. */
|
|
23
24
|
getSize: () => FigurePixels | null;
|
|
24
|
-
/**
|
|
25
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Writes the file: every frame, as an animated PNG, when `animated` is set.
|
|
27
|
+
*/
|
|
28
|
+
onExport: (
|
|
29
|
+
format: FigureFormat,
|
|
30
|
+
scale: number,
|
|
31
|
+
animated: boolean,
|
|
32
|
+
) => Promise<void>;
|
|
26
33
|
/** Puts a PNG at that scale on the clipboard, and says what came of it. */
|
|
27
34
|
onCopy: (scale: number) => Promise<FigureNotice>;
|
|
35
|
+
/**
|
|
36
|
+
* How many frames the scene holds. Above one, a PNG may hold them all, as an
|
|
37
|
+
* animation.
|
|
38
|
+
* @default 1
|
|
39
|
+
*/
|
|
40
|
+
frameCount?: number;
|
|
28
41
|
}
|
|
29
42
|
|
|
30
43
|
/**
|
|
@@ -33,11 +46,14 @@ export interface Molecule3DExportProps {
|
|
|
33
46
|
* @returns The panel.
|
|
34
47
|
*/
|
|
35
48
|
export function Molecule3DExport(props: Molecule3DExportProps): ReactElement {
|
|
36
|
-
const { getSize, onExport, onCopy } = props;
|
|
49
|
+
const { getSize, onExport, onCopy, frameCount = 1 } = props;
|
|
37
50
|
const t = useChromeT();
|
|
38
51
|
const [size] = useState(getSize);
|
|
39
52
|
const [format, setFormat] = useState<FigureFormat>('png');
|
|
40
53
|
const [scale, setScale] = useState(DEFAULT_FIGURE_SCALE);
|
|
54
|
+
const [frames, setFrames] = useState<FrameChoice>('all');
|
|
55
|
+
const offerFrames = frameCount > 1 && format === 'png';
|
|
56
|
+
const animated = offerFrames && frames === 'all';
|
|
41
57
|
const { run, busy, failure, notice, clear } = useFigureTask();
|
|
42
58
|
|
|
43
59
|
// What the last copy came to only describes the choices it was made with.
|
|
@@ -61,8 +77,30 @@ export function Molecule3DExport(props: Molecule3DExportProps): ReactElement {
|
|
|
61
77
|
rasterSvg
|
|
62
78
|
onFormatChange={changed(setFormat)}
|
|
63
79
|
onScaleChange={changed(setScale)}
|
|
64
|
-
onSave={() => run(onExport(format, scale).then(() => null))}
|
|
80
|
+
onSave={() => run(onExport(format, scale, animated).then(() => null))}
|
|
65
81
|
onCopy={() => run(onCopy(scale))}
|
|
66
|
-
|
|
82
|
+
>
|
|
83
|
+
{offerFrames && (
|
|
84
|
+
<OverlaySegmented<FrameChoice>
|
|
85
|
+
label={t('molecule3d.frames')}
|
|
86
|
+
help={{
|
|
87
|
+
title: t('molecule3d.frames'),
|
|
88
|
+
body: t('molecule3d.framesHelp'),
|
|
89
|
+
}}
|
|
90
|
+
value={frames}
|
|
91
|
+
options={[
|
|
92
|
+
{ value: 'one', label: t('molecule3d.frames.one') },
|
|
93
|
+
{
|
|
94
|
+
value: 'all',
|
|
95
|
+
label: t('molecule3d.frames.all', { count: frameCount }),
|
|
96
|
+
},
|
|
97
|
+
]}
|
|
98
|
+
onChange={changed(setFrames)}
|
|
99
|
+
/>
|
|
100
|
+
)}
|
|
101
|
+
</FigureDownloadPanel>
|
|
67
102
|
);
|
|
68
103
|
}
|
|
104
|
+
|
|
105
|
+
/** The frame on screen, or every frame as an animated PNG. */
|
|
106
|
+
type FrameChoice = 'one' | 'all';
|
|
@@ -225,11 +225,18 @@ export function Molecule3DToolbar(props: Molecule3DToolbarProps): ReactElement {
|
|
|
225
225
|
|
|
226
226
|
const TOOLTIP_PROPS = { placement: 'right' } as const;
|
|
227
227
|
|
|
228
|
+
// A canvas too short for both cards puts the second one beside the first,
|
|
229
|
+
// rather than cutting it off; the column itself lets clicks through to the
|
|
230
|
+
// canvas between and below the cards.
|
|
228
231
|
const COLUMN_STYLE: CSSProperties = {
|
|
229
232
|
display: 'flex',
|
|
230
233
|
flexDirection: 'column',
|
|
234
|
+
flexWrap: 'wrap',
|
|
235
|
+
alignContent: 'flex-start',
|
|
236
|
+
alignItems: 'flex-start',
|
|
231
237
|
gap: 6,
|
|
232
|
-
|
|
238
|
+
height: '100%',
|
|
239
|
+
pointerEvents: 'none',
|
|
233
240
|
};
|
|
234
241
|
|
|
235
|
-
const CARD_STYLE: CSSProperties = { padding: 0 };
|
|
242
|
+
const CARD_STYLE: CSSProperties = { padding: 0, pointerEvents: 'auto' };
|
|
@@ -10,6 +10,7 @@ import type { CSSProperties, ReactElement } from 'react';
|
|
|
10
10
|
import { useEffect, useRef, useState } from 'react';
|
|
11
11
|
|
|
12
12
|
import { DEFAULT_SPIN_SPEED } from '../../molstar/core/index.ts';
|
|
13
|
+
import { DEFAULT_FRAMES_PER_SECOND } from '../core/animatedPng.ts';
|
|
13
14
|
import type { Molecule3DHighlight } from '../core/highlight.ts';
|
|
14
15
|
import type { Measurement, MeasurementKind } from '../core/measurement.ts';
|
|
15
16
|
import {
|
|
@@ -21,14 +22,13 @@ import { Molecule3DExport } from './Molecule3DExport.tsx';
|
|
|
21
22
|
import { Molecule3DOptions } from './Molecule3DOptions.tsx';
|
|
22
23
|
import { Molecule3DStage } from './Molecule3DStage.tsx';
|
|
23
24
|
import { Molecule3DToolbar } from './Molecule3DToolbar.tsx';
|
|
24
|
-
import { createMolecule3DViewer } from './createViewer.ts';
|
|
25
25
|
import { highlightColorOf } from './highlightColor.ts';
|
|
26
26
|
import type { MoleculeViewer3DProps } from './moleculeViewer3DProps.ts';
|
|
27
27
|
import { useControlledState } from './useControlledState.ts';
|
|
28
28
|
import { useImageExport } from './useImageExport.ts';
|
|
29
29
|
import { useMoleculeScene } from './useMoleculeScene.ts';
|
|
30
|
+
import { useMoleculeViewer } from './useMoleculeViewer.ts';
|
|
30
31
|
import { usePolarSurfaceArea } from './usePolarSurfaceArea.ts';
|
|
31
|
-
import type { Molecule3DViewer } from './viewer.ts';
|
|
32
32
|
|
|
33
33
|
/** Props of {@link MoleculeCanvas3D}. */
|
|
34
34
|
export interface MoleculeCanvas3DProps extends Omit<
|
|
@@ -56,6 +56,8 @@ export function MoleculeCanvas3D(props: MoleculeCanvas3DProps): ReactElement {
|
|
|
56
56
|
defaultSpinning = false,
|
|
57
57
|
onSpinningChange,
|
|
58
58
|
spinSpeed = DEFAULT_SPIN_SPEED,
|
|
59
|
+
playing = false,
|
|
60
|
+
framesPerSecond = DEFAULT_FRAMES_PER_SECOND,
|
|
59
61
|
measurements: measurementsProp,
|
|
60
62
|
onMeasurementsChange,
|
|
61
63
|
highlights = NO_HIGHLIGHTS,
|
|
@@ -82,6 +84,7 @@ export function MoleculeCanvas3D(props: MoleculeCanvas3DProps): ReactElement {
|
|
|
82
84
|
readonly Measurement[]
|
|
83
85
|
>(measurementsProp, NO_MEASUREMENTS, onMeasurementsChange);
|
|
84
86
|
const [measureKind, setMeasureKind] = useState<MeasurementKind | null>(null);
|
|
87
|
+
const [frameCount, setFrameCount] = useState(0);
|
|
85
88
|
const polarSurfaceArea = usePolarSurfaceArea(
|
|
86
89
|
molfile,
|
|
87
90
|
settings.surfaceColoring === 'polarity',
|
|
@@ -90,51 +93,31 @@ export function MoleculeCanvas3D(props: MoleculeCanvas3DProps): ReactElement {
|
|
|
90
93
|
// A callback ref: the element arriving is what creates the viewer, and every
|
|
91
94
|
// effect keyed on it runs again, in order, after the viewer exists.
|
|
92
95
|
const [container, setContainer] = useState<HTMLDivElement | null>(null);
|
|
93
|
-
const viewerRef = useRef<Molecule3DViewer | null>(null);
|
|
94
96
|
// The viewer is created once, so what its callbacks read is kept fresh here.
|
|
95
|
-
const latest = useRef({
|
|
96
|
-
measurements,
|
|
97
|
-
setMeasurements,
|
|
98
|
-
onFailureChange,
|
|
99
|
-
background,
|
|
100
|
-
});
|
|
97
|
+
const latest = useRef({ measurements, setMeasurements });
|
|
101
98
|
useEffect(() => {
|
|
102
|
-
latest.current = {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
99
|
+
latest.current = { measurements, setMeasurements };
|
|
100
|
+
});
|
|
101
|
+
const viewerRef = useMoleculeViewer(container, {
|
|
102
|
+
background,
|
|
103
|
+
onMeasure: (measurement) => {
|
|
104
|
+
const current = latest.current;
|
|
105
|
+
current.setMeasurements([...current.measurements, measurement]);
|
|
106
|
+
},
|
|
107
|
+
onFrameCountChange: setFrameCount,
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
useEffect(() => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
background: latest.current.background,
|
|
114
|
-
onMeasure: (measurement) => {
|
|
115
|
-
const current = latest.current;
|
|
116
|
-
current.setMeasurements([...current.measurements, measurement]);
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
viewerRef.current = viewer;
|
|
120
|
-
const observer = new ResizeObserver(() => {
|
|
121
|
-
viewer.handleResize();
|
|
122
|
-
});
|
|
123
|
-
observer.observe(container);
|
|
124
|
-
return () => {
|
|
125
|
-
observer.disconnect();
|
|
126
|
-
viewerRef.current = null;
|
|
127
|
-
viewer.dispose();
|
|
128
|
-
};
|
|
129
|
-
}, [container]);
|
|
111
|
+
void viewerRef.current?.setSpin(spinning, spinSpeed);
|
|
112
|
+
}, [container, viewerRef, spinning, spinSpeed]);
|
|
130
113
|
|
|
131
114
|
useEffect(() => {
|
|
132
|
-
void viewerRef.current?.
|
|
133
|
-
}, [container,
|
|
115
|
+
void viewerRef.current?.frames.play(playing, framesPerSecond);
|
|
116
|
+
}, [container, viewerRef, playing, framesPerSecond]);
|
|
134
117
|
|
|
135
118
|
useEffect(() => {
|
|
136
119
|
void viewerRef.current?.setMeasureKind(measureKind);
|
|
137
|
-
}, [container, measureKind]);
|
|
120
|
+
}, [container, viewerRef, measureKind]);
|
|
138
121
|
|
|
139
122
|
// Kept out of the scene: a new highlight repaints the model, never redraws it.
|
|
140
123
|
useEffect(() => {
|
|
@@ -142,7 +125,7 @@ export function MoleculeCanvas3D(props: MoleculeCanvas3DProps): ReactElement {
|
|
|
142
125
|
highlights,
|
|
143
126
|
highlightColorOf(container),
|
|
144
127
|
);
|
|
145
|
-
}, [container, highlights]);
|
|
128
|
+
}, [container, viewerRef, highlights]);
|
|
146
129
|
|
|
147
130
|
const { resetView } = useMoleculeScene({
|
|
148
131
|
container,
|
|
@@ -160,6 +143,7 @@ export function MoleculeCanvas3D(props: MoleculeCanvas3DProps): ReactElement {
|
|
|
160
143
|
container,
|
|
161
144
|
viewerRef,
|
|
162
145
|
fileName,
|
|
146
|
+
framesPerSecond,
|
|
163
147
|
);
|
|
164
148
|
|
|
165
149
|
return (
|
|
@@ -202,6 +186,7 @@ export function MoleculeCanvas3D(props: MoleculeCanvas3DProps): ReactElement {
|
|
|
202
186
|
getSize={canvasSize}
|
|
203
187
|
onExport={exportImage}
|
|
204
188
|
onCopy={copyImage}
|
|
189
|
+
frameCount={frameCount}
|
|
205
190
|
/>
|
|
206
191
|
}
|
|
207
192
|
/>
|
|
@@ -231,6 +216,7 @@ const CANVAS_STYLE: CSSProperties = { position: 'absolute', inset: 0 };
|
|
|
231
216
|
const OVERLAY_STYLE: CSSProperties = {
|
|
232
217
|
position: 'absolute',
|
|
233
218
|
top: 8,
|
|
219
|
+
bottom: 8,
|
|
234
220
|
left: 8,
|
|
235
221
|
zIndex: 2,
|
|
236
222
|
display: 'flex',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { Molecule3DFile } from '../core/file.ts';
|
|
1
2
|
import type { Measurement } from '../core/measurement.ts';
|
|
2
|
-
import type {
|
|
3
|
+
import type { Molecule3DSettings } from '../core/settings.ts';
|
|
3
4
|
import { normalizeMolecule3DSettings } from '../core/settings.ts';
|
|
4
5
|
|
|
5
6
|
import type { Molecule3DViewer } from './viewer.ts';
|
|
@@ -4,13 +4,22 @@ import { FIGURE_PNG_TYPE } from '../../download/core/figurePng.ts';
|
|
|
4
4
|
import { figurePixels } from '../../download/core/figureScale.ts';
|
|
5
5
|
import { FIGURE_SVG_TYPE } from '../../download/core/figureSvg.ts';
|
|
6
6
|
import { sanitizeFileName } from '../../download/core/sanitizeFileName.ts';
|
|
7
|
+
import { encodeAnimatedPng } from '../core/animatedPng.ts';
|
|
7
8
|
import type { ImageSize } from '../core/exportImage.ts';
|
|
8
9
|
import { dataUriBytes, rasterSvgMarkup } from '../core/exportImage.ts';
|
|
9
10
|
|
|
10
|
-
/**
|
|
11
|
+
/** What an export asks of a viewer: a picture of its scene. */
|
|
11
12
|
export interface SceneCapture {
|
|
12
13
|
/** Render the scene at that pixel size, as a `data:` URI. */
|
|
13
14
|
captureImage: (size: ImageSize) => Promise<string | undefined>;
|
|
15
|
+
/**
|
|
16
|
+
* The frames of the scene, each rendered at that pixel size, for an animated
|
|
17
|
+
* PNG.
|
|
18
|
+
* @default undefined — the viewer has a single frame to give
|
|
19
|
+
*/
|
|
20
|
+
frames?: {
|
|
21
|
+
capture: (size: ImageSize) => Promise<ImageData[] | undefined>;
|
|
22
|
+
};
|
|
14
23
|
}
|
|
15
24
|
|
|
16
25
|
/** What {@link exportMoleculeImage} writes. */
|
|
@@ -22,6 +31,12 @@ export interface MoleculeImageRequest {
|
|
|
22
31
|
size: ImageSize;
|
|
23
32
|
/** File name without its extension. */
|
|
24
33
|
fileName: string;
|
|
34
|
+
/**
|
|
35
|
+
* Every frame of the scene as one animated PNG, played at this many frames a
|
|
36
|
+
* second, rather than the frame on screen. Ignored for an SVG.
|
|
37
|
+
* @default undefined — the frame on screen
|
|
38
|
+
*/
|
|
39
|
+
framesPerSecond?: number;
|
|
25
40
|
}
|
|
26
41
|
|
|
27
42
|
/**
|
|
@@ -54,11 +69,21 @@ export async function exportMoleculeImage(
|
|
|
54
69
|
viewer: SceneCapture,
|
|
55
70
|
request: MoleculeImageRequest,
|
|
56
71
|
): Promise<void> {
|
|
57
|
-
const { format, scale, size, fileName } = request;
|
|
72
|
+
const { format, scale, size, fileName, framesPerSecond } = request;
|
|
58
73
|
const pixels = figurePixels(size, scale);
|
|
74
|
+
const base = sanitizeFileName(fileName, 'molecule');
|
|
75
|
+
if (format === 'png' && framesPerSecond !== undefined) {
|
|
76
|
+
if (viewer.frames === undefined) {
|
|
77
|
+
throw new Error('This viewer has no frames to animate.');
|
|
78
|
+
}
|
|
79
|
+
const frames = await viewer.frames.capture(pixels);
|
|
80
|
+
if (frames === undefined) return;
|
|
81
|
+
const bytes = await encodeAnimatedPng(frames, framesPerSecond);
|
|
82
|
+
downloadBlob(new Blob([bytes], { type: FIGURE_PNG_TYPE }), `${base}.png`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
59
85
|
const dataUri = await viewer.captureImage(pixels);
|
|
60
86
|
if (dataUri === undefined) return;
|
|
61
|
-
const base = sanitizeFileName(fileName, 'molecule');
|
|
62
87
|
if (format === 'png') {
|
|
63
88
|
downloadBlob(
|
|
64
89
|
new Blob([dataUriBytes(dataUri)], { type: FIGURE_PNG_TYPE }),
|