1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Charts module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 or (at your option) any later version |
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by |
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | |
30 | #include <private/abstractdomain_p.h> |
31 | #include <private/qabstractaxis_p.h> |
32 | #include <QtCore/QtMath> |
33 | #include <cmath> |
34 | |
35 | QT_CHARTS_BEGIN_NAMESPACE |
36 | |
37 | AbstractDomain::AbstractDomain(QObject *parent) |
38 | : QObject(parent), |
39 | m_minX(0), |
40 | m_maxX(0), |
41 | m_minY(0), |
42 | m_maxY(0), |
43 | m_signalsBlocked(false), |
44 | m_zoomed(false), |
45 | m_zoomResetMinX(0), |
46 | m_zoomResetMaxX(0), |
47 | m_zoomResetMinY(0), |
48 | m_zoomResetMaxY(0), |
49 | m_reverseX(false), |
50 | m_reverseY(false) |
51 | |
52 | { |
53 | } |
54 | |
55 | AbstractDomain::~AbstractDomain() |
56 | { |
57 | } |
58 | |
59 | void AbstractDomain::setSize(const QSizeF &size) |
60 | { |
61 | if (!size.isValid()) |
62 | return; |
63 | |
64 | if (m_size != size) { |
65 | m_size=size; |
66 | emit updated(); |
67 | } |
68 | } |
69 | |
70 | QSizeF AbstractDomain::size() const |
71 | { |
72 | return m_size; |
73 | } |
74 | |
75 | void AbstractDomain::setRangeX(qreal min, qreal max) |
76 | { |
77 | setRange(minX: min, maxX: max, minY: m_minY, maxY: m_maxY); |
78 | } |
79 | |
80 | void AbstractDomain::setRangeY(qreal min, qreal max) |
81 | { |
82 | setRange(minX: m_minX, maxX: m_maxX, minY: min, maxY: max); |
83 | } |
84 | |
85 | void AbstractDomain::setMinX(qreal min) |
86 | { |
87 | setRange(minX: min, maxX: m_maxX, minY: m_minY, maxY: m_maxY); |
88 | } |
89 | |
90 | void AbstractDomain::setMaxX(qreal max) |
91 | { |
92 | setRange(minX: m_minX, maxX: max, minY: m_minY, maxY: m_maxY); |
93 | } |
94 | |
95 | void AbstractDomain::setMinY(qreal min) |
96 | { |
97 | setRange(minX: m_minX, maxX: m_maxX, minY: min, maxY: m_maxY); |
98 | } |
99 | |
100 | void AbstractDomain::setMaxY(qreal max) |
101 | { |
102 | setRange(minX: m_minX, maxX: m_maxX, minY: m_minY, maxY: max); |
103 | } |
104 | |
105 | qreal AbstractDomain::spanX() const |
106 | { |
107 | Q_ASSERT(m_maxX >= m_minX); |
108 | return m_maxX - m_minX; |
109 | } |
110 | |
111 | qreal AbstractDomain::spanY() const |
112 | { |
113 | Q_ASSERT(m_maxY >= m_minY); |
114 | return m_maxY - m_minY; |
115 | } |
116 | |
117 | bool AbstractDomain::isEmpty() const |
118 | { |
119 | return qFuzzyCompare(p1: spanX(), p2: 0) || qFuzzyCompare(p1: spanY(), p2: 0) || m_size.isEmpty(); |
120 | } |
121 | |
122 | // handlers |
123 | |
124 | void AbstractDomain::handleVerticalAxisRangeChanged(qreal min, qreal max) |
125 | { |
126 | setRangeY(min, max); |
127 | } |
128 | |
129 | void AbstractDomain::handleHorizontalAxisRangeChanged(qreal min, qreal max) |
130 | { |
131 | setRangeX(min, max); |
132 | } |
133 | |
134 | void AbstractDomain::handleReverseXChanged(bool reverse) |
135 | { |
136 | m_reverseX = reverse; |
137 | emit updated(); |
138 | } |
139 | |
140 | void AbstractDomain::handleReverseYChanged(bool reverse) |
141 | { |
142 | m_reverseY = reverse; |
143 | emit updated(); |
144 | } |
145 | |
146 | void AbstractDomain::blockRangeSignals(bool block) |
147 | { |
148 | if (m_signalsBlocked!=block) { |
149 | m_signalsBlocked=block; |
150 | if (!block) { |
151 | emit rangeHorizontalChanged(min: m_minX,max: m_maxX); |
152 | emit rangeVerticalChanged(min: m_minY,max: m_maxY); |
153 | } |
154 | } |
155 | } |
156 | |
157 | void AbstractDomain::zoomReset() |
158 | { |
159 | if (m_zoomed) { |
160 | setRange(minX: m_zoomResetMinX, |
161 | maxX: m_zoomResetMaxX, |
162 | minY: m_zoomResetMinY, |
163 | maxY: m_zoomResetMaxY); |
164 | m_zoomed = false; |
165 | } |
166 | } |
167 | |
168 | void AbstractDomain::storeZoomReset() |
169 | { |
170 | if (!m_zoomed) { |
171 | m_zoomed = true; |
172 | m_zoomResetMinX = m_minX; |
173 | m_zoomResetMaxX = m_maxX; |
174 | m_zoomResetMinY = m_minY; |
175 | m_zoomResetMaxY = m_maxY; |
176 | } |
177 | } |
178 | |
179 | //algorithm defined by Paul S.Heckbert GraphicalGems I |
180 | |
181 | void AbstractDomain::looseNiceNumbers(qreal &min, qreal &max, int &ticksCount) |
182 | { |
183 | qreal range = niceNumber(x: max - min, ceiling: true); //range with ceiling |
184 | qreal step = niceNumber(x: range / (ticksCount - 1), ceiling: false); |
185 | min = qFloor(v: min / step); |
186 | max = qCeil(v: max / step); |
187 | ticksCount = int(max - min) + 1; |
188 | min *= step; |
189 | max *= step; |
190 | } |
191 | |
192 | //nice numbers can be expressed as form of 1*10^n, 2* 10^n or 5*10^n |
193 | |
194 | qreal AbstractDomain::niceNumber(qreal x, bool ceiling) |
195 | { |
196 | qreal z = qPow(x: 10, y: qFloor(v: std::log10(x: x))); //find corresponding number of the form of 10^n than is smaller than x |
197 | qreal q = x / z; //q<10 && q>=1; |
198 | |
199 | if (ceiling) { |
200 | if (q <= 1.0) q = 1; |
201 | else if (q <= 2.0) q = 2; |
202 | else if (q <= 5.0) q = 5; |
203 | else q = 10; |
204 | } else { |
205 | if (q < 1.5) q = 1; |
206 | else if (q < 3.0) q = 2; |
207 | else if (q < 7.0) q = 5; |
208 | else q = 10; |
209 | } |
210 | return q * z; |
211 | } |
212 | |
213 | bool AbstractDomain::attachAxis(QAbstractAxis *axis) |
214 | { |
215 | if (axis->orientation() == Qt::Vertical) { |
216 | QObject::connect(sender: axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), receiver: this, SLOT(handleVerticalAxisRangeChanged(qreal,qreal))); |
217 | QObject::connect(sender: this, SIGNAL(rangeVerticalChanged(qreal,qreal)), receiver: axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal))); |
218 | QObject::connect(sender: axis, signal: &QAbstractAxis::reverseChanged, |
219 | receiver: this, slot: &AbstractDomain::handleReverseYChanged); |
220 | m_reverseY = axis->isReverse(); |
221 | } |
222 | |
223 | if (axis->orientation() == Qt::Horizontal) { |
224 | QObject::connect(sender: axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), receiver: this, SLOT(handleHorizontalAxisRangeChanged(qreal,qreal))); |
225 | QObject::connect(sender: this, SIGNAL(rangeHorizontalChanged(qreal,qreal)), receiver: axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal))); |
226 | QObject::connect(sender: axis, signal: &QAbstractAxis::reverseChanged, |
227 | receiver: this, slot: &AbstractDomain::handleReverseXChanged); |
228 | m_reverseX = axis->isReverse(); |
229 | } |
230 | |
231 | return true; |
232 | } |
233 | |
234 | bool AbstractDomain::detachAxis(QAbstractAxis *axis) |
235 | { |
236 | if (axis->orientation() == Qt::Vertical) { |
237 | QObject::disconnect(sender: axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), receiver: this, SLOT(handleVerticalAxisRangeChanged(qreal,qreal))); |
238 | QObject::disconnect(sender: this, SIGNAL(rangeVerticalChanged(qreal,qreal)), receiver: axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal))); |
239 | QObject::disconnect(sender: axis, signal: &QAbstractAxis::reverseChanged, |
240 | receiver: this, slot: &AbstractDomain::handleReverseYChanged); |
241 | } |
242 | |
243 | if (axis->orientation() == Qt::Horizontal) { |
244 | QObject::disconnect(sender: axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), receiver: this, SLOT(handleHorizontalAxisRangeChanged(qreal,qreal))); |
245 | QObject::disconnect(sender: this, SIGNAL(rangeHorizontalChanged(qreal,qreal)), receiver: axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal))); |
246 | QObject::disconnect(sender: axis, signal: &QAbstractAxis::reverseChanged, |
247 | receiver: this, slot: &AbstractDomain::handleReverseXChanged); |
248 | } |
249 | |
250 | return true; |
251 | } |
252 | |
253 | // operators |
254 | |
255 | bool Q_AUTOTEST_EXPORT operator== (const AbstractDomain &domain1, const AbstractDomain &domain2) |
256 | { |
257 | return (qFuzzyIsNull(d: domain1.m_maxX - domain2.m_maxX) |
258 | && qFuzzyIsNull(d: domain1.m_maxY - domain2.m_maxY) |
259 | && qFuzzyIsNull(d: domain1.m_minX - domain2.m_minX) |
260 | && qFuzzyIsNull(d: domain1.m_minY - domain2.m_minY)); |
261 | } |
262 | |
263 | |
264 | bool Q_AUTOTEST_EXPORT operator!= (const AbstractDomain &domain1, const AbstractDomain &domain2) |
265 | { |
266 | return !(domain1 == domain2); |
267 | } |
268 | |
269 | |
270 | QDebug Q_AUTOTEST_EXPORT operator<<(QDebug dbg, const AbstractDomain &domain) |
271 | { |
272 | #ifdef QT_NO_TEXTSTREAM |
273 | Q_UNUSED(domain) |
274 | #else |
275 | dbg.nospace() << "AbstractDomain(" << domain.m_minX << ',' << domain.m_maxX << ',' << domain.m_minY << ',' << domain.m_maxY << ')' << domain.m_size; |
276 | #endif |
277 | return dbg.maybeSpace(); |
278 | } |
279 | |
280 | // This function adjusts min/max ranges to failsafe values if negative/zero values are attempted. |
281 | void AbstractDomain::adjustLogDomainRanges(qreal &min, qreal &max) |
282 | { |
283 | if (min <= 0) { |
284 | min = 1.0; |
285 | if (max <= min) |
286 | max = min + 1.0; |
287 | } |
288 | } |
289 | |
290 | // This function fixes the zoom rect based on axis reversals |
291 | QRectF AbstractDomain::fixZoomRect(const QRectF &rect) |
292 | { |
293 | QRectF fixRect = rect; |
294 | if (m_reverseX || m_reverseY) { |
295 | QPointF center = rect.center(); |
296 | if (m_reverseX) |
297 | center.setX(m_size.width() - center.x()); |
298 | if (m_reverseY) |
299 | center.setY(m_size.height() - center.y()); |
300 | fixRect.moveCenter(p: QPointF(center.x(), center.y())); |
301 | } |
302 | |
303 | return fixRect; |
304 | } |
305 | |
306 | QT_CHARTS_END_NAMESPACE |
307 | |
308 | #include "moc_abstractdomain_p.cpp" |
309 | |