/**
 * Just to track changes for a single touch event, we create this state:
 */ export class TouchState { x : number ; y : number ; lastX : number ; lastY : number ; id : any ; createdAt : number ; constructor ( touch ) { this . x = touch . clientX ; this . y = touch . clientY ; this . lastX = this . x ; this . lastY = this . y ; this . id = touch . identifier ; this . createdAt = Date . now ( ) ; } move ( touch ) { this . lastX = this . x ; this . lastY = this . y ; this . x = touch . clientX ; this . y = touch . clientY ; } } const UNKNOWN = 0 ; const SCALE = 1 ; const ROTATE = 2 ; const INCLINE = 3 ; /**
 * This state is used to detect gestures. It answers the questions:
 * - Should we scale with this gesture?
 * - Should we rotate with this gesture?
 * - Should we change incline with this gesture?
 */ export class MultiTouchState { allowRotation : boolean ; state : number ; canRotate : boolean ; canScale : boolean ; canIncline : boolean ; first : any ; second : any ; stateChanged : boolean ; constructor ( allowRotation ) { this . allowRotation = allowRotation ; this . state = UNKNOWN ; this . canRotate = false ; this . canScale = false ; this . canIncline = false ; this . first = undefined ; this . second = undefined ; this . stateChanged = false ; } reset ( ) { this . state = UNKNOWN ; this . canRotate = false ; this . canScale = false ; this . canIncline = false ; this . first = undefined ; this . second = undefined ; } isUnknown ( ) { return this . state === UNKNOWN ; } track ( first , second ) { this . stateChanged = false ; if ( this . state !== UNKNOWN ) return ; if ( ! ( this . first && this . second ) ) { this . first = { id : first . id , x : first . x , y : first . y } ; this . second = { id : second . id , x : second . x , y : second . y } ; return ; } let originalFirst = this . first ; let originalSecond = this . second ; if ( first . id === originalSecond . id && second . id === originalFirst . id ) { let t = originalFirst ; originalFirst = originalSecond ; originalSecond = t ; } let dfy = originalFirst . y - originalSecond . y ; let dfx = originalFirst . x - originalSecond . x ; let dcy = first . y - second . y ; let dcx = first . x - second . x ; let scaleChange = Math . abs ( Math . hypot ( dfy , dfx ) - Math . hypot ( dcy , dcx ) ) ; let initialAngle = Math . atan2 ( dfy , dfx ) ; let angleChange = Math . abs ( initialAngle - Math . atan2 ( dcy , dcx ) ) ; initialAngle = ( Math . abs ( initialAngle ) * 180 ) / Math . PI ; let horizontalAngleInDegrees = 60 ; let isHorizontalLine = initialAngle < horizontalAngleInDegrees || 180 - initialAngle < horizontalAngleInDegrees ; if ( isHorizontalLine && this . allowRotation && Math . abs ( first . createdAt - second . createdAt ) < 100 ) { let vx = first . x - originalFirst . x + second . x - originalSecond . x ; let vy = first . y - originalFirst . y + second . y - originalSecond . y ; if ( Math . abs ( vx ) < 10 && Math . abs ( vy ) > 42 ) { this . canIncline = true ; } } if ( this . canIncline ) { this . canRotate = false ; this . canScale = false ; this . canIncline = true ; this . state = INCLINE ; } else if ( angleChange > 0.1 && this . allowRotation ) { this . canRotate = true ; this . canScale = true ; this . canIncline = false ; this . state = ROTATE ; } else if ( scaleChange > 15 ) { this . canRotate = false ; this . canScale = true ; this . canIncline = false ; this . state = SCALE ; } this . stateChanged = true ; } }