void Key() { float value = (float)(file.read_double()); float time = (float)(file.read_double()); Shape spantype = (Shape)file.read_int(); float p1 = (float)(file.read_double()); float p2 = (float)(file.read_double()); float p3 = (float)(file.read_double()); float p4 = (float)(file.read_double()); float p5 = (float)(file.read_double()); float p6 = (float)(file.read_double()); var channel_key = new LWChannelKey( value, time, spantype, p1, p2, p3, p4, p5, p6 ); currentEnvelope.insert(channel_key); }
// Interpolate the value of a BEZ2 curve. public static float bez2(LWChannelKey key0, LWChannelKey key1, float time) { float t0 = 0.0f; float t1 = 1.0f; float x = key0.Time + ((key0.Shape == Shape.Bezier2D) ? key0.p3 : (key1.Time - key0.Time) / 3.0f); float t = bez2_time(key0.Time, x, key1.Time + key1.p1, key1.Time, key0.Time, ref t0, ref t1); float y = key0.Value + ((key0.Shape == Shape.Bezier2D) ? key0.p4 : key0.p2 / 3.0f); return(bezier(key0.Value, y, key1.p2 + key1.Value, key1.Value, t)); }
public void insert(LWChannelKey channel_key) { keys.Add(channel_key); steps++; }
/* Given a list of keys and a time, returns the interpolated value of the * envelope at that time. */ public float eval(float time) { if (time == last_time) { return(last_value); } else { last_time = time; } float t; float @in; float @out; float offset = 0.0f; int noff; if (keys.Count == 0) { last_value = 0; return(last_value); } // If there's only one key, the value is constant if (keys.Count == 1) { last_value = keys.First().Value; return(last_value); } // Get the first key LWChannelKey skey = keys.First(); LWChannelKey ekey = keys.Last(); LWChannelKey next = (keys.Count > 1) ? keys[1] : null; LWChannelKey prev = (keys.Count > 1) ? keys[keys.Count - 2] : null; // Use pre-behavior if time is before first key time if (time < skey.Time) { switch (pre_behavior) { case Behavior.Reset: { last_value = 0; return(last_value); } case Behavior.Constant: { last_value = skey.Value; return(last_value); } case Behavior.Repeat: { int dummy; last_time = time = range(time, skey.Time, ekey.Time, out dummy); break; } case Behavior.Oscillate: { last_time = time = range(time, skey.Time, ekey.Time, out noff); if ((noff % 2) != 0) { last_time = time = ekey.Time - skey.Time - time; } break; } case Behavior.OffsetRepeat: { last_time = time = range(time, skey.Time, ekey.Time, out noff); offset = noff * (ekey.Value - skey.Value); break; } case Behavior.Linear: { @out = outgoing(null, skey, next) / (next.Time - skey.Time); last_value = @out * (time - skey.Time) + skey.Value;; return(last_value); } default: { last_value = 0; return(last_value); } } } // Use post-behavior if time is after last key time else if (time > ekey.Time) { switch (post_behavior) { case Behavior.Reset: { last_value = 0; return(last_value); } case Behavior.Constant: { last_value = ekey.Value; return(last_value); } case Behavior.Repeat: { int dummy; time = range(time, skey.Time, ekey.Time, out dummy); break; } case Behavior.Oscillate: { last_time = time = range(time, skey.Time, ekey.Time, out noff); if ((noff % 2) != 0) { last_time = time = ekey.Time - skey.Time - time; } break; } case Behavior.OffsetRepeat: { last_time = time = range(time, skey.Time, ekey.Time, out noff); offset = noff * (ekey.Value - skey.Value); break; } case Behavior.Linear: { @in = incoming(prev, ekey, null) / (ekey.Time - prev.Time); last_value = @in * (time - ekey.Time) + ekey.Value; return(last_value); } default: { last_value = 0; return(last_value); } } } // Get the endpoints of the interval being evaluated //k_it = keys.begin(); prev = null; LWChannelKey key0 = null; LWChannelKey key1 = null; next = null; int i; for (i = 0; i < keys.Count; ++i) { prev = key0; key0 = key1; key1 = keys[i]; if (time <= key1.Time) { if (key0 == null) { key0 = key1; if (i + 1 < keys.Count) { key1 = keys[i + 1]; } } break; } } if (i + 1 < keys.Count) { next = keys[i + 1]; } /* debug_msg( * "%9.4f <= %9.4f <= %9.4f", * key0 != NULL ? key0.time : 0, * time, * key1 != NULL ? key1.time : 0 * );*/ // Check for singularities first if (time == key0.Time) { last_value = key0.Value + offset; return(last_value); } else if (time == key1.Time) { last_value = key1.Value + offset; return(last_value); } if ((key0 == null) || (key1 == null)) { throw new System.Exception("Channel Interpolation error"); //return 0; } // Get interval length, time in [0, 1] t = (time - key0.Time) / (key1.Time - key0.Time); // Interpolate switch (key1.Shape) { case Shape.TCB: goto case Shape.Hermite; case Shape.Bezier1D: goto case Shape.Hermite; case Shape.Hermite: { @out = outgoing(prev, key0, key1); @in = incoming(key0, key1, next); float h1; float h2; float h3; float h4; hermite(t, out h1, out h2, out h3, out h4); last_value = h1 * key0.Value + h2 * key1.Value + h3 * @out + h4 * @in + offset; return(last_value); } case Shape.Bezier2D: { last_value = bez2(key0, key1, time) + offset; return(last_value); } case Shape.Linear: { last_value = key0.Value + t * (key1.Value - key0.Value) + offset; return(last_value); } case Shape.Stepped: { last_value = key0.Value + offset; return(last_value); } default: { last_value = offset; return(last_value); } } }
/* Return the incoming tangent to the curve at key1. The value returned * for the BEZ2 case is used when extrapolating a linear post behavior. */ public static float incoming(LWChannelKey key0, LWChannelKey key1, LWChannelKey next) { float a; float b; float d; float t; float @in; switch (key1.Shape) { case Shape.Linear: { d = key1.Value - key0.Value; if (next != null) { t = (key1.Time - key0.Time) / (next.Time - key0.Time); @in = t * (next.Value - key1.Value + d); } else { @in = d; } break; } case Shape.TCB: { a = (1.0f - key1.Tension) * (1.0f - key1.Continuity) * (1.0f + key1.Bias); b = (1.0f - key1.Tension) * (1.0f + key1.Continuity) * (1.0f - key1.Bias); d = key1.Value - key0.Value; if (next != null) { t = (key1.Time - key0.Time) / (next.Time - key0.Time); @in = t * (b * (next.Value - key1.Value) + a * d); } else { @in = a * d; } break; } case Shape.Bezier1D: goto case Shape.Hermite; case Shape.Hermite: { @in = key1.p4; if (next != null) { @in *= (key1.Time - key0.Time) / (next.Time - key0.Time); } break; } case Shape.Bezier2D: { @in = key1.p2 * (key1.Time - key0.Time); if (System.Math.Abs(key1.p1) > 1e-5f) { @in /= key1.p1; } else { @in *= 1e5f; } break; } case Shape.Stepped: goto default; default: { @in = 0.0f; break; } } return(@in); }
/* Return the outgoing tangent to the curve at key0. The value returned * for the BEZ2 case is used when extrapolating a linear pre behavior and * when interpolating a non-BEZ2 span. */ public float outgoing(LWChannelKey prev, LWChannelKey key0, LWChannelKey key1) { float a; float b; float d; float t; float @out; switch (key0.Shape) { case Shape.TCB: { a = (1.0f - key0.Tension) * (1.0f + key0.Continuity) * (1.0f + key0.Bias); b = (1.0f - key0.Tension) * (1.0f - key0.Continuity) * (1.0f - key0.Bias); d = key1.Value - key0.Value; if (prev != null) { t = (key1.Time - key0.Time) / (key1.Time - prev.Time); @out = t * (a * (key0.Value - prev.Value) + b * d); } else { @out = b * d; } break; } case Shape.Linear: { d = key1.Value - key0.Value; if (prev != null) { t = (key1.Time - key0.Time) / (key1.Time - prev.Time); @out = t * (key0.Value - prev.Value + d); } else { @out = d; } break; } case Shape.Bezier1D: goto case Shape.Hermite; case Shape.Hermite: { @out = key0.p5; if (prev != null) { @out *= (key1.Time - key0.Time) / (key1.Time - prev.Time); } break; } case Shape.Bezier2D: { @out = key0.p4 * (key1.Time - key0.Time); if (System.Math.Abs(key0.p3) > 1e-5f) { @out /= key0.p3; } else { @out *= 1e5f; } break; } case Shape.Stepped: goto default; default: { @out = 0.0f; break; } } return(@out); }