1/****************************************************************************
2**
3** Copyright (C) 2016 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 "q3dbars_p.h"
31
32QT_BEGIN_NAMESPACE_DATAVISUALIZATION
33
34/*!
35 * \class Q3DBars
36 * \inmodule QtDataVisualization
37 * \brief The Q3DBars class provides methods for rendering 3D bar graphs.
38 * \since QtDataVisualization 1.0
39 *
40 * This class enables developers to render bar graphs in 3D and to view them by rotating the scene
41 * freely. Rotation is done by holding down the right mouse button and moving the mouse. Zooming
42 * is done by mouse wheel. Selection, if enabled, is done by left mouse button. The scene can be
43 * reset to default camera view by clicking mouse wheel. In touch devices rotation is done
44 * by tap-and-move, selection by tap-and-hold and zoom by pinch.
45 *
46 * If no axes are set explicitly to Q3DBars, temporary default axes with no labels are created.
47 * These default axes can be modified via axis accessors, but as soon any axis is set explicitly
48 * for the orientation, the default axis for that orientation is destroyed.
49 *
50 * Q3DBars supports more than one series visible at the same time. It is not necessary for all series
51 * to have the same amount of rows and columns.
52 * Row and column labels are taken from the first added series, unless explicitly defined to
53 * row and column axes.
54 *
55 * \section1 How to construct a minimal Q3DBars graph
56 *
57 * First, construct an instance of Q3DBars. Since we are running the graph as top level window
58 * in this example, we need to clear the \c Qt::FramelessWindowHint flag, which gets set by
59 * default:
60 *
61 * \snippet doc_src_q3dbars_construction.cpp 4
62 *
63 * After constructing Q3DBars, you can set the data window by changing the range on the row and
64 * column axes. It is not mandatory, as data window will default to showing all of the data in
65 * the series. If the amount of data is large, it is usually preferable to show just a
66 * portion of it. For the example, let's set the data window to show first five rows and columns:
67 *
68 * \snippet doc_src_q3dbars_construction.cpp 0
69 *
70 * Now Q3DBars is ready to receive data to be rendered. Create a series with one row of 5 values:
71 *
72 * \snippet doc_src_q3dbars_construction.cpp 1
73 *
74 * \note We set the data window to 5 x 5, but we are adding only one row of data. This is ok,
75 * the rest of the rows will just be blank.
76 *
77 * Finally you will need to set it visible:
78 *
79 * \snippet doc_src_q3dbars_construction.cpp 2
80 *
81 * The complete code needed to create and display this graph is:
82 *
83 * \snippet doc_src_q3dbars_construction.cpp 3
84 *
85 * And this is what those few lines of code produce:
86 *
87 * \image q3dbars-minimal.png
88 *
89 * The scene can be rotated, zoomed into, and a bar can be selected to view its value,
90 * but no other interaction is included in this minimal code example. You can learn more by
91 * familiarizing yourself with the examples provided, like the \l{Bars Example}.
92 *
93 * \sa Q3DScatter, Q3DSurface, {Qt Data Visualization C++ Classes}
94 */
95
96/*!
97 * Constructs a new 3D bar graph with optional \a parent window
98 * and surface \a format.
99 */
100Q3DBars::Q3DBars(const QSurfaceFormat *format, QWindow *parent)
101 : QAbstract3DGraph(new Q3DBarsPrivate(this), format, parent)
102{
103 if (!dptr()->m_initialized)
104 return;
105
106 dptr()->m_shared = new Bars3DController(geometry());
107 d_ptr->setVisualController(dptr()->m_shared);
108 dptr()->m_shared->initializeOpenGL();
109 QObject::connect(sender: dptr()->m_shared, signal: &Bars3DController::primarySeriesChanged,
110 receiver: this, slot: &Q3DBars::primarySeriesChanged);
111 QObject::connect(sender: dptr()->m_shared, signal: &Bars3DController::selectedSeriesChanged,
112 receiver: this, slot: &Q3DBars::selectedSeriesChanged);
113}
114
115/*!
116 * Destroys the 3D bar graph.
117 */
118Q3DBars::~Q3DBars()
119{
120}
121
122/*!
123 * \property Q3DBars::primarySeries
124 *
125 * \brief The primary series of the graph.
126 */
127
128/*!
129 * Sets \a series as the primary series of the graph. The primary series
130 * determines the row and column axis labels when the labels are not explicitly
131 * set to the axes.
132 *
133 * If the specified series is not yet added to the graph, setting it as the
134 * primary series will also implicitly add it to the graph.
135 *
136 * If the primary series itself is removed from the graph, this property
137 * resets to default.
138 *
139 * If \a series is null, this property resets to default.
140 * Defaults to the first added series or zero if no series are added to the graph.
141 */
142void Q3DBars::setPrimarySeries(QBar3DSeries *series)
143{
144 dptr()->m_shared->setPrimarySeries(series);
145}
146
147QBar3DSeries *Q3DBars::primarySeries() const
148{
149 return dptrc()->m_shared->primarySeries();
150}
151
152/*!
153 * Adds the \a series to the graph. A graph can contain multiple series, but only one set of axes,
154 * so the rows and columns of all series must match for the visualized data to be meaningful.
155 * If the graph has multiple visible series, only the primary series will
156 * generate the row or column labels on the axes in cases where the labels are not explicitly set
157 * to the axes. If the newly added series has specified a selected bar, it will be highlighted and
158 * any existing selection will be cleared. Only one added series can have an active selection.
159 *
160 * \sa seriesList(), primarySeries
161 */
162void Q3DBars::addSeries(QBar3DSeries *series)
163{
164 dptr()->m_shared->addSeries(series);
165}
166
167/*!
168 * Removes the \a series from the graph.
169 */
170void Q3DBars::removeSeries(QBar3DSeries *series)
171{
172 dptr()->m_shared->removeSeries(series);
173}
174
175/*!
176 * Inserts the \a series into the position \a index in the series list.
177 * If the \a series has already been added to the list, it is moved to the
178 * new \a index.
179 * \note When moving a series to a new \a index that is after its old index,
180 * the new position in list is calculated as if the series was still in its old
181 * index, so the final index is actually the \a index decremented by one.
182 *
183 * \sa addSeries(), seriesList()
184 */
185void Q3DBars::insertSeries(int index, QBar3DSeries *series)
186{
187 dptr()->m_shared->insertSeries(index, series);
188}
189
190/*!
191 * Returns the list of series added to this graph.
192 */
193QList<QBar3DSeries *> Q3DBars::seriesList() const
194{
195 return dptrc()->m_shared->barSeriesList();
196}
197
198/*!
199 * \property Q3DBars::multiSeriesUniform
200 *
201 * \brief Whether bars are to be scaled with proportions set to a single series
202 * bar even if there are multiple series displayed.
203 *
204 * If set to \c {true}, \l{barSpacing}{bar spacing} will be correctly applied
205 * only to the X-axis. Preset to \c false by default.
206 */
207void Q3DBars::setMultiSeriesUniform(bool uniform)
208{
209 if (uniform != isMultiSeriesUniform()) {
210 dptr()->m_shared->setMultiSeriesScaling(uniform);
211 emit multiSeriesUniformChanged(uniform);
212 }
213}
214
215bool Q3DBars::isMultiSeriesUniform() const
216{
217 return dptrc()->m_shared->multiSeriesScaling();
218}
219
220/*!
221 * \property Q3DBars::barThickness
222 *
223 * \brief The bar thickness ratio between the X and Z dimensions.
224 *
225 * The value \c 1.0 means that the bars are as wide as they are deep, whereas
226 *\c 0.5 makes them twice as deep as they are wide. Preset to \c 1.0 by default.
227 */
228void Q3DBars::setBarThickness(float thicknessRatio)
229{
230 if (thicknessRatio != barThickness()) {
231 dptr()->m_shared->setBarSpecs(thicknessRatio: GLfloat(thicknessRatio), spacing: barSpacing(),
232 relative: isBarSpacingRelative());
233 emit barThicknessChanged(thicknessRatio);
234 }
235}
236
237float Q3DBars::barThickness() const
238{
239 return dptrc()->m_shared->barThickness();
240}
241
242/*!
243 * \property Q3DBars::barSpacing
244 *
245 * \brief Bar spacing in the X and Z dimensions.
246 *
247 * Preset to \c {(1.0, 1.0)} by default. Spacing is affected by the
248 * barSpacingRelative property.
249 *
250 * \sa barSpacingRelative, multiSeriesUniform
251 */
252void Q3DBars::setBarSpacing(const QSizeF &spacing)
253{
254 if (spacing != barSpacing()) {
255 dptr()->m_shared->setBarSpecs(thicknessRatio: GLfloat(barThickness()), spacing, relative: isBarSpacingRelative());
256 emit barSpacingChanged(spacing);
257 }
258}
259
260QSizeF Q3DBars::barSpacing() const
261{
262 return dptrc()->m_shared->barSpacing();
263}
264
265/*!
266 * \property Q3DBars::barSpacingRelative
267 *
268 * \brief Whether spacing is absolute or relative to bar thickness.
269 *
270 * If it is \c true, the value of \c 0.0 means that the bars are placed
271 * side-to-side, \c 1.0 means that a space as wide as the thickness of one bar
272 * is left between the bars, and so on. Preset to \c true.
273 */
274void Q3DBars::setBarSpacingRelative(bool relative)
275{
276 if (relative != isBarSpacingRelative()) {
277 dptr()->m_shared->setBarSpecs(thicknessRatio: GLfloat(barThickness()), spacing: barSpacing(), relative);
278 emit barSpacingRelativeChanged(relative);
279 }
280}
281
282bool Q3DBars::isBarSpacingRelative() const
283{
284 return dptrc()->m_shared->isBarSpecRelative();
285}
286
287/*!
288 * \property Q3DBars::rowAxis
289 *
290 * \brief The axis attached to the active row.
291 */
292
293/*!
294 * Sets the axis of the active row to \a axis. Implicitly calls addAxis() to
295 * transfer the ownership of the axis to this graph.
296 *
297 * If \a axis is null, a temporary default axis with no labels is created.
298 * This temporary axis is destroyed if another axis is set explicitly to the
299 * same orientation.
300 *
301 * \sa addAxis(), releaseAxis()
302 */
303void Q3DBars::setRowAxis(QCategory3DAxis *axis)
304{
305 dptr()->m_shared->setAxisZ(axis);
306}
307
308QCategory3DAxis *Q3DBars::rowAxis() const
309{
310 return static_cast<QCategory3DAxis *>(dptrc()->m_shared->axisZ());
311}
312
313/*!
314 * \property Q3DBars::columnAxis
315 *
316 * \brief The axis attached to the active column.
317 */
318
319/*!
320 * Sets the axis of the active column to \a axis. Implicitly calls addAxis() to
321 * transfer the ownership of the axis to this graph.
322 *
323 * If \a axis is null, a temporary default axis with no labels is created.
324 * This temporary axis is destroyed if another axis is set explicitly to the
325 * same orientation.
326 *
327 * \sa addAxis(), releaseAxis()
328 */
329void Q3DBars::setColumnAxis(QCategory3DAxis *axis)
330{
331 dptr()->m_shared->setAxisX(axis);
332}
333
334QCategory3DAxis *Q3DBars::columnAxis() const
335{
336 return static_cast<QCategory3DAxis *>(dptrc()->m_shared->axisX());
337}
338
339/*!
340 * \property Q3DBars::valueAxis
341 *
342 * Sets the active value axis (the Y-axis) to \a axis. Implicitly calls
343 * addAxis() to transfer the ownership of \a axis to this graph.
344 *
345 * If \a axis is null, a temporary default axis with no labels and
346 * an automatically adjusting range is created.
347 * This temporary axis is destroyed if another axis is set explicitly to the
348 * same orientation.
349 *
350 * \sa addAxis(), releaseAxis()
351 */
352void Q3DBars::setValueAxis(QValue3DAxis *axis)
353{
354 dptr()->m_shared->setAxisY(axis);
355}
356
357QValue3DAxis *Q3DBars::valueAxis() const
358{
359 return static_cast<QValue3DAxis *>(dptrc()->m_shared->axisY());
360}
361
362/*!
363 * \property Q3DBars::selectedSeries
364 *
365 * \brief The selected series or a null value.
366 *
367 * If selectionMode has the \c SelectionMultiSeries flag set, this
368 * property holds the series that owns the selected bar.
369 */
370QBar3DSeries *Q3DBars::selectedSeries() const
371{
372 return dptrc()->m_shared->selectedSeries();
373}
374
375/*!
376 * \property Q3DBars::floorLevel
377 *
378 * \brief The floor level for the bar graph in Y-axis data coordinates.
379 *
380 * The actual floor level will be restricted by the Y-axis minimum and maximum
381 * values.
382 * Defaults to zero.
383 */
384void Q3DBars::setFloorLevel(float level)
385{
386 if (level != floorLevel()) {
387 dptr()->m_shared->setFloorLevel(level);
388 emit floorLevelChanged(level);
389 }
390}
391
392float Q3DBars::floorLevel() const
393{
394 return dptrc()->m_shared->floorLevel();
395}
396
397/*!
398 * Adds \a axis to the graph. The axes added via addAxis are not yet taken to use,
399 * addAxis is simply used to give the ownership of the \a axis to the graph.
400 * The \a axis must not be null or added to another graph.
401 *
402 * \sa releaseAxis(), setValueAxis(), setRowAxis(), setColumnAxis()
403 */
404void Q3DBars::addAxis(QAbstract3DAxis *axis)
405{
406 dptr()->m_shared->addAxis(axis);
407}
408
409/*!
410 * Releases the ownership of the \a axis back to the caller, if it is added to this graph.
411 * If the released \a axis is in use, a new default axis will be created and set active.
412 *
413 * If the default axis is released and added back later, it behaves as any other axis would.
414 *
415 * \sa addAxis(), setValueAxis(), setRowAxis(), setColumnAxis()
416 */
417void Q3DBars::releaseAxis(QAbstract3DAxis *axis)
418{
419 dptr()->m_shared->releaseAxis(axis);
420}
421
422/*!
423 * Returns the list of all added axes.
424 *
425 * \sa addAxis()
426 */
427QList<QAbstract3DAxis *> Q3DBars::axes() const
428{
429 return dptrc()->m_shared->axes();
430}
431
432Q3DBarsPrivate *Q3DBars::dptr()
433{
434 return static_cast<Q3DBarsPrivate *>(d_ptr.data());
435}
436
437const Q3DBarsPrivate *Q3DBars::dptrc() const
438{
439 return static_cast<const Q3DBarsPrivate *>(d_ptr.data());
440}
441
442Q3DBarsPrivate::Q3DBarsPrivate(Q3DBars *q)
443 : QAbstract3DGraphPrivate(q),
444 m_shared(0)
445{
446}
447
448Q3DBarsPrivate::~Q3DBarsPrivate()
449{
450}
451
452void Q3DBarsPrivate::handleAxisXChanged(QAbstract3DAxis *axis)
453{
454 emit qptr()->columnAxisChanged(axis: static_cast<QCategory3DAxis *>(axis));
455}
456
457void Q3DBarsPrivate::handleAxisYChanged(QAbstract3DAxis *axis)
458{
459 emit qptr()->valueAxisChanged(axis: static_cast<QValue3DAxis *>(axis));
460}
461
462void Q3DBarsPrivate::handleAxisZChanged(QAbstract3DAxis *axis)
463{
464 emit qptr()->rowAxisChanged(axis: static_cast<QCategory3DAxis *>(axis));
465}
466
467Q3DBars *Q3DBarsPrivate::qptr()
468{
469 return static_cast<Q3DBars *>(q_ptr);
470}
471
472QT_END_NAMESPACE_DATAVISUALIZATION
473

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