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
10QT_BEGIN_NAMESPACE
11
12LogXLogYDomain::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
23LogXLogYDomain::~LogXLogYDomain()
24{
25}
26
27void 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
63void 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
84void 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
114void 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
136QPointF 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
165QList<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
191QPointF 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
202bool 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
220bool 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
234void 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
244void 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
256bool 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
265bool Q_AUTOTEST_EXPORT operator!= (const LogXLogYDomain &domain1, const LogXLogYDomain &domain2)
266{
267 return !(domain1 == domain2);
268}
269
270
271QDebug 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
281QT_END_NAMESPACE
282
283#include "moc_logxlogydomain_p.cpp"
284

source code of qtcharts/src/charts/domain/logxlogydomain.cpp