Code

Super duper mega (fun!) commit: replaced encoding=utf-8 with fileencoding=utf-8 in...
[inkscape.git] / src / 2geom / path.cpp
index 35c7b76bce80201f6ebd03f2e6e66bf94fc58c40..c47902649654953884b54247e50e8a00ce104d23 100644 (file)
@@ -4,7 +4,7 @@
  * Authors:
  *             MenTaLguY <mental@rydia.net>
  *             Marco Cecchetti <mrcekets at gmail.com>
- * 
+ *
  * Copyright 2007-2008  authors
  *
  * This library is free software; you can redistribute it and/or
 
 
 
-#include "path.h"
+#include <2geom/path.h>
+#include <algorithm>
 
 
-namespace Geom 
-{
+using namespace Geom::PathInternal;
 
-void Path::swap(Path &other) {
-  std::swap(curves_, other.curves_);
-  std::swap(closed_, other.closed_);
-  std::swap(*final_, *other.final_);
-  curves_[curves_.size()-1] = final_;
-  other.curves_[other.curves_.size()-1] = other.final_;
-}
+namespace Geom
+{
 
-Rect Path::boundsFast() const {
-  Rect bounds=front().boundsFast();
-  for ( const_iterator iter=++begin(); iter != end() ; ++iter ) {
-    bounds.unionWith(iter->boundsFast());
+OptRect Path::boundsFast() const {
+  OptRect bounds;
+  if (empty()) return bounds;
+  bounds=front().boundsFast();
+  const_iterator iter = begin();
+  if ( iter != end() ) {
+    for ( ++iter; iter != end() ; ++iter ) {
+      bounds.unionWith(iter->boundsFast());
+    }
   }
   return bounds;
 }
 
-Rect Path::boundsExact() const {
-  Rect bounds=front().boundsExact();
+OptRect Path::boundsExact() const {
+  OptRect bounds;
+  if (empty()) return bounds;
+  bounds=front().boundsExact();
   const_iterator iter = begin();
   if ( iter != end() ) {
     for ( ++iter; iter != end() ; ++iter ) {
@@ -75,7 +77,30 @@ iter inc(iter const &x, unsigned n) {
   return ret;
 }
 
-std::vector<double> 
+Path &Path::operator*=(Matrix const &m) {
+  unshare();
+  Sequence::iterator last = get_curves().end() - 1;
+  Sequence::iterator it;
+  Point prev;
+  for (it = get_curves().begin() ; it != last ; ++it) {
+    *it = boost::shared_ptr<Curve>((*it)->transformed(m));
+    if ( it != get_curves().begin() && (*it)->initialPoint() != prev ) {
+      THROW_CONTINUITYERROR();
+    }
+    prev = (*it)->finalPoint();
+  }
+  for ( int i = 0 ; i < 2 ; ++i ) {
+    final_->setPoint(i, (*final_)[i] * m);
+  }
+  if (get_curves().size() > 1) {
+    if ( front().initialPoint() != initialPoint() || back().finalPoint() != finalPoint() ) {
+      THROW_CONTINUITYERROR();
+    }
+  }
+  return *this;
+}
+
+std::vector<double>
 Path::allNearestPoints(Point const& _point, double from, double to) const
 {
        if ( from > to ) std::swap(from, to);
@@ -102,7 +127,7 @@ Path::allNearestPoints(Point const& _point, double from, double to) const
        }
        if ( si == ei )
        {
-               std::vector<double>     all_nearest = 
+               std::vector<double>     all_nearest =
                        _path[si].allNearestPoints(_point, st, et);
                for ( unsigned int i = 0; i < all_nearest.size(); ++i )
                {
@@ -116,12 +141,12 @@ Path::allNearestPoints(Point const& _point, double from, double to) const
        std::vector<unsigned int> ni;
        ni.push_back(si);
        double dsq;
-       double mindistsq 
+       double mindistsq
                = distanceSq( _point, _path[si].pointAt( all_np.front().front() ) );
-       Rect bb;
+       Rect bb(Geom::Point(0,0),Geom::Point(0,0));
        for ( unsigned int i = si + 1; i < ei; ++i )
        {
-               bb = _path[i].boundsFast();
+               bb = *(_path[i].boundsFast());
                dsq = distanceSq(_point, bb);
                if ( mindistsq < dsq ) continue;
                all_t = _path[i].allNearestPoints(_point);
@@ -140,7 +165,7 @@ Path::allNearestPoints(Point const& _point, double from, double to) const
                        ni.push_back(i);
                }
        }
-       bb = _path[ei].boundsFast();
+       bb = *(_path[ei].boundsFast());
        dsq = distanceSq(_point, bb);
        if ( mindistsq >= dsq )
        {
@@ -168,10 +193,25 @@ Path::allNearestPoints(Point const& _point, double from, double to) const
                        all_nearest.push_back( ni[i] + all_np[i][j] );
                }
        }
-       return all_nearest;     
+       all_nearest.erase(std::unique(all_nearest.begin(), all_nearest.end()),
+                         all_nearest.end());
+       return all_nearest;
 }
 
-double Path::nearestPoint(Point const& _point, double from, double to) const
+std::vector<double>
+Path::nearestPointPerCurve(Point const& _point) const
+{
+       //return a single nearest point for each curve in this path
+       std::vector<double> np;
+       for (const_iterator it = begin() ; it != end_default(); ++it)
+       //for (std::vector<Path>::const_iterator it = _path.begin(); it != _path.end(), ++it){
+       {
+           np.push_back(it->nearestPoint(_point));
+    }
+       return np;
+}  
+
+double Path::nearestPoint(Point const &_point, double from, double to, double *distance_squared) const
 {
        if ( from > to ) std::swap(from, to);
        const Path& _path = *this;
@@ -185,6 +225,11 @@ double Path::nearestPoint(Point const& _point, double from, double to) const
        double eif, et = modf(to, &eif);
        unsigned int si = static_cast<unsigned int>(sif);
        unsigned int ei = static_cast<unsigned int>(eif);
+        if(sz == 0) {// naked moveto
+            if (distance_squared != NULL)
+                *distance_squared = distanceSq(_point, _path.initialPoint());
+            return 0;
+        }
        if ( si == sz )
        {
                --si;
@@ -197,19 +242,21 @@ double Path::nearestPoint(Point const& _point, double from, double to) const
        }
        if ( si == ei )
        {
-               double nearest =
-                       _path[si].nearestPoint(_point, st, et);
+               double nearest = _path[si].nearestPoint(_point, st, et);
+               if (distance_squared != NULL)
+                   *distance_squared = distanceSq(_point, _path[si].pointAt(nearest));
                return si + nearest;
        }
+
        double t;
        double nearest = _path[si].nearestPoint(_point, st);
        unsigned int ni = si;
        double dsq;
        double mindistsq = distanceSq(_point, _path[si].pointAt(nearest));
-       Rect bb;
+       Rect bb(Geom::Point(0,0),Geom::Point(0,0));
        for ( unsigned int i = si + 1; i < ei; ++i )
        {
-               bb = _path[i].boundsFast();
+               bb = *(_path[i].boundsFast());
                dsq = distanceSq(_point, bb);
                if ( mindistsq <= dsq ) continue;
                t = _path[i].nearestPoint(_point);
@@ -221,7 +268,7 @@ double Path::nearestPoint(Point const& _point, double from, double to) const
                        mindistsq = dsq;
                }
        }
-       bb = _path[ei].boundsFast();
+       bb = *(_path[ei].boundsFast());
        dsq = distanceSq(_point, bb);
        if ( mindistsq > dsq )
        {
@@ -231,14 +278,20 @@ double Path::nearestPoint(Point const& _point, double from, double to) const
                {
                        nearest = t;
                        ni = ei;
+                       mindistsq = dsq;
                }
        }
+
+    if (distance_squared != NULL)
+        *distance_squared = mindistsq;
+
        return ni + nearest;
 }
 
-//This assumes that you can't be perfect in your t-vals, and as such, tweaks the start
 void Path::appendPortionTo(Path &ret, double from, double to) const {
-  assert(from >= 0 && to >= 0);
+  if (!(from >= 0 && to >= 0)) {
+    THROW_RANGEERROR("from and to must be >=0 in Path::appendPortionTo");
+  }
   if(to == 0) to = size()+0.999999;
   if(from == to) { return; }
   double fi, ti;
@@ -247,7 +300,7 @@ void Path::appendPortionTo(Path &ret, double from, double to) const {
   const_iterator fromi = inc(begin(), (unsigned)fi);
   if(fi == ti && from < to) {
     Curve *v = fromi->portion(ff, tf);
-    ret.append(*v);
+    ret.append(*v, STITCH_DISCONTINUOUS);
     delete v;
     return;
   }
@@ -255,86 +308,81 @@ void Path::appendPortionTo(Path &ret, double from, double to) const {
   if(ff != 1.) {
     Curve *fromv = fromi->portion(ff, 1.);
     //fromv->setInitial(ret.finalPoint());
-    ret.append(*fromv);
+    ret.append(*fromv, STITCH_DISCONTINUOUS);
     delete fromv;
   }
   if(from >= to) {
     const_iterator ender = end();
     if(ender->initialPoint() == ender->finalPoint()) ender++;
-    ret.insert(ret.end(), ++fromi, ender);
-    ret.insert(ret.end(), begin(), toi);
+    ret.insert(ret.end(), ++fromi, ender, STITCH_DISCONTINUOUS);
+    ret.insert(ret.end(), begin(), toi, STITCH_DISCONTINUOUS);
   } else {
-    ret.insert(ret.end(), ++fromi, toi);
+    ret.insert(ret.end(), ++fromi, toi, STITCH_DISCONTINUOUS);
   }
   Curve *tov = toi->portion(0., tf);
-  ret.append(*tov);
+  ret.append(*tov, STITCH_DISCONTINUOUS);
   delete tov;
 }
 
-const double eps = .1;
-
-void Path::append(Curve const &curve) {
-  if ( curves_.front() != final_ && !are_near(curve.initialPoint(), (*final_)[0], eps) ) {
-    THROW_CONTINUITYERROR();
-  }
-  do_append(curve.duplicate());
-}
-
-void Path::append(D2<SBasis> const &curve) {
-  if ( curves_.front() != final_ ) {
-    for ( int i = 0 ; i < 2 ; ++i ) {
-      if ( !are_near(curve[i][0][0], (*final_)[0][i], eps) ) {
-        THROW_CONTINUITYERROR();
-      }
-    }
-  }
-  do_append(new SBasisCurve(curve));
-}
-
-void Path::append(Path const &other)
-{
-    // Check that path stays continuous:
-    if ( !are_near( finalPoint(), other.initialPoint() ) ) {
-        THROW_CONTINUITYERROR();
-    }
-
-    insert(begin(), other.begin(), other.end());
-}
-
 void Path::do_update(Sequence::iterator first_replaced,
                      Sequence::iterator last_replaced,
                      Sequence::iterator first,
-                    Sequence::iterator last)
+                     Sequence::iterator last)
 {
   // note: modifies the contents of [first,last)
-
   check_continuity(first_replaced, last_replaced, first, last);
-  delete_range(first_replaced, last_replaced);
   if ( ( last - first ) == ( last_replaced - first_replaced ) ) {
     std::copy(first, last, first_replaced);
   } else {
     // this approach depends on std::vector's behavior WRT iterator stability
-    curves_.erase(first_replaced, last_replaced);
-    curves_.insert(first_replaced, first, last);
+    get_curves().erase(first_replaced, last_replaced);
+    get_curves().insert(first_replaced, first, last);
   }
 
-  if ( curves_.front() != final_ ) {
+  if ( get_curves().front().get() != final_ ) {
     final_->setPoint(0, back().finalPoint());
     final_->setPoint(1, front().initialPoint());
   }
 }
 
-void Path::do_append(Curve *curve) {
-  if ( curves_.front() == final_ ) {
+void Path::do_append(Curve *c) {
+  boost::shared_ptr<Curve> curve(c);
+  if ( get_curves().front().get() == final_ ) {
     final_->setPoint(1, curve->initialPoint());
+  } else {
+    if (curve->initialPoint() != finalPoint()) {
+      THROW_CONTINUITYERROR();
+    }
   }
-  curves_.insert(curves_.end()-1, curve);
+  get_curves().insert(get_curves().end()-1, curve);
   final_->setPoint(0, curve->finalPoint());
 }
 
-void Path::delete_range(Sequence::iterator first, Sequence::iterator last) {
-  for ( Sequence::iterator iter=first ; iter != last ; ++iter ) {
-    delete *iter;
+void Path::stitch(Sequence::iterator first_replaced,
+                  Sequence::iterator last_replaced,
+                  Sequence &source)
+{
+  if (!source.empty()) {
+    if ( first_replaced != get_curves().begin() ) {
+      if ( (*first_replaced)->initialPoint() != source.front()->initialPoint() ) {
+        Curve *stitch = new StitchSegment((*first_replaced)->initialPoint(),
+                                          source.front()->initialPoint());
+        source.insert(source.begin(), boost::shared_ptr<Curve>(stitch));
+      }
+    }
+    if ( last_replaced != (get_curves().end()-1) ) {
+      if ( (*last_replaced)->finalPoint() != source.back()->finalPoint() ) {
+        Curve *stitch = new StitchSegment(source.back()->finalPoint(),
+                                          (*last_replaced)->finalPoint());
+        source.insert(source.end(), boost::shared_ptr<Curve>(stitch));
+      }
+    }
+  } else if ( first_replaced != last_replaced && first_replaced != get_curves().begin() && last_replaced != get_curves().end()-1) {
+    if ( (*first_replaced)->initialPoint() != (*(last_replaced-1))->finalPoint() ) {
+      Curve *stitch = new StitchSegment((*(last_replaced-1))->finalPoint(),
+                                        (*first_replaced)->initialPoint());
+      source.insert(source.begin(), boost::shared_ptr<Curve>(stitch));
+    }
   }
 }
 
@@ -344,18 +392,18 @@ void Path::check_continuity(Sequence::iterator first_replaced,
                             Sequence::iterator last)
 {
   if ( first != last ) {
-    if ( first_replaced != curves_.begin() ) {
-      if ( !are_near( (*first_replaced)->initialPoint(), (*first)->initialPoint(), eps ) ) {
+    if ( first_replaced != get_curves().begin() ) {
+      if ( (*first_replaced)->initialPoint() != (*first)->initialPoint() ) {
         THROW_CONTINUITYERROR();
       }
     }
-    if ( last_replaced != (curves_.end()-1) ) {
-      if ( !are_near( (*(last_replaced-1))->finalPoint(), (*(last-1))->finalPoint(), eps ) ) {
+    if ( last_replaced != (get_curves().end()-1) ) {
+      if ( (*(last_replaced-1))->finalPoint() != (*(last-1))->finalPoint() ) {
         THROW_CONTINUITYERROR();
       }
     }
-  } else if ( first_replaced != last_replaced && first_replaced != curves_.begin() && last_replaced != curves_.end()-1) {
-    if ( !are_near((*first_replaced)->initialPoint(), (*(last_replaced-1))->finalPoint(), eps ) ) {
+  } else if ( first_replaced != last_replaced && first_replaced != get_curves().begin() && last_replaced != get_curves().end()-1) {
+    if ( (*first_replaced)->initialPoint() != (*(last_replaced-1))->finalPoint() ) {
       THROW_CONTINUITYERROR();
     }
   }
@@ -372,4 +420,4 @@ void Path::check_continuity(Sequence::iterator first_replaced,
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :