Bläddra i källkod

added selection support to canvas app

Alexander Rose 6 år sedan
förälder
incheckning
78c76a9214

+ 26 - 11
src/apps/canvas/component/viewport.tsx

@@ -9,6 +9,8 @@ import { App } from '../app';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { EmptyLoci, Loci, areLociEqual } from 'mol-model/loci';
 import { labelFirst } from 'mol-theme/label';
+import { ButtonsType } from 'mol-util/input/input-observer';
+import { throttleTime } from 'rxjs/operators'
 
 interface ViewportProps {
     app: App
@@ -40,21 +42,25 @@ export class Viewport extends React.Component<ViewportProps, ViewportState> {
         }
         this.handleResize()
 
-        const viewer = this.props.app.canvas3d
+        const canvas3d = this.props.app.canvas3d
 
-        viewer.input.resize.subscribe(() => this.handleResize())
+        canvas3d.input.resize.subscribe(() => this.handleResize())
 
-        let prevLoci: Loci = EmptyLoci
-        viewer.input.move.subscribe(async ({x, y, inside, buttons}) => {
-            if (!inside || buttons) return
-            const p = await viewer.identify(x, y)
+        let prevHighlightLoci: Loci = EmptyLoci
+        // TODO can the 'only ever have one extra element in the queue' functionality be done with rxjs?
+        let highlightQueueLength = 0
+        canvas3d.input.move.pipe(throttleTime(50)).subscribe(async ({x, y, inside, buttons}) => {
+            if (!inside || buttons || highlightQueueLength > 2) return
+            ++highlightQueueLength
+            const p = await canvas3d.identify(x, y)
+            --highlightQueueLength
             if (p) {
-                const loci = viewer.getLoci(p)
+                const loci = canvas3d.getLoci(p)
 
-                if (!areLociEqual(loci, prevLoci)) {
-                    viewer.mark(prevLoci, MarkerAction.RemoveHighlight)
-                    viewer.mark(loci, MarkerAction.Highlight)
-                    prevLoci = loci
+                if (!areLociEqual(loci, prevHighlightLoci)) {
+                    canvas3d.mark(prevHighlightLoci, MarkerAction.RemoveHighlight)
+                    canvas3d.mark(loci, MarkerAction.Highlight)
+                    prevHighlightLoci = loci
 
                     const label = labelFirst(loci)
                     const pickingInfo = `${label}`
@@ -63,6 +69,15 @@ export class Viewport extends React.Component<ViewportProps, ViewportState> {
             }
         })
 
+        canvas3d.input.click.subscribe(async ({x, y, buttons}) => {
+            if (buttons !== ButtonsType.Flag.Primary) return
+            const p = await canvas3d.identify(x, y)
+            if (p) {
+                const loci = canvas3d.getLoci(p)
+                canvas3d.mark(loci, MarkerAction.Toggle)
+            }
+        })
+
         this.props.app.taskCountChanged.subscribe(({ count, info }) => {
             this.setState({ taskInfo: count > 0 ? info : '' })
         })

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

@@ -213,6 +213,7 @@ namespace Canvas3D {
             render('pickObject', pickDirty)
             render('pickInstance', pickDirty)
             render('pickGroup', pickDirty)
+            ctx.gl.finish()
 
             pickDirty = false
         }
@@ -244,7 +245,12 @@ namespace Canvas3D {
 
             isPicking = false
 
-            return { objectId, instanceId, groupId }
+            // TODO
+            if (objectId === -1 || instanceId === -1 || groupId === -1) {
+                return { objectId: -1, instanceId: -1, groupId: -1 }
+            } else {
+                return { objectId, instanceId, groupId }
+            }
         }
 
         handleResize()

+ 7 - 7
src/mol-canvas3d/controls/trackball.ts

@@ -11,7 +11,7 @@
 
 import { Quat, Vec2, Vec3, EPSILON } from 'mol-math/linear-algebra';
 import { cameraLookAt, Viewport } from '../camera/util';
-import InputObserver, { DragInput, WheelInput, ButtonsFlag, PinchInput } from 'mol-util/input/input-observer';
+import InputObserver, { DragInput, WheelInput, ButtonsType, PinchInput } from 'mol-util/input/input-observer';
 import { Object3D } from 'mol-gl/object3d';
 
 export const DefaultTrackballControlsProps = {
@@ -243,24 +243,24 @@ namespace TrackballControls {
 
         function onDrag({ pageX, pageY, buttons, modifiers, isStart }: DragInput) {
             if (isStart) {
-                if (buttons === ButtonsFlag.Primary) {
+                if (buttons === ButtonsType.Flag.Primary) {
                     Vec2.copy(_moveCurr, getMouseOnCircle(pageX, pageY))
                     Vec2.copy(_movePrev, _moveCurr)
-                } else if (buttons === ButtonsFlag.Auxilary) {
+                } else if (buttons === ButtonsType.Flag.Auxilary) {
                     Vec2.copy(_zoomStart, getMouseOnScreen(pageX, pageY))
                     Vec2.copy(_zoomEnd, _zoomStart)
-                } else if (buttons === ButtonsFlag.Secondary) {
+                } else if (buttons === ButtonsType.Flag.Secondary) {
                     Vec2.copy(_panStart, getMouseOnScreen(pageX, pageY))
                     Vec2.copy(_panEnd, _panStart)
                 }
             }
 
-            if (buttons === ButtonsFlag.Primary) {
+            if (buttons === ButtonsType.Flag.Primary) {
                 Vec2.copy(_movePrev, _moveCurr)
                 Vec2.copy(_moveCurr, getMouseOnCircle(pageX, pageY))
-            } else if (buttons === ButtonsFlag.Auxilary) {
+            } else if (buttons === ButtonsType.Flag.Auxilary) {
                 Vec2.copy(_zoomEnd, getMouseOnScreen(pageX, pageY))
-            } else if (buttons === ButtonsFlag.Secondary) {
+            } else if (buttons === ButtonsType.Flag.Secondary) {
                 Vec2.copy(_panEnd, getMouseOnScreen(pageX, pageY))
             }
         }

+ 8 - 0
src/mol-geo/geometry/marker-data.ts

@@ -18,6 +18,7 @@ export enum MarkerAction {
     RemoveHighlight,
     Select,
     Deselect,
+    Toggle,
     Clear
 }
 
@@ -44,6 +45,13 @@ export function applyMarkerAction(array: Uint8Array, start: number, end: number,
                     v -= 2
                 }
                 break
+            case MarkerAction.Toggle:
+                if (v >= 2) {
+                    v -= 2
+                } else {
+                    v += 2
+                }
+                break
             case MarkerAction.Clear:
                 v = 0
                 break

+ 3 - 0
src/mol-geo/representation/structure/visual/util/polymer.ts

@@ -68,6 +68,9 @@ export function getPolymerElementLoci(pickingId: PickingId, group: Unit.Symmetry
     const { objectId, instanceId, groupId } = pickingId
     if (id === objectId) {
         const unit = group.units[instanceId]
+        if (unit === undefined) {
+            console.log(id, { objectId, instanceId, groupId }, group.units)
+        }
         const unitIndex = OrderedSet.indexOf(unit.elements, unit.polymerElements[groupId]) as StructureElement.UnitIndex
         if (unitIndex !== -1) {
             const indices = OrderedSet.ofSingleton(unitIndex)

+ 29 - 22
src/mol-util/input/input-observer.ts

@@ -9,6 +9,7 @@ import { Subject } from 'rxjs';
 import { Vec2 } from 'mol-math/linear-algebra';
 
 import toPixels from '../to-pixels'
+import { BitFlags } from 'mol-util';
 
 function getButtons(event: MouseEvent | Touch) {
     if (typeof event === 'object') {
@@ -21,7 +22,7 @@ function getButtons(event: MouseEvent | Touch) {
             } else if (b === 3) {
                 return 2
             } else if (b > 0) {
-                return 1<<(b-1)
+                return 1 << (b - 1)
             }
         } else if ('button' in event) {
             const b = (event as any).button  // 'any' to support older browsers
@@ -30,7 +31,7 @@ function getButtons(event: MouseEvent | Touch) {
             } else if (b === 2) {
                 return 2
             } else if (b >= 0) {
-                return 1<<b
+                return 1 << b
             }
         }
     }
@@ -50,19 +51,26 @@ export type ModifiersKeys = {
     meta: boolean
 }
 
-export const enum ButtonsFlag {
-    /** No button or un-initialized */
-    None = 0x0,
-    /** Primary button (usually left) */
-    Primary = 0x1,
-    /** Secondary button (usually right) */
-    Secondary = 0x2,
-    /** Auxilary button (usually middle or mouse wheel button)  */
-    Auxilary = 0x4,
-    /** 4th button (typically the "Browser Back" button) */
-    Forth = 0x8,
-    /** 5th button (typically the "Browser Forward" button) */
-    Five = 0x10,
+export interface ButtonsType extends BitFlags<ButtonsType.Flag> { }
+
+export namespace ButtonsType {
+    export const has: (ss: ButtonsType, f: Flag) => boolean = BitFlags.has
+    export const create: (fs: Flag) => ButtonsType = BitFlags.create
+
+    export const enum Flag {
+        /** No button or un-initialized */
+        None = 0x0,
+        /** Primary button (usually left) */
+        Primary = 0x1,
+        /** Secondary button (usually right) */
+        Secondary = 0x2,
+        /** Auxilary button (usually middle or mouse wheel button)  */
+        Auxilary = 0x4,
+        /** 4th button (typically the "Browser Back" button) */
+        Forth = 0x8,
+        /** 5th button (typically the "Browser Forward" button) */
+        Five = 0x10,
+    }
 }
 
 type BaseInput = {
@@ -190,11 +198,11 @@ namespace InputObserver {
             element.addEventListener( 'contextmenu', onContextMenu, false )
 
             element.addEventListener('wheel', onMouseWheel as any, false)
-            element.addEventListener('mousedown', onPointerDown as any, false)
+            element.addEventListener('mousedown', onMouseDown as any, false)
             // for dragging to work outside canvas bounds,
             // mouse move/up events have to be added to a parent, i.e. window
             window.addEventListener('mousemove', onMouseMove as any, false)
-            window.addEventListener('mouseup', onPointerUp as any, false)
+            window.addEventListener('mouseup', onMouseUp as any, false)
 
             element.addEventListener('touchstart', onTouchStart as any, false)
             element.addEventListener('touchmove', onTouchMove as any, false)
@@ -270,10 +278,10 @@ namespace InputObserver {
 
         function onTouchStart (ev: TouchEvent) {
             if (ev.touches.length === 1) {
-                buttons = ButtonsFlag.Primary
+                buttons = ButtonsType.Flag.Primary
                 onPointerDown(ev.touches[0])
             } else if (ev.touches.length >= 2) {
-                buttons = ButtonsFlag.Secondary
+                buttons = ButtonsType.Flag.Secondary
                 onPointerDown(getCenterTouch(ev))
 
                 pinch.next({ distance: lastTouchDistance, delta: 0, isStart: true })
@@ -284,12 +292,12 @@ namespace InputObserver {
 
         function onTouchMove (ev: TouchEvent) {
             if (ev.touches.length === 1) {
-                buttons = ButtonsFlag.Primary
+                buttons = ButtonsType.Flag.Primary
                 onPointerMove(ev.touches[0])
             } else if (ev.touches.length >= 2) {
                 const touchDistance = getTouchDistance(ev)
                 if (lastTouchDistance - touchDistance < 4) {
-                    buttons = ButtonsFlag.Secondary
+                    buttons = ButtonsType.Flag.Secondary
                     onPointerMove(getCenterTouch(ev))
                 } else {
                     pinch.next({
@@ -313,7 +321,6 @@ namespace InputObserver {
         }
 
         function onMouseUp (ev: MouseEvent) {
-            buttons = getButtons(ev)
             onPointerUp(ev)
         }