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
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The frames of the file a viewer shows: how many there are, whether they
|
|
3
|
+
* play, and a picture of each.
|
|
4
|
+
*
|
|
5
|
+
* Kept apart from the viewer because every redraw of the scene has to go
|
|
6
|
+
* through it: a running animation must not step a state tree being rebuilt —
|
|
7
|
+
* it finds no model and stops itself — so the frames are stopped first and
|
|
8
|
+
* started again once the new file is drawn.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { PluginContext } from 'molstar/lib/mol-plugin/context.js';
|
|
12
|
+
|
|
13
|
+
import type { FramePlayback, MolstarPlugin } from '../../molstar/core/index.ts';
|
|
14
|
+
import {
|
|
15
|
+
captureFrames,
|
|
16
|
+
setTrajectoryPlayback,
|
|
17
|
+
stopTrajectory,
|
|
18
|
+
} from '../../molstar/core/index.ts';
|
|
19
|
+
import { DEFAULT_FRAMES_PER_SECOND } from '../core/animatedPng.ts';
|
|
20
|
+
import type { ImageSize } from '../core/exportImage.ts';
|
|
21
|
+
|
|
22
|
+
/** The frames of what one viewer shows. */
|
|
23
|
+
export class MoleculeFrames {
|
|
24
|
+
readonly #plugin: MolstarPlugin;
|
|
25
|
+
readonly #onCountChange: (frameCount: number) => void;
|
|
26
|
+
// Kept across redraws, so the next file plays as the last one did.
|
|
27
|
+
#playback: FramePlayback = {
|
|
28
|
+
playing: false,
|
|
29
|
+
framesPerSecond: DEFAULT_FRAMES_PER_SECOND,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Follow the frames of what `plugin` draws.
|
|
34
|
+
* @param plugin - The canvas.
|
|
35
|
+
* @param onCountChange - Called with how many frames each scene drawn holds.
|
|
36
|
+
*/
|
|
37
|
+
constructor(
|
|
38
|
+
plugin: MolstarPlugin,
|
|
39
|
+
onCountChange: (frameCount: number) => void,
|
|
40
|
+
) {
|
|
41
|
+
this.#plugin = plugin;
|
|
42
|
+
this.#onCountChange = onCountChange;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Redraw the scene with the frames stopped, then report how many the new
|
|
47
|
+
* scene holds and play them if playback is on.
|
|
48
|
+
* @param plugin - The molstar context.
|
|
49
|
+
* @param draw - Draws the scene, and resolves with how many frames it holds.
|
|
50
|
+
* @returns Nothing; resolves once the scene is drawn and playing.
|
|
51
|
+
*/
|
|
52
|
+
async redraw(
|
|
53
|
+
plugin: PluginContext,
|
|
54
|
+
draw: () => Promise<number>,
|
|
55
|
+
): Promise<void> {
|
|
56
|
+
await stopTrajectory(plugin);
|
|
57
|
+
this.#onCountChange(await draw());
|
|
58
|
+
const { playing, framesPerSecond } = this.#playback;
|
|
59
|
+
await setTrajectoryPlayback(plugin, playing, framesPerSecond);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Play the frames in a loop, or stop them, now and for every scene drawn
|
|
64
|
+
* afterwards. A scene of one frame stays still.
|
|
65
|
+
* @param playing - Whether to play.
|
|
66
|
+
* @param framesPerSecond - How fast.
|
|
67
|
+
* @returns Nothing; resolves once the animation is started or stopped.
|
|
68
|
+
*/
|
|
69
|
+
play(playing: boolean, framesPerSecond: number): Promise<void> {
|
|
70
|
+
this.#playback = { playing, framesPerSecond };
|
|
71
|
+
return this.#plugin.run((plugin) =>
|
|
72
|
+
setTrajectoryPlayback(plugin, playing, framesPerSecond),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Render every frame off screen, as a still picture of the scene is
|
|
78
|
+
* rendered. Playback pauses while they are taken.
|
|
79
|
+
* @param size - Pixel size of each picture.
|
|
80
|
+
* @returns The pictures, in order; `undefined` once the canvas is disposed.
|
|
81
|
+
* @throws {Error} When the plugin has no screenshot helper.
|
|
82
|
+
*/
|
|
83
|
+
capture(size: ImageSize): Promise<ImageData[] | undefined> {
|
|
84
|
+
return this.#plugin.run((plugin) =>
|
|
85
|
+
captureFrames(plugin, size, this.#playback),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -2,13 +2,10 @@ import type { ReactNode } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import type { ViewerCapability } from '../../orbital/ui/capability.ts';
|
|
4
4
|
import type { Molecule3DCamera } from '../core/camera.ts';
|
|
5
|
+
import type { Molecule3DFile } from '../core/file.ts';
|
|
5
6
|
import type { Molecule3DHighlight } from '../core/highlight.ts';
|
|
6
7
|
import type { Measurement } from '../core/measurement.ts';
|
|
7
|
-
import type {
|
|
8
|
-
Molecule3DFile,
|
|
9
|
-
Molecule3DSettings,
|
|
10
|
-
Molecule3DTools,
|
|
11
|
-
} from '../core/settings.ts';
|
|
8
|
+
import type { Molecule3DSettings, Molecule3DTools } from '../core/settings.ts';
|
|
12
9
|
|
|
13
10
|
/**
|
|
14
11
|
* Props of `MoleculeViewer3D`.
|
|
@@ -69,6 +66,18 @@ export interface MoleculeViewer3DProps {
|
|
|
69
66
|
* @default 1 / 3
|
|
70
67
|
*/
|
|
71
68
|
spinSpeed?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Whether the frames of a file holding several are played in a loop. A file
|
|
71
|
+
* of one model stays still whatever this says.
|
|
72
|
+
* @default false
|
|
73
|
+
*/
|
|
74
|
+
playing?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* How many frames are shown per second while playing, and in an animated
|
|
77
|
+
* PNG; molstar plays between 5 and 60.
|
|
78
|
+
* @default 25
|
|
79
|
+
*/
|
|
80
|
+
framesPerSecond?: number;
|
|
72
81
|
/**
|
|
73
82
|
* The measurements drawn over the model, when the site owns them. They name
|
|
74
83
|
* atoms by position, so they carry over to another conformer of the same
|
|
@@ -15,7 +15,8 @@ import { StructureFromModel } from 'molstar/lib/mol-plugin-state/transforms/mode
|
|
|
15
15
|
import { StructureRepresentation3D } from 'molstar/lib/mol-plugin-state/transforms/representation.js';
|
|
16
16
|
import type { StateTransformer } from 'molstar/lib/mol-state/index.js';
|
|
17
17
|
|
|
18
|
-
import type { Molecule3DFile
|
|
18
|
+
import type { Molecule3DFile } from '../core/file.ts';
|
|
19
|
+
import type { RepresentationId } from '../core/settings.ts';
|
|
19
20
|
|
|
20
21
|
import type { MoleculeStyle } from './viewerTypes.ts';
|
|
21
22
|
|
|
@@ -48,7 +49,7 @@ const STICK_SCALE = 1.2;
|
|
|
48
49
|
* @param plugin - The molstar context.
|
|
49
50
|
* @param molfile - Molfile text plus the format id.
|
|
50
51
|
* @param style - See {@link MoleculeStyle}.
|
|
51
|
-
* @returns
|
|
52
|
+
* @returns How many frames the file holds, the first of which is on screen.
|
|
52
53
|
* @throws {Error} When the file parses to zero atoms. molstar's `mol` parser returns
|
|
53
54
|
* exactly that on a V3000 file, with no error, so without this check the viewer
|
|
54
55
|
* would show an empty scene and report nothing.
|
|
@@ -57,7 +58,7 @@ export async function renderMolecule(
|
|
|
57
58
|
plugin: PluginContext,
|
|
58
59
|
molfile: Molecule3DFile,
|
|
59
60
|
style: MoleculeStyle = {},
|
|
60
|
-
): Promise<
|
|
61
|
+
): Promise<number> {
|
|
61
62
|
const { representation = 'ball-and-stick', sizeFactor = 1 } = style;
|
|
62
63
|
await clearMolecule(plugin);
|
|
63
64
|
const data = await plugin.builders.data.rawData(
|
|
@@ -99,6 +100,7 @@ export async function renderMolecule(
|
|
|
99
100
|
),
|
|
100
101
|
)
|
|
101
102
|
.commit();
|
|
103
|
+
return trajectory.data?.frameCount ?? 1;
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
/**
|
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import type { Molecule3DCamera } from '../core/camera.ts';
|
|
14
|
+
import type { Molecule3DFile } from '../core/file.ts';
|
|
14
15
|
import type { Measurement } from '../core/measurement.ts';
|
|
15
16
|
import { sameMeasurements } from '../core/measurement.ts';
|
|
16
|
-
import type {
|
|
17
|
+
import type { Molecule3DSettings } from '../core/settings.ts';
|
|
17
18
|
import { sameMolecule3DSettings } from '../core/settings.ts';
|
|
18
19
|
|
|
19
20
|
import type { SceneViewer } from './drawScene.ts';
|
|
@@ -103,7 +104,7 @@ export class SceneDrawer {
|
|
|
103
104
|
const { molfile, settings, measurements, frameNewMolecule } = request;
|
|
104
105
|
const isNew =
|
|
105
106
|
molfile !== null &&
|
|
106
|
-
(this.#framed === null || !
|
|
107
|
+
(this.#framed === null || !sameMolecule(this.#framed, molfile));
|
|
107
108
|
this.#framed = molfile;
|
|
108
109
|
// A link's camera is spent on the first molecule framed; afterwards the
|
|
109
110
|
// camera is the reader's and a new molecule is framed as usual.
|
|
@@ -119,6 +120,17 @@ export class SceneDrawer {
|
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
|
|
123
|
+
// Two files a site names alike draw one molecule, whatever their text.
|
|
124
|
+
function sameMolecule(first: Molecule3DFile, second: Molecule3DFile): boolean {
|
|
125
|
+
if (
|
|
126
|
+
first.moleculeId !== undefined &&
|
|
127
|
+
first.moleculeId === second.moleculeId
|
|
128
|
+
) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
return sameFile(first, second);
|
|
132
|
+
}
|
|
133
|
+
|
|
122
134
|
function sameFile(
|
|
123
135
|
first: Molecule3DFile | null,
|
|
124
136
|
second: Molecule3DFile | null,
|
|
@@ -5,6 +5,7 @@ import type { FigureNotice } from '../../download/core/copyFigure.ts';
|
|
|
5
5
|
import { copyFigure } from '../../download/core/copyFigure.ts';
|
|
6
6
|
import type { FigureFormat } from '../../download/core/downloadFigure.ts';
|
|
7
7
|
import type { FigurePixels } from '../../download/core/figureScale.ts';
|
|
8
|
+
import { DEFAULT_FRAMES_PER_SECOND } from '../core/animatedPng.ts';
|
|
8
9
|
|
|
9
10
|
import type { SceneCapture } from './exportMoleculeImage.ts';
|
|
10
11
|
import {
|
|
@@ -16,8 +17,15 @@ import {
|
|
|
16
17
|
export interface ImageExport {
|
|
17
18
|
/** Size of the canvas on screen, or `null` before it is laid out. */
|
|
18
19
|
canvasSize: () => FigurePixels | null;
|
|
19
|
-
/**
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Render the scene at `scale` and download it as `format` — every frame of
|
|
22
|
+
* it, as an animated PNG, when `animated` is set and the format is PNG.
|
|
23
|
+
*/
|
|
24
|
+
exportImage: (
|
|
25
|
+
format: FigureFormat,
|
|
26
|
+
scale: number,
|
|
27
|
+
animated?: boolean,
|
|
28
|
+
) => Promise<void>;
|
|
21
29
|
/**
|
|
22
30
|
* Render the scene at `scale` and put it on the clipboard as a PNG. Call it
|
|
23
31
|
* from the click itself: Safari only allows a write the click started.
|
|
@@ -30,12 +38,15 @@ export interface ImageExport {
|
|
|
30
38
|
* @param container - The element molstar draws into.
|
|
31
39
|
* @param viewerRef - The viewer, once created.
|
|
32
40
|
* @param fileName - File name without its extension.
|
|
41
|
+
* @param framesPerSecond - How fast an animated PNG plays its frames.
|
|
42
|
+
* @default 25
|
|
33
43
|
* @returns See {@link ImageExport}.
|
|
34
44
|
*/
|
|
35
45
|
export function useImageExport(
|
|
36
46
|
container: HTMLDivElement | null,
|
|
37
47
|
viewerRef: RefObject<SceneCapture | null>,
|
|
38
48
|
fileName: string,
|
|
49
|
+
framesPerSecond = DEFAULT_FRAMES_PER_SECOND,
|
|
39
50
|
): ImageExport {
|
|
40
51
|
const canvasSize = useCallback((): FigurePixels | null => {
|
|
41
52
|
if (container === null) return null;
|
|
@@ -45,15 +56,21 @@ export function useImageExport(
|
|
|
45
56
|
}, [container]);
|
|
46
57
|
|
|
47
58
|
const exportImage = useCallback(
|
|
48
|
-
async (format: FigureFormat, scale: number) => {
|
|
59
|
+
async (format: FigureFormat, scale: number, animated = false) => {
|
|
49
60
|
const viewer = viewerRef.current;
|
|
50
61
|
const size = canvasSize();
|
|
51
62
|
if (viewer === null || size === null) {
|
|
52
63
|
throw new Error('There is no picture to export yet.');
|
|
53
64
|
}
|
|
54
|
-
await exportMoleculeImage(viewer, {
|
|
65
|
+
await exportMoleculeImage(viewer, {
|
|
66
|
+
format,
|
|
67
|
+
scale,
|
|
68
|
+
size,
|
|
69
|
+
fileName,
|
|
70
|
+
framesPerSecond: animated ? framesPerSecond : undefined,
|
|
71
|
+
});
|
|
55
72
|
},
|
|
56
|
-
[canvasSize, viewerRef, fileName],
|
|
73
|
+
[canvasSize, viewerRef, fileName, framesPerSecond],
|
|
57
74
|
);
|
|
58
75
|
|
|
59
76
|
const copyImage = useCallback(
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
import type { RefObject } from 'react';
|
|
13
13
|
import { useCallback, useEffect, useRef } from 'react';
|
|
14
14
|
|
|
15
|
+
import type { Molecule3DFile } from '../core/file.ts';
|
|
15
16
|
import type { Measurement } from '../core/measurement.ts';
|
|
16
|
-
import type {
|
|
17
|
+
import type { Molecule3DSettings } from '../core/settings.ts';
|
|
17
18
|
|
|
18
19
|
import type { MoleculeViewer3DProps } from './moleculeViewer3DProps.ts';
|
|
19
20
|
import { SceneDrawer } from './sceneDrawer.ts';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The molstar viewer of one canvas: created once its element exists, kept to
|
|
3
|
+
* the element's size, and disposed with it.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { RefObject } from 'react';
|
|
7
|
+
import { useEffect, useRef } from 'react';
|
|
8
|
+
|
|
9
|
+
import { createMolecule3DViewer } from './createViewer.ts';
|
|
10
|
+
import type { Molecule3DViewer } from './viewer.ts';
|
|
11
|
+
import type { Molecule3DViewerOptions } from './viewerTypes.ts';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Mount a viewer in `container` for as long as it exists.
|
|
15
|
+
* @param container - The element molstar draws in, or `null` before it exists.
|
|
16
|
+
* @param options - Read when the viewer is created; a later change is ignored,
|
|
17
|
+
* so a callback reads what it needs from a ref of its own.
|
|
18
|
+
* @returns The viewer, holding `null` while there is none.
|
|
19
|
+
*/
|
|
20
|
+
export function useMoleculeViewer(
|
|
21
|
+
container: HTMLElement | null,
|
|
22
|
+
options: Molecule3DViewerOptions,
|
|
23
|
+
): RefObject<Molecule3DViewer | null> {
|
|
24
|
+
const viewerRef = useRef<Molecule3DViewer | null>(null);
|
|
25
|
+
const latestOptions = useRef(options);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
latestOptions.current = options;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (container === null) return;
|
|
32
|
+
const viewer = createMolecule3DViewer(container, latestOptions.current);
|
|
33
|
+
viewerRef.current = viewer;
|
|
34
|
+
const observer = new ResizeObserver(() => {
|
|
35
|
+
viewer.handleResize();
|
|
36
|
+
});
|
|
37
|
+
observer.observe(container);
|
|
38
|
+
return () => {
|
|
39
|
+
observer.disconnect();
|
|
40
|
+
viewerRef.current = null;
|
|
41
|
+
viewer.dispose();
|
|
42
|
+
};
|
|
43
|
+
}, [container]);
|
|
44
|
+
|
|
45
|
+
return viewerRef;
|
|
46
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import type { Molecule3DFile } from '../core/
|
|
3
|
+
import type { Molecule3DFile } from '../core/file.ts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Ertl's topological polar surface area of the molecule, from openchemlib,
|
|
7
7
|
* loaded only once a caller asks for it.
|
|
8
8
|
* @param molfile - The molecule, or `null`.
|
|
9
9
|
* @param enabled - Whether to compute it at all.
|
|
10
|
-
* @returns The area in Ų, or `null` while unknown, disabled,
|
|
11
|
-
* openchemlib is not installed or cannot read the molfile.
|
|
10
|
+
* @returns The area in Ų, or `null` while unknown, disabled, for an XYZ
|
|
11
|
+
* file, or when openchemlib is not installed or cannot read the molfile.
|
|
12
12
|
*/
|
|
13
13
|
export function usePolarSurfaceArea(
|
|
14
14
|
molfile: Molecule3DFile | null,
|
|
@@ -18,7 +18,9 @@ export function usePolarSurfaceArea(
|
|
|
18
18
|
data: string;
|
|
19
19
|
area: number | null;
|
|
20
20
|
} | null>(null);
|
|
21
|
-
|
|
21
|
+
// An XYZ file has no bonds for openchemlib to find the polar atoms by.
|
|
22
|
+
const data =
|
|
23
|
+
enabled && molfile?.format !== 'xyz' ? (molfile?.data ?? null) : null;
|
|
22
24
|
|
|
23
25
|
useEffect(() => {
|
|
24
26
|
if (data === null) return;
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
* is the component's.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type { PluginContext } from 'molstar/lib/mol-plugin/context.js';
|
|
12
11
|
import { Color } from 'molstar/lib/mol-util/color/color.js';
|
|
13
12
|
|
|
14
13
|
import {
|
|
@@ -24,11 +23,12 @@ import {
|
|
|
24
23
|
} from '../../molstar/core/index.ts';
|
|
25
24
|
import type { Molecule3DCamera } from '../core/camera.ts';
|
|
26
25
|
import type { ImageSize } from '../core/exportImage.ts';
|
|
26
|
+
import type { Molecule3DFile } from '../core/file.ts';
|
|
27
27
|
import type { Molecule3DHighlight } from '../core/highlight.ts';
|
|
28
28
|
import type { Measurement, MeasurementKind } from '../core/measurement.ts';
|
|
29
|
-
import type { Molecule3DFile } from '../core/settings.ts';
|
|
30
29
|
|
|
31
30
|
import { applyCamera, resetCamera, watchCamera } from './camera.ts';
|
|
31
|
+
import { MoleculeFrames } from './moleculeFrames.ts';
|
|
32
32
|
import {
|
|
33
33
|
MODEL_REPRESENTATION_REF,
|
|
34
34
|
clearMolecule,
|
|
@@ -54,16 +54,24 @@ export class Molecule3DViewer {
|
|
|
54
54
|
/** Resolves once the canvas exists; every method awaits it internally. */
|
|
55
55
|
readonly ready: Promise<void>;
|
|
56
56
|
|
|
57
|
+
/** The frames of the file on screen: playing them, and their pictures. */
|
|
58
|
+
readonly frames: MoleculeFrames;
|
|
59
|
+
|
|
57
60
|
/**
|
|
58
61
|
* Mount the canvas and start initialising the plugin.
|
|
59
62
|
* @param container - A positioned element.
|
|
60
63
|
* @param options - See {@link Molecule3DViewerOptions}.
|
|
61
64
|
*/
|
|
62
65
|
constructor(container: HTMLElement, options: Molecule3DViewerOptions = {}) {
|
|
63
|
-
const {
|
|
66
|
+
const {
|
|
67
|
+
background,
|
|
68
|
+
onMeasure = ignore,
|
|
69
|
+
onFrameCountChange = ignore,
|
|
70
|
+
} = options;
|
|
64
71
|
this.#plugin = new MolstarPlugin(container, { background });
|
|
65
72
|
this.ready = this.#plugin.ready;
|
|
66
|
-
|
|
73
|
+
this.frames = new MoleculeFrames(this.#plugin, onFrameCountChange);
|
|
74
|
+
// Registered before any other `run`, so the picker exists by the time one runs.
|
|
67
75
|
void this.#plugin
|
|
68
76
|
.run((plugin) => {
|
|
69
77
|
this.#picker = new MeasurementPicker(
|
|
@@ -84,10 +92,12 @@ export class Molecule3DViewer {
|
|
|
84
92
|
* @throws {Error} When the file parses to zero atoms.
|
|
85
93
|
*/
|
|
86
94
|
showMolecule(molfile: Molecule3DFile, style?: MoleculeStyle): Promise<void> {
|
|
87
|
-
return this.#run(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
95
|
+
return this.#plugin.run((plugin) =>
|
|
96
|
+
this.frames.redraw(plugin, async () => {
|
|
97
|
+
await clearMeasurements(plugin);
|
|
98
|
+
return renderMolecule(plugin, molfile, style);
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
/**
|
|
@@ -95,10 +105,13 @@ export class Molecule3DViewer {
|
|
|
95
105
|
* @returns Nothing; resolves once the scene is empty.
|
|
96
106
|
*/
|
|
97
107
|
hideMolecule(): Promise<void> {
|
|
98
|
-
return this.#run(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
108
|
+
return this.#plugin.run((plugin) =>
|
|
109
|
+
this.frames.redraw(plugin, async () => {
|
|
110
|
+
await clearMeasurements(plugin);
|
|
111
|
+
await clearMolecule(plugin);
|
|
112
|
+
return 0;
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
102
115
|
}
|
|
103
116
|
|
|
104
117
|
/**
|
|
@@ -107,7 +120,7 @@ export class Molecule3DViewer {
|
|
|
107
120
|
* @returns Nothing; resolves once the surface is on screen.
|
|
108
121
|
*/
|
|
109
122
|
showSurface(style?: SurfaceStyle): Promise<void> {
|
|
110
|
-
return this.#run((plugin) => renderSurface(plugin, style));
|
|
123
|
+
return this.#plugin.run((plugin) => renderSurface(plugin, style));
|
|
111
124
|
}
|
|
112
125
|
|
|
113
126
|
/**
|
|
@@ -115,7 +128,7 @@ export class Molecule3DViewer {
|
|
|
115
128
|
* @returns Nothing; resolves once the surface is gone.
|
|
116
129
|
*/
|
|
117
130
|
hideSurface(): Promise<void> {
|
|
118
|
-
return this.#run((plugin) => clearSurface(plugin));
|
|
131
|
+
return this.#plugin.run((plugin) => clearSurface(plugin));
|
|
119
132
|
}
|
|
120
133
|
|
|
121
134
|
/**
|
|
@@ -124,7 +137,7 @@ export class Molecule3DViewer {
|
|
|
124
137
|
* @returns Nothing; resolves once the labels are on screen.
|
|
125
138
|
*/
|
|
126
139
|
showMeasurements(measurements: readonly Measurement[]): Promise<void> {
|
|
127
|
-
return this.#run((plugin) =>
|
|
140
|
+
return this.#plugin.run((plugin) =>
|
|
128
141
|
renderMeasurements(plugin, measurements, moleculeStructure),
|
|
129
142
|
);
|
|
130
143
|
}
|
|
@@ -140,7 +153,7 @@ export class Molecule3DViewer {
|
|
|
140
153
|
highlights: readonly Molecule3DHighlight[],
|
|
141
154
|
color: string,
|
|
142
155
|
): Promise<void> {
|
|
143
|
-
return this.#run(() => {
|
|
156
|
+
return this.#plugin.run(() => {
|
|
144
157
|
this.#painter?.set(highlights, Color.fromHexStyle(color));
|
|
145
158
|
});
|
|
146
159
|
}
|
|
@@ -151,7 +164,7 @@ export class Molecule3DViewer {
|
|
|
151
164
|
* @returns Nothing; resolves once the plugin has switched mode.
|
|
152
165
|
*/
|
|
153
166
|
setMeasureKind(kind: MeasurementKind | null): Promise<void> {
|
|
154
|
-
return this.#run(() => {
|
|
167
|
+
return this.#plugin.run(() => {
|
|
155
168
|
this.#picker?.setKind(kind);
|
|
156
169
|
});
|
|
157
170
|
}
|
|
@@ -164,7 +177,7 @@ export class Molecule3DViewer {
|
|
|
164
177
|
* @throws {Error} When the plugin has no screenshot helper.
|
|
165
178
|
*/
|
|
166
179
|
captureImage(size: ImageSize): Promise<string | undefined> {
|
|
167
|
-
return this.#run((plugin) => captureScene(plugin, size));
|
|
180
|
+
return this.#plugin.run((plugin) => captureScene(plugin, size));
|
|
168
181
|
}
|
|
169
182
|
|
|
170
183
|
/**
|
|
@@ -177,7 +190,7 @@ export class Molecule3DViewer {
|
|
|
177
190
|
durationMilliseconds = DEFAULT_CAMERA_DURATION,
|
|
178
191
|
fromFront = false,
|
|
179
192
|
): Promise<void> {
|
|
180
|
-
return this.#run((plugin) => {
|
|
193
|
+
return this.#plugin.run((plugin) => {
|
|
181
194
|
resetCamera(plugin, durationMilliseconds, fromFront);
|
|
182
195
|
});
|
|
183
196
|
}
|
|
@@ -189,7 +202,7 @@ export class Molecule3DViewer {
|
|
|
189
202
|
* @returns Nothing; resolves once the move has been ordered.
|
|
190
203
|
*/
|
|
191
204
|
setCamera(camera: Molecule3DCamera, durationMilliseconds = 0): Promise<void> {
|
|
192
|
-
return this.#run((plugin) => {
|
|
205
|
+
return this.#plugin.run((plugin) => {
|
|
193
206
|
applyCamera(plugin, camera, durationMilliseconds);
|
|
194
207
|
});
|
|
195
208
|
}
|
|
@@ -211,7 +224,7 @@ export class Molecule3DViewer {
|
|
|
211
224
|
* @returns Nothing; resolves once the trackball has been reconfigured.
|
|
212
225
|
*/
|
|
213
226
|
setSpin(spinning: boolean, speed = DEFAULT_SPIN_SPEED): Promise<void> {
|
|
214
|
-
return this.#run((plugin) => {
|
|
227
|
+
return this.#plugin.run((plugin) => {
|
|
215
228
|
setSpin(plugin, spinning, speed);
|
|
216
229
|
});
|
|
217
230
|
}
|
|
@@ -227,12 +240,6 @@ export class Molecule3DViewer {
|
|
|
227
240
|
this.#painter?.dispose();
|
|
228
241
|
this.#plugin.dispose();
|
|
229
242
|
}
|
|
230
|
-
|
|
231
|
-
#run<Result>(
|
|
232
|
-
action: (plugin: PluginContext) => Result | Promise<Result>,
|
|
233
|
-
): Promise<Result | undefined> {
|
|
234
|
-
return this.#plugin.run(action);
|
|
235
|
-
}
|
|
236
243
|
}
|
|
237
244
|
|
|
238
245
|
function ignore(): void {
|
|
@@ -19,6 +19,12 @@ export interface Molecule3DViewerOptions {
|
|
|
19
19
|
* @default undefined
|
|
20
20
|
*/
|
|
21
21
|
onMeasure?: (measurement: Measurement) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Called with how many frames the file on screen holds, each time a file is
|
|
24
|
+
* drawn, and with 0 once the scene is emptied.
|
|
25
|
+
* @default undefined
|
|
26
|
+
*/
|
|
27
|
+
onFrameCountChange?: (frameCount: number) => void;
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
/** How to draw the model. */
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A picture of the scene, rendered off screen at a size of its own
|
|
2
|
+
* A picture of the scene, rendered off screen at a size of its own — one
|
|
3
|
+
* frame, or every frame of the trajectory on screen.
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
import type { PluginContext } from 'molstar/lib/mol-plugin/context.js';
|
|
7
|
+
import type { ViewportScreenshotHelper } from 'molstar/lib/mol-plugin/util/viewport-screenshot.js';
|
|
8
|
+
import { Task } from 'molstar/lib/mol-task/index.js';
|
|
6
9
|
|
|
7
10
|
import type { ImageSize } from '../../molecule3d/core/exportImage.ts';
|
|
8
11
|
|
|
12
|
+
import {
|
|
13
|
+
setTrajectoryPlayback,
|
|
14
|
+
showTrajectoryFrame,
|
|
15
|
+
shownTrajectoryFrame,
|
|
16
|
+
stopTrajectory,
|
|
17
|
+
trajectoryFrameCount,
|
|
18
|
+
} from './trajectory.ts';
|
|
19
|
+
|
|
9
20
|
/**
|
|
10
21
|
* Render the scene without its selection highlights or its axes.
|
|
11
22
|
* @param plugin - The molstar context.
|
|
@@ -17,6 +28,78 @@ export async function captureScene(
|
|
|
17
28
|
plugin: PluginContext,
|
|
18
29
|
size: ImageSize,
|
|
19
30
|
): Promise<string> {
|
|
31
|
+
return pictureHelper(plugin, size).getImageDataUri();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** How a series of frames is played, so it can be resumed once captured. */
|
|
35
|
+
export interface FramePlayback {
|
|
36
|
+
playing: boolean;
|
|
37
|
+
framesPerSecond: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Render every frame of the trajectory on screen, in order, as the scene shows
|
|
42
|
+
* it: the model, its surface and the measurements over it, each at that frame.
|
|
43
|
+
* The animation is stopped while the frames are taken and resumed afterwards,
|
|
44
|
+
* and the scene is left on the frame it showed.
|
|
45
|
+
* @param plugin - The molstar context.
|
|
46
|
+
* @param size - Pixel size of each picture.
|
|
47
|
+
* @param playback - What to resume once the frames are taken.
|
|
48
|
+
* @returns One picture per frame; a scene of one model gives one.
|
|
49
|
+
* @throws {Error} When the plugin has no screenshot helper.
|
|
50
|
+
*/
|
|
51
|
+
export async function captureFrames(
|
|
52
|
+
plugin: PluginContext,
|
|
53
|
+
size: ImageSize,
|
|
54
|
+
playback: FramePlayback,
|
|
55
|
+
): Promise<ImageData[]> {
|
|
56
|
+
const pass = pictureHelper(plugin, size).imagePass;
|
|
57
|
+
const loop = plugin.animationLoop;
|
|
58
|
+
const shown = shownTrajectoryFrame(plugin);
|
|
59
|
+
const count = Math.max(1, trajectoryFrameCount(plugin));
|
|
60
|
+
await stopTrajectory(plugin);
|
|
61
|
+
loop.stop({ noDraw: true });
|
|
62
|
+
try {
|
|
63
|
+
await pass.updateBackground();
|
|
64
|
+
const frames: ImageData[] = [];
|
|
65
|
+
for (let index = 0; index < count; index++) {
|
|
66
|
+
// eslint-disable-next-line no-await-in-loop -- each frame is a commit of the state tree, and commits must not interleave
|
|
67
|
+
if (count > 1) await showTrajectoryFrame(plugin, index);
|
|
68
|
+
// The scene is otherwise committed on the next animation frame, after
|
|
69
|
+
// the picture has been taken of the previous one.
|
|
70
|
+
plugin.canvas3d?.commit(true);
|
|
71
|
+
// eslint-disable-next-line no-await-in-loop -- one render at a time on the one image pass
|
|
72
|
+
const image = await plugin.runTask(
|
|
73
|
+
Task.create('Render a frame', (runtime) =>
|
|
74
|
+
pass.getImageData(runtime, size.width, size.height),
|
|
75
|
+
),
|
|
76
|
+
);
|
|
77
|
+
frames.push(image);
|
|
78
|
+
}
|
|
79
|
+
return frames;
|
|
80
|
+
} finally {
|
|
81
|
+
if (count > 1) await showTrajectoryFrame(plugin, shown);
|
|
82
|
+
loop.start({ immediate: true });
|
|
83
|
+
await setTrajectoryPlayback(
|
|
84
|
+
plugin,
|
|
85
|
+
playback.playing,
|
|
86
|
+
playback.framesPerSecond,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The screenshot helper, set to draw a picture of `size` with no axes and an
|
|
93
|
+
* opaque background.
|
|
94
|
+
* @param plugin - The molstar context.
|
|
95
|
+
* @param size - Pixel size of the picture.
|
|
96
|
+
* @returns The helper.
|
|
97
|
+
* @throws {Error} When the plugin has no screenshot helper.
|
|
98
|
+
*/
|
|
99
|
+
function pictureHelper(
|
|
100
|
+
plugin: PluginContext,
|
|
101
|
+
size: ImageSize,
|
|
102
|
+
): ViewportScreenshotHelper {
|
|
20
103
|
const helper = plugin.helpers.viewportScreenshot;
|
|
21
104
|
if (helper === undefined) {
|
|
22
105
|
throw new Error('This viewer cannot take a picture of its scene.');
|
|
@@ -27,5 +110,5 @@ export async function captureScene(
|
|
|
27
110
|
axes: { name: 'off', params: {} },
|
|
28
111
|
transparent: false,
|
|
29
112
|
});
|
|
30
|
-
return helper
|
|
113
|
+
return helper;
|
|
31
114
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The molstar layer under the 3D views: the plugin lifecycle, the camera, the
|
|
3
|
-
* hover labels, the measurements, the highlights
|
|
3
|
+
* hover labels, the measurements, the highlights, the frames of a trajectory
|
|
4
|
+
* and the picture of a scene.
|
|
4
5
|
*
|
|
5
6
|
* Three of our sites mount a headless molstar canvas — the molecule viewer
|
|
6
7
|
* here, lcao's orbitals and symmetry's crystals — and each of them had its own
|
|
@@ -49,7 +50,8 @@ export {
|
|
|
49
50
|
} from './hover.ts';
|
|
50
51
|
export type { MolstarPluginOptions } from './plugin.ts';
|
|
51
52
|
export { MolstarPlugin } from './plugin.ts';
|
|
52
|
-
export {
|
|
53
|
+
export type { FramePlayback } from './captureScene.ts';
|
|
54
|
+
export { captureFrames, captureScene } from './captureScene.ts';
|
|
53
55
|
export type { MoleculeStructureSource } from './measurements.ts';
|
|
54
56
|
export {
|
|
55
57
|
MeasurementPicker,
|
|
@@ -57,3 +59,12 @@ export {
|
|
|
57
59
|
clearMeasurements,
|
|
58
60
|
renderMeasurements,
|
|
59
61
|
} from './measurements.ts';
|
|
62
|
+
export {
|
|
63
|
+
PLAYBACK_FPS_RANGE,
|
|
64
|
+
clampFramesPerSecond,
|
|
65
|
+
setTrajectoryPlayback,
|
|
66
|
+
showTrajectoryFrame,
|
|
67
|
+
shownTrajectoryFrame,
|
|
68
|
+
stopTrajectory,
|
|
69
|
+
trajectoryFrameCount,
|
|
70
|
+
} from './trajectory.ts';
|