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
34QT_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 */
188Q3DScene::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 */
199Q3DScene::~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 */
209QRect 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 */
227QRect 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
239void 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 */
275bool 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 */
292bool 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 */
314QRect 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
322void 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 */
368void 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
380QPoint Q3DScene::selectionQueryPosition() const
381{
382 return d_ptr->m_selectionQueryPosition;
383}
384
385/*!
386 * \return a QPoint signifying an invalid selection position.
387 */
388QPoint 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 */
415void 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
427QPoint 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 */
442bool Q3DScene::isSlicingActive() const
443{
444 return d_ptr->m_isSlicingActive;
445}
446
447void 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 */
469bool Q3DScene::isSecondarySubviewOnTop() const
470{
471 return d_ptr->m_isSecondarySubviewOnTop;
472}
473
474void 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 */
494Q3DCamera *Q3DScene::activeCamera() const
495{
496 return d_ptr->m_camera;
497}
498
499void 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 */
544Q3DLight *Q3DScene::activeLight() const
545{
546 return d_ptr->m_light;
547}
548
549void 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 */
573float Q3DScene::devicePixelRatio() const
574{
575 return d_ptr->m_devicePixelRatio;
576}
577
578void 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
591Q3DScenePrivate::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
607Q3DScenePrivate::~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.
615void 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
682void 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
691void 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 */
708void 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
718QSize Q3DScenePrivate::windowSize() const
719{
720 return m_windowSize;
721}
722
723void 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
737void 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
754void 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
788QRect Q3DScenePrivate::glViewport()
789{
790 return m_glViewport;
791}
792
793QRect Q3DScenePrivate::glPrimarySubViewport()
794{
795 return m_glPrimarySubViewport;
796}
797
798QRect 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 */
812void 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
820void Q3DScenePrivate::markDirty()
821{
822 m_sceneDirty = true;
823 emit needRender();
824}
825
826bool 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
835QT_END_NAMESPACE_DATAVISUALIZATION
836

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