Browse Source

added drag event to canvas3d interactivity

Alexander Rose 4 years ago
parent
commit
32869a9a45

+ 2 - 1
src/mol-canvas3d/canvas3d.ts

@@ -7,7 +7,7 @@
 
 import { BehaviorSubject, Subscription } from 'rxjs';
 import { now } from '../mol-util/now';
-import { Vec3 } from '../mol-math/linear-algebra';
+import { Vec3, Vec2 } from '../mol-math/linear-algebra';
 import InputObserver, { ModifiersKeys, ButtonsType } from '../mol-util/input/input-observer';
 import Renderer, { RendererStats, RendererParams } from '../mol-gl/renderer';
 import { GraphicsRenderObject } from '../mol-gl/render-object';
@@ -114,6 +114,7 @@ const requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnim
 
 namespace Canvas3D {
     export interface HoverEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
+    export interface DragEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, pageStart: Vec2, pageEnd: Vec2 }
     export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
 
     export function fromCanvas(canvas: HTMLCanvasElement, props: Partial<Canvas3DProps> = {}) {

+ 71 - 26
src/mol-canvas3d/helper/interaction-events.ts

@@ -1,36 +1,41 @@
 /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2020 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 { PickingId } from '../../mol-geo/geometry/picking';
-import { EmptyLoci } from '../../mol-model/loci';
 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';
 
 type Canvas3D = import('../canvas3d').Canvas3D
 type HoverEvent = import('../canvas3d').Canvas3D.HoverEvent
+type DragEvent = import('../canvas3d').Canvas3D.DragEvent
 type ClickEvent = import('../canvas3d').Canvas3D.ClickEvent
 
+const enum InputEvent { Move, Click, Drag }
+
 export class Canvas3dInteractionHelper {
     private ev = RxEventHelper.create();
 
     readonly events = {
         hover: this.ev<HoverEvent>(),
+        drag: this.ev<DragEvent>(),
         click: this.ev<ClickEvent>(),
     };
 
-    private cX = -1;
-    private cY = -1;
-
-    private lastX = -1;
-    private lastY = -1;
+    private startX = -1;
+    private startY = -1;
+    private endX = -1;
+    private endY = -1;
 
     private id: PickingId | undefined = void 0;
 
     private currentIdentifyT = 0;
+    private isInteracting = false;
 
     private prevLoci: Representation.Loci = Representation.Loci.Empty;
     private prevT = 0;
@@ -41,17 +46,34 @@ export class Canvas3dInteractionHelper {
     private button: ButtonsType.Flag = ButtonsType.create(0);
     private modifiers: ModifiersKeys = ModifiersKeys.None;
 
-    private identify(isClick: boolean, t: number) {
-        if (this.lastX !== this.cX || this.lastY !== this.cY) {
-            this.id = this.canvasIdentify(this.cX, this.cY);
-            this.lastX = this.cX;
-            this.lastY = this.cY;
+    private identify(e: InputEvent, t: number) {
+        const xyChanged = this.startX !== this.endX || this.startY !== this.endY;
+
+        if (e === InputEvent.Drag) {
+            if (xyChanged && !Representation.Loci.isEmpty(this.prevLoci)) {
+                this.events.drag.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers, pageStart: Vec2.create(this.startX, this.startY), pageEnd: Vec2.create(this.endX, this.endY) });
+
+                this.startX = this.endX;
+                this.startY = this.endY;
+            }
+            return;
+        }
+
+        if (xyChanged) {
+            this.id = this.canvasIdentify(this.endX, this.endY);
+            this.startX = this.endX;
+            this.startY = this.endY;
         }
 
-        if (!this.id) return;
+        if (!this.id) {
+            this.prevLoci = Representation.Loci.Empty;
+            return;
+        }
 
-        if (isClick) {
-            this.events.click.next({ current: this.getLoci(this.id), buttons: this.buttons, button: this.button, modifiers: this.modifiers });
+        if (e === InputEvent.Click) {
+            const loci = this.getLoci(this.id);
+            this.events.click.next({ current: loci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
+            this.prevLoci = loci;
             return;
         }
 
@@ -71,13 +93,13 @@ export class Canvas3dInteractionHelper {
         if (this.inside && t - this.prevT > 1000 / this.maxFps) {
             this.prevT = t;
             this.currentIdentifyT = t;
-            this.identify(false, t);
+            this.identify(this.isInteracting ? InputEvent.Drag : InputEvent.Move, t);
         }
     }
 
     leave() {
         this.inside = false;
-        if (this.prevLoci.loci !== EmptyLoci) {
+        if (Representation.Loci.isEmpty(this.prevLoci)) {
             this.prevLoci = Representation.Loci.Empty;
             this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
         }
@@ -88,21 +110,30 @@ export class Canvas3dInteractionHelper {
         this.buttons = buttons;
         this.button = button;
         this.modifiers = modifiers;
-        this.cX = x;
-        this.cY = y;
+        this.endX = x;
+        this.endY = y;
+    }
+
+    click(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) {
+        this.endX = x;
+        this.endY = y;
+        this.buttons = buttons;
+        this.button = button;
+        this.modifiers = modifiers;
+        this.identify(InputEvent.Click, 0);
     }
 
-    select(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) {
-        this.cX = x;
-        this.cY = y;
+    drag(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) {
+        this.endX = x;
+        this.endY = y;
         this.buttons = buttons;
         this.button = button;
         this.modifiers = modifiers;
-        this.identify(true, 0);
+        this.identify(InputEvent.Drag, 0);
     }
 
     modify(modifiers: ModifiersKeys) {
-        if (this.prevLoci.loci === EmptyLoci || ModifiersKeys.areEqual(modifiers, this.modifiers)) return;
+        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 });
     }
@@ -112,17 +143,31 @@ export class Canvas3dInteractionHelper {
     }
 
     constructor(private canvasIdentify: Canvas3D['identify'], private getLoci: Canvas3D['getLoci'], input: InputObserver, private maxFps: number = 30) {
+        input.drag.subscribe(({x, y, buttons, button, modifiers }) => {
+            this.isInteracting = true;
+            // console.log('drag');
+            this.drag(x, y, buttons, button, modifiers);
+        });
+
         input.move.subscribe(({x, y, inside, buttons, button, modifiers }) => {
-            if (!inside) return;
+            if (!inside || this.isInteracting) return;
+            // console.log('move');
             this.move(x, y, buttons, button, modifiers);
         });
 
         input.leave.subscribe(() => {
+            // console.log('leave');
             this.leave();
         });
 
         input.click.subscribe(({x, y, buttons, button, modifiers }) => {
-            this.select(x, y, buttons, button, modifiers);
+            // console.log('click');
+            this.click(x, y, buttons, button, modifiers);
+        });
+
+        input.interactionEnd.subscribe(() => {
+            // console.log('interactionEnd');
+            this.isInteracting = false;
         });
 
         input.modifiers.subscribe(modifiers => this.modify(modifiers));

+ 2 - 0
src/mol-plugin-state/manager/interactivity.ts

@@ -15,6 +15,7 @@ import { shallowEqual } from '../../mol-util/object';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { StatefulPluginComponent } from '../component';
 import { StructureSelectionManager } from './structure/selection';
+import { Vec2 } from '../../mol-math/linear-algebra';
 
 export { InteractivityManager };
 
@@ -70,6 +71,7 @@ namespace InteractivityManager {
     export type Props = PD.Values<Params>
 
     export interface HoverEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
+    export interface DragEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, pageStart: Vec2, pageEnd: Vec2 }
     export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
 
     export type LociMarkProvider = (loci: Representation.Loci, action: MarkerAction) => void

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

@@ -2,6 +2,7 @@
  * Copyright (c) 2020 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 * as React from 'react';
@@ -64,6 +65,7 @@ export class ViewportCanvas extends PluginUIComponent<ViewportCanvasParams, View
         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);
     }

+ 2 - 0
src/mol-plugin/context.ts

@@ -59,6 +59,7 @@ import { PluginAnimationManager } from '../mol-plugin-state/manager/animation';
 import { objectForEach } from '../mol-util/object';
 import { VolumeHierarchyManager } from '../mol-plugin-state/manager/volume/hierarchy';
 import { filter, take } from 'rxjs/operators';
+import { Vec2 } from '../mol-math/linear-algebra';
 
 export class PluginContext {
     runTask = <T>(task: Task<T>) => this.tasks.run(task);
@@ -98,6 +99,7 @@ export class PluginContext {
         interaction: {
             hover: this.ev.behavior<InteractivityManager.HoverEvent>({ current: Representation.Loci.Empty, modifiers: ModifiersKeys.None, buttons: 0, button: 0 }),
             click: this.ev.behavior<InteractivityManager.ClickEvent>({ current: Representation.Loci.Empty, modifiers: ModifiersKeys.None, buttons: 0, button: 0 }),
+            drag: this.ev.behavior<InteractivityManager.DragEvent>({ current: Representation.Loci.Empty, modifiers: ModifiersKeys.None, buttons: 0, button: 0, pageStart: Vec2(), pageEnd: Vec2() }),
             selectionMode: this.ev.behavior<boolean>(false)
         },
         labels: {

+ 4 - 0
src/mol-repr/representation.ts

@@ -160,6 +160,10 @@ namespace Representation {
             return a.repr === b.repr && ModelLoci.areEqual(a.loci, b.loci);
         }
 
+        export function isEmpty(a: Loci) {
+            return ModelLoci.isEmpty(a.loci);
+        }
+
         export const Empty: Loci = { loci: EmptyLoci };
     }