import type { Armature , Bone , Pose } from '../../armature/index' ; import { Transform } from '../../maths' ; import { vec3 , quat } from 'gl-matrix' ; import Vec3Util from '../../maths/Vec3Util' ; class IKLink { idx : number ; pidx : number ; len : number ; bind : Transform = new Transform ( ) ; effectorDir : vec3 = [ 0 , 1 , 0 ] ; poleDir : vec3 = [ 0 , 0 , 1 ] ; constructor ( idx , len ) { this . idx = idx ; this . pidx = - 1 ; this . len = len ; } static fromBone ( b ) { const l = new IKLink ( b . idx , b . len ) ; l . bind . copy ( b . local ) ; l . pidx = b . pidx ; return l ; } } class IKChain { links : IKLink [ ] = [ ] ; solver : any = null ; count : number = 0 ; length : number = 0 ; constructor ( bName ? , arm ? ) { if ( bName && arm ) this . setBones ( bName , arm ) ; } addBone ( b ) { this . length += b . len ; this . links . push ( IKLink . fromBone ( b ) ) ; this . count ++ ; return this ; } setBones ( bNames , arm ) { let b ; let n ; this . length = 0 ; for ( n of bNames ) { b = arm . getBone ( n ) ; if ( b ) { this . length += b . len ; this . links . push ( IKLink . fromBone ( b ) ) ; } else console . log ( 'Chain.setBones - Bone Not Found:' , n ) ; } this . count = this . links . length ; return this ; } setSolver ( s ) { this . solver = s ; return this ; } bindToPose ( pose ) { let lnk ; for ( lnk of this . links ) { lnk . bind . copy ( pose . bones [ lnk . idx ] . local ) ; } return this ; } /** For usecase when bone lengths have been recomputed for a pose which differs from the initial armature */ resetLengths ( pose ) { let lnk ; let len ; this . length = 0 ; for ( lnk of this . links ) { len = pose . bones [ lnk . idx ] . len ; lnk . len = len ; this . length += len ; } } first ( ) { return this . links [ 0 ] ; } last ( ) { return this . links [ this . count - 1 ] ; } getEndPositions ( pose ) { let rtn = [ ] ; if ( this . count != 0 ) rtn . push ( Vec3Util . toArray ( pose . bones [ this . links [ 0 ] . idx ] . world . pos ) as vec3 ) ; if ( this . count > 1 ) { const lnk = this . last ( ) ; const v = vec3 . fromValues ( 0 , lnk . len , 0 ) ; pose . bones [ lnk . idx ] . world . transformVec3 ( v ) ; rtn . push ( Vec3Util . toArray ( v ) as vec3 ) ; } return rtn ; } getPositionAt ( pose , idx ) { const b = pose . bones [ this . links [ idx ] . idx ] ; return Vec3Util . toArray ( b . world . pos ) as vec3 ; } getAllPositions ( pose ) { const rtn = [ ] ; let lnk ; for ( lnk of this . links ) { rtn . push ( Vec3Util . toArray ( pose . bones [ lnk . idx ] . world . pos ) as vec3 ) ; } lnk = this . links [ this . count - 1 ] ; const v = vec3 . fromValues ( 0 , lnk . len , 0 ) ; pose . bones [ lnk . idx ] . world . transformVec3 ( v ) ; rtn . push ( Vec3Util . toArray ( v ) as vec3 ) ; return rtn ; } getStartPosition ( pose ) { const b = pose . bones [ this . links [ 0 ] . idx ] ; return Vec3Util . toArray ( b . world . pos ) as vec3 ; } getMiddlePosition ( pose ) { if ( this . count == 2 ) { const b = pose . bones [ this . links [ 1 ] . idx ] ; return Vec3Util . toArray ( b . world . pos ) as vec3 ; } console . warn ( 'TODO: Implemenet IKChain.getMiddlePosition' ) ; return [ 0 , 0 , 0 ] ; } getLastPosition ( pose ) { const b = pose . bones [ this . links [ this . count - 1 ] . idx ] ; return Vec3Util . toArray ( b . world . pos ) as vec3 ; } getTailPosition ( pose , ignoreScale = false ) { const b = pose . bones [ this . links [ this . count - 1 ] . idx ] ; const v = vec3 . fromValues ( 0 , b . len , 0 ) ; if ( ! ignoreScale ) return Vec3Util . toArray ( b . world . transformVec3 ( v ) ) as vec3 ; vec3 . transformQuat ( v , v , b . world . rot ) ; vec3 . add ( v , v , b . world . pos ) ; return Vec3Util . toArray ( v ) as vec3 ; } getAltDirections ( pose , idx = 0 ) { const lnk = this . links [ idx ] ; const b = pose . bones [ lnk . idx ] ; const eff = lnk . effectorDir . slice ( 0 ) as vec3 ; const pol = lnk . poleDir . slice ( 0 ) as vec3 ; vec3 . transformQuat ( eff as vec3 , eff as vec3 , b . world . rot ) ; vec3 . transformQuat ( pol as vec3 , pol as vec3 , b . world . rot ) ; return [ eff , pol ] ; } bindAltDirections ( pose , effectorDir , poleDir ) { let l ; let v = vec3 . create ( ) ; let inv = quat . create ( ) ; for ( l of this . links ) { quat . invert ( inv , pose . bones [ l . idx ] . world . rot ) ; vec3 . transformQuat ( v , effectorDir , inv ) ; vec3 . copy ( l . effectorDir , v ) ; vec3 . transformQuat ( v , poleDir , inv ) ; vec3 . copy ( l . poleDir , v ) ; } return this ; } setAltDirections ( effectorDir , poleDir ) { let l ; for ( l of this . links ) { vec3 . copy ( l . effectorDir , effectorDir ) ; vec3 . copy ( l . poleDir , poleDir ) ; } return this ; } resolveToPose ( pose , debug ? ) { if ( ! this . solver ) { console . warn ( 'Chain.resolveToPose - Missing Solver' ) ; return this ; } this . solver . resolve ( this , pose , debug ) ; return this ; } } export { IKChain , IKLink } ;