diff --git a/src/mol-gl/camera/util.ts b/src/mol-gl/camera/util.ts index 4f23d6ee60ebbd277cba8f55fb47c781736cc649..a657c6fefa43f255835d8e8d37d9cad14ea42db6 100644 --- a/src/mol-gl/camera/util.ts +++ b/src/mol-gl/camera/util.ts @@ -20,6 +20,10 @@ export namespace Viewport { export function clone(viewport: Viewport): Viewport { return { ...viewport } } + + export function copy(target: Viewport, source: Viewport): Viewport { + return Object.assign(target, source) + } } const tmpVec3 = Vec3.zero() diff --git a/src/mol-gl/controls/orbit.ts b/src/mol-gl/controls/orbit.ts deleted file mode 100644 index aef8fed0c3c8c33feedaa0e55a2dc3add1784408..0000000000000000000000000000000000000000 --- a/src/mol-gl/controls/orbit.ts +++ /dev/null @@ -1,237 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import { map, filter, scan } from 'rxjs/operators'; - -import { Quat, Vec2, Vec3, EPSILON } from 'mol-math/linear-algebra'; -import { clamp } from 'mol-math/interpolate'; -import InputObserver from 'mol-util/input/input-observer'; - -const Y_UP = Vec3.create(0, 1, 0) -const tmpVec3 = Vec3.zero() - -function cameraLookAt (direction: Vec3, up: Vec3, position: Vec3, target: Vec3) { - Vec3.copy(direction, target) - Vec3.sub(direction, direction, position) - Vec3.normalize(direction, direction) -} - -export const DefaultOrbitControlsProps = { - parent: window as Window | Element, - noScroll: true, - - phi: Math.PI / 2, - theta: 0, - - position: Vec3.zero(), - up: Vec3.create(0, 1, 0), - target: Vec3.zero(), - - distance: undefined as (number|undefined), - damping: 0.25, - rotateSpeed: 0.28, - zoomSpeed: 0.0075, - pinchSpeed: 0.0075, - translateSpeed: 1.0, -} -export type OrbitControlsProps = Partial<typeof DefaultOrbitControlsProps> - -interface OrbitControls { - update: () => void - copyInto: (positionOut: Vec3, directionOut: Vec3, upOut: Vec3) => void - - position: Vec3 - direction: Vec3 - up: Vec3 - target: Vec3 - - distance: number - damping: number - rotateSpeed: number - zoomSpeed: number - pinchSpeed: number - translateSpeed: number - - phi: number - theta: number -} - -namespace OrbitControls { - export function create (element: Element, props: OrbitControlsProps = {}): OrbitControls { - const p = { ...DefaultOrbitControlsProps, ...props } - - const inputDelta = Vec3.zero() // x, y, zoom - const offset = Vec3.zero() - - const upQuat = Quat.identity() - const upQuatInverse = Quat.identity() - const translateVec3 = Vec3.zero() - - const position = Vec3.clone(p.position) - const direction = Vec3.zero() - const up = Vec3.clone(p.up) - const target = Vec3.clone(p.target) - - // const phiBounds = Vec2.create(0, Math.PI) - const phiBounds = Vec2.create(-Infinity, Infinity) - const thetaBounds = Vec2.create(-Infinity, Infinity) - const distanceBounds = Vec2.create(0, Infinity) - - let { damping, rotateSpeed, zoomSpeed, pinchSpeed, translateSpeed, phi, theta } = p - let distance = 0 - - // Compute distance if not defined in user options - if (p.distance === undefined) { - Vec3.sub(tmpVec3, position, target) - distance = Vec3.magnitude(tmpVec3) - } - - const input = InputObserver.create(element, { - parent: p.parent, - noScroll: p.noScroll - }) - input.drag.pipe(filter(v => v.buttons === 1)).subscribe(inputRotate) - input.drag.pipe(filter(v => v.buttons === 4)).subscribe(inputTranslate) - input.wheel.subscribe(inputZoom) - input.pinch.subscribe(inputPinch) - - // Apply an initial phi and theta - applyPhiTheta() - - return { - update, - copyInto, - - position, - direction, - up, - target, - - get distance() { return distance }, - set distance(value: number ) { distance = value }, - get damping() { return damping }, - set damping(value: number ) { damping = value }, - get rotateSpeed() { return rotateSpeed }, - set rotateSpeed(value: number ) { rotateSpeed = value }, - get zoomSpeed() { return zoomSpeed }, - set zoomSpeed(value: number ) { zoomSpeed = value }, - get pinchSpeed() { return pinchSpeed }, - set pinchSpeed(value: number ) { pinchSpeed = value }, - get translateSpeed() { return translateSpeed }, - set translateSpeed(value: number ) { translateSpeed = value }, - - get phi() { return phi }, - set phi(value: number ) { phi = value; applyPhiTheta() }, - get theta() { return theta }, - set theta(value: number ) { theta = value; applyPhiTheta() }, - } - - function copyInto(positionOut: Vec3, directionOut: Vec3, upOut: Vec3) { - Vec3.copy(positionOut, position) - Vec3.copy(directionOut, direction) - Vec3.copy(upOut, up) - } - - function inputRotate ({ dx, dy }: { dx: number, dy: number }) { - const PI2 = Math.PI * 2 - inputDelta[0] -= PI2 * dx * rotateSpeed - inputDelta[1] -= PI2 * dy * rotateSpeed - } - - function inputZoom ({ dy }: { dy: number }) { - inputDelta[2] += dy * zoomSpeed - } - - function inputPinch (delta: number) { - inputDelta[2] -= delta * pinchSpeed - } - - function inputTranslate ({ dx, dy }: { dx: number, dy: number }) { - // TODO - console.log('translate', { dx, dy }) - const x = dx * translateSpeed * distance - const y = dy * translateSpeed * distance - // Vec3.set(translateVec3, x, y, 0) - // Vec3.transformQuat(translateVec3, translateVec3, upQuat) - - // pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x ); - // pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) ); - - Vec3.copy(translateVec3, position) - Vec3.cross(translateVec3, translateVec3, up) - Vec3.normalize(translateVec3, translateVec3) - Vec3.scale(translateVec3, translateVec3, x ) - - const up2 = Vec3.clone(up) - Vec3.normalize(up2, up2) - Vec3.scale(up2, up2, y ) - Vec3.add(translateVec3, translateVec3, up2) - - Vec3.add(target, target, translateVec3) - Vec3.add(position, position, translateVec3) - } - - function updateDirection () { - Quat.fromUnitVec3(upQuat, up, Y_UP) - Quat.invert(upQuatInverse, upQuat) - - Vec3.sub(offset, position, target) - Vec3.transformQuat(offset, offset, upQuat) - - let _distance = distance - let _theta = Math.atan2(offset[0], offset[2]) - let _phi = Math.atan2(Math.sqrt(offset[0] * offset[0] + offset[2] * offset[2]), offset[1]) - - _theta += inputDelta[0] - _phi += inputDelta[1] - - _theta = clamp(_theta, thetaBounds[0], thetaBounds[1]) - _phi = clamp(_phi, phiBounds[0], phiBounds[1]) - _phi = clamp(_phi, EPSILON.Value, Math.PI - EPSILON.Value) - - _distance += inputDelta[2] - _distance = clamp(_distance, distanceBounds[0], distanceBounds[1]) - - const radius = Math.abs(_distance) <= EPSILON.Value ? EPSILON.Value : _distance - offset[0] = radius * Math.sin(_phi) * Math.sin(_theta) - offset[1] = radius * Math.cos(_phi) - offset[2] = radius * Math.sin(_phi) * Math.cos(_theta) - - phi = _phi - theta = _theta - distance = _distance - - Vec3.transformQuat(offset, offset, upQuatInverse) - Vec3.add(position, target, offset) - cameraLookAt(direction, up, position, target) - } - - function update () { - updateDirection() - for (let i = 0; i < inputDelta.length; i++) { - inputDelta[i] *= 1 - damping - } - } - - function applyPhiTheta () { - let _phi = phi - let _theta = theta - _theta = clamp(_theta, thetaBounds[0], thetaBounds[1]) - _phi = clamp(_phi, phiBounds[0], phiBounds[1]) - _phi = clamp(_phi, EPSILON.Value, Math.PI - EPSILON.Value) - - const dist = Math.max(EPSILON.Value, distance) - position[0] = dist * Math.sin(_phi) * Math.sin(_theta) - position[1] = dist * Math.cos(_phi) - position[2] = dist * Math.sin(_phi) * Math.cos(_theta) - Vec3.add(position, position, target) - - updateDirection() - } - } -} - -export default OrbitControls \ No newline at end of file diff --git a/src/mol-gl/controls/trackball.ts b/src/mol-gl/controls/trackball.ts index 0fd4a8d3e4fb7aec1f151d23892de6cbd54bfde6..6a61703bce30717fc3d77ec860b167dec20ccffb 100644 --- a/src/mol-gl/controls/trackball.ts +++ b/src/mol-gl/controls/trackball.ts @@ -9,16 +9,15 @@ * copyright (c) 2010-2018 three.js authors. MIT License */ -import { Subject } from 'rxjs'; - import { Quat, Vec2, Vec3, EPSILON } from 'mol-math/linear-algebra'; -import { clamp } from 'mol-math/interpolate'; -import InputObserver from 'mol-util/input/input-observer'; -import { cameraLookAt } from '../camera/util'; +import { cameraLookAt, Viewport } from '../camera/util'; +import InputObserver, { DragInput, WheelInput, MouseButtonsFlag, PinchInput } from 'mol-util/input/input-observer'; export const DefaultTrackballControlsProps = { + noScroll: true, + rotateSpeed: 3.0, - zoomSpeed: 2.2, + zoomSpeed: 2.0, panSpeed: 0.1, staticMoving: false, @@ -29,32 +28,14 @@ export const DefaultTrackballControlsProps = { } export type TrackballControlsProps = Partial<typeof DefaultTrackballControlsProps> -const enum STATE { - NONE = - 1, - ROTATE = 0, - ZOOM = 1, - PAN = 2, - TOUCH_ROTATE = 3, - TOUCH_ZOOM_PAN = 4 -} - interface Object { position: Vec3, direction: Vec3, up: Vec3, } -interface Screen { - left: number - top: number - width: number - height: number -} - interface TrackballControls { - change: Subject<void> - start: Subject<void> - end: Subject<void> + viewport: Viewport dynamicDampingFactor: number rotateSpeed: number @@ -62,7 +43,6 @@ interface TrackballControls { panSpeed: number update: () => void - handleResize: () => void reset: () => void dispose: () => void } @@ -71,23 +51,23 @@ namespace TrackballControls { export function create (element: Element, object: Object, props: TrackballControlsProps = {}): TrackballControls { const p = { ...DefaultTrackballControlsProps, ...props } - const screen: Screen = { left: 0, top: 0, width: 0, height: 0 } + const viewport: Viewport = { x: 0, y: 0, width: 0, height: 0 } let { rotateSpeed, zoomSpeed, panSpeed } = p let { staticMoving, dynamicDampingFactor } = p let { minDistance, maxDistance } = p - const change = new Subject<void>() - const start = new Subject<void>() - const end = new Subject<void>() + let disposed = false + + const input = InputObserver.create(element) + input.drag.subscribe(onDrag) + input.wheel.subscribe(onWheel) + input.pinch.subscribe(onPinch) // internals const target = Vec3.zero() const lastPosition = Vec3.zero() - let _state = STATE.NONE - let _prevState = STATE.NONE - const _eye = Vec3.zero() const _movePrev = Vec2.zero() @@ -99,8 +79,8 @@ namespace TrackballControls { const _zoomStart = Vec2.zero() const _zoomEnd = Vec2.zero() - let _touchZoomDistanceStart = 0 - let _touchZoomDistanceEnd = 0 + // let _touchZoomDistanceStart = 0 + // let _touchZoomDistanceEnd = 0 const _panStart = Vec2.zero() const _panEnd = Vec2.zero() @@ -110,30 +90,12 @@ namespace TrackballControls { const position0 = Vec3.clone(object.position) const up0 = Vec3.clone(object.up) - // methods - function handleResize () { - if ( element instanceof Document ) { - screen.left = 0; - screen.top = 0; - screen.width = window.innerWidth; - screen.height = window.innerHeight; - } else { - const box = element.getBoundingClientRect(); - // adjustments come from similar code in the jquery offset() function - const d = element.ownerDocument.documentElement; - screen.left = box.left + window.pageXOffset - d.clientLeft; - screen.top = box.top + window.pageYOffset - d.clientTop; - screen.width = box.width; - screen.height = box.height; - } - } - const mouseOnScreenVec2 = Vec2.zero() function getMouseOnScreen(pageX: number, pageY: number) { Vec2.set( mouseOnScreenVec2, - (pageX - screen.left) / screen.width, - (pageY - screen.top) / screen.height + (pageX - viewport.x) / viewport.width, + (pageY - viewport.y) / viewport.height ); return mouseOnScreenVec2; } @@ -142,8 +104,8 @@ namespace TrackballControls { function getMouseOnCircle(pageX: number, pageY: number) { Vec2.set( mouseOnCircleVec2, - ((pageX - screen.width * 0.5 - screen.left) / (screen.width * 0.5)), - ((screen.height + 2 * (screen.top - pageY)) / screen.width) // screen.width intentional + ((pageX - viewport.width * 0.5 - viewport.x) / (viewport.width * 0.5)), + ((viewport.height + 2 * (viewport.y - pageY)) / viewport.width) // screen.width intentional ); return mouseOnCircleVec2; } @@ -196,21 +158,32 @@ namespace TrackballControls { function zoomCamera () { - if (_state === STATE.TOUCH_ZOOM_PAN) { - const factor = _touchZoomDistanceStart / _touchZoomDistanceEnd - _touchZoomDistanceStart = _touchZoomDistanceEnd; + // if (_state === STATE.TOUCH_ZOOM_PAN) { + // const factor = _touchZoomDistanceStart / _touchZoomDistanceEnd + // _touchZoomDistanceStart = _touchZoomDistanceEnd; + // Vec3.scale(_eye, _eye, factor) + // } else { + // const factor = 1.0 + ( _zoomEnd[1] - _zoomStart[1] ) * zoomSpeed + // if (factor !== 1.0 && factor > 0.0) { + // Vec3.scale(_eye, _eye, factor) + // } + + // if (staticMoving) { + // Vec2.copy(_zoomStart, _zoomEnd) + // } else { + // _zoomStart[1] += ( _zoomEnd[1] - _zoomStart[1] ) * dynamicDampingFactor + // } + // } + + const factor = 1.0 + ( _zoomEnd[1] - _zoomStart[1] ) * zoomSpeed + if (factor !== 1.0 && factor > 0.0) { Vec3.scale(_eye, _eye, factor) - } else { - const factor = 1.0 + ( _zoomEnd[1] - _zoomStart[1] ) * zoomSpeed - if (factor !== 1.0 && factor > 0.0) { - Vec3.scale(_eye, _eye, factor) - } + } - if (staticMoving) { - Vec2.copy(_zoomStart, _zoomEnd) - } else { - _zoomStart[1] += ( _zoomEnd[1] - _zoomStart[1] ) * dynamicDampingFactor - } + if (staticMoving) { + Vec2.copy(_zoomStart, _zoomEnd) + } else { + _zoomStart[1] += ( _zoomEnd[1] - _zoomStart[1] ) * dynamicDampingFactor } } @@ -271,197 +244,130 @@ namespace TrackballControls { cameraLookAt(object.position, object.up, object.direction, target) if (Vec3.squaredDistance(lastPosition, object.position) > EPSILON.Value) { - change.next() Vec3.copy(lastPosition, object.position) } } function reset() { - _state = STATE.NONE; - _prevState = STATE.NONE; - Vec3.copy(target, target0) Vec3.copy(object.position, position0) Vec3.copy(object.up, up0) Vec3.sub(_eye, object.position, target) - cameraLookAt(object.position, object.up, object.direction, target) - - change.next() Vec3.copy(lastPosition, object.position) } // listeners - function mousedown(event: MouseEvent) { - event.preventDefault(); - event.stopPropagation(); - - if (_state === STATE.NONE) { - _state = event.button; - } - - if (_state === STATE.ROTATE) { - Vec2.copy(_moveCurr, getMouseOnCircle(event.pageX, event.pageY)) - Vec2.copy(_movePrev, _moveCurr) - } else if (_state === STATE.ZOOM) { - Vec2.copy(_zoomStart, getMouseOnScreen(event.pageX, event.pageY)) - Vec2.copy(_zoomEnd, _zoomStart) - } else if (_state === STATE.PAN) { - Vec2.copy(_panStart, getMouseOnScreen(event.pageX, event.pageY)) - Vec2.copy(_panEnd, _panStart) - } - - document.addEventListener('mousemove', mousemove, false); - document.addEventListener('mouseup', mouseup, false); - - start.next() - } - - function mousemove(event: MouseEvent) { - event.preventDefault(); - event.stopPropagation(); - - if (_state === STATE.ROTATE) { - Vec2.copy(_movePrev, _moveCurr) - Vec2.copy(_moveCurr, getMouseOnCircle(event.pageX, event.pageY)) - } else if (_state === STATE.ZOOM) { - Vec2.copy(_zoomEnd, getMouseOnScreen(event.pageX, event.pageY)) - } else if (_state === STATE.PAN) { - Vec2.copy(_panEnd, getMouseOnScreen(event.pageX, event.pageY)) - } - } - - function mouseup(event: MouseEvent) { - event.preventDefault(); - event.stopPropagation(); - - _state = STATE.NONE; - - document.removeEventListener( 'mousemove', mousemove ); - document.removeEventListener( 'mouseup', mouseup ); - - end.next() - } - - function mousewheel( event: MouseWheelEvent ) { - event.preventDefault(); - event.stopPropagation(); - - switch ( event.deltaMode ) { - case 2: - // Zoom in pages - _zoomStart[1] -= event.deltaY * 0.025; - break; - case 1: - // Zoom in lines - _zoomStart[1] -= event.deltaY * 0.01; - break; - default: - // undefined, 0, assume pixels - _zoomStart[1] -= event.deltaY * 0.00025; - break; - } - - start.next() - end.next() - } - - function touchstart(event: TouchEvent ) { - switch ( event.touches.length ) { - case 1: - _state = STATE.TOUCH_ROTATE; - Vec2.copy(_moveCurr, getMouseOnCircle(event.touches[0].pageX, event.touches[0].pageY)) + function onDrag({ pageX, pageY, buttons, modifiers, started }: DragInput) { + if (started) { + if (buttons === MouseButtonsFlag.Primary) { + Vec2.copy(_moveCurr, getMouseOnCircle(pageX, pageY)) Vec2.copy(_movePrev, _moveCurr) - break; - default: // 2 or more - _state = STATE.TOUCH_ZOOM_PAN; - const dx = event.touches[0].pageX - event.touches[1].pageX; - const dy = event.touches[0].pageY - event.touches[1].pageY; - _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt(dx * dx + dy * dy); - - const x = ( event.touches[0].pageX + event.touches[1].pageX) / 2; - const y = ( event.touches[0].pageY + event.touches[1].pageY) / 2; - Vec2.copy(_panStart, getMouseOnScreen(x, y)) + } else if (buttons === MouseButtonsFlag.Auxilary) { + Vec2.copy(_zoomStart, getMouseOnScreen(pageX, pageY)) + Vec2.copy(_zoomEnd, _zoomStart) + } else if (buttons === MouseButtonsFlag.Secondary) { + Vec2.copy(_panStart, getMouseOnScreen(pageX, pageY)) Vec2.copy(_panEnd, _panStart) - break; + } } - start.next() - } - - function touchmove(event: TouchEvent) { - event.preventDefault(); - event.stopPropagation(); - - switch ( event.touches.length ) { - case 1: - Vec2.copy(_movePrev, _moveCurr) - Vec2.copy(_moveCurr, getMouseOnCircle(event.touches[0].pageX, event.touches[0].pageY)) - break; - default: // 2 or more - const dx = event.touches[0].pageX - event.touches[1].pageX; - const dy = event.touches[0].pageY - event.touches[1].pageY; - _touchZoomDistanceEnd = Math.sqrt(dx * dx + dy * dy); - - const x = (event.touches[0].pageX + event.touches[1].pageX) / 2; - const y = (event.touches[0].pageY + event.touches[1].pageY) / 2; - Vec2.copy(_panEnd, getMouseOnScreen(x, y)) - break; + if (buttons === MouseButtonsFlag.Primary) { + Vec2.copy(_movePrev, _moveCurr) + Vec2.copy(_moveCurr, getMouseOnCircle(pageX, pageY)) + } else if (buttons === MouseButtonsFlag.Auxilary) { + Vec2.copy(_zoomEnd, getMouseOnScreen(pageX, pageY)) + } else if (buttons === MouseButtonsFlag.Secondary) { + Vec2.copy(_panEnd, getMouseOnScreen(pageX, pageY)) } } - function touchend(event: TouchEvent) { - switch ( event.touches.length ) { - case 0: - _state = STATE.NONE; - break; - case 1: - _state = STATE.TOUCH_ROTATE; - Vec2.copy(_moveCurr, getMouseOnCircle(event.touches[0].pageX, event.touches[0].pageY)) - Vec2.copy(_movePrev, _moveCurr) - break; - - } - - end.next() + function onWheel({ dy }: WheelInput) { + _zoomStart[1] -= dy } - function contextmenu(event: Event) { - event.preventDefault(); + function onPinch({ delta }: PinchInput) { + console.log(delta) + _zoomStart[1] -= delta } - function dispose() { - element.removeEventListener( 'contextmenu', contextmenu, false ); - element.removeEventListener( 'mousedown', mousedown as any, false ); - element.removeEventListener( 'wheel', mousewheel, false ); + // function touchstart(event: TouchEvent ) { + // switch ( event.touches.length ) { + // case 1: + // // _state = STATE.TOUCH_ROTATE; + // Vec2.copy(_moveCurr, getMouseOnCircle(event.touches[0].pageX, event.touches[0].pageY)) + // Vec2.copy(_movePrev, _moveCurr) + // break; + // default: // 2 or more + // // _state = STATE.TOUCH_ZOOM_PAN; + // const dx = event.touches[0].pageX - event.touches[1].pageX; + // const dy = event.touches[0].pageY - event.touches[1].pageY; + // _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt(dx * dx + dy * dy); + + // const x = ( event.touches[0].pageX + event.touches[1].pageX) / 2; + // const y = ( event.touches[0].pageY + event.touches[1].pageY) / 2; + // Vec2.copy(_panStart, getMouseOnScreen(x, y)) + // Vec2.copy(_panEnd, _panStart) + // break; + // } + // } + + // function touchmove(event: TouchEvent) { + // event.preventDefault(); + // event.stopPropagation(); + + // switch ( event.touches.length ) { + // case 1: + // Vec2.copy(_movePrev, _moveCurr) + // Vec2.copy(_moveCurr, getMouseOnCircle(event.touches[0].pageX, event.touches[0].pageY)) + // break; + // default: // 2 or more + // const dx = event.touches[0].pageX - event.touches[1].pageX; + // const dy = event.touches[0].pageY - event.touches[1].pageY; + // _touchZoomDistanceEnd = Math.sqrt(dx * dx + dy * dy); + + // const x = (event.touches[0].pageX + event.touches[1].pageX) / 2; + // const y = (event.touches[0].pageY + event.touches[1].pageY) / 2; + // Vec2.copy(_panEnd, getMouseOnScreen(x, y)) + // break; + // } + // } + + // function touchend(event: TouchEvent) { + // switch ( event.touches.length ) { + // case 0: + // // _state = STATE.NONE; + // break; + // case 1: + // // _state = STATE.TOUCH_ROTATE; + // Vec2.copy(_moveCurr, getMouseOnCircle(event.touches[0].pageX, event.touches[0].pageY)) + // Vec2.copy(_movePrev, _moveCurr) + // break; + + // } + // } - element.removeEventListener( 'touchstart', touchstart as any, false ); - element.removeEventListener( 'touchend', touchend as any, false ); - element.removeEventListener( 'touchmove', touchmove as any, false ); + function dispose() { + if (disposed) return + disposed = true + input.dispose() - document.removeEventListener( 'mousemove', mousemove, false ); - document.removeEventListener( 'mouseup', mouseup, false ); + // element.removeEventListener( 'touchstart', touchstart as any, false ); + // element.removeEventListener( 'touchend', touchend as any, false ); + // element.removeEventListener( 'touchmove', touchmove as any, false ); } - element.addEventListener( 'contextmenu', contextmenu, false ); - element.addEventListener( 'mousedown', mousedown as any, false ); - element.addEventListener( 'wheel', mousewheel, false ); - - element.addEventListener( 'touchstart', touchstart as any, false ); - element.addEventListener( 'touchend', touchend as any, false ); - element.addEventListener( 'touchmove', touchmove as any, false ); - - handleResize(); + // element.addEventListener( 'touchstart', touchstart as any, false ); + // element.addEventListener( 'touchend', touchend as any, false ); + // element.addEventListener( 'touchmove', touchmove as any, false ); // force an update at start update(); return { - change, - start, - end, + viewport, get dynamicDampingFactor() { return dynamicDampingFactor }, set dynamicDampingFactor(value: number ) { dynamicDampingFactor = value }, @@ -473,7 +379,6 @@ namespace TrackballControls { set panSpeed(value: number ) { panSpeed = value }, update, - handleResize, reset, dispose } diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index 0a939ec9eddaf92b5d2efb55a1606a0c1b93dc31..6ba0cfea705c5a2e01effc6368ec8857a1212e0e 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -13,7 +13,7 @@ import Stats from './stats' import { Vec3, Mat4 } from 'mol-math/linear-algebra' import { ValueCell } from 'mol-util'; import TrackballControls from './controls/trackball'; -import { element } from 'mol-util/input/mouse-event'; +import { Viewport } from './camera/util'; let _renderObjectId = 0; function getNextId() { @@ -55,6 +55,7 @@ interface Renderer { clear: () => void draw: () => void frame: () => void + handleResize: () => void } function resizeCanvas (canvas: HTMLCanvasElement, element: HTMLElement) { @@ -102,10 +103,7 @@ namespace Renderer { const objectIdRenderableMap: { [k: number]: Renderable } = {} const extensions = [ - 'OES_texture_float', - 'OES_texture_float_linear', 'OES_element_index_uint', - 'EXT_blend_minmax', 'ANGLE_instanced_arrays' ] const optionalExtensions = [ @@ -116,26 +114,20 @@ namespace Renderer { const camera = PerspectiveCamera.create({ near: 0.01, - far: 1000, + far: 10000, position: Vec3.create(0, 0, 50) }) const controls = TrackballControls.create(canvas, camera, { - - }) - // window.addEventListener('resize', (ev: Event) => { - // console.log({ viewport: { x: 0, y: 0, width: canvas.width, height: canvas.height }}) - // regl({ viewport: { x: 0, y: 0, width: canvas.width, height: canvas.height }}) - // }, false) + }) const baseContext = regl({ - viewport: { x: 0, y: 0, width: canvas.width, height: canvas.height }, context: { model: Mat4.identity(), transform: Mat4.identity(), view: camera.view, - projection: camera.projection + projection: camera.projection, }, uniforms: { model: regl.context('model' as any), @@ -168,6 +160,9 @@ namespace Renderer { }) } + window.addEventListener('resize', handleResize, false) + handleResize() + // TODO animate, draw, requestDraw return { camera, @@ -200,7 +195,15 @@ namespace Renderer { draw, frame: () => { regl.frame((ctx) => draw()) - } + }, + handleResize + } + + function handleResize() { + const viewport = { x: 0, y: 0, width: canvas.width, height: canvas.height } + regl({ viewport }) + Viewport.copy(camera.viewport, viewport) + Viewport.copy(controls.viewport, viewport) } } } diff --git a/src/mol-math/linear-algebra/3d/vec2.ts b/src/mol-math/linear-algebra/3d/vec2.ts index 6cf148597056a866b352729dd0ac5e99e25707f3..e02c793e13b4611942302dc9bbda02cdbd33cf39 100644 --- a/src/mol-math/linear-algebra/3d/vec2.ts +++ b/src/mol-math/linear-algebra/3d/vec2.ts @@ -75,6 +75,18 @@ namespace Vec2 { return out; } + export function mul(out: Vec2, a: Vec2, b: Vec2) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + return out; + } + + export function div(out: Vec2, a: Vec2, b: Vec2) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + return out; + } + export function scale(out: Vec2, a: Vec2, b: number) { out[0] = a[0] * b; out[1] = a[1] * b; diff --git a/src/mol-math/linear-algebra/3d/vec3.ts b/src/mol-math/linear-algebra/3d/vec3.ts index 7d584170f5a8a38c8da3f86b3519ee8f0dfe74cf..a2370289e13d16b7a129d7c0dd628c7b6b754567 100644 --- a/src/mol-math/linear-algebra/3d/vec3.ts +++ b/src/mol-math/linear-algebra/3d/vec3.ts @@ -94,6 +94,20 @@ namespace Vec3 { return out; } + export function mul(out: Vec3, a: Vec3, b: Vec3) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + return out; + } + + export function div(out: Vec3, a: Vec3, b: Vec3) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + return out; + } + export function scale(out: Vec3, a: Vec3, b: number) { out[0] = a[0] * b; out[1] = a[1] * b; diff --git a/src/mol-util/input/event-offset.ts b/src/mol-util/input/event-offset.ts index 12a14c21b8e6bafb1e30f324e6d5f02aeec72a64..c0c1751f242fefdebb23c77167ea91dedfd1f8bb 100644 --- a/src/mol-util/input/event-offset.ts +++ b/src/mol-util/input/event-offset.ts @@ -23,7 +23,7 @@ export function eventOffset (out: Vec2, ev: MouseEvent | Touch, target: Element) } function getBoundingClientOffset (element: Element | Window | Document) { - if (element !== window && element !== document && element !== document.body) { + if (element instanceof Window || element instanceof Document || element === document.body) { return rootPosition } else { return (element as Element).getBoundingClientRect() diff --git a/src/mol-util/input/input-observer.ts b/src/mol-util/input/input-observer.ts index 73825edd9a709acdbbd1a0d044f9685a8face65e..feaacce2ac38108a3aab1d4fd37cccf8c9328eb1 100644 --- a/src/mol-util/input/input-observer.ts +++ b/src/mol-util/input/input-observer.ts @@ -8,11 +8,10 @@ import { Subject } from 'rxjs'; import { Vec2 } from 'mol-math/linear-algebra'; -import MouseWheel from './mouse-wheel' +import toPixels from '../to-pixels' import TouchPinch from './touch-pinch' -import { eventOffset } from './event-offset' -export function getButtons(event: MouseEvent | Touch) { +function getButtons(event: MouseEvent | Touch) { if (typeof event === 'object') { if ('buttons' in event) { return event.buttons @@ -40,133 +39,193 @@ export function getButtons(event: MouseEvent | Touch) { } export const DefaultInputObserverProps = { - parent: window as Window | Element, - noScroll: true + noScroll: true, + noContextMenu: true } export type InputObserverProps = Partial<typeof DefaultInputObserverProps> -export type MouseModifiers = { +export type ModifiersKeys = { shift: boolean, alt: boolean, control: boolean, meta: boolean } +export const enum MouseButtonsFlag { + /** 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 = { + buttons: number + modifiers: ModifiersKeys +} + +export type DragInput = { + x: number, + y: number, + dx: number, + dy: number, + pageX: number, + pageY: number, + started: boolean +} & BaseInput + +export type WheelInput = { + dx: number, + dy: number, + dz: number, +} & BaseInput + +export type ClickInput = { + x: number, + y: number, + pageX: number, + pageY: number, +} & BaseInput + +export type PinchInput = { + delta: number, + distance: number +} + +const enum DraggingState { + Stopped = 0, + Started = 1, + Moving = 2 +} + +type PointerEvent = { + clientX: number + clientY: number + pageX: number + pageY: number +} + interface InputObserver { noScroll: boolean - isDragging: () => boolean - isPinching: () => boolean + noContextMenu: boolean - drag: Subject<{ dx: number, dy: number, buttons: number, modifiers: MouseModifiers }>, - wheel: Subject<{ dx: number, dy: number, dz: number, event: WheelEvent }>, - pinch: Subject<number>, - // click: Subject<{ x: number, y: number, buttons: number, modifiers: MouseModifiers }>, + drag: Subject<DragInput>, + wheel: Subject<WheelInput>, + pinch: Subject<PinchInput>, + click: Subject<ClickInput>, dispose: () => void } namespace InputObserver { export function create (element: Element, props: InputObserverProps = {}): InputObserver { - const { parent, noScroll } = { ...DefaultInputObserverProps, ...props } + let { noScroll, noContextMenu } = { ...DefaultInputObserverProps, ...props } - const mouseStart = Vec2.zero() - const tmp = Vec2.zero() - const tmp2 = Vec2.zero() - const modifiers: MouseModifiers = { + const lineHeight = toPixels('ex', element) + + let lastTouchDistance = 0 + const pointerStart = Vec2.zero() + const pointerEnd = Vec2.zero() + const pointerDelta = Vec2.zero() + const rectSize = Vec2.zero() + const modifiers: ModifiersKeys = { shift: false, alt: false, control: false, meta: false } - const touchPinch = TouchPinch.create(element) - const mouseWheel = MouseWheel.create(element, noScroll) + // const touchPinch = TouchPinch.create(element) - let dragging = false + let dragging: DraggingState = DraggingState.Stopped let disposed = false let buttons = 0 - const drag = new Subject<{ dx: number, dy: number, buttons: number, modifiers: MouseModifiers }>() - const wheel = mouseWheel.wheel - const pinch = new Subject<number>() + const drag = new Subject<DragInput>() + const click = new Subject<ClickInput>() + const wheel = new Subject<WheelInput>() + const pinch = new Subject<PinchInput>() attach() return { - get noScroll () { return mouseWheel.noScroll }, - set noScroll (value: boolean) { mouseWheel.noScroll = value }, - isDragging: () => dragging, - isPinching, + get noScroll () { return noScroll }, + set noScroll (value: boolean) { noScroll = value }, + get noContextMenu () { return noContextMenu }, + set noContextMenu (value: boolean) { noContextMenu = value }, drag, wheel, pinch, + click, dispose } function attach () { - element.addEventListener('mousedown', onInputDown as any, false) + element.addEventListener( 'contextmenu', onContextMenu, false ) + element.addEventListener('wheel', onMouseWheel, false) + element.addEventListener('mousedown', onPointerDown as any, false) // for dragging to work outside canvas bounds, - // mouse move/up events have to be added to parent, i.e. window - parent.addEventListener('mousemove', onInputMove as any, false) - parent.addEventListener('mouseup', onInputUp as any, false) - - // don't allow simulated mouse events - element.addEventListener('touchstart', preventDefault as any, false) + // 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) + element.addEventListener('touchstart', onTouchStart as any, false) element.addEventListener('touchmove', onTouchMove as any, false) + element.addEventListener('touchend', onTouchEnd as any, false) - touchPinch.place.subscribe(onPinchPlace) - touchPinch.lift.subscribe(onPinchLift) - touchPinch.change.subscribe(onPinchChange) + // touchPinch.place.subscribe(onPinchPlace) + // touchPinch.lift.subscribe(onPinchLift) + // touchPinch.change.subscribe(onPinchChange) element.addEventListener('blur', handleBlur) element.addEventListener('keyup', handleMods as EventListener) element.addEventListener('keydown', handleMods as EventListener) element.addEventListener('keypress', handleMods as EventListener) - - if (!(element instanceof Window)) { - window.addEventListener('blur', handleBlur) - window.addEventListener('keyup', handleMods) - window.addEventListener('keydown', handleMods) - window.addEventListener('keypress', handleMods) - } } function dispose () { if (disposed) return disposed = true - mouseWheel.dispose() - touchPinch.dispose() + // touchPinch.dispose() - element.removeEventListener('touchstart', preventDefault as any, false) - element.removeEventListener('touchmove', onTouchMove as any, false) + element.removeEventListener( 'contextmenu', onContextMenu, false ) - element.removeEventListener('mousedown', onInputDown as any, false) + element.removeEventListener('wheel', onMouseWheel, false) + element.removeEventListener('mousedown', onMouseDown as any, false) + window.removeEventListener('mousemove', onMouseMove as any, false) + window.removeEventListener('mouseup', onMouseUp as any, false) - parent.removeEventListener('mousemove', onInputMove as any, false) - parent.removeEventListener('mouseup', onInputUp as any, false) + element.removeEventListener('touchstart', onTouchStart as any, false) + element.removeEventListener('touchmove', onTouchMove as any, false) + element.removeEventListener('touchend', onTouchEnd as any, false) element.removeEventListener('blur', handleBlur) element.removeEventListener('keyup', handleMods as EventListener) element.removeEventListener('keydown', handleMods as EventListener) element.removeEventListener('keypress', handleMods as EventListener) - - if (!(element instanceof Window)) { - window.removeEventListener('blur', handleBlur) - window.removeEventListener('keyup', handleMods) - window.removeEventListener('keydown', handleMods) - window.removeEventListener('keypress', handleMods) - } } function preventDefault (ev: Event | Touch) { if ('preventDefault' in ev) ev.preventDefault() } + function onContextMenu(event: Event) { + if (noContextMenu) { + event.preventDefault() + } + } + function handleBlur () { if (buttons || modifiers.shift || modifiers.alt || modifiers.meta || modifiers.control) { buttons = 0 @@ -181,72 +240,182 @@ namespace InputObserver { if ('metaKey' in event) modifiers.meta = !!event.metaKey } - function onTouchMove (ev: TouchEvent) { - if (!dragging || isPinching()) return - - // find currently active finger - for (let i = 0; i < ev.changedTouches.length; i++) { - const changed = ev.changedTouches[i] - const idx = touchPinch.indexOfTouch(changed) - if (idx !== -1) { - onInputMove(changed) - break - } + function getCenterTouch (ev: TouchEvent): PointerEvent { + const t0 = ev.touches[0] + const t1 = ev.touches[1] + return { + clientX: (t0.clientX + t1.clientX) / 2, + clientY: (t0.clientY + t1.clientY) / 2, + pageX: (t0.pageX + t1.pageX) / 2, + pageY: (t0.pageY + t1.pageY) / 2 } } - function onPinchPlace ({ newTouch, oldTouch }: { newTouch?: Touch, oldTouch?: Touch }) { - dragging = !isPinching() - if (dragging) { - const firstFinger = oldTouch || newTouch - if (firstFinger) onInputDown(firstFinger) + function getTouchDistance (ev: TouchEvent) { + const dx = ev.touches[0].pageX - ev.touches[1].pageX; + const dy = ev.touches[0].pageY - ev.touches[1].pageY; + return Math.sqrt(dx * dx + dy * dy); + } + + function onTouchStart (ev: TouchEvent) { + preventDefault(ev) + + console.log('onTouchStart', ev) + if (ev.touches.length === 1) { + buttons = MouseButtonsFlag.Primary + onPointerDown(ev.touches[0]) + } else if (ev.touches.length >= 2) { + buttons = MouseButtonsFlag.Secondary + onPointerDown(getCenterTouch(ev)) + + let lastTouchDistance = getTouchDistance(ev) + pinch.next({ distance: lastTouchDistance, delta: 0 }) } } - function onPinchLift ({ removed, otherTouch }: { removed?: Touch, otherTouch?: Touch }) { - // if either finger is down, consider it dragging - const sum = touchPinch.fingers.reduce((sum, item) => sum + (item ? 1 : 0), 0) - dragging = sum >= 1 + function onTouchEnd (ev: TouchEvent) { + preventDefault(ev) + + console.log('onTouchEnd', ev) + } + + function onTouchMove (ev: TouchEvent) { + preventDefault(ev) + + if (ev.touches.length === 1) { + buttons = MouseButtonsFlag.Primary + onPointerMove(ev.touches[0]) + } else if (ev.touches.length >= 2) { + buttons = MouseButtonsFlag.Secondary + onPointerDown(getCenterTouch(ev)) - if (dragging && otherTouch) { - eventOffset(mouseStart, otherTouch, element) + const touchDistance = getTouchDistance(ev) + pinch.next({ delta: lastTouchDistance - touchDistance, distance: touchDistance }) + lastTouchDistance = touchDistance } + + // if (dragging === DraggingState.Stopped || isPinching()) return + + // // find currently active finger + // for (let i = 0; i < ev.changedTouches.length; i++) { + // const changed = ev.changedTouches[i] + // const idx = touchPinch.indexOfTouch(changed) + // if (idx !== -1) { + // onInputMove(changed) + // break + // } + // } } - function isPinching () { - return touchPinch.pinching + // function onPinchPlace ({ newTouch, oldTouch }: { newTouch?: Touch, oldTouch?: Touch }) { + // dragging = isPinching() ? DraggingState.Stopped : DraggingState.Started + // if (dragging === DraggingState.Started) { + // const firstFinger = oldTouch || newTouch + // if (firstFinger) onInputDown(firstFinger) + // } + // } + + // function onPinchLift ({ removed, otherTouch }: { removed?: Touch, otherTouch?: Touch }) { + // // if either finger is down, consider it dragging + // const sum = touchPinch.fingers.reduce((sum, item) => sum + (item ? 1 : 0), 0) + // dragging = sum >= 1 ? DraggingState.Moving : DraggingState.Stopped + + // if (dragging && otherTouch) { + // eventOffset(mouseStart, otherTouch, element) + // } + // } + + // function isPinching () { + // return touchPinch.pinching + // } + + // function onPinchChange ({ currentDistance, lastDistance }: { currentDistance: number, lastDistance: number }) { + // pinch.next({ delta: currentDistance - lastDistance }) + // } + + function onMouseDown (ev: MouseEvent) { + preventDefault(ev) + + buttons = getButtons(ev) + onPointerDown(ev) } - function onPinchChange ({ currentDistance, lastDistance }: { currentDistance: number, lastDistance: number }) { - pinch.next(currentDistance - lastDistance) + function onMouseMove (ev: MouseEvent) { + preventDefault(ev) + + buttons = getButtons(ev) + onPointerMove(ev) } - function onInputDown (ev: MouseEvent | Touch) { + function onMouseUp (ev: MouseEvent) { preventDefault(ev) - eventOffset(mouseStart, ev, element) - if (insideBounds(mouseStart)) { - dragging = true + + buttons = getButtons(ev) + onPointerUp(ev) + } + + function onPointerDown (ev: PointerEvent) { + eventOffset(pointerStart, ev) + if (insideBounds(pointerStart)) { + dragging = DraggingState.Started } } - function onInputUp () { - dragging = false + function onPointerUp (ev: PointerEvent) { + dragging = DraggingState.Stopped + + if (Vec2.distance(pointerEnd, pointerStart) < 4) { + eventOffset(pointerEnd, ev) + + const { pageX, pageY } = ev + const [ x, y ] = pointerEnd + + console.log('click', { x, y, pageX, pageY, buttons, modifiers }) + click.next({ x, y, pageX, pageY, buttons, modifiers }) + } } - function onInputMove (ev: MouseEvent | Touch) { - buttons = getButtons(ev) - const end = eventOffset(tmp, ev, element) - if (pinch && isPinching()) { - Vec2.copy(mouseStart, end) - return + function onPointerMove (ev: PointerEvent) { + eventOffset(pointerEnd, ev) + // if (pinch && isPinching()) { + // Vec2.copy(pointerStart, pointerEnd) + // return + // } + if (dragging === DraggingState.Stopped) return + + Vec2.div(pointerDelta, Vec2.sub(pointerDelta, pointerEnd, pointerStart), getClientSize(rectSize)) + + const started = dragging === DraggingState.Started + const { pageX, pageY } = ev + const [ x, y ] = pointerEnd + const [ dx, dy ] = pointerDelta + // console.log({ x, y, dx, dy, pageX, pageY, buttons, modifiers, started }) + drag.next({ x, y, dx, dy, pageX, pageY, buttons, modifiers, started }) + + Vec2.copy(pointerStart, pointerEnd) + dragging = DraggingState.Moving + } + + function onMouseWheel(ev: MouseWheelEvent) { + if (noScroll) { + ev.preventDefault() + } + const mode = ev.deltaMode + let dx = ev.deltaX || 0 + let dy = ev.deltaY || 0 + let dz = ev.deltaZ || 0 + let scale = 1 + switch (mode) { + case 1: scale = lineHeight; break + case 2: scale = window.innerHeight; break + } + scale *= 0.0001 + dx *= scale + dy *= scale + dz *= scale + if (dx || dy || dz) { + wheel.next({ dx, dy, dz, buttons, modifiers }) } - if (!dragging) return - const rect = getClientSize(tmp2) - const dx = (end[0] - mouseStart[0]) / rect[0] - const dy = (end[1] - mouseStart[1]) / rect[1] - drag.next({ dx, dy, buttons, modifiers }) - mouseStart[0] = end[0] - mouseStart[1] = end[1] } function insideBounds (pos: Vec2) { @@ -259,12 +428,17 @@ namespace InputObserver { } function getClientSize (out: Vec2) { - let source = element - if (source instanceof Window || source instanceof Document || source === document.body) { - source = document.documentElement - } - out[0] = source.clientWidth - out[1] = source.clientHeight + out[0] = element.clientWidth + out[1] = element.clientHeight + return out + } + + function eventOffset (out: Vec2, ev: PointerEvent) { + const cx = ev.clientX || 0 + const cy = ev.clientY || 0 + const rect = element.getBoundingClientRect() + out[0] = cx - rect.left + out[1] = cy - rect.top return out } } diff --git a/src/mol-util/input/mouse-change.ts b/src/mol-util/input/mouse-change.ts deleted file mode 100644 index 5dc41eaf9c40c1e4d4cac7bea532f04728818680..0000000000000000000000000000000000000000 --- a/src/mol-util/input/mouse-change.ts +++ /dev/null @@ -1,184 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -/* - * This code has been modified from https://github.com/mikolalysenko/mouse-change, - * copyright (c) 2015 Mikola Lysenko. MIT License - */ - -import { Subject } from 'rxjs'; - -import * as mouse from './mouse-event' - - - -interface MouseChange { - change: Subject<number>, - dispose: () => void -} - -namespace MouseChange { - export type Modifiers = { - shift: boolean, - alt: boolean, - control: boolean, - meta: boolean - } - export type Info = { - buttons: number, - x: number, - y: number, - modifiers: Modifiers - } - - export function create(element: Element) { - let buttonState = 0 - let x = 0 - let y = 0 - const mods: Modifiers = { - shift: false, - alt: false, - control: false, - meta: false - } - let attached = false - - const change = new Subject<Info>() - - // Attach listeners - attachListeners() - - return { - change, - dispose - } - - function updateMods (event: MouseEvent | KeyboardEvent) { - let changed = false - if ('altKey' in event) { - changed = changed || event.altKey !== mods.alt - mods.alt = !!event.altKey - } - if ('shiftKey' in event) { - changed = changed || event.shiftKey !== mods.shift - mods.shift = !!event.shiftKey - } - if ('ctrlKey' in event) { - changed = changed || event.ctrlKey !== mods.control - mods.control = !!event.ctrlKey - } - if ('metaKey' in event) { - changed = changed || event.metaKey !== mods.meta - mods.meta = !!event.metaKey - } - return changed - } - - function handleEvent (nextButtons: number, event: MouseEvent) { - const nextX = mouse.x(event) - const nextY = mouse.y(event) - if ('buttons' in event) { - nextButtons = event.buttons | 0 - } - if (nextButtons !== buttonState || nextX !== x || nextY !== y || updateMods(event) ) { - buttonState = nextButtons | 0 - x = nextX || 0 - y = nextY || 0 - - change.next({ buttons: buttonState, x, y, modifiers: mods }) - } - } - - function clearState (event: MouseEvent) { - handleEvent(0, event) - } - - function handleBlur () { - if (buttonState || x || y || mods.shift || mods.alt || mods.meta || mods.control) { - x = y = 0 - buttonState = 0 - mods.shift = mods.alt = mods.control = mods.meta = false - change.next({ buttons: 0, x: 0, y: 0, modifiers: mods }) - } - } - - function handleMods (event: MouseEvent | KeyboardEvent) { - if (updateMods(event)) { - change.next({ buttons: buttonState, x, y, modifiers: mods }) - } - } - - function handleMouseMove (event: MouseEvent) { - if (mouse.buttons(event) === 0) { - handleEvent(0, event) - } else { - handleEvent(buttonState, event) - } - } - - function handleMouseDown (event: MouseEvent) { - handleEvent(buttonState | mouse.buttons(event), event) - } - - function handleMouseUp (event: MouseEvent) { - handleEvent(buttonState & ~mouse.buttons(event), event) - } - - function attachListeners () { - if (attached) return - attached = true - - element.addEventListener('mousemove', handleMouseMove as EventListener) - element.addEventListener('mousedown', handleMouseDown as EventListener) - element.addEventListener('mouseup', handleMouseUp as EventListener) - - element.addEventListener('mouseleave', clearState as EventListener) - element.addEventListener('mouseenter', clearState as EventListener) - element.addEventListener('mouseout', clearState as EventListener) - element.addEventListener('mouseover', clearState as EventListener) - - element.addEventListener('blur', handleBlur) - element.addEventListener('keyup', handleMods as EventListener) - element.addEventListener('keydown', handleMods as EventListener) - element.addEventListener('keypress', handleMods as EventListener) - - if (!(element instanceof Window)) { - window.addEventListener('blur', handleBlur) - window.addEventListener('keyup', handleMods) - window.addEventListener('keydown', handleMods) - window.addEventListener('keypress', handleMods) - } - } - - function dispose () { - if (!attached) return - attached = false - - element.removeEventListener('mousemove', handleMouseMove as EventListener) - element.removeEventListener('mousedown', handleMouseDown as EventListener) - element.removeEventListener('mouseup', handleMouseUp as EventListener) - - element.removeEventListener('mouseleave', clearState as EventListener) - element.removeEventListener('mouseenter', clearState as EventListener) - element.removeEventListener('mouseout', clearState as EventListener) - element.removeEventListener('mouseover', clearState as EventListener) - - element.removeEventListener('blur', handleBlur) - element.removeEventListener('keyup', handleMods as EventListener) - element.removeEventListener('keydown', handleMods as EventListener) - element.removeEventListener('keypress', handleMods as EventListener) - - if (!(element instanceof Window)) { - window.removeEventListener('blur', handleBlur) - window.removeEventListener('keyup', handleMods) - window.removeEventListener('keydown', handleMods) - window.removeEventListener('keypress', handleMods) - } - } - } -} - -export default MouseChange \ No newline at end of file diff --git a/src/mol-util/input/mouse-event.ts b/src/mol-util/input/mouse-event.ts deleted file mode 100644 index 7c4966b82ad3992bb6f5584ad0802384fea039db..0000000000000000000000000000000000000000 --- a/src/mol-util/input/mouse-event.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -/* - * This code has been modified from https://github.com/mikolalysenko/mouse-event, - * copyright (c) 2015 Mikola Lysenko. MIT License - */ - -export function buttons(event: MouseEvent) { - if (typeof event === 'object') { - if ('buttons' in event) { - return event.buttons - } else if ('which' in event) { - const b = (event as any).which // 'any' to support older browsers - if (b === 2) { - return 4 - } else if (b === 3) { - return 2 - } else if (b > 0) { - return 1<<(b-1) - } - } else if ('button' in event) { - const b = (event as any).button // 'any' to support older browsers - if (b === 1) { - return 4 - } else if (b === 2) { - return 2 - } else if (b >= 0) { - return 1<<b - } - } - } - return 0 -} - -export function element(event: MouseEvent) { - return event.target as Element -} - -export function x(event: MouseEvent) { - if (typeof event === 'object') { - if ('offsetX' in event) { - return event.offsetX - } - const target = element(event) - const bounds = target.getBoundingClientRect() - return (event as any).clientX - bounds.left // 'any' to support older browsers - } - return 0 -} - -export function y(event: MouseEvent) { - if (typeof event === 'object') { - if ('offsetY' in event) { - return event.offsetY - } - const target = element(event) - const bounds = target.getBoundingClientRect() - return (event as any).clientY - bounds.top // 'any' to support older browsers - } - return 0 -} \ No newline at end of file diff --git a/src/mol-util/input/mouse-wheel.ts b/src/mol-util/input/mouse-wheel.ts deleted file mode 100644 index b56bd320b0414124cbfb8219eb3f94d6ed868077..0000000000000000000000000000000000000000 --- a/src/mol-util/input/mouse-wheel.ts +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -/* - * This code has been modified from https://github.com/mikolalysenko/mouse-wheel, - * copyright (c) 2015 Mikola Lysenko. MIT License - */ - -import { Subject } from 'rxjs'; -import toPixels from '../to-pixels' - -interface MouseWheel { - noScroll: boolean - wheel: Subject<{ dx: number, dy: number, dz: number, event: WheelEvent }> - dispose: () => void -} - -namespace MouseWheel { - export function create(element: Element, noScroll = true): MouseWheel { - const lineHeight = toPixels('ex', element) - let disposed = false - const wheel = new Subject<{ dx: number, dy: number, dz: number, event: WheelEvent }>() - - element.addEventListener('wheel', listener) - - return { - get noScroll () { return noScroll }, - set noScroll (value: boolean) { noScroll = value }, - - wheel, - dispose - } - - function listener(event: MouseWheelEvent) { - if (noScroll) { - event.preventDefault() - } - const mode = event.deltaMode - let dx = event.deltaX || 0 - let dy = event.deltaY || 0 - let dz = event.deltaZ || 0 - let scale = 1 - switch (mode) { - case 1: scale = lineHeight; break - case 2: scale = window.innerHeight; break - } - dx *= scale - dy *= scale - dz *= scale - if (dx || dy || dz) { - wheel.next({ dx, dy, dz, event }) - } - } - - function dispose() { - if (disposed) return - disposed = true - element.removeEventListener('wheel', listener) - wheel.unsubscribe() - } - } -} - -export default MouseWheel \ No newline at end of file diff --git a/src/mol-util/input/touch-pinch.ts b/src/mol-util/input/touch-pinch.ts index 963a84ef7b36809d23fcb82cda382f53f0e42c23..7c4b12d3e2602944c21ecfa5103094e3a96be658 100644 --- a/src/mol-util/input/touch-pinch.ts +++ b/src/mol-util/input/touch-pinch.ts @@ -147,9 +147,9 @@ namespace TouchPinch { eventOffset(finger.position, movedTouch, target) } } - } + } - if (activeCount === 2 && changed) { + if (activeCount === 2 && changed) { const currentDistance = computeDistance() change.next({ currentDistance, lastDistance }) lastDistance = currentDistance