react-cheminfo 0.18.0 → 0.19.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/lib/about/core/about.d.ts +9 -0
- package/lib/about/core/about.d.ts.map +1 -1
- package/lib/about/core/about.js +1 -0
- package/lib/about/core/about.js.map +1 -1
- package/lib/about/core/index.d.ts +1 -0
- package/lib/about/core/index.d.ts.map +1 -1
- package/lib/about/core/index.js +1 -0
- package/lib/about/core/index.js.map +1 -1
- package/lib/about/core/repository.d.ts +14 -0
- package/lib/about/core/repository.d.ts.map +1 -0
- package/lib/about/core/repository.js +21 -0
- package/lib/about/core/repository.js.map +1 -0
- package/lib/about/ui/AboutBuild.d.ts +5 -4
- package/lib/about/ui/AboutBuild.d.ts.map +1 -1
- package/lib/about/ui/AboutBuild.js +10 -10
- package/lib/about/ui/AboutBuild.js.map +1 -1
- package/lib/about/ui/AboutPage.d.ts.map +1 -1
- package/lib/about/ui/AboutPage.js +7 -8
- package/lib/about/ui/AboutPage.js.map +1 -1
- package/lib/about/ui/AboutVersion.d.ts +25 -0
- package/lib/about/ui/AboutVersion.d.ts.map +1 -0
- package/lib/about/ui/AboutVersion.js +31 -0
- package/lib/about/ui/AboutVersion.js.map +1 -0
- package/lib/about/ui/index.d.ts +2 -0
- package/lib/about/ui/index.d.ts.map +1 -1
- package/lib/about/ui/index.js +1 -0
- package/lib/about/ui/index.js.map +1 -1
- package/lib/build/core/buildInfo.d.ts +16 -0
- package/lib/build/core/buildInfo.d.ts.map +1 -1
- package/lib/build/core/buildInfo.js +21 -0
- package/lib/build/core/buildInfo.js.map +1 -1
- package/lib/build/core/index.d.ts +1 -1
- package/lib/build/core/index.d.ts.map +1 -1
- package/lib/build/core/index.js +1 -1
- package/lib/build/core/index.js.map +1 -1
- package/lib/build/vite/buildInfo.d.ts.map +1 -1
- package/lib/build/vite/buildInfo.js +2 -2
- package/lib/build/vite/buildInfo.js.map +1 -1
- package/lib/ecosystem/core/sites.d.ts.map +1 -1
- package/lib/ecosystem/core/sites.js +12 -0
- package/lib/ecosystem/core/sites.js.map +1 -1
- package/lib/ecosystem/core/types.d.ts +8 -0
- package/lib/ecosystem/core/types.d.ts.map +1 -1
- package/lib/molecule3d/core/camera.d.ts +89 -0
- package/lib/molecule3d/core/camera.d.ts.map +1 -0
- package/lib/molecule3d/core/camera.js +172 -0
- package/lib/molecule3d/core/camera.js.map +1 -0
- package/lib/molecule3d/core/index.d.ts +2 -0
- 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/ui/MoleculeCanvas3D.d.ts.map +1 -1
- package/lib/molecule3d/ui/MoleculeCanvas3D.js +14 -37
- package/lib/molecule3d/ui/MoleculeCanvas3D.js.map +1 -1
- package/lib/molecule3d/ui/camera.d.ts +35 -2
- package/lib/molecule3d/ui/camera.d.ts.map +1 -1
- package/lib/molecule3d/ui/camera.js +113 -4
- package/lib/molecule3d/ui/camera.js.map +1 -1
- package/lib/molecule3d/ui/captureScene.d.ts +14 -0
- package/lib/molecule3d/ui/captureScene.d.ts.map +1 -0
- package/lib/molecule3d/ui/captureScene.js +24 -0
- package/lib/molecule3d/ui/captureScene.js.map +1 -0
- package/lib/molecule3d/ui/createViewer.d.ts +18 -0
- package/lib/molecule3d/ui/createViewer.d.ts.map +1 -0
- package/lib/molecule3d/ui/createViewer.js +19 -0
- package/lib/molecule3d/ui/createViewer.js.map +1 -0
- package/lib/molecule3d/ui/moleculeViewer3DProps.d.ts +21 -0
- package/lib/molecule3d/ui/moleculeViewer3DProps.d.ts.map +1 -1
- package/lib/molecule3d/ui/useMoleculeScene.d.ts +47 -0
- package/lib/molecule3d/ui/useMoleculeScene.d.ts.map +1 -0
- package/lib/molecule3d/ui/useMoleculeScene.js +82 -0
- package/lib/molecule3d/ui/useMoleculeScene.js.map +1 -0
- package/lib/molecule3d/ui/useSharedCamera.d.ts +52 -0
- package/lib/molecule3d/ui/useSharedCamera.d.ts.map +1 -0
- package/lib/molecule3d/ui/useSharedCamera.js +82 -0
- package/lib/molecule3d/ui/useSharedCamera.js.map +1 -0
- package/lib/molecule3d/ui/viewer.d.ts +21 -15
- package/lib/molecule3d/ui/viewer.d.ts.map +1 -1
- package/lib/molecule3d/ui/viewer.js +41 -47
- package/lib/molecule3d/ui/viewer.js.map +1 -1
- package/lib/molecule3d/ui/viewerSpec.d.ts +17 -0
- package/lib/molecule3d/ui/viewerSpec.d.ts.map +1 -0
- package/lib/molecule3d/ui/viewerSpec.js +34 -0
- package/lib/molecule3d/ui/viewerSpec.js.map +1 -0
- package/package.json +1 -1
- package/src/about/core/about.ts +11 -0
- package/src/about/core/index.ts +1 -0
- package/src/about/core/repository.ts +22 -0
- package/src/about/ui/AboutBuild.tsx +13 -20
- package/src/about/ui/AboutPage.tsx +30 -22
- package/src/about/ui/AboutVersion.tsx +67 -0
- package/src/about/ui/index.ts +2 -0
- package/src/build/core/buildInfo.ts +24 -0
- package/src/build/core/index.ts +6 -1
- package/src/build/vite/buildInfo.ts +2 -2
- package/src/ecosystem/core/sites.ts +12 -0
- package/src/ecosystem/core/types.ts +8 -0
- package/src/molecule3d/core/camera.ts +212 -0
- package/src/molecule3d/core/index.ts +14 -0
- package/src/molecule3d/ui/MoleculeCanvas3D.tsx +14 -43
- package/src/molecule3d/ui/camera.ts +128 -7
- package/src/molecule3d/ui/captureScene.ts +31 -0
- package/src/molecule3d/ui/createViewer.ts +24 -0
- package/src/molecule3d/ui/moleculeViewer3DProps.ts +21 -0
- package/src/molecule3d/ui/useMoleculeScene.ts +141 -0
- package/src/molecule3d/ui/useSharedCamera.ts +121 -0
- package/src/molecule3d/ui/viewer.ts +45 -50
- package/src/molecule3d/ui/viewerSpec.ts +38 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeping one molstar canvas showing what the props describe: the model, its
|
|
3
|
+
* surface, its measurements, and where the camera stands over them.
|
|
4
|
+
*
|
|
5
|
+
* The draw is coalesced into one animation frame, so dragging a slider costs
|
|
6
|
+
* one rebuild rather than one per pixel, and the camera is framed only when the
|
|
7
|
+
* molecule is new — a restyle that snapped the view back would undo the
|
|
8
|
+
* reader's orientation.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { RefObject } from 'react';
|
|
12
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
13
|
+
|
|
14
|
+
import type { Molecule3DCamera } from '../core/camera.ts';
|
|
15
|
+
import type { Measurement } from '../core/measurement.ts';
|
|
16
|
+
import type { Molecule3DFile, Molecule3DSettings } from '../core/settings.ts';
|
|
17
|
+
|
|
18
|
+
import { drawScene } from './drawScene.ts';
|
|
19
|
+
import type { MoleculeViewer3DProps } from './moleculeViewer3DProps.ts';
|
|
20
|
+
import {
|
|
21
|
+
DEFAULT_CAMERA_SETTLE_DELAY,
|
|
22
|
+
useSharedCamera,
|
|
23
|
+
} from './useSharedCamera.ts';
|
|
24
|
+
import type { Molecule3DViewer } from './viewer.ts';
|
|
25
|
+
|
|
26
|
+
/** What {@link useMoleculeScene} is given. */
|
|
27
|
+
export interface MoleculeSceneOptions {
|
|
28
|
+
/** The element molstar draws in, or `null` before it exists. */
|
|
29
|
+
container: HTMLElement | null;
|
|
30
|
+
/** The viewer mounted in it. */
|
|
31
|
+
viewerRef: RefObject<Molecule3DViewer | null>;
|
|
32
|
+
/** What to draw, or `null` to empty the scene. */
|
|
33
|
+
molfile: Molecule3DFile | null;
|
|
34
|
+
/** How the camera meets a molecule it has not seen before. */
|
|
35
|
+
frameNewMolecule: 'front' | 'keep';
|
|
36
|
+
/** How the model is drawn. */
|
|
37
|
+
settings: Molecule3DSettings;
|
|
38
|
+
/** What is drawn over it. */
|
|
39
|
+
measurements: readonly Measurement[];
|
|
40
|
+
/** Whether the model is turning on its own. */
|
|
41
|
+
spinning: boolean;
|
|
42
|
+
/** Called with a drawing failure, and with `null` once a draw succeeds. */
|
|
43
|
+
onFailureChange: (message: string | null) => void;
|
|
44
|
+
/** Where the camera stands when the first molecule is framed, and who to tell. */
|
|
45
|
+
camera: Pick<
|
|
46
|
+
MoleculeViewer3DProps,
|
|
47
|
+
'cameraSettleDelay' | 'initialCamera' | 'onCameraChange'
|
|
48
|
+
>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** What {@link useMoleculeScene} answers. */
|
|
52
|
+
export interface MoleculeScene {
|
|
53
|
+
/** Frame everything on screen again, as the toolbar's reset button does. */
|
|
54
|
+
resetView: () => void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Draw the scene the options describe, and keep it drawn.
|
|
59
|
+
* @param options - See {@link MoleculeSceneOptions}.
|
|
60
|
+
* @returns See {@link MoleculeScene}.
|
|
61
|
+
*/
|
|
62
|
+
export function useMoleculeScene(options: MoleculeSceneOptions): MoleculeScene {
|
|
63
|
+
const {
|
|
64
|
+
container,
|
|
65
|
+
viewerRef,
|
|
66
|
+
molfile,
|
|
67
|
+
frameNewMolecule,
|
|
68
|
+
settings,
|
|
69
|
+
measurements,
|
|
70
|
+
spinning,
|
|
71
|
+
onFailureChange,
|
|
72
|
+
camera: cameraProps,
|
|
73
|
+
} = options;
|
|
74
|
+
const framedRef = useRef<{
|
|
75
|
+
molfile: Molecule3DFile;
|
|
76
|
+
viewer: Molecule3DViewer;
|
|
77
|
+
} | null>(null);
|
|
78
|
+
const reportFailure = useRef(onFailureChange);
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
reportFailure.current = onFailureChange;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const camera = useSharedCamera({
|
|
84
|
+
container,
|
|
85
|
+
viewerRef,
|
|
86
|
+
spinning,
|
|
87
|
+
initialCamera: cameraProps.initialCamera ?? null,
|
|
88
|
+
onCameraChange: cameraProps.onCameraChange,
|
|
89
|
+
settleDelay: cameraProps.cameraSettleDelay ?? DEFAULT_CAMERA_SETTLE_DELAY,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
const viewer = viewerRef.current;
|
|
94
|
+
if (container === null || viewer === null) return;
|
|
95
|
+
let cancelled = false;
|
|
96
|
+
const frame = requestAnimationFrame(() => {
|
|
97
|
+
const framed = framedRef.current;
|
|
98
|
+
const isNew =
|
|
99
|
+
molfile !== null &&
|
|
100
|
+
(framed?.molfile !== molfile || framed.viewer !== viewer);
|
|
101
|
+
framedRef.current = molfile === null ? null : { molfile, viewer };
|
|
102
|
+
// The link's camera is spent on the first molecule framed; afterwards the
|
|
103
|
+
// camera is the reader's and a new molecule is framed as usual.
|
|
104
|
+
const linked: Molecule3DCamera | null = isNew
|
|
105
|
+
? camera.takeLinkedCamera()
|
|
106
|
+
: null;
|
|
107
|
+
const frameCamera = isNew && linked === null ? frameNewMolecule : 'none';
|
|
108
|
+
void drawScene(viewer, molfile, settings, measurements, frameCamera)
|
|
109
|
+
.then(() => {
|
|
110
|
+
if (cancelled) return;
|
|
111
|
+
if (linked !== null) void viewer.setCamera(linked);
|
|
112
|
+
reportFailure.current(null);
|
|
113
|
+
})
|
|
114
|
+
.catch((error: unknown) => {
|
|
115
|
+
if (cancelled) return;
|
|
116
|
+
reportFailure.current(
|
|
117
|
+
error instanceof Error ? error.message : String(error),
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
return () => {
|
|
122
|
+
cancelled = true;
|
|
123
|
+
cancelAnimationFrame(frame);
|
|
124
|
+
};
|
|
125
|
+
}, [
|
|
126
|
+
container,
|
|
127
|
+
viewerRef,
|
|
128
|
+
molfile,
|
|
129
|
+
frameNewMolecule,
|
|
130
|
+
settings,
|
|
131
|
+
measurements,
|
|
132
|
+
camera,
|
|
133
|
+
]);
|
|
134
|
+
|
|
135
|
+
const resetView = useCallback(() => {
|
|
136
|
+
camera.noteMove();
|
|
137
|
+
void viewerRef.current?.resetCamera();
|
|
138
|
+
}, [camera, viewerRef]);
|
|
139
|
+
|
|
140
|
+
return { resetView };
|
|
141
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The camera of the canvas as something a site can keep: the one a link
|
|
3
|
+
* carried, applied to the first molecule framed, and every move the reader
|
|
4
|
+
* makes afterwards, reported once it has come to rest.
|
|
5
|
+
*
|
|
6
|
+
* The camera is never controlled. It belongs to whoever is dragging it, so the
|
|
7
|
+
* site is told where it went rather than asked where it should be — anything
|
|
8
|
+
* else fights the trackball.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { RefObject } from 'react';
|
|
12
|
+
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
13
|
+
|
|
14
|
+
import type { Molecule3DCamera } from '../core/camera.ts';
|
|
15
|
+
import { formatMolecule3DCamera } from '../core/camera.ts';
|
|
16
|
+
|
|
17
|
+
import type { Molecule3DViewer } from './viewer.ts';
|
|
18
|
+
|
|
19
|
+
/** How long the camera must be still before a move is reported, milliseconds. */
|
|
20
|
+
export const DEFAULT_CAMERA_SETTLE_DELAY = 400;
|
|
21
|
+
|
|
22
|
+
/** What {@link useSharedCamera} is given. */
|
|
23
|
+
export interface SharedCameraOptions {
|
|
24
|
+
/** The element molstar draws in, or `null` before it exists. */
|
|
25
|
+
container: HTMLElement | null;
|
|
26
|
+
/** The viewer mounted in it. */
|
|
27
|
+
viewerRef: RefObject<Molecule3DViewer | null>;
|
|
28
|
+
/** Where the camera stands when the first molecule is framed. */
|
|
29
|
+
initialCamera: Molecule3DCamera | null;
|
|
30
|
+
/** Called once a move of the reader's has come to rest. */
|
|
31
|
+
onCameraChange?: (camera: Molecule3DCamera) => void;
|
|
32
|
+
/** Whether the model is turning, which suspends reporting. */
|
|
33
|
+
spinning: boolean;
|
|
34
|
+
/** How long the camera must be still before a move is reported. */
|
|
35
|
+
settleDelay: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* What {@link useSharedCamera} answers. The same object on every render: the
|
|
40
|
+
* draw effect keys on it, and a fresh one would tear the scene down and build
|
|
41
|
+
* it again — which makes molstar frame the model afresh and loses the camera a
|
|
42
|
+
* link had just restored.
|
|
43
|
+
*/
|
|
44
|
+
export interface SharedCamera {
|
|
45
|
+
/**
|
|
46
|
+
* The camera a link carried, answered once and then never again — the next
|
|
47
|
+
* molecule is framed the way the viewer frames any other.
|
|
48
|
+
* @returns The camera to stand at, or `null` to frame the molecule.
|
|
49
|
+
*/
|
|
50
|
+
takeLinkedCamera: () => Molecule3DCamera | null;
|
|
51
|
+
/** Note a move of the reader's, which is what starts reporting. */
|
|
52
|
+
noteMove: () => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Keep the camera of one canvas in step with the site that shows it.
|
|
57
|
+
* @param options - See {@link SharedCameraOptions}.
|
|
58
|
+
* @returns See {@link SharedCamera}.
|
|
59
|
+
*/
|
|
60
|
+
export function useSharedCamera(options: SharedCameraOptions): SharedCamera {
|
|
61
|
+
const { container, viewerRef, settleDelay } = options;
|
|
62
|
+
// Spent on the first molecule framed.
|
|
63
|
+
const linkedRef = useRef(false);
|
|
64
|
+
// What the last report said, so the settling of a move we ordered ourselves
|
|
65
|
+
// is not handed back to the site as a move the reader made.
|
|
66
|
+
const reportedRef = useRef<string | null>(null);
|
|
67
|
+
// Whether the reader has touched the canvas, which is what starts reporting.
|
|
68
|
+
const movedRef = useRef(false);
|
|
69
|
+
const latest = useRef(options);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
latest.current = options;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const noteMove = useCallback(() => {
|
|
75
|
+
movedRef.current = true;
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
const takeLinkedCamera = useCallback(() => {
|
|
79
|
+
if (linkedRef.current) return null;
|
|
80
|
+
linkedRef.current = true;
|
|
81
|
+
const camera = latest.current.initialCamera;
|
|
82
|
+
if (camera !== null) reportedRef.current = formatMolecule3DCamera(camera);
|
|
83
|
+
return camera;
|
|
84
|
+
}, []);
|
|
85
|
+
|
|
86
|
+
// Reporting waits for a first gesture. The camera also drifts on its own — a
|
|
87
|
+
// surface added after the model was framed grows the scene, and the same
|
|
88
|
+
// camera then reads as slightly closer — and a view nobody chose is not one
|
|
89
|
+
// to hand back to the site, let alone to put in a link.
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
const viewer = viewerRef.current;
|
|
92
|
+
if (container === null || viewer === null) return;
|
|
93
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
94
|
+
container.addEventListener('pointerdown', noteMove);
|
|
95
|
+
container.addEventListener('wheel', noteMove, { passive: true });
|
|
96
|
+
const stopWatching = viewer.watchCamera((camera) => {
|
|
97
|
+
if (camera === null) return;
|
|
98
|
+
if (timer !== null) clearTimeout(timer);
|
|
99
|
+
timer = setTimeout(() => {
|
|
100
|
+
timer = null;
|
|
101
|
+
const current = latest.current;
|
|
102
|
+
if (current.spinning || !movedRef.current) return;
|
|
103
|
+
const text = formatMolecule3DCamera(camera);
|
|
104
|
+
if (text === reportedRef.current) return;
|
|
105
|
+
reportedRef.current = text;
|
|
106
|
+
current.onCameraChange?.(camera);
|
|
107
|
+
}, settleDelay);
|
|
108
|
+
});
|
|
109
|
+
return () => {
|
|
110
|
+
if (timer !== null) clearTimeout(timer);
|
|
111
|
+
container.removeEventListener('pointerdown', noteMove);
|
|
112
|
+
container.removeEventListener('wheel', noteMove);
|
|
113
|
+
stopWatching();
|
|
114
|
+
};
|
|
115
|
+
}, [container, viewerRef, settleDelay, noteMove]);
|
|
116
|
+
|
|
117
|
+
return useMemo(
|
|
118
|
+
() => ({ takeLinkedCamera, noteMove }),
|
|
119
|
+
[takeLinkedCamera, noteMove],
|
|
120
|
+
);
|
|
121
|
+
}
|
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Lifecycle of one molstar molecule viewer
|
|
2
|
+
* Lifecycle of one molstar molecule viewer: `createMolecule3DViewer` builds
|
|
3
|
+
* one, and every operation a component performs on it is a method here.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* always be called; the work is queued behind `ready`.
|
|
9
|
-
*
|
|
10
|
-
* molstar's own UI is not mounted: every control is the component's.
|
|
5
|
+
* The constructor returns before the canvas exists — see `createViewer.ts` —
|
|
6
|
+
* so every method queues its work behind `ready` and resolves to nothing once
|
|
7
|
+
* the viewer has been disposed. molstar's own UI is not mounted: every control
|
|
8
|
+
* is the component's.
|
|
11
9
|
*/
|
|
12
10
|
|
|
13
|
-
import { PluginViewModel } from 'molstar/lib/extensions/plugin/view-model.js';
|
|
11
|
+
import type { PluginViewModel } from 'molstar/lib/extensions/plugin/view-model.js';
|
|
14
12
|
import type { PluginContext } from 'molstar/lib/mol-plugin/context.js';
|
|
15
|
-
// Lowercased on import: it is a factory, not a constructor.
|
|
16
|
-
import { DefaultPluginSpec as defaultPluginSpec } from 'molstar/lib/mol-plugin/spec.js';
|
|
17
|
-
import { Color } from 'molstar/lib/mol-util/color/color.js';
|
|
18
13
|
|
|
14
|
+
import type { Molecule3DCamera } from '../core/camera.ts';
|
|
19
15
|
import type { ImageSize } from '../core/exportImage.ts';
|
|
20
16
|
import type { Measurement, MeasurementKind } from '../core/measurement.ts';
|
|
21
17
|
import type { Molecule3DFile } from '../core/settings.ts';
|
|
@@ -23,9 +19,12 @@ import type { Molecule3DFile } from '../core/settings.ts';
|
|
|
23
19
|
import {
|
|
24
20
|
DEFAULT_CAMERA_DURATION,
|
|
25
21
|
DEFAULT_SPIN_SPEED,
|
|
22
|
+
applyCamera,
|
|
26
23
|
resetCamera,
|
|
27
24
|
setSpin,
|
|
25
|
+
watchCamera,
|
|
28
26
|
} from './camera.ts';
|
|
27
|
+
import { captureScene } from './captureScene.ts';
|
|
29
28
|
import {
|
|
30
29
|
MeasurementPicker,
|
|
31
30
|
clearMeasurements,
|
|
@@ -33,25 +32,13 @@ import {
|
|
|
33
32
|
} from './measurements.ts';
|
|
34
33
|
import { clearMolecule, renderMolecule } from './renderMolecule.ts';
|
|
35
34
|
import { clearSurface, renderSurface } from './renderSurface.ts';
|
|
35
|
+
import { mountMolecule3DPlugin } from './viewerSpec.ts';
|
|
36
36
|
import type {
|
|
37
37
|
Molecule3DViewerOptions,
|
|
38
38
|
MoleculeStyle,
|
|
39
39
|
SurfaceStyle,
|
|
40
40
|
} from './viewerTypes.ts';
|
|
41
41
|
|
|
42
|
-
/**
|
|
43
|
-
* Create a viewer inside `container` and start initialising it.
|
|
44
|
-
* @param container - A positioned element; molstar inserts its canvas into it.
|
|
45
|
-
* @param options - See {@link Molecule3DViewerOptions}.
|
|
46
|
-
* @returns A handle that is safe to dispose immediately.
|
|
47
|
-
*/
|
|
48
|
-
export function createMolecule3DViewer(
|
|
49
|
-
container: HTMLElement,
|
|
50
|
-
options: Molecule3DViewerOptions = {},
|
|
51
|
-
): Molecule3DViewer {
|
|
52
|
-
return new Molecule3DViewer(container, options);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
42
|
/**
|
|
56
43
|
* One molstar canvas, and every operation the component performs on it. Every
|
|
57
44
|
* method resolves to nothing once `dispose` has been called.
|
|
@@ -74,18 +61,7 @@ export class Molecule3DViewer {
|
|
|
74
61
|
background = '#ffffff', // tokens-ok: a WebGL clear colour
|
|
75
62
|
onMeasure = ignore,
|
|
76
63
|
} = options;
|
|
77
|
-
|
|
78
|
-
this.#model = new PluginViewModel({
|
|
79
|
-
spec: {
|
|
80
|
-
...spec,
|
|
81
|
-
canvas3d: {
|
|
82
|
-
...spec.canvas3d,
|
|
83
|
-
renderer: { backgroundColor: Color.fromHexStyle(background) },
|
|
84
|
-
camera: { helper: { axes: { name: 'off', params: {} } } },
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
this.#model.mount(container);
|
|
64
|
+
this.#model = mountMolecule3DPlugin(container, background);
|
|
89
65
|
this.ready = this.#model.initialized;
|
|
90
66
|
// Registered before any `#run`, so the picker exists by the time one runs.
|
|
91
67
|
this.ready.then(() => {
|
|
@@ -164,19 +140,7 @@ export class Molecule3DViewer {
|
|
|
164
140
|
* @throws {Error} When the plugin has no screenshot helper.
|
|
165
141
|
*/
|
|
166
142
|
captureImage(size: ImageSize): Promise<string | undefined> {
|
|
167
|
-
return this.#run(
|
|
168
|
-
const helper = plugin.helpers.viewportScreenshot;
|
|
169
|
-
if (helper === undefined) {
|
|
170
|
-
throw new Error('This viewer cannot take a picture of its scene.');
|
|
171
|
-
}
|
|
172
|
-
helper.behaviors.values.next({
|
|
173
|
-
...helper.values,
|
|
174
|
-
resolution: { name: 'custom', params: size },
|
|
175
|
-
axes: { name: 'off', params: {} },
|
|
176
|
-
transparent: false,
|
|
177
|
-
});
|
|
178
|
-
return helper.getImageDataUri();
|
|
179
|
-
});
|
|
143
|
+
return this.#run((plugin) => captureScene(plugin, size));
|
|
180
144
|
}
|
|
181
145
|
|
|
182
146
|
/**
|
|
@@ -194,6 +158,37 @@ export class Molecule3DViewer {
|
|
|
194
158
|
});
|
|
195
159
|
}
|
|
196
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Put the camera where a link says it stood.
|
|
163
|
+
* @param camera - Where to stand.
|
|
164
|
+
* @param durationMilliseconds - Transition length; 0 jumps.
|
|
165
|
+
* @returns Nothing; resolves once the move has been ordered.
|
|
166
|
+
*/
|
|
167
|
+
setCamera(camera: Molecule3DCamera, durationMilliseconds = 0): Promise<void> {
|
|
168
|
+
return this.#run((plugin) => {
|
|
169
|
+
applyCamera(plugin, camera, durationMilliseconds);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Follow the camera, however it moves.
|
|
175
|
+
* @param listener - Called after every move.
|
|
176
|
+
* @returns A function that stops calling the listener; safe to call before
|
|
177
|
+
* the canvas exists.
|
|
178
|
+
*/
|
|
179
|
+
watchCamera(listener: (camera: Molecule3DCamera | null) => void): () => void {
|
|
180
|
+
let stop: (() => void) | null = null;
|
|
181
|
+
let cancelled = false;
|
|
182
|
+
void this.#run((plugin) => {
|
|
183
|
+
if (!cancelled) stop = watchCamera(plugin, listener);
|
|
184
|
+
});
|
|
185
|
+
return () => {
|
|
186
|
+
cancelled = true;
|
|
187
|
+
stop?.();
|
|
188
|
+
stop = null;
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
197
192
|
/**
|
|
198
193
|
* Turn the automatic spin on or off.
|
|
199
194
|
* @param spinning - Whether the scene should keep turning.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How the molecule viewer configures molstar: the plugin spec it mounts with.
|
|
3
|
+
*
|
|
4
|
+
* molstar's own UI is not mounted — every control is the component's — and the
|
|
5
|
+
* axes helper is off, since a molecule is read from its own shape rather than
|
|
6
|
+
* from the world axes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { PluginViewModel } from 'molstar/lib/extensions/plugin/view-model.js';
|
|
10
|
+
// Lowercased on import: it is a factory, not a constructor.
|
|
11
|
+
import { DefaultPluginSpec as defaultPluginSpec } from 'molstar/lib/mol-plugin/spec.js';
|
|
12
|
+
import { Color } from 'molstar/lib/mol-util/color/color.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Build a view model for one canvas and mount it.
|
|
16
|
+
* @param container - A positioned element; molstar inserts its canvas into it.
|
|
17
|
+
* @param background - Scene background as `#rrggbb`; a WebGL clear colour, so
|
|
18
|
+
* it cannot be a CSS custom property.
|
|
19
|
+
* @returns The mounted view model, still initialising.
|
|
20
|
+
*/
|
|
21
|
+
export function mountMolecule3DPlugin(
|
|
22
|
+
container: HTMLElement,
|
|
23
|
+
background: string,
|
|
24
|
+
): PluginViewModel {
|
|
25
|
+
const spec = defaultPluginSpec();
|
|
26
|
+
const model = new PluginViewModel({
|
|
27
|
+
spec: {
|
|
28
|
+
...spec,
|
|
29
|
+
canvas3d: {
|
|
30
|
+
...spec.canvas3d,
|
|
31
|
+
renderer: { backgroundColor: Color.fromHexStyle(background) },
|
|
32
|
+
camera: { helper: { axes: { name: 'off', params: {} } } },
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
model.mount(container);
|
|
37
|
+
return model;
|
|
38
|
+
}
|