contrib/oul/ouml/CardinalSpline.cxx
Go to the documentation of this file.
00001 #include "CardinalSpline.h"
00002 //:
00003 // \file
00004 #include <vsl/vsl_vector_io.txx>
00005 #include <vcl_cassert.h>
00006 
00007 //: Return a list of points on the boundary of the curve.
00008 // This is intended for drawing the curve as a list of line segments.
00009 // \param int num_points: the number of points to return.
00010 // \return vcl_vector<Vector3D>: a list of points.
00011 vcl_vector<CardinalSpline::Vector3D> CardinalSpline::getPoints(int num_points) const
00012 {
00013   assert(num_points!=0);
00014   vcl_vector<Vector3D> points;
00015   Vector3D current_point;
00016   for (int i=0; i<num_points; i++)
00017   {
00018     current_point = getPoint((double)i/(double)num_points);
00019     points.push_back(current_point);
00020   }
00021   return points;
00022 }
00023 
00024 // Internal helper function. This is a commonly computed value which
00025 // I've just put in its own method for convenience.
00026 CardinalSpline::Vector3D CardinalSpline::getVal(
00027     const vnl_matrix_fixed<double, 1, 4> &uvec, int pk) const
00028 {
00029   // first find the relevant control points associated with t
00030   int n = controlPoints.size();
00031   int pkn1 = (pk+n-1)%n;
00032   int pk1 = (pk+1)%n;
00033   int pk2 = (pk+2)%n;
00034 
00035   vnl_matrix_fixed<double, 1, 4> weightMatrix;
00036   weightMatrix = uvec*Mc;
00037 
00038   // now weight the control points
00039   Vector3D res = controlPoints[pkn1]*weightMatrix(0,0)+
00040                  controlPoints[pk  ]*weightMatrix(0,1)+
00041                  controlPoints[pk1 ]*weightMatrix(0,2)+
00042                  controlPoints[pk2 ]*weightMatrix(0,3);
00043   return res;
00044 }
00045 
00046 //: Returns the relevant point on the spline parameterised by t.
00047 // t should be between 0 and 1, 0 being the start of the curve, 1 at the
00048 // end. Actually, in this implementation, it is a closed curve, so 0
00049 // and 1 will return the same point.
00050 //
00051 // I am currently double checking this, I'm not convinced getPoint,
00052 // firstDeriv etc work near t=0.0 and t=1.0
00053 CardinalSpline::Vector3D CardinalSpline::getPoint(double t) const
00054 {
00055   assert(t>=0.0);
00056   assert(t<=1.0);
00057   // first find the relevant control points associated with t
00058   int n = controlPoints.size();
00059   assert(n!=0);
00060   int pk = ((int)(t*n))%n;
00061   // calculate the parameter u which indicates how far between pk
00062   // and pk1 the wanted point is.
00063   double u = t*n-(int)(t*n);
00064   vnl_matrix<double> uvec(1, 4);
00065   uvec(0,3) = 1;
00066   for (int i=2; i>=0; i--) uvec(0,i) = uvec(0,i+1)*u;
00067   return getVal(uvec, pk);
00068 }
00069 
00070 //: Gradient of the spline functions - ie $[d_c(x)/du d_c(y)/du d_c(z)/du]^T$.
00071 CardinalSpline::Vector3D CardinalSpline::firstDerivative(double t) const
00072 {
00073   assert(t>=0.0);
00074   assert(t<=1.0);
00075   // first find the relevant control points associated with t
00076   int n = controlPoints.size();
00077   int pk = ((int)(t*n))%n;
00078   // calculate the parameter u which indicates how far between pk
00079   // and pk1 the wanted point is.
00080   double u = t*n-(int)(t*n);
00081 
00082   vnl_matrix<double> uvec(1, 4);
00083   uvec(0,3) = 0;
00084   uvec(0,2) = 1;
00085   uvec(0,1) = 2*u;
00086   uvec(0,0) = 3*u*u;
00087   //for (int i=1; i>=0; i--) uvec(0,i) = uvec(0,i+1)*u;
00088   return ((double)n)*getVal(uvec, pk);
00089 }
00090 
00091 //: Second derivative of the spline functions - ie $[d^2_c(x)/du^2 d^2_c(y)/du^2 d^2_c(z)/du^2]^T$.
00092 CardinalSpline::Vector3D CardinalSpline::secondDerivative(double t) const
00093 {
00094   assert(t>=0.0);
00095   assert(t<=1.0);
00096   // first find the relevant control points associated with t
00097   int n = controlPoints.size();
00098   int pk = ((int)(t*n))%n;
00099   // calculate the parameter u which indicates how far between pk
00100   // and pk1 the wanted point is.
00101   double u = t*n-(int)(t*n);
00102 
00103   vnl_matrix<double> uvec(1, 4);
00104   uvec(0,3) = 0;
00105   uvec(0,2) = 0;
00106   uvec(0,1) = 2;
00107   uvec(0,0) = 6*u;
00108   return ((double)(n*n))*getVal(uvec, pk);
00109 }
00110 
00111 //: Derivative of the distance function from a point to the curve at parameter t.
00112 // Useful for finding the closest point to the curve.
00113 double CardinalSpline::distanceFunctionFirstDerivative(double t,
00114                                                        const Vector3D &point) const
00115 {
00116   Vector3D curvePt = getPoint(t);
00117   Vector3D firstDeriv = firstDerivative(t);
00118   Vector3D diff = curvePt-point;
00119   return 2*dot_product(firstDeriv, diff);
00120 }
00121 
00122 double CardinalSpline::distanceFunctionSecondDerivative(double t,
00123                                                         const Vector3D &point) const
00124 {
00125   Vector3D curvePt = getPoint(t);
00126   Vector3D diff = curvePt-point;
00127   Vector3D firstDeriv = firstDerivative(t);
00128   Vector3D secondDeriv = secondDerivative(t);
00129   return 2*dot_product(secondDeriv, diff)
00130        + 2*dot_product(firstDeriv, firstDeriv);
00131 }
00132 
00133 //: Return the t value of the closest point to the input point.
00134 // This is calculated using Newton's method after an initial estimate is
00135 // bracketed using the control points of the spline.
00136 double CardinalSpline::closest_point_t(const Vector3D &point) const
00137 {
00138   // first bracket the t value
00139   // do 10 steps per spline segment
00140   int n = controlPoints.size()*10;
00141   double delta = 1.0/(double)n;
00142   double t1=0.0;
00143   double d1 = (point-getPoint(t1)).magnitude();
00144   for (double t=delta; t<1.0; t+=delta)
00145   {
00146     double dist = (point-getPoint(t)).magnitude();
00147     // replace d1
00148     if (dist<d1)
00149     {
00150       d1 = dist; t1 = t;
00151     }
00152   }
00153 
00154   // now we need to find the second closest neighbouring pt
00155   double t2 = t1+delta;
00156   if (t2>1.0) t2 = 0.0; // wraparound
00157   double t3 = t1-delta;
00158   if (t3<0.0) t3 = 1.0-delta;
00159 
00160   // find closest neighbour
00161   double d2 = (point-getPoint(t2)).magnitude();
00162   double d3 = (point-getPoint(t3)).magnitude();
00163   if (d3<d2)
00164   {
00165     t2 = t3; // d2 = d3;
00166   }
00167 
00168   // now if the neighbours happen to be pt 0 and pt 1-delta, then
00169   // set pt 0 to pt 1
00170   // can only happen if there's a wraparound
00171   if (vcl_fabs(t2-t1)>2.0*delta)
00172   {
00173     if (t2>t1) t1 = 1.0; //  else t2 = 1.0;
00174   }
00175 
00176   // now do a Newton's iteration until we converge
00177   int numloops=0;
00178   delta = distanceFunctionFirstDerivative(t1, point)/
00179           distanceFunctionSecondDerivative(t1, point);
00180   for (double t = t1 /* (d2*t1+d1*t2)/(d1+d2) */;
00181                      // do a simple interpolation
00182        vcl_fabs(delta)>1e-8;
00183        delta = distanceFunctionFirstDerivative(t, point)/
00184                distanceFunctionSecondDerivative(t, point)
00185       )
00186   {
00187     t -= delta;
00188     if (t<0.0) t += 1.0;
00189     if (t>1.0) t -= 1.0;
00190     double dist = (point-getPoint(t)).magnitude();
00191     // if the following condition is true, then Newton's method
00192     // is not converging (or at least not my implementation),
00193     // so just return the closest point found so far
00194     if (dist>d1)
00195     {
00196       vcl_cerr << "Closest point not converging:\n"
00197                << " t = " << t1
00198                << ", dist = " << dist << ", d1 = " << d1
00199                << ", delta = " << delta << vcl_endl;
00200       break;
00201     }
00202     else if (dist<d1)
00203     {
00204       t1 = t; d1 = dist;
00205     }
00206     if (++numloops>50)
00207       assert(false);
00208   }
00209 
00210   return t1;
00211 }
00212 
00213 //
00214 // binary I/O
00215 //
00216 
00217 VSL_VECTOR_IO_INSTANTIATE(CardinalSpline::Vector3D);
00218 
00219 void CardinalSpline::b_write(vsl_b_ostream &os) const
00220 {
00221   vsl_b_write(os, controlPoints);
00222   vsl_b_write(os, Mc);
00223   vsl_b_write(os, s);
00224 }
00225 
00226 void CardinalSpline::b_read(vsl_b_istream &is)
00227 {
00228   vsl_b_read(is, controlPoints);
00229   vsl_b_read(is, Mc);
00230   vsl_b_read(is, s);
00231 }
00232 
00233 void vsl_b_write(vsl_b_ostream &os, const CardinalSpline &e)
00234 {
00235   e.b_write(os);
00236 }
00237 
00238 void vsl_b_read(vsl_b_istream &is, CardinalSpline &e)
00239 {
00240   e.b_read(is);
00241 }
00242 
00243 void vsl_print_summary(vcl_ostream &os, const CardinalSpline &e)
00244 {
00245   e.print_summary(os);
00246 }