Krita Source Code Documentation
Loading...
Searching...
No Matches
EllipseInPolygon Class Reference

Public Member Functions

 EllipseInPolygon ()
 
bool isValid () const
 
bool setSimpleEllipseVertices (Ellipse &ellipse) const
 setSimpleEllipseVertices sets vertices of this ellipse to the "simple ellipse" class to be drawn and used
 
bool updateToPolygon (QVector< QPointF > _polygon)
 updateToPolygon This function makes all the necessary calculations and updates all data, not just the polygon according to the polygon that was provided as the parameter
 

Static Public Member Functions

static bool formulaRepresentsAnEllipse (double a, double b, double c)
 formulaRepresentsAnEllipse parameters are first three coefficients from a formula: ax^2 + bxy + cy^2 + dx + ey + f = 0
 

Public Attributes

double axisXLength {0.0}
 
double axisYLength {0.0}
 
double finalAxisAngle {0.0}
 
double finalAxisReverseAngleCos {0.0}
 
double finalAxisReverseAngleSin {0.0}
 
QVector< double > finalEllipseCenter
 
QVector< double > finalFormula
 
QVector< QPointF > finalVertices
 
QTransform originalTransform
 
QVector< QPointF > polygon
 
QVector< double > rerotatedFormula
 

Protected Member Functions

void setFormula (QVector< double > &formula, double a, double b, double c, double d, double e, double f)
 
void setPoint (QVector< double > &point, double x, double y)
 

Protected Attributes

bool m_valid {false}
 

Detailed Description

Definition at line 30 of file PerspectiveEllipseAssistant.cc.

Constructor & Destructor Documentation

◆ EllipseInPolygon()

EllipseInPolygon::EllipseInPolygon ( )

Definition at line 114 of file PerspectiveEllipseAssistant.cc.

115{
116 finalFormula.clear();
117 rerotatedFormula.clear();
118 finalFormula << 1 << 0 << 1 << 0 << 0 << 0;
119 rerotatedFormula << 1 << 0 << 1 << 0 << 0 << 0;
120
121 finalEllipseCenter.clear();
122 finalEllipseCenter << 0 << 0;
123
124 finalVertices.clear();
125 finalVertices << QPointF(-1, 0) << QPointF(1, 0) << QPointF(0, 1);
126}
QVector< double > finalEllipseCenter
QVector< double > rerotatedFormula

References finalEllipseCenter, finalFormula, finalVertices, and rerotatedFormula.

Member Function Documentation

◆ formulaRepresentsAnEllipse()

bool EllipseInPolygon::formulaRepresentsAnEllipse ( double a,
double b,
double c )
static

formulaRepresentsAnEllipse parameters are first three coefficients from a formula: ax^2 + bxy + cy^2 + dx + ey + f = 0

Parameters
a- first coefficient
b- second coefficient
c- third coefficient
Returns
true if the formula represents an ellipse, otherwise false

Definition at line 281 of file PerspectiveEllipseAssistant.cc.

282{
283 return (b*b - 4*a*c) < 0;
284}

◆ isValid()

bool EllipseInPolygon::isValid ( ) const
inline

Definition at line 66 of file PerspectiveEllipseAssistant.cc.

References m_valid.

◆ setFormula()

void EllipseInPolygon::setFormula ( QVector< double > & formula,
double a,
double b,
double c,
double d,
double e,
double f )
protected

Definition at line 286 of file PerspectiveEllipseAssistant.cc.

287{
288 if (formula.size() != 6) {
289 formula.clear();
290 formula << a << b << c << d << e << f;
291 } else {
292 formula[0] = a;
293 formula[1] = b;
294 formula[2] = c;
295 formula[3] = d;
296 formula[4] = e;
297 formula[5] = f;
298 }
299}

◆ setPoint()

void EllipseInPolygon::setPoint ( QVector< double > & point,
double x,
double y )
protected

Definition at line 301 of file PerspectiveEllipseAssistant.cc.

302{
303 if (point.size() != 2) {
304 point.clear();
305 point << x << y;
306 } else {
307 point[0] = x;
308 point[1] = y;
309 }
310}

◆ setSimpleEllipseVertices()

bool EllipseInPolygon::setSimpleEllipseVertices ( Ellipse & ellipse) const

setSimpleEllipseVertices sets vertices of this ellipse to the "simple ellipse" class to be drawn and used

Parameters
ellipse
Returns

Definition at line 273 of file PerspectiveEllipseAssistant.cc.

274{
275 if (finalVertices.size() > 2) {
276 return ellipse.set(finalVertices[0], finalVertices[1], finalVertices[2]);
277 }
278 return false;
279}
bool set(const QPointF &m1, const QPointF &m2, const QPointF &p)
Definition Ellipse.cc:23

References finalVertices, and Ellipse::set().

◆ updateToPolygon()

bool EllipseInPolygon::updateToPolygon ( QVector< QPointF > _polygon)

updateToPolygon This function makes all the necessary calculations and updates all data, not just the polygon according to the polygon that was provided as the parameter

Parameters
polygonpolygon that contains the ellipse
Returns
whether the ellipse is valid or not

Definition at line 128 of file PerspectiveEllipseAssistant.cc.

129{
130 QTransform transform;
131
132 m_valid = false; // let's make it false in case we return in the middle of the work
133 polygon = _polygon; // the assistant needs to know the polygon even when it doesn't allow for a correct ellipse
134
135 // this calculates the perspective transform that represents the current quad (polygon)
136 // this is the transform that changes the original (0, 0, 1, 1) square to the quad
137 // that means that our "original" ellipse is the circle in that square (with center in (0.5, 0.5), and radius 0.5)
138 if (!QTransform::squareToQuad(polygon, transform)) {
139 return false;
140 }
141
142 originalTransform = transform;
143
144 // using the perspective transform, we can calculate some points on the ellipse
145 // any points from the original ellipse would work here
146 // but pt1-4 are just the simplest ones to write
147 // and pR is another one easy to calculate (common point between the original ellipse and a line `y = x`)
148 QPointF pt1 = originalTransform.map(QPointF(0.5, 1.0));
149 QPointF pt2 = originalTransform.map(QPointF(1.0, 0.5));
150 QPointF pt3 = originalTransform.map(QPointF(0.5, 0.0));
151 QPointF pt4 = originalTransform.map(QPointF(0.0, 0.5));
152 // a point on the ellipse and on the `y = x` line
153 QPointF ptR = originalTransform.map(QPointF(0.5 - 1/(2*sqrt(2)), 0.5 - 1/(2*sqrt(2))));
154
155
156 // using the points from above (pt1-4 and ptR) we can construct a linear equation for the final ellipse formula
157 // the general ellipse formula is: `ax^2 + bxy + cy^2 + dx + ey + f = 0`
158 // but since a cannot ever be 0, we can temporarily reduce the formula to be `x^2 + Bxy + Cy^2 + Dx + Ey + F = 0`
159 // where B = b/a etc.
160 Eigen::MatrixXd A(5, 5);
161 A << ptR.x() * ptR.y(), ptR.y() * ptR.y(), ptR.x(), ptR.y(), 1.0,
162 pt1.x() * pt1.y(), pt1.y() * pt1.y(), pt1.x(), pt1.y(), 1.0,
163 pt2.x() * pt2.y(), pt2.y() * pt2.y(), pt2.x(), pt2.y(), 1.0,
164 pt3.x() * pt3.y(), pt3.y() * pt3.y(), pt3.x(), pt3.y(), 1.0,
165 pt4.x() * pt4.y(), pt4.y() * pt4.y(), pt4.x(), pt4.y(), 1.0;
166
167 Eigen::VectorXd bVector(5);
168 bVector << - ptR.x() * ptR.x(), - pt1.x() * pt1.x(), - pt2.x() * pt2.x(), - pt3.x() * pt3.x(), - pt4.x() * pt4.x();
169
170 Eigen::VectorXd xSolution = A.fullPivLu().solve(bVector);
171
172 // generic ellipse formula coefficients for the final formula
173 // assigned to new variables to better see the calculations
174 // (even with "x" as a solution vector variable, it would be difficult to spot error when everything looks like x(2)*x(4)/x(3)*x(1) etc.)
175 qreal a = 1;
176 qreal b = xSolution(0);
177
178 qreal c = xSolution(1);
179 qreal d = xSolution(2);
180
181 qreal e = xSolution(3);
182 qreal f = xSolution(4);
183
184 // check if this is an ellipse
185 if (!formulaRepresentsAnEllipse(a, b, c)) {
186 return false;
187 }
188
189 setFormula(finalFormula, a, b, c, d, e, f);
190
191 // x = (be - 2cd)/(4c - b^2)
192 // y = (bd - 2e)/(4c - b^2)
193 finalEllipseCenter.clear();
194 finalEllipseCenter << ((double)b*e - 2*c*d)/(4*c - b*b) << ((double)b*d - 2*e)/(4*c - b*b);
195 finalAxisAngle = qAtan2(b, a - c)/2;
196
197 // use finalAxisAngle to find the cos and sin
198 // and replace the final coordinate system with the rerotated one
199 qreal K = qCos(-finalAxisAngle);
200 qreal L = qSin(-finalAxisAngle);
201
202 // this allows to calculate the formula for the rerotated ellipse
203 qreal aprim = K*K*a - K*L*b + L*L*c;
204 qreal bprim = 2*K*L*a + K*K*b - L*L*b - 2*K*L*c;
205 qreal cprim = L*L*a + K*L*b + K*K*c;
206 qreal dprim = K*d - L*e;
207 qreal eprim = L*d + K*e;
208 qreal fprim = f;
209
210 if (!formulaRepresentsAnEllipse(aprim, bprim, cprim)) {
211 return false;
212 }
213
216
217 setFormula(rerotatedFormula, aprim, bprim, cprim, dprim, eprim, fprim);
218
219 // third attempt at new center:
220 // K' = K
221 // L' = -L
222 // note that this will be in a different place, because the ellipse wasn't moved to have center in (0, 0), but still rotate around point (0,0)
223 // and any point that is not (0, 0), when rotated around (0, 0) with an angle that isn't 0, 360 etc. degrees, will end up in a different place
224 QPointF rerotatedCenter = QPointF(K*finalEllipseCenter[0] - L*finalEllipseCenter[1], K*finalEllipseCenter[1] + L*finalEllipseCenter[0]);
225
226 qreal rx = sqrt(qPow(rerotatedCenter.x(), 2) + qPow(rerotatedCenter.y(), 2)*cprim/aprim - fprim/aprim);
227 qreal ry = sqrt(rx*rx*aprim/cprim);
228
229 axisXLength = rx;
230 axisYLength = ry;
231
232#if 0 // debug
233 // they should be very close to cprim, dprim etc., when multiplied by aprim (since this only gives us a formula where aprim_recreated would be equal to 1)
234 qreal cprim_recreated = (rx*rx)/(ry*ry);
235 qreal dprim_recreated = -2*rerotatedCenter.x();
236 qreal eprim_recreated = -2*rerotatedCenter.y()*(rx*rx)/(ry*ry);
237 qreal fprim_recreated = qPow(rerotatedCenter.x(), 2) + qPow(rerotatedCenter.y(), 2)*(rx*rx)/(ry*ry) - (rx*rx);
238
239 if (debug) qCritical() << "recreated equation (with 1): " << 1 << 0 << cprim_recreated << dprim_recreated << eprim_recreated << fprim_recreated;
240 if (debug) qCritical() << "recreated equation: (actual)" << aprim << 0 << aprim*cprim_recreated << aprim*dprim_recreated << aprim*eprim_recreated << aprim*fprim_recreated;
241
242 qreal eps = 0.00001;
243 auto fuzzyCompareWithEps = [eps] (qreal a, qreal b) { return abs(a - b) < eps; };
244
245 KIS_SAFE_ASSERT_RECOVER_NOOP(fuzzyCompareWithEps(aprim*cprim_recreated, cprim));
246 KIS_SAFE_ASSERT_RECOVER_NOOP(fuzzyCompareWithEps(aprim*dprim_recreated, dprim));
247 KIS_SAFE_ASSERT_RECOVER_NOOP(fuzzyCompareWithEps(aprim*eprim_recreated, eprim));
248 KIS_SAFE_ASSERT_RECOVER_NOOP(fuzzyCompareWithEps(aprim*fprim_recreated, fprim));
249
250#endif
251
252 auto convertToPreviousCoordsSystem = [K, L] (QPointF p) { return QPointF(K*p.x() + L*p.y(), K*p.y() - L*p.x()); };
253
254 // they most probably don't need a higher precision than float
255 // (though they are used to calculate the brush position...)
256 QPointF leftVertexRerotated = rerotatedCenter + QPointF(-rx, 0);
257 QPointF rightVertedRerotated = rerotatedCenter + QPointF(rx, 0);
258 QPointF topVertedRerotated = rerotatedCenter + QPointF(0, ry);
259
260 QPointF leftVertexFinal = convertToPreviousCoordsSystem(leftVertexRerotated);
261 QPointF rightVertexFinal = convertToPreviousCoordsSystem(rightVertedRerotated);
262 QPointF topVertexFinal = convertToPreviousCoordsSystem(topVertedRerotated);
263
264 QVector<QPointF> result;
265 result << leftVertexFinal << rightVertexFinal << topVertexFinal;
266
267 finalVertices = result;
268
269 m_valid = true;
270 return true;
271}
const Params2D p
static bool formulaRepresentsAnEllipse(double a, double b, double c)
formulaRepresentsAnEllipse parameters are first three coefficients from a formula: ax^2 + bxy + cy^2 ...
void setFormula(QVector< double > &formula, double a, double b, double c, double d, double e, double f)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
const qreal eps
Point abs(const Point &pt)

References A, axisXLength, axisYLength, eps, finalAxisAngle, finalAxisReverseAngleCos, finalAxisReverseAngleSin, finalEllipseCenter, finalFormula, finalVertices, formulaRepresentsAnEllipse(), KIS_SAFE_ASSERT_RECOVER_NOOP, m_valid, originalTransform, p, polygon, rerotatedFormula, and setFormula().

Member Data Documentation

◆ axisXLength

double EllipseInPolygon::axisXLength {0.0}

Definition at line 98 of file PerspectiveEllipseAssistant.cc.

98{0.0}; // all "final", "rerotated" and "canonical" ellipses have the same axes lengths

◆ axisYLength

double EllipseInPolygon::axisYLength {0.0}

Definition at line 99 of file PerspectiveEllipseAssistant.cc.

99{0.0};

◆ finalAxisAngle

double EllipseInPolygon::finalAxisAngle {0.0}

Definition at line 92 of file PerspectiveEllipseAssistant.cc.

92{0.0}; // theta - angle of the final ellipse's X axis

◆ finalAxisReverseAngleCos

double EllipseInPolygon::finalAxisReverseAngleCos {0.0}

Definition at line 93 of file PerspectiveEllipseAssistant.cc.

93{0.0}; // cos(-theta) -> used for calculating rerotatedFormula

◆ finalAxisReverseAngleSin

double EllipseInPolygon::finalAxisReverseAngleSin {0.0}

Definition at line 94 of file PerspectiveEllipseAssistant.cc.

94{0.0}; // sin(-theta) -> used for calculating rerotatedFormula

◆ finalEllipseCenter

QVector<double> EllipseInPolygon::finalEllipseCenter

Definition at line 96 of file PerspectiveEllipseAssistant.cc.

◆ finalFormula

QVector<double> EllipseInPolygon::finalFormula

Definition at line 89 of file PerspectiveEllipseAssistant.cc.

◆ finalVertices

QVector<QPointF> EllipseInPolygon::finalVertices

Definition at line 101 of file PerspectiveEllipseAssistant.cc.

◆ m_valid

bool EllipseInPolygon::m_valid {false}
protected

Definition at line 109 of file PerspectiveEllipseAssistant.cc.

109{false};

◆ originalTransform

QTransform EllipseInPolygon::originalTransform

Definition at line 86 of file PerspectiveEllipseAssistant.cc.

◆ polygon

QVector<QPointF> EllipseInPolygon::polygon

Definition at line 85 of file PerspectiveEllipseAssistant.cc.

◆ rerotatedFormula

QVector<double> EllipseInPolygon::rerotatedFormula

Definition at line 90 of file PerspectiveEllipseAssistant.cc.


The documentation for this class was generated from the following file: