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, 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 138 of file kis_free_transform_strategy.cpp.

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

◆ ~KisFreeTransformStrategy()

KisFreeTransformStrategy::~KisFreeTransformStrategy ( )
override

Definition at line 147 of file kis_free_transform_strategy.cpp.

148{
149}

Member Function Documentation

◆ beginPrimaryAction() [1/2]

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

Implements KisSimplifiedActionPolicyStrategy.

Definition at line 435 of file kis_free_transform_strategy.cpp.

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

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 450 of file kis_free_transform_strategy.cpp.

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

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

◆ endPrimaryAction() [1/2]

bool KisFreeTransformStrategy::endPrimaryAction ( )
overridevirtual

Implements KisSimplifiedActionPolicyStrategy.

Definition at line 835 of file kis_free_transform_strategy.cpp.

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

References m_d.

◆ endPrimaryAction() [2/2]

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

◆ externalConfigChanged()

void KisFreeTransformStrategy::externalConfigChanged ( )
overridevirtual

Implements KisTransformStrategyBase.

Definition at line 430 of file kis_free_transform_strategy.cpp.

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

References m_d.

◆ getCurrentCursor()

QCursor KisFreeTransformStrategy::getCurrentCursor ( ) const
overridevirtual

Implements KisTransformStrategyBase.

Definition at line 292 of file kis_free_transform_strategy.cpp.

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

Implements KisTransformStrategyBase.

Definition at line 354 of file kis_free_transform_strategy.cpp.

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

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

265{
266 return true;
267}

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: