import { Nullable } from "babylonjs/types" ; import { Animation } from "babylonjs/Animations/animation" ; import { Observable } from "babylonjs/Misc/observable" ; import { KeyPointComponent } from "./graph/keyPoint" ; import { Scene } from "babylonjs/scene" ; import { IAnimatable } from "babylonjs/Animations/animatable.interface" ; import { AnimationGroup , TargetedAnimation } from "babylonjs/Animations/animationGroup" ; import { Animatable } from "babylonjs/Animations/animatable" ; import { AnimationKeyInterpolation } from "babylonjs/Animations/animationKey" ; export class Context { title : string ; animations : Nullable < Animation [ ] | TargetedAnimation [ ] > ; scene : Scene ; target : Nullable < IAnimatable > ; rootAnimationGroup : Nullable < AnimationGroup > ; activeAnimations : Animation [ ] = [ ] ; activeChannels : { [ key ] : string ; } = { } ; activeKeyPoints : Nullable < KeyPointComponent [ ] > ; mainKeyPoint : Nullable < KeyPointComponent > ; snippetId : string ; useTargetAnimations : boolean ; activeFrame : number ; fromKey : number ; toKey : number ; forwardAnimation = true ; isPlaying : boolean ; clipLength : number ; referenceMinFrame = 0 ; referenceMaxFrame = 100 ; focusedInput = false ; onActiveAnimationChanged = new Observable < void > ( ) ; onActiveKeyPointChanged = new Observable < void > ( ) ; onHostWindowResized = new Observable < void > ( ) ; onSelectAllKeys = new Observable < void > ( ) ; onActiveKeyFrameChanged = new Observable < number > ( ) ; onFrameSet = new Observable < number > ( ) ; onFrameManuallyEntered = new Observable < number > ( ) ; onMainKeyPointSet = new Observable < void > ( ) ; onMainKeyPointMoved = new Observable < void > ( ) ; onValueSet = new Observable < number > ( ) ; onValueManuallyEntered = new Observable < number > ( ) ; onFrameRequired = new Observable < void > ( ) ; onCreateOrUpdateKeyPointRequired = new Observable < void > ( ) ; onFlattenTangentRequired = new Observable < void > ( ) ; onLinearTangentRequired = new Observable < void > ( ) ; onBreakTangentRequired = new Observable < void > ( ) ; onUnifyTangentRequired = new Observable < void > ( ) ; onStepTangentRequired = new Observable < void > ( ) ; onDeleteAnimation = new Observable < Animation > ( ) ; onGraphMoved = new Observable < number > ( ) ; onGraphScaled = new Observable < number > ( ) ; onRangeUpdated = new Observable < void > ( ) ; onMoveToFrameRequired = new Observable < number > ( ) ; onAnimationStateChanged = new Observable < void > ( ) ; onDeleteKeyActiveKeyPoints = new Observable < void > ( ) ; onSelectionRectangleMoved = new Observable < DOMRect > ( ) ; onAnimationsLoaded = new Observable < void > ( ) ; onEditAnimationRequired = new Observable < Animation > ( ) ; onEditAnimationUIClosed = new Observable < void > ( ) ; onClipLengthIncreased = new Observable < number > ( ) ; onClipLengthDecreased = new Observable < number > ( ) ; onInterpolationModeSet = new Observable < { keyId : number ; value : AnimationKeyInterpolation ; } > ( ) ; onSelectToActivated = new Observable < { from : number ; to : number ; } > ( ) ; lockLastFrameValue : boolean = false ; lockLastFrameFrame : boolean = false ; onActiveKeyDataChanged = new Observable < number > ( ) ; public prepare ( ) { this . isPlaying = false ; if ( ! this . animations || ! this . animations . length ) { return ; } const animation = this . useTargetAnimations ? ( this . animations [ 0 ] as TargetedAnimation ) . animation : ( this . animations [ 0 ] as Animation ) ; const keys = animation . getKeys ( ) ; this . fromKey = keys [ 0 ] . frame ; this . toKey = keys [ keys . length - 1 ] . frame ; this . referenceMinFrame = 0 ; this . referenceMaxFrame = this . toKey ; this . snippetId = animation . snippetId ; this . clipLength = this . referenceMaxFrame ; if ( ! animation || ! animation . hasRunningRuntimeAnimations ) { return ; } this . isPlaying = true ; } public play ( forward ) { this . isPlaying = true ; this . scene . stopAnimation ( this . target ) ; let animatable ; if ( forward ) { if ( this . rootAnimationGroup ) { this . rootAnimationGroup . start ( true , 1.0 , this . fromKey , this . toKey ) ; } else { animatable = this . scene . beginAnimation ( this . target , this . fromKey , this . toKey , true ) ; } } else { if ( this . rootAnimationGroup ) { this . rootAnimationGroup . start ( true , 1.0 , this . toKey , this . fromKey ) ; } else { animatable = this . scene . beginAnimation ( this . target , this . toKey , this . fromKey , true ) ; } } this . forwardAnimation = forward ; if ( this . rootAnimationGroup ) { this . rootAnimationGroup . goToFrame ( this . activeFrame ) ; } else { animatable ! . goToFrame ( this . activeFrame ) ; } this . onAnimationStateChanged . notifyObservers ( ) ; } public stop ( ) { this . isPlaying = false ; if ( this . rootAnimationGroup ) { this . rootAnimationGroup . stop ( ) ; } else { this . scene . stopAnimation ( this . target ) ; } this . onAnimationStateChanged . notifyObservers ( ) ; } public moveToFrame ( frame ) { if ( ! this . animations || ! this . animations . length ) { return ; } this . activeFrame = frame ; if ( ! this . isPlaying ) { if ( this . rootAnimationGroup ) { this . rootAnimationGroup . start ( false , 1.0 , this . fromKey , this . toKey ) ; } else { this . scene . beginAnimation ( this . target , this . fromKey , this . toKey , false ) ; } } for ( var animationEntry of this . animations ) { const animation = this . useTargetAnimations ? ( animationEntry as TargetedAnimation ) . animation : ( animationEntry as Animation ) ; if ( ! animation . hasRunningRuntimeAnimations ) { return ; } for ( var runtimeAnimation of animation . runtimeAnimations ) { runtimeAnimation . goToFrame ( frame ) ; } } this . stop ( ) ; } public refreshTarget ( ) { if ( ! this . animations || ! this . animations . length ) { return ; } if ( this . isPlaying ) { return ; } this . moveToFrame ( this . activeFrame ) ; } public clearSelection ( ) { this . activeKeyPoints = [ ] ; this . onActiveKeyPointChanged . notifyObservers ( ) ; } public enableChannel ( animation , color ) { this . activeChannels [ animation . uniqueId ] = color ; } public disableChannel ( animation ) { delete this . activeChannels [ animation . uniqueId ] ; } public isChannelEnabled ( animation , color ) { return this . activeChannels [ animation . uniqueId ] === undefined || this . activeChannels [ animation . uniqueId ] === color ; } public getActiveChannel ( animation ) { return this . activeChannels [ animation . uniqueId ] ; } public resetAllActiveChannels ( ) { this . activeChannels = { } ; } public getAnimationSortIndex ( animation ) { if ( ! this . animations ) { return - 1 ; } for ( var index = 0 ; index < this . animations ?. length ; index ++ ) { if ( animation === ( this . useTargetAnimations ? ( this . animations [ 0 ] as TargetedAnimation ) . animation : ( this . animations [ index ] as Animation ) ) ) { return index ; } } return - 1 ; } public getPrevKey ( ) { if ( ! this . animations || ! this . animations . length || this . activeAnimations . length === 0 ) { return null ; } let prevKey = - Number . MAX_VALUE ; for ( var animation of this . activeAnimations ) { const keys = animation . getKeys ( ) ; for ( var key of keys ) { if ( key . frame < this . activeFrame && key . frame > prevKey ) { prevKey = key . frame ; } } } if ( prevKey === - Number . MAX_VALUE ) { prevKey = this . fromKey ; } return prevKey ; } public getNextKey ( ) { if ( ! this . animations || ! this . animations . length ) { return null ; } let nextKey = Number . MAX_VALUE ; for ( var animation of this . activeAnimations ) { const keys = animation . getKeys ( ) ; for ( var key of keys ) { if ( key . frame > this . activeFrame && key . frame < nextKey ) { nextKey = key . frame ; } } } if ( nextKey === Number . MAX_VALUE ) { nextKey = this . toKey ; } return nextKey ; } /**
     * If any current active animation has a key at the received frameNumber,
     * return the index of the animation in the active animation array, and
     * the index of the frame on the animation.
     */ public getKeyAtAnyFrameIndex ( frameNumber ) { if ( ! this . animations || ! this . animations . length || ! this . activeAnimations || ! this . activeAnimations . length ) { return null ; } let animIdx = 0 ; for ( let animation of this . activeAnimations ) { const keys = animation . getKeys ( ) ; let idx = 0 ; for ( let key of keys ) { if ( Math . floor ( frameNumber - key . frame ) === 0 ) { return { animationIndex : animIdx , keyIndex : idx } ; } idx ++ ; } animIdx ++ ; } return null ; } }