Commit 9509bce9 authored by Dmitry Kazakov's avatar Dmitry Kazakov
Browse files

Fix small jumps in free transform when the object is mirrored

When the scale crosses the zero value, the algorithm could behave
in a less stable way. To overcome this issue, for the reference value
we shouldn't use any distance term. Instead we should calculate the
expected position for the moved point and pass it to the algorithm
explicitly.

BUG:426949
parent da452ab1
......@@ -482,40 +482,40 @@ void KisFreeTransformStrategy::continuePrimaryAction(const QPointF &mousePos,
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 staticPointInView = m_d->transform.map(staticPoint);
const QPointF movingPointInView = m_d->transform.map(movingPoint);
const QPointF projNormVector =
KisAlgebra2D::normalize(movingPointInView - staticPointInView);
const qreal projLength =
KisAlgebra2D::dotProduct(mousePos - staticPointInView, projNormVector);
const QPointF targetMovingPointInView = staticPointInView + projNormVector * projLength;
// override scale static point if it is locked
if ((m_d->currentArgs.transformAroundRotationCenter() ^ altModifierActive) &&
!qFuzzyCompare(anchorPoint.y(), movingPoint.y())) {
staticPoint = anchorPoint;
staticPointInView = m_d->transform.map(staticPoint);
}
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);
targetMovingPointInView);
if (!result.isValid) {
break;
......@@ -535,41 +535,40 @@ void KisFreeTransformStrategy::continuePrimaryAction(const QPointF &mousePos,
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 staticPointInView = m_d->transform.map(staticPoint);
const QPointF movingPointInView = m_d->transform.map(movingPoint);
const QPointF projNormVector =
KisAlgebra2D::normalize(movingPointInView - staticPointInView);
const qreal projLength =
KisAlgebra2D::dotProduct(mousePos - staticPointInView, projNormVector);
const QPointF targetMovingPointInView = staticPointInView + projNormVector * projLength;
// override scale static point if it is locked
if ((m_d->currentArgs.transformAroundRotationCenter() ^ altModifierActive) &&
!qFuzzyCompare(anchorPoint.x(), movingPoint.x())) {
staticPoint = anchorPoint;
staticPointInView = m_d->transform.map(staticPoint);
}
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);
targetMovingPointInView);
if (!result.isValid) {
break;
......
......@@ -22,12 +22,15 @@
#include "kis_transform_utils.h"
#include <QMessageBox>
#include <kis_algebra_2d.h>
#include <config-gsl.h>
#ifdef HAVE_GSL
#include <gsl/gsl_multimin.h>
namespace GSL
{
......@@ -54,9 +57,8 @@ namespace GSL
struct Params1D {
QPointF staticPointSrc;
QPointF staticPointDst;
QPointF movingPointSrc;
qreal viewDistance;
QPointF movingPointDst;
const ToolTransformArgs *srcArgs;
};
......@@ -82,11 +84,8 @@ namespace GSL
QPointF transformedMovingPoint = t.map(params->movingPointSrc);
qreal result =
qAbs(kisDistance(transformedStaticPoint, transformedMovingPoint)
- params->viewDistance) +
qAbs(transformedStaticPoint.x() - params->staticPointDst.x()) +
qAbs(transformedStaticPoint.y() - params->staticPointDst.y());
qAbs((transformedMovingPoint - params->movingPointDst).manhattanLength()) +
qAbs((transformedStaticPoint - params->staticPointDst).manhattanLength());
return result;
}
......@@ -96,7 +95,7 @@ namespace GSL
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
qreal viewDistance)
const QPointF &movingPointDst)
{
const gsl_multimin_fminimizer_type *T =
gsl_multimin_fminimizer_nmsimplex2;
......@@ -114,18 +113,29 @@ namespace GSL
gsl_vector_set (x, 1, args.transformedCenter().x());
gsl_vector_set (x, 2, args.transformedCenter().y());
KisTransformUtils::MatricesPack m(args);
QTransform t = m.finalTransform();
/**
* Approximate initial offset step by 10% of the moving point
* offset. It means that the destination point will be reached
* in at most 10 steps.
*/
const QPointF transformedMovingPoint = t.map(movingPointSrc);
const qreal initialStep = 0.1 * kisDistance(transformedMovingPoint, movingPointDst);
/* Set initial step sizes to 0.1 */
ss = gsl_vector_alloc (3);
gsl_vector_set (ss, 0, 0.1);
gsl_vector_set (ss, 1, 10);
gsl_vector_set (ss, 2, 10);
gsl_vector_set (ss, 1, initialStep);
gsl_vector_set (ss, 2, initialStep);
Params1D p;
p.staticPointSrc = staticPointSrc;
p.staticPointDst = staticPointDst;
p.movingPointSrc = movingPointSrc;
p.viewDistance = viewDistance;
p.movingPointDst = movingPointDst;
p.srcArgs = &args;
/* Initialize method and iterate */
......@@ -313,26 +323,26 @@ namespace GSL
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
qreal viewDistance)
const QPointF &movingPointDst)
{
return calculateScale1D<XScaleStrategy>(args,
staticPointSrc,
staticPointDst,
movingPointSrc,
viewDistance);
movingPointDst);
}
ScaleResult1D calculateScaleY(const ToolTransformArgs &args,
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
qreal viewDistance)
const QPointF &movingPointDst)
{
return calculateScale1D<YScaleStrategy>(args,
staticPointSrc,
staticPointDst,
movingPointSrc,
viewDistance);
movingPointDst);
}
}
......@@ -372,7 +382,7 @@ namespace GSL
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
qreal viewDistance)
const QPointF &movingPointDst)
{
warnNoGSL();
......@@ -386,7 +396,7 @@ namespace GSL
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
qreal viewDistance)
const QPointF &movingPointDst)
{
warnNoGSL();
......
......@@ -37,13 +37,13 @@ namespace GSL
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
qreal viewDistance);
const QPointF &movingPointDst);
ScaleResult1D calculateScaleY(const ToolTransformArgs &args,
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
qreal viewDistance);
const QPointF &movingPointDst);
struct ScaleResult2D {
ScaleResult2D() : scaleX(1.0), scaleY(1.0) {}
......
Supports Markdown
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