Krita Source Code Documentation
Loading...
Searching...
No Matches
Ellipse.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2010 Geoffry Song <goffrie@gmail.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7#include "Ellipse.h"
8#include <cmath>
9
10#include <iostream>
11
12Ellipse::Ellipse() : a(-1), b(-1)
13{
14}
15Ellipse::Ellipse(const QPointF& _p1, const QPointF& _p2, const QPointF& _p3) : p1(_p1), p2(_p2), p3(_p3) {
17}
18
22
23bool Ellipse::set(const QPointF& m1, const QPointF& m2, const QPointF& p)
24{
25 bool changedMajor = m1 != p1 || m2 != p2,
26 changedMinor = !changedMajor && p != p3;
27 p1 = m1;
28 p2 = m2;
29 p3 = p;
30 if (changedMajor) {
31 return changeMajor();
32 } else if (changedMinor) {
33 return changeMinor();
34 } else {
35 return a > 0 && b > 0;
36 }
37}
38
39QPointF Ellipse::project(const QPointF& pt) const
40{
41 if (a <= 0 || b <= 0) return pt; // not a valid ellipse
42 QPointF p = matrix.map(pt);
43 /*
44 * intersect line from (0,0) to p with the ellipse in canonical position
45 * the equation of the line is y = py/px x
46 * the equation of the ellipse is x^2/a^2 + y^2/b^2 = 1
47 * x=(a*b*px)/sqrt(a^2*py^2+b^2*px^2)
48 * y=(a*b*py)/sqrt(a^2*py^2+b^2*px^2)
49 */
50 const qreal divisor = sqrt(a * a * p.y() * p.y() + b * b * p.x() * p.x());
51 if (divisor <= 0) return inverse.map(QPointF(a, 0)); // give up
52 const qreal ab = a * b, factor = 1.0 / divisor;
53 QPointF ep(ab * p.x() * factor, ab * p.y() * factor);
54 return inverse.map(ep);
55/* return inverse.map(closest(matrix.map(pt)));*/
56}
57
58inline QPointF rotate90(const QPointF& p) {
59 return QPointF(p.y(), -p.x());
60}
61
63{
64 const QPointF d = rotate90((p2 - p1) * 0.5 * b / a);
65 const QPointF pts[4] = {
66 p1 + d,
67 p1 - d,
68 p2 + d,
69 p2 - d
70 };
71 QRectF ret;
72 for (int i = 0; i < 4; ++i) {
73 ret = ret.united(QRectF(pts[i], QSizeF(0.0001, 0.0001)));
74 }
75 return ret;
76}
77
78inline qreal sqrlength(const QPointF& vec)
79{
80 return vec.x() * vec.x() + vec.y() * vec.y();
81}
82inline qreal length(const QPointF& vec)
83{
84 return sqrt(vec.x() * vec.x() + vec.y() * vec.y());
85}
86
88{
89 a = length(p1 - p2) * 0.5;
90
91 /*
92 * calculate transformation matrix
93 * x' = m11*x + m21*y + dx
94 * y' = m22*y + m12*x + dy
95 * m11 = m22, m12 = -m21 (rotations and translations only)
96 * x' = m11*x - m12*y + dx
97 * y' = m11*y + m12*x + dy
98 *
99 * then, transforming (x1, y1) to (x1', y1') and (x2, y2) to (x2', y2'):
100 *
101 * m11 = (y2*y2' + y1 * (y1'-y2') - y2*y1' + x2*x'2 - x1*x'2 + (x1-x2)*x1')
102 * ------------------------------------------------------------------
103 * (y2^2 - 2*y1*y2 + y1^2 + x2^2 - 2*x1*x2 + x1^2)
104 * m12 = -(x1*(y2'-y1') - x2*y2' + x2*y1' + x2'*y2 - x1'*y2 + (x1'-x2')*y1)
105 * ------------------------------------------------------------------
106 * (y2^2 - 2*y1*y2 + y1^2 + x2^2 - 2*x1*x2 + x1^2)
107 * dx = (x1*(-y2*y2' + y2*y1' - x2*x2') + y1*( x2*y2' - x2*y1' - x2'*y2 - x1'*y2) + x2'*y1^2 + x1^2*x2' + x1'*(y2^2 + x2^2 - x1*x2))
108 * ----------------------------------------------------------------------------------------------------------------------------
109 * (y2^2 - 2*y1*y2 + y1^2 + x2^2 - 2*x1*x2 + x1^2)
110 * dy = (x1*(-x2*y2' - x2*y1' + x2'*y2) + y1*(-y2*y2' - y2*y1' - x2*x2' + x2*x1') + y2'*y1^2 + x1^2*y2' + y1'(y2^2 + x2^2) - x1*x1'*y2)
111 * -------------------------------------------------------------------------------------------------------------------------------
112 * (y2^2 - 2*y1*y2 + y1^2 + x2^2 - 2*x1*x2 + x1^2)
113 *
114 * in our usage, to move the ellipse into canonical position:
115 *
116 * (x1, y1) = p1
117 * (x2, y2) = p2
118 * (x1', y1') = (-a, 0)
119 * (x2', y2') = (a, 0)
120 */
121
122 const qreal
123 x1 = p1.x(),
124 x2 = p2.x(),
125 y1 = p1.y(),
126 y2 = p2.y(),
127 x1p = -a,
128 x2p = a,
129 x1sqr = x1 * x1,
130 x2sqr = x2 * x2,
131 y1sqr = y1 * y1,
132 y2sqr = y2 * y2,
133 factor = 1.0 / (x1sqr + y1sqr + x2sqr + y2sqr - 2.0 * y1 * y2 - 2.0 * x1 * x2),
134 m11 = (x2*x2p - x1*x2p + (x1-x2)*x1p) * factor,
135 m12 = -(x2p*y2 - x1p*y2 + (x1p-x2p)*y1) * factor,
136 dx = (x1*(-x2*x2p) + y1*(-x2p*y2 - x1p*y2) + x2p*y1sqr + x1sqr*x2p + x1p*(y2sqr + x2sqr - x1*x2)) * factor,
137 dy = (x1*(x2p*y2) + y1*(-x2*x2p + x2*x1p) - x1*x1p*y2) * factor;
138
139 matrix = QTransform(m11, m12, -m12, m11, dx, dy);
140 inverse = matrix.inverted();
141
142 return changeMinor();
143}
144
146{
147 QPointF p = matrix.map(p3);
148
149 /*
150 * ellipse formula:
151 * x^2/a^2 + y^2/b^2 = 1
152 * b = sqrt(y^2 / (1 - x^2/a^2))
153 */
154 const qreal
155 asqr = a * a,
156 xsqr = p.x() * p.x(),
157 ysqr = p.y() * p.y(),
158 divisor = (1.0 - xsqr / asqr);
159 if (divisor <= 0) {
160 // division by zero!
161 b = -1;
162 return false;
163 }
164 b = sqrt(ysqr / divisor);
165 return true;
166}
167
168bool Ellipse::setMajor1(const QPointF& p)
169{
170 p1 = p;
171 return changeMajor();
172}
173bool Ellipse::setMajor2(const QPointF& p) {
174 p2 = p;
175 return changeMajor();
176}
177bool Ellipse::setPoint(const QPointF& p)
178{
179 p3 = p;
180 return changeMinor();
181}
QPointF rotate90(const QPointF &p)
Definition Ellipse.cc:58
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
qreal sqrlength(const QPointF &vec)
Definition Ellipse.cc:78
const Params2D p
QPointF p2
QPointF p3
QPointF p1
bool setPoint(const QPointF &p)
Definition Ellipse.cc:177
QPointF p2
Definition Ellipse.h:47
QTransform inverse
Definition Ellipse.h:41
bool set(const QPointF &m1, const QPointF &m2, const QPointF &p)
Definition Ellipse.cc:23
QPointF p1
Definition Ellipse.h:46
bool changeMinor()
Definition Ellipse.cc:145
~Ellipse()
Definition Ellipse.cc:19
QRectF boundingRect() const
Definition Ellipse.cc:62
qreal b
Definition Ellipse.h:43
QTransform matrix
Definition Ellipse.h:40
Ellipse()
Definition Ellipse.cc:12
bool changeMajor()
Definition Ellipse.cc:87
bool setMajor1(const QPointF &p)
Definition Ellipse.cc:168
QPointF p3
Definition Ellipse.h:48
qreal a
Definition Ellipse.h:42
QPointF project(const QPointF &) const
Definition Ellipse.cc:39
bool setMajor2(const QPointF &p)
Definition Ellipse.cc:173