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

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