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