import { vec2 } from 'gl-matrix' ; import { Observable , Subscription } from 'rxjs' ; import { assert } from './auxiliaries' ; import { EyeGazeEvent } from './eyegazeevent' ; import { EyeGazeEventProvider } from './eyegazeeventprovider' ; import { MouseEventProvider } from './mouseeventprovider' ; import { PointerEventProvider } from './pointereventprovider' ; import { Invalidate } from './renderer' ; import { TouchEventProvider } from './toucheventprovider' ; export interface EventProvider { pointerEventProvider : PointerEventProvider ; mouseEventProvider : MouseEventProvider ; eyeGazeEventProvider ? : EyeGazeEventProvider ; } /**
 * Callback for handling mouse events, given the latest mouse events (since last update) as well as the previous.
 */ export interface MouseEventHandler { ( latests , previous ) : void ; } /**
 * Callback for handling touch events, given the latest touch events (since last update) as well as the previous.
 */ export interface TouchEventHandler { ( latests , previous ) : void ; } /**
 * Callback for handling pointer events, given the latest touch events (since last update) as well as the previous.
 */ export interface PointerEventHandler { ( latests , previous ) : void ; } /**
 * Callback for handling eye gaze events, given the latest eye gaze events (since last update) as well as the previous.
 */ export interface EyeGazeEventHandler { ( latests , previous ) : void ; } /**
 * ... Provider and event handler are explicitly separated in order to reduce the number of observables (reuse of event
 * provider for multiple handler).
 */ export class EventHandler { /**
     * The event handler's invalidation callback. This should usually be setup by the owning renderer and invoke the
     * same callback the renderer has been given by the canvas. This invalidation is required, when continuous
     * rendering is not present, events might cause need for new rendering requests.
     */ protected _invalidate : Invalidate | undefined ; protected _subscriptions = new Array < Subscription > ( ) ; /**
     * Assigned key event provider. This is usually created and owned by the canvas.
     */ /**
     * Assigned mouse event provider. This is usually created and owned by the canvas.
     */ protected _mouseEventProvider : MouseEventProvider | undefined ; /**
     * Assigned touch event provider. This is usually created and owned by the canvas.
     */ protected _touchEventProvider : TouchEventProvider | undefined ; /**
     * Assigned pointer event provider. This is usually created and owned by the canvas.
     */ protected _pointerEventProvider : PointerEventProvider | undefined ; /**
     * Assigned eye gaze event provider. This is usually created and owned by the eye gaze data stream.
     */ protected _eyeGazeEventProvider : EyeGazeEventProvider | undefined ; protected _latestMouseEventsByType = new Map < MouseEventProvider . Type , Array < MouseEvent > > ( ) ; protected _previousMouseEventsByType = new Map < MouseEventProvider . Type , Array < MouseEvent > > ( ) ; protected _mouseEventHandlerByType = new Map < MouseEventProvider . Type , Array < MouseEventHandler > > ( ) ; protected _latestTouchEventsByType = new Map < TouchEventProvider . Type , Array < TouchEvent > > ( ) ; protected _previousTouchEventsByType = new Map < TouchEventProvider . Type , Array < TouchEvent > > ( ) ; protected _touchEventHandlerByType = new Map < TouchEventProvider . Type , Array < TouchEventHandler > > ( ) ; protected _latestPointerEventsByType = new Map < PointerEventProvider . Type , Array < PointerEvent > > ( ) ; protected _previousPointerEventsByType = new Map < PointerEventProvider . Type , Array < PointerEvent > > ( ) ; protected _pointerEventHandlerByType = new Map < PointerEventProvider . Type , Array < PointerEventHandler > > ( ) ; protected _latestEyeGazeEventsByType = new Map < EyeGazeEventProvider . Type , Array < EyeGazeEvent > > ( ) ; protected _previousEyeGazeEventsByType = new Map < EyeGazeEventProvider . Type , Array < EyeGazeEvent > > ( ) ; protected _eyeGazeEventHandlerByType = new Map < EyeGazeEventProvider . Type , Array < EyeGazeEventHandler > > ( ) ; constructor ( invalidate , eventProvider ) { this . _invalidate = invalidate ; this . _mouseEventProvider = eventProvider . mouseEventProvider ; this . _mouseEventProvider . preventDefault ( MouseEventProvider . Type . Wheel ) ; this . _pointerEventProvider = eventProvider . pointerEventProvider ; this . _eyeGazeEventProvider = eventProvider . eyeGazeEventProvider ; } callback Invalidate @callback Invalidate
     * A callback intended to be invoked whenever the specialized event handler itself is invalid. By default only
     * optional updates (renderer decides whether or not to continue redraw) are triggered.
      protected invalidate ( force = false ) { if ( this . _invalidate ) { this . _invalidate ( force ) ; } } param type param handler protected pushMouseEventHandler ( type , handler ) { if ( this . _mouseEventHandlerByType . has ( type ) ) { ( this . _mouseEventHandlerByType . get ( type ) as Array < MouseEventHandler > ) . push ( handler ) ; return ; } this . _mouseEventHandlerByType . set ( type , new Array < MouseEventHandler > ( ) ) ; this . _previousMouseEventsByType . set ( type , new Array < MouseEvent > ( ) ) ; const latest = new Array < MouseEvent > ( ) ; this . _latestMouseEventsByType . set ( type , latest ) ; assert ( this . _mouseEventProvider !== undefined , `expected valid mouse event provider` ) ; const observable = ( this . _mouseEventProvider as MouseEventProvider ) . observable ( type ) ; switch ( type ) { case MouseEventProvider . Type . Wheel : this . _subscriptions . push ( ( observable as Observable < WheelEvent > ) . subscribe ( ( event ) => { latest . push ( event ) ; this . invalidate ( ) ; } ) ) ; break ; default : this . _subscriptions . push ( ( observable as Observable < MouseEvent > ) . subscribe ( ( event ) => { latest . push ( event ) ; this . invalidate ( ) ; } ) ) ; break ; } ( this . _mouseEventHandlerByType . get ( type ) as Array < MouseEventHandler > ) . push ( handler ) ; } protected invokeMouseEventHandler ( type ) { const handlers = this . _mouseEventHandlerByType . get ( type ) ; if ( handlers === undefined || handlers . length === 0 ) { return ; } const latest = this . _latestMouseEventsByType . get ( type ) as Array < MouseEvent > ; if ( latest . length === 0 ) { return ; } const previous = this . _previousMouseEventsByType . get ( type ) as Array < MouseEvent > ; handlers . forEach ( ( handler ) => handler ( latest , previous ) ) ; Object . assign ( previous , latest ) ; latest . length = 0 ; } param type param handler protected pushTouchEventHandler ( type , handler ) { if ( this . _touchEventHandlerByType . has ( type ) ) { ( this . _touchEventHandlerByType . get ( type ) as Array < TouchEventHandler > ) . push ( handler ) ; return ; } this . _touchEventHandlerByType . set ( type , new Array < TouchEventHandler > ( ) ) ; this . _previousTouchEventsByType . set ( type , new Array < TouchEvent > ( ) ) ; const latest = new Array < TouchEvent > ( ) ; this . _latestTouchEventsByType . set ( type , latest ) ; assert ( this . _touchEventProvider !== undefined , `expected valid touch event provider` ) ; const observable = ( this . _touchEventProvider as TouchEventProvider ) . observable ( type ) ; this . _subscriptions . push ( ( observable as Observable < TouchEvent > ) . subscribe ( ( event ) => { latest . push ( event ) ; this . invalidate ( ) ; } ) ) ; ( this . _touchEventHandlerByType . get ( type ) as Array < TouchEventHandler > ) . push ( handler ) ; } protected invokeTouchEventHandler ( type ) { const handlers = this . _touchEventHandlerByType . get ( type ) ; if ( handlers === undefined || handlers . length === 0 ) { return ; } const latest = this . _latestTouchEventsByType . get ( type ) as Array < TouchEvent > ; if ( latest . length === 0 ) { return ; } const previous = this . _previousTouchEventsByType . get ( type ) as Array < TouchEvent > ; handlers . forEach ( ( handler ) => handler ( latest , previous ) ) ; Object . assign ( previous , latest ) ; latest . length = 0 ; } param type param handler protected pushPointerEventHandler ( type , handler ) { if ( this . _pointerEventHandlerByType . has ( type ) ) { ( this . _pointerEventHandlerByType . get ( type ) as Array < PointerEventHandler > ) . push ( handler ) ; return ; } this . _pointerEventHandlerByType . set ( type , new Array < PointerEventHandler > ( ) ) ; this . _previousPointerEventsByType . set ( type , new Array < PointerEvent > ( ) ) ; const latest = new Array < PointerEvent > ( ) ; this . _latestPointerEventsByType . set ( type , latest ) ; assert ( this . _pointerEventProvider !== undefined , `expected valid pointer event provider` ) ; const observable = ( this . _pointerEventProvider as PointerEventProvider ) . observable ( type ) ; this . _subscriptions . push ( ( observable as Observable < PointerEvent > ) . subscribe ( ( event ) => { latest . push ( event ) ; this . invalidate ( ) ; } ) ) ; ( this . _pointerEventHandlerByType . get ( type ) as Array < PointerEventHandler > ) . push ( handler ) ; } protected invokePointerEventHandler ( type ) { const handlers = this . _pointerEventHandlerByType . get ( type ) ; if ( handlers === undefined || handlers . length === 0 ) { return ; } const latest = this . _latestPointerEventsByType . get ( type ) as Array < PointerEvent > ; if ( latest . length === 0 ) { return ; } const previous = this . _previousPointerEventsByType . get ( type ) as Array < PointerEvent > ; handlers . forEach ( ( handler ) => handler ( latest , previous ) ) ; Object . assign ( previous , latest ) ; latest . length = 0 ; } param type param handler protected pushEyeGazeEventHandler ( type , handler ) { if ( this . _eyeGazeEventHandlerByType . has ( type ) ) { ( this . _eyeGazeEventHandlerByType . get ( type ) as Array < EyeGazeEventHandler > ) . push ( handler ) ; return ; } this . _eyeGazeEventHandlerByType . set ( type , new Array < EyeGazeEventHandler > ( ) ) ; this . _previousEyeGazeEventsByType . set ( type , new Array < EyeGazeEvent > ( ) ) ; const latest = new Array < EyeGazeEvent > ( ) ; this . _latestEyeGazeEventsByType . set ( type , latest ) ; assert ( this . _eyeGazeEventProvider !== undefined , `expected valid eye gaze event provider` ) ; const observable = ( this . _eyeGazeEventProvider as EyeGazeEventProvider ) . observable ( type ) ; this . _subscriptions . push ( ( observable as Observable < EyeGazeEvent > ) . subscribe ( ( event ) => { latest . push ( event ) ; this . invalidate ( ) ; } ) ) ; ( this . _eyeGazeEventHandlerByType . get ( type ) as Array < EyeGazeEventHandler > ) . push ( handler ) ; } protected invokeEyeGazeEventHandler ( type ) { const handlers = this . _eyeGazeEventHandlerByType . get ( type ) ; if ( handlers === undefined || handlers . length === 0 ) { return ; } const latest = this . _latestEyeGazeEventsByType . get ( type ) as Array < EyeGazeEvent > ; if ( latest . length === 0 ) { return ; } const previous = this . _previousEyeGazeEventsByType . get ( type ) as Array < EyeGazeEvent > ; handlers . forEach ( ( handler ) => handler ( latest , previous ) ) ; Object . assign ( previous , latest ) ; latest . length = 0 ; } /**
     * Disposes all registered handlers of all event types.
     */ dispose ( ) { this . _latestMouseEventsByType . forEach ( ( value ) => value . length = 0 ) ; this . _previousMouseEventsByType . forEach ( ( value ) => value . length = 0 ) ; this . _latestTouchEventsByType . forEach ( ( value ) => value . length = 0 ) ; this . _previousTouchEventsByType . forEach ( ( value ) => value . length = 0 ) ; this . _latestPointerEventsByType . forEach ( ( value ) => value . length = 0 ) ; this . _previousPointerEventsByType . forEach ( ( value ) => value . length = 0 ) ; this . _previousEyeGazeEventsByType . forEach ( ( value ) => value . length = 0 ) ; this . _latestEyeGazeEventsByType . forEach ( ( value ) => value . length = 0 ) ; for ( const subscription of this . _subscriptions ) { subscription . unsubscribe ( ) ; } } /**
     * Triggers (by means of a helper function) invocation of all registered handler of all event types.
     */ update ( ) { this . invokeMouseEventHandler ( MouseEventProvider . Type . Click ) ; this . invokeMouseEventHandler ( MouseEventProvider . Type . Enter ) ; this . invokeMouseEventHandler ( MouseEventProvider . Type . Leave ) ; this . invokeMouseEventHandler ( MouseEventProvider . Type . Down ) ; this . invokeMouseEventHandler ( MouseEventProvider . Type . Up ) ; this . invokeMouseEventHandler ( MouseEventProvider . Type . Move ) ; this . invokeMouseEventHandler ( MouseEventProvider . Type . Wheel ) ; this . invokeTouchEventHandler ( TouchEventProvider . Type . Start ) ; this . invokeTouchEventHandler ( TouchEventProvider . Type . End ) ; this . invokeTouchEventHandler ( TouchEventProvider . Type . Move ) ; this . invokeTouchEventHandler ( TouchEventProvider . Type . Cancel ) ; this . invokePointerEventHandler ( PointerEventProvider . Type . Move ) ; this . invokePointerEventHandler ( PointerEventProvider . Type . Down ) ; this . invokePointerEventHandler ( PointerEventProvider . Type . Enter ) ; this . invokePointerEventHandler ( PointerEventProvider . Type . Up ) ; this . invokePointerEventHandler ( PointerEventProvider . Type . Leave ) ; this . invokePointerEventHandler ( PointerEventProvider . Type . Cancel ) ; this . invokeEyeGazeEventHandler ( EyeGazeEventProvider . Type . EyeGazeData ) ; this . invokeEyeGazeEventHandler ( EyeGazeEventProvider . Type . NewServerMessage ) ; this . invokeEyeGazeEventHandler ( EyeGazeEventProvider . Type . ConnectionStatus ) ; this . invokeEyeGazeEventHandler ( EyeGazeEventProvider . Type . BinaryMessageParsingError ) ; } param event param normalize returns offsets ( event , normalize = true ) { const offsets = new Array < vec2 > ( ) ; let chromeWorkaround = false ; if ( event instanceof MouseEvent ) { const e = event as MouseEvent ; chromeWorkaround = ( e . offsetX !== 0 && e . offsetY !== 0 ) ; offsets . push ( chromeWorkaround ? vec2 . fromValues ( e . offsetX , e . offsetY ) : vec2 . fromValues ( e . clientX , e . clientY ) ) ; } else if ( event instanceof WheelEvent ) { const e = event as WheelEvent ; chromeWorkaround = ( e . offsetX !== 0 && e . offsetY !== 0 ) ; offsets . push ( chromeWorkaround ? vec2 . fromValues ( e . offsetX , e . offsetY ) : vec2 . fromValues ( e . clientX , e . clientY ) ) ; } else if ( event instanceof TouchEvent ) { const e = event as TouchEvent ; for ( let index = 0 ; index < e . touches . length ; ++ index ) { const touch = e . touches . item ( index ) ! ; offsets . push ( vec2 . fromValues ( touch . clientX , touch . clientY ) ) ; } } if ( chromeWorkaround ) { if ( normalize ) { for ( const offset of offsets ) { vec2 . scale ( offset , offset , window . devicePixelRatio ) ; } } return offsets ; } const target = event . target || event . currentTarget || event . srcElement ; const rect = ( target as HTMLElement ) . getBoundingClientRect ( ) ; for ( const offset of offsets ) { offset [ 0 ] = Math . floor ( offset [ 0 ] - rect . left ) ; offset [ 1 ] = Math . floor ( offset [ 1 ] - rect . top ) ; if ( normalize ) { vec2 . scale ( offset , offset , window . devicePixelRatio ) ; } } return offsets ; } param handler pushClickHandler ( handler ) { this . pushMouseEventHandler ( MouseEventProvider . Type . Click , handler ) ; } param handler pushMouseEnterHandler ( handler ) { this . pushMouseEventHandler ( MouseEventProvider . Type . Enter , handler ) ; } param handler pushMouseLeaveHandler ( handler ) { this . pushMouseEventHandler ( MouseEventProvider . Type . Leave , handler ) ; } param handler pushMouseDownHandler ( handler ) { this . pushMouseEventHandler ( MouseEventProvider . Type . Down , handler ) ; } param handler pushMouseUpHandler ( handler ) { this . pushMouseEventHandler ( MouseEventProvider . Type . Up , handler ) ; } param handler pushMouseMoveHandler ( handler ) { this . pushMouseEventHandler ( MouseEventProvider . Type . Move , handler ) ; } param handler pushMouseWheelHandler ( handler ) { this . pushMouseEventHandler ( MouseEventProvider . Type . Wheel , handler ) ; } param handler pushTouchStartHandler ( handler ) { this . pushTouchEventHandler ( TouchEventProvider . Type . Start , handler ) ; } param handler pushTouchEndHandler ( handler ) { this . pushTouchEventHandler ( TouchEventProvider . Type . End , handler ) ; } param handler pushTouchMoveHandler ( handler ) { this . pushTouchEventHandler ( TouchEventProvider . Type . Move , handler ) ; } param handler pushTouchCancelHandler ( handler ) { this . pushTouchEventHandler ( TouchEventProvider . Type . Cancel , handler ) ; } param handler pushPointerUpHandler ( handler ) { this . pushPointerEventHandler ( PointerEventProvider . Type . Up , handler ) ; } param handler pushPointerDownHandler ( handler ) { this . pushPointerEventHandler ( PointerEventProvider . Type . Down , handler ) ; } param handler pushPointerEnterHandler ( handler ) { this . pushPointerEventHandler ( PointerEventProvider . Type . Enter , handler ) ; } param handler pushPointerLeaveHandler ( handler ) { this . pushPointerEventHandler ( PointerEventProvider . Type . Leave , handler ) ; } param handler pushPointerMoveHandler ( handler ) { this . pushPointerEventHandler ( PointerEventProvider . Type . Move , handler ) ; } param handler pushPointerCancelHandler ( handler ) { this . pushPointerEventHandler ( PointerEventProvider . Type . Cancel , handler ) ; } param handler pushEyeGazeDataHandler ( handler ) { this . pushEyeGazeEventHandler ( EyeGazeEventProvider . Type . EyeGazeData , handler ) ; } param handler pushEyeGazeServerMessageHandler ( handler ) { this . pushEyeGazeEventHandler ( EyeGazeEventProvider . Type . NewServerMessage , handler ) ; } param handler pushEyeGazeConnectionStatusHandler ( handler ) { this . pushEyeGazeEventHandler ( EyeGazeEventProvider . Type . ConnectionStatus , handler ) ; } param handler pushEyeGazeBinaryMessageParsingErrorHandler ( handler ) { this . pushEyeGazeEventHandler ( EyeGazeEventProvider . Type . BinaryMessageParsingError , handler ) ; } /**
     * Forward pointer lock request to the mouse event provider (if one exists).
     */ requestPointerLock ( ) { if ( this . _mouseEventProvider ) { this . _mouseEventProvider . pointerLock = true ; } } /**
     * Forward pointer lock release request to the mouse event provider (if one exists).
     */ exitPointerLock ( ) { if ( this . _mouseEventProvider ) { this . _mouseEventProvider . pointerLock = false ; } } }