src/vamos/geometry/Spline.cc

Go to the documentation of this file.
00001 // Spline.cc - a cubic spline interpolator.
00002 //
00003 //  Vamos Automotive Simulator
00004 //  Copyright (C) 2001--2004 Sam Varner
00005 //
00006 //  This program is free software; you can redistribute it and/or modify
00007 //  it under the terms of the GNU General Public License as published by
00008 //  the Free Software Foundation; either version 2 of the License, or
00009 //  (at your option) any later version.
00010 //
00011 //  This program is distributed in the hope that it will be useful,
00012 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 //  GNU General Public License for more details.
00015 //
00016 //  You should have received a copy of the GNU General Public License
00017 //  along with this program; if not, write to the Free Software
00018 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 #include <vamos/geometry/Spline.h>
00021 
00022 #include <cmath>
00023 #include <cassert>
00024 
00025 // Construct an empty curve.
00026 Vamos_Geometry::
00027 Spline::Spline (double first_slope, double last_slope) :
00028   m_first_slope (first_slope),
00029   m_last_slope (last_slope),
00030   m_calculated (false),
00031   m_slope (0.0)
00032 {
00033 }
00034 
00035 // Construct a cuvre from an array of points.
00036 Vamos_Geometry::
00037 Spline::Spline (const std::vector <Two_Point>& points,
00038                 double first_slope, double last_slope) :
00039   m_first_slope (first_slope),
00040   m_last_slope (last_slope),
00041   m_calculated (false),
00042   m_slope (0.0)
00043 {
00044   clear ();
00045   load (points);
00046 }
00047 
00048 // Add a point to the curve.
00049 void Vamos_Geometry::
00050 Spline::load (const Two_Point& point)
00051 {
00052   m_points.push_back (point);
00053   m_calculated = false;
00054 }
00055 
00056 // Add multiple points to the curve.
00057 void Vamos_Geometry::
00058 Spline::load (const std::vector <Two_Point>& points)
00059 {
00060   for (std::vector <Two_Point>::const_iterator it = points.begin ();
00061        it != points.end ();
00062        it++)
00063     {
00064       m_points.push_back (*it);
00065     }
00066   m_calculated = false;
00067 }
00068 
00069 // Remove all points from the curve.
00070 void Vamos_Geometry::
00071 Spline::clear ()
00072 {
00073   m_points.clear ();
00074   m_calculated = false;
00075 }
00076 
00077 // Remove points with x > LIMIT.
00078 void Vamos_Geometry::
00079 Spline::remove_greater (double limit)
00080 {
00081   size_t size = 0;
00082   for (std::vector <Two_Point>::const_iterator it = m_points.begin ();
00083        it != m_points.end ();
00084        it++)
00085     {
00086       if (it->x > limit)
00087         {
00088           m_points.resize (size);
00089           break;
00090         }
00091       size++;
00092     }
00093   m_calculated = false;
00094 }
00095 
00096 // Scale all of the x values by FACTOR.
00097 void Vamos_Geometry::
00098 Spline::scale (double factor)
00099 {
00100   for (std::vector <Two_Point>::iterator it = m_points.begin ();
00101        it != m_points.end ();
00102        it++)
00103     {
00104       it->x *= factor;
00105     }
00106 
00107   m_calculated = false;
00108 }
00109 
00110 // calculate() and interpolate() follow the discussion on cubic
00111 // splines found in Numerical Recipes.  The implementation here is
00112 // original. 
00113 
00114 // Return the y value at the x value DISTANCE
00115 double Vamos_Geometry::
00116 Spline::interpolate (double distance) const
00117 {
00118   if (m_points.size () == 1)
00119     {
00120       m_slope = 0.0;
00121       return m_points [0].y;
00122     }
00123 
00124   // calculate() only needs to be called once for a given set of
00125   // points.
00126   if (!m_calculated)
00127     calculate ();
00128 
00129 
00130   size_t low = 0;
00131   size_t high = m_points.size () - 1;
00132   size_t index;
00133 
00134   // Bisect to find the interval that distance is on.
00135   while ((high - low) > 1)
00136     {
00137       index = size_t ((high + low) / 2.0);
00138       if (m_points [index].x > distance)
00139         high = index;
00140       else
00141         low = index;
00142     }
00143 
00144   // Make sure that x_high > x_low.
00145   const double diff = m_points [high].x - m_points [low].x;
00146   assert (diff >= 0.0);
00147 
00148   // Evaluate the coefficients for the cubic spline equation.
00149   const double a = (m_points [high].x - distance) / diff;
00150   const double b = 1.0 - a;
00151   const double sq = diff*diff / 6.0;
00152   const double a2 = a*a;
00153   const double b2 = b*b;
00154 
00155   // Find the first derivitive.
00156   m_slope =
00157  (m_points [high].y - m_points [low].y)/diff
00158     - (3.0 * a2- 1.0) / 6.0 * diff * m_second_deriv [low]
00159     + (3.0 * b2 - 1.0) / 6.0 * diff * m_second_deriv [high];
00160 
00161   // Return the interpolated value.
00162   return a * m_points [low].y 
00163     + b * m_points [high].y 
00164     + a * (a2 - 1.0) * sq * m_second_deriv [low] 
00165     + b * (b2 - 1.0) * sq * m_second_deriv [high];
00166 }
00167 
00168 
00169 // Calculate the coefficients for interpolation.
00170 void Vamos_Geometry::
00171 Spline::calculate () const
00172 {
00173   size_t n = m_points.size ();
00174   double* a = new double [n];
00175   double* b = new double [n];
00176   double* c = new double [n];
00177   double* r = new double [n];
00178 
00179   // Fill in the arrays that represent the tridiagonal matrix.
00180   // a [0] is not used. 
00181   double diff = m_points [1].x - m_points [0].x;
00182   b [0] = diff / 3.0;
00183   c [0] = diff / 6.0;
00184   r [0] = (m_points [1].y - m_points [0].y) / diff - m_first_slope;
00185     
00186   for (size_t i = 1; i < n - 1; i++)
00187     {
00188       double diff1 = m_points [i+1].x - m_points [i].x;
00189       double diff2 = m_points [i].x - m_points [i-1].x;
00190 
00191       a [i] = diff2 / 6.0;
00192       b [i] = (m_points [i+1].x - m_points [i-1].x) / 3.0;
00193       c [i] = diff1 / 6.0;
00194       r [i] = (m_points [i+1].y - m_points [i].y) / diff1
00195         - (m_points [i].y - m_points [i-1].y) / diff2;
00196     }
00197 
00198   diff = m_points [n-1].x - m_points [n-2].x;
00199   a [n-1] = diff / 6.0;
00200   b [n-1] = diff / 3.0;
00201   // c [n-1] is not used.
00202   r [n-1] = m_last_slope - (m_points [n-1].y - m_points [n-2]).y / diff;
00203     
00204     // Gauss-Jordan Elimination
00205     for (size_t i = 1; i < n; i++)
00206     {
00207         // Replace row i with row i - k * row (i-1) such that A_{i,i-1} = 0.0.
00208         double factor = a [i] / b [i-1];
00209         // A_{i,i-1} is not used again, so it need not be calculated.
00210         b [i] -= factor * c [i-1];
00211         // A_{i,i+1} is unchanged because A_{i-1,i+1} = 0.0.
00212         r [i] -= factor * r [i-1];
00213     }
00214 
00215     // Back-Substitution
00216 
00217     // Solve for y"[N].
00218     m_second_deriv.resize (n);
00219     m_second_deriv [n-1] = r [n-1] / b [n-1];
00220     for (int i = n - 2; i >= 0; i--)
00221     {
00222         // Use the solution for y"[i+1] to find y"[i].
00223         m_second_deriv [i] = (r [i] - c [i] * m_second_deriv [i+1]) / b [i];
00224     }
00225 
00226     delete [] r;
00227     delete [] c;
00228     delete [] b;
00229     delete [] a;
00230 
00231   m_calculated = true;
00232 }
00233 
00234 // Return the normal to the tanget at DISTANCE.
00235 Vamos_Geometry::Two_Point Vamos_Geometry::
00236 Spline::normal (double distance) const
00237 {
00238   interpolate (distance);
00239   double theta = std::atan (m_slope);
00240   return Two_Point (-std::sin (theta), std::cos (theta));
00241 }

Generated on Thu Oct 19 04:05:55 2006 by  doxygen 1.4.6