1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include <private/logxlogydomain_p.h> |
5 | #include <private/qabstractaxis_p.h> |
6 | #include <QtCharts/QLogValueAxis> |
7 | #include <QtCore/QtMath> |
8 | #include <cmath> |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | LogXLogYDomain::LogXLogYDomain(QObject *parent) |
13 | : AbstractDomain(parent), |
14 | m_logLeftX(0), |
15 | m_logRightX(1), |
16 | m_logBaseX(10), |
17 | m_logLeftY(0), |
18 | m_logRightY(1), |
19 | m_logBaseY(10) |
20 | { |
21 | } |
22 | |
23 | LogXLogYDomain::~LogXLogYDomain() |
24 | { |
25 | } |
26 | |
27 | void LogXLogYDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) |
28 | { |
29 | bool axisXChanged = false; |
30 | bool axisYChanged = false; |
31 | |
32 | adjustLogDomainRanges(min&: minX, max&: maxX); |
33 | adjustLogDomainRanges(min&: minY, max&: maxY); |
34 | |
35 | if (!qFuzzyIsNull(d: m_minX - minX) || !qFuzzyIsNull(d: m_maxX - maxX)) { |
36 | m_minX = minX; |
37 | m_maxX = maxX; |
38 | axisXChanged = true; |
39 | qreal logMinX = qLn(v: m_minX) / qLn(v: m_logBaseX); |
40 | qreal logMaxX = qLn(v: m_maxX) / qLn(v: m_logBaseX); |
41 | m_logLeftX = logMinX < logMaxX ? logMinX : logMaxX; |
42 | m_logRightX = logMinX > logMaxX ? logMinX : logMaxX; |
43 | if(!m_signalsBlocked) |
44 | emit rangeHorizontalChanged(min: m_minX, max: m_maxX); |
45 | } |
46 | |
47 | if (!qFuzzyIsNull(d: m_minY - minY) || !qFuzzyIsNull(d: m_maxY - maxY)) { |
48 | m_minY = minY; |
49 | m_maxY = maxY; |
50 | axisYChanged = true; |
51 | qreal logMinY = qLn(v: m_minY) / qLn(v: m_logBaseY); |
52 | qreal logMaxY = qLn(v: m_maxY) / qLn(v: m_logBaseY); |
53 | m_logLeftY = logMinY < logMaxY ? logMinY : logMaxY; |
54 | m_logRightY = logMinY > logMaxY ? logMinY : logMaxY; |
55 | if (!m_signalsBlocked) |
56 | emit rangeVerticalChanged(min: m_minY, max: m_maxY); |
57 | } |
58 | |
59 | if (axisXChanged || axisYChanged) |
60 | emit updated(); |
61 | } |
62 | |
63 | void LogXLogYDomain::zoomIn(const QRectF &rect) |
64 | { |
65 | storeZoomReset(); |
66 | QRectF fixedRect = fixZoomRect(rect); |
67 | qreal logLeftX = fixedRect.left() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; |
68 | qreal logRightX = fixedRect.right() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; |
69 | qreal leftX = qPow(x: m_logBaseX, y: logLeftX); |
70 | qreal rightX = qPow(x: m_logBaseX, y: logRightX); |
71 | qreal minX = leftX < rightX ? leftX : rightX; |
72 | qreal maxX = leftX > rightX ? leftX : rightX; |
73 | |
74 | qreal logLeftY = m_logRightY - fixedRect.bottom() * (m_logRightY - m_logLeftY) / m_size.height(); |
75 | qreal logRightY = m_logRightY - fixedRect.top() * (m_logRightY - m_logLeftY) / m_size.height(); |
76 | qreal leftY = qPow(x: m_logBaseY, y: logLeftY); |
77 | qreal rightY = qPow(x: m_logBaseY, y: logRightY); |
78 | qreal minY = leftY < rightY ? leftY : rightY; |
79 | qreal maxY = leftY > rightY ? leftY : rightY; |
80 | |
81 | setRange(minX, maxX, minY, maxY); |
82 | } |
83 | |
84 | void LogXLogYDomain::zoomOut(const QRectF &rect) |
85 | { |
86 | storeZoomReset(); |
87 | QRectF fixedRect = fixZoomRect(rect); |
88 | const qreal factorX = m_size.width() / fixedRect.width(); |
89 | const qreal factorY = m_size.height() / fixedRect.height(); |
90 | |
91 | qreal logLeftX = m_logLeftX + (m_logRightX - m_logLeftX) / 2 * (1 - factorX); |
92 | qreal logRightX = m_logLeftX + (m_logRightX - m_logLeftX) / 2 * (1 + factorX); |
93 | qreal leftX = qPow(x: m_logBaseX, y: logLeftX); |
94 | qreal rightX = qPow(x: m_logBaseX, y: logRightX); |
95 | qreal minX = leftX < rightX ? leftX : rightX; |
96 | qreal maxX = leftX > rightX ? leftX : rightX; |
97 | |
98 | qreal newLogMinY = m_logLeftY + (m_logRightY - m_logLeftY) / 2 * (1 - factorY); |
99 | qreal newLogMaxY = m_logLeftY + (m_logRightY - m_logLeftY) / 2 * (1 + factorY); |
100 | qreal leftY = qPow(x: m_logBaseY, y: newLogMinY); |
101 | qreal rightY = qPow(x: m_logBaseY, y: newLogMaxY); |
102 | qreal minY = leftY < rightY ? leftY : rightY; |
103 | qreal maxY = leftY > rightY ? leftY : rightY; |
104 | |
105 | if (logRightX > m_size.width() || newLogMaxY > m_size.height()) |
106 | return; |
107 | |
108 | if (qIsInf(d: maxX) || qIsInf(d: maxY)) |
109 | return; |
110 | |
111 | setRange(minX, maxX, minY, maxY); |
112 | } |
113 | |
114 | void LogXLogYDomain::move(qreal dx, qreal dy) |
115 | { |
116 | if (m_reverseX) |
117 | dx = -dx; |
118 | if (m_reverseY) |
119 | dy = -dy; |
120 | |
121 | qreal stepX = dx * qAbs(t: m_logRightX - m_logLeftX) / m_size.width(); |
122 | qreal leftX = qPow(x: m_logBaseX, y: m_logLeftX + stepX); |
123 | qreal rightX = qPow(x: m_logBaseX, y: m_logRightX + stepX); |
124 | qreal minX = leftX < rightX ? leftX : rightX; |
125 | qreal maxX = leftX > rightX ? leftX : rightX; |
126 | |
127 | qreal stepY = dy * (m_logRightY - m_logLeftY) / m_size.height(); |
128 | qreal leftY = qPow(x: m_logBaseY, y: m_logLeftY + stepY); |
129 | qreal rightY = qPow(x: m_logBaseY, y: m_logRightY + stepY); |
130 | qreal minY = leftY < rightY ? leftY : rightY; |
131 | qreal maxY = leftY > rightY ? leftY : rightY; |
132 | |
133 | setRange(minX, maxX, minY, maxY); |
134 | } |
135 | |
136 | QPointF LogXLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) const |
137 | { |
138 | const qreal deltaX = m_size.width() / qAbs(t: m_logRightX - m_logLeftX); |
139 | const qreal deltaY = m_size.height() / qAbs(t: m_logRightY - m_logLeftY); |
140 | qreal x(0); |
141 | qreal y(0); |
142 | if (point.x() > 0 && point.y() > 0) { |
143 | x = ((qLn(v: point.x()) / qLn(v: m_logBaseX)) - m_logLeftX) * deltaX; |
144 | y = ((qLn(v: point.y()) / qLn(v: m_logBaseY)) - m_logLeftY) * deltaY; |
145 | ok = true; |
146 | } else { |
147 | qWarning() << "Logarithms of zero and negative values are undefined." ; |
148 | ok = false; |
149 | if (point.x() > 0) |
150 | x = ((qLn(v: point.x()) / qLn(v: m_logBaseX)) - m_logLeftX) * deltaX; |
151 | else |
152 | x = 0; |
153 | if (point.y() > 0) |
154 | y = ((qLn(v: point.y()) / qLn(v: m_logBaseY)) - m_logLeftY) * deltaY; |
155 | else |
156 | y = 0; |
157 | } |
158 | if (m_reverseX) |
159 | x = m_size.width() - x; |
160 | if (!m_reverseY) |
161 | y = m_size.height() - y; |
162 | return QPointF(x, y); |
163 | } |
164 | |
165 | QList<QPointF> LogXLogYDomain::calculateGeometryPoints(const QList<QPointF> &list) const |
166 | { |
167 | const qreal deltaX = m_size.width() / qAbs(t: m_logRightX - m_logLeftX); |
168 | const qreal deltaY = m_size.height() / qAbs(t: m_logRightY - m_logLeftY); |
169 | |
170 | QList<QPointF> result; |
171 | result.resize(size: list.size()); |
172 | |
173 | for (int i = 0; i < list.size(); ++i) { |
174 | if (list[i].x() > 0 && list[i].y() > 0) { |
175 | qreal x = ((qLn(v: list[i].x()) / qLn(v: m_logBaseX)) - m_logLeftX) * deltaX; |
176 | if (m_reverseX) |
177 | x = m_size.width() - x; |
178 | qreal y = ((qLn(v: list[i].y()) / qLn(v: m_logBaseY)) - m_logLeftY) * deltaY; |
179 | if (!m_reverseY) |
180 | y = m_size.height() - y; |
181 | result[i].setX(x); |
182 | result[i].setY(y); |
183 | } else { |
184 | qWarning() << "Logarithms of zero and negative values are undefined." ; |
185 | return QList<QPointF>(); |
186 | } |
187 | } |
188 | return result; |
189 | } |
190 | |
191 | QPointF LogXLogYDomain::calculateDomainPoint(const QPointF &point) const |
192 | { |
193 | const qreal deltaX = m_size.width() / qAbs(t: m_logRightX - m_logLeftX); |
194 | const qreal deltaY = m_size.height() / qAbs(t: m_logRightY - m_logLeftY); |
195 | qreal x = m_reverseX ? (m_size.width() - point.x()) : point.x(); |
196 | x = qPow(x: m_logBaseX, y: m_logLeftX + x / deltaX); |
197 | qreal y = m_reverseY ? point.y() : (m_size.height() - point.y()); |
198 | y = qPow(x: m_logBaseY, y: m_logLeftY + y / deltaY); |
199 | return QPointF(x, y); |
200 | } |
201 | |
202 | bool LogXLogYDomain::attachAxis(QAbstractAxis *axis) |
203 | { |
204 | AbstractDomain::attachAxis(axis); |
205 | QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(object: axis); |
206 | |
207 | if (logAxis && logAxis->orientation() == Qt::Vertical) { |
208 | QObject::connect(sender: logAxis, SIGNAL(baseChanged(qreal)), receiver: this, SLOT(handleVerticalAxisBaseChanged(qreal))); |
209 | handleVerticalAxisBaseChanged(baseY: logAxis->base()); |
210 | } |
211 | |
212 | if (logAxis && logAxis->orientation() == Qt::Horizontal) { |
213 | QObject::connect(sender: logAxis, SIGNAL(baseChanged(qreal)), receiver: this, SLOT(handleHorizontalAxisBaseChanged(qreal))); |
214 | handleHorizontalAxisBaseChanged(baseX: logAxis->base()); |
215 | } |
216 | |
217 | return true; |
218 | } |
219 | |
220 | bool LogXLogYDomain::detachAxis(QAbstractAxis *axis) |
221 | { |
222 | AbstractDomain::detachAxis(axis); |
223 | QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(object: axis); |
224 | |
225 | if (logAxis && logAxis->orientation() == Qt::Vertical) |
226 | QObject::disconnect(sender: logAxis, SIGNAL(baseChanged(qreal)), receiver: this, SLOT(handleVerticalAxisBaseChanged(qreal))); |
227 | |
228 | if (logAxis && logAxis->orientation() == Qt::Horizontal) |
229 | QObject::disconnect(sender: logAxis, SIGNAL(baseChanged(qreal)), receiver: this, SLOT(handleHorizontalAxisBaseChanged(qreal))); |
230 | |
231 | return true; |
232 | } |
233 | |
234 | void LogXLogYDomain::handleVerticalAxisBaseChanged(qreal baseY) |
235 | { |
236 | m_logBaseY = baseY; |
237 | qreal logMinY = qLn(v: m_minY) / qLn(v: m_logBaseY); |
238 | qreal logMaxY = qLn(v: m_maxY) / qLn(v: m_logBaseY); |
239 | m_logLeftY = logMinY < logMaxY ? logMinY : logMaxY; |
240 | m_logRightY = logMinY > logMaxY ? logMinY : logMaxY; |
241 | emit updated(); |
242 | } |
243 | |
244 | void LogXLogYDomain::handleHorizontalAxisBaseChanged(qreal baseX) |
245 | { |
246 | m_logBaseX = baseX; |
247 | qreal logMinX = qLn(v: m_minX) / qLn(v: m_logBaseX); |
248 | qreal logMaxX = qLn(v: m_maxX) / qLn(v: m_logBaseX); |
249 | m_logLeftX = logMinX < logMaxX ? logMinX : logMaxX; |
250 | m_logRightX = logMinX > logMaxX ? logMinX : logMaxX; |
251 | emit updated(); |
252 | } |
253 | |
254 | // operators |
255 | |
256 | bool Q_AUTOTEST_EXPORT operator== (const LogXLogYDomain &domain1, const LogXLogYDomain &domain2) |
257 | { |
258 | return (qFuzzyIsNull(d: domain1.m_maxX - domain2.m_maxX) |
259 | && qFuzzyIsNull(d: domain1.m_maxY - domain2.m_maxY) |
260 | && qFuzzyIsNull(d: domain1.m_minX - domain2.m_minX) |
261 | && qFuzzyIsNull(d: domain1.m_minY - domain2.m_minY)); |
262 | } |
263 | |
264 | |
265 | bool Q_AUTOTEST_EXPORT operator!= (const LogXLogYDomain &domain1, const LogXLogYDomain &domain2) |
266 | { |
267 | return !(domain1 == domain2); |
268 | } |
269 | |
270 | |
271 | QDebug Q_AUTOTEST_EXPORT operator<<(QDebug dbg, const LogXLogYDomain &domain) |
272 | { |
273 | #ifdef QT_NO_TEXTSTREAM |
274 | Q_UNUSED(domain); |
275 | #else |
276 | dbg.nospace() << "AbstractDomain(" << domain.m_minX << ',' << domain.m_maxX << ',' << domain.m_minY << ',' << domain.m_maxY << ')' << domain.m_size; |
277 | #endif |
278 | return dbg.maybeSpace(); |
279 | } |
280 | |
281 | QT_END_NAMESPACE |
282 | |
283 | #include "moc_logxlogydomain_p.cpp" |
284 | |