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

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