201 Q_UNUSED(shiftModifierActive);
203 if (perspectiveModifierActive && !
m_d->transaction.shouldAvoidPerspectiveTransform()) {
204 m_d->function = PERSPECTIVE;
208 QTransform boundsFullTransform =
m_d->boundsTransform *
m_d->transform;
209 QPolygonF transformedPolygon = boundsFullTransform.map(QPolygonF(
m_d->bounds));
214 StrokeFunction defaultFunction;
215 if (transformedPolygon.containsPoint(mousePos, Qt::OddEvenFill))
216 defaultFunction =
MOVE;
217 else if (
m_d->transaction.boundsRotationAllowed() && altModifierActive)
218 defaultFunction = ROTATEBOUNDS;
220 defaultFunction = ROTATE;
222 handleChooser(mousePos, defaultFunction);
225 handleRadius, TOPSCALE);
227 handleRadius, TOPRIGHTSCALE);
229 handleRadius, RIGHTSCALE);
232 handleRadius, BOTTOMRIGHTSCALE);
234 handleRadius, BOTTOMSCALE);
236 handleRadius, BOTTOMLEFTSCALE);
238 handleRadius, LEFTSCALE);
240 handleRadius, TOPLEFTSCALE);
242 rotationHandleRadius, MOVECENTER);
246 if (
m_d->function == ROTATE ||
m_d->function ==
MOVE) {
248 QPointF t = boundsFullTransform.inverted().map(mousePos);
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;
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;
460 bool shiftModifierActive,
461 bool altModifierActive,
462 bool hasOriginalModifiersSet)
469 m_d->isTransforming =
true;
470 const QPointF anchorPoint =
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset();
472 switch (
m_d->function) {
474 QPointF diff = mousePos -
m_d->clickPos;
476 if (shiftModifierActive) {
480 QPointF originalDiff = t.inverted().map(diff);
482 if (qAbs(originalDiff.x()) >= qAbs(originalDiff.y())) {
483 originalDiff.setY(0);
485 originalDiff.setX(0);
488 diff = t.map(originalDiff);
492 m_d->currentArgs.setTransformedCenter(
m_d->clickArgs.transformedCenter() + diff);
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;
505 const qreal a1 = atan2(clickMouseImagePos.y(), clickMouseImagePos.x());
506 const qreal a2 = atan2(mouseImagePosClickSpace.y(), mouseImagePosClickSpace.x());
508 const qreal theta = a2 - a1;
509 m_d->currentArgs.setBoundsRotation(
m_d->clickArgs.boundsRotation() + theta);
512 QTransform newBR; newBR.rotateRadians(
m_d->currentArgs.boundsRotation());
513 QTransform clickZ; clickZ.rotateRadians(
m_d->clickArgs.aZ());
516 QTransform desired = newBR * clickM.
BRI * clickM.
SC * clickM.
S * clickZ;
522 m_d->currentArgs.setShearY(0);
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;
544 const qreal a1 = atan2(clickMouseImagePos.y(), clickMouseImagePos.x());
545 const qreal a2 = atan2(mouseImagePosClickSpace.y(), mouseImagePosClickSpace.x());
554 if (shiftModifierActive) {
555 const qreal snapAngle = M_PI_4 / 6.0;
556 qint32 thetaIndex =
static_cast<qint32
>((theta / snapAngle) + 0.5);
557 m_d->currentArgs.setAZ(thetaIndex * snapAngle);
560 const qreal clickAngle =
m_d->clickArgs.aZ();
561 const qreal targetAngle =
m_d->clickArgs.aZ() + theta;
563 const bool clockwise = (theta <= M_PI && theta >= 0) || (theta < -
M_PI);
564 shortestDistance = clockwise ? shortestDistance : shortestDistance * -1;
566 m_d->currentArgs.setAZ(
m_d->clickArgs.aZ() + shortestDistance);
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());
574 m_d->currentArgs.setTransformedCenter(
m_d->currentArgs.transformedCenter() + oldRotationCenter - newRotationCenter);
579 if (!hasOriginalModifiersSet) {
580 m_d->function = CAMERAHEIGHT;
581 m_d->clickArgs =
m_d->currentArgs;
582 m_d->clickPos = mousePos;
586 QPointF diff = mousePos -
m_d->clickPos;
587 double thetaX = - diff.y() *
M_PI /
m_d->transaction.originalHalfHeight() / 2 / fabs(
m_d->currentArgs.scaleY());
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());
596 QPointF newRotationCenter = t.map(
m_d->currentArgs.originalCenter() +
m_d->currentArgs.rotationCenterOffset());
600 QPointF oldRotationCenter = clickT.map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
602 m_d->currentArgs.setTransformedCenter(
m_d->currentArgs.transformedCenter() + oldRotationCenter - newRotationCenter);
606 if (hasOriginalModifiersSet) {
607 m_d->function = PERSPECTIVE;
608 m_d->clickPos = mousePos;
612 qreal dy = mousePos.y() -
m_d->clickPos.y();
613 m_d->clickPos = mousePos;
616 qreal currentHeight =
m_d->currentArgs.cameraPos().z();
617 qreal currentHeightScaled = (currentHeight - 10150.0) / 9850.0;
618 qreal heightDelta = -(currentHeightScaled * currentHeightScaled) + 1.001;
620 const qreal sensitivityFactor = 0.02;
621 qreal scaledDy = dy / sensitivityFactor;
624 heightDelta = heightDelta * scaledDy;
625 qreal newHeight = currentHeight + heightDelta;
627 newHeight = qMax(300.0, qMin(20000.0, newHeight));
628 m_d->currentArgs.setCameraPos(QVector3D(0, 0, newHeight));
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));
640 staticPoint =
m_d->boundsTransform.map(topMiddle(
m_d->bounds));
641 movingPoint =
m_d->boundsTransform.map(bottomMiddle(
m_d->bounds));
644 QPointF staticPointInView =
m_d->clickTransform.map(staticPoint);
645 const QPointF movingPointInView =
m_d->clickTransform.map(movingPoint);
647 const QPointF projNormVector =
650 const qreal projLength =
653 const QPointF targetMovingPointInView = staticPointInView + projNormVector * projLength;
656 if ((
m_d->clickArgs.transformAroundRotationCenter() ^ altModifierActive) &&
657 !qFuzzyCompare(anchorPoint.y(), movingPoint.y())) {
659 staticPoint = anchorPoint;
660 staticPointInView =
m_d->clickTransform.map(staticPoint);
668 targetMovingPointInView);
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);
679 m_d->currentArgs.setScaleY(result.
scale);
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));
693 staticPoint =
m_d->boundsTransform.map(middleLeft(
m_d->bounds));
694 movingPoint =
m_d->boundsTransform.map(middleRight(
m_d->bounds));
697 QPointF staticPointInView =
m_d->clickTransform.map(staticPoint);
698 const QPointF movingPointInView =
m_d->clickTransform.map(movingPoint);
700 const QPointF projNormVector =
703 const qreal projLength =
706 const QPointF targetMovingPointInView = staticPointInView + projNormVector * projLength;
709 if ((
m_d->currentArgs.transformAroundRotationCenter() ^ altModifierActive) &&
710 !qFuzzyCompare(anchorPoint.x(), movingPoint.x())) {
712 staticPoint = anchorPoint;
713 staticPointInView =
m_d->clickTransform.map(staticPoint);
721 targetMovingPointInView);
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);
732 m_d->currentArgs.setScaleX(result.
scale);
737 case BOTTOMRIGHTSCALE:
739 case BOTTOMLEFTSCALE: {
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());
753 staticPoint =
m_d->boundsTransform.map(
m_d->bounds.topRight());
754 movingPoint =
m_d->boundsTransform.map(
m_d->bounds.bottomLeft());
758 if ((
m_d->currentArgs.transformAroundRotationCenter() ^ altModifierActive) &&
759 !(qFuzzyCompare(anchorPoint.x(), movingPoint.x()) ||
760 qFuzzyCompare(anchorPoint.y(), movingPoint.y()))) {
762 staticPoint = anchorPoint;
765 QPointF staticPointInView =
m_d->clickTransform.map(staticPoint);
766 QPointF movingPointInView = mousePos;
768 if (shiftModifierActive ||
m_d->currentArgs.keepAspectRatio()) {
769 QPointF refDiff =
m_d->clickTransform.map(movingPoint) - staticPointInView;
770 QPointF realDiff = mousePos - staticPointInView;
773 movingPointInView = staticPointInView + realDiff;
776 const bool isAffine =
794 m_d->currentArgs.setScaleX(result.
scaleX);
795 m_d->currentArgs.setScaleY(result.
scaleY);
804 if (altModifierActive) {
805 pt = (
m_d->boundsTransform *
m_d->transform).inverted().map(mousePos);
807 pt =
m_d->boundsTransform.map(pt);
809 pt =
m_d->transform.inverted().map(mousePos);
812 QPointF newRotationCenterOffset = pt -
m_d->currentArgs.originalCenter();
814 if (shiftModifierActive) {
815 if (qAbs(newRotationCenterOffset.x()) > qAbs(newRotationCenterOffset.y())) {
816 newRotationCenterOffset.ry() = 0;
818 newRotationCenterOffset.rx() = 0;
822 m_d->currentArgs.setRotationCenterOffset(newRotationCenterOffset);
830 QPointF oldStaticPoint = m.
finalTransform().map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
832 QTransform backwardT = (m.
S * m.
projectedP).inverted();
833 QPointF diff = backwardT.map(mousePos -
m_d->clickPos);
835 qreal
sign =
m_d->function == BOTTOMSHEAR ? 1.0 : -1.0;
838 qreal dx =
sign *
m_d->clickArgs.shearX() *
m_d->clickArgs.scaleY() * (
m_d->bounds.height() / 2.0);
842 m_d->currentArgs.setShearX(
sign * dx /
m_d->currentArgs.scaleY() / (
m_d->bounds.height() / 2.0));
846 QPointF newStaticPoint = t.map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
847 m_d->currentArgs.setTransformedCenter(
m_d->currentArgs.transformedCenter() + oldStaticPoint - newStaticPoint);
855 QPointF oldStaticPoint = m.
finalTransform().map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
857 QTransform backwardT = (m.
S * m.
projectedP).inverted();
858 QPointF diff = backwardT.map(mousePos -
m_d->clickPos);
860 qreal
sign =
m_d->function == RIGHTSHEAR ? 1.0 : -1.0;
863 qreal dy =
sign *
m_d->clickArgs.shearY() *
m_d->clickArgs.scaleX() * (
m_d->bounds.width() / 2.0);
867 m_d->currentArgs.setShearY(
sign * dy /
m_d->clickArgs.scaleX() / (
m_d->bounds.width() / 2.0));
871 QPointF newStaticPoint = t.map(
m_d->clickArgs.originalCenter() +
m_d->clickArgs.rotationCenterOffset());
872 m_d->currentArgs.setTransformedCenter(
m_d->currentArgs.transformedCenter() + oldStaticPoint - newStaticPoint);
877 m_d->recalculateTransformations();