1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtCore/qmath.h>
5#include <private/abstractdomain_p.h>
6#include <private/chartlogvalueaxisx_p.h>
7#include <private/chartlogvalueaxisy_p.h>
8#include <private/polarchartlogvalueaxisangular_p.h>
9#include <private/polarchartlogvalueaxisradial_p.h>
10#include <private/qlogvalueaxis_p.h>
11
12QT_BEGIN_NAMESPACE
13
14/*!
15 \class QLogValueAxis
16 \inmodule QtCharts
17 \brief The QLogValueAxis class adds a logarithmic scale to a chart's axis.
18
19 A logarithmic scale is a nonlinear scale that is based on orders of magnitude,
20 so that each tick mark on the axis is the previous tick mark multiplied by a value.
21
22 \note If QLogValueAxis is attached to a series with one or more points with
23 negative or zero values on the associated dimension, the series will not be
24 plotted at all. This is particularly relevant when XYModelMappers are used,
25 since empty cells in models typically contain zero values.
26*/
27
28/*!
29 \qmltype LogValueAxis
30 \instantiates QLogValueAxis
31 \inqmlmodule QtCharts
32
33 \brief Adds a logarithmic scale to a chart's axis.
34 \inherits AbstractAxis
35
36 A logarithmic scale is a nonlinear scale that is based on orders of magnitude,
37 so that each tick mark on the axis is the previous tick mark multiplied by a value.
38
39 \note If a LogValueAxis type is attached to a series with one or more points with
40 negative or zero values on the associated dimension, the series will not be
41 plotted at all. This is particularly relevant when XYModelMappers are used,
42 since empty cells in models typically contain zero values.
43*/
44
45/*!
46 \property QLogValueAxis::min
47 \brief The minimum value on the axis.
48
49 When setting this property, the maximum value is adjusted if necessary, to ensure that
50 the range remains valid.
51 The value has to be greater than 0.
52*/
53/*!
54 \qmlproperty real LogValueAxis::min
55 The minimum value on the axis.
56
57 When setting this property, the maximum value is adjusted if necessary, to ensure that
58 the range remains valid.
59 The value has to be greater than 0.
60*/
61
62/*!
63 \property QLogValueAxis::max
64 \brief The maximum value on the axis.
65
66 When setting this property, the minimum value is adjusted if necessary, to ensure that
67 the range remains valid.
68 The value has to be greater than 0.
69*/
70/*!
71 \qmlproperty real LogValueAxis::max
72 The maximum value on the axis.
73
74 When setting this property, the minimum value is adjusted if necessary, to ensure that
75 the range remains valid.
76 The value has to be greater than 0.
77*/
78
79/*!
80 \property QLogValueAxis::base
81 \brief The base of the logarithm.
82
83 The value has to be greater than 0 and cannot equal 1.
84*/
85/*!
86 \qmlproperty real LogValueAxis::base
87 The base of the logarithm.
88
89 The value has to be greater than 0 and cannot equal 1.
90*/
91
92/*!
93 \property QLogValueAxis::tickCount
94 \brief The number of tick marks on the axis. This indicates how many grid lines are drawn on the
95 chart. This value is read-only.
96*/
97/*!
98 \qmlproperty int LogValueAxis::tickCount
99 The number of tick marks on the axis. This indicates how many grid lines are drawn on the
100 chart. This value is read-only.
101*/
102
103/*!
104 \property QLogValueAxis::minorTickCount
105 \brief The number of minor tick marks on the axis. This indicates how many grid lines are drawn
106 between major ticks on the chart. Labels are not drawn for minor ticks. The default value is 0.
107 Set the value to -1 and the number of grid lines between major ticks will be calculated
108 automatically.
109*/
110/*!
111 \qmlproperty int LogValueAxis::minorTickCount
112 The number of minor tick marks on the axis. This indicates how many grid lines are drawn between
113 major ticks on the chart. Labels are not drawn for minor ticks. The default value is 0. Set the
114 value to -1 and the number of grid lines between major ticks will be calculated automatically.
115*/
116
117/*!
118 \property QLogValueAxis::labelFormat
119 \brief The label format of the axis.
120
121 The format string supports the following conversion specifiers, length modifiers, and flags
122 provided by \c printf() in the standard C++ library: d, i, o, x, X, f, F, e, E, g, G, c.
123
124 If QChart::localizeNumbers is \c true, the supported specifiers are limited to:
125 d, e, E, f, g, G, and i. Also, only the precision modifier is supported. The rest of the
126 formatting comes from the default QLocale of the application.
127
128 \sa QString::asprintf()
129*/
130/*!
131 \qmlproperty string LogValueAxis::labelFormat
132 The label format of the axis.
133
134 The format string supports the following conversion specifiers, length modifiers, and flags
135 provided by \c printf() in the standard C++ library: d, i, o, x, X, f, F, e, E, g, G, c.
136
137 If \l{ChartView::localizeNumbers}{ChartView.localizeNumbers} is \c true, the supported
138 specifiers are limited to: d, e, E, f, g, G, and i. Also, only the precision modifier is
139 supported. The rest of the formatting comes from the default QLocale of the application.
140
141 \sa QString::asprintf()
142*/
143
144/*!
145 \fn void QLogValueAxis::minChanged(qreal min)
146 This signal is emitted when the minimum value of the axis, specified by \a min, changes.
147*/
148
149/*!
150 \fn void QLogValueAxis::maxChanged(qreal max)
151 This signal is emitted when the maximum value of the axis, specified by \a max, changes.
152*/
153
154/*!
155 \fn void QLogValueAxis::rangeChanged(qreal min, qreal max)
156 This signal is emitted when the minimum or maximum value of the axis, specified by \a min
157 and \a max, changes.
158*/
159
160/*!
161 \fn void QLogValueAxis::tickCountChanged(int tickCount)
162 This signal is emitted when the number of tick marks on the axis, specified by \a tickCount,
163 changes.
164*/
165/*!
166 \qmlsignal LogValueAxis::tickCountChanged(int tickCount)
167 This signal is emitted when the number of tick marks on the axis, specified by \a tickCount,
168 changes.
169*/
170
171/*!
172 \fn void QLogValueAxis::minorTickCountChanged(int minorTickCount)
173 This signal is emitted when the number of minor tick marks on the axis, specified by
174 \a minorTickCount, changes.
175*/
176/*!
177 \qmlsignal LogValueAxis::minorTickCountChanged(int minorTickCount)
178 This signal is emitted when the number of minor tick marks on the axis, specified by
179 \a minorTickCount, changes.
180*/
181
182/*!
183 \fn void QLogValueAxis::labelFormatChanged(const QString &format)
184 This signal is emitted when the \a format of axis labels changes.
185*/
186
187/*!
188 \fn void QLogValueAxis::baseChanged(qreal base)
189 This signal is emitted when the \a base of the logarithm of the axis changes.
190*/
191
192/*!
193 Constructs an axis object that is a child of \a parent.
194*/
195QLogValueAxis::QLogValueAxis(QObject *parent) :
196 QAbstractAxis(*new QLogValueAxisPrivate(this), parent)
197{
198
199}
200
201/*!
202 \internal
203*/
204QLogValueAxis::QLogValueAxis(QLogValueAxisPrivate &d, QObject *parent) : QAbstractAxis(d, parent)
205{
206
207}
208
209/*!
210 Destroys the object.
211*/
212QLogValueAxis::~QLogValueAxis()
213{
214 Q_D(QLogValueAxis);
215 if (d->m_chart)
216 d->m_chart->removeAxis(axis: this);
217}
218
219void QLogValueAxis::setMin(qreal min)
220{
221 Q_D(QLogValueAxis);
222 setRange(min, max: qMax(a: d->m_max, b: min));
223}
224
225qreal QLogValueAxis::min() const
226{
227 Q_D(const QLogValueAxis);
228 return d->m_min;
229}
230
231void QLogValueAxis::setMax(qreal max)
232{
233 Q_D(QLogValueAxis);
234 setRange(min: qMin(a: d->m_min, b: max), max);
235}
236
237qreal QLogValueAxis::max() const
238{
239 Q_D(const QLogValueAxis);
240 return d->m_max;
241}
242
243/*!
244 Sets the range from \a min to \a max on the axis.
245 If \a min is greater than \a max, this function returns without making any changes.
246*/
247void QLogValueAxis::setRange(qreal min, qreal max)
248{
249 Q_D(QLogValueAxis);
250
251 if (min > max)
252 return;
253
254 if (min > 0) {
255 bool changed = false;
256
257 if (!qFuzzyCompare(p1: d->m_min, p2: min)) {
258 d->m_min = min;
259 changed = true;
260 emit minChanged(min);
261 }
262
263 if (!qFuzzyCompare(p1: d->m_max, p2: max)) {
264 d->m_max = max;
265 changed = true;
266 emit maxChanged(max);
267 }
268
269 if (changed) {
270 d->updateTickCount();
271 emit rangeChanged(min, max);
272 emit d->rangeChanged(min,max);
273 }
274 }
275}
276
277void QLogValueAxis::setLabelFormat(const QString &format)
278{
279 Q_D(QLogValueAxis);
280
281 if (d->m_labelFormat == format)
282 return;
283
284 d->m_labelFormat = format;
285 emit labelFormatChanged(format: d->m_labelFormat);
286}
287
288QString QLogValueAxis::labelFormat() const
289{
290 Q_D(const QLogValueAxis);
291 return d->m_labelFormat;
292}
293
294void QLogValueAxis::setBase(qreal base)
295{
296 Q_D(QLogValueAxis);
297
298 if (base < 0.0 || qFuzzyIsNull(d: base) || qFuzzyCompare(p1: base, p2: 1.0) // check if base is correct
299 || qFuzzyCompare(p1: d->m_base, p2: base)) {
300 return;
301 }
302
303 d->m_base = base;
304 d->updateTickCount();
305 emit baseChanged(base: d->m_base);
306}
307
308qreal QLogValueAxis::base() const
309{
310 Q_D(const QLogValueAxis);
311 return d->m_base;
312}
313
314int QLogValueAxis::tickCount() const
315{
316 Q_D(const QLogValueAxis);
317 return d->m_tickCount;
318}
319
320void QLogValueAxis::setMinorTickCount(int minorTickCount)
321{
322 Q_D(QLogValueAxis);
323
324 if (minorTickCount < 0)
325 minorTickCount = -1;
326
327 if (d->m_minorTickCount == minorTickCount)
328 return;
329
330 d->m_minorTickCount = minorTickCount;
331 emit minorTickCountChanged(minorTickCount);
332}
333
334int QLogValueAxis::minorTickCount() const
335{
336 Q_D(const QLogValueAxis);
337 return d->m_minorTickCount;
338}
339
340/*!
341 Returns the type of the axis.
342*/
343QAbstractAxis::AxisType QLogValueAxis::type() const
344{
345 return AxisTypeLogValue;
346}
347
348////////////////////////////////////////////////////////////////////////////////////////////////////
349
350QLogValueAxisPrivate::QLogValueAxisPrivate(QLogValueAxis *q)
351 : QAbstractAxisPrivate(q),
352 m_min(1),
353 m_max(1),
354 m_base(10),
355 m_tickCount(0),
356 m_minorTickCount(0),
357 m_labelFormat()
358{
359}
360
361QLogValueAxisPrivate::~QLogValueAxisPrivate()
362{
363}
364
365void QLogValueAxisPrivate::setMin(const QVariant &min)
366{
367 Q_Q(QLogValueAxis);
368 bool ok;
369 qreal value = min.toReal(ok: &ok);
370 if (ok)
371 q->setMin(value);
372}
373
374void QLogValueAxisPrivate::setMax(const QVariant &max)
375{
376
377 Q_Q(QLogValueAxis);
378 bool ok;
379 qreal value = max.toReal(ok: &ok);
380 if (ok)
381 q->setMax(value);
382}
383
384void QLogValueAxisPrivate::setRange(const QVariant &min, const QVariant &max)
385{
386 Q_Q(QLogValueAxis);
387 bool ok1;
388 bool ok2;
389 qreal value1 = min.toReal(ok: &ok1);
390 qreal value2 = max.toReal(ok: &ok2);
391 if (ok1 && ok2)
392 q->setRange(min: value1, max: value2);
393}
394
395void QLogValueAxisPrivate::setRange(qreal min, qreal max)
396{
397 Q_Q(QLogValueAxis);
398
399 if (min > max)
400 return;
401
402 if (min > 0) {
403 bool changed = false;
404
405 if (!qFuzzyCompare(p1: m_min, p2: min)) {
406 m_min = min;
407 changed = true;
408 emit q->minChanged(min);
409 }
410
411 if (!qFuzzyCompare(p1: m_max, p2: max)) {
412 m_max = max;
413 changed = true;
414 emit q->maxChanged(max);
415 }
416
417 if (changed) {
418 updateTickCount();
419 emit rangeChanged(min,max);
420 emit q->rangeChanged(min, max);
421 }
422 }
423}
424
425void QLogValueAxisPrivate::updateTickCount()
426{
427 Q_Q(QLogValueAxis);
428
429 const qreal logMax = qLn(v: m_max) / qLn(v: m_base);
430 const qreal logMin = qLn(v: m_min) / qLn(v: m_base);
431 int tickCount = qAbs(t: qCeil(v: logMax) - qCeil(v: logMin));
432
433 // If the high edge sits exactly on the tick value, add a tick
434 qreal highValue = logMin < logMax ? logMax : logMin;
435 if (qFuzzyCompare(p1: highValue, p2: qreal(qCeil(v: highValue))))
436 ++tickCount;
437
438 if (m_tickCount == tickCount)
439 return;
440
441 m_tickCount = tickCount;
442 emit q->tickCountChanged(tickCount: m_tickCount);
443}
444
445void QLogValueAxisPrivate::initializeGraphics(QGraphicsItem *parent)
446{
447 Q_Q(QLogValueAxis);
448 ChartAxisElement *axis(0);
449
450 if (m_chart->chartType() == QChart::ChartTypeCartesian) {
451 if (orientation() == Qt::Vertical)
452 axis = new ChartLogValueAxisY(q,parent);
453 if (orientation() == Qt::Horizontal)
454 axis = new ChartLogValueAxisX(q,parent);
455 }
456
457 if (m_chart->chartType() == QChart::ChartTypePolar) {
458 if (orientation() == Qt::Vertical)
459 axis = new PolarChartLogValueAxisRadial(q, parent);
460 if (orientation() == Qt::Horizontal)
461 axis = new PolarChartLogValueAxisAngular(q, parent);
462 }
463
464 m_item.reset(p: axis);
465 QAbstractAxisPrivate::initializeGraphics(parent);
466}
467
468
469void QLogValueAxisPrivate::initializeDomain(AbstractDomain *domain)
470{
471 if (orientation() == Qt::Vertical) {
472 if (!qFuzzyCompare(p1: m_max, p2: m_min)) {
473 domain->setRangeY(min: m_min, max: m_max);
474 } else if (domain->minY() > 0) {
475 setRange(min: domain->minY(), max: domain->maxY());
476 } else if (domain->maxY() > 0) {
477 domain->setRangeY(min: m_min, max: domain->maxY());
478 } else {
479 domain->setRangeY(min: 1, max: 10);
480 }
481 }
482 if (orientation() == Qt::Horizontal) {
483 if (!qFuzzyCompare(p1: m_max, p2: m_min)) {
484 domain->setRangeX(min: m_min, max: m_max);
485 } else if (domain->minX() > 0){
486 setRange(min: domain->minX(), max: domain->maxX());
487 } else if (domain->maxX() > 0) {
488 domain->setRangeX(min: m_min, max: domain->maxX());
489 } else {
490 domain->setRangeX(min: 1, max: 10);
491 }
492 }
493}
494
495QT_END_NAMESPACE
496
497#include "moc_qlogvalueaxis.cpp"
498#include "moc_qlogvalueaxis_p.cpp"
499

source code of qtcharts/src/charts/axis/logvalueaxis/qlogvalueaxis.cpp