1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include <private/logxypolardomain_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 | LogXYPolarDomain::LogXYPolarDomain(QObject *parent) |
13 | : PolarDomain(parent), |
14 | m_logLeftX(0), |
15 | m_logRightX(1), |
16 | m_logBaseX(10) |
17 | { |
18 | } |
19 | |
20 | LogXYPolarDomain::~LogXYPolarDomain() |
21 | { |
22 | } |
23 | |
24 | void LogXYPolarDomain::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 | |
55 | void LogXYPolarDomain::zoomIn(const QRectF &rect) |
56 | { |
57 | storeZoomReset(); |
58 | qreal logLeftX = rect.left() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; |
59 | qreal logRightX = rect.right() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; |
60 | qreal leftX = qPow(x: m_logBaseX, y: logLeftX); |
61 | qreal rightX = qPow(x: m_logBaseX, y: logRightX); |
62 | qreal minX = leftX < rightX ? leftX : rightX; |
63 | qreal maxX = leftX > rightX ? leftX : rightX; |
64 | |
65 | qreal dy = spanY() / m_size.height(); |
66 | qreal minY = m_minY; |
67 | qreal maxY = m_maxY; |
68 | |
69 | minY = maxY - dy * rect.bottom(); |
70 | maxY = maxY - dy * rect.top(); |
71 | |
72 | setRange(minX, maxX, minY, maxY); |
73 | } |
74 | |
75 | void LogXYPolarDomain::zoomOut(const QRectF &rect) |
76 | { |
77 | storeZoomReset(); |
78 | const qreal factorX = m_size.width() / rect.width(); |
79 | |
80 | qreal logLeftX = m_logLeftX + (m_logRightX - m_logLeftX) / 2.0 * (1.0 - factorX); |
81 | qreal logRIghtX = m_logLeftX + (m_logRightX - m_logLeftX) / 2.0 * (1.0 + factorX); |
82 | qreal leftX = qPow(x: m_logBaseX, y: logLeftX); |
83 | qreal rightX = qPow(x: m_logBaseX, y: logRIghtX); |
84 | qreal minX = leftX < rightX ? leftX : rightX; |
85 | qreal maxX = leftX > rightX ? leftX : rightX; |
86 | |
87 | qreal dy = spanY() / rect.height(); |
88 | qreal minY = m_minY; |
89 | qreal maxY = m_maxY; |
90 | |
91 | maxY = minY + dy * rect.bottom(); |
92 | minY = maxY - dy * m_size.height(); |
93 | |
94 | setRange(minX, maxX, minY, maxY); |
95 | } |
96 | |
97 | void LogXYPolarDomain::move(qreal dx, qreal dy) |
98 | { |
99 | qreal stepX = dx * (m_logRightX - m_logLeftX) / m_size.width(); |
100 | qreal leftX = qPow(x: m_logBaseX, y: m_logLeftX + stepX); |
101 | qreal rightX = qPow(x: m_logBaseX, y: m_logRightX + stepX); |
102 | qreal minX = leftX < rightX ? leftX : rightX; |
103 | qreal maxX = leftX > rightX ? leftX : rightX; |
104 | |
105 | qreal y = spanY() / m_radius; |
106 | qreal minY = m_minY; |
107 | qreal maxY = m_maxY; |
108 | |
109 | if (dy != 0) { |
110 | minY = minY + y * dy; |
111 | maxY = maxY + y * dy; |
112 | } |
113 | setRange(minX, maxX, minY, maxY); |
114 | } |
115 | |
116 | qreal LogXYPolarDomain::toAngularCoordinate(qreal value, bool &ok) const |
117 | { |
118 | qreal retVal; |
119 | if (value <= 0) { |
120 | ok = false; |
121 | retVal = 0.0; |
122 | } else { |
123 | ok = true; |
124 | const qreal tickSpan = 360.0 / qAbs(t: m_logRightX - m_logLeftX); |
125 | const qreal logValue = qLn(v: value) / qLn(v: m_logBaseX); |
126 | const qreal valueDelta = logValue - m_logLeftX; |
127 | |
128 | retVal = valueDelta * tickSpan; |
129 | } |
130 | return retVal; |
131 | } |
132 | |
133 | qreal LogXYPolarDomain::toRadialCoordinate(qreal value, bool &ok) const |
134 | { |
135 | ok = true; |
136 | if (value < m_minY) |
137 | value = m_minY; |
138 | |
139 | // Dont limit the max. The drawing should clip the stuff that goes out of the grid |
140 | qreal f = (value - m_minY) / (m_maxY - m_minY); |
141 | |
142 | return f * m_radius; |
143 | } |
144 | |
145 | QPointF LogXYPolarDomain::calculateDomainPoint(const QPointF &point) const |
146 | { |
147 | if (point == m_center) |
148 | return QPointF(0.0, m_minY); |
149 | |
150 | QLineF line(m_center, point); |
151 | qreal a = 90.0 - line.angle(); |
152 | if (a < 0.0) |
153 | a += 360.0; |
154 | |
155 | const qreal deltaX = 360.0 / qAbs(t: m_logRightX - m_logLeftX); |
156 | a = qPow(x: m_logBaseX, y: m_logLeftX + (a / deltaX)); |
157 | |
158 | qreal r = m_minY + ((m_maxY - m_minY) * (line.length() / m_radius)); |
159 | |
160 | return QPointF(a, r); |
161 | } |
162 | |
163 | bool LogXYPolarDomain::attachAxis(QAbstractAxis *axis) |
164 | { |
165 | AbstractDomain::attachAxis(axis); |
166 | QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(object: axis); |
167 | |
168 | if (logAxis && logAxis->orientation() == Qt::Horizontal) { |
169 | QObject::connect(sender: logAxis, SIGNAL(baseChanged(qreal)), receiver: this, SLOT(handleHorizontalAxisBaseChanged(qreal))); |
170 | handleHorizontalAxisBaseChanged(baseX: logAxis->base()); |
171 | } |
172 | |
173 | return true; |
174 | } |
175 | |
176 | bool LogXYPolarDomain::detachAxis(QAbstractAxis *axis) |
177 | { |
178 | AbstractDomain::detachAxis(axis); |
179 | QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(object: axis); |
180 | |
181 | if (logAxis && logAxis->orientation() == Qt::Horizontal) |
182 | QObject::disconnect(sender: logAxis, SIGNAL(baseChanged(qreal)), receiver: this, SLOT(handleHorizontalAxisBaseChanged(qreal))); |
183 | |
184 | return true; |
185 | } |
186 | |
187 | void LogXYPolarDomain::handleHorizontalAxisBaseChanged(qreal baseX) |
188 | { |
189 | m_logBaseX = baseX; |
190 | qreal logMinX = qLn(v: m_minX) / qLn(v: m_logBaseX); |
191 | qreal logMaxX = qLn(v: m_maxX) / qLn(v: m_logBaseX); |
192 | m_logLeftX = logMinX < logMaxX ? logMinX : logMaxX; |
193 | m_logRightX = logMinX > logMaxX ? logMinX : logMaxX; |
194 | emit updated(); |
195 | } |
196 | |
197 | // operators |
198 | |
199 | bool Q_AUTOTEST_EXPORT operator== (const LogXYPolarDomain &domain1, const LogXYPolarDomain &domain2) |
200 | { |
201 | return (qFuzzyIsNull(d: domain1.m_maxX - domain2.m_maxX) |
202 | && qFuzzyIsNull(d: domain1.m_maxY - domain2.m_maxY) |
203 | && qFuzzyIsNull(d: domain1.m_minX - domain2.m_minX) |
204 | && qFuzzyIsNull(d: domain1.m_minY - domain2.m_minY)); |
205 | } |
206 | |
207 | |
208 | bool Q_AUTOTEST_EXPORT operator!= (const LogXYPolarDomain &domain1, const LogXYPolarDomain &domain2) |
209 | { |
210 | return !(domain1 == domain2); |
211 | } |
212 | |
213 | |
214 | QDebug Q_AUTOTEST_EXPORT operator<<(QDebug dbg, const LogXYPolarDomain &domain) |
215 | { |
216 | #ifdef QT_NO_TEXTSTREAM |
217 | Q_UNUSED(domain); |
218 | #else |
219 | dbg.nospace() << "AbstractDomain(" << domain.m_minX << ',' << domain.m_maxX << ',' << domain.m_minY << ',' << domain.m_maxY << ')' << domain.m_size; |
220 | #endif |
221 | return dbg.maybeSpace(); |
222 | } |
223 | |
224 | QT_END_NAMESPACE |
225 | |
226 | #include "moc_logxypolardomain_p.cpp" |
227 | |