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
 
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) 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 QPainterPath getCursorOutline () const
 
 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 137 of file kis_free_transform_strategy.cpp.

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

◆ ~KisFreeTransformStrategy()

KisFreeTransformStrategy::~KisFreeTransformStrategy ( )
override

Definition at line 146 of file kis_free_transform_strategy.cpp.

147{
148}

Member Function Documentation

◆ beginPrimaryAction() [1/2]

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

Implements KisSimplifiedActionPolicyStrategy.

Definition at line 434 of file kis_free_transform_strategy.cpp.

435{
436 m_d->clickArgs = m_d->currentArgs;
437 m_d->clickPos = pt;
438
440 m_d->clickTransform = m.finalTransform();
441
442 if (m_d->function == ROTATEBOUNDS) {
444 }
445
446 return true;
447}

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

◆ beginPrimaryAction() [2/2]

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

◆ continuePrimaryAction() [1/2]

void KisFreeTransformStrategy::continuePrimaryAction ( const QPointF & pt,
bool shiftModifierActive,
bool altModifierActive )
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.

Implements KisSimplifiedActionPolicyStrategy.

Definition at line 449 of file kis_free_transform_strategy.cpp.

452{
453 // Note: "shiftModifierActive" just tells us if the shift key is being pressed
454 // Note: "altModifierActive" just tells us if the alt key is being pressed
455
456 m_d->isTransforming = true;
457 const QPointF anchorPoint = m_d->clickArgs.originalCenter() + m_d->clickArgs.rotationCenterOffset();
458
459 switch (m_d->function) {
460 case MOVE: {
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 {
485 const KisTransformUtils::MatricesPack clickM(m_d->clickArgs);
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 // Find new scale/shear/rotation for the rotated bounds
499 QTransform newBR; newBR.rotateRadians(m_d->currentArgs.boundsRotation());
500 QTransform clickZ; clickZ.rotateRadians(m_d->clickArgs.aZ());
501 // newM.BRI * newM.SC * newM.S * newZ = clickM.BRI * clickM.SC * clickM.S * clickZ
502 // newM.SC * newM.S * newZ = newM.BR * clickM.BRI * clickM.SC * clickM.S * clickZ
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);
510 m_d->currentArgs.setAZ(kisDegreesToRadians(dm.angle));
511 }
512
513 // Snap with shift key
514 // if (shiftModifierActive) {
515 // const qreal angle = m_d->currentArgs.boundsRotation();
516 // const qreal snapAngle = M_PI_4 / 6.0; // 7.5 degrees
517 // qint32 angleIndex = static_cast<qint32>((angle / snapAngle) + 0.5);
518 // m_d->currentArgs.setBoundsRotation(angleIndex * snapAngle);
519 // }
520 }
521 break;
522 case ROTATE:
523 {
524 const KisTransformUtils::MatricesPack clickM(m_d->clickArgs);
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
538 const qreal theta = KisAlgebra2D::signZZ(clickM.SC.determinant()) * (a2 - a1);
539
540 // Snap with shift key
541 if (shiftModifierActive) {
542 const qreal snapAngle = M_PI_4 / 6.0; // 7.5 degrees
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;
549 qreal shortestDistance = shortestAngularDistance(clickAngle, targetAngle);
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
556 KisTransformUtils::MatricesPack m(m_d->currentArgs);
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());
568 m_d->currentArgs.setAX(normalizeAngle(m_d->clickArgs.aX() + thetaX));
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());
572 m_d->currentArgs.setAY(normalizeAngle(m_d->clickArgs.aY() + thetaY));
573
574 KisTransformUtils::MatricesPack m(m_d->currentArgs);
575 QTransform t = m.finalTransform();
576 QPointF newRotationCenter = t.map(m_d->currentArgs.originalCenter() + m_d->currentArgs.rotationCenterOffset());
577
578 KisTransformUtils::MatricesPack clickM(m_d->clickArgs);
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 =
602 KisAlgebra2D::normalize(movingPointInView - staticPointInView);
603
604 const qreal projLength =
605 KisAlgebra2D::dotProduct(mousePos - staticPointInView, projNormVector);
606
607 const QPointF targetMovingPointInView = staticPointInView + projNormVector * projLength;
608
609 // override scale static point if it is locked
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
617 GSL::ScaleResult1D result =
618 GSL::calculateScaleY(m_d->currentArgs,
619 staticPoint,
620 staticPointInView,
621 movingPoint,
622 targetMovingPointInView);
623
624 if (!result.isValid) {
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);
634 m_d->currentArgs.setTransformedCenter(result.transformedCenter);
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 =
655 KisAlgebra2D::normalize(movingPointInView - staticPointInView);
656
657 const qreal projLength =
658 KisAlgebra2D::dotProduct(mousePos - staticPointInView, projNormVector);
659
660 const QPointF targetMovingPointInView = staticPointInView + projNormVector * projLength;
661
662 // override scale static point if it is locked
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
670 GSL::ScaleResult1D result =
671 GSL::calculateScaleX(m_d->currentArgs,
672 staticPoint,
673 staticPointInView,
674 movingPoint,
675 targetMovingPointInView);
676
677 if (!result.isValid) {
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);
687 m_d->currentArgs.setTransformedCenter(result.transformedCenter);
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 // override scale static point if it is locked
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;
725 realDiff = kisProjectOnVector(refDiff, realDiff);
726
727 movingPointInView = staticPointInView + realDiff;
728 }
729
730 const bool isAffine =
731 qFuzzyIsNull(m_d->currentArgs.aX()) &&
732 qFuzzyIsNull(m_d->currentArgs.aY());
733
734 GSL::ScaleResult2D result =
735 !isAffine ?
736 GSL::calculateScale2D(m_d->currentArgs,
737 staticPoint,
738 staticPointInView,
739 movingPoint,
740 movingPointInView) :
741 GSL::calculateScale2DAffine(m_d->currentArgs,
742 staticPoint,
743 staticPointInView,
744 movingPoint,
745 movingPointInView);
746
747 if (result.isValid) {
748 m_d->currentArgs.setScaleX(result.scaleX);
749 m_d->currentArgs.setScaleY(result.scaleY);
750 m_d->currentArgs.setTransformedCenter(result.transformedCenter);
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);
760 pt = KisTransformUtils::clipInRect(pt, m_d->bounds);
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 // get the dx pixels corresponding to the current shearX factor
792 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
793 dx += diff.x();
794
795 // calculate the new shearX factor
796 m_d->currentArgs.setShearX(sign * dx / m_d->currentArgs.scaleY() / (m_d->bounds.height() / 2.0)); // calculate the new shearX factor
797
798 KisTransformUtils::MatricesPack currentM(m_d->currentArgs);
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 // get the dx pixels corresponding to the current shearX factor
817 qreal dy = sign * m_d->clickArgs.shearY() * m_d->clickArgs.scaleX() * (m_d->bounds.width() / 2.0);
818 dy += diff.y();
819
820 // calculate the new shearY factor
821 m_d->currentArgs.setShearY(sign * dy / m_d->clickArgs.scaleX() / (m_d->bounds.width() / 2.0));
822
823 KisTransformUtils::MatricesPack currentM(m_d->currentArgs);
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}
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:256
#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() [2/2]

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

◆ endPrimaryAction() [1/2]

bool KisFreeTransformStrategy::endPrimaryAction ( )
overridevirtual

Implements KisSimplifiedActionPolicyStrategy.

Definition at line 834 of file kis_free_transform_strategy.cpp.

835{
836 bool shouldSave = !m_d->imageTooBig;
837 m_d->isTransforming = false;
838
839 if (m_d->imageTooBig) {
840 m_d->currentArgs = m_d->clickArgs;
841 m_d->recalculateTransformations();
842 }
843
844 return shouldSave;
845}

References m_d.

◆ endPrimaryAction() [2/2]

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

◆ externalConfigChanged()

void KisFreeTransformStrategy::externalConfigChanged ( )
overridevirtual

Implements KisTransformStrategyBase.

Definition at line 429 of file kis_free_transform_strategy.cpp.

430{
431 m_d->recalculateTransformations();
432}

References m_d.

◆ getCurrentCursor()

QCursor KisFreeTransformStrategy::getCurrentCursor ( ) const
overridevirtual

Implements KisTransformStrategyBase.

Definition at line 291 of file kis_free_transform_strategy.cpp.

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

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

◆ paint()

void KisFreeTransformStrategy::paint ( QPainter & gc)
overridevirtual

Implements KisTransformStrategyBase.

Definition at line 353 of file kis_free_transform_strategy.cpp.

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

References KisTransformStrategyBase::decorationThickness(), KisTransformUtils::handleRect(), KisTransformUtils::handleVisualRadius, 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 197 of file kis_free_transform_strategy.cpp.

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

264{
265 return true;
266}

Member Data Documentation

◆ m_d

const QScopedPointer<Private> KisFreeTransformStrategy::m_d
private

Definition at line 57 of file kis_free_transform_strategy.h.


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