| 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 "q3dscene_p.h" | 
| 31 | #include "q3dcamera_p.h" | 
| 32 | #include "q3dlight_p.h" | 
| 33 |  | 
| 34 | QT_BEGIN_NAMESPACE_DATAVISUALIZATION | 
| 35 |  | 
| 36 | /*! | 
| 37 |  * \class Q3DScene | 
| 38 |  * \inmodule QtDataVisualization | 
| 39 |  * \brief Q3DScene class provides description of the 3D scene being visualized. | 
| 40 |  * \since QtDataVisualization 1.0 | 
| 41 |  * | 
| 42 |  * The 3D scene contains a single active camera and a single active light source. | 
| 43 |  * Visualized data is assumed to be at a fixed location. | 
| 44 |  * | 
| 45 |  * The 3D scene also keeps track of the viewport in which visualization rendering is done, | 
| 46 |  * the primary subviewport inside the viewport where the main 3D data visualization view resides | 
| 47 |  * and the secondary subviewport where the 2D sliced view of the data resides. The subviewports are | 
| 48 |  * by default resized by the \a Q3DScene. To override the resize behavior you need to listen to both | 
| 49 |  * \l viewportChanged() and \l slicingActiveChanged() signals and recalculate the subviewports accordingly. | 
| 50 |  * | 
| 51 |  * Also the scene has flag for tracking if the secondary 2D slicing view is currently active or not. | 
| 52 |  * \note Not all visualizations support the secondary 2D slicing view. | 
| 53 |  */ | 
| 54 |  | 
| 55 | /*! | 
| 56 |  * \class Q3DSceneChangeBitField | 
| 57 |  * \internal | 
| 58 |  */ | 
| 59 |  | 
| 60 | /*! | 
| 61 |  * \qmltype Scene3D | 
| 62 |  * \inqmlmodule QtDataVisualization | 
| 63 |  * \since QtDataVisualization 1.0 | 
| 64 |  * \ingroup datavisualization_qml | 
| 65 |  * \instantiates Q3DScene | 
| 66 |  * \brief Scene3D type provides description of the 3D scene being visualized. | 
| 67 |  * | 
| 68 |  * The 3D scene contains a single active camera and a single active light source. | 
| 69 |  * Visualized data is assumed to be at a fixed location. | 
| 70 |  * | 
| 71 |  * The 3D scene also keeps track of the viewport in which visualization rendering is done, | 
| 72 |  * the primary subviewport inside the viewport where the main 3D data visualization view resides | 
| 73 |  * and the secondary subviewport where the 2D sliced view of the data resides. | 
| 74 |  * | 
| 75 |  * Also the scene has flag for tracking if the secondary 2D slicing view is currently active or not. | 
| 76 |  * \note Not all visualizations support the secondary 2D slicing view. | 
| 77 |  */ | 
| 78 |  | 
| 79 | /*! | 
| 80 |  * \qmlproperty rect Scene3D::viewport | 
| 81 |  * | 
| 82 |  * The current viewport rectangle where all 3D rendering is targeted. | 
| 83 |  */ | 
| 84 |  | 
| 85 | /*! | 
| 86 |  * \qmlproperty rect Scene3D::primarySubViewport | 
| 87 |  * | 
| 88 |  * The current subviewport rectangle inside the viewport where the | 
| 89 |  * primary view of the data visualization is targeted. | 
| 90 |  * | 
| 91 |  * If slicingActive is \c false, the primary sub viewport will be equal to the | 
| 92 |  * viewport. If slicingActive is \c true and the primary sub viewport has not | 
| 93 |  * been explicitly set, it will be one fifth of the viewport. | 
| 94 |  * \note Setting primarySubViewport larger than or outside of viewport resizes viewport accordingly. | 
| 95 |  */ | 
| 96 |  | 
| 97 | /*! | 
| 98 |  * \qmlproperty rect Scene3D::secondarySubViewport | 
| 99 |  * | 
| 100 |  * The secondary viewport rectangle inside the viewport. The secondary viewport | 
| 101 |  * is used for drawing the 2D slice view in some visualizations. If it has not | 
| 102 |  * been explicitly set, it will be null. If slicingActive is \c true, it will | 
| 103 |  * be equal to the viewport. | 
| 104 |  * \note If the secondary sub viewport is larger than or outside of the | 
| 105 |  * viewport, the viewport is resized accordingly. | 
| 106 | */ | 
| 107 |  | 
| 108 | /*! | 
| 109 |  * \qmlproperty point Scene3D::selectionQueryPosition | 
| 110 |  * | 
| 111 |  * The coordinates for the user input that should be processed | 
| 112 |  * by the scene as a selection. If this property is set to a value other than | 
| 113 |  * invalidSelectionPoint, the | 
| 114 |  * graph tries to select a data item at the given point within the primary viewport. | 
| 115 |  * After the rendering pass, the property is returned to its default state of | 
| 116 |  * invalidSelectionPoint. | 
| 117 |  */ | 
| 118 |  | 
| 119 | /*! | 
| 120 |  * \qmlproperty point Scene3D::graphPositionQuery | 
| 121 |  * | 
| 122 |  * The coordinates for the user input that should be processed by the scene as a | 
| 123 |  * graph position query. If this property is set to value other than | 
| 124 |  * invalidSelectionPoint, the graph tries to match a graph position to the given point | 
| 125 |  * within the primary viewport. | 
| 126 |  * After the rendering pass, this property is returned to its default state of | 
| 127 |  * invalidSelectionPoint. The queried graph position can be read from the | 
| 128 |  * AbstractGraph3D::queriedGraphPosition property after the next render pass. | 
| 129 |  * | 
| 130 |  * There is no single correct 3D coordinate to match a particular screen position, so to be | 
| 131 |  * consistent, the queries are always done against the inner sides of an invisible box surrounding | 
| 132 |  * the graph. | 
| 133 |  * | 
| 134 |  * \note Bar graphs allow graph position queries only at the graph floor level. | 
| 135 |  * | 
| 136 |  * \sa AbstractGraph3D::queriedGraphPosition | 
| 137 |  */ | 
| 138 |  | 
| 139 | /*! | 
| 140 |  * \qmlproperty bool Scene3D::slicingActive | 
| 141 |  * | 
| 142 |  * Defines whether the 2D slicing view is currently active. If \c true, | 
| 143 |  * AbstractGraph3D::selectionMode must have either the | 
| 144 |  * \l{QAbstract3DGraph::SelectionRow}{AbstractGraph3D.SelectionRow} or | 
| 145 |  * \l{QAbstract3DGraph::SelectionColumn}{AbstractGraph3D.SelectionColumn} | 
| 146 |  * set to a valid selection. | 
| 147 |  * \note Not all visualizations support the 2D slicing view. | 
| 148 |  */ | 
| 149 |  | 
| 150 | /*! | 
| 151 |  * \qmlproperty bool Scene3D::secondarySubviewOnTop | 
| 152 |  * | 
| 153 |  * Defines whether the 2D slicing view or the 3D view is drawn on top. | 
| 154 |  */ | 
| 155 |  | 
| 156 | /*! | 
| 157 |  * \qmlproperty Camera3D Scene3D::activeCamera | 
| 158 |  * | 
| 159 |  * The currently active camera in the 3D scene. | 
| 160 |  * When a Camera3D is set in the property, it is automatically added as child of | 
| 161 |  * the scene. | 
| 162 |  */ | 
| 163 |  | 
| 164 | /*! | 
| 165 |  * \qmlproperty Light3D Scene3D::activeLight | 
| 166 |  * | 
| 167 |  * The currently active light in the 3D scene. | 
| 168 |  * When a Light3D is set in the property, it is automatically added as child of | 
| 169 |  * the scene. | 
| 170 |  */ | 
| 171 |  | 
| 172 | /*! | 
| 173 |  * \qmlproperty float Scene3D::devicePixelRatio | 
| 174 |  * | 
| 175 |  * The current device pixel ratio that is used when mapping input | 
| 176 |  * coordinates to pixel coordinates. | 
| 177 |  */ | 
| 178 |  | 
| 179 | /*! | 
| 180 |  * \qmlproperty point Scene3D::invalidSelectionPoint | 
| 181 |  * A constant property providing an invalid point for selection. | 
| 182 |  */ | 
| 183 |  | 
| 184 | /*! | 
| 185 |  * Constructs a basic scene with one light and one camera in it. An | 
| 186 |  * optional \a parent parameter can be given and is then passed to QObject constructor. | 
| 187 |  */ | 
| 188 | Q3DScene::Q3DScene(QObject *parent) : | 
| 189 |     QObject(parent), | 
| 190 |     d_ptr(new Q3DScenePrivate(this)) | 
| 191 | { | 
| 192 |     setActiveCamera(new Q3DCamera(0)); | 
| 193 |     setActiveLight(new Q3DLight(0)); | 
| 194 | } | 
| 195 |  | 
| 196 | /*! | 
| 197 |  * Destroys the 3D scene and all the objects contained within it. | 
| 198 |  */ | 
| 199 | Q3DScene::~Q3DScene() | 
| 200 | { | 
| 201 | } | 
| 202 |  | 
| 203 | /*! | 
| 204 |  * \property Q3DScene::viewport | 
| 205 |  * | 
| 206 |  * \brief A read only property that contains the current viewport rectangle | 
| 207 |  * where all the 3D rendering is targeted. | 
| 208 |  */ | 
| 209 | QRect Q3DScene::viewport() const | 
| 210 | { | 
| 211 |     return d_ptr->m_viewport; | 
| 212 | } | 
| 213 |  | 
| 214 | /*! | 
| 215 |  * \property Q3DScene::primarySubViewport | 
| 216 |  * | 
| 217 |  * \brief The current subviewport rectangle inside the viewport where the | 
| 218 |  * primary view of the data visualization is targeted. | 
| 219 |  * | 
| 220 |  * If isSlicingActive() is \c false, the primary sub viewport is equal to | 
| 221 |  * viewport(). If isSlicingActive() is \c true and the primary sub viewport has | 
| 222 |  * not been explicitly set, it will be one fifth of viewport(). | 
| 223 |  * | 
| 224 |  * \note Setting primarySubViewport larger than or outside of the viewport | 
| 225 |  * resizes the viewport accordingly. | 
| 226 |  */ | 
| 227 | QRect Q3DScene::primarySubViewport() const | 
| 228 | { | 
| 229 |     QRect primary = d_ptr->m_primarySubViewport; | 
| 230 |     if (primary.isNull()) { | 
| 231 |         if (d_ptr->m_isSlicingActive) | 
| 232 |             primary = d_ptr->m_defaultSmallViewport; | 
| 233 |         else | 
| 234 |             primary = d_ptr->m_defaultLargeViewport; | 
| 235 |     } | 
| 236 |     return primary; | 
| 237 | } | 
| 238 |  | 
| 239 | void Q3DScene::setPrimarySubViewport(const QRect &primarySubViewport) | 
| 240 | { | 
| 241 |     if (d_ptr->m_primarySubViewport != primarySubViewport) { | 
| 242 |         if (!primarySubViewport.isValid() && !primarySubViewport.isNull()) { | 
| 243 |             qWarning(msg: "Viewport is invalid." ); | 
| 244 |             return; | 
| 245 |         } | 
| 246 |  | 
| 247 |         // If viewport is smaller than primarySubViewport, enlarge it | 
| 248 |         if ((d_ptr->m_viewport.width() < (primarySubViewport.width() | 
| 249 |                                           + primarySubViewport.x())) | 
| 250 |                 || (d_ptr->m_viewport.height() < (primarySubViewport.height() | 
| 251 |                                                   + primarySubViewport.y()))) { | 
| 252 |             d_ptr->m_viewport.setWidth(qMax(a: d_ptr->m_viewport.width(), | 
| 253 |                                             b: primarySubViewport.width() + primarySubViewport.x())); | 
| 254 |             d_ptr->m_viewport.setHeight(qMax(a: d_ptr->m_viewport.height(), | 
| 255 |                                              b: primarySubViewport.height() + primarySubViewport.y())); | 
| 256 |             d_ptr->calculateSubViewports(); | 
| 257 |         } | 
| 258 |  | 
| 259 |         d_ptr->m_primarySubViewport = primarySubViewport; | 
| 260 |         d_ptr->updateGLSubViewports(); | 
| 261 |         d_ptr->m_changeTracker.primarySubViewportChanged = true; | 
| 262 |         d_ptr->m_sceneDirty = true; | 
| 263 |  | 
| 264 |         emit primarySubViewportChanged(subViewport: primarySubViewport); | 
| 265 |         emit d_ptr->needRender(); | 
| 266 |     } | 
| 267 | } | 
| 268 |  | 
| 269 | /*! | 
| 270 |  * Returns whether the given \a point resides inside the primary subview or not. | 
| 271 |  * \return \c true if the point is inside the primary subview. | 
| 272 |  * \note If subviews are superimposed, and the given \a point resides inside both, result is | 
| 273 |  * \c true only when the primary subview is on top. | 
| 274 |  */ | 
| 275 | bool Q3DScene::isPointInPrimarySubView(const QPoint &point) | 
| 276 | { | 
| 277 |     int x = point.x(); | 
| 278 |     int y = point.y(); | 
| 279 |     bool isInSecondary = d_ptr->isInArea(area: secondarySubViewport(), x, y); | 
| 280 |     if (!isInSecondary || (isInSecondary && !d_ptr->m_isSecondarySubviewOnTop)) | 
| 281 |         return d_ptr->isInArea(area: primarySubViewport(), x, y); | 
| 282 |     else | 
| 283 |         return false; | 
| 284 | } | 
| 285 |  | 
| 286 | /*! | 
| 287 |  * Returns whether the given \a point resides inside the secondary subview or not. | 
| 288 |  * \return \c true if the point is inside the secondary subview. | 
| 289 |  * \note If subviews are superimposed, and the given \a point resides inside both, result is | 
| 290 |  * \c true only when the secondary subview is on top. | 
| 291 |  */ | 
| 292 | bool Q3DScene::isPointInSecondarySubView(const QPoint &point) | 
| 293 | { | 
| 294 |     int x = point.x(); | 
| 295 |     int y = point.y(); | 
| 296 |     bool isInPrimary = d_ptr->isInArea(area: primarySubViewport(), x, y); | 
| 297 |     if (!isInPrimary || (isInPrimary && d_ptr->m_isSecondarySubviewOnTop)) | 
| 298 |         return d_ptr->isInArea(area: secondarySubViewport(), x, y); | 
| 299 |     else | 
| 300 |         return false; | 
| 301 | } | 
| 302 |  | 
| 303 | /*! | 
| 304 |  * \property Q3DScene::secondarySubViewport | 
| 305 |  * | 
| 306 |  * \brief The secondary viewport rectangle inside the viewport. | 
| 307 |  * | 
| 308 |  * The secondary viewport is used for drawing the 2D slice view in some | 
| 309 |  * visualizations. If it has not been explicitly set, it will be equal to | 
| 310 |  * QRect. If isSlicingActive() is \c true, it will be equal to \l viewport. | 
| 311 |  * \note If the secondary sub viewport is larger than or outside of the | 
| 312 |  * viewport, the viewport is resized accordingly. | 
| 313 |  */ | 
| 314 | QRect Q3DScene::secondarySubViewport() const | 
| 315 | { | 
| 316 |     QRect secondary = d_ptr->m_secondarySubViewport; | 
| 317 |     if (secondary.isNull() && d_ptr->m_isSlicingActive) | 
| 318 |         secondary = d_ptr->m_defaultLargeViewport; | 
| 319 |     return secondary; | 
| 320 | } | 
| 321 |  | 
| 322 | void Q3DScene::setSecondarySubViewport(const QRect &secondarySubViewport) | 
| 323 | { | 
| 324 |     if (d_ptr->m_secondarySubViewport != secondarySubViewport) { | 
| 325 |         if (!secondarySubViewport.isValid() && !secondarySubViewport.isNull()) { | 
| 326 |             qWarning(msg: "Viewport is invalid." ); | 
| 327 |             return; | 
| 328 |         } | 
| 329 |  | 
| 330 |         // If viewport is smaller than secondarySubViewport, enlarge it | 
| 331 |         if ((d_ptr->m_viewport.width() < (secondarySubViewport.width() | 
| 332 |                                           + secondarySubViewport.x())) | 
| 333 |                 || (d_ptr->m_viewport.height() < (secondarySubViewport.height() | 
| 334 |                                                   + secondarySubViewport.y()))) { | 
| 335 |             d_ptr->m_viewport.setWidth(qMax(a: d_ptr->m_viewport.width(), | 
| 336 |                                             b: secondarySubViewport.width() | 
| 337 |                                             + secondarySubViewport.x())); | 
| 338 |             d_ptr->m_viewport.setHeight(qMax(a: d_ptr->m_viewport.height(), | 
| 339 |                                              b: secondarySubViewport.height() | 
| 340 |                                              + secondarySubViewport.y())); | 
| 341 |             d_ptr->calculateSubViewports(); | 
| 342 |         } | 
| 343 |  | 
| 344 |         d_ptr->m_secondarySubViewport = secondarySubViewport; | 
| 345 |         d_ptr->updateGLSubViewports(); | 
| 346 |         d_ptr->m_changeTracker.secondarySubViewportChanged = true; | 
| 347 |         d_ptr->m_sceneDirty = true; | 
| 348 |  | 
| 349 |         emit secondarySubViewportChanged(subViewport: secondarySubViewport); | 
| 350 |         emit d_ptr->needRender(); | 
| 351 |     } | 
| 352 | } | 
| 353 |  | 
| 354 | /*! | 
| 355 |  * \property Q3DScene::selectionQueryPosition | 
| 356 |  * | 
| 357 |  * \brief The coordinates for the user input that should be processed | 
| 358 |  * by the scene as a selection. | 
| 359 |  * | 
| 360 |  * If this property is set to a value other than invalidSelectionPoint(), the | 
| 361 |  * graph tries to select a data item, axis label, or a custom item at the | 
| 362 |  * specified coordinates within the primary viewport. | 
| 363 |  * After the rendering pass, the property is returned to its default state of | 
| 364 |  * invalidSelectionPoint(). | 
| 365 |  * | 
| 366 |  * \sa QAbstract3DGraph::selectedElement | 
| 367 |  */ | 
| 368 | void Q3DScene::setSelectionQueryPosition(const QPoint &point) | 
| 369 | { | 
| 370 |     if (point != d_ptr->m_selectionQueryPosition) { | 
| 371 |         d_ptr->m_selectionQueryPosition = point; | 
| 372 |         d_ptr->m_changeTracker.selectionQueryPositionChanged = true; | 
| 373 |         d_ptr->m_sceneDirty = true; | 
| 374 |  | 
| 375 |         emit selectionQueryPositionChanged(position: point); | 
| 376 |         emit d_ptr->needRender(); | 
| 377 |     } | 
| 378 | } | 
| 379 |  | 
| 380 | QPoint Q3DScene::selectionQueryPosition() const | 
| 381 | { | 
| 382 |     return d_ptr->m_selectionQueryPosition; | 
| 383 | } | 
| 384 |  | 
| 385 | /*! | 
| 386 |  * \return a QPoint signifying an invalid selection position. | 
| 387 |  */ | 
| 388 | QPoint Q3DScene::invalidSelectionPoint() | 
| 389 | { | 
| 390 |     static const QPoint invalidSelectionPos(-1, -1); | 
| 391 |     return invalidSelectionPos; | 
| 392 | } | 
| 393 |  | 
| 394 | /*! | 
| 395 |  * \property Q3DScene::graphPositionQuery | 
| 396 |  * | 
| 397 |  * \brief The coordinates for the user input that should be processed | 
| 398 |  * by the scene as a graph position query. | 
| 399 |  * | 
| 400 |  * If this property is set to a value other than invalidSelectionPoint(), the | 
| 401 |  * graph tries to match a graph position to the specified coordinates | 
| 402 |  * within the primary viewport. | 
| 403 |  * After the rendering pass, this property is returned to its default state of | 
| 404 |  * invalidSelectionPoint(). The queried graph position can be read from the | 
| 405 |  * QAbstract3DGraph::queriedGraphPosition property after the next render pass. | 
| 406 |  * | 
| 407 |  * There is no single correct 3D coordinate to match a particular screen position, so to be | 
| 408 |  * consistent, the queries are always done against the inner sides of an invisible box surrounding | 
| 409 |  * the graph. | 
| 410 |  * | 
| 411 |  * \note Bar graphs allow graph position queries only at the graph floor level. | 
| 412 |  * | 
| 413 |  * \sa QAbstract3DGraph::queriedGraphPosition | 
| 414 |  */ | 
| 415 | void Q3DScene::setGraphPositionQuery(const QPoint &point) | 
| 416 | { | 
| 417 |     if (point != d_ptr->m_graphPositionQueryPosition) { | 
| 418 |         d_ptr->m_graphPositionQueryPosition = point; | 
| 419 |         d_ptr->m_changeTracker.graphPositionQueryPositionChanged = true; | 
| 420 |         d_ptr->m_sceneDirty = true; | 
| 421 |  | 
| 422 |         emit graphPositionQueryChanged(position: point); | 
| 423 |         emit d_ptr->needRender(); | 
| 424 |     } | 
| 425 | } | 
| 426 |  | 
| 427 | QPoint Q3DScene::graphPositionQuery() const | 
| 428 | { | 
| 429 |     return d_ptr->m_graphPositionQueryPosition; | 
| 430 | } | 
| 431 |  | 
| 432 | /*! | 
| 433 |  * \property Q3DScene::slicingActive | 
| 434 |  * | 
| 435 |  * \brief Whether the 2D slicing view is currently active. | 
| 436 |  * | 
| 437 |  * If \c true, QAbstract3DGraph::selectionMode must have either | 
| 438 |  * QAbstract3DGraph::SelectionRow or QAbstract3DGraph::SelectionColumn set | 
| 439 |  * to a valid selection. | 
| 440 |  * \note Not all visualizations support the 2D slicing view. | 
| 441 |  */ | 
| 442 | bool Q3DScene::isSlicingActive() const | 
| 443 | { | 
| 444 |     return d_ptr->m_isSlicingActive; | 
| 445 | } | 
| 446 |  | 
| 447 | void Q3DScene::setSlicingActive(bool isSlicing) | 
| 448 | { | 
| 449 |     if (d_ptr->m_isSlicingActive != isSlicing) { | 
| 450 |         d_ptr->m_isSlicingActive = isSlicing; | 
| 451 |         d_ptr->m_changeTracker.slicingActivatedChanged = true; | 
| 452 |         d_ptr->m_sceneDirty = true; | 
| 453 |  | 
| 454 |         // Set secondary subview behind primary to achieve default functionality (= clicking on | 
| 455 |         // primary disables slice) | 
| 456 |         setSecondarySubviewOnTop(!isSlicing); | 
| 457 |  | 
| 458 |         d_ptr->calculateSubViewports(); | 
| 459 |         emit slicingActiveChanged(isSlicingActive: isSlicing); | 
| 460 |         emit d_ptr->needRender(); | 
| 461 |     } | 
| 462 | } | 
| 463 |  | 
| 464 | /*! | 
| 465 |  * \property Q3DScene::secondarySubviewOnTop | 
| 466 |  * | 
| 467 |  * \brief Whether the 2D slicing view or the 3D view is drawn on top. | 
| 468 |  */ | 
| 469 | bool Q3DScene::isSecondarySubviewOnTop() const | 
| 470 | { | 
| 471 |     return d_ptr->m_isSecondarySubviewOnTop; | 
| 472 | } | 
| 473 |  | 
| 474 | void Q3DScene::setSecondarySubviewOnTop(bool isSecondaryOnTop) | 
| 475 | { | 
| 476 |     if (d_ptr->m_isSecondarySubviewOnTop != isSecondaryOnTop) { | 
| 477 |         d_ptr->m_isSecondarySubviewOnTop = isSecondaryOnTop; | 
| 478 |         d_ptr->m_changeTracker.subViewportOrderChanged = true; | 
| 479 |         d_ptr->m_sceneDirty = true; | 
| 480 |  | 
| 481 |         emit secondarySubviewOnTopChanged(isSecondaryOnTop); | 
| 482 |         emit d_ptr->needRender(); | 
| 483 |     } | 
| 484 | } | 
| 485 |  | 
| 486 | /*! | 
| 487 |  * \property Q3DScene::activeCamera | 
| 488 |  * | 
| 489 |  * \brief The currently active camera in the 3D scene. | 
| 490 |  * | 
| 491 |  * When a new Q3DCamera object is set, it is automatically added as child of | 
| 492 |  * the scene. | 
| 493 |  */ | 
| 494 | Q3DCamera *Q3DScene::activeCamera() const | 
| 495 | { | 
| 496 |     return d_ptr->m_camera; | 
| 497 | } | 
| 498 |  | 
| 499 | void Q3DScene::setActiveCamera(Q3DCamera *camera) | 
| 500 | { | 
| 501 |     Q_ASSERT(camera); | 
| 502 |  | 
| 503 |     // Add new camera as child of the scene | 
| 504 |     if (camera->parent() != this) | 
| 505 |         camera->setParent(this); | 
| 506 |  | 
| 507 |     if (camera != d_ptr->m_camera) { | 
| 508 |         if (d_ptr->m_camera) { | 
| 509 |             disconnect(sender: d_ptr->m_camera, signal: &Q3DCamera::xRotationChanged, receiver: d_ptr.data(), | 
| 510 |                        slot: &Q3DScenePrivate::needRender); | 
| 511 |             disconnect(sender: d_ptr->m_camera, signal: &Q3DCamera::yRotationChanged, receiver: d_ptr.data(), | 
| 512 |                        slot: &Q3DScenePrivate::needRender); | 
| 513 |             disconnect(sender: d_ptr->m_camera, signal: &Q3DCamera::zoomLevelChanged, receiver: d_ptr.data(), | 
| 514 |                        slot: &Q3DScenePrivate::needRender); | 
| 515 |         } | 
| 516 |  | 
| 517 |         d_ptr->m_camera = camera; | 
| 518 |         d_ptr->m_changeTracker.cameraChanged = true; | 
| 519 |         d_ptr->m_sceneDirty = true; | 
| 520 |  | 
| 521 |  | 
| 522 |         if (camera) { | 
| 523 |             connect(sender: camera, signal: &Q3DCamera::xRotationChanged, receiver: d_ptr.data(), | 
| 524 |                     slot: &Q3DScenePrivate::needRender); | 
| 525 |             connect(sender: camera, signal: &Q3DCamera::yRotationChanged, receiver: d_ptr.data(), | 
| 526 |                     slot: &Q3DScenePrivate::needRender); | 
| 527 |             connect(sender: camera, signal: &Q3DCamera::zoomLevelChanged, receiver: d_ptr.data(), | 
| 528 |                     slot: &Q3DScenePrivate::needRender); | 
| 529 |         } | 
| 530 |  | 
| 531 |         emit activeCameraChanged(camera); | 
| 532 |         emit d_ptr->needRender(); | 
| 533 |     } | 
| 534 | } | 
| 535 |  | 
| 536 | /*! | 
| 537 |  * \property Q3DScene::activeLight | 
| 538 |  * | 
| 539 |  * \brief The currently active light in the 3D scene. | 
| 540 |  * | 
| 541 |  * When a new Q3DLight objects is set, it is automatically added as child of | 
| 542 |  * the scene. | 
| 543 |  */ | 
| 544 | Q3DLight *Q3DScene::activeLight() const | 
| 545 | { | 
| 546 |     return d_ptr->m_light; | 
| 547 | } | 
| 548 |  | 
| 549 | void Q3DScene::setActiveLight(Q3DLight *light) | 
| 550 | { | 
| 551 |     Q_ASSERT(light); | 
| 552 |  | 
| 553 |     // Add new light as child of the scene | 
| 554 |     if (light->parent() != this) | 
| 555 |         light->setParent(this); | 
| 556 |  | 
| 557 |     if (light != d_ptr->m_light) { | 
| 558 |         d_ptr->m_light = light; | 
| 559 |         d_ptr->m_changeTracker.lightChanged = true; | 
| 560 |         d_ptr->m_sceneDirty = true; | 
| 561 |  | 
| 562 |         emit activeLightChanged(light); | 
| 563 |         emit d_ptr->needRender(); | 
| 564 |     } | 
| 565 | } | 
| 566 |  | 
| 567 | /*! | 
| 568 |  * \property Q3DScene::devicePixelRatio | 
| 569 |  * | 
| 570 |  * \brief The device pixel ratio that is used when mapping input | 
| 571 |  * coordinates to pixel coordinates. | 
| 572 |  */ | 
| 573 | float Q3DScene::devicePixelRatio() const | 
| 574 | { | 
| 575 |     return d_ptr->m_devicePixelRatio; | 
| 576 | } | 
| 577 |  | 
| 578 | void Q3DScene::setDevicePixelRatio(float pixelRatio) | 
| 579 | { | 
| 580 |     if (d_ptr->m_devicePixelRatio != pixelRatio) { | 
| 581 |         d_ptr->m_devicePixelRatio = pixelRatio; | 
| 582 |         d_ptr->m_changeTracker.devicePixelRatioChanged = true; | 
| 583 |         d_ptr->m_sceneDirty = true; | 
| 584 |  | 
| 585 |         emit devicePixelRatioChanged(pixelRatio); | 
| 586 |         d_ptr->updateGLViewport(); | 
| 587 |         emit d_ptr->needRender(); | 
| 588 |     } | 
| 589 | } | 
| 590 |  | 
| 591 | Q3DScenePrivate::Q3DScenePrivate(Q3DScene *q) : | 
| 592 |     QObject(0), | 
| 593 |     q_ptr(q), | 
| 594 |     m_isSecondarySubviewOnTop(true), | 
| 595 |     m_devicePixelRatio(1.f), | 
| 596 |     m_camera(), | 
| 597 |     m_light(), | 
| 598 |     m_isUnderSideCameraEnabled(false), | 
| 599 |     m_isSlicingActive(false), | 
| 600 |     m_selectionQueryPosition(Q3DScene::invalidSelectionPoint()), | 
| 601 |     m_graphPositionQueryPosition(Q3DScene::invalidSelectionPoint()), | 
| 602 |     m_windowSize(QSize(0, 0)), | 
| 603 |     m_sceneDirty(true) | 
| 604 | { | 
| 605 | } | 
| 606 |  | 
| 607 | Q3DScenePrivate::~Q3DScenePrivate() | 
| 608 | { | 
| 609 |     delete m_camera; | 
| 610 |     delete m_light; | 
| 611 | } | 
| 612 |  | 
| 613 | // Copies changed values from this scene to the other scene. If the other scene had same changes, | 
| 614 | // those changes are discarded. | 
| 615 | void Q3DScenePrivate::sync(Q3DScenePrivate &other) | 
| 616 | { | 
| 617 |     if (m_changeTracker.windowSizeChanged) { | 
| 618 |         other.setWindowSize(windowSize()); | 
| 619 |         m_changeTracker.windowSizeChanged = false; | 
| 620 |         other.m_changeTracker.windowSizeChanged = false; | 
| 621 |     } | 
| 622 |     if (m_changeTracker.viewportChanged) { | 
| 623 |         other.setViewport(m_viewport); | 
| 624 |         m_changeTracker.viewportChanged = false; | 
| 625 |         other.m_changeTracker.viewportChanged = false; | 
| 626 |     } | 
| 627 |     if (m_changeTracker.subViewportOrderChanged) { | 
| 628 |         other.q_ptr->setSecondarySubviewOnTop(q_ptr->isSecondarySubviewOnTop()); | 
| 629 |         m_changeTracker.subViewportOrderChanged = false; | 
| 630 |         other.m_changeTracker.subViewportOrderChanged = false; | 
| 631 |     } | 
| 632 |     if (m_changeTracker.primarySubViewportChanged) { | 
| 633 |         other.q_ptr->setPrimarySubViewport(q_ptr->primarySubViewport()); | 
| 634 |         m_changeTracker.primarySubViewportChanged = false; | 
| 635 |         other.m_changeTracker.primarySubViewportChanged = false; | 
| 636 |     } | 
| 637 |     if (m_changeTracker.secondarySubViewportChanged) { | 
| 638 |         other.q_ptr->setSecondarySubViewport(q_ptr->secondarySubViewport()); | 
| 639 |         m_changeTracker.secondarySubViewportChanged = false; | 
| 640 |         other.m_changeTracker.secondarySubViewportChanged = false; | 
| 641 |     } | 
| 642 |     if (m_changeTracker.selectionQueryPositionChanged) { | 
| 643 |         other.q_ptr->setSelectionQueryPosition(q_ptr->selectionQueryPosition()); | 
| 644 |         m_changeTracker.selectionQueryPositionChanged = false; | 
| 645 |         other.m_changeTracker.selectionQueryPositionChanged = false; | 
| 646 |     } | 
| 647 |     if (m_changeTracker.graphPositionQueryPositionChanged) { | 
| 648 |         other.q_ptr->setGraphPositionQuery(q_ptr->graphPositionQuery()); | 
| 649 |         m_changeTracker.graphPositionQueryPositionChanged = false; | 
| 650 |         other.m_changeTracker.graphPositionQueryPositionChanged = false; | 
| 651 |     } | 
| 652 |     if (m_changeTracker.cameraChanged) { | 
| 653 |         m_camera->setDirty(true); | 
| 654 |         m_changeTracker.cameraChanged = false; | 
| 655 |         other.m_changeTracker.cameraChanged = false; | 
| 656 |     } | 
| 657 |     m_camera->d_ptr->sync(other&: *other.m_camera); | 
| 658 |  | 
| 659 |     if (m_changeTracker.lightChanged) { | 
| 660 |         m_light->setDirty(true); | 
| 661 |         m_changeTracker.lightChanged = false; | 
| 662 |         other.m_changeTracker.lightChanged = false; | 
| 663 |     } | 
| 664 |     m_light->d_ptr->sync(other&: *other.m_light); | 
| 665 |  | 
| 666 |     if (m_changeTracker.slicingActivatedChanged) { | 
| 667 |         other.q_ptr->setSlicingActive(q_ptr->isSlicingActive()); | 
| 668 |         m_changeTracker.slicingActivatedChanged = false; | 
| 669 |         other.m_changeTracker.slicingActivatedChanged = false; | 
| 670 |     } | 
| 671 |  | 
| 672 |     if (m_changeTracker.devicePixelRatioChanged) { | 
| 673 |         other.q_ptr->setDevicePixelRatio(q_ptr->devicePixelRatio()); | 
| 674 |         m_changeTracker.devicePixelRatioChanged = false; | 
| 675 |         other.m_changeTracker.devicePixelRatioChanged = false; | 
| 676 |     } | 
| 677 |  | 
| 678 |     m_sceneDirty = false; | 
| 679 |     other.m_sceneDirty = false; | 
| 680 | } | 
| 681 |  | 
| 682 | void Q3DScenePrivate::setViewport(const QRect &viewport) | 
| 683 | { | 
| 684 |     if (m_viewport != viewport && viewport.isValid()) { | 
| 685 |         m_viewport = viewport; | 
| 686 |         calculateSubViewports(); | 
| 687 |         emit needRender(); | 
| 688 |     } | 
| 689 | } | 
| 690 |  | 
| 691 | void Q3DScenePrivate::setViewportSize(int width, int height) | 
| 692 | { | 
| 693 |     if (m_viewport.width() != width || m_viewport.height() != height) { | 
| 694 |         m_viewport.setWidth(width); | 
| 695 |         m_viewport.setHeight(height); | 
| 696 |         calculateSubViewports(); | 
| 697 |         emit needRender(); | 
| 698 |     } | 
| 699 | } | 
| 700 |  | 
| 701 | /*! | 
| 702 |  * \internal | 
| 703 |  * Sets the size of the window being rendered to. With widget based graphs, this | 
| 704 |  * is equal to the size of the QWindow and is same as the bounding rectangle. | 
| 705 |  * With declarative graphs this is equal to the size of the QQuickWindow and | 
| 706 |  * can be different from the bounding rectangle. | 
| 707 |  */ | 
| 708 | void Q3DScenePrivate::setWindowSize(const QSize &size) | 
| 709 | { | 
| 710 |     if (m_windowSize != size) { | 
| 711 |         m_windowSize = size; | 
| 712 |         updateGLViewport(); | 
| 713 |         m_changeTracker.windowSizeChanged = true; | 
| 714 |         emit needRender(); | 
| 715 |     } | 
| 716 | } | 
| 717 |  | 
| 718 | QSize Q3DScenePrivate::windowSize() const | 
| 719 | { | 
| 720 |     return m_windowSize; | 
| 721 | } | 
| 722 |  | 
| 723 | void Q3DScenePrivate::calculateSubViewports() | 
| 724 | { | 
| 725 |     // Calculates the default subviewport layout, used when slicing | 
| 726 |     const float smallerViewPortRatio = 0.2f; | 
| 727 |     m_defaultSmallViewport = QRect(0, 0, | 
| 728 |                                    m_viewport.width() * smallerViewPortRatio, | 
| 729 |                                    m_viewport.height() * smallerViewPortRatio); | 
| 730 |     m_defaultLargeViewport = QRect(0, 0, | 
| 731 |                                    m_viewport.width(), | 
| 732 |                                    m_viewport.height()); | 
| 733 |  | 
| 734 |     updateGLViewport(); | 
| 735 | } | 
| 736 |  | 
| 737 | void Q3DScenePrivate::updateGLViewport() | 
| 738 | { | 
| 739 |     // Update GL viewport | 
| 740 |     m_glViewport.setX(m_viewport.x() * m_devicePixelRatio); | 
| 741 |     m_glViewport.setY((m_windowSize.height() - (m_viewport.y() + m_viewport.height())) | 
| 742 |                       * m_devicePixelRatio); | 
| 743 |     m_glViewport.setWidth(m_viewport.width() * m_devicePixelRatio); | 
| 744 |     m_glViewport.setHeight(m_viewport.height() * m_devicePixelRatio); | 
| 745 |  | 
| 746 |     m_changeTracker.viewportChanged = true; | 
| 747 |     m_sceneDirty = true; | 
| 748 |  | 
| 749 |     // Do default subviewport changes first, then allow signal listeners to override. | 
| 750 |     updateGLSubViewports(); | 
| 751 |     emit q_ptr->viewportChanged(viewport: m_viewport); | 
| 752 | } | 
| 753 |  | 
| 754 | void Q3DScenePrivate::updateGLSubViewports() | 
| 755 | { | 
| 756 |     if (m_isSlicingActive) { | 
| 757 |         QRect primary = m_primarySubViewport; | 
| 758 |         QRect secondary = m_secondarySubViewport; | 
| 759 |         if (primary.isNull()) | 
| 760 |             primary = m_defaultSmallViewport; | 
| 761 |         if (secondary.isNull()) | 
| 762 |             secondary = m_defaultLargeViewport; | 
| 763 |  | 
| 764 |         m_glPrimarySubViewport.setX((primary.x() + m_viewport.x()) * m_devicePixelRatio); | 
| 765 |         m_glPrimarySubViewport.setY((m_windowSize.height() | 
| 766 |                                      - (primary.y() + primary.height() + m_viewport.y())) | 
| 767 |                                     * m_devicePixelRatio); | 
| 768 |         m_glPrimarySubViewport.setWidth(primary.width() * m_devicePixelRatio); | 
| 769 |         m_glPrimarySubViewport.setHeight(primary.height() * m_devicePixelRatio); | 
| 770 |  | 
| 771 |         m_glSecondarySubViewport.setX((secondary.x() + m_viewport.x()) * m_devicePixelRatio); | 
| 772 |         m_glSecondarySubViewport.setY((m_windowSize.height() | 
| 773 |                                        - (secondary.y() + secondary.height() + m_viewport.y())) | 
| 774 |                                       * m_devicePixelRatio); | 
| 775 |         m_glSecondarySubViewport.setWidth(secondary.width() * m_devicePixelRatio); | 
| 776 |         m_glSecondarySubViewport.setHeight(secondary.height() * m_devicePixelRatio); | 
| 777 |     } else { | 
| 778 |         m_glPrimarySubViewport.setX(m_viewport.x() * m_devicePixelRatio); | 
| 779 |         m_glPrimarySubViewport.setY((m_windowSize.height() - (m_viewport.y() + m_viewport.height())) | 
| 780 |                                     * m_devicePixelRatio); | 
| 781 |         m_glPrimarySubViewport.setWidth(m_viewport.width() * m_devicePixelRatio); | 
| 782 |         m_glPrimarySubViewport.setHeight(m_viewport.height() * m_devicePixelRatio); | 
| 783 |  | 
| 784 |         m_glSecondarySubViewport = QRect(); | 
| 785 |     } | 
| 786 | } | 
| 787 |  | 
| 788 | QRect Q3DScenePrivate::glViewport() | 
| 789 | { | 
| 790 |     return m_glViewport; | 
| 791 | } | 
| 792 |  | 
| 793 | QRect Q3DScenePrivate::glPrimarySubViewport() | 
| 794 | { | 
| 795 |     return m_glPrimarySubViewport; | 
| 796 | } | 
| 797 |  | 
| 798 | QRect Q3DScenePrivate::glSecondarySubViewport() | 
| 799 | { | 
| 800 |     return m_glSecondarySubViewport; | 
| 801 | } | 
| 802 |  | 
| 803 | /*! | 
| 804 |  * \internal | 
| 805 |  * Calculates and sets the light position relative to the currently active camera using the given | 
| 806 |  * parameters. | 
| 807 |  * The relative 3D offset to the current camera position is defined in \a relativePosition. | 
| 808 |  * Optional \a fixedRotation fixes the light rotation around the data visualization area to the | 
| 809 |  * given value in degrees. | 
| 810 |  * Optional \a distanceModifier modifies the distance of the light from the data visualization. | 
| 811 |  */ | 
| 812 | void Q3DScenePrivate::setLightPositionRelativeToCamera(const QVector3D &relativePosition, | 
| 813 |                                                        float fixedRotation, float distanceModifier) | 
| 814 | { | 
| 815 |     m_light->setPosition(m_camera->d_ptr->calculatePositionRelativeToCamera(relativePosition, | 
| 816 |                                                                             fixedRotation, | 
| 817 |                                                                             distanceModifier)); | 
| 818 | } | 
| 819 |  | 
| 820 | void Q3DScenePrivate::markDirty() | 
| 821 | { | 
| 822 |     m_sceneDirty = true; | 
| 823 |     emit needRender(); | 
| 824 | } | 
| 825 |  | 
| 826 | bool Q3DScenePrivate::isInArea(const QRect &area, int x, int y) const | 
| 827 | { | 
| 828 |     int areaMinX = area.x(); | 
| 829 |     int areaMaxX = area.x() + area.width(); | 
| 830 |     int areaMinY = area.y(); | 
| 831 |     int areaMaxY = area.y() + area.height(); | 
| 832 |     return ( x >= areaMinX && x <= areaMaxX && y >= areaMinY && y <= areaMaxY ); | 
| 833 | } | 
| 834 |  | 
| 835 | QT_END_NAMESPACE_DATAVISUALIZATION | 
| 836 |  |