core/vgl/vgl_conic_segment_2d.h
Go to the documentation of this file.
00001 // This is core/vgl/vgl_conic_segment_2d.h
00002 #ifndef vgl_conic_segment_2d_h_
00003 #define vgl_conic_segment_2d_h_
00004 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00005 #pragma interface
00006 #endif
00007 //:
00008 // \file
00009 // \author J.L. Mundy  June 18, 2005
00010 // \brief A curve segment with the geometry of a conic
00011 //
00012 // A conic segment contains those points of a given conic which lie between the
00013 // given start point (inclusive) and the given end point, in a certain direction
00014 // (counter-clockwise by default).
00015 // No automatic validation checking is done for those two points effectively
00016 // lying on the conic; this is left to the user of the class. Operations on
00017 // vgl_conic_segment_2d<T> like intersection should actually take the "points
00018 // closest to the given end points" as the effective end points.
00019 //
00020 // The concept of counter-clockwise-ness is not projectively invariant, but is
00021 // invariant under most projective transformations that are useful in vision,
00022 // and most certainly is under affine and Euclidean transformations. Actually,
00023 // as long as the centre of the conic does not traverse the line at infinity,
00024 // orientation and thus counter-clockwise-ness of the conic is kept. There is
00025 // only one affine situation which is still ambiguous, viz. when the conic is
00026 // a hyperbola. In that case, the definition of "counter-clockwise" is based
00027 // --by definition-- on the view of the start point. Only when both end points
00028 // are at infinity, i.e. when, the segment is one of the two branches of the
00029 // hyperbola, this is still ambiguous and there is no way to distinguish which
00030 // of the two branches is meant by just giving the two end points.
00031 //
00032 // The only projectively "correct" way to define a segment would be to specify
00033 // a third point on the conic, or a direction vector from the start point.
00034 // This would be an overkill for most applications, though.
00035 // Those applications that require a fully projectively invariant segment should
00036 // consider storing an additional third point together with the conic segment,
00037 // and swap the two endpoints (by using the swap_endpoints() method) whenever
00038 // a transformation is applied that would invert the orientation of the conic.
00039 //
00040 // One more ambiguous situation left is a conic segment specified by two
00041 // identical end points. By definition, in this case, the segment only contains
00042 // of this single point. This is compatible with the use of conic segments in
00043 // conic fitting algorithms, where the shortest rather than the longest segment
00044 // should be selected.
00045 //
00046 // When the conic is an ellipse, the semantics of the conic segment are clear:
00047 // seen from the inside of the ellipse, and starting from the first end point,
00048 // one goes to the left to traverse the conic segment in a counter-clockwise way.
00049 //
00050 // If the conic is a parabola, the first endpoint should typically lie to the
00051 // right of the second one (as seen from the focal point of the parabola): in
00052 // that case, the conic segment has finite length when traversed in counter-
00053 // clockwise direction. Otherwise, we still have a valid conic segment but it
00054 // consists of two separate, infinite branches.
00055 //
00056 // If the conic is a hyperbola, there are even three cases to be considered:
00057 // if both end points lie on the same branch of the hyperbola, and the first
00058 // one lies to the right of the second one as seen from the "inside" of that
00059 // branch, the conic segment is a finite curve. This is the only finite case.
00060 // By swapping the end points, the segment will have three branches, not just
00061 // two: it will contain the two infinite fragments "outside" the end points on
00062 // the hyperbola branch of the end points, and also the complete other branch
00063 // of the hyperbola.
00064 // Finally, if the end points lie on different hyperbola branches, the segment
00065 // consists of all points to the left of the start point (as seen from the
00066 // "inside" of the start point's branch) and of all points to the left of the
00067 // end point (as seen from the "inside" of the end point's branch).
00068 // In this case, swapping the end points does not change the segment! As a
00069 // consequence, it will never be possible to specify the complement of this
00070 // segment as a single vgl_conic_segment_2d, only as the union of two.
00071 //
00072 // End points can of course happily be points at infinity (if the conic is
00073 // either a parabola or a hyperbola). A parabolic segment with the start point
00074 // at infinity contains all points to the left of the end point, as seen from
00075 // the focal point of the parabola. If the end point lies at infinity, it's the
00076 // points to the left of the start point which form the segment.
00077 // A hyperbolic segment for which the two endpoints are the two different points
00078 // at infinity of the hyperbola, contains all points of one of the two hyperbola
00079 // branches and none of the other branch. This is the only ambiguous situation
00080 // so it should be avoided unless a third point is used to define the segment.
00081 // If only one end point of a hyperbolic segment lies at infinity, the segment
00082 // either consists of just the points to the left on the branch of the other end
00083 // point, or of those points plus all points on the other branch.
00084 //
00085 // \verbatim
00086 //  Modifications
00087 //   2009-06-06 Peter Vanroose - Added member "counterclockwise_"
00088 //   2009-06-06 Peter Vanroose - Added swap_endpoints(), swap_direction(), normalize()
00089 //   2009-06-06 Peter Vanroose - Added the is_finite() method (not yet implem.)
00090 //   2009-06-06 Peter Vanroose - Re-implemented to be fully homogeneous
00091 //   2009-06-06 Peter Vanroose - Added explicit "semantics" documentation
00092 //   2009-06-06 Peter Vanroose - Added the contains() method (not yet implem.)
00093 // \endverbatim
00094 
00095 #include <vgl/vgl_homg_point_2d.h> // data member of this class
00096 #include <vgl/vgl_conic.h>         // data member of this class
00097 #include <vgl/vgl_point_2d.h>      // return type of some methods
00098 #include <vcl_iosfwd.h>
00099 #include <vcl_cassert.h>
00100 
00101 //: Represents a 2D conic segment using two points.
00102 template <class Type>
00103 class vgl_conic_segment_2d
00104 {
00105   //: One end of conic segment
00106   vgl_homg_point_2d<Type> p1_;
00107 
00108   //: The other end of the conic segment
00109   vgl_homg_point_2d<Type> p2_;
00110 
00111   //: The conic that represents the curve between point1 and point2
00112   vgl_conic<Type> conic_;
00113 
00114   //: Whether traversal is in counter-clockwise direction (the default) or not
00115   bool counterclockwise_;
00116 
00117  public:
00118   //: Default constructor - does not initialise!
00119   //  Use the set() method to make this conic segment useful.
00120   inline vgl_conic_segment_2d() {}
00121 
00122   //: Copy constructor
00123   inline vgl_conic_segment_2d(vgl_conic_segment_2d<Type> const& l)
00124   : p1_(l.p1_), p2_(l.p2_), conic_(l.conic_),
00125     counterclockwise_(l.counterclockwise_) {}
00126 
00127   //: Construct from two end points (homogeneous) and a conic
00128   inline vgl_conic_segment_2d(vgl_homg_point_2d<Type> const& p1,
00129                               vgl_homg_point_2d<Type> const& p2,
00130                               vgl_conic<Type> const& co,
00131                               bool counterclockwise = true)
00132   : p1_(p1), p2_(p2), conic_(co),
00133     counterclockwise_(counterclockwise) {}
00134 
00135   //: Construct from two end points (Cartesian) and a conic
00136   inline vgl_conic_segment_2d(vgl_point_2d<Type> const& p1,
00137                               vgl_point_2d<Type> const& p2,
00138                               vgl_conic<Type> const& co,
00139                               bool counterclockwise = true)
00140   : p1_(p1.x(), p1.y(), (Type)1), p2_(p2.x(), p2.y(), (Type)1),
00141     conic_(co), counterclockwise_(counterclockwise) {}
00142 
00143   //: Construct from a conic and two end points (homogeneous)
00144   inline vgl_conic_segment_2d(vgl_conic<Type> const& co,
00145                               vgl_homg_point_2d<Type> const& p1,
00146                               vgl_homg_point_2d<Type> const& p2,
00147                               bool counterclockwise = true)
00148   : p1_(p1), p2_(p2), conic_(co),
00149     counterclockwise_(counterclockwise) {}
00150 
00151   //: Construct from a conic and two end points (Cartesian)
00152   inline vgl_conic_segment_2d(vgl_conic<Type> const& co,
00153                               vgl_point_2d<Type> const& p1,
00154                               vgl_point_2d<Type> const& p2,
00155                               bool counterclockwise = true)
00156   : p1_(p1.x(), p1.y(), (Type)1), p2_(p2.x(), p2.y(), (Type)1),
00157     conic_(co), counterclockwise_(counterclockwise) {}
00158 
00159   //: Destructor
00160   inline ~vgl_conic_segment_2d() {}
00161 
00162   //: Normalise the direction of the segment to counterclockwise.
00163   //  This will also swap the end points if the direction is to be swapped.
00164   void normalize() { if (!counterclockwise_) { counterclockwise_=true; swap_endpoints(); } }
00165 
00166   //: Interchange the two endpoints but keep the direction.
00167   //  This implies that now the conic segment contains those points of the conic
00168   //  which before did not belong to the conic segment! (Except for the two end
00169   //  points, of course.)
00170   void swap_endpoints() { vgl_homg_point_2d<Type> p=p1_; p1_=p2_; p2_=p; }
00171 
00172   //: Change the direction of the conic section but keep the end points.
00173   //  This implies that now the conic segment contains those points of the conic
00174   //  which before did not belong to the conic segment! (Except for the two end
00175   //  points, of course.)
00176   void swap_direction() { counterclockwise_ = !counterclockwise_; }
00177 
00178   //: The first end-point of the conic segment.
00179   inline vgl_homg_point_2d<Type> point1() const { return p1_; } // return a copy
00180 
00181   //: The second end-point of the conic segment.
00182   inline vgl_homg_point_2d<Type> point2() const { return p2_; } // return a copy
00183 
00184   //: The conic underlying the segment
00185   inline vgl_conic<Type> conic() const { return conic_; } // return a copy
00186 
00187   //: The direction of the segment (clockwise or counterclockwise)
00188   bool is_counterclockwise() const { return counterclockwise_; }
00189 
00190   //: The direction of the segment (clockwise or counterclockwise)
00191   bool is_clockwise() const { return !counterclockwise_; }
00192 
00193   //: The equality comparison operator
00194   //  Two conic segments are only identical if the underlying conic is identical
00195   //  and if direction and  both endpoints are identical, in the same order!
00196   //  Two conic segments with identical conic and identical end points but
00197   //  in the opposite order are not identical but rather complementary: they
00198   //  share no other points than the two end points.
00199   //  Use the swap_direction or the swap_endpoints() method on one of the two
00200   //  segments to turn complementary segments into identical ones.
00201   //  Note that two conic segments \e are equal if both the direction and the
00202   //  two end points are swapped. To normalize a conic segment such that its
00203   //  direction becomes counterclockwise, use the normalize() method.
00204   inline bool operator==(vgl_conic_segment_2d<Type> const& l) const {
00205     return this==&l ||
00206            (l.conic() == conic_ &&
00207             l.is_counterclockwise() == counterclockwise_ &&
00208             point1() == l.point1() &&
00209             point2() == l.point2()) ||
00210            (l.conic() == conic_ &&
00211             l.is_counterclockwise() != counterclockwise_ &&
00212             point2() == l.point1() &&
00213             point1() == l.point2());
00214   }
00215 
00216   //: The inequality comparison operator.
00217   inline bool operator!=(vgl_conic_segment_2d<Type>const& other) const { return !operator==(other); }
00218 
00219   //: (Re)initialise the conic segment by passing it its three "constructors"
00220   inline void set(vgl_homg_point_2d<Type> const& p1, vgl_homg_point_2d<Type> const& p2,
00221                   vgl_conic<Type> co, bool counterclockwise = true)
00222   { p1_ = p1; p2_ = p2; conic_ = co; counterclockwise_ = counterclockwise; }
00223 
00224   //: (Re)initialise the conic segment by passing it its three "constructors"
00225   inline void set(vgl_conic<Type> co,
00226                   vgl_homg_point_2d<Type> const& p1, vgl_homg_point_2d<Type> const& p2,
00227                   bool counterclockwise = true)
00228   { p1_ = p1; p2_ = p2; conic_ = co; counterclockwise_ = counterclockwise; }
00229 
00230   //: (Re)initialise the conic segment by passing it its three "constructors"
00231   inline void set(vgl_point_2d<Type> const& p1, vgl_point_2d<Type> const& p2,
00232                   vgl_conic<Type> co, bool counterclockwise = true)
00233   { p1_.set(p1.x(), p1.y()); p2_.set(p2.x(), p2.y()); conic_ = co;
00234     counterclockwise_ = counterclockwise; }
00235 
00236   //: (Re)initialise the conic segment by passing it its three "constructors"
00237   inline void set(vgl_conic<Type> co,
00238                   vgl_point_2d<Type> const& p1, vgl_point_2d<Type> const& p2,
00239                   bool counterclockwise = true)
00240   { p1_.set(p1.x(), p1.y()); p2_.set(p2.x(), p2.y()); conic_ = co;
00241     counterclockwise_ = counterclockwise; }
00242 
00243   //: Finds out whether this curve has a finite length.
00244   //  If the conic segment has an underlying ellipse, the segment is of course
00245   //  always finite. Otherwise, is_finite returns false whenever the segment
00246   //  passes through one of the points at infinity of the hyperbola or parabola.
00247   //  Note that the methods swap_endpoints() and swap_direction() always swaps
00248   //  finiteness of a parabolic segment (unless the endpoints coincide). For
00249   //  hyperbolic segments this is not necessarily the case: both can be infinite.
00250   //  \todo not yet implemented
00251   bool is_finite() const { assert(!"Not yet implemented"); return true; }
00252 
00253   //: Finds out whether the given point lies on the conic segment.
00254   //  More specifically, lying on the segment implies lying on the conic.
00255   //  Moreover, the two endpoints (if effectively on the conic) will always
00256   //  lie on the segment. All other points of the conic lie either on this
00257   //  segment, or on the "swapped" segment, but never on both.
00258   //  \todo not yet implemented
00259   bool contains(vgl_homg_point_2d<Type> const& /*pt*/) const { assert(!"Not yet implemented"); return false; }
00260 };
00261 
00262 //: Write to stream
00263 // \relatesalso vgl_conic_segment_2d
00264 template <class Type>
00265 vcl_ostream&  operator<<(vcl_ostream& s, const vgl_conic_segment_2d<Type>& c_s);
00266 
00267 //: Read from stream
00268 // \relatesalso vgl_conic_segment_2d
00269 template <class Type>
00270 vcl_istream& operator>>(vcl_istream& is, vgl_conic_segment_2d<Type>& c_s);
00271 
00272 #define VGL_CONIC_SEGMENT_2D_INSTANTIATE(T) extern "please include vgl/vgl_conic_segment_2d.txx first"
00273 
00274 #endif // vgl_conic_segment_2d_h_