summaryrefslogtreecommitdiffstats
path: root/karbon/core/vpath.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'karbon/core/vpath.cpp')
-rw-r--r--karbon/core/vpath.cpp1153
1 files changed, 1153 insertions, 0 deletions
diff --git a/karbon/core/vpath.cpp b/karbon/core/vpath.cpp
new file mode 100644
index 00000000..ea05fc76
--- /dev/null
+++ b/karbon/core/vpath.cpp
@@ -0,0 +1,1153 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002, The Karbon Developers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+
+#include <math.h>
+
+#include <tqdom.h>
+#include <tqvaluelist.h>
+#include <tqwmatrix.h>
+
+#include "vpath.h"
+#include "vsegment.h"
+#include "vvisitor.h"
+
+#include <kdebug.h>
+
+
+class VSubpathIteratorList
+{
+public:
+ VSubpathIteratorList()
+ : m_list( 0L ), m_iterator( 0L )
+ {}
+
+ ~VSubpathIteratorList()
+ {
+ notifyClear( true );
+ delete m_list;
+ }
+
+ void add( VSubpathIterator* itr )
+ {
+ if( !m_iterator )
+ m_iterator = itr;
+ else if( m_list )
+ m_list->push_front( itr );
+ else
+ {
+ m_list = new TQValueList<VSubpathIterator*>;
+ m_list->push_front( itr );
+ }
+ }
+
+ void remove( VSubpathIterator* itr )
+ {
+ if( m_iterator == itr )
+ m_iterator = 0L;
+ else if( m_list )
+ {
+ m_list->remove( itr );
+
+ if( m_list->isEmpty() )
+ {
+ delete m_list;
+ m_list = 0L;
+ }
+ }
+ }
+
+ void notifyClear( bool zeroList )
+ {
+ if( m_iterator )
+ {
+ if( zeroList )
+ m_iterator->m_list = 0L;
+
+ m_iterator->m_current = 0L;
+ }
+
+ if( m_list )
+ {
+ for(
+ TQValueList<VSubpathIterator*>::Iterator itr = m_list->begin();
+ itr != m_list->end();
+ ++itr )
+ {
+ if( zeroList )
+ ( *itr )->m_list = 0L;
+
+ ( *itr )->m_current = 0L;
+ }
+ }
+ }
+
+ void notifyRemove( VSegment* segment, VSegment* current )
+ {
+ if( m_iterator )
+ {
+ if( m_iterator->m_current == segment )
+ m_iterator->m_current = current;
+ }
+
+ if( m_list )
+ {
+ for(
+ TQValueList<VSubpathIterator*>::Iterator itr = m_list->begin();
+ itr != m_list->end();
+ ++itr )
+ {
+ if( ( *itr )->m_current == segment )
+ ( *itr )->m_current = current;
+ }
+ }
+ }
+
+private:
+ TQValueList<VSubpathIterator*>* m_list;
+ VSubpathIterator* m_iterator;
+};
+
+
+VSubpath::VSubpath( VObject* parent )
+ : VObject( parent )
+{
+ m_isClosed = false;
+
+ m_first = m_last = m_current = 0L;
+ m_number = 0;
+ m_currentIndex = -1;
+ m_iteratorList = 0L;
+
+ // Add an initial segment.
+ append( new VSegment( 1 ) );
+}
+
+VSubpath::VSubpath( const VSubpath& list )
+ : VObject( list )
+{
+ m_isClosed = list.isClosed();
+
+ m_first = m_last = m_current = 0L;
+ m_number = 0;
+ m_currentIndex = -1;
+ m_iteratorList = 0L;
+
+ VSegment* segment = list.m_first;
+
+ while( segment )
+ {
+ append( segment->clone() );
+ segment = segment->m_next;
+ }
+}
+
+VSubpath::VSubpath( const VSegment& segment )
+ : VObject( 0L )
+{
+ m_isClosed = false;
+
+ m_first = m_last = m_current = 0L;
+ m_number = 0;
+ m_currentIndex = -1;
+ m_iteratorList = 0L;
+
+ // The segment is not a "begin" segment.
+ if( segment.prev() )
+ {
+ // Add an initial segment.
+ append( new VSegment( 1 ) );
+
+ // Move the "begin" segment to the new segment's previous knot.
+ moveTo( segment.prev()->knot() );
+ }
+
+ // Append a copy of the segment.
+ append( segment.clone() );
+}
+
+VSubpath::~VSubpath()
+{
+ clear();
+ delete m_iteratorList;
+}
+
+const KoPoint&
+VSubpath::currentPoint() const
+{
+ return getLast()->knot();
+}
+
+bool
+VSubpath::moveTo( const KoPoint& p )
+{
+ // Move "begin" segment if path is still empty.
+ if( isEmpty() )
+ {
+ getLast()->setKnot( p );
+ return true;
+ }
+
+ return false;
+}
+
+bool
+VSubpath::lineTo( const KoPoint& p )
+{
+ if( isClosed() )
+ return false;
+
+ VSegment* s = new VSegment( 1 );
+
+ s->setDegree( 1 );
+ s->setKnot( p );
+
+ append( s );
+
+ return true;
+}
+
+bool
+VSubpath::curveTo(
+ const KoPoint& p1, const KoPoint& p2, const KoPoint& p3 )
+{
+ if( isClosed() )
+ return false;
+
+ VSegment* s = new VSegment();
+
+ s->setDegree( 3 );
+ s->setPoint( 0, p1 );
+ s->setPoint( 1, p2 );
+ s->setPoint( 2, p3 );
+
+ append( s );
+
+
+ return true;
+}
+
+bool
+VSubpath::curve1To( const KoPoint& p2, const KoPoint& p3 )
+{
+ if( isClosed() )
+ return false;
+
+ VSegment* s = new VSegment();
+
+ s->setDegree( 3 );
+ s->setPoint( 0, currentPoint() );
+ s->setPoint( 1, p2 );
+ s->setPoint( 2, p3 );
+
+ append( s );
+
+
+ return true;
+}
+
+bool
+VSubpath::curve2To( const KoPoint& p1, const KoPoint& p3 )
+{
+ if( isClosed() )
+ return false;
+
+ VSegment* s = new VSegment();
+
+ s->setDegree( 3 );
+ s->setPoint( 0, p1 );
+ s->setPoint( 1, p3 );
+ s->setPoint( 2, p3 );
+
+ append( s );
+
+
+ return true;
+}
+
+bool
+VSubpath::arcTo(
+ const KoPoint& p1, const KoPoint& p2, const double r )
+{
+ /* This routine is inspired by code in GNU ghostscript.
+ *
+ * |- P1B3 -|
+ *
+ * |- - - T12- - -|
+ *
+ * - - P1 x....__--o.....x P2
+ * | | : _/ B3
+ * P1B0 : /
+ * | :/
+ * | |
+ * - T10 o B0
+ * |
+ * | |
+ * |
+ * | |
+ * - x P0
+ */
+
+ if( isClosed() || r < 0.0 )
+ return false;
+
+
+ // We need to calculate the tangent points. Therefore calculate tangents
+ // T10=P1P0 and T12=P1P2 first.
+ double dx0 = currentPoint().x() - p1.x();
+ double dy0 = currentPoint().y() - p1.y();
+ double dx2 = p2.x() - p1.x();
+ double dy2 = p2.y() - p1.y();
+
+ // Calculate distance squares.
+ double dsqT10 = dx0 * dx0 + dy0 * dy0;
+ double dsqT12 = dx2 * dx2 + dy2 * dy2;
+
+ // We now calculate tan(a/2) where a is the angle between T10 and T12.
+ // We benefit from the facts T10*T12 = |T10|*|T12|*cos(a),
+ // |T10xT12| = |T10|*|T12|*sin(a) (cross product) and tan(a/2) = sin(a)/[1-cos(a)].
+ double num = dy0 * dx2 - dy2 * dx0;
+
+ double denom = sqrt( dsqT10 * dsqT12 ) - ( dx0 * dx2 + dy0 * dy2 );
+
+ // The points are colinear.
+ if( 1.0 + denom == 1.0 )
+ {
+ // Just add a line.
+ lineTo( p1 );
+ }
+ else
+ {
+ // |P1B0| = |P1B3| = r * tan(a/2).
+ double dP1B0 = fabs( r * num / denom );
+
+ // B0 = P1 + |P1B0| * T10/|T10|.
+ KoPoint b0 = p1 + KoPoint( dx0, dy0 ) * ( dP1B0 / sqrt( dsqT10 ) );
+
+ // If B0 deviates from current point P0, add a line to it.
+ if( !b0.isNear( currentPoint(), VGlobal::isNearRange ) )
+ lineTo( b0 );
+
+ // B3 = P1 + |P1B3| * T12/|T12|.
+ KoPoint b3 = p1 + KoPoint( dx2, dy2 ) * ( dP1B0 / sqrt( dsqT12 ) );
+
+
+ // The two bezier-control points are located on the tangents at a fraction
+ // of the distance[ tangent points <-> tangent intersection ].
+ const KoPoint d = p1 - b0;
+
+ double distsq = d * d;
+
+ double rsq = r * r;
+
+ double fract;
+
+ // r is very small.
+ if( distsq >= rsq * VGlobal::veryBigNumber )
+ {
+ // Assume dist = r = 0.
+ fract = 0.0;
+ }
+ else
+ {
+ fract = ( 4.0 / 3.0 ) / ( 1.0 + sqrt( 1.0 + distsq / rsq ) );
+ }
+
+ KoPoint b1 = b0 + ( p1 - b0 ) * fract;
+ KoPoint b2 = b3 + ( p1 - b3 ) * fract;
+
+ // Finally add the bezier-segment.
+ curveTo( b1, b2, b3 );
+ }
+
+ return true;
+}
+
+void
+VSubpath::close()
+{
+ // In the case the list is 100% empty (which should actually never happen),
+ // append a "begin" first, to avoid a crash.
+ if( count() == 0 )
+ append( new VSegment( 1 ) );
+
+ // Move last segment if we are already closed.
+ if( isClosed() )
+ {
+ getLast()->setKnot( getFirst()->knot() );
+ }
+ // Append a line, if necessary.
+ else
+ {
+ if(
+ getLast()->knot().isNear(
+ getFirst()->knot(), VGlobal::isNearRange ) )
+ {
+ // Move last knot.
+ getLast()->setKnot( getFirst()->knot() );
+ }
+ else
+ {
+ // Add a line.
+ lineTo( getFirst()->knot() );
+ }
+
+ m_isClosed = true;
+ }
+}
+
+bool
+VSubpath::pointIsInside( const KoPoint& p ) const
+{
+ // If the point is not inside the boundingbox, it cannot be inside the path either.
+ if( !boundingBox().contains( p ) )
+ return false;
+
+ // First check if the point is inside the knot polygon (beziers are treated
+ // as lines).
+
+ /* This algorithm is taken from "Fast Winding Number Inclusion of a Point
+ * in a Polygon" by Dan Sunday, geometryalgorithms.com.
+ */
+
+ /*
+ int windingNumber = 0;
+
+ // Ommit first segment.
+ VSegment* segment = getFirst()->next();
+
+ while( segment )
+ {
+ if( segment->prev()->knot().y() <= p.y() )
+ {
+ // Upward crossing.
+ if( segment->knot().y() > p.y() )
+ {
+ // Point is left.
+ if( segment->pointIsLeft( p ) > 0 )
+ {
+ // Valid up intersection.
+ ++windingNumber;
+ }
+ }
+ }
+ else
+ {
+ // Downward crossing.
+ if( segment->knot().y() <= p.y() )
+ {
+ // Point is right.
+ if( segment->pointIsLeft( p ) < 0 )
+ {
+ // Valid down intersection.
+ --windingNumber;
+ }
+ }
+ }
+
+ segment = segment->next();
+ }
+
+ if( static_cast<bool>( windingNumber ) )
+ return true;
+ */
+
+ // Then check if the point is located in between the knot polygon
+ // and the bezier curves.
+
+ /* We rotate each segment in order to make their chord (the line between
+ * the previous knot and the knot ) parallel to the x-axis. Then we
+ * calculate y(xp) on the segment for the rotated input point (xp,yp)
+ * and compare y(xp) with yp.
+ */
+// TODO
+
+ // cache the closed evaluation
+ bool closed = isClosed() || getLast()->knot() == getFirst()->knot();
+
+ TQValueList<double> rparams;
+
+ VSegment* segment = getFirst()->next();
+
+ // move all segements so that p is the origin
+ // and compute their intersections with the x-axis
+ while( segment )
+ {
+ VSubpath tmpCurve( 0L );
+ tmpCurve.append( new VSegment( segment->degree() ) );
+
+ for( int i = 0; i <= segment->degree(); ++i )
+ tmpCurve.current()->setP(i, segment->p(i)-p );
+
+ tmpCurve.current()->rootParams( rparams );
+
+ segment = segment->next();
+ }
+
+ // if the path is not closed, compute the intersection of
+ // the line through the first and last knot and the x-axis too
+ if( ! closed )
+ {
+ KoPoint prevKnot = getLast()->knot() - p;
+ KoPoint nextKnot = getFirst()->knot() - p;
+
+ double dx = nextKnot.x() - prevKnot.x();
+ double dy = nextKnot.y() - prevKnot.y();
+ if( dx == 0.0 )
+ {
+ rparams.append( nextKnot.x() );
+ }
+ else if( dy != 0.0 )
+ {
+ if( ( prevKnot.y() < 0.0 && nextKnot.y() > 0.0 ) || ( prevKnot.y() > 0.0 && nextKnot.y() < 0.0 ) )
+ {
+ double n = prevKnot.y() - dy / dx * prevKnot.x();
+ rparams.append( -n * dx / dy );
+ }
+ }
+ }
+
+ kdDebug(38000) << "intersection count: " << rparams.count() << endl;
+
+ // sort all intersections
+ qHeapSort( rparams );
+
+ TQValueList<double>::iterator itr, etr = rparams.end();
+
+ for( itr = rparams.begin(); itr != etr; ++itr )
+ kdDebug(38000) << "intersection: " << *itr << endl;
+
+ if( closed )
+ {
+ // pair the intersections and check if the origin is within a pair
+ for( itr = rparams.begin(); itr != etr; ++itr )
+ {
+ if( *itr > 0.0 )
+ return false;
+
+ if( ++itr == etr )
+ return false;
+
+ if( *itr > 0.0 )
+ return true;
+ }
+ }
+ else
+ {
+ // only check if point is between first and last intersection if we have an open path
+ if ( rparams.front() < 0.0 && rparams.back() > 0.0 )
+ return true;
+ }
+
+ return false;
+}
+
+bool
+VSubpath::intersects( const VSegment& s ) const
+{
+ // Check if path is empty and if boundingboxes intersect.
+ if(
+ isEmpty() ||
+ !boundingBox().intersects( s.boundingBox() ) )
+ {
+ return false;
+ }
+
+
+ // Ommit first segment.
+ VSegment* segment = getFirst()->next();
+
+ while( segment )
+ {
+ if( segment->intersects( s ) )
+ {
+ return true;
+ }
+
+ segment = segment->next();
+ }
+
+ return false;
+}
+
+bool
+VSubpath::counterClockwise() const
+{
+ /* This algorithm is taken from the FAQ of comp.graphics.algorithms:
+ * "Find the lowest vertex (or, if there is more than one vertex with the
+ * same lowest coordinate, the rightmost of those vertices) and then take
+ * the cross product of the edges fore and aft of it."
+ */
+
+ // A non closed path does not have a winding.
+ if( !isClosed() )
+ {
+ return false;
+ }
+
+
+ VSegment* segment = getFirst();
+
+ // We save the segment not the knot itself. Initialize it with the
+ // first segment:
+ const VSegment* bottomRight = getFirst();
+
+ while( segment )
+ {
+ if( segment->knot().y() < bottomRight->knot().y() )
+ bottomRight = segment;
+ else if( segment->knot().y() - bottomRight->knot().y()
+ < VGlobal::isNearRange )
+ {
+ if( segment->knot().x() > bottomRight->knot().x() )
+ bottomRight = segment;
+ }
+
+ segment = segment->next();
+ }
+
+
+ // Catch boundary case (bottomRight is first or last segment):
+ const VSegment* current;
+ const VSegment* next;
+
+ if( bottomRight == getFirst() )
+ current = getLast();
+ else
+ current = bottomRight;
+
+ if( bottomRight == getLast() )
+ next = getFirst()->next();
+ else
+ next = bottomRight->next();
+
+ // Check "z-component" of cross product:
+ return
+ ( next->knot().x() - next->prev()->knot().x() ) *
+ ( current->knot().y() - current->prev()->knot().y() )
+ -
+ ( next->knot().y() - next->prev()->knot().y() ) *
+ ( current->knot().x() - current->prev()->knot().x() ) < 0.0;
+}
+
+void
+VSubpath::revert()
+{
+ // Catch case where the list is "empty".
+ if( isEmpty() )
+ return;
+
+
+ VSubpath list( parent() );
+ list.moveTo( getLast()->knot() );
+
+ VSegment* segment = getLast();
+
+ while( segment->prev() )
+ {
+ list.append( segment->revert() );
+ segment = segment->prev();
+ }
+
+ list.m_isClosed = isClosed();
+
+ *this = list;
+}
+
+const KoRect&
+VSubpath::boundingBox() const
+{
+ if( m_boundingBoxIsInvalid )
+ {
+ // Reset the boundingbox.
+ m_boundingBox = KoRect();
+
+ VSegment* segment = m_first;
+
+ while( segment )
+ {
+ if( segment->state() != VSegment::deleted )
+ m_boundingBox |= segment->boundingBox();
+
+ segment = segment->m_next;
+ }
+
+ m_boundingBoxIsInvalid = false;
+ }
+
+ return m_boundingBox;
+}
+
+VSubpath*
+VSubpath::clone() const
+{
+ return new VSubpath( *this );
+}
+
+void
+VSubpath::saveSvgPath( TQString &d ) const
+{
+ // Save segments.
+ VSegment* segment = getFirst();
+
+ while( segment )
+ {
+ if( segment->state() == VSegment::normal )
+ {
+ if( segment->degree() <= 2 )
+ {
+ // Line.
+ if( segment->prev() )
+ {
+ d += TQString( "L%1 %2" ).
+ arg( segment->knot().x() ).arg( segment->knot().y() );
+ }
+ // Moveto.
+ else
+ {
+ d += TQString( "M%1 %2" ).
+ arg( segment->knot().x() ).arg( segment->knot().y() );
+ }
+ }
+ // Bezier ( degree >= 3 ).
+ else
+ {
+ // We currently treat all beziers as cubic beziers.
+ d += TQString( "C%1 %2 %3 %4 %5 %6" ).
+ arg( segment->point( segment->degree() - 3 ).x() ).
+ arg( segment->point( segment->degree() - 3 ).y() ).
+ arg( segment->point( segment->degree() - 2 ).x() ).
+ arg( segment->point( segment->degree() - 2 ).y() ).
+ arg( segment->knot().x() ).
+ arg( segment->knot().y() );
+ }
+ }
+
+ segment = segment->m_next;
+ }
+
+ if( isClosed() )
+ d += "Z";
+}
+
+// TODO: remove this backward compatibility function after koffice 1.3.x
+void
+VSubpath::load( const TQDomElement& element )
+{
+ // We might have a "begin" segment.
+ clear();
+
+ TQDomNodeList list = element.childNodes();
+
+ for( uint i = 0; i < list.count(); ++i )
+ {
+ if( list.item( i ).isElement() )
+ {
+ TQDomElement segment = list.item( i ).toElement();
+
+ VSegment* s = new VSegment();
+ s->load( segment );
+ append( s );
+ }
+ }
+
+ if( element.attribute( "isClosed" ) == 0 ? false : true )
+ close();
+}
+
+void
+VSubpath::accept( VVisitor& visitor )
+{
+ visitor.visitVSubpath( *this );
+}
+
+
+VSubpath&
+VSubpath::operator=( const VSubpath& list )
+{
+ if( this == &list )
+ return *this;
+
+ m_isClosed = list.isClosed();
+
+ clear();
+
+ VSegment* segment = list.m_first;
+
+ while( segment )
+ {
+ append( segment->clone() );
+ segment = segment->m_next;
+ }
+
+ m_current = m_first;
+ m_currentIndex = 0;
+
+ return *this;
+}
+
+bool
+VSubpath::insert( const VSegment* segment )
+{
+ if( m_currentIndex == -1 )
+ return false;
+
+ VSegment* s = const_cast<VSegment*>( segment );
+
+ VSegment* prev = m_current->m_prev;
+
+ m_current->m_prev = s;
+ prev->m_next = s;
+ s->m_prev = prev;
+ s->m_next = m_current;
+
+ m_current = s;
+ ++m_number;
+
+ invalidateBoundingBox();
+
+ return true;
+}
+
+bool
+VSubpath::insert( uint index, const VSegment* segment )
+{
+ VSegment* s = const_cast<VSegment*>( segment );
+
+ if( index == 0 )
+ {
+ prepend( s );
+ return true;
+ }
+ else if( index == m_number )
+ {
+ append( s );
+ return true;
+ }
+
+ VSegment* next = locate( index );
+
+ if( !next )
+ return false;
+
+ VSegment* prev = next->m_prev;
+
+ next->m_prev = s;
+ prev->m_next = s;
+ s->m_prev = prev;
+ s->m_next = next;
+
+ m_current = s;
+ ++m_number;
+
+ invalidateBoundingBox();
+
+ return true;
+}
+
+void
+VSubpath::prepend( const VSegment* segment )
+{
+ VSegment* s = const_cast<VSegment*>( segment );
+
+ s->m_prev = 0L;
+
+ if( ( s->m_next = m_first ) )
+ m_first->m_prev = s;
+ else
+ m_last = s;
+
+ m_first = m_current = s;
+
+ ++m_number;
+ m_currentIndex = 0;
+
+ invalidateBoundingBox();
+}
+
+void
+VSubpath::append( const VSegment* segment )
+{
+ VSegment* s = const_cast<VSegment*>( segment );
+
+ s->m_next = 0L;
+
+ if( ( s->m_prev = m_last ) )
+ m_last->m_next = s;
+ else
+ m_first = s;
+
+ m_last = m_current = s;
+
+ m_currentIndex = m_number;
+ ++m_number;
+
+ invalidateBoundingBox();
+}
+
+void
+VSubpath::clear()
+{
+ VSegment* segment = m_first;
+
+ m_first = m_last = m_current = 0L;
+ m_number = 0;
+ m_currentIndex = -1;
+
+ if( m_iteratorList )
+ m_iteratorList->notifyClear( false );
+
+ VSegment* prev;
+
+ while( segment )
+ {
+ prev = segment;
+ segment = segment->m_next;
+ delete prev;
+ }
+
+ m_isClosed = false;
+
+ invalidateBoundingBox();
+}
+
+VSegment*
+VSubpath::first()
+{
+ if( m_first )
+ {
+ m_currentIndex = 0;
+ return m_current = m_first;
+ }
+
+ return 0L;
+}
+
+VSegment*
+VSubpath::last()
+{
+ if( m_last )
+ {
+ m_currentIndex = m_number - 1;
+ return m_current = m_last;
+ }
+
+ return 0L;
+}
+
+VSegment*
+VSubpath::prev()
+{
+ if( m_current )
+ {
+ if( m_current->m_prev )
+ {
+ --m_currentIndex;
+ return m_current = m_current->m_prev;
+ }
+
+ m_currentIndex = -1;
+ m_current = 0L;
+ }
+
+ return 0L;
+}
+
+VSegment*
+VSubpath::next()
+{
+ if( m_current )
+ {
+ if( m_current->m_next )
+ {
+ ++m_currentIndex;
+ return m_current = m_current->m_next;
+ }
+
+ m_currentIndex = -1;
+ m_current = 0L;
+ }
+
+ return 0L;
+}
+
+VSegment*
+VSubpath::locate( uint index )
+{
+ if( index == static_cast<uint>( m_currentIndex ) )
+ return m_current;
+
+ if( !m_current && m_first )
+ {
+ m_current = m_first;
+ m_currentIndex = 0;
+ }
+
+ VSegment* segment;
+ int distance = index - m_currentIndex;
+ bool forward;
+
+ if( index >= m_number )
+ return 0L;
+
+ if( distance < 0 )
+ distance = -distance;
+
+ if(
+ static_cast<uint>( distance ) < index &&
+ static_cast<uint>( distance ) < m_number - index )
+ {
+ segment = m_current;
+ forward = index > static_cast<uint>( m_currentIndex );
+ }
+ else if( index < m_number - index )
+ {
+ segment = m_first;
+ distance = index;
+ forward = true;
+ }
+ else
+ {
+ segment = m_last;
+ distance = m_number - index - 1;
+ if( distance < 0 )
+ distance = 0;
+ forward = false;
+ }
+
+ if( forward )
+ {
+ while( distance-- )
+ segment = segment->m_next;
+ }
+ else
+ {
+ while( distance-- )
+ segment = segment->m_prev;
+ }
+
+ m_currentIndex = index;
+ return m_current = segment;
+}
+
+
+VSubpathIterator::VSubpathIterator( const VSubpath& list )
+{
+ m_list = const_cast<VSubpath*>( &list );
+ m_current = m_list->m_first;
+
+ if( !m_list->m_iteratorList )
+ m_list->m_iteratorList = new VSubpathIteratorList();
+
+ m_list->m_iteratorList->add( this );
+}
+
+VSubpathIterator::VSubpathIterator( const VSubpathIterator& itr )
+{
+ m_list = itr.m_list;
+ m_current = itr.m_current;
+
+ if( m_list )
+ m_list->m_iteratorList->add( this );
+}
+
+VSubpathIterator::~VSubpathIterator()
+{
+ if( m_list )
+ m_list->m_iteratorList->remove( this );
+}
+
+VSubpathIterator&
+VSubpathIterator::operator=( const VSubpathIterator& itr )
+{
+ if( m_list )
+ m_list->m_iteratorList->remove( this );
+
+ m_list = itr.m_list;
+ m_current = itr.m_current;
+
+ if( m_list )
+ m_list->m_iteratorList->add( this );
+
+ return *this;
+}
+
+VSegment*
+VSubpathIterator::current() const
+{
+ // If m_current points to a deleted segment, find the next not
+ // deleted segment.
+ if(
+ m_current &&
+ m_current->state() == VSegment::deleted )
+ {
+ return m_current->next();
+ }
+
+ return m_current;
+}
+
+VSegment*
+VSubpathIterator::operator()()
+{
+ if( VSegment* const old = current() )
+ {
+ m_current = current()->next();
+ return old;
+ }
+
+ return 0L;
+}
+
+VSegment*
+VSubpathIterator::operator++()
+{
+ if( current() )
+ return m_current = current()->next();
+
+ return 0L;
+}
+
+VSegment*
+VSubpathIterator::operator+=( uint i )
+{
+ while( current() && i-- )
+ m_current = current()->next();
+
+ return current();
+}
+
+VSegment*
+VSubpathIterator::operator--()
+{
+ if( current() )
+ return m_current = current()->prev();
+
+ return 0L;
+}
+
+VSegment*
+VSubpathIterator::operator-=( uint i )
+{
+ while( current() && i-- )
+ m_current = current()->prev();
+
+ return current();
+}
+