F:/KPlato/koffice/libs/flake/KoPathShape.cpp

Aller à la documentation de ce fichier.
00001 /* This file is part of the KDE project
00002    Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
00003    Copyright (C) 2006 Jan Hambrecht <jaham@gmx.net>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "KoPathShape.h"
00022 #include "KoInsets.h"
00023 #include "KoShapeBorderModel.h"
00024 #include "KoViewConverter.h"
00025 
00026 #include <QDebug>
00027 #include <QPainter>
00028 #include <math.h>
00029 
00030 KoPathPoint::KoPathPoint( const KoPathPoint & pathPoint )
00031 : m_pointGroup( 0 )
00032 {
00033     m_shape = pathPoint.m_shape;
00034     m_point = pathPoint.m_point;
00035     m_controlPoint1 = pathPoint.m_controlPoint1;
00036     m_controlPoint2 = pathPoint.m_controlPoint2;
00037     m_properties = pathPoint.m_properties;
00038 }
00039 
00040 KoPathPoint& KoPathPoint::operator=( const KoPathPoint &rhs )
00041 {
00042     if( this == &rhs )
00043         return (*this);
00044 
00045     m_shape = rhs.m_shape;
00046     m_point = rhs.m_point;
00047     m_controlPoint1 = rhs.m_controlPoint1;
00048     m_controlPoint2 = rhs.m_controlPoint2;
00049     m_properties = rhs.m_properties;
00050     //m_pointGroup = rhs.m_pointGroup;
00051 
00052     return (*this);
00053 }
00054 
00055 void KoPathPoint::setPoint( const QPointF & point ) 
00056 {
00057     m_point = point;
00058     m_shape->update();
00059 }
00060 
00061 void KoPathPoint::setControlPoint1( const QPointF & point ) 
00062 { 
00063     m_controlPoint1 = point; 
00064     m_properties |= HasControlPoint1; 
00065     m_shape->update(); 
00066 }
00067 
00068 void KoPathPoint::setControlPoint2( const QPointF & point ) 
00069 { 
00070     m_controlPoint2 = point; 
00071     m_properties |= HasControlPoint2; 
00072     m_shape->update();
00073 }
00074 
00075 void KoPathPoint::setProperties( KoPointProperties properties ) 
00076 {
00077     if( properties & HasControlPoint1 == 0 || properties & HasControlPoint2 == 0 )
00078     {
00079         // strip smooth and symmetric flags if points has not two control points
00080         properties &= ~IsSmooth;
00081         properties &= ~IsSymmetric;
00082     }
00083     if( properties & KoPathPoint::StartSubpath )
00084     {
00085         properties &= ~CloseSubpath;
00086     }
00087     m_properties = properties;
00088     m_shape->update();
00089 }
00090 
00091 void KoPathPoint::setProperty( KoPointProperty property )
00092 {
00093     switch( property )
00094     {
00095         case StartSubpath:
00096             m_properties &= ~CloseSubpath;
00097         break;
00098         case CloseSubpath:
00099             m_properties &= ~StartSubpath;
00100             m_properties |= CanHaveControlPoint2;
00101         break;
00102         case CanHaveControlPoint1:
00103             if( m_properties & StartSubpath )
00104                 return;
00105         break;
00106         case HasControlPoint1:
00107             if( m_properties & CanHaveControlPoint1 == 0 )
00108                 return;
00109         break;
00110         case CanHaveControlPoint2:
00111             if( m_properties & CloseSubpath )
00112                 return;
00113         break;
00114         case HasControlPoint2:
00115             if( m_properties & CanHaveControlPoint2 == 0 )
00116                 return;
00117         break;
00118         case IsSmooth:
00119             if( m_properties & HasControlPoint1 == 0 && m_properties & HasControlPoint2 == 0 )
00120                 return;
00121             m_properties &= ~IsSymmetric;
00122         break;
00123         case IsSymmetric:
00124             if( m_properties & HasControlPoint1 == 0 && m_properties & HasControlPoint2 == 0 )
00125                 return;
00126             m_properties &= ~IsSmooth;
00127         break;
00128         default: return;
00129     }
00130     m_properties |= property;
00131 }
00132 
00133 void KoPathPoint::unsetProperty( KoPointProperty property )
00134 {
00135     switch( property )
00136     {
00137         case StartSubpath:
00138             m_properties |= CanHaveControlPoint1;
00139         break;
00140         case CloseSubpath:
00141             m_properties |= CanHaveControlPoint2;
00142         break;
00143         case CanHaveControlPoint1:
00144             m_properties &= ~HasControlPoint1;
00145             m_properties &= ~IsSmooth;
00146             m_properties &= ~IsSymmetric;
00147         break;
00148         case CanHaveControlPoint2:
00149             m_properties &= ~HasControlPoint2;
00150             m_properties &= ~IsSmooth;
00151             m_properties &= ~IsSymmetric;
00152         break;
00153         case HasControlPoint1:
00154         case HasControlPoint2:
00155             m_properties &= ~IsSmooth;
00156             m_properties &= ~IsSymmetric;
00157         break;
00158         case IsSmooth:
00159         case IsSymmetric:
00160             // no others depend on these
00161         break;
00162         default: return;
00163     }
00164     m_properties &= ~property;
00165 }
00166 
00167 bool KoPathPoint::activeControlPoint1() const
00168 {
00169     return ( properties() & HasControlPoint1 && properties() & CanHaveControlPoint1 );
00170 }
00171 
00172 bool KoPathPoint::activeControlPoint2() const
00173 {
00174     return ( properties() & HasControlPoint2 && properties() & CanHaveControlPoint2 );
00175 }
00176 
00177 void KoPathPoint::map( const QMatrix &matrix, bool mapGroup )
00178 { 
00179     if ( m_pointGroup && mapGroup )
00180     {
00181         m_pointGroup->map( matrix );
00182     }
00183     else
00184     {
00185         m_point = matrix.map( m_point ); 
00186         m_controlPoint1 = matrix.map( m_controlPoint1 );
00187         m_controlPoint2 = matrix.map( m_controlPoint2 );
00188     }
00189     m_shape->update(); 
00190 }
00191 
00192 void KoPathPoint::paint(QPainter &painter, const QSizeF &size, bool selected)
00193 {
00194     QRectF handle( QPointF(-0.5*size.width(),0-0.5*size.height()), size );
00195 
00196     if( selected )
00197     {
00198         if( activeControlPoint1() )
00199         {
00200             painter.drawLine( point(), controlPoint1() );
00201             painter.drawEllipse( handle.translated( controlPoint1() ) );
00202         }
00203         if( activeControlPoint2() )
00204         {
00205             painter.drawLine( point(), controlPoint2() );
00206             painter.drawEllipse( handle.translated( controlPoint2() ) );
00207         }
00208     }
00209 
00210     if( properties() & IsSmooth )
00211         painter.drawRect( handle.translated( point() ) );
00212     else if( properties() & IsSymmetric )
00213     {
00214         QWMatrix matrix;
00215         matrix.rotate( 45.0 );
00216         QPolygonF poly( handle );
00217         poly = matrix.map( poly );
00218         poly.translate( point() );
00219         painter.drawPolygon( poly );
00220     }
00221     else
00222         painter.drawEllipse( handle.translated( point() ) );
00223 }
00224 
00225 void KoPathPoint::setParent( KoPathShape* parent )
00226 {
00227     // don't set to zero
00228     Q_ASSERT( parent );
00229     m_shape = parent;
00230 }
00231 
00232 QRectF KoPathPoint::boundingRect() const
00233 {
00234     QRectF rect( m_point, QSize( 1, 1 ) );
00235     if ( activeControlPoint1() )
00236     {
00237         QRectF r1( m_point, QSize( 1, 1 ) );
00238         r1.setBottomRight( m_controlPoint1 );
00239         rect = rect.unite( r1 );
00240     }
00241     if ( activeControlPoint2() )
00242     {
00243         QRectF r2( m_point, QSize( 1, 1 ) );
00244         r2.setBottomRight( m_controlPoint2 );
00245         rect = rect.unite( r2 );
00246     }
00247     return m_shape->shapeToDocument( rect );
00248 }
00249 
00250 void KoPathPoint::reverse()
00251 {
00252     qSwap( m_controlPoint1, m_controlPoint2 );
00253     KoPointProperties newProps = Normal;
00254     if( m_properties & CanHaveControlPoint1 )
00255         newProps |= CanHaveControlPoint2;
00256     if( m_properties & CanHaveControlPoint2 )
00257         newProps |= CanHaveControlPoint1;
00258     if( m_properties & HasControlPoint1 )
00259         newProps |= HasControlPoint2;
00260     if( m_properties & HasControlPoint2 )
00261         newProps |= HasControlPoint1;
00262     newProps |= m_properties & IsSmooth;
00263     newProps |= m_properties & IsSymmetric;
00264     newProps |= m_properties & StartSubpath;
00265     newProps |= m_properties & CloseSubpath;
00266     qDebug() << "oldProps = " << m_properties;
00267     m_properties = newProps;
00268     qDebug() << "newProps = " << m_properties;
00269 }
00270 
00271 void KoPathPoint::removeFromGroup()
00272 { 
00273     if ( m_pointGroup ) 
00274         m_pointGroup->remove( this ); 
00275     m_pointGroup = 0; 
00276 }
00277 
00278 void KoPathPoint::addToGroup( KoPointGroup *pointGroup ) 
00279 { 
00280     if ( m_pointGroup && m_pointGroup != pointGroup )
00281     {
00282         //TODO error message as this should not happen
00283         removeFromGroup();
00284     }
00285     m_pointGroup = pointGroup; 
00286 }
00287 
00288 void KoPointGroup::add( KoPathPoint * point )
00289 { 
00290     m_points.insert( point ); 
00291     point->addToGroup( this );
00292 }
00293 
00294 void KoPointGroup::remove( KoPathPoint * point ) 
00295 { 
00296     if ( m_points.remove( point ) ) 
00297     {    
00298         point->removeFromGroup();
00299         if ( m_points.size() == 1 )
00300         {
00301             ( * m_points.begin() )->removeFromGroup();
00302             //commit suicide as it is no longer used
00303             delete this;
00304         }
00305     }
00306 }
00307 
00308 void KoPointGroup::map( const QMatrix &matrix )
00309 {
00310     QSet<KoPathPoint *>::iterator it = m_points.begin();
00311     for ( ; it != m_points.end(); ++it )
00312     {
00313         ( *it )->map( matrix, false );
00314     }
00315 }
00316 
00317 KoPathShape::KoPathShape()
00318 {
00319 }
00320 
00321 KoPathShape::~KoPathShape()
00322 {
00323 }
00324 
00325 void KoPathShape::paint( QPainter &painter, const KoViewConverter &converter )
00326 {
00327     applyConversion( painter, converter );
00328     QPainterPath path( outline() );
00329     
00330     painter.setBrush( background() );
00331     painter.drawPath( path );
00332     //paintDebug( painter );
00333 }
00334 
00335 #ifndef NDEBUG
00336 void KoPathShape::paintDebug( QPainter &painter )
00337 {
00338     KoSubpathList::const_iterator pathIt( m_subpaths.begin() );
00339     int i = 0;
00340 
00341     QPen pen( Qt::black );
00342     painter.save();
00343     painter.setPen( pen );
00344     for ( ; pathIt != m_subpaths.end(); ++pathIt )
00345     {
00346         KoSubpath::const_iterator it( ( *pathIt )->begin() );
00347         for ( ; it != ( *pathIt )->end(); ++it )
00348         {
00349             ++i;
00350             KoPathPoint *point = ( *it );
00351             QRectF r( point->point(), QSizeF( 5, 5 ) );
00352             r.translate( -2.5, -2.5 );
00353             QPen pen( Qt::black );
00354             painter.setPen( pen );
00355             if ( point->group() )
00356             {
00357                 QBrush b( Qt::blue );
00358                 painter.setBrush( b );
00359             }
00360             else if ( point->properties() & KoPathPoint::CanHaveControlPoint1 && point->properties() & KoPathPoint::CanHaveControlPoint2 )
00361             {
00362                 QBrush b( Qt::red );
00363                 painter.setBrush( b );
00364             }
00365             else if ( point->properties() & KoPathPoint::CanHaveControlPoint1 ) 
00366             {
00367                 QBrush b( Qt::yellow );
00368                 painter.setBrush( b );
00369             }
00370             else if ( point->properties() & KoPathPoint::CanHaveControlPoint2 ) 
00371             {
00372                 QBrush b( Qt::darkYellow );
00373                 painter.setBrush( b );
00374             }
00375             painter.drawEllipse( r );
00376         }
00377     }
00378     painter.restore();
00379     qDebug() << "nop = " << i;
00380 }
00381 #endif
00382 
00383 void KoPathShape::debugPath()
00384 {
00385     KoSubpathList::iterator pathIt( m_subpaths.begin() );
00386     for ( ; pathIt != m_subpaths.end(); ++pathIt )
00387     {
00388         KoSubpath::const_iterator it( ( *pathIt )->begin() );
00389         for ( ; it != ( *pathIt )->end(); ++it )
00390         {
00391             qDebug() << "p:" << ( *pathIt ) << "," << *it << "," << ( *it )->point() << "," << ( *it )->properties() << "," << ( *it )->group();
00392         }
00393     }
00394 }
00395 
00396 QPointF KoPathShape::shapeToDocument( const QPointF &point ) const
00397 {
00398     return transformationMatrix(0).map( point );
00399 }
00400 
00401 QRectF KoPathShape::shapeToDocument( const QRectF &rect ) const 
00402 {
00403     return transformationMatrix(0).mapRect( rect );
00404 }
00405 
00406 QPointF KoPathShape::documentToShape( const QPointF &point ) const
00407 {
00408     return transformationMatrix(0).inverted().map( point );
00409 }
00410 
00411 QRectF KoPathShape::documentToShape( const QRectF &rect ) const 
00412 {
00413     return transformationMatrix(0).inverted().mapRect( rect );
00414 }
00415 
00416 
00417 void KoPathShape::paintPoints( QPainter &painter, const KoViewConverter &converter )
00418 {
00419     applyConversion( painter, converter );
00420 
00421     KoSubpathList::const_iterator pathIt( m_subpaths.begin() );
00422 
00423     QRectF handle = converter.viewToDocument( handleRect( QPoint(0,0) ) );
00424 
00425     for ( ; pathIt != m_subpaths.end(); ++pathIt )
00426     {
00427         KoSubpath::const_iterator it( ( *pathIt )->begin() );
00428         for ( ; it != ( *pathIt )->end(); ++it )
00429         {
00430             KoPathPoint *point = ( *it );
00431             point->paint( painter, handle.size(), false );
00432         }
00433     }
00434 }
00435 
00436 QRectF KoPathShape::handleRect( const QPointF &p ) const
00437 {
00438     const qreal handleRadius = 3.0;
00439     return QRectF( p.x()-handleRadius, p.y()-handleRadius, 2*handleRadius, 2*handleRadius );
00440 }
00441 
00442 const QPainterPath KoPathShape::outline() const
00443 {
00444     KoSubpathList::const_iterator pathIt( m_subpaths.begin() );
00445     QPainterPath path;
00446     for ( ; pathIt != m_subpaths.end(); ++pathIt )
00447     {
00448         KoSubpath::const_iterator it( ( *pathIt )->begin() );
00449         KoPathPoint * lastPoint( *it );
00450         bool activeCP = false;
00451         for ( ; it != ( *pathIt )->end(); ++it )
00452         {
00453             if ( it == ( *pathIt )->begin() )
00454             {
00455                 if ( ( *it )->properties() & KoPathPoint::StartSubpath )
00456                 {
00457                     //qDebug() << "moveTo(" << ( *it )->point() << ")";
00458                     path.moveTo( ( *it )->point() );
00459                 }
00460             }
00461             else if ( activeCP || ( *it )->activeControlPoint1() )
00462             {
00463                 //qDebug() << "cubicTo(" << ( activeCP ? lastPoint->controlPoint2() : lastPoint->point() )
00464                 //         << "," << ( ( *it )->activeControlPoint1() ? ( *it )->controlPoint1() : ( *it )->point() )
00465                 //         << "," << ( *it )->point() << ")";
00466 
00467                 path.cubicTo( activeCP ? lastPoint->controlPoint2() : lastPoint->point()
00468                             , ( *it )->activeControlPoint1() ? ( *it )->controlPoint1() : ( *it )->point()
00469                             , ( *it )->point() );
00470             }
00471             else
00472             {
00473                 //qDebug() << "lineTo(" << ( *it )->point() << ")";
00474                 path.lineTo( ( *it )->point() );
00475             }
00476             if ( ( *it )->properties() & KoPathPoint::CloseSubpath )
00477             {
00478                 // add curve when there is a curve on the way to the first point
00479                 KoPathPoint * firstPoint = ( *pathIt )->first();
00480                 if ( ( *it )->activeControlPoint2() || firstPoint->activeControlPoint1() )
00481                 {
00482                     //qDebug() << "cubicTo(" << ( ( *it )->activeControlPoint2() ? ( *it )->controlPoint2() : ( *it )->point() )
00483                     //         << "," << ( firstPoint->activeControlPoint1() ? firstPoint->controlPoint1() : firstPoint->point() )
00484                     //         << "," << firstPoint->point() << ")";
00485                     path.cubicTo( (*it)->activeControlPoint2() ? ( *it )->controlPoint2() : ( *it )->point()
00486                                 , firstPoint->activeControlPoint1() ? firstPoint->controlPoint1() : firstPoint->point()
00487                                 , firstPoint->point() );
00488                 }
00489                 //qDebug() << "closeSubpath()";
00490                 path.closeSubpath();
00491             }
00492 
00493             if ( ( *it )->activeControlPoint2() )
00494             {
00495                 activeCP = true;
00496             }
00497             else
00498             {
00499                 activeCP = false;
00500             }
00501             lastPoint = *it;
00502         }
00503     }
00504     return path;
00505 }
00506 
00507 QRectF KoPathShape::boundingRect() const
00508 {
00509     QRectF bb( outline().boundingRect() );
00510     if( m_border )
00511     {
00512         KoInsets inset;
00513         m_border->borderInsets( this, inset );
00514         bb.adjust( -inset.left, -inset.top, inset.right, inset.bottom );
00515     }
00516     //qDebug() << "KoPathShape::boundingRect = " << bb;
00517     return transformationMatrix( 0 ).mapRect( bb );
00518 }
00519 
00520 
00521 QSizeF KoPathShape::size() const
00522 {
00523     // don't call boundingRect here as it uses transformationMatrix which leads to invinit reccursion
00524     return outline().boundingRect().size();
00525 }
00526 
00527 QPointF KoPathShape::position() const
00528 {
00529     //return boundingRect().topLeft();
00530     return KoShape::position();
00531 }
00532 
00533 void KoPathShape::resize( const QSizeF &newSize )
00534 {
00535     QSizeF oldSize = size();
00536     double zoomX = newSize.width() / oldSize.width(); 
00537     double zoomY = newSize.height() / oldSize.height(); 
00538     QMatrix matrix( zoomX, 0, 0, zoomY, 0, 0 );
00539 
00540     qDebug() << "resize" << zoomX << "," << zoomY << "," << newSize;
00541     map( matrix );
00542     KoShape::resize( newSize );
00543 }
00544 
00545 KoPathPoint * KoPathShape::moveTo( const QPointF &p )
00546 {
00547     KoPathPoint * point = new KoPathPoint( this, p, KoPathPoint::StartSubpath | KoPathPoint::CanHaveControlPoint2 );
00548     KoSubpath * path = new KoSubpath;
00549     path->push_back( point );
00550     m_subpaths.push_back( path );
00551     return point;
00552 }
00553 
00554 KoPathPoint * KoPathShape::lineTo( const QPointF &p )
00555 {
00556     if ( m_subpaths.empty() )
00557     {
00558         moveTo( QPointF( 0, 0 ) );
00559     }
00560     KoPathPoint * point = new KoPathPoint( this, p, KoPathPoint::CanHaveControlPoint1 );
00561     KoPathPoint * lastPoint = m_subpaths.last()->last();
00562     updateLast( &lastPoint );
00563     m_subpaths.last()->push_back( point );
00564     return point;
00565 }
00566 
00567 KoPathPoint * KoPathShape::curveTo( const QPointF &c1, const QPointF &c2, const QPointF &p )
00568 {
00569     if ( m_subpaths.empty() )
00570     {
00571         moveTo( QPointF( 0, 0 ) );
00572     }
00573     KoPathPoint * lastPoint = m_subpaths.last()->last();
00574     updateLast( &lastPoint );
00575     lastPoint->setControlPoint2( c1 );
00576     KoPathPoint * point = new KoPathPoint( this, p, KoPathPoint::CanHaveControlPoint1 );
00577     point->setControlPoint1( c2 );
00578     m_subpaths.last()->push_back( point );
00579     return point;
00580 }
00581 
00582 KoPathPoint * KoPathShape::arcTo( double rx, double ry, double startAngle, double sweepAngle )
00583 {
00584     if ( m_subpaths.empty() )
00585     {
00586         moveTo( QPointF( 0, 0 ) );
00587     }
00588 
00589     KoPathPoint * lastPoint = m_subpaths.last()->last();
00590     if ( lastPoint->properties() & KoPathPoint::CloseSubpath )
00591     {
00592         lastPoint = m_subpaths.last()->first();
00593     }
00594     QPointF startpoint( lastPoint->point() );
00595 
00596     KoPathPoint * newEndPoint = lastPoint;
00597 
00598     QPointF curvePoints[12];
00599     int pointCnt = arcToCurve( rx, ry, startAngle, sweepAngle, startpoint, curvePoints );
00600     for ( int i = 0; i < pointCnt; i += 3 )
00601     {
00602         newEndPoint = curveTo( curvePoints[i], curvePoints[i+1], curvePoints[i+2] );
00603     }
00604     return newEndPoint;
00605 }
00606 
00607 int KoPathShape::arcToCurve( double rx, double ry, double startAngle, double sweepAngle, const QPointF & offset, QPointF * curvePoints ) const
00608 {
00609     int pointCnt = 0;
00610 
00611     // check Parameters
00612     if ( sweepAngle == 0 )
00613         return pointCnt;
00614     if (  sweepAngle > 360 )
00615         sweepAngle = 360;
00616     else if (  sweepAngle < -360 )
00617         sweepAngle = - 360;
00618 
00619     if ( rx == 0 || ry == 0 )
00620     {
00621         //TODO
00622     }
00623 
00624     // split angles bigger than 90° so that it gives a good aproximation to the circle
00625     double parts = ceil( qAbs( sweepAngle / 90.0 ) );
00626 
00627     double sa_rad = startAngle * M_PI / 180.0;
00628     double partangle = sweepAngle / parts;
00629     double endangle = startAngle + partangle;
00630     double se_rad = endangle * M_PI / 180.0;
00631     double sinsa = sin( sa_rad );
00632     double cossa = cos( sa_rad );
00633     double kappa = 4.0 / 3.0 * tan( ( se_rad - sa_rad ) / 4 );
00634 
00635     // startpoint is at the last point is the path but when it is closed
00636     // it is at the first point
00637     QPointF startpoint( offset );
00638 
00639     //center berechnen
00640     QPointF center( startpoint - QPointF( cossa * rx, -sinsa * ry ) );
00641 
00642     qDebug() << "kappa" << kappa << "parts" << parts;
00643     
00644     for ( int part = 0; part < parts; ++part )
00645     {
00646         // start tangent
00647         curvePoints[pointCnt++] = QPointF( startpoint - QPointF( sinsa * rx * kappa, cossa * ry * kappa ) );
00648 
00649         double sinse = sin( se_rad );
00650         double cosse = cos( se_rad );
00651 
00652         // end point
00653         QPointF endpoint( center + QPointF( cosse * rx, -sinse * ry ) );
00654         // end tangent
00655         curvePoints[pointCnt++] = QPointF( endpoint - QPointF( -sinse * rx * kappa, -cosse * ry * kappa ) );
00656         curvePoints[pointCnt++] = endpoint;
00657 
00658         // set the endpoint as next start point
00659         startpoint = endpoint;
00660         sinsa = sinse;
00661         cossa = cosse;
00662         endangle += partangle;
00663         se_rad = endangle * M_PI / 180.0;
00664     }
00665 
00666     return pointCnt;
00667 }
00668 
00669 void KoPathShape::close()
00670 {
00671     if ( m_subpaths.empty() )
00672     {
00673         return;
00674     }
00675     closeSubpath( m_subpaths.last() );
00676 }
00677 
00678 void KoPathShape::closeMerge()
00679 {
00680     if ( m_subpaths.empty() )
00681     {
00682         return;
00683     }
00684     closeMergeSubpath( m_subpaths.last() );
00685 }
00686 
00687 void KoPathShape::update()
00688 {
00689     updateTree();
00690 }
00691 
00692 QPointF KoPathShape::normalize()
00693 {
00694     QPointF oldTL( boundingRect().topLeft() );
00695     
00696     QPointF tl( outline().boundingRect().topLeft() );
00697     QMatrix matrix;
00698     matrix.translate( -tl.x(), -tl.y() );
00699     map( matrix );
00700 
00701     // keep the top left point of the object
00702     QPointF newTL( boundingRect().topLeft() );
00703     QPointF diff( oldTL - newTL );
00704     moveBy( diff.x(), diff.y() );
00705     return tl;
00706 }
00707 
00708 void KoPathShape::map( const QMatrix &matrix )
00709 {
00710     KoSubpathList::iterator pathIt( m_subpaths.begin() );
00711     for ( ; pathIt != m_subpaths.end(); ++pathIt )
00712     {
00713         KoSubpath::iterator it( ( *pathIt )->begin() );
00714         for ( ; it != ( *pathIt )->end(); ++it )
00715         {
00716             ( *it )->map( matrix );
00717         }
00718     }
00719 }
00720 
00721 void KoPathShape::updateLast( KoPathPoint ** lastPoint )
00722 {
00723     if ( ( *lastPoint )->properties() & KoPathPoint::CloseSubpath )
00724     {
00725         KoPathPoint * subpathStart = m_subpaths.last()->first();
00726         KoPathPoint * newLastPoint = new KoPathPoint( *subpathStart );
00727         newLastPoint->setProperties( KoPathPoint::Normal );
00728         KoPointGroup * group = subpathStart->group();
00729         if ( group == 0 )
00730         {
00731             group = new KoPointGroup();
00732             group->add( subpathStart );
00733         }
00734         group->add( newLastPoint );
00735 
00736         KoSubpath *path = new KoSubpath;
00737         path->push_back( newLastPoint );
00738         m_subpaths.push_back( path );
00739         *lastPoint = newLastPoint;
00740     }
00741     ( *lastPoint )->setProperties( ( *lastPoint )->properties() | KoPathPoint::CanHaveControlPoint2 );
00742 }
00743 
00744 QList<KoPathPoint*> KoPathShape::pointsAt( const QRectF &r )
00745 {
00746     QList<KoPathPoint*> result;
00747 
00748     KoSubpathList::iterator pathIt( m_subpaths.begin() );
00749     for ( ; pathIt != m_subpaths.end(); ++pathIt )
00750     {
00751         KoSubpath::iterator it( ( *pathIt )->begin() );
00752         for ( ; it != ( *pathIt )->end(); ++it )
00753         {
00754             if( r.contains( (*it)->point() ) )
00755                 result.append( *it );
00756             else if( (*it)->activeControlPoint1() && r.contains( (*it)->controlPoint1() ) )
00757                 result.append( *it );
00758             else if( (*it)->activeControlPoint2() && r.contains( (*it)->controlPoint2() ) )
00759                 result.append( *it );
00760         }
00761     }
00762     return result;
00763 }
00764 
00765 KoPointPosition KoPathShape::removePoint( KoPathPoint *point )
00766 {
00767     KoSubpathList::iterator pathIt( m_subpaths.begin() );
00768     for ( ; pathIt != m_subpaths.end(); ++pathIt )
00769     {
00770         int index = ( *pathIt )->indexOf( point );
00771         if( index != -1 )
00772         {
00773             ( *pathIt )->removeAt( index );
00774 
00775             if ( !( *pathIt )->isEmpty() )
00776             {
00777                 // the first point of the sub path has been removed
00778                 if ( index == 0 )
00779                 {
00780                     if ( ( *pathIt )->last()->properties() & KoPathPoint::CloseSubpath )
00781                     {
00782                         ( *pathIt )->first()->setProperties( ( *pathIt )->first()->properties() | KoPathPoint::StartSubpath );
00783                     }
00784                     else
00785                     {
00786                         ( *pathIt )->first()->setProperties( ( ( *pathIt )->first()->properties() & ~KoPathPoint::CanHaveControlPoint1 ) | KoPathPoint::StartSubpath );
00787                     }
00788                 }
00789                 // the last point of the sub path has been removed
00790                 else if ( index == ( *pathIt )->size() )
00791                 {
00792                     if ( point->properties() & KoPathPoint::CloseSubpath )
00793                     {
00794                         ( *pathIt )->last()->setProperties( ( *pathIt )->last()->properties() | KoPathPoint::CloseSubpath );
00795                     }
00796                     else
00797                     {
00798                         ( *pathIt )->last()->setProperties( ( *pathIt )->last()->properties() & ~KoPathPoint::CanHaveControlPoint2 );
00799                     }
00800                 }
00801                 return QPair<KoSubpath*, int>( *pathIt, index );
00802             }
00803         }
00804     }
00805     return KoPointPosition( 0, 0 );
00806 }
00807 
00808 void KoPathShape::insertPoint( KoPathPoint* point, KoSubpath* subpath, int position )
00809 {
00810     if ( position == 0 )
00811     {
00812         subpath->first()->setProperties( subpath->first()->properties() & ~KoPathPoint::StartSubpath | KoPathPoint::CanHaveControlPoint1 );
00813     }
00814     else if ( position == subpath->size() )
00815     {
00816         subpath->last()->setProperties( subpath->last()->properties() & ~KoPathPoint::CloseSubpath | KoPathPoint::CanHaveControlPoint2 );
00817     }
00818     subpath->insert( position, point );
00819 }
00820 
00821 KoPathPoint* KoPathShape::nextPoint( KoPathPoint* point )
00822 {
00823     KoSubpathList::iterator pathIt( m_subpaths.begin() );
00824     for ( ; pathIt != m_subpaths.end(); ++pathIt )
00825     {
00826         int index = ( *pathIt )->indexOf( point );
00827         if( index != -1 )
00828         {
00829             if( index >= ( *pathIt )->size()-1 )
00830                 return 0;
00831             else
00832                 return ( *pathIt )->value( index+1 );
00833         }
00834     }
00835     return 0;
00836 }
00837 
00838 #if 0
00839 
00840 KoPathPoint* KoPathShape::prevPoint( KoPathPoint* point )
00841 {
00842     KoSubpathList::iterator pathIt( m_subpaths.begin() );
00843     for ( ; pathIt != m_subpaths.end(); ++pathIt )
00844     {
00845         int index = ( *pathIt )->indexOf( point );
00846         if( index != -1 )
00847         {
00848             if( index == 0 )
00849                 return 0;
00850             else
00851                 return ( *pathIt )->value( index-1 );
00852         }
00853     }
00854     return 0;
00855 }
00856 
00857 bool KoPathShape::insertPointAfter( KoPathPoint *point, KoPathPoint *prevPoint )
00858 {
00859    KoSubpathList::iterator pathIt( m_subpaths.begin() );
00860     for ( ; pathIt != m_subpaths.end(); ++pathIt )
00861     {
00862         int index = ( *pathIt )->indexOf( prevPoint );
00863         if( index != -1 )
00864         {
00865             // we insert after the last point
00866             if( index >= ( *pathIt )->size() )
00867                 ( *pathIt )->append( point );
00868             else
00869                 ( *pathIt )->insert( index+1, point );
00870             return true;
00871         }
00872     }
00873     return false;
00874 }
00875 
00876 bool KoPathShape::insertPointBefore( KoPathPoint *point, KoPathPoint *nextPoint )
00877 {
00878     KoSubpathList::iterator pathIt( m_subpaths.begin() );
00879     for ( ; pathIt != m_subpaths.end(); ++pathIt )
00880     {
00881         int index = ( *pathIt )->indexOf( nextPoint );
00882         if( index != -1 )
00883         {
00884             // we insert before the first point
00885             if( index == 0 )
00886                 ( *pathIt )->prepend( point );
00887             else
00888                 ( *pathIt )->insert( index-1, point );
00889             return true;
00890         }
00891     }
00892     return false;
00893 }
00894 #endif
00895 
00896 KoPathPoint* KoPathShape::splitAt( const KoPathSegment &segment, double t )
00897 {
00898     if( t < 0.0 || t > 1.0 )
00899         return 0;
00900 
00901     KoSubpath *subPath = 0;
00902     int index = 0;
00903     // first check if the segment is part of the path
00904     KoSubpathList::iterator pathIt( m_subpaths.begin() );
00905     for ( ; pathIt != m_subpaths.end(); ++pathIt )
00906     {
00907         index = ( *pathIt )->indexOf( segment.first );
00908 
00909         if( index != -1 )
00910         {
00911             subPath = *pathIt;
00912             if( index == ( *pathIt )->size()-1 )
00913                 return 0;
00914             if( ( *pathIt )->at( index + 1 ) != segment.second )
00915                 return 0;
00916             break;
00917         }
00918     }
00919     // both segment points were not found
00920     if( ! subPath )
00921         return 0;
00922 
00923     KoPathPoint *splitPoint = 0;
00924 
00925     // check if we have a curve
00926     if( segment.first->properties() & KoPathPoint::HasControlPoint2 || segment.second->properties() & KoPathPoint::HasControlPoint1 )
00927     {
00928         QPointF q[4] ={ 
00929            segment.first->point(), 
00930            segment.first->activeControlPoint2() ? segment.first->controlPoint2() : segment.first->point(), 
00931            segment.second->activeControlPoint1() ? segment.second->controlPoint1() : segment.second->point(), 
00932            segment.second->point()
00933         };
00934         QPointF p[3];
00935         // the De Casteljau algorithm.
00936         for( unsigned short j = 1; j <= 3; ++j )
00937         {
00938             for( unsigned short i = 0; i <= 3 - j; ++i )
00939             {
00940                 q[ i ] = ( 1.0 - t ) * q[ i ] + t * q[ i + 1 ];
00941             }
00942             // modify the new segment.
00943             p[j - 1] = q[ 0 ];
00944         }
00945         splitPoint = new KoPathPoint( this, p[2], KoPathPoint::CanHaveControlPoint1|KoPathPoint::CanHaveControlPoint2 );
00946         // modify the second control point of the segment start point
00947         segment.first->setControlPoint2( p[0] );
00948 
00949         splitPoint->setControlPoint1( p[1] );
00950         splitPoint->setControlPoint2( q[1] );
00951 
00952         // modify the first control point of the segment end point
00953         segment.second->setControlPoint1( q[2] );
00954     }
00955     else
00956     {
00957         QPointF splitPointPos = segment.first->point() + t * (segment.second->point() - segment.first->point());
00958         // easy, just a line
00959         splitPoint = new KoPathPoint( this, splitPointPos, KoPathPoint::CanHaveControlPoint1|KoPathPoint::CanHaveControlPoint2 );
00960     }
00961 
00962     subPath->insert( index+1, splitPoint );
00963 
00964     return splitPoint;
00965 }
00966 
00967 bool KoPathShape::breakAt( KoPathPoint *breakPoint, KoPathPoint* &insertedPoint )
00968 {
00969     if( ! breakPoint )
00970         return false;
00971 
00972     KoPointPosition pointPos = findPoint( breakPoint );
00973     if( ! pointPos.first )
00974         return false;
00975 
00976     KoSubpath *subpath = pointPos.first;
00977     KoPathPoint *newPoint = 0;
00978 
00979     if( &insertedPoint )
00980         insertedPoint = 0;
00981 
00982     // check if the subpath is closed
00983     if( subpath->last()->properties() & KoPathPoint::CloseSubpath )
00984     {
00985         // break at the first subpath point
00986         if( pointPos.second == 0 )
00987         {
00988             // duplicate the first point, which becomes the new last point
00989             newPoint = new KoPathPoint( *subpath->first() );
00990             newPoint->unsetProperty( KoPathPoint::StartSubpath );
00991             newPoint->unsetProperty( KoPathPoint::CanHaveControlPoint2 );
00992             subpath->last()->unsetProperty( KoPathPoint::CloseSubpath );
00993             subpath->append( newPoint );
00994         }
00995         // break at the last point
00996         else if( pointPos.second == subpath->size()-1 )
00997         {
00998             // duplicate the last point, which becomes the new first point
00999             newPoint = new KoPathPoint( *subpath->last() );
01000             // the last point no longer closes the subpath
01001             subpath->last()->unsetProperty( KoPathPoint::CloseSubpath );
01002             // the first point no longer starts the subpath 
01003             subpath->first()->unsetProperty( KoPathPoint::StartSubpath );
01004             // the new point start the open subpath
01005             newPoint->setProperty( KoPathPoint::StartSubpath );
01006             newPoint->unsetProperty( KoPathPoint::CanHaveControlPoint1 );
01007             subpath->prepend( newPoint );
01008         }
01009         // break in between
01010         else
01011         {
01012             // duplicate the break point, which becomes the new first point
01013             newPoint = new KoPathPoint( *breakPoint );
01014             // the old starting node no longer starts the subpath
01015             subpath->first()->unsetProperty( KoPathPoint::StartSubpath );
01016             subpath->first()->setProperty( KoPathPoint::CanHaveControlPoint1 );
01017             // the old end node no longer closes nor ends the subpath
01018             subpath->last()->unsetProperty( KoPathPoint::CloseSubpath );
01019             // now reorder the subpath
01020             for( int i = 0; i <= pointPos.second; ++i )
01021                 subpath->append( subpath->takeFirst() );
01022 
01023             // make the breakpoint an ending node
01024             breakPoint->unsetProperty( KoPathPoint::CanHaveControlPoint2 );
01025             // make the new point a starting node 
01026             newPoint->setProperty( KoPathPoint::StartSubpath );
01027             newPoint->unsetProperty( KoPathPoint::CanHaveControlPoint1 );
01028             subpath->prepend( newPoint );
01029         }
01030     }
01031     else
01032     {
01033         // the subpath is not closed, so breaking at the end nodes has no effect
01034         if( pointPos.second == 0 || pointPos.second == subpath->size()-1 )
01035             return false;
01036 
01037         // now break the subpath into two subpaths
01038         KoSubpath *newSubpath = new KoSubpath;
01039 
01040         // copy the break point and make it a starting node
01041         newPoint = new KoPathPoint( *breakPoint );
01042         newPoint->setProperty( KoPathPoint::StartSubpath );
01043         newPoint->unsetProperty( KoPathPoint::CanHaveControlPoint1 );
01044         newSubpath->append( newPoint );
01045 
01046         // make the break point an end node
01047         breakPoint->unsetProperty( KoPathPoint::CanHaveControlPoint2 );
01048 
01049         int size = subpath->size();
01050         for( int i = pointPos.second+1; i < size; ++i )
01051             newSubpath->append( subpath->takeAt( pointPos.second+1 ) );
01052 
01053         m_subpaths.append( newSubpath );
01054     }
01055 
01056     if( &insertedPoint )
01057         insertedPoint = newPoint;
01058 
01059     return true;
01060 }
01061 
01062 bool KoPathShape::breakAt( const KoPathSegment &segment )
01063 {
01064     if( ! segment.first || ! segment.second )
01065         return false;
01066     if( segment.first == segment.second )
01067         return false;
01068     KoPointPosition pos1 = findPoint( segment.first );
01069     if( ! pos1.first )
01070         return false;
01071     KoPointPosition pos2 = findPoint( segment.second );
01072     if( ! pos2.first )
01073         return false;
01074     if( pos1.first != pos2.first )
01075         return false;
01076 
01077     // get ordered point indexes 
01078     KoSubpath *subpath = pos1.first;
01079     int index1 = pos1.second < pos2.second ? pos1.second : pos2.second;
01080     int index2 = pos1.second > pos2.second ? pos1.second : pos2.second;
01081     KoPathPoint *p1 = (*subpath)[index1];
01082     KoPathPoint *p2 = (*subpath)[index2];
01083 
01084     // check if the path is closed
01085     if( subpath->last()->properties() & KoPathPoint::CloseSubpath )
01086     {
01087         // check if we break at the closing segment
01088         if( index1 == 0 && index2 == subpath->size()-1 )
01089         {
01090             // just unclose the subpath
01091             p2->setProperties( p2->properties() & ~KoPathPoint::CloseSubpath );
01092         }
01093         else
01094         {
01095             // we break in between -> reorder the subpath
01096             // make first point the new endpoint
01097             p1->unsetProperty( KoPathPoint::CanHaveControlPoint2 );
01098             // make second point the new start point
01099             p2->setProperty( KoPathPoint::StartSubpath );
01100             // the last point no longer closes the subpath
01101             subpath->last()->unsetProperty( KoPathPoint::CloseSubpath );
01102             // the first no longer start the subath
01103             subpath->first()->unsetProperty( KoPathPoint::StartSubpath );
01104             // now reorder the subpath
01105             for( int i = 0; i <= index1; ++i )
01106                 subpath->append( subpath->takeFirst() );
01107         }
01108     }
01109     else
01110     {
01111         // subpath is not closed, breaking between first and last has no effect
01112         if( index1 == 0 && index2 == subpath->size()-1 )
01113             return true;
01114 
01115         // break into two subpaths
01116         KoSubpath *newSubpath = new KoSubpath;
01117         int size = subpath->size();
01118         for( int i = index2; i < size; ++i )
01119             newSubpath->append( subpath->takeAt( index2 ) );
01120 
01121         // now make the first point of the new subpath a starting node
01122         newSubpath->first()->setProperty( KoPathPoint::StartSubpath );
01123         newSubpath->first()->unsetProperty( KoPathPoint::CanHaveControlPoint1 );
01124         // the last point of the old subpath is now an ending node 
01125         subpath->last()->unsetProperty( KoPathPoint::CanHaveControlPoint2 );
01126         m_subpaths.append( newSubpath );
01127     }
01128     repaint();
01129     return true;
01130 }
01131 
01132 bool KoPathShape::joinBetween( KoPathPoint *endPoint1, KoPathPoint *endPoint2 )
01133 {
01134     if( endPoint1 == endPoint2 )
01135         return false;
01136 
01137     KoPointPosition pos1 = findPoint( endPoint1 );
01138     if( ! pos1.first )
01139         return false;
01140     // check if first point is end or start node
01141     if( pos1.second != 0 && pos1.second != pos1.first->size()-1 )
01142         return false;
01143     KoPointPosition pos2 = findPoint( endPoint2 );
01144     if( ! pos2.first )
01145         return false;
01146     // check if second point is end or start node
01147     if( pos2.second != 0 && pos2.second != pos2.first->size()-1 )
01148         return false;
01149 
01150     // check if one of the subpaths is already closed
01151     if( pos1.first->last()->properties() & KoPathPoint::CloseSubpath )
01152         return false;
01153     if( pos2.first->last()->properties() & KoPathPoint::CloseSubpath )
01154         return false;
01155 
01156     // check if points are end nodes of same subpath
01157     if( pos1.first == pos2.first )
01158     {
01159         // just close the subpath
01160         closeSubpath( pos1.first );
01161     }
01162     else
01163     {
01164         // merge the two subpaths
01165         if( pos1.second == 0 )
01166             reverseSubpath( *pos1.first );
01167 
01168         // the last point is no longer an end node
01169         pos1.first->last()->setProperty( KoPathPoint::CanHaveControlPoint2 );
01170 
01171         if( pos2.second != 0 )
01172             reverseSubpath( *pos2.first );
01173 
01174         // the first point does not start a subpath anymore
01175         pos2.first->first()->unsetProperty( KoPathPoint::StartSubpath );
01176 
01177         // append the second subpath to the first
01178         foreach( KoPathPoint* p, *pos2.first )
01179             pos1.first->append( p );
01180 
01181         // delete the second subpaths
01182         int index = m_subpaths.indexOf( pos2.first );
01183         m_subpaths.removeAt( index );
01184     }
01185 
01186     return true;
01187 }
01188 
01189 bool KoPathShape::combine( KoPathShape *path )
01190 {
01191     if( ! path )
01192         return false;
01193 
01194     QMatrix pathMatrix = path->transformationMatrix(0);
01195     QMatrix myMatrix = transformationMatrix(0).inverted();
01196 
01197     foreach( KoSubpath* subpath, path->m_subpaths )
01198     {
01199         KoSubpath *newSubpath = new KoSubpath();
01200 
01201         foreach( KoPathPoint* point, *subpath )
01202         {
01203             KoPathPoint *newPoint = new KoPathPoint( *point );
01204             newPoint->map( pathMatrix );
01205             newPoint->map( myMatrix );
01206             newSubpath->append( newPoint );
01207         }
01208         m_subpaths.append( newSubpath );
01209     }
01210     normalize();
01211     return true;
01212 }
01213 
01214 bool KoPathShape::separate( QList<KoPathShape*> & separatedPaths )
01215 {
01216     if( ! m_subpaths.size() )
01217         return false;
01218 
01219     QMatrix myMatrix = transformationMatrix(0);
01220 
01221     foreach( KoSubpath* subpath, m_subpaths )
01222     {
01223         KoPathShape *shape = new KoPathShape();
01224         if( ! shape ) continue;
01225 
01226         shape->setBorder( border() );
01227         shape->setShapeId( shapeId() );
01228 
01229         KoSubpath *newSubpath = new KoSubpath();
01230 
01231         foreach( KoPathPoint* point, *subpath )
01232         {
01233             KoPathPoint *newPoint = new KoPathPoint( *point );
01234             newPoint->map( myMatrix );
01235             newSubpath->append( newPoint );
01236         }
01237         shape->m_subpaths.append( newSubpath );
01238         shape->normalize();
01239         separatedPaths.append( shape );
01240     }
01241     return true;
01242 }
01243 
01244 KoPointPosition KoPathShape::findPoint( KoPathPoint* point )
01245 {
01246     KoSubpathList::iterator pathIt( m_subpaths.begin() );
01247     for ( ; pathIt != m_subpaths.end(); ++pathIt )
01248     {
01249         int index = ( *pathIt )->indexOf( point );
01250         if( index != -1 )
01251             return KoPointPosition( *pathIt, index );
01252     }
01253     return KoPointPosition( 0, 0 );
01254 }
01255 
01256 void KoPathShape::closeSubpath( KoSubpath *subpath )
01257 {
01258     if( ! subpath )
01259         return;
01260 
01261     KoPathPoint * lastPoint = subpath->last();
01262     lastPoint->setProperties( lastPoint->properties() | KoPathPoint::CloseSubpath | KoPathPoint::CanHaveControlPoint2 );
01263     KoPathPoint * firstPoint = subpath->first();
01264     firstPoint->setProperties( firstPoint->properties() | KoPathPoint::CanHaveControlPoint1 );
01265 }
01266 
01267 void KoPathShape::closeMergeSubpath( KoSubpath *subpath )
01268 {
01269     if ( ! subpath )
01270         return;
01271 
01272     KoPathPoint * lastPoint = subpath->last();
01273     KoPathPoint * firstPoint = subpath->first();
01274 
01275     if ( lastPoint->point() == firstPoint->point() )
01276     {
01277         firstPoint->setProperties( firstPoint->properties() | KoPathPoint::CanHaveControlPoint1 );
01278         if ( lastPoint->activeControlPoint1() )
01279             firstPoint->setControlPoint1( lastPoint->controlPoint1() );
01280         removePoint( lastPoint );
01281         // remove point
01282         delete lastPoint;
01283         lastPoint = subpath->last();
01284         lastPoint->setProperties( lastPoint->properties() | KoPathPoint::CanHaveControlPoint2 | KoPathPoint::CloseSubpath );
01285     }
01286     else
01287     {
01288         closeSubpath( subpath );
01289     }
01290 }
01291 
01292 void KoPathShape::reverseSubpath( KoSubpath &subpath )
01293 {
01294     int size = subpath.size();
01295     for( int i = 0; i < size; ++i )
01296     {
01297         KoPathPoint *p = subpath.takeAt( i );
01298         p->reverse();
01299         subpath.prepend( p );
01300     }
01301 
01302     // adjust the position dependent properties
01303     KoPathPoint *first = subpath.first();
01304     KoPathPoint *last = subpath.last();
01305 
01306     KoPathPoint::KoPointProperties firstProps = first->properties();
01307     KoPathPoint::KoPointProperties lastProps = last->properties();
01308 
01309     firstProps |= KoPathPoint::StartSubpath;
01310     lastProps &= ~KoPathPoint::StartSubpath;
01311     if( firstProps & KoPathPoint::CloseSubpath )
01312     {
01313         firstProps &= ~KoPathPoint::CloseSubpath;
01314         lastProps |= KoPathPoint::CloseSubpath;
01315     }
01316     first->setProperties( firstProps );
01317     last->setProperties( lastProps );
01318 }

Généré le Wed Nov 22 23:40:59 2006 pour KPlato par  doxygen 1.5.1-p1