452{
453
454
455
456 m_d->isTransforming =
true;
457 const QPointF anchorPoint =
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset();
458
459 switch (
m_d->function) {
461 QPointF diff = mousePos -
m_d->clickPos;
462
463 if (shiftModifierActive) {
464
466 QTransform t = m.S * m.projectedP;
467 QPointF originalDiff = t.inverted().map(diff);
468
469 if (qAbs(originalDiff.x()) >= qAbs(originalDiff.y())) {
470 originalDiff.setY(0);
471 } else {
472 originalDiff.setX(0);
473 }
474
475 diff = t.map(originalDiff);
476
477 }
478
479 m_d->currentArgs.setTransformedCenter(
m_d->clickArgs.transformedCenter() + diff);
480
481 break;
482 }
483 case ROTATEBOUNDS:
484 {
486 const QTransform clickT = clickM.finalTransform();
487
488 const QPointF rotationCenter =
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset();
489 const QPointF clickMouseImagePos = clickT.inverted().map(
m_d->clickPos) - rotationCenter;
490 const QPointF mouseImagePosClickSpace = clickT.inverted().map(mousePos) - rotationCenter;
491
492 const qreal a1 =
atan2(clickMouseImagePos.y(), clickMouseImagePos.x());
493 const qreal a2 =
atan2(mouseImagePosClickSpace.y(), mouseImagePosClickSpace.x());
494
495 const qreal theta = a2 - a1;
496 m_d->currentArgs.setBoundsRotation(
m_d->clickArgs.boundsRotation() + theta);
497
498
499 QTransform newBR; newBR.rotateRadians(
m_d->currentArgs.boundsRotation());
500 QTransform clickZ; clickZ.rotateRadians(
m_d->clickArgs.aZ());
501
502
503 QTransform desired = newBR * clickM.BRI * clickM.SC * clickM.S * clickZ;
505 if (dm.isValid()) {
506 m_d->currentArgs.setScaleX(dm.scaleX);
507 m_d->currentArgs.setScaleY(dm.scaleY);
508 m_d->currentArgs.setShearX(dm.shearXY);
509 m_d->currentArgs.setShearY(0);
511 }
512
513
514
515
516
517
518
519
520 }
521 break;
522 case ROTATE:
523 {
525 const QTransform clickT = clickM.finalTransform();
526
527 const QPointF rotationCenter =
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset();
528 const QPointF clickMouseImagePos = clickT.inverted().map(
m_d->clickPos) - rotationCenter;
529 const QPointF mouseImagePosClickSpace = clickT.inverted().map(mousePos) - rotationCenter;
530
531 const qreal a1 =
atan2(clickMouseImagePos.y(), clickMouseImagePos.x());
532 const qreal a2 =
atan2(mouseImagePosClickSpace.y(), mouseImagePosClickSpace.x());
533
539
540
541 if (shiftModifierActive) {
542 const qreal snapAngle = M_PI_4 / 6.0;
543 qint32 thetaIndex = static_cast<qint32>((theta / snapAngle) + 0.5);
544 m_d->currentArgs.setAZ(thetaIndex * snapAngle);
545 }
546 else {
547 const qreal clickAngle =
m_d->clickArgs.aZ();
548 const qreal targetAngle =
m_d->clickArgs.aZ() + theta;
550 const bool clockwise = (theta <= M_PI && theta >= 0) || (theta < -
M_PI);
551 shortestDistance = clockwise ? shortestDistance : shortestDistance * -1;
552
553 m_d->currentArgs.setAZ(
m_d->clickArgs.aZ() + shortestDistance);
554 }
555
557 QTransform t = m.finalTransform();
558 QPointF newRotationCenter = t.map(
m_d->currentArgs.originalCenter() +
m_d->currentArgs.rotationCenterOffset());
559 QPointF oldRotationCenter = clickT.map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
560
561 m_d->currentArgs.setTransformedCenter(
m_d->currentArgs.transformedCenter() + oldRotationCenter - newRotationCenter);
562 }
563 break;
564 case PERSPECTIVE:
565 {
566 QPointF diff = mousePos -
m_d->clickPos;
567 double thetaX = - diff.y() *
M_PI /
m_d->transaction.originalHalfHeight() / 2 / fabs(
m_d->currentArgs.scaleY());
569
570 qreal
sign = qAbs(
m_d->currentArgs.aX() -
M_PI) <
M_PI / 2 ? -1.0 : 1.0;
571 double thetaY =
sign * diff.x() *
M_PI /
m_d->transaction.originalHalfWidth() / 2 / fabs(
m_d->currentArgs.scaleX());
573
575 QTransform t = m.finalTransform();
576 QPointF newRotationCenter = t.map(
m_d->currentArgs.originalCenter() +
m_d->currentArgs.rotationCenterOffset());
577
579 QTransform clickT = clickM.finalTransform();
580 QPointF oldRotationCenter = clickT.map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
581
582 m_d->currentArgs.setTransformedCenter(
m_d->currentArgs.transformedCenter() + oldRotationCenter - newRotationCenter);
583 }
584 break;
585 case TOPSCALE:
586 case BOTTOMSCALE: {
587 QPointF staticPoint;
588 QPointF movingPoint;
589
590 if (
m_d->function == TOPSCALE) {
591 staticPoint =
m_d->boundsTransform.map(bottomMiddle(
m_d->bounds));
592 movingPoint =
m_d->boundsTransform.map(topMiddle(
m_d->bounds));
593 } else {
594 staticPoint =
m_d->boundsTransform.map(topMiddle(
m_d->bounds));
595 movingPoint =
m_d->boundsTransform.map(bottomMiddle(
m_d->bounds));
596 }
597
598 QPointF staticPointInView =
m_d->clickTransform.map(staticPoint);
599 const QPointF movingPointInView =
m_d->clickTransform.map(movingPoint);
600
601 const QPointF projNormVector =
603
604 const qreal projLength =
606
607 const QPointF targetMovingPointInView = staticPointInView + projNormVector * projLength;
608
609
610 if ((
m_d->clickArgs.transformAroundRotationCenter() ^ altModifierActive) &&
611 !qFuzzyCompare(anchorPoint.y(), movingPoint.y())) {
612
613 staticPoint = anchorPoint;
614 staticPointInView =
m_d->clickTransform.map(staticPoint);
615 }
616
619 staticPoint,
620 staticPointInView,
621 movingPoint,
622 targetMovingPointInView);
623
625 break;
626 }
627
628 if (shiftModifierActive ||
m_d->currentArgs.keepAspectRatio()) {
629 qreal aspectRatio =
m_d->clickArgs.scaleX() /
m_d->clickArgs.scaleY();
630 m_d->currentArgs.setScaleX(aspectRatio * result.
scale);
631 }
632
633 m_d->currentArgs.setScaleY(result.
scale);
635 break;
636 }
637
638 case LEFTSCALE:
639 case RIGHTSCALE: {
640 QPointF staticPoint;
641 QPointF movingPoint;
642
643 if (
m_d->function == LEFTSCALE) {
644 staticPoint =
m_d->boundsTransform.map(middleRight(
m_d->bounds));
645 movingPoint =
m_d->boundsTransform.map(middleLeft(
m_d->bounds));
646 } else {
647 staticPoint =
m_d->boundsTransform.map(middleLeft(
m_d->bounds));
648 movingPoint =
m_d->boundsTransform.map(middleRight(
m_d->bounds));
649 }
650
651 QPointF staticPointInView =
m_d->clickTransform.map(staticPoint);
652 const QPointF movingPointInView =
m_d->clickTransform.map(movingPoint);
653
654 const QPointF projNormVector =
656
657 const qreal projLength =
659
660 const QPointF targetMovingPointInView = staticPointInView + projNormVector * projLength;
661
662
663 if ((
m_d->currentArgs.transformAroundRotationCenter() ^ altModifierActive) &&
664 !qFuzzyCompare(anchorPoint.x(), movingPoint.x())) {
665
666 staticPoint = anchorPoint;
667 staticPointInView =
m_d->clickTransform.map(staticPoint);
668 }
669
672 staticPoint,
673 staticPointInView,
674 movingPoint,
675 targetMovingPointInView);
676
678 break;
679 }
680
681 if (shiftModifierActive ||
m_d->currentArgs.keepAspectRatio()) {
682 qreal aspectRatio =
m_d->clickArgs.scaleY() /
m_d->clickArgs.scaleX();
683 m_d->currentArgs.setScaleY(aspectRatio * result.
scale);
684 }
685
686 m_d->currentArgs.setScaleX(result.
scale);
688 break;
689 }
690 case TOPRIGHTSCALE:
691 case BOTTOMRIGHTSCALE:
692 case TOPLEFTSCALE:
693 case BOTTOMLEFTSCALE: {
694 QPointF staticPoint;
695 QPointF movingPoint;
696
697 if (
m_d->function == TOPRIGHTSCALE) {
698 staticPoint =
m_d->boundsTransform.map(
m_d->bounds.bottomLeft());
699 movingPoint =
m_d->boundsTransform.map(
m_d->bounds.topRight());
700 }
else if (
m_d->function == BOTTOMRIGHTSCALE) {
701 staticPoint =
m_d->boundsTransform.map(
m_d->bounds.topLeft());
702 movingPoint =
m_d->boundsTransform.map(
m_d->bounds.bottomRight());
703 }
else if (
m_d->function == TOPLEFTSCALE) {
704 staticPoint =
m_d->boundsTransform.map(
m_d->bounds.bottomRight());
705 movingPoint =
m_d->boundsTransform.map(
m_d->bounds.topLeft());
706 } else {
707 staticPoint =
m_d->boundsTransform.map(
m_d->bounds.topRight());
708 movingPoint =
m_d->boundsTransform.map(
m_d->bounds.bottomLeft());
709 }
710
711
712 if ((
m_d->currentArgs.transformAroundRotationCenter() ^ altModifierActive) &&
713 !(qFuzzyCompare(anchorPoint.x(), movingPoint.x()) ||
714 qFuzzyCompare(anchorPoint.y(), movingPoint.y()))) {
715
716 staticPoint = anchorPoint;
717 }
718
719 QPointF staticPointInView =
m_d->clickTransform.map(staticPoint);
720 QPointF movingPointInView = mousePos;
721
722 if (shiftModifierActive ||
m_d->currentArgs.keepAspectRatio()) {
723 QPointF refDiff =
m_d->clickTransform.map(movingPoint) - staticPointInView;
724 QPointF realDiff = mousePos - staticPointInView;
726
727 movingPointInView = staticPointInView + realDiff;
728 }
729
730 const bool isAffine =
733
735 !isAffine ?
737 staticPoint,
738 staticPointInView,
739 movingPoint,
740 movingPointInView) :
742 staticPoint,
743 staticPointInView,
744 movingPoint,
745 movingPointInView);
746
748 m_d->currentArgs.setScaleX(result.
scaleX);
749 m_d->currentArgs.setScaleY(result.
scaleY);
751 }
752
753 break;
754 }
755 case MOVECENTER: {
756
757 QPointF pt;
758 if (altModifierActive) {
759 pt = (
m_d->boundsTransform *
m_d->transform).inverted().map(mousePos);
761 pt =
m_d->boundsTransform.map(pt);
762 } else {
763 pt =
m_d->transform.inverted().map(mousePos);
764 }
765
766 QPointF newRotationCenterOffset = pt -
m_d->currentArgs.originalCenter();
767
768 if (shiftModifierActive) {
769 if (qAbs(newRotationCenterOffset.x()) > qAbs(newRotationCenterOffset.y())) {
770 newRotationCenterOffset.ry() = 0;
771 } else {
772 newRotationCenterOffset.rx() = 0;
773 }
774 }
775
776 m_d->currentArgs.setRotationCenterOffset(newRotationCenterOffset);
778 }
779 break;
780 case TOPSHEAR:
781 case BOTTOMSHEAR: {
783
784 QPointF oldStaticPoint = m.finalTransform().map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
785
786 QTransform backwardT = (m.S * m.projectedP).inverted();
787 QPointF diff = backwardT.map(mousePos -
m_d->clickPos);
788
789 qreal
sign =
m_d->function == BOTTOMSHEAR ? 1.0 : -1.0;
790
791
792 qreal dx =
sign *
m_d->clickArgs.shearX() *
m_d->clickArgs.scaleY() * (
m_d->bounds.height() / 2.0);
793 dx += diff.x();
794
795
796 m_d->currentArgs.setShearX(
sign * dx /
m_d->currentArgs.scaleY() / (
m_d->bounds.height() / 2.0));
797
799 QTransform t = currentM.finalTransform();
800 QPointF newStaticPoint = t.map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
801 m_d->currentArgs.setTransformedCenter(
m_d->currentArgs.transformedCenter() + oldStaticPoint - newStaticPoint);
802 break;
803 }
804
805 case LEFTSHEAR:
806 case RIGHTSHEAR: {
808
809 QPointF oldStaticPoint = m.finalTransform().map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
810
811 QTransform backwardT = (m.S * m.projectedP).inverted();
812 QPointF diff = backwardT.map(mousePos -
m_d->clickPos);
813
814 qreal
sign =
m_d->function == RIGHTSHEAR ? 1.0 : -1.0;
815
816
817 qreal dy =
sign *
m_d->clickArgs.shearY() *
m_d->clickArgs.scaleX() * (
m_d->bounds.width() / 2.0);
818 dy += diff.y();
819
820
821 m_d->currentArgs.setShearY(
sign * dy /
m_d->clickArgs.scaleX() / (
m_d->bounds.width() / 2.0));
822
824 QTransform t = currentM.finalTransform();
825 QPointF newStaticPoint = t.map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
826 m_d->currentArgs.setTransformedCenter(
m_d->currentArgs.transformedCenter() + oldStaticPoint - newStaticPoint);
827 break;
828 }
829 }
830
831 m_d->recalculateTransformations();
832}
static bool qFuzzyIsNull(half h)
qreal shortestAngularDistance(qreal a, qreal b)
T kisDegreesToRadians(T degrees)
std::enable_if< std::is_floating_point< T >::value, T >::type normalizeAngle(T a)
QPointF kisProjectOnVector(const QPointF &base, const QPointF &v)
ScaleResult2D calculateScale2DAffine(const ToolTransformArgs &args, const QPointF &staticPointSrc, const QPointF &staticPointDst, const QPointF &movingPointSrc, const QPointF &movingPointDst)
ScaleResult2D calculateScale2D(const ToolTransformArgs &args, const QPointF &staticPointSrc, const QPointF &staticPointDst, const QPointF &movingPointSrc, const QPointF &movingPointDst)
ScaleResult1D calculateScaleX(const ToolTransformArgs &args, const QPointF &staticPointSrc, const QPointF &staticPointDst, const QPointF &movingPointSrc, const QPointF &movingPointDst)
ScaleResult1D calculateScaleY(const ToolTransformArgs &args, const QPointF &staticPointSrc, const QPointF &staticPointDst, const QPointF &movingPointSrc, const QPointF &movingPointDst)
Point normalize(const Point &a)
PointTypeTraits< T >::value_type dotProduct(const T &a, const T &b)
KRITAIMAGE_EXPORT qreal atan2(qreal y, qreal x)
atan2 replacement
QPointF transformedCenter
QPointF transformedCenter