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