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

source code of qtdatavis3d/src/datavisualization/data/qcustom3ditem.cpp