1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3#include "qquickgraphsbarsnode_p.h"
4
5QT_BEGIN_NAMESPACE
6
7/*!
8 * \qmltype Bars3DNode
9 * \inherits GraphsNode
10 * \inqmlmodule QtGraphs
11 * \ingroup graphs_qml_3D
12 * \brief 3D bar graph node.
13 *
14 * This type enables developers to render a bar graph node in 3D with Qt Quick.
15 *
16 * You will need to import Qt Graphs module to use this type:
17 *
18 * \snippet doc_src_qmlnodes.qml 0
19 *
20 * After that you can use Bars3DNode in your qml files:
21 *
22 * \snippet doc_src_qmlnodes.qml 1
23 *
24 * \sa Bar3DSeries, ItemModelBarDataProxy, Scatter3DNode, Surface3DNode, {Qt Graphs C++ Classes for 3D}
25 */
26
27/*!
28 * \qmlproperty Category3DAxis Bars3DNode::rowAxis
29 * The active row axis.
30 *
31 * If an axis is not given, a temporary default axis with no labels is created.
32 * This temporary axis is destroyed if another axis is explicitly set to the
33 * same orientation.
34 */
35
36/*!
37 * \qmlproperty ValueAxis3D Bars3DNode::valueAxis
38 * The active value axis.
39 *
40 * If an axis is not given, a temporary default axis with no labels and an
41 * automatically adjusting range is created.
42 * This temporary axis is destroyed if another axis is explicitly set to the
43 * same orientation.
44 */
45
46/*!
47 * \qmlproperty Category3DAxis Bars3DNode::columnAxis
48 * The active column axis.
49 *
50 * If an axis is not given, a temporary default axis with no labels is created.
51 * This temporary axis is destroyed if another axis is explicitly set to the
52 * same orientation.
53 */
54
55/*!
56 * \qmlproperty bool Bars3DNode::multiSeriesUniform
57 * Defines whether bars are to be scaled with proportions set to a single series bar even
58 * if there are multiple series displayed. If set to \c {true}, \l{barSpacing}{bar spacing} will
59 * be correctly applied only to the X-axis. Preset to \c false by default.
60 */
61
62/*!
63 * \qmlproperty real Bars3DNode::barThickness
64 * The bar thickness ratio between the X and Z dimensions. The value \c 1.0
65 * means that the bars are as wide as they are deep, whereas \c 0.5
66 * makes them twice as deep as they are wide.
67 */
68
69/*!
70 * \qmlproperty size Bars3DNode::barSpacing
71 * Bar spacing in X and Z dimensions.
72 *
73 * Preset to \c {(1.0, 1.0)} by default. Spacing is affected by the
74 * barSpacingRelative property.
75 */
76
77/*!
78 * \qmlproperty bool Bars3DNode::barSpacingRelative
79 * Whether spacing is absolute or relative to bar thickness.
80 *
81 * If \c true, the value of \c 0.0 means that the bars are placed
82 * side-to-side, \c 1.0 means that a space as wide as the thickness of one bar
83 * is left between the bars, and so on. Preset to \c true.
84 */
85
86/*!
87 * \qmlproperty size Bars3DNode::barSeriesMargin
88 *
89 * Margin between series columns in X and Z dimensions. Preset to \c {(0.0, 0.0)} by default.
90 * Sensible values are on the range [0,1).
91 */
92
93/*!
94 * \qmlproperty Bar3DSeries Bars3DNode::selectedSeries
95 * \readonly
96 *
97 * The selected series or \c null. If \l {GraphsNode::selectionMode}{selectionMode} has
98 * the \c SelectionMultiSeries flag set, this property holds the series that
99 * owns the selected bar.
100 */
101
102/*!
103 * \qmlproperty list<Bar3DSeries> Bars3DNode::seriesList
104 * \qmldefault
105 * The series of the graph.
106 * By default, this property contains an empty list.
107 * To set the series, either use the addSeries() function or define them as children of the graph.
108 */
109
110/*!
111 * \qmlproperty Bar3DSeries Bars3DNode::primarySeries
112 * The primary series of the graph. It
113 * is used to determine the row and column axis labels when the labels are not explicitly
114 * set to the axes.
115 *
116 * If the specified series is not yet added to the graph, setting it as the
117 * primary series will also implicitly add it to the graph.
118 *
119 * If the primary series itself is removed from the graph, this property
120 * resets to default.
121 *
122 * If the series is null, this property resets to default.
123 * Defaults to the first added series or zero if no series are added to the graph.
124 */
125
126/*!
127 * \qmlproperty real Bars3DNode::floorLevel
128 *
129 * The floor level for the bar graph in Y-axis data coordinates.
130 *
131 * The actual floor level will be restricted by the Y-axis minimum and maximum
132 * values.
133 * Defaults to zero.
134 */
135
136/*!
137 * \qmlmethod void Bars3DNode::addSeries(Bar3DSeries series)
138 * Adds the \a series to the graph. A graph can contain multiple series, but only one set of axes,
139 * so the rows and columns of all series must match for the visualized data to be meaningful.
140 * If the graph has multiple visible series, only the first one added will
141 * generate the row or column labels on the axes in cases where the labels are not explicitly set
142 * to the axes. If the newly added series has specified a selected bar, it will be highlighted and
143 * any existing selection will be cleared. Only one added series can have an active selection.
144 * \sa GraphsNode::hasSeries()
145 */
146
147/*!
148 * \qmlmethod void Bars3DNode::removeSeries(Bar3DSeries series)
149 * Remove the \a series from the graph.
150 * \sa GraphsNode::hasSeries()
151 */
152
153/*!
154 * \qmlmethod void Bars3DNode::insertSeries(int index, Bar3DSeries series)
155 * Inserts the \a series into the position \a index in the series list.
156 * If the \a series has already been added to the list, it is moved to the
157 * new \a index.
158 * \note When moving a series to a new \a index that is after its old index,
159 * the new position in list is calculated as if the series was still in its old
160 * index, so the final index is actually the \a index decremented by one.
161 * \sa GraphsNode::hasSeries()
162 */
163
164/*!
165 * \qmlsignal Bars3DNode::multiSeriesUniformChanged(bool uniform)
166 *
167 * This signal is emitted when multiSeriesUniform changes to \a uniform.
168 */
169
170/*!
171 * \qmlsignal Bars3DNode::barThicknessChanged(real thicknessRatio)
172 *
173 * This signal is emitted when barThickness changes to \a thicknessRatio.
174 */
175
176/*!
177 * \qmlsignal Bars3DNode::barSpacingChanged(size spacing)
178 *
179 * This signal is emitted when barSpacing changes to \a spacing.
180 */
181
182/*!
183 * \qmlsignal Bars3DNode::barSpacingRelativeChanged(bool relative)
184 *
185 * This signal is emitted when barSpacingRelative changes to \a relative.
186 */
187
188/*!
189 * \qmlsignal Bars3DNode::barSeriesMarginChanged(size margin)
190 *
191 * This signal is emitted when barSeriesMargin changes to \a margin.
192 */
193
194/*!
195 * \qmlsignal Bars3DNode::rowAxisChanged(Category3DAxis axis)
196 *
197 * This signal is emitted when rowAxis changes to \a axis.
198 */
199
200/*!
201 * \qmlsignal Bars3DNode::columnAxisChanged(Category3DAxis axis)
202 *
203 * This signal is emitted when columnAxis changes to \a axis.
204 */
205
206/*!
207 * \qmlsignal Bars3DNode::valueAxisChanged(ValueAxis3D axis)
208 *
209 * This signal is emitted when valueAxis changes to \a axis.
210 */
211
212/*!
213 * \qmlsignal Bars3DNode::primarySeriesChanged(Bar3DSeries series)
214 *
215 * This signal is emitted when primarySeries changes to \a series.
216 */
217
218/*!
219 * \qmlsignal Bars3DNode::selectedSeriesChanged(Bar3DSeries series)
220 *
221 * This signal is emitted when selectedSeries changes to \a series.
222 */
223
224/*!
225 * \qmlsignal Bars3DNode::floorLevelChanged(real level)
226 *
227 * This signal is emitted when floorLevel changes to \a level.
228 */
229
230QQuickGraphsBarsNode::QQuickGraphsBarsNode(QQuick3DNode *parent)
231 : QQuickGraphsNode(parent)
232 , m_multiSeriesUniform(false)
233 , m_barSpacing(QSizeF(1.0f, 1.0f))
234 , m_barThickness(1.0f)
235 , m_barSpacingRelative(false)
236 , m_floorLevel(0.0f)
237{}
238
239QQuickGraphsBarsNode::~QQuickGraphsBarsNode() {}
240
241void QQuickGraphsBarsNode::componentComplete()
242{
243 const QString qmlData = QLatin1StringView(R"QML(
244 import QtQuick;
245 import QtGraphs;
246
247 Bars3D{}
248 )QML");
249
250 QQmlComponent *component = new QQmlComponent(qmlEngine(this), this);
251 component->setData(qmlData.toUtf8(), baseUrl: QUrl());
252 m_graph.reset(p: qobject_cast<QQuickGraphsItem *>(object: component->create()));
253 setGraphParent();
254 QQuickGraphsNode::componentComplete();
255
256 //initialize components
257
258 if (m_axisX)
259 graphBars()->setAxisX(static_cast<QCategory3DAxis *>(m_axisX));
260 if (m_axisY)
261 graphBars()->setAxisY(static_cast<QValue3DAxis *>(m_axisY));
262 if (m_axisZ)
263 graphBars()->setAxisZ(static_cast<QCategory3DAxis *>(m_axisZ));
264
265 graphBars()->setSelectionMode(m_selectionMode);
266 graphBars()->setMultiSeriesUniform(m_multiSeriesUniform);
267 graphBars()->setBarSpacing(m_barSpacing);
268 graphBars()->setBarThickness(m_barThickness);
269 graphBars()->setBarSpacingRelative(m_barSpacingRelative);
270 graphBars()->setFloorLevel(m_floorLevel);
271
272 for (auto series : std::as_const(t&: m_seriesList))
273 graphBars()->addSeries(series: static_cast<QBar3DSeries *>(series));
274
275 //connect signals
276 connect(sender: graphBars(),
277 signal: &QQuickGraphsBars::rowAxisChanged,
278 context: this,
279 slot: &QQuickGraphsBarsNode::rowAxisChanged);
280 connect(sender: graphBars(),
281 signal: &QQuickGraphsBars::valueAxisChanged,
282 context: this,
283 slot: &QQuickGraphsBarsNode::valueAxisChanged);
284 connect(sender: graphBars(),
285 signal: &QQuickGraphsBars::columnAxisChanged,
286 context: this,
287 slot: &QQuickGraphsBarsNode::columnAxisChanged);
288 connect(sender: graphBars(),
289 signal: &QQuickGraphsBars::multiSeriesUniformChanged,
290 context: this,
291 slot: &QQuickGraphsBarsNode::multiSeriesUniformChanged);
292 connect(sender: graphBars(),
293 signal: &QQuickGraphsBars::barThicknessChanged,
294 context: this,
295 slot: &QQuickGraphsBarsNode::barThicknessChanged);
296 connect(sender: graphBars(),
297 signal: &QQuickGraphsBars::barSpacingChanged,
298 context: this,
299 slot: &QQuickGraphsBarsNode::barSpacingChanged);
300 connect(sender: graphBars(),
301 signal: &QQuickGraphsBars::barSpacingRelativeChanged,
302 context: this,
303 slot: &QQuickGraphsBarsNode::barSpacingRelativeChanged);
304 connect(sender: graphBars(),
305 signal: &QQuickGraphsBars::barSeriesMarginChanged,
306 context: this,
307 slot: &QQuickGraphsBarsNode::barSeriesMarginChanged);
308 connect(sender: graphBars(),
309 signal: &QQuickGraphsBars::primarySeriesChanged,
310 context: this,
311 slot: &QQuickGraphsBarsNode::primarySeriesChanged);
312 connect(sender: graphBars(),
313 signal: &QQuickGraphsBars::selectedSeriesChanged,
314 context: this,
315 slot: &QQuickGraphsBarsNode::selectedSeriesChanged);
316 connect(sender: graphBars(),
317 signal: &QQuickGraphsBars::floorLevelChanged,
318 context: this,
319 slot: &QQuickGraphsBarsNode::floorLevelChanged);
320}
321
322void QQuickGraphsBarsNode::setRowAxis(QCategory3DAxis *axis)
323{
324 if (m_axisZ == axis)
325 return;
326
327 m_axisZ = axis;
328 if (graphBars())
329 graphBars()->setRowAxis(axis);
330}
331
332QCategory3DAxis *QQuickGraphsBarsNode::rowAxis() const
333{
334 if (graphBars())
335 return graphBars()->rowAxis();
336 else
337 return static_cast<QCategory3DAxis *>(m_axisZ);
338}
339
340void QQuickGraphsBarsNode::setValueAxis(QValue3DAxis *axis)
341{
342 if (m_axisY == axis)
343 return;
344
345 m_axisY = axis;
346 if (graphBars())
347 graphBars()->setValueAxis(axis);
348}
349
350QValue3DAxis *QQuickGraphsBarsNode::valueAxis() const
351{
352 if (graphBars())
353 return graphBars()->valueAxis();
354 else
355 return static_cast<QValue3DAxis *>(m_axisY);
356}
357
358void QQuickGraphsBarsNode::setColumnAxis(QCategory3DAxis *axis)
359{
360 if (m_axisX == axis)
361 return;
362
363 m_axisX = axis;
364 if (graphBars())
365 graphBars()->setColumnAxis(axis);
366}
367
368QCategory3DAxis *QQuickGraphsBarsNode::columnAxis() const
369{
370 if (graphBars())
371 return graphBars()->columnAxis();
372 else
373 return static_cast<QCategory3DAxis *>(m_axisX);
374}
375
376void QQuickGraphsBarsNode::setMultiSeriesUniform(bool uniform)
377{
378 if (m_multiSeriesUniform == uniform)
379 return;
380 m_multiSeriesUniform = uniform;
381 if (graphBars())
382 graphBars()->setMultiSeriesUniform(uniform);
383}
384
385bool QQuickGraphsBarsNode::isMultiSeriesUniform() const
386{
387 if (graphBars())
388 return graphBars()->isMultiSeriesUniform();
389 else
390 return m_multiSeriesUniform;
391}
392
393void QQuickGraphsBarsNode::setBarThickness(float thickness)
394{
395 if (m_barThickness == thickness)
396 return;
397 m_barThickness = thickness;
398 if (graphBars())
399 graphBars()->setBarThickness(thickness);
400}
401
402float QQuickGraphsBarsNode::barThickness() const
403{
404 if (graphBars())
405 return graphBars()->barThickness();
406 else
407 return m_barThickness;
408}
409
410void QQuickGraphsBarsNode::setBarSpacing(QSizeF spacing)
411{
412 if (m_barSpacing == spacing)
413 return;
414 m_barSpacing = spacing;
415 if (graphBars())
416 graphBars()->setBarSpacing(spacing);
417}
418
419QSizeF QQuickGraphsBarsNode::barSpacing() const
420{
421 if (graphBars())
422 return graphBars()->barSpacing();
423 else
424 return m_barSpacing;
425}
426
427void QQuickGraphsBarsNode::setBarSpacingRelative(bool relative)
428{
429 if (m_barSpacingRelative == relative)
430 return;
431 m_barSpacingRelative = relative;
432
433 if (graphBars())
434 graphBars()->setBarSpacingRelative(relative);
435}
436
437bool QQuickGraphsBarsNode::isBarSpacingRelative() const
438{
439 if (graphBars())
440 return graphBars()->isBarSpacingRelative();
441 else
442 return m_barSpacingRelative;
443}
444
445void QQuickGraphsBarsNode::setBarSeriesMargin(QSizeF margin)
446{
447 if (m_barSeriesMargin == margin)
448 return;
449
450 m_barSeriesMargin = margin;
451 if (graphBars())
452 graphBars()->setBarSeriesMargin(margin);
453}
454
455QSizeF QQuickGraphsBarsNode::barSeriesMargin() const
456{
457 if (graphBars())
458 return graphBars()->barSeriesMargin();
459 else
460 return m_barSeriesMargin;
461}
462
463QBar3DSeries *QQuickGraphsBarsNode::selectedSeries() const
464{
465 if (graphBars())
466 return graphBars()->selectedSeries();
467 else
468 return nullptr;
469}
470
471void QQuickGraphsBarsNode::setPrimarySeries(QBar3DSeries *series)
472{
473 if (m_primarySeries == series)
474 return;
475
476 m_primarySeries = series;
477 if (graphBars())
478 graphBars()->setPrimarySeries(series);
479}
480
481QBar3DSeries *QQuickGraphsBarsNode::primarySeries() const
482{
483 if (graphBars())
484 return graphBars()->primarySeries();
485 else
486 return m_primarySeries;
487}
488
489void QQuickGraphsBarsNode::setFloorLevel(float floorLevel)
490{
491 if (m_floorLevel == floorLevel)
492 return;
493
494 m_floorLevel = floorLevel;
495 if (graphBars())
496 graphBars()->setFloorLevel(floorLevel);
497}
498
499float QQuickGraphsBarsNode::floorLevel() const
500{
501 if (graphBars())
502 return graphBars()->floorLevel();
503 else
504 return m_floorLevel;
505}
506
507QList<QBar3DSeries *> QQuickGraphsBarsNode::barSeriesList()
508{
509 if (graphBars()) {
510 return graphBars()->barSeriesList();
511 } else {
512 QList<QBar3DSeries *> barSeriesList;
513 for (QAbstract3DSeries *abstractSeries : std::as_const(t&: m_seriesList)) {
514 QBar3DSeries *barSeries = qobject_cast<QBar3DSeries *>(object: abstractSeries);
515 if (barSeries)
516 barSeriesList.append(t: barSeries);
517 }
518 return barSeriesList;
519 }
520}
521
522QQmlListProperty<QBar3DSeries> QQuickGraphsBarsNode::seriesList()
523{
524 if (graphBars()) {
525 return graphBars()->seriesList();
526 } else {
527 return QQmlListProperty<QBar3DSeries>(this,
528 this,
529 &QQuickGraphsBarsNode::appendSeriesFunc,
530 &QQuickGraphsBarsNode::countSeriesFunc,
531 &QQuickGraphsBarsNode::atSeriesFunc,
532 &QQuickGraphsBarsNode::clearSeriesFunc);
533 }
534}
535
536void QQuickGraphsBarsNode::appendSeriesFunc(QQmlListProperty<QBar3DSeries> *list,
537 QBar3DSeries *series)
538{
539 reinterpret_cast<QQuickGraphsBarsNode *>(list->data)->addSeries(series);
540}
541
542qsizetype QQuickGraphsBarsNode::countSeriesFunc(QQmlListProperty<QBar3DSeries> *list)
543{
544 return reinterpret_cast<QQuickGraphsBarsNode *>(list->data)->barSeriesList().size();
545}
546
547QBar3DSeries *QQuickGraphsBarsNode::atSeriesFunc(QQmlListProperty<QBar3DSeries> *list,
548 qsizetype index)
549{
550 return reinterpret_cast<QQuickGraphsBarsNode *>(list->data)->barSeriesList().at(i: index);
551}
552
553void QQuickGraphsBarsNode::clearSeriesFunc(QQmlListProperty<QBar3DSeries> *list)
554{
555 QQuickGraphsBarsNode *declBarsNode = reinterpret_cast<QQuickGraphsBarsNode *>(list->data);
556 QList<QBar3DSeries *> realList = declBarsNode->barSeriesList();
557 qsizetype count = realList.size();
558 for (qsizetype i = 0; i < count; i++)
559 declBarsNode->removeSeries(series: realList.at(i));
560}
561
562void QQuickGraphsBarsNode::addSeries(QBar3DSeries *series)
563{
564 Q_ASSERT(series && series->type() == QAbstract3DSeries::SeriesType::Bar);
565
566 if (graphBars())
567 graphBars()->addSeries(series);
568
569 QQuickGraphsNode::addSeriesInternal(series);
570}
571
572void QQuickGraphsBarsNode::removeSeries(QBar3DSeries *series)
573{
574 if (graphBars())
575 graphBars()->removeSeries(series);
576 QQuickGraphsNode::removeSeriesInternal(series);
577}
578
579void QQuickGraphsBarsNode::insertSeries(qsizetype index, QBar3DSeries *series)
580{
581 if (graphBars())
582 graphBars()->insertSeries(index, series);
583
584 QQuickGraphsNode::insertSeries(index, series);
585}
586
587void QQuickGraphsBarsNode::clearSelection()
588{
589 if (graphBars())
590 graphBars()->clearSelection();
591}
592
593bool QQuickGraphsBarsNode::doPicking(QPointF point)
594{
595 if (graphBars())
596 return graphBars()->doPicking(position: point);
597 else
598 return false;
599}
600
601bool QQuickGraphsBarsNode::doRayPicking(const QVector3D &origin, const QVector3D &direction)
602{
603 if (graphBars())
604 return graphBars()->doRayPicking(origin, direction);
605 else
606 return false;
607}
608
609
610/*!
611 * \internal
612 */
613QQuickGraphsBars *QQuickGraphsBarsNode::graphBars()
614{
615 return static_cast<QQuickGraphsBars *>(m_graph.get());
616}
617
618/*!
619 * \internal
620 */
621const QQuickGraphsBars *QQuickGraphsBarsNode::graphBars() const
622{
623 return static_cast<QQuickGraphsBars *>(m_graph.get());
624}
625
626QT_END_NAMESPACE
627

source code of qtgraphs/src/graphs3d/qml/qquickgraphsbarsnode.cpp