spline.cpp

00001 
00002 /***************************************************************************
00003  *  spline.cpp - Cubic spline curve
00004  *
00005  *  Created: Tue Oct 07 19:03:51 2008
00006  *  Copyright  2008  Daniel Beck
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <geometry/spline.h>
00025 #include <geometry/hom_vector.h>
00026 #include <cmath>
00027 
00028 using namespace std;
00029 
00030 namespace fawkes {
00031 
00032 /** @class fawkes::Spline <geometry/spline.h>
00033  * A spline made up of cubic Bezier curves.
00034  * @author Daniel Beck
00035  */
00036 
00037 /** Constructor. */
00038 Spline::Spline()
00039 {
00040   m_num_subdivisions = 0;
00041 }
00042 
00043 /** Constructor.
00044  * @param control_points the control points of the spline
00045  */
00046 Spline::Spline(const vector<HomPoint>& control_points)
00047   : m_control_points(control_points)
00048 {
00049   m_num_subdivisions = 0;
00050 
00051   register_primitives();
00052   construct_bezier_curves();
00053 }
00054 
00055 /** Destructor. */
00056 Spline::~Spline()
00057 {
00058 }
00059 
00060 /** Set the control points.
00061  * @param control_points the new control points
00062  */
00063 void
00064 Spline::set_control_points(const vector<HomPoint>& control_points)
00065 {
00066   m_control_points = control_points;
00067 
00068   clear_primitives();
00069   register_primitives();
00070   construct_bezier_curves();
00071 }
00072 
00073 /** Set a specific control point.
00074  * @param i the index of the control point
00075  * @param point the replacement control point
00076  */
00077 void
00078 Spline::set_control_point(unsigned int i, const HomPoint& point)
00079 {
00080   m_control_points[i] = point;
00081 
00082   clear_primitives();
00083   register_primitives();
00084   construct_bezier_curves();
00085 }
00086 
00087 /** Get the control points.
00088  * @return const reference to the current control points
00089  */
00090 const vector<HomPoint>&
00091 Spline::get_control_points() const
00092 {
00093   return m_control_points;
00094 }
00095 
00096 /** Get the Bezier curves.
00097  * @return const reference to the current Bezier curves
00098  */
00099 const vector<Bezier>&
00100 Spline::get_bezier_curves() const
00101 {
00102   return m_bezier_curves;
00103 }
00104 
00105 /** Get a point on the curve for a specified segment and value t.
00106  * @param bezier_index the index of the curve
00107  * @param t a value between 0.0 and 1.0
00108  * @return a point on the i-th curve for the parameter t
00109  */
00110 HomPoint
00111 Spline::eval(unsigned int bezier_index, float t)
00112 {
00113   return m_bezier_curves[bezier_index].eval(t);
00114 }
00115 
00116 /** Compute the tangent vector at position t of the i-th Bezier curve.
00117  * @param bezier_index the index of the Bezier patch
00118  * @param t the curve parameter t
00119  * @return the tangent vector
00120  */
00121 HomVector
00122 Spline::tangent(unsigned int bezier_index, float t)
00123 {
00124   return m_bezier_curves[bezier_index].tangent_at_t(t);
00125 }
00126 
00127 /** Compute the tangent vector at the specified approximation point.
00128  * The range of the index is determined by number of subidivisions
00129  * that have been performed during the last approximation.
00130  * @param point_index index of the approximation point
00131  * @return tangent vector
00132  */
00133 HomVector
00134 Spline::tangent(unsigned int point_index)
00135 {
00136   unsigned int points_per_bezier = (unsigned int) rint( powf(2.0, m_num_subdivisions) ) * 3;
00137   unsigned int points_total = m_bezier_curves.size() * (points_per_bezier - 1) + 1;
00138 
00139   if (point_index >= points_total)
00140     { return m_bezier_curves.back().tangent_at_t(1.0); }
00141   else
00142     {
00143       unsigned int bezier_index = point_index / points_per_bezier;
00144       unsigned int index = point_index - bezier_index * points_per_bezier;
00145       
00146       return m_bezier_curves[bezier_index].tangent_at_t( index / (float) points_per_bezier );
00147     }
00148 }
00149 
00150 /** Get linear approximation of the curve.
00151  * Instead of evaluating the Bezier polynoms at constant intervals the
00152  * Bezier curves are subdivided and the control points are taken as an
00153  * approximation. This is more efficient and yields better
00154  * results. The control points converge to the curve quadratically in
00155  * the number of subdivisions.
00156  * @param num_subdivisions the number of subdivision that shall be
00157  * performed.
00158  * @return points approximating the curve
00159  */
00160 vector<HomPoint>
00161 Spline::approximate(unsigned int num_subdivisions)
00162 {
00163   vector<HomPoint> approximation;
00164 
00165   for ( vector<Bezier>::iterator bit = m_bezier_curves.begin();
00166         bit != m_bezier_curves.end();
00167         ++bit )
00168     {
00169       vector<HomPoint> points = bit->approximate(num_subdivisions);
00170       vector<HomPoint>::iterator pit = points.begin();
00171 
00172       if ( bit != m_bezier_curves.begin() )
00173         // skip first control point for all Bezier curves except for
00174         // the first one
00175         { ++pit; }
00176 
00177       for ( vector<HomPoint>::iterator iter = pit;
00178             iter != points.end();
00179             ++iter )
00180         {
00181           approximation.push_back( *iter );
00182         }
00183     }
00184 
00185   m_num_subdivisions = num_subdivisions;
00186 
00187   return approximation;
00188 }
00189 
00190 void
00191 Spline::register_primitives()
00192 {
00193   vector<HomPoint>::iterator iter;
00194   for ( iter  = m_control_points.begin();
00195         iter != m_control_points.end();
00196         ++iter )
00197     {
00198       HomCoord& c = *iter;
00199       add_primitive( &c );
00200     }
00201 }
00202 
00203 void
00204 Spline::post_transform()
00205 {
00206   construct_bezier_curves();
00207 }
00208 
00209 void
00210 Spline::construct_bezier_curves()
00211 {
00212   m_bezier_curves.clear();
00213 
00214   if ( 0 == m_control_points.size() )
00215   { return; }
00216 
00217   vector<HomPoint>::iterator i    = m_control_points.begin();
00218   vector<HomPoint>::iterator prev = i;
00219   vector<HomPoint>::iterator cur  = ++i;
00220   vector<HomPoint>::iterator next = ++i;
00221 
00222   HomPoint cp1 = (*prev);
00223 
00224   while ( cur != m_control_points.end() )
00225     {
00226       HomPoint cp2;
00227       HomPoint cp3;
00228       HomPoint cp4;
00229 
00230       HomVector v;
00231 
00232       v = (*cur) - (*prev);
00233       
00234       cp2 = (*prev) + v * (1.0 / 3.0);
00235       cp3 = (*prev) + v * (2.0 / 3.0);
00236 
00237       if ( next == m_control_points.end() )
00238         { cp4 = *cur; }
00239       else
00240         {
00241           HomPoint t;
00242 
00243           v = (*next) - (*cur);
00244           t = (*cur) + v * (1.0 / 3.0);
00245           cp4 = cp3 + (t - cp3) * 0.5;
00246         }
00247 
00248       vector<HomPoint> control_points;
00249       control_points.push_back(cp1);
00250       control_points.push_back(cp2);
00251       control_points.push_back(cp3);
00252       control_points.push_back(cp4);
00253       
00254       m_bezier_curves.push_back( Bezier(control_points) );
00255 
00256       cp1 = cp4;
00257       ++prev;
00258       ++cur;
00259       ++next;
00260     }
00261 }
00262 
00263 } // end namespace fawkes

Generated on Tue Feb 22 13:31:26 2011 for Fawkes API by  doxygen 1.4.7