Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

pointlist.C

Go to the documentation of this file.
00001 /*!
00002  *  \file Pointlist.C
00003  *  \brief Contains the implementation of non-inline functions of the Pointlist
00004  *  class.
00005  *  \ingroup group_MLIB
00006  *
00007  */
00008 
00009 #include <climits>
00010 
00011 #include "mlib/global.H"
00012 
00013 #include "mlib/pointlist.H"
00014 
00015 /* Point List Property Queries */
00016 
00017 template <class L, class P, class V, class S>
00018 MLIB_INLINE
00019 bool
00020 mlib::Pointlist<L,P,V,S>::is_straight(double len_scale) const
00021 {
00022 
00023    // Reject if < 2 points:
00024    if (num() < 2)
00025       return 0;
00026 
00027    // Accept if exactly 2 points:
00028    if (num() == 2)
00029       return 1;
00030 
00031    // Get a proposed line direction, guarding against the case
00032    // that some of the points are identical.
00033    int k;
00034    V v;
00035    for (k=1; k<num() && v.is_null(); k++)
00036       v = ((*this)[k] - (*this)[0]).normalized();
00037 
00038    if (v.is_null())
00039       return 0;   // Those bastards!! Give up.
00040 
00041    // We now return 'true' if the points lie close to the line.
00042    //
00043    // "Close" is defined relative to the overall length of
00044    // the polyline. We need the partial lengths to be
00045    // updated.
00046    if (num() != _partial_length.num()) {
00047       cerr << "Pointlist::is_straight: Error: lengths are not updated"
00048            << endl;
00049       return 0;
00050    }
00051    double thresh = length()*len_scale;
00052    for (k=1; k<num(); k++)
00053       if ((pt(k) - pt(0)).orthogonalized(v).length() > thresh)
00054          return 0;
00055 
00056    // No more tests to pass...
00057    return 1;
00058 }
00059 
00060 template <class L, class P, class V, class S>
00061 MLIB_INLINE
00062 bool
00063 mlib::Pointlist<L,P,V,S>::self_intersects() const
00064 {
00065 
00066    int n = num();
00067    for (int i = 0; i < n-3; i++) {
00068       S seg_i = seg(i);
00069       // first time thru, for closed polylines, don't
00070       // compare 1st segment w/ last segment, since they
00071       // share an endpoint:
00072       int max_j = (is_closed() && (i == 0)) ? (n-3) : (n-2);
00073       for (int j = i+2; j <= max_j; j++)
00074          if (seg_i.intersect_segs(seg(j)))
00075             return true;
00076    }
00077    return false;
00078 }
00079 
00080 /* Near Point Functions */
00081 
00082 template <class L, class P, class V, class S>
00083 MLIB_INLINE
00084 int
00085 mlib::Pointlist<L,P,V,S>::nearest_point(
00086     const P &p
00087     ) const
00088 {
00089    int nearest = 0;
00090    double min_dist = DBL_MAX; 
00091 
00092    for ( int i = 0; i < num(); i++ ) {
00093       double dist = (p - (*this)[i]).length_sqrd();
00094       if ( dist < min_dist ) {
00095          min_dist = dist;
00096          nearest = i;
00097       }
00098    }
00099    return nearest;
00100 }
00101 
00102 /*!
00103  *  \return the point, its distance to the line,
00104  *  and the index of the point that starts its segment.
00105  *  (that is, the point is on the line segment between
00106  *  points [seg_index] and [seg_index+1])
00107  *
00108  */
00109 template <class L, class P, class V, class S>
00110 MLIB_INLINE
00111 void
00112 mlib::Pointlist<L,P,V,S>::closest(
00113    const P &p,
00114    P       &nearpt,
00115    double  &neardist,
00116    int     &seg_index
00117    ) const
00118 {
00119    if (num()>1) {
00120       neardist = DBL_MAX;
00121 
00122       for (int i = 0; i < num()-1; i++) {
00123          const P a((*this)[i]);
00124          const P b((*this)[i+1]);
00125          const P npt = nearest_pt_to_line_seg(p,a,b);
00126          const double d = (npt-p).length();
00127          if (d < neardist) {
00128             neardist = d;
00129             nearpt  = npt;
00130             seg_index = i;
00131          }
00132       }
00133    }
00134    else {
00135       if (empty()) { // no points, so do something else.
00136          err_msg("Pointlist::closest() called on empty list");
00137          neardist = DBL_MAX;
00138          seg_index = 0;
00139       }
00140       else {  // one point
00141          nearpt = (*this)[0];
00142          seg_index = 0;
00143          neardist = p.dist(nearpt); 
00144       }
00145    }
00146 }
00147 
00148 template <class L, class P, class V, class S>
00149 MLIB_INLINE
00150 double
00151 mlib::Pointlist<L,P,V,S>::closest(
00152    const P &p,
00153    P       &nearpt,
00154    int     &pt_ind
00155    ) const
00156 {
00157    if (num() == 0) 
00158       return DBL_MAX;
00159 
00160    double d;
00161    int i;
00162    closest(p, nearpt, d, i);
00163 
00164    P a((*this)[i]);
00165    P b((*this)[i+1]);
00166    if ((p-a).length() < (p-b).length())
00167         pt_ind = i;
00168    else pt_ind = i+1;
00169    return d;
00170 }
00171 
00172 template <class L, class P, class V, class S>
00173 MLIB_INLINE
00174 P
00175 mlib::Pointlist<L,P,V,S>::closest(
00176    const P &p
00177    ) const
00178 {
00179   P nearpt(DBL_MAX);
00180   double dist;
00181   int    index;
00182   closest(p, nearpt, dist, index);
00183   return nearpt;
00184 }
00185 
00186 /* Geometric Computations */
00187 
00188 template <class L, class P, class V, class S>
00189 MLIB_INLINE
00190 P
00191 mlib::Pointlist<L,P,V,S>::sum() const {
00192 
00193    P ret;
00194    for (int i=0; i<num(); i++)
00195       ret += (*this)[i];
00196    return ret;
00197 
00198 }
00199 
00200 template <class L, class P, class V, class S>
00201 MLIB_INLINE
00202 double  
00203 mlib::Pointlist<L,P,V,S>::dist_to_seg(
00204    const P &p, 
00205    int k
00206    ) const
00207 {
00208    return (p - 
00209            nearest_pt_to_line_seg(p, (*this)[k], (*this)[(k+1)%num()])).length();
00210 }
00211 
00212 
00213 template <class L, class P, class V, class S>
00214 MLIB_INLINE
00215 double  
00216 mlib::Pointlist<L,P,V,S>::avg_dist_to_seg(
00217    const P &p, 
00218    int k
00219    ) const
00220 {
00221   return ((p - (*this)[k]).length() + (p - (*this)[(k+1)%num()]).length() ) / 2.0;
00222 }
00223 
00224 template <class L, class P, class V, class S>
00225 MLIB_INLINE
00226 double
00227 mlib::Pointlist<L,P,V,S>::spread() const
00228 {
00229    // returns max distance of any point to average()
00230 
00231    if (empty())
00232       return 0;
00233 
00234    P avg = average();
00235 
00236    double ret = avg.dist((*this)[0]);
00237    for (int k=1; k<num(); k++)
00238       ret = max(avg.dist((*this)[k]), ret);
00239 
00240    return ret;
00241 }
00242 
00243 /*!
00244  *  Components are numbered starting at 0 and are in the
00245  *  same order that they are accessed throught the subscripting operator
00246  *  for the polyline's point type.
00247  *
00248  */
00249 template <class L, class P, class V, class S>
00250 typename P::value_type
00251 mlib::Pointlist<L,P,V,S>::min_val(int i) const {
00252    if (empty()) {
00253       err_msg("Point3list::min_val: Error: polyline is empty");
00254       return 0;
00255    }
00256    assert(i >= 0 && i<P::dim());
00257    typename P::value_type ret = pt(0)[i];
00258    for (int k=1; k<num(); k++)
00259       ret = min(ret, pt(k)[i]);
00260    return ret;
00261 }
00262 
00263 /*!
00264  *  Components are numbered starting at 0 and are in the
00265  *  same order that they are accessed throught the subscripting operator
00266  *  for the polyline's point type.
00267  *
00268  */
00269 template <class L, class P, class V, class S>
00270 typename P::value_type
00271 mlib::Pointlist<L,P,V,S>::max_val(int i) const {
00272    if (empty()) {
00273       err_msg("Point3list::max_val: Error: polyline is empty");
00274       return 0;
00275    }
00276    assert(i >= 0 && i<P::dim());
00277    typename P::value_type ret = pt(0)[i];
00278    for (int k=1; k<num(); k++)
00279       ret = max(ret, pt(k)[i]);
00280    return ret;
00281 }
00282 
00283 /* Length Functions */
00284 
00285 template<class L, class P, class V, class S>
00286 MLIB_INLINE
00287 void    
00288 mlib::Pointlist<L,P,V,S>::update_length()
00289 {
00290    _partial_length.clear();
00291    if (num())
00292       _partial_length.realloc(num());
00293    _partial_length += 0;
00294    double cur_length = 0;
00295    for ( int i=1; i< num(); i++ ) {
00296       cur_length += segment_length(i-1);
00297       _partial_length += cur_length;
00298    }
00299    err_mesg(ERR_LEV_SPAM,
00300             "Pointlist<L,P,V,S>::update_length():  # of Segments = %i Total Length = %f",
00301             num(), (float)_partial_length.last());
00302 }
00303 
00304 /* Interpolation Functions */
00305 
00306 //! Given interpolation parameter s varying from 0 to 1
00307 //! along the polyline, return the corresponding point,
00308 //! and optionally the tangent, segment index, and segment
00309 //! paramter there.
00310 //!
00311 //! \param[in] s Interpolation parameter.  Varies from 0 to 1 (0 is the begining
00312 //! of the line and 1 is the end).
00313 //! \param[out] tan Tanget of segment \p segp (pass NULL if you don't want it).
00314 //! \param[out] segp Index of the segment that contains the interpolated position
00315 //! at \p s (pass NULL if you don't want it).
00316 //! \param[out] tp Parameter varying from 0 to 1 representing the corresponding
00317 //! position of \p s on the segment \p segp (pass NULL if you don't want it).
00318 //! \return The interpolated point along on the polyline at \p s.
00319 template<class L, class P, class V, class S>
00320 MLIB_INLINE
00321 P
00322 mlib::Pointlist<L,P,V,S>::interpolate(
00323    double s, 
00324    V *tan, 
00325    int *segp, 
00326    double *tp
00327    ) const
00328 {
00329 
00330    // Q: Would they ever call this on an empty list?
00331    // A: Yes.
00332    if (empty()) {
00333       cerr << "Pointlist::interpolate: Error: list is empty"
00334            << endl;
00335       return P();
00336    }
00337 
00338    int seg;
00339    double t;
00340    interpolate_length(s, seg, t);
00341    const V v = (*this)[seg+1] - (*this)[seg];
00342 
00343    if (tan)   *tan  = v.normalized();
00344    if (segp)  *segp = seg;
00345    if (tp)    *tp   = t;
00346 
00347    return (*this)[seg]+v*t;
00348 }
00349 
00350 //! Parameter s should lie in the range [0,1].
00351 //! Returns the corresponding segment index, and the
00352 //! paramter t varying from 0 to 1 along that segment.
00353 //!
00354 //! \param[in] s Interpolation parameter.  Varies from 0 to 1 (0 is the begining
00355 //! of the line and 1 is the end).
00356 //! \param[out] seg Index of the segment that contains the interpolated position
00357 //! at \p s.
00358 //! \param[out] t Parameter varying from 0 to 1 representing the corresponding
00359 //! position of \p s on the segment \p seg.
00360 template<class L, class P, class V, class S>
00361 MLIB_INLINE
00362 void
00363 mlib::Pointlist<L,P,V,S>::interpolate_length(
00364    double s, 
00365    int &seg, 
00366    double &t 
00367    ) const
00368 {
00369 
00370    if (empty()) {
00371       cerr << "Pointlist::interpolate_length: Error: list is empty"
00372            << endl;
00373       return;
00374    }
00375 
00376    // Assume partial lengths have been update...
00377 
00378    // It turns out that when we ASSUME we make an ass of
00379    // everybody.  Not updating the partial length array remains a
00380    // common mistake... so we check for it here. This check will
00381    // not catch all cases where the partial length array is out
00382    // of date, but it will catch many of them.
00383    if (_partial_length.num() != num()) {
00384       cerr << "Pointlist::interpolate_length: "
00385            << "Warning: partial lengths not updated"
00386            << endl;
00387       // You know you're bad when you cast away the const:
00388       ((L*)this)->update_length();
00389    }
00390 
00391    // Note if there are 0 or 1 points, _partial_length.last() will be 0, we'll
00392    // return early.
00393    //
00394    // note also, generally returns 0<=t<1,
00395    // except at the extreme right, returns t=1;
00396 
00397    const double val = s*_partial_length.last();
00398    if (val <= 0)       { seg = 0; t = 0; return; }
00399    if (val >= _partial_length.last()) { seg = num()-2; t = 1; return; }
00400 
00401    int l=0, r=num()-1, m;
00402    while ((m = (l+r) >> 1) != l) {
00403       if (val < _partial_length[m])   { r = m; }
00404       else                            { l = m; }
00405    }
00406    seg = m;
00407    t = (val-_partial_length[m])/(_partial_length[m+1]-_partial_length[m]);
00408 }
00409 
00410 template<class L, class P, class V, class S>
00411 MLIB_INLINE
00412 V
00413 mlib::Pointlist<L,P,V,S>::get_tangent(double s) const
00414 {
00415 
00416    if (s == (int)s) { 
00417       int i = s;
00418       const int n=num()-1;
00419       if (i<0 || i>n || n<1) return V();
00420       if (i==0) return ((*this)[1]-(*this)[  0]).normalized();
00421       if (i==n) return ((*this)[n]-(*this)[n-1]).normalized();
00422       return (((*this)[i+1]-(*this)[i]).normalized() +
00423             ((*this)[i]-(*this)[i-1]).normalized()).
00424          normalize();
00425    }
00426    else {
00427       int    seg;
00428       double t;
00429       V   tan0;
00430       V   tan1;
00431 
00432       interpolate_length(s, seg, t);
00433 
00434       // Interpolate
00435       if ( t < .5 ) {
00436          tan0 = get_tangent(seg);
00437          tan1 = ((*this)[seg+1] - (*this)[seg]).normalized();
00438          t *= 2.0;
00439       }
00440       else {
00441          tan0 = ((*this)[seg+1] - (*this)[seg]).normalized();
00442          tan1 = get_tangent(seg+1);
00443          t = ( t - .5 ) * 2.0;
00444       }
00445 
00446       return (((1-t) * tan0) + (t * tan1)).normalized();
00447    }
00448 }
00449 
00450 /* Inversion Functions */
00451 
00452 template<class L, class P, class V, class S>
00453 MLIB_INLINE
00454 double
00455 mlib::Pointlist<L,P,V,S>::invert(
00456    const P &p) const
00457 {
00458    P      nearpt;
00459    double neardist;
00460    int    seg_index;
00461 
00462    closest(p, nearpt, neardist, seg_index);
00463    return invert(p, seg_index);
00464 }
00465 
00466 template<class L, class P, class V, class S>
00467 MLIB_INLINE
00468 double
00469 mlib::Pointlist<L,P,V,S>::invert(
00470    const P &p,
00471    int      seg) const
00472 {
00473    const V v     = (*this)[seg + 1] - (*this)[seg];
00474    const V vnorm = v.normalized();
00475    const P pp    = (*this)[seg] + ( (p-(*this)[seg]) * vnorm ) * vnorm;
00476 
00477    double len = _partial_length[seg] + (pp-(*this)[seg]).length();
00478    return len/length();
00479 }
00480 
00481 /* List Operations */
00482 
00483 template <class L, class P, class V, class S>
00484 MLIB_INLINE
00485 void
00486 mlib::Pointlist<L,P,V,S>::resample(
00487    int num_segs
00488    )
00489 {
00490    update_length();
00491 
00492    Pointlist<L,P,V,S> result;
00493    double dt = 1.0 / num_segs;
00494    for (int k=0; k<num_segs; k++)
00495       result += interpolate(k*dt);
00496    result += last();
00497 
00498    *this = result;
00499 }
00500 
00501 /* List Editing Functions */
00502 
00503 //! \param[in] k1 Index of first vertex.
00504 //! \param[in] k2 Index of last vertex.
00505 //! 
00506 //! \warning Allocates memory for the copied list.  Presumably the caller is
00507 //! responsible for deallocating this memory (using delete).
00508 template<class L, class P, class V, class S>
00509 MLIB_INLINE
00510 L
00511 mlib::Pointlist<L,P,V,S>::clone_piece(
00512    int k1, 
00513    int k2) const
00514 {
00515    if (!(valid_index(k1) && valid_index(k2)))
00516       return L();
00517 
00518    L ret(num());
00519 
00520    bool loop = (k2 < k1);
00521    if (loop) {
00522       int n = num();
00523       int end = (k2+1)%n;
00524       if (k1 == end) {
00525          ret = *this;
00526       } else {
00527          for (int k=k1; k != end; k = (k+1)%n) {
00528             ret.add((*this)[k]);
00529          }
00530       }
00531    } else {
00532       for (int k=k1; k<=k2; k++) {
00533          ret.add((*this)[k]);
00534       }
00535    }
00536    return ret;
00537 }
00538 
00539 template<class L, class P, class V, class S>
00540 MLIB_INLINE
00541 void
00542 mlib::Pointlist<L,P,V,S>::append(
00543    Pointlist<L,P,V,S> * poly
00544    )
00545 {
00546    this->operator+=(*poly);
00547 }
00548 
00549 template<class L, class P, class V, class S>
00550 MLIB_INLINE
00551 void
00552 mlib::Pointlist<L,P,V,S>::prepend(
00553    Pointlist<L,P,V,S> * poly
00554    )
00555 {
00556    Pointlist<L,P,V,S> pts(*poly); 
00557    for ( int i = 0; i < num(); i++ ) {
00558       pts += (*this)[i];
00559    }
00560    clear();   // Is this necessary?
00561    (*this) = pts;
00562 }
00563 
00564 /* end of file Pointlist.C */

Generated on Mon Sep 18 11:39:33 2006 for jot by  doxygen 1.4.4