1 | // Copyright (C) 2023 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qquickgraphsitem_p.h" |
5 | |
6 | #include "q3dscene_p.h" |
7 | #include "qabstract3daxis_p.h" |
8 | #include "qabstract3dseries.h" |
9 | #include "qabstract3dseries_p.h" |
10 | #include "qcategory3daxis.h" |
11 | #include "qcategory3daxis_p.h" |
12 | #include "qcustom3ditem.h" |
13 | #include "qcustom3ditem_p.h" |
14 | #include "qcustom3dlabel.h" |
15 | #include "qcustom3dvolume.h" |
16 | #include "qgraphsinputhandler_p.h" |
17 | #include "qgraphstheme.h" |
18 | #include "qvalue3daxis.h" |
19 | #include "qvalue3daxis_p.h" |
20 | #include "utils_p.h" |
21 | |
22 | #include <QtGui/QGuiApplication> |
23 | |
24 | #include <QtQuick/private/qquickitem_p.h> |
25 | #include <QtQuick3D/private/qquick3dcustommaterial_p.h> |
26 | #include <QtQuick3D/private/qquick3ddirectionallight_p.h> |
27 | #include <QtQuick3D/private/qquick3dloader_p.h> |
28 | #include <QtQuick3D/private/qquick3dorthographiccamera_p.h> |
29 | #include <QtQuick3D/private/qquick3dperspectivecamera_p.h> |
30 | #include <QtQuick3D/private/qquick3dprincipledmaterial_p.h> |
31 | #include <QtQuick3D/private/qquick3drepeater_p.h> |
32 | |
33 | #if defined(Q_OS_IOS) |
34 | #include <QtCore/QTimer> |
35 | #endif |
36 | |
37 | #if defined(Q_OS_MACOS) |
38 | #include <qpa/qplatformnativeinterface.h> |
39 | #endif |
40 | |
41 | QT_BEGIN_NAMESPACE |
42 | |
43 | constexpr float doublePi = static_cast<float>(M_PI) * 2.0f; |
44 | constexpr float polarRoundness = 64.0f; |
45 | |
46 | /*! |
47 | * \qmltype GraphsItem3D |
48 | * \qmlabstract |
49 | * \inqmlmodule QtGraphs |
50 | * \ingroup graphs_qml_3D |
51 | * \brief Base type for 3D graphs. |
52 | * |
53 | * The base type for all 3D graphs in QtGraphs. |
54 | * |
55 | * \sa Bars3D, Scatter3D, Surface3D, {Qt Graphs C++ Classes for 3D} |
56 | */ |
57 | |
58 | /*! |
59 | * \qmlproperty Graphs3D.SelectionMode GraphsItem3D::selectionMode |
60 | * The active selection mode in the graph. |
61 | * One of the \l Graphs3D.SelectionFlag enum values. |
62 | */ |
63 | |
64 | /*! |
65 | * \qmlproperty Graphs3D.ShadowQuality GraphsItem3D::shadowQuality |
66 | * The quality of shadows. One of the \l Graphs3D.ShadowQuality enum |
67 | * values. |
68 | */ |
69 | |
70 | /*! |
71 | * \qmlproperty Graphs3D.CameraPreset GraphsItem3D::cameraPreset |
72 | * |
73 | * The currently active camera preset, which is one of |
74 | * \l{Graphs3D.CameraPreset}. If no |
75 | * preset is active, the value is \c {Graphs3D.CameraPreset.NoPreset}. |
76 | */ |
77 | |
78 | /*! |
79 | * \qmlproperty float GraphsItem3D::cameraXRotation |
80 | * |
81 | * The X-rotation angle of the camera around the target point in degrees |
82 | * starting from the current base position. |
83 | */ |
84 | |
85 | /*! |
86 | * \qmlproperty float GraphsItem3D::cameraYRotation |
87 | * |
88 | * The Y-rotation angle of the camera around the target point in degrees |
89 | * starting from the current base position. |
90 | */ |
91 | |
92 | /*! |
93 | * \qmlproperty bool GraphsItem3D::zoomAtTargetEnabled |
94 | * |
95 | * Whether zooming should change the camera target so that the zoomed point |
96 | * of the graph stays at the same location after the zoom. |
97 | * |
98 | * Defaults to \c{true}. |
99 | */ |
100 | |
101 | /*! |
102 | * \qmlproperty bool GraphsItem3D::zoomEnabled |
103 | * |
104 | * Whether this input handler allows graph zooming. |
105 | * |
106 | * Defaults to \c{true}. |
107 | */ |
108 | |
109 | /*! |
110 | * \qmlproperty bool GraphsItem3D::selectionEnabled |
111 | * |
112 | * Whether this input handler allows selection from the graph. |
113 | * |
114 | * Defaults to \c{true}. |
115 | */ |
116 | |
117 | /*! |
118 | * \qmlproperty bool GraphsItem3D::rotationEnabled |
119 | * |
120 | * Whether this input handler allows graph rotation. |
121 | * |
122 | * Defaults to \c{true}. |
123 | */ |
124 | |
125 | /*! |
126 | * \qmlproperty float GraphsItem3D::cameraZoomLevel |
127 | * |
128 | * The camera zoom level in percentage. The default value of \c{100.0} |
129 | * means there is no zoom in or out set in the camera. |
130 | * The value is limited by the minCameraZoomLevel and maxCameraZoomLevel |
131 | * properties. |
132 | * |
133 | * \sa minCameraZoomLevel, maxCameraZoomLevel |
134 | */ |
135 | |
136 | /*! |
137 | * \qmlproperty float GraphsItem3D::minCameraZoomLevel |
138 | * |
139 | * Sets the minimum allowed camera zoom level. |
140 | * If the new minimum level is higher than the existing maximum level, the |
141 | * maximum level is adjusted to the new minimum as well. |
142 | * If the current cameraZoomLevel is outside the new bounds, it is adjusted as |
143 | * well. The minCameraZoomLevel cannot be set below \c{1.0}. |
144 | * Defaults to \c{10.0}. |
145 | * |
146 | * \sa cameraZoomLevel, maxCameraZoomLevel |
147 | */ |
148 | |
149 | /*! |
150 | * \qmlproperty float GraphsItem3D::maxCameraZoomLevel |
151 | * |
152 | * Sets the maximum allowed camera zoom level. |
153 | * If the new maximum level is lower than the existing minimum level, the |
154 | * minimum level is adjusted to the new maximum as well. |
155 | * If the current cameraZoomLevel is outside the new bounds, it is adjusted as |
156 | * well. Defaults to \c{500.0f}. |
157 | * |
158 | * \sa cameraZoomLevel, minCameraZoomLevel |
159 | */ |
160 | |
161 | /*! |
162 | * \qmlproperty bool GraphsItem3D::wrapCameraXRotation |
163 | * |
164 | * The behavior of the minimum and maximum limits in the X-rotation. |
165 | * By default, the X-rotation wraps from minimum value to maximum and from |
166 | * maximum to minimum. |
167 | * |
168 | * If set to \c true, the X-rotation of the camera is wrapped from minimum to |
169 | * maximum and from maximum to minimum. If set to \c false, the X-rotation of |
170 | * the camera is limited to the sector determined by the minimum and maximum |
171 | * values. |
172 | */ |
173 | |
174 | /*! |
175 | * \qmlproperty bool GraphsItem3D::wrapCameraYRotation |
176 | * |
177 | * The behavior of the minimum and maximum limits in the Y-rotation. |
178 | * By default, the Y-rotation is limited between the minimum and maximum values |
179 | * without any wrapping. |
180 | * |
181 | * If \c true, the Y-rotation of the camera is wrapped from minimum to maximum |
182 | * and from maximum to minimum. If \c false, the Y-rotation of the camera is |
183 | * limited to the sector determined by the minimum and maximum values. |
184 | */ |
185 | |
186 | /*! |
187 | * \qmlproperty vector3d GraphsItem3D::cameraTargetPosition |
188 | * |
189 | * The camera target as a vector3d. Defaults to \c {vector3d(0.0, 0.0, 0.0)}. |
190 | * |
191 | * Valid coordinate values are between \c{-1.0...1.0}, where the edge values |
192 | * indicate the edges of the corresponding axis range. Any values outside this |
193 | * range are clamped to the edge. |
194 | * |
195 | * \note For bar graphs, the Y-coordinate is ignored and camera always targets |
196 | * a point on the horizontal background. |
197 | */ |
198 | |
199 | /*! |
200 | * \qmlproperty Scene3D GraphsItem3D::scene |
201 | * \readonly |
202 | * |
203 | * The Scene3D pointer that can be used to manipulate the scene and access the |
204 | * scene elements. |
205 | * |
206 | * This property is read-only. |
207 | */ |
208 | |
209 | /*! |
210 | * \qmlproperty GraphsTheme GraphsItem3D::theme |
211 | * The active theme of the graph. |
212 | * |
213 | * \sa GraphsTheme |
214 | */ |
215 | |
216 | /*! |
217 | * \qmlproperty Graphs3D.RenderingMode GraphsItem3D::renderingMode |
218 | * |
219 | * How the graph will be rendered. Defaults to \c{Indirect}. |
220 | * |
221 | * \note Setting the \c antialiasing property of the graph does not do anything. |
222 | * However, it is set by the graph itself if the current rendering mode uses |
223 | * antialiasing. |
224 | * |
225 | * \sa msaaSamples, Graphs3D.RenderingMode |
226 | */ |
227 | |
228 | /*! |
229 | * \qmlproperty int GraphsItem3D::msaaSamples |
230 | * The number of samples used in multisample antialiasing when renderingMode |
231 | * is \c Indirect. When renderingMode is \c DirectToBackground, this property |
232 | * value is read-only and returns the number of samples specified by the window |
233 | * surface format. |
234 | * Defaults to \c{4}. |
235 | * |
236 | * \sa renderingMode |
237 | */ |
238 | |
239 | /*! |
240 | * \qmlproperty bool GraphsItem3D::measureFps |
241 | * |
242 | * If \c {true}, the rendering is done continuously instead of on demand, and |
243 | * the value of the currentFps property is updated. Defaults to \c{false}. |
244 | * |
245 | * \sa currentFps |
246 | */ |
247 | |
248 | /*! |
249 | * \qmlproperty int GraphsItem3D::currentFps |
250 | * |
251 | * When FPS measuring is enabled, the results for the last second are stored in |
252 | * this read-only property. It takes at least a second before this value updates |
253 | * after measuring is activated. |
254 | * |
255 | * \sa measureFps |
256 | */ |
257 | |
258 | /*! |
259 | * \qmlproperty list<Custom3DItem> GraphsItem3D::customItemList |
260 | * |
261 | * The list of \l{Custom3DItem} items added to the graph. The graph takes |
262 | * ownership of the added items. |
263 | */ |
264 | |
265 | /*! |
266 | * \qmlproperty bool GraphsItem3D::polar |
267 | * |
268 | * If \c {true}, the horizontal axes are changed into polar axes. The x-axis |
269 | * becomes the angular axis and the z-axis becomes the radial axis. |
270 | * Polar mode is not available for bar graphs. |
271 | * |
272 | * Defaults to \c{false}. |
273 | * |
274 | * \sa orthoProjection, radialLabelOffset |
275 | */ |
276 | |
277 | /*! |
278 | * \qmlproperty real GraphsItem3D::labelMargin |
279 | * |
280 | * \brief This property specifies the margin for the placement of the axis labels. |
281 | * |
282 | * Negative values place the labels inside the plot-area while positive values |
283 | * place them outside the plot-area. Label automatic rotation is disabled when |
284 | * the value is negative. Defaults to \c 0.1 |
285 | * |
286 | * \sa QAbstract3DAxis::labelAutoAngle |
287 | * |
288 | */ |
289 | |
290 | /*! |
291 | * \qmlproperty real GraphsItem3D::radialLabelOffset |
292 | * |
293 | * This property specifies the normalized horizontal offset for the axis labels |
294 | * of the radial polar axis. The value \c 0.0 indicates that the labels should |
295 | * be drawn next to the 0-angle angular axis grid line. The value \c 1.0 |
296 | * indicates that the labels are drawn in their usual place at the edge of the |
297 | * graph background. This property is ignored if the polar property value is |
298 | * \c{false}. Defaults to \c 1.0. |
299 | * |
300 | * \sa polar |
301 | */ |
302 | |
303 | /*! |
304 | * \qmlmethod void GraphsItem3D::clearSelection() |
305 | * Clears selection from all attached series. |
306 | */ |
307 | |
308 | /*! |
309 | * \qmlmethod bool GraphsItem3D::hasSeries(Abstract3DSeries series) |
310 | * Returns whether the \a series has already been added to the graph. |
311 | */ |
312 | |
313 | /*! |
314 | * \qmlmethod qsizetype GraphsItem3D::addCustomItem(Custom3DItem item) |
315 | * |
316 | * Adds a Custom3DItem \a item to the graph. Graph takes ownership of the added |
317 | * item. |
318 | * |
319 | * \return index to the added item if add was successful, -1 if trying to add a |
320 | * null item, and index of the item if trying to add an already added item. |
321 | * |
322 | * \sa removeCustomItems(), removeCustomItem(), removeCustomItemAt() |
323 | */ |
324 | |
325 | /*! |
326 | * \qmlmethod void GraphsItem3D::removeCustomItems() |
327 | * |
328 | * Removes all custom items. Deletes the resources allocated to them. |
329 | */ |
330 | |
331 | /*! |
332 | * \qmlmethod void GraphsItem3D::removeCustomItem(Custom3DItem item) |
333 | * |
334 | * Removes the custom \a {item}. Deletes the resources allocated to it. |
335 | */ |
336 | |
337 | /*! |
338 | * \qmlmethod void GraphsItem3D::removeCustomItemAt(vector3d position) |
339 | * |
340 | * Removes all custom items at \a {position}. Deletes the resources allocated to them. |
341 | */ |
342 | |
343 | /*! |
344 | * \qmlmethod void GraphsItem3D::releaseCustomItem(Custom3DItem item) |
345 | * |
346 | * Gets ownership of \a item back and removes the \a item from the graph. |
347 | * |
348 | * \note If the same item is added back to the graph, the texture file needs to |
349 | * be re-set. |
350 | * |
351 | * \sa Custom3DItem::textureFile |
352 | */ |
353 | |
354 | /*! |
355 | * \qmlmethod int GraphsItem3D::selectedLabelIndex() |
356 | * |
357 | * Can be used to query the index of the selected label after receiving |
358 | * \c selectedElementChanged signal with any label type. Selection is valid |
359 | * until the next \c selectedElementChanged signal. |
360 | * |
361 | * \return index of the selected label, or -1. |
362 | * |
363 | * \sa selectedElement |
364 | */ |
365 | |
366 | /*! |
367 | * \qmlmethod Abstract3DAxis GraphsItem3D::selectedAxis() |
368 | * |
369 | * Can be used to get the selected axis after receiving \c selectedElementChanged |
370 | * signal with any label type. Selection is valid until the next |
371 | * \c selectedElementChanged signal. |
372 | * |
373 | * \return the selected axis, or null. |
374 | * |
375 | * \sa selectedElement |
376 | */ |
377 | |
378 | /*! |
379 | * \qmlmethod qsizetype GraphsItem3D::selectedCustomItemIndex() |
380 | * |
381 | * Can be used to query the index of the selected custom item after receiving |
382 | * \c selectedElementChanged signal with |
383 | * \l{QtGraphs3D::ElementType::CustomItem}{ElementType.CustomItem} type. |
384 | * Selection is valid until the next \c selectedElementChanged signal. |
385 | * |
386 | * \return index of the selected custom item, or -1. |
387 | * |
388 | * \sa selectedElement |
389 | */ |
390 | |
391 | /*! |
392 | * \qmlmethod Custom3DItem GraphsItem3D::selectedCustomItem() |
393 | * |
394 | * Can be used to get the selected custom item after receiving |
395 | * \c selectedElementChanged signal with |
396 | * \l{QtGraphs3D::ElementType::CustomItem}{ElementType.CustomItem} type. |
397 | * Ownership of the item remains with the graph. |
398 | * Selection is valid until the next \c selectedElementChanged signal. |
399 | * |
400 | * \return the selected custom item, or null. |
401 | * |
402 | * \sa selectedElement |
403 | */ |
404 | |
405 | /*! |
406 | * \qmlproperty Graphs3D.ElementType GraphsItem3D::selectedElement |
407 | * \readonly |
408 | * |
409 | * The element selected in the graph. |
410 | * |
411 | * This property can be used to query the selected element type. |
412 | * The type is valid until a new selection is made in the graph and the |
413 | * \c selectedElementChanged signal is emitted. |
414 | * |
415 | * The signal can be used for example for implementing customized input |
416 | * handling, as demonstrated by the \l {Axis Handling} example. |
417 | * |
418 | * \sa selectedLabelIndex(), selectedAxis(), selectedCustomItemIndex(), |
419 | * selectedCustomItem(), Bars3D::selectedSeries, Scatter3D::selectedSeries, |
420 | * Scene3D::selectionQueryPosition, Graphs3D.ElementType |
421 | */ |
422 | |
423 | /*! |
424 | * \qmlproperty bool GraphsItem3D::orthoProjection |
425 | * |
426 | * If \c {true}, orthographic projection will be used for displaying the graph. |
427 | * Defaults to \c{false}. |
428 | * \note Shadows will be disabled when set to \c{true}. |
429 | */ |
430 | |
431 | /*! |
432 | * \qmlproperty real GraphsItem3D::aspectRatio |
433 | * |
434 | * The ratio of the graph scaling between the longest axis on the horizontal |
435 | * plane and the y-axis. Defaults to \c{2.0}. |
436 | * |
437 | * \note Has no effect on Bars3D. |
438 | * |
439 | * \sa horizontalAspectRatio |
440 | */ |
441 | |
442 | /*! |
443 | * \qmlproperty real GraphsItem3D::horizontalAspectRatio |
444 | * |
445 | * The ratio of the graph scaling between the x-axis and z-axis. |
446 | * The value of \c 0.0 indicates automatic scaling according to axis ranges. |
447 | * Defaults to \c{0.0}. |
448 | * |
449 | * \note Has no effect on Bars3D, which handles scaling on the horizontal plane |
450 | * via the \l{Bars3D::barThickness}{barThickness} and |
451 | * \l{Bars3D::barSpacing}{barSpacing} properties. Polar graphs also ignore this |
452 | * property. |
453 | * |
454 | * \sa aspectRatio, polar, Bars3D::barThickness, Bars3D::barSpacing |
455 | */ |
456 | |
457 | /*! |
458 | * \qmlproperty Graphs3D.OptimizationHint GraphsItem3D::optimizationHint |
459 | * |
460 | * \brief Specifies whether the default or legacy mode is used for rendering optimization. |
461 | * |
462 | * The default mode uses instanced rendering, and provides the full feature set |
463 | * at the best level of performance on most systems. The static mode optimizes |
464 | * graph rendering and is ideal for large non-changing data sets. It is slower |
465 | * with dynamic data changes and item rotations. Selection is not optimized, so |
466 | * using the static mode with massive data sets is not advisable. Legacy mode |
467 | * renders all items in th graph individually, without instancing. It should be |
468 | * used only if default mode does not work, that is the same as if the target |
469 | * system does not support instancing. Defaults to |
470 | * \l{QtGraphs3D::OptimizationHint::Default}{Default}. |
471 | * |
472 | * \note On some environments, large graphs using static optimization may not |
473 | * render, because all of the items are rendered using a single draw call, and |
474 | * different graphics drivers support different maximum vertice counts per call. |
475 | * This is mostly an issue on 32bit and OpenGL ES2 platforms. To work around |
476 | * this issue, choose an item mesh with a low vertex count or use the point mesh. |
477 | * |
478 | * \sa Abstract3DSeries::mesh, Graphs3D.OptimizationHint |
479 | */ |
480 | |
481 | /*! |
482 | * \qmlproperty locale GraphsItem3D::locale |
483 | * |
484 | * Sets the locale used for formatting various numeric labels. |
485 | * Defaults to the \c{"C"} locale. |
486 | * |
487 | * \sa Value3DAxis::labelFormat |
488 | */ |
489 | |
490 | /*! |
491 | * \qmlproperty vector3d GraphsItem3D::queriedGraphPosition |
492 | * \readonly |
493 | * |
494 | * This read-only property contains the latest graph position values along each |
495 | * axis queried using Scene3D::graphPositionQuery. The values are normalized to |
496 | * range \c{[-1, 1]}. If the queried position was outside the graph bounds, the |
497 | * values will not reflect the real position, but will instead be some undefined |
498 | * position outside the range \c{[-1, 1]}. The value will be undefined until a |
499 | * query is made. |
500 | * |
501 | * There is no single correct 3D coordinate to match a particular screen |
502 | * position, so to be consistent, the queries are always done against the inner |
503 | * sides of an invisible box surrounding the graph. |
504 | * |
505 | * \note Bar graphs only allow querying graph position at the graph floor level, |
506 | * so the y-value is always zero for bar graphs and valid queries can be only |
507 | * made at screen positions that contain the floor of the graph. |
508 | * |
509 | * \sa Scene3D::graphPositionQuery |
510 | */ |
511 | |
512 | /*! |
513 | * \qmlproperty real GraphsItem3D::margin |
514 | * |
515 | * The absolute value used for the space left between the edge of the |
516 | * plottable graph area and the edge of the graph background. |
517 | * |
518 | * If the margin value is negative, the margins are determined automatically and |
519 | * can vary according to the size of the items in the series and the type of the |
520 | * graph. The value is interpreted as a fraction of the y-axis range if the |
521 | * graph aspect ratios have not been changed from the default values. |
522 | * Defaults to \c{-1.0}. |
523 | * |
524 | * \note Setting a smaller margin for a scatter graph than the automatically |
525 | * determined margin can cause the scatter items at the edges of the graph to |
526 | * overlap with the graph background. |
527 | * |
528 | * \note On scatter and surface graphs, if the margin is small in comparison to |
529 | * the axis label size, the positions of the edge labels of the axes are |
530 | * adjusted to avoid overlap with the edge labels of the neighboring axes. |
531 | */ |
532 | |
533 | /*! |
534 | * \qmlproperty Graphs3D.GridLineType GraphsItem3D::gridLineType |
535 | * |
536 | * Defines whether the grid lines type is \c Graphs3D.GridLineType.Shader or |
537 | * \c Graphs3D.GridLineType.Geometry. |
538 | * |
539 | * This value affects all grid lines. |
540 | * |
541 | * \sa Graphs3D.GridLineType |
542 | */ |
543 | |
544 | /*! |
545 | * \qmlproperty real GraphsItem3D::shadowStrength |
546 | * |
547 | * The shadow strength for the whole graph. The higher the number, the darker |
548 | * the shadows will be. The value must be between \c 0.0 and \c 100.0. |
549 | * |
550 | * This value affects the light specified in Scene3D. |
551 | */ |
552 | |
553 | /*! |
554 | * \qmlproperty real GraphsItem3D::lightStrength |
555 | * |
556 | * The specular light strength for the whole graph. The value must be between |
557 | * \c 0.0 and \c 10.0. |
558 | * |
559 | * This value affects the light specified in Scene3D. |
560 | */ |
561 | |
562 | /*! |
563 | * \qmlproperty real GraphsItem3D::ambientLightStrength |
564 | * |
565 | * The ambient light strength for the whole graph. This value determines how |
566 | * evenly and brightly the colors are shown throughout the graph regardless of |
567 | * the light position. The value must be between \c 0.0 and \c 1.0. |
568 | */ |
569 | |
570 | /*! |
571 | * \qmlproperty color GraphsItem3D::lightColor |
572 | * |
573 | * The color of the ambient and specular light defined in Scene3D. |
574 | */ |
575 | |
576 | /*! |
577 | * \qmlsignal GraphsItem3D::tapped(QEventPoint eventPoint, Qt::MouseButton button) |
578 | * |
579 | * This signal is emitted when the graph item is tapped once. The \a eventPoint |
580 | * signal parameter contains information from the release event about the point |
581 | * that was tapped, and \a button is the \l {Qt::MouseButton}{mouse button} that was clicked, |
582 | * or \c NoButton on a touchscreen. |
583 | * |
584 | * \sa QEventPoint, Qt::MouseButtons, TapHandler::singleTapped |
585 | */ |
586 | |
587 | /*! |
588 | * \qmlsignal GraphsItem3D::doubleTapped(QEventPoint eventPoint, Qt::MouseButton button) |
589 | * |
590 | * This signal is emitted when the graph item is tapped twice within a short span of time. |
591 | * The \a eventPoint signal parameter contains information from the release event about the |
592 | * point that was tapped, and \a button is the \l {Qt::MouseButton}{mouse button} that was |
593 | * clicked, or \c NoButton on a touchscreen. |
594 | * |
595 | * \sa QEventPoint, Qt::MouseButtons, TapHandler::doubleTapped |
596 | */ |
597 | |
598 | /*! |
599 | * \qmlsignal GraphsItem3D::longPressed() |
600 | * |
601 | * This signal is emitted when the \c parent Item is pressed and held for a |
602 | * time period greater than \l TapHandler::longPressThreshold. |
603 | * |
604 | * \sa TapHandler::longPressed |
605 | */ |
606 | |
607 | /*! |
608 | * \qmlsignal GraphsItem3D::dragged(QVector2D delta) |
609 | * |
610 | * This signal is emitted when the translation of the cluster of points |
611 | * on the graph is changed while the pinch gesture is being performed. |
612 | * The \a delta vector gives the change in translation. |
613 | * |
614 | * \sa PinchHandler::translationChanged |
615 | */ |
616 | |
617 | /*! |
618 | * \qmlsignal GraphsItem3D::wheel(QQuickWheelEvent *event) |
619 | * |
620 | * This signal is emitted every time the graph receives an \a event |
621 | * of type \l QWheelEvent: that is, every time the wheel is moved or the |
622 | * scrolling gesture is updated. |
623 | * |
624 | * \sa WheelEvent, WheelHandler::wheel |
625 | */ |
626 | |
627 | /*! |
628 | * \qmlsignal GraphsItem3D::pinch(qreal delta) |
629 | * |
630 | * This signal is emitted when the scale factor on the graph |
631 | * changes while the pinch gesture is being performed. |
632 | * The \a delta value gives the multiplicative change in scale. |
633 | * |
634 | * \sa PinchHandler::scaleChanged |
635 | */ |
636 | |
637 | /*! |
638 | * \qmlsignal GraphsItem3D::mouseMove(QPoint mousePos) |
639 | * |
640 | * This signal is emitted when the graph receives a mouseMove event. |
641 | * \a mousePos value gives the position of mouse while mouse is moving. |
642 | * |
643 | * \sa QQuickItem::mouseMoveEvent |
644 | */ |
645 | |
646 | QQuickGraphsItem::QQuickGraphsItem(QQuickItem *parent) |
647 | : QQuick3DViewport(parent) |
648 | , m_locale(QLocale::c()) |
649 | { |
650 | if (!m_scene) |
651 | m_scene = new Q3DScene; |
652 | m_scene->setParent(this); |
653 | |
654 | m_qml = this; |
655 | |
656 | // Set initial theme |
657 | QGraphsTheme *theme = new QGraphsTheme(m_scene); |
658 | setTheme(theme); |
659 | QGraphsLine grid = theme->grid(); |
660 | grid.setMainWidth(0.25); |
661 | theme->setGrid(grid); |
662 | m_themes.append(t: theme); |
663 | |
664 | m_scene->d_func()->setViewport(boundingRect().toRect()); |
665 | |
666 | connect(sender: m_scene, signal: &Q3DScene::needRender, context: this, slot: &QQuickGraphsItem::emitNeedRender); |
667 | connect(sender: m_scene, |
668 | signal: &Q3DScene::graphPositionQueryChanged, |
669 | context: this, |
670 | slot: &QQuickGraphsItem::handleQueryPositionChanged); |
671 | connect(sender: m_scene, signal: &Q3DScene::primarySubViewportChanged, |
672 | context: this, |
673 | slot: &QQuickGraphsItem::handlePrimarySubViewportChanged); |
674 | connect(sender: m_scene, signal: &Q3DScene::secondarySubViewportChanged, |
675 | context: this, |
676 | slot: &QQuickGraphsItem::handleSecondarySubViewportChanged); |
677 | |
678 | m_nodeMutex = QSharedPointer<QMutex>::create(); |
679 | |
680 | QQuick3DSceneEnvironment *scene = environment(); |
681 | scene->setBackgroundMode(QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes::Color); |
682 | scene->setClearColor(Qt::transparent); |
683 | |
684 | auto sceneManager = QQuick3DObjectPrivate::get(item: rootNode())->sceneManager; |
685 | connect(sender: sceneManager.data(), |
686 | signal: &QQuick3DSceneManager::windowChanged, |
687 | context: this, |
688 | slot: &QQuickGraphsItem::handleWindowChanged); |
689 | // Set contents to false in case we are in qml designer to make component look |
690 | // nice |
691 | m_runningInDesigner = QGuiApplication::applicationDisplayName() == QLatin1String("Qml2Puppet"); |
692 | setFlag(flag: ItemHasContents /*, !m_runningInDesigner*/); // Is this relevant anymore? |
693 | |
694 | // Set 4x MSAA by default |
695 | setRenderingMode(QtGraphs3D::RenderingMode::Indirect); |
696 | setMsaaSamples(4); |
697 | |
698 | // Accept touchevents |
699 | setAcceptTouchEvents(true); |
700 | |
701 | m_inputHandler = new QGraphsInputHandler(this); |
702 | m_inputHandler->bindableHeight().setBinding(f: [&] { return height(); }); |
703 | m_inputHandler->bindableWidth().setBinding(f: [&] { return width(); }); |
704 | } |
705 | |
706 | QQuickGraphsItem::~QQuickGraphsItem() |
707 | { |
708 | disconnect(sender: this, signal: 0, receiver: this, member: 0); |
709 | checkWindowList(window: 0); |
710 | |
711 | m_repeaterX->model().clear(); |
712 | m_repeaterY->model().clear(); |
713 | m_repeaterZ->model().clear(); |
714 | m_repeaterX->deleteLater(); |
715 | m_repeaterY->deleteLater(); |
716 | m_repeaterZ->deleteLater(); |
717 | |
718 | delete m_gridGeometryModel; |
719 | delete m_subgridGeometryModel; |
720 | delete m_sliceGridGeometryModel; |
721 | |
722 | // Make sure not deleting locked mutex |
723 | QMutexLocker locker(&m_mutex); |
724 | locker.unlock(); |
725 | |
726 | m_nodeMutex.clear(); |
727 | } |
728 | |
729 | void QQuickGraphsItem::handleAxisTitleChanged(const QString &title) |
730 | { |
731 | Q_UNUSED(title); |
732 | handleAxisTitleChangedBySender(sender: sender()); |
733 | } |
734 | |
735 | void QQuickGraphsItem::handleAxisTitleChangedBySender(QObject *sender) |
736 | { |
737 | if (sender == m_axisX) |
738 | m_changeTracker.axisXTitleChanged = true; |
739 | else if (sender == m_axisY) |
740 | m_changeTracker.axisYTitleChanged = true; |
741 | else if (sender == m_axisZ) |
742 | m_changeTracker.axisZTitleChanged = true; |
743 | else |
744 | qWarning(msg: "%ls invoked for invalid axis", qUtf16Printable(QString::fromUtf8(__func__))); |
745 | |
746 | markSeriesItemLabelsDirty(); |
747 | emitNeedRender(); |
748 | } |
749 | |
750 | void QQuickGraphsItem::handleAxisLabelsChanged() |
751 | { |
752 | handleAxisLabelsChangedBySender(sender: sender()); |
753 | } |
754 | |
755 | void QQuickGraphsItem::handleAxisLabelsChangedBySender(QObject *sender) |
756 | { |
757 | if (sender == m_axisX) |
758 | m_changeTracker.axisXLabelsChanged = true; |
759 | else if (sender == m_axisY) |
760 | m_changeTracker.axisYLabelsChanged = true; |
761 | else if (sender == m_axisZ) |
762 | m_changeTracker.axisZLabelsChanged = true; |
763 | else |
764 | qWarning(msg: "%ls invoked for invalid axis", qUtf16Printable(QString::fromUtf8(__func__))); |
765 | |
766 | markSeriesItemLabelsDirty(); |
767 | emitNeedRender(); |
768 | } |
769 | |
770 | void QQuickGraphsItem::handleAxisRangeChanged(float min, float max) |
771 | { |
772 | Q_UNUSED(min); |
773 | Q_UNUSED(max); |
774 | handleAxisRangeChangedBySender(sender: sender()); |
775 | } |
776 | |
777 | void QQuickGraphsItem::handleAxisRangeChangedBySender(QObject *sender) |
778 | { |
779 | if (sender == m_axisX) { |
780 | m_isSeriesVisualsDirty = true; |
781 | m_changeTracker.axisXRangeChanged = true; |
782 | } else if (sender == m_axisY) { |
783 | m_isSeriesVisualsDirty = true; |
784 | m_changeTracker.axisYRangeChanged = true; |
785 | } else if (sender == m_axisZ) { |
786 | m_isSeriesVisualsDirty = true; |
787 | m_changeTracker.axisZRangeChanged = true; |
788 | } else { |
789 | qWarning(msg: "%ls invoked for invalid axis", qUtf16Printable(QString::fromUtf8(__func__))); |
790 | } |
791 | emitNeedRender(); |
792 | } |
793 | |
794 | void QQuickGraphsItem::handleAxisSegmentCountChanged(qsizetype count) |
795 | { |
796 | Q_UNUSED(count); |
797 | handleAxisSegmentCountChangedBySender(sender: sender()); |
798 | } |
799 | |
800 | void QQuickGraphsItem::handleAxisSegmentCountChangedBySender(QObject *sender) |
801 | { |
802 | if (sender == m_axisX) |
803 | m_changeTracker.axisXSegmentCountChanged = true; |
804 | else if (sender == m_axisY) |
805 | m_changeTracker.axisYSegmentCountChanged = true; |
806 | else if (sender == m_axisZ) |
807 | m_changeTracker.axisZSegmentCountChanged = true; |
808 | else |
809 | qWarning(msg: "%ls invoked for invalid axis", qUtf16Printable(QString::fromUtf8(__func__))); |
810 | emitNeedRender(); |
811 | } |
812 | |
813 | void QQuickGraphsItem::handleAxisSubSegmentCountChanged(qsizetype count) |
814 | { |
815 | Q_UNUSED(count); |
816 | handleAxisSubSegmentCountChangedBySender(sender: sender()); |
817 | } |
818 | |
819 | void QQuickGraphsItem::handleAxisSubSegmentCountChangedBySender(QObject *sender) |
820 | { |
821 | if (sender == m_axisX) |
822 | m_changeTracker.axisXSubSegmentCountChanged = true; |
823 | else if (sender == m_axisY) |
824 | m_changeTracker.axisYSubSegmentCountChanged = true; |
825 | else if (sender == m_axisZ) |
826 | m_changeTracker.axisZSubSegmentCountChanged = true; |
827 | else |
828 | qWarning(msg: "%ls invoked for invalid axis", qUtf16Printable(QString::fromUtf8(__func__))); |
829 | emitNeedRender(); |
830 | } |
831 | |
832 | void QQuickGraphsItem::handleAxisAutoAdjustRangeChanged(bool autoAdjust) |
833 | { |
834 | QObject *sender = QObject::sender(); |
835 | if (sender != m_axisX && sender != m_axisY && sender != m_axisZ) |
836 | return; |
837 | |
838 | QAbstract3DAxis *axis = static_cast<QAbstract3DAxis *>(sender); |
839 | handleAxisAutoAdjustRangeChangedInOrientation(orientation: axis->orientation(), autoAdjust); |
840 | } |
841 | |
842 | void QQuickGraphsItem::handleAxisLabelFormatChanged(const QString &format) |
843 | { |
844 | Q_UNUSED(format); |
845 | handleAxisLabelFormatChangedBySender(sender: sender()); |
846 | } |
847 | |
848 | void QQuickGraphsItem::handleAxisReversedChanged(bool enable) |
849 | { |
850 | Q_UNUSED(enable); |
851 | handleAxisReversedChangedBySender(sender: sender()); |
852 | } |
853 | |
854 | void QQuickGraphsItem::handleAxisFormatterDirty() |
855 | { |
856 | handleAxisFormatterDirtyBySender(sender: sender()); |
857 | } |
858 | |
859 | void QQuickGraphsItem::handleAxisLabelAutoRotationChanged(float angle) |
860 | { |
861 | Q_UNUSED(angle); |
862 | handleAxisLabelAutoRotationChangedBySender(sender: sender()); |
863 | } |
864 | |
865 | void QQuickGraphsItem::handleAxisTitleVisibilityChanged(bool visible) |
866 | { |
867 | Q_UNUSED(visible); |
868 | handleAxisTitleVisibilityChangedBySender(sender: sender()); |
869 | } |
870 | |
871 | void QQuickGraphsItem::handleAxisLabelVisibilityChanged(bool visible) |
872 | { |
873 | Q_UNUSED(visible); |
874 | handleAxisLabelVisibilityChangedBySender(sender: sender()); |
875 | } |
876 | |
877 | void QQuickGraphsItem::handleAxisTitleFixedChanged(bool fixed) |
878 | { |
879 | Q_UNUSED(fixed); |
880 | handleAxisTitleFixedChangedBySender(sender: sender()); |
881 | } |
882 | |
883 | void QQuickGraphsItem::handleAxisTitleOffsetChanged(float offset) |
884 | { |
885 | Q_UNUSED(offset); |
886 | handleAxisTitleFixedChangedBySender(sender: sender()); |
887 | } |
888 | |
889 | void QQuickGraphsItem::handleInputPositionChanged(QPoint position) |
890 | { |
891 | Q_UNUSED(position); |
892 | emitNeedRender(); |
893 | } |
894 | |
895 | void QQuickGraphsItem::handleSeriesVisibilityChanged(bool visible) |
896 | { |
897 | Q_UNUSED(visible); |
898 | |
899 | handleSeriesVisibilityChangedBySender(sender: sender()); |
900 | } |
901 | |
902 | void QQuickGraphsItem::handleRequestShadowQuality(QtGraphs3D::ShadowQuality quality) |
903 | { |
904 | setShadowQuality(quality); |
905 | } |
906 | |
907 | void QQuickGraphsItem::handleQueryPositionChanged(QPoint position) |
908 | { |
909 | QVector3D data = graphPositionAt(point: position); |
910 | setGraphPositionQueryPending(false); |
911 | setQueriedGraphPosition(data); |
912 | emit queriedGraphPositionChanged(data); |
913 | } |
914 | |
915 | void QQuickGraphsItem::handlePrimarySubViewportChanged(const QRect rect) |
916 | { |
917 | m_primarySubView = rect; |
918 | updateSubViews(); |
919 | } |
920 | |
921 | void QQuickGraphsItem::handleSecondarySubViewportChanged(const QRect rect) |
922 | { |
923 | m_secondarySubView = rect; |
924 | updateSubViews(); |
925 | } |
926 | |
927 | void QQuickGraphsItem::handleAxisLabelFormatChangedBySender(QObject *sender) |
928 | { |
929 | // Label format changing needs to dirty the data so that labels are reset. |
930 | if (sender == m_axisX) { |
931 | m_isDataDirty = true; |
932 | m_changeTracker.axisXLabelFormatChanged = true; |
933 | } else if (sender == m_axisY) { |
934 | m_isDataDirty = true; |
935 | m_changeTracker.axisYLabelFormatChanged = true; |
936 | } else if (sender == m_axisZ) { |
937 | m_isDataDirty = true; |
938 | m_changeTracker.axisZLabelFormatChanged = true; |
939 | } else { |
940 | qWarning(msg: "%ls invoked for invalid axis", qUtf16Printable(QString::fromUtf8(__func__))); |
941 | } |
942 | emitNeedRender(); |
943 | } |
944 | |
945 | void QQuickGraphsItem::handleAxisReversedChangedBySender(QObject *sender) |
946 | { |
947 | // Reversing change needs to dirty the data so item positions are recalculated |
948 | if (sender == m_axisX) { |
949 | m_isDataDirty = true; |
950 | m_changeTracker.axisXReversedChanged = true; |
951 | } else if (sender == m_axisY) { |
952 | m_isDataDirty = true; |
953 | m_changeTracker.axisYReversedChanged = true; |
954 | } else if (sender == m_axisZ) { |
955 | m_isDataDirty = true; |
956 | m_changeTracker.axisZReversedChanged = true; |
957 | } else { |
958 | qWarning(msg: "%ls invoked for invalid axis", qUtf16Printable(QString::fromUtf8(__func__))); |
959 | } |
960 | emitNeedRender(); |
961 | } |
962 | |
963 | void QQuickGraphsItem::handleAxisFormatterDirtyBySender(QObject *sender) |
964 | { |
965 | // Sender is QValue3DAxisPrivate |
966 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(sender); |
967 | if (valueAxis == m_axisX) { |
968 | m_isDataDirty = true; |
969 | m_changeTracker.axisXFormatterChanged = true; |
970 | } else if (valueAxis == m_axisY) { |
971 | m_isDataDirty = true; |
972 | m_changeTracker.axisYFormatterChanged = true; |
973 | } else if (valueAxis == m_axisZ) { |
974 | m_isDataDirty = true; |
975 | m_changeTracker.axisZFormatterChanged = true; |
976 | } else { |
977 | qWarning(msg: "%ls invoked for invalid axis", qUtf16Printable(QString::fromUtf8(__func__))); |
978 | } |
979 | emitNeedRender(); |
980 | } |
981 | |
982 | void QQuickGraphsItem::handleAxisLabelAutoRotationChangedBySender(QObject *sender) |
983 | { |
984 | if (sender == m_axisX) |
985 | m_changeTracker.axisXLabelAutoRotationChanged = true; |
986 | else if (sender == m_axisY) |
987 | m_changeTracker.axisYLabelAutoRotationChanged = true; |
988 | else if (sender == m_axisZ) |
989 | m_changeTracker.axisZLabelAutoRotationChanged = true; |
990 | else |
991 | qWarning(msg: "%ls invoked for invalid axis", qUtf16Printable(QString::fromUtf8(__func__))); |
992 | |
993 | emitNeedRender(); |
994 | } |
995 | |
996 | void QQuickGraphsItem::handleAxisTitleVisibilityChangedBySender(QObject *sender) |
997 | { |
998 | if (sender == m_axisX) |
999 | m_changeTracker.axisXTitleVisibilityChanged = true; |
1000 | else if (sender == m_axisY) |
1001 | m_changeTracker.axisYTitleVisibilityChanged = true; |
1002 | else if (sender == m_axisZ) |
1003 | m_changeTracker.axisZTitleVisibilityChanged = true; |
1004 | else |
1005 | qWarning(msg: "%ls invoked for invalid axis", qUtf16Printable(QString::fromUtf8(__func__))); |
1006 | |
1007 | emitNeedRender(); |
1008 | } |
1009 | |
1010 | void QQuickGraphsItem::handleAxisLabelVisibilityChangedBySender(QObject *sender) |
1011 | { |
1012 | if (sender == m_axisX) |
1013 | m_changeTracker.axisXLabelVisibilityChanged = true; |
1014 | else if (sender == m_axisY) |
1015 | m_changeTracker.axisYLabelVisibilityChanged = true; |
1016 | else if (sender == m_axisZ) |
1017 | m_changeTracker.axisZLabelVisibilityChanged = true; |
1018 | else |
1019 | qWarning(msg: "%ls invoked for invalid axis", qUtf16Printable(QString::fromUtf8(__func__))); |
1020 | |
1021 | emitNeedRender(); |
1022 | } |
1023 | |
1024 | void QQuickGraphsItem::handleAxisTitleFixedChangedBySender(QObject *sender) |
1025 | { |
1026 | if (sender == m_axisX) |
1027 | m_changeTracker.axisXTitleFixedChanged = true; |
1028 | else if (sender == m_axisY) |
1029 | m_changeTracker.axisYTitleFixedChanged = true; |
1030 | else if (sender == m_axisZ) |
1031 | m_changeTracker.axisZTitleFixedChanged = true; |
1032 | else |
1033 | qWarning(msg: "%ls invoked for invalid axis", qUtf16Printable(QString::fromUtf8(__func__))); |
1034 | |
1035 | emitNeedRender(); |
1036 | } |
1037 | |
1038 | void QQuickGraphsItem::handleAxisTitleOffsetChangedBySender(QObject *sender) |
1039 | { |
1040 | if (sender == m_axisX) |
1041 | m_changeTracker.axisXTitleOffsetChanged = true; |
1042 | else if (sender == m_axisY) |
1043 | m_changeTracker.axisYTitleOffsetChanged = true; |
1044 | else if (sender == m_axisZ) |
1045 | m_changeTracker.axisZTitleOffsetChanged = true; |
1046 | else |
1047 | qWarning(msg: "%ls invoked for invalid axis", qUtf16Printable(QString::fromUtf8(__func__))); |
1048 | |
1049 | emitNeedRender(); |
1050 | } |
1051 | |
1052 | void QQuickGraphsItem::handleSeriesVisibilityChangedBySender(QObject *sender) |
1053 | { |
1054 | QAbstract3DSeries *series = static_cast<QAbstract3DSeries *>(sender); |
1055 | series->d_func()->m_changeTracker.visibilityChanged = true; |
1056 | |
1057 | m_isDataDirty = true; |
1058 | m_isSeriesVisualsDirty = true; |
1059 | |
1060 | adjustAxisRanges(); |
1061 | |
1062 | emitNeedRender(); |
1063 | } |
1064 | |
1065 | void QQuickGraphsItem::markDataDirty() |
1066 | { |
1067 | m_isDataDirty = true; |
1068 | |
1069 | markSeriesItemLabelsDirty(); |
1070 | emitNeedRender(); |
1071 | } |
1072 | |
1073 | void QQuickGraphsItem::markSeriesVisualsDirty() |
1074 | { |
1075 | m_isSeriesVisualsDirty = true; |
1076 | emitNeedRender(); |
1077 | } |
1078 | |
1079 | void QQuickGraphsItem::markSeriesItemLabelsDirty() |
1080 | { |
1081 | for (int i = 0; i < m_seriesList.size(); i++) |
1082 | m_seriesList.at(i)->d_func()->markItemLabelDirty(); |
1083 | } |
1084 | |
1085 | QAbstract3DAxis *QQuickGraphsItem::createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation) |
1086 | { |
1087 | Q_UNUSED(orientation); |
1088 | |
1089 | // The default default axis is a value axis. If the graph type has a different |
1090 | // default axis for some orientation, this function needs to be overridden. |
1091 | QAbstract3DAxis *defaultAxis = createDefaultValueAxis(); |
1092 | return defaultAxis; |
1093 | } |
1094 | |
1095 | QValue3DAxis *QQuickGraphsItem::createDefaultValueAxis() |
1096 | { |
1097 | // Default value axis has single segment, empty label format, and auto scaling |
1098 | QValue3DAxis *defaultAxis = new QValue3DAxis; |
1099 | defaultAxis->d_func()->setDefaultAxis(true); |
1100 | |
1101 | return defaultAxis; |
1102 | } |
1103 | |
1104 | QCategory3DAxis *QQuickGraphsItem::createDefaultCategoryAxis() |
1105 | { |
1106 | // Default category axis has no labels |
1107 | QCategory3DAxis *defaultAxis = new QCategory3DAxis; |
1108 | defaultAxis->d_func()->setDefaultAxis(true); |
1109 | return defaultAxis; |
1110 | } |
1111 | |
1112 | void QQuickGraphsItem::setAxisHelper(QAbstract3DAxis::AxisOrientation orientation, |
1113 | QAbstract3DAxis *axis, |
1114 | QAbstract3DAxis **axisPtr) |
1115 | { |
1116 | // Setting null axis indicates using default axis |
1117 | if (!axis) |
1118 | axis = createDefaultAxis(orientation); |
1119 | |
1120 | // If old axis is default axis, delete it |
1121 | QAbstract3DAxis *oldAxis = *axisPtr; |
1122 | if (oldAxis) { |
1123 | if (oldAxis->d_func()->isDefaultAxis()) { |
1124 | m_axes.removeAll(t: oldAxis); |
1125 | delete oldAxis; |
1126 | oldAxis = 0; |
1127 | } else { |
1128 | // Disconnect the old axis from use |
1129 | QObject::disconnect(sender: oldAxis, signal: 0, receiver: this, member: 0); |
1130 | oldAxis->d_func()->setOrientation(QAbstract3DAxis::AxisOrientation::None); |
1131 | } |
1132 | } |
1133 | |
1134 | // Assume ownership |
1135 | addAxis(axis); |
1136 | |
1137 | // Connect the new axis |
1138 | *axisPtr = axis; |
1139 | |
1140 | axis->d_func()->setOrientation(orientation); |
1141 | |
1142 | QObject::connect(sender: axis, |
1143 | signal: &QAbstract3DAxis::titleChanged, |
1144 | context: this, |
1145 | slot: &QQuickGraphsItem::handleAxisTitleChanged); |
1146 | QObject::connect(sender: axis, |
1147 | signal: &QAbstract3DAxis::labelsChanged, |
1148 | context: this, |
1149 | slot: &QQuickGraphsItem::handleAxisLabelsChanged); |
1150 | QObject::connect(sender: axis, |
1151 | signal: &QAbstract3DAxis::rangeChanged, |
1152 | context: this, |
1153 | slot: &QQuickGraphsItem::handleAxisRangeChanged); |
1154 | QObject::connect(sender: axis, |
1155 | signal: &QAbstract3DAxis::autoAdjustRangeChanged, |
1156 | context: this, |
1157 | slot: &QQuickGraphsItem::handleAxisAutoAdjustRangeChanged); |
1158 | QObject::connect(sender: axis, |
1159 | signal: &QAbstract3DAxis::labelAutoAngleChanged, |
1160 | context: this, |
1161 | slot: &QQuickGraphsItem::handleAxisLabelAutoRotationChanged); |
1162 | QObject::connect(sender: axis, |
1163 | signal: &QAbstract3DAxis::titleVisibleChanged, |
1164 | context: this, |
1165 | slot: &QQuickGraphsItem::handleAxisTitleVisibilityChanged); |
1166 | QObject::connect(sender: axis, |
1167 | signal: &QAbstract3DAxis::labelVisibleChanged, |
1168 | context: this, |
1169 | slot: &QQuickGraphsItem::handleAxisLabelVisibilityChanged); |
1170 | QObject::connect(sender: axis, |
1171 | signal: &QAbstract3DAxis::titleFixedChanged, |
1172 | context: this, |
1173 | slot: &QQuickGraphsItem::handleAxisTitleFixedChanged); |
1174 | QObject::connect(sender: axis, |
1175 | signal: &QAbstract3DAxis::titleOffsetChanged, |
1176 | context: this, |
1177 | slot: &QQuickGraphsItem::handleAxisTitleOffsetChanged); |
1178 | |
1179 | if (orientation == QAbstract3DAxis::AxisOrientation::X) |
1180 | m_changeTracker.axisXTypeChanged = true; |
1181 | else if (orientation == QAbstract3DAxis::AxisOrientation::Y) |
1182 | m_changeTracker.axisYTypeChanged = true; |
1183 | else if (orientation == QAbstract3DAxis::AxisOrientation::Z) |
1184 | m_changeTracker.axisZTypeChanged = true; |
1185 | |
1186 | handleAxisTitleChangedBySender(sender: axis); |
1187 | handleAxisLabelsChangedBySender(sender: axis); |
1188 | handleAxisRangeChangedBySender(sender: axis); |
1189 | handleAxisAutoAdjustRangeChangedInOrientation(orientation: axis->orientation(), autoAdjust: axis->isAutoAdjustRange()); |
1190 | handleAxisLabelAutoRotationChangedBySender(sender: axis); |
1191 | handleAxisTitleVisibilityChangedBySender(sender: axis); |
1192 | handleAxisLabelVisibilityChangedBySender(sender: axis); |
1193 | handleAxisTitleFixedChangedBySender(sender: axis); |
1194 | handleAxisTitleOffsetChangedBySender(sender: axis); |
1195 | |
1196 | if (axis->type() == QAbstract3DAxis::AxisType::Value) { |
1197 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis); |
1198 | QObject::connect(sender: valueAxis, |
1199 | signal: &QValue3DAxis::segmentCountChanged, |
1200 | context: this, |
1201 | slot: &QQuickGraphsItem::handleAxisSegmentCountChanged); |
1202 | QObject::connect(sender: valueAxis, |
1203 | signal: &QValue3DAxis::subSegmentCountChanged, |
1204 | context: this, |
1205 | slot: &QQuickGraphsItem::handleAxisSubSegmentCountChanged); |
1206 | QObject::connect(sender: valueAxis, |
1207 | signal: &QValue3DAxis::labelFormatChanged, |
1208 | context: this, |
1209 | slot: &QQuickGraphsItem::handleAxisLabelFormatChanged); |
1210 | QObject::connect(sender: valueAxis, |
1211 | signal: &QValue3DAxis::reversedChanged, |
1212 | context: this, |
1213 | slot: &QQuickGraphsItem::handleAxisReversedChanged); |
1214 | // TODO: Handle this somehow (add API to QValue3DAxis?) |
1215 | // QObject::connect(valueAxis->d_func(), &QValue3DAxisPrivate::formatterDirty, |
1216 | // this, &Abstract3DController::handleAxisFormatterDirty); |
1217 | |
1218 | handleAxisSegmentCountChangedBySender(sender: valueAxis); |
1219 | handleAxisSubSegmentCountChangedBySender(sender: valueAxis); |
1220 | handleAxisLabelFormatChangedBySender(sender: valueAxis); |
1221 | handleAxisReversedChangedBySender(sender: valueAxis); |
1222 | // TODO: Handle this somehow (add API to QValue3DAxis?) |
1223 | // handleAxisFormatterDirtyBySender(valueAxis->d_func()); |
1224 | |
1225 | valueAxis->formatter()->setLocale(m_locale); |
1226 | } |
1227 | } |
1228 | |
1229 | void QQuickGraphsItem::startRecordingRemovesAndInserts() |
1230 | { |
1231 | // Default implementation does nothing |
1232 | } |
1233 | |
1234 | int QQuickGraphsItem::horizontalFlipFactor() const |
1235 | { |
1236 | return m_horizontalFlipFactor; |
1237 | } |
1238 | |
1239 | void QQuickGraphsItem::setHorizontalFlipFactor(int newHorizontalFlipFactor) |
1240 | { |
1241 | m_gridUpdate = true; |
1242 | m_horizontalFlipFactor = newHorizontalFlipFactor; |
1243 | } |
1244 | |
1245 | void QQuickGraphsItem::emitNeedRender() |
1246 | { |
1247 | if (!m_renderPending) { |
1248 | emit needRender(); |
1249 | m_renderPending = true; |
1250 | } |
1251 | } |
1252 | |
1253 | void QQuickGraphsItem::handleThemeColorStyleChanged(QGraphsTheme::ColorStyle style) |
1254 | { |
1255 | // Set value for series that have not explicitly set this value |
1256 | for (QAbstract3DSeries *series : m_seriesList) { |
1257 | if (!series->d_func()->m_themeTracker.colorStyleOverride) { |
1258 | series->setColorStyle(style); |
1259 | series->d_func()->m_themeTracker.colorStyleOverride = false; |
1260 | } |
1261 | } |
1262 | theme()->dirtyBits()->colorStyleDirty = false; |
1263 | markSeriesVisualsDirty(); |
1264 | } |
1265 | |
1266 | void QQuickGraphsItem::handleThemeBaseColorsChanged(const QList<QColor> &colors) |
1267 | { |
1268 | int colorIdx = 0; |
1269 | // Set value for series that have not explicitly set this value |
1270 | if (!colors.size()) |
1271 | return; |
1272 | |
1273 | for (QAbstract3DSeries *series : m_seriesList) { |
1274 | if (!series->d_func()->m_themeTracker.baseColorOverride) { |
1275 | series->setBaseColor(colors.at(i: colorIdx)); |
1276 | series->d_func()->m_themeTracker.baseColorOverride = false; |
1277 | } |
1278 | if (++colorIdx >= colors.size()) |
1279 | colorIdx = 0; |
1280 | } |
1281 | |
1282 | theme()->dirtyBits()->seriesColorsDirty = false; |
1283 | markSeriesVisualsDirty(); |
1284 | } |
1285 | |
1286 | void QQuickGraphsItem::handleThemeBaseGradientsChanged(const QList<QLinearGradient> &gradients) |
1287 | { |
1288 | int gradientIdx = 0; |
1289 | // Set value for series that have not explicitly set this value |
1290 | for (QAbstract3DSeries *series : m_seriesList) { |
1291 | if (!series->d_func()->m_themeTracker.baseGradientOverride) { |
1292 | series->setBaseGradient(gradients.at(i: gradientIdx)); |
1293 | series->d_func()->m_themeTracker.baseGradientOverride = false; |
1294 | } |
1295 | if (++gradientIdx >= gradients.size()) |
1296 | gradientIdx = 0; |
1297 | } |
1298 | theme()->dirtyBits()->seriesGradientDirty = false; |
1299 | markSeriesVisualsDirty(); |
1300 | } |
1301 | |
1302 | void QQuickGraphsItem::handleThemeSingleHighlightColorChanged(QColor color) |
1303 | { |
1304 | // Set value for series that have not explicitly set this value |
1305 | for (QAbstract3DSeries *series : m_seriesList) { |
1306 | if (!series->d_func()->m_themeTracker.singleHighlightColorOverride) { |
1307 | series->setSingleHighlightColor(color); |
1308 | series->d_func()->m_themeTracker.singleHighlightColorOverride = false; |
1309 | } |
1310 | } |
1311 | markSeriesVisualsDirty(); |
1312 | } |
1313 | |
1314 | void QQuickGraphsItem::handleThemeSingleHighlightGradientChanged(const QLinearGradient &gradient) |
1315 | { |
1316 | // Set value for series that have not explicitly set this value |
1317 | for (QAbstract3DSeries *series : m_seriesList) { |
1318 | if (!series->d_func()->m_themeTracker.singleHighlightGradientOverride) { |
1319 | series->setSingleHighlightGradient(gradient); |
1320 | series->d_func()->m_themeTracker.singleHighlightGradientOverride = false; |
1321 | } |
1322 | } |
1323 | markSeriesVisualsDirty(); |
1324 | } |
1325 | |
1326 | void QQuickGraphsItem::handleThemeMultiHighlightColorChanged(QColor color) |
1327 | { |
1328 | // Set value for series that have not explicitly set this value |
1329 | for (QAbstract3DSeries *series : m_seriesList) { |
1330 | if (!series->d_func()->m_themeTracker.multiHighlightColorOverride) { |
1331 | series->setMultiHighlightColor(color); |
1332 | series->d_func()->m_themeTracker.multiHighlightColorOverride = false; |
1333 | } |
1334 | } |
1335 | markSeriesVisualsDirty(); |
1336 | } |
1337 | |
1338 | void QQuickGraphsItem::handleThemeMultiHighlightGradientChanged(const QLinearGradient &gradient) |
1339 | { |
1340 | // Set value for series that have not explicitly set this value |
1341 | for (QAbstract3DSeries *series : m_seriesList) { |
1342 | if (!series->d_func()->m_themeTracker.multiHighlightGradientOverride) { |
1343 | series->setMultiHighlightGradient(gradient); |
1344 | series->d_func()->m_themeTracker.multiHighlightGradientOverride = false; |
1345 | } |
1346 | } |
1347 | markSeriesVisualsDirty(); |
1348 | } |
1349 | |
1350 | void QQuickGraphsItem::handleThemeTypeChanged(QGraphsTheme::Theme theme) |
1351 | { |
1352 | Q_UNUSED(theme); |
1353 | |
1354 | // Changing theme type is logically equivalent of changing the entire theme |
1355 | // object, so reset all attached series to the new theme. |
1356 | bool force = m_qml->isReady(); |
1357 | QGraphsTheme *activeTheme = this->theme(); |
1358 | for (int i = 0; i < m_seriesList.size(); i++) |
1359 | m_seriesList.at(i)->d_func()->resetToTheme(theme: *activeTheme, seriesIndex: i, force); |
1360 | |
1361 | markSeriesVisualsDirty(); |
1362 | |
1363 | emit themeTypeChanged(); |
1364 | } |
1365 | |
1366 | void QQuickGraphsItem::addSeriesInternal(QAbstract3DSeries *series) |
1367 | { |
1368 | insertSeries(index: m_seriesList.size(), series); |
1369 | } |
1370 | |
1371 | void QQuickGraphsItem::insertSeries(qsizetype index, QAbstract3DSeries *series) |
1372 | { |
1373 | if (series) { |
1374 | if (m_seriesList.contains(t: series)) { |
1375 | qsizetype oldIndex = m_seriesList.indexOf(t: series); |
1376 | if (index != oldIndex) { |
1377 | m_seriesList.removeOne(t: series); |
1378 | if (oldIndex < index) |
1379 | index--; |
1380 | m_seriesList.insert(i: index, t: series); |
1381 | } |
1382 | } else { |
1383 | qsizetype oldSize = m_seriesList.size(); |
1384 | m_seriesList.insert(i: index, t: series); |
1385 | series->d_func()->setGraph(this); |
1386 | QObject::connect(sender: series, |
1387 | signal: &QAbstract3DSeries::visibleChanged, |
1388 | context: this, |
1389 | slot: &QQuickGraphsItem::handleSeriesVisibilityChanged); |
1390 | series->d_func()->resetToTheme(theme: *theme(), seriesIndex: oldSize, force: false); |
1391 | } |
1392 | if (series->isVisible()) |
1393 | handleSeriesVisibilityChangedBySender(sender: series); |
1394 | } |
1395 | } |
1396 | |
1397 | void QQuickGraphsItem::removeSeriesInternal(QAbstract3DSeries *series) |
1398 | { |
1399 | if (series && series->d_func()->m_graph == this) { |
1400 | m_seriesList.removeAll(t: series); |
1401 | QObject::disconnect(sender: series, |
1402 | signal: &QAbstract3DSeries::visibleChanged, |
1403 | receiver: this, |
1404 | slot: &QQuickGraphsItem::handleSeriesVisibilityChanged); |
1405 | series->d_func()->setGraph(0); |
1406 | m_isDataDirty = true; |
1407 | m_isSeriesVisualsDirty = true; |
1408 | emitNeedRender(); |
1409 | } |
1410 | } |
1411 | |
1412 | QList<QAbstract3DSeries *> QQuickGraphsItem::seriesList() |
1413 | { |
1414 | return m_seriesList; |
1415 | } |
1416 | |
1417 | void QQuickGraphsItem::setAxisX(QAbstract3DAxis *axis) |
1418 | { |
1419 | // Setting null axis will always create new default axis |
1420 | if (!axis || axis != m_axisX) { |
1421 | setAxisHelper(orientation: QAbstract3DAxis::AxisOrientation::X, axis, axisPtr: &m_axisX); |
1422 | emit axisXChanged(axis: m_axisX); |
1423 | } |
1424 | } |
1425 | |
1426 | QAbstract3DAxis *QQuickGraphsItem::axisX() const |
1427 | { |
1428 | return m_axisX; |
1429 | } |
1430 | |
1431 | void QQuickGraphsItem::setAxisY(QAbstract3DAxis *axis) |
1432 | { |
1433 | // Setting null axis will always create new default axis |
1434 | if (!axis || axis != m_axisY) { |
1435 | setAxisHelper(orientation: QAbstract3DAxis::AxisOrientation::Y, axis, axisPtr: &m_axisY); |
1436 | emit axisYChanged(axis: m_axisY); |
1437 | } |
1438 | } |
1439 | |
1440 | QAbstract3DAxis *QQuickGraphsItem::axisY() const |
1441 | { |
1442 | return m_axisY; |
1443 | } |
1444 | |
1445 | void QQuickGraphsItem::setAxisZ(QAbstract3DAxis *axis) |
1446 | { |
1447 | // Setting null axis will always create new default axis |
1448 | if (!axis || axis != m_axisZ) { |
1449 | setAxisHelper(orientation: QAbstract3DAxis::AxisOrientation::Z, axis, axisPtr: &m_axisZ); |
1450 | emit axisZChanged(axis: m_axisZ); |
1451 | } |
1452 | } |
1453 | |
1454 | QAbstract3DAxis *QQuickGraphsItem::axisZ() const |
1455 | { |
1456 | return m_axisZ; |
1457 | } |
1458 | |
1459 | void QQuickGraphsItem::addAxis(QAbstract3DAxis *axis) |
1460 | { |
1461 | Q_ASSERT(axis); |
1462 | QQuickGraphsItem *owner = qobject_cast<QQuickGraphsItem *>(object: axis->parent()); |
1463 | if (owner != this) { |
1464 | Q_ASSERT_X(!owner, "addAxis", "Axis already attached to a graph."); |
1465 | axis->setParent(this); |
1466 | } |
1467 | if (!m_axes.contains(t: axis)) |
1468 | m_axes.append(t: axis); |
1469 | } |
1470 | |
1471 | void QQuickGraphsItem::releaseAxis(QAbstract3DAxis *axis) |
1472 | { |
1473 | if (axis && m_axes.contains(t: axis)) { |
1474 | // Clear the default status from released default axes |
1475 | if (axis->d_func()->isDefaultAxis()) |
1476 | axis->d_func()->setDefaultAxis(false); |
1477 | |
1478 | // If the axis is in use, replace it with a temporary one |
1479 | switch (axis->orientation()) { |
1480 | case QAbstract3DAxis::AxisOrientation::X: |
1481 | setAxisX(0); |
1482 | break; |
1483 | case QAbstract3DAxis::AxisOrientation::Y: |
1484 | setAxisY(0); |
1485 | break; |
1486 | case QAbstract3DAxis::AxisOrientation::Z: |
1487 | setAxisZ(0); |
1488 | break; |
1489 | default: |
1490 | break; |
1491 | } |
1492 | |
1493 | m_axes.removeAll(t: axis); |
1494 | axis->setParent(0); |
1495 | } |
1496 | } |
1497 | |
1498 | QList<QAbstract3DAxis *> QQuickGraphsItem::axes() const |
1499 | { |
1500 | return m_axes; |
1501 | } |
1502 | |
1503 | void QQuickGraphsItem::setRenderingMode(QtGraphs3D::RenderingMode mode) |
1504 | { |
1505 | if (mode == m_renderMode) |
1506 | return; |
1507 | |
1508 | QtGraphs3D::RenderingMode previousMode = m_renderMode; |
1509 | |
1510 | m_renderMode = mode; |
1511 | |
1512 | m_initialisedSize = QSize(0, 0); |
1513 | setFlag(flag: ItemHasContents /*, !m_runningInDesigner*/); |
1514 | |
1515 | // TODO - Need to check if the mode is set properly |
1516 | switch (mode) { |
1517 | case QtGraphs3D::RenderingMode::DirectToBackground: |
1518 | update(); |
1519 | setRenderMode(QQuick3DViewport::Underlay); |
1520 | if (previousMode == QtGraphs3D::RenderingMode::Indirect) { |
1521 | checkWindowList(window: window()); |
1522 | setAntialiasing(m_windowSamples > 0); |
1523 | if (m_windowSamples != m_samples) |
1524 | emit msaaSamplesChanged(samples: m_windowSamples); |
1525 | } |
1526 | break; |
1527 | case QtGraphs3D::RenderingMode::Indirect: |
1528 | update(); |
1529 | setRenderMode(QQuick3DViewport::Offscreen); |
1530 | break; |
1531 | } |
1532 | if (m_sliceView) |
1533 | m_sliceView->setRenderMode(renderMode()); |
1534 | |
1535 | updateWindowParameters(); |
1536 | |
1537 | emit renderingModeChanged(mode); |
1538 | } |
1539 | |
1540 | QtGraphs3D::RenderingMode QQuickGraphsItem::renderingMode() const |
1541 | { |
1542 | return m_renderMode; |
1543 | } |
1544 | |
1545 | void QQuickGraphsItem::keyPressEvent(QKeyEvent *ev) |
1546 | { |
1547 | ev->ignore(); |
1548 | setFlag(flag: ItemHasContents); |
1549 | update(); |
1550 | } |
1551 | |
1552 | void QQuickGraphsItem::checkSliceEnabled() |
1553 | { |
1554 | if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Slice) |
1555 | && (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Column) |
1556 | != selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row))) { |
1557 | m_sliceEnabled = true; |
1558 | } else { |
1559 | m_sliceEnabled = false; |
1560 | } |
1561 | } |
1562 | |
1563 | QtGraphs3D::GridLineType QQuickGraphsItem::gridLineType() const |
1564 | { |
1565 | return m_gridLineType; |
1566 | } |
1567 | |
1568 | void QQuickGraphsItem::setGridLineType(const QtGraphs3D::GridLineType &gridLineType) |
1569 | { |
1570 | m_gridLineTypeDirty = true; |
1571 | if (m_gridLineType != gridLineType) { |
1572 | m_gridLineType = gridLineType; |
1573 | emit gridLineTypeChanged(); |
1574 | emitNeedRender(); |
1575 | } |
1576 | } |
1577 | |
1578 | void QQuickGraphsItem::handleThemeTypeChange() {} |
1579 | |
1580 | void QQuickGraphsItem::handleFpsChanged() |
1581 | { |
1582 | int fps = renderStats()->fps(); |
1583 | if (m_currentFps != fps) { |
1584 | m_currentFps = fps; |
1585 | emit currentFpsChanged(fps); |
1586 | } |
1587 | } |
1588 | |
1589 | void QQuickGraphsItem::handleParentWidthChange() |
1590 | { |
1591 | m_cachedGeometry = parentItem()->boundingRect(); |
1592 | updateWindowParameters(); |
1593 | updateSubViews(); |
1594 | } |
1595 | |
1596 | void QQuickGraphsItem::handleParentHeightChange() |
1597 | { |
1598 | m_cachedGeometry = parentItem()->boundingRect(); |
1599 | updateWindowParameters(); |
1600 | updateSubViews(); |
1601 | } |
1602 | |
1603 | void QQuickGraphsItem::componentComplete() |
1604 | { |
1605 | QQuick3DViewport::componentComplete(); |
1606 | |
1607 | auto url = QUrl(QStringLiteral("defaultMeshes/backgroundMesh")); |
1608 | m_background = new QQuick3DModel(); |
1609 | m_backgroundScale = new QQuick3DNode(); |
1610 | m_backgroundRotation = new QQuick3DNode(); |
1611 | m_graphNode = new QQuick3DNode(); |
1612 | |
1613 | m_backgroundScale->setParent(rootNode()); |
1614 | m_backgroundScale->setParentItem(rootNode()); |
1615 | |
1616 | m_backgroundRotation->setParent(m_backgroundScale); |
1617 | m_backgroundRotation->setParentItem(m_backgroundScale); |
1618 | |
1619 | m_background->setObjectName("Background"); |
1620 | m_background->setParent(m_backgroundRotation); |
1621 | m_background->setParentItem(m_backgroundRotation); |
1622 | |
1623 | m_background->setSource(url); |
1624 | |
1625 | m_backgroundBB = new QQuick3DModel(); |
1626 | m_backgroundBB->setObjectName("BackgroundBB"); |
1627 | m_backgroundBB->setParent(m_background); |
1628 | m_backgroundBB->setParentItem(m_background); |
1629 | m_backgroundBB->setSource(QUrl(QStringLiteral("defaultMeshes/barMeshFull"))); |
1630 | m_backgroundBB->setPickable(true); |
1631 | |
1632 | m_graphNode->setParent(rootNode()); |
1633 | m_graphNode->setParentItem(rootNode()); |
1634 | |
1635 | setUpCamera(); |
1636 | setUpLight(); |
1637 | |
1638 | // Create repeaters for each axis X, Y, Z |
1639 | m_repeaterX = createRepeater(); |
1640 | m_repeaterY = createRepeater(); |
1641 | m_repeaterZ = createRepeater(); |
1642 | |
1643 | m_delegateModelX.reset(p: new QQmlComponent(qmlEngine(this), (QStringLiteral(":/axis/AxisLabel")))); |
1644 | m_delegateModelY.reset(p: new QQmlComponent(qmlEngine(this), (QStringLiteral(":/axis/AxisLabel")))); |
1645 | m_delegateModelZ.reset(p: new QQmlComponent(qmlEngine(this), (QStringLiteral(":/axis/AxisLabel")))); |
1646 | |
1647 | m_repeaterX->setDelegate(m_delegateModelX.get()); |
1648 | m_repeaterY->setDelegate(m_delegateModelY.get()); |
1649 | m_repeaterZ->setDelegate(m_delegateModelZ.get()); |
1650 | |
1651 | // title labels for axes |
1652 | m_titleLabelX = createTitleLabel(); |
1653 | m_titleLabelX->setVisible(axisX()->isTitleVisible()); |
1654 | m_titleLabelX->setProperty(name: "labelText", value: axisX()->title()); |
1655 | |
1656 | m_titleLabelY = createTitleLabel(); |
1657 | m_titleLabelY->setVisible(axisY()->isTitleVisible()); |
1658 | m_titleLabelY->setProperty(name: "labelText", value: axisY()->title()); |
1659 | |
1660 | m_titleLabelZ = createTitleLabel(); |
1661 | m_titleLabelZ->setVisible(axisZ()->isTitleVisible()); |
1662 | m_titleLabelZ->setProperty(name: "labelText", value: axisZ()->title()); |
1663 | |
1664 | // Grid with geometry |
1665 | m_gridGeometryModel = new QQuick3DModel(m_graphNode); |
1666 | m_gridGeometryModel->setCastsShadows(false); |
1667 | m_gridGeometryModel->setReceivesShadows(false); |
1668 | auto gridGeometry = new QQuick3DGeometry(m_gridGeometryModel); |
1669 | gridGeometry->setStride(sizeof(QVector3D)); |
1670 | gridGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines); |
1671 | gridGeometry->addAttribute(semantic: QQuick3DGeometry::Attribute::PositionSemantic, |
1672 | offset: 0, |
1673 | componentType: QQuick3DGeometry::Attribute::F32Type); |
1674 | m_gridGeometryModel->setGeometry(gridGeometry); |
1675 | QQmlListReference gridMaterialRef(m_gridGeometryModel, "materials"); |
1676 | auto gridMaterial = new QQuick3DPrincipledMaterial(m_gridGeometryModel); |
1677 | gridMaterial->setLighting(QQuick3DPrincipledMaterial::Lighting::NoLighting); |
1678 | gridMaterial->setCullMode(QQuick3DMaterial::CullMode::BackFaceCulling); |
1679 | gridMaterial->setBaseColor(theme()->grid().mainColor()); |
1680 | gridMaterialRef.append(gridMaterial); |
1681 | |
1682 | // subgrid with geometry |
1683 | m_subgridGeometryModel = new QQuick3DModel(m_graphNode); |
1684 | m_subgridGeometryModel->setCastsShadows(false); |
1685 | m_subgridGeometryModel->setReceivesShadows(false); |
1686 | auto subgridGeometry = new QQuick3DGeometry(m_subgridGeometryModel); |
1687 | subgridGeometry->setStride(sizeof(QVector3D)); |
1688 | subgridGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines); |
1689 | subgridGeometry->addAttribute(semantic: QQuick3DGeometry::Attribute::PositionSemantic, |
1690 | offset: 0, |
1691 | componentType: QQuick3DGeometry::Attribute::F32Type); |
1692 | m_subgridGeometryModel->setGeometry(subgridGeometry); |
1693 | |
1694 | QQmlListReference subgridMaterialRef(m_subgridGeometryModel, "materials"); |
1695 | auto subgridMaterial = new QQuick3DPrincipledMaterial(m_subgridGeometryModel); |
1696 | subgridMaterial->setLighting(QQuick3DPrincipledMaterial::Lighting::NoLighting); |
1697 | subgridMaterial->setCullMode(QQuick3DMaterial::CullMode::BackFaceCulling); |
1698 | subgridMaterialRef.append(subgridMaterial); |
1699 | |
1700 | createItemLabel(); |
1701 | |
1702 | auto axis = axisX(); |
1703 | m_repeaterX->setModel(axis->labels().size()); |
1704 | handleAxisLabelsChangedBySender(sender: axisX()); |
1705 | |
1706 | axis = axisY(); |
1707 | m_repeaterY->setModel(2 * axis->labels().size()); |
1708 | handleAxisLabelsChangedBySender(sender: axisY()); |
1709 | |
1710 | axis = axisZ(); |
1711 | m_repeaterZ->setModel(axis->labels().size()); |
1712 | handleAxisLabelsChangedBySender(sender: axisZ()); |
1713 | |
1714 | if (!m_pendingCustomItemList.isEmpty()) { |
1715 | for (const auto &item : std::as_const(t&: m_pendingCustomItemList)) |
1716 | addCustomItem(item); |
1717 | } |
1718 | } |
1719 | |
1720 | QQuick3DDirectionalLight *QQuickGraphsItem::light() const |
1721 | { |
1722 | return m_light; |
1723 | } |
1724 | |
1725 | bool QQuickGraphsItem::isSlicingActive() const |
1726 | { |
1727 | return m_scene->isSlicingActive(); |
1728 | } |
1729 | |
1730 | void QQuickGraphsItem::setSlicingActive(bool isSlicing) |
1731 | { |
1732 | m_scene->setSlicingActive(isSlicing); |
1733 | } |
1734 | |
1735 | bool QQuickGraphsItem::isCustomLabelItem(QCustom3DItem *item) const |
1736 | { |
1737 | return item->d_func()->m_isLabelItem; |
1738 | } |
1739 | |
1740 | bool QQuickGraphsItem::isCustomVolumeItem(QCustom3DItem *item) const |
1741 | { |
1742 | return item->d_func()->m_isVolumeItem; |
1743 | } |
1744 | |
1745 | QImage QQuickGraphsItem::customTextureImage(QCustom3DItem *item) |
1746 | { |
1747 | return item->d_func()->textureImage(); |
1748 | } |
1749 | |
1750 | Q3DScene *QQuickGraphsItem::scene() |
1751 | { |
1752 | return m_scene; |
1753 | } |
1754 | |
1755 | void QQuickGraphsItem::addTheme(QGraphsTheme *theme) |
1756 | { |
1757 | Q_ASSERT(theme); |
1758 | QQuickGraphsItem *owner = qobject_cast<QQuickGraphsItem *>(object: theme->parent()); |
1759 | if (owner != this) { |
1760 | Q_ASSERT_X(!owner, "addTheme", "Theme already attached to a graph."); |
1761 | theme->setParent(this); |
1762 | } |
1763 | if (!m_themes.contains(t: theme)) |
1764 | m_themes.append(t: theme); |
1765 | } |
1766 | |
1767 | void QQuickGraphsItem::releaseTheme(QGraphsTheme *theme) |
1768 | { |
1769 | QGraphsTheme *oldTheme = m_activeTheme; |
1770 | |
1771 | if (theme && m_themes.contains(t: theme)) { |
1772 | // If the theme is in use, replace it with a temporary one |
1773 | if (theme == m_activeTheme) { |
1774 | m_activeTheme = nullptr; |
1775 | disconnect(sender: theme, signal: &QGraphsTheme::themeChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeTypeChanged); |
1776 | disconnect(sender: theme, signal: &QGraphsTheme::colorStyleChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeColorStyleChanged); |
1777 | disconnect(sender: theme, signal: &QGraphsTheme::seriesColorsChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeBaseColorsChanged); |
1778 | disconnect(sender: theme, signal: &QGraphsTheme::seriesGradientsChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeBaseGradientsChanged); |
1779 | disconnect(sender: theme, signal: &QGraphsTheme::singleHighlightColorChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeSingleHighlightColorChanged); |
1780 | disconnect(sender: theme, signal: &QGraphsTheme::singleHighlightGradientChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeSingleHighlightGradientChanged); |
1781 | disconnect(sender: theme, signal: &QGraphsTheme::multiHighlightColorChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeMultiHighlightColorChanged); |
1782 | disconnect(sender: theme, signal: &QGraphsTheme::multiHighlightGradientChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeMultiHighlightGradientChanged); |
1783 | disconnect(sender: theme, signal: &QGraphsTheme::update, receiver: this, slot: &QQuickGraphsItem::emitNeedRender); |
1784 | } |
1785 | m_themes.removeAll(t: theme); |
1786 | theme->setParent(nullptr); |
1787 | } |
1788 | |
1789 | if (oldTheme != m_activeTheme) |
1790 | emit activeThemeChanged(activeTheme: m_activeTheme); |
1791 | } |
1792 | |
1793 | QList<QGraphsTheme *> QQuickGraphsItem::themes() const |
1794 | { |
1795 | return m_themes; |
1796 | } |
1797 | |
1798 | void QQuickGraphsItem::setTheme(QGraphsTheme *theme) |
1799 | { |
1800 | if (theme != m_activeTheme) { |
1801 | if (m_activeTheme) { |
1802 | disconnect(sender: m_activeTheme, signal: &QGraphsTheme::themeChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeTypeChanged); |
1803 | disconnect(sender: m_activeTheme, signal: &QGraphsTheme::colorStyleChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeColorStyleChanged); |
1804 | disconnect(sender: m_activeTheme, signal: &QGraphsTheme::seriesColorsChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeBaseColorsChanged); |
1805 | disconnect(sender: m_activeTheme, signal: &QGraphsTheme::seriesGradientsChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeBaseGradientsChanged); |
1806 | disconnect(sender: m_activeTheme, signal: &QGraphsTheme::singleHighlightColorChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeSingleHighlightColorChanged); |
1807 | disconnect(sender: m_activeTheme, signal: &QGraphsTheme::singleHighlightGradientChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeSingleHighlightGradientChanged); |
1808 | disconnect(sender: m_activeTheme, signal: &QGraphsTheme::multiHighlightColorChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeMultiHighlightColorChanged); |
1809 | disconnect(sender: m_activeTheme, signal: &QGraphsTheme::multiHighlightGradientChanged, receiver: this, slot: &QQuickGraphsItem::handleThemeMultiHighlightGradientChanged); |
1810 | disconnect(sender: m_activeTheme, signal: &QGraphsTheme::update, receiver: this, slot: &QQuickGraphsItem::emitNeedRender); |
1811 | } |
1812 | |
1813 | connect(sender: theme, signal: &QGraphsTheme::themeChanged, context: this, slot: &QQuickGraphsItem::handleThemeTypeChanged); |
1814 | connect(sender: theme, signal: &QGraphsTheme::colorStyleChanged, context: this, slot: &QQuickGraphsItem::handleThemeColorStyleChanged); |
1815 | connect(sender: theme, signal: &QGraphsTheme::seriesColorsChanged, context: this, slot: &QQuickGraphsItem::handleThemeBaseColorsChanged); |
1816 | connect(sender: theme, signal: &QGraphsTheme::seriesGradientsChanged, context: this, slot: &QQuickGraphsItem::handleThemeBaseGradientsChanged); |
1817 | connect(sender: theme, signal: &QGraphsTheme::singleHighlightColorChanged, context: this, slot: &QQuickGraphsItem::handleThemeSingleHighlightColorChanged); |
1818 | connect(sender: theme, signal: &QGraphsTheme::singleHighlightGradientChanged, context: this, slot: &QQuickGraphsItem::handleThemeSingleHighlightGradientChanged); |
1819 | connect(sender: theme, signal: &QGraphsTheme::multiHighlightColorChanged, context: this, slot: &QQuickGraphsItem::handleThemeMultiHighlightColorChanged); |
1820 | connect(sender: theme, signal: &QGraphsTheme::multiHighlightGradientChanged, context: this, slot: &QQuickGraphsItem::handleThemeMultiHighlightGradientChanged); |
1821 | connect(sender: theme, signal: &QGraphsTheme::update, context: this, slot: &QQuickGraphsItem::emitNeedRender); |
1822 | |
1823 | m_activeTheme = theme; |
1824 | m_changeTracker.themeChanged = true; |
1825 | // Default theme can be created by theme manager, so ensure we have correct theme |
1826 | QGraphsTheme *newActiveTheme = m_activeTheme; |
1827 | // Reset all attached series to the new theme |
1828 | for (int i = 0; i < m_seriesList.size(); i++) |
1829 | m_seriesList.at(i)->d_func()->resetToTheme(theme: *newActiveTheme, seriesIndex: i, force: isComponentComplete()); |
1830 | markSeriesVisualsDirty(); |
1831 | emit activeThemeChanged(activeTheme: newActiveTheme); |
1832 | } |
1833 | } |
1834 | |
1835 | QGraphsTheme *QQuickGraphsItem::theme() const |
1836 | { |
1837 | return m_activeTheme; |
1838 | } |
1839 | |
1840 | bool QQuickGraphsItem::hasSeries(QAbstract3DSeries *series) |
1841 | { |
1842 | return m_seriesList.contains(t: series); |
1843 | } |
1844 | |
1845 | void QQuickGraphsItem::setSelectionMode(QtGraphs3D::SelectionFlags mode) |
1846 | { |
1847 | if (mode != m_selectionMode) { |
1848 | m_selectionMode = mode; |
1849 | m_changeTracker.selectionModeChanged = true; |
1850 | emit selectionModeChanged(mode); |
1851 | emitNeedRender(); |
1852 | } |
1853 | } |
1854 | |
1855 | QtGraphs3D::SelectionFlags QQuickGraphsItem::selectionMode() const |
1856 | { |
1857 | return m_selectionMode; |
1858 | } |
1859 | |
1860 | void QQuickGraphsItem::doSetShadowQuality(QtGraphs3D::ShadowQuality quality) |
1861 | { |
1862 | if (quality != m_shadowQuality) { |
1863 | m_shadowQuality = quality; |
1864 | m_changeTracker.shadowQualityChanged = true; |
1865 | emit shadowQualityChanged(quality: m_shadowQuality); |
1866 | emitNeedRender(); |
1867 | } |
1868 | } |
1869 | |
1870 | void QQuickGraphsItem::setShadowQuality(QtGraphs3D::ShadowQuality quality) |
1871 | { |
1872 | if (!m_useOrthoProjection) |
1873 | doSetShadowQuality(quality); |
1874 | } |
1875 | |
1876 | QtGraphs3D::ShadowQuality QQuickGraphsItem::shadowQuality() const |
1877 | { |
1878 | return m_shadowQuality; |
1879 | } |
1880 | |
1881 | qsizetype QQuickGraphsItem::addCustomItem(QCustom3DItem *item) |
1882 | { |
1883 | if (isComponentComplete()) { |
1884 | if (isCustomLabelItem(item)) { |
1885 | QQuick3DNode *label = createTitleLabel(); |
1886 | QCustom3DLabel *key = static_cast<QCustom3DLabel *>(item); |
1887 | m_customLabelList.insert(key, value: label); |
1888 | } else if (isCustomVolumeItem(item)) { |
1889 | QQuick3DModel *model = new QQuick3DModel(); |
1890 | model->setParent(graphNode()); |
1891 | model->setParentItem(graphNode()); |
1892 | m_customItemList.insert(key: item, value: model); |
1893 | } else { |
1894 | QQuick3DModel *model = new QQuick3DModel(); |
1895 | model->setParent(graphNode()); |
1896 | model->setParentItem(graphNode()); |
1897 | QQmlListReference materialsRef(model, "materials"); |
1898 | QQuick3DPrincipledMaterial *material = new QQuick3DPrincipledMaterial(); |
1899 | material->setParent(model); |
1900 | material->setParentItem(model); |
1901 | materialsRef.append(material); |
1902 | if (!selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::None)) |
1903 | model->setPickable(true); |
1904 | m_customItemList.insert(key: item, value: model); |
1905 | } |
1906 | } else { |
1907 | m_pendingCustomItemList.append(t: item); |
1908 | } |
1909 | |
1910 | if (!item) |
1911 | return -1; |
1912 | |
1913 | qsizetype index = m_customItems.indexOf(t: item); |
1914 | |
1915 | if (index != -1) |
1916 | return index; |
1917 | |
1918 | item->setParent(this); |
1919 | connect(sender: item, signal: &QCustom3DItem::needUpdate, context: this, slot: &QQuickGraphsItem::updateCustomItem); |
1920 | m_customItems.append(t: item); |
1921 | item->d_func()->resetDirtyBits(); |
1922 | m_isCustomDataDirty = true; |
1923 | emitNeedRender(); |
1924 | return m_customItems.size() - 1; |
1925 | } |
1926 | |
1927 | void QQuickGraphsItem::deleteCustomItems() |
1928 | { |
1929 | for (QCustom3DItem *item : m_customItems) |
1930 | delete item; |
1931 | m_customItems.clear(); |
1932 | m_isCustomDataDirty = true; |
1933 | emitNeedRender(); |
1934 | } |
1935 | |
1936 | void QQuickGraphsItem::deleteCustomItem(QCustom3DItem *item) |
1937 | { |
1938 | if (!item) |
1939 | return; |
1940 | |
1941 | m_customItems.removeOne(t: item); |
1942 | delete item; |
1943 | item = 0; |
1944 | m_isCustomDataDirty = true; |
1945 | emitNeedRender(); |
1946 | } |
1947 | |
1948 | void QQuickGraphsItem::deleteCustomItem(QVector3D position) |
1949 | { |
1950 | // Get the item for the position |
1951 | for (QCustom3DItem *item : m_customItems) { |
1952 | if (item->position() == position) |
1953 | deleteCustomItem(item); |
1954 | } |
1955 | } |
1956 | |
1957 | QList<QCustom3DItem *> QQuickGraphsItem::customItems() const |
1958 | { |
1959 | return m_customItems; |
1960 | } |
1961 | |
1962 | void QQuickGraphsItem::updateCustomItem() |
1963 | { |
1964 | m_isCustomItemDirty = true; |
1965 | m_isCustomDataDirty = true; |
1966 | emitNeedRender(); |
1967 | } |
1968 | |
1969 | void QQuickGraphsItem::removeCustomItems() |
1970 | { |
1971 | m_customItemList.clear(); |
1972 | m_customLabelList.clear(); |
1973 | deleteCustomItems(); |
1974 | } |
1975 | |
1976 | void QQuickGraphsItem::removeCustomItem(QCustom3DItem *item) |
1977 | { |
1978 | if (isCustomLabelItem(item)) { |
1979 | m_customLabelList.remove(key: static_cast<QCustom3DLabel *>(item)); |
1980 | } else if (isCustomVolumeItem(item)) { |
1981 | m_customItemList.remove(key: item); |
1982 | auto volume = static_cast<QCustom3DVolume *>(item); |
1983 | if (m_customVolumes.contains(key: volume)) { |
1984 | m_customVolumes[volume].model->deleteLater(); |
1985 | m_customVolumes.remove(key: volume); |
1986 | } |
1987 | } else { |
1988 | m_customItemList.remove(key: item); |
1989 | } |
1990 | deleteCustomItem(item); |
1991 | } |
1992 | |
1993 | void QQuickGraphsItem::removeCustomItemAt(QVector3D position) |
1994 | { |
1995 | auto labelIterator = m_customLabelList.constBegin(); |
1996 | while (labelIterator != m_customLabelList.constEnd()) { |
1997 | QCustom3DLabel *label = labelIterator.key(); |
1998 | if (label->position() == position) { |
1999 | labelIterator.value()->setVisible(false); |
2000 | labelIterator = m_customLabelList.erase(it: labelIterator); |
2001 | } else { |
2002 | ++labelIterator; |
2003 | } |
2004 | } |
2005 | |
2006 | auto itemIterator = m_customItemList.constBegin(); |
2007 | while (itemIterator != m_customItemList.constEnd()) { |
2008 | QCustom3DItem *item = itemIterator.key(); |
2009 | if (item->position() == position) { |
2010 | itemIterator.value()->setVisible(false); |
2011 | itemIterator = m_customItemList.erase(it: itemIterator); |
2012 | if (isCustomVolumeItem(item)) { |
2013 | auto volume = static_cast<QCustom3DVolume *>(item); |
2014 | if (m_customVolumes.contains(key: volume)) { |
2015 | m_customVolumes[volume].model->deleteLater(); |
2016 | m_customVolumes.remove(key: volume); |
2017 | } |
2018 | } |
2019 | } else { |
2020 | ++itemIterator; |
2021 | } |
2022 | } |
2023 | deleteCustomItem(position); |
2024 | } |
2025 | |
2026 | void QQuickGraphsItem::releaseCustomItem(QCustom3DItem *item) |
2027 | { |
2028 | if (isCustomLabelItem(item)) { |
2029 | m_customLabelList.remove(key: static_cast<QCustom3DLabel *>(item)); |
2030 | } else if (isCustomVolumeItem(item)) { |
2031 | m_customItemList.remove(key: item); |
2032 | auto volume = static_cast<QCustom3DVolume *>(item); |
2033 | if (m_customVolumes.contains(key: volume)) { |
2034 | m_customVolumes[volume].model->deleteLater(); |
2035 | m_customVolumes.remove(key: volume); |
2036 | } |
2037 | } else { |
2038 | m_customItemList.remove(key: item); |
2039 | } |
2040 | |
2041 | if (item && m_customItems.contains(t: item)) { |
2042 | disconnect(sender: item, signal: &QCustom3DItem::needUpdate, receiver: this, slot: &QQuickGraphsItem::updateCustomItem); |
2043 | m_customItems.removeOne(t: item); |
2044 | item->setParent(0); |
2045 | m_isCustomDataDirty = true; |
2046 | emitNeedRender(); |
2047 | } |
2048 | } |
2049 | |
2050 | int QQuickGraphsItem::selectedLabelIndex() const |
2051 | { |
2052 | int index = m_selectedLabelIndex; |
2053 | QAbstract3DAxis *axis = selectedAxis(); |
2054 | if (axis && axis->labels().size() <= index) |
2055 | index = -1; |
2056 | return index; |
2057 | } |
2058 | |
2059 | QAbstract3DAxis *QQuickGraphsItem::selectedAxis() const |
2060 | { |
2061 | QAbstract3DAxis *axis = 0; |
2062 | QtGraphs3D::ElementType type = m_clickedType; |
2063 | switch (type) { |
2064 | case QtGraphs3D::ElementType::AxisXLabel: |
2065 | axis = axisX(); |
2066 | break; |
2067 | case QtGraphs3D::ElementType::AxisYLabel: |
2068 | axis = axisY(); |
2069 | break; |
2070 | case QtGraphs3D::ElementType::AxisZLabel: |
2071 | axis = axisZ(); |
2072 | break; |
2073 | default: |
2074 | axis = 0; |
2075 | break; |
2076 | } |
2077 | |
2078 | return axis; |
2079 | } |
2080 | |
2081 | qsizetype QQuickGraphsItem::selectedCustomItemIndex() const |
2082 | { |
2083 | qsizetype index = m_selectedCustomItemIndex; |
2084 | if (m_customItems.size() <= index) |
2085 | index = -1; |
2086 | return index; |
2087 | } |
2088 | |
2089 | QCustom3DItem *QQuickGraphsItem::selectedCustomItem() const |
2090 | { |
2091 | QCustom3DItem *item = 0; |
2092 | qsizetype index = selectedCustomItemIndex(); |
2093 | if (index >= 0) |
2094 | item = m_customItems[index]; |
2095 | return item; |
2096 | } |
2097 | |
2098 | QQmlListProperty<QCustom3DItem> QQuickGraphsItem::customItemList() |
2099 | { |
2100 | return QQmlListProperty<QCustom3DItem>(this, |
2101 | this, |
2102 | &QQuickGraphsItem::appendCustomItemFunc, |
2103 | &QQuickGraphsItem::countCustomItemFunc, |
2104 | &QQuickGraphsItem::atCustomItemFunc, |
2105 | &QQuickGraphsItem::clearCustomItemFunc); |
2106 | } |
2107 | |
2108 | void QQuickGraphsItem::appendCustomItemFunc(QQmlListProperty<QCustom3DItem> *list, |
2109 | QCustom3DItem *item) |
2110 | { |
2111 | QQuickGraphsItem *decl = reinterpret_cast<QQuickGraphsItem *>(list->data); |
2112 | decl->addCustomItem(item); |
2113 | } |
2114 | |
2115 | qsizetype QQuickGraphsItem::countCustomItemFunc(QQmlListProperty<QCustom3DItem> *list) |
2116 | { |
2117 | Q_UNUSED(list); |
2118 | return reinterpret_cast<QQuickGraphsItem *>(list->data)->m_customItems.size(); |
2119 | } |
2120 | |
2121 | QCustom3DItem *QQuickGraphsItem::atCustomItemFunc(QQmlListProperty<QCustom3DItem> *list, |
2122 | qsizetype index) |
2123 | { |
2124 | Q_UNUSED(list); |
2125 | Q_UNUSED(index); |
2126 | return reinterpret_cast<QQuickGraphsItem *>(list->data)->m_customItems.at(i: index); |
2127 | } |
2128 | |
2129 | void QQuickGraphsItem::clearCustomItemFunc(QQmlListProperty<QCustom3DItem> *list) |
2130 | { |
2131 | QQuickGraphsItem *decl = reinterpret_cast<QQuickGraphsItem *>(list->data); |
2132 | decl->removeCustomItems(); |
2133 | } |
2134 | |
2135 | void QQuickGraphsItem::synchData() |
2136 | { |
2137 | if (!isVisible()) |
2138 | return; |
2139 | |
2140 | m_renderPending = false; |
2141 | |
2142 | if (m_changeTracker.selectionModeChanged) { |
2143 | updateSelectionMode(newMode: selectionMode()); |
2144 | m_changeTracker.selectionModeChanged = false; |
2145 | } |
2146 | |
2147 | bool recalculateScale = false; |
2148 | if (m_changeTracker.aspectRatioChanged) { |
2149 | recalculateScale = true; |
2150 | m_changeTracker.aspectRatioChanged = false; |
2151 | } |
2152 | |
2153 | if (m_changeTracker.horizontalAspectRatioChanged) { |
2154 | recalculateScale = true; |
2155 | m_changeTracker.horizontalAspectRatioChanged = false; |
2156 | } |
2157 | |
2158 | if (m_changeTracker.marginChanged) { |
2159 | recalculateScale = true; |
2160 | m_changeTracker.marginChanged = false; |
2161 | } |
2162 | |
2163 | if (m_changeTracker.polarChanged) { |
2164 | recalculateScale = true; |
2165 | m_changeTracker.polarChanged = false; |
2166 | } |
2167 | |
2168 | if (recalculateScale) |
2169 | calculateSceneScalingFactors(); |
2170 | |
2171 | bool axisDirty = recalculateScale; |
2172 | if (m_changeTracker.axisXFormatterChanged) { |
2173 | m_changeTracker.axisXFormatterChanged = false; |
2174 | if (axisX()->type() == QAbstract3DAxis::AxisType::Value) { |
2175 | QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(axisX()); |
2176 | valueAxisX->recalculate(); |
2177 | repeaterX()->setModel(valueAxisX->formatter()->labelPositions().size()); |
2178 | } |
2179 | axisDirty = true; |
2180 | } |
2181 | |
2182 | if (m_changeTracker.axisYFormatterChanged) { |
2183 | m_changeTracker.axisYFormatterChanged = false; |
2184 | if (axisY()->type() == QAbstract3DAxis::AxisType::Value) { |
2185 | QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(axisY()); |
2186 | valueAxisY->recalculate(); |
2187 | repeaterY()->setModel(2 * valueAxisY->formatter()->labelPositions().size()); |
2188 | } |
2189 | axisDirty = true; |
2190 | } |
2191 | |
2192 | if (m_changeTracker.axisZFormatterChanged) { |
2193 | m_changeTracker.axisZFormatterChanged = false; |
2194 | if (axisZ()->type() == QAbstract3DAxis::AxisType::Value) { |
2195 | QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(axisZ()); |
2196 | valueAxisZ->recalculate(); |
2197 | repeaterZ()->setModel(valueAxisZ->formatter()->labelPositions().size()); |
2198 | } |
2199 | axisDirty = true; |
2200 | } |
2201 | |
2202 | if (m_changeTracker.axisXSegmentCountChanged) { |
2203 | if (axisX()->type() == QAbstract3DAxis::AxisType::Value) { |
2204 | QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(axisX()); |
2205 | valueAxisX->recalculate(); |
2206 | } |
2207 | m_changeTracker.axisXSegmentCountChanged = false; |
2208 | axisDirty = true; |
2209 | } |
2210 | |
2211 | if (m_changeTracker.axisYSegmentCountChanged) { |
2212 | if (axisY()->type() == QAbstract3DAxis::AxisType::Value) { |
2213 | QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(axisY()); |
2214 | valueAxisY->recalculate(); |
2215 | } |
2216 | m_changeTracker.axisYSegmentCountChanged = false; |
2217 | axisDirty = true; |
2218 | } |
2219 | |
2220 | if (m_changeTracker.axisZSegmentCountChanged) { |
2221 | if (axisZ()->type() == QAbstract3DAxis::AxisType::Value) { |
2222 | QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(axisZ()); |
2223 | valueAxisZ->recalculate(); |
2224 | } |
2225 | m_changeTracker.axisZSegmentCountChanged = false; |
2226 | axisDirty = true; |
2227 | } |
2228 | |
2229 | if (m_changeTracker.axisXSubSegmentCountChanged) { |
2230 | if (axisX()->type() == QAbstract3DAxis::AxisType::Value) { |
2231 | QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(axisX()); |
2232 | valueAxisX->recalculate(); |
2233 | } |
2234 | m_changeTracker.axisXSubSegmentCountChanged = false; |
2235 | axisDirty = true; |
2236 | } |
2237 | |
2238 | if (m_changeTracker.axisYSubSegmentCountChanged) { |
2239 | if (axisY()->type() == QAbstract3DAxis::AxisType::Value) { |
2240 | QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(axisY()); |
2241 | valueAxisY->recalculate(); |
2242 | } |
2243 | m_changeTracker.axisYSubSegmentCountChanged = false; |
2244 | axisDirty = true; |
2245 | } |
2246 | |
2247 | if (m_changeTracker.axisZSubSegmentCountChanged) { |
2248 | if (axisZ()->type() == QAbstract3DAxis::AxisType::Value) { |
2249 | QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(axisZ()); |
2250 | valueAxisZ->recalculate(); |
2251 | } |
2252 | m_changeTracker.axisZSubSegmentCountChanged = false; |
2253 | axisDirty = true; |
2254 | } |
2255 | |
2256 | if (m_changeTracker.axisXLabelsChanged) { |
2257 | if (axisX()->type() == QAbstract3DAxis::AxisType::Value) { |
2258 | auto valueAxisX = static_cast<QValue3DAxis *>(axisX()); |
2259 | valueAxisX->recalculate(); |
2260 | repeaterX()->setModel(valueAxisX->formatter()->labelPositions().size()); |
2261 | } else if (axisX()->type() == QAbstract3DAxis::AxisType::Category) { |
2262 | auto categoryAxis = static_cast<QCategory3DAxis *>(axisX()); |
2263 | repeaterX()->setModel(categoryAxis->labels().size()); |
2264 | } |
2265 | |
2266 | m_changeTracker.axisXLabelsChanged = false; |
2267 | handleLabelCountChanged(repeater: m_repeaterX, axisLabelColor: theme()->axisX().labelTextColor()); |
2268 | axisDirty = true; |
2269 | } |
2270 | |
2271 | if (m_changeTracker.axisYLabelsChanged) { |
2272 | if (axisY()->type() == QAbstract3DAxis::AxisType::Value) { |
2273 | auto valueAxisY = static_cast<QValue3DAxis *>(axisY()); |
2274 | valueAxisY->recalculate(); |
2275 | repeaterY()->setModel(2 * valueAxisY->formatter()->labelPositions().size()); |
2276 | } else if (axisY()->type() == QAbstract3DAxis::AxisType::Category) { |
2277 | auto categoryAxis = static_cast<QCategory3DAxis *>(axisY()); |
2278 | repeaterY()->setModel(2 * categoryAxis->labels().size()); |
2279 | } |
2280 | |
2281 | m_changeTracker.axisYLabelsChanged = false; |
2282 | handleLabelCountChanged(repeater: m_repeaterY, axisLabelColor: theme()->axisY().labelTextColor()); |
2283 | axisDirty = true; |
2284 | } |
2285 | |
2286 | if (m_changeTracker.axisZLabelsChanged) { |
2287 | if (axisZ()->type() == QAbstract3DAxis::AxisType::Value) { |
2288 | auto valueAxisZ = static_cast<QValue3DAxis *>(axisZ()); |
2289 | valueAxisZ->recalculate(); |
2290 | repeaterZ()->setModel(valueAxisZ->formatter()->labelPositions().size()); |
2291 | } else if (axisZ()->type() == QAbstract3DAxis::AxisType::Category) { |
2292 | auto categoryAxis = static_cast<QCategory3DAxis *>(axisZ()); |
2293 | repeaterZ()->setModel(categoryAxis->labels().size()); |
2294 | } |
2295 | |
2296 | m_changeTracker.axisZLabelsChanged = false; |
2297 | handleLabelCountChanged(repeater: m_repeaterZ, axisLabelColor: theme()->axisZ().labelTextColor()); |
2298 | axisDirty = true; |
2299 | } |
2300 | |
2301 | if (m_changeTracker.axisXLabelVisibilityChanged) { |
2302 | repeaterX()->setVisible(axisX()->labelsVisible()); |
2303 | m_changeTracker.axisXLabelVisibilityChanged = false; |
2304 | } |
2305 | |
2306 | if (m_changeTracker.axisYLabelVisibilityChanged) { |
2307 | repeaterY()->setVisible(axisY()->labelsVisible()); |
2308 | m_changeTracker.axisYLabelVisibilityChanged = false; |
2309 | } |
2310 | |
2311 | if (m_changeTracker.axisZLabelVisibilityChanged) { |
2312 | repeaterZ()->setVisible(axisZ()->labelsVisible()); |
2313 | m_changeTracker.axisZLabelVisibilityChanged = false; |
2314 | } |
2315 | updateTitleLabels(); |
2316 | |
2317 | if (m_changeTracker.shadowQualityChanged) { |
2318 | updateShadowQuality(quality: shadowQuality()); |
2319 | m_changeTracker.shadowQualityChanged = false; |
2320 | } |
2321 | |
2322 | if (m_changeTracker.axisXRangeChanged) { |
2323 | axisDirty = true; |
2324 | calculateSceneScalingFactors(); |
2325 | m_changeTracker.axisXRangeChanged = false; |
2326 | } |
2327 | |
2328 | if (m_changeTracker.axisYRangeChanged) { |
2329 | axisDirty = true; |
2330 | QAbstract3DAxis *axis = axisY(); |
2331 | updateAxisRange(min: axis->min(), max: axis->max()); |
2332 | calculateSceneScalingFactors(); |
2333 | m_changeTracker.axisYRangeChanged = false; |
2334 | } |
2335 | |
2336 | if (m_changeTracker.axisZRangeChanged) { |
2337 | axisDirty = true; |
2338 | calculateSceneScalingFactors(); |
2339 | m_changeTracker.axisZRangeChanged = false; |
2340 | } |
2341 | |
2342 | if (m_changeTracker.axisYReversedChanged) { |
2343 | m_changeTracker.axisYReversedChanged = false; |
2344 | if (m_axisY->type() == QAbstract3DAxis::AxisType::Value) { |
2345 | QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY); |
2346 | updateAxisReversed(enable: valueAxisY->reversed()); |
2347 | } |
2348 | } |
2349 | |
2350 | if (m_changeTracker.axisXLabelAutoRotationChanged) { |
2351 | axisDirty = true; |
2352 | m_changeTracker.axisXLabelAutoRotationChanged = false; |
2353 | } |
2354 | |
2355 | if (m_changeTracker.axisYLabelAutoRotationChanged) { |
2356 | axisDirty = true; |
2357 | m_changeTracker.axisYLabelAutoRotationChanged = false; |
2358 | } |
2359 | |
2360 | if (m_changeTracker.axisZLabelAutoRotationChanged) { |
2361 | axisDirty = true; |
2362 | m_changeTracker.axisZLabelAutoRotationChanged = false; |
2363 | } |
2364 | |
2365 | if (m_changeTracker.axisXTitleFixedChanged) { |
2366 | axisDirty = true; |
2367 | m_changeTracker.axisXTitleFixedChanged = false; |
2368 | } |
2369 | |
2370 | if (m_changeTracker.axisYTitleFixedChanged) { |
2371 | axisDirty = true; |
2372 | m_changeTracker.axisYTitleFixedChanged = false; |
2373 | } |
2374 | |
2375 | if (m_changeTracker.axisZTitleFixedChanged) { |
2376 | axisDirty = true; |
2377 | m_changeTracker.axisZTitleFixedChanged = false; |
2378 | } |
2379 | |
2380 | if (m_changeTracker.axisXTitleOffsetChanged) { |
2381 | axisDirty = true; |
2382 | m_changeTracker.axisXTitleOffsetChanged = false; |
2383 | } |
2384 | if (m_changeTracker.axisYTitleOffsetChanged) { |
2385 | axisDirty = true; |
2386 | m_changeTracker.axisYTitleOffsetChanged = false; |
2387 | } |
2388 | if (m_changeTracker.axisZTitleOffsetChanged) { |
2389 | axisDirty = true; |
2390 | m_changeTracker.axisZTitleOffsetChanged = false; |
2391 | } |
2392 | |
2393 | updateCamera(); |
2394 | |
2395 | QVector3D forward = camera()->forward(); |
2396 | auto targetRotation = cameraTarget()->eulerRotation(); |
2397 | if (m_yFlipped != (targetRotation.x() > 0)) { |
2398 | m_yFlipped = (targetRotation.x() > 0); |
2399 | axisDirty = true; |
2400 | } |
2401 | if (m_xFlipped != (forward.x() > 0)) { |
2402 | m_xFlipped = (forward.x() > 0); |
2403 | axisDirty = true; |
2404 | } |
2405 | if (m_zFlipped != ((forward.z() > .1f))) { |
2406 | m_zFlipped = ((forward.z() > .1f)); |
2407 | axisDirty = true; |
2408 | } |
2409 | |
2410 | if (axisDirty) { |
2411 | QQmlListReference materialsRef(m_background, "materials"); |
2412 | if (!materialsRef.size()) { |
2413 | QQuick3DCustomMaterial *bgMat |
2414 | = createQmlCustomMaterial(QStringLiteral(":/materials/BackgroundMaterial")); |
2415 | bgMat->setParent(m_background); |
2416 | materialsRef.append(bgMat); |
2417 | } |
2418 | if (m_gridLineType == QtGraphs3D::GridLineType::Shader) |
2419 | updateGridLineType(); |
2420 | else |
2421 | updateGrid(); |
2422 | updateLabels(); |
2423 | updateCustomData(); |
2424 | if (m_sliceView && isSliceEnabled()) { |
2425 | updateSliceGrid(); |
2426 | updateSliceLabels(); |
2427 | } |
2428 | m_gridUpdated = true; |
2429 | } |
2430 | |
2431 | if (m_changeTracker.radialLabelOffsetChanged) { |
2432 | updateRadialLabelOffset(); |
2433 | m_changeTracker.radialLabelOffsetChanged = false; |
2434 | } |
2435 | if (m_changeTracker.labelMarginChanged) { |
2436 | updateLabels(); |
2437 | m_changeTracker.labelMarginChanged = false; |
2438 | } |
2439 | |
2440 | QMatrix4x4 modelMatrix; |
2441 | m_backgroundScale->setScale(m_scaleWithBackground + m_backgroundScaleMargin); |
2442 | |
2443 | QVector3D rotVec; |
2444 | if (!m_yFlipped) { |
2445 | rotVec = QVector3D(0, 270, 0); |
2446 | if (m_xFlipped && m_zFlipped) |
2447 | rotVec.setY(90); |
2448 | else if (!m_xFlipped && m_zFlipped) |
2449 | rotVec.setY(0); |
2450 | else if (m_xFlipped && !m_zFlipped) |
2451 | rotVec.setY(180); |
2452 | } else { |
2453 | rotVec = QVector3D(0, 180, 180); |
2454 | if (m_xFlipped && m_zFlipped) |
2455 | rotVec.setY(0); |
2456 | else if (!m_xFlipped && m_zFlipped) |
2457 | rotVec.setY(270); |
2458 | else if (m_xFlipped && !m_zFlipped) |
2459 | rotVec.setY(90); |
2460 | } |
2461 | |
2462 | auto rotation = Utils::calculateRotation(xyzRotations: rotVec); |
2463 | if (m_yFlipped) { |
2464 | m_backgroundRotation->setRotation(rotation); |
2465 | } else { |
2466 | modelMatrix.rotate(quaternion: rotation); |
2467 | m_backgroundRotation->setRotation(rotation); |
2468 | } |
2469 | |
2470 | bool forceUpdateCustomVolumes = false; |
2471 | if (m_changeTracker.projectionChanged) { |
2472 | forceUpdateCustomVolumes = true; |
2473 | bool useOrtho = isOrthoProjection(); |
2474 | if (useOrtho) |
2475 | setCamera(m_oCamera); |
2476 | else |
2477 | setCamera(m_pCamera); |
2478 | m_changeTracker.projectionChanged = false; |
2479 | } |
2480 | |
2481 | if (m_changeTracker.themeChanged) { |
2482 | theme()->resetDirtyBits(); |
2483 | m_changeTracker.themeChanged = false; |
2484 | } |
2485 | |
2486 | if (m_lightStrengthDirty) { |
2487 | light()->setBrightness(lightStrength() * .2f); |
2488 | if (qFuzzyIsNull(f: light()->brightness())) |
2489 | light()->setBrightness(.0000001f); |
2490 | updateLightStrength(); |
2491 | m_lightStrengthDirty = false; |
2492 | } |
2493 | |
2494 | if (m_ambientLightStrengthDirty) { |
2495 | float ambientStrength = m_ambientLightStrength; |
2496 | QColor ambientColor = QColor::fromRgbF(r: ambientStrength, g: ambientStrength, b: ambientStrength); |
2497 | light()->setAmbientColor(ambientColor); |
2498 | if (qFuzzyIsNull(f: light()->brightness())) |
2499 | light()->setBrightness(.0000001f); |
2500 | m_ambientLightStrengthDirty = false; |
2501 | } |
2502 | |
2503 | if (m_lightColorDirty) { |
2504 | light()->setColor(lightColor()); |
2505 | m_lightColorDirty = false; |
2506 | } |
2507 | |
2508 | if (m_shadowStrengthDirty) { |
2509 | light()->setShadowFactor(shadowStrength()); |
2510 | m_shadowStrengthDirty = false; |
2511 | } |
2512 | |
2513 | if (theme()->dirtyBits()->gridDirty) { |
2514 | QQmlListReference materialRef(m_background, "materials"); |
2515 | Q_ASSERT(materialRef.size()); |
2516 | float mainWidth = theme()->grid().mainWidth(); |
2517 | if ((m_gridLineType == QtGraphs3D::GridLineType::Shader) && mainWidth > 1.0f) { |
2518 | qWarning(msg: "Invalid value for shader grid. Valid range for grid width is between" |
2519 | " 0.0 and 1.0. Value exceeds 1.0. Set it to 1.0"); |
2520 | mainWidth = 1.0f; |
2521 | } |
2522 | |
2523 | if ((m_gridLineType == QtGraphs3D::GridLineType::Shader) && mainWidth < 0.0f) { |
2524 | qWarning(msg: "Invalid value for shader grid. Valid range for grid width is between" |
2525 | " 0.0 and 1.0. Value is smaller than 0.0. Set it to 0.0"); |
2526 | mainWidth = 0.0f; |
2527 | } |
2528 | auto *material = static_cast<QQuick3DCustomMaterial *>(materialRef.at(0)); |
2529 | material->setProperty(name: "gridWidth", value: mainWidth); |
2530 | |
2531 | QColor gridMainColor = theme()->grid().mainColor(); |
2532 | QQmlListReference backgroundRef(m_background, "materials"); |
2533 | auto *backgroundMaterial = static_cast<QQuick3DCustomMaterial *>(backgroundRef.at(0)); |
2534 | backgroundMaterial->setProperty(name: "gridLineColor", value: gridMainColor); |
2535 | QQmlListReference mainGridRef(m_gridGeometryModel, "materials"); |
2536 | auto *gridMaterial = static_cast<QQuick3DPrincipledMaterial *>(mainGridRef.at(0)); |
2537 | gridMaterial->setBaseColor(gridMainColor); |
2538 | |
2539 | QColor gridSubColor = theme()->grid().subColor(); |
2540 | backgroundMaterial->setProperty(name: "subgridLineColor", value: gridSubColor); |
2541 | |
2542 | QQmlListReference subGridRef(m_subgridGeometryModel, "materials"); |
2543 | auto *subgridMaterial = static_cast<QQuick3DPrincipledMaterial *>(subGridRef.at(0)); |
2544 | subgridMaterial->setBaseColor(gridSubColor); |
2545 | |
2546 | theme()->dirtyBits()->gridDirty = false; |
2547 | } |
2548 | |
2549 | // label Adjustments |
2550 | if (theme()->dirtyBits()->labelBackgroundColorDirty) { |
2551 | QColor labelBackgroundColor = theme()->labelBackgroundColor(); |
2552 | changeLabelBackgroundColor(repeater: m_repeaterX, color: labelBackgroundColor); |
2553 | changeLabelBackgroundColor(repeater: m_repeaterY, color: labelBackgroundColor); |
2554 | changeLabelBackgroundColor(repeater: m_repeaterZ, color: labelBackgroundColor); |
2555 | m_titleLabelX->setProperty(name: "backgroundColor", value: labelBackgroundColor); |
2556 | m_titleLabelY->setProperty(name: "backgroundColor", value: labelBackgroundColor); |
2557 | m_titleLabelZ->setProperty(name: "backgroundColor", value: labelBackgroundColor); |
2558 | m_itemLabel->setProperty(name: "backgroundColor", value: labelBackgroundColor); |
2559 | |
2560 | if (m_sliceView) { |
2561 | changeLabelBackgroundColor(repeater: m_sliceHorizontalLabelRepeater, color: labelBackgroundColor); |
2562 | changeLabelBackgroundColor(repeater: m_sliceVerticalLabelRepeater, color: labelBackgroundColor); |
2563 | m_sliceItemLabel->setProperty(name: "backgroundColor", value: labelBackgroundColor); |
2564 | m_sliceHorizontalTitleLabel->setProperty(name: "backgroundColor", value: labelBackgroundColor); |
2565 | m_sliceVerticalTitleLabel->setProperty(name: "backgroundColor", value: labelBackgroundColor); |
2566 | } |
2567 | theme()->dirtyBits()->labelBackgroundColorDirty = false; |
2568 | } |
2569 | |
2570 | if (theme()->dirtyBits()->labelBackgroundVisibilityDirty) { |
2571 | bool visible = theme()->isLabelBackgroundVisible(); |
2572 | changeLabelBackgroundVisible(repeater: m_repeaterX, visible); |
2573 | changeLabelBackgroundVisible(repeater: m_repeaterY, visible); |
2574 | changeLabelBackgroundVisible(repeater: m_repeaterZ, visible); |
2575 | m_titleLabelX->setProperty(name: "backgroundVisible", value: visible); |
2576 | m_titleLabelY->setProperty(name: "backgroundVisible", value: visible); |
2577 | m_titleLabelZ->setProperty(name: "backgroundVisible", value: visible); |
2578 | m_itemLabel->setProperty(name: "backgroundVisible", value: visible); |
2579 | |
2580 | if (m_sliceView) { |
2581 | changeLabelBackgroundVisible(repeater: m_sliceHorizontalLabelRepeater, visible); |
2582 | changeLabelBackgroundVisible(repeater: m_sliceVerticalLabelRepeater, visible); |
2583 | m_sliceItemLabel->setProperty(name: "backgroundVisible", value: visible); |
2584 | m_sliceHorizontalTitleLabel->setProperty(name: "backgroundVisible", value: visible); |
2585 | m_sliceVerticalTitleLabel->setProperty(name: "backgroundVisible", value: visible); |
2586 | } |
2587 | theme()->dirtyBits()->labelBackgroundVisibilityDirty = false; |
2588 | } |
2589 | |
2590 | if (theme()->dirtyBits()->labelBorderVisibilityDirty) { |
2591 | bool visible = theme()->isLabelBorderVisible(); |
2592 | changeLabelBorderVisible(repeater: m_repeaterX, visible); |
2593 | changeLabelBorderVisible(repeater: m_repeaterY, visible); |
2594 | changeLabelBorderVisible(repeater: m_repeaterZ, visible); |
2595 | m_titleLabelX->setProperty(name: "borderVisible", value: visible); |
2596 | m_titleLabelY->setProperty(name: "borderVisible", value: visible); |
2597 | m_titleLabelZ->setProperty(name: "borderVisible", value: visible); |
2598 | m_itemLabel->setProperty(name: "borderVisible", value: visible); |
2599 | |
2600 | if (m_sliceView) { |
2601 | changeLabelBorderVisible(repeater: m_sliceHorizontalLabelRepeater, visible); |
2602 | changeLabelBorderVisible(repeater: m_sliceVerticalLabelRepeater, visible); |
2603 | m_sliceItemLabel->setProperty(name: "borderVisible", value: visible); |
2604 | m_sliceHorizontalTitleLabel->setProperty(name: "borderVisible", value: visible); |
2605 | m_sliceVerticalTitleLabel->setProperty(name: "borderVisible", value: visible); |
2606 | } |
2607 | theme()->dirtyBits()->labelBorderVisibilityDirty = false; |
2608 | } |
2609 | |
2610 | if (theme()->dirtyBits()->labelTextColorDirty) { |
2611 | QColor labelTextColor = theme()->labelTextColor(); |
2612 | m_itemLabel->setProperty(name: "labelTextColor", value: labelTextColor); |
2613 | |
2614 | if (m_sliceView && isSliceEnabled()) |
2615 | m_sliceItemLabel->setProperty(name: "labelTextColor", value: labelTextColor); |
2616 | theme()->dirtyBits()->labelTextColorDirty = false; |
2617 | } |
2618 | |
2619 | if (theme()->dirtyBits()->axisXDirty) { |
2620 | QColor labelTextColor = theme()->axisX().labelTextColor(); |
2621 | changeLabelTextColor(repeater: m_repeaterX, color: labelTextColor); |
2622 | m_titleLabelX->setProperty(name: "labelTextColor", value: labelTextColor); |
2623 | if (m_sliceView && isSliceEnabled()) { |
2624 | if (m_selectionMode == SelectionRow) |
2625 | changeLabelTextColor(repeater: m_sliceHorizontalLabelRepeater, color: labelTextColor); |
2626 | m_sliceHorizontalTitleLabel->setProperty(name: "labelTextColor", value: labelTextColor); |
2627 | } |
2628 | theme()->dirtyBits()->axisXDirty = false; |
2629 | } |
2630 | |
2631 | if (theme()->dirtyBits()->axisYDirty) { |
2632 | QColor labelTextColor = theme()->axisY().labelTextColor(); |
2633 | changeLabelTextColor(repeater: m_repeaterY, color: labelTextColor); |
2634 | m_titleLabelY->setProperty(name: "labelTextColor", value: labelTextColor); |
2635 | if (m_sliceView && isSliceEnabled()) { |
2636 | changeLabelTextColor(repeater: m_sliceVerticalLabelRepeater, color: labelTextColor); |
2637 | m_sliceVerticalTitleLabel->setProperty(name: "labelTextColor", value: labelTextColor); |
2638 | } |
2639 | theme()->dirtyBits()->axisYDirty = false; |
2640 | } |
2641 | |
2642 | if (theme()->dirtyBits()->axisZDirty) { |
2643 | QColor labelTextColor = theme()->axisZ().labelTextColor(); |
2644 | changeLabelTextColor(repeater: m_repeaterZ, color: labelTextColor); |
2645 | m_titleLabelZ->setProperty(name: "labelTextColor", value: labelTextColor); |
2646 | if (m_sliceView && isSliceEnabled()) { |
2647 | if (m_selectionMode == SelectionColumn) |
2648 | changeLabelTextColor(repeater: m_sliceHorizontalLabelRepeater, color: labelTextColor); |
2649 | m_sliceHorizontalTitleLabel->setProperty(name: "labelTextColor", value: labelTextColor); |
2650 | } |
2651 | theme()->dirtyBits()->axisZDirty = false; |
2652 | } |
2653 | |
2654 | if (theme()->dirtyBits()->labelFontDirty) { |
2655 | auto font = theme()->labelFont(); |
2656 | changeLabelFont(repeater: m_repeaterX, font); |
2657 | changeLabelFont(repeater: m_repeaterY, font); |
2658 | changeLabelFont(repeater: m_repeaterZ, font); |
2659 | m_titleLabelX->setProperty(name: "labelFont", value: font); |
2660 | m_titleLabelY->setProperty(name: "labelFont", value: font); |
2661 | m_titleLabelZ->setProperty(name: "labelFont", value: font); |
2662 | m_itemLabel->setProperty(name: "labelFont", value: font); |
2663 | updateLabels(); |
2664 | |
2665 | if (m_sliceView && isSliceEnabled()) { |
2666 | changeLabelFont(repeater: m_sliceHorizontalLabelRepeater, font); |
2667 | changeLabelFont(repeater: m_sliceVerticalLabelRepeater, font); |
2668 | m_sliceItemLabel->setProperty(name: "labelFont", value: font); |
2669 | m_sliceHorizontalTitleLabel->setProperty(name: "labelFont", value: font); |
2670 | m_sliceVerticalTitleLabel->setProperty(name: "labelFont", value: font); |
2671 | updateSliceLabels(); |
2672 | } |
2673 | theme()->dirtyBits()->labelFontDirty = false; |
2674 | m_isSeriesVisualsDirty = true; |
2675 | } |
2676 | |
2677 | if (theme()->dirtyBits()->labelsVisibilityDirty) { |
2678 | bool visible = theme()->labelsVisible(); |
2679 | changeLabelsVisible(repeater: m_repeaterX, visible); |
2680 | changeLabelsVisible(repeater: m_repeaterY, visible); |
2681 | changeLabelsVisible(repeater: m_repeaterZ, visible); |
2682 | m_titleLabelX->setProperty(name: "visible", value: visible && axisX()->isTitleVisible()); |
2683 | m_titleLabelY->setProperty(name: "visible", value: visible && axisY()->isTitleVisible()); |
2684 | m_titleLabelZ->setProperty(name: "visible", value: visible && axisZ()->isTitleVisible()); |
2685 | m_itemLabel->setProperty(name: "visible", value: visible && m_itemSelected); |
2686 | |
2687 | if (m_sliceView) { |
2688 | changeLabelsVisible(repeater: m_sliceHorizontalLabelRepeater, visible); |
2689 | changeLabelsVisible(repeater: m_sliceVerticalLabelRepeater, visible); |
2690 | m_sliceItemLabel->setProperty(name: "visible", value: visible && selectionMode() |
2691 | .testFlag(flag: QtGraphs3D::SelectionFlag::Item)); |
2692 | m_sliceHorizontalTitleLabel->setProperty(name: "visible", value: visible); |
2693 | m_sliceVerticalTitleLabel->setProperty(name: "visible", value: visible); |
2694 | } |
2695 | theme()->dirtyBits()->labelsVisibilityDirty = false; |
2696 | } |
2697 | |
2698 | // Grid and background adjustments |
2699 | if (theme()->dirtyBits()->plotAreaBackgroundColorDirty) { |
2700 | QQmlListReference materialRef(m_background, "materials"); |
2701 | Q_ASSERT(materialRef.size()); |
2702 | auto material = static_cast<QQuick3DCustomMaterial *>(materialRef.at(0)); |
2703 | material->setProperty(name: "baseColor", value: theme()->plotAreaBackgroundColor()); |
2704 | theme()->dirtyBits()->plotAreaBackgroundColorDirty = false; |
2705 | } |
2706 | |
2707 | if (theme()->dirtyBits()->plotAreaBackgroundVisibilityDirty) { |
2708 | QQmlListReference materialRef(m_background, "materials"); |
2709 | Q_ASSERT(materialRef.size()); |
2710 | auto *material = static_cast<QQuick3DCustomMaterial *>(materialRef.at(0)); |
2711 | material->setProperty(name: "baseVisible", value: theme()->isPlotAreaBackgroundVisible()); |
2712 | theme()->dirtyBits()->plotAreaBackgroundVisibilityDirty = false; |
2713 | } |
2714 | |
2715 | if (m_gridLineTypeDirty) { |
2716 | m_gridLineType = gridLineType(); |
2717 | theme()->dirtyBits()->gridVisibilityDirty = true; |
2718 | theme()->dirtyBits()->gridDirty = true; |
2719 | m_gridUpdate = true; |
2720 | m_gridLineTypeDirty = false; |
2721 | } |
2722 | |
2723 | if (theme()->dirtyBits()->gridVisibilityDirty) { |
2724 | bool visible = theme()->isGridVisible(); |
2725 | QQmlListReference materialRef(m_background, "materials"); |
2726 | Q_ASSERT(materialRef.size()); |
2727 | auto *material = static_cast<QQuick3DCustomMaterial *>(materialRef.at(0)); |
2728 | material->setProperty(name: "gridVisible", value: visible && (m_gridLineType == QtGraphs3D::GridLineType::Shader)); |
2729 | m_gridGeometryModel->setVisible(visible &! (m_gridLineType == QtGraphs3D::GridLineType::Shader)); |
2730 | m_subgridGeometryModel->setVisible(visible &! (m_gridLineType == QtGraphs3D::GridLineType::Shader)); |
2731 | |
2732 | if (m_sliceView && isSliceEnabled()) |
2733 | m_sliceGridGeometryModel->setVisible(visible); |
2734 | |
2735 | theme()->dirtyBits()->gridVisibilityDirty = false; |
2736 | } |
2737 | |
2738 | if (theme()->dirtyBits()->singleHighlightColorDirty) { |
2739 | updateSingleHighlightColor(); |
2740 | theme()->dirtyBits()->singleHighlightColorDirty = false; |
2741 | } |
2742 | |
2743 | // Other adjustments |
2744 | if (theme()->dirtyBits()->backgroundColorDirty || theme()->dirtyBits()->backgroundVisibilityDirty) { |
2745 | updateBackgroundColor(); |
2746 | theme()->dirtyBits()->backgroundColorDirty = false; |
2747 | theme()->dirtyBits()->backgroundVisibilityDirty = false; |
2748 | } |
2749 | |
2750 | if (isCustomDataDirty()) { |
2751 | forceUpdateCustomVolumes = true; |
2752 | updateCustomData(); |
2753 | setCustomDataDirty(false); |
2754 | } |
2755 | |
2756 | if (m_changedSeriesList.size()) { |
2757 | forceUpdateCustomVolumes = true; |
2758 | updateGraph(); |
2759 | m_changedSeriesList.clear(); |
2760 | } |
2761 | |
2762 | if (m_isSeriesVisualsDirty) { |
2763 | forceUpdateCustomVolumes = true; |
2764 | if (m_gridLineType == QtGraphs3D::GridLineType::Shader) |
2765 | updateGridLineType(); |
2766 | else |
2767 | updateGrid(); |
2768 | updateLabels(); |
2769 | if (m_sliceView && isSliceEnabled()) { |
2770 | updateSliceGrid(); |
2771 | updateSliceLabels(); |
2772 | } |
2773 | updateGraph(); |
2774 | m_isSeriesVisualsDirty = false; |
2775 | } |
2776 | |
2777 | if (m_gridUpdate) { |
2778 | if (m_gridLineType == QtGraphs3D::GridLineType::Shader) |
2779 | updateGridLineType(); |
2780 | else |
2781 | updateGrid(); |
2782 | } |
2783 | |
2784 | if (m_isDataDirty) { |
2785 | forceUpdateCustomVolumes = true; |
2786 | updateGraph(); |
2787 | m_isDataDirty = false; |
2788 | } |
2789 | |
2790 | if (m_sliceActivatedChanged) |
2791 | toggleSliceGraph(); |
2792 | |
2793 | if (isCustomItemDirty() || forceUpdateCustomVolumes) |
2794 | updateCustomVolumes(); |
2795 | |
2796 | if (m_measureFps) |
2797 | QQuickItem::update(); |
2798 | } |
2799 | |
2800 | void QQuickGraphsItem::updateGrid() |
2801 | { |
2802 | |
2803 | QQmlListReference materialsRef(m_background, "materials"); |
2804 | auto *bgMat = static_cast<QQuick3DCustomMaterial *>(materialsRef.at(0)); |
2805 | bgMat->setProperty(name: "scale", value: m_scaleWithBackground); |
2806 | qsizetype gridLineCountX = 0; |
2807 | qsizetype subGridLineCountX = 0; |
2808 | gridLineCountHelper(axis: axisX(), lineCount&: gridLineCountX, sublineCount&: subGridLineCountX); |
2809 | |
2810 | qsizetype gridLineCountY = 0; |
2811 | qsizetype subGridLineCountY = 0; |
2812 | gridLineCountHelper(axis: axisY(), lineCount&: gridLineCountY, sublineCount&: subGridLineCountY); |
2813 | |
2814 | qsizetype gridLineCountZ = 0; |
2815 | qsizetype subGridLineCountZ = 0; |
2816 | gridLineCountHelper(axis: axisZ(), lineCount&: gridLineCountZ, sublineCount&: subGridLineCountZ); |
2817 | |
2818 | auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin; |
2819 | QVector3D scaleX(backgroundScale.x() * lineLengthScaleFactor(), |
2820 | lineWidthScaleFactor(), |
2821 | lineWidthScaleFactor()); |
2822 | QVector3D scaleY(lineWidthScaleFactor(), |
2823 | backgroundScale.y() * lineLengthScaleFactor(), |
2824 | lineWidthScaleFactor()); |
2825 | const QVector3D scaleZ(backgroundScale.z() * lineLengthScaleFactor(), |
2826 | lineWidthScaleFactor(), |
2827 | lineWidthScaleFactor()); |
2828 | |
2829 | const bool xFlipped = isXFlipped(); |
2830 | const bool yFlipped = isYFlipped(); |
2831 | const bool zFlipped = isZFlipped(); |
2832 | |
2833 | const float lineOffset = 0.01f; |
2834 | const float backOffsetAdjustment = 0.005f; |
2835 | |
2836 | QQuaternion lineRotation(.0f, .0f, .0f, .0f); |
2837 | QVector3D rotation(90.0f, 0.0f, 0.0f); |
2838 | |
2839 | QByteArray vertices; |
2840 | qsizetype calculatedSize = 0; |
2841 | |
2842 | QByteArray subvertices; |
2843 | qsizetype subCalculatedSize = 0; |
2844 | |
2845 | bool usePolar = isPolar() && (m_graphType != QAbstract3DSeries::SeriesType::Bar); |
2846 | |
2847 | if (!usePolar) { |
2848 | int factor = m_hasVerticalSegmentLine ? 2 : 1; |
2849 | calculatedSize = (factor * gridLineCountX + factor * gridLineCountZ + 2 * gridLineCountY) |
2850 | * 2 * sizeof(QVector3D); |
2851 | subCalculatedSize = (factor * subGridLineCountX + factor * subGridLineCountZ + 2 * subGridLineCountY) |
2852 | * 2 * sizeof(QVector3D); |
2853 | } else { |
2854 | int radialMainGridSize = static_cast<QValue3DAxis *>(axisZ())->gridSize() * polarRoundness; |
2855 | int radialSubGridSize = static_cast<QValue3DAxis *>(axisZ())->subGridSize() |
2856 | * polarRoundness; |
2857 | |
2858 | qsizetype angularMainGridsize = static_cast<QValue3DAxis *>(axisX())->gridSize(); |
2859 | qsizetype angularSubGridsize = static_cast<QValue3DAxis *>(axisX())->subGridSize(); |
2860 | |
2861 | calculatedSize = (radialMainGridSize + angularMainGridsize + (2 * gridLineCountY) - 1) |
2862 | * 2 * sizeof(QVector3D); |
2863 | subCalculatedSize = (radialSubGridSize + + angularSubGridsize + (2 * subGridLineCountY)) |
2864 | |
2865 | * 2 * sizeof(QVector3D); |
2866 | } |
2867 | vertices.resize(size: calculatedSize); |
2868 | QVector3D *data = reinterpret_cast<QVector3D *>(vertices.data()); |
2869 | |
2870 | subvertices.resize(size: subCalculatedSize); |
2871 | QVector3D *subdata = reinterpret_cast<QVector3D *>(subvertices.data()); |
2872 | |
2873 | // Floor horizontal line |
2874 | float linePosX = 0.0f; |
2875 | float linePosY = backgroundScale.y(); |
2876 | float linePosZ = 0.0f; |
2877 | float scale = m_scaleWithBackground.z(); |
2878 | |
2879 | float x0 = backgroundScale.x(); |
2880 | float x1 = -backgroundScale.x(); |
2881 | |
2882 | float tempLineOffset = -lineOffset; |
2883 | if (!yFlipped) { |
2884 | linePosY *= -1.0f; |
2885 | rotation.setZ(180.0f); |
2886 | tempLineOffset *= -1.0f; |
2887 | } |
2888 | lineRotation = Utils::calculateRotation(xyzRotations: rotation); |
2889 | linePosY *= m_horizontalFlipFactor; |
2890 | tempLineOffset *= m_horizontalFlipFactor; |
2891 | if (!usePolar) { |
2892 | for (int i = 0; i < subGridLineCountZ; i++) { |
2893 | if (axisZ()->type() == QAbstract3DAxis::AxisType::Value) { |
2894 | linePosZ = static_cast<QValue3DAxis *>(axisZ())->subGridPositionAt(gridLine: i) * -scale |
2895 | * 2.0f |
2896 | + scale; |
2897 | } else if (axisZ()->type() == QAbstract3DAxis::AxisType::Category) { |
2898 | linePosZ = calculateCategoryGridLinePosition(axis: axisZ(), index: i); |
2899 | linePosY = calculateCategoryGridLinePosition(axis: axisY(), index: i); |
2900 | } |
2901 | |
2902 | *subdata++ = QVector3D(x0, linePosY + tempLineOffset, linePosZ); |
2903 | *subdata++ = QVector3D(x1, linePosY + tempLineOffset, linePosZ); |
2904 | } |
2905 | |
2906 | for (int i = 0; i < gridLineCountZ; i++) { |
2907 | if (axisZ()->type() == QAbstract3DAxis::AxisType::Value) { |
2908 | linePosZ = static_cast<QValue3DAxis *>(axisZ())->gridPositionAt(gridLine: i) * -scale * 2.0f |
2909 | + scale; |
2910 | } else if (axisZ()->type() == QAbstract3DAxis::AxisType::Category) { |
2911 | linePosZ = calculateCategoryGridLinePosition(axis: axisZ(), index: i); |
2912 | linePosY = calculateCategoryGridLinePosition(axis: axisY(), index: i); |
2913 | } |
2914 | |
2915 | *data++ = QVector3D(x0, linePosY + tempLineOffset, linePosZ); |
2916 | *data++ = QVector3D(x1, linePosY + tempLineOffset, linePosZ); |
2917 | } |
2918 | } else { |
2919 | auto valueAxisZ = static_cast<QValue3DAxis *>(axisZ()); |
2920 | |
2921 | for (int k = 0; k < subGridLineCountZ; k++) { |
2922 | float degrees = 0.0f; |
2923 | const float r = (m_polarRadius) *valueAxisZ->subGridPositionAt(gridLine: k); |
2924 | QVector3D lastPoint(r * qCos(v: degrees), linePosY + tempLineOffset, r * qSin(v: degrees)); |
2925 | for (int i = 1; i <= polarRoundness; i++) { |
2926 | degrees = doublePi * i / polarRoundness; |
2927 | const float xPos = qCos(v: degrees); |
2928 | const float zPos = qSin(v: degrees); |
2929 | |
2930 | const QVector3D pos(r * xPos, linePosY + tempLineOffset, r * zPos); |
2931 | *subdata++ = lastPoint; |
2932 | *subdata++ = pos; |
2933 | lastPoint = pos; |
2934 | } |
2935 | } |
2936 | |
2937 | for (int k = 0; k < gridLineCountZ; k++) { |
2938 | float degrees = 0.0f; |
2939 | const float r = (m_polarRadius) *valueAxisZ->gridPositionAt(gridLine: k); |
2940 | QVector3D lastPoint(r * qCos(v: degrees), linePosY + tempLineOffset, r * qSin(v: degrees)); |
2941 | |
2942 | for (int i = 1; i <= polarRoundness; i++) { |
2943 | degrees = doublePi * i / polarRoundness; |
2944 | const float xPos = qCos(v: degrees); |
2945 | const float zPos = qSin(v: degrees); |
2946 | |
2947 | const QVector3D pos(r * xPos, linePosY + tempLineOffset, r * zPos); |
2948 | *data++ = lastPoint; |
2949 | *data++ = pos; |
2950 | lastPoint = pos; |
2951 | } |
2952 | } |
2953 | } |
2954 | |
2955 | // Side vertical line |
2956 | linePosX = -backgroundScale.x(); |
2957 | linePosY = 0.0f; |
2958 | rotation = QVector3D(0.0f, 90.0f, 0.0f); |
2959 | |
2960 | float y0 = -backgroundScale.y(); |
2961 | float y1 = backgroundScale.y(); |
2962 | |
2963 | x0 = -backgroundScale.x(); |
2964 | x1 = -backgroundScale.x(); |
2965 | |
2966 | tempLineOffset = lineOffset; |
2967 | |
2968 | if (xFlipped) { |
2969 | linePosX *= -1.0f; |
2970 | rotation.setY(-90.0f); |
2971 | tempLineOffset *= -1.0f; |
2972 | x0 *= -1.0f; |
2973 | x1 *= -1.0f; |
2974 | } |
2975 | lineRotation = Utils::calculateRotation(xyzRotations: rotation); |
2976 | if (m_hasVerticalSegmentLine) { |
2977 | for (int i = 0; i < subGridLineCountZ; i++) { |
2978 | if (axisZ()->type() == QAbstract3DAxis::AxisType::Value) { |
2979 | linePosZ = static_cast<QValue3DAxis *>(axisZ())->subGridPositionAt(gridLine: i) * scale * 2.0f |
2980 | - scale; |
2981 | } |
2982 | |
2983 | *subdata++ = QVector3D(x0 + tempLineOffset, y0, linePosZ); |
2984 | *subdata++ = QVector3D(x1 + tempLineOffset, y1, linePosZ); |
2985 | } |
2986 | |
2987 | for (int i = 0; i < gridLineCountZ; i++) { |
2988 | if (axisZ()->type() == QAbstract3DAxis::AxisType::Value) { |
2989 | linePosZ = static_cast<QValue3DAxis *>(axisZ())->gridPositionAt(gridLine: i) * scale * 2.0f |
2990 | - scale; |
2991 | } |
2992 | |
2993 | *data++ = QVector3D(x0 + tempLineOffset, y0, linePosZ); |
2994 | *data++ = QVector3D(x1 + tempLineOffset, y1, linePosZ); |
2995 | } |
2996 | } |
2997 | |
2998 | // Side horizontal line |
2999 | scale = m_scaleWithBackground.y(); |
3000 | rotation = QVector3D(180.0f, -90.0f, 0.0f); |
3001 | |
3002 | float z0 = backgroundScale.z(); |
3003 | float z1 = -backgroundScale.z(); |
3004 | |
3005 | x0 = -backgroundScale.x(); |
3006 | x1 = -backgroundScale.x(); |
3007 | |
3008 | tempLineOffset = lineOffset; |
3009 | |
3010 | if (xFlipped) { |
3011 | rotation.setY(90.0f); |
3012 | tempLineOffset *= -1.0f; |
3013 | x0 *= -1.0f; |
3014 | x1 *= -1.0f; |
3015 | } |
3016 | lineRotation = Utils::calculateRotation(xyzRotations: rotation); |
3017 | for (int i = 0; i < gridLineCountY; i++) { |
3018 | if (axisY()->type() == QAbstract3DAxis::AxisType::Value) { |
3019 | linePosY = static_cast<QValue3DAxis *>(axisY())->gridPositionAt(gridLine: i) * scale * 2.0f |
3020 | - scale; |
3021 | } else if (axisY()->type() == QAbstract3DAxis::AxisType::Category) { |
3022 | linePosY = calculateCategoryGridLinePosition(axis: axisY(), index: i); |
3023 | } |
3024 | |
3025 | *data++ = QVector3D(x0 + tempLineOffset, linePosY, z0); |
3026 | *data++ = QVector3D(x1 + tempLineOffset, linePosY, z1); |
3027 | } |
3028 | |
3029 | for (int i = 0; i < subGridLineCountY; i++) { |
3030 | if (axisY()->type() == QAbstract3DAxis::AxisType::Value) { |
3031 | linePosY = static_cast<QValue3DAxis *>(axisY())->subGridPositionAt(gridLine: i) * scale * 2.0f |
3032 | - scale; |
3033 | } else if (axisY()->type() == QAbstract3DAxis::AxisType::Category) { |
3034 | linePosY = calculateCategoryGridLinePosition(axis: axisY(), index: i); |
3035 | } |
3036 | |
3037 | *subdata++ = QVector3D(x0 + tempLineOffset, linePosY, z0); |
3038 | *subdata++ = QVector3D(x1 + tempLineOffset, linePosY, z1); |
3039 | } |
3040 | |
3041 | // Floor vertical line |
3042 | linePosY = -backgroundScale.y(); |
3043 | rotation = QVector3D(-90.0f, 90.0f, 0.0f); |
3044 | |
3045 | tempLineOffset = lineOffset; |
3046 | z0 = backgroundScale.z(); |
3047 | z1 = -backgroundScale.z(); |
3048 | |
3049 | if (yFlipped) { |
3050 | linePosY *= -1.0f; |
3051 | rotation.setZ(180.0f); |
3052 | tempLineOffset *= -1.0f; |
3053 | } |
3054 | scale = m_scaleWithBackground.x(); |
3055 | linePosY *= m_horizontalFlipFactor; |
3056 | tempLineOffset *= m_horizontalFlipFactor; |
3057 | |
3058 | if (!usePolar) { |
3059 | for (int i = 0; i < subGridLineCountX; i++) { |
3060 | if (axisX()->type() == QAbstract3DAxis::AxisType::Value) { |
3061 | linePosX = static_cast<QValue3DAxis *>(axisX())->subGridPositionAt(gridLine: i) * scale * 2.0f |
3062 | - scale; |
3063 | } else if (axisX()->type() == QAbstract3DAxis::AxisType::Category) { |
3064 | linePosX = calculateCategoryGridLinePosition(axis: axisX(), index: i); |
3065 | linePosY = calculateCategoryGridLinePosition(axis: axisY(), index: i); |
3066 | } |
3067 | |
3068 | *subdata++ = QVector3D(linePosX, linePosY + tempLineOffset, z0); |
3069 | *subdata++ = QVector3D(linePosX, linePosY + tempLineOffset, z1); |
3070 | } |
3071 | |
3072 | for (int i = 0; i < gridLineCountX; i++) { |
3073 | if (axisX()->type() == QAbstract3DAxis::AxisType::Value) { |
3074 | linePosX = static_cast<QValue3DAxis *>(axisX())->gridPositionAt(gridLine: i) * scale * 2.0f |
3075 | - scale; |
3076 | } else if (axisX()->type() == QAbstract3DAxis::AxisType::Category) { |
3077 | linePosX = calculateCategoryGridLinePosition(axis: axisX(), index: i); |
3078 | linePosY = calculateCategoryGridLinePosition(axis: axisY(), index: i); |
3079 | } |
3080 | |
3081 | *data++ = QVector3D(linePosX, linePosY + tempLineOffset, backgroundScale.z()); |
3082 | *data++ = QVector3D(linePosX, linePosY + tempLineOffset, -backgroundScale.z()); |
3083 | } |
3084 | } else { |
3085 | auto valueAxisX = static_cast<QValue3DAxis *>(axisX()); |
3086 | const QVector3D center(0.0f, linePosY + tempLineOffset, 0.0f); |
3087 | const float halfRatio = ((m_polarRadius) + (m_labelMargin * 0.5f)); |
3088 | |
3089 | for (int i = 0; i < subGridLineCountX; i++) { |
3090 | float angle = valueAxisX->subGridPositionAt(gridLine: i) * 360.0f - rotationOffset; |
3091 | float posX = halfRatio * qCos(v: qDegreesToRadians(degrees: angle)); |
3092 | float posZ = halfRatio * qSin(v: qDegreesToRadians(degrees: angle)); |
3093 | *subdata++ = center; |
3094 | *subdata++ = QVector3D(posX, linePosY + tempLineOffset, posZ); |
3095 | } |
3096 | |
3097 | for (int i = 0; i < gridLineCountX - 1; i++) { |
3098 | float angle = valueAxisX->gridPositionAt(gridLine: i) * 360.0f - rotationOffset; |
3099 | float posX = halfRatio * qCos(v: qDegreesToRadians(degrees: angle)); |
3100 | float posZ = halfRatio * qSin(v: qDegreesToRadians(degrees: angle)); |
3101 | *data++ = center; |
3102 | *data++ = QVector3D(posX, linePosY + tempLineOffset, posZ); |
3103 | } |
3104 | } |
3105 | |
3106 | // Back horizontal line |
3107 | linePosX = 0.0f; |
3108 | rotation = QVector3D(0.0f, 0.0f, 0.0f); |
3109 | |
3110 | x0 = -backgroundScale.x(); |
3111 | x1 = backgroundScale.x(); |
3112 | |
3113 | z0 = -backgroundScale.z(); |
3114 | z1 = -backgroundScale.z(); |
3115 | |
3116 | tempLineOffset = lineOffset; |
3117 | float tempBackOffsetAdjustment = backOffsetAdjustment; |
3118 | |
3119 | if (zFlipped) { |
3120 | rotation.setX(180.0f); |
3121 | z0 *= -1.0f; |
3122 | z1 *= -1.0f; |
3123 | tempLineOffset *= -1.0f; |
3124 | tempBackOffsetAdjustment *= -1.0f; |
3125 | } |
3126 | lineRotation = Utils::calculateRotation(xyzRotations: rotation); |
3127 | scale = m_scaleWithBackground.y(); |
3128 | for (int i = 0; i < subGridLineCountY; i++) { |
3129 | if (axisY()->type() == QAbstract3DAxis::AxisType::Value) { |
3130 | linePosY = static_cast<QValue3DAxis *>(axisY())->subGridPositionAt(gridLine: i) * scale * 2.0f |
3131 | - scale; |
3132 | } else if (axisY()->type() == QAbstract3DAxis::AxisType::Category) { |
3133 | linePosY = calculateCategoryGridLinePosition(axis: axisY(), index: i); |
3134 | } |
3135 | *subdata++ = QVector3D(x0, linePosY, z0 + tempLineOffset + tempBackOffsetAdjustment); |
3136 | *subdata++ = QVector3D(x1, linePosY, z1 + tempLineOffset + tempBackOffsetAdjustment); |
3137 | } |
3138 | |
3139 | for (int i = 0; i < gridLineCountY; i++) { |
3140 | if (axisY()->type() == QAbstract3DAxis::AxisType::Value) { |
3141 | linePosY = static_cast<QValue3DAxis *>(axisY())->gridPositionAt(gridLine: i) * scale * 2.0f |
3142 | - scale; |
3143 | } else if (axisY()->type() == QAbstract3DAxis::AxisType::Category) { |
3144 | linePosY = calculateCategoryGridLinePosition(axis: axisY(), index: i); |
3145 | } |
3146 | *data++ = QVector3D(x0, linePosY, z0 + tempLineOffset + tempBackOffsetAdjustment); |
3147 | *data++ = QVector3D(x1, linePosY, z1 + tempLineOffset + tempBackOffsetAdjustment); |
3148 | } |
3149 | |
3150 | // Back vertical line |
3151 | scale = m_scaleWithBackground.x(); |
3152 | rotation = QVector3D(0.0f, 0.0f, 0.0f); |
3153 | |
3154 | y0 = -backgroundScale.y(); |
3155 | y1 = backgroundScale.y(); |
3156 | |
3157 | z0 = -backgroundScale.z(); |
3158 | z1 = -backgroundScale.z(); |
3159 | |
3160 | tempLineOffset = lineOffset; |
3161 | tempBackOffsetAdjustment = backOffsetAdjustment; |
3162 | |
3163 | if (zFlipped) { |
3164 | rotation.setY(180.0f); |
3165 | z0 *= -1.0f; |
3166 | z1 *= -1.0f; |
3167 | tempLineOffset *= -1.0f; |
3168 | tempBackOffsetAdjustment *= -1.0f; |
3169 | } |
3170 | lineRotation = Utils::calculateRotation(xyzRotations: rotation); |
3171 | if (m_hasVerticalSegmentLine) { |
3172 | for (int i = 0; i < gridLineCountX; i++) { |
3173 | if (axisX()->type() == QAbstract3DAxis::AxisType::Value) { |
3174 | linePosX = static_cast<QValue3DAxis *>(axisX())->gridPositionAt(gridLine: i) * scale * 2.0f |
3175 | - scale; |
3176 | } |
3177 | *data++ = QVector3D(linePosX, y0, z0 + tempLineOffset + tempBackOffsetAdjustment); |
3178 | *data++ = QVector3D(linePosX, y1, z1 + tempLineOffset + tempBackOffsetAdjustment); |
3179 | } |
3180 | |
3181 | for (int i = 0; i < subGridLineCountX; i++) { |
3182 | if (axisX()->type() == QAbstract3DAxis::AxisType::Value) { |
3183 | linePosX = static_cast<QValue3DAxis *>(axisX())->subGridPositionAt(gridLine: i) * scale * 2.0f |
3184 | - scale; |
3185 | } |
3186 | *subdata++ = QVector3D(linePosX, y0, z0 + tempLineOffset + tempBackOffsetAdjustment); |
3187 | *subdata++ = QVector3D(linePosX, y1, z1 + tempLineOffset + tempBackOffsetAdjustment); |
3188 | } |
3189 | } |
3190 | QQuick3DGeometry *gridGeometry = m_gridGeometryModel->geometry(); |
3191 | gridGeometry->setVertexData(vertices); |
3192 | gridGeometry->update(); |
3193 | QQuick3DGeometry *subgridGeometry = m_subgridGeometryModel->geometry(); |
3194 | subgridGeometry->setVertexData(subvertices); |
3195 | subgridGeometry->update(); |
3196 | m_gridUpdate = false; |
3197 | } |
3198 | |
3199 | void QQuickGraphsItem::updateGridLineType() |
3200 | { |
3201 | const int textureSize = 4096; |
3202 | QVector<QVector4D> grid(textureSize * 2, QVector4D(0, 0, 0, 0)); |
3203 | QQmlListReference materialsRef(m_background, "materials"); |
3204 | QQuick3DCustomMaterial *bgMat; |
3205 | if (!materialsRef.size()) { |
3206 | bgMat = createQmlCustomMaterial(QStringLiteral(":/materials/BackgroundMaterial")); |
3207 | bgMat->setParent(m_background); |
3208 | materialsRef.append(bgMat); |
3209 | } else { |
3210 | bgMat = static_cast<QQuick3DCustomMaterial *>(materialsRef.at(0)); |
3211 | } |
3212 | |
3213 | QVariant texAsVariant = bgMat->property(name: "gridTex"); |
3214 | auto *texinput = texAsVariant.value<QQuick3DShaderUtilsTextureInput *>(); |
3215 | QQuick3DTexture *texMap = texinput->texture(); |
3216 | QQuick3DTextureData *mapData = nullptr; |
3217 | if (!texMap) { |
3218 | texMap = new QQuick3DTexture(); |
3219 | texMap->setParent(this); |
3220 | texMap->setHorizontalTiling(QQuick3DTexture::MirroredRepeat); |
3221 | texMap->setVerticalTiling(QQuick3DTexture::MirroredRepeat); |
3222 | texMap->setMinFilter(QQuick3DTexture::Linear); |
3223 | texMap->setMagFilter(QQuick3DTexture::Nearest); |
3224 | mapData = new QQuick3DTextureData(); |
3225 | mapData->setSize(QSize(textureSize, 2)); |
3226 | mapData->setFormat(QQuick3DTextureData::RGBA32F); |
3227 | mapData->setParent(texMap); |
3228 | mapData->setParentItem(texMap); |
3229 | } else { |
3230 | mapData = texMap->textureData(); |
3231 | } |
3232 | |
3233 | QVector<qsizetype> lineCounts(6); |
3234 | gridLineCountHelper(axis: axisX(), lineCount&: lineCounts[0], sublineCount&: lineCounts[3]); |
3235 | gridLineCountHelper(axis: axisY(), lineCount&: lineCounts[1], sublineCount&: lineCounts[4]); |
3236 | gridLineCountHelper(axis: axisZ(), lineCount&: lineCounts[2], sublineCount&: lineCounts[5]); |
3237 | |
3238 | float baseWidth = 100; |
3239 | QVector<int> lineWidths(3); |
3240 | lineWidths[0] = baseWidth / m_scaleWithBackground.x(); |
3241 | lineWidths[1] = baseWidth / m_scaleWithBackground.y(); |
3242 | lineWidths[2] = baseWidth / m_scaleWithBackground.z(); |
3243 | |
3244 | QVector<QVector4D> axisMask = {QVector4D(1, 0, 0, 1), |
3245 | QVector4D(0, 1, 0, 1), |
3246 | QVector4D(0, 0, 1, 1)}; |
3247 | |
3248 | bgMat->setProperty(name: "scale", value: m_scaleWithBackground); |
3249 | bgMat->setProperty(name: "polar", value: isPolar()); |
3250 | bool xCat = axisX()->type() == QAbstract3DAxis::AxisType::Category; |
3251 | bool zCat = axisZ()->type() == QAbstract3DAxis::AxisType::Category; |
3252 | bgMat->setProperty(name: "xCategory", value: xCat); |
3253 | bgMat->setProperty(name: "zCategory", value: zCat); |
3254 | bgMat->setProperty(name: "margin", value: backgroundScaleMargin()); |
3255 | |
3256 | for (int i = 0; i < lineCounts.size(); i++) { |
3257 | qsizetype lineCount = lineCounts[i]; |
3258 | int axis = i % 3; |
3259 | int subGridOffset = textureSize * float(i > 2); |
3260 | QVector4D mask = axisMask.at(i: axis); |
3261 | QVector4D revMask = QVector4D(1, 1, 1, 1) - mask; |
3262 | for (int j = 0; j < lineCount; j++) { |
3263 | float linePos = -1; |
3264 | switch (i) { |
3265 | case 0: |
3266 | if (!xCat) |
3267 | linePos = static_cast<QValue3DAxis *>(axisX())->gridPositionAt(gridLine: j); |
3268 | else |
3269 | linePos = float(j) / float(lineCount); |
3270 | break; |
3271 | case 1: |
3272 | if (axisY()->type() == QAbstract3DAxis::AxisType::Value) |
3273 | linePos = static_cast<QValue3DAxis *>(axisY())->gridPositionAt(gridLine: j); |
3274 | else |
3275 | linePos = float(j) / float(lineCount); |
3276 | break; |
3277 | case 2: |
3278 | if (!zCat) |
3279 | linePos = static_cast<QValue3DAxis *>(axisZ())->gridPositionAt(gridLine: j); |
3280 | else |
3281 | linePos = float(j) / float(lineCount); |
3282 | break; |
3283 | case 3: |
3284 | if (!xCat) |
3285 | linePos = static_cast<QValue3DAxis *>(axisX())->subGridPositionAt(gridLine: j); |
3286 | break; |
3287 | case 4: |
3288 | if (axisY()->type() == QAbstract3DAxis::AxisType::Value) |
3289 | linePos = static_cast<QValue3DAxis *>(axisY())->subGridPositionAt(gridLine: j); |
3290 | break; |
3291 | case 5: |
3292 | if (!zCat) |
3293 | linePos = static_cast<QValue3DAxis *>(axisZ())->subGridPositionAt(gridLine: j); |
3294 | break; |
3295 | } |
3296 | if (linePos < 0) |
3297 | continue; |
3298 | |
3299 | int index = ((textureSize - 1) * linePos) + subGridOffset; |
3300 | for (int k = 0; k < lineWidths[axis]; k++) { |
3301 | float nextIdx = qMin(a: index + k, b: textureSize * 2 - 1); |
3302 | float prevIdx = qMax(a: index - k, b: 0); |
3303 | |
3304 | float dist = float(lineWidths[axis] - k) / float(lineWidths[axis]); |
3305 | float curDist = (grid[nextIdx] * mask).toVector3D().length(); |
3306 | |
3307 | if (dist > curDist) |
3308 | grid[nextIdx] = grid[nextIdx] * revMask + dist * mask; |
3309 | |
3310 | curDist = (grid[prevIdx] * mask).toVector3D().length(); |
3311 | if (dist > curDist) |
3312 | grid[prevIdx] = grid[prevIdx] * revMask + dist * mask; |
3313 | } |
3314 | } |
3315 | } |
3316 | |
3317 | QByteArray data = QByteArray(reinterpret_cast<char *>(grid.data()), |
3318 | grid.size() * sizeof(QVector4D)); |
3319 | mapData->setTextureData(data); |
3320 | texMap->setTextureData(mapData); |
3321 | texinput->setTexture(texMap); |
3322 | m_gridUpdate = false; |
3323 | } |
3324 | |
3325 | float QQuickGraphsItem::fontScaleFactor(float pointSize) |
3326 | { |
3327 | return 0.00007f + pointSize / (500000.0f * pointSize); |
3328 | } |
3329 | |
3330 | float QQuickGraphsItem::labelAdjustment(float width) |
3331 | { |
3332 | float a = -2.43761e-13f; |
3333 | float b = 4.23579e-10f; |
3334 | float c = 0.00414881f; |
3335 | |
3336 | float factor = a * qPow(x: width, y: 3) + b * qPow(x: width, y: 2) + c; |
3337 | #if defined(Q_OS_WIN) |
3338 | factor *= .8f; |
3339 | #endif |
3340 | float ret = width * .5f * factor; |
3341 | return ret; |
3342 | } |
3343 | |
3344 | void QQuickGraphsItem::gridLineCountHelper(QAbstract3DAxis *axis, qsizetype &lineCount, qsizetype &sublineCount) |
3345 | { |
3346 | if (axis->type() == QAbstract3DAxis::AxisType::Value) { |
3347 | auto valueAxis = static_cast<QValue3DAxis *>(axis); |
3348 | lineCount = valueAxis->gridSize(); |
3349 | sublineCount = valueAxis->subGridSize(); |
3350 | } else if (axis->type() == QAbstract3DAxis::AxisType::Category) { |
3351 | lineCount = axis->labels().size(); |
3352 | sublineCount = 0; |
3353 | } |
3354 | } |
3355 | |
3356 | QVector3D QQuickGraphsItem::graphPosToAbsolute(QVector3D position) |
3357 | { |
3358 | QVector3D pos = position; |
3359 | const int maxX = axisX()->max(); |
3360 | const int minX = axisX()->min(); |
3361 | const int maxY = axisY()->max(); |
3362 | const int minY = axisY()->min(); |
3363 | const int maxZ = axisZ()->max(); |
3364 | const int minZ = axisZ()->min(); |
3365 | const QVector3D adjustment = m_scaleWithBackground * QVector3D(1.0f, 1.0f, -1.0f); |
3366 | |
3367 | float xNormalizer = maxX - minX; |
3368 | float xPos = (pos.x() - minX) / xNormalizer; |
3369 | float yNormalizer = maxY - minY; |
3370 | float yPos = (pos.y() - minY) / yNormalizer; |
3371 | float zNormalizer = maxZ - minZ; |
3372 | float zPos = (pos.z() - minZ) / zNormalizer; |
3373 | pos = QVector3D(xPos, yPos, zPos); |
3374 | if (isPolar()) { |
3375 | float angle = xPos * M_PI * 2.0f; |
3376 | float radius = zPos; |
3377 | xPos = radius * qSin(v: angle) * 1.0f; |
3378 | zPos = -(radius * qCos(v: angle)) * 1.0f; |
3379 | yPos = yPos * adjustment.y() * 2.0f - adjustment.y(); |
3380 | pos = QVector3D(xPos, yPos, zPos); |
3381 | } else { |
3382 | pos = pos * adjustment * 2.0f - adjustment; |
3383 | } |
3384 | return pos; |
3385 | } |
3386 | |
3387 | void QQuickGraphsItem::updateLabels() |
3388 | { |
3389 | auto labels = axisX()->labels(); |
3390 | qsizetype labelCount = labels.size(); |
3391 | float labelAutoAngle = m_labelMargin >= 0? axisX()->labelAutoAngle() : 0; |
3392 | float labelAngleFraction = labelAutoAngle / 90.0f; |
3393 | float fractionCamX = m_xRotation * labelAngleFraction; |
3394 | float fractionCamY = m_yRotation * labelAngleFraction; |
3395 | |
3396 | QVector3D labelRotation = QVector3D(0.0f, 0.0f, 0.0f); |
3397 | |
3398 | float xPos = 0.0f; |
3399 | float yPos = 0.0f; |
3400 | float zPos = 0.0f; |
3401 | |
3402 | const bool xFlipped = isXFlipped(); |
3403 | const bool yFlipped = isYFlipped(); |
3404 | const bool zFlipped = isZFlipped(); |
3405 | |
3406 | auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin; |
3407 | |
3408 | if (labelAutoAngle == 0.0f) { |
3409 | labelRotation = QVector3D(-90.0f, 90.0f, 0.0f); |
3410 | if (xFlipped) |
3411 | labelRotation.setY(-90.0f); |
3412 | if (yFlipped) { |
3413 | if (xFlipped) |
3414 | labelRotation.setY(-90.0f); |
3415 | else |
3416 | labelRotation.setY(90.0f); |
3417 | labelRotation.setX(90.0f); |
3418 | } |
3419 | } else { |
3420 | if (xFlipped) |
3421 | labelRotation.setY(-90.0f); |
3422 | else |
3423 | labelRotation.setY(90.0f); |
3424 | if (yFlipped) { |
3425 | if (zFlipped) { |
3426 | if (xFlipped) { |
3427 | labelRotation.setX(90.0f |
3428 | - (2.0f * labelAutoAngle - fractionCamX) |
3429 | * (labelAutoAngle + fractionCamY) / labelAutoAngle); |
3430 | labelRotation.setZ(-labelAutoAngle - fractionCamY); |
3431 | } else { |
3432 | labelRotation.setX(90.0f |
3433 | - (2.0f * labelAutoAngle + fractionCamX) |
3434 | * (labelAutoAngle + fractionCamY) / labelAutoAngle); |
3435 | labelRotation.setZ(labelAutoAngle + fractionCamY); |
3436 | } |
3437 | } else { |
3438 | if (xFlipped) { |
3439 | labelRotation.setX( |
3440 | 90.0f + fractionCamX * -(labelAutoAngle + fractionCamY) / labelAutoAngle); |
3441 | labelRotation.setZ(labelAutoAngle + fractionCamY); |
3442 | } else { |
3443 | labelRotation.setX( |
3444 | 90.0f - fractionCamX * (-labelAutoAngle - fractionCamY) / labelAutoAngle); |
3445 | labelRotation.setZ(-labelAutoAngle - fractionCamY); |
3446 | } |
3447 | } |
3448 | } else { |
3449 | if (zFlipped) { |
3450 | if (xFlipped) { |
3451 | labelRotation.setX(-90.0f |
3452 | + (2.0f * labelAutoAngle - fractionCamX) |
3453 | * (labelAutoAngle - fractionCamY) / labelAutoAngle); |
3454 | labelRotation.setZ(labelAutoAngle - fractionCamY); |
3455 | } else { |
3456 | labelRotation.setX(-90.0f |
3457 | + (2.0f * labelAutoAngle + fractionCamX) |
3458 | * (labelAutoAngle - fractionCamY) / labelAutoAngle); |
3459 | labelRotation.setZ(-labelAutoAngle + fractionCamY); |
3460 | } |
3461 | } else { |
3462 | if (xFlipped) { |
3463 | labelRotation.setX( |
3464 | -90.0f - fractionCamX * (-labelAutoAngle + fractionCamY) / labelAutoAngle); |
3465 | labelRotation.setZ(-labelAutoAngle + fractionCamY); |
3466 | } else { |
3467 | labelRotation.setX( |
3468 | -90.0f + fractionCamX * -(labelAutoAngle - fractionCamY) / labelAutoAngle); |
3469 | labelRotation.setZ(labelAutoAngle - fractionCamY); |
3470 | } |
3471 | } |
3472 | } |
3473 | } |
3474 | if (isPolar()) |
3475 | labelRotation.setY(0.0f); |
3476 | QQuaternion totalRotation = Utils::calculateRotation(xyzRotations: labelRotation); |
3477 | |
3478 | float scale = backgroundScale.x() - m_backgroundScaleMargin.x(); |
3479 | |
3480 | float pointSize = theme()->labelFont().pointSizeF(); |
3481 | |
3482 | float textPadding = pointSize * .5f; |
3483 | |
3484 | float labelsMaxWidth = float(findLabelsMaxWidth(labels: axisX()->labels())) + textPadding; |
3485 | QFontMetrics fm(theme()->labelFont()); |
3486 | float labelHeight = fm.height() + textPadding; |
3487 | |
3488 | float scaleFactor = fontScaleFactor(pointSize) * pointSize; |
3489 | float fontRatio = labelsMaxWidth / labelHeight; |
3490 | m_fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f); |
3491 | float adjustment = labelAdjustment(width: labelsMaxWidth); |
3492 | zPos = backgroundScale.z() + adjustment + m_labelMargin; |
3493 | |
3494 | adjustment *= qAbs(t: qSin(v: qDegreesToRadians(degrees: labelRotation.z()))); |
3495 | const float labelDepthMargin = 0.03f; //margin to prevent z-fighting |
3496 | yPos = backgroundScale.y() + adjustment - labelDepthMargin; |
3497 | |
3498 | float yOffset = -0.1f; |
3499 | if (!yFlipped) { |
3500 | yPos *= -1.0f; |
3501 | yOffset *= -1.0f; |
3502 | } |
3503 | |
3504 | if (zFlipped) |
3505 | zPos *= -1.0f; |
3506 | |
3507 | auto labelTrans = QVector3D(0.0f, yPos, zPos); |
3508 | float angularLabelZPos = 0.0f; |
3509 | |
3510 | const float angularAdjustment{1.1f}; |
3511 | if (axisX()->type() == QAbstract3DAxis::AxisType::Value) { |
3512 | auto valueAxisX = static_cast<QValue3DAxis *>(axisX()); |
3513 | for (int i = 0; i < repeaterX()->count(); i++) { |
3514 | if (labelCount <= i) |
3515 | break; |
3516 | auto obj = static_cast<QQuick3DNode *>(repeaterX()->objectAt(index: i)); |
3517 | if (isPolar()) { |
3518 | if (i == repeaterX()->count() - 1) { |
3519 | obj->setVisible(false); |
3520 | break; |
3521 | } |
3522 | float rad = qDegreesToRadians(degrees: valueAxisX->labelPositionAt(index: i) * 360.0f); |
3523 | labelTrans.setX((-qSin(v: rad) * -scale + qSin(v: rad) * m_labelMargin * m_polarRadius) |
3524 | * angularAdjustment); |
3525 | labelTrans.setY(yPos + yOffset); |
3526 | labelTrans.setZ((qCos(v: rad) * -scale - qCos(v: rad) * m_labelMargin * m_polarRadius) |
3527 | * angularAdjustment); |
3528 | if (i == 0) { |
3529 | angularLabelZPos = labelTrans.z(); |
3530 | rad = qDegreesToRadians(degrees: valueAxisX->labelPositionAt(index: i) * 360.0f); |
3531 | labelTrans.setX( |
3532 | (-qSin(v: rad) * -scale + qSin(v: rad) * m_labelMargin * m_polarRadius)); |
3533 | labelTrans.setY(yPos + yOffset); |
3534 | labelTrans.setZ( |
3535 | (qCos(v: rad) * -scale - qCos(v: rad) * m_labelMargin * m_polarRadius)); |
3536 | } |
3537 | } else { |
3538 | labelTrans.setX(valueAxisX->labelPositionAt(index: i) * scale * 2.0f - scale); |
3539 | } |
3540 | obj->setObjectName(QStringLiteral("ElementAxisXLabel")); |
3541 | obj->setScale(m_fontScaled); |
3542 | obj->setPosition(labelTrans); |
3543 | obj->setRotation(totalRotation); |
3544 | obj->setProperty(name: "labelText", value: labels[i]); |
3545 | obj->setProperty(name: "labelWidth", value: labelsMaxWidth); |
3546 | obj->setProperty(name: "labelHeight", value: labelHeight); |
3547 | } |
3548 | } else if (axisX()->type() == QAbstract3DAxis::AxisType::Category) { |
3549 | for (int i = 0; i < repeaterX()->count(); i++) { |
3550 | if (labelCount <= i) |
3551 | break; |
3552 | labelTrans = calculateCategoryLabelPosition(axis: axisX(), labelPosition: labelTrans, index: i); |
3553 | auto obj = static_cast<QQuick3DNode *>(repeaterX()->objectAt(index: i)); |
3554 | obj->setObjectName(QStringLiteral("ElementAxisXLabel")); |
3555 | obj->setScale(m_fontScaled); |
3556 | obj->setPosition(labelTrans); |
3557 | obj->setRotation(totalRotation); |
3558 | obj->setProperty(name: "labelText", value: labels[i]); |
3559 | obj->setProperty(name: "labelWidth", value: labelsMaxWidth); |
3560 | obj->setProperty(name: "labelHeight", value: labelHeight); |
3561 | } |
3562 | } |
3563 | |
3564 | float x = labelTrans.x(); |
3565 | labelTrans.setX(0.0f); |
3566 | updateXTitle(labelRotation, labelTrans, totalRotation, labelsMaxWidth, scale: m_fontScaled); |
3567 | if (isPolar()) { |
3568 | m_titleLabelX->setZ(angularLabelZPos - m_labelMargin * 2.0f); |
3569 | m_titleLabelX->setRotation(totalRotation); |
3570 | } |
3571 | labelTrans.setX(x); |
3572 | |
3573 | labels = axisY()->labels(); |
3574 | labelCount = labels.size(); |
3575 | labelAutoAngle = m_labelMargin >= 0 ? axisY()->labelAutoAngle() : 0; |
3576 | labelAngleFraction = labelAutoAngle / 90.0f; |
3577 | fractionCamX = m_xRotation * labelAngleFraction; |
3578 | fractionCamY = m_yRotation * labelAngleFraction; |
3579 | |
3580 | QVector3D sideLabelRotation(0.0f, -90.0f, 0.0f); |
3581 | QVector3D backLabelRotation(0.0f, 0.0f, 0.0f); |
3582 | |
3583 | if (labelAutoAngle == 0.0f) { |
3584 | if (!xFlipped) |
3585 | sideLabelRotation.setY(90.0f); |
3586 | if (zFlipped) |
3587 | backLabelRotation.setY(180.f); |
3588 | } else { |
3589 | // Orient side labels somewhat towards the camera |
3590 | if (xFlipped) { |
3591 | if (zFlipped) |
3592 | backLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX); |
3593 | else |
3594 | backLabelRotation.setY(-fractionCamX); |
3595 | sideLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX); |
3596 | } else { |
3597 | if (zFlipped) |
3598 | backLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX); |
3599 | else |
3600 | backLabelRotation.setY(-fractionCamX); |
3601 | sideLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX); |
3602 | } |
3603 | } |
3604 | |
3605 | backLabelRotation.setX(-fractionCamY); |
3606 | sideLabelRotation.setX(-fractionCamY); |
3607 | |
3608 | totalRotation = Utils::calculateRotation(xyzRotations: sideLabelRotation); |
3609 | scale = backgroundScale.y() - m_backgroundScaleMargin.y(); |
3610 | labelsMaxWidth = float(findLabelsMaxWidth(labels: axisY()->labels())) + textPadding; |
3611 | fontRatio = labelsMaxWidth / labelHeight; |
3612 | m_fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f); |
3613 | |
3614 | xPos = backgroundScale.x() - labelDepthMargin; |
3615 | if (!xFlipped) |
3616 | xPos *= -1.0f; |
3617 | labelTrans.setX(xPos); |
3618 | |
3619 | adjustment = labelAdjustment(width: labelsMaxWidth); |
3620 | zPos = backgroundScale.z() + adjustment + m_labelMargin; |
3621 | if (zFlipped) |
3622 | zPos *= -1.0f; |
3623 | labelTrans.setZ(zPos); |
3624 | |
3625 | for (int i = 0; i < repeaterY()->count() / 2; i++) { |
3626 | if (labelCount <= i) |
3627 | break; |
3628 | auto obj = static_cast<QQuick3DNode *>(repeaterY()->objectAt(index: i)); |
3629 | labelTrans.setY(static_cast<QValue3DAxis *>(axisY())->labelPositionAt(index: i) * scale * 2.0f |
3630 | - scale); |
3631 | obj->setObjectName(QStringLiteral("ElementAxisYLabel")); |
3632 | obj->setScale(m_fontScaled); |
3633 | obj->setPosition(labelTrans); |
3634 | obj->setRotation(totalRotation); |
3635 | obj->setProperty(name: "labelText", value: labels[i]); |
3636 | obj->setProperty(name: "labelWidth", value: labelsMaxWidth); |
3637 | obj->setProperty(name: "labelHeight", value: labelHeight); |
3638 | } |
3639 | |
3640 | auto sideLabelTrans = labelTrans; |
3641 | auto totalSideLabelRotation = totalRotation; |
3642 | |
3643 | labels = axisZ()->labels(); |
3644 | labelCount = labels.size(); |
3645 | labelAutoAngle = m_labelMargin >= 0 ? axisZ()->labelAutoAngle() : 0; |
3646 | labelAngleFraction = labelAutoAngle / 90.0f; |
3647 | fractionCamX = m_xRotation * labelAngleFraction; |
3648 | fractionCamY = m_yRotation * labelAngleFraction; |
3649 | |
3650 | if (labelAutoAngle == 0.0f) { |
3651 | labelRotation = QVector3D(90.0f, 0.0f, 0.0f); |
3652 | if (zFlipped) |
3653 | labelRotation.setY(180.0f); |
3654 | if (yFlipped) { |
3655 | if (zFlipped) |
3656 | labelRotation.setY(180.0f); |
3657 | else |
3658 | labelRotation.setY(0.0f); |
3659 | labelRotation.setX(90.0f); |
3660 | } else { |
3661 | labelRotation.setX(-90.0f); |
3662 | } |
3663 | } else { |
3664 | if (zFlipped) |
3665 | labelRotation.setY(180.0f); |
3666 | else |
3667 | labelRotation.setY(0.0f); |
3668 | if (yFlipped) { |
3669 | if (zFlipped) { |
3670 | if (xFlipped) { |
3671 | labelRotation.setX(90.0f |
3672 | - (labelAutoAngle - fractionCamX) |
3673 | * (-labelAutoAngle - fractionCamY) / labelAutoAngle); |
3674 | labelRotation.setZ(labelAutoAngle + fractionCamY); |
3675 | } else { |
3676 | labelRotation.setX(90.0f |
3677 | + (labelAutoAngle + fractionCamX) |
3678 | * (labelAutoAngle + fractionCamY) / labelAutoAngle); |
3679 | labelRotation.setZ(-labelAutoAngle - fractionCamY); |
3680 | } |
3681 | } else { |
3682 | if (xFlipped) { |
3683 | labelRotation.setX(90.0f |
3684 | + (labelAutoAngle - fractionCamX) |
3685 | * -(labelAutoAngle + fractionCamY) / labelAutoAngle); |
3686 | labelRotation.setZ(-labelAutoAngle - fractionCamY); |
3687 | } else { |
3688 | labelRotation.setX(90.0f |
3689 | - (labelAutoAngle + fractionCamX) |
3690 | * (labelAutoAngle + fractionCamY) / labelAutoAngle); |
3691 | labelRotation.setZ(labelAutoAngle + fractionCamY); |
3692 | } |
3693 | } |
3694 | } else { |
3695 | if (zFlipped) { |
3696 | if (xFlipped) { |
3697 | labelRotation.setX(-90.0f |
3698 | + (labelAutoAngle - fractionCamX) |
3699 | * (-labelAutoAngle + fractionCamY) / labelAutoAngle); |
3700 | labelRotation.setZ(-labelAutoAngle + fractionCamY); |
3701 | } else { |
3702 | labelRotation.setX(-90.0f |
3703 | - (labelAutoAngle + fractionCamX) |
3704 | * (labelAutoAngle - fractionCamY) / labelAutoAngle); |
3705 | labelRotation.setZ(labelAutoAngle - fractionCamY); |
3706 | } |
3707 | } else { |
3708 | if (xFlipped) { |
3709 | labelRotation.setX(-90.0f |
3710 | - (labelAutoAngle - fractionCamX) |
3711 | * (-labelAutoAngle + fractionCamY) / labelAutoAngle); |
3712 | labelRotation.setZ(labelAutoAngle - fractionCamY); |
3713 | } else { |
3714 | labelRotation.setX(-90.0f |
3715 | + (labelAutoAngle + fractionCamX) |
3716 | * (labelAutoAngle - fractionCamY) / labelAutoAngle); |
3717 | labelRotation.setZ(-labelAutoAngle + fractionCamY); |
3718 | } |
3719 | } |
3720 | } |
3721 | } |
3722 | |
3723 | totalRotation = Utils::calculateRotation(xyzRotations: labelRotation); |
3724 | |
3725 | scale = backgroundScale.z() - m_backgroundScaleMargin.z(); |
3726 | labelsMaxWidth = float(findLabelsMaxWidth(labels: axisZ()->labels())) + textPadding; |
3727 | fontRatio = labelsMaxWidth / labelHeight; |
3728 | m_fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f); |
3729 | adjustment = labelAdjustment(width: labelsMaxWidth); |
3730 | xPos = backgroundScale.x() + adjustment + m_labelMargin; |
3731 | if (xFlipped) |
3732 | xPos *= -1.0f; |
3733 | |
3734 | adjustment *= qAbs(t: qSin(v: qDegreesToRadians(degrees: labelRotation.z()))); |
3735 | yPos = backgroundScale.y() + adjustment - labelDepthMargin; |
3736 | if (!yFlipped) |
3737 | yPos *= -1.0f; |
3738 | |
3739 | labelTrans = QVector3D(xPos, yPos, 0.0f); |
3740 | if (axisZ()->type() == QAbstract3DAxis::AxisType::Value) { |
3741 | auto valueAxisZ = static_cast<QValue3DAxis *>(axisZ()); |
3742 | float offsetAdjustment = 0.05f; |
3743 | float offset = radialLabelOffset() + offsetAdjustment; |
3744 | for (int i = 0; i < repeaterZ()->count(); i++) { |
3745 | if (labelCount <= i) |
3746 | break; |
3747 | |
3748 | auto obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(index: i)); |
3749 | if (isPolar()) { |
3750 | // RADIAL LABELS |
3751 | float polarX = backgroundScale.x() * offset + m_labelMargin * 2.0f; |
3752 | if (xFlipped) |
3753 | polarX *= -1; |
3754 | labelTrans.setX(polarX); |
3755 | labelTrans.setY(yPos + yOffset); |
3756 | |
3757 | labelTrans.setZ(-valueAxisZ->labelPositionAt(index: i) * m_polarRadius); |
3758 | } else { |
3759 | labelTrans.setZ(valueAxisZ->labelPositionAt(index: i) * scale * -2.0f + scale); |
3760 | } |
3761 | obj->setObjectName(QStringLiteral("ElementAxisZLabel")); |
3762 | obj->setScale(m_fontScaled); |
3763 | obj->setPosition(labelTrans); |
3764 | obj->setRotation(totalRotation); |
3765 | obj->setProperty(name: "labelText", value: labels[i]); |
3766 | obj->setProperty(name: "labelWidth", value: labelsMaxWidth); |
3767 | obj->setProperty(name: "labelHeight", value: labelHeight); |
3768 | } |
3769 | } else if (axisZ()->type() == QAbstract3DAxis::AxisType::Category) { |
3770 | for (int i = 0; i < repeaterZ()->count(); i++) { |
3771 | if (labelCount <= i) |
3772 | break; |
3773 | labelTrans = calculateCategoryLabelPosition(axis: axisZ(), labelPosition: labelTrans, index: i); |
3774 | auto obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(index: i)); |
3775 | obj->setObjectName(QStringLiteral("ElementAxisZLabel")); |
3776 | obj->setScale(m_fontScaled); |
3777 | obj->setPosition(labelTrans); |
3778 | obj->setRotation(totalRotation); |
3779 | obj->setProperty(name: "labelText", value: labels[i]); |
3780 | obj->setProperty(name: "labelWidth", value: labelsMaxWidth); |
3781 | obj->setProperty(name: "labelHeight", value: labelHeight); |
3782 | } |
3783 | } |
3784 | |
3785 | float z = labelTrans.z(); |
3786 | labelTrans.setZ(0.0f); |
3787 | updateZTitle(labelRotation, labelTrans, totalRotation, labelsMaxWidth, scale: m_fontScaled); |
3788 | labelTrans.setZ(z); |
3789 | |
3790 | labels = axisY()->labels(); |
3791 | labelCount = labels.size(); |
3792 | totalRotation = Utils::calculateRotation(xyzRotations: backLabelRotation); |
3793 | scale = backgroundScale.y() - m_backgroundScaleMargin.y(); |
3794 | labelsMaxWidth = float(findLabelsMaxWidth(labels: axisY()->labels())) + textPadding; |
3795 | fontRatio = labelsMaxWidth / labelHeight; |
3796 | m_fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f); |
3797 | adjustment = labelAdjustment(width: labelsMaxWidth); |
3798 | |
3799 | xPos = backgroundScale.x() + adjustment + m_labelMargin; |
3800 | if (xFlipped) |
3801 | xPos *= -1.0f; |
3802 | labelTrans.setX(xPos); |
3803 | |
3804 | zPos = -backgroundScale.z() + labelDepthMargin; |
3805 | if (zFlipped) |
3806 | zPos *= -1.0f; |
3807 | labelTrans.setZ(zPos); |
3808 | |
3809 | for (int i = 0; i < repeaterY()->count() / 2; i++) { |
3810 | if (labelCount <= i) |
3811 | break; |
3812 | auto obj = static_cast<QQuick3DNode *>( |
3813 | repeaterY()->objectAt(index: i + (repeaterY()->count() / 2))); |
3814 | labelTrans.setY(static_cast<QValue3DAxis *>(axisY())->labelPositionAt(index: i) * scale * 2.0f |
3815 | - scale); |
3816 | obj->setObjectName(QStringLiteral("ElementAxisYLabel")); |
3817 | obj->setScale(m_fontScaled); |
3818 | obj->setPosition(labelTrans); |
3819 | obj->setRotation(totalRotation); |
3820 | obj->setProperty(name: "labelText", value: labels[i]); |
3821 | obj->setProperty(name: "labelWidth", value: labelsMaxWidth); |
3822 | obj->setProperty(name: "labelHeight", value: labelHeight); |
3823 | } |
3824 | |
3825 | QVector3D backLabelTrans = labelTrans; |
3826 | QQuaternion totalBackLabelRotation = totalRotation; |
3827 | updateYTitle(sideLabelRotation, |
3828 | backLabelRotation, |
3829 | sideLabelTrans, |
3830 | backLabelTrans, |
3831 | totalSideRotation: totalSideLabelRotation, |
3832 | totalBackRotation: totalBackLabelRotation, |
3833 | labelsMaxWidth, |
3834 | scale: m_fontScaled); |
3835 | } |
3836 | |
3837 | void QQuickGraphsItem::updateRadialLabelOffset() |
3838 | { |
3839 | if (!isPolar()) |
3840 | return; |
3841 | |
3842 | QVector3D backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin; |
3843 | float offset = radialLabelOffset(); |
3844 | float scale = backgroundScale.x() + (m_backgroundScaleMargin.x()); |
3845 | float polarX = scale * offset + m_labelMargin * 2.0f; |
3846 | if (isXFlipped()) |
3847 | polarX *= -1; |
3848 | if (axisZ()->type() == QAbstract3DAxis::AxisType::Value) { |
3849 | for (int i = 0; i < repeaterZ()->count(); i++) { |
3850 | QQuick3DNode *obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(index: i)); |
3851 | QVector3D pos = obj->position(); |
3852 | pos.setX(polarX); |
3853 | obj->setPosition(pos); |
3854 | } |
3855 | } |
3856 | |
3857 | polarX += m_labelMargin * 2.5f; |
3858 | QVector3D pos = m_titleLabelZ->position(); |
3859 | pos.setX(polarX); |
3860 | m_titleLabelZ->setPosition(pos); |
3861 | } |
3862 | |
3863 | void QQuickGraphsItem::positionAndScaleLine(QQuick3DNode *lineNode, |
3864 | QVector3D scale, |
3865 | QVector3D position) |
3866 | { |
3867 | lineNode->setScale(scale); |
3868 | lineNode->setPosition(position); |
3869 | } |
3870 | |
3871 | QVector3D QQuickGraphsItem::graphPositionAt(const QPoint point) |
3872 | { |
3873 | auto result = pick(x: point.x(), y: point.y()); |
3874 | QVector3D position = QVector3D(); |
3875 | if (result.objectHit()) |
3876 | position = result.scenePosition(); |
3877 | |
3878 | return position; |
3879 | } |
3880 | |
3881 | void QQuickGraphsItem::updateShadowQuality(QtGraphs3D::ShadowQuality quality) |
3882 | { |
3883 | if (quality != QtGraphs3D::ShadowQuality::None) { |
3884 | light()->setCastsShadow(true); |
3885 | light()->setShadowFactor(25.f); |
3886 | |
3887 | QQuick3DAbstractLight::QSSGShadowMapQuality shadowMapQuality; |
3888 | switch (quality) { |
3889 | case QtGraphs3D::ShadowQuality::Low: |
3890 | case QtGraphs3D::ShadowQuality::SoftLow: |
3891 | shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityMedium; |
3892 | break; |
3893 | case QtGraphs3D::ShadowQuality::Medium: |
3894 | case QtGraphs3D::ShadowQuality::SoftMedium: |
3895 | shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityHigh; |
3896 | break; |
3897 | case QtGraphs3D::ShadowQuality::High: |
3898 | case QtGraphs3D::ShadowQuality::SoftHigh: |
3899 | shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityVeryHigh; |
3900 | break; |
3901 | default: |
3902 | shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityHigh; |
3903 | break; |
3904 | } |
3905 | light()->setShadowMapQuality(shadowMapQuality); |
3906 | if (quality >= QtGraphs3D::ShadowQuality::SoftLow) |
3907 | light()->setShadowFilter(10.f); |
3908 | else |
3909 | light()->setShadowFilter(2.f); |
3910 | } else { |
3911 | light()->setCastsShadow(false); |
3912 | light()->setShadowFactor(0.f); |
3913 | } |
3914 | } |
3915 | |
3916 | void QQuickGraphsItem::updateItemLabel(QVector3D position) |
3917 | { |
3918 | if (m_labelPosition != position) |
3919 | m_labelPosition = position; |
3920 | QVector3D pos2d = mapFrom3DScene(scenePos: m_labelPosition); |
3921 | int pointSize = theme()->labelFont().pointSize(); |
3922 | float scale = m_labelScale.x() * ((-10.0f * pointSize) + 650.0f) / pos2d.z(); |
3923 | if (m_sliceView && m_sliceView->isVisible()) |
3924 | m_itemLabel->setScale(scale * .2f); |
3925 | else |
3926 | m_itemLabel->setScale(scale); |
3927 | pos2d.setX(pos2d.x() - (m_itemLabel->width() / 2.f)); |
3928 | pos2d.setY(pos2d.y() - (m_itemLabel->height() / 2.f) |
3929 | - (m_itemLabel->height() * m_itemLabel->scale())); |
3930 | m_itemLabel->setPosition(pos2d.toPointF()); |
3931 | } |
3932 | |
3933 | void QQuickGraphsItem::updateSliceItemLabel(const QString &label, QVector3D position) |
3934 | { |
3935 | Q_UNUSED(position); |
3936 | |
3937 | QFontMetrics fm(theme()->labelFont()); |
3938 | float textPadding = theme()->labelFont().pointSizeF() * .7f; |
3939 | float labelHeight = fm.height() + textPadding; |
3940 | float labelWidth = fm.horizontalAdvance(label) + textPadding; |
3941 | |
3942 | float pointSize = theme()->labelFont().pointSizeF(); |
3943 | float scaleFactor = fontScaleFactor(pointSize) * pointSize; |
3944 | float fontRatio = labelWidth / labelHeight; |
3945 | |
3946 | QVector3D fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f); |
3947 | m_sliceItemLabel->setScale(fontScaled); |
3948 | } |
3949 | |
3950 | void QQuickGraphsItem::createVolumeMaterial(QCustom3DVolume *volume, Volume &volumeItem) |
3951 | { |
3952 | if (volumeItem.texture) |
3953 | volumeItem.texture->deleteLater(); |
3954 | volumeItem.texture = new QQuick3DTexture(); |
3955 | auto texture = volumeItem.texture; |
3956 | |
3957 | texture->setParent(this); |
3958 | texture->setMinFilter(QQuick3DTexture::Filter::Nearest); |
3959 | texture->setMagFilter(QQuick3DTexture::Filter::Nearest); |
3960 | texture->setHorizontalTiling(QQuick3DTexture::TilingMode::ClampToEdge); |
3961 | texture->setVerticalTiling(QQuick3DTexture::TilingMode::ClampToEdge); |
3962 | |
3963 | if (volumeItem.textureData) |
3964 | volumeItem.textureData->deleteLater(); |
3965 | volumeItem.textureData = new QQuick3DTextureData(); |
3966 | auto textureData = volumeItem.textureData; |
3967 | |
3968 | int color8Bit = (volume->textureFormat() == QImage::Format_Indexed8) ? 1 : 0; |
3969 | |
3970 | textureData->setParent(texture); |
3971 | textureData->setParentItem(texture); |
3972 | textureData->setSize(QSize(volume->textureWidth(), volume->textureHeight())); |
3973 | textureData->setDepth(volume->textureDepth()); |
3974 | if (color8Bit) |
3975 | textureData->setFormat(QQuick3DTextureData::R8); |
3976 | else |
3977 | textureData->setFormat(QQuick3DTextureData::RGBA8); |
3978 | textureData->setTextureData( |
3979 | QByteArray::fromRawData(data: reinterpret_cast<const char *>(volume->textureData()->constData()), |
3980 | size: volume->textureData()->size())); |
3981 | texture->setTextureData(textureData); |
3982 | |
3983 | QObject::connect(sender: volume, signal: &QCustom3DVolume::textureDataChanged, context: this, slot: [this, volume] { |
3984 | m_customVolumes[volume].updateTextureData = true; |
3985 | }); |
3986 | |
3987 | if (color8Bit) { |
3988 | if (volumeItem.colorTexture) |
3989 | volumeItem.colorTexture->deleteLater(); |
3990 | volumeItem.colorTexture = new QQuick3DTexture(); |
3991 | auto colorTexture = volumeItem.colorTexture; |
3992 | |
3993 | colorTexture->setParent(this); |
3994 | colorTexture->setMinFilter(QQuick3DTexture::Filter::Nearest); |
3995 | colorTexture->setMagFilter(QQuick3DTexture::Filter::Nearest); |
3996 | colorTexture->setHorizontalTiling(QQuick3DTexture::TilingMode::ClampToEdge); |
3997 | colorTexture->setVerticalTiling(QQuick3DTexture::TilingMode::ClampToEdge); |
3998 | |
3999 | QByteArray colorTableBytes; |
4000 | const QList<QRgb> &colorTable = volume->colorTable(); |
4001 | for (int i = 0; i < colorTable.size(); i++) { |
4002 | QRgb shifted = qRgba(r: qBlue(rgb: colorTable[i]), |
4003 | g: qGreen(rgb: colorTable[i]), |
4004 | b: qRed(rgb: colorTable[i]), |
4005 | a: qAlpha(rgb: colorTable[i])); |
4006 | colorTableBytes.append( |
4007 | a: QByteArray::fromRawData(data: reinterpret_cast<const char *>(&shifted), size: sizeof(shifted))); |
4008 | } |
4009 | |
4010 | if (volumeItem.colorTextureData) |
4011 | volumeItem.colorTextureData->deleteLater(); |
4012 | volumeItem.colorTextureData = new QQuick3DTextureData(); |
4013 | auto colorTextureData = volumeItem.colorTextureData; |
4014 | |
4015 | colorTextureData->setParent(colorTexture); |
4016 | colorTextureData->setParentItem(colorTexture); |
4017 | colorTextureData->setSize(QSize(int(volume->colorTable().size()), 1)); |
4018 | colorTextureData->setFormat(QQuick3DTextureData::RGBA8); |
4019 | colorTextureData->setTextureData(colorTableBytes); |
4020 | colorTexture->setTextureData(colorTextureData); |
4021 | |
4022 | QObject::connect(sender: volume, signal: &QCustom3DVolume::colorTableChanged, context: this, slot: [this, volume] { |
4023 | m_customVolumes[volume].updateColorTextureData = true; |
4024 | }); |
4025 | } |
4026 | |
4027 | auto model = volumeItem.model; |
4028 | QQmlListReference materialsRef(model, "materials"); |
4029 | |
4030 | QQuick3DCustomMaterial *material = nullptr; |
4031 | |
4032 | if (volume->drawSlices() && m_validVolumeSlice) |
4033 | material = createQmlCustomMaterial(QStringLiteral(":/materials/VolumeSliceMaterial")); |
4034 | else if (volume->useHighDefShader()) |
4035 | material = createQmlCustomMaterial(QStringLiteral(":/materials/VolumeMaterial")); |
4036 | else |
4037 | material = createQmlCustomMaterial(QStringLiteral(":/materials/VolumeLowDefMaterial")); |
4038 | |
4039 | auto textureSamplerVariant = material->property(name: "textureSampler"); |
4040 | auto textureSampler = textureSamplerVariant.value<QQuick3DShaderUtilsTextureInput *>(); |
4041 | textureSampler->setTexture(volumeItem.texture); |
4042 | |
4043 | if (color8Bit) { |
4044 | auto colorSamplerVariant = material->property(name: "colorSampler"); |
4045 | auto colorSampler = colorSamplerVariant.value<QQuick3DShaderUtilsTextureInput *>(); |
4046 | colorSampler->setTexture(volumeItem.colorTexture); |
4047 | } |
4048 | |
4049 | material->setProperty(name: "textureDimensions", |
4050 | value: QVector3D(1.0f / float(volume->textureWidth()), |
4051 | 1.0f / float(volume->textureHeight()), |
4052 | 1.0f / float(volume->textureDepth()))); |
4053 | |
4054 | materialsRef.append(material); |
4055 | |
4056 | volumeItem.useHighDefShader = volume->useHighDefShader(); |
4057 | volumeItem.drawSlices = volume->drawSlices() && m_validVolumeSlice; |
4058 | } |
4059 | |
4060 | QQuick3DModel *QQuickGraphsItem::createSliceFrame(Volume &volumeItem) |
4061 | { |
4062 | QQuick3DModel *model = new QQuick3DModel(); |
4063 | model->setParent(volumeItem.model); |
4064 | model->setParentItem(volumeItem.model); |
4065 | model->setSource(QUrl(QStringLiteral("defaultMeshes/barMeshFull"))); |
4066 | model->setScale(QVector3D(1, 1, 0.01f)); |
4067 | model->setDepthBias(-100.0f); |
4068 | |
4069 | QQmlListReference materialsRef(model, "materials"); |
4070 | QQuick3DCustomMaterial *material = createQmlCustomMaterial( |
4071 | QStringLiteral(":/materials/VolumeFrameMaterial")); |
4072 | material->setParent(model); |
4073 | material->setParentItem(model); |
4074 | material->setCullMode(QQuick3DMaterial::NoCulling); |
4075 | materialsRef.append(material); |
4076 | |
4077 | return model; |
4078 | } |
4079 | |
4080 | void QQuickGraphsItem::updateSliceFrameMaterials(QCustom3DVolume *volume, Volume &volumeItem) |
4081 | { |
4082 | QQmlListReference materialsRefX(volumeItem.sliceFrameX, "materials"); |
4083 | QQmlListReference materialsRefY(volumeItem.sliceFrameY, "materials"); |
4084 | QQmlListReference materialsRefZ(volumeItem.sliceFrameZ, "materials"); |
4085 | |
4086 | QVector2D frameWidth; |
4087 | QVector3D frameScaling; |
4088 | |
4089 | frameScaling = QVector3D(volume->scaling().z() |
4090 | + (volume->scaling().z() * volume->sliceFrameGaps().z()) |
4091 | + (volume->scaling().z() * volume->sliceFrameWidths().z()), |
4092 | volume->scaling().y() |
4093 | + (volume->scaling().y() * volume->sliceFrameGaps().y()) |
4094 | + (volume->scaling().y() * volume->sliceFrameWidths().y()), |
4095 | volume->scaling().x() * volume->sliceFrameThicknesses().x()); |
4096 | |
4097 | frameWidth = QVector2D(volume->scaling().z() * volume->sliceFrameWidths().z(), |
4098 | volume->scaling().y() * volume->sliceFrameWidths().y()); |
4099 | |
4100 | frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x())); |
4101 | frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y())); |
4102 | |
4103 | auto material = materialsRefX.at(0); |
4104 | material->setProperty(name: "color", value: volume->sliceFrameColor()); |
4105 | material->setProperty(name: "sliceFrameWidth", value: frameWidth); |
4106 | |
4107 | frameScaling = QVector3D(volume->scaling().x() |
4108 | + (volume->scaling().x() * volume->sliceFrameGaps().x()) |
4109 | + (volume->scaling().x() * volume->sliceFrameWidths().x()), |
4110 | volume->scaling().z() |
4111 | + (volume->scaling().z() * volume->sliceFrameGaps().z()) |
4112 | + (volume->scaling().z() * volume->sliceFrameWidths().z()), |
4113 | volume->scaling().y() * volume->sliceFrameThicknesses().y()); |
4114 | frameWidth = QVector2D(volume->scaling().x() * volume->sliceFrameWidths().x(), |
4115 | volume->scaling().z() * volume->sliceFrameWidths().z()); |
4116 | |
4117 | frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x())); |
4118 | frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y())); |
4119 | |
4120 | material = materialsRefY.at(0); |
4121 | material->setProperty(name: "color", value: volume->sliceFrameColor()); |
4122 | material->setProperty(name: "sliceFrameWidth", value: frameWidth); |
4123 | |
4124 | frameScaling = QVector3D(volume->scaling().x() |
4125 | + (volume->scaling().x() * volume->sliceFrameGaps().x()) |
4126 | + (volume->scaling().x() * volume->sliceFrameWidths().x()), |
4127 | volume->scaling().y() |
4128 | + (volume->scaling().y() * volume->sliceFrameGaps().y()) |
4129 | + (volume->scaling().y() * volume->sliceFrameWidths().y()), |
4130 | volume->scaling().z() * volume->sliceFrameThicknesses().z()); |
4131 | frameWidth = QVector2D(volume->scaling().x() * volume->sliceFrameWidths().x(), |
4132 | volume->scaling().y() * volume->sliceFrameWidths().y()); |
4133 | |
4134 | frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x())); |
4135 | frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y())); |
4136 | |
4137 | material = materialsRefZ.at(0); |
4138 | material->setProperty(name: "color", value: volume->sliceFrameColor()); |
4139 | material->setProperty(name: "sliceFrameWidth", value: frameWidth); |
4140 | } |
4141 | |
4142 | void QQuickGraphsItem::updateCustomVolumes() |
4143 | { |
4144 | auto itemIterator = m_customItemList.constBegin(); |
4145 | while (itemIterator != m_customItemList.constEnd()) { |
4146 | QCustom3DItem *item = itemIterator.key(); |
4147 | QQuick3DModel *model = itemIterator.value(); |
4148 | |
4149 | if (auto volume = qobject_cast<QCustom3DVolume *>(object: item)) { |
4150 | auto &&volumeItem = m_customVolumes[volume]; |
4151 | |
4152 | QQmlListReference materialsRef(model, "materials"); |
4153 | if (volumeItem.useHighDefShader != volume->useHighDefShader()) { |
4154 | materialsRef.clear(); |
4155 | createVolumeMaterial(volume, volumeItem); |
4156 | } |
4157 | |
4158 | m_validVolumeSlice = volume->sliceIndexX() >= 0 |
4159 | || volume->sliceIndexY() >= 0 |
4160 | || volume->sliceIndexZ() >= 0; |
4161 | |
4162 | if (volumeItem.drawSlices != (volume->drawSlices() && m_validVolumeSlice)) { |
4163 | materialsRef.clear(); |
4164 | createVolumeMaterial(volume, volumeItem); |
4165 | } |
4166 | |
4167 | QVector3D sliceIndices( |
4168 | (float(volume->sliceIndexX()) + 0.5f) / float(volume->textureWidth()) * 2.0 - 1.0, |
4169 | (float(volume->sliceIndexY()) + 0.5f) / float(volume->textureHeight()) * 2.0 - 1.0, |
4170 | (float(volume->sliceIndexZ()) + 0.5f) / float(volume->textureDepth()) * 2.0 - 1.0); |
4171 | |
4172 | if (volumeItem.drawSliceFrames != volume->drawSliceFrames()) { |
4173 | if (volume->drawSliceFrames()) { |
4174 | volumeItem.sliceFrameX->setVisible(true); |
4175 | volumeItem.sliceFrameY->setVisible(true); |
4176 | volumeItem.sliceFrameZ->setVisible(true); |
4177 | |
4178 | volumeItem.sliceFrameX->setRotation(QQuaternion::fromEulerAngles(pitch: 0, yaw: 90, roll: 0)); |
4179 | volumeItem.sliceFrameY->setRotation(QQuaternion::fromEulerAngles(pitch: 90, yaw: 0, roll: 0)); |
4180 | |
4181 | updateSliceFrameMaterials(volume, volumeItem); |
4182 | } else { |
4183 | volumeItem.sliceFrameX->setVisible(false); |
4184 | volumeItem.sliceFrameY->setVisible(false); |
4185 | volumeItem.sliceFrameZ->setVisible(false); |
4186 | } |
4187 | volumeItem.drawSliceFrames = volume->drawSliceFrames(); |
4188 | } |
4189 | |
4190 | auto material = materialsRef.at(0); |
4191 | QVector3D minBounds(-1, 1, 1); |
4192 | QVector3D maxBounds(1, -1, -1); |
4193 | QVector3D translation(0, 0, 0); |
4194 | QVector3D scaling(1, 1, 1); |
4195 | |
4196 | model->setVisible(volume->isVisible()); |
4197 | if (!volume->isScalingAbsolute() && !volume->isPositionAbsolute()) { |
4198 | |
4199 | QVector3D pos = volume->position(); |
4200 | QVector3D scale = volume->scaling() / 2; |
4201 | |
4202 | QVector3D minGraphBounds(pos.x() - scale.x(), |
4203 | pos.y() - scale.y(), |
4204 | pos.z() + scale.z()); |
4205 | QVector3D maxGraphBounds(pos.x() + scale.x(), |
4206 | pos.y() + scale.y(), |
4207 | pos.z() - scale.z()); |
4208 | |
4209 | QVector3D minCorner = graphPosToAbsolute(position: minGraphBounds); |
4210 | QVector3D maxCorner = graphPosToAbsolute(position: maxGraphBounds); |
4211 | |
4212 | scale = QVector3D(qAbs(t: maxCorner.x() - minCorner.x()), |
4213 | qAbs(t: maxCorner.y() - minCorner.y()), |
4214 | qAbs(t: maxCorner.z() - minCorner.z())) / 2.0f; |
4215 | |
4216 | const QVector3D mScale = scaleWithBackground(); |
4217 | const QVector3D itemRange = maxCorner - minCorner; |
4218 | if (minCorner.x() < -mScale.x()) |
4219 | minBounds.setX(-1.0f + (2.0f * qAbs(t: minCorner.x() + mScale.x()) / itemRange.x())); |
4220 | if (minCorner.y() < -mScale.y()) |
4221 | minBounds.setY(-(-1.0f + (2.0f * qAbs(t: minCorner.y() + mScale.y()) / itemRange.y()))); |
4222 | if (minCorner.z() < -mScale.z()) |
4223 | minBounds.setZ(-(-1.0f + (2.0f * qAbs(t: minCorner.z() + mScale.z()) / itemRange.z()))); |
4224 | |
4225 | if (maxCorner.x() > mScale.x()) |
4226 | maxBounds.setX(1.0f - (2.0f * qAbs(t: maxCorner.x() - mScale.x()) / itemRange.x())); |
4227 | if (maxCorner.y() > mScale.y()) |
4228 | maxBounds.setY(-(1.0f - (2.0f * qAbs(t: maxCorner.y() - mScale.y()) / itemRange.y()))); |
4229 | if (maxCorner.z() > mScale.z()) |
4230 | maxBounds.setZ(-(1.0f - (2.0f * qAbs(t: maxCorner.z() - mScale.z()) / itemRange.z()))); |
4231 | |
4232 | QVector3D minBoundsNorm = minBounds; |
4233 | QVector3D maxBoundsNorm = maxBounds; |
4234 | |
4235 | minBoundsNorm.setY(-minBoundsNorm.y()); |
4236 | minBoundsNorm.setZ(-minBoundsNorm.z()); |
4237 | minBoundsNorm = 0.5f * (minBoundsNorm + QVector3D(1,1,1)); |
4238 | |
4239 | maxBoundsNorm.setY(-maxBoundsNorm.y()); |
4240 | maxBoundsNorm.setZ(-maxBoundsNorm.z()); |
4241 | maxBoundsNorm = 0.5f * (maxBoundsNorm + QVector3D(1,1,1)); |
4242 | |
4243 | QVector3D adjScaling = scale * (maxBoundsNorm - minBoundsNorm); |
4244 | model->setScale(adjScaling); |
4245 | |
4246 | QVector3D adjPos = volume->position(); |
4247 | QVector3D dataExtents = (maxGraphBounds - minGraphBounds) / 2.0f; |
4248 | |
4249 | adjPos = adjPos + (dataExtents * minBoundsNorm) |
4250 | - (dataExtents * (QVector3D(1,1,1) - maxBoundsNorm)); |
4251 | adjPos = graphPosToAbsolute(position: adjPos); |
4252 | model->setPosition(adjPos); |
4253 | } else { |
4254 | model->setScale(volume->scaling()); |
4255 | } |
4256 | model->setRotation(volume->rotation()); |
4257 | |
4258 | material->setProperty(name: "minBounds", value: minBounds); |
4259 | material->setProperty(name: "maxBounds", value: maxBounds); |
4260 | |
4261 | if (volume->drawSlices()) |
4262 | material->setProperty(name: "volumeSliceIndices", value: sliceIndices); |
4263 | |
4264 | if (volume->drawSliceFrames()) { |
4265 | float sliceFrameX = sliceIndices.x(); |
4266 | float sliceFrameY = sliceIndices.y(); |
4267 | float sliceFrameZ = sliceIndices.z(); |
4268 | if (volume->sliceIndexX() >= 0 && scaling.x() > 0) |
4269 | sliceFrameX = (sliceFrameX + translation.x()) / scaling.x(); |
4270 | if (volume->sliceIndexY() >= 0 && scaling.y() > 0) |
4271 | sliceFrameY = (sliceFrameY - translation.y()) / scaling.y(); |
4272 | if (volume->sliceIndexZ() >= 0 && scaling.z() > 0) |
4273 | sliceFrameZ = (sliceFrameZ + translation.z()) / scaling.z(); |
4274 | |
4275 | if (sliceFrameX < -1 || sliceFrameX > 1) |
4276 | volumeItem.sliceFrameX->setVisible(false); |
4277 | else |
4278 | volumeItem.sliceFrameX->setVisible(true); |
4279 | |
4280 | if (sliceFrameY < -1 || sliceFrameY > 1) |
4281 | volumeItem.sliceFrameY->setVisible(false); |
4282 | else |
4283 | volumeItem.sliceFrameY->setVisible(true); |
4284 | |
4285 | if (sliceFrameZ < -1 || sliceFrameZ > 1) |
4286 | volumeItem.sliceFrameZ->setVisible(false); |
4287 | else |
4288 | volumeItem.sliceFrameZ->setVisible(true); |
4289 | |
4290 | volumeItem.sliceFrameX->setX(sliceFrameX); |
4291 | volumeItem.sliceFrameY->setY(-sliceFrameY); |
4292 | volumeItem.sliceFrameZ->setZ(-sliceFrameZ); |
4293 | } |
4294 | |
4295 | material->setProperty(name: "alphaMultiplier", value: volume->alphaMultiplier()); |
4296 | material->setProperty(name: "preserveOpacity", value: volume->preserveOpacity()); |
4297 | material->setProperty(name: "useOrtho", value: isOrthoProjection()); |
4298 | |
4299 | int sampleCount = volume->textureWidth() + volume->textureHeight() |
4300 | + volume->textureDepth(); |
4301 | material->setProperty(name: "sampleCount", value: sampleCount); |
4302 | |
4303 | int color8Bit = (volume->textureFormat() == QImage::Format_Indexed8) ? 1 : 0; |
4304 | material->setProperty(name: "color8Bit", value: color8Bit); |
4305 | |
4306 | if (volumeItem.updateTextureData) { |
4307 | auto textureData = volumeItem.textureData; |
4308 | textureData->setSize(QSize(volume->textureWidth(), volume->textureHeight())); |
4309 | textureData->setDepth(volume->textureDepth()); |
4310 | |
4311 | if (color8Bit) |
4312 | textureData->setFormat(QQuick3DTextureData::R8); |
4313 | else |
4314 | textureData->setFormat(QQuick3DTextureData::RGBA8); |
4315 | |
4316 | textureData->setTextureData( |
4317 | QByteArray::fromRawData(data: reinterpret_cast<const char *>( |
4318 | volume->textureData()->constData()), |
4319 | size: volume->textureData()->size())); |
4320 | |
4321 | material->setProperty(name: "textureDimensions", |
4322 | value: QVector3D(1.0f / float(volume->textureWidth()), |
4323 | 1.0f / float(volume->textureHeight()), |
4324 | 1.0f / float(volume->textureDepth()))); |
4325 | |
4326 | volumeItem.updateTextureData = false; |
4327 | } |
4328 | |
4329 | if (volumeItem.updateColorTextureData) { |
4330 | auto colorTextureData = volumeItem.colorTextureData; |
4331 | QByteArray colorTableBytes; |
4332 | const QList<QRgb> &colorTable = volume->colorTable(); |
4333 | for (int i = 0; i < colorTable.size(); i++) { |
4334 | QRgb shifted = qRgba(r: qBlue(rgb: colorTable[i]), |
4335 | g: qGreen(rgb: colorTable[i]), |
4336 | b: qRed(rgb: colorTable[i]), |
4337 | a: qAlpha(rgb: colorTable[i])); |
4338 | colorTableBytes.append( |
4339 | a: QByteArray::fromRawData(data: reinterpret_cast<const char *>(&shifted), |
4340 | size: sizeof(shifted))); |
4341 | } |
4342 | colorTextureData->setTextureData(colorTableBytes); |
4343 | } |
4344 | } |
4345 | ++itemIterator; |
4346 | } |
4347 | } |
4348 | |
4349 | void QQuickGraphsItem::updateAxisRange(float min, float max) |
4350 | { |
4351 | Q_UNUSED(min); |
4352 | Q_UNUSED(max); |
4353 | } |
4354 | |
4355 | void QQuickGraphsItem::updateAxisReversed(bool enable) |
4356 | { |
4357 | Q_UNUSED(enable); |
4358 | } |
4359 | |
4360 | int QQuickGraphsItem::findLabelsMaxWidth(const QStringList &labels) |
4361 | { |
4362 | int labelWidth = 0; |
4363 | QFontMetrics labelFM(theme()->labelFont()); |
4364 | |
4365 | for (const auto &label : std::as_const(t: labels)) { |
4366 | auto width = labelFM.horizontalAdvance(label); |
4367 | if (labelWidth < width) |
4368 | labelWidth = width; |
4369 | } |
4370 | return labelWidth; |
4371 | } |
4372 | |
4373 | QVector3D QQuickGraphsItem::calculateCategoryLabelPosition(QAbstract3DAxis *axis, |
4374 | QVector3D labelPosition, |
4375 | int index) |
4376 | { |
4377 | Q_UNUSED(axis); |
4378 | Q_UNUSED(index); |
4379 | return labelPosition; |
4380 | } |
4381 | |
4382 | float QQuickGraphsItem::calculateCategoryGridLinePosition(QAbstract3DAxis *axis, int index) |
4383 | { |
4384 | Q_UNUSED(axis); |
4385 | Q_UNUSED(index); |
4386 | return 0.0f; |
4387 | } |
4388 | |
4389 | float QQuickGraphsItem::calculatePolarBackgroundMargin() |
4390 | { |
4391 | // Check each extents of each angular label |
4392 | // Calculate angular position |
4393 | auto valueAxisX = static_cast<QValue3DAxis *>(axisX()); |
4394 | auto labelPositions = const_cast<QList<float> &>(valueAxisX->formatter()->labelPositions()); |
4395 | float actualLabelHeight = m_fontScaled.y() * 2.0f; // All labels are same height |
4396 | float maxNeededMargin = 0.0f; |
4397 | |
4398 | // Axis title needs to be accounted for |
4399 | if (valueAxisX->isTitleVisible()) |
4400 | maxNeededMargin = 2.0f * actualLabelHeight + 3.0f * labelMargin(); |
4401 | |
4402 | for (int label = 0; label < labelPositions.size(); label++) { |
4403 | QSizeF labelSize{m_fontScaled.x(), m_fontScaled.z()}; |
4404 | float actualLabelWidth = actualLabelHeight / labelSize.height() * labelSize.width(); |
4405 | float labelPosition = labelPositions.at(i: label); |
4406 | qreal angle = labelPosition * M_PI * 2.0; |
4407 | float x = qAbs(t: (m_polarRadius + labelMargin()) * float(qSin(v: angle))) + actualLabelWidth |
4408 | - m_polarRadius + labelMargin(); |
4409 | float z = qAbs(t: -(m_polarRadius + labelMargin()) * float(qCos(v: angle))) + actualLabelHeight |
4410 | - m_polarRadius + labelMargin(); |
4411 | float neededMargin = qMax(a: x, b: z); |
4412 | maxNeededMargin = qMax(a: maxNeededMargin, b: neededMargin); |
4413 | } |
4414 | |
4415 | maxNeededMargin *= 0.2f; |
4416 | return maxNeededMargin; |
4417 | } |
4418 | |
4419 | void QQuickGraphsItem::updateXTitle(QVector3D labelRotation, |
4420 | QVector3D labelTrans, |
4421 | const QQuaternion &totalRotation, |
4422 | float labelsMaxWidth, |
4423 | QVector3D scale) |
4424 | { |
4425 | QFont font = theme()->axisXLabelFont() == QFont() ? theme()->labelFont() : theme()->axisXLabelFont(); |
4426 | float pointSize = font.pointSizeF(); |
4427 | float textPadding = pointSize * .5f; |
4428 | QFontMetrics fm(font); |
4429 | float height = fm.height() + textPadding; |
4430 | float width = fm.horizontalAdvance(axisX()->title()) + textPadding; |
4431 | |
4432 | float titleOffset; |
4433 | |
4434 | bool radial = false; |
4435 | if (radial) |
4436 | titleOffset = -2.0f * (m_labelMargin + scale.y()); |
4437 | else |
4438 | titleOffset = 2.0f * m_labelMargin + (labelsMaxWidth * scale.y()); |
4439 | |
4440 | float zRotation = 0.0f; |
4441 | float yRotation = 0.0f; |
4442 | float xRotation = -90.0f + labelRotation.z(); |
4443 | float offsetRotation = labelRotation.z(); |
4444 | float extraRotation = -90.0f; |
4445 | if (m_yFlipped) { |
4446 | zRotation = 180.0f; |
4447 | if (m_zFlipped) { |
4448 | titleOffset = -titleOffset; |
4449 | if (m_xFlipped) { |
4450 | offsetRotation = -offsetRotation; |
4451 | extraRotation = -extraRotation; |
4452 | } else { |
4453 | xRotation = -90.0f - labelRotation.z(); |
4454 | } |
4455 | } else { |
4456 | yRotation = 180.0f; |
4457 | if (m_xFlipped) { |
4458 | offsetRotation = -offsetRotation; |
4459 | xRotation = -90.0f - labelRotation.z(); |
4460 | } else { |
4461 | extraRotation = -extraRotation; |
4462 | } |
4463 | } |
4464 | } else { |
4465 | if (m_zFlipped) { |
4466 | titleOffset = -titleOffset; |
4467 | if (m_xFlipped) { |
4468 | offsetRotation = -offsetRotation; |
4469 | } else { |
4470 | xRotation = -90.0f - labelRotation.z(); |
4471 | extraRotation = -extraRotation; |
4472 | } |
4473 | yRotation = 180.0f; |
4474 | if (m_yFlipped) { |
4475 | extraRotation = -extraRotation; |
4476 | if (m_xFlipped) |
4477 | xRotation = 90.0f + labelRotation.z(); |
4478 | else |
4479 | xRotation = 90.0f - labelRotation.z(); |
4480 | } |
4481 | } else { |
4482 | if (m_xFlipped) { |
4483 | offsetRotation = -offsetRotation; |
4484 | xRotation = -90.0f - labelRotation.z(); |
4485 | extraRotation = -extraRotation; |
4486 | } |
4487 | if (m_yFlipped) { |
4488 | xRotation = 90.0f + labelRotation.z(); |
4489 | extraRotation = -extraRotation; |
4490 | if (m_xFlipped) |
4491 | xRotation = 90.0f - labelRotation.z(); |
4492 | } |
4493 | } |
4494 | } |
4495 | |
4496 | if (offsetRotation == 180.0f || offsetRotation == -180.0f) |
4497 | offsetRotation = 0.0f; |
4498 | |
4499 | QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: offsetRotation); |
4500 | QVector3D titleOffsetVector = offsetRotator.rotatedVector(vector: QVector3D(0.0f, 0.0f, titleOffset)); |
4501 | titleOffsetVector.setX(axisX()->titleOffset() * scaleWithBackground().x()); |
4502 | |
4503 | QQuaternion titleRotation; |
4504 | if (axisX()->isTitleFixed()) { |
4505 | titleRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: zRotation) |
4506 | * QQuaternion::fromAxisAndAngle(x: 0.0f, y: 1.0f, z: 0.0f, angle: yRotation) |
4507 | * QQuaternion::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: xRotation); |
4508 | } else { |
4509 | titleRotation = totalRotation |
4510 | * QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: extraRotation); |
4511 | } |
4512 | |
4513 | QVector3D titleScale = scale; |
4514 | titleScale.setX(titleScale.y() * width / height); |
4515 | m_titleLabelX->setScale(titleScale); |
4516 | m_titleLabelX->setPosition(labelTrans + titleOffsetVector); |
4517 | m_titleLabelX->setRotation(titleRotation); |
4518 | m_titleLabelX->setProperty(name: "labelWidth", value: width); |
4519 | m_titleLabelX->setProperty(name: "labelHeight", value: height); |
4520 | } |
4521 | |
4522 | void QQuickGraphsItem::updateYTitle(QVector3D sideLabelRotation, |
4523 | QVector3D backLabelRotation, |
4524 | QVector3D sideLabelTrans, |
4525 | QVector3D backLabelTrans, |
4526 | const QQuaternion &totalSideRotation, |
4527 | const QQuaternion &totalBackRotation, |
4528 | float labelsMaxWidth, |
4529 | QVector3D scale) |
4530 | { |
4531 | QFont font = theme()->axisYLabelFont() == QFont() ? theme()->labelFont() : theme()->axisYLabelFont(); |
4532 | float pointSize = font.pointSizeF(); |
4533 | float textPadding = pointSize * .5f; |
4534 | QFontMetrics fm(font); |
4535 | float height = fm.height() + textPadding; |
4536 | float width = fm.horizontalAdvance(axisY()->title()) + textPadding; |
4537 | |
4538 | float titleOffset = m_labelMargin + (labelsMaxWidth * scale.x()); |
4539 | |
4540 | QQuaternion zRightAngleRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: 90.0f); |
4541 | float yRotation; |
4542 | QVector3D titleTrans; |
4543 | QQuaternion totalRotation; |
4544 | if (m_xFlipped != m_zFlipped) { |
4545 | yRotation = backLabelRotation.y(); |
4546 | titleTrans = backLabelTrans; |
4547 | totalRotation = totalBackRotation; |
4548 | } else { |
4549 | yRotation = sideLabelRotation.y(); |
4550 | titleTrans = sideLabelTrans; |
4551 | totalRotation = totalSideRotation; |
4552 | } |
4553 | titleTrans.setY(.0f); |
4554 | |
4555 | QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 1.0f, z: 0.0f, angle: yRotation); |
4556 | QVector3D titleOffsetVector = offsetRotator.rotatedVector(vector: QVector3D(-titleOffset, 0.0f, 0.0f)); |
4557 | titleOffsetVector.setY(axisY()->titleOffset() * scaleWithBackground().y()); |
4558 | |
4559 | QQuaternion titleRotation; |
4560 | if (axisY()->isTitleFixed()) { |
4561 | titleRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 1.0f, z: 0.0f, angle: yRotation) |
4562 | * zRightAngleRotation; |
4563 | } else { |
4564 | titleRotation = totalRotation * zRightAngleRotation; |
4565 | } |
4566 | |
4567 | QVector3D titleScale = scale; |
4568 | titleScale.setX(titleScale.y() * width / height); |
4569 | m_titleLabelY->setScale(titleScale); |
4570 | m_titleLabelY->setPosition(titleTrans + titleOffsetVector); |
4571 | m_titleLabelY->setRotation(titleRotation); |
4572 | m_titleLabelY->setProperty(name: "labelWidth", value: width); |
4573 | m_titleLabelY->setProperty(name: "labelHeight", value: height); |
4574 | } |
4575 | |
4576 | void QQuickGraphsItem::updateZTitle(QVector3D labelRotation, |
4577 | QVector3D labelTrans, |
4578 | const QQuaternion &totalRotation, |
4579 | float labelsMaxWidth, |
4580 | QVector3D scale) |
4581 | { |
4582 | QFont font = theme()->axisZLabelFont() == QFont() ? theme()->labelFont() : theme()->axisZLabelFont(); |
4583 | float pointSize = font.pointSizeF(); |
4584 | float textPadding = pointSize * .5f; |
4585 | QFontMetrics fm(font); |
4586 | float height = fm.height() + textPadding; |
4587 | float width = fm.horizontalAdvance(axisZ()->title()) + textPadding; |
4588 | |
4589 | float titleOffset = m_labelMargin + (labelsMaxWidth * scale.x()); |
4590 | |
4591 | float zRotation = labelRotation.z(); |
4592 | float yRotation = -90.0f; |
4593 | float xRotation = -90.0f; |
4594 | float extraRotation = 90.0f; |
4595 | |
4596 | if (m_yFlipped) { |
4597 | xRotation = -xRotation; |
4598 | if (m_zFlipped) { |
4599 | if (m_xFlipped) { |
4600 | titleOffset = -titleOffset; |
4601 | zRotation = -zRotation; |
4602 | extraRotation = -extraRotation; |
4603 | } else { |
4604 | zRotation = -zRotation; |
4605 | yRotation = -yRotation; |
4606 | } |
4607 | } else { |
4608 | if (m_xFlipped) { |
4609 | titleOffset = -titleOffset; |
4610 | } else { |
4611 | extraRotation = -extraRotation; |
4612 | yRotation = -yRotation; |
4613 | } |
4614 | } |
4615 | } else { |
4616 | if (m_zFlipped) { |
4617 | zRotation = -zRotation; |
4618 | if (m_xFlipped) { |
4619 | titleOffset = -titleOffset; |
4620 | } else { |
4621 | extraRotation = -extraRotation; |
4622 | yRotation = -yRotation; |
4623 | } |
4624 | } else { |
4625 | if (m_xFlipped) { |
4626 | titleOffset = -titleOffset; |
4627 | extraRotation = -extraRotation; |
4628 | } else { |
4629 | yRotation = -yRotation; |
4630 | } |
4631 | } |
4632 | if (m_yFlipped) { |
4633 | xRotation = -xRotation; |
4634 | extraRotation = -extraRotation; |
4635 | } |
4636 | } |
4637 | |
4638 | float offsetRotation = zRotation; |
4639 | if (offsetRotation == 180.0f || offsetRotation == -180.0f) |
4640 | offsetRotation = 0.0f; |
4641 | |
4642 | QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: offsetRotation); |
4643 | QVector3D titleOffsetVector = offsetRotator.rotatedVector(vector: QVector3D(titleOffset, 0.0f, 0.0f)); |
4644 | titleOffsetVector.setZ(axisZ()->titleOffset() * scaleWithBackground().z()); |
4645 | |
4646 | QQuaternion titleRotation; |
4647 | if (axisZ()->isTitleFixed()) { |
4648 | titleRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: zRotation) |
4649 | * QQuaternion::fromAxisAndAngle(x: 0.0f, y: 1.0f, z: 0.0f, angle: yRotation) |
4650 | * QQuaternion::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: xRotation); |
4651 | } else { |
4652 | titleRotation = totalRotation |
4653 | * QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: extraRotation); |
4654 | } |
4655 | |
4656 | QVector3D titleScale = scale; |
4657 | titleScale.setX(titleScale.y() * width / height); |
4658 | m_titleLabelZ->setScale(titleScale); |
4659 | m_titleLabelZ->setPosition(labelTrans + titleOffsetVector); |
4660 | m_titleLabelZ->setRotation(titleRotation); |
4661 | m_titleLabelZ->setProperty(name: "labelWidth", value: width); |
4662 | m_titleLabelZ->setProperty(name: "labelHeight", value: height); |
4663 | } |
4664 | |
4665 | void QQuickGraphsItem::updateCamera() |
4666 | { |
4667 | QVector3D lookingPosition = m_requestedTarget; |
4668 | |
4669 | const float scale = qMin(a: width(), b: height() * 1.6f); |
4670 | const float magnificationScaleFactor = 1.0f / 640.0f; |
4671 | const float magnification = scale * magnificationScaleFactor; |
4672 | |
4673 | auto useOrtho = isOrthoProjection(); |
4674 | if (useOrtho) { |
4675 | if (m_sliceView && m_sliceView->isVisible()) { |
4676 | m_oCamera->setVerticalMagnification(m_zoomLevel * .4f); |
4677 | m_oCamera->setHorizontalMagnification(m_zoomLevel * .4f); |
4678 | } else { |
4679 | m_oCamera->setVerticalMagnification(m_zoomLevel * magnification); |
4680 | m_oCamera->setHorizontalMagnification(m_zoomLevel * magnification); |
4681 | } |
4682 | } |
4683 | cameraTarget()->setPosition(lookingPosition); |
4684 | auto rotation = QVector3D(-m_yRotation, -m_xRotation, 0); |
4685 | cameraTarget()->setEulerRotation(rotation); |
4686 | float zoom = 720.f / m_zoomLevel; |
4687 | m_pCamera->setZ(zoom); |
4688 | updateCustomLabelsRotation(); |
4689 | updateItemLabel(position: m_labelPosition); |
4690 | } |
4691 | |
4692 | void QQuickGraphsItem::handleLabelCountChanged(QQuick3DRepeater *repeater, QColor axisLabelColor) |
4693 | { |
4694 | changeLabelBackgroundColor(repeater, color: theme()->labelBackgroundColor()); |
4695 | changeLabelBackgroundVisible(repeater, visible: theme()->isLabelBackgroundVisible()); |
4696 | changeLabelBorderVisible(repeater, visible: theme()->isLabelBorderVisible()); |
4697 | changeLabelTextColor(repeater, color: axisLabelColor); |
4698 | changeLabelFont(repeater, font: theme()->labelFont()); |
4699 | |
4700 | if (m_sliceView) { |
4701 | changeLabelBackgroundColor(repeater: m_sliceHorizontalLabelRepeater, color: theme()->labelBackgroundColor()); |
4702 | changeLabelBackgroundColor(repeater: m_sliceVerticalLabelRepeater, color: theme()->labelBackgroundColor()); |
4703 | changeLabelBackgroundVisible(repeater: m_sliceHorizontalLabelRepeater, |
4704 | visible: theme()->isLabelBackgroundVisible()); |
4705 | changeLabelBackgroundVisible(repeater: m_sliceVerticalLabelRepeater, |
4706 | visible: theme()->isLabelBackgroundVisible()); |
4707 | changeLabelBorderVisible(repeater: m_sliceHorizontalLabelRepeater, visible: theme()->isLabelBorderVisible()); |
4708 | changeLabelBorderVisible(repeater: m_sliceVerticalLabelRepeater, visible: theme()->isLabelBorderVisible()); |
4709 | if (m_selectionMode == SelectionRow) |
4710 | changeLabelTextColor(repeater: m_sliceHorizontalLabelRepeater, color: theme()->axisX().labelTextColor()); |
4711 | else if (m_selectionMode == SelectionColumn) |
4712 | changeLabelTextColor(repeater: m_sliceHorizontalLabelRepeater, color: theme()->axisZ().labelTextColor()); |
4713 | changeLabelTextColor(repeater: m_sliceVerticalLabelRepeater, color: theme()->axisY().labelTextColor()); |
4714 | changeLabelFont(repeater: m_sliceHorizontalLabelRepeater, font: theme()->labelFont()); |
4715 | changeLabelFont(repeater: m_sliceVerticalLabelRepeater, font: theme()->labelFont()); |
4716 | } |
4717 | } |
4718 | |
4719 | void QQuickGraphsItem::updateCustomData() |
4720 | { |
4721 | int maxX = axisX()->max(); |
4722 | int minX = axisX()->min(); |
4723 | int maxY = axisY()->max(); |
4724 | int minY = axisY()->min(); |
4725 | int maxZ = axisZ()->max(); |
4726 | int minZ = axisZ()->min(); |
4727 | |
4728 | auto labelIterator = m_customLabelList.constBegin(); |
4729 | while (labelIterator != m_customLabelList.constEnd()) { |
4730 | QCustom3DLabel *label = labelIterator.key(); |
4731 | QQuick3DNode *customLabel = labelIterator.value(); |
4732 | |
4733 | QVector3D pos = label->position(); |
4734 | if (!label->isPositionAbsolute()) { |
4735 | if (label->position().x() < minX || label->position().x() > maxX |
4736 | || label->position().y() < minY || label->position().y() > maxY |
4737 | || label->position().z() < minZ || label->position().z() > maxZ) { |
4738 | customLabel->setVisible(false); |
4739 | ++labelIterator; |
4740 | continue; |
4741 | } |
4742 | pos = graphPosToAbsolute(position: pos); |
4743 | } |
4744 | |
4745 | QFontMetrics fm(label->font()); |
4746 | int width = fm.horizontalAdvance(label->text()); |
4747 | int height = fm.height(); |
4748 | customLabel->setProperty(name: "labelWidth", value: width); |
4749 | customLabel->setProperty(name: "labelHeight", value: height); |
4750 | customLabel->setPosition(pos); |
4751 | QQuaternion rotation = label->rotation(); |
4752 | if (label->isFacingCamera()) |
4753 | rotation = Utils::calculateRotation(xyzRotations: QVector3D(-m_yRotation, -m_xRotation, 0)); |
4754 | customLabel->setRotation(rotation); |
4755 | float pointSize = theme()->labelFont().pointSizeF(); |
4756 | float scaleFactor = fontScaleFactor(pointSize) * pointSize; |
4757 | float fontRatio = float(height) / float(width); |
4758 | QVector3D fontScaled = QVector3D(scaleFactor / fontRatio, scaleFactor, 0.0f); |
4759 | customLabel->setScale(fontScaled); |
4760 | customLabel->setProperty(name: "labelText", value: label->text()); |
4761 | customLabel->setProperty(name: "labelTextColor", value: label->textColor()); |
4762 | customLabel->setProperty(name: "labelFont", value: label->font()); |
4763 | customLabel->setProperty(name: "backgroundVisible", value: label->isBackgroundVisible()); |
4764 | customLabel->setProperty(name: "backgroundColor", value: label->backgroundColor()); |
4765 | customLabel->setProperty(name: "borderVisible", value: label->isBackgroundVisible()); |
4766 | customLabel->setVisible(label->isVisible()); |
4767 | |
4768 | ++labelIterator; |
4769 | } |
4770 | |
4771 | auto itemIterator = m_customItemList.constBegin(); |
4772 | while (itemIterator != m_customItemList.constEnd()) { |
4773 | QCustom3DItem *item = itemIterator.key(); |
4774 | QQuick3DModel *model = itemIterator.value(); |
4775 | |
4776 | QVector3D pos = item->position(); |
4777 | QVector<QAbstract3DAxis *> axes{axisX(), axisY(), axisZ()}; |
4778 | QVector<float> bScales{scaleWithBackground().x(), |
4779 | scaleWithBackground().y(), |
4780 | scaleWithBackground().z()}; |
4781 | if (!item->isPositionAbsolute()) { |
4782 | if (item->position().x() < minX || item->position().x() > maxX |
4783 | || item->position().y() < minY || item->position().y() > maxY |
4784 | || item->position().z() < minZ || item->position().z() > maxZ) { |
4785 | model->setVisible(false); |
4786 | ++itemIterator; |
4787 | continue; |
4788 | } |
4789 | pos = graphPosToAbsolute(position: pos); |
4790 | } |
4791 | model->setPosition(pos); |
4792 | |
4793 | if (!item->isScalingAbsolute()) { |
4794 | QVector<float> iScales{item->scaling().x(), item->scaling().y(), item->scaling().z()}; |
4795 | for (int i = 0; i < axes.count(); i++) { |
4796 | if (auto vAxis = static_cast<QValue3DAxis *>(axes.at(i))) { |
4797 | float axisRange = vAxis->max() - vAxis->min(); |
4798 | float realRange = bScales.at(i); |
4799 | float ratio = realRange / axisRange; |
4800 | iScales[i] *= ratio; |
4801 | } |
4802 | } |
4803 | model->setScale(QVector3D(iScales.at(i: 0), iScales.at(i: 1), iScales.at(i: 2))); |
4804 | } else { |
4805 | model->setScale(item->scaling()); |
4806 | } |
4807 | |
4808 | if (auto volume = qobject_cast<QCustom3DVolume *>(object: item)) { |
4809 | if (!m_customVolumes.contains(key: volume)) { |
4810 | auto &&volumeItem = m_customVolumes[volume]; |
4811 | |
4812 | volumeItem.model = model; |
4813 | model->setSource(QUrl(volume->meshFile())); |
4814 | |
4815 | volumeItem.useHighDefShader = volume->useHighDefShader(); |
4816 | |
4817 | m_validVolumeSlice = volume->sliceIndexX() >= 0 |
4818 | || volume->sliceIndexY() >= 0 |
4819 | || volume->sliceIndexZ() >= 0; |
4820 | |
4821 | volumeItem.drawSlices = volume->drawSlices() && m_validVolumeSlice; |
4822 | |
4823 | createVolumeMaterial(volume, volumeItem); |
4824 | |
4825 | volumeItem.sliceFrameX = createSliceFrame(volumeItem); |
4826 | volumeItem.sliceFrameY = createSliceFrame(volumeItem); |
4827 | volumeItem.sliceFrameZ = createSliceFrame(volumeItem); |
4828 | |
4829 | if (volume->drawSliceFrames()) { |
4830 | volumeItem.sliceFrameX->setVisible(true); |
4831 | volumeItem.sliceFrameY->setVisible(true); |
4832 | volumeItem.sliceFrameZ->setVisible(true); |
4833 | |
4834 | QVector3D sliceIndices((float(volume->sliceIndexX()) + 0.5f) |
4835 | / float(volume->textureWidth()) * 2.0 |
4836 | - 1.0, |
4837 | (float(volume->sliceIndexY()) + 0.5f) |
4838 | / float(volume->textureHeight()) * 2.0 |
4839 | - 1.0, |
4840 | (float(volume->sliceIndexZ()) + 0.5f) |
4841 | / float(volume->textureDepth()) * 2.0 |
4842 | - 1.0); |
4843 | |
4844 | volumeItem.sliceFrameX->setX(sliceIndices.x()); |
4845 | volumeItem.sliceFrameY->setY(-sliceIndices.y()); |
4846 | volumeItem.sliceFrameZ->setZ(-sliceIndices.z()); |
4847 | |
4848 | volumeItem.sliceFrameX->setRotation(QQuaternion::fromEulerAngles(pitch: 0, yaw: 90, roll: 0)); |
4849 | volumeItem.sliceFrameY->setRotation(QQuaternion::fromEulerAngles(pitch: 90, yaw: 0, roll: 0)); |
4850 | |
4851 | updateSliceFrameMaterials(volume, volumeItem); |
4852 | } else { |
4853 | volumeItem.sliceFrameX->setVisible(false); |
4854 | volumeItem.sliceFrameY->setVisible(false); |
4855 | volumeItem.sliceFrameZ->setVisible(false); |
4856 | } |
4857 | volumeItem.drawSliceFrames = volume->drawSliceFrames(); |
4858 | m_customItemList.insert(key: item, value: model); |
4859 | } |
4860 | } else { |
4861 | model->setSource(QUrl::fromLocalFile(localfile: item->meshFile())); |
4862 | QQmlListReference materialsRef(model, "materials"); |
4863 | QQuick3DPrincipledMaterial *material = static_cast<QQuick3DPrincipledMaterial *>( |
4864 | materialsRef.at(0)); |
4865 | QQuick3DTexture *texture = material->baseColorMap(); |
4866 | if (!texture) { |
4867 | texture = new QQuick3DTexture(); |
4868 | texture->setParent(model); |
4869 | texture->setParentItem(model); |
4870 | material->setBaseColorMap(texture); |
4871 | } |
4872 | if (!item->textureFile().isEmpty()) { |
4873 | texture->setSource(QUrl::fromLocalFile(localfile: item->textureFile())); |
4874 | } else { |
4875 | QImage textureImage = customTextureImage(item); |
4876 | textureImage.convertTo(f: QImage::Format_RGBA32FPx4); |
4877 | QQuick3DTextureData *textureData = texture->textureData(); |
4878 | if (!textureData) { |
4879 | textureData = new QQuick3DTextureData(); |
4880 | textureData->setParent(texture); |
4881 | textureData->setParentItem(texture); |
4882 | textureData->setFormat(QQuick3DTextureData::RGBA32F); |
4883 | texture->setTextureData(textureData); |
4884 | } |
4885 | textureData->setSize(textureImage.size()); |
4886 | textureData->setTextureData( |
4887 | QByteArray(reinterpret_cast<const char *>(textureImage.bits()), |
4888 | textureImage.sizeInBytes())); |
4889 | } |
4890 | model->setRotation(item->rotation()); |
4891 | model->setVisible(item->isVisible()); |
4892 | } |
4893 | ++itemIterator; |
4894 | } |
4895 | } |
4896 | |
4897 | void QQuickGraphsItem::updateCustomLabelsRotation() |
4898 | { |
4899 | auto labelIterator = m_customLabelList.constBegin(); |
4900 | while (labelIterator != m_customLabelList.constEnd()) { |
4901 | QCustom3DLabel *label = labelIterator.key(); |
4902 | QQuick3DNode *customLabel = labelIterator.value(); |
4903 | QQuaternion rotation = label->rotation(); |
4904 | if (label->isFacingCamera()) |
4905 | rotation = Utils::calculateRotation(xyzRotations: QVector3D(-m_yRotation, -m_xRotation, 0)); |
4906 | customLabel->setRotation(rotation); |
4907 | ++labelIterator; |
4908 | } |
4909 | } |
4910 | |
4911 | int QQuickGraphsItem::msaaSamples() const |
4912 | { |
4913 | if (m_renderMode == QtGraphs3D::RenderingMode::Indirect) |
4914 | return m_samples; |
4915 | else |
4916 | return m_windowSamples; |
4917 | } |
4918 | |
4919 | void QQuickGraphsItem::setMsaaSamples(int samples) |
4920 | { |
4921 | if (m_renderMode != QtGraphs3D::RenderingMode::Indirect) { |
4922 | qWarning(msg: "Multisampling cannot be adjusted in this render mode"); |
4923 | } else if (m_samples != samples) { |
4924 | m_samples = samples; |
4925 | setAntialiasing(m_samples > 0); |
4926 | auto sceneEnv = environment(); |
4927 | sceneEnv->setAntialiasingMode( |
4928 | m_samples > 0 ? QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues::MSAA |
4929 | : QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues::NoAA); |
4930 | switch (m_samples) { |
4931 | case 0: |
4932 | // no-op |
4933 | break; |
4934 | case 2: |
4935 | sceneEnv->setAntialiasingQuality( |
4936 | QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::Medium); |
4937 | break; |
4938 | case 4: |
4939 | sceneEnv->setAntialiasingQuality( |
4940 | QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::High); |
4941 | break; |
4942 | case 8: |
4943 | sceneEnv->setAntialiasingQuality( |
4944 | QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::VeryHigh); |
4945 | break; |
4946 | default: |
4947 | qWarning(msg: "Invalid multisampling sample number, using 4x instead"); |
4948 | sceneEnv->setAntialiasingQuality( |
4949 | QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::High); |
4950 | m_samples = 4; |
4951 | break; |
4952 | } |
4953 | emit msaaSamplesChanged(samples: m_samples); |
4954 | update(); |
4955 | } |
4956 | } |
4957 | |
4958 | void QQuickGraphsItem::handleWindowChanged(/*QQuickWindow *window*/) |
4959 | { |
4960 | auto window = QQuick3DObjectPrivate::get(item: rootNode())->sceneManager->window(); |
4961 | checkWindowList(window); |
4962 | if (!window) |
4963 | return; |
4964 | |
4965 | #if defined(Q_OS_MACOS) |
4966 | bool previousVisibility = window->isVisible(); |
4967 | // Enable touch events for Mac touchpads |
4968 | window->setVisible(true); |
4969 | typedef void *(*EnableTouch)(QWindow *, bool); |
4970 | EnableTouch enableTouch = (EnableTouch) QGuiApplication::platformNativeInterface() |
4971 | ->nativeResourceFunctionForIntegration("registertouchwindow"); |
4972 | if (enableTouch) |
4973 | enableTouch(window, true); |
4974 | window->setVisible(previousVisibility); |
4975 | #endif |
4976 | |
4977 | connect(sender: window, signal: &QObject::destroyed, context: this, slot: &QQuickGraphsItem::windowDestroyed); |
4978 | |
4979 | int oldWindowSamples = m_windowSamples; |
4980 | m_windowSamples = window->format().samples(); |
4981 | if (m_windowSamples < 0) |
4982 | m_windowSamples = 0; |
4983 | |
4984 | connect(sender: window, signal: &QQuickWindow::beforeSynchronizing, context: this, slot: &QQuickGraphsItem::synchData); |
4985 | |
4986 | if (m_renderMode == QtGraphs3D::RenderingMode::DirectToBackground) { |
4987 | setAntialiasing(m_windowSamples > 0); |
4988 | if (m_windowSamples != oldWindowSamples) |
4989 | emit msaaSamplesChanged(samples: m_windowSamples); |
4990 | } |
4991 | |
4992 | connect(sender: this, signal: &QQuickGraphsItem::needRender, context: window, slot: &QQuickWindow::update); |
4993 | // Force camera update before rendering the first frame |
4994 | // to workaround a Quick3D device pixel ratio bug |
4995 | connect(sender: window, signal: &QQuickWindow::beforeRendering, context: this, slot: [this, window]() { |
4996 | m_oCamera->setClipNear(0.001f); |
4997 | disconnect(sender: window, signal: &QQuickWindow::beforeRendering, receiver: this, zero: nullptr); |
4998 | }); |
4999 | updateWindowParameters(); |
5000 | |
5001 | #if defined(Q_OS_IOS) |
5002 | // Scenegraph render cycle in iOS sometimes misses update after |
5003 | // beforeSynchronizing signal. This ensures we don't end up displaying the |
5004 | // graph without any data, in case update is skipped after synchData. |
5005 | QTimer::singleShot(0, window, SLOT(update())); |
5006 | #endif |
5007 | } |
5008 | |
5009 | void QQuickGraphsItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) |
5010 | { |
5011 | QQuickItem::geometryChange(newGeometry, oldGeometry); |
5012 | // Do not cache primary subviewport geometry, as that will mess up window size |
5013 | m_cachedGeometry = parentItem()->boundingRect(); |
5014 | updateWindowParameters(); |
5015 | } |
5016 | |
5017 | void QQuickGraphsItem::itemChange(ItemChange change, const ItemChangeData &value) |
5018 | { |
5019 | QQuick3DViewport::itemChange(change, value); |
5020 | updateWindowParameters(); |
5021 | } |
5022 | |
5023 | void QQuickGraphsItem::updateWindowParameters() |
5024 | { |
5025 | const QMutexLocker locker(&m_mutex); |
5026 | // Update the device pixel ratio, window size and bounding box |
5027 | QQuickWindow *win = window(); |
5028 | if (win) { |
5029 | if (win->devicePixelRatio() != scene()->devicePixelRatio()) { |
5030 | scene()->setDevicePixelRatio(win->devicePixelRatio()); |
5031 | win->update(); |
5032 | } |
5033 | |
5034 | QSize windowSize; |
5035 | |
5036 | if (m_renderMode == QtGraphs3D::RenderingMode::DirectToBackground) |
5037 | windowSize = win->size(); |
5038 | else |
5039 | windowSize = m_cachedGeometry.size().toSize(); |
5040 | |
5041 | if (windowSize != scene()->d_func()->windowSize()) { |
5042 | scene()->d_func()->setWindowSize(windowSize); |
5043 | win->update(); |
5044 | } |
5045 | |
5046 | resizeViewports(viewportSize: m_cachedGeometry.size()); |
5047 | } |
5048 | } |
5049 | |
5050 | void QQuickGraphsItem::handleSelectionModeChange(QtGraphs3D::SelectionFlags mode) |
5051 | { |
5052 | emit selectionModeChanged(mode); |
5053 | } |
5054 | |
5055 | void QQuickGraphsItem::handleShadowQualityChange(QtGraphs3D::ShadowQuality quality) |
5056 | { |
5057 | emit shadowQualityChanged(quality); |
5058 | } |
5059 | |
5060 | void QQuickGraphsItem::handleSelectedElementChange(QtGraphs3D::ElementType type) |
5061 | { |
5062 | m_clickedType = type; |
5063 | emit selectedElementChanged(type); |
5064 | } |
5065 | |
5066 | void QQuickGraphsItem::handleOptimizationHintChange(QtGraphs3D::OptimizationHint hint) |
5067 | { |
5068 | Q_UNUSED(hint) |
5069 | } |
5070 | |
5071 | void QQuickGraphsItem::resizeViewports(QSizeF viewportSize) |
5072 | { |
5073 | if (!viewportSize.isEmpty()) { |
5074 | scene()->d_func()->setViewport( |
5075 | QRect(0.0f, 0.0f, viewportSize.width() + 0.5f, viewportSize.height() + 0.5f)); |
5076 | } |
5077 | } |
5078 | |
5079 | void QQuickGraphsItem::checkWindowList(QQuickWindow *window) |
5080 | { |
5081 | QQuickWindow *oldWindow = m_graphWindowList.value(key: this); |
5082 | m_graphWindowList[this] = window; |
5083 | |
5084 | if (oldWindow != window && oldWindow) { |
5085 | QObject::disconnect(sender: oldWindow, |
5086 | signal: &QObject::destroyed, |
5087 | receiver: this, |
5088 | slot: &QQuickGraphsItem::windowDestroyed); |
5089 | QObject::disconnect(sender: oldWindow, |
5090 | signal: &QQuickWindow::beforeSynchronizing, |
5091 | receiver: this, |
5092 | slot: &QQuickGraphsItem::synchData); |
5093 | QObject::disconnect(sender: this, signal: &QQuickGraphsItem::needRender, receiver: oldWindow, slot: &QQuickWindow::update); |
5094 | } |
5095 | |
5096 | QList<QQuickWindow *> windowList; |
5097 | |
5098 | const auto keys = m_graphWindowList.keys(); |
5099 | for (const auto &graph : keys) { |
5100 | if (graph->m_renderMode == QtGraphs3D::RenderingMode::DirectToBackground) |
5101 | windowList.append(t: m_graphWindowList.value(key: graph)); |
5102 | } |
5103 | |
5104 | if (!window) { |
5105 | m_graphWindowList.remove(key: this); |
5106 | return; |
5107 | } |
5108 | } |
5109 | |
5110 | void QQuickGraphsItem::setMeasureFps(bool enable) |
5111 | { |
5112 | if (m_measureFps != enable) { |
5113 | m_measureFps = enable; |
5114 | if (enable) { |
5115 | QObject::connect(sender: renderStats(), |
5116 | signal: &QQuick3DRenderStats::fpsChanged, |
5117 | context: this, |
5118 | slot: &QQuickGraphsItem::handleFpsChanged); |
5119 | emitNeedRender(); |
5120 | } else { |
5121 | QObject::disconnect(sender: renderStats(), signal: 0, receiver: this, member: 0); |
5122 | } |
5123 | } |
5124 | } |
5125 | |
5126 | bool QQuickGraphsItem::measureFps() const |
5127 | { |
5128 | return m_measureFps; |
5129 | } |
5130 | |
5131 | int QQuickGraphsItem::currentFps() const |
5132 | { |
5133 | return m_currentFps; |
5134 | } |
5135 | |
5136 | void QQuickGraphsItem::setOrthoProjection(bool enable) |
5137 | { |
5138 | if (enable != m_useOrthoProjection) { |
5139 | m_useOrthoProjection = enable; |
5140 | m_changeTracker.projectionChanged = true; |
5141 | emit orthoProjectionChanged(enabled: m_useOrthoProjection); |
5142 | // If changed to ortho, disable shadows |
5143 | if (m_useOrthoProjection) |
5144 | doSetShadowQuality(quality: QtGraphs3D::ShadowQuality::None); |
5145 | emitNeedRender(); |
5146 | } |
5147 | } |
5148 | |
5149 | bool QQuickGraphsItem::isOrthoProjection() const |
5150 | { |
5151 | return m_useOrthoProjection; |
5152 | } |
5153 | |
5154 | QtGraphs3D::ElementType QQuickGraphsItem::selectedElement() const |
5155 | { |
5156 | return m_clickedType; |
5157 | } |
5158 | |
5159 | void QQuickGraphsItem::setAspectRatio(qreal ratio) |
5160 | { |
5161 | if (m_aspectRatio != ratio) { |
5162 | m_aspectRatio = ratio; |
5163 | m_changeTracker.aspectRatioChanged = true; |
5164 | emit aspectRatioChanged(ratio: m_aspectRatio); |
5165 | m_isDataDirty = true; |
5166 | emitNeedRender(); |
5167 | } |
5168 | } |
5169 | |
5170 | qreal QQuickGraphsItem::aspectRatio() const |
5171 | { |
5172 | return m_aspectRatio; |
5173 | } |
5174 | |
5175 | void QQuickGraphsItem::setOptimizationHint(QtGraphs3D::OptimizationHint hint) |
5176 | { |
5177 | if (hint != m_optimizationHint) { |
5178 | m_optimizationHint = hint; |
5179 | m_changeTracker.optimizationHintChanged = true; |
5180 | m_isDataDirty = true; |
5181 | handleOptimizationHintChange(hint: m_optimizationHint); |
5182 | emit optimizationHintChanged(hint); |
5183 | emitNeedRender(); |
5184 | } |
5185 | } |
5186 | |
5187 | QtGraphs3D::OptimizationHint QQuickGraphsItem::optimizationHint() const |
5188 | { |
5189 | return m_optimizationHint; |
5190 | } |
5191 | |
5192 | void QQuickGraphsItem::setPolar(bool enable) |
5193 | { |
5194 | if (enable != m_isPolar) { |
5195 | if (m_graphType == QAbstract3DSeries::SeriesType::Bar) |
5196 | qWarning(msg: "Polar type with bars is not supported."); |
5197 | m_isPolar = enable; |
5198 | m_changeTracker.polarChanged = true; |
5199 | setVerticalSegmentLine(!m_isPolar); |
5200 | m_isDataDirty = true; |
5201 | emit polarChanged(enabled: m_isPolar); |
5202 | emitNeedRender(); |
5203 | } |
5204 | } |
5205 | |
5206 | bool QQuickGraphsItem::isPolar() const |
5207 | { |
5208 | return m_isPolar; |
5209 | } |
5210 | |
5211 | void QQuickGraphsItem::setLabelMargin(float margin) |
5212 | { |
5213 | if (m_labelMargin != margin) { |
5214 | m_labelMargin = margin; |
5215 | m_changeTracker.labelMarginChanged = true; |
5216 | emit labelMarginChanged(margin: m_labelMargin); |
5217 | emitNeedRender(); |
5218 | } |
5219 | } |
5220 | |
5221 | float QQuickGraphsItem::labelMargin() const |
5222 | { |
5223 | return m_labelMargin; |
5224 | } |
5225 | |
5226 | void QQuickGraphsItem::setRadialLabelOffset(float offset) |
5227 | { |
5228 | if (m_radialLabelOffset != offset) { |
5229 | m_radialLabelOffset = offset; |
5230 | m_changeTracker.radialLabelOffsetChanged = true; |
5231 | emit radialLabelOffsetChanged(offset: m_radialLabelOffset); |
5232 | emitNeedRender(); |
5233 | } |
5234 | } |
5235 | |
5236 | float QQuickGraphsItem::radialLabelOffset() const |
5237 | { |
5238 | return m_radialLabelOffset; |
5239 | } |
5240 | |
5241 | void QQuickGraphsItem::setHorizontalAspectRatio(qreal ratio) |
5242 | { |
5243 | if (m_horizontalAspectRatio != ratio) { |
5244 | m_horizontalAspectRatio = ratio; |
5245 | m_changeTracker.horizontalAspectRatioChanged = true; |
5246 | emit horizontalAspectRatioChanged(ratio: m_horizontalAspectRatio); |
5247 | m_isDataDirty = true; |
5248 | emitNeedRender(); |
5249 | } |
5250 | } |
5251 | |
5252 | qreal QQuickGraphsItem::horizontalAspectRatio() const |
5253 | { |
5254 | return m_horizontalAspectRatio; |
5255 | } |
5256 | |
5257 | void QQuickGraphsItem::setLocale(const QLocale &locale) |
5258 | { |
5259 | if (m_locale != locale) { |
5260 | m_locale = locale; |
5261 | |
5262 | // Value axis formatters need to be updated |
5263 | QValue3DAxis *axis = qobject_cast<QValue3DAxis *>(object: m_axisX); |
5264 | if (axis) |
5265 | axis->formatter()->setLocale(m_locale); |
5266 | axis = qobject_cast<QValue3DAxis *>(object: m_axisY); |
5267 | if (axis) |
5268 | axis->formatter()->setLocale(m_locale); |
5269 | axis = qobject_cast<QValue3DAxis *>(object: m_axisZ); |
5270 | if (axis) |
5271 | axis->formatter()->setLocale(m_locale); |
5272 | emit localeChanged(locale: m_locale); |
5273 | } |
5274 | } |
5275 | |
5276 | QLocale QQuickGraphsItem::locale() const |
5277 | { |
5278 | return m_locale; |
5279 | } |
5280 | |
5281 | QVector3D QQuickGraphsItem::queriedGraphPosition() const |
5282 | { |
5283 | return m_queriedGraphPosition; |
5284 | } |
5285 | |
5286 | void QQuickGraphsItem::setMargin(qreal margin) |
5287 | { |
5288 | if (m_margin != margin) { |
5289 | m_margin = margin; |
5290 | m_changeTracker.marginChanged = true; |
5291 | emit marginChanged(margin); |
5292 | emitNeedRender(); |
5293 | } |
5294 | } |
5295 | |
5296 | qreal QQuickGraphsItem::margin() const |
5297 | { |
5298 | return m_margin; |
5299 | } |
5300 | |
5301 | QQuick3DNode *QQuickGraphsItem::rootNode() const |
5302 | { |
5303 | return QQuick3DViewport::scene(); |
5304 | } |
5305 | |
5306 | void QQuickGraphsItem::changeLabelBackgroundColor(QQuick3DRepeater *repeater, QColor color) |
5307 | { |
5308 | int count = repeater->count(); |
5309 | for (int i = 0; i < count; i++) { |
5310 | auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
5311 | label->setProperty(name: "backgroundColor", value: color); |
5312 | } |
5313 | } |
5314 | |
5315 | void QQuickGraphsItem::changeLabelBackgroundVisible(QQuick3DRepeater *repeater, const bool &visible) |
5316 | { |
5317 | int count = repeater->count(); |
5318 | for (int i = 0; i < count; i++) { |
5319 | auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
5320 | label->setProperty(name: "backgroundVisible", value: visible); |
5321 | } |
5322 | } |
5323 | |
5324 | void QQuickGraphsItem::changeLabelBorderVisible(QQuick3DRepeater *repeater, const bool &visible) |
5325 | { |
5326 | int count = repeater->count(); |
5327 | for (int i = 0; i < count; i++) { |
5328 | auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
5329 | label->setProperty(name: "borderVisible", value: visible); |
5330 | } |
5331 | } |
5332 | |
5333 | void QQuickGraphsItem::changeLabelTextColor(QQuick3DRepeater *repeater, QColor color) |
5334 | { |
5335 | int count = repeater->count(); |
5336 | for (int i = 0; i < count; i++) { |
5337 | auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
5338 | label->setProperty(name: "labelTextColor", value: color); |
5339 | } |
5340 | } |
5341 | |
5342 | void QQuickGraphsItem::changeLabelFont(QQuick3DRepeater *repeater, const QFont &font) |
5343 | { |
5344 | int count = repeater->count(); |
5345 | for (int i = 0; i < count; i++) { |
5346 | auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
5347 | label->setProperty(name: "labelFont", value: font); |
5348 | } |
5349 | } |
5350 | |
5351 | void QQuickGraphsItem::changeLabelsVisible(QQuick3DRepeater *repeater, const bool &visible) |
5352 | { |
5353 | int count = repeater->count(); |
5354 | for (int i = 0; i < count; i++) { |
5355 | auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
5356 | label->setProperty(name: "visible", value: visible); |
5357 | } |
5358 | } |
5359 | |
5360 | void QQuickGraphsItem::changeGridLineColor(QQuick3DRepeater *repeater, QColor color) |
5361 | { |
5362 | for (int i = 0; i < repeater->count(); i++) { |
5363 | auto lineNode = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
5364 | lineNode->setProperty(name: "lineColor", value: color); |
5365 | } |
5366 | } |
5367 | |
5368 | void QQuickGraphsItem::updateTitleLabels() |
5369 | { |
5370 | if (m_changeTracker.axisXTitleVisibilityChanged) { |
5371 | m_titleLabelX->setVisible(axisX()->isTitleVisible()); |
5372 | m_changeTracker.axisXTitleVisibilityChanged = false; |
5373 | } |
5374 | |
5375 | if (m_changeTracker.axisYTitleVisibilityChanged) { |
5376 | m_titleLabelY->setVisible(axisY()->isTitleVisible()); |
5377 | m_changeTracker.axisYTitleVisibilityChanged = false; |
5378 | } |
5379 | |
5380 | if (m_changeTracker.axisZTitleVisibilityChanged) { |
5381 | m_titleLabelZ->setVisible(axisZ()->isTitleVisible()); |
5382 | m_changeTracker.axisZTitleVisibilityChanged = false; |
5383 | } |
5384 | |
5385 | if (m_changeTracker.axisXTitleChanged) { |
5386 | m_titleLabelX->setProperty(name: "labelText", value: axisX()->title()); |
5387 | m_changeTracker.axisXTitleChanged = false; |
5388 | } |
5389 | |
5390 | if (m_changeTracker.axisYTitleChanged) { |
5391 | m_titleLabelY->setProperty(name: "labelText", value: axisY()->title()); |
5392 | m_changeTracker.axisYTitleChanged = false; |
5393 | } |
5394 | |
5395 | if (m_changeTracker.axisZTitleChanged) { |
5396 | m_titleLabelZ->setProperty(name: "labelText", value: axisZ()->title()); |
5397 | m_changeTracker.axisZTitleChanged = false; |
5398 | } |
5399 | } |
5400 | |
5401 | |
5402 | void QQuickGraphsItem::updateSelectionMode(QtGraphs3D::SelectionFlags newMode) |
5403 | { |
5404 | Q_UNUSED(newMode); |
5405 | |
5406 | if (m_sliceView && m_sliceView->isVisible()) |
5407 | toggleSliceGraph(); |
5408 | } |
5409 | |
5410 | bool QQuickGraphsItem::doPicking(QPointF point) |
5411 | { |
5412 | checkSliceEnabled(); |
5413 | |
5414 | QList<QQuick3DPickResult> results = pickAll(x: point.x(), y: point.y()); |
5415 | if (!m_customItemList.isEmpty()) { |
5416 | // Try to pick custom item only |
5417 | for (const auto &result : results) { |
5418 | QCustom3DItem *customItem = m_customItemList.key(value: result.objectHit(), defaultKey: nullptr); |
5419 | |
5420 | if (customItem) { |
5421 | qsizetype selectedIndex = m_customItems.indexOf(t: customItem); |
5422 | m_selectedCustomItemIndex = selectedIndex; |
5423 | handleSelectedElementChange(type: QtGraphs3D::ElementType::CustomItem); |
5424 | // Don't allow picking in subclasses if custom item is picked |
5425 | return false; |
5426 | } |
5427 | } |
5428 | } |
5429 | |
5430 | for (const auto &result : results) { |
5431 | if (!result.objectHit()) |
5432 | continue; |
5433 | QString objName = result.objectHit()->objectName(); |
5434 | if (objName.contains(QStringLiteral("ElementAxisXLabel"))) { |
5435 | for (int i = 0; i < repeaterX()->count(); i++) { |
5436 | auto obj = static_cast<QQuick3DNode *>(repeaterX()->objectAt(index: i)); |
5437 | if (result.objectHit() == obj) |
5438 | m_selectedLabelIndex = i; |
5439 | } |
5440 | handleSelectedElementChange(type: QtGraphs3D::ElementType::AxisXLabel); |
5441 | break; |
5442 | } else if (objName.contains(QStringLiteral("ElementAxisYLabel"))) { |
5443 | handleSelectedElementChange(type: QtGraphs3D::ElementType::AxisYLabel); |
5444 | break; |
5445 | } else if (objName.contains(QStringLiteral("ElementAxisZLabel"))) { |
5446 | for (int i = 0; i < repeaterX()->count(); i++) { |
5447 | auto obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(index: i)); |
5448 | if (result.objectHit() == obj) |
5449 | m_selectedLabelIndex = i; |
5450 | } |
5451 | handleSelectedElementChange(type: QtGraphs3D::ElementType::AxisZLabel); |
5452 | break; |
5453 | } else { |
5454 | continue; |
5455 | } |
5456 | } |
5457 | return true; |
5458 | } |
5459 | |
5460 | void QQuickGraphsItem::minimizeMainGraph() |
5461 | { |
5462 | QQuickItem *anchor = QQuickItemPrivate::get(item: this)->anchors()->fill(); |
5463 | if (anchor) |
5464 | QQuickItemPrivate::get(item: this)->anchors()->resetFill(); |
5465 | |
5466 | m_inputHandler->setX(x()); |
5467 | m_inputHandler->setY(y()); |
5468 | } |
5469 | |
5470 | void QQuickGraphsItem::toggleSliceGraph() |
5471 | { |
5472 | if (!m_sliceView || !m_sliceActivatedChanged) |
5473 | return; |
5474 | |
5475 | if (m_sliceView->isVisible()) { |
5476 | // Maximize main view |
5477 | m_sliceView->setVisible(false); |
5478 | setSlicingActive(false); |
5479 | updateSubViews(); |
5480 | } else { |
5481 | // Minimize main view |
5482 | setSlicingActive(true); |
5483 | m_sliceView->setVisible(true); |
5484 | minimizeMainGraph(); |
5485 | updateSubViews(); |
5486 | updateSliceGrid(); |
5487 | updateSliceLabels(); |
5488 | } |
5489 | |
5490 | m_sliceActivatedChanged = false; |
5491 | } |
5492 | |
5493 | void QQuickGraphsItem::updateSubViews() |
5494 | { |
5495 | QRect newMainView = isSlicingActive() ? scene()->primarySubViewport() : scene()->viewport(); |
5496 | QRect newSliceView = scene()->secondarySubViewport(); |
5497 | |
5498 | if (newMainView.isValid() && newMainView.toRectF() != boundingRect()) { |
5499 | // Set main view dimensions and position |
5500 | setX(newMainView.x()); |
5501 | setY(newMainView.y()); |
5502 | setSize(newMainView.size()); |
5503 | update(); |
5504 | } |
5505 | |
5506 | if (sliceView()) { |
5507 | if (newSliceView.isValid() && m_sliceView->boundingRect() != newSliceView.toRectF()) { |
5508 | // Set slice view dimensions and position |
5509 | m_sliceView->setX(newSliceView.x()); |
5510 | m_sliceView->setY(newSliceView.y()); |
5511 | m_sliceView->setSize(newSliceView.size()); |
5512 | m_sliceView->update(); |
5513 | } |
5514 | |
5515 | if (isSliceOrthoProjection()) { |
5516 | const float scale = qMin(a: m_sliceView->width(), b: m_sliceView->height()); |
5517 | QQuick3DOrthographicCamera *camera = static_cast<QQuick3DOrthographicCamera *>( |
5518 | m_sliceView->camera()); |
5519 | const float magnificationScaleFactor = .16f; // this controls the size of the slice view |
5520 | const float magnification = scale * magnificationScaleFactor; |
5521 | camera->setHorizontalMagnification(magnification); |
5522 | camera->setVerticalMagnification(magnification); |
5523 | } |
5524 | } |
5525 | } |
5526 | |
5527 | void QQuickGraphsItem::windowDestroyed(QObject *obj) |
5528 | { |
5529 | // Remove destroyed window from window lists |
5530 | QQuickWindow *win = static_cast<QQuickWindow *>(obj); |
5531 | QQuickWindow *oldWindow = m_graphWindowList.value(key: this); |
5532 | |
5533 | if (win == oldWindow) |
5534 | m_graphWindowList.remove(key: this); |
5535 | } |
5536 | |
5537 | QQmlComponent *QQuickGraphsItem::createRepeaterDelegateComponent(const QString &fileName) |
5538 | { |
5539 | QQmlComponent component(qmlEngine(this), fileName); |
5540 | return qobject_cast<QQmlComponent *>(object: component.create()); |
5541 | } |
5542 | |
5543 | QQuick3DRepeater *QQuickGraphsItem::createRepeater(QQuick3DNode *parent) |
5544 | { |
5545 | auto engine = qmlEngine(this); |
5546 | QQmlComponent repeaterComponent(engine); |
5547 | repeaterComponent.setData("import QtQuick3D; Repeater3D{}", baseUrl: QUrl()); |
5548 | auto repeater = qobject_cast<QQuick3DRepeater *>(object: repeaterComponent.create()); |
5549 | repeater->setParent(parent ? parent : graphNode()); |
5550 | repeater->setParentItem(parent ? parent : graphNode()); |
5551 | return repeater; |
5552 | } |
5553 | |
5554 | QQuick3DNode *QQuickGraphsItem::createTitleLabel(QQuick3DNode *parent) |
5555 | { |
5556 | auto engine = qmlEngine(this); |
5557 | QQmlComponent comp(engine, QStringLiteral(":/axis/TitleLabel")); |
5558 | auto titleLabel = qobject_cast<QQuick3DNode *>(object: comp.create()); |
5559 | titleLabel->setParent(parent ? parent : graphNode()); |
5560 | titleLabel->setParentItem(parent ? parent : graphNode()); |
5561 | titleLabel->setVisible(false); |
5562 | titleLabel->setScale(m_labelScale); |
5563 | return titleLabel; |
5564 | } |
5565 | |
5566 | void QQuickGraphsItem::createItemLabel() |
5567 | { |
5568 | auto engine = qmlEngine(this); |
5569 | QQmlComponent comp(engine, QStringLiteral(":/axis/ItemLabel")); |
5570 | m_itemLabel = qobject_cast<QQuickItem *>(o: comp.create()); |
5571 | m_itemLabel->setParent(this); |
5572 | m_itemLabel->setParentItem(this); |
5573 | m_itemLabel->setVisible(false); |
5574 | } |
5575 | |
5576 | QQuick3DCustomMaterial *QQuickGraphsItem::createQmlCustomMaterial(const QString &fileName) |
5577 | { |
5578 | QQmlComponent component(qmlEngine(this), fileName); |
5579 | QQuick3DCustomMaterial *material = qobject_cast<QQuick3DCustomMaterial *>(object: component.create()); |
5580 | return material; |
5581 | } |
5582 | |
5583 | QQuick3DPrincipledMaterial *QQuickGraphsItem::createPrincipledMaterial() |
5584 | { |
5585 | QQmlComponent component(qmlEngine(this)); |
5586 | component.setData("import QtQuick3D; PrincipledMaterial{}", baseUrl: QUrl()); |
5587 | return qobject_cast<QQuick3DPrincipledMaterial *>(object: component.create()); |
5588 | } |
5589 | |
5590 | QtGraphs3D::CameraPreset QQuickGraphsItem::cameraPreset() const |
5591 | { |
5592 | return m_activePreset; |
5593 | } |
5594 | |
5595 | void QQuickGraphsItem::setCameraPreset(QtGraphs3D::CameraPreset preset) |
5596 | { |
5597 | switch (preset) { |
5598 | case QtGraphs3D::CameraPreset::FrontLow: { |
5599 | m_xRotation = 0.0f; |
5600 | m_yRotation = 0.0f; |
5601 | break; |
5602 | } |
5603 | case QtGraphs3D::CameraPreset::Front: { |
5604 | m_xRotation = 0.0f; |
5605 | m_yRotation = 22.5f; |
5606 | break; |
5607 | } |
5608 | case QtGraphs3D::CameraPreset::FrontHigh: { |
5609 | m_xRotation = 0.0f; |
5610 | m_yRotation = 45.0f; |
5611 | break; |
5612 | } |
5613 | case QtGraphs3D::CameraPreset::LeftLow: { |
5614 | m_xRotation = 90.0f; |
5615 | m_yRotation = 0.0f; |
5616 | break; |
5617 | } |
5618 | case QtGraphs3D::CameraPreset::Left: { |
5619 | m_xRotation = 90.0f; |
5620 | m_yRotation = 22.5f; |
5621 | break; |
5622 | } |
5623 | case QtGraphs3D::CameraPreset::LeftHigh: { |
5624 | m_xRotation = 90.0f; |
5625 | m_yRotation = 45.0f; |
5626 | break; |
5627 | } |
5628 | case QtGraphs3D::CameraPreset::RightLow: { |
5629 | m_xRotation = -90.0f; |
5630 | m_yRotation = 0.0f; |
5631 | break; |
5632 | } |
5633 | case QtGraphs3D::CameraPreset::Right: { |
5634 | m_xRotation = -90.0f; |
5635 | m_yRotation = 22.5f; |
5636 | break; |
5637 | } |
5638 | case QtGraphs3D::CameraPreset::RightHigh: { |
5639 | m_xRotation = -90.0f; |
5640 | m_yRotation = 45.0f; |
5641 | break; |
5642 | } |
5643 | case QtGraphs3D::CameraPreset::BehindLow: { |
5644 | m_xRotation = 180.0f; |
5645 | m_yRotation = 0.0f; |
5646 | break; |
5647 | } |
5648 | case QtGraphs3D::CameraPreset::Behind: { |
5649 | m_xRotation = 180.0f; |
5650 | m_yRotation = 22.5f; |
5651 | break; |
5652 | } |
5653 | case QtGraphs3D::CameraPreset::BehindHigh: { |
5654 | m_xRotation = 180.0f; |
5655 | m_yRotation = 45.0f; |
5656 | break; |
5657 | } |
5658 | case QtGraphs3D::CameraPreset::IsometricLeft: { |
5659 | m_xRotation = 45.0f; |
5660 | m_yRotation = 22.5f; |
5661 | break; |
5662 | } |
5663 | case QtGraphs3D::CameraPreset::IsometricLeftHigh: { |
5664 | m_xRotation = 45.0f; |
5665 | m_yRotation = 45.0f; |
5666 | break; |
5667 | } |
5668 | case QtGraphs3D::CameraPreset::IsometricRight: { |
5669 | m_xRotation = -45.0f; |
5670 | m_yRotation = 22.5f; |
5671 | break; |
5672 | } |
5673 | case QtGraphs3D::CameraPreset::IsometricRightHigh: { |
5674 | m_xRotation = -45.0f; |
5675 | m_yRotation = 45.0f; |
5676 | break; |
5677 | } |
5678 | case QtGraphs3D::CameraPreset::DirectlyAbove: { |
5679 | m_xRotation = 0.0f; |
5680 | m_yRotation = 90.0f; |
5681 | break; |
5682 | } |
5683 | case QtGraphs3D::CameraPreset::DirectlyAboveCW45: { |
5684 | m_xRotation = -45.0f; |
5685 | m_yRotation = 90.0f; |
5686 | break; |
5687 | } |
5688 | case QtGraphs3D::CameraPreset::DirectlyAboveCCW45: { |
5689 | m_xRotation = 45.0f; |
5690 | m_yRotation = 90.0f; |
5691 | break; |
5692 | } |
5693 | case QtGraphs3D::CameraPreset::FrontBelow: { |
5694 | m_xRotation = 0.0f; |
5695 | m_yRotation = -45.0f; |
5696 | break; |
5697 | } |
5698 | case QtGraphs3D::CameraPreset::LeftBelow: { |
5699 | m_xRotation = 90.0f; |
5700 | m_yRotation = -45.0f; |
5701 | break; |
5702 | } |
5703 | case QtGraphs3D::CameraPreset::RightBelow: { |
5704 | m_xRotation = -90.0f; |
5705 | m_yRotation = -45.0f; |
5706 | break; |
5707 | } |
5708 | case QtGraphs3D::CameraPreset::BehindBelow: { |
5709 | m_xRotation = 180.0f; |
5710 | m_yRotation = -45.0f; |
5711 | break; |
5712 | } |
5713 | case QtGraphs3D::CameraPreset::DirectlyBelow: { |
5714 | m_xRotation = 0.0f; |
5715 | m_yRotation = -90.0f; |
5716 | break; |
5717 | } |
5718 | default: |
5719 | preset = QtGraphs3D::CameraPreset::NoPreset; |
5720 | break; |
5721 | } |
5722 | |
5723 | // All presets target the center of the graph |
5724 | setCameraTargetPosition(QVector3D()); |
5725 | |
5726 | if (m_activePreset != preset) { |
5727 | m_activePreset = preset; |
5728 | emit cameraPresetChanged(preset); |
5729 | } |
5730 | if (camera()) { |
5731 | updateCamera(); |
5732 | connect(sender: this, signal: &QQuickGraphsItem::cameraXRotationChanged, context: m_scene, slot: &Q3DScene::needRender); |
5733 | connect(sender: this, signal: &QQuickGraphsItem::cameraYRotationChanged, context: m_scene, slot: &Q3DScene::needRender); |
5734 | connect(sender: this, signal: &QQuickGraphsItem::cameraZoomLevelChanged, context: m_scene, slot: &Q3DScene::needRender); |
5735 | } |
5736 | } |
5737 | |
5738 | void QQuickGraphsItem::setCameraXRotation(float rotation) |
5739 | { |
5740 | if (m_wrapXRotation) |
5741 | rotation = Utils::wrapValue(value: rotation, min: m_minXRotation, max: m_maxXRotation); |
5742 | else |
5743 | rotation = qBound(min: m_minXRotation, val: rotation, max: m_maxXRotation); |
5744 | if (rotation != m_xRotation) { |
5745 | m_xRotation = rotation; |
5746 | emit cameraXRotationChanged(rotation: m_xRotation); |
5747 | } |
5748 | } |
5749 | |
5750 | void QQuickGraphsItem::setCameraYRotation(float rotation) |
5751 | { |
5752 | if (m_wrapYRotation) |
5753 | rotation = Utils::wrapValue(value: rotation, min: m_minYRotation, max: m_maxYRotation); |
5754 | else |
5755 | rotation = qBound(min: m_minYRotation, val: rotation, max: m_maxYRotation); |
5756 | if (rotation != m_yRotation) { |
5757 | m_yRotation = rotation; |
5758 | emit cameraYRotationChanged(rotation: m_yRotation); |
5759 | } |
5760 | } |
5761 | |
5762 | void QQuickGraphsItem::setMinCameraXRotation(float rotation) |
5763 | { |
5764 | if (m_minXRotation == rotation) |
5765 | return; |
5766 | |
5767 | m_minXRotation = rotation; |
5768 | emit minCameraXRotationChanged(rotation); |
5769 | } |
5770 | |
5771 | void QQuickGraphsItem::setMaxCameraXRotation(float rotation) |
5772 | { |
5773 | if (m_maxXRotation == rotation) |
5774 | return; |
5775 | |
5776 | m_maxXRotation = rotation; |
5777 | emit maxCameraXRotationChanged(rotation); |
5778 | } |
5779 | |
5780 | void QQuickGraphsItem::setMinCameraYRotation(float rotation) |
5781 | { |
5782 | if (m_minYRotation == rotation) |
5783 | return; |
5784 | |
5785 | m_minYRotation = rotation; |
5786 | emit minCameraYRotationChanged(rotation); |
5787 | } |
5788 | |
5789 | void QQuickGraphsItem::setMaxCameraYRotation(float rotation) |
5790 | { |
5791 | if (m_maxYRotation == rotation) |
5792 | return; |
5793 | |
5794 | m_maxYRotation = rotation; |
5795 | emit maxCameraYRotationChanged(rotation); |
5796 | } |
5797 | |
5798 | void QQuickGraphsItem::setZoomAtTargetEnabled(bool enable) |
5799 | { |
5800 | m_inputHandler->setZoomAtTargetEnabled(enable); |
5801 | } |
5802 | |
5803 | bool QQuickGraphsItem::zoomAtTargetEnabled() |
5804 | { |
5805 | return m_inputHandler->isZoomAtTargetEnabled(); |
5806 | } |
5807 | |
5808 | void QQuickGraphsItem::setZoomEnabled(bool enable) |
5809 | { |
5810 | m_inputHandler->setZoomEnabled(enable); |
5811 | } |
5812 | |
5813 | bool QQuickGraphsItem::zoomEnabled() |
5814 | { |
5815 | return m_inputHandler->isZoomEnabled(); |
5816 | } |
5817 | |
5818 | void QQuickGraphsItem::setSelectionEnabled(bool enable) |
5819 | { |
5820 | m_inputHandler->setSelectionEnabled(enable); |
5821 | } |
5822 | |
5823 | bool QQuickGraphsItem::selectionEnabled() |
5824 | { |
5825 | return m_inputHandler->isSelectionEnabled(); |
5826 | } |
5827 | |
5828 | void QQuickGraphsItem::setRotationEnabled(bool enable) |
5829 | { |
5830 | m_inputHandler->setRotationEnabled(enable); |
5831 | } |
5832 | |
5833 | bool QQuickGraphsItem::rotationEnabled() |
5834 | { |
5835 | return m_inputHandler->isRotationEnabled(); |
5836 | } |
5837 | |
5838 | void QQuickGraphsItem::unsetDefaultInputHandler() |
5839 | { |
5840 | m_inputHandler->unsetDefaultInputHandler(); |
5841 | } |
5842 | |
5843 | void QQuickGraphsItem::unsetDefaultTapHandler() |
5844 | { |
5845 | m_inputHandler->unsetDefaultTapHandler(); |
5846 | } |
5847 | |
5848 | void QQuickGraphsItem::unsetDefaultDragHandler() |
5849 | { |
5850 | m_inputHandler->unsetDefaultDragHandler(); |
5851 | } |
5852 | |
5853 | void QQuickGraphsItem::unsetDefaultWheelHandler() |
5854 | { |
5855 | m_inputHandler->unsetDefaultWheelHandler(); |
5856 | } |
5857 | |
5858 | void QQuickGraphsItem::unsetDefaultPinchHandler() |
5859 | { |
5860 | m_inputHandler->unsetDefaultPinchHandler(); |
5861 | } |
5862 | |
5863 | void QQuickGraphsItem::setDragButton(Qt::MouseButtons button) |
5864 | { |
5865 | m_inputHandler->setDragButton(button); |
5866 | } |
5867 | |
5868 | void QQuickGraphsItem::setDefaultInputHandler() |
5869 | { |
5870 | m_inputHandler->setDefaultInputHandler(); |
5871 | } |
5872 | |
5873 | void QQuickGraphsItem::setCameraZoomLevel(float level) |
5874 | { |
5875 | if (m_zoomLevel == level) |
5876 | return; |
5877 | |
5878 | m_zoomLevel = level; |
5879 | emit cameraZoomLevelChanged(zoomLevel: level); |
5880 | } |
5881 | |
5882 | void QQuickGraphsItem::setMinCameraZoomLevel(float level) |
5883 | { |
5884 | if (m_minZoomLevel == level || level < 1.f) |
5885 | return; |
5886 | |
5887 | m_minZoomLevel = level; |
5888 | emit minCameraZoomLevelChanged(zoomLevel: level); |
5889 | |
5890 | setMaxCameraZoomLevel(std::max(a: m_minZoomLevel, b: m_maxZoomLevel)); |
5891 | |
5892 | if (cameraZoomLevel() < level) |
5893 | setCameraZoomLevel(level); |
5894 | } |
5895 | |
5896 | void QQuickGraphsItem::setMaxCameraZoomLevel(float level) |
5897 | { |
5898 | if (m_maxZoomLevel == level) |
5899 | return; |
5900 | |
5901 | m_maxZoomLevel = level; |
5902 | emit maxCameraZoomLevelChanged(zoomLevel: level); |
5903 | |
5904 | setMinCameraZoomLevel(std::min(a: m_minZoomLevel, b: m_maxZoomLevel)); |
5905 | |
5906 | if (cameraZoomLevel() > level) |
5907 | setCameraZoomLevel(level); |
5908 | } |
5909 | |
5910 | void QQuickGraphsItem::setCameraTargetPosition(QVector3D target) |
5911 | { |
5912 | if (m_requestedTarget == target) |
5913 | return; |
5914 | |
5915 | m_requestedTarget = target; |
5916 | emit cameraTargetPositionChanged(target); |
5917 | } |
5918 | |
5919 | void QQuickGraphsItem::setCameraPosition(float horizontal, float vertical, float zoom) |
5920 | { |
5921 | setCameraZoomLevel(zoom); |
5922 | setCameraXRotation(horizontal); |
5923 | setCameraYRotation(vertical); |
5924 | } |
5925 | |
5926 | bool QQuickGraphsItem::event(QEvent *event) |
5927 | { |
5928 | return QQuickItem::event(event); |
5929 | } |
5930 | |
5931 | void QQuickGraphsItem::createSliceView() |
5932 | { |
5933 | if (m_sliceView) |
5934 | return; |
5935 | |
5936 | connect(sender: parentItem(), |
5937 | signal: &QQuickItem::widthChanged, |
5938 | context: this, |
5939 | slot: &QQuickGraphsItem::handleParentWidthChange); |
5940 | connect(sender: parentItem(), |
5941 | signal: &QQuickItem::heightChanged, |
5942 | context: this, |
5943 | slot: &QQuickGraphsItem::handleParentHeightChange); |
5944 | connect(sender: this, signal: &QQuickItem::heightChanged, |
5945 | context: this, |
5946 | slot: &QQuickGraphsItem::handleParentHeightChange); |
5947 | connect(sender: this, signal: &QQuickItem::widthChanged, |
5948 | context: this, |
5949 | slot: &QQuickGraphsItem::handleParentWidthChange); |
5950 | |
5951 | m_sliceView = new QQuick3DViewport(); |
5952 | m_sliceView->setParent(parent()); |
5953 | m_sliceView->setParentItem(parentItem()); |
5954 | m_sliceView->setVisible(false); |
5955 | m_sliceView->setWidth(parentItem()->width()); |
5956 | m_sliceView->setHeight(parentItem()->height()); |
5957 | m_sliceView->setZ(-1); |
5958 | m_sliceView->environment()->setBackgroundMode(QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes::Color); |
5959 | m_sliceView->environment()->setClearColor(environment()->clearColor()); |
5960 | m_sliceView->setRenderMode(renderMode()); |
5961 | |
5962 | auto scene = m_sliceView->scene(); |
5963 | |
5964 | createSliceCamera(); |
5965 | |
5966 | // auto gridDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine")); |
5967 | m_labelDelegate.reset(p: new QQmlComponent(qmlEngine(this), QStringLiteral(":/axis/AxisLabel"))); |
5968 | |
5969 | m_sliceGridGeometryModel = new QQuick3DModel(scene); |
5970 | |
5971 | auto sliceGridGeometry = new QQuick3DGeometry(m_sliceGridGeometryModel); |
5972 | sliceGridGeometry->setStride(sizeof(QVector3D)); |
5973 | sliceGridGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines); |
5974 | sliceGridGeometry->addAttribute(semantic: QQuick3DGeometry::Attribute::PositionSemantic, |
5975 | offset: 0, |
5976 | componentType: QQuick3DGeometry::Attribute::F32Type); |
5977 | m_sliceGridGeometryModel->setGeometry(sliceGridGeometry); |
5978 | |
5979 | QQmlListReference gridMaterialRef(m_sliceGridGeometryModel, "materials"); |
5980 | auto gridMaterial = new QQuick3DPrincipledMaterial(m_sliceGridGeometryModel); |
5981 | gridMaterial->setLighting(QQuick3DPrincipledMaterial::Lighting::NoLighting); |
5982 | gridMaterial->setCullMode(QQuick3DMaterial::CullMode::BackFaceCulling); |
5983 | gridMaterial->setBaseColor(Qt::red); |
5984 | gridMaterialRef.append(gridMaterial); |
5985 | |
5986 | m_sliceHorizontalLabelRepeater = createRepeater(parent: scene); |
5987 | m_sliceHorizontalLabelRepeater->setDelegate(m_labelDelegate.get()); |
5988 | |
5989 | m_sliceVerticalLabelRepeater = createRepeater(parent: scene); |
5990 | m_sliceVerticalLabelRepeater->setDelegate(m_labelDelegate.get()); |
5991 | |
5992 | m_sliceHorizontalTitleLabel = createTitleLabel(parent: scene); |
5993 | m_sliceHorizontalTitleLabel->setVisible(true); |
5994 | |
5995 | m_sliceVerticalTitleLabel = createTitleLabel(parent: scene); |
5996 | m_sliceVerticalTitleLabel->setVisible(true); |
5997 | |
5998 | m_sliceItemLabel = createTitleLabel(parent: scene); |
5999 | m_sliceItemLabel->setVisible(false); |
6000 | } |
6001 | |
6002 | void QQuickGraphsItem::createSliceCamera() |
6003 | { |
6004 | if (isSliceOrthoProjection()) { |
6005 | auto camera = new QQuick3DOrthographicCamera(sliceView()->scene()); |
6006 | camera->setPosition(QVector3D(.0f, .0f, 20.0f)); |
6007 | const float scale = qMin(a: sliceView()->width(), b: sliceView()->height()); |
6008 | const float magnificationScaleFactor = 2 * window()->devicePixelRatio() |
6009 | * .08f; // this controls the size of the slice view |
6010 | const float magnification = scale * magnificationScaleFactor; |
6011 | camera->setHorizontalMagnification(magnification); |
6012 | camera->setVerticalMagnification(magnification); |
6013 | sliceView()->setCamera(camera); |
6014 | |
6015 | auto light = new QQuick3DDirectionalLight(sliceView()->scene()); |
6016 | light->setParent(camera); |
6017 | light->setParentItem(camera); |
6018 | } else { |
6019 | auto camera = new QQuick3DPerspectiveCamera(sliceView()->scene()); |
6020 | camera->setFieldOfViewOrientation( |
6021 | QQuick3DPerspectiveCamera::FieldOfViewOrientation::Vertical); |
6022 | camera->setClipNear(5.f); |
6023 | camera->setClipFar(15.f); |
6024 | camera->setFieldOfView(35.f); |
6025 | camera->setPosition(QVector3D(.0f, .0f, 10.f)); |
6026 | sliceView()->setCamera(camera); |
6027 | |
6028 | auto light = new QQuick3DDirectionalLight(sliceView()->scene()); |
6029 | light->setParent(camera); |
6030 | light->setParentItem(camera); |
6031 | light->setAmbientColor(QColor::fromRgbF(r: 1.f, g: 1.f, b: 1.f)); |
6032 | } |
6033 | } |
6034 | |
6035 | void QQuickGraphsItem::updateSliceGrid() |
6036 | { |
6037 | QAbstract3DAxis *horizontalAxis = nullptr; |
6038 | QAbstract3DAxis *verticalAxis = axisY(); |
6039 | auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin; |
6040 | float scale; |
6041 | float translate; |
6042 | |
6043 | float horizontalScale = 0.0f; |
6044 | |
6045 | if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row)) { |
6046 | horizontalAxis = axisX(); |
6047 | horizontalScale = backgroundScale.x(); |
6048 | scale = m_scaleWithBackground.x(); |
6049 | translate = m_scaleWithBackground.x(); |
6050 | } else if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Column)) { |
6051 | horizontalAxis = axisZ(); |
6052 | horizontalScale = backgroundScale.z(); |
6053 | scale = m_scaleWithBackground.z(); |
6054 | translate = m_scaleWithBackground.z(); |
6055 | } |
6056 | |
6057 | if (horizontalAxis == nullptr) { |
6058 | qWarning(msg: "Invalid axis type"); |
6059 | return; |
6060 | } |
6061 | int lineCount = 0; |
6062 | if (m_hasVerticalSegmentLine || isPolar()) { |
6063 | if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Value) { |
6064 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(horizontalAxis); |
6065 | lineCount += valueAxis->gridSize() + valueAxis->subGridSize(); |
6066 | } else if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Category) { |
6067 | lineCount += horizontalAxis->labels().size(); |
6068 | } |
6069 | } |
6070 | |
6071 | if (verticalAxis->type() == QAbstract3DAxis::AxisType::Value) { |
6072 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(verticalAxis); |
6073 | lineCount += valueAxis->gridSize() + valueAxis->subGridSize(); |
6074 | } else if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Category) { |
6075 | lineCount += verticalAxis->labels().size(); |
6076 | } |
6077 | |
6078 | QByteArray vertices; |
6079 | vertices.resize(size: lineCount * 2 * sizeof(QVector3D)); |
6080 | auto data = reinterpret_cast<QVector3D *>(vertices.data()); |
6081 | float linePosX = .0f; |
6082 | float linePosY = .0f; |
6083 | const float linePosZ = -1.f; // Draw grid lines behind slice (especially for surface) |
6084 | |
6085 | float x0, x1; |
6086 | float y0, y1; |
6087 | y0 = -backgroundScale.y(); |
6088 | y1 = backgroundScale.y(); |
6089 | if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Value) { |
6090 | auto axis = static_cast<QValue3DAxis *>(horizontalAxis); |
6091 | for (int i = 0; i < axis->subGridSize(); i++) { |
6092 | linePosX = axis->subGridPositionAt(gridLine: i) * scale * 2.0f - translate; |
6093 | *data++ = QVector3D(linePosX, y0, linePosZ); |
6094 | *data++ = QVector3D(linePosX, y1, linePosZ); |
6095 | } |
6096 | for (int i = 0; i < axis->gridSize(); i++) { |
6097 | linePosX = axis->gridPositionAt(gridLine: i) * scale * 2.0f - translate; |
6098 | *data++ = QVector3D(linePosX, y0, linePosZ); |
6099 | *data++ = QVector3D(linePosX, y1, linePosZ); |
6100 | } |
6101 | } |
6102 | |
6103 | scale = m_scaleWithBackground.y(); |
6104 | translate = m_scaleWithBackground.y(); |
6105 | |
6106 | x0 = horizontalScale * 1.1f; |
6107 | x1 = -horizontalScale * 1.1f; |
6108 | if (verticalAxis->type() == QAbstract3DAxis::AxisType::Value) { |
6109 | auto axis = static_cast<QValue3DAxis *>(verticalAxis); |
6110 | for (int i = 0; i < axis->gridSize(); i++) { |
6111 | linePosY = axis->gridPositionAt(gridLine: i) * scale * 2.0f - translate; |
6112 | *data++ = QVector3D(x0, linePosY, linePosZ); |
6113 | *data++ = QVector3D(x1, linePosY, linePosZ); |
6114 | } |
6115 | for (int i = 0; i < axis->subGridSize(); i++) { |
6116 | linePosY = axis->subGridPositionAt(gridLine: i) * scale * 2.0f - translate; |
6117 | *data++ = QVector3D(x0, linePosY, linePosZ); |
6118 | *data++ = QVector3D(x1, linePosY, linePosZ); |
6119 | } |
6120 | } else if (verticalAxis->type() == QAbstract3DAxis::AxisType::Category) { |
6121 | for (int i = 0; i < verticalAxis->labels().size(); i++) { |
6122 | linePosY = calculateCategoryGridLinePosition(axis: verticalAxis, index: i); |
6123 | *data++ = QVector3D(x0, linePosY, linePosZ); |
6124 | *data++ = QVector3D(x1, linePosY, linePosZ); |
6125 | } |
6126 | } |
6127 | |
6128 | auto geometry = m_sliceGridGeometryModel->geometry(); |
6129 | geometry->setVertexData(vertices); |
6130 | geometry->update(); |
6131 | |
6132 | QQmlListReference materialRef(m_sliceGridGeometryModel, "materials"); |
6133 | auto material = static_cast<QQuick3DPrincipledMaterial *>(materialRef.at(0)); |
6134 | material->setBaseColor(theme()->grid().mainColor()); |
6135 | } |
6136 | |
6137 | void QQuickGraphsItem::updateSliceLabels() |
6138 | { |
6139 | QAbstract3DAxis *horizontalAxis = nullptr; |
6140 | QAbstract3DAxis *verticalAxis = axisY(); |
6141 | auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin; |
6142 | float scale; |
6143 | float translate; |
6144 | QColor horizontalLabelTextColor; |
6145 | |
6146 | if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row)) { |
6147 | horizontalAxis = axisX(); |
6148 | scale = backgroundScale.x() - m_backgroundScaleMargin.x(); |
6149 | translate = backgroundScale.x() - m_backgroundScaleMargin.x(); |
6150 | horizontalLabelTextColor = theme()->axisX().labelTextColor(); |
6151 | } else if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Column)) { |
6152 | horizontalAxis = axisZ(); |
6153 | scale = backgroundScale.z() - m_backgroundScaleMargin.z(); |
6154 | translate = backgroundScale.z() - m_backgroundScaleMargin.z(); |
6155 | horizontalLabelTextColor = theme()->axisZ().labelTextColor(); |
6156 | } |
6157 | |
6158 | if (horizontalAxis == nullptr) { |
6159 | qWarning(msg: "Invalid selection mode"); |
6160 | return; |
6161 | } |
6162 | |
6163 | if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Value) { |
6164 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(horizontalAxis); |
6165 | m_sliceHorizontalLabelRepeater->model().clear(); |
6166 | m_sliceHorizontalLabelRepeater->setModel(valueAxis->labels().size()); |
6167 | } else if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Category) { |
6168 | m_sliceHorizontalLabelRepeater->model().clear(); |
6169 | m_sliceHorizontalLabelRepeater->setModel(horizontalAxis->labels().size()); |
6170 | } |
6171 | |
6172 | if (verticalAxis->type() == QAbstract3DAxis::AxisType::Value) { |
6173 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(verticalAxis); |
6174 | m_sliceVerticalLabelRepeater->model().clear(); |
6175 | m_sliceVerticalLabelRepeater->setModel(valueAxis->labels().size()); |
6176 | } else if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Category) { |
6177 | m_sliceVerticalLabelRepeater->model().clear(); |
6178 | m_sliceVerticalLabelRepeater->setModel(verticalAxis->labels().size()); |
6179 | } |
6180 | |
6181 | float textPadding = 12.0f; |
6182 | float labelsMaxWidth = float(findLabelsMaxWidth(labels: horizontalAxis->labels())) + textPadding; |
6183 | QFontMetrics fm(theme()->labelFont()); |
6184 | float labelHeight = fm.height() + textPadding; |
6185 | |
6186 | float pointSize = theme()->labelFont().pointSizeF(); |
6187 | float scaleFactor = fontScaleFactor(pointSize) * pointSize; |
6188 | float fontRatio = labelsMaxWidth / labelHeight; |
6189 | QVector3D fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f); |
6190 | |
6191 | float adjustment = labelsMaxWidth * scaleFactor; |
6192 | float yPos = backgroundScale.y() + adjustment; |
6193 | |
6194 | QVector3D labelTrans = QVector3D(0.0f, -yPos, 0.0f); |
6195 | QStringList labels = horizontalAxis->labels(); |
6196 | QFont font = theme()->labelFont(); |
6197 | bool borderVisible = theme()->isLabelBorderVisible(); |
6198 | |
6199 | bool backgroundVisible = theme()->isLabelBackgroundVisible(); |
6200 | QColor backgroundColor = theme()->labelBackgroundColor(); |
6201 | |
6202 | if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Value) { |
6203 | for (int i = 0; i < m_sliceHorizontalLabelRepeater->count(); i++) { |
6204 | auto obj = static_cast<QQuick3DNode *>(m_sliceHorizontalLabelRepeater->objectAt(index: i)); |
6205 | // It is important to use the position of vertical grids so that they can be in the same |
6206 | // position when col/row ranges are updated. |
6207 | float linePosX = static_cast<QValue3DAxis *>(horizontalAxis)->gridPositionAt(gridLine: i) * scale |
6208 | * 2.0f |
6209 | - translate; |
6210 | labelTrans.setX(linePosX); |
6211 | labelTrans.setY(-yPos - adjustment); |
6212 | obj->setScale(fontScaled); |
6213 | obj->setPosition(labelTrans); |
6214 | obj->setProperty(name: "labelText", value: labels[i]); |
6215 | obj->setProperty(name: "labelWidth", value: labelsMaxWidth); |
6216 | obj->setProperty(name: "labelHeight", value: labelHeight); |
6217 | obj->setProperty(name: "labelFont", value: font); |
6218 | obj->setProperty(name: "borderVisible", value: borderVisible); |
6219 | obj->setProperty(name: "labelTextColor", value: horizontalLabelTextColor); |
6220 | obj->setProperty(name: "backgroundVisible", value: backgroundVisible); |
6221 | obj->setProperty(name: "backgroundColor", value: backgroundColor); |
6222 | obj->setEulerRotation(QVector3D(.0f, .0f, -45.0f)); |
6223 | } |
6224 | } else if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Category) { |
6225 | for (int i = 0; i < m_sliceHorizontalLabelRepeater->count(); i++) { |
6226 | labelTrans = calculateCategoryLabelPosition(axis: horizontalAxis, labelPosition: labelTrans, index: i); |
6227 | labelTrans.setY(-yPos /*- (adjustment / 2.f)*/); |
6228 | if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Column)) |
6229 | labelTrans.setX(labelTrans.z()); |
6230 | labelTrans.setZ(1.0f); // Bring the labels on top of bars and grid |
6231 | auto obj = static_cast<QQuick3DNode *>(m_sliceHorizontalLabelRepeater->objectAt(index: i)); |
6232 | obj->setScale(fontScaled); |
6233 | obj->setPosition(labelTrans); |
6234 | obj->setProperty(name: "labelText", value: labels[i]); |
6235 | obj->setProperty(name: "labelWidth", value: labelsMaxWidth); |
6236 | obj->setProperty(name: "labelHeight", value: labelHeight); |
6237 | obj->setProperty(name: "labelFont", value: font); |
6238 | obj->setProperty(name: "borderVisible", value: borderVisible); |
6239 | obj->setProperty(name: "labelTextColor", value: horizontalLabelTextColor); |
6240 | obj->setProperty(name: "backgroundVisible", value: backgroundVisible); |
6241 | obj->setProperty(name: "backgroundColor", value: backgroundColor); |
6242 | obj->setEulerRotation(QVector3D(0.0f, 0.0f, -60.0f)); |
6243 | } |
6244 | } |
6245 | |
6246 | scale = backgroundScale.y() - m_backgroundScaleMargin.y(); |
6247 | translate = backgroundScale.y() - m_backgroundScaleMargin.y(); |
6248 | labels = verticalAxis->labels(); |
6249 | labelsMaxWidth = float(findLabelsMaxWidth(labels)) + textPadding; |
6250 | // Since labelsMaxWidth changes for each axis, these needs to be recalculated for scaling. |
6251 | fontRatio = labelsMaxWidth / labelHeight; |
6252 | fontScaled.setX(scaleFactor * fontRatio); |
6253 | adjustment = labelsMaxWidth * scaleFactor; |
6254 | float xPos = 0.0f; |
6255 | if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row)) |
6256 | xPos = backgroundScale.x() + (adjustment * 1.5f); |
6257 | else if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Column)) |
6258 | xPos = backgroundScale.z() + (adjustment * 1.5f); |
6259 | labelTrans = QVector3D(xPos, 0.0f, 0.0f); |
6260 | QColor verticalLabelTextColor = theme()->axisY().labelTextColor(); |
6261 | |
6262 | if (verticalAxis->type() == QAbstract3DAxis::AxisType::Value) { |
6263 | auto valueAxis = static_cast<QValue3DAxis *>(verticalAxis); |
6264 | for (int i = 0; i < m_sliceVerticalLabelRepeater->count(); i++) { |
6265 | auto obj = static_cast<QQuick3DNode *>(m_sliceVerticalLabelRepeater->objectAt(index: i)); |
6266 | labelTrans.setY(valueAxis->labelPositionAt(index: i) * scale * 2.0f - translate); |
6267 | obj->setScale(fontScaled); |
6268 | obj->setPosition(labelTrans); |
6269 | obj->setProperty(name: "labelText", value: labels[i]); |
6270 | obj->setProperty(name: "labelWidth", value: labelsMaxWidth); |
6271 | obj->setProperty(name: "labelHeight", value: labelHeight); |
6272 | obj->setProperty(name: "labelFont", value: font); |
6273 | obj->setProperty(name: "borderVisible", value: borderVisible); |
6274 | obj->setProperty(name: "labelTextColor", value: verticalLabelTextColor); |
6275 | obj->setProperty(name: "backgroundVisible", value: backgroundVisible); |
6276 | obj->setProperty(name: "backgroundColor", value: backgroundColor); |
6277 | } |
6278 | } else if (verticalAxis->type() == QAbstract3DAxis::AxisType::Category) { |
6279 | for (int i = 0; i < m_sliceVerticalLabelRepeater->count(); i++) { |
6280 | labelTrans = calculateCategoryLabelPosition(axis: verticalAxis, labelPosition: labelTrans, index: i); |
6281 | auto obj = static_cast<QQuick3DNode *>(m_sliceVerticalLabelRepeater->objectAt(index: i)); |
6282 | obj->setScale(fontScaled); |
6283 | obj->setPosition(labelTrans); |
6284 | obj->setProperty(name: "labelText", value: labels[i]); |
6285 | obj->setProperty(name: "labelWidth", value: labelsMaxWidth); |
6286 | obj->setProperty(name: "labelHeight", value: labelHeight); |
6287 | obj->setProperty(name: "labelFont", value: font); |
6288 | obj->setProperty(name: "borderVisible", value: borderVisible); |
6289 | obj->setProperty(name: "labelTextColor", value: verticalLabelTextColor); |
6290 | obj->setProperty(name: "backgroundVisible", value: backgroundVisible); |
6291 | obj->setProperty(name: "backgroundColor", value: backgroundColor); |
6292 | } |
6293 | } |
6294 | |
6295 | labelHeight = fm.height() + textPadding; |
6296 | float labelWidth = fm.horizontalAdvance(verticalAxis->title()) + textPadding; |
6297 | QVector3D vTitleScale = fontScaled; |
6298 | vTitleScale.setX(fontScaled.y() * labelWidth / labelHeight); |
6299 | adjustment = labelHeight * scaleFactor; |
6300 | if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row)) |
6301 | xPos = backgroundScale.x() + adjustment; |
6302 | else if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Column)) |
6303 | xPos = backgroundScale.z() + adjustment; |
6304 | labelTrans = QVector3D(-(xPos + adjustment), 0.0f, 0.0f); |
6305 | |
6306 | if (!verticalAxis->title().isEmpty()) { |
6307 | m_sliceVerticalTitleLabel->setScale(vTitleScale); |
6308 | m_sliceVerticalTitleLabel->setPosition(labelTrans); |
6309 | m_sliceVerticalTitleLabel->setProperty(name: "labelWidth", value: labelWidth); |
6310 | m_sliceVerticalTitleLabel->setProperty(name: "labelHeight", value: labelHeight); |
6311 | m_sliceVerticalTitleLabel->setProperty(name: "labelText", value: verticalAxis->title()); |
6312 | m_sliceVerticalTitleLabel->setProperty(name: "labelFont", value: font); |
6313 | m_sliceVerticalTitleLabel->setProperty(name: "borderVisible", value: borderVisible); |
6314 | m_sliceVerticalTitleLabel->setProperty(name: "labelTextColor", value: verticalLabelTextColor); |
6315 | m_sliceVerticalTitleLabel->setProperty(name: "backgroundVisible", value: backgroundVisible); |
6316 | m_sliceVerticalTitleLabel->setProperty(name: "backgroundColor", value: backgroundColor); |
6317 | m_sliceVerticalTitleLabel->setEulerRotation(QVector3D(.0f, .0f, 90.0f)); |
6318 | } else { |
6319 | m_sliceVerticalTitleLabel->setVisible(false); |
6320 | } |
6321 | |
6322 | labelHeight = fm.height() + textPadding; |
6323 | labelWidth = fm.horizontalAdvance(horizontalAxis->title()) + textPadding; |
6324 | QVector3D hTitleScale = fontScaled; |
6325 | hTitleScale.setX(fontScaled.y() * labelWidth / labelHeight); |
6326 | adjustment = labelHeight * scaleFactor; |
6327 | yPos = backgroundScale.y() * 1.5f + (adjustment * 6.f); |
6328 | labelTrans = QVector3D(0.0f, -yPos, 0.0f); |
6329 | |
6330 | if (!horizontalAxis->title().isEmpty()) { |
6331 | m_sliceHorizontalTitleLabel->setScale(hTitleScale); |
6332 | m_sliceHorizontalTitleLabel->setPosition(labelTrans); |
6333 | m_sliceHorizontalTitleLabel->setProperty(name: "labelWidth", value: labelWidth); |
6334 | m_sliceHorizontalTitleLabel->setProperty(name: "labelHeight", value: labelHeight); |
6335 | m_sliceHorizontalTitleLabel->setProperty(name: "labelText", value: horizontalAxis->title()); |
6336 | m_sliceHorizontalTitleLabel->setProperty(name: "labelFont", value: font); |
6337 | m_sliceHorizontalTitleLabel->setProperty(name: "borderVisible", value: borderVisible); |
6338 | m_sliceHorizontalTitleLabel->setProperty(name: "labelTextColor", value: horizontalLabelTextColor); |
6339 | m_sliceHorizontalTitleLabel->setProperty(name: "backgroundVisible", value: backgroundVisible); |
6340 | m_sliceHorizontalTitleLabel->setProperty(name: "backgroundColor", value: backgroundColor); |
6341 | } else { |
6342 | m_sliceHorizontalTitleLabel->setVisible(false); |
6343 | } |
6344 | |
6345 | m_sliceItemLabel->setProperty(name: "labelFont", value: font); |
6346 | m_sliceItemLabel->setProperty(name: "borderVisible", value: borderVisible); |
6347 | m_sliceItemLabel->setProperty(name: "labelTextColor", value: theme()->labelTextColor()); |
6348 | m_sliceItemLabel->setProperty(name: "backgroundVisible", value: backgroundVisible); |
6349 | m_sliceItemLabel->setProperty(name: "backgroundColor", value: backgroundColor); |
6350 | } |
6351 | |
6352 | void QQuickGraphsItem::setUpCamera() |
6353 | { |
6354 | // By default we could get away with a value of 10 or 15, but as camera zoom is implemented |
6355 | // by moving it, we have to take into account the maximum zoom out level. The other |
6356 | // option would be to adjust far clip whenever zoom level changes. |
6357 | const float farclip = 700.f; |
6358 | |
6359 | m_pCamera = new QQuick3DPerspectiveCamera(rootNode()); |
6360 | m_pCamera->setClipNear(0.001f); |
6361 | m_pCamera->setClipFar(farclip); |
6362 | m_pCamera->setFieldOfView(45.0f); |
6363 | m_pCamera->setPosition(QVector3D(.0f, .0f, 5.f)); |
6364 | |
6365 | auto cameraTarget = new QQuick3DNode(rootNode()); |
6366 | cameraTarget->setParentItem(rootNode()); |
6367 | |
6368 | setCameraTarget(cameraTarget); |
6369 | cameraTarget->setPosition(QVector3D(0, 0, 0)); |
6370 | QQuick3DObjectPrivate::get(item: cameraTarget) |
6371 | ->refSceneManager(*QQuick3DObjectPrivate::get(item: rootNode())->sceneManager); |
6372 | |
6373 | m_pCamera->lookAt(node: cameraTarget); |
6374 | m_pCamera->setParent(cameraTarget); |
6375 | m_pCamera->setParentItem(cameraTarget); |
6376 | |
6377 | m_oCamera = new QQuick3DOrthographicCamera(rootNode()); |
6378 | // Set clip near 0.0001f so that it can be set correct value to workaround |
6379 | // a Quick3D device pixel ratio bug |
6380 | m_oCamera->setClipNear(0.0001f); |
6381 | m_oCamera->setClipFar(farclip); |
6382 | m_oCamera->setPosition(QVector3D(0.f, 0.f, 5.f)); |
6383 | m_oCamera->setParent(cameraTarget); |
6384 | m_oCamera->setParentItem(cameraTarget); |
6385 | m_oCamera->lookAt(node: cameraTarget); |
6386 | |
6387 | auto useOrtho = isOrthoProjection(); |
6388 | if (useOrtho) |
6389 | setCamera(m_oCamera); |
6390 | else |
6391 | setCamera(m_pCamera); |
6392 | } |
6393 | |
6394 | void QQuickGraphsItem::setUpLight() |
6395 | { |
6396 | auto light = new QQuick3DDirectionalLight(rootNode()); |
6397 | QQuick3DObjectPrivate::get(item: light)->refSceneManager( |
6398 | *QQuick3DObjectPrivate::get(item: rootNode())->sceneManager); |
6399 | light->setParent(camera()); |
6400 | light->setParentItem(camera()); |
6401 | light->setShadowBias(0.1f); |
6402 | light->setSoftShadowQuality(QQuick3DAbstractLight::QSSGSoftShadowQuality::Hard); |
6403 | m_light = light; |
6404 | } |
6405 | |
6406 | void QQuickGraphsItem::setWrapCameraXRotation(bool wrap) |
6407 | { |
6408 | if (m_wrapXRotation == wrap) |
6409 | return; |
6410 | m_wrapXRotation = wrap; |
6411 | emit wrapCameraXRotationChanged(wrap); |
6412 | } |
6413 | |
6414 | void QQuickGraphsItem::setWrapCameraYRotation(bool wrap) |
6415 | { |
6416 | if (m_wrapYRotation == wrap) |
6417 | return; |
6418 | m_wrapYRotation = wrap; |
6419 | emit wrapCameraYRotationChanged(wrap); |
6420 | } |
6421 | |
6422 | float QQuickGraphsItem::ambientLightStrength() const |
6423 | { |
6424 | return m_ambientLightStrength; |
6425 | } |
6426 | |
6427 | void QQuickGraphsItem::setAmbientLightStrength(float newAmbientLightStrength) |
6428 | { |
6429 | if (qFuzzyCompare(p1: m_ambientLightStrength, p2: newAmbientLightStrength)) |
6430 | return; |
6431 | |
6432 | if (newAmbientLightStrength < 0.0f || newAmbientLightStrength > 1.0f) { |
6433 | qWarning(msg: "Invalid value. Valid range for ambientLightStrength is between " |
6434 | "0.0f and 1.0f"); |
6435 | } else { |
6436 | m_ambientLightStrengthDirty = true; |
6437 | m_ambientLightStrength = newAmbientLightStrength; |
6438 | emit ambientLightStrengthChanged(); |
6439 | emitNeedRender(); |
6440 | } |
6441 | } |
6442 | |
6443 | float QQuickGraphsItem::lightStrength() const |
6444 | { |
6445 | return m_lightStrength; |
6446 | } |
6447 | |
6448 | void QQuickGraphsItem::setLightStrength(float newLightStrength) |
6449 | { |
6450 | if (qFuzzyCompare(p1: m_lightStrength, p2: newLightStrength)) |
6451 | return; |
6452 | |
6453 | if (newLightStrength < 0.0f || newLightStrength > 10.0f) { |
6454 | qWarning(msg: "Invalid value. Valid range for lightStrength is between 0.0f and " |
6455 | "10.0f"); |
6456 | } else { |
6457 | m_lightStrengthDirty = true; |
6458 | m_lightStrength = newLightStrength; |
6459 | emit lightStrengthChanged(); |
6460 | emitNeedRender(); |
6461 | } |
6462 | } |
6463 | |
6464 | float QQuickGraphsItem::shadowStrength() const |
6465 | { |
6466 | return m_shadowStrength; |
6467 | } |
6468 | |
6469 | void QQuickGraphsItem::setShadowStrength(float newShadowStrength) |
6470 | { |
6471 | if (qFuzzyCompare(p1: m_shadowStrength, p2: newShadowStrength)) |
6472 | return; |
6473 | |
6474 | if (newShadowStrength < 0.0f || newShadowStrength > 100.0f) { |
6475 | qWarning(msg: "Invalid value. Valid range for shadowStrength is between 0.0f " |
6476 | "and 100.0f"); |
6477 | } else { |
6478 | m_shadowStrengthDirty = true; |
6479 | m_shadowStrength = newShadowStrength; |
6480 | emit shadowStrengthChanged(); |
6481 | emitNeedRender(); |
6482 | } |
6483 | } |
6484 | |
6485 | QColor QQuickGraphsItem::lightColor() const |
6486 | { |
6487 | return m_lightColor; |
6488 | } |
6489 | |
6490 | void QQuickGraphsItem::setLightColor(QColor newLightColor) |
6491 | { |
6492 | if (m_lightColor == newLightColor) |
6493 | return; |
6494 | m_lightColorDirty = true; |
6495 | m_lightColor = newLightColor; |
6496 | emit lightColorChanged(); |
6497 | emitNeedRender(); |
6498 | } |
6499 | |
6500 | void QQuickGraphsItem::updateBackgroundColor() |
6501 | { |
6502 | if (theme()->isBackgroundVisible()) |
6503 | environment()->setClearColor(theme()->backgroundColor()); |
6504 | else |
6505 | environment()->setClearColor(Qt::transparent); |
6506 | |
6507 | if (m_sliceView) |
6508 | m_sliceView->environment()->setClearColor(environment()->clearColor()); |
6509 | |
6510 | } |
6511 | |
6512 | void QQuickGraphsItem::setItemSelected(bool selected) |
6513 | { |
6514 | m_itemSelected = selected; |
6515 | } |
6516 | |
6517 | QT_END_NAMESPACE |
6518 |
Definitions
- doublePi
- polarRoundness
- QQuickGraphsItem
- ~QQuickGraphsItem
- handleAxisTitleChanged
- handleAxisTitleChangedBySender
- handleAxisLabelsChanged
- handleAxisLabelsChangedBySender
- handleAxisRangeChanged
- handleAxisRangeChangedBySender
- handleAxisSegmentCountChanged
- handleAxisSegmentCountChangedBySender
- handleAxisSubSegmentCountChanged
- handleAxisSubSegmentCountChangedBySender
- handleAxisAutoAdjustRangeChanged
- handleAxisLabelFormatChanged
- handleAxisReversedChanged
- handleAxisFormatterDirty
- handleAxisLabelAutoRotationChanged
- handleAxisTitleVisibilityChanged
- handleAxisLabelVisibilityChanged
- handleAxisTitleFixedChanged
- handleAxisTitleOffsetChanged
- handleInputPositionChanged
- handleSeriesVisibilityChanged
- handleRequestShadowQuality
- handleQueryPositionChanged
- handlePrimarySubViewportChanged
- handleSecondarySubViewportChanged
- handleAxisLabelFormatChangedBySender
- handleAxisReversedChangedBySender
- handleAxisFormatterDirtyBySender
- handleAxisLabelAutoRotationChangedBySender
- handleAxisTitleVisibilityChangedBySender
- handleAxisLabelVisibilityChangedBySender
- handleAxisTitleFixedChangedBySender
- handleAxisTitleOffsetChangedBySender
- handleSeriesVisibilityChangedBySender
- markDataDirty
- markSeriesVisualsDirty
- markSeriesItemLabelsDirty
- createDefaultAxis
- createDefaultValueAxis
- createDefaultCategoryAxis
- setAxisHelper
- startRecordingRemovesAndInserts
- horizontalFlipFactor
- setHorizontalFlipFactor
- emitNeedRender
- handleThemeColorStyleChanged
- handleThemeBaseColorsChanged
- handleThemeBaseGradientsChanged
- handleThemeSingleHighlightColorChanged
- handleThemeSingleHighlightGradientChanged
- handleThemeMultiHighlightColorChanged
- handleThemeMultiHighlightGradientChanged
- handleThemeTypeChanged
- addSeriesInternal
- insertSeries
- removeSeriesInternal
- seriesList
- setAxisX
- axisX
- setAxisY
- axisY
- setAxisZ
- axisZ
- addAxis
- releaseAxis
- axes
- setRenderingMode
- renderingMode
- keyPressEvent
- checkSliceEnabled
- gridLineType
- setGridLineType
- handleThemeTypeChange
- handleFpsChanged
- handleParentWidthChange
- handleParentHeightChange
- componentComplete
- light
- isSlicingActive
- setSlicingActive
- isCustomLabelItem
- isCustomVolumeItem
- customTextureImage
- scene
- addTheme
- releaseTheme
- themes
- setTheme
- theme
- hasSeries
- setSelectionMode
- selectionMode
- doSetShadowQuality
- setShadowQuality
- shadowQuality
- addCustomItem
- deleteCustomItems
- deleteCustomItem
- deleteCustomItem
- customItems
- updateCustomItem
- removeCustomItems
- removeCustomItem
- removeCustomItemAt
- releaseCustomItem
- selectedLabelIndex
- selectedAxis
- selectedCustomItemIndex
- selectedCustomItem
- customItemList
- appendCustomItemFunc
- countCustomItemFunc
- atCustomItemFunc
- clearCustomItemFunc
- synchData
- updateGrid
- updateGridLineType
- fontScaleFactor
- labelAdjustment
- gridLineCountHelper
- graphPosToAbsolute
- updateLabels
- updateRadialLabelOffset
- positionAndScaleLine
- graphPositionAt
- updateShadowQuality
- updateItemLabel
- updateSliceItemLabel
- createVolumeMaterial
- createSliceFrame
- updateSliceFrameMaterials
- updateCustomVolumes
- updateAxisRange
- updateAxisReversed
- findLabelsMaxWidth
- calculateCategoryLabelPosition
- calculateCategoryGridLinePosition
- calculatePolarBackgroundMargin
- updateXTitle
- updateYTitle
- updateZTitle
- updateCamera
- handleLabelCountChanged
- updateCustomData
- updateCustomLabelsRotation
- msaaSamples
- setMsaaSamples
- handleWindowChanged
- geometryChange
- itemChange
- updateWindowParameters
- handleSelectionModeChange
- handleShadowQualityChange
- handleSelectedElementChange
- handleOptimizationHintChange
- resizeViewports
- checkWindowList
- setMeasureFps
- measureFps
- currentFps
- setOrthoProjection
- isOrthoProjection
- selectedElement
- setAspectRatio
- aspectRatio
- setOptimizationHint
- optimizationHint
- setPolar
- isPolar
- setLabelMargin
- labelMargin
- setRadialLabelOffset
- radialLabelOffset
- setHorizontalAspectRatio
- horizontalAspectRatio
- setLocale
- locale
- queriedGraphPosition
- setMargin
- margin
- rootNode
- changeLabelBackgroundColor
- changeLabelBackgroundVisible
- changeLabelBorderVisible
- changeLabelTextColor
- changeLabelFont
- changeLabelsVisible
- changeGridLineColor
- updateTitleLabels
- updateSelectionMode
- doPicking
- minimizeMainGraph
- toggleSliceGraph
- updateSubViews
- windowDestroyed
- createRepeaterDelegateComponent
- createRepeater
- createTitleLabel
- createItemLabel
- createQmlCustomMaterial
- createPrincipledMaterial
- cameraPreset
- setCameraPreset
- setCameraXRotation
- setCameraYRotation
- setMinCameraXRotation
- setMaxCameraXRotation
- setMinCameraYRotation
- setMaxCameraYRotation
- setZoomAtTargetEnabled
- zoomAtTargetEnabled
- setZoomEnabled
- zoomEnabled
- setSelectionEnabled
- selectionEnabled
- setRotationEnabled
- rotationEnabled
- unsetDefaultInputHandler
- unsetDefaultTapHandler
- unsetDefaultDragHandler
- unsetDefaultWheelHandler
- unsetDefaultPinchHandler
- setDragButton
- setDefaultInputHandler
- setCameraZoomLevel
- setMinCameraZoomLevel
- setMaxCameraZoomLevel
- setCameraTargetPosition
- setCameraPosition
- event
- createSliceView
- createSliceCamera
- updateSliceGrid
- updateSliceLabels
- setUpCamera
- setUpLight
- setWrapCameraXRotation
- setWrapCameraYRotation
- ambientLightStrength
- setAmbientLightStrength
- lightStrength
- setLightStrength
- shadowStrength
- setShadowStrength
- lightColor
- setLightColor
- updateBackgroundColor
Start learning QML with our Intro Training
Find out more