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

source code of qtgraphs/src/graphs/engine/q3dscene.cpp