1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qcustom3ditem_p.h"
5
6QT_BEGIN_NAMESPACE
7
8/*!
9 * \class QCustom3DItem
10 * \inmodule QtGraphs
11 * \ingroup graphs_3D
12 * \brief The QCustom3DItem class adds a custom item to a graph.
13 *
14 * A custom item has a custom mesh, position, scaling, rotation, and an optional
15 * texture.
16 *
17 * \sa Q3DGraphsWidgetItem::addCustomItem()
18 */
19
20/*!
21 * \qmltype Custom3DItem
22 * \inqmlmodule QtGraphs
23 * \ingroup graphs_qml_3D
24 * \nativetype QCustom3DItem
25 * \brief Adds a custom item to a graph.
26 *
27 * A custom item has a custom mesh, position, scaling, rotation, and an optional
28 * texture.
29 */
30
31/*! \qmlproperty string Custom3DItem::meshFile
32 *
33 * The item mesh file name. The item in the file must be mesh format.
34 * The mesh files are recommended to include vertices, normals, and UVs.
35 */
36
37/*! \qmlproperty string Custom3DItem::textureFile
38 *
39 * The texture file name for the item. If left unset, a solid gray texture will
40 * be used.
41 *
42 * \note To conserve memory, the QImage loaded from the file is cleared after a
43 * texture is created.
44 */
45
46/*! \qmlproperty vector3d Custom3DItem::position
47 *
48 * The item position as a \l [QtQuick] vector3d. Defaults to \c {vector3d(0.0,
49 * 0.0, 0.0)}.
50 *
51 * Item position is specified either in data coordinates or in absolute
52 * coordinates, depending on the value of the positionAbsolute property. When
53 * using absolute coordinates, values between \c{-1.0...1.0} are
54 * within axis ranges.
55 *
56 * \note Items positioned outside any axis range are not rendered if
57 * positionAbsolute is \c{false}, unless the item is a Custom3DVolume that would
58 * be partially visible and scalingAbsolute is also \c{false}. In that case, the
59 * visible portion of the volume will be rendered.
60 *
61 * \sa positionAbsolute, scalingAbsolute
62 */
63
64/*! \qmlproperty bool Custom3DItem::positionAbsolute
65 *
66 * Defines whether item position is to be handled in data coordinates or in
67 * absolute coordinates. Defaults to \c{false}. Items with absolute coordinates
68 * will always be rendered, whereas items with data coordinates are only
69 * rendered if they are within axis ranges.
70 *
71 * \sa position
72 */
73
74/*! \qmlproperty vector3d Custom3DItem::scaling
75 *
76 * The item scaling as a \l [QtQuick] vector3d type. Defaults to
77 * \c {vector3d(0.1, 0.1, 0.1)}.
78 *
79 * Item scaling is specified either in data values or in absolute values,
80 * depending on the value of the scalingAbsolute property. The default vector
81 * interpreted as absolute values sets the item to
82 * 10% of the height of the graph, provided the item mesh is normalized and the
83 * graph aspect ratios have not been changed from the defaults.
84 *
85 * \sa scalingAbsolute
86 */
87
88/*! \qmlproperty bool Custom3DItem::scalingAbsolute
89 *
90 * Defines whether item scaling is to be handled in data values or in absolute
91 * values. Defaults to \c{true}. Items with absolute scaling will be rendered at
92 * the same size, regardless of axis ranges. Items with data scaling will change
93 * their apparent size according to the axis ranges. If positionAbsolute is
94 * \c{true}, this property is ignored and scaling is interpreted as an absolute
95 * value. If the item has rotation, the data scaling is calculated on the
96 * unrotated item. Similarly, for Custom3DVolume items, the range clipping is
97 * calculated on the unrotated item.
98 *
99 * \note Only absolute scaling is supported for Custom3DLabel items or for
100 * custom items used in \l{GraphsItem3D::polar}{polar} graphs.
101 *
102 * \note The custom item's mesh must be normalized to the range \c{[-1 ,1]}, or
103 * the data scaling will not be accurate.
104 *
105 * \sa scaling, positionAbsolute
106 */
107
108/*! \qmlproperty quaternion Custom3DItem::rotation
109 *
110 * The item rotation as a \l [QtQuick] quaternion. Defaults to
111 * \c {quaternion(0.0, 0.0, 0.0, 0.0)}.
112 */
113
114/*! \qmlproperty bool Custom3DItem::visible
115 *
116 * The visibility of the item. Defaults to \c{true}.
117 */
118
119/*! \qmlproperty bool Custom3DItem::shadowCasting
120 *
121 * Defines whether shadow casting for the item is enabled. Defaults to \c{true}.
122 * If \c{false}, the item does not cast shadows regardless of
123 * \l{QtGraphs3D::ShadowQuality}{ShadowQuality}.
124 */
125
126/*!
127 * \qmlmethod void Custom3DItem::setRotationAxisAndAngle(vector3d axis, real
128 * angle)
129 *
130 * A convenience function to construct the rotation quaternion from \a axis and
131 * \a angle.
132 *
133 * \sa rotation
134 */
135
136/*!
137 \qmlsignal Custom3DItem::meshFileChanged(string meshFile)
138
139 This signal is emitted when meshFile changes to \a meshFile.
140*/
141/*!
142 \qmlsignal Custom3DItem::textureFileChanged(string textureFile)
143
144 This signal is emitted when textureFile changes to \a textureFile.
145*/
146/*!
147 \qmlsignal Custom3DItem::positionChanged(vector3d position)
148
149 This signal is emitted when item \l position changes to \a position.
150*/
151/*!
152 \qmlsignal Custom3DItem::positionAbsoluteChanged(bool positionAbsolute)
153
154 This signal is emitted when positionAbsolute changes to \a positionAbsolute.
155*/
156/*!
157 \qmlsignal Custom3DItem::scalingChanged(vector3d scaling)
158
159 This signal is emitted when \l scaling changes to \a scaling.
160*/
161/*!
162 \qmlsignal Custom3DItem::rotationChanged(quaternion rotation)
163
164 This signal is emitted when \l rotation changes to \a rotation.
165*/
166/*!
167 \qmlsignal Custom3DItem::visibleChanged(bool visible)
168
169 This signal is emitted when \l visible changes to \a visible.
170*/
171/*!
172 \qmlsignal Custom3DItem::shadowCastingChanged(bool shadowCasting)
173
174 This signal is emitted when shadowCasting changes to \a shadowCasting.
175*/
176/*!
177 \qmlsignal Custom3DItem::scalingAbsoluteChanged(bool scalingAbsolute)
178
179 This signal is emitted when scalingAbsolute changes to \a scalingAbsolute.
180*/
181
182/*!
183 * Constructs a custom 3D item with the specified \a parent.
184 */
185QCustom3DItem::QCustom3DItem(QObject *parent)
186 : QObject(*(new QCustom3DItemPrivate()), parent)
187{
188 setTextureImage(QImage());
189}
190
191/*!
192 * \internal
193 */
194QCustom3DItem::QCustom3DItem(QCustom3DItemPrivate &d, QObject *parent)
195 : QObject(d, parent)
196{
197 setTextureImage(QImage());
198}
199
200/*!
201 * Constructs a custom 3D item with the specified \a meshFile, \a position, \a
202 * scaling, \a rotation, \a texture image, and optional \a parent.
203 */
204QCustom3DItem::QCustom3DItem(const QString &meshFile,
205 QVector3D position,
206 QVector3D scaling,
207 const QQuaternion &rotation,
208 const QImage &texture,
209 QObject *parent)
210 : QObject(*(new QCustom3DItemPrivate(meshFile, position, scaling, rotation)), parent)
211{
212 setTextureImage(texture);
213}
214
215/*!
216 * Deletes the custom 3D item.
217 */
218QCustom3DItem::~QCustom3DItem() {}
219
220/*! \property QCustom3DItem::meshFile
221 *
222 * \brief The item mesh file name.
223 *
224 * The item in the file must be in mesh format. The other types
225 * can be converted by \l {Balsam Asset Import Tool}{Balsam}
226 * asset import tool. The mesh files are recommended to include
227 * vertices, normals, and UVs.
228 */
229void QCustom3DItem::setMeshFile(const QString &meshFile)
230{
231 Q_D(QCustom3DItem);
232 if (d->m_meshFile != meshFile) {
233 d->m_meshFile = meshFile;
234 d->m_dirtyBits.meshDirty = true;
235 emit meshFileChanged(meshFile);
236 emit needUpdate();
237 }
238}
239
240QString QCustom3DItem::meshFile() const
241{
242 Q_D(const QCustom3DItem);
243 return d->m_meshFile;
244}
245
246/*! \property QCustom3DItem::position
247 *
248 * \brief The item position as a QVector3D.
249 *
250 * Defaults to \c {QVector3D(0.0, 0.0, 0.0)}.
251 *
252 * Item position is specified either in data coordinates or in absolute
253 * coordinates, depending on the
254 * positionAbsolute property. When using absolute coordinates, values between
255 * \c{-1.0...1.0} are within axis ranges.
256 *
257 * \note Items positioned outside any axis range are not rendered if
258 * positionAbsolute is \c{false}, unless the item is a QCustom3DVolume that
259 * would be partially visible and scalingAbsolute is also \c{false}. In that
260 * case, the visible portion of the volume will be rendered.
261 *
262 * \sa positionAbsolute
263 */
264void QCustom3DItem::setPosition(QVector3D position)
265{
266 Q_D(QCustom3DItem);
267 if (d->m_position != position) {
268 d->m_position = position;
269 d->m_dirtyBits.positionDirty = true;
270 emit positionChanged(position);
271 emit needUpdate();
272 }
273}
274
275QVector3D QCustom3DItem::position() const
276{
277 Q_D(const QCustom3DItem);
278 return d->m_position;
279}
280
281/*! \property QCustom3DItem::positionAbsolute
282 *
283 * \brief Whether item position is to be handled in data coordinates or in
284 * absolute coordinates.
285 *
286 * Defaults to \c{false}. Items with absolute coordinates will always be
287 * rendered, whereas items with data coordinates are only rendered if they are
288 * within axis ranges.
289 *
290 * \sa position
291 */
292void QCustom3DItem::setPositionAbsolute(bool positionAbsolute)
293{
294 Q_D(QCustom3DItem);
295 if (d->m_positionAbsolute != positionAbsolute) {
296 d->m_positionAbsolute = positionAbsolute;
297 d->m_dirtyBits.positionDirty = true;
298 emit positionAbsoluteChanged(positionAbsolute);
299 emit needUpdate();
300 }
301}
302
303bool QCustom3DItem::isPositionAbsolute() const
304{
305 Q_D(const QCustom3DItem);
306 return d->m_positionAbsolute;
307}
308
309/*! \property QCustom3DItem::scaling
310 *
311 * \brief The item scaling as a QVector3D.
312 *
313 * Defaults to \c {QVector3D(0.1, 0.1, 0.1)}.
314 *
315 * Item scaling is either in data values or in absolute values, depending on the
316 * scalingAbsolute property. The default vector interpreted as absolute values
317 * sets the item to 10% of the height of the graph, provided the item mesh is
318 * normalized and the graph aspect ratios have not been changed from the
319 * defaults.
320 *
321 * \sa scalingAbsolute
322 */
323void QCustom3DItem::setScaling(QVector3D scaling)
324{
325 Q_D(QCustom3DItem);
326 if (d->m_scaling != scaling) {
327 d->m_scaling = scaling;
328 d->m_dirtyBits.scalingDirty = true;
329 emit scalingChanged(scaling);
330 emit needUpdate();
331 }
332}
333
334QVector3D QCustom3DItem::scaling() const
335{
336 Q_D(const QCustom3DItem);
337 return d->m_scaling;
338}
339
340/*! \property QCustom3DItem::scalingAbsolute
341 *
342 * \brief Whether item scaling is to be handled in data values or in absolute
343 * values.
344 *
345 * Defaults to \c{true}.
346 *
347 * Items with absolute scaling will be rendered at the same
348 * size, regardless of axis ranges. Items with data scaling will change their
349 * apparent size according to the axis ranges. If positionAbsolute is \c{true},
350 * this property is ignored and scaling is interpreted as an absolute value. If
351 * the item has rotation, the data scaling is calculated on the unrotated item.
352 * Similarly, for QCustom3DVolume items, the range clipping is calculated on the
353 * unrotated item.
354 *
355 * \note Only absolute scaling is supported for QCustom3DLabel items or for
356 * custom items used in \l{Q3DGraphsWidgetItem::polar}{polar} graphs.
357 *
358 * \note The custom item's mesh must be normalized to the range \c{[-1 ,1]}, or
359 * the data scaling will not be accurate.
360 *
361 * \sa scaling, positionAbsolute
362 */
363void QCustom3DItem::setScalingAbsolute(bool scalingAbsolute)
364{
365 Q_D(QCustom3DItem);
366 if (d->m_isLabelItem && !scalingAbsolute) {
367 qWarning(msg: "%ls Data bounds are not supported for label items.",
368 qUtf16Printable(QString::fromUtf8(__func__)));
369 } else if (d->m_scalingAbsolute != scalingAbsolute) {
370 d->m_scalingAbsolute = scalingAbsolute;
371 d->m_dirtyBits.scalingDirty = true;
372 emit scalingAbsoluteChanged(scalingAbsolute);
373 emit needUpdate();
374 }
375}
376
377bool QCustom3DItem::isScalingAbsolute() const
378{
379 Q_D(const QCustom3DItem);
380 return d->m_scalingAbsolute;
381}
382
383/*! \property QCustom3DItem::rotation
384 *
385 * \brief The item rotation as a QQuaternion.
386 *
387 * Defaults to \c {QQuaternion(0.0, 0.0, 0.0, 0.0)}.
388 */
389void QCustom3DItem::setRotation(const QQuaternion &rotation)
390{
391 Q_D(QCustom3DItem);
392 if (d->m_rotation != rotation) {
393 d->m_rotation = rotation;
394 d->m_dirtyBits.rotationDirty = true;
395 emit rotationChanged(rotation);
396 emit needUpdate();
397 }
398}
399
400QQuaternion QCustom3DItem::rotation()
401{
402 Q_D(const QCustom3DItem);
403 return d->m_rotation;
404}
405
406/*! \property QCustom3DItem::visible
407 *
408 * \brief The visibility of the item.
409 *
410 * Defaults to \c{true}.
411 */
412void QCustom3DItem::setVisible(bool visible)
413{
414 Q_D(QCustom3DItem);
415 if (d->m_visible != visible) {
416 d->m_visible = visible;
417 d->m_dirtyBits.visibleDirty = true;
418 emit visibleChanged(visible);
419 emit needUpdate();
420 }
421}
422
423bool QCustom3DItem::isVisible() const
424{
425 Q_D(const QCustom3DItem);
426 return d->m_visible;
427}
428
429/*! \property QCustom3DItem::shadowCasting
430 *
431 * \brief Whether shadow casting for the item is enabled.
432 *
433 * Defaults to \c{true}.
434 * If \c{false}, the item does not cast shadows regardless of
435 * Q3DGraphsWidgetItem::ShadowQuality.
436 */
437void QCustom3DItem::setShadowCasting(bool enabled)
438{
439 Q_D(QCustom3DItem);
440 if (d->m_shadowCasting != enabled) {
441 d->m_shadowCasting = enabled;
442 d->m_dirtyBits.shadowCastingDirty = true;
443 emit shadowCastingChanged(shadowCasting: enabled);
444 emit needUpdate();
445 }
446}
447
448bool QCustom3DItem::isShadowCasting() const
449{
450 Q_D(const QCustom3DItem);
451 return d->m_shadowCasting;
452}
453
454/*!
455 * A convenience function to construct the rotation quaternion from \a axis and
456 * \a angle.
457 *
458 * \sa rotation
459 */
460void QCustom3DItem::setRotationAxisAndAngle(QVector3D axis, float angle)
461{
462 setRotation(QQuaternion::fromAxisAndAngle(axis, angle));
463}
464
465/*!
466 * Sets the value of \a textureImage as a QImage for the item. The texture
467 * defaults to solid gray.
468 *
469 * \note To conserve memory, the given QImage is cleared after a texture is
470 * created.
471 */
472void QCustom3DItem::setTextureImage(const QImage &textureImage)
473{
474 Q_D(QCustom3DItem);
475 if (textureImage != d->m_textureImage) {
476 if (textureImage.isNull()) {
477 // Make a solid gray texture
478 d->m_textureImage = QImage(2, 2, QImage::Format_RGB32);
479 d->m_textureImage.fill(color: Qt::gray);
480 } else {
481 d->m_textureImage = textureImage;
482 }
483
484 if (!d->m_textureFile.isEmpty()) {
485 d->m_textureFile.clear();
486 emit textureFileChanged(textureFile: d->m_textureFile);
487 }
488 d->m_dirtyBits.textureDirty = true;
489 emit needUpdate();
490 }
491}
492
493/*! \property QCustom3DItem::textureFile
494 *
495 * \brief The texture file name for the item.
496 *
497 * If both this property and the texture image are unset, a solid
498 * gray texture will be used.
499 *
500 * \note To conserve memory, the QImage loaded from the file is cleared after a
501 * texture is created.
502 */
503void QCustom3DItem::setTextureFile(const QString &textureFile)
504{
505 Q_D(QCustom3DItem);
506 if (d->m_textureFile != textureFile) {
507 d->m_textureFile = textureFile;
508 if (!textureFile.isEmpty()) {
509 d->m_textureImage = QImage(textureFile);
510 } else {
511 d->m_textureImage = QImage(2, 2, QImage::Format_RGB32);
512 d->m_textureImage.fill(color: Qt::gray);
513 }
514 emit textureFileChanged(textureFile);
515 d->m_dirtyBits.textureDirty = true;
516 emit needUpdate();
517 }
518}
519
520QString QCustom3DItem::textureFile() const
521{
522 Q_D(const QCustom3DItem);
523 return d->m_textureFile;
524}
525
526QCustom3DItemPrivate::QCustom3DItemPrivate()
527 : m_textureImage(QImage(1, 1, QImage::Format_ARGB32))
528 , m_position(QVector3D(0.0f, 0.0f, 0.0f))
529 , m_positionAbsolute(false)
530 , m_scaling(QVector3D(0.1f, 0.1f, 0.1f))
531 , m_scalingAbsolute(true)
532 , m_rotation(QQuaternion())
533 , m_visible(true)
534 , m_shadowCasting(true)
535 , m_isLabelItem(false)
536 , m_isVolumeItem(false)
537{}
538
539QCustom3DItemPrivate::QCustom3DItemPrivate(const QString &meshFile,
540 QVector3D position,
541 QVector3D scaling,
542 const QQuaternion &rotation)
543 : m_textureImage(QImage(1, 1, QImage::Format_ARGB32))
544 , m_meshFile(meshFile)
545 , m_position(position)
546 , m_positionAbsolute(false)
547 , m_scaling(scaling)
548 , m_scalingAbsolute(true)
549 , m_rotation(rotation)
550 , m_visible(true)
551 , m_shadowCasting(true)
552 , m_isLabelItem(false)
553 , m_isVolumeItem(false)
554{}
555
556QCustom3DItemPrivate::~QCustom3DItemPrivate() {}
557
558QImage QCustom3DItemPrivate::textureImage()
559{
560 return m_textureImage;
561}
562
563void QCustom3DItemPrivate::clearTextureImage()
564{
565 m_textureImage = QImage();
566 m_textureFile.clear();
567}
568
569void QCustom3DItemPrivate::resetDirtyBits()
570{
571 m_dirtyBits.textureDirty = false;
572 m_dirtyBits.meshDirty = false;
573 m_dirtyBits.positionDirty = false;
574 m_dirtyBits.scalingDirty = false;
575 m_dirtyBits.rotationDirty = false;
576 m_dirtyBits.visibleDirty = false;
577 m_dirtyBits.shadowCastingDirty = false;
578}
579
580QT_END_NAMESPACE
581

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtgraphs/src/graphs3d/data/qcustom3ditem.cpp