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 }