Browse Source

mol-plugin: subscribe to events in initViewer
+ handle plugin resize in main render loop

dsehnal 4 years ago
parent
commit
5e5d5a63dc

+ 22 - 6
src/mol-canvas3d/canvas3d.ts

@@ -89,6 +89,7 @@ export { Canvas3DContext };
 
 /** Can be used to create multiple Canvas3D objects */
 interface Canvas3DContext {
+    readonly canvas: HTMLCanvasElement
     readonly webgl: WebGLContext
     readonly input: InputObserver
     readonly passes: Passes
@@ -170,6 +171,7 @@ namespace Canvas3DContext {
         canvas.addEventListener('webglcontextrestored', handlewWebglContextRestored, false);
 
         return {
+            canvas,
             webgl,
             input,
             passes,
@@ -224,6 +226,8 @@ interface Canvas3D {
     readonly resized: BehaviorSubject<any>
 
     handleResize(): void
+    /** performs handleResize on the next animation frame */
+    requestResize(): void
     /** Focuses camera on scene's bounding sphere, centered and zoomed. */
     requestCameraReset(options?: { durationMs?: number, snapshot?: Partial<Camera.Snapshot> }): void
     readonly camera: Camera
@@ -293,6 +297,7 @@ namespace Canvas3D {
         let cameraResetRequested = false;
         let nextCameraResetDuration: number | undefined = void 0;
         let nextCameraResetSnapshot: Partial<Camera.Snapshot> | undefined = void 0;
+        let resizeRequested = false;
 
         let notifyDidDraw = true;
 
@@ -335,6 +340,12 @@ namespace Canvas3D {
 
         function render(force: boolean) {
             if (webgl.isContextLost) return false;
+
+            if (resizeRequested) {
+                handleResize(false);
+                resizeRequested = false;
+            }
+
             if (x > gl.drawingBufferWidth || x + width < 0 ||
                 y > gl.drawingBufferHeight || y + height < 0
             ) return false;
@@ -610,6 +621,14 @@ namespace Canvas3D {
 
         const resized = new BehaviorSubject<any>(0);
 
+        function handleResize(draw = true) {
+            passes.updateSize();
+            updateViewport();
+            syncViewport();
+            if (draw) requestDraw(true);
+            resized.next(+new Date());
+        }
+
         return {
             webgl,
 
@@ -655,12 +674,9 @@ namespace Canvas3D {
             mark,
             getLoci,
 
-            handleResize: () => {
-                passes.updateSize();
-                updateViewport();
-                syncViewport();
-                requestDraw(true);
-                resized.next(+new Date());
+            handleResize,
+            requestResize: () => {
+                resizeRequested = true;
             },
             requestCameraReset: options => {
                 nextCameraResetDuration = options?.durationMs;

+ 2 - 33
src/mol-plugin-ui/viewport/canvas.tsx

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -7,11 +7,6 @@
 
 import * as React from 'react';
 import { PluginUIComponent } from '../base';
-import { resizeCanvas } from '../../mol-canvas3d/util';
-import { Subject } from 'rxjs';
-import { debounceTime } from 'rxjs/internal/operators/debounceTime';
-import { PluginConfig } from '../../mol-plugin/config';
-import { Color } from '../../mol-util/color';
 
 interface ViewportCanvasState {
     noWebGl: boolean
@@ -41,39 +36,13 @@ export class ViewportCanvas extends PluginUIComponent<ViewportCanvasParams, View
         this.setState({ showLogo: !this.plugin.canvas3d?.reprCount.value });
     }
 
-    private handleResize = () => {
-        const container = this.container.current;
-        const canvas = this.canvas.current;
-        if (container && canvas) {
-            const pixelScale = this.plugin.config.get(PluginConfig.General.PixelScale) || 1;
-            resizeCanvas(canvas, container, pixelScale);
-            const [r, g, b] = Color.toRgbNormalized(this.plugin.canvas3d!.props.renderer.backgroundColor);
-            const a = this.plugin.canvas3d!.props.transparentBackground ? 0 : 1;
-            this.plugin.canvas3d!.webgl.clear(r, g, b, a);
-            this.plugin.canvas3d!.handleResize();
-        }
-    }
-
     componentDidMount() {
         if (!this.canvas.current || !this.container.current || !this.plugin.initViewer(this.canvas.current!, this.container.current!)) {
             this.setState({ noWebGl: true });
             return;
         }
         this.handleLogo();
-        this.handleResize();
-
-        const canvas3d = this.plugin.canvas3d!;
-        this.subscribe(canvas3d.reprCount, this.handleLogo);
-
-        const resized = new Subject();
-        const resize = () => resized.next();
-
-        this.subscribe(resized.pipe(debounceTime(1000 / 24)), () => this.handleResize());
-        this.subscribe(canvas3d.input.resize, resize);
-        this.subscribe(canvas3d.interaction.click, e => this.plugin.behaviors.interaction.click.next(e));
-        this.subscribe(canvas3d.interaction.drag, e => this.plugin.behaviors.interaction.drag.next(e));
-        this.subscribe(canvas3d.interaction.hover, e => this.plugin.behaviors.interaction.hover.next(e));
-        this.subscribe(this.plugin.layout.events.updated, resize);
+        this.subscribe(this.plugin.canvas3d!.reprCount, this.handleLogo);
     }
 
     componentWillUnmount() {

+ 37 - 9
src/mol-plugin/context.ts

@@ -7,7 +7,7 @@
 
 import produce, { setAutoFreeze } from 'immer';
 import { List } from 'immutable';
-import { merge } from 'rxjs';
+import { merge, Subscription } from 'rxjs';
 import { Canvas3D, Canvas3DContext, DefaultCanvas3DParams } from '../mol-canvas3d/canvas3d';
 import { CustomProperty } from '../mol-model-props/common/custom-property';
 import { Model, Structure } from '../mol-model/structure';
@@ -61,6 +61,7 @@ import { VolumeHierarchyManager } from '../mol-plugin-state/manager/volume/hiera
 import { filter, take } from 'rxjs/operators';
 import { Vec2 } from '../mol-math/linear-algebra';
 import { PluginAnimationLoop } from './animation-loop';
+import { resizeCanvas } from '../mol-canvas3d/util';
 
 export class PluginContext {
     runTask = <T>(task: Task<T>, params?: { useOverlay?: boolean }) => this.managers.task.run(task, params);
@@ -70,6 +71,8 @@ export class PluginContext {
         return object;
     }
 
+    private subs: Subscription[] = [];
+
     private disposed = false;
     private ev = RxEventHelper.create();
 
@@ -213,6 +216,15 @@ export class PluginContext {
             }
             this.animationLoop.start();
             (this.helpers.viewportScreenshot as ViewportScreenshotHelper) = new ViewportScreenshotHelper(this);
+
+            this.subs.push(this.canvas3d!.interaction.click.subscribe(e => this.behaviors.interaction.click.next(e)));
+            this.subs.push(this.canvas3d!.interaction.drag.subscribe(e => this.behaviors.interaction.drag.next(e)));
+            this.subs.push(this.canvas3d!.interaction.hover.subscribe(e => this.behaviors.interaction.hover.next(e)));
+            this.subs.push(this.canvas3d!.input.resize.subscribe(() => this.handleResize()));
+            this.subs.push(this.layout.events.updated.subscribe(() => requestAnimationFrame(() => this.handleResize())));
+
+            this.handleResize();
+
             return true;
         } catch (e) {
             this.log.error('' + e);
@@ -221,6 +233,16 @@ export class PluginContext {
         }
     }
 
+    handleResize() {
+        const canvas = this.canvas3dContext?.canvas;
+        const container = this.layout.root;
+        if (container && canvas) {
+            const pixelScale = this.config.get(PluginConfig.General.PixelScale) || 1;
+            resizeCanvas(canvas, container, pixelScale);
+            this.canvas3d?.requestResize();
+        }
+    }
+
     readonly log = {
         entries: List<LogEntry>(),
         entry: (e: LogEntry) => this.events.log.next(e),
@@ -260,6 +282,12 @@ export class PluginContext {
 
     dispose(options?: { doNotForceWebGLContextLoss?: boolean }) {
         if (this.disposed) return;
+
+        for (const s of this.subs) {
+            s.unsubscribe();
+        }
+        this.subs = [];
+
         this.commands.dispose();
         this.canvas3d?.dispose();
         this.canvas3dContext?.dispose(options);
@@ -276,9 +304,9 @@ export class PluginContext {
     }
 
     private initBehaviorEvents() {
-        merge(this.state.data.behaviors.isUpdating, this.state.behaviors.behaviors.isUpdating).subscribe(u => {
+        this.subs.push(merge(this.state.data.behaviors.isUpdating, this.state.behaviors.behaviors.isUpdating).subscribe(u => {
             if (this.behaviors.state.isUpdating.value !== u) this.behaviors.state.isUpdating.next(u);
-        });
+        }));
 
         const timeoutMs = this.config.get(PluginConfig.General.IsBusyTimeoutMs) || 750;
         const isBusy = this.behaviors.state.isBusy;
@@ -292,7 +320,7 @@ export class PluginContext {
             timeout = void 0;
         };
 
-        merge(this.behaviors.state.isUpdating, this.behaviors.state.isAnimating).subscribe(v => {
+        this.subs.push(merge(this.behaviors.state.isUpdating, this.behaviors.state.isAnimating).subscribe(v => {
             const isUpdating = this.behaviors.state.isUpdating.value;
             const isAnimating = this.behaviors.state.isAnimating.value;
 
@@ -305,13 +333,13 @@ export class PluginContext {
                 reset();
                 isBusy.next(false);
             }
-        });
+        }));
 
-        this.behaviors.interaction.selectionMode.subscribe(v => {
+        this.subs.push(this.behaviors.interaction.selectionMode.subscribe(v => {
             if (!v) {
                 this.managers.interactivity?.lociSelects.deselectAll();
             }
-        });
+        }));
     }
 
     private initBuiltInBehavior() {
@@ -320,7 +348,7 @@ export class PluginContext {
         BuiltInPluginBehaviors.Camera.registerDefault(this);
         BuiltInPluginBehaviors.Misc.registerDefault(this);
 
-        merge(this.state.data.events.log, this.state.behaviors.events.log).subscribe(e => this.events.log.next(e));
+        this.subs.push(merge(this.state.data.events.log, this.state.behaviors.events.log).subscribe(e => this.events.log.next(e)));
     }
 
     private async initBehaviors() {
@@ -379,7 +407,7 @@ export class PluginContext {
     }
 
     async init() {
-        this.events.log.subscribe(e => this.log.entries = this.log.entries.push(e));
+        this.subs.push(this.events.log.subscribe(e => this.log.entries = this.log.entries.push(e)));
 
         this.initCustomFormats();
         this.initBehaviorEvents();

+ 1 - 1
src/mol-plugin/layout.ts

@@ -72,7 +72,7 @@ export class PluginLayout extends StatefulPluginComponent<PluginLayoutStateProps
         this.events.updated.next();
     }
 
-    private root: HTMLElement | undefined;
+    root: HTMLElement | undefined;
     private rootState: RootState | undefined = void 0;
     private expandedViewport: HTMLMetaElement;