| 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 |  | 
| 35 | QT_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 |  */ | 
| 199 | Q3DCamera::Q3DCamera(QObject *parent) : | 
| 200 |     Q3DObject(parent), | 
| 201 |     d_ptr(new Q3DCameraPrivate(this)) | 
| 202 | { | 
| 203 | } | 
| 204 |  | 
| 205 | /*! | 
| 206 |  *  Destroys the camera object. | 
| 207 |  */ | 
| 208 | Q3DCamera::~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 |  */ | 
| 216 | void 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 |  */ | 
| 246 | float Q3DCamera::xRotation() const { | 
| 247 |     return d_ptr->m_xRotation; | 
| 248 | } | 
| 249 |  | 
| 250 | void 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 |  */ | 
| 275 | float Q3DCamera::yRotation() const { | 
| 276 |     return d_ptr->m_yRotation; | 
| 277 | } | 
| 278 |  | 
| 279 | void 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 |  */ | 
| 306 | Q3DCamera::CameraPreset Q3DCamera::cameraPreset() const | 
| 307 | { | 
| 308 |     return d_ptr->m_activePreset; | 
| 309 | } | 
| 310 |  | 
| 311 | void 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 |  */ | 
| 459 | float Q3DCamera::zoomLevel() const | 
| 460 | { | 
| 461 |     return d_ptr->m_zoomLevel; | 
| 462 | } | 
| 463 |  | 
| 464 | void 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 |  */ | 
| 488 | float Q3DCamera::minZoomLevel() const | 
| 489 | { | 
| 490 |     return d_ptr->m_minZoomLevel; | 
| 491 | } | 
| 492 |  | 
| 493 | void 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 |  */ | 
| 519 | float Q3DCamera::maxZoomLevel() const | 
| 520 | { | 
| 521 |     return d_ptr->m_maxZoomLevel; | 
| 522 | } | 
| 523 |  | 
| 524 | void 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 |  */ | 
| 548 | bool Q3DCamera::wrapXRotation() const | 
| 549 | { | 
| 550 |     return d_ptr->m_wrapXRotation; | 
| 551 | } | 
| 552 |  | 
| 553 | void 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 |  */ | 
| 568 | bool Q3DCamera::wrapYRotation() const | 
| 569 | { | 
| 570 |     return d_ptr->m_wrapYRotation; | 
| 571 | } | 
| 572 |  | 
| 573 | void 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 |  */ | 
| 584 | void 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 |  */ | 
| 605 | QVector3D Q3DCamera::target() const | 
| 606 | { | 
| 607 |     return d_ptr->m_requestedTarget; | 
| 608 | } | 
| 609 |  | 
| 610 | void 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 |  | 
| 638 | Q3DCameraPrivate::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 |  | 
| 656 | Q3DCameraPrivate::~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. | 
| 662 | void 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 |  | 
| 671 | void Q3DCameraPrivate::setXRotation(const float rotation) | 
| 672 | { | 
| 673 |     if (m_xRotation != rotation) { | 
| 674 |         m_xRotation = rotation; | 
| 675 |         q_ptr->setDirty(true); | 
| 676 |     } | 
| 677 | } | 
| 678 |  | 
| 679 | void 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 |  */ | 
| 695 | float Q3DCameraPrivate::minXRotation() const | 
| 696 | { | 
| 697 |     return m_minXRotation; | 
| 698 | } | 
| 699 |  | 
| 700 | void 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 |  */ | 
| 724 | float Q3DCameraPrivate::minYRotation() const | 
| 725 | { | 
| 726 |     return m_minYRotation; | 
| 727 | } | 
| 728 |  | 
| 729 | void 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 |  */ | 
| 753 | float Q3DCameraPrivate::maxXRotation() const | 
| 754 | { | 
| 755 |     return m_maxXRotation; | 
| 756 | } | 
| 757 |  | 
| 758 | void 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 |  */ | 
| 783 | float Q3DCameraPrivate::maxYRotation() const | 
| 784 | { | 
| 785 |     return m_maxYRotation; | 
| 786 | } | 
| 787 |  | 
| 788 | void 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. | 
| 807 | void 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 |  */ | 
| 841 | QMatrix4x4 Q3DCameraPrivate::viewMatrix() const | 
| 842 | { | 
| 843 |     return m_viewMatrix; | 
| 844 | } | 
| 845 |  | 
| 846 | void 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 |  */ | 
| 861 | bool Q3DCameraPrivate::isViewMatrixAutoUpdateEnabled() const | 
| 862 | { | 
| 863 |     return m_isViewMatrixUpdateActive; | 
| 864 | } | 
| 865 |  | 
| 866 | void 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 |  */ | 
| 880 | void 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 |  */ | 
| 903 | QVector3D 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 |  | 
| 942 | QT_END_NAMESPACE_DATAVISUALIZATION | 
| 943 |  |