core/vgl/vgl_conic.txx
Go to the documentation of this file.
00001 // This is core/vgl/vgl_conic.txx
00002 #ifndef vgl_conic_txx_
00003 #define vgl_conic_txx_
00004 //:
00005 // \file
00006 // Written by Peter Vanroose, ESAT, K.U.Leuven, Belgium, 30 August 2001.
00007 
00008 #include "vgl_conic.h"
00009 
00010 #include <vcl_cmath.h>
00011 #include <vcl_iostream.h>
00012 #include <vcl_compiler.h>
00013 
00014 static const char *vgl_conic_name[] =
00015 {
00016   "invalid conic",
00017   "real ellipse",
00018   "real circle",
00019   "imaginary ellipse",
00020   "imaginary circle",
00021   "hyperbola",
00022   "parabola",
00023   "real intersecting lines",
00024   "complex intersecting lines",
00025   "real parallel lines",
00026   "complex parallel lines",
00027   "coincident lines"
00028 };
00029 
00030 
00031 //--------------------------------------------------------------
00032 //: Returns the type name of the conic.
00033 
00034 template <class T>
00035 vcl_string vgl_conic<T>::real_type() const { return vgl_conic_name[(int)type_]; }
00036 
00037 template <class T>
00038 typename vgl_conic<T>::vgl_conic_type vgl_conic<T>::type_by_name(vcl_string const& name)
00039 {
00040   for (int i = (int)no_type; i < num_conic_types; i++)
00041     if (name == vgl_conic_name[i])
00042       return (typename vgl_conic<T>::vgl_conic_type)i;
00043   return no_type; // should never reach this point
00044 }
00045 
00046 template <class T>
00047 vcl_string vgl_conic<T>::type_by_number(typename vgl_conic<T>::vgl_conic_type type)
00048 {
00049   if (type <= 0 || type >= num_conic_types) return vgl_conic_name[no_type];
00050   return vgl_conic_name[type];
00051 }
00052 
00053 //-------------------------------------------------------------
00054 //: equality test
00055 template <class T>
00056 bool vgl_conic<T>::operator==(vgl_conic<T> const& that) const
00057 {
00058   if ( type() != that.type() ) return false;
00059   return   a()*that.b() == b()*that.a()
00060         && a()*that.c() == c()*that.a()
00061         && a()*that.d() == d()*that.a()
00062         && a()*that.e() == e()*that.a()
00063         && a()*that.f() == f()*that.a()
00064         && b()*that.c() == c()*that.b()
00065         && b()*that.d() == d()*that.b()
00066         && b()*that.e() == e()*that.b()
00067         && b()*that.f() == f()*that.b()
00068         && c()*that.d() == d()*that.c()
00069         && c()*that.e() == e()*that.c()
00070         && c()*that.f() == f()*that.c()
00071         && d()*that.e() == e()*that.d()
00072         && d()*that.f() == f()*that.d()
00073         && e()*that.f() == f()*that.e();
00074 }
00075 
00076 //-------------------------------------------------------------
00077 //: set values
00078 template <class T>
00079 void vgl_conic<T>::set(T ta, T tb, T tc, T td, T te, T tf)
00080 {
00081   a_ = ta; b_ = tb; c_ = tc; d_ = td; e_ = te; f_ = tf;
00082   set_type_from_equation();
00083 }
00084 
00085 //-------------------------------------------------------------
00086 //: constructor using polynomial coefficients.
00087 template <class T>
00088 vgl_conic<T>::vgl_conic(T const co[])
00089   : type_(no_type), a_(co[0]), b_(co[1]), c_(co[2]), d_(co[3]), e_(co[4]), f_(co[5])
00090 {
00091   set_type_from_equation();
00092 }
00093 
00094 //-------------------------------------------------------------
00095 //: constructor using polynomial coefficients.
00096 template <class T>
00097 vgl_conic<T>::vgl_conic(T ta, T tb, T tc, T td, T te, T tf)
00098   : type_(no_type), a_(ta), b_(tb), c_(tc), d_(td), e_(te), f_(tf)
00099 {
00100   set_type_from_equation();
00101 }
00102 
00103 //: ctor using centre, signed radii, and angle, or (for parabola) top + eccentricity
00104 template <class T>
00105 vgl_conic<T>::vgl_conic(vgl_homg_point_2d<T> const& co, T rx, T ry, T theta)
00106 {
00107   if (co.w() == 0) { // This is a parabola
00108     a_ = co.y()*co.y();
00109     b_ = -2*co.x()*co.y();
00110     c_ = co.x()*co.x();
00111     // polar line of (rx,ry) must have direction (co.y(),-co.x()), hence
00112     // 2*a_*rx + b_*ry + d_ = 2*t*co.x() and b_*rx + 2*c_*ry +e_ = 2*t*co.y() :
00113     theta /= vcl_sqrt(co.x()*co.x()+co.y()*co.y()); // cannot be 0
00114     d_ = -2*a_*rx - b_*ry + 2*theta*co.x();
00115     e_ = -2*c_*ry - b_*rx + 2*theta*co.y();
00116     // conic must go through (rx,ry):
00117     f_ = -a_*rx*rx-b_*rx*ry-c_*ry*ry-d_*rx-e_*ry;
00118   }
00119   else { // hyperbola or ellipse
00120     rx = (rx < 0) ? (-rx*rx) : rx*rx; // abs of square
00121     ry = (ry < 0) ? (-ry*ry) : ry*ry; // idem
00122 
00123     double ct = vcl_cos(-theta); // rotate counterclockwise over theta
00124     double st = vcl_sin(-theta);
00125     T u = co.x();
00126     T v = co.y();
00127     a_ = T(rx*st*st + ry*ct*ct);
00128     b_ = T(2*(rx-ry)*ct*st);
00129     c_ = T(rx*ct*ct + ry*st*st);
00130     d_ = T(-2*(rx*st*st + ry*ct*ct)*u - 2*(rx-ry)*ct*st*v);
00131     e_ = T(-2*(rx-ry)*ct*st*u - 2*(rx*ct*ct + ry*st*st)*v);
00132     f_ = T((rx*st*st +ry*ct*ct)*u*u + 2*(rx-ry)*ct*st*u*v + (rx*ct*ct + ry*st*st)*v*v - rx*ry);
00133   }
00134   set_type_from_equation();
00135 }
00136 
00137 //--------------------------------------------------------------------------
00138 //: set conic type from polynomial coefficients.
00139 // This method must be called by all constructors (except the default
00140 // constructor) and all methods that change the coefficients.
00141 
00142 template <class T>
00143 void vgl_conic<T>::set_type_from_equation()
00144 {
00145   T A = a_, B = b_/2, C = c_, D = d_/2, E = e_/2, F = f_;
00146 
00147   /* determinant, subdeterminants and trace values */
00148   T det = A*(C*F - E*E) - B*(B*F - D*E) + D*(B*E - C*D); // determinant
00149   T J = A*C - B*B;  // upper 2x2 determinant
00150   T K = (C*F - E*E) + (A*F - D*D); // sum of two other 2x2 determinants
00151   T I = A + C; // trace of upper 2x2
00152 
00153   if (det != 0) {
00154     if (J > 0) {
00155       if (det*I < 0) {
00156         if (A==C && B==0)      type_ = real_circle;
00157         else                   type_ = real_ellipse;
00158       }
00159       else {
00160         if (A==C && B==0)      type_ = imaginary_circle;
00161         else                   type_ = imaginary_ellipse;
00162       }
00163     }
00164     else if (J < 0)            type_ = hyperbola;
00165     else /* J == 0 */          type_ = parabola;
00166   }
00167   else {    // limiting cases
00168     if (J < 0)                 type_ = real_intersecting_lines;
00169     else if (J > 0)            type_ = complex_intersecting_lines;
00170     else /* J == 0 */ {
00171       if ( A == 0 && B == 0 && C == 0 ) { // line at infinity is component
00172         if ( D !=0 || E != 0 ) type_ = real_intersecting_lines;
00173         else if (F != 0)       type_ = coincident_lines; // 2x w=0
00174         else                   type_ = no_type; // all coefficients are 0
00175       }
00176       else if (K < 0)          type_ = real_parallel_lines;
00177       else if (K > 0)          type_ = complex_parallel_lines;
00178       else                     type_ = coincident_lines;
00179     }
00180   }
00181 }
00182 
00183 template <class T>
00184 bool vgl_conic<T>::contains(vgl_homg_point_2d<T> const& p) const
00185 {
00186   return p.x()*p.x()*a_+p.x()*p.y()*b_+p.y()*p.y()*c_+p.x()*p.w()*d_+p.y()*p.w()*e_+p.w()*p.w()*f_ == 0;
00187 }
00188 
00189 template <class T>
00190 bool vgl_conic<T>::is_degenerate() const
00191 {
00192   T A = a_, B = b_/2, C = c_, D = d_/2, E = e_/2, F = f_;
00193   T det = A*(C*F - E*E) - B*(B*F - D*E) + D*(B*E - C*D);
00194   return det==0;
00195 }
00196 
00197 //: return a geometric description of the conic if an ellipse
00198 // The centre of the ellipse is (xc, yc)
00199 template <class T>
00200 bool vgl_conic<T>::
00201 ellipse_geometry(double& xc, double& yc, double& major_axis_length,
00202                  double& minor_axis_length, double& angle_in_radians) const
00203 {
00204   if (type_!=real_ellipse && type_ != real_circle)
00205     return false;
00206 
00207   // Cast to double and half the non-diagonal (non-quadratic) entries B, D, E.
00208   double A = static_cast<double>(a_), B = static_cast<double>(b_)*0.5,
00209          C = static_cast<double>(c_), D = static_cast<double>(d_)*0.5,
00210          F = static_cast<double>(f_), E = static_cast<double>(e_)*0.5;
00211   if (A < 0)
00212     A=-A, B=-B, C=-C, D=-D, E=-E, F=-F;
00213 
00214   double det = A*(C*F - E*E) - B*(B*F- D*E) + D*(B*E-C*D);
00215   double D2 =  A*C - B*B;
00216   xc = (E*B - C*D)/D2;
00217   yc = (D*B - A*E)/D2;
00218 
00219   double trace = A + C;
00220   double disc = vcl_sqrt(trace*trace - 4.0*D2);
00221   double cmaj = (trace+disc)*D2/(2*det); if (cmaj < 0) cmaj = -cmaj;
00222   double cmin = (trace-disc)*D2/(2*det); if (cmin < 0) cmin = -cmin;
00223   minor_axis_length = 1.0/vcl_sqrt(cmaj>cmin?cmaj:cmin);
00224   major_axis_length = 1.0/vcl_sqrt(cmaj>cmin?cmin:cmaj);
00225 
00226   // Find the angle that diagonalizes the upper 2x2 sub-matrix
00227   angle_in_radians  = -0.5 * vcl_atan2(2*B, C-A);
00228   //                  ^
00229   // and return the negative of this angle
00230   return true;
00231 }
00232 
00233 
00234 //: Returns the list of component lines, when degenerate and real components.
00235 //  Otherwise returns an empty list.
00236 template <class T>
00237 vcl_list<vgl_homg_line_2d<T> > vgl_conic<T>::components() const
00238 {
00239   if (!is_degenerate() ||
00240       type() == complex_intersecting_lines ||
00241       type() == complex_parallel_lines)
00242     return vcl_list<vgl_homg_line_2d<T> >(); // no real components
00243 
00244   T A = a_, B = b_/2, C = c_, D = d_/2, E = e_/2, F = f_;
00245 
00246   if (type() == coincident_lines) {
00247     // coincident lines: rank of the matrix of this conic must be 1
00248     vgl_homg_line_2d<T> l;
00249     if (A!=0 || B!=0 || D!=0)
00250       l = vgl_homg_line_2d<T>(A,B,D);
00251     else if (C!=0 || E!=0)
00252       l = vgl_homg_line_2d<T>(B,C,E);
00253     else // only F!=0 : 2x line at infinity w=0
00254       l = vgl_homg_line_2d<T>(D,E,F);
00255     return vcl_list<vgl_homg_line_2d<T> >(2,l); // two identical lines
00256   }
00257 
00258   // Both component lines must pass through the centre of this conic
00259   vgl_homg_point_2d<T> cntr = centre();
00260 
00261   if (type() == real_parallel_lines)
00262   {
00263     // In this case the centre lies at infinity.
00264     // Either these lines both intersect the X axis, or both intersect the Y axis:
00265     if (A!=0 || D!=0) { // X axis: intersections satisfy y=0 && Axx+2Dxw+Fww=0:
00266       vgl_homg_line_2d<T> l1(cntr, vgl_homg_point_2d<T>(-D+vcl_sqrt(D*D-A*F),0,A)),
00267                           l2(cntr, vgl_homg_point_2d<T>(-D-vcl_sqrt(D*D-A*F),0,A));
00268       vcl_list<vgl_homg_line_2d<T> > v(1,l1); v.push_back(l2);
00269       return v;
00270     }
00271     else { // Y axis: x=0 && Cyy+2Eyw+Fww=0:
00272       vgl_homg_line_2d<T> l1(cntr, vgl_homg_point_2d<T>(0,-E+vcl_sqrt(E*E-C*F),C)),
00273                           l2(cntr, vgl_homg_point_2d<T>(0,-E-vcl_sqrt(E*E-C*F),C));
00274       vcl_list<vgl_homg_line_2d<T> > v(1,l1); v.push_back(l2);
00275       return v;
00276     }
00277   }
00278 
00279   // Only remaining case: type() == real_intersecting_lines.
00280   if (A==0 && B==0 && C==0) { // line at infinity (w=0) is a component
00281     vcl_list<vgl_homg_line_2d<T> > v(1,vgl_homg_line_2d<T>(0,0,1));
00282     v.push_back(vgl_homg_line_2d<T>(d_,e_,f_));
00283     return v;
00284   }
00285   // If not, the two component lines are determined by cntr and their direction,
00286   // i.e., they pass through one of the two pts satisfying w=0 && Axx+2Bxy+Cyy=0:
00287   if (A==0 && C==0) { // components are vertical and horizontal, resp.
00288     vgl_homg_line_2d<T> l1(cntr, vgl_homg_point_2d<T>(0,1,0)),
00289                         l2(cntr, vgl_homg_point_2d<T>(1,0,0));
00290     vcl_list<vgl_homg_line_2d<T> > v(1,l1); v.push_back(l2);
00291     return v;
00292   }
00293   else {
00294     vgl_homg_line_2d<T> l1(cntr, vgl_homg_point_2d<T>(-B+vcl_sqrt(B*B-A*C),A,0)),
00295                         l2(cntr, vgl_homg_point_2d<T>(-B-vcl_sqrt(B*B-A*C),A,0));
00296     vcl_list<vgl_homg_line_2d<T> > v(1,l1); v.push_back(l2);
00297     return v;
00298   }
00299 }
00300 
00301 //: Return true if a central conic
00302 //  (This is an affine property, not a projective one.)
00303 //  Equivalent to saying that the line at infinity does not touch the conic.
00304 template <class T>
00305 bool vgl_conic<T>::is_central() const
00306 {
00307   return type_ == real_ellipse|| type_ == imaginary_ellipse|| type_ == hyperbola
00308       || type_ == real_circle || type_ == imaginary_circle
00309       || type_ == real_intersecting_lines|| type_ == complex_intersecting_lines;
00310 }
00311 
00312 //--------------------------------------------------------------------------------
00313 template <class T>
00314 void vgl_conic<T>::translate_by(T x, T y)
00315 {
00316   d_ += 2*a_*x + b_*y;
00317   f_ += c_ * y*y - a_ * x*x + d_ * x + e_ * y;
00318   e_ += 2*c_*y + b_*x;
00319   // This does not change the type, so no need to run set_type_from_equation()
00320 }
00321 
00322 template <class T>
00323 vgl_conic<T> vgl_conic<T>::dual_conic() const
00324 {
00325   T A = a_, B = b_/2, C = c_, D = d_/2, E = e_/2, F = f_;
00326   return vgl_conic<T>(E*E-C*F, 2*(B*F-D*E), D*D-A*F, 2*(C*D-B*E), 2*(A*E-B*D), B*B-A*C);
00327 }
00328 
00329 //: return the polar line of the homogeneous 2-D point p.
00330 template <class T>
00331 vgl_homg_line_2d<T> vgl_conic<T>::polar_line(vgl_homg_point_2d<T> const& p) const
00332 {
00333   return vgl_homg_line_2d<T> (p.x()*a_  +p.y()*b_/2+p.w()*d_/2,
00334                               p.x()*b_/2+p.y()*c_  +p.w()*e_/2,
00335                               p.x()*d_/2+p.y()*e_/2+p.w()*f_  );
00336 }
00337 
00338 //: return the polar point of the homogeneous 2-D line l.
00339 template <class T>
00340 vgl_homg_point_2d<T> vgl_conic<T>::polar_point(vgl_homg_line_2d<T> const& l) const
00341 {
00342   if (!is_degenerate()) {
00343     vgl_conic<T> co = this->dual_conic();
00344     return vgl_homg_point_2d<T> (l.a()*co.a()  +l.b()*co.b()/2+l.c()*co.d()/2,
00345                                  l.a()*co.b()/2+l.b()*co.c()  +l.c()*co.e()/2,
00346                                  l.a()*co.d()/2+l.b()*co.e()/2+l.c()*co.f()  );
00347   }
00348   else // a degenerate conic has no dual; in this case, return the centre:
00349     if (a_==0 && b_==0 && d_==0) // two horizontal lines
00350       return vgl_homg_point_2d<T>(1,0,0);
00351     else if (a_*c_*4==b_*b_ && a_*e_*2==b_*d_)
00352       return vgl_homg_point_2d<T>(b_*f_*2-e_*d_, d_*d_-a_*f_*4, a_*e_*2-b_*d_);
00353     else
00354       return vgl_homg_point_2d<T>(b_*e_-c_*d_*2, b_*d_-a_*e_*2, a_*c_*4-b_*b_);
00355 }
00356 
00357 //: Returns the curvature of the conic at point p, assuming p is on the conic.
00358 template <class T>
00359 double vgl_conic<T>::curvature_at(vgl_point_2d<T> const& p) const
00360 {
00361   // Shorthands
00362   const T &a_xx = a_;
00363   const T &a_xy = b_;
00364   const T &a_yy = c_;
00365   const T &a_xw = d_;
00366   const T &a_yw = e_;
00367 
00368   const T x = p.x();
00369   const T y = p.y();
00370 
00371   double f_x  = 2*a_xx*x + a_xy*y + a_xw;
00372   double f_y  = 2*a_yy*y + a_xy*x + a_yw;
00373   double f_xy = a_xy;
00374   double f_xx = 2*a_xx;
00375   double f_yy = 2*a_yy;
00376 
00377   double f_x_2 = f_x*f_x;
00378   double f_y_2 = f_y*f_y;
00379   double denom = f_x_2 + f_y_2;
00380   denom = vcl_sqrt(denom*denom*denom);
00381 
00382   // Divergent of the unit normal grad f/|grad f|
00383   return (f_xx*f_y_2 - 2*f_x*f_y*f_xy + f_yy*f_x_2) / denom;
00384 }
00385 
00386 
00387 //: Write "<vgl_conic aX^2+bXY+cY^2+dXW+eYW+fW^2=0>" to stream
00388 template <class T>
00389 vcl_ostream& operator<<(vcl_ostream& s, vgl_conic<T> const& co)
00390 {
00391   s << "<vgl_conic ";
00392   if (co.a() == 1) s << "X^2";
00393   else if (co.a() == -1) s << "-X^2";
00394   else if (co.a() != 0) s << co.a() << "X^2";
00395   if (co.b() > 0) s << '+';
00396   if (co.b() == 1) s << "XY";
00397   else if (co.b() == -1) s << "-XY";
00398   else if (co.b() != 0) s << co.b() << "XY";
00399   if (co.c() > 0) s << '+';
00400   if (co.c() == 1) s << "Y^2";
00401   else if (co.c() == -1) s << "-Y^2";
00402   else if (co.c() != 0) s << co.c() << "Y^2";
00403   if (co.d() > 0) s << '+';
00404   if (co.d() == 1) s << "XW";
00405   else if (co.d() == -1) s << "-XW";
00406   else if (co.d() != 0) s << co.d() << "XW";
00407   if (co.e() > 0) s << '+';
00408   if (co.e() == 1) s << "YW";
00409   else if (co.e() == -1) s << "-YW";
00410   else if (co.e() != 0) s << co.e() << "YW";
00411   if (co.f() > 0) s << '+';
00412   if (co.f() == 1) s << "W^2";
00413   else if (co.f() == -1) s << "-W^2";
00414   else if (co.f() != 0) s << co.f() << "W^2";
00415   return s << "=0 " << co.real_type() << "> ";
00416 }
00417 
00418 //: Read a b c d e f from stream
00419 template <class T>
00420 vcl_istream& operator>>(vcl_istream& is, vgl_conic<T>& co)
00421 {
00422   T ta, tb, tc, td, te, tf; is >> ta >> tb >> tc >> td >> te >> tf;
00423   co.set(ta,tb,tc,td,te,tf); return is;
00424 }
00425 
00426 #undef VGL_CONIC_INSTANTIATE
00427 #define VGL_CONIC_INSTANTIATE(T) \
00428 template class vgl_conic<T >; \
00429 template vcl_ostream& operator<<(vcl_ostream&, const vgl_conic<T >&); \
00430 template vcl_istream& operator>>(vcl_istream&, vgl_conic<T >&)
00431 
00432 #endif // vgl_conic_txx_
00433