Game Accessibility Library logo SourceForge.net Logo
Game Accessibility Suite: CAT/CATCurveFit.cpp Source File

CATCurveFit.cpp

Go to the documentation of this file.
00001 /// \file CATCurveFit.cpp
00002 /// \brief Fit a curve to a set of points
00003 /// \ingroup CAT
00004 ///
00005 /// Copyright (c) 2002-2008 by Michael Ellison.
00006 /// See COPYING.txt for the \ref gaslicense License (MIT License).
00007 ///
00008 // $Author: mikeellison $
00009 // $Date: 2008-01-19 19:19:35 -0600 (Sat, 19 Jan 2008) $
00010 // $Revision:   $
00011 // $NoKeywords: $
00012 
00013 #include "CATCurveFit.h"
00014 
00015 CATCurveFit::CATCurveFit(CATUInt32 degree)
00016 {
00017     fDirty  = true;
00018     if (degree < 3)
00019     {
00020         CATASSERT(degree >= 3,"Degree must be 3 or greater for curve fitting. Use CATLineFit for lines.");
00021         degree = 3;
00022     }
00023     fDegree = degree;
00024 }
00025 
00026 CATCurveFit::~CATCurveFit()
00027 {
00028     Clear();
00029 }
00030 
00031 bool CATCurveFit::AddPoint( CATFloat64 x, CATFloat64 y)
00032 {   
00033     fPointList.push_back(CATPoint(x,y));
00034     fDirty = true;
00035     return true;
00036 }
00037 
00038 bool CATCurveFit::Clear()
00039 {   
00040     // Clear all the Lists...
00041     fCoef.clear();
00042     fPointList.clear(); 
00043 
00044     // Set dirty flag
00045     fDirty = true;
00046     return true;
00047 }
00048 
00049 
00050 bool CATCurveFit::GetDegree(CATUInt32 &degree)
00051 {
00052     degree = 0;
00053 
00054     // Recalc if table is dirty
00055     if (fDirty)
00056     {
00057         if (!CalcFit())
00058         {
00059             return false;
00060         }
00061     }
00062 
00063     // return number of coefficients
00064     degree = (CATUInt32)fCoef.size();
00065     return degree > 0;
00066 }
00067 
00068 bool CATCurveFit::GetCoefficient( CATUInt32 deg, CATFloat64& coef)
00069 {
00070     // Recalc if the coefficient table is dirty
00071     if (fDirty)
00072     {
00073         if (!CalcFit())
00074         {
00075             return false;
00076         }
00077     }
00078 
00079     // Bail if degree is invalid
00080     if (deg >= fCoef.size())
00081     {
00082         return false;
00083     }
00084 
00085     // Get the coefficient and return
00086     coef = fCoef[deg];
00087     return true;
00088 }
00089 
00090 
00091 CATUInt32 CATCurveFit::GetNumPoints()
00092 {   
00093     return (CATUInt32)fPointList.size();    
00094 }
00095 
00096 bool CATCurveFit::GetDataPoint( CATUInt32 n, CATFloat64& x, CATFloat64& y)
00097 {   
00098     if (n >= fPointList.size())
00099     {
00100         return false;
00101     }
00102 
00103     CATPoint point = fPointList[n];
00104 
00105     x = point.x;
00106     y = point.y;
00107 
00108     return true;
00109 }
00110 
00111 bool CATCurveFit::GetCurrentErr(CATFloat64 &err)
00112 {
00113     err = 0.0;
00114     // Recalc if the coefficient table is dirty
00115     if (fDirty)
00116     {
00117         if (!CalcFit())
00118         {
00119             return false;
00120         }
00121     }
00122     
00123     // Return the last calc'd error
00124     err = fLastErr;
00125 
00126     return true;
00127 }
00128 
00129 
00130 
00131 bool CATCurveFit::CalcYVal( CATFloat64 x, CATFloat64& y)
00132 {
00133     y = 0.0;
00134     // Recalc if the coefficient table is dirty
00135     if (fDirty)
00136     {
00137         if (!CalcFit())
00138         {
00139             return false;
00140         }
00141     }
00142 
00143     // Calculate Y val from X and calculated coefficients....
00144     CATUInt32 degree = (CATUInt32)fCoef.size();
00145     
00146     for (CATUInt32 i=0; i < degree; i++)
00147     {
00148         CATFloat64 coef = fCoef[i];
00149         y += (coef) * pow(x,(CATFloat64)i);
00150     }
00151     
00152     return true;
00153 }
00154 
00155 
00156 bool CATCurveFit::CalcFit()
00157 {
00158     // Return false if we don't have any points to calculate....
00159     CATUInt32 numPoints = GetNumPoints();
00160     
00161     if (numPoints < fDegree)
00162     {
00163         CATTRACE("Too few points for calc fit.\n");
00164         return false;
00165     }
00166 
00167     // Clear coefficients
00168     void* data=0;
00169 
00170     fCoef.clear();
00171 
00172     this->fLastErr = 0.0;
00173 
00174 
00175     // Setup for whatever type they instantiated.
00176     // Eventually we should try different degrees to find the best fit
00177 
00178     CATMatrix primary(fDegree, (CATUInt32)fPointList.size() ); 
00179     CATMatrix ymatrix(1,       (CATUInt32)fPointList.size() );
00180     
00181     // Setup initial matrix for least-squares approximation.
00182     // primary matrix is setup as:
00183     //
00184     // x1^0  x1^1 ... x1^degree
00185     // x2^0  x2^1 ... x2^degree
00186     // ........................
00187     // xn^0  xn^1 ... xn^degree
00188     //
00189     // where n = number of points
00190     //
00191     // ymatrix is setup as:
00192     //
00193     // y1
00194     // y2
00195     // ..
00196     // yn
00197     //
00198     CATUInt32 i;
00199     for (i=0; i < fPointList.size(); i++)
00200     {
00201         CATPoint curPoint = fPointList[i];  
00202         ymatrix.Val(0,i) = curPoint.y;
00203 
00204         for (CATUInt32 curDegree = 0; curDegree < fDegree; curDegree++)
00205         {
00206             primary.Val(curDegree,i) = pow(curPoint.x,(CATFloat64)curDegree);
00207         }
00208     }
00209 
00210     // Find the pseudoInverse of the primary matrix
00211     CATMatrix pseudoInverse = primary.GetPseudoInverse();
00212     
00213     // Multiply it by the ymatrix
00214     CATMatrix coefs = pseudoInverse * ymatrix;
00215 
00216     // We now have the coefficients
00217     for (i = 0; i < coefs.Height(); i++)
00218     {       
00219         fCoef.push_back(coefs.cVal(0,i));       
00220     }
00221 
00222     // Reset dirty flag
00223     fDirty = false;
00224     return true;
00225 }
00226 
00227 bool CATCurveFit::GetMinMax( CATFloat64& minX, CATFloat64& minY, CATFloat64& maxX, CATFloat64& maxY)
00228 {
00229     CATUInt32 numPoints = GetNumPoints();
00230     if (numPoints == 0)
00231         return false;   
00232 
00233     for (CATUInt32 i=0; i<numPoints; i++)
00234     {
00235         CATPoint curPoint = fPointList[i];
00236 
00237         if (i == 0)
00238         {
00239             minX = maxX = curPoint.x;
00240             minY = maxY = curPoint.y;
00241         }
00242         else
00243         {
00244             minX = min(curPoint.x, minX);
00245             maxX = max(curPoint.x, maxX);
00246             minY = min(curPoint.y, minY);
00247             maxY = max(curPoint.y, maxY);
00248         }
00249     }
00250 
00251     return true;
00252 }
00253 
00254 
00255 
00256 //--------------------------------------------------
00257 // Brute force Langrangian interpolation - not really useful 
00258 // for our needs (too slow and unpredictable with large samples,
00259 // and it doesn't like experimental error much).
00260 // But good for testing the class and display....
00261 bool CATCurveFit::LangrangianCalcY( CATFloat64 x, CATFloat64& y)
00262 {
00263     y = 0.0;
00264     // Calculate Y val from X and calculated coefficients....
00265     CATFloat64 sum = 0;
00266     CATUInt32 degree = (CATUInt32)fPointList.size();    
00267     
00268     for (CATUInt32 i = 0; i < degree; i++)
00269     {
00270         CATPoint pi = fPointList[i];        
00271 
00272         CATFloat64 p = 1;
00273         for (CATUInt32 j=0; j < degree; j++)
00274         {
00275             if (j != i)
00276             {               
00277                 CATPoint pj = fPointList[j];
00278                 p = p * (x - pj.x) / (pi.x - pj.x);
00279             }
00280         }
00281                 
00282         sum += p * pi.y;
00283     }
00284     y = sum;
00285     return true;
00286 }
00287 

Generated on Mon Feb 11 04:09:42 2008 for Game Accessibility Suite by doxygen 1.5.4