1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "q3dcamera_p.h"
5#include "utils_p.h"
6
7#include <QtCore/qmath.h>
8
9QT_BEGIN_NAMESPACE
10
11/*!
12 * \class Q3DCamera
13 * \inmodule QtGraphs
14 * \brief Representation of a camera in 3D space.
15 *
16 * Q3DCamera represents a basic orbit around centerpoint 3D camera that is used when rendering the
17 * graphs. The class offers simple methods for rotating the camera around the origin
18 * and setting the zoom level.
19 */
20
21/*!
22 * \enum Q3DCamera::CameraPreset
23 *
24 * Predefined positions for camera.
25 *
26 * \value CameraPresetNone
27 * Used to indicate a preset has not been set, or the scene has been rotated freely.
28 * \value CameraPresetFrontLow
29 * \value CameraPresetFront
30 * \value CameraPresetFrontHigh
31 * \value CameraPresetLeftLow
32 * \value CameraPresetLeft
33 * \value CameraPresetLeftHigh
34 * \value CameraPresetRightLow
35 * \value CameraPresetRight
36 * \value CameraPresetRightHigh
37 * \value CameraPresetBehindLow
38 * \value CameraPresetBehind
39 * \value CameraPresetBehindHigh
40 * \value CameraPresetIsometricLeft
41 * \value CameraPresetIsometricLeftHigh
42 * \value CameraPresetIsometricRight
43 * \value CameraPresetIsometricRightHigh
44 * \value CameraPresetDirectlyAbove
45 * \value CameraPresetDirectlyAboveCW45
46 * \value CameraPresetDirectlyAboveCCW45
47 * \value CameraPresetFrontBelow
48 * In Q3DBars from CameraPresetFrontBelow onward these only work for graphs including negative
49 * values. They act as Preset...Low for positive-only values.
50 * \value CameraPresetLeftBelow
51 * \value CameraPresetRightBelow
52 * \value CameraPresetBehindBelow
53 * \value CameraPresetDirectlyBelow
54 * Acts as CameraPresetFrontLow for positive-only bars.
55 */
56
57/*!
58 * \qmltype Camera3D
59 * \inqmlmodule QtGraphs
60 * \ingroup graphs_qml
61 * \instantiates Q3DCamera
62 * \brief Representation of a camera in 3D space.
63 *
64 * Camera3D represents a basic orbit around centerpoint 3D camera that is used when rendering the
65 * graphs. The type offers simple methods for rotating the camera around the origin
66 * and setting the zoom level.
67 *
68 * For Camera3D enums, see \l{Q3DCamera::CameraPreset}.
69 */
70
71/*!
72 * \qmlproperty float Camera3D::xRotation
73 *
74 * The X-rotation angle of the camera around the target point in degrees
75 * starting from the current base position.
76 */
77
78/*!
79 * \qmlproperty float Camera3D::yRotation
80 *
81 * The Y-rotation angle of the camera around the target point in degrees
82 * starting from the current base position.
83 */
84
85/*!
86 * \qmlproperty Camera3D.CameraPreset Camera3D::cameraPreset
87 *
88 * The currently active camera preset, which is one of
89 * \l{Q3DCamera::CameraPreset}{Camera3D.CameraPreset}. If no preset is active, the value
90 * is \l{Q3DCamera::CameraPresetNone}{Camera3D.CameraPresetNone}.
91 */
92
93/*!
94 * \qmlproperty float Camera3D::zoomLevel
95 *
96 * The camera zoom level in percentage. The default value of \c{100.0}
97 * means there is no zoom in or out set in the camera.
98 * The value is limited by the minZoomLevel and maxZoomLevel properties.
99 *
100 * \sa minZoomLevel, maxZoomLevel
101 */
102
103/*!
104 * \qmlproperty float Camera3D::minZoomLevel
105 *
106 * Sets the minimum allowed camera zoom level.
107 * If the new minimum level is higher than the existing maximum level, the maximum level is
108 * adjusted to the new minimum as well.
109 * If the current zoomLevel is outside the new bounds, it is adjusted as well.
110 * The minZoomLevel cannot be set below \c{1.0}.
111 * Defaults to \c{10.0}.
112 *
113 * \sa zoomLevel, maxZoomLevel
114 */
115
116/*!
117 * \qmlproperty float Camera3D::maxZoomLevel
118 *
119 * Sets the maximum allowed camera zoom level.
120 * If the new maximum level is lower than the existing minimum level, the minimum level is
121 * adjusted to the new maximum as well.
122 * If the current zoomLevel is outside the new bounds, it is adjusted as well.
123 * Defaults to \c{500.0f}.
124 *
125 * \sa zoomLevel, minZoomLevel
126 */
127
128/*!
129 * \qmlproperty bool Camera3D::wrapXRotation
130 *
131 * The behavior of the minimum and maximum limits in the X-rotation.
132 * By default, the X-rotation wraps from minimum value to maximum and from
133 * maximum to minimum.
134 *
135 * If set to \c true, the X-rotation of the camera is wrapped from minimum to
136 * maximum and from maximum to minimum. If set to \c false, the X-rotation of
137 * the camera is limited to the sector determined by the minimum and maximum
138 * values.
139 */
140
141/*!
142 * \qmlproperty bool Camera3D::wrapYRotation
143 *
144 * The behavior of the minimum and maximum limits in the Y-rotation.
145 * By default, the Y-rotation is limited between the minimum and maximum values
146 * without any wrapping.
147 *
148 * If \c true, the Y-rotation of the camera is wrapped from minimum to maximum
149 * and from maximum to minimum. If \c false, the Y-rotation of the camera is
150 * limited to the sector determined by the minimum and maximum values.
151 */
152
153/*!
154 * \qmlproperty vector3d Camera3D::target
155 *
156 * The camera target as a vector3d. Defaults to \c {vector3d(0.0, 0.0, 0.0)}.
157 *
158 * Valid coordinate values are between \c{-1.0...1.0}, where the edge values indicate
159 * the edges of the corresponding axis range. Any values outside this range are clamped to the edge.
160 *
161 * \note For bar graphs, the Y-coordinate is ignored and camera always targets a point on
162 * the horizontal background.
163 */
164
165/*!
166 * Constructs a new 3D camera with position set to origin, up direction facing towards the Y-axis
167 * and looking at origin by default. An optional \a parent parameter can be given and is then passed
168 * to QObject constructor.
169 */
170Q3DCamera::Q3DCamera(QObject *parent) :
171 Q3DObject(new Q3DCameraPrivate(this), parent)
172{
173}
174
175/*!
176 * Destroys the camera object.
177 */
178Q3DCamera::~Q3DCamera()
179{
180}
181
182/*!
183 * Copies the 3D camera's properties from the given source camera.
184 * Values are copied from the \a source to this object.
185 */
186void Q3DCamera::copyValuesFrom(const Q3DObject &source)
187{
188 Q_D(Q3DCamera);
189 // Note: Do not copy values from parent, as we are handling the position internally
190
191 const Q3DCamera &sourceCamera = static_cast<const Q3DCamera &>(source);
192
193 d->m_requestedTarget = sourceCamera.d_func()->m_requestedTarget;
194
195 d->m_xRotation = sourceCamera.d_func()->m_xRotation;
196 d->m_yRotation = sourceCamera.d_func()->m_yRotation;
197
198 d->m_minXRotation = sourceCamera.d_func()->m_minXRotation;
199 d->m_minYRotation = sourceCamera.d_func()->m_minYRotation;
200 d->m_maxXRotation = sourceCamera.d_func()->m_maxXRotation;
201 d->m_maxYRotation = sourceCamera.d_func()->m_maxYRotation;
202
203 d->m_wrapXRotation = sourceCamera.d_func()->m_wrapXRotation;
204 d->m_wrapYRotation = sourceCamera.d_func()->m_wrapYRotation;
205
206 d->m_zoomLevel = sourceCamera.d_func()->m_zoomLevel;
207 d->m_minZoomLevel = sourceCamera.d_func()->m_minZoomLevel;
208 d->m_maxZoomLevel = sourceCamera.d_func()->m_maxZoomLevel;
209 d->m_activePreset = sourceCamera.d_func()->m_activePreset;
210}
211
212/*!
213 * \property Q3DCamera::xRotation
214 *
215 * \brief The X-rotation angle of the camera around the target point in degrees.
216 */
217float Q3DCamera::xRotation() const
218{
219 const Q_D(Q3DCamera);
220 return d->m_xRotation;
221}
222
223void Q3DCamera::setXRotation(float rotation)
224{
225 Q_D(Q3DCamera);
226 if (d->m_wrapXRotation) {
227 rotation = Utils::wrapValue(value: rotation, min: d->m_minXRotation, max: d->m_maxXRotation);
228 } else {
229 rotation = qBound(min: float(d->m_minXRotation), val: float(rotation),
230 max: float(d->m_maxXRotation));
231 }
232
233 if (d->m_xRotation != rotation) {
234 d->setXRotation(rotation);
235 if (d->m_activePreset != CameraPresetNone) {
236 d->m_activePreset = CameraPresetNone;
237 setDirty(true);
238 }
239
240 emit xRotationChanged(rotation: d->m_xRotation);
241 }
242}
243
244/*!
245 * \property Q3DCamera::yRotation
246 *
247 * \brief The Y-rotation angle of the camera around the target point in degrees.
248 */
249float Q3DCamera::yRotation() const
250{
251 const Q_D(Q3DCamera);
252 return d->m_yRotation;
253}
254
255void Q3DCamera::setYRotation(float rotation)
256{
257 Q_D(Q3DCamera);
258 if (d->m_wrapYRotation) {
259 rotation = Utils::wrapValue(value: rotation, min: d->m_minYRotation, max: d->m_maxYRotation);
260 } else {
261 rotation = qBound(min: float(d->m_minYRotation), val: float(rotation),
262 max: float(d->m_maxYRotation));
263 }
264
265 if (d->m_yRotation != rotation) {
266 d->setYRotation(rotation);
267 if (d->m_activePreset != CameraPresetNone) {
268 d->m_activePreset = CameraPresetNone;
269 setDirty(true);
270 }
271
272 emit yRotationChanged(rotation: d->m_yRotation);
273 }
274}
275
276/*!
277 * \property Q3DCamera::cameraPreset
278 *
279 * \brief The currently active camera preset.
280 *
281 * If no CameraPreset value is set, CameraPresetNone is used by default.
282 */
283Q3DCamera::CameraPreset Q3DCamera::cameraPreset() const
284{
285 const Q_D(Q3DCamera);
286 return d->m_activePreset;
287}
288
289void Q3DCamera::setCameraPreset(Q3DCamera::CameraPreset preset)
290{
291 Q_D(Q3DCamera);
292 switch (preset) {
293 case CameraPresetFrontLow: {
294 setXRotation(0.0f);
295 setYRotation(0.0f);
296 break;
297 }
298 case CameraPresetFront: {
299 setXRotation(0.0f);
300 setYRotation(22.5f);
301 break;
302 }
303 case CameraPresetFrontHigh: {
304 setXRotation(0.0f);
305 setYRotation(45.0f);
306 break;
307 }
308 case CameraPresetLeftLow: {
309 setXRotation(90.0f);
310 setYRotation(0.0f);
311 break;
312 }
313 case CameraPresetLeft: {
314 setXRotation(90.0f);
315 setYRotation(22.5f);
316 break;
317 }
318 case CameraPresetLeftHigh: {
319 setXRotation(90.0f);
320 setYRotation(45.0f);
321 break;
322 }
323 case CameraPresetRightLow: {
324 setXRotation(-90.0f);
325 setYRotation(0.0f);
326 break;
327 }
328 case CameraPresetRight: {
329 setXRotation(-90.0f);
330 setYRotation(22.5f);
331 break;
332 }
333 case CameraPresetRightHigh: {
334 setXRotation(-90.0f);
335 setYRotation(45.0f);
336 break;
337 }
338 case CameraPresetBehindLow: {
339 setXRotation(180.0f);
340 setYRotation(0.0f);
341 break;
342 }
343 case CameraPresetBehind: {
344 setXRotation(180.0f);
345 setYRotation(22.5f);
346 break;
347 }
348 case CameraPresetBehindHigh: {
349 setXRotation(180.0f);
350 setYRotation(45.0f);
351 break;
352 }
353 case CameraPresetIsometricLeft: {
354 setXRotation(45.0f);
355 setYRotation(22.5f);
356 break;
357 }
358 case CameraPresetIsometricLeftHigh: {
359 setXRotation(45.0f);
360 setYRotation(45.0f);
361 break;
362 }
363 case CameraPresetIsometricRight: {
364 setXRotation(-45.0f);
365 setYRotation(22.5f);
366 break;
367 }
368 case CameraPresetIsometricRightHigh: {
369 setXRotation(-45.0f);
370 setYRotation(45.0f);
371 break;
372 }
373 case CameraPresetDirectlyAbove: {
374 setXRotation(0.0f);
375 setYRotation(90.0f);
376 break;
377 }
378 case CameraPresetDirectlyAboveCW45: {
379 setXRotation(-45.0f);
380 setYRotation(90.0f);
381 break;
382 }
383 case CameraPresetDirectlyAboveCCW45: {
384 setXRotation(45.0f);
385 setYRotation(90.0f);
386 break;
387 }
388 case CameraPresetFrontBelow: {
389 setXRotation(0.0f);
390 setYRotation(-45.0f);
391 break;
392 }
393 case CameraPresetLeftBelow: {
394 setXRotation(90.0f);
395 setYRotation(-45.0f);
396 break;
397 }
398 case CameraPresetRightBelow: {
399 setXRotation(-90.0f);
400 setYRotation(-45.0f);
401 break;
402 }
403 case CameraPresetBehindBelow: {
404 setXRotation(180.0f);
405 setYRotation(-45.0f);
406 break;
407 }
408 case CameraPresetDirectlyBelow: {
409 setXRotation(0.0f);
410 setYRotation(-90.0f);
411 break;
412 }
413 default:
414 preset = CameraPresetNone;
415 break;
416 }
417
418 // All presets target the center of the graph
419 setTarget(zeroVector);
420
421 if (d->m_activePreset != preset) {
422 d->m_activePreset = preset;
423 setDirty(true);
424 emit cameraPresetChanged(preset);
425 }
426}
427
428/*!
429 * \property Q3DCamera::zoomLevel
430 *
431 * \brief The camera zoom level in percentage.
432 *
433 * The default value of \c{100.0f} means there is no zoom in or out set in the
434 * camera. The value is limited by the minZoomLevel and maxZoomLevel properties.
435 *
436 * \sa minZoomLevel, maxZoomLevel
437 */
438float Q3DCamera::zoomLevel() const
439{
440 const Q_D(Q3DCamera);
441 return d->m_zoomLevel;
442}
443
444void Q3DCamera::setZoomLevel(float zoomLevel)
445{
446 Q_D(Q3DCamera);
447 float newZoomLevel = qBound(min: d->m_minZoomLevel, val: zoomLevel, max: d->m_maxZoomLevel);
448
449 if (d->m_zoomLevel != newZoomLevel) {
450 d->m_zoomLevel = newZoomLevel;
451 setDirty(true);
452 emit zoomLevelChanged(zoomLevel: newZoomLevel);
453 }
454}
455
456/*!
457 * \property Q3DCamera::minZoomLevel
458 *
459 * \brief The minimum allowed camera zoom level.
460 *
461 * If the minimum level is set to a new value that is higher than the existing
462 * maximum level, the maximum level is adjusted to the new minimum as well.
463 * If the current zoomLevel is outside the new bounds, it is adjusted as well.
464 * The minZoomLevel cannot be set below \c{1.0f}.
465 * Defaults to \c{10.0f}.
466 *
467 * \sa zoomLevel, maxZoomLevel
468 */
469float Q3DCamera::minZoomLevel() const
470{
471 const Q_D(Q3DCamera);
472 return d->m_minZoomLevel;
473}
474
475void Q3DCamera::setMinZoomLevel(float zoomLevel)
476{
477 Q_D(Q3DCamera);
478 // Don't allow minimum to be below one, as that can cause zoom to break.
479 float newMinLevel = qMax(a: zoomLevel, b: 1.0f);
480 if (d->m_minZoomLevel != newMinLevel) {
481 d->m_minZoomLevel = newMinLevel;
482 if (d->m_maxZoomLevel < newMinLevel)
483 setMaxZoomLevel(newMinLevel);
484 setZoomLevel(d->m_zoomLevel);
485 setDirty(true);
486 emit minZoomLevelChanged(zoomLevel: newMinLevel);
487 }
488}
489
490/*!
491 * \property Q3DCamera::maxZoomLevel
492 *
493 * \brief The maximum allowed camera zoom level.
494 *
495 * If the maximum level is set to a new value that is lower than the existing
496 * minimum level, the minimum level is adjusted to the new maximum as well.
497 * If the current zoomLevel is outside the new bounds, it is adjusted as well.
498 * Defaults to \c{500.0f}.
499 *
500 * \sa zoomLevel, minZoomLevel
501 */
502float Q3DCamera::maxZoomLevel() const
503{
504 const Q_D(Q3DCamera);
505 return d->m_maxZoomLevel;
506}
507
508void Q3DCamera::setMaxZoomLevel(float zoomLevel)
509{
510 Q_D(Q3DCamera);
511 // Don't allow maximum to be below one, as that can cause zoom to break.
512 float newMaxLevel = qMax(a: zoomLevel, b: 1.0f);
513 if (d->m_maxZoomLevel != newMaxLevel) {
514 d->m_maxZoomLevel = newMaxLevel;
515 if (d->m_minZoomLevel > newMaxLevel)
516 setMinZoomLevel(newMaxLevel);
517 setZoomLevel(d->m_zoomLevel);
518 setDirty(true);
519 emit maxZoomLevelChanged(zoomLevel: newMaxLevel);
520 }
521}
522
523/*!
524 * \property Q3DCamera::wrapXRotation
525 *
526 * \brief The behavior of the minimum and maximum limits in the X-rotation.
527 *
528 * If set to \c true, the X-rotation of the camera is wrapped from minimum to
529 * maximum and from maximum to minimum. If set to \c false, the X-rotation of
530 * the camera is limited to the sector determined by the minimum and maximum
531 * values. Set to \c true by default.
532 */
533bool Q3DCamera::wrapXRotation() const
534{
535 const Q_D(Q3DCamera);
536 return d->m_wrapXRotation;
537}
538
539void Q3DCamera::setWrapXRotation(bool isEnabled)
540{
541 Q_D(Q3DCamera);
542 d->m_wrapXRotation = isEnabled;
543}
544
545/*!
546 * \property Q3DCamera::wrapYRotation
547 *
548 * \brief The behavior of the minimum and maximum limits in the Y-rotation.
549 *
550 * If \c true, the Y-rotation of the camera is wrapped from minimum to maximum
551 * and from maximum to minimum. If \c false, the Y-rotation of the camera is
552 * limited to the sector determined by the minimum and maximum values.
553 * Set to \c true by default.
554 */
555bool Q3DCamera::wrapYRotation() const
556{
557 const Q_D(Q3DCamera);
558 return d->m_wrapYRotation;
559}
560
561void Q3DCamera::setWrapYRotation(bool isEnabled)
562{
563 Q_D(Q3DCamera);
564 d->m_wrapYRotation = isEnabled;
565}
566
567/*!
568 * Utility function that sets the camera rotations and distance.\a horizontal and \a vertical
569 * define the camera rotations to be used.
570 * Optional \a zoom parameter can be given to set the zoom percentage of the camera within
571 * the bounds defined by minZoomLevel and maxZoomLevel properties.
572 */
573void Q3DCamera::setCameraPosition(float horizontal, float vertical, float zoom)
574{
575 setZoomLevel(zoom);
576 setXRotation(horizontal);
577 setYRotation(vertical);
578}
579
580/*!
581 * \property Q3DCamera::target
582 *
583 * \brief The camera target as a vector or vertex in the 3D space.
584 *
585 * Defaults to \c {QVector3D(0.0, 0.0, 0.0)}.
586 *
587 * Valid coordinate values are between \c{-1.0...1.0}, where the edge values indicate
588 * the edges of the corresponding axis range. Any values outside this range are clamped to the edge.
589 *
590 * \note For bar graphs, the Y-coordinate is ignored and camera always targets a point on
591 * the horizontal background.
592 */
593QVector3D Q3DCamera::target() const
594{
595 const Q_D(Q3DCamera);
596 return d->m_requestedTarget;
597}
598
599void Q3DCamera::setTarget(const QVector3D &target)
600{
601 Q_D(Q3DCamera);
602 QVector3D newTarget = target;
603
604 if (newTarget.x() < -1.0f)
605 newTarget.setX(-1.0f);
606 else if (newTarget.x() > 1.0f)
607 newTarget.setX(1.0f);
608
609 if (newTarget.y() < -1.0f)
610 newTarget.setY(-1.0f);
611 else if (newTarget.y() > 1.0f)
612 newTarget.setY(1.0f);
613
614 if (newTarget.z() < -1.0f)
615 newTarget.setZ(-1.0f);
616 else if (newTarget.z() > 1.0f)
617 newTarget.setZ(1.0f);
618
619 if (d->m_requestedTarget != newTarget) {
620 if (d->m_activePreset != CameraPresetNone)
621 d->m_activePreset = CameraPresetNone;
622 d->m_requestedTarget = newTarget;
623 setDirty(true);
624 emit targetChanged(target: newTarget);
625 }
626}
627
628Q3DCameraPrivate::Q3DCameraPrivate(Q3DCamera *q) :
629 Q3DObjectPrivate(q),
630 m_isViewMatrixUpdateActive(true),
631 m_xRotation(0.0f),
632 m_yRotation(0.0f),
633 m_minXRotation(-180.0f),
634 m_minYRotation(0.0f),
635 m_maxXRotation(180.0f),
636 m_maxYRotation(90.0f),
637 m_zoomLevel(100.0f),
638 m_minZoomLevel(10.0f),
639 m_maxZoomLevel(500.0f),
640 m_wrapXRotation(true),
641 m_wrapYRotation(false),
642 m_activePreset(Q3DCamera::CameraPresetNone)
643{
644}
645
646Q3DCameraPrivate::~Q3DCameraPrivate()
647{
648}
649
650// Copies changed values from this camera to the other camera. If the other camera had same changes,
651// those changes are discarded.
652void Q3DCameraPrivate::sync(Q3DCamera &other)
653{
654 Q_Q(Q3DCamera);
655 if (q->isDirty()) {
656 other.copyValuesFrom(source: *q);
657 q->setDirty(false);
658 other.setDirty(false);
659 }
660}
661
662void Q3DCameraPrivate::setXRotation(const float rotation)
663{
664 Q_Q(Q3DCamera);
665 if (m_xRotation != rotation) {
666 m_xRotation = rotation;
667 q->setDirty(true);
668 }
669}
670
671void Q3DCameraPrivate::setYRotation(const float rotation)
672{
673 Q_Q(Q3DCamera);
674 if (m_yRotation != rotation) {
675 m_yRotation = rotation;
676 q->setDirty(true);
677 }
678}
679
680/*!
681 * \internal
682 * The current minimum X-rotation for the camera.
683 * The full circle range is \c{[-180, 180]} and the minimum value is limited to \c -180.
684 * Also the value can't be higher than the maximum, and is adjusted if necessary.
685 *
686 * \sa wrapXRotation, maxXRotation
687 */
688float Q3DCameraPrivate::minXRotation() const
689{
690 return m_minXRotation;
691}
692
693void Q3DCameraPrivate::setMinXRotation(float minRotation)
694{
695 Q_Q(Q3DCamera);
696 minRotation = qBound(min: -180.0f, val: minRotation, max: 180.0f);
697 if (minRotation > m_maxXRotation)
698 minRotation = m_maxXRotation;
699
700 if (m_minXRotation != minRotation) {
701 m_minXRotation = minRotation;
702 emit q->minXRotationChanged(rotation: minRotation);
703
704 if (m_xRotation < m_minXRotation)
705 setXRotation(m_xRotation);
706 q->setDirty(true);
707 }
708}
709
710/*!
711 * \internal
712 * The current minimum Y-rotation for the camera.
713 * The full Y angle range is \c{[-90, 90]} and the minimum value is limited to \c -90.
714 * Also the value can't be higher than the maximum, and is adjusted if necessary.
715 *
716 * \sa wrapYRotation, maxYRotation
717 */
718float Q3DCameraPrivate::minYRotation() const
719{
720 return m_minYRotation;
721}
722
723void Q3DCameraPrivate::setMinYRotation(float minRotation)
724{
725 Q_Q(Q3DCamera);
726 minRotation = qBound(min: -90.0f, val: minRotation, max: 90.0f);
727 if (minRotation > m_maxYRotation)
728 minRotation = m_maxYRotation;
729
730 if (m_minYRotation != minRotation) {
731 m_minYRotation = minRotation;
732 emit q->minYRotationChanged(rotation: minRotation);
733
734 if (m_yRotation < m_minYRotation)
735 setYRotation(m_yRotation);
736 q->setDirty(true);
737 }
738}
739
740/*!
741 * \internal
742 * The current maximum X-rotation for the camera.
743 * The full circle range is \c{[-180, 180]} and the maximum value is limited to \c 180.
744 * Also the value can't be lower than the minimum, and is adjusted if necessary.
745 *
746 * \sa wrapXRotation, minXRotation
747 */
748float Q3DCameraPrivate::maxXRotation() const
749{
750 return m_maxXRotation;
751}
752
753void Q3DCameraPrivate::setMaxXRotation(float maxRotation)
754{
755 Q_Q(Q3DCamera);
756 maxRotation = qBound(min: -180.0f, val: maxRotation, max: 180.0f);
757
758 if (maxRotation < m_minXRotation)
759 maxRotation = m_minXRotation;
760
761 if (m_maxXRotation != maxRotation) {
762 m_maxXRotation = maxRotation;
763 emit q->maxXRotationChanged(rotation: maxRotation);
764
765 if (m_xRotation > m_maxXRotation)
766 setXRotation(m_xRotation);
767 q->setDirty(true);
768 }
769}
770
771/*!
772 * \internal
773 * The current maximum Y-rotation for the camera.
774 * The full Y angle range is \c{[-90, 90]} and the maximum value is limited to \c 90.
775 * Also the value can't be lower than the minimum, and is adjusted if necessary.
776 *
777 * \sa wrapYRotation, minYRotation
778 */
779float Q3DCameraPrivate::maxYRotation() const
780{
781 return m_maxYRotation;
782}
783
784void Q3DCameraPrivate::setMaxYRotation(float maxRotation)
785{
786 Q_Q(Q3DCamera);
787 maxRotation = qBound(min: -90.0f, val: maxRotation, max: 90.0f);
788
789 if (maxRotation < m_minYRotation)
790 maxRotation = m_minYRotation;
791
792 if (m_maxYRotation != maxRotation) {
793 m_maxYRotation = maxRotation;
794 emit q->maxYRotationChanged(rotation: maxRotation);
795
796 if (m_yRotation > m_maxYRotation)
797 setYRotation(m_yRotation);
798 q->setDirty(true);
799 }
800}
801
802// Recalculates the view matrix based on the currently set base orientation, rotation and zoom level values.
803// zoomAdjustment is adjustment to ensure that the 3D visualization stays inside the view area in the 100% zoom.
804void Q3DCameraPrivate::updateViewMatrix(float zoomAdjustment)
805{
806 Q_Q(Q3DCamera);
807 if (!m_isViewMatrixUpdateActive)
808 return;
809
810 float zoom = m_zoomLevel * zoomAdjustment;
811 QMatrix4x4 viewMatrix;
812
813 // Apply to view matrix
814 viewMatrix.lookAt(eye: q->position(), center: m_actualTarget, up: m_up);
815 // Compensate for translation (if d->m_target is off origin)
816 viewMatrix.translate(x: m_actualTarget.x(), y: m_actualTarget.y(), z: m_actualTarget.z());
817 // Apply rotations
818 // Handle x and z rotation when y -angle is other than 0
819 viewMatrix.rotate(angle: m_xRotation, x: 0, y: qCos(v: qDegreesToRadians(degrees: m_yRotation)),
820 z: qSin(v: qDegreesToRadians(degrees: m_yRotation)));
821 // y rotation is always "clean"
822 viewMatrix.rotate(angle: m_yRotation, x: 1.0f, y: 0.0f, z: 0.0f);
823 // handle zoom by scaling
824 viewMatrix.scale(factor: zoom / 100.0f);
825 // Compensate for translation (if d->m_target is off origin)
826 viewMatrix.translate(x: -m_actualTarget.x(), y: -m_actualTarget.y(), z: -m_actualTarget.z());
827
828 setViewMatrix(viewMatrix);
829}
830
831/*!
832 * \internal
833 * The view matrix used in the 3D calculations. When the default orbiting
834 * camera behavior is sufficient, there is no need to touch this property. If the default
835 * behavior is insufficient, the view matrix can be set directly.
836 * \note When setting the view matrix directly remember to set viewMatrixAutoUpdateEnabled to
837 * \c false.
838 */
839QMatrix4x4 Q3DCameraPrivate::viewMatrix() const
840{
841 return m_viewMatrix;
842}
843
844void Q3DCameraPrivate::setViewMatrix(const QMatrix4x4 &viewMatrix)
845{
846 Q_Q(Q3DCamera);
847 if (m_viewMatrix != viewMatrix) {
848 m_viewMatrix = viewMatrix;
849 q->setDirty(true);
850 emit q->viewMatrixChanged(viewMatrix: m_viewMatrix);
851 }
852}
853
854/*!
855 * \internal
856 * This property determines if view matrix is automatically updated each render cycle using the
857 * current base orientation and rotations. If set to \c false, no automatic recalculation is done
858 * and the view matrix can be set using the viewMatrix property.
859 */
860bool Q3DCameraPrivate::isViewMatrixAutoUpdateEnabled() const
861{
862 return m_isViewMatrixUpdateActive;
863}
864
865void Q3DCameraPrivate::setViewMatrixAutoUpdateEnabled(bool isEnabled)
866{
867 Q_Q(Q3DCamera);
868 m_isViewMatrixUpdateActive = isEnabled;
869 emit q->viewMatrixAutoUpdateChanged(enabled: isEnabled);
870}
871
872/*!
873 * \internal
874 * Sets the base values for the camera that are used when calculating the camera position using the
875 * rotation values. The base position of the camera is defined by \a basePosition, expectation is
876 * that the x and y values are 0. Look at target point is defined by \a target and the camera
877 * rotates around it. Up direction for the camera is defined by \a baseUp, normally this is a
878 * vector with only y value set to 1.
879 */
880void Q3DCameraPrivate::setBaseOrientation(const QVector3D &basePosition,
881 const QVector3D &target,
882 const QVector3D &baseUp)
883{
884 Q_Q(Q3DCamera);
885 if (q->position() != basePosition || m_actualTarget != target || m_up != baseUp) {
886 q->setPosition(basePosition);
887 m_actualTarget = target;
888 m_up = baseUp;
889 q->setDirty(true);
890 }
891}
892
893/*!
894 * \internal
895 * Calculates and returns a position relative to the camera using the given parameters
896 * and the current camera viewMatrix property.
897 * The relative 3D offset to the current camera position is defined in \a relativePosition.
898 * An optional fixed rotation of the calculated point around the graph area can be
899 * given in \a fixedRotation. The rotation is given in degrees.
900 * An optional \a distanceModifier modifies the distance of the calculated point from the graph.
901 * \return calculated position relative to this camera's position.
902 */
903QVector3D Q3DCameraPrivate::calculatePositionRelativeToCamera(const QVector3D &relativePosition,
904 float fixedRotation,
905 float distanceModifier) const
906{
907 // Move the position with camera
908 const float radiusFactor = cameraDistance * (1.5f + distanceModifier);
909 float xAngle;
910 float yAngle;
911
912 if (!fixedRotation) {
913 xAngle = qDegreesToRadians(degrees: m_xRotation);
914 float yRotation = m_yRotation;
915 // Light must not be paraller to eye vector, so fudge the y rotation a bit.
916 // Note: This needs redoing if we ever allow arbitrary light positioning.
917 const float yMargin = 0.1f; // Smaller margins cause weird shadow artifacts on tops of bars
918 const float absYRotation = qAbs(t: yRotation);
919 if (absYRotation < 90.0f + yMargin && absYRotation > 90.0f - yMargin) {
920 if (yRotation < 0.0f)
921 yRotation = -90.0f + yMargin;
922 else
923 yRotation = 90.0f - yMargin;
924 }
925 yAngle = qDegreesToRadians(degrees: yRotation);
926 } else {
927 xAngle = qDegreesToRadians(degrees: fixedRotation);
928 yAngle = 0;
929 }
930 // Set radius to match the highest height of the position
931 const float radius = (radiusFactor + relativePosition.y());
932 const float zPos = radius * qCos(v: xAngle) * qCos(v: yAngle);
933 const float xPos = radius * qSin(v: xAngle) * qCos(v: yAngle);
934 const float yPos = radius * qSin(v: yAngle);
935
936 // Keep in the set position in relation to camera
937 return QVector3D(-xPos + relativePosition.x(),
938 yPos + relativePosition.y(),
939 zPos + relativePosition.z());
940}
941
942QT_END_NAMESPACE
943

source code of qtgraphs/src/graphs/engine/q3dcamera.cpp