diff --git a/src/apps/viewer/index.html b/src/apps/viewer/index.html index 55cba2ba8c4c160185894d8df4fd71aaa294792f..9fe81219e16721be9b77f5bbba68b30dbf0bb317 100644 --- a/src/apps/viewer/index.html +++ b/src/apps/viewer/index.html @@ -49,11 +49,13 @@ if (debugMode) molstar.setDebugMode(debugMode, debugMode); var disableAntialiasing = getParam('disable-antialiasing', '[^&]+').trim() === '1'; + var pixelScale = parseFloat(getParam('pixel-scale', '[^&]+').trim() || '1'); var hideControls = getParam('hide-controls', '[^&]+').trim() === '1'; var pdbProvider = getParam('pdb-provider', '[^&]+').trim().toLowerCase(); var emdbProvider = getParam('emdb-provider', '[^&]+').trim().toLowerCase(); var viewer = new molstar.Viewer('app', { disableAntialiasing: disableAntialiasing, + pixelScale: pixelScale, layoutShowControls: !hideControls, viewportShowExpand: false, pdbProvider: pdbProvider || 'pdbe', diff --git a/src/mol-canvas3d/camera.ts b/src/mol-canvas3d/camera.ts index 0fadca2a5dd7fbadcb769aff4d2c04006beb5a65..01d54d6deacc14bff2f11bc204ada3e974d2ae06 100644 --- a/src/mol-canvas3d/camera.ts +++ b/src/mol-canvas3d/camera.ts @@ -218,10 +218,10 @@ namespace Camera { function updateOrtho(camera: Camera) { const { viewport, zoom, near, far, viewOffset } = camera; - const fullLeft = -(viewport.width - viewport.x) / 2; - const fullRight = (viewport.width - viewport.x) / 2; - const fullTop = (viewport.height - viewport.y) / 2; - const fullBottom = -(viewport.height - viewport.y) / 2; + const fullLeft = -viewport.width / 2; + const fullRight = viewport.width / 2; + const fullTop = viewport.height / 2; + const fullBottom = -viewport.height / 2; const dx = (fullRight - fullLeft) / (2 * zoom); const dy = (fullTop - fullBottom) / (2 * zoom); diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts index 0e6812abd2c955b408397de28f55f5ef05aac59f..2a76be15e99d5f0310709719842e3da676fe7560 100644 --- a/src/mol-canvas3d/canvas3d.ts +++ b/src/mol-canvas3d/canvas3d.ts @@ -212,7 +212,7 @@ namespace Canvas3D { const renderer = Renderer.create(webgl, p.renderer); const debugHelper = new BoundingSphereHelper(webgl, scene, p.debug); const handleHelper = new HandleHelper(webgl, p.handle); - const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input); + const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input, camera); const drawPass = new DrawPass(webgl, renderer, scene, camera, debugHelper, handleHelper, { cameraHelper: p.camera.helper diff --git a/src/mol-canvas3d/controls/trackball.ts b/src/mol-canvas3d/controls/trackball.ts index ebc56f695018556cb5f7b1841dbf31d20b6c2477..6cc53d244f48317ef6ca82c99135de830feb1661 100644 --- a/src/mol-canvas3d/controls/trackball.ts +++ b/src/mol-canvas3d/controls/trackball.ts @@ -137,7 +137,7 @@ namespace TrackballControls { const dy = _rotCurr[1] - _rotPrev[1]; Vec3.set(rotMoveDir, dx, dy, 0); - const angle = Vec3.magnitude(rotMoveDir) * p.rotateSpeed; + const angle = Vec3.magnitude(rotMoveDir) * p.rotateSpeed * input.pixelRatio; if (angle) { Vec3.sub(_eye, camera.position, camera.target); diff --git a/src/mol-canvas3d/helper/interaction-events.ts b/src/mol-canvas3d/helper/interaction-events.ts index 7e0c48a8f97b798a785b1cf2fa56e7ca2629f9d1..5cde1ea863b311a93c4e94f89c4b1dafa9e9e422 100644 --- a/src/mol-canvas3d/helper/interaction-events.ts +++ b/src/mol-canvas3d/helper/interaction-events.ts @@ -10,6 +10,7 @@ import { Representation } from '../../mol-repr/representation'; import InputObserver, { ModifiersKeys, ButtonsType } from '../../mol-util/input/input-observer'; import { RxEventHelper } from '../../mol-util/rx-event-helper'; import { Vec2 } from '../../mol-math/linear-algebra'; +import { Camera } from '../camera'; type Canvas3D = import('../canvas3d').Canvas3D type HoverEvent = import('../canvas3d').Canvas3D.HoverEvent @@ -92,7 +93,7 @@ export class Canvas3dInteractionHelper { } } - leave() { + private leave() { this.inside = false; if (Representation.Loci.isEmpty(this.prevLoci)) { this.prevLoci = Representation.Loci.Empty; @@ -100,7 +101,7 @@ export class Canvas3dInteractionHelper { } } - move(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) { + private move(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) { this.inside = true; this.buttons = buttons; this.button = button; @@ -109,7 +110,7 @@ export class Canvas3dInteractionHelper { this.endY = y; } - click(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) { + private click(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) { this.endX = x; this.endY = y; this.buttons = buttons; @@ -118,7 +119,7 @@ export class Canvas3dInteractionHelper { this.identify(InputEvent.Click, 0); } - drag(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) { + private drag(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) { this.endX = x; this.endY = y; this.buttons = buttons; @@ -127,17 +128,29 @@ export class Canvas3dInteractionHelper { this.identify(InputEvent.Drag, 0); } - modify(modifiers: ModifiersKeys) { + private modify(modifiers: ModifiersKeys) { if (Representation.Loci.isEmpty(this.prevLoci) || ModifiersKeys.areEqual(modifiers, this.modifiers)) return; this.modifiers = modifiers; this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers }); } + private outsideViewport(x: number, y: number) { + const { input, camera: { viewport } } = this; + x *= input.pixelRatio; + y *= input.pixelRatio; + return ( + x > viewport.x + viewport.width || + input.height - y > viewport.y + viewport.height || + x < viewport.x || + input.height - y < viewport.y + ); + } + dispose() { this.ev.dispose(); } - constructor(private canvasIdentify: Canvas3D['identify'], private getLoci: Canvas3D['getLoci'], input: InputObserver, private maxFps: number = 30) { + constructor(private canvasIdentify: Canvas3D['identify'], private getLoci: Canvas3D['getLoci'], private input: InputObserver, private camera: Camera, private maxFps: number = 30) { input.drag.subscribe(({x, y, buttons, button, modifiers }) => { this.isInteracting = true; // console.log('drag'); @@ -156,6 +169,7 @@ export class Canvas3dInteractionHelper { }); input.click.subscribe(({x, y, buttons, button, modifiers }) => { + if (this.outsideViewport(x, y)) return; // console.log('click'); this.click(x, y, buttons, button, modifiers); }); diff --git a/src/mol-canvas3d/passes/multi-sample.ts b/src/mol-canvas3d/passes/multi-sample.ts index 8a8dfdb40a6a65ede6e1278550b6562da0da8ec7..d75964b4174112145211a8d011bb91cf6f4cf0aa 100644 --- a/src/mol-canvas3d/passes/multi-sample.ts +++ b/src/mol-canvas3d/passes/multi-sample.ts @@ -161,6 +161,7 @@ export class MultiSamplePass { } this.setQuadShift(0, 0); gl.viewport(0, 0, width, height); + gl.scissor(0, 0, width, height); compose.render(); } @@ -175,6 +176,7 @@ export class MultiSamplePass { } this.setQuadShift(x / width, y / height); gl.viewport(x, y, width, height); + gl.scissor(x, y, width, height); state.disable(gl.BLEND); compose.render(); @@ -216,6 +218,7 @@ export class MultiSamplePass { state.depthMask(false); this.setQuadShift(0, 0); gl.viewport(0, 0, width, height); + gl.scissor(0, 0, width, height); compose.render(); this.sampleIndex += 1; } else { @@ -251,6 +254,7 @@ export class MultiSamplePass { } this.setQuadShift(0, 0); gl.viewport(0, 0, width, height); + gl.scissor(0, 0, width, height); compose.render(); this.sampleIndex += 1; @@ -262,10 +266,12 @@ export class MultiSamplePass { webgl.unbindFramebuffer(); this.setQuadShift(x / width, y / height); gl.viewport(x, y, width, height); + gl.scissor(x, y, width, height); } else { this.colorTarget.bind(); this.setQuadShift(0, 0); gl.viewport(0, 0, width, height); + gl.scissor(0, 0, width, height); } const accumulationWeight = this.sampleIndex * sampleWeight; diff --git a/src/mol-canvas3d/passes/pick.ts b/src/mol-canvas3d/passes/pick.ts index caf6c1b9d767c2fc22c86a0f57fb8a87b322fcfe..ea33d8a17ad8abbc1d48af0377687099695647ef 100644 --- a/src/mol-canvas3d/passes/pick.ts +++ b/src/mol-canvas3d/passes/pick.ts @@ -130,8 +130,8 @@ export class PickPass { this.syncBuffers(); } - x -= viewport.x * pixelRatio; - y += viewport.y * pixelRatio; // plus because of flipped y + x -= viewport.x; + y += viewport.y; // plus because of flipped y y = gl.drawingBufferHeight - y; // flip y const xp = Math.floor(x * pickScale); diff --git a/src/mol-canvas3d/passes/postprocessing.ts b/src/mol-canvas3d/passes/postprocessing.ts index a14fca29d853856157b05c26e71e08bbe7e55aff..5f1279e42905017d8848fe4c7093aee05f899a3b 100644 --- a/src/mol-canvas3d/passes/postprocessing.ts +++ b/src/mol-canvas3d/passes/postprocessing.ts @@ -172,10 +172,12 @@ export class PostprocessingPass { this.webgl.unbindFramebuffer(); this.setQuadShift(x / width, y / height); gl.viewport(x, y, width, height); + gl.scissor(x, y, width, height); } else { this.target.bind(); this.setQuadShift(0, 0); gl.viewport(0, 0, width, height); + gl.scissor(0, 0, width, height); } state.disable(gl.SCISSOR_TEST); state.disable(gl.BLEND); diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index 8c4ba27b91810f74e7fab5daf0b6e21f62d63f18..f0a66e82af8c8ee396a0c70771702640a1cd11d1 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -377,6 +377,7 @@ namespace Renderer { return { clear: (transparentBackground: boolean) => { + ctx.unbindFramebuffer(); state.enable(gl.SCISSOR_TEST); state.depthMask(true); state.colorMask(true, true, true, true); diff --git a/src/mol-plugin-ui/viewport/canvas.tsx b/src/mol-plugin-ui/viewport/canvas.tsx index cbe501ae3d5184de2d93f4fbf97f25d38e4b7d61..e10489d139683b2e12b2a7769572e7a06f4e45ab 100644 --- a/src/mol-plugin-ui/viewport/canvas.tsx +++ b/src/mol-plugin-ui/viewport/canvas.tsx @@ -11,6 +11,7 @@ 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 @@ -46,6 +47,9 @@ export class ViewportCanvas extends PluginUIComponent<ViewportCanvasParams, View 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(); } } diff --git a/src/mol-util/input/input-observer.ts b/src/mol-util/input/input-observer.ts index c81e787ebb7683944876ea59dc4ef853c1b0185d..f2b4d6f5a75e6c155c63ecb29da13ea4a201e1ef 100644 --- a/src/mol-util/input/input-observer.ts +++ b/src/mol-util/input/input-observer.ts @@ -525,9 +525,10 @@ namespace InputObserver { } function onPointerDown(ev: PointerEvent) { + if (!mask(ev.clientX, ev.clientY)) return; + eventOffset(pointerStart, ev); Vec2.copy(pointerDown, pointerStart); - if (!mask(ev.clientX, ev.clientY)) return; if (insideBounds(pointerStart)) { dragging = DraggingState.Started; @@ -536,6 +537,7 @@ namespace InputObserver { function onPointerUp(ev: PointerEvent) { dragging = DraggingState.Stopped; + if (!mask(ev.clientX, ev.clientY)) return; eventOffset(pointerEnd, ev); if (Vec2.distance(pointerEnd, pointerDown) < 4) { @@ -573,9 +575,10 @@ namespace InputObserver { } function onMouseWheel(ev: WheelEvent) { + if (!mask(ev.clientX, ev.clientY)) return; + eventOffset(pointerEnd, ev); const [ x, y ] = pointerEnd; - if (!mask(ev.clientX, ev.clientY)) return; if (noScroll) { ev.preventDefault();