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