dhewm3/neo/idlib/math/Curve.h
Daniel Gibson b756276b54 ID_MAYBE_INLINE for not-forced inlining
On Windows, ID_INLINE does __forceinline, which is bad if the function
calls alloca() and is called in a loop..
So use just __inline there so the compiler can choose not to inline
(if called in a loop).
This didn't cause actual stack overflows as far as I know, but it could
(and MSVC warns about it).
2020-01-11 16:15:34 +01:00

2545 lines
71 KiB
C++

/*
===========================================================================
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 <http://www.gnu.org/licenses/>.
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<float> times; // knots
idList<type> 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<type>::idCurve( void ) {
currentIndex = -1;
changed = false;
}
/*
====================
idCurve::~idCurve
====================
*/
template< class type >
ID_INLINE idCurve<type>::~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<type>::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<type>::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<type>::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<type>::GetCurrentSecondDerivative( const float time ) const {
return ( values[0] - values[0] );
}
/*
====================
idCurve::IsDone
====================
*/
template< class type >
ID_INLINE bool idCurve<type>::IsDone( const float time ) const {
return ( time >= times[ times.Num() - 1 ] );
}
/*
====================
idCurve::GetSpeed
====================
*/
template< class type >
ID_INLINE float idCurve<type>::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_MAYBE_INLINE float idCurve<type>::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_MAYBE_INLINE float idCurve<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type> {
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<type>::idCurve_Bezier( void ) {
}
/*
====================
idCurve_Bezier::GetCurrentValue
get the value for the given time
====================
*/
template< class type >
ID_INLINE type idCurve_Bezier<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type> {
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<type>::idCurve_QuadraticBezier( void ) {
}
/*
====================
idCurve_QuadraticBezier::GetCurrentValue
get the value for the given time
====================
*/
template< class type >
ID_INLINE type idCurve_QuadraticBezier<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type> {
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<type>::idCurve_CubicBezier( void ) {
}
/*
====================
idCurve_CubicBezier::GetCurrentValue
get the value for the given time
====================
*/
template< class type >
ID_INLINE type idCurve_CubicBezier<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type> {
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<type>::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<type>::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<type>::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<type>::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<type>::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<type> {
public:
idCurve_NaturalCubicSpline( void );
virtual void Clear( void ) { idCurve_Spline<type>::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 idList<type>b;
mutable idList<type>c;
mutable idList<type>d;
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<type>::idCurve_NaturalCubicSpline( void ) {
}
/*
====================
idCurve_NaturalCubicSpline::GetCurrentValue
get the value for the given time
====================
*/
template< class type >
ID_INLINE type idCurve_NaturalCubicSpline<type>::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<type>::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<type>::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<type>::Setup( void ) const {
if ( this->changed ) {
switch( this->boundaryType ) {
case idCurve_Spline<type>::BT_FREE: SetupFree(); break;
case idCurve_Spline<type>::BT_CLAMPED: SetupClamped(); break;
case idCurve_Spline<type>::BT_CLOSED: SetupClosed(); break;
}
this->changed = false;
}
}
/*
====================
idCurve_NaturalCubicSpline::SetupFree
====================
*/
template< class type >
ID_INLINE void idCurve_NaturalCubicSpline<type>::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<type>::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<type>::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<type> {
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<type>::idCurve_CatmullRomSpline( void ) {
}
/*
====================
idCurve_CatmullRomSpline::GetCurrentValue
get the value for the given time
====================
*/
template< class type >
ID_INLINE type idCurve_CatmullRomSpline<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type> {
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<float> tension;
idList<float> continuity;
idList<float> 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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type> {
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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type> {
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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type> {
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<type>::idCurve_NonUniformBSpline( void ) {
}
/*
====================
idCurve_NonUniformBSpline::GetCurrentValue
get the value for the given time
====================
*/
template< class type >
ID_INLINE type idCurve_NonUniformBSpline<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type> {
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<float> weights;
float WeightForIndex( const int index ) const;
};
/*
====================
idCurve_NURBS::idCurve_NURBS
====================
*/
template< class type >
ID_INLINE idCurve_NURBS<type>::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<type>::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<type>::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<type>::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<type>::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<type>::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<type>::WeightForIndex( const int index ) const {
int n = weights.Num()-1;
if ( index < 0 ) {
if ( this->boundaryType == idCurve_Spline<type>::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<type>::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__ */