etqw-sdk/source/idlib/math/Ode.cpp

331 lines
7.7 KiB
C++

// Copyright (C) 2007 Id Software, Inc.
//
#include "../precompiled.h"
#pragma hdrstop
//===============================================================
//
// idODE_Euler
//
//===============================================================
/*
=============
idODE_Euler::idODE_Euler
=============
*/
idODE_Euler::idODE_Euler( const int dim, deriveFunction_t dr, const void *ud ) {
dimension = dim;
derivatives = new float[dim];
derive = dr;
userData = ud;
}
/*
=============
idODE_Euler::~idODE_Euler
=============
*/
idODE_Euler::~idODE_Euler( void ) {
delete[] derivatives;
}
/*
=============
idODE_Euler::Evaluate
=============
*/
float idODE_Euler::Evaluate( const float *state, float *newState, float t0, float t1 ) {
float delta;
int i;
derive( t0, userData, state, derivatives );
delta = t1 - t0;
for ( i = 0; i < dimension; i++ ) {
newState[i] = state[i] + delta * derivatives[i];
}
return delta;
}
//===============================================================
//
// idODE_Midpoint
//
//===============================================================
/*
=============
idODE_Midpoint::idODE_Midpoint
=============
*/
idODE_Midpoint::idODE_Midpoint( const int dim, deriveFunction_t dr, const void *ud ) {
dimension = dim;
tmpState = new float[dim];
derivatives = new float[dim];
derive = dr;
userData = ud;
}
/*
=============
idODE_Midpoint::~idODE_Midpoint
=============
*/
idODE_Midpoint::~idODE_Midpoint( void ) {
delete tmpState;
delete derivatives;
}
/*
=============
idODE_Midpoint::~Evaluate
=============
*/
float idODE_Midpoint::Evaluate( const float *state, float *newState, float t0, float t1 ) {
double delta, halfDelta;
int i;
delta = t1 - t0;
halfDelta = delta * 0.5;
// first step
derive( t0, userData, state, derivatives );
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + halfDelta * derivatives[i];
}
// second step
derive( t0 + halfDelta, userData, tmpState, derivatives );
for ( i = 0; i < dimension; i++ ) {
newState[i] = state[i] + delta * derivatives[i];
}
return delta;
}
//===============================================================
//
// idODE_RK4
//
//===============================================================
/*
=============
idODE_RK4::idODE_RK4
=============
*/
idODE_RK4::idODE_RK4( const int dim, deriveFunction_t dr, const void *ud ) {
dimension = dim;
derive = dr;
userData = ud;
tmpState = new float[dim];
d1 = new float[dim];
d2 = new float[dim];
d3 = new float[dim];
d4 = new float[dim];
}
/*
=============
idODE_RK4::~idODE_RK4
=============
*/
idODE_RK4::~idODE_RK4( void ) {
delete tmpState;
delete d1;
delete d2;
delete d3;
delete d4;
}
/*
=============
idODE_RK4::Evaluate
=============
*/
float idODE_RK4::Evaluate( const float *state, float *newState, float t0, float t1 ) {
double delta, halfDelta, sixthDelta;
int i;
delta = t1 - t0;
halfDelta = delta * 0.5;
// first step
derive( t0, userData, state, d1 );
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + halfDelta * d1[i];
}
// second step
derive( t0 + halfDelta, userData, tmpState, d2 );
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + halfDelta * d2[i];
}
// third step
derive( t0 + halfDelta, userData, tmpState, d3 );
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + delta * d3[i];
}
// fourth step
derive( t0 + delta, userData, tmpState, d4 );
sixthDelta = delta * (1.0/6.0);
for ( i = 0; i < dimension; i++ ) {
newState[i] = state[i] + sixthDelta * (d1[i] + 2.0 * (d2[i] + d3[i]) + d4[i]);
}
return delta;
}
//===============================================================
//
// idODE_RK4Adaptive
//
//===============================================================
/*
=============
idODE_RK4Adaptive::idODE_RK4Adaptive
=============
*/
idODE_RK4Adaptive::idODE_RK4Adaptive( const int dim, deriveFunction_t dr, const void *ud ) {
dimension = dim;
derive = dr;
userData = ud;
maxError = 0.01f;
tmpState = new float[dim];
d1 = new float[dim];
d1half = new float [dim];
d2 = new float[dim];
d3 = new float[dim];
d4 = new float[dim];
}
/*
=============
idODE_RK4Adaptive::~idODE_RK4Adaptive
=============
*/
idODE_RK4Adaptive::~idODE_RK4Adaptive( void ) {
delete tmpState;
delete d1;
delete d1half;
delete d2;
delete d3;
delete d4;
}
/*
=============
idODE_RK4Adaptive::SetMaxError
=============
*/
void idODE_RK4Adaptive::SetMaxError( const float err ) {
if ( err > 0.0f ) {
maxError = err;
}
}
/*
=============
idODE_RK4Adaptive::Evaluate
=============
*/
float idODE_RK4Adaptive::Evaluate( const float *state, float *newState, float t0, float t1 ) {
double delta, halfDelta, fourthDelta, sixthDelta;
double error, max;
int i, n;
delta = t1 - t0;
for ( n = 0; n < 4; n++ ) {
halfDelta = delta * 0.5;
fourthDelta = delta * 0.25;
// first step of first half delta
derive( t0, userData, state, d1 );
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + fourthDelta * d1[i];
}
// second step of first half delta
derive( t0 + fourthDelta, userData, tmpState, d2 );
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + fourthDelta * d2[i];
}
// third step of first half delta
derive( t0 + fourthDelta, userData, tmpState, d3 );
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + halfDelta * d3[i];
}
// fourth step of first half delta
derive( t0 + halfDelta, userData, tmpState, d4 );
sixthDelta = halfDelta * (1.0/6.0);
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + sixthDelta * (d1[i] + 2.0 * (d2[i] + d3[i]) + d4[i]);
}
// first step of second half delta
derive( t0 + halfDelta, userData, tmpState, d1half );
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + fourthDelta * d1half[i];
}
// second step of second half delta
derive( t0 + halfDelta + fourthDelta, userData, tmpState, d2 );
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + fourthDelta * d2[i];
}
// third step of second half delta
derive( t0 + halfDelta + fourthDelta, userData, tmpState, d3 );
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + halfDelta * d3[i];
}
// fourth step of second half delta
derive( t0 + delta, userData, tmpState, d4 );
sixthDelta = halfDelta * (1.0/6.0);
for ( i = 0; i < dimension; i++ ) {
newState[i] = state[i] + sixthDelta * (d1[i] + 2.0 * (d2[i] + d3[i]) + d4[i]);
}
// first step of full delta
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + halfDelta * d1[i];
}
// second step of full delta
derive( t0 + halfDelta, userData, tmpState, d2 );
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + halfDelta * d2[i];
}
// third step of full delta
derive( t0 + halfDelta, userData, tmpState, d3 );
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + delta * d3[i];
}
// fourth step of full delta
derive( t0 + delta, userData, tmpState, d4 );
sixthDelta = delta * (1.0/6.0);
for ( i = 0; i < dimension; i++ ) {
tmpState[i] = state[i] + sixthDelta * (d1[i] + 2.0 * (d2[i] + d3[i]) + d4[i]);
}
// get max estimated error
max = 0.0;
for ( i = 0; i < dimension; i++ ) {
error = idMath::Fabs( (newState[i] - tmpState[i]) / (delta * d1[i] + 1e-10) );
if ( error > max ) {
max = error;
}
}
error = max / maxError;
if ( error <= 1.0f ) {
return delta * 4.0;
}
if ( delta <= 1e-7 ) {
return delta;
}
delta *= 0.25;
}
return delta;
}