Преглед на файлове

mol-plugin: added Download Screenshot icon

David Sehnal преди 5 години
родител
ревизия
ebcc45d1fd
променени са 4 файла, в които са добавени 93 реда и са изтрити 45 реда
  1. 4 1
      src/mol-plugin/context.ts
  2. 8 44
      src/mol-plugin/ui/image.tsx
  3. 5 0
      src/mol-plugin/ui/viewport.tsx
  4. 76 0
      src/mol-plugin/util/viewport-screenshot.ts

+ 4 - 1
src/mol-plugin/context.ts

@@ -42,6 +42,7 @@ import { StructureSelectionHelper } from './util/structure-selection-helper';
 import { StructureOverpaintHelper } from './util/structure-overpaint-helper';
 import { PluginToastManager } from './state/toast';
 import { StructureMeasurementManager } from './util/structure-measurement';
+import { ViewportScreenshotWrapper } from './util/viewport-screenshot';
 
 interface Log {
     entries: List<LogEntry>
@@ -132,7 +133,8 @@ export class PluginContext {
         structureRepresentation: new StructureRepresentationHelper(this),
         structureOverpaint: new StructureOverpaintHelper(this),
         substructureParent: new SubstructureParentHelper(this),
-        measurement: new StructureMeasurementManager(this)
+        measurement: new StructureMeasurementManager(this),
+        viewportScreenshot: void 0 as ViewportScreenshotWrapper | undefined
     } as const;
 
     /**
@@ -151,6 +153,7 @@ export class PluginContext {
             const renderer = this.canvas3d.props.renderer;
             PluginCommands.Canvas3D.SetSettings.dispatch(this, { settings: { renderer: { ...renderer, backgroundColor: Color(0xFCFBF9) } } });
             this.canvas3d.animate();
+            (this.helpers.viewportScreenshot as ViewportScreenshotWrapper) = new ViewportScreenshotWrapper(this);
             return true;
         } catch (e) {
             this.log.error('' + e);

+ 8 - 44
src/mol-plugin/ui/image.tsx

@@ -8,12 +8,7 @@ import * as React from 'react';
 import { CollapsableControls, CollapsableState } from './base';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ParameterControls } from './controls/parameters';
-import { ImagePass } from '../../mol-canvas3d/passes/image';
-import { download } from '../../mol-util/download';
-import { setCanvasSize, canvasToBlob } from '../../mol-canvas3d/util';
-import { Task } from '../../mol-task';
-import { StateSelection } from '../../mol-state';
-import { PluginStateObject } from '../state/objects';
+import { setCanvasSize } from '../../mol-canvas3d/util';
 
 interface ImageControlsState extends CollapsableState {
     showPreview: boolean
@@ -34,7 +29,11 @@ export class ImageControls<P, S extends ImageControlsState> extends CollapsableC
     private canvas: HTMLCanvasElement
     private canvasContext: CanvasRenderingContext2D
 
-    private imagePass: ImagePass
+    // private imagePass: ImagePass
+
+    get imagePass() {
+        return this.plugin.helpers.viewportScreenshot!.imagePass;
+    }
 
     constructor(props: P, context?: any) {
         super(props, context)
@@ -74,38 +73,8 @@ export class ImageControls<P, S extends ImageControlsState> extends CollapsableC
         this.canvasContext.putImageData(imageData, 0, 0)
     }
 
-    private getFilename() {
-        const models = this.plugin.state.dataState.select(StateSelection.Generators.rootsOfType(PluginStateObject.Molecule.Model)).map(s => s.obj!.data)
-        const uniqueIds = new Set<string>()
-        models.forEach(m => uniqueIds.add(m.entryId.toUpperCase()))
-        const idString = Array.from(uniqueIds).join('-')
-        return `${idString || 'molstar-image'}.png`
-    }
-
-    private downloadTask = () => {
-        return Task.create('Download Image', async ctx => {
-            const { width, height } = this.getSize()
-            if (width <= 0 || height <= 0) return
-
-            await ctx.update('Rendering image...')
-            const imageData = this.imagePass.getImageData(width, height)
-
-            await ctx.update('Encoding image...')
-            const canvas = document.createElement('canvas')
-            canvas.width = imageData.width
-            canvas.height = imageData.height
-            const canvasCtx = canvas.getContext('2d')
-            if (!canvasCtx) throw new Error('Could not create canvas 2d context')
-            canvasCtx.putImageData(imageData, 0, 0)
-
-            await ctx.update('Downloading image...')
-            const blob = await canvasToBlob(canvas, 'png')
-            download(blob, this.getFilename())
-        })
-    }
-
     private download = () => {
-        this.plugin.runTask(this.downloadTask())
+        this.plugin.helpers.viewportScreenshot?.download();
     }
 
     private syncCanvas() {
@@ -126,16 +95,11 @@ export class ImageControls<P, S extends ImageControlsState> extends CollapsableC
     }
 
     componentDidUpdate() {
+        this.plugin.helpers.viewportScreenshot!.size = this.getSize();
         this.handlePreview()
     }
 
     componentDidMount() {
-        this.imagePass = this.plugin.canvas3d.getImagePass()
-        this.imagePass.setProps({
-            multiSample: { mode: 'on', sampleLevel: 2 },
-            postprocessing: this.plugin.canvas3d.props.postprocessing
-        })
-
         this.handlePreview()
 
         this.subscribe(this.plugin.events.canvas3d.settingsUpdated, () => {

+ 5 - 0
src/mol-plugin/ui/viewport.tsx

@@ -58,6 +58,10 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
         PluginCommands.Interactivity.SetProps.dispatch(this.plugin, { props: { [p.name]: p.value } });
     }
 
+    screenshot = () => {
+        this.plugin.helpers.viewportScreenshot?.download();
+    }
+
     componentDidMount() {
         this.subscribe(this.plugin.events.canvas3d.settingsUpdated, () => this.forceUpdate());
         this.subscribe(this.plugin.layout.events.updated, () => this.forceUpdate());
@@ -78,6 +82,7 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
             <div className='msp-viewport-controls-buttons'>
                 <div className='msp-semi-transparent-background' />
                 {this.icon('reset-scene', this.resetCamera, 'Reset Camera')}
+                {this.icon('screenshot', this.screenshot, 'Download Screenshot')}
                 {this.icon('tools', this.toggleControls, 'Toggle Controls', this.plugin.layout.state.showControls)}
                 {this.icon('expand-layout', this.toggleExpanded, 'Toggle Expanded', this.plugin.layout.state.isExpanded)}
                 {!this.props.hideSettingsIcon && this.icon('settings', this.toggleSettingsExpanded, 'Settings', this.state.isSettingsExpanded)}

+ 76 - 0
src/mol-plugin/util/viewport-screenshot.ts

@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2019 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>
+ */
+
+import { PluginContext } from '../context';
+import { ImagePass } from '../../mol-canvas3d/passes/image';
+import { StateSelection } from '../../mol-state';
+import { PluginStateObject } from '../state/objects';
+import { Task } from '../../mol-task';
+import { canvasToBlob } from '../../mol-canvas3d/util';
+import { download } from '../../mol-util/download';
+
+export class ViewportScreenshotWrapper {
+    private getCanvasSize() {
+        return {
+            width: this.plugin.canvas3d.webgl.gl.drawingBufferWidth,
+            height: this.plugin.canvas3d.webgl.gl.drawingBufferHeight
+        };
+    }
+
+    size = this.getCanvasSize();
+
+    private _imagePass: ImagePass;
+
+    get imagePass() {
+        if (this._imagePass) return this._imagePass;
+
+        this._imagePass = this.plugin.canvas3d.getImagePass()
+        this._imagePass.setProps({
+            multiSample: { mode: 'on', sampleLevel: 2 },
+            postprocessing: this.plugin.canvas3d.props.postprocessing
+        });
+        return this._imagePass;
+    }
+
+    getFilename() {
+        const models = this.plugin.state.dataState.select(StateSelection.Generators.rootsOfType(PluginStateObject.Molecule.Model)).map(s => s.obj!.data)
+        const uniqueIds = new Set<string>()
+        models.forEach(m => uniqueIds.add(m.entryId.toUpperCase()))
+        const idString = Array.from(uniqueIds).join('-')
+        return `${idString || 'molstar-image'}.png`
+    }
+
+    private downloadTask() {
+        return Task.create('Download Image', async ctx => {
+            const { width, height } = this.size
+            if (width <= 0 || height <= 0) return
+
+            await ctx.update('Rendering image...')
+            const imageData = this.imagePass.getImageData(width, height)
+
+            await ctx.update('Encoding image...')
+            const canvas = document.createElement('canvas')
+            canvas.width = imageData.width
+            canvas.height = imageData.height
+            const canvasCtx = canvas.getContext('2d')
+            if (!canvasCtx) throw new Error('Could not create canvas 2d context')
+            canvasCtx.putImageData(imageData, 0, 0)
+
+            await ctx.update('Downloading image...')
+            const blob = await canvasToBlob(canvas, 'png')
+            download(blob, this.getFilename())
+        })
+    }
+
+    download() {
+        this.plugin.runTask(this.downloadTask());
+    }
+
+    constructor(private plugin: PluginContext) {
+
+    }
+}