Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_zoom_and_rotate_action.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the KDE project
3 * SPDX-FileCopyrightText: 2019 Sharaf Zaman <sharafzaz121@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include <QApplication>
9#include <QTouchEvent>
10
11#include <klocalizedstring.h>
13#include <kis_canvas2.h>
14#include <KisViewManager.h>
15#include <kis_algebra_2d.h>
16
18#include "kis_input_manager.h"
20
34
36 : KisAbstractInputAction ("Zoom and Rotate Canvas")
37 , d(new Private)
38{
39 setName(i18n("Zoom and Rotate Canvas"));
40 QHash<QString, int> shortcuts;
41 shortcuts.insert(i18n("Rotate Mode"), ContinuousRotateMode);
42 shortcuts.insert(i18n("Discrete Rotate Mode"), DiscreteRotateMode);
43 setShortcutIndexes(shortcuts);
44}
45
49
51{
52 return 5;
53}
54
56{
57 Q_UNUSED(shortcut);
58}
59
61{
62 Q_UNUSED(shortcut);
63}
64
65void KisZoomAndRotateAction::begin(int shortcut, QEvent *event)
66{
67 QTouchEvent *touchEvent = dynamic_cast<QTouchEvent *>(event);
68
69 if (touchEvent && touchEvent->touchPoints().size() > 0) {
70 d->shortcutIndex = shortcut;
71 d->lastPosition = touchEvent->touchPoints().at(0).pos();
72 d->lastDistance = 0;
73 d->previousAngle = 0;
74 d->initialReferenceAngle = 0;
75 d->accumRotationAngle = 0;
76 d->actionStillPoint = inputManager()->canvas()->coordinatesConverter()->makeWidgetStillPoint(d->lastPosition);
77 }
78}
79
80void KisZoomAndRotateAction::cursorMovedAbsolute(const QPointF &, const QPointF &)
81{
82}
83
84qreal angleForSnapping(qreal angle)
85{
86 if (angle < 0) {
87 return std::fmod(angle - 2, 45) + 2;
88 } else {
89 return std::fmod(angle + 2, 45) - 2;
90 }
91}
92
94{
95 switch (event->type()) {
96 case QEvent::TouchUpdate: {
97 QTouchEvent *tevent = dynamic_cast<QTouchEvent *>(event);
98 if (tevent && tevent->touchPoints().size() > 1) {
99
100 const QPointF p0 = tevent->touchPoints().at(0).pos();
101 const QPointF p1 = tevent->touchPoints().at(1).pos();
102
103 const qreal rotationAngle = canvasRotationAngle(p0, p1);
104 const float dist = QLineF(p0, p1).length();
105 const float scaleDelta = qFuzzyCompare(1.0f, 1.0f + d->lastDistance) ? 1.f : dist / d->lastDistance;
106
108 KisCanvasController *controller = static_cast<KisCanvasController *>(canvas->canvasController());
109 const qreal newZoom = canvas->viewConverter()->zoom() * scaleDelta;
110 KoViewTransformStillPoint adjustedStillPoint = d->actionStillPoint;
111 adjustedStillPoint.second = p0;
112 controller->setZoom(KoZoomMode::ZOOM_CONSTANT, newZoom, adjustedStillPoint);
113 controller->rotateCanvas(rotationAngle, adjustedStillPoint);
114
115 d->lastPosition = p0;
116 d->lastDistance = dist;
117
118 return;
119 }
120 }
121 default:
122 break;
123 }
125}
126
128{
129 Q_UNUSED(shortcut);
131}
132
134{
135 const QPointF slope = p1 - p0;
136 const qreal currentAngle = std::atan2(slope.y(), slope.x());
137
138 switch (d->shortcutIndex) {
140 if (!d->previousAngle) {
141 d->previousAngle = currentAngle;
142 return 0;
143 }
144 qreal rotationAngle = (180 / M_PI) * (currentAngle - d->previousAngle);
145 d->previousAngle = currentAngle;
146
148 KisCanvasController *controller = static_cast<KisCanvasController *>(canvas->canvasController());
149 const qreal canvasAnglePostRotation = controller->rotation() + rotationAngle;
150 const qreal snapDelta = angleForSnapping(canvasAnglePostRotation);
151 // we snap the canvas to an angle that is a multiple of 45
152 if (abs(snapDelta) <= 2 && abs(d->accumRotationAngle) <= 2) {
153 // accumulate the relative angle of finger from the point when we started snapping
154 d->accumRotationAngle += rotationAngle;
155 rotationAngle = rotationAngle - snapDelta;
156 } else {
157 // snap the canvas out using the accumulated angle
158 rotationAngle += d->accumRotationAngle;
159 d->accumRotationAngle = 0;
160 }
161
162 return rotationAngle;
163 }
164 case DiscreteRotateMode: {
165 if (!d->initialReferenceAngle) {
166 d->initialReferenceAngle = currentAngle;
167 return 0;
168 }
169 qreal rotationAngle = 0;
170 const qreal relativeAngle = (180 / M_PI) * (currentAngle - d->initialReferenceAngle);
171 const qreal rotationThreshold = 15;
172
173 // if the canvas is moved in either direction with an angle greater than the threshold, we rotate the canvas in
174 // that direction by 15°.
175 if (std::abs(relativeAngle) >= rotationThreshold && std::abs(relativeAngle) <= (360 - rotationThreshold)) {
176 // set reference as currentAngle to check if we go beyond the threshold next time
177 d->initialReferenceAngle = currentAngle;
178
179 if (std::abs(relativeAngle) <= 180) {
180 rotationAngle = KisAlgebra2D::copysign(15.0, relativeAngle);
181 } else {
182 // if we're over 180, it means the canvas has to be rotated in the opposite direction of the current
183 // angle. E.g if the relative angle is +341° then we move the canvas by -15° (because the actual effect
184 // is 341 - 360 = -19° on the original theta).
185 rotationAngle = KisAlgebra2D::copysign(15.0, -relativeAngle);
186 }
187 }
188 return rotationAngle;
189 }
190 default:
191 qWarning() << "KisZoomAndRotateAction: Unrecognized shortcut" << d->shortcutIndex;
192 return 0;
193 }
194}
QPointF p0
QPointF p1
KisInputActionGroup
@ ViewTransformActionGroup
Abstract base class for input actions.
virtual void inputEvent(QEvent *event)
static KisInputManager * inputManager
void setShortcutIndexes(const QHash< QString, int > &indexes)
void setName(const QString &name)
KisCoordinatesConverter * coordinatesConverter
const KoViewConverter * viewConverter() const override
void rotateCanvas(qreal angle, const std::optional< KoViewTransformStillPoint > &stillPoint, bool isNativeGesture=false)
KoViewTransformStillPoint makeWidgetStillPoint(const QPointF &viewPoint) const override
Creates a still point that links the viewPoint of the widget to the corresponding point of the image.
KisCanvas2 * canvas() const
void cursorMovedAbsolute(const QPointF &lastPos, const QPointF &pos) override
void deactivate(int shortcut) override
KisInputActionGroup inputActionGroup(int shortcut) const override
void begin(int shortcut, QEvent *event) override
void activate(int shortcut) override
const QScopedPointer< Private > d
void inputEvent(QEvent *event) override
qreal canvasRotationAngle(QPointF p0, QPointF p1)
KoCanvasController * canvasController() const
void setZoom(KoZoomMode::Mode mode, qreal zoom) override
virtual void zoom(qreal *zoomX, qreal *zoomY) const
@ ZOOM_CONSTANT
zoom x %
Definition KoZoomMode.h:24
static bool qFuzzyCompare(half p1, half p2)
#define M_PI
Definition kis_global.h:111
qreal angleForSnapping(qreal angle)
T copysign(T x, T y)
KisCanvas2 * canvas