00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
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
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
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
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
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
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
00458 path.moveTo( ( *it )->point() );
00459 }
00460 }
00461 else if ( activeCP || ( *it )->activeControlPoint1() )
00462 {
00463
00464
00465
00466
00467 path.cubicTo( activeCP ? lastPoint->controlPoint2() : lastPoint->point()
00468 , ( *it )->activeControlPoint1() ? ( *it )->controlPoint1() : ( *it )->point()
00469 , ( *it )->point() );
00470 }
00471 else
00472 {
00473
00474 path.lineTo( ( *it )->point() );
00475 }
00476 if ( ( *it )->properties() & KoPathPoint::CloseSubpath )
00477 {
00478
00479 KoPathPoint * firstPoint = ( *pathIt )->first();
00480 if ( ( *it )->activeControlPoint2() || firstPoint->activeControlPoint1() )
00481 {
00482
00483
00484
00485 path.cubicTo( (*it)->activeControlPoint2() ? ( *it )->controlPoint2() : ( *it )->point()
00486 , firstPoint->activeControlPoint1() ? firstPoint->controlPoint1() : firstPoint->point()
00487 , firstPoint->point() );
00488 }
00489
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
00517 return transformationMatrix( 0 ).mapRect( bb );
00518 }
00519
00520
00521 QSizeF KoPathShape::size() const
00522 {
00523
00524 return outline().boundingRect().size();
00525 }
00526
00527 QPointF KoPathShape::position() const
00528 {
00529
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
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
00622 }
00623
00624
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
00636
00637 QPointF startpoint( offset );
00638
00639
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
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
00653 QPointF endpoint( center + QPointF( cosse * rx, -sinse * ry ) );
00654
00655 curvePoints[pointCnt++] = QPointF( endpoint - QPointF( -sinse * rx * kappa, -cosse * ry * kappa ) );
00656 curvePoints[pointCnt++] = endpoint;
00657
00658
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
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
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
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
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
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
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
00920 if( ! subPath )
00921 return 0;
00922
00923 KoPathPoint *splitPoint = 0;
00924
00925
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
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
00943 p[j - 1] = q[ 0 ];
00944 }
00945 splitPoint = new KoPathPoint( this, p[2], KoPathPoint::CanHaveControlPoint1|KoPathPoint::CanHaveControlPoint2 );
00946
00947 segment.first->setControlPoint2( p[0] );
00948
00949 splitPoint->setControlPoint1( p[1] );
00950 splitPoint->setControlPoint2( q[1] );
00951
00952
00953 segment.second->setControlPoint1( q[2] );
00954 }
00955 else
00956 {
00957 QPointF splitPointPos = segment.first->point() + t * (segment.second->point() - segment.first->point());
00958
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
00983 if( subpath->last()->properties() & KoPathPoint::CloseSubpath )
00984 {
00985
00986 if( pointPos.second == 0 )
00987 {
00988
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
00996 else if( pointPos.second == subpath->size()-1 )
00997 {
00998
00999 newPoint = new KoPathPoint( *subpath->last() );
01000
01001 subpath->last()->unsetProperty( KoPathPoint::CloseSubpath );
01002
01003 subpath->first()->unsetProperty( KoPathPoint::StartSubpath );
01004
01005 newPoint->setProperty( KoPathPoint::StartSubpath );
01006 newPoint->unsetProperty( KoPathPoint::CanHaveControlPoint1 );
01007 subpath->prepend( newPoint );
01008 }
01009
01010 else
01011 {
01012
01013 newPoint = new KoPathPoint( *breakPoint );
01014
01015 subpath->first()->unsetProperty( KoPathPoint::StartSubpath );
01016 subpath->first()->setProperty( KoPathPoint::CanHaveControlPoint1 );
01017
01018 subpath->last()->unsetProperty( KoPathPoint::CloseSubpath );
01019
01020 for( int i = 0; i <= pointPos.second; ++i )
01021 subpath->append( subpath->takeFirst() );
01022
01023
01024 breakPoint->unsetProperty( KoPathPoint::CanHaveControlPoint2 );
01025
01026 newPoint->setProperty( KoPathPoint::StartSubpath );
01027 newPoint->unsetProperty( KoPathPoint::CanHaveControlPoint1 );
01028 subpath->prepend( newPoint );
01029 }
01030 }
01031 else
01032 {
01033
01034 if( pointPos.second == 0 || pointPos.second == subpath->size()-1 )
01035 return false;
01036
01037
01038 KoSubpath *newSubpath = new KoSubpath;
01039
01040
01041 newPoint = new KoPathPoint( *breakPoint );
01042 newPoint->setProperty( KoPathPoint::StartSubpath );
01043 newPoint->unsetProperty( KoPathPoint::CanHaveControlPoint1 );
01044 newSubpath->append( newPoint );
01045
01046
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
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
01085 if( subpath->last()->properties() & KoPathPoint::CloseSubpath )
01086 {
01087
01088 if( index1 == 0 && index2 == subpath->size()-1 )
01089 {
01090
01091 p2->setProperties( p2->properties() & ~KoPathPoint::CloseSubpath );
01092 }
01093 else
01094 {
01095
01096
01097 p1->unsetProperty( KoPathPoint::CanHaveControlPoint2 );
01098
01099 p2->setProperty( KoPathPoint::StartSubpath );
01100
01101 subpath->last()->unsetProperty( KoPathPoint::CloseSubpath );
01102
01103 subpath->first()->unsetProperty( KoPathPoint::StartSubpath );
01104
01105 for( int i = 0; i <= index1; ++i )
01106 subpath->append( subpath->takeFirst() );
01107 }
01108 }
01109 else
01110 {
01111
01112 if( index1 == 0 && index2 == subpath->size()-1 )
01113 return true;
01114
01115
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
01122 newSubpath->first()->setProperty( KoPathPoint::StartSubpath );
01123 newSubpath->first()->unsetProperty( KoPathPoint::CanHaveControlPoint1 );
01124
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
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
01147 if( pos2.second != 0 && pos2.second != pos2.first->size()-1 )
01148 return false;
01149
01150
01151 if( pos1.first->last()->properties() & KoPathPoint::CloseSubpath )
01152 return false;
01153 if( pos2.first->last()->properties() & KoPathPoint::CloseSubpath )
01154 return false;
01155
01156
01157 if( pos1.first == pos2.first )
01158 {
01159
01160 closeSubpath( pos1.first );
01161 }
01162 else
01163 {
01164
01165 if( pos1.second == 0 )
01166 reverseSubpath( *pos1.first );
01167
01168
01169 pos1.first->last()->setProperty( KoPathPoint::CanHaveControlPoint2 );
01170
01171 if( pos2.second != 0 )
01172 reverseSubpath( *pos2.first );
01173
01174
01175 pos2.first->first()->unsetProperty( KoPathPoint::StartSubpath );
01176
01177
01178 foreach( KoPathPoint* p, *pos2.first )
01179 pos1.first->append( p );
01180
01181
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
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
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 }