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

source code of qtdatavis3d/src/datavisualization/engine/q3dcamera.cpp