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
35QT_CHARTS_BEGIN_NAMESPACE
36
37AbstractDomain::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
55AbstractDomain::~AbstractDomain()
56{
57}
58
59void 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
70QSizeF AbstractDomain::size() const
71{
72 return m_size;
73}
74
75void AbstractDomain::setRangeX(qreal min, qreal max)
76{
77 setRange(minX: min, maxX: max, minY: m_minY, maxY: m_maxY);
78}
79
80void AbstractDomain::setRangeY(qreal min, qreal max)
81{
82 setRange(minX: m_minX, maxX: m_maxX, minY: min, maxY: max);
83}
84
85void AbstractDomain::setMinX(qreal min)
86{
87 setRange(minX: min, maxX: m_maxX, minY: m_minY, maxY: m_maxY);
88}
89
90void AbstractDomain::setMaxX(qreal max)
91{
92 setRange(minX: m_minX, maxX: max, minY: m_minY, maxY: m_maxY);
93}
94
95void AbstractDomain::setMinY(qreal min)
96{
97 setRange(minX: m_minX, maxX: m_maxX, minY: min, maxY: m_maxY);
98}
99
100void AbstractDomain::setMaxY(qreal max)
101{
102 setRange(minX: m_minX, maxX: m_maxX, minY: m_minY, maxY: max);
103}
104
105qreal AbstractDomain::spanX() const
106{
107 Q_ASSERT(m_maxX >= m_minX);
108 return m_maxX - m_minX;
109}
110
111qreal AbstractDomain::spanY() const
112{
113 Q_ASSERT(m_maxY >= m_minY);
114 return m_maxY - m_minY;
115}
116
117bool AbstractDomain::isEmpty() const
118{
119 return qFuzzyCompare(p1: spanX(), p2: 0) || qFuzzyCompare(p1: spanY(), p2: 0) || m_size.isEmpty();
120}
121
122// handlers
123
124void AbstractDomain::handleVerticalAxisRangeChanged(qreal min, qreal max)
125{
126 setRangeY(min, max);
127}
128
129void AbstractDomain::handleHorizontalAxisRangeChanged(qreal min, qreal max)
130{
131 setRangeX(min, max);
132}
133
134void AbstractDomain::handleReverseXChanged(bool reverse)
135{
136 m_reverseX = reverse;
137 emit updated();
138}
139
140void AbstractDomain::handleReverseYChanged(bool reverse)
141{
142 m_reverseY = reverse;
143 emit updated();
144}
145
146void 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
157void 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
168void 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
181void 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
194qreal 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
213bool 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
234bool 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
255bool 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
264bool Q_AUTOTEST_EXPORT operator!= (const AbstractDomain &domain1, const AbstractDomain &domain2)
265{
266 return !(domain1 == domain2);
267}
268
269
270QDebug 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.
281void 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
291QRectF 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
306QT_CHARTS_END_NAMESPACE
307
308#include "moc_abstractdomain_p.cpp"
309

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