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();