float lwEvalEnvelope( lwEnvelope *env, float time ) { lwKey *key0, *key1, *skey, *ekey; float t, h1, h2, h3, h4, in, out, offset = 0.0f; int noff; /* if there's no key, the value is 0 */ if ( env->nkeys == 0 ) return 0.0f; /* if there's only one key, the value is constant */ if ( env->nkeys == 1 ) return env->key->value; /* find the first and last keys */ skey = ekey = env->key; while ( ekey->next ) ekey = ekey->next; /* use pre-behavior if time is before first key time */ if ( time < skey->time ) { switch ( env->behavior[ 0 ] ) { case BEH_RESET: return 0.0f; case BEH_CONSTANT: return skey->value; case BEH_REPEAT: time = range( time, skey->time, ekey->time, NULL ); break; case BEH_OSCILLATE: time = range( time, skey->time, ekey->time, &noff ); if ( noff % 2 ) time = ekey->time - skey->time - time; break; case BEH_OFFSET: time = range( time, skey->time, ekey->time, &noff ); offset = noff * ( ekey->value - skey->value ); break; case BEH_LINEAR: switch ( skey->shape ) { case SHAPE_STEP: return skey->value; case SHAPE_LINE: return ( skey->value - skey->next->value ) / ( skey->time - skey->next->time ) * ( time - skey->next->time ) + skey->next->value; case SHAPE_TCB: case SHAPE_HERM: case SHAPE_BEZI: out = outgoing( skey, (Key*)(skey->next) ) / ( skey->next->time - skey->time ); return out * ( time - skey->time ) + skey->value; case SHAPE_BEZ2: return ( skey->param[ 1 ] - skey->param[ 3 ] ) / ( skey->param[ 0 ] - skey->param[ 2 ] ) * ( time - skey->time ) + skey->value; } } } /* use post-behavior if time is after last key time */ else if ( time > ekey->time ) { switch ( env->behavior[ 1 ] ) { case BEH_RESET: return 0.0f; case BEH_CONSTANT: return ekey->value; case BEH_REPEAT: time = range( time, skey->time, ekey->time, NULL ); break; case BEH_OSCILLATE: time = range( time, skey->time, ekey->time, &noff ); if ( noff % 2 ) time = ekey->time - skey->time - time; break; case BEH_OFFSET: time = range( time, skey->time, ekey->time, &noff ); offset = noff * ( ekey->value - skey->value ); break; case BEH_LINEAR: switch ( ekey->shape ) { case SHAPE_STEP: return ekey->value; case SHAPE_LINE: return ( ekey->value - ekey->prev->value ) / ( ekey->time - ekey->prev->time ) * ( time - ekey->prev->time ) + ekey->prev->value; case SHAPE_TCB: case SHAPE_HERM: case SHAPE_BEZI: in = incoming( (Key*)(ekey->prev), (Key*)ekey ) / ( ekey->time - ekey->prev->time ); return in * ( time - ekey->time ) + ekey->value; case SHAPE_BEZ2: return ( ekey->param[ 3 ] - ekey->param[ 1 ] ) / ( ekey->param[ 2 ] - ekey->param[ 0 ] ) * ( time - ekey->time ) + ekey->value; } } } /* get the endpoints of the interval being evaluated */ key0 = env->key; while ( time > key0->next->time ) key0 = key0->next; key1 = key0->next; /* check for singularities first */ if ( time == key0->time ) return key0->value + offset; else if ( time == key1->time ) return key1->value + offset; /* get interval length, time in [0, 1] */ t = ( time - key0->time ) / ( key1->time - key0->time ); /* interpolate */ switch ( key1->shape ) { case SHAPE_TCB: case SHAPE_BEZI: case SHAPE_HERM: out = outgoing( key0, (Key*)key1 ); in = incoming( (Key*)key0, (Key*)key1 ); hermite( t, &h1, &h2, &h3, &h4 ); return h1 * key0->value + h2 * key1->value + h3 * out + h4 * in + offset; case SHAPE_BEZ2: return bez2( (Key*)key0, (Key*)key1, time ) + offset; case SHAPE_LINE: return key0->value + t * ( key1->value - key0->value ) + offset; case SHAPE_STEP: return key0->value + offset; default: return offset; } }
float bez3(float p0, float p1, float p2, float p3, float out){ return bez2(lerp(p0, p1, out), lerp(p1, p2, out), lerp(p2, p3, out), out); }