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

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