463{
464
465
466
467
468
469 m_d->isTransforming =
true;
470 const QPointF anchorPoint =
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset();
471
472 switch (
m_d->function) {
474 QPointF diff = mousePos -
m_d->clickPos;
475
476 if (shiftModifierActive) {
477
479 QTransform t = m.S * m.projectedP;
480 QPointF originalDiff = t.inverted().map(diff);
481
482 if (qAbs(originalDiff.x()) >= qAbs(originalDiff.y())) {
483 originalDiff.setY(0);
484 } else {
485 originalDiff.setX(0);
486 }
487
488 diff = t.map(originalDiff);
489
490 }
491
492 m_d->currentArgs.setTransformedCenter(
m_d->clickArgs.transformedCenter() + diff);
493
494 break;
495 }
496 case ROTATEBOUNDS:
497 {
499 const QTransform clickT = clickM.finalTransform();
500
501 const QPointF rotationCenter =
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset();
502 const QPointF clickMouseImagePos = clickT.inverted().map(
m_d->clickPos) - rotationCenter;
503 const QPointF mouseImagePosClickSpace = clickT.inverted().map(mousePos) - rotationCenter;
504
505 const qreal a1 =
atan2(clickMouseImagePos.y(), clickMouseImagePos.x());
506 const qreal a2 =
atan2(mouseImagePosClickSpace.y(), mouseImagePosClickSpace.x());
507
508 const qreal theta = a2 - a1;
509 m_d->currentArgs.setBoundsRotation(
m_d->clickArgs.boundsRotation() + theta);
510
511
512 QTransform newBR; newBR.rotateRadians(
m_d->currentArgs.boundsRotation());
513 QTransform clickZ; clickZ.rotateRadians(
m_d->clickArgs.aZ());
514
515
516 QTransform desired = newBR * clickM.BRI * clickM.SC * clickM.S * clickZ;
518 if (dm.isValid()) {
519 m_d->currentArgs.setScaleX(dm.scaleX);
520 m_d->currentArgs.setScaleY(dm.scaleY);
521 m_d->currentArgs.setShearX(dm.shearXY);
522 m_d->currentArgs.setShearY(0);
524 }
525
526
527
528
529
530
531
532
533 }
534 break;
535 case ROTATE:
536 {
538 const QTransform clickT = clickM.finalTransform();
539
540 const QPointF rotationCenter =
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset();
541 const QPointF clickMouseImagePos = clickT.inverted().map(
m_d->clickPos) - rotationCenter;
542 const QPointF mouseImagePosClickSpace = clickT.inverted().map(mousePos) - rotationCenter;
543
544 const qreal a1 =
atan2(clickMouseImagePos.y(), clickMouseImagePos.x());
545 const qreal a2 =
atan2(mouseImagePosClickSpace.y(), mouseImagePosClickSpace.x());
546
552
553
554 if (shiftModifierActive) {
555 const qreal snapAngle = M_PI_4 / 6.0;
556 qint32 thetaIndex = static_cast<qint32>((theta / snapAngle) + 0.5);
557 m_d->currentArgs.setAZ(thetaIndex * snapAngle);
558 }
559 else {
560 const qreal clickAngle =
m_d->clickArgs.aZ();
561 const qreal targetAngle =
m_d->clickArgs.aZ() + theta;
563 const bool clockwise = (theta <= M_PI && theta >= 0) || (theta < -
M_PI);
564 shortestDistance = clockwise ? shortestDistance : shortestDistance * -1;
565
566 m_d->currentArgs.setAZ(
m_d->clickArgs.aZ() + shortestDistance);
567 }
568
570 QTransform t = m.finalTransform();
571 QPointF newRotationCenter = t.map(
m_d->currentArgs.originalCenter() +
m_d->currentArgs.rotationCenterOffset());
572 QPointF oldRotationCenter = clickT.map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
573
574 m_d->currentArgs.setTransformedCenter(
m_d->currentArgs.transformedCenter() + oldRotationCenter - newRotationCenter);
575 }
576 break;
577 case PERSPECTIVE:
578 {
579 if (!hasOriginalModifiersSet) {
580 m_d->function = CAMERAHEIGHT;
581 m_d->clickArgs =
m_d->currentArgs;
582 m_d->clickPos = mousePos;
583 break;
584 }
585
586 QPointF diff = mousePos -
m_d->clickPos;
587 double thetaX = - diff.y() *
M_PI /
m_d->transaction.originalHalfHeight() / 2 / fabs(
m_d->currentArgs.scaleY());
589
590 qreal
sign = qAbs(
m_d->currentArgs.aX() -
M_PI) <
M_PI / 2 ? -1.0 : 1.0;
591 double thetaY =
sign * diff.x() *
M_PI /
m_d->transaction.originalHalfWidth() / 2 / fabs(
m_d->currentArgs.scaleX());
593
595 QTransform t = m.finalTransform();
596 QPointF newRotationCenter = t.map(
m_d->currentArgs.originalCenter() +
m_d->currentArgs.rotationCenterOffset());
597
599 QTransform clickT = clickM.finalTransform();
600 QPointF oldRotationCenter = clickT.map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
601
602 m_d->currentArgs.setTransformedCenter(
m_d->currentArgs.transformedCenter() + oldRotationCenter - newRotationCenter);
603 }
604 break;
605 case CAMERAHEIGHT: {
606 if (hasOriginalModifiersSet) {
607 m_d->function = PERSPECTIVE;
608 m_d->clickPos = mousePos;
609 break;
610 }
611
612 qreal dy = mousePos.y() -
m_d->clickPos.y();
613 m_d->clickPos = mousePos;
614
615
616 qreal currentHeight =
m_d->currentArgs.cameraPos().z();
617 qreal currentHeightScaled = (currentHeight - 10150.0) / 9850.0;
618 qreal heightDelta = -(currentHeightScaled * currentHeightScaled) + 1.001;
619
620 const qreal sensitivityFactor = 0.02;
621 qreal scaledDy = dy / sensitivityFactor;
622
623
624 heightDelta = heightDelta * scaledDy;
625 qreal newHeight = currentHeight + heightDelta;
626
627 newHeight = qMax(300.0, qMin(20000.0, newHeight));
628 m_d->currentArgs.setCameraPos(QVector3D(0, 0, newHeight));
629 }
630 break;
631 case TOPSCALE:
632 case BOTTOMSCALE: {
633 QPointF staticPoint;
634 QPointF movingPoint;
635
636 if (
m_d->function == TOPSCALE) {
637 staticPoint =
m_d->boundsTransform.map(bottomMiddle(
m_d->bounds));
638 movingPoint =
m_d->boundsTransform.map(topMiddle(
m_d->bounds));
639 } else {
640 staticPoint =
m_d->boundsTransform.map(topMiddle(
m_d->bounds));
641 movingPoint =
m_d->boundsTransform.map(bottomMiddle(
m_d->bounds));
642 }
643
644 QPointF staticPointInView =
m_d->clickTransform.map(staticPoint);
645 const QPointF movingPointInView =
m_d->clickTransform.map(movingPoint);
646
647 const QPointF projNormVector =
649
650 const qreal projLength =
652
653 const QPointF targetMovingPointInView = staticPointInView + projNormVector * projLength;
654
655
656 if ((
m_d->clickArgs.transformAroundRotationCenter() ^ altModifierActive) &&
657 !qFuzzyCompare(anchorPoint.y(), movingPoint.y())) {
658
659 staticPoint = anchorPoint;
660 staticPointInView =
m_d->clickTransform.map(staticPoint);
661 }
662
665 staticPoint,
666 staticPointInView,
667 movingPoint,
668 targetMovingPointInView);
669
671 break;
672 }
673
674 if (shiftModifierActive ||
m_d->currentArgs.keepAspectRatio()) {
675 qreal aspectRatio =
m_d->clickArgs.scaleX() /
m_d->clickArgs.scaleY();
676 m_d->currentArgs.setScaleX(aspectRatio * result.
scale);
677 }
678
679 m_d->currentArgs.setScaleY(result.
scale);
681 break;
682 }
683
684 case LEFTSCALE:
685 case RIGHTSCALE: {
686 QPointF staticPoint;
687 QPointF movingPoint;
688
689 if (
m_d->function == LEFTSCALE) {
690 staticPoint =
m_d->boundsTransform.map(middleRight(
m_d->bounds));
691 movingPoint =
m_d->boundsTransform.map(middleLeft(
m_d->bounds));
692 } else {
693 staticPoint =
m_d->boundsTransform.map(middleLeft(
m_d->bounds));
694 movingPoint =
m_d->boundsTransform.map(middleRight(
m_d->bounds));
695 }
696
697 QPointF staticPointInView =
m_d->clickTransform.map(staticPoint);
698 const QPointF movingPointInView =
m_d->clickTransform.map(movingPoint);
699
700 const QPointF projNormVector =
702
703 const qreal projLength =
705
706 const QPointF targetMovingPointInView = staticPointInView + projNormVector * projLength;
707
708
709 if ((
m_d->currentArgs.transformAroundRotationCenter() ^ altModifierActive) &&
710 !qFuzzyCompare(anchorPoint.x(), movingPoint.x())) {
711
712 staticPoint = anchorPoint;
713 staticPointInView =
m_d->clickTransform.map(staticPoint);
714 }
715
718 staticPoint,
719 staticPointInView,
720 movingPoint,
721 targetMovingPointInView);
722
724 break;
725 }
726
727 if (shiftModifierActive ||
m_d->currentArgs.keepAspectRatio()) {
728 qreal aspectRatio =
m_d->clickArgs.scaleY() /
m_d->clickArgs.scaleX();
729 m_d->currentArgs.setScaleY(aspectRatio * result.
scale);
730 }
731
732 m_d->currentArgs.setScaleX(result.
scale);
734 break;
735 }
736 case TOPRIGHTSCALE:
737 case BOTTOMRIGHTSCALE:
738 case TOPLEFTSCALE:
739 case BOTTOMLEFTSCALE: {
740 QPointF staticPoint;
741 QPointF movingPoint;
742
743 if (
m_d->function == TOPRIGHTSCALE) {
744 staticPoint =
m_d->boundsTransform.map(
m_d->bounds.bottomLeft());
745 movingPoint =
m_d->boundsTransform.map(
m_d->bounds.topRight());
746 }
else if (
m_d->function == BOTTOMRIGHTSCALE) {
747 staticPoint =
m_d->boundsTransform.map(
m_d->bounds.topLeft());
748 movingPoint =
m_d->boundsTransform.map(
m_d->bounds.bottomRight());
749 }
else if (
m_d->function == TOPLEFTSCALE) {
750 staticPoint =
m_d->boundsTransform.map(
m_d->bounds.bottomRight());
751 movingPoint =
m_d->boundsTransform.map(
m_d->bounds.topLeft());
752 } else {
753 staticPoint =
m_d->boundsTransform.map(
m_d->bounds.topRight());
754 movingPoint =
m_d->boundsTransform.map(
m_d->bounds.bottomLeft());
755 }
756
757
758 if ((
m_d->currentArgs.transformAroundRotationCenter() ^ altModifierActive) &&
759 !(qFuzzyCompare(anchorPoint.x(), movingPoint.x()) ||
760 qFuzzyCompare(anchorPoint.y(), movingPoint.y()))) {
761
762 staticPoint = anchorPoint;
763 }
764
765 QPointF staticPointInView =
m_d->clickTransform.map(staticPoint);
766 QPointF movingPointInView = mousePos;
767
768 if (shiftModifierActive ||
m_d->currentArgs.keepAspectRatio()) {
769 QPointF refDiff =
m_d->clickTransform.map(movingPoint) - staticPointInView;
770 QPointF realDiff = mousePos - staticPointInView;
772
773 movingPointInView = staticPointInView + realDiff;
774 }
775
776 const bool isAffine =
779
781 !isAffine ?
783 staticPoint,
784 staticPointInView,
785 movingPoint,
786 movingPointInView) :
788 staticPoint,
789 staticPointInView,
790 movingPoint,
791 movingPointInView);
792
794 m_d->currentArgs.setScaleX(result.
scaleX);
795 m_d->currentArgs.setScaleY(result.
scaleY);
797 }
798
799 break;
800 }
801 case MOVECENTER: {
802
803 QPointF pt;
804 if (altModifierActive) {
805 pt = (
m_d->boundsTransform *
m_d->transform).inverted().map(mousePos);
807 pt =
m_d->boundsTransform.map(pt);
808 } else {
809 pt =
m_d->transform.inverted().map(mousePos);
810 }
811
812 QPointF newRotationCenterOffset = pt -
m_d->currentArgs.originalCenter();
813
814 if (shiftModifierActive) {
815 if (qAbs(newRotationCenterOffset.x()) > qAbs(newRotationCenterOffset.y())) {
816 newRotationCenterOffset.ry() = 0;
817 } else {
818 newRotationCenterOffset.rx() = 0;
819 }
820 }
821
822 m_d->currentArgs.setRotationCenterOffset(newRotationCenterOffset);
824 }
825 break;
826 case TOPSHEAR:
827 case BOTTOMSHEAR: {
829
830 QPointF oldStaticPoint = m.finalTransform().map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
831
832 QTransform backwardT = (m.S * m.projectedP).inverted();
833 QPointF diff = backwardT.map(mousePos -
m_d->clickPos);
834
835 qreal
sign =
m_d->function == BOTTOMSHEAR ? 1.0 : -1.0;
836
837
838 qreal dx =
sign *
m_d->clickArgs.shearX() *
m_d->clickArgs.scaleY() * (
m_d->bounds.height() / 2.0);
839 dx += diff.x();
840
841
842 m_d->currentArgs.setShearX(
sign * dx /
m_d->currentArgs.scaleY() / (
m_d->bounds.height() / 2.0));
843
845 QTransform t = currentM.finalTransform();
846 QPointF newStaticPoint = t.map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
847 m_d->currentArgs.setTransformedCenter(
m_d->currentArgs.transformedCenter() + oldStaticPoint - newStaticPoint);
848 break;
849 }
850
851 case LEFTSHEAR:
852 case RIGHTSHEAR: {
854
855 QPointF oldStaticPoint = m.finalTransform().map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
856
857 QTransform backwardT = (m.S * m.projectedP).inverted();
858 QPointF diff = backwardT.map(mousePos -
m_d->clickPos);
859
860 qreal
sign =
m_d->function == RIGHTSHEAR ? 1.0 : -1.0;
861
862
863 qreal dy =
sign *
m_d->clickArgs.shearY() *
m_d->clickArgs.scaleX() * (
m_d->bounds.width() / 2.0);
864 dy += diff.y();
865
866
867 m_d->currentArgs.setShearY(
sign * dy /
m_d->clickArgs.scaleX() / (
m_d->bounds.width() / 2.0));
868
870 QTransform t = currentM.finalTransform();
871 QPointF newStaticPoint = t.map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
872 m_d->currentArgs.setTransformedCenter(
m_d->currentArgs.transformedCenter() + oldStaticPoint - newStaticPoint);
873 break;
874 }
875 }
876
877 m_d->recalculateTransformations();
878}
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