1 /* This file is part of the KDE project
2  * Copyright (C) 2008 Jan Hambrecht <jaham@gmx.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #include "KoSnapStrategy.h"
21 #include "KoSnapProxy.h"
22 #include "KoSnapGuide.h"
23 #include <KoPathShape.h>
24 #include <KoPathPoint.h>
25 #include <KoPathSegment.h>
26 #include <KoCanvasBase.h>
27 #include <KoViewConverter.h>
28 
29 #include <QPainter>
30 #include <QPainterPath>
31 
32 #include <cmath>
33 
34 #if defined(_MSC_VER) && (_MSC_VER < 1800)
35 #define isfinite(x) (double)(x)
36 #endif
37 
KoSnapStrategy(KoSnapGuide::Strategy type)38 KoSnapStrategy::KoSnapStrategy(KoSnapGuide::Strategy type)
39     : m_snapType(type)
40 {
41 }
42 
snappedPosition() const43 QPointF KoSnapStrategy::snappedPosition() const
44 {
45     return m_snappedPosition;
46 }
47 
setSnappedPosition(const QPointF & position)48 void KoSnapStrategy::setSnappedPosition(const QPointF &position)
49 {
50     m_snappedPosition = position;
51 }
52 
type() const53 KoSnapGuide::Strategy KoSnapStrategy::type() const
54 {
55     return m_snapType;
56 }
57 
squareDistance(const QPointF & p1,const QPointF & p2)58 qreal KoSnapStrategy::squareDistance(const QPointF &p1, const QPointF &p2)
59 {
60     const qreal dx = p1.x() - p2.x();
61     const qreal dy = p1.y() - p2.y();
62 
63     return dx*dx + dy*dy;
64 }
65 
scalarProduct(const QPointF & p1,const QPointF & p2)66 qreal KoSnapStrategy::scalarProduct(const QPointF &p1, const QPointF &p2)
67 {
68     return p1.x() * p2.x() + p1.y() * p2.y();
69 }
70 
OrthogonalSnapStrategy()71 OrthogonalSnapStrategy::OrthogonalSnapStrategy()
72     : KoSnapStrategy(KoSnapGuide::OrthogonalSnapping)
73 {
74 }
75 
snap(const QPointF & mousePosition,KoSnapProxy * proxy,qreal maxSnapDistance)76 bool OrthogonalSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy * proxy, qreal maxSnapDistance)
77 {
78     Q_ASSERT(std::isfinite(maxSnapDistance));
79     QPointF horzSnap, vertSnap;
80     qreal minVertDist = HUGE_VAL;
81     qreal minHorzDist = HUGE_VAL;
82 
83     QList<KoShape*> shapes = proxy->shapes(true);
84     Q_FOREACH (KoShape * shape, shapes) {
85         QList<QPointF> points = proxy->pointsFromShape(shape);
86         foreach (const QPointF &point, points) {
87             qreal dx = fabs(point.x() - mousePosition.x());
88             if (dx < minHorzDist && dx < maxSnapDistance) {
89                 minHorzDist = dx;
90                 horzSnap = point;
91             }
92             qreal dy = fabs(point.y() - mousePosition.y());
93             if (dy < minVertDist && dy < maxSnapDistance) {
94                 minVertDist = dy;
95                 vertSnap = point;
96             }
97         }
98     }
99 
100     QPointF snappedPoint = mousePosition;
101 
102     if (minHorzDist < HUGE_VAL)
103         snappedPoint.setX(horzSnap.x());
104     if (minVertDist < HUGE_VAL)
105         snappedPoint.setY(vertSnap.y());
106 
107     if (minHorzDist < HUGE_VAL)
108         m_hLine = QLineF(horzSnap, snappedPoint);
109     else
110         m_hLine = QLineF();
111 
112     if (minVertDist < HUGE_VAL)
113         m_vLine = QLineF(vertSnap, snappedPoint);
114     else
115         m_vLine = QLineF();
116 
117     setSnappedPosition(snappedPoint);
118 
119     return (minHorzDist < HUGE_VAL || minVertDist < HUGE_VAL);
120 }
121 
decoration(const KoViewConverter &) const122 QPainterPath OrthogonalSnapStrategy::decoration(const KoViewConverter &/*converter*/) const
123 {
124     QPainterPath decoration;
125     if (! m_hLine.isNull()) {
126         decoration.moveTo(m_hLine.p1());
127         decoration.lineTo(m_hLine.p2());
128     }
129     if (! m_vLine.isNull()) {
130         decoration.moveTo(m_vLine.p1());
131         decoration.lineTo(m_vLine.p2());
132     }
133     return decoration;
134 }
135 
NodeSnapStrategy()136 NodeSnapStrategy::NodeSnapStrategy()
137     : KoSnapStrategy(KoSnapGuide::NodeSnapping)
138 {
139 }
140 
snap(const QPointF & mousePosition,KoSnapProxy * proxy,qreal maxSnapDistance)141 bool NodeSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy * proxy, qreal maxSnapDistance)
142 {
143     Q_ASSERT(std::isfinite(maxSnapDistance));
144     const qreal maxDistance = maxSnapDistance * maxSnapDistance;
145     qreal minDistance = HUGE_VAL;
146 
147     QRectF rect(-maxSnapDistance, -maxSnapDistance, maxSnapDistance, maxSnapDistance);
148     rect.moveCenter(mousePosition);
149     QList<QPointF> points = proxy->pointsInRect(rect, false);
150     QPointF snappedPoint = mousePosition;
151 
152     foreach (const QPointF &point, points) {
153         qreal distance = squareDistance(mousePosition, point);
154         if (distance < maxDistance && distance < minDistance) {
155             snappedPoint = point;
156             minDistance = distance;
157         }
158     }
159 
160     setSnappedPosition(snappedPoint);
161 
162     return (minDistance < HUGE_VAL);
163 }
164 
decoration(const KoViewConverter & converter) const165 QPainterPath NodeSnapStrategy::decoration(const KoViewConverter &converter) const
166 {
167     QRectF unzoomedRect = converter.viewToDocument(QRectF(0, 0, 11, 11));
168     unzoomedRect.moveCenter(snappedPosition());
169     QPainterPath decoration;
170     decoration.addEllipse(unzoomedRect);
171     return decoration;
172 }
173 
ExtensionSnapStrategy()174 ExtensionSnapStrategy::ExtensionSnapStrategy()
175     : KoSnapStrategy(KoSnapGuide::ExtensionSnapping)
176 {
177 }
178 
snap(const QPointF & mousePosition,KoSnapProxy * proxy,qreal maxSnapDistance)179 bool ExtensionSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy * proxy, qreal maxSnapDistance)
180 {
181     Q_ASSERT(std::isfinite(maxSnapDistance));
182 
183     const qreal maxDistance = maxSnapDistance * maxSnapDistance;
184     qreal minDistances[2] = { HUGE_VAL, HUGE_VAL };
185 
186     QPointF snappedPoints[2] = { mousePosition, mousePosition };
187     QPointF startPoints[2];
188 
189     QList<KoShape*> shapes = proxy->shapes(true);
190 
191     Q_FOREACH (KoShape * shape, shapes) {
192         KoPathShape * path = dynamic_cast<KoPathShape*>(shape);
193         if (! path) {
194             continue;
195         }
196         QTransform matrix = path->absoluteTransformation();
197 
198         const int subpathCount = path->subpathCount();
199         for (int subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) {
200             if (path->isClosedSubpath(subpathIndex))
201                 continue;
202 
203             int pointCount = path->subpathPointCount(subpathIndex);
204 
205             // check the extension from the start point
206             KoPathPoint * first = path->pointByIndex(KoPathPointIndex(subpathIndex, 0));
207             QPointF firstSnapPosition = mousePosition;
208             if (snapToExtension(firstSnapPosition, first, matrix)) {
209                 qreal distance = squareDistance(firstSnapPosition, mousePosition);
210                 if (distance < maxDistance) {
211                     if (distance < minDistances[0]) {
212                         minDistances[1] = minDistances[0];
213                         snappedPoints[1] = snappedPoints[0];
214                         startPoints[1] = startPoints[0];
215 
216                         minDistances[0] = distance;
217                         snappedPoints[0] = firstSnapPosition;
218                         startPoints[0] = matrix.map(first->point());
219                     }
220                     else if (distance < minDistances[1]) {
221                         minDistances[1] = distance;
222                         snappedPoints[1] = firstSnapPosition;
223                         startPoints[1] = matrix.map(first->point());
224                     }
225                 }
226             }
227 
228             // now check the extension from the last point
229             KoPathPoint * last = path->pointByIndex(KoPathPointIndex(subpathIndex, pointCount - 1));
230             QPointF lastSnapPosition = mousePosition;
231             if (snapToExtension(lastSnapPosition, last, matrix)) {
232                 qreal distance = squareDistance(lastSnapPosition, mousePosition);
233                 if (distance < maxDistance) {
234                     if (distance < minDistances[0]) {
235                         minDistances[1] = minDistances[0];
236                         snappedPoints[1] = snappedPoints[0];
237                         startPoints[1] = startPoints[0];
238 
239                         minDistances[0] = distance;
240                         snappedPoints[0] = lastSnapPosition;
241                         startPoints[0] = matrix.map(last->point());
242                     }
243                     else if (distance < minDistances[1]) {
244                         minDistances[1] = distance;
245                         snappedPoints[1] = lastSnapPosition;
246                         startPoints[1] = matrix.map(last->point());
247                     }
248                 }
249             }
250         }
251     }
252 
253     m_lines.clear();
254     // if we have to extension near our mouse position, they might have an intersection
255     // near our mouse position which we want to use as the snapped position
256     if (minDistances[0] < HUGE_VAL && minDistances[1] < HUGE_VAL) {
257         // check if intersection of extension lines is near mouse position
258         KoPathSegment s1(startPoints[0], snappedPoints[0] + snappedPoints[0]-startPoints[0]);
259         KoPathSegment s2(startPoints[1], snappedPoints[1] + snappedPoints[1]-startPoints[1]);
260         QList<QPointF> isects = s1.intersections(s2);
261         if (isects.count() == 1 && squareDistance(isects[0], mousePosition) < maxDistance) {
262             // add both extension lines
263             m_lines.append(QLineF(startPoints[0], isects[0]));
264             m_lines.append(QLineF(startPoints[1], isects[0]));
265             setSnappedPosition(isects[0]);
266         }
267         else {
268             // only add nearest extension line of both
269             uint index = minDistances[0] < minDistances[1] ? 0 : 1;
270             m_lines.append(QLineF(startPoints[index], snappedPoints[index]));
271             setSnappedPosition(snappedPoints[index]);
272         }
273     }
274     else  if (minDistances[0] < HUGE_VAL) {
275         m_lines.append(QLineF(startPoints[0], snappedPoints[0]));
276         setSnappedPosition(snappedPoints[0]);
277     }
278     else if (minDistances[1] < HUGE_VAL) {
279         m_lines.append(QLineF(startPoints[1], snappedPoints[1]));
280         setSnappedPosition(snappedPoints[1]);
281     }
282     else {
283         // none of the extension lines is near our mouse position
284         return false;
285     }
286     return true;
287 }
288 
decoration(const KoViewConverter &) const289 QPainterPath ExtensionSnapStrategy::decoration(const KoViewConverter &/*converter*/) const
290 {
291     QPainterPath decoration;
292     foreach (const QLineF &line, m_lines) {
293         decoration.moveTo(line.p1());
294         decoration.lineTo(line.p2());
295     }
296     return decoration;
297 }
298 
snapToExtension(QPointF & position,KoPathPoint * point,const QTransform & matrix)299 bool ExtensionSnapStrategy::snapToExtension(QPointF &position, KoPathPoint * point, const QTransform &matrix)
300 {
301     Q_ASSERT(point);
302     QPointF direction = extensionDirection(point, matrix);
303     if (direction.isNull())
304         return false;
305 
306     QPointF extensionStart = matrix.map(point->point());
307     QPointF extensionStop = matrix.map(point->point()) + direction;
308     float posOnExtension = project(extensionStart, extensionStop, position);
309     if (posOnExtension < 0.0)
310         return false;
311 
312     position = extensionStart + posOnExtension * direction;
313     return true;
314 }
315 
project(const QPointF & lineStart,const QPointF & lineEnd,const QPointF & point)316 qreal ExtensionSnapStrategy::project(const QPointF &lineStart, const QPointF &lineEnd, const QPointF &point)
317 {
318     // This is how the returned value should be used to get the
319     // projectionPoint: ProjectionPoint = lineStart(1-resultingReal) + resultingReal*lineEnd;
320 
321     QPointF diff = lineEnd - lineStart;
322     QPointF relPoint = point - lineStart;
323     qreal diffLength = sqrt(diff.x() * diff.x() + diff.y() * diff.y());
324     if (diffLength == 0.0)
325         return 0.0;
326 
327     diff /= diffLength;
328     // project mouse position relative to stop position on extension line
329     qreal scalar = relPoint.x() * diff.x() + relPoint.y() * diff.y();
330     return scalar /= diffLength;
331 }
332 
extensionDirection(KoPathPoint * point,const QTransform & matrix)333 QPointF ExtensionSnapStrategy::extensionDirection(KoPathPoint * point, const QTransform &matrix)
334 {
335     Q_ASSERT(point);
336 
337     KoPathShape * path = point->parent();
338     KoPathPointIndex index = path->pathPointIndex(point);
339 
340     // check if it is a start point
341     if (point->properties() & KoPathPoint::StartSubpath) {
342         if (point->activeControlPoint2()) {
343             return matrix.map(point->point()) - matrix.map(point->controlPoint2());
344         } else {
345             KoPathPoint * next = path->pointByIndex(KoPathPointIndex(index.first, index.second + 1));
346             if (! next){
347                 return QPointF();
348             }
349             else if (next->activeControlPoint1()) {
350                 return matrix.map(point->point()) - matrix.map(next->controlPoint1());
351             }
352             else {
353                 return matrix.map(point->point()) - matrix.map(next->point());
354             }
355         }
356     }
357     else {
358         if (point->activeControlPoint1()) {
359             return matrix.map(point->point()) - matrix.map(point->controlPoint1());
360         }
361         else {
362             KoPathPoint * prev = path->pointByIndex(KoPathPointIndex(index.first, index.second - 1));
363             if (! prev){
364                 return QPointF();
365             }
366             else if (prev->activeControlPoint2()) {
367                 return matrix.map(point->point()) - matrix.map(prev->controlPoint2());
368             }
369             else {
370                 return matrix.map(point->point()) - matrix.map(prev->point());
371             }
372         }
373     }
374 }
375 
IntersectionSnapStrategy()376 IntersectionSnapStrategy::IntersectionSnapStrategy()
377     : KoSnapStrategy(KoSnapGuide::IntersectionSnapping)
378 {
379 }
380 
snap(const QPointF & mousePosition,KoSnapProxy * proxy,qreal maxSnapDistance)381 bool IntersectionSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy *proxy, qreal maxSnapDistance)
382 {
383     Q_ASSERT(std::isfinite(maxSnapDistance));
384     const qreal maxDistance = maxSnapDistance * maxSnapDistance;
385     qreal minDistance = HUGE_VAL;
386 
387     QRectF rect(-maxSnapDistance, -maxSnapDistance, maxSnapDistance, maxSnapDistance);
388     rect.moveCenter(mousePosition);
389     QPointF snappedPoint = mousePosition;
390 
391     QList<KoPathSegment> segments = proxy->segmentsInRect(rect, false);
392     int segmentCount = segments.count();
393     for (int i = 0; i < segmentCount; ++i) {
394         const KoPathSegment &s1 = segments[i];
395         for (int j = i + 1; j < segmentCount; ++j) {
396             QList<QPointF> isects = s1.intersections(segments[j]);
397             Q_FOREACH (const QPointF &point, isects) {
398                 if (! rect.contains(point))
399                     continue;
400                 qreal distance = squareDistance(mousePosition, point);
401                 if (distance < maxDistance && distance < minDistance) {
402                     snappedPoint = point;
403                     minDistance = distance;
404                 }
405             }
406         }
407     }
408 
409     setSnappedPosition(snappedPoint);
410 
411     return (minDistance < HUGE_VAL);
412 }
413 
decoration(const KoViewConverter & converter) const414 QPainterPath IntersectionSnapStrategy::decoration(const KoViewConverter &converter) const
415 {
416     QRectF unzoomedRect = converter.viewToDocument(QRectF(0, 0, 11, 11));
417     unzoomedRect.moveCenter(snappedPosition());
418     QPainterPath decoration;
419     decoration.addRect(unzoomedRect);
420     return decoration;
421 }
422 
GridSnapStrategy()423 GridSnapStrategy::GridSnapStrategy()
424     : KoSnapStrategy(KoSnapGuide::GridSnapping)
425 {
426 }
427 
snap(const QPointF & mousePosition,KoSnapProxy * proxy,qreal maxSnapDistance)428 bool GridSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy *proxy, qreal maxSnapDistance)
429 {
430     Q_ASSERT(std::isfinite(maxSnapDistance));
431     if (! proxy->canvas()->snapToGrid())
432         return false;
433 
434     // The 1e-10 here is a workaround for some weird division problem.
435     // 360.00062366 / 2.83465058 gives 127 'exactly' when shown as a qreal,
436     // but when casting into an int, we get 126. In fact it's 127 - 5.64e-15 !
437     QPointF offset;
438     QSizeF spacing;
439     proxy->canvas()->gridSize(&offset, &spacing);
440 
441     // we want to snap to the nearest grid point, so calculate
442     // the grid rows/columns before and after the points position
443     int col = static_cast<int>((mousePosition.x() - offset.x()) / spacing.width() + 1e-10);
444     int nextCol = col + 1;
445     int row = static_cast<int>((mousePosition.y() - offset.y()) / spacing.height() + 1e-10);
446     int nextRow = row + 1;
447 
448     // now check which grid line has less distance to the point
449     qreal distToCol = qAbs(offset.x() + col * spacing.width() - mousePosition.x());
450     qreal distToNextCol = qAbs(offset.x() + nextCol * spacing.width() - mousePosition.x());
451 
452     if (distToCol > distToNextCol) {
453         col = nextCol;
454         distToCol = distToNextCol;
455     }
456 
457     qreal distToRow = qAbs(offset.y() + row * spacing.height() - mousePosition.y());
458     qreal distToNextRow = qAbs(offset.y() + nextRow * spacing.height() - mousePosition.y());
459     if (distToRow > distToNextRow) {
460         row = nextRow;
461         distToRow = distToNextRow;
462     }
463 
464     QPointF snappedPoint = mousePosition;
465 
466     bool pointIsSnapped = false;
467 
468     const qreal sqDistance = distToCol * distToCol + distToRow * distToRow;
469     const qreal maxSqDistance = maxSnapDistance * maxSnapDistance;
470     // now check if we are inside the snap distance
471     if (sqDistance < maxSqDistance) {
472         snappedPoint = QPointF(offset.x() + col * spacing.width(), offset.y() + row * spacing.height());
473         pointIsSnapped = true;
474     } else if (distToRow < maxSnapDistance) {
475         snappedPoint.ry() = offset.y() + row * spacing.height();
476         pointIsSnapped = true;
477     } else if (distToCol < maxSnapDistance) {
478         snappedPoint.rx() = offset.x() + col * spacing.width();
479         pointIsSnapped = true;
480     }
481 
482     setSnappedPosition(snappedPoint);
483 
484     return pointIsSnapped;
485 }
486 
decoration(const KoViewConverter & converter) const487 QPainterPath GridSnapStrategy::decoration(const KoViewConverter &converter) const
488 {
489     QSizeF unzoomedSize = converter.viewToDocument(QSizeF(5, 5));
490     QPainterPath decoration;
491     decoration.moveTo(snappedPosition() - QPointF(unzoomedSize.width(), 0));
492     decoration.lineTo(snappedPosition() + QPointF(unzoomedSize.width(), 0));
493     decoration.moveTo(snappedPosition() - QPointF(0, unzoomedSize.height()));
494     decoration.lineTo(snappedPosition() + QPointF(0, unzoomedSize.height()));
495     return decoration;
496 }
497 
BoundingBoxSnapStrategy()498 BoundingBoxSnapStrategy::BoundingBoxSnapStrategy()
499     : KoSnapStrategy(KoSnapGuide::BoundingBoxSnapping)
500 {
501 }
502 
snap(const QPointF & mousePosition,KoSnapProxy * proxy,qreal maxSnapDistance)503 bool BoundingBoxSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy *proxy, qreal maxSnapDistance)
504 {
505     Q_ASSERT(std::isfinite(maxSnapDistance));
506     const qreal maxDistance = maxSnapDistance * maxSnapDistance;
507     qreal minDistance = HUGE_VAL;
508 
509     QRectF rect(-maxSnapDistance, -maxSnapDistance, maxSnapDistance, maxSnapDistance);
510 
511     rect.moveCenter(mousePosition);
512     QPointF snappedPoint = mousePosition;
513 
514     KoFlake::AnchorPosition pointId[5] = {
515         KoFlake::TopLeft,
516         KoFlake::TopRight,
517         KoFlake::BottomRight,
518         KoFlake::BottomLeft,
519         KoFlake::Center
520     };
521 
522     QList<KoShape*> shapes = proxy->shapesInRect(rect, true);
523     Q_FOREACH (KoShape * shape, shapes) {
524         qreal shapeMinDistance = HUGE_VAL;
525         // first check the corner and center points
526         for (int i = 0; i < 5; ++i) {
527             m_boxPoints[i] = shape->absolutePosition(pointId[i]);
528             qreal d = squareDistance(mousePosition, m_boxPoints[i]);
529             if (d < minDistance && d < maxDistance) {
530                 shapeMinDistance = d;
531                 minDistance = d;
532                 snappedPoint = m_boxPoints[i];
533             }
534         }
535         // prioritize points over edges
536         if (shapeMinDistance < maxDistance)
537             continue;
538 
539         // now check distances to edges of bounding box
540         for (int i = 0; i < 4; ++i) {
541             QPointF pointOnLine;
542             qreal d = squareDistanceToLine(m_boxPoints[i], m_boxPoints[(i+1)%4], mousePosition, pointOnLine);
543             if (d < minDistance && d < maxDistance) {
544                 minDistance = d;
545                 snappedPoint = pointOnLine;
546             }
547         }
548     }
549     setSnappedPosition(snappedPoint);
550 
551     return (minDistance < maxDistance);
552 }
553 
squareDistanceToLine(const QPointF & lineA,const QPointF & lineB,const QPointF & point,QPointF & pointOnLine)554 qreal BoundingBoxSnapStrategy::squareDistanceToLine(const QPointF &lineA, const QPointF &lineB, const QPointF &point, QPointF &pointOnLine)
555 {
556     QPointF diff = lineB - lineA;
557     if(lineA == lineB)
558         return HUGE_VAL;
559     const qreal diffLength = sqrt(diff.x() * diff.x() + diff.y() * diff.y());
560 
561     // project mouse position relative to start position on line
562     const qreal scalar = KoSnapStrategy::scalarProduct(point - lineA, diff / diffLength);
563 
564     if (scalar < 0.0 || scalar > diffLength)
565         return HUGE_VAL;
566     // calculate vector between relative mouse position and projected mouse position
567     pointOnLine = lineA + scalar / diffLength * diff;
568     QPointF distVec = pointOnLine - point;
569     return distVec.x()*distVec.x() + distVec.y()*distVec.y();
570 }
571 
decoration(const KoViewConverter & converter) const572 QPainterPath BoundingBoxSnapStrategy::decoration(const KoViewConverter &converter) const
573 {
574     QSizeF unzoomedSize = converter.viewToDocument(QSizeF(5, 5));
575 
576     QPainterPath decoration;
577     decoration.moveTo(snappedPosition() - QPointF(unzoomedSize.width(), unzoomedSize.height()));
578     decoration.lineTo(snappedPosition() + QPointF(unzoomedSize.width(), unzoomedSize.height()));
579     decoration.moveTo(snappedPosition() - QPointF(unzoomedSize.width(), -unzoomedSize.height()));
580     decoration.lineTo(snappedPosition() + QPointF(unzoomedSize.width(), -unzoomedSize.height()));
581 
582     return decoration;
583 }
584 
585 // KoGuidesData has been moved into Krita. Please port this class!
586 
587 // LineGuideSnapStrategy::LineGuideSnapStrategy()
588 //     : KoSnapStrategy(KoSnapGuide::GuideLineSnapping)
589 // {
590 // }
591 
592 // bool LineGuideSnapStrategy::snap(const QPointF &mousePosition, KoSnapProxy * proxy, qreal maxSnapDistance)
593 // {
594 //     Q_ASSERT(std::isfinite(maxSnapDistance));
595 
596 //     KoGuidesData * guidesData = proxy->canvas()->guidesData();
597 
598 //     if (!guidesData || !guidesData->showGuideLines())
599 //         return false;
600 
601 //     QPointF snappedPoint = mousePosition;
602 //     m_orientation = 0;
603 
604 //     qreal minHorzDistance = maxSnapDistance;
605 //     Q_FOREACH (qreal guidePos, guidesData->horizontalGuideLines()) {
606 //         qreal distance = qAbs(guidePos - mousePosition.y());
607 //         if (distance < minHorzDistance) {
608 //             snappedPoint.setY(guidePos);
609 //             minHorzDistance = distance;
610 //             m_orientation |= Qt::Horizontal;
611 //         }
612 //     }
613 //     qreal minVertSnapDistance = maxSnapDistance;
614 //     Q_FOREACH (qreal guidePos, guidesData->verticalGuideLines()) {
615 //         qreal distance = qAbs(guidePos - mousePosition.x());
616 //         if (distance < minVertSnapDistance) {
617 //             snappedPoint.setX(guidePos);
618 //             minVertSnapDistance = distance;
619 //             m_orientation |= Qt::Vertical;
620 //         }
621 //     }
622 //     setSnappedPosition(snappedPoint);
623 //     return (minHorzDistance < maxSnapDistance || minVertSnapDistance < maxSnapDistance);
624 // }
625 
626 // QPainterPath LineGuideSnapStrategy::decoration(const KoViewConverter &converter) const
627 // {
628 //     QSizeF unzoomedSize = converter.viewToDocument(QSizeF(5, 5));
629 //     Q_ASSERT(unzoomedSize.isValid());
630 
631 //     QPainterPath decoration;
632 //     if (m_orientation & Qt::Horizontal) {
633 //         decoration.moveTo(snappedPosition() - QPointF(unzoomedSize.width(), 0));
634 //         decoration.lineTo(snappedPosition() + QPointF(unzoomedSize.width(), 0));
635 //     }
636 //     if (m_orientation & Qt::Vertical) {
637 //         decoration.moveTo(snappedPosition() - QPointF(0, unzoomedSize.height()));
638 //         decoration.lineTo(snappedPosition() + QPointF(0, unzoomedSize.height()));
639 //     }
640 
641 //     return decoration;
642 // }
643