Commit 63bef1e4 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fix cursor drift when Pan/Zoom/Rotate

The actions should use absolute values for pan/zoom/rotation to
avoid relative drift between canvas and cursor.

NOTE: rotation still drifts a bit because of a bud in the underlying
      algorithm.

CCBUG:409460
parent 15ccec55
......@@ -32,6 +32,7 @@ public:
QHash<QString, int> indexes;
QPointF lastCursorPosition;
QPointF startCursorPosition;
static KisInputManager *inputManager;
};
......@@ -66,6 +67,7 @@ void KisAbstractInputAction::begin(int shortcut, QEvent *event)
if (event) {
d->lastCursorPosition = eventPosF(event);
d->startCursorPosition = d->lastCursorPosition;
}
}
......@@ -74,6 +76,7 @@ void KisAbstractInputAction::inputEvent(QEvent *event)
if (event) {
QPointF newPosition = eventPosF(event);
cursorMoved(d->lastCursorPosition, newPosition);
cursorMovedAbsolute(d->startCursorPosition, newPosition);
d->lastCursorPosition = newPosition;
}
}
......@@ -89,6 +92,12 @@ void KisAbstractInputAction::cursorMoved(const QPointF &lastPos, const QPointF &
Q_UNUSED(pos);
}
void KisAbstractInputAction::cursorMovedAbsolute(const QPointF &startPos, const QPointF &pos)
{
Q_UNUSED(startPos);
Q_UNUSED(pos);
}
bool KisAbstractInputAction::supportsHiResInputEvents() const
{
return false;
......
......@@ -203,6 +203,7 @@ protected:
* The default implementation of inputEvent calls this function.
*/
virtual void cursorMoved(const QPointF &lastPos, const QPointF &pos);
virtual void cursorMovedAbsolute(const QPointF &startPos, const QPointF &pos);
/**
* Convenience method to extract the position from a cursor movement event.
......
......@@ -171,9 +171,9 @@ void KisGammaExposureAction::begin(int shortcut, QEvent *event)
}
}
void KisGammaExposureAction::cursorMoved(const QPointF &lastPos, const QPointF &pos)
void KisGammaExposureAction::cursorMovedAbsolute(const QPointF &startPos, const QPointF &pos)
{
QPointF diff = -(pos - lastPos);
QPointF diff = -(pos - startPos);
const int step = 200;
......@@ -184,11 +184,11 @@ void KisGammaExposureAction::cursorMoved(const QPointF &lastPos, const QPointF &
if (d->mode == ExposureShortcut) {
d->baseExposure += qreal(diff.y()) / step;
interface->setCurrentExposure(d->baseExposure);
const qreal currentExposure = d->baseExposure + qreal(diff.y()) / step;
interface->setCurrentExposure(currentExposure);
} else if (d->mode == GammaShortcut) {
d->baseGamma += qreal(diff.y()) / step;
interface->setCurrentGamma(d->baseGamma);
const qreal currentGamma = d->baseExposure + qreal(diff.y()) / step;
interface->setCurrentGamma(currentGamma);
}
}
......
......@@ -50,7 +50,7 @@ public:
void deactivate(int shortcut) override;
void begin(int shortcut, QEvent *event = 0) override;
void cursorMoved(const QPointF &lastPos, const QPointF &pos) override;
void cursorMovedAbsolute(const QPointF &lastPos, const QPointF &pos) override;
bool isShortcutRequired(int shortcut) const override;
......
......@@ -41,6 +41,7 @@ public:
const int panDistance;
QPointF lastPosition;
QPointF originalPreferredCenter;
};
KisPanAction::KisPanAction()
......@@ -104,6 +105,8 @@ void KisPanAction::begin(int shortcut, QEvent *event)
break;
}
d->originalPreferredCenter = inputManager()->canvas()->canvasController()->preferredCenter();
break;
}
case PanLeftShortcut:
......@@ -161,10 +164,9 @@ void KisPanAction::inputEvent(QEvent *event)
KisAbstractInputAction::inputEvent(event);
}
void KisPanAction::cursorMoved(const QPointF &lastPos, const QPointF &pos)
void KisPanAction::cursorMovedAbsolute(const QPointF &startPos, const QPointF &pos)
{
QPointF relMovement = -(pos - lastPos);
inputManager()->canvas()->canvasController()->pan(relMovement.toPoint());
inputManager()->canvas()->canvasController()->setPreferredCenter(-pos + startPos + d->originalPreferredCenter);
}
QPointF KisPanAction::Private::averagePoint( QTouchEvent* event )
......
......@@ -52,7 +52,7 @@ public:
void end(QEvent *event) override;
void inputEvent(QEvent* event) override;
void cursorMoved(const QPointF &lastPos, const QPointF &pos) override;
void cursorMovedAbsolute(const QPointF &lastPos, const QPointF &pos) override;
bool isShortcutRequired(int shortcut) const override;
......
......@@ -32,12 +32,13 @@
class KisRotateCanvasAction::Private
{
public:
Private() : angleDrift(0), previousAngle(0) {}
Private() : previousAngle(0) {}
Shortcut mode;
qreal angleDrift;
qreal previousAngle;
qreal startRotation;
qreal previousRotation;
};
......@@ -92,11 +93,10 @@ void KisRotateCanvasAction::begin(int shortcut, QEvent *event)
switch(shortcut) {
case RotateModeShortcut:
d->mode = (Shortcut)shortcut;
break;
case DiscreteRotateModeShortcut:
d->mode = (Shortcut)shortcut;
d->angleDrift = 0;
d->startRotation = inputManager()->canvas()->rotationAngle();
d->previousRotation = 0;
break;
case RotateLeftShortcut:
canvasController->rotateCanvasLeft15();
......@@ -110,29 +110,27 @@ void KisRotateCanvasAction::begin(int shortcut, QEvent *event)
}
}
void KisRotateCanvasAction::cursorMoved(const QPointF &lastPos, const QPointF &pos)
void KisRotateCanvasAction::cursorMovedAbsolute(const QPointF &startPos, const QPointF &pos)
{
const KisCoordinatesConverter *converter = inputManager()->canvas()->coordinatesConverter();
QPointF centerPoint = converter->flakeToWidget(converter->flakeCenterPoint());
QPointF oldPoint = lastPos - centerPoint;
QPointF newPoint = pos - centerPoint;
const QPointF centerPoint = converter->flakeToWidget(converter->flakeCenterPoint());
const QPointF startPoint = startPos - centerPoint;
const QPointF newPoint = pos - centerPoint;
qreal oldAngle = atan2(oldPoint.y(), oldPoint.x());
qreal newAngle = atan2(newPoint.y(), newPoint.x());
const qreal oldAngle = atan2(startPoint.y(), startPoint.x());
const qreal newAngle = atan2(newPoint.y(), newPoint.x());
qreal angle = (180 / M_PI) * (newAngle - oldAngle);
qreal newRotation = (180 / M_PI) * (newAngle - oldAngle);
if (d->mode == DiscreteRotateModeShortcut) {
const qreal angleStep = 15;
qreal initialAngle = inputManager()->canvas()->rotationAngle();
qreal roundedAngle = qRound((initialAngle + angle + d->angleDrift) / angleStep) * angleStep - initialAngle;
d->angleDrift += angle - roundedAngle;
angle = roundedAngle;
newRotation = qRound(newRotation / angleStep) * angleStep;
}
KisCanvasController *canvasController =
dynamic_cast<KisCanvasController*>(inputManager()->canvas()->canvasController());
canvasController->rotateCanvas(angle);
canvasController->rotateCanvas(newRotation - d->previousRotation);
d->previousRotation = newRotation;
}
void KisRotateCanvasAction::inputEvent(QEvent* event)
......
......@@ -49,7 +49,7 @@ public:
void activate(int shortcut) override;
void deactivate(int shortcut) override;
void begin(int shortcut, QEvent *event) override;
void cursorMoved(const QPointF &lastPos, const QPointF &pos) override;
void cursorMovedAbsolute(const QPointF &startPos, const QPointF &pos);
void inputEvent(QEvent* event) override;
KisInputActionGroup inputActionGroup(int shortcut) const override;
......
......@@ -52,18 +52,18 @@ inline QPoint pointFromEvent(QEvent *event) {
class KisZoomAction::Private
{
public:
Private(KisZoomAction *qq) : q(qq), distance(0), lastDistance(0.f) {}
Private(KisZoomAction *qq) : q(qq), lastDistance(0.f) {}
QPointF centerPoint(QTouchEvent* event);
KisZoomAction *q;
int distance;
Shortcuts mode;
QPointF lastPosition;
float lastDistance;
QPoint startPoint;
qreal startZoom = 1.0;
qreal lastDescreteZoomDistance = 0.0;
void zoomTo(bool zoomIn, const QPoint &pos);
};
......@@ -165,7 +165,7 @@ void KisZoomAction::begin(int shortcut, QEvent *event)
switch(shortcut) {
case ZoomModeShortcut:
case RelativeZoomModeShortcut: {
d->startPoint = pointFromEvent(event);
d->startZoom = inputManager()->canvas()->viewManager()->zoomController()->zoomAction()->effectiveZoom();
d->mode = (Shortcuts)shortcut;
QTouchEvent *tevent = dynamic_cast<QTouchEvent*>(event);
if(tevent)
......@@ -174,9 +174,9 @@ void KisZoomAction::begin(int shortcut, QEvent *event)
}
case DiscreteZoomModeShortcut:
case RelativeDiscreteZoomModeShortcut:
d->startPoint = pointFromEvent(event);
d->startZoom = inputManager()->canvas()->viewManager()->zoomController()->zoomAction()->effectiveZoom();
d->lastDescreteZoomDistance = 0;
d->mode = (Shortcuts)shortcut;
d->distance = 0;
break;
case ZoomInShortcut:
d->zoomTo(true, pointFromEvent(event));
......@@ -253,46 +253,52 @@ void KisZoomAction::inputEvent( QEvent* event )
KisAbstractInputAction::inputEvent(event);
}
void KisZoomAction::cursorMoved(const QPointF &lastPos, const QPointF &pos)
void KisZoomAction::cursorMovedAbsolute(const QPointF &startPos, const QPointF &pos)
{
QPointF diff = -(pos - lastPos);
QPointF diff = -(pos - startPos);
const int stepCont = 100;
const int stepDisc = 20;
const int stepDisc = 50;
if (d->mode == ZoomModeShortcut ||
d->mode == RelativeZoomModeShortcut) {
const qreal zoom = inputManager()->canvas()->viewManager()->zoomController()->zoomAction()->effectiveZoom();
const qreal logDistance = std::pow(2.0, qreal(diff.y()) / qreal(stepCont));
KisConfig cfg(true);
float coeff;
qreal newZoom = zoom;
if (cfg.readEntry<bool>("InvertMiddleClickZoom", false)) {
coeff = 1.0 - qreal(diff.y()) / stepCont;
}
else {
coeff = 1.0 + qreal(diff.y()) / stepCont;
newZoom = d->startZoom / logDistance;
} else {
newZoom = d->startZoom * logDistance;
}
if (d->mode == ZoomModeShortcut) {
float zoom = coeff * inputManager()->canvas()->viewManager()->zoomController()->zoomAction()->effectiveZoom();
inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, zoom);
inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, newZoom);
} else {
const qreal coeff = newZoom / zoom;
KoCanvasControllerWidget *controller =
dynamic_cast<KoCanvasControllerWidget*>(
inputManager()->canvas()->canvasController());
controller->zoomRelativeToPoint(d->startPoint, coeff);
controller->zoomRelativeToPoint(startPos.toPoint(), coeff);
}
} else if (d->mode == DiscreteZoomModeShortcut ||
d->mode == RelativeDiscreteZoomModeShortcut) {
d->distance += diff.y();
QPoint stillPoint = d->mode == RelativeDiscreteZoomModeShortcut ?
d->startPoint : QPoint();
startPos.toPoint() : QPoint();
qreal currentDiff = qreal(diff.y()) / stepDisc - d->lastDescreteZoomDistance;
bool zoomIn = d->distance > 0;
while (qAbs(d->distance) > stepDisc) {
bool zoomIn = currentDiff > 0;
while (qAbs(currentDiff) > 1.0) {
d->zoomTo(zoomIn, stillPoint);
d->distance += zoomIn ? -stepDisc : stepDisc;
d->lastDescreteZoomDistance += zoomIn ? 1.0 : -1.0;
currentDiff = qreal(diff.y()) / stepDisc - d->lastDescreteZoomDistance;
}
}
}
......
......@@ -53,7 +53,7 @@ public:
void begin(int shortcut, QEvent *event = 0) override;
void inputEvent(QEvent* event) override;
void cursorMoved(const QPointF &lastPos, const QPointF &pos) override;
void cursorMovedAbsolute(const QPointF &startPos, const QPointF &pos) override;
bool isShortcutRequired(int shortcut) const override;
bool supportsHiResInputEvents() const override;
......
......@@ -60,10 +60,10 @@ void KisZoomAndRotateAction::begin(int shortcut, QEvent *event)
d->rotateAction->begin(shortcut, event);
}
void KisZoomAndRotateAction::cursorMoved(const QPointF &lastPos, const QPointF &pos)
void KisZoomAndRotateAction::cursorMovedAbsolute(const QPointF &lastPos, const QPointF &pos)
{
d->zoomAction->cursorMoved(lastPos, pos);
d->rotateAction->cursorMoved(lastPos, pos);
d->zoomAction->cursorMovedAbsolute(lastPos, pos);
d->rotateAction->cursorMovedAbsolute(lastPos, pos);
}
void KisZoomAndRotateAction::inputEvent(QEvent *event)
......
......@@ -36,7 +36,7 @@ public:
void activate(int shortcut) override;
void deactivate(int shortcut) override;
void begin(int shortcut, QEvent *event) override;
void cursorMoved(const QPointF &lastPos, const QPointF &pos) override;
void cursorMovedAbsolute(const QPointF &lastPos, const QPointF &pos) override;
void inputEvent(QEvent* event) override;
KisInputActionGroup inputActionGroup(int shortcut) const override;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment