12#include <QPainterPath>
29 DRAG_X_VANISHING_POINT,
30 DRAG_Y_VANISHING_POINT,
122 m_d(new
Private(this, converter, currentArgs, transaction))
150 v = QVector4D(1, 0, 0, 0);
155 v = QVector4D(0, 1, 0, 0);
163 Q_UNUSED(perspectiveModifierActive);
164 Q_UNUSED(shiftModifierActive);
165 Q_UNUSED(altModifierActive);
167 QPolygonF transformedPolygon =
m_d->transform.map(QPolygonF(
m_d->transaction.originalRect()));
168 StrokeFunction defaultFunction = transformedPolygon.containsPoint(mousePos, Qt::OddEvenFill) ?
MOVE :
NONE;
170 handleChooser(mousePos, defaultFunction);
174 if (!
m_d->transformedHandles.xVanishing.isNull()) {
176 handleRadius, DRAG_X_VANISHING_POINT);
179 if (!
m_d->transformedHandles.yVanishing.isNull()) {
181 handleRadius, DRAG_Y_VANISHING_POINT);
184 m_d->currentDraggingHandlePoint = -1;
185 for (
int i = 0; i <
m_d->dstHandlePoints.size(); i++) {
187 handleRadius, DRAG_HANDLE)) {
189 m_d->currentDraggingHandlePoint = i;
200 switch (
m_d->function) {
208 case DRAG_X_VANISHING_POINT:
209 case DRAG_Y_VANISHING_POINT:
221 gc.setOpacity(
m_d->transaction.basePreviewOpacity());
222 gc.setTransform(
m_d->paintingTransform,
true);
228 QPainterPath handles;
230 handles.moveTo(
m_d->transaction.originalTopLeft());
231 handles.lineTo(
m_d->transaction.originalTopRight());
232 handles.lineTo(
m_d->transaction.originalBottomRight());
233 handles.lineTo(
m_d->transaction.originalBottomLeft());
234 handles.lineTo(
m_d->transaction.originalTopLeft());
237 auto addHandleRectFunc =
238 [&](
const QPointF &pt) {
241 m_d->handlesTransform,
242 m_d->transaction.originalRect(), pt)
246 addHandleRectFunc(
m_d->transaction.originalTopLeft());
247 addHandleRectFunc(
m_d->transaction.originalTopRight());
248 addHandleRectFunc(
m_d->transaction.originalBottomLeft());
249 addHandleRectFunc(
m_d->transaction.originalBottomRight());
250 addHandleRectFunc(
m_d->transaction.originalMiddleTop());
251 addHandleRectFunc(
m_d->transaction.originalMiddleBottom());
252 addHandleRectFunc(
m_d->transaction.originalMiddleLeft());
253 addHandleRectFunc(
m_d->transaction.originalMiddleRight());
257 if (
m_d->isTransforming) {
273 QPainterPath mappedHandles =
m_d->handlesTransform.map(handles);
277 pen[0].setCosmetic(
true);
279 pen[1].setCosmetic(
true);
280 pen[1].setColor(Qt::lightGray);
282 for (
int i = 1; i >= 0; --i) {
284 gc.drawPath(mappedHandles);
290 QPainterPath perspectiveHandles;
295 m_d->transaction.originalRect(), 0, 0);
297 if (
m_d->transformedHandles.xVanishingExists) {
298 QRectF rc = handleRect.translated(
m_d->transformedHandles.xVanishing);
299 perspectiveHandles.addEllipse(rc);
302 if (
m_d->transformedHandles.yVanishingExists) {
303 QRectF rc = handleRect.translated(
m_d->transformedHandles.yVanishing);
304 perspectiveHandles.addEllipse(rc);
307 if (!perspectiveHandles.isEmpty()) {
309 gc.setTransform(
m_d->converter->imageToWidgetTransform());
311 gc.setBrush(Qt::red);
313 for (
int i = 1; i >= 0; --i) {
315 gc.drawPath(perspectiveHandles);
325 m_d->recalculateTransformations();
332 if (
m_d->function ==
NONE)
return false;
335 m_d->clickArgs =
m_d->currentArgs;
345 A << sp[HANDLE_TOP_LEFT].x() , sp[HANDLE_TOP_RIGHT].x() , sp[HANDLE_BOTTOM_LEFT].x()
346 ,sp[HANDLE_TOP_LEFT].y() , sp[HANDLE_TOP_RIGHT].y() , sp[HANDLE_BOTTOM_LEFT].y()
349 v3 << sp[HANDLE_BOTTOM_RIGHT].x() , sp[HANDLE_BOTTOM_RIGHT].y() , 1;
351 Eigen::Vector3f coeffs =
A.colPivHouseholderQr().solve(v3);
353 A.col(0) *= coeffs(0);
354 A.col(1) *= coeffs(1);
355 A.col(2) *= coeffs(2);
362 return QTransform(m(0,0), m(1,0), m(2,0),
363 m(0,1), m(1,1), m(2,1),
364 m(0,2), m(1,2), m(2,2));
371 m << t.m11() , t.m21() , t.m31()
372 ,t.m12() , t.m22() , t.m32()
373 ,t.m13() , t.m23() , t.m33();
413 Eigen::Matrix3f TS =
fromTranslate(-currentArgs.originalCenter());
415 Eigen::Matrix3f m = t * TS.inverse();
417 qreal tX = m(0,2) / m(2,2);
418 qreal tY = m(1,2) / m(2,2);
442 currentArgs.setScaleX(dm.
scaleX);
443 currentArgs.setScaleY(dm.
scaleY);
445 currentArgs.setShearX(dm.
shearXY);
446 currentArgs.setShearY(0.0);
453 currentArgs.setScaleX(1.0);
454 currentArgs.setScaleY(1.0);
455 currentArgs.setShearX(0.0);
456 currentArgs.setShearY(0.0);
457 currentArgs.setAZ(0.0);
460 currentArgs.setTransformedCenter(QPointF(tX, tY));
461 currentArgs.setFlattenedPerspectiveTransform(
toQTransform(m));
471 return QVector4D(pt.x(), pt.y(), 0, 1.0);
475 return v.toVector2DAffine().toPointF();
480 Q_UNUSED(shiftModifierActive);
481 Q_UNUSED(altModifierActive);
483 m_d->isTransforming =
true;
485 switch (
m_d->function) {
489 QPointF diff = mousePos -
m_d->clickPos;
490 m_d->currentArgs.setTransformedCenter(
491 m_d->clickArgs.transformedCenter() + diff);
497 if (
m_d->currentDraggingHandlePoint < HANDLE_MIDDLE_TOP) {
499 m_d->dstHandlePoints[
m_d->currentDraggingHandlePoint] = mousePos;
502 QPointF delta = mousePos -
m_d->dstHandlePoints[
m_d->currentDraggingHandlePoint];
503 switch(
m_d->currentDraggingHandlePoint) {
504 case HANDLE_MIDDLE_TOP:
505 m_d->dstHandlePoints[HANDLE_TOP_LEFT] += delta;
506 m_d->dstHandlePoints[HANDLE_TOP_RIGHT] += delta;
508 case HANDLE_MIDDLE_BOTTOM:
509 m_d->dstHandlePoints[HANDLE_BOTTOM_LEFT] += delta;
510 m_d->dstHandlePoints[HANDLE_BOTTOM_RIGHT] += delta;
512 case HANDLE_MIDDLE_LEFT:
513 m_d->dstHandlePoints[HANDLE_TOP_LEFT] += delta;
514 m_d->dstHandlePoints[HANDLE_BOTTOM_LEFT] += delta;
516 case HANDLE_MIDDLE_RIGHT:
517 m_d->dstHandlePoints[HANDLE_TOP_RIGHT] += delta;
518 m_d->dstHandlePoints[HANDLE_BOTTOM_RIGHT] += delta;
525 Eigen::Matrix3f result =
B *
A.inverse();
527 m_d->transformIntoArgs(result);
531 case DRAG_X_VANISHING_POINT:
532 case DRAG_Y_VANISHING_POINT: {
534 QMatrix4x4 m(
m_d->transform);
536 QPointF tl =
m_d->transaction.originalTopLeft();
537 QPointF tr =
m_d->transaction.originalTopRight();
538 QPointF bl =
m_d->transaction.originalBottomLeft();
539 QPointF br =
m_d->transaction.originalBottomRight();
541 QVector4D
v(1,0,0,0);
542 QVector4D otherV(0,1,0,0);
544 if (
m_d->function == DRAG_X_VANISHING_POINT) {
545 v = QVector4D(1,0,0,0);
546 otherV = QVector4D(0,1,0,0);
548 v = QVector4D(0,1,0,0);
549 otherV = QVector4D(1,0,0,0);
557 QPointF otherV_dst =
toQPointF(m * otherV);
572 if (
m_d->function == DRAG_X_VANISHING_POINT) {
626 QLineF l0(far1_dst, mousePos);
627 QLineF l1(far2_dst, mousePos);
628 QLineF l2(otherV_dst, near1_dst);
629 l0.intersects(l2, &near1_dst);
630 l1.intersects(l2, &near2_dst);
632 srcPoints << far1_src;
633 srcPoints << far2_src;
634 srcPoints << near1_src;
635 srcPoints << near2_src;
637 dstPoints << far1_dst;
638 dstPoints << far2_dst;
639 dstPoints << near1_dst;
640 dstPoints << near2_dst;
644 Eigen::Matrix3f result =
B *
A.inverse();
646 m_d->transformIntoArgs(result);
651 m_d->recalculateTransformations();
656 bool shouldSave = !
m_d->imageTooBig;
657 m_d->isTransforming =
false;
659 if (
m_d->imageTooBig) {
660 m_d->currentArgs =
m_d->clickArgs;
661 m_d->recalculateTransformations();
669 transform = transformFromArgs();
671 QTransform viewScaleTransform = converter->imageToDocumentTransform() * converter->documentToFlakeTransform();
672 handlesTransform = transform * viewScaleTransform;
674 QTransform tl = QTransform::fromTranslate(transaction.originalTopLeft().x(), transaction.originalTopLeft().y());
675 paintingTransform = tl.inverted() * q->thumbToImageTransform() * tl * transform * viewScaleTransform;
676 paintingOffset = transaction.originalTopLeft();
679 const qreal maxScale = 20.0;
683 if (qAbs(currentArgs.scaleX()) > maxScale ||
684 qAbs(currentArgs.scaleY()) > maxScale) {
690 points << transaction.originalRect().topLeft();
691 points << transaction.originalRect().topRight();
692 points << transaction.originalRect().bottomRight();
693 points << transaction.originalRect().bottomLeft();
695 for (
int i = 0; i < points.size(); i++) {
696 points[i] = transform.map(points[i]);
699 for (
int i = 0; i < points.size(); i++) {
700 const QPointF &pt = points[i];
701 const QPointF &prev = points[(i - 1 + 4) % 4];
702 const QPointF &next = points[(i + 1) % 4];
703 const QPointF &other = points[(i + 2) % 4];
705 QLineF l1(pt, other);
706 QLineF l2(prev, next);
708 QPointF intersection;
709 l1.intersects(l2, &intersection);
720 const qreal thresholdDistance = 0.02 * l2.length();
730 recalculateTransformedHandles();
732 Q_EMIT q->requestShowImageTooBig(imageTooBig);
733 Q_EMIT q->requestImageRecalculation();
static QCursor moveCursor()
static QCursor arrowCursor()
static QCursor pointingHandCursor()
static bool qFuzzyCompare(half p1, half p2)
#define KIS_ASSERT_RECOVER_RETURN(cond)
T kisDegreesToRadians(T degrees)
qreal kisDistanceToLine(const QPointF &m, const QLineF &line)
qreal kisSquareDistance(const QPointF &pt1, const QPointF &pt2)
QPointF toQPointF(const ExpressionType &expr)
QTransform shearTransform() const
QTransform rotateTransform() const
QTransform scaleTransform() const