Krita Source Code Documentation
Loading...
Searching...
No Matches
KisFreeTransformStrategy Class Reference

#include <kis_free_transform_strategy.h>

+ Inheritance diagram for KisFreeTransformStrategy:

Classes

struct  Private
 

Signals

void requestCanvasUpdate ()
 
void requestConvexHullCalculation ()
 
void requestImageRecalculation ()
 
void requestResetRotationCenterButtons ()
 
void requestShowImageTooBig (bool value)
 

Public Member Functions

bool beginPrimaryAction (const QPointF &pt) override
 
virtual bool beginPrimaryAction (KoPointerEvent *event)=0
 
void continuePrimaryAction (const QPointF &pt, bool shiftModifierActive, bool altModifierActive) override
 
void continuePrimaryAction (const QPointF &pt, bool shiftModifierActive, bool altModifierActive, bool hasOriginalModifiersSet) override
 
virtual void continuePrimaryAction (KoPointerEvent *event)=0
 
bool endPrimaryAction () override
 
virtual bool endPrimaryAction (KoPointerEvent *event)=0
 
void externalConfigChanged () override
 
QCursor getCurrentCursor () const override
 
 KisFreeTransformStrategy (const KisCoordinatesConverter *converter, KoSnapGuide *snapGuide, ToolTransformArgs &currentArgs, TransformTransactionProperties &transaction)
 
void paint (QPainter &gc, const KoColorDisplayRendererInterface *displayRendererInterface) override
 
void setTransformFunction (const QPointF &mousePos, bool perspectiveModifierActive, bool shiftModifierActive, bool altModifierActive) override
 
bool shiftModifierIsUsed () const override
 
 ~KisFreeTransformStrategy () override
 
- Public Member Functions inherited from KisSimplifiedActionPolicyStrategy
void activateAlternateAction (KisTool::AlternateAction action) override
 
void activatePrimaryAction () override
 
bool beginAlternateAction (KoPointerEvent *event, KisTool::AlternateAction action) override
 
bool beginPrimaryAction (KoPointerEvent *event) override
 
void continueAlternateAction (KoPointerEvent *event, KisTool::AlternateAction action) override
 
void continuePrimaryAction (KoPointerEvent *event) override
 
void deactivateAlternateAction (KisTool::AlternateAction action) override
 
bool endAlternateAction (KoPointerEvent *event, KisTool::AlternateAction action) override
 
bool endPrimaryAction (KoPointerEvent *event) override
 
virtual QPointF handleSnapPoint (const QPointF &imagePos)
 
void hoverActionCommon (KoPointerEvent *event) override
 
 KisSimplifiedActionPolicyStrategy (const KisCoordinatesConverter *_converter, KoSnapGuide *snapGuide=0)
 
 ~KisSimplifiedActionPolicyStrategy () override
 
- Public Member Functions inherited from KisTransformStrategyBase
virtual bool acceptsClicks () const
 
virtual void deactivatePrimaryAction ()
 
virtual int decorationThickness () const
 
virtual void decreaseBrushSize (KoCanvasBase *canvas)
 
virtual QPainterPath getCursorOutline () const
 
virtual void increaseBrushSize (KoCanvasBase *canvas)
 
 KisTransformStrategyBase ()
 
QImage originalImage () const
 
virtual void setDecorationThickness (int thickness)
 
void setThumbnailImage (const QImage &image, QTransform thumbToImageTransform)
 
QTransform thumbToImageTransform () const
 
 ~KisTransformStrategyBase () override
 

Private Attributes

const QScopedPointer< Privatem_d
 

Additional Inherited Members

- Protected Member Functions inherited from KisSimplifiedActionPolicyStrategy
virtual void hoverActionCommon (const QPointF &pt)
 

Detailed Description

Definition at line 22 of file kis_free_transform_strategy.h.

Constructor & Destructor Documentation

◆ KisFreeTransformStrategy()

KisFreeTransformStrategy::KisFreeTransformStrategy ( const KisCoordinatesConverter * converter,
KoSnapGuide * snapGuide,
ToolTransformArgs & currentArgs,
TransformTransactionProperties & transaction )

Definition at line 139 of file kis_free_transform_strategy.cpp.

143 : KisSimplifiedActionPolicyStrategy(converter, snapGuide),
144 m_d(new Private(this, converter, currentArgs, transaction))
145{
146}
const QScopedPointer< Private > m_d
KisSimplifiedActionPolicyStrategy(const KisCoordinatesConverter *_converter, KoSnapGuide *snapGuide=0)

◆ ~KisFreeTransformStrategy()

KisFreeTransformStrategy::~KisFreeTransformStrategy ( )
override

Definition at line 148 of file kis_free_transform_strategy.cpp.

149{
150}

Member Function Documentation

◆ beginPrimaryAction() [1/2]

bool KisFreeTransformStrategy::beginPrimaryAction ( const QPointF & pt)
overridevirtual

Implements KisSimplifiedActionPolicyStrategy.

Definition at line 439 of file kis_free_transform_strategy.cpp.

440{
441 m_d->clickArgs = m_d->currentArgs;
442 m_d->clickPos = pt;
443
445 m_d->clickTransform = m.finalTransform();
446
447 if (m_d->function == ROTATEBOUNDS) {
449 }
450
451 return true;
452}

References KisTransformUtils::MatricesPack::finalTransform(), m_d, and requestConvexHullCalculation().

◆ beginPrimaryAction() [2/2]

virtual bool KisTransformStrategyBase::beginPrimaryAction ( KoPointerEvent * event)
virtual

◆ continuePrimaryAction() [1/3]

void KisFreeTransformStrategy::continuePrimaryAction ( const QPointF & pt,
bool shiftModifierActive,
bool altModifierActive )
overridevirtual

Implements KisSimplifiedActionPolicyStrategy.

Definition at line 454 of file kis_free_transform_strategy.cpp.

455{
456 KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "this override is not used in KisFreeTransformStrategy");
457}
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130

References KIS_SAFE_ASSERT_RECOVER_NOOP.

◆ continuePrimaryAction() [2/3]

void KisFreeTransformStrategy::continuePrimaryAction ( const QPointF & pt,
bool shiftModifierActive,
bool altModifierActive,
bool hasOriginalModifiersSet )
overridevirtual

We use determinant of clickM.SC instead of clickT to be able to catch the case when the image is flipped by 0x or 0y perspective rotations.

Reimplemented from KisSimplifiedActionPolicyStrategy.

Definition at line 459 of file kis_free_transform_strategy.cpp.

463{
464 // Note: "shiftModifierActive" just tells us if the shift key is being pressed
465 // Note: "altModifierActive" just tells us if the alt key is being pressed
466 // Note: "hasOriginalModifiersSet" tells if the set of modifiers that triggered
467 // the whole action is still pressed in the original form
468
469 m_d->isTransforming = true;
470 const QPointF anchorPoint = m_d->clickArgs.originalCenter() + m_d->clickArgs.rotationCenterOffset();
471
472 switch (m_d->function) {
473 case MOVE: {
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 {
498 const KisTransformUtils::MatricesPack clickM(m_d->clickArgs);
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 // Find new scale/shear/rotation for the rotated bounds
512 QTransform newBR; newBR.rotateRadians(m_d->currentArgs.boundsRotation());
513 QTransform clickZ; clickZ.rotateRadians(m_d->clickArgs.aZ());
514 // newM.BRI * newM.SC * newM.S * newZ = clickM.BRI * clickM.SC * clickM.S * clickZ
515 // newM.SC * newM.S * newZ = newM.BR * clickM.BRI * clickM.SC * clickM.S * clickZ
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);
523 m_d->currentArgs.setAZ(kisDegreesToRadians(dm.angle));
524 }
525
526 // Snap with shift key
527 // if (shiftModifierActive) {
528 // const qreal angle = m_d->currentArgs.boundsRotation();
529 // const qreal snapAngle = M_PI_4 / 6.0; // 7.5 degrees
530 // qint32 angleIndex = static_cast<qint32>((angle / snapAngle) + 0.5);
531 // m_d->currentArgs.setBoundsRotation(angleIndex * snapAngle);
532 // }
533 }
534 break;
535 case ROTATE:
536 {
537 const KisTransformUtils::MatricesPack clickM(m_d->clickArgs);
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
551 const qreal theta = KisAlgebra2D::signZZ(clickM.SC.determinant()) * (a2 - a1);
552
553 // Snap with shift key
554 if (shiftModifierActive) {
555 const qreal snapAngle = M_PI_4 / 6.0; // 7.5 degrees
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;
562 qreal shortestDistance = shortestAngularDistance(clickAngle, targetAngle);
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
569 KisTransformUtils::MatricesPack m(m_d->currentArgs);
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());
588 m_d->currentArgs.setAX(normalizeAngle(m_d->clickArgs.aX() + thetaX));
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());
592 m_d->currentArgs.setAY(normalizeAngle(m_d->clickArgs.aY() + thetaY));
593
594 KisTransformUtils::MatricesPack m(m_d->currentArgs);
595 QTransform t = m.finalTransform();
596 QPointF newRotationCenter = t.map(m_d->currentArgs.originalCenter() + m_d->currentArgs.rotationCenterOffset());
597
598 KisTransformUtils::MatricesPack clickM(m_d->clickArgs);
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 // normalize distance from extremes 0 (close) to 1 (far)
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 // Multiply normalized distance by mouse movement to calculate new camera height to add.
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 =
648 KisAlgebra2D::normalize(movingPointInView - staticPointInView);
649
650 const qreal projLength =
651 KisAlgebra2D::dotProduct(mousePos - staticPointInView, projNormVector);
652
653 const QPointF targetMovingPointInView = staticPointInView + projNormVector * projLength;
654
655 // override scale static point if it is locked
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
663 GSL::ScaleResult1D result =
664 GSL::calculateScaleY(m_d->currentArgs,
665 staticPoint,
666 staticPointInView,
667 movingPoint,
668 targetMovingPointInView);
669
670 if (!result.isValid) {
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);
680 m_d->currentArgs.setTransformedCenter(result.transformedCenter);
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 =
701 KisAlgebra2D::normalize(movingPointInView - staticPointInView);
702
703 const qreal projLength =
704 KisAlgebra2D::dotProduct(mousePos - staticPointInView, projNormVector);
705
706 const QPointF targetMovingPointInView = staticPointInView + projNormVector * projLength;
707
708 // override scale static point if it is locked
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
716 GSL::ScaleResult1D result =
717 GSL::calculateScaleX(m_d->currentArgs,
718 staticPoint,
719 staticPointInView,
720 movingPoint,
721 targetMovingPointInView);
722
723 if (!result.isValid) {
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);
733 m_d->currentArgs.setTransformedCenter(result.transformedCenter);
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 // override scale static point if it is locked
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;
771 realDiff = kisProjectOnVector(refDiff, realDiff);
772
773 movingPointInView = staticPointInView + realDiff;
774 }
775
776 const bool isAffine =
777 qFuzzyIsNull(m_d->currentArgs.aX()) &&
778 qFuzzyIsNull(m_d->currentArgs.aY());
779
780 GSL::ScaleResult2D result =
781 !isAffine ?
782 GSL::calculateScale2D(m_d->currentArgs,
783 staticPoint,
784 staticPointInView,
785 movingPoint,
786 movingPointInView) :
787 GSL::calculateScale2DAffine(m_d->currentArgs,
788 staticPoint,
789 staticPointInView,
790 movingPoint,
791 movingPointInView);
792
793 if (result.isValid) {
794 m_d->currentArgs.setScaleX(result.scaleX);
795 m_d->currentArgs.setScaleY(result.scaleY);
796 m_d->currentArgs.setTransformedCenter(result.transformedCenter);
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);
806 pt = KisTransformUtils::clipInRect(pt, m_d->bounds);
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 // get the dx pixels corresponding to the current shearX factor
838 qreal dx = sign * m_d->clickArgs.shearX() * m_d->clickArgs.scaleY() * (m_d->bounds.height() / 2.0); // get the dx pixels corresponding to the current shearX factor
839 dx += diff.x();
840
841 // calculate the new shearX factor
842 m_d->currentArgs.setShearX(sign * dx / m_d->currentArgs.scaleY() / (m_d->bounds.height() / 2.0)); // calculate the new shearX factor
843
844 KisTransformUtils::MatricesPack currentM(m_d->currentArgs);
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 // get the dx pixels corresponding to the current shearX factor
863 qreal dy = sign * m_d->clickArgs.shearY() * m_d->clickArgs.scaleX() * (m_d->bounds.width() / 2.0);
864 dy += diff.y();
865
866 // calculate the new shearY factor
867 m_d->currentArgs.setShearY(sign * dy / m_d->clickArgs.scaleX() / (m_d->bounds.width() / 2.0));
868
869 KisTransformUtils::MatricesPack currentM(m_d->currentArgs);
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}
void requestResetRotationCenterButtons()
static QPointF clipInRect(QPointF p, QRectF r)
static bool qFuzzyIsNull(half h)
qreal shortestAngularDistance(qreal a, qreal b)
Definition kis_global.h:140
T kisDegreesToRadians(T degrees)
Definition kis_global.h:176
std::enable_if< std::is_floating_point< T >::value, T >::type normalizeAngle(T a)
Definition kis_global.h:121
QPointF kisProjectOnVector(const QPointF &base, const QPointF &v)
Definition kis_global.h:280
#define M_PI
Definition kis_global.h:111
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

References KisAlgebra2D::DecomposedMatrix::angle, KisTransformUtils::MatricesPack::BRI, GSL::calculateScale2D(), GSL::calculateScale2DAffine(), GSL::calculateScaleX(), GSL::calculateScaleY(), KisTransformUtils::clipInRect(), KisAlgebra2D::dotProduct(), KisTransformUtils::MatricesPack::finalTransform(), KisAlgebra2D::DecomposedMatrix::isValid(), GSL::ScaleResult1D::isValid, GSL::ScaleResult2D::isValid, kisDegreesToRadians(), kisProjectOnVector(), m_d, M_PI, MOVE, KisAlgebra2D::normalize(), normalizeAngle(), KisTransformUtils::MatricesPack::projectedP, qFuzzyIsNull(), requestResetRotationCenterButtons(), KisTransformUtils::MatricesPack::S, KisTransformUtils::MatricesPack::SC, GSL::ScaleResult1D::scale, KisAlgebra2D::DecomposedMatrix::scaleX, GSL::ScaleResult2D::scaleX, KisAlgebra2D::DecomposedMatrix::scaleY, GSL::ScaleResult2D::scaleY, KisAlgebra2D::DecomposedMatrix::shearXY, shortestAngularDistance(), sign(), KisAlgebra2D::signZZ(), GSL::ScaleResult1D::transformedCenter, and GSL::ScaleResult2D::transformedCenter.

◆ continuePrimaryAction() [3/3]

virtual void KisTransformStrategyBase::continuePrimaryAction ( KoPointerEvent * event)
virtual

◆ endPrimaryAction() [1/2]

bool KisFreeTransformStrategy::endPrimaryAction ( )
overridevirtual

Implements KisSimplifiedActionPolicyStrategy.

Definition at line 880 of file kis_free_transform_strategy.cpp.

881{
882 bool shouldSave = !m_d->imageTooBig;
883 m_d->isTransforming = false;
884
885 if (m_d->imageTooBig) {
886 m_d->currentArgs = m_d->clickArgs;
887 m_d->recalculateTransformations();
888 }
889
890 return shouldSave;
891}

References m_d.

◆ endPrimaryAction() [2/2]

virtual bool KisTransformStrategyBase::endPrimaryAction ( KoPointerEvent * event)
virtual

◆ externalConfigChanged()

void KisFreeTransformStrategy::externalConfigChanged ( )
overridevirtual

Implements KisTransformStrategyBase.

Definition at line 434 of file kis_free_transform_strategy.cpp.

435{
436 m_d->recalculateTransformations();
437}

References m_d.

◆ getCurrentCursor()

QCursor KisFreeTransformStrategy::getCurrentCursor ( ) const
overridevirtual

Implements KisTransformStrategyBase.

Definition at line 293 of file kis_free_transform_strategy.cpp.

294{
295 QCursor cursor;
296
297 switch (m_d->function) {
298 case MOVE:
299 cursor = KisCursor::moveCursor();
300 break;
301 case ROTATEBOUNDS:
302 cursor = m_d->rotateHandlesCursor;
303 break;
304 case ROTATE:
305 cursor = KisCursor::rotateCursor();
306 break;
307 case PERSPECTIVE:
308 //TODO: find another cursor for perspective
309 cursor = KisCursor::rotateCursor();
310 break;
311 case CAMERAHEIGHT:
312 cursor = KisCursor::sizeVerCursor();
313 break;
314 case RIGHTSCALE:
315 cursor = m_d->getScaleCursor(m_d->transformedHandles.middleRight);
316 break;
317 case TOPSCALE:
318 cursor = m_d->getScaleCursor(m_d->transformedHandles.topMiddle);
319 break;
320 case LEFTSCALE:
321 cursor = m_d->getScaleCursor(m_d->transformedHandles.middleLeft);
322 break;
323 case BOTTOMSCALE:
324 cursor = m_d->getScaleCursor(m_d->transformedHandles.bottomMiddle);
325 break;
326 case TOPRIGHTSCALE:
327 cursor = m_d->getScaleCursor(m_d->transformedHandles.topRight);
328 break;
329 case BOTTOMLEFTSCALE:
330 cursor = m_d->getScaleCursor(m_d->transformedHandles.bottomLeft);
331 break;
332 case TOPLEFTSCALE:
333 cursor = m_d->getScaleCursor(m_d->transformedHandles.topLeft);
334 break;
335 case BOTTOMRIGHTSCALE:
336 cursor = m_d->getScaleCursor(m_d->transformedHandles.bottomRight);
337 break;
338 case MOVECENTER:
339 cursor = KisCursor::handCursor();
340 break;
341 case BOTTOMSHEAR:
342 cursor = m_d->getShearCursor(m_d->transformedHandles.bottomLeft, m_d->transformedHandles.bottomRight);
343 break;
344 case RIGHTSHEAR:
345 cursor = m_d->getShearCursor(m_d->transformedHandles.bottomRight, m_d->transformedHandles.topRight);
346 break;
347 case TOPSHEAR:
348 cursor = m_d->getShearCursor(m_d->transformedHandles.topRight, m_d->transformedHandles.topLeft);
349 break;
350 case LEFTSHEAR:
351 cursor = m_d->getShearCursor(m_d->transformedHandles.topLeft, m_d->transformedHandles.bottomLeft);
352 break;
353 }
354
355 return cursor;
356}
static QCursor handCursor()
static QCursor rotateCursor()
static QCursor moveCursor()
static QCursor sizeVerCursor()
Definition kis_cursor.cc:64

References KisCursor::handCursor(), m_d, MOVE, KisCursor::moveCursor(), KisCursor::rotateCursor(), and KisCursor::sizeVerCursor().

◆ paint()

void KisFreeTransformStrategy::paint ( QPainter & gc,
const KoColorDisplayRendererInterface * displayRendererInterface )
overridevirtual

Implements KisTransformStrategyBase.

Definition at line 358 of file kis_free_transform_strategy.cpp.

359{
360 gc.save();
361
362 gc.setOpacity(m_d->transaction.basePreviewOpacity());
363 gc.setTransform(m_d->paintingTransform, true);
364 gc.drawImage(m_d->paintingOffset, originalImage());
365
366 gc.restore();
367
368 // Draw Handles
369
370 QRectF handleRect =
372 m_d->handlesTransform,
373 m_d->bounds, 0, 0);
374
375 qreal rX = 1;
376 qreal rY = 1;
377 QRectF rotationCenterRect =
379 m_d->handlesTransform,
380 m_d->bounds,
381 &rX,
382 &rY);
383
384 QPainterPath handles;
385
386 handles.moveTo(m_d->bounds.topLeft());
387 handles.lineTo(m_d->bounds.topRight());
388 handles.lineTo(m_d->bounds.bottomRight());
389 handles.lineTo(m_d->bounds.bottomLeft());
390 handles.lineTo(m_d->bounds.topLeft());
391
392 handles.addRect(handleRect.translated(m_d->bounds.topLeft()));
393 handles.addRect(handleRect.translated(m_d->bounds.topRight()));
394 handles.addRect(handleRect.translated(m_d->bounds.bottomLeft()));
395 handles.addRect(handleRect.translated(m_d->bounds.bottomRight()));
396 handles.addRect(handleRect.translated(middleLeft(m_d->bounds)));
397 handles.addRect(handleRect.translated(middleRight(m_d->bounds)));
398 handles.addRect(handleRect.translated(topMiddle(m_d->bounds)));
399 handles.addRect(handleRect.translated(bottomMiddle(m_d->bounds)));
400
401 QPointF rotationCenter = m_d->boundsTransform.inverted().map(m_d->currentArgs.originalCenter() + m_d->currentArgs.rotationCenterOffset());
402 QPointF dx(rX + 3, 0);
403 QPointF dy(0, rY + 3);
404 handles.addEllipse(rotationCenterRect.translated(rotationCenter));
405 handles.moveTo(rotationCenter - dx);
406 handles.lineTo(rotationCenter + dx);
407 handles.moveTo(rotationCenter - dy);
408 handles.lineTo(rotationCenter + dy);
409
410 gc.save();
411
412 if (m_d->isTransforming) {
413 gc.setOpacity(0.1);
414 }
415
416 //gc.setTransform(m_d->handlesTransform, true); <-- don't do like this!
417 QPainterPath mappedHandles = m_d->handlesTransform.map(handles);
418
419 QPen pen[2];
420 pen[0].setWidth(decorationThickness());
421 pen[0].setCosmetic(true);
422 pen[1].setWidth(decorationThickness() * 2);
423 pen[1].setCosmetic(true);
424 pen[1].setColor(displayRendererInterface->convertColorToDisplayColorSpace(KoColor(Qt::lightGray, KoColorSpaceRegistry::instance()->rgb8())));
425
426 for (int i = 1; i >= 0; --i) {
427 gc.setPen(pen[i]);
428 gc.drawPath(mappedHandles);
429 }
430
431 gc.restore();
432}
static const int handleVisualRadius
static QRectF handleRect(qreal radius, const QTransform &t, const QRectF &limitingRect, qreal *dOutX, qreal *dOutY)
static const int rotationHandleVisualRadius
virtual QColor convertColorToDisplayColorSpace(const KoColor color) const =0
convertColorToDisplayColorSpace
static KoColorSpaceRegistry * instance()

References KoColorDisplayRendererInterface::convertColorToDisplayColorSpace(), KisTransformStrategyBase::decorationThickness(), KisTransformUtils::handleRect(), KisTransformUtils::handleVisualRadius, KoColorSpaceRegistry::instance(), m_d, KisTransformStrategyBase::originalImage(), and KisTransformUtils::rotationHandleVisualRadius.

◆ requestCanvasUpdate

void KisFreeTransformStrategy::requestCanvasUpdate ( )
signal

◆ requestConvexHullCalculation

void KisFreeTransformStrategy::requestConvexHullCalculation ( )
signal

◆ requestImageRecalculation

void KisFreeTransformStrategy::requestImageRecalculation ( )
signal

◆ requestResetRotationCenterButtons

void KisFreeTransformStrategy::requestResetRotationCenterButtons ( )
signal

◆ requestShowImageTooBig

void KisFreeTransformStrategy::requestShowImageTooBig ( bool value)
signal

◆ setTransformFunction()

void KisFreeTransformStrategy::setTransformFunction ( const QPointF & mousePos,
bool perspectiveModifierActive,
bool shiftModifierActive,
bool altModifierActive )
overridevirtual

Implements KisSimplifiedActionPolicyStrategy.

Definition at line 199 of file kis_free_transform_strategy.cpp.

200{
201 Q_UNUSED(shiftModifierActive);
202
203 if (perspectiveModifierActive && !m_d->transaction.shouldAvoidPerspectiveTransform()) {
204 m_d->function = PERSPECTIVE;
205 return;
206 }
207
208 QTransform boundsFullTransform = m_d->boundsTransform * m_d->transform;
209 QPolygonF transformedPolygon = boundsFullTransform.map(QPolygonF(m_d->bounds));
210 qreal handleRadius = KisTransformUtils::effectiveHandleGrabRadius(m_d->converter);
211 qreal rotationHandleRadius = KisTransformUtils::effectiveHandleGrabRadius(m_d->converter);
212
213
214 StrokeFunction defaultFunction;
215 if (transformedPolygon.containsPoint(mousePos, Qt::OddEvenFill))
216 defaultFunction = MOVE;
217 else if (m_d->transaction.boundsRotationAllowed() && altModifierActive)
218 defaultFunction = ROTATEBOUNDS;
219 else
220 defaultFunction = ROTATE;
222 handleChooser(mousePos, defaultFunction);
223
224 handleChooser.addFunction(m_d->transformedHandles.topMiddle,
225 handleRadius, TOPSCALE);
226 handleChooser.addFunction(m_d->transformedHandles.topRight,
227 handleRadius, TOPRIGHTSCALE);
228 handleChooser.addFunction(m_d->transformedHandles.middleRight,
229 handleRadius, RIGHTSCALE);
230
231 handleChooser.addFunction(m_d->transformedHandles.bottomRight,
232 handleRadius, BOTTOMRIGHTSCALE);
233 handleChooser.addFunction(m_d->transformedHandles.bottomMiddle,
234 handleRadius, BOTTOMSCALE);
235 handleChooser.addFunction(m_d->transformedHandles.bottomLeft,
236 handleRadius, BOTTOMLEFTSCALE);
237 handleChooser.addFunction(m_d->transformedHandles.middleLeft,
238 handleRadius, LEFTSCALE);
239 handleChooser.addFunction(m_d->transformedHandles.topLeft,
240 handleRadius, TOPLEFTSCALE);
241 handleChooser.addFunction(m_d->transformedHandles.rotationCenter,
242 rotationHandleRadius, MOVECENTER);
243
244 m_d->function = handleChooser.function();
245
246 if (m_d->function == ROTATE || m_d->function == MOVE) {
247 QRectF bounds = m_d->bounds;
248 QPointF t = boundsFullTransform.inverted().map(mousePos);
249
250 if (t.x() >= bounds.left() && t.x() <= bounds.right()) {
251 if (fabs(t.y() - bounds.top()) <= handleRadius)
252 m_d->function = TOPSHEAR;
253 if (fabs(t.y() - bounds.bottom()) <= handleRadius)
254 m_d->function = BOTTOMSHEAR;
255 }
256 if (t.y() >= bounds.top() && t.y() <= bounds.bottom()) {
257 if (fabs(t.x() - bounds.left()) <= handleRadius)
258 m_d->function = LEFTSHEAR;
259 if (fabs(t.x() - bounds.right()) <= handleRadius)
260 m_d->function = RIGHTSHEAR;
261 }
262 }
263}
static qreal effectiveHandleGrabRadius(const KisCoordinatesConverter *converter)
#define bounds(x, a, b)

References KisTransformUtils::HandleChooser< Function >::addFunction(), bounds, KisTransformUtils::effectiveHandleGrabRadius(), KisTransformUtils::HandleChooser< Function >::function(), m_d, and MOVE.

◆ shiftModifierIsUsed()

bool KisFreeTransformStrategy::shiftModifierIsUsed ( ) const
overridevirtual

Reimplemented from KisSimplifiedActionPolicyStrategy.

Definition at line 265 of file kis_free_transform_strategy.cpp.

266{
267 return true;
268}

Member Data Documentation

◆ m_d

const QScopedPointer<Private> KisFreeTransformStrategy::m_d
private

Definition at line 58 of file kis_free_transform_strategy.h.


The documentation for this class was generated from the following files: