00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "KoPathShape.h"
00023 #include "KoPathShape_p.h"
00024 #include "KoPathPoint.h"
00025 #include "KoPointGroup.h"
00026 #include "KoShapeBorderModel.h"
00027 #include "KoViewConverter.h"
00028 #include "KoPathShapeLoader.h"
00029 #include "KoShapeSavingContext.h"
00030 #include "KoShapeLoadingContext.h"
00031 #include "KoShapeShadow.h"
00032 #include "KoShapeBackground.h"
00033 #include "KoShapeContainer.h"
00034 #include "KoFilterEffectStack.h"
00035
00036 #include <KoXmlReader.h>
00037 #include <KoXmlWriter.h>
00038 #include <KoXmlNS.h>
00039 #include <KoUnit.h>
00040 #include <KoGenStyle.h>
00041 #include <KoStyleStack.h>
00042 #include <KoOdfLoadingContext.h>
00043
00044 #include <KDebug>
00045 #include <QtGui/QPainter>
00046
00047 KoPathShapePrivate::KoPathShapePrivate(KoPathShape *q)
00048 : KoShapePrivate(q),
00049 fillRule(Qt::OddEvenFill)
00050 {
00051 }
00052
00053 KoPathShape::KoPathShape()
00054 :KoShape(*(new KoPathShapePrivate(this)))
00055 {
00056 }
00057
00058 KoPathShape::KoPathShape(KoPathShapePrivate &dd)
00059 : KoShape(dd)
00060 {
00061 }
00062
00063 KoPathShape::~KoPathShape()
00064 {
00065 clear();
00066 }
00067
00068 void KoPathShape::saveOdf(KoShapeSavingContext & context) const
00069 {
00070 context.xmlWriter().startElement("draw:path");
00071 saveOdfAttributes(context, OdfAllAttributes | OdfViewbox);
00072
00073 context.xmlWriter().addAttribute("svg:d", toString(QMatrix()));
00074 context.xmlWriter().addAttribute("koffice:nodeTypes", nodeTypes());
00075
00076 saveOdfCommonChildElements(context);
00077 context.xmlWriter().endElement();
00078 }
00079
00080 bool KoPathShape::loadOdf(const KoXmlElement & element, KoShapeLoadingContext &context)
00081 {
00082 loadOdfAttributes(element, context, OdfMandatories | OdfAdditionalAttributes | OdfCommonChildElements);
00083
00084
00085 clear();
00086
00087 if (element.localName() == "line") {
00088 QPointF start;
00089 start.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x1", "")));
00090 start.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y1", "")));
00091 QPointF end;
00092 end.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x2", "")));
00093 end.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y2", "")));
00094 moveTo(start);
00095 lineTo(end);
00096 } else if (element.localName() == "polyline" || element.localName() == "polygon") {
00097 QString points = element.attributeNS(KoXmlNS::draw, "points").simplified();
00098 points.replace(',', ' ');
00099 points.remove('\r');
00100 points.remove('\n');
00101 bool firstPoint = true;
00102 const QStringList coordinateList = points.split(' ');
00103 for (QStringList::ConstIterator it = coordinateList.constBegin(); it != coordinateList.constEnd(); ++it) {
00104 QPointF point;
00105 point.setX((*it).toDouble());
00106 ++it;
00107 point.setY((*it).toDouble());
00108 if (firstPoint) {
00109 moveTo(point);
00110 firstPoint = false;
00111 } else
00112 lineTo(point);
00113 }
00114 if (element.localName() == "polygon")
00115 close();
00116 } else {
00117 KoPathShapeLoader loader(this);
00118 loader.parseSvg(element.attributeNS(KoXmlNS::svg, "d"), true);
00119 loadNodeTypes(element);
00120 }
00121
00122 applyViewboxTransformation(element);
00123 QPointF pos = normalize();
00124 setTransformation(QMatrix());
00125
00126 if (element.hasAttributeNS(KoXmlNS::svg, "x") || element.hasAttributeNS(KoXmlNS::svg, "y")) {
00127 pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString())));
00128 pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString())));
00129 }
00130
00131 setPosition(pos);
00132
00133 loadOdfAttributes(element, context, OdfTransformation);
00134
00135 return true;
00136 }
00137
00138 QString KoPathShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const
00139 {
00140 Q_D(const KoPathShape);
00141 style.addProperty("svg:fill-rule", d->fillRule == Qt::OddEvenFill ? "evenodd" : "nonzero");
00142
00143 return KoShape::saveStyle(style, context);
00144 }
00145
00146 void KoPathShape::loadStyle(const KoXmlElement & element, KoShapeLoadingContext &context)
00147 {
00148 Q_D(KoPathShape);
00149 KoShape::loadStyle(element, context);
00150
00151 KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
00152 styleStack.save();
00153
00154
00155 if (element.hasAttributeNS(KoXmlNS::draw, "style-name")) {
00156 context.odfLoadingContext().fillStyleStack(element, KoXmlNS::draw, "style-name", "graphic");
00157 } else if (element.hasAttributeNS(KoXmlNS::presentation, "style-name")) {
00158 context.odfLoadingContext().fillStyleStack(element, KoXmlNS::presentation, "style-name", "presentation");
00159 }
00160 styleStack.setTypeProperties("graphic");
00161
00162 if (styleStack.hasProperty(KoXmlNS::svg, "fill-rule")) {
00163 QString rule = styleStack.property(KoXmlNS::svg, "fill-rule");
00164 d->fillRule = rule == "nonzero" ? Qt::WindingFill : Qt::OddEvenFill;
00165 }
00166 styleStack.restore();
00167 }
00168
00169 QRectF KoPathShape::loadOdfViewbox(const KoXmlElement & element) const
00170 {
00171 QRectF viewbox;
00172
00173 QString data = element.attributeNS(KoXmlNS::svg, "viewBox");
00174 if (! data.isEmpty()) {
00175 data.replace(',', ' ');
00176 QStringList coordinates = data.simplified().split(' ', QString::SkipEmptyParts);
00177 if (coordinates.count() == 4) {
00178 viewbox.setRect(coordinates[0].toDouble(), coordinates[1].toDouble(),
00179 coordinates[2].toDouble(), coordinates[3].toDouble());
00180 }
00181 }
00182
00183 return viewbox;
00184 }
00185
00186 void KoPathShape::applyViewboxTransformation(const KoXmlElement & element)
00187 {
00188
00189 QRectF viewBox = loadOdfViewbox(element);
00190 if (! viewBox.isEmpty()) {
00191
00192 QSizeF size;
00193 size.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString())));
00194 size.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString())));
00195
00196
00197 QPointF pos;
00198 pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString())));
00199 pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString())));
00200
00201
00202 QMatrix viewMatrix;
00203 viewMatrix.translate(-viewBox.left(), -viewBox.top());
00204 viewMatrix.scale(size.width() / viewBox.width(), size.height() / viewBox.height());
00205 viewMatrix.translate(pos.x(), pos.y());
00206
00207
00208 map(viewMatrix);
00209 }
00210 }
00211
00212 void KoPathShape::clear()
00213 {
00214 foreach(KoSubpath *subpath, m_subpaths) {
00215 foreach(KoPathPoint *point, *subpath)
00216 delete point;
00217 delete subpath;
00218 }
00219 m_subpaths.clear();
00220 }
00221
00222 void KoPathShape::paint(QPainter &painter, const KoViewConverter &converter)
00223 {
00224 Q_D(KoPathShape);
00225 applyConversion(painter, converter);
00226 QPainterPath path(outline());
00227 path.setFillRule(d->fillRule);
00228
00229 if (background())
00230 background()->paint(painter, path);
00231
00232 }
00233
00234 #ifndef NDEBUG
00235 void KoPathShape::paintDebug(QPainter &painter)
00236 {
00237 KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
00238 int i = 0;
00239
00240 QPen pen(Qt::black);
00241 painter.save();
00242 painter.setPen(pen);
00243 for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
00244 KoSubpath::const_iterator it((*pathIt)->constBegin());
00245 for (; it != (*pathIt)->constEnd(); ++it) {
00246 ++i;
00247 KoPathPoint *point = (*it);
00248 QRectF r(point->point(), QSizeF(5, 5));
00249 r.translate(-2.5, -2.5);
00250 QPen pen(Qt::black);
00251 painter.setPen(pen);
00252 if (point->group()) {
00253 QBrush b(Qt::blue);
00254 painter.setBrush(b);
00255 } else if (point->activeControlPoint1() && point->activeControlPoint2()) {
00256 QBrush b(Qt::red);
00257 painter.setBrush(b);
00258 } else if (point->activeControlPoint1()) {
00259 QBrush b(Qt::yellow);
00260 painter.setBrush(b);
00261 } else if (point->activeControlPoint2()) {
00262 QBrush b(Qt::darkYellow);
00263 painter.setBrush(b);
00264 }
00265 painter.drawEllipse(r);
00266 }
00267 }
00268 painter.restore();
00269 kDebug(30006) << "nop =" << i;
00270 }
00271
00272 void KoPathShape::debugPath()
00273 {
00274 KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
00275 for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
00276 KoSubpath::const_iterator it((*pathIt)->constBegin());
00277 for (; it != (*pathIt)->constEnd(); ++it) {
00278 kDebug(30006) << "p:" << (*pathIt) << "," << *it << "," << (*it)->point() << "," << (*it)->properties() << "," << (*it)->group();
00279 }
00280 }
00281 }
00282 #endif
00283
00284 void KoPathShape::paintPoints(QPainter &painter, const KoViewConverter &converter, int handleRadius)
00285 {
00286 applyConversion(painter, converter);
00287
00288 KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
00289
00290 QRectF handle = converter.viewToDocument(handleRect(QPoint(0, 0), handleRadius));
00291
00292 for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
00293 KoSubpath::const_iterator it((*pathIt)->constBegin());
00294 for (; it != (*pathIt)->constEnd(); ++it)
00295 (*it)->paint(painter, handleRadius, KoPathPoint::Node);
00296 }
00297 }
00298
00299 QRectF KoPathShape::handleRect(const QPointF &p, qreal radius) const
00300 {
00301 return QRectF(p.x() - radius, p.y() - radius, 2*radius, 2*radius);
00302 }
00303
00304 QPainterPath KoPathShape::outline() const
00305 {
00306 QPainterPath path;
00307 foreach(KoSubpath * subpath, m_subpaths) {
00308 KoPathPoint * lastPoint = subpath->first();
00309 bool activeCP = false;
00310 foreach(KoPathPoint * currPoint, *subpath) {
00311 KoPathPoint::KoPointProperties currProperties = currPoint->properties();
00312 if (currPoint == subpath->first()) {
00313 if (currProperties & KoPathPoint::StartSubpath) {
00314 path.moveTo(currPoint->point());
00315 }
00316 } else if (activeCP && currPoint->activeControlPoint1()) {
00317 path.cubicTo(
00318 lastPoint->controlPoint2(),
00319 currPoint->controlPoint1(),
00320 currPoint->point());
00321 } else if( activeCP || currPoint->activeControlPoint1()) {
00322 path.quadTo(
00323 activeCP ? lastPoint->controlPoint2() : currPoint->controlPoint1(),
00324 currPoint->point());
00325 } else {
00326 path.lineTo(currPoint->point());
00327 }
00328 if (currProperties & KoPathPoint::CloseSubpath && currProperties & KoPathPoint::StopSubpath) {
00329
00330 KoPathPoint * firstPoint = subpath->first();
00331 if (currPoint->activeControlPoint2() && firstPoint->activeControlPoint1()) {
00332 path.cubicTo(
00333 currPoint->controlPoint2(),
00334 firstPoint->controlPoint1(),
00335 firstPoint->point());
00336 }
00337 else if(currPoint->activeControlPoint2() || firstPoint->activeControlPoint1()) {
00338 path.quadTo(
00339 currPoint->activeControlPoint2() ? currPoint->controlPoint2() : firstPoint->controlPoint1(),
00340 firstPoint->point());
00341 }
00342 path.closeSubpath();
00343 }
00344
00345 if (currPoint->activeControlPoint2()) {
00346 activeCP = true;
00347 } else {
00348 activeCP = false;
00349 }
00350 lastPoint = currPoint;
00351 }
00352 }
00353 return path;
00354 }
00355
00356 QRectF KoPathShape::boundingRect() const
00357 {
00358 QMatrix transform = absoluteTransformation(0);
00359
00360 QRectF bb(transform.map(outline()).boundingRect());
00361 if (border()) {
00362 KoInsets inset;
00363 border()->borderInsets(this, inset);
00364
00365
00366 QPointF center = transform.map(QPointF());
00367 QPointF tl = transform.map(QPointF(-inset.left,-inset.top)) - center;
00368 QPointF br = transform.map(QPointF(inset.right,inset.bottom)) -center;
00369 qreal left = qMin(tl.x(),br.x());
00370 qreal right = qMax(tl.x(),br.x());
00371 qreal top = qMin(tl.y(),br.y());
00372 qreal bottom = qMax(tl.y(),br.y());
00373 bb.adjust(left, top, right, bottom);
00374 }
00375 if (shadow()) {
00376 KoInsets insets;
00377 shadow()->insets(this, insets);
00378 bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
00379 }
00380 if (filterEffectStack()) {
00381 QRectF clipRect = filterEffectStack()->clipRectForBoundingRect(QRectF(QPointF(), size()));
00382 bb |= transform.mapRect(clipRect);
00383 }
00384 return bb;
00385 }
00386
00387 QSizeF KoPathShape::size() const
00388 {
00389
00390
00391 return outline().boundingRect().size();
00392 }
00393
00394 void KoPathShape::setSize(const QSizeF &newSize)
00395 {
00396 QMatrix matrix(resizeMatrix(newSize));
00397
00398 KoShape::setSize(newSize);
00399 map(matrix);
00400 }
00401
00402 QMatrix KoPathShape::resizeMatrix( const QSizeF & newSize ) const
00403 {
00404 QSizeF oldSize = size();
00405 if (oldSize.width() == 0.0) {
00406 oldSize.setWidth(0.000001);
00407 }
00408 if (oldSize.height() == 0.0) {
00409 oldSize.setHeight(0.000001);
00410 }
00411
00412 QSizeF sizeNew(newSize);
00413 if (sizeNew.width() == 0.0) {
00414 sizeNew.setWidth(0.000001);
00415 }
00416 if (sizeNew.height() == 0.0) {
00417 sizeNew.setHeight(0.000001);
00418 }
00419
00420 return QMatrix(sizeNew.width() / oldSize.width(), 0, 0, sizeNew.height() / oldSize.height(), 0, 0);
00421 }
00422
00423 KoPathPoint * KoPathShape::moveTo(const QPointF &p)
00424 {
00425 KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StartSubpath | KoPathPoint::StopSubpath);
00426 KoSubpath * path = new KoSubpath;
00427 path->push_back(point);
00428 m_subpaths.push_back(path);
00429 return point;
00430 }
00431
00432 KoPathPoint * KoPathShape::lineTo(const QPointF &p)
00433 {
00434 if (m_subpaths.empty()) {
00435 moveTo(QPointF(0, 0));
00436 }
00437 KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath);
00438 KoPathPoint * lastPoint = m_subpaths.last()->last();
00439 updateLast(&lastPoint);
00440 m_subpaths.last()->push_back(point);
00441 return point;
00442 }
00443
00444 KoPathPoint * KoPathShape::curveTo(const QPointF &c1, const QPointF &c2, const QPointF &p)
00445 {
00446 if (m_subpaths.empty()) {
00447 moveTo(QPointF(0, 0));
00448 }
00449 KoPathPoint * lastPoint = m_subpaths.last()->last();
00450 updateLast(&lastPoint);
00451 lastPoint->setControlPoint2(c1);
00452 KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath);
00453 point->setControlPoint1(c2);
00454 m_subpaths.last()->push_back(point);
00455 return point;
00456 }
00457
00458 KoPathPoint * KoPathShape::curveTo(const QPointF &c, const QPointF &p)
00459 {
00460 if (m_subpaths.empty())
00461 moveTo(QPointF(0, 0));
00462
00463 KoPathPoint * lastPoint = m_subpaths.last()->last();
00464 updateLast(&lastPoint);
00465 lastPoint->setControlPoint2(c);
00466 KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath);
00467 m_subpaths.last()->push_back(point);
00468
00469 return point;
00470 }
00471
00472 KoPathPoint * KoPathShape::arcTo(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle)
00473 {
00474 if (m_subpaths.empty()) {
00475 moveTo(QPointF(0, 0));
00476 }
00477
00478 KoPathPoint * lastPoint = m_subpaths.last()->last();
00479 if (lastPoint->properties() & KoPathPoint::CloseSubpath) {
00480 lastPoint = m_subpaths.last()->first();
00481 }
00482 QPointF startpoint(lastPoint->point());
00483
00484 KoPathPoint * newEndPoint = lastPoint;
00485
00486 QPointF curvePoints[12];
00487 int pointCnt = arcToCurve(rx, ry, startAngle, sweepAngle, startpoint, curvePoints);
00488 for (int i = 0; i < pointCnt; i += 3) {
00489 newEndPoint = curveTo(curvePoints[i], curvePoints[i+1], curvePoints[i+2]);
00490 }
00491 return newEndPoint;
00492 }
00493
00494 int KoPathShape::arcToCurve(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle, const QPointF & offset, QPointF * curvePoints) const
00495 {
00496 int pointCnt = 0;
00497
00498
00499 if (sweepAngle == 0)
00500 return pointCnt;
00501 if (sweepAngle > 360)
00502 sweepAngle = 360;
00503 else if (sweepAngle < -360)
00504 sweepAngle = - 360;
00505
00506 if (rx == 0 || ry == 0) {
00507
00508 }
00509
00510
00511 qreal parts = ceil(qAbs(sweepAngle / 90.0));
00512
00513 qreal sa_rad = startAngle * M_PI / 180.0;
00514 qreal partangle = sweepAngle / parts;
00515 qreal endangle = startAngle + partangle;
00516 qreal se_rad = endangle * M_PI / 180.0;
00517 qreal sinsa = sin(sa_rad);
00518 qreal cossa = cos(sa_rad);
00519 qreal kappa = 4.0 / 3.0 * tan((se_rad - sa_rad) / 4);
00520
00521
00522
00523 QPointF startpoint(offset);
00524
00525
00526 QPointF center(startpoint - QPointF(cossa * rx, -sinsa * ry));
00527
00528
00529
00530 for (int part = 0; part < parts; ++part) {
00531
00532 curvePoints[pointCnt++] = QPointF(startpoint - QPointF(sinsa * rx * kappa, cossa * ry * kappa));
00533
00534 qreal sinse = sin(se_rad);
00535 qreal cosse = cos(se_rad);
00536
00537
00538 QPointF endpoint(center + QPointF(cosse * rx, -sinse * ry));
00539
00540 curvePoints[pointCnt++] = QPointF(endpoint - QPointF(-sinse * rx * kappa, -cosse * ry * kappa));
00541 curvePoints[pointCnt++] = endpoint;
00542
00543
00544 startpoint = endpoint;
00545 sinsa = sinse;
00546 cossa = cosse;
00547 endangle += partangle;
00548 se_rad = endangle * M_PI / 180.0;
00549 }
00550
00551 return pointCnt;
00552 }
00553
00554 void KoPathShape::close()
00555 {
00556 if (m_subpaths.empty()) {
00557 return;
00558 }
00559 closeSubpath(m_subpaths.last());
00560 }
00561
00562 void KoPathShape::closeMerge()
00563 {
00564 if (m_subpaths.empty()) {
00565 return;
00566 }
00567 closeMergeSubpath(m_subpaths.last());
00568 }
00569
00570 QPointF KoPathShape::normalize()
00571 {
00572 QPointF tl(outline().boundingRect().topLeft());
00573 QMatrix matrix;
00574 matrix.translate(-tl.x(), -tl.y());
00575 map(matrix);
00576
00577
00578 applyTransformation(matrix.inverted());
00579
00580 return tl;
00581 }
00582
00583 void KoPathShape::map(const QMatrix &matrix)
00584 {
00585 KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
00586 for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
00587 KoSubpath::const_iterator it((*pathIt)->constBegin());
00588 for (; it != (*pathIt)->constEnd(); ++it) {
00589 (*it)->map(matrix);
00590 }
00591 }
00592 }
00593
00594 void KoPathShape::updateLast(KoPathPoint ** lastPoint)
00595 {
00596
00597 if ((*lastPoint)->properties() & KoPathPoint::StopSubpath
00598 && (*lastPoint)->properties() & KoPathPoint::CloseSubpath) {
00599
00600 KoPathPoint * subpathStart = m_subpaths.last()->first();
00601
00602 KoPathPoint * newLastPoint = new KoPathPoint(*subpathStart);
00603
00604 newLastPoint->setProperties(KoPathPoint::Normal);
00605
00606 KoPointGroup * group = subpathStart->group();
00607 if (group == 0) {
00608 group = new KoPointGroup();
00609 group->add(subpathStart);
00610 }
00611 group->add(newLastPoint);
00612
00613
00614 KoSubpath *path = new KoSubpath;
00615 path->push_back(newLastPoint);
00616 m_subpaths.push_back(path);
00617 *lastPoint = newLastPoint;
00618 } else {
00619
00620
00621 (*lastPoint)->unsetProperty(KoPathPoint::StopSubpath);
00622 }
00623 (*lastPoint)->unsetProperty(KoPathPoint::CloseSubpath);
00624 }
00625
00626 QList<KoPathPoint*> KoPathShape::pointsAt(const QRectF &r)
00627 {
00628 QList<KoPathPoint*> result;
00629
00630 KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
00631 for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
00632 KoSubpath::const_iterator it((*pathIt)->constBegin());
00633 for (; it != (*pathIt)->constEnd(); ++it) {
00634 if (r.contains((*it)->point()))
00635 result.append(*it);
00636 else if ((*it)->activeControlPoint1() && r.contains((*it)->controlPoint1()))
00637 result.append(*it);
00638 else if ((*it)->activeControlPoint2() && r.contains((*it)->controlPoint2()))
00639 result.append(*it);
00640 }
00641 }
00642 return result;
00643 }
00644
00645 QList<KoPathSegment> KoPathShape::segmentsAt(const QRectF &r)
00646 {
00647 QList<KoPathSegment> segments;
00648 int subpathCount = m_subpaths.count();
00649 for (int subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) {
00650 KoSubpath * subpath = m_subpaths[subpathIndex];
00651 int pointCount = subpath->count();
00652 bool subpathClosed = isClosedSubpath(subpathIndex);
00653 for (int pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
00654 if (pointIndex == (pointCount - 1) && ! subpathClosed)
00655 break;
00656 KoPathSegment s(subpath->at(pointIndex), subpath->at((pointIndex + 1) % pointCount));
00657 QRectF controlRect = s.controlPointRect();
00658 if (! r.intersects(controlRect) && ! controlRect.contains(r))
00659 continue;
00660 QRectF bound = s.boundingRect();
00661 if (! r.intersects(bound) && ! bound.contains(r))
00662 continue;
00663
00664 segments.append(s);
00665 }
00666 }
00667 return segments;
00668 }
00669
00670 KoPathPointIndex KoPathShape::pathPointIndex(const KoPathPoint *point) const
00671 {
00672 for (int subpathIndex = 0; subpathIndex < m_subpaths.size(); ++subpathIndex) {
00673 KoSubpath * subpath = m_subpaths.at(subpathIndex);
00674 for (int pointPos = 0; pointPos < subpath->size(); ++pointPos) {
00675 if (subpath->at(pointPos) == point) {
00676 return KoPathPointIndex(subpathIndex, pointPos);
00677 }
00678 }
00679 }
00680 return KoPathPointIndex(-1, -1);
00681 }
00682
00683 KoPathPoint * KoPathShape::pointByIndex(const KoPathPointIndex &pointIndex) const
00684 {
00685 KoSubpath * subpath = subPath(pointIndex.first);
00686
00687 if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size())
00688 return 0;
00689
00690 return subpath->at(pointIndex.second);
00691 }
00692
00693 KoPathSegment KoPathShape::segmentByIndex(const KoPathPointIndex &pointIndex) const
00694 {
00695 KoPathSegment segment(0, 0);
00696
00697 KoSubpath * subpath = subPath(pointIndex.first);
00698
00699 if (subpath != 0 && pointIndex.second >= 0 && pointIndex.second < subpath->size()) {
00700 KoPathPoint * point = subpath->at(pointIndex.second);
00701 int index = pointIndex.second;
00702
00703 if ((index == subpath->size() - 1) && point->properties() & KoPathPoint::CloseSubpath)
00704 index = 0;
00705 else
00706 ++index;
00707
00708 if (index < subpath->size()) {
00709 segment = KoPathSegment(point, subpath->at(index));
00710 }
00711 }
00712 return segment;
00713 }
00714
00715 int KoPathShape::pointCount() const
00716 {
00717 int i = 0;
00718 KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
00719 for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
00720 i += (*pathIt)->size();
00721 }
00722
00723 return i;
00724 }
00725
00726 int KoPathShape::subpathCount() const
00727 {
00728 return m_subpaths.count();
00729 }
00730
00731 int KoPathShape::pointCountSubpath(int subpathIndex) const
00732 {
00733 KoSubpath * subpath = subPath(subpathIndex);
00734
00735 if (subpath == 0)
00736 return -1;
00737
00738 return subpath->size();
00739 }
00740
00741 bool KoPathShape::isClosedSubpath(int subpathIndex)
00742 {
00743 KoSubpath * subpath = subPath(subpathIndex);
00744
00745 if (subpath == 0)
00746 return false;
00747
00748 const bool firstClosed = subpath->first()->properties() & KoPathPoint::CloseSubpath;
00749 const bool lastClosed = subpath->last()->properties() & KoPathPoint::CloseSubpath;
00750
00751 return firstClosed && lastClosed;
00752 }
00753
00754 bool KoPathShape::insertPoint(KoPathPoint* point, const KoPathPointIndex &pointIndex)
00755 {
00756 KoSubpath * subpath = subPath(pointIndex.first);
00757
00758 if (subpath == 0 || pointIndex.second < 0 || pointIndex.second > subpath->size())
00759 return false;
00760
00761 KoPathPoint::KoPointProperties properties = point->properties();
00762 properties &= ~KoPathPoint::StartSubpath;
00763 properties &= ~KoPathPoint::StopSubpath;
00764 properties &= ~KoPathPoint::CloseSubpath;
00765
00766 if (pointIndex.second == 0) {
00767 properties |= KoPathPoint::StartSubpath;
00768
00769 if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
00770
00771 properties |= KoPathPoint::CloseSubpath;
00772 }
00773
00774 subpath->first()->unsetProperty(KoPathPoint::StartSubpath);
00775 }
00776
00777 else if (pointIndex.second == subpath->size()) {
00778 properties |= KoPathPoint::StopSubpath;
00779
00780 if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
00781
00782 properties = properties | KoPathPoint::CloseSubpath;
00783 }
00784
00785 subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
00786 }
00787
00788 point->setProperties(properties);
00789 point->setParent(this);
00790 subpath->insert(pointIndex.second , point);
00791 return true;
00792 }
00793
00794 KoPathPoint * KoPathShape::removePoint(const KoPathPointIndex &pointIndex)
00795 {
00796 KoSubpath * subpath = subPath(pointIndex.first);
00797
00798 if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size())
00799 return 0;
00800
00801 KoPathPoint * point = subpath->takeAt(pointIndex.second);
00802
00803
00804 if (pointIndex.second == 0) {
00805
00806 subpath->first()->setProperty(KoPathPoint::StartSubpath);
00807
00808 if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
00809
00810 subpath->first()->setProperty(KoPathPoint::CloseSubpath);
00811 }
00812 }
00813
00814 else if (pointIndex.second == subpath->size()) {
00815
00816 subpath->last()->setProperty(KoPathPoint::StopSubpath);
00817
00818 if (point->properties() & KoPathPoint::CloseSubpath) {
00819
00820 subpath->last()->setProperty(KoPathPoint::CloseSubpath);
00821 }
00822 }
00823
00824 return point;
00825 }
00826
00827 bool KoPathShape::breakAfter(const KoPathPointIndex &pointIndex)
00828 {
00829 KoSubpath * subpath = subPath(pointIndex.first);
00830
00831 if (!subpath || pointIndex.second < 0 || pointIndex.second > subpath->size() - 2
00832 || isClosedSubpath(pointIndex.first))
00833 return false;
00834
00835 KoSubpath * newSubpath = new KoSubpath;
00836
00837 int size = subpath->size();
00838 for (int i = pointIndex.second + 1; i < size; ++i) {
00839 newSubpath->append(subpath->takeAt(pointIndex.second + 1));
00840 }
00841
00842 newSubpath->first()->setProperty(KoPathPoint::StartSubpath);
00843
00844 subpath->last()->setProperty(KoPathPoint::StopSubpath);
00845
00846
00847 m_subpaths.insert(pointIndex.first + 1, newSubpath);
00848
00849 return true;
00850 }
00851
00852 bool KoPathShape::join(int subpathIndex)
00853 {
00854 KoSubpath * subpath = subPath(subpathIndex);
00855 KoSubpath * nextSubpath = subPath(subpathIndex + 1);
00856
00857 if (!subpath || !nextSubpath || isClosedSubpath(subpathIndex)
00858 || isClosedSubpath(subpathIndex+1))
00859 return false;
00860
00861
00862 subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
00863
00864 nextSubpath->first()->unsetProperty(KoPathPoint::StartSubpath);
00865
00866
00867 foreach(KoPathPoint * p, *nextSubpath)
00868 subpath->append(p);
00869
00870
00871 m_subpaths.removeAt(subpathIndex + 1);
00872
00873
00874 delete nextSubpath;
00875
00876 return true;
00877 }
00878
00879 bool KoPathShape::moveSubpath(int oldSubpathIndex, int newSubpathIndex)
00880 {
00881 KoSubpath * subpath = subPath(oldSubpathIndex);
00882
00883 if (subpath == 0 || newSubpathIndex >= m_subpaths.size())
00884 return false;
00885
00886 if (oldSubpathIndex == newSubpathIndex)
00887 return true;
00888
00889 m_subpaths.removeAt(oldSubpathIndex);
00890 m_subpaths.insert(newSubpathIndex, subpath);
00891
00892 return true;
00893 }
00894
00895 KoPathPointIndex KoPathShape::openSubpath(const KoPathPointIndex &pointIndex)
00896 {
00897 KoSubpath * subpath = subPath(pointIndex.first);
00898
00899 if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size()
00900 || !isClosedSubpath(pointIndex.first))
00901 return KoPathPointIndex(-1, -1);
00902
00903 KoPathPoint * oldStartPoint = subpath->first();
00904
00905 oldStartPoint->unsetProperty(KoPathPoint::StartSubpath);
00906
00907 subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
00908
00909
00910 for (int i = 0; i < pointIndex.second; ++i) {
00911 subpath->append(subpath->takeFirst());
00912 }
00913
00914 subpath->first()->setProperty(KoPathPoint::StartSubpath);
00915
00916 subpath->last()->setProperty(KoPathPoint::StopSubpath);
00917
00918 return pathPointIndex(oldStartPoint);
00919 }
00920
00921 KoPathPointIndex KoPathShape::closeSubpath(const KoPathPointIndex &pointIndex)
00922 {
00923 KoSubpath * subpath = subPath(pointIndex.first);
00924
00925 if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size()
00926 || isClosedSubpath(pointIndex.first))
00927 return KoPathPointIndex(-1, -1);
00928
00929 KoPathPoint * oldStartPoint = subpath->first();
00930
00931 oldStartPoint->unsetProperty(KoPathPoint::StartSubpath);
00932
00933 subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
00934
00935
00936 for (int i = 0; i < pointIndex.second; ++i) {
00937 subpath->append(subpath->takeFirst());
00938 }
00939 subpath->first()->setProperty(KoPathPoint::StartSubpath);
00940 subpath->last()->setProperty(KoPathPoint::StopSubpath);
00941
00942 closeSubpath(subpath);
00943 return pathPointIndex(oldStartPoint);
00944 }
00945
00946 bool KoPathShape::reverseSubpath(int subpathIndex)
00947 {
00948 KoSubpath * subpath = subPath(subpathIndex);
00949
00950 if (subpath == 0)
00951 return false;
00952
00953 int size = subpath->size();
00954 for (int i = 0; i < size; ++i) {
00955 KoPathPoint *p = subpath->takeAt(i);
00956 p->reverse();
00957 subpath->prepend(p);
00958 }
00959
00960
00961 KoPathPoint *first = subpath->first();
00962 KoPathPoint *last = subpath->last();
00963
00964 KoPathPoint::KoPointProperties firstProps = first->properties();
00965 KoPathPoint::KoPointProperties lastProps = last->properties();
00966
00967 firstProps |= KoPathPoint::StartSubpath;
00968 firstProps &= ~KoPathPoint::StopSubpath;
00969 lastProps |= KoPathPoint::StopSubpath;
00970 lastProps &= ~KoPathPoint::StartSubpath;
00971 if (firstProps & KoPathPoint::CloseSubpath) {
00972 firstProps |= KoPathPoint::CloseSubpath;
00973 lastProps |= KoPathPoint::CloseSubpath;
00974 }
00975 first->setProperties(firstProps);
00976 last->setProperties(lastProps);
00977
00978 return true;
00979 }
00980
00981 KoSubpath * KoPathShape::removeSubpath(int subpathIndex)
00982 {
00983 KoSubpath * subpath = subPath(subpathIndex);
00984
00985 if (subpath != 0)
00986 m_subpaths.removeAt(subpathIndex);
00987
00988 return subpath;
00989 }
00990
00991 bool KoPathShape::addSubpath(KoSubpath * subpath, int subpathIndex)
00992 {
00993 if (subpathIndex < 0 || subpathIndex > m_subpaths.size())
00994 return false;
00995
00996 m_subpaths.insert(subpathIndex, subpath);
00997
00998 return true;
00999 }
01000
01001 bool KoPathShape::combine(KoPathShape *path)
01002 {
01003 if (! path)
01004 return false;
01005
01006 QMatrix pathMatrix = path->absoluteTransformation(0);
01007 QMatrix myMatrix = absoluteTransformation(0).inverted();
01008
01009 foreach(KoSubpath* subpath, path->m_subpaths) {
01010 KoSubpath *newSubpath = new KoSubpath();
01011
01012 foreach(KoPathPoint* point, *subpath) {
01013 KoPathPoint *newPoint = new KoPathPoint(*point);
01014 newPoint->map(pathMatrix);
01015 newPoint->map(myMatrix);
01016 newPoint->setParent(this);
01017 newSubpath->append(newPoint);
01018 }
01019 m_subpaths.append(newSubpath);
01020 }
01021 normalize();
01022 return true;
01023 }
01024
01025 bool KoPathShape::separate(QList<KoPathShape*> & separatedPaths)
01026 {
01027 if (! m_subpaths.size())
01028 return false;
01029
01030 QMatrix myMatrix = absoluteTransformation(0);
01031
01032 foreach(KoSubpath* subpath, m_subpaths) {
01033 KoPathShape *shape = new KoPathShape();
01034 if (! shape) continue;
01035
01036 shape->setBorder(border());
01037 shape->setShapeId(shapeId());
01038
01039 KoSubpath *newSubpath = new KoSubpath();
01040
01041 foreach(KoPathPoint* point, *subpath) {
01042 KoPathPoint *newPoint = new KoPathPoint(*point);
01043 newPoint->map(myMatrix);
01044 newSubpath->append(newPoint);
01045 }
01046 shape->m_subpaths.append(newSubpath);
01047 shape->normalize();
01048 separatedPaths.append(shape);
01049 }
01050 return true;
01051 }
01052
01053 void KoPathShape::closeSubpath(KoSubpath *subpath)
01054 {
01055 if (! subpath)
01056 return;
01057
01058 subpath->last()->setProperty(KoPathPoint::CloseSubpath);
01059 subpath->first()->setProperty(KoPathPoint::CloseSubpath);
01060 }
01061
01062 void KoPathShape::closeMergeSubpath(KoSubpath *subpath)
01063 {
01064 if (! subpath || subpath->size() < 2)
01065 return;
01066
01067 KoPathPoint * lastPoint = subpath->last();
01068 KoPathPoint * firstPoint = subpath->first();
01069
01070
01071 if (lastPoint->point() == firstPoint->point()) {
01072
01073
01074 firstPoint->setProperty(KoPathPoint::StartSubpath);
01075 firstPoint->setProperty(KoPathPoint::CloseSubpath);
01076 if (lastPoint->activeControlPoint1())
01077 firstPoint->setControlPoint1(lastPoint->controlPoint1());
01078
01079 delete subpath->takeLast();
01080
01081 lastPoint = subpath->last();
01082 lastPoint->setProperty(KoPathPoint::StopSubpath);
01083 lastPoint->setProperty(KoPathPoint::CloseSubpath);
01084 } else {
01085 closeSubpath(subpath);
01086 }
01087 }
01088
01089 KoSubpath * KoPathShape::subPath(int subpathIndex) const
01090 {
01091 if (subpathIndex < 0 || subpathIndex >= m_subpaths.size())
01092 return 0;
01093
01094 return m_subpaths.at(subpathIndex);
01095 }
01096
01097 QString KoPathShape::pathShapeId() const
01098 {
01099 return KoPathShapeId;
01100 }
01101
01102 QString KoPathShape::toString(const QMatrix &matrix) const
01103 {
01104 QString d;
01105
01106 KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
01107 for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
01108 KoSubpath::const_iterator it((*pathIt)->constBegin());
01109 KoPathPoint * lastPoint(*it);
01110 bool activeCP = false;
01111 for (; it != (*pathIt)->constEnd(); ++it) {
01112
01113 if (it == (*pathIt)->constBegin()) {
01114 if ((*it)->properties() & KoPathPoint::StartSubpath) {
01115 QPointF p = matrix.map((*it)->point());
01116 d += QString("M%1 %2").arg(p.x()).arg(p.y());
01117 }
01118 }
01119
01120 else if (activeCP || (*it)->activeControlPoint1()) {
01121 QPointF cp1 = matrix.map(activeCP ? lastPoint->controlPoint2() : lastPoint->point());
01122 QPointF cp2 = matrix.map((*it)->activeControlPoint1() ? (*it)->controlPoint1() : (*it)->point());
01123 QPointF p = matrix.map((*it)->point());
01124 d += QString("C%1 %2 %3 %4 %5 %6")
01125 .arg(cp1.x()).arg(cp1.y())
01126 .arg(cp2.x()).arg(cp2.y())
01127 .arg(p.x()).arg(p.y());
01128 }
01129
01130 else {
01131 QPointF p = matrix.map((*it)->point());
01132 d += QString("L%1 %2").arg(p.x()).arg(p.y());
01133 }
01134
01135 if ((*it)->properties() & KoPathPoint::StopSubpath
01136 && (*it)->properties() & KoPathPoint::CloseSubpath) {
01137
01138 KoPathPoint * firstPoint = (*pathIt)->first();
01139 if ((*it)->activeControlPoint2() || firstPoint->activeControlPoint1()) {
01140 QPointF cp1 = matrix.map((*it)->activeControlPoint2() ? (*it)->controlPoint2() : (*it)->point());
01141 QPointF cp2 = matrix.map(firstPoint->activeControlPoint1() ? firstPoint->controlPoint1() : (firstPoint)->point());
01142 QPointF p = matrix.map(firstPoint->point());
01143
01144 d += QString("C%1 %2 %3 %4 %5 %6")
01145 .arg(cp1.x()).arg(cp1.y())
01146 .arg(cp2.x()).arg(cp2.y())
01147 .arg(p.x()).arg(p.y());
01148 }
01149 d += QString("Z");
01150 }
01151
01152 activeCP = (*it)->activeControlPoint2();
01153 lastPoint = *it;
01154 }
01155 }
01156
01157 return d;
01158 }
01159
01160 char nodeType( const KoPathPoint * point )
01161 {
01162 if (point->properties() & KoPathPoint::IsSmooth) {
01163 return 's';
01164 }
01165 else if (point->properties() & KoPathPoint::IsSymmetric) {
01166 return 'z';
01167 }
01168 else {
01169 return 'c';
01170 }
01171 }
01172
01173 QString KoPathShape::nodeTypes() const
01174 {
01175 QString types;
01176 KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
01177 for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
01178 KoSubpath::const_iterator it((*pathIt)->constBegin());
01179 for (; it != (*pathIt)->constEnd(); ++it) {
01180 if (it == (*pathIt)->constBegin()) {
01181 types.append( 'c' );
01182 }
01183 else {
01184 types.append( nodeType( *it ) );
01185 }
01186
01187 if ((*it)->properties() & KoPathPoint::StopSubpath
01188 && (*it)->properties() & KoPathPoint::CloseSubpath) {
01189 KoPathPoint * firstPoint = (*pathIt)->first();
01190 types.append( nodeType( firstPoint ) );
01191 }
01192 }
01193 }
01194 return types;
01195 }
01196
01197 void updateNodeType( KoPathPoint * point, const QChar & nodeType )
01198 {
01199 if (nodeType == 's') {
01200 point->setProperty(KoPathPoint::IsSmooth);
01201 }
01202 else if (nodeType == 'z') {
01203 point->setProperty(KoPathPoint::IsSymmetric);
01204 }
01205 }
01206
01207 void KoPathShape::loadNodeTypes(const KoXmlElement & element)
01208 {
01209 if (element.hasAttributeNS(KoXmlNS::koffice, "nodeTypes")) {
01210 QString nodeTypes = element.attributeNS(KoXmlNS::koffice, "nodeTypes");
01211 QString::const_iterator nIt(nodeTypes.constBegin());
01212 KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
01213 for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
01214 KoSubpath::const_iterator it((*pathIt)->constBegin());
01215 for (; it != (*pathIt)->constEnd(); ++it, nIt++) {
01216
01217 if (nIt == nodeTypes.constEnd()) {
01218 kWarning(30006) << "not enough nodes in koffice:nodeTypes";
01219 return;
01220 }
01221
01222 if (it != (*pathIt)->constBegin()) {
01223 updateNodeType(*it, *nIt);
01224 }
01225
01226 if ((*it)->properties() & KoPathPoint::StopSubpath
01227 && (*it)->properties() & KoPathPoint::CloseSubpath) {
01228 ++nIt;
01229 updateNodeType((*pathIt)->first(), *nIt);
01230 }
01231 }
01232 }
01233 }
01234 }
01235
01236 Qt::FillRule KoPathShape::fillRule() const
01237 {
01238 Q_D(const KoPathShape);
01239 return d->fillRule;
01240 }
01241
01242 void KoPathShape::setFillRule(Qt::FillRule fillRule)
01243 {
01244 Q_D(KoPathShape);
01245 d->fillRule = fillRule;
01246 }
01247
01248 KoPathShape * KoPathShape::fromQPainterPath(const QPainterPath &path)
01249 {
01250 KoPathShape * shape = new KoPathShape();
01251
01252 int elementCount = path.elementCount();
01253 for (int i = 0; i < elementCount; i++) {
01254 QPainterPath::Element element = path.elementAt(i);
01255 switch (element.type) {
01256 case QPainterPath::MoveToElement:
01257 shape->moveTo(QPointF(element.x, element.y));
01258 break;
01259 case QPainterPath::LineToElement:
01260 shape->lineTo(QPointF(element.x, element.y));
01261 break;
01262 case QPainterPath::CurveToElement:
01263 shape->curveTo(QPointF(element.x, element.y),
01264 QPointF(path.elementAt(i + 1).x, path.elementAt(i + 1).y),
01265 QPointF(path.elementAt(i + 2).x, path.elementAt(i + 2).y));
01266 break;
01267 default:
01268 continue;
01269 }
01270 }
01271
01272 shape->normalize();
01273 return shape;
01274 }
01275
01276 bool KoPathShape::hitTest(const QPointF &position) const
01277 {
01278 if (parent() && parent()->childClipped(this) && ! parent()->hitTest(position))
01279 return false;
01280
01281 QPointF point = absoluteTransformation(0).inverted().map(position);
01282 const QPainterPath outlinePath = outline();
01283 if (border()) {
01284 KoInsets insets;
01285 border()->borderInsets(this, insets);
01286 QRectF roi( QPointF(-insets.left, -insets.top), QPointF(insets.right, insets.bottom) );
01287 roi.moveCenter( point );
01288 if( outlinePath.intersects( roi ) || outlinePath.contains( roi ) )
01289 return true;
01290 } else {
01291 if( outlinePath.contains( point ) )
01292 return true;
01293 }
01294
01295
01296 if (! shadow())
01297 return false;
01298
01299
01300
01301 point = absoluteTransformation(0).inverted().map(position - shadow()->offset());
01302
01303 return outlinePath.contains(point);
01304 }