Fawkes API
Fawkes Development Version
|
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