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_