1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <private/abstractdomain_p.h>
5#include <private/qabstractaxis_p.h>
6#include <QtCore/QtMath>
7#include <cmath>
8
9QT_BEGIN_NAMESPACE
10
11AbstractDomain::AbstractDomain(QObject *parent)
12 : QObject(parent),
13 m_minX(0),
14 m_maxX(0),
15 m_minY(0),
16 m_maxY(0),
17 m_signalsBlocked(false),
18 m_zoomed(false),
19 m_zoomResetMinX(0),
20 m_zoomResetMaxX(0),
21 m_zoomResetMinY(0),
22 m_zoomResetMaxY(0),
23 m_reverseX(false),
24 m_reverseY(false)
25
26{
27}
28
29AbstractDomain::~AbstractDomain()
30{
31}
32
33void AbstractDomain::setSize(const QSizeF &size)
34{
35 if (!size.isValid())
36 return;
37
38 if (m_size != size) {
39 m_size=size;
40 emit updated();
41 }
42}
43
44QSizeF AbstractDomain::size() const
45{
46 return m_size;
47}
48
49void AbstractDomain::setRangeX(qreal min, qreal max)
50{
51 setRange(minX: min, maxX: max, minY: m_minY, maxY: m_maxY);
52}
53
54void AbstractDomain::setRangeY(qreal min, qreal max)
55{
56 setRange(minX: m_minX, maxX: m_maxX, minY: min, maxY: max);
57}
58
59void AbstractDomain::setMinX(qreal min)
60{
61 setRange(minX: min, maxX: m_maxX, minY: m_minY, maxY: m_maxY);
62}
63
64void AbstractDomain::setMaxX(qreal max)
65{
66 setRange(minX: m_minX, maxX: max, minY: m_minY, maxY: m_maxY);
67}
68
69void AbstractDomain::setMinY(qreal min)
70{
71 setRange(minX: m_minX, maxX: m_maxX, minY: min, maxY: m_maxY);
72}
73
74void AbstractDomain::setMaxY(qreal max)
75{
76 setRange(minX: m_minX, maxX: m_maxX, minY: m_minY, maxY: max);
77}
78
79qreal AbstractDomain::spanX() const
80{
81 Q_ASSERT(m_maxX >= m_minX);
82 return m_maxX - m_minX;
83}
84
85qreal AbstractDomain::spanY() const
86{
87 Q_ASSERT(m_maxY >= m_minY);
88 return m_maxY - m_minY;
89}
90
91bool AbstractDomain::isEmpty() const
92{
93 return qFuzzyCompare(p1: spanX(), p2: 0) || qFuzzyCompare(p1: spanY(), p2: 0) || m_size.isEmpty();
94}
95
96// handlers
97
98void AbstractDomain::handleVerticalAxisRangeChanged(qreal min, qreal max)
99{
100 setRangeY(min, max);
101}
102
103void AbstractDomain::handleHorizontalAxisRangeChanged(qreal min, qreal max)
104{
105 setRangeX(min, max);
106}
107
108void AbstractDomain::handleReverseXChanged(bool reverse)
109{
110 m_reverseX = reverse;
111 emit updated();
112}
113
114void AbstractDomain::handleReverseYChanged(bool reverse)
115{
116 m_reverseY = reverse;
117 emit updated();
118}
119
120void AbstractDomain::blockRangeSignals(bool block)
121{
122 if (m_signalsBlocked!=block) {
123 m_signalsBlocked=block;
124 if (!block) {
125 emit rangeHorizontalChanged(min: m_minX,max: m_maxX);
126 emit rangeVerticalChanged(min: m_minY,max: m_maxY);
127 }
128 }
129}
130
131void AbstractDomain::zoomReset()
132{
133 if (m_zoomed) {
134 setRange(minX: m_zoomResetMinX,
135 maxX: m_zoomResetMaxX,
136 minY: m_zoomResetMinY,
137 maxY: m_zoomResetMaxY);
138 m_zoomed = false;
139 }
140}
141
142void AbstractDomain::storeZoomReset()
143{
144 if (!m_zoomed) {
145 m_zoomed = true;
146 m_zoomResetMinX = m_minX;
147 m_zoomResetMaxX = m_maxX;
148 m_zoomResetMinY = m_minY;
149 m_zoomResetMaxY = m_maxY;
150 }
151}
152
153//algorithm defined by Paul S.Heckbert GraphicalGems I
154
155void AbstractDomain::looseNiceNumbers(qreal &min, qreal &max, int &ticksCount)
156{
157 qreal range = niceNumber(x: max - min, ceiling: true); //range with ceiling
158 qreal step = niceNumber(x: range / (ticksCount - 1), ceiling: false);
159 min = std::floor(x: min / step);
160 max = std::ceil(x: max / step);
161 ticksCount = int(max - min) + 1;
162 min *= step;
163 max *= step;
164}
165
166//nice numbers can be expressed as form of 1*10^n, 2* 10^n or 5*10^n
167
168qreal AbstractDomain::niceNumber(qreal x, bool ceiling)
169{
170 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
171 qreal q = x / z; //q<10 && q>=1;
172
173 if (ceiling) {
174 if (q <= 1.0) q = 1;
175 else if (q <= 2.0) q = 2;
176 else if (q <= 5.0) q = 5;
177 else q = 10;
178 } else {
179 if (q < 1.5) q = 1;
180 else if (q < 3.0) q = 2;
181 else if (q < 7.0) q = 5;
182 else q = 10;
183 }
184 return q * z;
185}
186
187bool AbstractDomain::attachAxis(QAbstractAxis *axis)
188{
189 if (axis->orientation() == Qt::Vertical) {
190 // Color axis isn't connected to range-related slots/signals as it doesn't need
191 // geometry domain and it doesn't need to handle zooming or scrolling.
192 if (axis->type() != QAbstractAxis::AxisTypeColor) {
193 QObject::connect(sender: axis->d_ptr.data(), SIGNAL(rangeChanged(qreal, qreal)), receiver: this,
194 SLOT(handleVerticalAxisRangeChanged(qreal, qreal)));
195 QObject::connect(sender: this, SIGNAL(rangeVerticalChanged(qreal, qreal)), receiver: axis->d_ptr.data(),
196 SLOT(handleRangeChanged(qreal, qreal)));
197 }
198 QObject::connect(sender: axis, signal: &QAbstractAxis::reverseChanged,
199 context: this, slot: &AbstractDomain::handleReverseYChanged);
200 m_reverseY = axis->isReverse();
201 }
202
203 if (axis->orientation() == Qt::Horizontal) {
204 if (axis->type() != QAbstractAxis::AxisTypeColor) {
205 QObject::connect(sender: axis->d_ptr.data(), SIGNAL(rangeChanged(qreal, qreal)), receiver: this,
206 SLOT(handleHorizontalAxisRangeChanged(qreal, qreal)));
207 QObject::connect(sender: this, SIGNAL(rangeHorizontalChanged(qreal, qreal)), receiver: axis->d_ptr.data(),
208 SLOT(handleRangeChanged(qreal, qreal)));
209 }
210 QObject::connect(sender: axis, signal: &QAbstractAxis::reverseChanged,
211 context: this, slot: &AbstractDomain::handleReverseXChanged);
212 m_reverseX = axis->isReverse();
213 }
214
215 return true;
216}
217
218bool AbstractDomain::detachAxis(QAbstractAxis *axis)
219{
220 if (axis->orientation() == Qt::Vertical) {
221 QObject::disconnect(sender: axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), receiver: this, SLOT(handleVerticalAxisRangeChanged(qreal,qreal)));
222 QObject::disconnect(sender: this, SIGNAL(rangeVerticalChanged(qreal,qreal)), receiver: axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal)));
223 QObject::disconnect(sender: axis, signal: &QAbstractAxis::reverseChanged,
224 receiver: this, slot: &AbstractDomain::handleReverseYChanged);
225 }
226
227 if (axis->orientation() == Qt::Horizontal) {
228 QObject::disconnect(sender: axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), receiver: this, SLOT(handleHorizontalAxisRangeChanged(qreal,qreal)));
229 QObject::disconnect(sender: this, SIGNAL(rangeHorizontalChanged(qreal,qreal)), receiver: axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal)));
230 QObject::disconnect(sender: axis, signal: &QAbstractAxis::reverseChanged,
231 receiver: this, slot: &AbstractDomain::handleReverseXChanged);
232 }
233
234 return true;
235}
236
237// operators
238
239bool Q_AUTOTEST_EXPORT operator== (const AbstractDomain &domain1, const AbstractDomain &domain2)
240{
241 return (qFuzzyIsNull(d: domain1.m_maxX - domain2.m_maxX)
242 && qFuzzyIsNull(d: domain1.m_maxY - domain2.m_maxY)
243 && qFuzzyIsNull(d: domain1.m_minX - domain2.m_minX)
244 && qFuzzyIsNull(d: domain1.m_minY - domain2.m_minY));
245}
246
247
248bool Q_AUTOTEST_EXPORT operator!= (const AbstractDomain &domain1, const AbstractDomain &domain2)
249{
250 return !(domain1 == domain2);
251}
252
253
254QDebug Q_AUTOTEST_EXPORT operator<<(QDebug dbg, const AbstractDomain &domain)
255{
256#ifdef QT_NO_TEXTSTREAM
257 Q_UNUSED(domain);
258#else
259 dbg.nospace() << "AbstractDomain(" << domain.m_minX << ',' << domain.m_maxX << ',' << domain.m_minY << ',' << domain.m_maxY << ')' << domain.m_size;
260#endif
261 return dbg.maybeSpace();
262}
263
264// This function adjusts min/max ranges to failsafe values if negative/zero values are attempted.
265void AbstractDomain::adjustLogDomainRanges(qreal &min, qreal &max)
266{
267 if (min <= 0) {
268 min = 1.0;
269 if (max <= min)
270 max = min + 1.0;
271 }
272}
273
274// This function fixes the zoom rect based on axis reversals
275QRectF AbstractDomain::fixZoomRect(const QRectF &rect)
276{
277 QRectF fixRect = rect;
278 if (m_reverseX || m_reverseY) {
279 QPointF center = rect.center();
280 if (m_reverseX)
281 center.setX(m_size.width() - center.x());
282 if (m_reverseY)
283 center.setY(m_size.height() - center.y());
284 fixRect.moveCenter(p: QPointF(center.x(), center.y()));
285 }
286
287 return fixRect;
288}
289
290QT_END_NAMESPACE
291
292#include "moc_abstractdomain_p.cpp"
293

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