/* =========================================================================== Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). Doom 3 Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 Source Code. If not, see . In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #ifndef __MATH_CURVE_H__ #define __MATH_CURVE_H__ #include "idlib/containers/List.h" #include "idlib/math/Math.h" #include "idlib/math/Matrix.h" /* =============================================================================== Curve base template. =============================================================================== */ template< class type > class idCurve { public: idCurve( void ); virtual ~idCurve( void ); virtual int AddValue( const float time, const type &value ); virtual void RemoveIndex( const int index ) { values.RemoveIndex(index); times.RemoveIndex(index); changed = true; } virtual void Clear( void ) { values.Clear(); times.Clear(); currentIndex = -1; changed = true; } virtual type GetCurrentValue( const float time ) const; virtual type GetCurrentFirstDerivative( const float time ) const; virtual type GetCurrentSecondDerivative( const float time ) const; virtual bool IsDone( const float time ) const; int GetNumValues( void ) const { return values.Num(); } void SetValue( const int index, const type &value ) { values[index] = value; changed = true; } type GetValue( const int index ) const { return values[index]; } type * GetValueAddress( const int index ) { return &values[index]; } float GetTime( const int index ) const { return times[index]; } float GetLengthForTime( const float time ) const; float GetTimeForLength( const float length, const float epsilon = 0.1f ) const; float GetLengthBetweenKnots( const int i0, const int i1 ) const; void MakeUniform( const float totalTime ); void SetConstantSpeed( const float totalTime ); void ShiftTime( const float deltaTime ); void Translate( const type &translation ); protected: idList times; // knots idList values; // knot values mutable int currentIndex; // cached index for fast lookup mutable bool changed; // set whenever the curve changes int IndexForTime( const float time ) const; float TimeForIndex( const int index ) const; type ValueForIndex( const int index ) const; float GetSpeed( const float time ) const; float RombergIntegral( const float t0, const float t1, const int order ) const; }; /* ==================== idCurve::idCurve ==================== */ template< class type > ID_INLINE idCurve::idCurve( void ) { currentIndex = -1; changed = false; } /* ==================== idCurve::~idCurve ==================== */ template< class type > ID_INLINE idCurve::~idCurve( void ) { } /* ==================== idCurve::AddValue add a timed/value pair to the spline returns the index to the inserted pair ==================== */ template< class type > ID_INLINE int idCurve::AddValue( const float time, const type &value ) { int i; i = IndexForTime( time ); times.Insert( time, i ); values.Insert( value, i ); changed = true; return i; } /* ==================== idCurve::GetCurrentValue get the value for the given time ==================== */ template< class type > ID_INLINE type idCurve::GetCurrentValue( const float time ) const { int i; i = IndexForTime( time ); if ( i >= values.Num() ) { return values[values.Num() - 1]; } else { return values[i]; } } /* ==================== idCurve::GetCurrentFirstDerivative get the first derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve::GetCurrentFirstDerivative( const float time ) const { return ( values[0] - values[0] ); } /* ==================== idCurve::GetCurrentSecondDerivative get the second derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve::GetCurrentSecondDerivative( const float time ) const { return ( values[0] - values[0] ); } /* ==================== idCurve::IsDone ==================== */ template< class type > ID_INLINE bool idCurve::IsDone( const float time ) const { return ( time >= times[ times.Num() - 1 ] ); } /* ==================== idCurve::GetSpeed ==================== */ template< class type > ID_INLINE float idCurve::GetSpeed( const float time ) const { int i; float speed; type value; value = GetCurrentFirstDerivative( time ); for ( speed = 0.0f, i = 0; i < value.GetDimension(); i++ ) { speed += value[i] * value[i]; } return idMath::Sqrt( speed ); } /* ==================== idCurve::RombergIntegral ==================== */ template< class type > ID_INLINE float idCurve::RombergIntegral( const float t0, const float t1, const int order ) const { int i, j, k, m, n; float sum, delta; float *temp[2]; temp[0] = (float *) _alloca16( order * sizeof( float ) ); temp[1] = (float *) _alloca16( order * sizeof( float ) ); delta = t1 - t0; temp[0][0] = 0.5f * delta * ( GetSpeed( t0 ) + GetSpeed( t1 ) ); for ( i = 2, m = 1; i <= order; i++, m *= 2, delta *= 0.5f ) { // approximate using the trapezoid rule sum = 0.0f; for ( j = 1; j <= m; j++ ) { sum += GetSpeed( t0 + delta * ( j - 0.5f ) ); } // Richardson extrapolation temp[1][0] = 0.5f * ( temp[0][0] + delta * sum ); for ( k = 1, n = 4; k < i; k++, n *= 4 ) { temp[1][k] = ( n * temp[1][k-1] - temp[0][k-1] ) / ( n - 1 ); } for ( j = 0; j < i; j++ ) { temp[0][j] = temp[1][j]; } } return temp[0][order-1]; } /* ==================== idCurve::GetLengthBetweenKnots ==================== */ template< class type > ID_INLINE float idCurve::GetLengthBetweenKnots( const int i0, const int i1 ) const { float length = 0.0f; for ( int i = i0; i < i1; i++ ) { length += RombergIntegral( times[i], times[i+1], 5 ); } return length; } /* ==================== idCurve::GetLengthForTime ==================== */ template< class type > ID_INLINE float idCurve::GetLengthForTime( const float time ) const { float length = 0.0f; int index = IndexForTime( time ); for ( int i = 0; i < index; i++ ) { length += RombergIntegral( times[i], times[i+1], 5 ); } length += RombergIntegral( times[index], time, 5 ); return length; } /* ==================== idCurve::GetTimeForLength ==================== */ template< class type > ID_INLINE float idCurve::GetTimeForLength( const float length, const float epsilon ) const { int i, index; float *accumLength, totalLength, len0, len1, t, diff; if ( length <= 0.0f ) { return times[0]; } accumLength = (float *) _alloca16( values.Num() * sizeof( float ) ); totalLength = 0.0f; for ( index = 0; index < values.Num() - 1; index++ ) { totalLength += GetLengthBetweenKnots( index, index + 1 ); accumLength[index] = totalLength; if ( length < accumLength[index] ) { break; } } if ( index >= values.Num() - 1 ) { return times[times.Num() - 1]; } if ( index == 0 ) { len0 = length; len1 = accumLength[0]; } else { len0 = length - accumLength[index-1]; len1 = accumLength[index] - accumLength[index-1]; } // invert the arc length integral using Newton's method t = ( times[index+1] - times[index] ) * len0 / len1; for ( i = 0; i < 32; i++ ) { diff = RombergIntegral( times[index], times[index] + t, 5 ) - len0; if ( idMath::Fabs( diff ) <= epsilon ) { return times[index] + t; } t -= diff / GetSpeed( times[index] + t ); } return times[index] + t; } /* ==================== idCurve::MakeUniform ==================== */ template< class type > ID_INLINE void idCurve::MakeUniform( const float totalTime ) { int i, n; n = times.Num() - 1; for ( i = 0; i <= n; i++ ) { times[i] = i * totalTime / n; } changed = true; } /* ==================== idCurve::SetConstantSpeed ==================== */ template< class type > ID_INLINE void idCurve::SetConstantSpeed( const float totalTime ) { int i; float *length, totalLength, scale, t; length = (float *) _alloca16( values.Num() * sizeof( float ) ); totalLength = 0.0f; for ( i = 0; i < values.Num() - 1; i++ ) { length[i] = GetLengthBetweenKnots( i, i + 1 ); totalLength += length[i]; } scale = totalTime / totalLength; for ( t = 0.0f, i = 0; i < times.Num() - 1; i++ ) { times[i] = t; t += scale * length[i]; } times[times.Num() - 1] = totalTime; changed = true; } /* ==================== idCurve::ShiftTime ==================== */ template< class type > ID_INLINE void idCurve::ShiftTime( const float deltaTime ) { for ( int i = 0; i < times.Num(); i++ ) { times[i] += deltaTime; } changed = true; } /* ==================== idCurve::Translate ==================== */ template< class type > ID_INLINE void idCurve::Translate( const type &translation ) { for ( int i = 0; i < values.Num(); i++ ) { values[i] += translation; } changed = true; } /* ==================== idCurve::IndexForTime find the index for the first time greater than or equal to the given time ==================== */ template< class type > ID_INLINE int idCurve::IndexForTime( const float time ) const { int len, mid, offset, res; if ( currentIndex >= 0 && currentIndex <= times.Num() ) { // use the cached index if it is still valid if ( currentIndex == 0 ) { if ( time <= times[currentIndex] ) { return currentIndex; } } else if ( currentIndex == times.Num() ) { if ( time > times[currentIndex-1] ) { return currentIndex; } } else if ( time > times[currentIndex-1] && time <= times[currentIndex] ) { return currentIndex; } else if ( time > times[currentIndex] && ( currentIndex+1 == times.Num() || time <= times[currentIndex+1] ) ) { // use the next index currentIndex++; return currentIndex; } } // use binary search to find the index for the given time len = times.Num(); mid = len; offset = 0; res = 0; while( mid > 0 ) { mid = len >> 1; if ( time == times[offset+mid] ) { return offset+mid; } else if ( time > times[offset+mid] ) { offset += mid; len -= mid; res = 1; } else { len -= mid; res = 0; } } currentIndex = offset+res; return currentIndex; } /* ==================== idCurve::ValueForIndex get the value for the given time ==================== */ template< class type > ID_INLINE type idCurve::ValueForIndex( const int index ) const { int n = values.Num()-1; if ( index < 0 ) { return values[0] + index * ( values[1] - values[0] ); } else if ( index > n ) { return values[n] + ( index - n ) * ( values[n] - values[n-1] ); } return values[index]; } /* ==================== idCurve::TimeForIndex get the value for the given time ==================== */ template< class type > ID_INLINE float idCurve::TimeForIndex( const int index ) const { int n = times.Num()-1; if ( index < 0 ) { return times[0] + index * ( times[1] - times[0] ); } else if ( index > n ) { return times[n] + ( index - n ) * ( times[n] - times[n-1] ); } return times[index]; } /* =============================================================================== Bezier Curve template. The degree of the polynomial equals the number of knots minus one. =============================================================================== */ template< class type > class idCurve_Bezier : public idCurve { public: idCurve_Bezier( void ); virtual type GetCurrentValue( const float time ) const; virtual type GetCurrentFirstDerivative( const float time ) const; virtual type GetCurrentSecondDerivative( const float time ) const; protected: void Basis( const int order, const float t, float *bvals ) const; void BasisFirstDerivative( const int order, const float t, float *bvals ) const; void BasisSecondDerivative( const int order, const float t, float *bvals ) const; }; /* ==================== idCurve_Bezier::idCurve_Bezier ==================== */ template< class type > ID_INLINE idCurve_Bezier::idCurve_Bezier( void ) { } /* ==================== idCurve_Bezier::GetCurrentValue get the value for the given time ==================== */ template< class type > ID_INLINE type idCurve_Bezier::GetCurrentValue( const float time ) const { int i; float *bvals; type v; bvals = (float *) _alloca16( this->values.Num() * sizeof( float ) ); Basis( this->values.Num(), time, bvals ); v = bvals[0] * this->values[0]; for ( i = 1; i < this->values.Num(); i++ ) { v += bvals[i] * this->values[i]; } return v; } /* ==================== idCurve_Bezier::GetCurrentFirstDerivative get the first derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_Bezier::GetCurrentFirstDerivative( const float time ) const { int i; float *bvals, d; type v; bvals = (float *) _alloca16( this->values.Num() * sizeof( float ) ); BasisFirstDerivative( this->values.Num(), time, bvals ); v = bvals[0] * this->values[0]; for ( i = 1; i < this->values.Num(); i++ ) { v += bvals[i] * this->values[i]; } d = ( this->times[this->times.Num()-1] - this->times[0] ); return ( (float) (this->values.Num()-1) / d ) * v; } /* ==================== idCurve_Bezier::GetCurrentSecondDerivative get the second derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_Bezier::GetCurrentSecondDerivative( const float time ) const { int i; float *bvals, d; type v; bvals = (float *) _alloca16( this->values.Num() * sizeof( float ) ); BasisSecondDerivative( this->values.Num(), time, bvals ); v = bvals[0] * this->values[0]; for ( i = 1; i < this->values.Num(); i++ ) { v += bvals[i] * this->values[i]; } d = ( this->times[this->times.Num()-1] - this->times[0] ); return ( (float) (this->values.Num()-2) * (this->values.Num()-1) / ( d * d ) ) * v; } /* ==================== idCurve_Bezier::Basis bezier basis functions ==================== */ template< class type > ID_INLINE void idCurve_Bezier::Basis( const int order, const float t, float *bvals ) const { int i, j, d; float *c, c1, c2, s, o, ps, po; bvals[0] = 1.0f; d = order - 1; if ( d <= 0 ) { return; } c = (float *) _alloca16( (d+1) * sizeof( float ) ); s = (float) ( t - this->times[0] ) / ( this->times[this->times.Num()-1] - this->times[0] ); o = 1.0f - s; ps = s; po = o; for ( i = 1; i < d; i++ ) { c[i] = 1.0f; } for ( i = 1; i < d; i++ ) { c[i-1] = 0.0f; c1 = c[i]; c[i] = 1.0f; for ( j = i+1; j <= d; j++ ) { c2 = c[j]; c[j] = c1 + c[j-1]; c1 = c2; } bvals[i] = c[d] * ps; ps *= s; } for ( i = d-1; i >= 0; i-- ) { bvals[i] *= po; po *= o; } bvals[d] = ps; } /* ==================== idCurve_Bezier::BasisFirstDerivative first derivative of bezier basis functions ==================== */ template< class type > ID_INLINE void idCurve_Bezier::BasisFirstDerivative( const int order, const float t, float *bvals ) const { int i; Basis( order-1, t, bvals+1 ); bvals[0] = 0.0f; for ( i = 0; i < order-1; i++ ) { bvals[i] -= bvals[i+1]; } } /* ==================== idCurve_Bezier::BasisSecondDerivative second derivative of bezier basis functions ==================== */ template< class type > ID_INLINE void idCurve_Bezier::BasisSecondDerivative( const int order, const float t, float *bvals ) const { int i; BasisFirstDerivative( order-1, t, bvals+1 ); bvals[0] = 0.0f; for ( i = 0; i < order-1; i++ ) { bvals[i] -= bvals[i+1]; } } /* =============================================================================== Quadratic Bezier Curve template. Should always have exactly three knots. =============================================================================== */ template< class type > class idCurve_QuadraticBezier : public idCurve { public: idCurve_QuadraticBezier( void ); virtual type GetCurrentValue( const float time ) const; virtual type GetCurrentFirstDerivative( const float time ) const; virtual type GetCurrentSecondDerivative( const float time ) const; protected: void Basis( const float t, float *bvals ) const; void BasisFirstDerivative( const float t, float *bvals ) const; void BasisSecondDerivative( const float t, float *bvals ) const; }; /* ==================== idCurve_QuadraticBezier::idCurve_QuadraticBezier ==================== */ template< class type > ID_INLINE idCurve_QuadraticBezier::idCurve_QuadraticBezier( void ) { } /* ==================== idCurve_QuadraticBezier::GetCurrentValue get the value for the given time ==================== */ template< class type > ID_INLINE type idCurve_QuadraticBezier::GetCurrentValue( const float time ) const { float bvals[3]; assert( this->values.Num() == 3 ); Basis( time, bvals ); return ( bvals[0] * this->values[0] + bvals[1] * this->values[1] + bvals[2] * this->values[2] ); } /* ==================== idCurve_QuadraticBezier::GetCurrentFirstDerivative get the first derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_QuadraticBezier::GetCurrentFirstDerivative( const float time ) const { float bvals[3], d; assert( this->values.Num() == 3 ); BasisFirstDerivative( time, bvals ); d = ( this->times[2] - this->times[0] ); return ( bvals[0] * this->values[0] + bvals[1] * this->values[1] + bvals[2] * this->values[2] ) / d; } /* ==================== idCurve_QuadraticBezier::GetCurrentSecondDerivative get the second derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_QuadraticBezier::GetCurrentSecondDerivative( const float time ) const { float bvals[3], d; assert( this->values.Num() == 3 ); BasisSecondDerivative( time, bvals ); d = ( this->times[2] - this->times[0] ); return ( bvals[0] * this->values[0] + bvals[1] * this->values[1] + bvals[2] * this->values[2] ) / ( d * d ); } /* ==================== idCurve_QuadraticBezier::Basis quadratic bezier basis functions ==================== */ template< class type > ID_INLINE void idCurve_QuadraticBezier::Basis( const float t, float *bvals ) const { float s1 = (float) ( t - this->times[0] ) / ( this->times[2] - this->times[0] ); float s2 = s1 * s1; bvals[0] = s2 - 2.0f * s1 + 1.0f; bvals[1] = -2.0f * s2 + 2.0f * s1; bvals[2] = s2; } /* ==================== idCurve_QuadraticBezier::BasisFirstDerivative first derivative of quadratic bezier basis functions ==================== */ template< class type > ID_INLINE void idCurve_QuadraticBezier::BasisFirstDerivative( const float t, float *bvals ) const { float s1 = (float) ( t - this->times[0] ) / ( this->times[2] - this->times[0] ); bvals[0] = 2.0f * s1 - 2.0f; bvals[1] = -4.0f * s1 + 2.0f; bvals[2] = 2.0f * s1; } /* ==================== idCurve_QuadraticBezier::BasisSecondDerivative second derivative of quadratic bezier basis functions ==================== */ template< class type > ID_INLINE void idCurve_QuadraticBezier::BasisSecondDerivative( const float t, float *bvals ) const { bvals[0] = 2.0f; bvals[1] = -4.0f; bvals[2] = 2.0f; } /* =============================================================================== Cubic Bezier Curve template. Should always have exactly four knots. =============================================================================== */ template< class type > class idCurve_CubicBezier : public idCurve { public: idCurve_CubicBezier( void ); virtual type GetCurrentValue( const float time ) const; virtual type GetCurrentFirstDerivative( const float time ) const; virtual type GetCurrentSecondDerivative( const float time ) const; protected: void Basis( const float t, float *bvals ) const; void BasisFirstDerivative( const float t, float *bvals ) const; void BasisSecondDerivative( const float t, float *bvals ) const; }; /* ==================== idCurve_CubicBezier::idCurve_CubicBezier ==================== */ template< class type > ID_INLINE idCurve_CubicBezier::idCurve_CubicBezier( void ) { } /* ==================== idCurve_CubicBezier::GetCurrentValue get the value for the given time ==================== */ template< class type > ID_INLINE type idCurve_CubicBezier::GetCurrentValue( const float time ) const { float bvals[4]; assert( this->values.Num() == 4 ); Basis( time, bvals ); return ( bvals[0] * this->values[0] + bvals[1] * this->values[1] + bvals[2] * this->values[2] + bvals[3] * this->values[3] ); } /* ==================== idCurve_CubicBezier::GetCurrentFirstDerivative get the first derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_CubicBezier::GetCurrentFirstDerivative( const float time ) const { float bvals[4], d; assert( this->values.Num() == 4 ); BasisFirstDerivative( time, bvals ); d = ( this->times[3] - this->times[0] ); return ( bvals[0] * this->values[0] + bvals[1] * this->values[1] + bvals[2] * this->values[2] + bvals[3] * this->values[3] ) / d; } /* ==================== idCurve_CubicBezier::GetCurrentSecondDerivative get the second derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_CubicBezier::GetCurrentSecondDerivative( const float time ) const { float bvals[4], d; assert( this->values.Num() == 4 ); BasisSecondDerivative( time, bvals ); d = ( this->times[3] - this->times[0] ); return ( bvals[0] * this->values[0] + bvals[1] * this->values[1] + bvals[2] * this->values[2] + bvals[3] * this->values[3] ) / ( d * d ); } /* ==================== idCurve_CubicBezier::Basis cubic bezier basis functions ==================== */ template< class type > ID_INLINE void idCurve_CubicBezier::Basis( const float t, float *bvals ) const { float s1 = (float) ( t - this->times[0] ) / ( this->times[3] - this->times[0] ); float s2 = s1 * s1; float s3 = s2 * s1; bvals[0] = -s3 + 3.0f * s2 - 3.0f * s1 + 1.0f; bvals[1] = 3.0f * s3 - 6.0f * s2 + 3.0f * s1; bvals[2] = -3.0f * s3 + 3.0f * s2; bvals[3] = s3; } /* ==================== idCurve_CubicBezier::BasisFirstDerivative first derivative of cubic bezier basis functions ==================== */ template< class type > ID_INLINE void idCurve_CubicBezier::BasisFirstDerivative( const float t, float *bvals ) const { float s1 = (float) ( t - this->times[0] ) / ( this->times[3] - this->times[0] ); float s2 = s1 * s1; bvals[0] = -3.0f * s2 + 6.0f * s1 - 3.0f; bvals[1] = 9.0f * s2 - 12.0f * s1 + 3.0f; bvals[2] = -9.0f * s2 + 6.0f * s1; bvals[3] = 3.0f * s2; } /* ==================== idCurve_CubicBezier::BasisSecondDerivative second derivative of cubic bezier basis functions ==================== */ template< class type > ID_INLINE void idCurve_CubicBezier::BasisSecondDerivative( const float t, float *bvals ) const { float s1 = (float) ( t - this->times[0] ) / ( this->times[3] - this->times[0] ); bvals[0] = -6.0f * s1 + 6.0f; bvals[1] = 18.0f * s1 - 12.0f; bvals[2] = -18.0f * s1 + 6.0f; bvals[3] = 6.0f * s1; } /* =============================================================================== Spline base template. =============================================================================== */ template< class type > class idCurve_Spline : public idCurve { public: enum boundary_t { BT_FREE, BT_CLAMPED, BT_CLOSED }; idCurve_Spline( void ); virtual bool IsDone( const float time ) const; virtual void SetBoundaryType( const boundary_t bt ) { boundaryType = bt; this->changed = true; } virtual boundary_t GetBoundaryType( void ) const { return boundaryType; } virtual void SetCloseTime( const float t ) { closeTime = t; this->changed = true; } virtual float GetCloseTime( void ) { return boundaryType == BT_CLOSED ? closeTime : 0.0f; } protected: boundary_t boundaryType; float closeTime; type ValueForIndex( const int index ) const; float TimeForIndex( const int index ) const; float ClampedTime( const float t ) const; }; /* ==================== idCurve_Spline::idCurve_Spline ==================== */ template< class type > ID_INLINE idCurve_Spline::idCurve_Spline( void ) { boundaryType = BT_FREE; closeTime = 0.0f; } /* ==================== idCurve_Spline::ValueForIndex get the value for the given time ==================== */ template< class type > ID_INLINE type idCurve_Spline::ValueForIndex( const int index ) const { int n = this->values.Num()-1; if ( index < 0 ) { if ( boundaryType == BT_CLOSED ) { return this->values[ this->values.Num() + index % this->values.Num() ]; } else { return this->values[0] + index * ( this->values[1] - this->values[0] ); } } else if ( index > n ) { if ( boundaryType == BT_CLOSED ) { return this->values[ index % this->values.Num() ]; } else { return this->values[n] + ( index - n ) * ( this->values[n] - this->values[n-1] ); } } return this->values[index]; } /* ==================== idCurve_Spline::TimeForIndex get the value for the given time ==================== */ template< class type > ID_INLINE float idCurve_Spline::TimeForIndex( const int index ) const { int n = this->times.Num()-1; if ( index < 0 ) { if ( boundaryType == BT_CLOSED ) { return ( index / this->times.Num() ) * ( this->times[n] + closeTime ) - ( this->times[n] + closeTime - this->times[this->times.Num() + index % this->times.Num()] ); } else { return this->times[0] + index * ( this->times[1] - this->times[0] ); } } else if ( index > n ) { if ( boundaryType == BT_CLOSED ) { return ( index / this->times.Num() ) * ( this->times[n] + closeTime ) + this->times[index % this->times.Num()]; } else { return this->times[n] + ( index - n ) * ( this->times[n] - this->times[n-1] ); } } return this->times[index]; } /* ==================== idCurve_Spline::ClampedTime return the clamped time based on the boundary type ==================== */ template< class type > ID_INLINE float idCurve_Spline::ClampedTime( const float t ) const { if ( boundaryType == BT_CLAMPED ) { if ( t < this->times[0] ) { return this->times[0]; } else if ( t >= this->times[this->times.Num()-1] ) { return this->times[this->times.Num()-1]; } } return t; } /* ==================== idCurve_Spline::IsDone ==================== */ template< class type > ID_INLINE bool idCurve_Spline::IsDone( const float time ) const { return ( boundaryType != BT_CLOSED && time >= this->times[ this->times.Num() - 1 ] ); } /* =============================================================================== Cubic Interpolating Spline template. The curve goes through all the knots. =============================================================================== */ template< class type > class idCurve_NaturalCubicSpline : public idCurve_Spline { public: idCurve_NaturalCubicSpline( void ); virtual void Clear( void ) { idCurve_Spline::Clear(); this->values.Clear(); b.Clear(); c.Clear(); d.Clear(); } virtual type GetCurrentValue( const float time ) const; virtual type GetCurrentFirstDerivative( const float time ) const; virtual type GetCurrentSecondDerivative( const float time ) const; protected: mutable idListb; mutable idListc; mutable idListd; void Setup( void ) const; void SetupFree( void ) const; void SetupClamped( void ) const; void SetupClosed( void ) const; }; /* ==================== idCurve_NaturalCubicSpline::idCurve_NaturalCubicSpline ==================== */ template< class type > ID_INLINE idCurve_NaturalCubicSpline::idCurve_NaturalCubicSpline( void ) { } /* ==================== idCurve_NaturalCubicSpline::GetCurrentValue get the value for the given time ==================== */ template< class type > ID_INLINE type idCurve_NaturalCubicSpline::GetCurrentValue( const float time ) const { float clampedTime = this->ClampedTime( time ); int i = this->IndexForTime( clampedTime ); float s = time - this->TimeForIndex( i ); Setup(); return ( this->values[i] + s * ( b[i] + s * ( c[i] + s * d[i] ) ) ); } /* ==================== idCurve_NaturalCubicSpline::GetCurrentFirstDerivative get the first derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_NaturalCubicSpline::GetCurrentFirstDerivative( const float time ) const { float clampedTime = this->ClampedTime( time ); int i = this->IndexForTime( clampedTime ); float s = time - this->TimeForIndex( i ); Setup(); return ( b[i] + s * ( 2.0f * c[i] + 3.0f * s * d[i] ) ); } /* ==================== idCurve_NaturalCubicSpline::GetCurrentSecondDerivative get the second derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_NaturalCubicSpline::GetCurrentSecondDerivative( const float time ) const { float clampedTime = this->ClampedTime( time ); int i = this->IndexForTime( clampedTime ); float s = time - this->TimeForIndex( i ); Setup(); return ( 2.0f * c[i] + 6.0f * s * d[i] ); } /* ==================== idCurve_NaturalCubicSpline::Setup ==================== */ template< class type > ID_INLINE void idCurve_NaturalCubicSpline::Setup( void ) const { if ( this->changed ) { switch( this->boundaryType ) { case idCurve_Spline::BT_FREE: SetupFree(); break; case idCurve_Spline::BT_CLAMPED: SetupClamped(); break; case idCurve_Spline::BT_CLOSED: SetupClosed(); break; } this->changed = false; } } /* ==================== idCurve_NaturalCubicSpline::SetupFree ==================== */ template< class type > ID_INLINE void idCurve_NaturalCubicSpline::SetupFree( void ) const { int i; float inv; float *d0, *d1, *beta, *gamma; type *alpha, *delta; d0 = (float *) _alloca16( ( this->values.Num() - 1 ) * sizeof( float ) ); d1 = (float *) _alloca16( ( this->values.Num() - 1 ) * sizeof( float ) ); alpha = (type *) _alloca16( ( this->values.Num() - 1 ) * sizeof( type ) ); beta = (float *) _alloca16( this->values.Num() * sizeof( float ) ); gamma = (float *) _alloca16( ( this->values.Num() - 1 ) * sizeof( float ) ); delta = (type *) _alloca16( this->values.Num() * sizeof( type ) ); for ( i = 0; i < this->values.Num() - 1; i++ ) { d0[i] = this->times[i+1] - this->times[i]; } for ( i = 1; i < this->values.Num() - 1; i++ ) { d1[i] = this->times[i+1] - this->times[i-1]; } for ( i = 1; i < this->values.Num() - 1; i++ ) { type sum = 3.0f * ( d0[i-1] * this->values[i+1] - d1[i] * this->values[i] + d0[i] * this->values[i-1] ); inv = 1.0f / ( d0[i-1] * d0[i] ); alpha[i] = inv * sum; } beta[0] = 1.0f; gamma[0] = 0.0f; delta[0] = this->values[0] - this->values[0]; for ( i = 1; i < this->values.Num() - 1; i++ ) { beta[i] = 2.0f * d1[i] - d0[i-1] * gamma[i-1]; inv = 1.0f / beta[i]; gamma[i] = inv * d0[i]; delta[i] = inv * ( alpha[i] - d0[i-1] * delta[i-1] ); } beta[this->values.Num() - 1] = 1.0f; delta[this->values.Num() - 1] = this->values[0] - this->values[0]; b.AssureSize( this->values.Num() ); c.AssureSize( this->values.Num() ); d.AssureSize( this->values.Num() ); c[this->values.Num() - 1] = this->values[0] - this->values[0]; for ( i = this->values.Num() - 2; i >= 0; i-- ) { c[i] = delta[i] - gamma[i] * c[i+1]; inv = 1.0f / d0[i]; b[i] = inv * ( this->values[i+1] - this->values[i] ) - ( 1.0f / 3.0f ) * d0[i] * ( c[i+1] + 2.0f * c[i] ); d[i] = ( 1.0f / 3.0f ) * inv * ( c[i+1] - c[i] ); } } /* ==================== idCurve_NaturalCubicSpline::SetupClamped ==================== */ template< class type > ID_INLINE void idCurve_NaturalCubicSpline::SetupClamped( void ) const { int i; float inv; float *d0, *d1, *beta, *gamma; type *alpha, *delta; d0 = (float *) _alloca16( ( this->values.Num() - 1 ) * sizeof( float ) ); d1 = (float *) _alloca16( ( this->values.Num() - 1 ) * sizeof( float ) ); alpha = (type *) _alloca16( ( this->values.Num() - 1 ) * sizeof( type ) ); beta = (float *) _alloca16( this->values.Num() * sizeof( float ) ); gamma = (float *) _alloca16( ( this->values.Num() - 1 ) * sizeof( float ) ); delta = (type *) _alloca16( this->values.Num() * sizeof( type ) ); for ( i = 0; i < this->values.Num() - 1; i++ ) { d0[i] = this->times[i+1] - this->times[i]; } for ( i = 1; i < this->values.Num() - 1; i++ ) { d1[i] = this->times[i+1] - this->times[i-1]; } inv = 1.0f / d0[0]; alpha[0] = 3.0f * ( inv - 1.0f ) * ( this->values[1] - this->values[0] ); inv = 1.0f / d0[this->values.Num() - 2]; alpha[this->values.Num() - 1] = 3.0f * ( 1.0f - inv ) * ( this->values[this->values.Num() - 1] - this->values[this->values.Num() - 2] ); for ( i = 1; i < this->values.Num() - 1; i++ ) { type sum = 3.0f * ( d0[i-1] * this->values[i+1] - d1[i] * this->values[i] + d0[i] * this->values[i-1] ); inv = 1.0f / ( d0[i-1] * d0[i] ); alpha[i] = inv * sum; } beta[0] = 2.0f * d0[0]; gamma[0] = 0.5f; inv = 1.0f / beta[0]; delta[0] = inv * alpha[0]; for ( i = 1; i < this->values.Num() - 1; i++ ) { beta[i] = 2.0f * d1[i] - d0[i-1] * gamma[i-1]; inv = 1.0f / beta[i]; gamma[i] = inv * d0[i]; delta[i] = inv * ( alpha[i] - d0[i-1] * delta[i-1] ); } beta[this->values.Num() - 1] = d0[this->values.Num() - 2] * ( 2.0f - gamma[this->values.Num() - 2] ); inv = 1.0f / beta[this->values.Num() - 1]; delta[this->values.Num() - 1] = inv * ( alpha[this->values.Num() - 1] - d0[this->values.Num() - 2] * delta[this->values.Num() - 2] ); b.AssureSize( this->values.Num() ); c.AssureSize( this->values.Num() ); d.AssureSize( this->values.Num() ); c[this->values.Num() - 1] = delta[this->values.Num() - 1]; for ( i = this->values.Num() - 2; i >= 0; i-- ) { c[i] = delta[i] - gamma[i] * c[i+1]; inv = 1.0f / d0[i]; b[i] = inv * ( this->values[i+1] - this->values[i] ) - ( 1.0f / 3.0f ) * d0[i]* ( c[i+1] + 2.0f * c[i] ); d[i] = ( 1.0f / 3.0f ) * inv * ( c[i+1] - c[i] ); } } /* ==================== idCurve_NaturalCubicSpline::SetupClosed ==================== */ template< class type > ID_INLINE void idCurve_NaturalCubicSpline::SetupClosed( void ) const { int i, j; float c0, c1; float *d0; idMatX mat; idVecX x; d0 = (float *) _alloca16( ( this->values.Num() - 1 ) * sizeof( float ) ); x.SetData( this->values.Num(), VECX_ALLOCA( this->values.Num() ) ); mat.SetData( this->values.Num(), this->values.Num(), MATX_ALLOCA( this->values.Num() * this->values.Num() ) ); b.AssureSize( this->values.Num() ); c.AssureSize( this->values.Num() ); d.AssureSize( this->values.Num() ); for ( i = 0; i < this->values.Num() - 1; i++ ) { d0[i] = this->times[i+1] - this->times[i]; } // matrix of system mat[0][0] = 1.0f; mat[0][this->values.Num() - 1] = -1.0f; for ( i = 1; i <= this->values.Num() - 2; i++ ) { mat[i][i-1] = d0[i-1]; mat[i][i ] = 2.0f * ( d0[i-1] + d0[i] ); mat[i][i+1] = d0[i]; } mat[this->values.Num() - 1][this->values.Num() - 2] = d0[this->values.Num() - 2]; mat[this->values.Num() - 1][0] = 2.0f * ( d0[this->values.Num() - 2] + d0[0] ); mat[this->values.Num() - 1][1] = d0[0]; // right-hand side c[0].Zero(); for ( i = 1; i <= this->values.Num() - 2; i++ ) { c0 = 1.0f / d0[i]; c1 = 1.0f / d0[i-1]; c[i] = 3.0f * ( c0 * ( this->values[i + 1] - this->values[i] ) - c1 * ( this->values[i] - this->values[i - 1] ) ); } c0 = 1.0f / d0[0]; c1 = 1.0f / d0[this->values.Num() - 2]; c[this->values.Num() - 1] = 3.0f * ( c0 * ( this->values[1] - this->values[0] ) - c1 * ( this->values[0] - this->values[this->values.Num() - 2] ) ); // solve system for each dimension mat.LU_Factor( NULL ); for ( i = 0; i < this->values[0].GetDimension(); i++ ) { for ( j = 0; j < this->values.Num(); j++ ) { x[j] = c[j][i]; } mat.LU_Solve( x, x, NULL ); for ( j = 0; j < this->values.Num(); j++ ) { c[j][i] = x[j]; } } for ( i = 0; i < this->values.Num() - 1; i++ ) { c0 = 1.0f / d0[i]; b[i] = c0 * ( this->values[i + 1] - this->values[i] ) - ( 1.0f / 3.0f ) * ( c[i+1] + 2.0f * c[i] ) * d0[i]; d[i] = ( 1.0f / 3.0f ) * c0 * ( c[i + 1] - c[i] ); } } /* =============================================================================== Uniform Cubic Interpolating Spline template. The curve goes through all the knots. =============================================================================== */ template< class type > class idCurve_CatmullRomSpline : public idCurve_Spline { public: idCurve_CatmullRomSpline( void ); virtual type GetCurrentValue( const float time ) const; virtual type GetCurrentFirstDerivative( const float time ) const; virtual type GetCurrentSecondDerivative( const float time ) const; protected: void Basis( const int index, const float t, float *bvals ) const; void BasisFirstDerivative( const int index, const float t, float *bvals ) const; void BasisSecondDerivative( const int index, const float t, float *bvals ) const; }; /* ==================== idCurve_CatmullRomSpline::idCurve_CatmullRomSpline ==================== */ template< class type > ID_INLINE idCurve_CatmullRomSpline::idCurve_CatmullRomSpline( void ) { } /* ==================== idCurve_CatmullRomSpline::GetCurrentValue get the value for the given time ==================== */ template< class type > ID_INLINE type idCurve_CatmullRomSpline::GetCurrentValue( const float time ) const { int i, j, k; float bvals[4], clampedTime; type v; if ( this->times.Num() == 1 ) { return this->values[0]; } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); Basis( i-1, clampedTime, bvals ); v = this->values[0] - this->values[0]; for ( j = 0; j < 4; j++ ) { k = i + j - 2; v += bvals[j] * this->ValueForIndex( k ); } return v; } /* ==================== idCurve_CatmullRomSpline::GetCurrentFirstDerivative get the first derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_CatmullRomSpline::GetCurrentFirstDerivative( const float time ) const { int i, j, k; float bvals[4], d, clampedTime; type v; if ( this->times.Num() == 1 ) { return ( this->values[0] - this->values[0] ); } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); BasisFirstDerivative( i-1, clampedTime, bvals ); v = this->values[0] - this->values[0]; for ( j = 0; j < 4; j++ ) { k = i + j - 2; v += bvals[j] * this->ValueForIndex( k ); } d = ( this->TimeForIndex( i ) - this->TimeForIndex( i-1 ) ); return v / d; } /* ==================== idCurve_CatmullRomSpline::GetCurrentSecondDerivative get the second derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_CatmullRomSpline::GetCurrentSecondDerivative( const float time ) const { int i, j, k; float bvals[4], d, clampedTime; type v; if ( this->times.Num() == 1 ) { return ( this->values[0] - this->values[0] ); } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); BasisSecondDerivative( i-1, clampedTime, bvals ); v = this->values[0] - this->values[0]; for ( j = 0; j < 4; j++ ) { k = i + j - 2; v += bvals[j] * this->ValueForIndex( k ); } d = ( this->TimeForIndex( i ) - this->TimeForIndex( i-1 ) ); return v / ( d * d ); } /* ==================== idCurve_CatmullRomSpline::Basis spline basis functions ==================== */ template< class type > ID_INLINE void idCurve_CatmullRomSpline::Basis( const int index, const float t, float *bvals ) const { float s = (float) ( t - this->TimeForIndex( index ) ) / ( this->TimeForIndex( index+1 ) - this->TimeForIndex( index ) ); bvals[0] = ( ( -s + 2.0f ) * s - 1.0f ) * s * 0.5f; // -0.5f s * s * s + s * s - 0.5f * s bvals[1] = ( ( ( 3.0f * s - 5.0f ) * s ) * s + 2.0f ) * 0.5f; // 1.5f * s * s * s - 2.5f * s * s + 1.0f bvals[2] = ( ( -3.0f * s + 4.0f ) * s + 1.0f ) * s * 0.5f; // -1.5f * s * s * s - 2.0f * s * s + 0.5f s bvals[3] = ( ( s - 1.0f ) * s * s ) * 0.5f; // 0.5f * s * s * s - 0.5f * s * s } /* ==================== idCurve_CatmullRomSpline::BasisFirstDerivative first derivative of spline basis functions ==================== */ template< class type > ID_INLINE void idCurve_CatmullRomSpline::BasisFirstDerivative( const int index, const float t, float *bvals ) const { float s = (float) ( t - this->TimeForIndex( index ) ) / ( this->TimeForIndex( index+1 ) - this->TimeForIndex( index ) ); bvals[0] = ( -1.5f * s + 2.0f ) * s - 0.5f; // -1.5f * s * s + 2.0f * s - 0.5f bvals[1] = ( 4.5f * s - 5.0f ) * s; // 4.5f * s * s - 5.0f * s bvals[2] = ( -4.5 * s + 4.0f ) * s + 0.5f; // -4.5 * s * s + 4.0f * s + 0.5f bvals[3] = 1.5f * s * s - s; // 1.5f * s * s - s } /* ==================== idCurve_CatmullRomSpline::BasisSecondDerivative second derivative of spline basis functions ==================== */ template< class type > ID_INLINE void idCurve_CatmullRomSpline::BasisSecondDerivative( const int index, const float t, float *bvals ) const { float s = (float) ( t - this->TimeForIndex( index ) ) / ( this->TimeForIndex( index+1 ) - this->TimeForIndex( index ) ); bvals[0] = -3.0f * s + 2.0f; bvals[1] = 9.0f * s - 5.0f; bvals[2] = -9.0f * s + 4.0f; bvals[3] = 3.0f * s - 1.0f; } /* =============================================================================== Cubic Interpolating Spline template. The curve goes through all the knots. The curve becomes the Catmull-Rom spline if the tension, continuity and bias are all set to zero. =============================================================================== */ template< class type > class idCurve_KochanekBartelsSpline : public idCurve_Spline { public: idCurve_KochanekBartelsSpline( void ); virtual int AddValue( const float time, const type &value ); virtual int AddValue( const float time, const type &value, const float tension, const float continuity, const float bias ); virtual void RemoveIndex( const int index ) { this->values.RemoveIndex(index); this->times.RemoveIndex(index); tension.RemoveIndex(index); continuity.RemoveIndex(index); bias.RemoveIndex(index); } virtual void Clear( void ) { this->values.Clear(); this->times.Clear(); tension.Clear(); continuity.Clear(); bias.Clear(); this->currentIndex = -1; } virtual type GetCurrentValue( const float time ) const; virtual type GetCurrentFirstDerivative( const float time ) const; virtual type GetCurrentSecondDerivative( const float time ) const; protected: idList tension; idList continuity; idList bias; void TangentsForIndex( const int index, type &t0, type &t1 ) const; void Basis( const int index, const float t, float *bvals ) const; void BasisFirstDerivative( const int index, const float t, float *bvals ) const; void BasisSecondDerivative( const int index, const float t, float *bvals ) const; }; /* ==================== idCurve_KochanekBartelsSpline::idCurve_KochanekBartelsSpline ==================== */ template< class type > ID_INLINE idCurve_KochanekBartelsSpline::idCurve_KochanekBartelsSpline( void ) { } /* ==================== idCurve_KochanekBartelsSpline::AddValue add a timed/value pair to the spline returns the index to the inserted pair ==================== */ template< class type > ID_INLINE int idCurve_KochanekBartelsSpline::AddValue( const float time, const type &value ) { int i; i = this->IndexForTime( time ); this->times.Insert( time, i ); this->values.Insert( value, i ); tension.Insert( 0.0f, i ); continuity.Insert( 0.0f, i ); bias.Insert( 0.0f, i ); return i; } /* ==================== idCurve_KochanekBartelsSpline::AddValue add a timed/value pair to the spline returns the index to the inserted pair ==================== */ template< class type > ID_INLINE int idCurve_KochanekBartelsSpline::AddValue( const float time, const type &value, const float tension, const float continuity, const float bias ) { int i; i = this->IndexForTime( time ); this->times.Insert( time, i ); this->values.Insert( value, i ); this->tension.Insert( tension, i ); this->continuity.Insert( continuity, i ); this->bias.Insert( bias, i ); return i; } /* ==================== idCurve_KochanekBartelsSpline::GetCurrentValue get the value for the given time ==================== */ template< class type > ID_INLINE type idCurve_KochanekBartelsSpline::GetCurrentValue( const float time ) const { int i; float bvals[4], clampedTime; type v, t0, t1; if ( this->times.Num() == 1 ) { return this->values[0]; } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); TangentsForIndex( i - 1, t0, t1 ); Basis( i - 1, clampedTime, bvals ); v = bvals[0] * this->ValueForIndex( i - 1 ); v += bvals[1] * this->ValueForIndex( i ); v += bvals[2] * t0; v += bvals[3] * t1; return v; } /* ==================== idCurve_KochanekBartelsSpline::GetCurrentFirstDerivative get the first derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_KochanekBartelsSpline::GetCurrentFirstDerivative( const float time ) const { int i; float bvals[4], d, clampedTime; type v, t0, t1; if ( this->times.Num() == 1 ) { return ( this->values[0] - this->values[0] ); } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); TangentsForIndex( i - 1, t0, t1 ); BasisFirstDerivative( i - 1, clampedTime, bvals ); v = bvals[0] * this->ValueForIndex( i - 1 ); v += bvals[1] * this->ValueForIndex( i ); v += bvals[2] * t0; v += bvals[3] * t1; d = ( this->TimeForIndex( i ) - this->TimeForIndex( i-1 ) ); return v / d; } /* ==================== idCurve_KochanekBartelsSpline::GetCurrentSecondDerivative get the second derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_KochanekBartelsSpline::GetCurrentSecondDerivative( const float time ) const { int i; float bvals[4], d, clampedTime; type v, t0, t1; if ( this->times.Num() == 1 ) { return ( this->values[0] - this->values[0] ); } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); TangentsForIndex( i - 1, t0, t1 ); BasisSecondDerivative( i - 1, clampedTime, bvals ); v = bvals[0] * this->ValueForIndex( i - 1 ); v += bvals[1] * this->ValueForIndex( i ); v += bvals[2] * t0; v += bvals[3] * t1; d = ( this->TimeForIndex( i ) - this->TimeForIndex( i-1 ) ); return v / ( d * d ); } /* ==================== idCurve_KochanekBartelsSpline::TangentsForIndex ==================== */ template< class type > ID_INLINE void idCurve_KochanekBartelsSpline::TangentsForIndex( const int index, type &t0, type &t1 ) const { float dt, omt, omc, opc, omb, opb, adj, s0, s1; type delta; delta = this->ValueForIndex( index + 1 ) - this->ValueForIndex( index ); dt = this->TimeForIndex( index + 1 ) - this->TimeForIndex( index ); omt = 1.0f - tension[index]; omc = 1.0f - continuity[index]; opc = 1.0f + continuity[index]; omb = 1.0f - bias[index]; opb = 1.0f + bias[index]; adj = 2.0f * dt / ( this->TimeForIndex( index + 1 ) - this->TimeForIndex( index - 1 ) ); s0 = 0.5f * adj * omt * opc * opb; s1 = 0.5f * adj * omt * omc * omb; // outgoing tangent at first point t0 = s1 * delta + s0 * ( this->ValueForIndex( index ) - this->ValueForIndex( index - 1 ) ); omt = 1.0f - tension[index + 1]; omc = 1.0f - continuity[index + 1]; opc = 1.0f + continuity[index + 1]; omb = 1.0f - bias[index + 1]; opb = 1.0f + bias[index + 1]; adj = 2.0f * dt / ( this->TimeForIndex( index + 2 ) - this->TimeForIndex( index ) ); s0 = 0.5f * adj * omt * omc * opb; s1 = 0.5f * adj * omt * opc * omb; // incoming tangent at second point t1 = s1 * ( this->ValueForIndex( index + 2 ) - this->ValueForIndex( index + 1 ) ) + s0 * delta; } /* ==================== idCurve_KochanekBartelsSpline::Basis spline basis functions ==================== */ template< class type > ID_INLINE void idCurve_KochanekBartelsSpline::Basis( const int index, const float t, float *bvals ) const { float s = (float) ( t - this->TimeForIndex( index ) ) / ( this->TimeForIndex( index+1 ) - this->TimeForIndex( index ) ); bvals[0] = ( ( 2.0f * s - 3.0f ) * s ) * s + 1.0f; // 2.0f * s * s * s - 3.0f * s * s + 1.0f bvals[1] = ( ( -2.0f * s + 3.0f ) * s ) * s; // -2.0f * s * s * s + 3.0f * s * s bvals[2] = ( ( s - 2.0f ) * s ) * s + s; // s * s * s - 2.0f * s * s + s bvals[3] = ( ( s - 1.0f ) * s ) * s; // s * s * s - s * s } /* ==================== idCurve_KochanekBartelsSpline::BasisFirstDerivative first derivative of spline basis functions ==================== */ template< class type > ID_INLINE void idCurve_KochanekBartelsSpline::BasisFirstDerivative( const int index, const float t, float *bvals ) const { float s = (float) ( t - this->TimeForIndex( index ) ) / ( this->TimeForIndex( index+1 ) - this->TimeForIndex( index ) ); bvals[0] = ( 6.0f * s - 6.0f ) * s; // 6.0f * s * s - 6.0f * s bvals[1] = ( -6.0f * s + 6.0f ) * s; // -6.0f * s * s + 6.0f * s bvals[2] = ( 3.0f * s - 4.0f ) * s + 1.0f; // 3.0f * s * s - 4.0f * s + 1.0f bvals[3] = ( 3.0f * s - 2.0f ) * s; // 3.0f * s * s - 2.0f * s } /* ==================== idCurve_KochanekBartelsSpline::BasisSecondDerivative second derivative of spline basis functions ==================== */ template< class type > ID_INLINE void idCurve_KochanekBartelsSpline::BasisSecondDerivative( const int index, const float t, float *bvals ) const { float s = (float) ( t - this->TimeForIndex( index ) ) / ( this->TimeForIndex( index+1 ) - this->TimeForIndex( index ) ); bvals[0] = 12.0f * s - 6.0f; bvals[1] = -12.0f * s + 6.0f; bvals[2] = 6.0f * s - 4.0f; bvals[3] = 6.0f * s - 2.0f; } /* =============================================================================== B-Spline base template. Uses recursive definition and is slow. Use idCurve_UniformCubicBSpline or idCurve_NonUniformBSpline instead. =============================================================================== */ template< class type > class idCurve_BSpline : public idCurve_Spline { public: idCurve_BSpline( void ); virtual int GetOrder( void ) const { return order; } virtual void SetOrder( const int i ) { assert( i > 0 && i < 10 ); order = i; } virtual type GetCurrentValue( const float time ) const; virtual type GetCurrentFirstDerivative( const float time ) const; virtual type GetCurrentSecondDerivative( const float time ) const; protected: int order; float Basis( const int index, const int order, const float t ) const; float BasisFirstDerivative( const int index, const int order, const float t ) const; float BasisSecondDerivative( const int index, const int order, const float t ) const; }; /* ==================== idCurve_BSpline::idCurve_NaturalCubicSpline ==================== */ template< class type > ID_INLINE idCurve_BSpline::idCurve_BSpline( void ) { order = 4; // default to cubic } /* ==================== idCurve_BSpline::GetCurrentValue get the value for the given time ==================== */ template< class type > ID_INLINE type idCurve_BSpline::GetCurrentValue( const float time ) const { int i, j, k; float clampedTime; type v; if ( this->times.Num() == 1 ) { return this->values[0]; } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); v = this->values[0] - this->values[0]; for ( j = 0; j < order; j++ ) { k = i + j - ( order >> 1 ); v += Basis( k-2, order, clampedTime ) * this->ValueForIndex( k ); } return v; } /* ==================== idCurve_BSpline::GetCurrentFirstDerivative get the first derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_BSpline::GetCurrentFirstDerivative( const float time ) const { int i, j, k; float clampedTime; type v; if ( this->times.Num() == 1 ) { return this->values[0]; } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); v = this->values[0] - this->values[0]; for ( j = 0; j < order; j++ ) { k = i + j - ( order >> 1 ); v += BasisFirstDerivative( k-2, order, clampedTime ) * this->ValueForIndex( k ); } return v; } /* ==================== idCurve_BSpline::GetCurrentSecondDerivative get the second derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_BSpline::GetCurrentSecondDerivative( const float time ) const { int i, j, k; float clampedTime; type v; if ( this->times.Num() == 1 ) { return this->values[0]; } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); v = this->values[0] - this->values[0]; for ( j = 0; j < order; j++ ) { k = i + j - ( order >> 1 ); v += BasisSecondDerivative( k-2, order, clampedTime ) * this->ValueForIndex( k ); } return v; } /* ==================== idCurve_BSpline::Basis spline basis function ==================== */ template< class type > ID_INLINE float idCurve_BSpline::Basis( const int index, const int order, const float t ) const { if ( order <= 1 ) { if ( this->TimeForIndex( index ) < t && t <= this->TimeForIndex( index + 1 ) ) { return 1.0f; } else { return 0.0f; } } else { float sum = 0.0f; float d1 = this->TimeForIndex( index+order-1 ) - this->TimeForIndex( index ); if ( d1 != 0.0f ) { sum += (float) ( t - this->TimeForIndex( index ) ) * Basis( index, order-1, t ) / d1; } float d2 = this->TimeForIndex( index+order ) - this->TimeForIndex( index+1 ); if ( d2 != 0.0f ) { sum += (float) ( this->TimeForIndex( index+order ) - t ) * Basis( index+1, order-1, t ) / d2; } return sum; } } /* ==================== idCurve_BSpline::BasisFirstDerivative first derivative of spline basis function ==================== */ template< class type > ID_INLINE float idCurve_BSpline::BasisFirstDerivative( const int index, const int order, const float t ) const { return ( Basis( index, order-1, t ) - Basis( index+1, order-1, t ) ) * (float) ( order - 1 ) / ( this->TimeForIndex( index + ( order - 1 ) - 2 ) - this->TimeForIndex( index - 2 ) ); } /* ==================== idCurve_BSpline::BasisSecondDerivative second derivative of spline basis function ==================== */ template< class type > ID_INLINE float idCurve_BSpline::BasisSecondDerivative( const int index, const int order, const float t ) const { return ( BasisFirstDerivative( index, order-1, t ) - BasisFirstDerivative( index+1, order-1, t ) ) * (float) ( order - 1 ) / ( this->TimeForIndex( index + ( order - 1 ) - 2 ) - this->TimeForIndex( index - 2 ) ); } /* =============================================================================== Uniform Non-Rational Cubic B-Spline template. =============================================================================== */ template< class type > class idCurve_UniformCubicBSpline : public idCurve_BSpline { public: idCurve_UniformCubicBSpline( void ); virtual type GetCurrentValue( const float time ) const; virtual type GetCurrentFirstDerivative( const float time ) const; virtual type GetCurrentSecondDerivative( const float time ) const; protected: void Basis( const int index, const float t, float *bvals ) const; void BasisFirstDerivative( const int index, const float t, float *bvals ) const; void BasisSecondDerivative( const int index, const float t, float *bvals ) const; }; /* ==================== idCurve_UniformCubicBSpline::idCurve_UniformCubicBSpline ==================== */ template< class type > ID_INLINE idCurve_UniformCubicBSpline::idCurve_UniformCubicBSpline( void ) { this->order = 4; // always cubic } /* ==================== idCurve_UniformCubicBSpline::GetCurrentValue get the value for the given time ==================== */ template< class type > ID_INLINE type idCurve_UniformCubicBSpline::GetCurrentValue( const float time ) const { int i, j, k; float bvals[4], clampedTime; type v; if ( this->times.Num() == 1 ) { return this->values[0]; } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); Basis( i-1, clampedTime, bvals ); v = this->values[0] - this->values[0]; for ( j = 0; j < 4; j++ ) { k = i + j - 2; v += bvals[j] * this->ValueForIndex( k ); } return v; } /* ==================== idCurve_UniformCubicBSpline::GetCurrentFirstDerivative get the first derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_UniformCubicBSpline::GetCurrentFirstDerivative( const float time ) const { int i, j, k; float bvals[4], d, clampedTime; type v; if ( this->times.Num() == 1 ) { return ( this->values[0] - this->values[0] ); } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); BasisFirstDerivative( i-1, clampedTime, bvals ); v = this->values[0] - this->values[0]; for ( j = 0; j < 4; j++ ) { k = i + j - 2; v += bvals[j] * this->ValueForIndex( k ); } d = ( this->TimeForIndex( i ) - this->TimeForIndex( i-1 ) ); return v / d; } /* ==================== idCurve_UniformCubicBSpline::GetCurrentSecondDerivative get the second derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_UniformCubicBSpline::GetCurrentSecondDerivative( const float time ) const { int i, j, k; float bvals[4], d, clampedTime; type v; if ( this->times.Num() == 1 ) { return ( this->values[0] - this->values[0] ); } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); BasisSecondDerivative( i-1, clampedTime, bvals ); v = this->values[0] - this->values[0]; for ( j = 0; j < 4; j++ ) { k = i + j - 2; v += bvals[j] * this->ValueForIndex( k ); } d = ( this->TimeForIndex( i ) - this->TimeForIndex( i-1 ) ); return v / ( d * d ); } /* ==================== idCurve_UniformCubicBSpline::Basis spline basis functions ==================== */ template< class type > ID_INLINE void idCurve_UniformCubicBSpline::Basis( const int index, const float t, float *bvals ) const { float s = (float) ( t - this->TimeForIndex( index ) ) / ( this->TimeForIndex( index+1 ) - this->TimeForIndex( index ) ); bvals[0] = ( ( ( -s + 3.0f ) * s - 3.0f ) * s + 1.0f ) * ( 1.0f / 6.0f ); bvals[1] = ( ( ( 3.0f * s - 6.0f ) * s ) * s + 4.0f ) * ( 1.0f / 6.0f ); bvals[2] = ( ( ( -3.0f * s + 3.0f ) * s + 3.0f ) * s + 1.0f ) * ( 1.0f / 6.0f ); bvals[3] = ( s * s * s ) * ( 1.0f / 6.0f ); } /* ==================== idCurve_UniformCubicBSpline::BasisFirstDerivative first derivative of spline basis functions ==================== */ template< class type > ID_INLINE void idCurve_UniformCubicBSpline::BasisFirstDerivative( const int index, const float t, float *bvals ) const { float s = (float) ( t - this->TimeForIndex( index ) ) / ( this->TimeForIndex( index+1 ) - this->TimeForIndex( index ) ); bvals[0] = -0.5f * s * s + s - 0.5f; bvals[1] = 1.5f * s * s - 2.0f * s; bvals[2] = -1.5f * s * s + s + 0.5f; bvals[3] = 0.5f * s * s; } /* ==================== idCurve_UniformCubicBSpline::BasisSecondDerivative second derivative of spline basis functions ==================== */ template< class type > ID_INLINE void idCurve_UniformCubicBSpline::BasisSecondDerivative( const int index, const float t, float *bvals ) const { float s = (float) ( t - this->TimeForIndex( index ) ) / ( this->TimeForIndex( index+1 ) - this->TimeForIndex( index ) ); bvals[0] = -s + 1.0f; bvals[1] = 3.0f * s - 2.0f; bvals[2] = -3.0f * s + 1.0f; bvals[3] = s; } /* =============================================================================== Non-Uniform Non-Rational B-Spline (NUBS) template. =============================================================================== */ template< class type > class idCurve_NonUniformBSpline : public idCurve_BSpline { public: idCurve_NonUniformBSpline( void ); virtual type GetCurrentValue( const float time ) const; virtual type GetCurrentFirstDerivative( const float time ) const; virtual type GetCurrentSecondDerivative( const float time ) const; protected: void Basis( const int index, const int order, const float t, float *bvals ) const; void BasisFirstDerivative( const int index, const int order, const float t, float *bvals ) const; void BasisSecondDerivative( const int index, const int order, const float t, float *bvals ) const; }; /* ==================== idCurve_NonUniformBSpline::idCurve_NonUniformBSpline ==================== */ template< class type > ID_INLINE idCurve_NonUniformBSpline::idCurve_NonUniformBSpline( void ) { } /* ==================== idCurve_NonUniformBSpline::GetCurrentValue get the value for the given time ==================== */ template< class type > ID_INLINE type idCurve_NonUniformBSpline::GetCurrentValue( const float time ) const { int i, j, k; float clampedTime; type v; float *bvals = (float *) _alloca16( this->order * sizeof(float) ); if ( this->times.Num() == 1 ) { return this->values[0]; } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); Basis( i-1, this->order, clampedTime, bvals ); v = this->values[0] - this->values[0]; for ( j = 0; j < this->order; j++ ) { k = i + j - ( this->order >> 1 ); v += bvals[j] * this->ValueForIndex( k ); } return v; } /* ==================== idCurve_NonUniformBSpline::GetCurrentFirstDerivative get the first derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_NonUniformBSpline::GetCurrentFirstDerivative( const float time ) const { int i, j, k; float clampedTime; type v; float *bvals = (float *) _alloca16( this->order * sizeof(float) ); if ( this->times.Num() == 1 ) { return ( this->values[0] - this->values[0] ); } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); BasisFirstDerivative( i-1, this->order, clampedTime, bvals ); v = this->values[0] - this->values[0]; for ( j = 0; j < this->order; j++ ) { k = i + j - ( this->order >> 1 ); v += bvals[j] * this->ValueForIndex( k ); } return v; } /* ==================== idCurve_NonUniformBSpline::GetCurrentSecondDerivative get the second derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_NonUniformBSpline::GetCurrentSecondDerivative( const float time ) const { int i, j, k; float clampedTime; type v; float *bvals = (float *) _alloca16( this->order * sizeof(float) ); if ( this->times.Num() == 1 ) { return ( this->values[0] - this->values[0] ); } clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); BasisSecondDerivative( i-1, this->order, clampedTime, bvals ); v = this->values[0] - this->values[0]; for ( j = 0; j < this->order; j++ ) { k = i + j - ( this->order >> 1 ); v += bvals[j] * this->ValueForIndex( k ); } return v; } /* ==================== idCurve_NonUniformBSpline::Basis spline basis functions ==================== */ template< class type > ID_INLINE void idCurve_NonUniformBSpline::Basis( const int index, const int order, const float t, float *bvals ) const { int r, s, i; float omega; bvals[order-1] = 1.0f; for ( r = 2; r <= order; r++ ) { i = index - r + 1; bvals[order - r] = 0.0f; for ( s = order - r + 1; s < order; s++ ) { i++; omega = (float) ( t - this->TimeForIndex( i ) ) / ( this->TimeForIndex( i + r - 1 ) - this->TimeForIndex( i ) ); bvals[s - 1] += ( 1.0f - omega ) * bvals[s]; bvals[s] *= omega; } } } /* ==================== idCurve_NonUniformBSpline::BasisFirstDerivative first derivative of spline basis functions ==================== */ template< class type > ID_INLINE void idCurve_NonUniformBSpline::BasisFirstDerivative( const int index, const int order, const float t, float *bvals ) const { int i; Basis( index, order-1, t, bvals+1 ); bvals[0] = 0.0f; for ( i = 0; i < order-1; i++ ) { bvals[i] -= bvals[i+1]; bvals[i] *= (float) ( order - 1) / ( this->TimeForIndex( index + i + (order-1) - 2 ) - this->TimeForIndex( index + i - 2 ) ); } bvals[i] *= (float) ( order - 1) / ( this->TimeForIndex( index + i + (order-1) - 2 ) - this->TimeForIndex( index + i - 2 ) ); } /* ==================== idCurve_NonUniformBSpline::BasisSecondDerivative second derivative of spline basis functions ==================== */ template< class type > ID_INLINE void idCurve_NonUniformBSpline::BasisSecondDerivative( const int index, const int order, const float t, float *bvals ) const { int i; BasisFirstDerivative( index, order-1, t, bvals+1 ); bvals[0] = 0.0f; for ( i = 0; i < order-1; i++ ) { bvals[i] -= bvals[i+1]; bvals[i] *= (float) ( order - 1) / ( this->TimeForIndex( index + i + (order-1) - 2 ) - this->TimeForIndex( index + i - 2 ) ); } bvals[i] *= (float) ( order - 1) / ( this->TimeForIndex( index + i + (order-1) - 2 ) - this->TimeForIndex( index + i - 2 ) ); } /* =============================================================================== Non-Uniform Rational B-Spline (NURBS) template. =============================================================================== */ template< class type > class idCurve_NURBS : public idCurve_NonUniformBSpline { public: idCurve_NURBS( void ); virtual int AddValue( const float time, const type &value ); virtual int AddValue( const float time, const type &value, const float weight ); virtual void RemoveIndex( const int index ) { this->values.RemoveIndex(index); this->times.RemoveIndex(index); weights.RemoveIndex(index); } virtual void Clear( void ) { this->values.Clear(); this->times.Clear(); weights.Clear(); this->currentIndex = -1; } virtual type GetCurrentValue( const float time ) const; virtual type GetCurrentFirstDerivative( const float time ) const; virtual type GetCurrentSecondDerivative( const float time ) const; protected: idList weights; float WeightForIndex( const int index ) const; }; /* ==================== idCurve_NURBS::idCurve_NURBS ==================== */ template< class type > ID_INLINE idCurve_NURBS::idCurve_NURBS( void ) { } /* ==================== idCurve_NURBS::AddValue add a timed/value pair to the spline returns the index to the inserted pair ==================== */ template< class type > ID_INLINE int idCurve_NURBS::AddValue( const float time, const type &value ) { int i; i = this->IndexForTime( time ); this->times.Insert( time, i ); this->values.Insert( value, i ); weights.Insert( 1.0f, i ); return i; } /* ==================== idCurve_NURBS::AddValue add a timed/value pair to the spline returns the index to the inserted pair ==================== */ template< class type > ID_INLINE int idCurve_NURBS::AddValue( const float time, const type &value, const float weight ) { int i; i = this->IndexForTime( time ); this->times.Insert( time, i ); this->values.Insert( value, i ); weights.Insert( weight, i ); return i; } /* ==================== idCurve_NURBS::GetCurrentValue get the value for the given time ==================== */ template< class type > ID_INLINE type idCurve_NURBS::GetCurrentValue( const float time ) const { int i, j, k; float w, b, *bvals, clampedTime; type v; if ( this->times.Num() == 1 ) { return this->values[0]; } bvals = (float *) _alloca16( this->order * sizeof(float) ); clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); this->Basis( i-1, this->order, clampedTime, bvals ); v = this->values[0] - this->values[0]; w = 0.0f; for ( j = 0; j < this->order; j++ ) { k = i + j - ( this->order >> 1 ); b = bvals[j] * WeightForIndex( k ); w += b; v += b * this->ValueForIndex( k ); } return v / w; } /* ==================== idCurve_NURBS::GetCurrentFirstDerivative get the first derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_NURBS::GetCurrentFirstDerivative( const float time ) const { int i, j, k; float w, wb, wd1, b, d1, *bvals, *d1vals, clampedTime; type v, vb, vd1; if ( this->times.Num() == 1 ) { return this->values[0]; } bvals = (float *) _alloca16( this->order * sizeof(float) ); d1vals = (float *) _alloca16( this->order * sizeof(float) ); clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); this->Basis( i-1, this->order, clampedTime, bvals ); this->BasisFirstDerivative( i-1, this->order, clampedTime, d1vals ); vb = vd1 = this->values[0] - this->values[0]; wb = wd1 = 0.0f; for ( j = 0; j < this->order; j++ ) { k = i + j - ( this->order >> 1 ); w = WeightForIndex( k ); b = bvals[j] * w; d1 = d1vals[j] * w; wb += b; wd1 += d1; v = this->ValueForIndex( k ); vb += b * v; vd1 += d1 * v; } return ( wb * vd1 - vb * wd1 ) / ( wb * wb ); } /* ==================== idCurve_NURBS::GetCurrentSecondDerivative get the second derivative for the given time ==================== */ template< class type > ID_INLINE type idCurve_NURBS::GetCurrentSecondDerivative( const float time ) const { int i, j, k; float w, wb, wd1, wd2, b, d1, d2, *bvals, *d1vals, *d2vals, clampedTime; type v, vb, vd1, vd2; if ( this->times.Num() == 1 ) { return this->values[0]; } bvals = (float *) _alloca16( this->order * sizeof(float) ); d1vals = (float *) _alloca16( this->order * sizeof(float) ); d2vals = (float *) _alloca16( this->order * sizeof(float) ); clampedTime = this->ClampedTime( time ); i = this->IndexForTime( clampedTime ); this->Basis( i-1, this->order, clampedTime, bvals ); this->BasisFirstDerivative( i-1, this->order, clampedTime, d1vals ); this->BasisSecondDerivative( i-1, this->order, clampedTime, d2vals ); vb = vd1 = vd2 = this->values[0] - this->values[0]; wb = wd1 = wd2 = 0.0f; for ( j = 0; j < this->order; j++ ) { k = i + j - ( this->order >> 1 ); w = WeightForIndex( k ); b = bvals[j] * w; d1 = d1vals[j] * w; d2 = d2vals[j] * w; wb += b; wd1 += d1; wd2 += d2; v = this->ValueForIndex( k ); vb += b * v; vd1 += d1 * v; vd2 += d2 * v; } return ( ( wb * wb ) * ( wb * vd2 - vb * wd2 ) - ( wb * vd1 - vb * wd1 ) * 2.0f * wb * wd1 ) / ( wb * wb * wb * wb ); } /* ==================== idCurve_NURBS::WeightForIndex get the weight for the given index ==================== */ template< class type > ID_INLINE float idCurve_NURBS::WeightForIndex( const int index ) const { int n = weights.Num()-1; if ( index < 0 ) { if ( this->boundaryType == idCurve_Spline::BT_CLOSED ) { return weights[ weights.Num() + index % weights.Num() ]; } else { return weights[0] + index * ( weights[1] - weights[0] ); } } else if ( index > n ) { if ( this->boundaryType == idCurve_Spline::BT_CLOSED ) { return weights[ index % weights.Num() ]; } else { return weights[n] + ( index - n ) * ( weights[n] - weights[n-1] ); } } return weights[index]; } #endif /* !__MATH_CURVE_H__ */