Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_free_transform_strategy_gsl_helpers.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
10#include "kis_transform_utils.h"
11
12#include <QMessageBox>
13#include <kis_algebra_2d.h>
14
15#include <Eigen/Dense>
16#include <kis_algebra_2d.h>
17
18namespace KisAlgebra2D {
19
20// TODO: avoid code-duplication
21inline Eigen::Matrix3d fromQTransformStraight(const QTransform &t)
22{
23 Eigen::Matrix3d m;
24
25 m << t.m11() , t.m12() , t.m13()
26 ,t.m21() , t.m22() , t.m23()
27 ,t.m31() , t.m32() , t.m33();
28
29 return m;
30}
31}
32
33#include <config-gsl.h>
34
35#ifdef HAVE_GSL
36#include <gsl/gsl_multimin.h>
37
38
39
40namespace GSL
41{
42
43 struct YScaleStrategy {
44 static qreal getScale(const ToolTransformArgs &args) {
45 return args.scaleY();
46 }
47
48 static void setScale(ToolTransformArgs *args, qreal scale) {
49 return args->setScaleY(scale);
50 }
51 };
52
53 struct XScaleStrategy {
54 static qreal getScale(const ToolTransformArgs &args) {
55 return args.scaleX();
56 }
57
58 static void setScale(ToolTransformArgs *args, qreal scale) {
59 return args->setScaleX(scale);
60 }
61 };
62
63 struct Params1D {
64 QPointF staticPointSrc;
65 QPointF staticPointDst;
66 QPointF movingPointSrc;
67 QPointF movingPointDst;
68
69 const ToolTransformArgs *srcArgs;
70 };
71
72 template <class Strategy>
73 double scaleError1D (const gsl_vector * x, void *paramsPtr)
74 {
75 double scale = gsl_vector_get(x, 0);
76 double tX = gsl_vector_get(x, 1);
77 double tY = gsl_vector_get(x, 2);
78
79 const Params1D *params = static_cast<const Params1D*>(paramsPtr);
80
81 ToolTransformArgs args(*params->srcArgs);
82
83 Strategy::setScale(&args, scale);
84 args.setTransformedCenter(QPointF(tX, tY));
85
87 QTransform t = m.finalTransform();
88
89 QPointF transformedStaticPoint = t.map(params->staticPointSrc);
90 QPointF transformedMovingPoint = t.map(params->movingPointSrc);
91
92 qreal result =
93 qAbs((transformedMovingPoint - params->movingPointDst).manhattanLength()) +
94 qAbs((transformedStaticPoint - params->staticPointDst).manhattanLength());
95
96 return result;
97 }
98
99 template <class Strategy>
100 ScaleResult1D calculateScale1D(const ToolTransformArgs &args,
101 const QPointF &staticPointSrc,
102 const QPointF &staticPointDst,
103 const QPointF &movingPointSrc,
104 const QPointF &movingPointDst)
105 {
106 const gsl_multimin_fminimizer_type *T =
107 gsl_multimin_fminimizer_nmsimplex2;
108 gsl_multimin_fminimizer *s = 0;
109 gsl_vector *ss, *x;
110 gsl_multimin_function minex_func;
111
112 size_t iter = 0;
113 int status;
114 double size;
115
116 /* Starting point */
117 x = gsl_vector_alloc (3);
118 gsl_vector_set (x, 0, Strategy::getScale(args));
119 gsl_vector_set (x, 1, args.transformedCenter().x());
120 gsl_vector_set (x, 2, args.transformedCenter().y());
121
123 QTransform t = m.finalTransform();
124
130 const QPointF transformedMovingPoint = t.map(movingPointSrc);
131 const qreal initialStep = 0.1 * kisDistance(transformedMovingPoint, movingPointDst);
132
133 /* Set initial step sizes to 0.1 */
134 ss = gsl_vector_alloc (3);
135 gsl_vector_set (ss, 0, 0.1);
136 gsl_vector_set (ss, 1, initialStep);
137 gsl_vector_set (ss, 2, initialStep);
138
139 Params1D p;
140
141 p.staticPointSrc = staticPointSrc;
142 p.staticPointDst = staticPointDst;
143 p.movingPointSrc = movingPointSrc;
144 p.movingPointDst = movingPointDst;
145 p.srcArgs = &args;
146
147 /* Initialize method and iterate */
148 minex_func.n = 3;
149 minex_func.f = scaleError1D<Strategy>;
150 minex_func.params = (void*)&p;
151
152 s = gsl_multimin_fminimizer_alloc (T, 3);
153 gsl_multimin_fminimizer_set (s, &minex_func, x, ss);
154
155 ScaleResult1D result;
156 result.scale = Strategy::getScale(args);
157 result.transformedCenter = args.transformedCenter();
158
159 do
160 {
161 iter++;
162 status = gsl_multimin_fminimizer_iterate(s);
163
164 if (status)
165 break;
166
167 size = gsl_multimin_fminimizer_size (s);
168 status = gsl_multimin_test_size (size, 1e-6);
169
175 if (status == GSL_SUCCESS && scaleError1D<Strategy>(s->x, &p) > 0.5) {
176 status = GSL_CONTINUE;
177 }
178
179 if (status == GSL_SUCCESS)
180 {
181 // dbgKrita << "*******Converged to minimum";
182 // dbgKrita << gsl_vector_get (s->x, 0)
183 // << gsl_vector_get (s->x, 1)
184 // << gsl_vector_get (s->x, 2)
185 // << "|" << s->fval << size;
186 result.scale = gsl_vector_get (s->x, 0);
187 result.transformedCenter =
188 QPointF(gsl_vector_get (s->x, 1),
189 gsl_vector_get (s->x, 2));
190 result.isValid = true;
191 }
192 }
193 while (status == GSL_CONTINUE && iter < 10000);
194
195 gsl_vector_free(x);
196 gsl_vector_free(ss);
197 gsl_multimin_fminimizer_free (s);
198
199 return result;
200 }
201
202 struct Params2D {
203 QPointF staticPointSrc;
204 QPointF staticPointDst;
205
206 QPointF movingPointSrc;
207 QPointF movingPointDst;
208
209 const ToolTransformArgs *srcArgs;
210 };
211
212 double scaleError2D (const gsl_vector * x, void *paramsPtr)
213 {
214 double scaleX = gsl_vector_get(x, 0);
215 double scaleY = gsl_vector_get(x, 1);
216 double tX = gsl_vector_get(x, 2);
217 double tY = gsl_vector_get(x, 3);
218
219 const Params2D *params = static_cast<const Params2D*>(paramsPtr);
220
221 ToolTransformArgs args(*params->srcArgs);
222
223 args.setScaleX(scaleX);
224 args.setScaleY(scaleY);
225 args.setTransformedCenter(QPointF(tX, tY));
226
228 QTransform t = m.finalTransform();
229
230 QPointF transformedStaticPoint = t.map(params->staticPointSrc);
231 QPointF transformedMovingPoint = t.map(params->movingPointSrc);
232
233 qreal result =
234 qAbs(transformedMovingPoint.x() - params->movingPointDst.x()) +
235 qAbs(transformedMovingPoint.y() - params->movingPointDst.y()) +
236 qAbs(transformedStaticPoint.x() - params->staticPointDst.x()) +
237 qAbs(transformedStaticPoint.y() - params->staticPointDst.y());
238
239 return result;
240 }
241
242 ScaleResult2D calculateScale2D(const ToolTransformArgs &args,
243 const QPointF &staticPointSrc,
244 const QPointF &staticPointDst,
245 const QPointF &movingPointSrc,
246 const QPointF &movingPointDst)
247 {
248 const gsl_multimin_fminimizer_type *T =
249 gsl_multimin_fminimizer_nmsimplex2;
250 gsl_multimin_fminimizer *s = 0;
251 gsl_vector *ss, *x;
252 gsl_multimin_function minex_func;
253
254 size_t iter = 0;
255 int status;
256 double size;
257
258 /* Starting point */
259 x = gsl_vector_alloc (4);
260 gsl_vector_set (x, 0, args.scaleX());
261 gsl_vector_set (x, 1, args.scaleY());
262 gsl_vector_set (x, 2, args.transformedCenter().x());
263 gsl_vector_set (x, 3, args.transformedCenter().y());
264
265 /* Set initial step sizes to 0.1 */
266 ss = gsl_vector_alloc (4);
267 gsl_vector_set (ss, 0, 0.1);
268 gsl_vector_set (ss, 1, 0.1);
269 gsl_vector_set (ss, 2, 10);
270 gsl_vector_set (ss, 3, 10);
271
272 Params2D p;
273
274 p.staticPointSrc = staticPointSrc;
275 p.staticPointDst = staticPointDst;
276 p.movingPointSrc = movingPointSrc;
277 p.movingPointDst = movingPointDst;
278 p.srcArgs = &args;
279
280 /* Initialize method and iterate */
281 minex_func.n = 4;
282 minex_func.f = scaleError2D;
283 minex_func.params = (void*)&p;
284
285 s = gsl_multimin_fminimizer_alloc (T, 4);
286 gsl_multimin_fminimizer_set (s, &minex_func, x, ss);
287
288 ScaleResult2D result;
289 result.scaleX = args.scaleX();
290 result.scaleY = args.scaleY();
291 result.transformedCenter = args.transformedCenter();
292
293 do
294 {
295 iter++;
296 status = gsl_multimin_fminimizer_iterate(s);
297
298 if (status)
299 break;
300
301 size = gsl_multimin_fminimizer_size (s);
302 status = gsl_multimin_test_size (size, 1e-6);
303
309 if (status == GSL_SUCCESS && scaleError2D(s->x, &p) > 0.5) {
310 status = GSL_CONTINUE;
311 }
312
313 if (status == GSL_SUCCESS)
314 {
315 // dbgKrita << "*******Converged to minimum";
316 // dbgKrita << gsl_vector_get (s->x, 0)
317 // << gsl_vector_get (s->x, 1)
318 // << gsl_vector_get (s->x, 2)
319 // << gsl_vector_get (s->x, 3)
320 // << "|" << s->fval << size;
321 result.scaleX = gsl_vector_get (s->x, 0);
322 result.scaleY = gsl_vector_get (s->x, 1);
323 result.transformedCenter =
324 QPointF(gsl_vector_get (s->x, 2),
325 gsl_vector_get (s->x, 3));
326 result.isValid = true;
327 }
328 }
329 while (status == GSL_CONTINUE && iter < 10000);
330
331 gsl_vector_free(x);
332 gsl_vector_free(ss);
333 gsl_multimin_fminimizer_free (s);
334
335 return result;
336 }
337
338 ScaleResult2D calculateScale2DAffine(const ToolTransformArgs &args,
339 const QPointF &staticPointSrc,
340 const QPointF &staticPointDst,
341 const QPointF &movingPointSrc,
342 const QPointF &movingPointDst)
343 {
345
355
356 Eigen::Matrix3d TS_t = KisAlgebra2D::fromQTransformStraight(m.TS.transposed());
357 Eigen::Matrix3d S_t = KisAlgebra2D::fromQTransformStraight(m.S.transposed());
358 Eigen::Matrix3d projP_t = KisAlgebra2D::fromQTransformStraight(m.projectedP.transposed());
359 Eigen::Matrix3d BRI_t = KisAlgebra2D::fromQTransformStraight(m.BRI.transposed());
360
361 Eigen::Matrix3d M1 = projP_t * S_t;
362
363 Eigen::Matrix<double, 3, 2> P_src;
364 P_src << staticPointSrc.x(), movingPointSrc.x(),
365 staticPointSrc.y(), movingPointSrc.y(),
366 1, 1;
367
368 P_src = BRI_t * TS_t * P_src;
369
370 Eigen::Matrix<double, 3, 2> P_dst;
371 P_dst << staticPointDst.x(), movingPointDst.x(),
372 staticPointDst.y(), movingPointDst.y(),
373 1, 1;
374
375 Eigen::Matrix<double, 4, 4> A;
376 A << M1(0,0) * P_src(0,0), M1(0,1) * P_src(1,0), 1, 0,
377 M1(1,0) * P_src(0,0), M1(1,1) * P_src(1,0), 0, 1,
378 M1(0,0) * P_src(0,1), M1(0,1) * P_src(1,1), 1, 0,
379 M1(1,0) * P_src(0,1), M1(1,1) * P_src(1,1), 0, 1;
380
381 Eigen::Matrix<double, 4, 1> B;
382 B << P_dst(0,0), P_dst(1,0), P_dst(0,1), P_dst(1,1);
383
384
385 ScaleResult2D result;
386
387 Eigen::Matrix<double, 4, 1> X = A.inverse() * B;
388
389 result.isValid = !qFuzzyIsNull(A.determinant());
390
391 if (result.isValid) {
392 result.scaleX = X(0);
393 result.scaleY = X(1);
394 result.transformedCenter.rx() = X(2);
395 result.transformedCenter.ry() = X(3);
396 }
397
398 return result;
399 }
400
401 ScaleResult1D calculateScaleX(const ToolTransformArgs &args,
402 const QPointF &staticPointSrc,
403 const QPointF &staticPointDst,
404 const QPointF &movingPointSrc,
405 const QPointF &movingPointDst)
406 {
407 return calculateScale1D<XScaleStrategy>(args,
408 staticPointSrc,
409 staticPointDst,
410 movingPointSrc,
411 movingPointDst);
412 }
413
414 ScaleResult1D calculateScaleY(const ToolTransformArgs &args,
415 const QPointF &staticPointSrc,
416 const QPointF &staticPointDst,
417 const QPointF &movingPointSrc,
418 const QPointF &movingPointDst)
419 {
420 return calculateScale1D<YScaleStrategy>(args,
421 staticPointSrc,
422 staticPointDst,
423 movingPointSrc,
424 movingPointDst);
425 }
426
427}
428
429#else /* HAVE_GSL */
430
431namespace GSL
432{
433
435 {
436 QMessageBox::warning(qApp->activeWindow(),
437 i18nc("@title:window", "Krita"),
438 i18n("Krita was built without the support "
439 "of GNU Scientific Library, so you cannot scale "
440 "the selection with handles. Please compile "
441 "Krita with GNU Scientific Library support, or use "
442 "options widget for editing scale values manually."));
443 }
444
446 const QPointF &staticPointSrc,
447 const QPointF &staticPointDst,
448 const QPointF &movingPointSrc,
449 const QPointF &movingPointDst)
450 {
451 warnNoGSL();
452
453 ScaleResult2D result;
454 result.scaleX = args.scaleX();
455 result.scaleY = args.scaleY();
456 result.transformedCenter = args.transformedCenter();
457 return result;
458 }
459
461 const QPointF &staticPointSrc,
462 const QPointF &staticPointDst,
463 const QPointF &movingPointSrc,
464 const QPointF &movingPointDst)
465 {
466 return calculateScale2D(args, staticPointSrc, staticPointDst, movingPointSrc, movingPointDst);
467 }
468
470 const QPointF &staticPointSrc,
471 const QPointF &staticPointDst,
472 const QPointF &movingPointSrc,
473 const QPointF &movingPointDst)
474 {
475 warnNoGSL();
476
477 ScaleResult1D result;
478 result.scale = args.scaleX();
479 result.transformedCenter = args.transformedCenter();
480 return result;
481 }
482
484 const QPointF &staticPointSrc,
485 const QPointF &staticPointDst,
486 const QPointF &movingPointSrc,
487 const QPointF &movingPointDst)
488 {
489 warnNoGSL();
490
491 ScaleResult1D result;
492 result.scale = args.scaleY();
493 result.transformedCenter = args.transformedCenter();
494 return result;
495 }
496}
497
498#endif /* HAVE_GSL */
const Params2D p
QPointF transformedCenter() const
void setScaleX(double scaleX)
void setTransformedCenter(QPointF transformedCenter)
void setScaleY(double scaleY)
static bool qFuzzyIsNull(half h)
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:190
ScaleResult2D calculateScale2DAffine(const ToolTransformArgs &args, const QPointF &staticPointSrc, const QPointF &staticPointDst, const QPointF &movingPointSrc, const QPointF &movingPointDst)
ScaleResult2D calculateScale2D(const ToolTransformArgs &args, const QPointF &staticPointSrc, const QPointF &staticPointDst, const QPointF &movingPointSrc, const QPointF &movingPointDst)
ScaleResult1D calculateScaleX(const ToolTransformArgs &args, const QPointF &staticPointSrc, const QPointF &staticPointDst, const QPointF &movingPointSrc, const QPointF &movingPointDst)
ScaleResult1D calculateScaleY(const ToolTransformArgs &args, const QPointF &staticPointSrc, const QPointF &staticPointDst, const QPointF &movingPointSrc, const QPointF &movingPointDst)
const QString X
Eigen::Matrix3d fromQTransformStraight(const QTransform &t)
int size(const Forest< T > &forest)
Definition KisForest.h:1232