1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtCore/qdebug.h>
5#include "qabstract3daxis_p.h"
6
7QT_BEGIN_NAMESPACE
8
9/*!
10 * \class QAbstract3DAxis
11 * \inmodule QtGraphs
12 * \brief The QAbstract3DAxis class is a base class for the axes of a graph.
13 *
14 * This class specifies the enumerations, properties, and functions shared by
15 * graph axes. It should not be used directly, but one of its subclasses should
16 * be used instead.
17 *
18 * \sa QCategory3DAxis, QValue3DAxis
19 */
20
21/*!
22 * \qmltype AbstractAxis3D
23 * \inqmlmodule QtGraphs
24 * \ingroup graphs_qml
25 * \instantiates QAbstract3DAxis
26 * \brief A base type for the axes of a graph.
27 *
28 * This type is uncreatable, but contains properties that are exposed via subtypes.
29 *
30 * For AbstractAxis3D enums, see \l QAbstract3DAxis::AxisOrientation and
31 * \l{QAbstract3DAxis::AxisType}.
32 */
33
34/*!
35 * \qmlproperty string AbstractAxis3D::title
36 * The title for the axis.
37 *
38 * \sa titleVisible, titleFixed
39 */
40
41/*!
42 * \qmlproperty list AbstractAxis3D::labels
43 * The labels for the axis.
44 * \note Setting this property for ValueAxis3D does nothing, as it generates labels automatically.
45 */
46
47/*!
48 * \qmlproperty AbstractAxis3D.AxisOrientation AbstractAxis3D::orientation
49 * The orientation of the axis.
50 */
51
52/*!
53 * \qmlproperty AbstractAxis3D.AxisType AbstractAxis3D::type
54 * The type of the axis.
55 */
56
57/*!
58 * \qmlproperty real AbstractAxis3D::min
59 *
60 * The minimum value on the axis.
61 * When setting this property, the maximum value is adjusted if necessary, to
62 * ensure that the range remains valid.
63 */
64
65/*!
66 * \qmlproperty real AbstractAxis3D::max
67 *
68 * The maximum value on the axis.
69 * When setting this property, the minimum value is adjusted if necessary, to
70 * ensure that the range remains valid.
71 */
72
73/*!
74 * \qmlproperty bool AbstractAxis3D::autoAdjustRange
75 *
76 * Defines whether the axis will automatically adjust the range so that all data fits in it.
77 */
78
79/*!
80 * \qmlproperty real AbstractAxis3D::labelAutoRotation
81 *
82 * The maximum angle the labels can autorotate when the camera angle changes.
83 * The angle can be between 0 and 90, inclusive. The default value is 0.
84 * If the value is 0, axis labels do not automatically rotate.
85 * If the value is greater than zero, labels attempt to orient themselves toward the camera, up to
86 * the specified angle.
87 */
88
89/*!
90 * \qmlproperty bool AbstractAxis3D::titleVisible
91 *
92 * Defines whether the axis title is visible in the primary graph view.
93 *
94 * The default value is \c{false}.
95 *
96 * \sa title, titleFixed
97 */
98
99/*!
100 * \qmlproperty bool AbstractAxis3D::titleFixed
101 *
102 * The rotation of axis titles.
103 *
104 * If \c{true}, axis titles in the primary graph view will be rotated towards the camera similarly
105 * to the axis labels.
106 * If \c{false}, axis titles are only rotated around their axis but are not otherwise oriented
107 * towards the camera.
108 * This property does not have any effect if the labelAutoRotation property
109 * value is zero.
110 * Default value is \c{true}.
111 *
112 * \sa labelAutoRotation, title, titleVisible
113 */
114
115/*!
116 * \enum QAbstract3DAxis::AxisOrientation
117 *
118 * The orientation of the axis object.
119 *
120 * \value AxisOrientationNone
121 * \value AxisOrientationX
122 * \value AxisOrientationY
123 * \value AxisOrientationZ
124 */
125
126/*!
127 * \enum QAbstract3DAxis::AxisType
128 *
129 * The type of the axis object.
130 *
131 * \value AxisTypeNone
132 * \value AxisTypeCategory
133 * \value AxisTypeValue
134 */
135
136/*!
137 * \internal
138 */
139QAbstract3DAxis::QAbstract3DAxis(QAbstract3DAxisPrivate *d, QObject *parent) :
140 QObject(parent),
141 d_ptr(d)
142{
143}
144
145/*!
146 * Destroys QAbstract3DAxis.
147 */
148QAbstract3DAxis::~QAbstract3DAxis()
149{
150}
151
152/*!
153 * \property QAbstract3DAxis::orientation
154 *
155 * \brief The orientation of the axis.
156 *
157 * The value is one of AxisOrientation values.
158 */
159QAbstract3DAxis::AxisOrientation QAbstract3DAxis::orientation() const
160{
161 const Q_D(QAbstract3DAxis);
162 return d->m_orientation;
163}
164
165/*!
166 * \property QAbstract3DAxis::type
167 *
168 * \brief The type of the axis.
169 *
170 * The value is one of AxisType values.
171 */
172QAbstract3DAxis::AxisType QAbstract3DAxis::type() const
173{
174 const Q_D(QAbstract3DAxis);
175 return d->m_type;
176}
177
178/*!
179 * \property QAbstract3DAxis::title
180 *
181 * \brief The title for the axis.
182 *
183 * \sa titleVisible, titleFixed
184 */
185void QAbstract3DAxis::setTitle(const QString &title)
186{
187 Q_D(QAbstract3DAxis);
188 if (d->m_title != title) {
189 d->m_title = title;
190 emit titleChanged(newTitle: title);
191 }
192}
193
194QString QAbstract3DAxis::title() const
195{
196 const Q_D(QAbstract3DAxis);
197 return d->m_title;
198}
199
200/*!
201 * \property QAbstract3DAxis::labels
202 *
203 * \brief The labels for the axis.
204 * \note Setting this property for QValue3DAxis does nothing, as it generates labels automatically.
205 */
206void QAbstract3DAxis::setLabels(const QStringList &labels)
207{
208 Q_UNUSED(labels);
209}
210
211QStringList QAbstract3DAxis::labels() const
212{
213 const Q_D(QAbstract3DAxis);
214 const_cast<QAbstract3DAxisPrivate *>(d)->updateLabels();
215 return const_cast<QAbstract3DAxisPrivate *>(d)->m_labels;
216}
217
218/*!
219 * Sets the value range of the axis from \a min to \a max.
220 * When setting the range, the maximum value is adjusted if necessary, to ensure
221 * that the range remains valid.
222 * \note For QCategory3DAxis, specifies the index range of rows or columns to
223 * show.
224 */
225void QAbstract3DAxis::setRange(float min, float max)
226{
227 Q_D(QAbstract3DAxis);
228 d->setRange(min, max);
229 setAutoAdjustRange(false);
230}
231
232/*!
233 * \property QAbstract3DAxis::labelAutoRotation
234 *
235 * \brief The maximum angle the labels can autorotate when the camera angle changes.
236 *
237 * The angle can be between 0 and 90, inclusive. The default value is 0.
238 * If the value is 0, axis labels do not automatically rotate.
239 * If the value is greater than zero, labels attempt to orient themselves toward the camera, up to
240 * the specified angle.
241 */
242void QAbstract3DAxis::setLabelAutoRotation(float angle)
243{
244 Q_D(QAbstract3DAxis);
245 if (angle < 0.0f)
246 angle = 0.0f;
247 if (angle > 90.0f)
248 angle = 90.0f;
249 if (d->m_labelAutoRotation != angle) {
250 d->m_labelAutoRotation = angle;
251 emit labelAutoRotationChanged(angle);
252 }
253}
254
255float QAbstract3DAxis::labelAutoRotation() const
256{
257 const Q_D(QAbstract3DAxis);
258 return d->m_labelAutoRotation;
259}
260
261/*!
262 * \property QAbstract3DAxis::titleVisible
263 *
264 * \brief Whether the axis title is visible in the primary graph view.
265 *
266 * The default value is \c{false}.
267 *
268 * \sa title, titleFixed
269 */
270void QAbstract3DAxis::setTitleVisible(bool visible)
271{
272 Q_D(QAbstract3DAxis);
273 if (d->m_titleVisible != visible) {
274 d->m_titleVisible = visible;
275 emit titleVisibilityChanged(visible);
276 }
277}
278
279bool QAbstract3DAxis::isTitleVisible() const
280{
281 const Q_D(QAbstract3DAxis);
282 return d->m_titleVisible;
283}
284
285/*!
286 * \property QAbstract3DAxis::titleFixed
287 *
288 * \brief The rotation of the axis titles.
289 *
290 * If \c{true}, axis titles in the primary graph view will be rotated towards the camera similarly
291 * to the axis labels.
292 * If \c{false}, axis titles are only rotated around their axis but are not otherwise oriented
293 * towards the camera.
294 * This property does not have any effect if the labelAutoRotation property
295 * value is zero.
296 * Default value is \c{true}.
297 *
298 * \sa labelAutoRotation, title, titleVisible
299 */
300void QAbstract3DAxis::setTitleFixed(bool fixed)
301{
302 Q_D(QAbstract3DAxis);
303 if (d->m_titleFixed != fixed) {
304 d->m_titleFixed = fixed;
305 emit titleFixedChanged(fixed);
306 }
307}
308
309bool QAbstract3DAxis::isTitleFixed() const
310{
311 const Q_D(QAbstract3DAxis);
312 return d->m_titleFixed;
313}
314
315/*!
316 * \property QAbstract3DAxis::min
317 *
318 * \brief The minimum value on the axis.
319 *
320 * When setting this property, the maximum value is adjusted if necessary, to
321 * ensure that the range remains valid.
322 * \note For QCategory3DAxis, specifies the index of the first row or column to
323 * show.
324 */
325void QAbstract3DAxis::setMin(float min)
326{
327 Q_D(QAbstract3DAxis);
328 d->setMin(min);
329 setAutoAdjustRange(false);
330}
331
332/*!
333 * \property QAbstract3DAxis::max
334 *
335 * \brief The maximum value on the axis.
336 *
337 * When setting this property, the minimum value is adjusted if necessary, to
338 * ensure that the range remains valid.
339 * \note For QCategory3DAxis, specifies the index of the last row or column to
340 * show.
341 */
342void QAbstract3DAxis::setMax(float max)
343{
344 Q_D(QAbstract3DAxis);
345 d->setMax(max);
346 setAutoAdjustRange(false);
347}
348
349float QAbstract3DAxis::min() const
350{
351 const Q_D(QAbstract3DAxis);
352 return d->m_min;
353}
354
355float QAbstract3DAxis::max() const
356{
357 const Q_D(QAbstract3DAxis);
358 return d->m_max;
359}
360
361/*!
362 * \property QAbstract3DAxis::autoAdjustRange
363 *
364 * \brief Whether the axis will automatically adjust the range so that all data fits in it.
365 *
366 * \sa setRange(), setMin(), setMax()
367 */
368void QAbstract3DAxis::setAutoAdjustRange(bool autoAdjust)
369{
370 Q_D(QAbstract3DAxis);
371 if (d->m_autoAdjust != autoAdjust) {
372 d->m_autoAdjust = autoAdjust;
373 emit autoAdjustRangeChanged(autoAdjust);
374 }
375}
376
377bool QAbstract3DAxis::isAutoAdjustRange() const
378{
379 const Q_D(QAbstract3DAxis);
380 return d->m_autoAdjust;
381}
382
383/*!
384 * \fn QAbstract3DAxis::rangeChanged(float min, float max)
385 *
386 * Emits the minimum and maximum values of the range, \a min and \a max, when
387 * the range changes.
388 */
389
390// QAbstract3DAxisPrivate
391QAbstract3DAxisPrivate::QAbstract3DAxisPrivate(QAbstract3DAxis *q, QAbstract3DAxis::AxisType type)
392 : q_ptr(q),
393 m_orientation(QAbstract3DAxis::AxisOrientationNone),
394 m_type(type),
395 m_isDefaultAxis(false),
396 m_min(0.0f),
397 m_max(10.0f),
398 m_autoAdjust(true),
399 m_labelAutoRotation(0.0f),
400 m_titleVisible(false),
401 m_titleFixed(true)
402{
403}
404
405QAbstract3DAxisPrivate::~QAbstract3DAxisPrivate()
406{
407}
408
409void QAbstract3DAxisPrivate::setOrientation(QAbstract3DAxis::AxisOrientation orientation)
410{
411 Q_Q(QAbstract3DAxis);
412 if (m_orientation == QAbstract3DAxis::AxisOrientationNone) {
413 m_orientation = orientation;
414 emit q->orientationChanged(orientation);
415 } else {
416 Q_ASSERT("Attempted to reset axis orientation.");
417 }
418}
419
420void QAbstract3DAxisPrivate::updateLabels()
421{
422 // Default implementation does nothing
423}
424
425void QAbstract3DAxisPrivate::setRange(float min, float max, bool suppressWarnings)
426{
427 Q_Q(QAbstract3DAxis);
428 bool adjusted = false;
429 if (!allowNegatives()) {
430 if (allowZero()) {
431 if (min < 0.0f) {
432 min = 0.0f;
433 adjusted = true;
434 }
435 if (max < 0.0f) {
436 max = 0.0f;
437 adjusted = true;
438 }
439 } else {
440 if (min <= 0.0f) {
441 min = 1.0f;
442 adjusted = true;
443 }
444 if (max <= 0.0f) {
445 max = 1.0f;
446 adjusted = true;
447 }
448 }
449 }
450 // If min >= max, we adjust ranges so that
451 // m_max becomes (min + 1.0f)
452 // as axes need some kind of valid range.
453 bool minDirty = false;
454 bool maxDirty = false;
455 if (m_min != min) {
456 m_min = min;
457 minDirty = true;
458 }
459 if (m_max != max || min > max || (!allowMinMaxSame() && min == max)) {
460 if (min > max || (!allowMinMaxSame() && min == max)) {
461 m_max = min + 1.0f;
462 adjusted = true;
463 } else {
464 m_max = max;
465 }
466 maxDirty = true;
467 }
468
469 if (minDirty || maxDirty) {
470 if (adjusted && !suppressWarnings) {
471 qWarning() << "Warning: Tried to set invalid range for axis."
472 " Range automatically adjusted to a valid one:"
473 << min << "-" << max << "-->" << m_min << "-" << m_max;
474 }
475 emit q->rangeChanged(min: m_min, max: m_max);
476 }
477
478 if (minDirty)
479 emit q->minChanged(value: m_min);
480 if (maxDirty)
481 emit q->maxChanged(value: m_max);
482}
483
484void QAbstract3DAxisPrivate::setMin(float min)
485{
486 Q_Q(QAbstract3DAxis);
487 if (!allowNegatives()) {
488 if (allowZero()) {
489 if (min < 0.0f) {
490 min = 0.0f;
491 qWarning() << "Warning: Tried to set negative minimum for an axis that only"
492 "supports positive values and zero:" << min;
493 }
494 } else {
495 if (min <= 0.0f) {
496 min = 1.0f;
497 qWarning() << "Warning: Tried to set negative or zero minimum for an axis that only"
498 "supports positive values:" << min;
499 }
500 }
501 }
502
503 if (m_min != min) {
504 bool maxChanged = false;
505 if (min > m_max || (!allowMinMaxSame() && min == m_max)) {
506 float oldMax = m_max;
507 m_max = min + 1.0f;
508 qWarning() << "Warning: Tried to set minimum to equal or larger than maximum for"
509 " value axis. Maximum automatically adjusted to a valid one:"
510 << oldMax << "-->" << m_max;
511 maxChanged = true;
512 }
513 m_min = min;
514
515 emit q->rangeChanged(min: m_min, max: m_max);
516 emit q->minChanged(value: m_min);
517 if (maxChanged)
518 emit q->maxChanged(value: m_max);
519 }
520}
521
522void QAbstract3DAxisPrivate::setMax(float max)
523{
524 Q_Q(QAbstract3DAxis);
525 if (!allowNegatives()) {
526 if (allowZero()) {
527 if (max < 0.0f) {
528 max = 0.0f;
529 qWarning() << "Warning: Tried to set negative maximum for an axis that only"
530 "supports positive values and zero:" << max;
531 }
532 } else {
533 if (max <= 0.0f) {
534 max = 1.0f;
535 qWarning() << "Warning: Tried to set negative or zero maximum for an axis that only"
536 "supports positive values:" << max;
537 }
538 }
539 }
540
541 if (m_max != max) {
542 bool minChanged = false;
543 if (m_min > max || (!allowMinMaxSame() && m_min == max)) {
544 float oldMin = m_min;
545 m_min = max - 1.0f;
546 if (!allowNegatives() && m_min < 0.0f) {
547 if (allowZero())
548 m_min = 0.0f;
549 else
550 m_min = max / 2.0f; // Need some positive value smaller than max
551
552 if (!allowMinMaxSame() && max == 0.0f) {
553 m_min = oldMin;
554 qWarning() << "Unable to set maximum value to zero.";
555 return;
556 }
557 }
558 qWarning() << "Warning: Tried to set maximum to equal or smaller than minimum for"
559 " value axis. Minimum automatically adjusted to a valid one:"
560 << oldMin << "-->" << m_min;
561 minChanged = true;
562 }
563 m_max = max;
564 emit q->rangeChanged(min: m_min, max: m_max);
565 emit q->maxChanged(value: m_max);
566 if (minChanged)
567 emit q->minChanged(value: m_min);
568 }
569}
570
571QT_END_NAMESPACE
572

source code of qtgraphs/src/graphs/axis/qabstract3daxis.cpp