Commit 9c919cde authored by Dmitry Kazakov's avatar Dmitry Kazakov

Split the transform tool into several classes

Now the tool is split into three classes:

o KisToolTransform
o KisWarpTransformStrategy
o KisFreeTransformStrategy

They handle the transformation modes separately.

This patch also ports the handwritten gradient descent methods into
GNU Scientific Library.
parent 7e2450a4
......@@ -293,6 +293,14 @@ if (USEOPENGL)
macro_log_feature(HAVE_OPENGL "OpenGL" "OpenGL support" "" FALSE "" "Required by parts of Krita and optionally by flake")
endif(USEOPENGL)
##
## Test for GNU Scientific Library
##
macro_optional_find_package(GSL)
macro_log_feature(GSL_FOUND "GSL" "GNU Scientific Library" "http://www.gnu.org/software/gsl"
FALSE "1.7" "Required by Krita's Transform tool and Sheets' solver plugin")
macro_bool_to_01(GSL_FOUND HAVE_GSL)
configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h )
##
## Test for kdepimlibs
......
/* config-gsl.h. Generated by cmake from config-gsl.h.cmake */
/* Define if you have GSL, GNU Scientific Library */
#cmakedefine HAVE_GSL 1
......@@ -139,5 +139,9 @@ inline qreal kisDistance(const QPointF &pt1, const QPointF &pt2) {
return std::sqrt(pow2(pt1.x() - pt2.x()) + pow2(pt1.y() - pt2.y()));
}
inline qreal kisSquareDistance(const QPointF &pt1, const QPointF &pt2) {
return pow2(pt1.x() - pt2.x()) + pow2(pt1.y() - pt2.y());
}
#endif // KISGLOBAL_H_
set(kritatooltransform_PART_SRCS
tool_transform.cc
tool_transform_args.cc
tool_transform_changes_tracker.cpp
kis_tool_transform.cc
kis_tool_transform_config_widget.cpp
kis_warp_transform_strategy.cpp
kis_free_transform_strategy.cpp
kis_free_transform_strategy_gsl_helpers.cpp
kis_transform_utils.cpp
strokes/transform_stroke_strategy.cpp
)
......@@ -14,9 +16,11 @@ kde4_add_ui_files(kritatooltransform_PART_SRCS wdg_tool_transform.ui)
kde4_add_plugin(kritatooltransform ${kritatooltransform_PART_SRCS})
if (NOT GSL_FOUND)
message (WARNING "KRITA WARNING! No GNU Scientific Library was found! Krita's Transform Tool will not be able to scale the image with handles. Please install GSL library.")
endif (NOT GSL_FOUND)
target_link_libraries(kritatooltransform kritaui)
target_link_libraries(kritatooltransform kritaui ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES})
install(TARGETS kritatooltransform DESTINATION ${PLUGIN_INSTALL_DIR})
......
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_free_transform_strategy.h"
#include <QPointF>
#include <QPainter>
#include <QMatrix4x4>
#include <kstandarddirs.h>
#include "kis_coordinates_converter.h"
#include "tool_transform_args.h"
#include "transform_transaction_properties.h"
#include "krita_utils.h"
#include "kis_cursor.h"
#include "kis_transform_utils.h"
#include "kis_free_transform_strategy_gsl_helpers.h"
struct KisFreeTransformStrategy::Private
{
Private(KisFreeTransformStrategy *_q,
const KisCoordinatesConverter *_converter,
ToolTransformArgs &_currentArgs,
TransformTransactionProperties &_transaction,
QTransform &_transform)
: q(_q),
converter(_converter),
currentArgs(_currentArgs),
transaction(_transaction),
transform(_transform),
imageTooBig(false)
{
scaleCursors[0] = KisCursor::sizeHorCursor();
scaleCursors[1] = KisCursor::sizeFDiagCursor();
scaleCursors[2] = KisCursor::sizeVerCursor();
scaleCursors[3] = KisCursor::sizeBDiagCursor();
scaleCursors[4] = KisCursor::sizeHorCursor();
scaleCursors[5] = KisCursor::sizeFDiagCursor();
scaleCursors[6] = KisCursor::sizeVerCursor();
scaleCursors[7] = KisCursor::sizeBDiagCursor();
shearCursorPixmap.load(KStandardDirs::locate("data", "calligra/icons/cursor_shear.png"));
}
KisFreeTransformStrategy *q;
const KisCoordinatesConverter *converter;
//////
ToolTransformArgs &currentArgs;
//////
TransformTransactionProperties &transaction;
QTransform thumbToImageTransform;
QImage originalImage;
QTransform paintingTransform;
QPointF paintingOffset;
QImage transformedImage;
QTransform handlesTransform;
void recalculateTransformations();
inline QPointF imageToThumb(const QPointF &pt, bool useFlakeOptimization);
/// standard ended ///
StrokeFunction function;
struct HandlePoints {
QPointF topLeft;
QPointF topMiddle;
QPointF topRight;
QPointF middleLeft;
QPointF rotationCenter;
QPointF middleRight;
QPointF bottomLeft;
QPointF bottomMiddle;
QPointF bottomRight;
};
HandlePoints transformedHandles;
QTransform &transform;
QCursor scaleCursors[8]; // cursors for the 8 directions
QPixmap shearCursorPixmap;
bool imageTooBig;
ToolTransformArgs clickArgs;
QPointF clickPos;
QCursor getScaleCursor(const QPointF &handlePt);
QCursor getShearCursor(const QPointF &start, const QPointF &end);
void recalculateTransformedHandles();
};
KisFreeTransformStrategy::KisFreeTransformStrategy(const KisCoordinatesConverter *converter,
ToolTransformArgs &currentArgs,
TransformTransactionProperties &transaction,
QTransform &transform)
: m_d(new Private(this, converter, currentArgs, transaction, transform))
{
}
KisFreeTransformStrategy::~KisFreeTransformStrategy()
{
}
void KisFreeTransformStrategy::Private::recalculateTransformedHandles()
{
transformedHandles.topLeft = transform.map(transaction.originalTopLeft());
transformedHandles.topMiddle = transform.map(transaction.originalMiddleTop());
transformedHandles.topRight = transform.map(transaction.originalTopRight());
transformedHandles.middleLeft = transform.map(transaction.originalMiddleLeft());
transformedHandles.rotationCenter = transform.map(currentArgs.originalCenter() + currentArgs.rotationCenterOffset());
transformedHandles.middleRight = transform.map(transaction.originalMiddleRight());
transformedHandles.bottomLeft = transform.map(transaction.originalBottomLeft());
transformedHandles.bottomMiddle = transform.map(transaction.originalMiddleBottom());
transformedHandles.bottomRight = transform.map(transaction.originalBottomRight());
}
void KisFreeTransformStrategy::setTransformFunction(const QPointF &mousePos, bool usePerspective)
{
if (usePerspective) {
m_d->function = PERSPECTIVE;
return;
}
QPolygonF transformedPolygon = m_d->transform.map(QPolygonF(m_d->transaction.originalRect()));
m_d->function =
transformedPolygon.containsPoint(mousePos, Qt::OddEvenFill) ? MOVE : ROTATE;
qreal handleRadius = KisTransformUtils::effectiveHandleGrabRadius(m_d->converter);
qreal handleRadiusSq = pow2(handleRadius);
qreal rotationHandleRadiusSq = pow2(KisTransformUtils::effectiveHandleGrabRadius(m_d->converter));
if (kisSquareDistance(mousePos, m_d->transformedHandles.topMiddle) <= handleRadiusSq)
m_d->function = TOPSCALE;
if (kisSquareDistance(mousePos, m_d->transformedHandles.topRight) <= handleRadiusSq)
m_d->function = TOPRIGHTSCALE;
if (kisSquareDistance(mousePos, m_d->transformedHandles.middleRight) <= handleRadiusSq)
m_d->function = RIGHTSCALE;
if (kisSquareDistance(mousePos, m_d->transformedHandles.bottomRight) <= handleRadiusSq)
m_d->function = BOTTOMRIGHTSCALE;
if (kisSquareDistance(mousePos, m_d->transformedHandles.bottomMiddle) <= handleRadiusSq)
m_d->function = BOTTOMSCALE;
if (kisSquareDistance(mousePos, m_d->transformedHandles.bottomLeft) <= handleRadiusSq)
m_d->function = BOTTOMLEFTSCALE;
if (kisSquareDistance(mousePos, m_d->transformedHandles.middleLeft) <= handleRadiusSq)
m_d->function = LEFTSCALE;
if (kisSquareDistance(mousePos, m_d->transformedHandles.topLeft) <= handleRadiusSq)
m_d->function = TOPLEFTSCALE;
if (kisSquareDistance(mousePos, m_d->transformedHandles.rotationCenter) <= rotationHandleRadiusSq)
m_d->function = MOVECENTER;
if (m_d->function == ROTATE || m_d->function == MOVE) {
QRectF originalRect = m_d->transaction.originalRect();
QPointF t = m_d->transform.inverted().map(mousePos);
if (t.x() >= originalRect.left() && t.x() <= originalRect.right()) {
if (fabs(t.y() - originalRect.top()) <= handleRadius)
m_d->function = TOPSHEAR;
if (fabs(t.y() - originalRect.bottom()) <= handleRadius)
m_d->function = BOTTOMSHEAR;
}
if (t.y() >= originalRect.top() && t.y() <= originalRect.bottom()) {
if (fabs(t.x() - originalRect.left()) <= handleRadius)
m_d->function = LEFTSHEAR;
if (fabs(t.x() - originalRect.right()) <= handleRadius)
m_d->function = RIGHTSHEAR;
}
}
}
KisFreeTransformStrategy::StrokeFunction KisFreeTransformStrategy::functionWORKAROUND() const
{
return m_d->function;
}
QCursor KisFreeTransformStrategy::Private::getScaleCursor(const QPointF &handlePt)
{
QPointF handlePtInWidget = converter->imageToWidget(handlePt);
QPointF centerPtInWidget = converter->imageToWidget(currentArgs.transformedCenter());
QPointF direction = handlePtInWidget - centerPtInWidget;
qreal angle = atan2(direction.y(), direction.x());
angle = normalizeAngle(angle);
int octant = qRound(angle * 4. / M_PI) % 8;
return scaleCursors[octant];
}
QCursor KisFreeTransformStrategy::Private::getShearCursor(const QPointF &start, const QPointF &end)
{
QPointF startPtInWidget = converter->imageToWidget(start);
QPointF endPtInWidget = converter->imageToWidget(end);
QPointF direction = endPtInWidget - startPtInWidget;
qreal angle = atan2(-direction.y(), direction.x());
return QCursor(shearCursorPixmap.transformed(QTransform().rotateRadians(-angle)));
}
QCursor KisFreeTransformStrategy::getCurrentCursor() const
{
QCursor cursor;
switch (m_d->function) {
case MOVE:
cursor = KisCursor::moveCursor();
break;
case ROTATE:
cursor = KisCursor::rotateCursor();
break;
case PERSPECTIVE:
//TODO: find another cursor for perspective
cursor = KisCursor::rotateCursor();
break;
case RIGHTSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.middleRight);
break;
case TOPSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.topMiddle);
break;
case LEFTSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.middleLeft);
break;
case BOTTOMSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.bottomMiddle);
break;
case TOPRIGHTSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.topRight);
break;
case BOTTOMLEFTSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.bottomLeft);
break;
case TOPLEFTSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.topLeft);
break;
case BOTTOMRIGHTSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.bottomRight);
break;
case MOVECENTER:
cursor = KisCursor::handCursor();
break;
case BOTTOMSHEAR:
cursor = m_d->getShearCursor(m_d->transformedHandles.bottomLeft, m_d->transformedHandles.bottomRight);
break;
case RIGHTSHEAR:
cursor = m_d->getShearCursor(m_d->transformedHandles.bottomRight, m_d->transformedHandles.topRight);
break;
case TOPSHEAR:
cursor = m_d->getShearCursor(m_d->transformedHandles.topRight, m_d->transformedHandles.topLeft);
break;
case LEFTSHEAR:
cursor = m_d->getShearCursor(m_d->transformedHandles.topLeft, m_d->transformedHandles.bottomLeft);
break;
}
return cursor;
}
void KisFreeTransformStrategy::paint(QPainter &gc)
{
gc.save();
gc.setOpacity(0.9);
gc.setTransform(m_d->paintingTransform, true);
gc.drawImage(m_d->paintingOffset, m_d->originalImage);
gc.restore();
// Draw Handles
qreal handlesExtraScale = KisTransformUtils::scaleFromAffineMatrix(m_d->handlesTransform);
qreal d = KisTransformUtils::handleVisualRadius / handlesExtraScale;
QRectF handleRect(-0.5 * d, -0.5 * d, d, d);
qreal r = KisTransformUtils::rotationHandleVisualRadius / handlesExtraScale;
QRectF rotationCenterRect(-0.5 * r, -0.5 * r, r, r);
QPainterPath handles;
handles.moveTo(m_d->transaction.originalTopLeft());
handles.lineTo(m_d->transaction.originalTopRight());
handles.lineTo(m_d->transaction.originalBottomRight());
handles.lineTo(m_d->transaction.originalBottomLeft());
handles.lineTo(m_d->transaction.originalTopLeft());
handles.addRect(handleRect.translated(m_d->transaction.originalTopLeft()));
handles.addRect(handleRect.translated(m_d->transaction.originalTopRight()));
handles.addRect(handleRect.translated(m_d->transaction.originalBottomLeft()));
handles.addRect(handleRect.translated(m_d->transaction.originalBottomRight()));
handles.addRect(handleRect.translated(m_d->transaction.originalMiddleLeft()));
handles.addRect(handleRect.translated(m_d->transaction.originalMiddleRight()));
handles.addRect(handleRect.translated(m_d->transaction.originalMiddleTop()));
handles.addRect(handleRect.translated(m_d->transaction.originalMiddleBottom()));
QPointF rotationCenter = m_d->transaction.originalCenter() + m_d->currentArgs.rotationCenterOffset();
QPointF dx(r + 3, 0);
QPointF dy(0, r + 3);
handles.addEllipse(rotationCenterRect.translated(rotationCenter));
handles.moveTo(rotationCenter - dx);
handles.lineTo(rotationCenter + dx);
handles.moveTo(rotationCenter - dy);
handles.lineTo(rotationCenter + dy);
gc.save();
gc.setTransform(m_d->handlesTransform, true);
QPen pen[2];
//pen[0].setCosmetic(true);
pen[0].setWidth(1);
//pen[1].setCosmetic(true);
pen[1].setWidth(2);
pen[1].setColor(Qt::lightGray);
for (int i = 1; i >= 0; --i) {
gc.setPen(pen[i]);
gc.drawPath(handles);
}
gc.restore();
}
void KisFreeTransformStrategy::externalConfigChanged()
{
m_d->recalculateTransformations();
}
bool KisFreeTransformStrategy::beginPrimaryAction(const QPointF &pt)
{
m_d->clickArgs = m_d->currentArgs;
m_d->clickPos = pt;
return true;
}
void KisFreeTransformStrategy::continuePrimaryAction(const QPointF &mousePos, bool specialModifierActve)
{
switch (m_d->function) {
case MOVE: {
QPointF diff = mousePos - m_d->clickPos;
if (specialModifierActve) {
KisTransformUtils::MatricesPack m(m_d->clickArgs);
QTransform t = m.S * m.projectedP;
QPointF originalDiff = t.inverted().map(diff);
if (qAbs(originalDiff.x()) >= qAbs(originalDiff.y())) {
originalDiff.setY(0);
} else {
originalDiff.setX(0);
}
diff = t.map(originalDiff);
}
m_d->currentArgs.setTransformedCenter(m_d->clickArgs.transformedCenter() + diff);
break;
}
case ROTATE:
{
KisTransformUtils::MatricesPack clickM(m_d->clickArgs);
QTransform clickT = clickM.finalTransform();
QPointF rotationCenter = m_d->clickArgs.originalCenter() + m_d->clickArgs.rotationCenterOffset();
QPointF clickMouseImagePos = clickT.inverted().map(m_d->clickPos) - rotationCenter;
QPointF mouseImagePos = clickT.inverted().map(mousePos) - rotationCenter;
qreal a1 = atan2(clickMouseImagePos.y(), clickMouseImagePos.x());
qreal a2 = atan2(mouseImagePos.y(), mouseImagePos.x());
double theta = -a1 + a2;
m_d->currentArgs.setAZ(normalizeAngle(m_d->clickArgs.aZ() + theta));
KisTransformUtils::MatricesPack m(m_d->currentArgs);
QTransform t = m.finalTransform();
QPointF newRotationCenter = t.map(m_d->currentArgs.originalCenter() + m_d->currentArgs.rotationCenterOffset());
QPointF oldRotationCenter = clickT.map(m_d->clickArgs.originalCenter() + m_d->clickArgs.rotationCenterOffset());
m_d->currentArgs.setTransformedCenter(m_d->currentArgs.transformedCenter() + oldRotationCenter - newRotationCenter);
}
break;
case PERSPECTIVE:
{
QPointF diff = mousePos - m_d->clickPos;
double thetaX = - diff.y() * M_PI / m_d->transaction.originalHalfHeight() / 2 / fabs(m_d->currentArgs.scaleY());
m_d->currentArgs.setAX(normalizeAngle(m_d->clickArgs.aX() + thetaX));
qreal sign = qAbs(m_d->currentArgs.aX() - M_PI) < M_PI / 2 ? -1.0 : 1.0;
double thetaY = sign * diff.x() * M_PI / m_d->transaction.originalHalfWidth() / 2 / fabs(m_d->currentArgs.scaleX());
m_d->currentArgs.setAY(normalizeAngle(m_d->clickArgs.aY() + thetaY));
KisTransformUtils::MatricesPack m(m_d->currentArgs);
QTransform t = m.finalTransform();
QPointF newRotationCenter = t.map(m_d->currentArgs.originalCenter() + m_d->currentArgs.rotationCenterOffset());
KisTransformUtils::MatricesPack clickM(m_d->clickArgs);
QTransform clickT = clickM.finalTransform();
QPointF oldRotationCenter = clickT.map(m_d->clickArgs.originalCenter() + m_d->clickArgs.rotationCenterOffset());
m_d->currentArgs.setTransformedCenter(m_d->currentArgs.transformedCenter() + oldRotationCenter - newRotationCenter);
}
break;
case TOPSCALE:
case BOTTOMSCALE: {
QPointF staticPoint;
QPointF movingPoint;
qreal extraSign;
if (m_d->function == TOPSCALE) {
staticPoint = m_d->transaction.originalMiddleBottom();
movingPoint = m_d->transaction.originalMiddleTop();
extraSign = -1.0;
} else {
staticPoint = m_d->transaction.originalMiddleTop();
movingPoint = m_d->transaction.originalMiddleBottom();
extraSign = 1.0;
}
QPointF mouseImagePos = m_d->transform.inverted().map(mousePos);
qreal sign = mouseImagePos.y() <= staticPoint.y() ? -extraSign : extraSign;
m_d->currentArgs.setScaleY(sign * m_d->currentArgs.scaleY());
QPointF staticPointInView = m_d->transform.map(staticPoint);
qreal dist = kisDistance(staticPointInView, mousePos);
GSL::ScaleResult1D result =
GSL::calculateScaleY(m_d->currentArgs,
staticPoint,
staticPointInView,
movingPoint,
dist);
m_d->currentArgs.setScaleY(result.scale);
m_d->currentArgs.setTransformedCenter(result.transformedCenter);
break;
}
case LEFTSCALE:
case RIGHTSCALE: {
QPointF staticPoint;
QPointF movingPoint;
qreal extraSign;
if (m_d->function == LEFTSCALE) {
staticPoint = m_d->transaction.originalMiddleRight();
movingPoint = m_d->transaction.originalMiddleLeft();
extraSign = -1.0;
} else {
staticPoint = m_d->transaction.originalMiddleLeft();
movingPoint = m_d->transaction.originalMiddleRight();
extraSign = 1.0;
}
QPointF mouseImagePos = m_d->transform.inverted().map(mousePos);
qreal sign = mouseImagePos.x() <= staticPoint.x() ? -extraSign : extraSign;
m_d->currentArgs.setScaleX(sign * m_d->currentArgs.scaleX());
QPointF staticPointInView = m_d->transform.map(staticPoint);
qreal dist = kisDistance(staticPointInView, mousePos);
GSL::ScaleResult1D result =
GSL::calculateScaleX(m_d->currentArgs,
staticPoint,
staticPointInView,
movingPoint,
dist);
m_d->currentArgs.setScaleX(result.scale);
m_d->currentArgs.setTransformedCenter(result.transformedCenter);
break;
}
case TOPRIGHTSCALE:
case BOTTOMRIGHTSCALE:
case TOPLEFTSCALE:
case BOTTOMLEFTSCALE: {
QPointF staticPoint;
QPointF movingPoint;
if (m_d->function == TOPRIGHTSCALE) {
staticPoint = m_d->transaction.originalBottomLeft();
movingPoint = m_d->transaction.originalTopRight();
} else if (m_d->function == BOTTOMRIGHTSCALE) {
staticPoint = m_d->transaction.originalTopLeft();
movingPoint = m_d->transaction.originalBottomRight();
} else if (m_d->function == TOPLEFTSCALE) {
staticPoint = m_d->transaction.originalBottomRight();
movingPoint = m_d->transaction.originalTopLeft();
} else {
staticPoint = m_d->transaction.originalTopRight();
movingPoint = m_d->transaction.originalBottomLeft();
}
QPointF staticPointInView = m_d->transform.map(staticPoint);
GSL::ScaleResult2D result =
GSL::calculateScale2D(m_d->currentArgs,
staticPoint,
staticPointInView,
movingPoint,
mousePos);
m_d->currentArgs.setScaleX(result.scaleX);
m_d->currentArgs.setScaleY(result.scaleY);
m_d->currentArgs.setTransformedCenter(result.transformedCenter);
break;
}
case MOVECENTER: {
QPointF pt = m_d->transform.inverted().map(mousePos);
pt = KisTransformUtils::clipInRect(pt, m_d->transaction.originalRect());
QPointF newRotationCenterOffset = pt - m_d->transaction.originalCenter();
if (specialModifierActve) {
if (qAbs(newRotationCenterOffset.x()) > qAbs(newRotationCenterOffset.y())) {
newRotationCenterOffset.ry() = 0;
} else {
newRotationCenterOffset.rx() = 0;
}
}
m_d->currentArgs.setRotationCenterOffset(newRotationCenterOffset);
emit requestResetRotationCenterButtons();
}
break;
case TOPSHEAR:
case BOTTOMSHEAR: {
KisTransformUtils::MatricesPack m(m_d->clickArgs);
QTransform backwardT = (m.S * m.projectedP).inverted();
QPointF diff = backwardT.map(mousePos - m_d->clickPos);
qreal sign = m_d->function == BOTTOMSHEAR ? 1.0 : -1.0;
// get the dx pixels corresponding to the current shearX factor
qreal dx = sign * m_d->clickArgs.shearX() * m_d->clickArgs.scaleY() * m_d->transaction.originalHalfHeight(); // get the dx pixels corresponding to the current shearX factor
dx += diff.x();
// calculate the new shearX factor
m_d->currentArgs.setShearX(sign * dx / m_d->currentArgs.scaleY() / m_d->transaction.originalHalfHeight()); // calculate the new shearX factor
break;
}
case LEFTSHEAR:
case RIGHTSHEAR: {
KisTransformUtils::MatricesPack m(m_d->clickArgs);
QTransform backwardT = (m.S * m.projectedP).inverted();
QPointF diff = backwardT.map(mousePos - m_d->clickPos);
qreal sign = m_d->function == RIGHTSHEAR ? 1.0 : -1.0;
// get the dx pixels corresponding to the current shearX factor
qreal dy = sign * m_d->clickArgs.shearY() * m_d->clickArgs.scaleX() * m_d->transaction.originalHalfWidth();
dy += diff.y();
// calculate the new shearY factor
m_d->currentArgs.setShearY(sign * dy / m_d->clickArgs.scaleX() / m_d->transaction.originalHalfWidth());
break;
}
}
m_d->recalculateTransformations();
}
bool KisFreeTransformStrategy::endPrimaryAction()
{
m_d->recalculateTransformations();
if (m_d->imageTooBig) {
m_d->currentArgs = m_d->clickArgs;
}
return !m_d->imageTooBig;