Warning, file /office/calligra/libs/flake/KoSnapStrategy.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2008 Jan Hambrecht <jaham@gmx.net>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Library General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Library General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Library General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  */
0019 
0020 #include "KoSnapStrategy.h"
0021 #include "KoSnapProxy.h"
0022 #include "KoSnapGuide.h"
0023 #include <KoPathShape.h>
0024 #include <KoPathPoint.h>
0025 #include <KoPathSegment.h>
0026 #include <KoCanvasBase.h>
0027 #include <KoViewConverter.h>
0028 #include <KoGuidesData.h>
0029 
0030 #include <QPainter>
0031 #include <QPainterPath>
0032 
0033 #include <cmath>
0034 
0035 KoSnapStrategy::KoSnapStrategy(KoSnapGuide::Strategy type)
0036     : m_snapType(type)
0037 {
0038 }
0039 
0040 QPointF KoSnapStrategy::snappedPosition() const
0041 {
0042     return m_snappedPosition;
0043 }
0044 
0045 void KoSnapStrategy::setSnappedPosition(const QPointF &position)
0046 {
0047     m_snappedPosition = position;
0048 }
0049 
0050 KoSnapGuide::Strategy KoSnapStrategy::type() const
0051 {
0052     return m_snapType;
0053 }
0054 
0055 qreal KoSnapStrategy::squareDistance(const QPointF &p1, const QPointF &p2)
0056 {
0057     const qreal dx = p1.x() - p2.x();
0058     const qreal dy = p1.y() - p2.y();
0059 
0060     return dx*dx + dy*dy;
0061 }
0062 
0063 qreal KoSnapStrategy::scalarProduct(const QPointF &p1, const QPointF &p2)
0064 {
0065     return p1.x() * p2.x() + p1.y() * p2.y();
0066 }
0067 
0068 OrthogonalSnapStrategy::OrthogonalSnapStrategy()
0069     : KoSnapStrategy(KoSnapGuide::OrthogonalSnapping)
0070 {
0071 }
0072 
0073 bool OrthogonalSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy * proxy, qreal maxSnapDistance)
0074 {
0075     Q_ASSERT(std::isfinite(maxSnapDistance));
0076     QPointF horzSnap, vertSnap;
0077     qreal minVertDist = HUGE_VAL;
0078     qreal minHorzDist = HUGE_VAL;
0079 
0080     QList<KoShape*> shapes = proxy->shapes();
0081     for (KoShape * shape : shapes) {
0082         QVector<QPointF> points = proxy->pointsFromShape(shape);
0083         // There exists a problem on msvc with for(each) and QVector<QPointF>
0084         for (int i = 0; i < points.count(); ++i) {
0085             const QPointF point(points[i]);
0086             qreal dx = fabs(point.x() - mousePosition.x());
0087             if (dx < minHorzDist && dx < maxSnapDistance) {
0088                 minHorzDist = dx;
0089                 horzSnap = point;
0090             }
0091             qreal dy = fabs(point.y() - mousePosition.y());
0092             if (dy < minVertDist && dy < maxSnapDistance) {
0093                 minVertDist = dy;
0094                 vertSnap = point;
0095             }
0096         }
0097     }
0098 
0099     QPointF snappedPoint = mousePosition;
0100 
0101     if (minHorzDist < HUGE_VAL)
0102         snappedPoint.setX(horzSnap.x());
0103     if (minVertDist < HUGE_VAL)
0104         snappedPoint.setY(vertSnap.y());
0105 
0106     if (minHorzDist < HUGE_VAL)
0107         m_hLine = QLineF(horzSnap, snappedPoint);
0108     else
0109         m_hLine = QLineF();
0110 
0111     if (minVertDist < HUGE_VAL)
0112         m_vLine = QLineF(vertSnap, snappedPoint);
0113     else
0114         m_vLine = QLineF();
0115 
0116     setSnappedPosition(snappedPoint);
0117 
0118     return (minHorzDist < HUGE_VAL || minVertDist < HUGE_VAL);
0119 }
0120 
0121 QPainterPath OrthogonalSnapStrategy::decoration(const KoViewConverter &/*converter*/) const
0122 {
0123     QPainterPath decoration;
0124     if (! m_hLine.isNull()) {
0125         decoration.moveTo(m_hLine.p1());
0126         decoration.lineTo(m_hLine.p2());
0127     }
0128     if (! m_vLine.isNull()) {
0129         decoration.moveTo(m_vLine.p1());
0130         decoration.lineTo(m_vLine.p2());
0131     }
0132     return decoration;
0133 }
0134 
0135 NodeSnapStrategy::NodeSnapStrategy()
0136     : KoSnapStrategy(KoSnapGuide::NodeSnapping)
0137 {
0138 }
0139 
0140 bool NodeSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy * proxy, qreal maxSnapDistance)
0141 {
0142     Q_ASSERT(std::isfinite(maxSnapDistance));
0143     const qreal maxDistance = maxSnapDistance * maxSnapDistance;
0144     qreal minDistance = HUGE_VAL;
0145 
0146     QRectF rect(-maxSnapDistance, -maxSnapDistance, maxSnapDistance, maxSnapDistance);
0147     rect.moveCenter(mousePosition);
0148     QVector<QPointF> points = proxy->pointsInRect(rect);
0149     QPointF snappedPoint = mousePosition;
0150 
0151     // There exists a problem on msvc with for(each) and QVector<QPointF>
0152     for (int i = 0; i < points.count(); ++i) {
0153         const QPointF point(points[i]);
0154         qreal distance = squareDistance(mousePosition, point);
0155         if (distance < maxDistance && distance < minDistance) {
0156             snappedPoint = point;
0157             minDistance = distance;
0158         }
0159     }
0160 
0161     setSnappedPosition(snappedPoint);
0162 
0163     return (minDistance < HUGE_VAL);
0164 }
0165 
0166 QPainterPath NodeSnapStrategy::decoration(const KoViewConverter &converter) const
0167 {
0168     QRectF unzoomedRect = converter.viewToDocument(QRectF(0, 0, 11, 11));
0169     unzoomedRect.moveCenter(snappedPosition());
0170     QPainterPath decoration;
0171     decoration.addEllipse(unzoomedRect);
0172     return decoration;
0173 }
0174 
0175 ExtensionSnapStrategy::ExtensionSnapStrategy()
0176     : KoSnapStrategy(KoSnapGuide::ExtensionSnapping)
0177 {
0178 }
0179 
0180 bool ExtensionSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy * proxy, qreal maxSnapDistance)
0181 {
0182     Q_ASSERT(std::isfinite(maxSnapDistance));
0183 
0184     const qreal maxDistance = maxSnapDistance * maxSnapDistance;
0185     // NOTE: HUGE_VAL with qreal, which can be either float or double,
0186     // is not necessarily ideal, but seems to work good enough
0187     qreal minDistances[2] = { (qreal)HUGE_VAL, (qreal)HUGE_VAL };
0188 
0189     QPointF snappedPoints[2] = { mousePosition, mousePosition };
0190     QPointF startPoints[2];
0191 
0192     QList<KoShape*> shapes = proxy->shapes(true);
0193 
0194     for (KoShape * shape : shapes) {
0195         KoPathShape * path = dynamic_cast<KoPathShape*>(shape);
0196         if (! path) {
0197             continue;
0198         }
0199         QTransform matrix = path->absoluteTransformation(0);
0200 
0201         const int subpathCount = path->subpathCount();
0202         for (int subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) {
0203             if (path->isClosedSubpath(subpathIndex))
0204                 continue;
0205 
0206             int pointCount = path->subpathPointCount(subpathIndex);
0207 
0208             // check the extension from the start point
0209             KoPathPoint * first = path->pointByIndex(KoPathPointIndex(subpathIndex, 0));
0210             QPointF firstSnapPosition = mousePosition;
0211             if (snapToExtension(firstSnapPosition, first, matrix)) {
0212                 qreal distance = squareDistance(firstSnapPosition, mousePosition);
0213                 if (distance < maxDistance) {
0214                     if (distance < minDistances[0]) {
0215                         minDistances[1] = minDistances[0];
0216                         snappedPoints[1] = snappedPoints[0];
0217                         startPoints[1] = startPoints[0];
0218 
0219                         minDistances[0] = distance;
0220                         snappedPoints[0] = firstSnapPosition;
0221                         startPoints[0] = matrix.map(first->point());
0222                     }
0223                     else if (distance < minDistances[1]) {
0224                         minDistances[1] = distance;
0225                         snappedPoints[1] = firstSnapPosition;
0226                         startPoints[1] = matrix.map(first->point());
0227                     }
0228                 }
0229             }
0230 
0231             // now check the extension from the last point
0232             KoPathPoint * last = path->pointByIndex(KoPathPointIndex(subpathIndex, pointCount - 1));
0233             QPointF lastSnapPosition = mousePosition;
0234             if (snapToExtension(lastSnapPosition, last, matrix)) {
0235                 qreal distance = squareDistance(lastSnapPosition, mousePosition);
0236                 if (distance < maxDistance) {
0237                     if (distance < minDistances[0]) {
0238                         minDistances[1] = minDistances[0];
0239                         snappedPoints[1] = snappedPoints[0];
0240                         startPoints[1] = startPoints[0];
0241 
0242                         minDistances[0] = distance;
0243                         snappedPoints[0] = lastSnapPosition;
0244                         startPoints[0] = matrix.map(last->point());
0245                     }
0246                     else if (distance < minDistances[1]) {
0247                         minDistances[1] = distance;
0248                         snappedPoints[1] = lastSnapPosition;
0249                         startPoints[1] = matrix.map(last->point());
0250                     }
0251                 }
0252             }
0253         }
0254     }
0255 
0256     m_lines.clear();
0257     // if we have to extension near our mouse position, they might have an intersection
0258     // near our mouse position which we want to use as the snapped position
0259     if (minDistances[0] < HUGE_VAL && minDistances[1] < HUGE_VAL) {
0260         // check if intersection of extension lines is near mouse position
0261         KoPathSegment s1(startPoints[0], snappedPoints[0] + snappedPoints[0]-startPoints[0]);
0262         KoPathSegment s2(startPoints[1], snappedPoints[1] + snappedPoints[1]-startPoints[1]);
0263         QVector<QPointF> isects = s1.intersections(s2);
0264         if (isects.count() == 1 && squareDistance(isects[0], mousePosition) < maxDistance) {
0265             // add both extension lines
0266             m_lines.append(QLineF(startPoints[0], isects[0]));
0267             m_lines.append(QLineF(startPoints[1], isects[0]));
0268             setSnappedPosition(isects[0]);
0269         }
0270         else {
0271             // only add nearest extension line of both
0272             uint index = minDistances[0] < minDistances[1] ? 0 : 1;
0273             m_lines.append(QLineF(startPoints[index], snappedPoints[index]));
0274             setSnappedPosition(snappedPoints[index]);
0275         }
0276     }
0277     else  if (minDistances[0] < HUGE_VAL) {
0278         m_lines.append(QLineF(startPoints[0], snappedPoints[0]));
0279         setSnappedPosition(snappedPoints[0]);
0280     }
0281     else if (minDistances[1] < HUGE_VAL) {
0282         m_lines.append(QLineF(startPoints[1], snappedPoints[1]));
0283         setSnappedPosition(snappedPoints[1]);
0284     }
0285     else {
0286         // none of the extension lines is near our mouse position
0287         return false;
0288     }
0289     return true;
0290 }
0291 
0292 QPainterPath ExtensionSnapStrategy::decoration(const KoViewConverter &/*converter*/) const
0293 {
0294     QPainterPath decoration;
0295     for (const QLineF &line : m_lines) {
0296         decoration.moveTo(line.p1());
0297         decoration.lineTo(line.p2());
0298     }
0299     return decoration;
0300 }
0301 
0302 bool ExtensionSnapStrategy::snapToExtension(QPointF &position, KoPathPoint * point, const QTransform &matrix)
0303 {
0304     Q_ASSERT(point);
0305     QPointF direction = extensionDirection(point, matrix);
0306     if (direction.isNull())
0307         return false;
0308 
0309     QPointF extensionStart = matrix.map(point->point());
0310     QPointF extensionStop = matrix.map(point->point()) + direction;
0311     float posOnExtension = project(extensionStart, extensionStop, position);
0312     if (posOnExtension < 0.0)
0313         return false;
0314 
0315     position = extensionStart + posOnExtension * direction;
0316     return true;
0317 }
0318 
0319 qreal ExtensionSnapStrategy::project(const QPointF &lineStart, const QPointF &lineEnd, const QPointF &point)
0320 {
0321     // This is how the returned value should be used to get the
0322     // projectionPoint: ProjectionPoint = lineStart(1-resultingReal) + resultingReal*lineEnd;
0323 
0324     QPointF diff = lineEnd - lineStart;
0325     QPointF relPoint = point - lineStart;
0326     qreal diffLength = sqrt(diff.x() * diff.x() + diff.y() * diff.y());
0327     if (diffLength == 0.0)
0328         return 0.0;
0329 
0330     diff /= diffLength;
0331     // project mouse position relative to stop position on extension line
0332     qreal scalar = relPoint.x() * diff.x() + relPoint.y() * diff.y();
0333     return scalar /= diffLength;
0334 }
0335 
0336 QPointF ExtensionSnapStrategy::extensionDirection(KoPathPoint * point, const QTransform &matrix)
0337 {
0338     Q_ASSERT(point);
0339 
0340     KoPathShape * path = point->parent();
0341     KoPathPointIndex index = path->pathPointIndex(point);
0342 
0343     // check if it is a start point
0344     if (point->properties() & KoPathPoint::StartSubpath) {
0345         if (point->activeControlPoint2()) {
0346             return matrix.map(point->point()) - matrix.map(point->controlPoint2());
0347         } else {
0348             KoPathPoint * next = path->pointByIndex(KoPathPointIndex(index.first, index.second + 1));
0349             if (! next){
0350                 return QPointF();
0351             }
0352             else if (next->activeControlPoint1()) {
0353                 return matrix.map(point->point()) - matrix.map(next->controlPoint1());
0354             }
0355             else {
0356                 return matrix.map(point->point()) - matrix.map(next->point());
0357             }
0358         }
0359     }
0360     else {
0361         if (point->activeControlPoint1()) {
0362             return matrix.map(point->point()) - matrix.map(point->controlPoint1());
0363         }
0364         else {
0365             KoPathPoint * prev = path->pointByIndex(KoPathPointIndex(index.first, index.second - 1));
0366             if (! prev){
0367                 return QPointF();
0368             }
0369             else if (prev->activeControlPoint2()) {
0370                 return matrix.map(point->point()) - matrix.map(prev->controlPoint2());
0371             }
0372             else {
0373                 return matrix.map(point->point()) - matrix.map(prev->point());
0374             }
0375         }
0376     }
0377 }
0378 
0379 IntersectionSnapStrategy::IntersectionSnapStrategy()
0380     : KoSnapStrategy(KoSnapGuide::IntersectionSnapping)
0381 {
0382 }
0383 
0384 bool IntersectionSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy *proxy, qreal maxSnapDistance)
0385 {
0386     Q_ASSERT(std::isfinite(maxSnapDistance));
0387     const qreal maxDistance = maxSnapDistance * maxSnapDistance;
0388     qreal minDistance = HUGE_VAL;
0389 
0390     QRectF rect(-maxSnapDistance, -maxSnapDistance, maxSnapDistance, maxSnapDistance);
0391     rect.moveCenter(mousePosition);
0392     QPointF snappedPoint = mousePosition;
0393 
0394     QList<KoPathSegment> segments = proxy->segmentsInRect(rect);
0395     int segmentCount = segments.count();
0396     for (int i = 0; i < segmentCount; ++i) {
0397         const KoPathSegment &s1 = segments[i];
0398         for (int j = i + 1; j < segmentCount; ++j) {
0399             QVector<QPointF> isects = s1.intersections(segments[j]);
0400             // There exists a problem on msvc with for(each) and QVector<QPointF>
0401             for (int a = 0; a < isects.count(); ++a) {
0402                 const QPointF& point(isects[a]);
0403                 if (! rect.contains(point))
0404                     continue;
0405                 qreal distance = squareDistance(mousePosition, point);
0406                 if (distance < maxDistance && distance < minDistance) {
0407                     snappedPoint = point;
0408                     minDistance = distance;
0409                 }
0410             }
0411         }
0412     }
0413 
0414     setSnappedPosition(snappedPoint);
0415 
0416     return (minDistance < HUGE_VAL);
0417 }
0418 
0419 QPainterPath IntersectionSnapStrategy::decoration(const KoViewConverter &converter) const
0420 {
0421     QRectF unzoomedRect = converter.viewToDocument(QRectF(0, 0, 11, 11));
0422     unzoomedRect.moveCenter(snappedPosition());
0423     QPainterPath decoration;
0424     decoration.addRect(unzoomedRect);
0425     return decoration;
0426 }
0427 
0428 GridSnapStrategy::GridSnapStrategy()
0429     : KoSnapStrategy(KoSnapGuide::GridSnapping)
0430 {
0431 }
0432 
0433 bool GridSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy *proxy, qreal maxSnapDistance)
0434 {
0435     Q_ASSERT(std::isfinite(maxSnapDistance));
0436     if (! proxy->canvas()->snapToGrid())
0437         return false;
0438 
0439     // The 1e-10 here is a workaround for some weird division problem.
0440     // 360.00062366 / 2.83465058 gives 127 'exactly' when shown as a qreal,
0441     // but when casting into an int, we get 126. In fact it's 127 - 5.64e-15 !
0442     qreal gridX, gridY;
0443     proxy->canvas()->gridSize(&gridX, &gridY);
0444 
0445     // we want to snap to the nearest grid point, so calculate
0446     // the grid rows/columns before and after the points position
0447     int col = static_cast<int>(mousePosition.x() / gridX + 1e-10);
0448     int nextCol = col + 1;
0449     int row = static_cast<int>(mousePosition.y() / gridY + 1e-10);
0450     int nextRow = row + 1;
0451 
0452     // now check which grid line has less distance to the point
0453     qreal distToCol = qAbs(col * gridX - mousePosition.x());
0454     qreal distToNextCol = qAbs(nextCol * gridX - mousePosition.x());
0455 
0456     if (distToCol > distToNextCol) {
0457         col = nextCol;
0458         distToCol = distToNextCol;
0459     }
0460 
0461     qreal distToRow = qAbs(row * gridY - mousePosition.y());
0462     qreal distToNextRow = qAbs(nextRow * gridY - mousePosition.y());
0463     if (distToRow > distToNextRow) {
0464         row = nextRow;
0465         distToRow = distToNextRow;
0466     }
0467 
0468     QPointF snappedPoint = mousePosition;
0469 
0470     const qreal distance = distToCol * distToCol + distToRow * distToRow;
0471     const qreal maxDistance = maxSnapDistance * maxSnapDistance;
0472     // now check if we are inside the snap distance
0473     if (distance < maxDistance) {
0474         snappedPoint = QPointF(col * gridX, row * gridY);
0475     }
0476 
0477     setSnappedPosition(snappedPoint);
0478 
0479     return (distance < maxDistance);
0480 }
0481 
0482 QPainterPath GridSnapStrategy::decoration(const KoViewConverter &converter) const
0483 {
0484     QSizeF unzoomedSize = converter.viewToDocument(QSizeF(5, 5));
0485     QPainterPath decoration;
0486     decoration.moveTo(snappedPosition() - QPointF(unzoomedSize.width(), 0));
0487     decoration.lineTo(snappedPosition() + QPointF(unzoomedSize.width(), 0));
0488     decoration.moveTo(snappedPosition() - QPointF(0, unzoomedSize.height()));
0489     decoration.lineTo(snappedPosition() + QPointF(0, unzoomedSize.height()));
0490     return decoration;
0491 }
0492 
0493 BoundingBoxSnapStrategy::BoundingBoxSnapStrategy()
0494     : KoSnapStrategy(KoSnapGuide::BoundingBoxSnapping)
0495 {
0496 }
0497 
0498 bool BoundingBoxSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy *proxy, qreal maxSnapDistance)
0499 {
0500     Q_ASSERT(std::isfinite(maxSnapDistance));
0501     const qreal maxDistance = maxSnapDistance * maxSnapDistance;
0502     qreal minDistance = HUGE_VAL;
0503 
0504     QRectF rect(-maxSnapDistance, -maxSnapDistance, maxSnapDistance, maxSnapDistance);
0505 
0506     rect.moveCenter(mousePosition);
0507     QPointF snappedPoint = mousePosition;
0508 
0509     KoFlake::Position pointId[5] = {
0510         KoFlake::TopLeftCorner,
0511         KoFlake::TopRightCorner,
0512         KoFlake::BottomRightCorner,
0513         KoFlake::BottomLeftCorner,
0514         KoFlake::CenteredPosition
0515     };
0516 
0517     QList<KoShape*> shapes = proxy->shapesInRect(rect, true);
0518     for (KoShape * shape : shapes) {
0519         qreal shapeMinDistance = HUGE_VAL;
0520         // first check the corner and center points
0521         for (int i = 0; i < 5; ++i) {
0522             m_boxPoints[i] = shape->absolutePosition(pointId[i]);
0523             qreal d = squareDistance(mousePosition, m_boxPoints[i]);
0524             if (d < minDistance && d < maxDistance) {
0525                 shapeMinDistance = d;
0526                 minDistance = d;
0527                 snappedPoint = m_boxPoints[i];
0528             }
0529         }
0530         // prioritize points over edges
0531         if (shapeMinDistance < maxDistance)
0532             continue;
0533 
0534         // now check distances to edges of bounding box
0535         for (int i = 0; i < 4; ++i) {
0536             QPointF pointOnLine;
0537             qreal d = squareDistanceToLine(m_boxPoints[i], m_boxPoints[(i+1)%4], mousePosition, pointOnLine);
0538             if (d < minDistance && d < maxDistance) {
0539                 minDistance = d;
0540                 snappedPoint = pointOnLine;
0541             }
0542         }
0543     }
0544     setSnappedPosition(snappedPoint);
0545 
0546     return (minDistance < maxDistance);
0547 }
0548 
0549 qreal BoundingBoxSnapStrategy::squareDistanceToLine(const QPointF &lineA, const QPointF &lineB, const QPointF &point, QPointF &pointOnLine)
0550 {
0551     QPointF diff = lineB - lineA;
0552     if(lineA == lineB)
0553         return HUGE_VAL;
0554     const qreal diffLength = sqrt(diff.x() * diff.x() + diff.y() * diff.y());
0555 
0556     // project mouse position relative to start position on line
0557     const qreal scalar = KoSnapStrategy::scalarProduct(point - lineA, diff / diffLength);
0558 
0559     if (scalar < 0.0 || scalar > diffLength)
0560         return HUGE_VAL;
0561     // calculate vector between relative mouse position and projected mouse position
0562     pointOnLine = lineA + scalar / diffLength * diff;
0563     QPointF distVec = pointOnLine - point;
0564     return distVec.x()*distVec.x() + distVec.y()*distVec.y();
0565 }
0566 
0567 QPainterPath BoundingBoxSnapStrategy::decoration(const KoViewConverter &converter) const
0568 {
0569     QSizeF unzoomedSize = converter.viewToDocument(QSizeF(5, 5));
0570 
0571     QPainterPath decoration;
0572     decoration.moveTo(snappedPosition() - QPointF(unzoomedSize.width(), unzoomedSize.height()));
0573     decoration.lineTo(snappedPosition() + QPointF(unzoomedSize.width(), unzoomedSize.height()));
0574     decoration.moveTo(snappedPosition() - QPointF(unzoomedSize.width(), -unzoomedSize.height()));
0575     decoration.lineTo(snappedPosition() + QPointF(unzoomedSize.width(), -unzoomedSize.height()));
0576 
0577     return decoration;
0578 }
0579 
0580 LineGuideSnapStrategy::LineGuideSnapStrategy()
0581     : KoSnapStrategy(KoSnapGuide::GuideLineSnapping)
0582 {
0583 }
0584 
0585 bool LineGuideSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy * proxy, qreal maxSnapDistance)
0586 {
0587     Q_ASSERT(std::isfinite(maxSnapDistance));
0588 
0589     KoGuidesData * guidesData = proxy->canvas()->guidesData();
0590 
0591     if (!guidesData || !guidesData->showGuideLines())
0592         return false;
0593 
0594     QPointF snappedPoint = mousePosition;
0595     m_orientation = 0;
0596 
0597     qreal minHorzDistance = maxSnapDistance;
0598     for (qreal guidePos : guidesData->horizontalGuideLines()) {
0599         qreal distance = qAbs(guidePos - mousePosition.y());
0600         if (distance < minHorzDistance) {
0601             snappedPoint.setY(guidePos);
0602             minHorzDistance = distance;
0603             m_orientation |= Qt::Horizontal;
0604         }
0605     }
0606     qreal minVertSnapDistance = maxSnapDistance;
0607     for (qreal guidePos : guidesData->verticalGuideLines()) {
0608         qreal distance = qAbs(guidePos - mousePosition.x());
0609         if (distance < minVertSnapDistance) {
0610             snappedPoint.setX(guidePos);
0611             minVertSnapDistance = distance;
0612             m_orientation |= Qt::Vertical;
0613         }
0614     }
0615     setSnappedPosition(snappedPoint);
0616     return (minHorzDistance < maxSnapDistance || minVertSnapDistance < maxSnapDistance);
0617 }
0618 
0619 QPainterPath LineGuideSnapStrategy::decoration(const KoViewConverter &converter) const
0620 {
0621     QSizeF unzoomedSize = converter.viewToDocument(QSizeF(5, 5));
0622     Q_ASSERT(unzoomedSize.isValid());
0623 
0624     QPainterPath decoration;
0625     if (m_orientation & Qt::Horizontal) {
0626         decoration.moveTo(snappedPosition() - QPointF(unzoomedSize.width(), 0));
0627         decoration.lineTo(snappedPosition() + QPointF(unzoomedSize.width(), 0));
0628     }
0629     if (m_orientation & Qt::Vertical) {
0630         decoration.moveTo(snappedPosition() - QPointF(0, unzoomedSize.height()));
0631         decoration.lineTo(snappedPosition() + QPointF(0, unzoomedSize.height()));
0632     }
0633 
0634     return decoration;
0635 }