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

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