Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_four_point_interpolator_backward.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
3 * SPDX-FileCopyrightText: 2025 Agata Cacko <cacko.azh@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#ifndef __KIS_FOUR_POINT_INTERPOLATOR_BACKWARD_H
9#define __KIS_FOUR_POINT_INTERPOLATOR_BACKWARD_H
10
11#include <QPolygon>
12#include <QPointF>
13
14#include "kis_global.h"
15#include "kis_algebra_2d.h"
16
17//#define FPIB_DEBUG
18
19
28{
29public:
30 KisFourPointInterpolatorBackward(const QPolygonF &srcPolygon, const QPolygonF &dstPolygon) {
31#ifdef FPIB_DEBUG
32 m_dbgSrcPolygon = srcPolygon;
33 m_dbgDstPolygon = dstPolygon;
34#endif
35
36 m_a = dstPolygon[1] - dstPolygon[0]; // AB
37 m_b = dstPolygon[2] - dstPolygon[1]; // BD
38 m_c = dstPolygon[3] - dstPolygon[0]; // AC
39 m_d = m_b - m_c; // BD - AC
40
41 m_qA = m_c.x() * m_d.y() - m_c.y() * m_d.x();
42
43 m_srcBase = srcPolygon[0];
44 m_dstBase = dstPolygon[0];
45 m_xCoeff = srcPolygon[1].x() - srcPolygon[0].x(); // AB_src
46 m_yCoeff = srcPolygon[3].y() - srcPolygon[0].y(); // AC_src
47
48 m_qB_const = m_c.x() * m_a.y() - m_c.y() * m_a.x();
49
50 m_qD_div = 1.0 / (2 * m_qA);
51 }
52
57 inline bool isValid(const qreal tolerance = 0.1) const {
58 const qreal toleranceSq = pow2(tolerance);
59
60 const qreal sq1 = qAbs(m_qB_const);
61 const qreal sq2 = qAbs(KisAlgebra2D::crossProduct(m_b, m_c - m_b + m_a));
62
63 return sq1 + sq2 > 2 * toleranceSq;
64 }
65
66 inline QPointF fallbackSourcePoint() const {
67 return m_srcBase + QPointF(0.5 * m_xCoeff, 0.5 * m_yCoeff);
68 }
69
70 inline QPointF map(const QPointF &pt) {
71 setX(pt.x());
72 setY(pt.y());
73 return getValue();
74 }
75
76 inline void setX(qreal x) {
77#ifdef FPIB_DEBUG
78 m_dbgOrigX = x;
79#endif
80 x -= m_dstBase.x();
81
82 m_qB_varX = - x * m_d.y();
83 m_qC_varX = - x * m_a.y();
84 m_px = x;
85 }
86
87 inline void setY(qreal y) {
88#ifdef FPIB_DEBUG
89 m_dbgOrigY = y;
90#endif
91 y -= m_dstBase.y();
92
93 m_qB_varY = y * m_d.x();
94 m_qC_varY = y * m_a.x();
95 m_py = y;
96 }
97
98
99 inline QPointF getValue() const {
100 static const qreal eps = 1e-6; // pixels in Krita only get to 32-33k in every direction
101
102
103 qreal qB = m_qB_const + m_qB_varX + m_qB_varY;
104 qreal qC = m_qC_varX + m_qC_varY;
105
106 qreal nu = 0.0;
107 qreal D = 0.0;
108 qreal sqrtD = 0.0;
109
110 bool dontCheckOtherNu = false;
111
112 if (qAbs(m_qA) < eps) {
113 nu = -qC / qB;
114 dontCheckOtherNu = true;
115 } else {
116 D = pow2(qB) - 4 * m_qA * qC;
117 if (D > 0.0) {
118 sqrtD = std::sqrt(D);
119 nu = (-qB - sqrtD) * m_qD_div;
120 } else {
121 nu = 0.0;
122 dontCheckOtherNu = true;
123 }
124 }
125
126 qreal nu1 = nu;
127
128 qreal xDenomNu1 = xBasedDenom(nu1);
129 qreal xMu1 = xBasedMu(nu1, xDenomNu1);
130
131 bool goodNu1 = inGoodRange(nu1);
132
133 if (goodNu1 && inGoodRange(xMu1)) {
134 return getResult(nu1, xMu1);
135 }
136
137 qreal yDenomNu1 = yBasedDenom(nu1);
138 qreal yMu1 = yBasedMu(nu1, yDenomNu1);
139
140
141 if (goodNu1 && inGoodRange(yMu1)) {
142 return getResult(nu1, yMu1);
143 }
144
145 qreal nu2 = nu1, xDenomNu2 = xDenomNu1, xMu2 = xMu1, yDenomNu2 = yDenomNu1, yMu2 = yMu1;
146
147 if (!dontCheckOtherNu) {
148 nu2 = (-qB + sqrtD) * m_qD_div;
149
150 xDenomNu2 = xBasedDenom(nu2);
151 xMu2 = xBasedMu(nu2, xDenomNu2);
152
153 bool goodNu2 = inGoodRange(nu2);
154
155 if (goodNu2 && inGoodRange(xMu2)) {
156 return getResult(nu2, xMu2);
157 }
158
159 yDenomNu2 = yBasedDenom(nu2);
160 yMu2 = xBasedMu(nu2, yDenomNu2);
161
162 if (goodNu2 && inGoodRange(yMu2)) {
163 return getResult(nu2, yMu2);
164 }
165 }
166
167
168 const int count = 4;
169 qreal denoms[count] = {xDenomNu1, yDenomNu1, xDenomNu2, yDenomNu2};
170 qreal mus[count] = {xMu1, yMu1, xMu2, yMu2};
171 qreal nus[count] = {nu1, nu1, nu2, nu2};
172 QPointF results[count];
173#ifdef FPIB_DEBUG
174 qCritical() << "For point: x = " << m_dbgOrigX << " y = " << m_dbgOrigY << " | src polygon = " << m_dbgSrcPolygon << " | dst polygon = " << m_dbgDstPolygon;
175 for (int i = 0; i < count; i++) {
176 qCritical() << "for i = " << i << ": denoms[i] = " << denoms[i] << "mus[i] = " << mus[i] << "nus[i] = " << nus[i] << "result: " << getResult(nus[i], mus[i]);
177 }
178#endif
179
180 int bestI = -1;
181 qreal distanceFromCenter = 0.0;
182 QPointF center = fallbackSourcePoint();
183
184 int meaningfulCount = dontCheckOtherNu ? 2 : 4;
185 for (int i = 0; i < meaningfulCount; i++) {
186
187 if (qAbs(denoms[i]) < eps) {
188 continue;
189 }
190
191 results[i] = getResult(nus[i], mus[i]);
192 qreal dist = KisAlgebra2D::normSquared(center - results[i]);
193 if (bestI < 0 || dist < distanceFromCenter) {
194 distanceFromCenter = dist;
195 bestI = i;
196 }
197 }
198#ifdef FPIB_DEBUG
199 if (bestI < 0) {
200 for (int i = 0; i < count; i++) {
201 qCritical() << "for i = " << i << "denom=" << denoms[i] << "mu=" << mus[i] << "nu=" << nus[i] << "result: " << getResult(nus[i], mus[i]);
202 }
203 qCritical() << "Center point: " << center;
204 qCritical() << "dont check other nu: " << dontCheckOtherNu;
205
206 }
207#endif
208
209 // there won't be any sane result here in case we didn't find any bestI
210 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(bestI >= 0, center);
211
212 return results[bestI];
213 }
214
215private:
216
217
218 inline qreal xBasedDenom(qreal nu) const {
219 return m_a.x() + nu * m_d.x();
220 }
221
222 inline qreal yBasedDenom(qreal nu) const {
223 return m_a.y() + nu * m_d.y();
224 }
225
226 inline qreal xBasedMu(qreal nu, qreal xBasedDenominator) const {
227 return (m_px - nu * m_c.x()) / xBasedDenominator;
228 }
229
230 inline qreal yBasedMu(qreal nu, qreal yBasedDenominator) const {
231 return (m_py - nu * m_c.y()) / yBasedDenominator;
232 }
233
234 inline QPointF getResult(qreal nu, qreal mu) const {
235 return m_srcBase + QPointF(mu * m_xCoeff, nu * m_yCoeff);
236 }
237
238 inline bool inGoodRange(qreal value) const {
239 return value >= 0.0 && value <= 1.0;
240 }
241
242
243private:
244 QPointF m_a; // AB
245 QPointF m_b; // BD
246 QPointF m_c; // AC
247 QPointF m_d; // m_b - m_c
248
249 qreal m_qA {0.0}; // quadratic equation A coeff
250 qreal m_qB_const {0.0}; // quadratic equation B coeff, const part
251 qreal m_qB_varX {0.0}; // quadratic equation B coeff, X-dep part
252 qreal m_qB_varY {0.0}; // quadratic equation B coeff, Y-dep part
253 qreal m_qC_varX {0.0}; // quadratic equation C coeff, X-dep part
254 qreal m_qC_varY {0.0}; // quadratic equation C coeff, Y-dep part
255 qreal m_qD_div {0.0}; // inverted divisor of the quadratic equation solution
256 qreal m_px {0.0}; // saved relative X coordinate
257 qreal m_py {0.0}; // saved relative Y coordinate
258
259 QPointF m_srcBase;
260 QPointF m_dstBase;
261 qreal m_xCoeff {0.0};
262 qreal m_yCoeff {0.0};
263
264#ifdef FPIB_DEBUG
265 QPolygonF m_dbgDstPolygon;
266 QPolygonF m_dbgSrcPolygon;
267
268 qreal m_dbgOrigX;
269 qreal m_dbgOrigY;
270#endif
271
272
273};
274
275#endif /* __KIS_FOUR_POINT_INTERPOLATOR_BACKWARD_H */
float value(const T *src, size_t ch)
qreal D(qreal t, const QPointF &P0, const QPointF &P1, const QPointF &P2, const QPointF &P3, const QPointF &p)
qreal yBasedMu(qreal nu, qreal yBasedDenominator) const
bool isValid(const qreal tolerance=0.1) const
KisFourPointInterpolatorBackward(const QPolygonF &srcPolygon, const QPolygonF &dstPolygon)
qreal xBasedMu(qreal nu, qreal xBasedDenominator) const
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
const qreal eps
T pow2(const T &x)
Definition kis_global.h:166
qreal normSquared(const T &a)
PointTypeTraits< T >::value_type crossProduct(const T &a, const T &b)