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 "bars3dcontroller_p.h"
31#include "bars3drenderer_p.h"
32#include "qvalue3daxis_p.h"
33#include "qcategory3daxis_p.h"
34#include "qbardataproxy_p.h"
35#include "qbar3dseries_p.h"
36#include "thememanager_p.h"
37#include "q3dtheme_p.h"
38#include <QtCore/QMutexLocker>
39
40QT_BEGIN_NAMESPACE_DATAVISUALIZATION
41
42Bars3DController::Bars3DController(QRect boundRect, Q3DScene *scene)
43 : Abstract3DController(boundRect, scene),
44 m_selectedBar(invalidSelectionPosition()),
45 m_selectedBarSeries(0),
46 m_primarySeries(0),
47 m_isMultiSeriesUniform(false),
48 m_isBarSpecRelative(true),
49 m_barThicknessRatio(1.0f),
50 m_barSpacing(QSizeF(1.0, 1.0)),
51 m_floorLevel(0.0f),
52 m_renderer(0)
53{
54 // Setting a null axis creates a new default axis according to orientation and graph type.
55 // Note: these cannot be set in the Abstract3DController constructor, as they will call virtual
56 // functions implemented by subclasses.
57 setAxisX(0);
58 setAxisY(0);
59 setAxisZ(0);
60}
61
62Bars3DController::~Bars3DController()
63{
64}
65
66void Bars3DController::initializeOpenGL()
67{
68 QMutexLocker mutexLocker(&m_renderMutex);
69
70 // Initialization is called multiple times when Qt Quick components are used
71 if (isInitialized())
72 return;
73
74 m_renderer = new Bars3DRenderer(this);
75
76 setRenderer(m_renderer);
77
78 mutexLocker.unlock();
79 synchDataToRenderer();
80
81 emitNeedRender();
82}
83
84void Bars3DController::synchDataToRenderer()
85{
86 QMutexLocker mutexLocker(&m_renderMutex);
87
88 if (!isInitialized())
89 return;
90
91 // Background change requires reloading the meshes in bar graphs, so dirty the series visuals
92 if (m_themeManager->activeTheme()->d_ptr->m_dirtyBits.backgroundEnabledDirty) {
93 m_isSeriesVisualsDirty = true;
94 foreach (QAbstract3DSeries *series, m_seriesList)
95 series->d_ptr->m_changeTracker.meshChanged = true;
96 }
97
98 // If y range or reverse changed, scene needs to be updated to update camera limits
99 bool needSceneUpdate = false;
100 if (Abstract3DController::m_changeTracker.axisYRangeChanged
101 || Abstract3DController::m_changeTracker.axisYReversedChanged) {
102 needSceneUpdate = true;
103 }
104
105 // Floor level update requires data update, so do before abstract sync
106 if (m_changeTracker.floorLevelChanged) {
107 m_renderer->updateFloorLevel(level: m_floorLevel);
108 m_changeTracker.floorLevelChanged = false;
109 }
110
111 Abstract3DController::synchDataToRenderer();
112
113 // Notify changes to renderer
114 if (m_changeTracker.rowsChanged) {
115 m_renderer->updateRows(rows: m_changedRows);
116 m_changeTracker.rowsChanged = false;
117 m_changedRows.clear();
118 }
119
120 if (m_changeTracker.itemChanged) {
121 m_renderer->updateItems(items: m_changedItems);
122 m_changeTracker.itemChanged = false;
123 m_changedItems.clear();
124 }
125
126 if (m_changeTracker.multiSeriesScalingChanged) {
127 m_renderer->updateMultiSeriesScaling(uniform: m_isMultiSeriesUniform);
128 m_changeTracker.multiSeriesScalingChanged = false;
129 }
130
131 if (m_changeTracker.barSpecsChanged) {
132 m_renderer->updateBarSpecs(thicknessRatio: m_barThicknessRatio, spacing: m_barSpacing, relative: m_isBarSpecRelative);
133 m_changeTracker.barSpecsChanged = false;
134 }
135
136 // Needs to be done after data is set, as it needs to know the visual array.
137 if (m_changeTracker.selectedBarChanged) {
138 m_renderer->updateSelectedBar(position: m_selectedBar, series: m_selectedBarSeries);
139 m_changeTracker.selectedBarChanged = false;
140 }
141
142 // Since scene is updated before axis updates are handled, do another render pass to
143 // properly update controller side camera limits.
144 if (needSceneUpdate)
145 m_scene->d_ptr->markDirty();
146}
147
148void Bars3DController::handleArrayReset()
149{
150 QBar3DSeries *series;
151 if (qobject_cast<QBarDataProxy *>(object: sender()))
152 series = static_cast<QBarDataProxy *>(sender())->series();
153 else
154 series = static_cast<QBar3DSeries *>(sender());
155
156 if (series->isVisible()) {
157 adjustAxisRanges();
158 m_isDataDirty = true;
159 series->d_ptr->markItemLabelDirty();
160 }
161 if (!m_changedSeriesList.contains(t: series))
162 m_changedSeriesList.append(t: series);
163 // Clear selection unless still valid
164 setSelectedBar(position: m_selectedBar, series: m_selectedBarSeries, enterSlice: false);
165 emitNeedRender();
166}
167
168void Bars3DController::handleRowsAdded(int startIndex, int count)
169{
170 Q_UNUSED(startIndex)
171 Q_UNUSED(count)
172 QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
173 if (series->isVisible()) {
174 adjustAxisRanges();
175 m_isDataDirty = true;
176 }
177 if (!m_changedSeriesList.contains(t: series))
178 m_changedSeriesList.append(t: series);
179 emitNeedRender();
180}
181
182void Bars3DController::handleRowsChanged(int startIndex, int count)
183{
184 QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
185 int oldChangeCount = m_changedRows.size();
186 if (!oldChangeCount)
187 m_changedRows.reserve(asize: count);
188
189 for (int i = 0; i < count; i++) {
190 bool newItem = true;
191 int candidate = startIndex + i;
192 for (int j = 0; j < oldChangeCount; j++) {
193 const ChangeRow &oldChangeItem = m_changedRows.at(i: j);
194 if (oldChangeItem.row == candidate && series == oldChangeItem.series) {
195 newItem = false;
196 break;
197 }
198 }
199 if (newItem) {
200 ChangeRow newChangeItem = {.series: series, .row: candidate};
201 m_changedRows.append(t: newChangeItem);
202 if (series == m_selectedBarSeries && m_selectedBar.x() == candidate)
203 series->d_ptr->markItemLabelDirty();
204 }
205 }
206 if (count) {
207 m_changeTracker.rowsChanged = true;
208
209 if (series->isVisible())
210 adjustAxisRanges();
211
212 // Clear selection unless still valid (row length might have changed)
213 setSelectedBar(position: m_selectedBar, series: m_selectedBarSeries, enterSlice: false);
214 emitNeedRender();
215 }
216}
217
218void Bars3DController::handleRowsRemoved(int startIndex, int count)
219{
220 Q_UNUSED(startIndex)
221 Q_UNUSED(count)
222
223 QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
224 if (series == m_selectedBarSeries) {
225 // If rows removed from selected series before the selection, adjust the selection
226 int selectedRow = m_selectedBar.x();
227 if (startIndex <= selectedRow) {
228 if ((startIndex + count) > selectedRow)
229 selectedRow = -1; // Selected row removed
230 else
231 selectedRow -= count; // Move selected row down by amount of rows removed
232
233 setSelectedBar(position: QPoint(selectedRow, m_selectedBar.y()), series: m_selectedBarSeries, enterSlice: false);
234 }
235 }
236
237 if (series->isVisible()) {
238 adjustAxisRanges();
239 m_isDataDirty = true;
240 }
241 if (!m_changedSeriesList.contains(t: series))
242 m_changedSeriesList.append(t: series);
243
244 emitNeedRender();
245}
246
247void Bars3DController::handleRowsInserted(int startIndex, int count)
248{
249 Q_UNUSED(startIndex)
250 Q_UNUSED(count)
251 QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
252 if (series == m_selectedBarSeries) {
253 // If rows inserted to selected series before the selection, adjust the selection
254 int selectedRow = m_selectedBar.x();
255 if (startIndex <= selectedRow) {
256 selectedRow += count;
257 setSelectedBar(position: QPoint(selectedRow, m_selectedBar.y()), series: m_selectedBarSeries, enterSlice: false);
258 }
259 }
260
261 if (series->isVisible()) {
262 adjustAxisRanges();
263 m_isDataDirty = true;
264 }
265 if (!m_changedSeriesList.contains(t: series))
266 m_changedSeriesList.append(t: series);
267
268 emitNeedRender();
269}
270
271void Bars3DController::handleItemChanged(int rowIndex, int columnIndex)
272{
273 QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
274
275 bool newItem = true;
276 QPoint candidate(rowIndex, columnIndex);
277 foreach (ChangeItem item, m_changedItems) {
278 if (item.point == candidate && item.series == series) {
279 newItem = false;
280 break;
281 }
282 }
283
284 if (newItem) {
285 ChangeItem newItem = {.series: series, .point: candidate};
286 m_changedItems.append(t: newItem);
287 m_changeTracker.itemChanged = true;
288
289 if (series == m_selectedBarSeries && m_selectedBar == candidate)
290 series->d_ptr->markItemLabelDirty();
291 if (series->isVisible())
292 adjustAxisRanges();
293 emitNeedRender();
294 }
295}
296
297void Bars3DController::handleDataRowLabelsChanged()
298{
299 if (m_axisZ) {
300 // Grab a sublist equal to data window (no need to have more labels in axis)
301 int min = int(m_axisZ->min());
302 int count = int(m_axisZ->max()) - min + 1;
303 QStringList subList;
304 if (m_primarySeries && m_primarySeries->dataProxy())
305 subList = m_primarySeries->dataProxy()->rowLabels().mid(pos: min, alength: count);
306 static_cast<QCategory3DAxis *>(m_axisZ)->dptr()->setDataLabels(subList);
307 }
308}
309
310void Bars3DController::handleDataColumnLabelsChanged()
311{
312 if (m_axisX) {
313 // Grab a sublist equal to data window (no need to have more labels in axis)
314 int min = int(m_axisX->min());
315 int count = int(m_axisX->max()) - min + 1;
316 QStringList subList;
317 if (m_primarySeries && m_primarySeries->dataProxy()) {
318 subList = static_cast<QBarDataProxy *>(m_primarySeries->dataProxy())
319 ->columnLabels().mid(pos: min, alength: count);
320 }
321 static_cast<QCategory3DAxis *>(m_axisX)->dptr()->setDataLabels(subList);
322 }
323}
324
325void Bars3DController::handleAxisAutoAdjustRangeChangedInOrientation(
326 QAbstract3DAxis::AxisOrientation orientation, bool autoAdjust)
327{
328 Q_UNUSED(orientation)
329 Q_UNUSED(autoAdjust)
330 adjustAxisRanges();
331}
332
333void Bars3DController::handleSeriesVisibilityChangedBySender(QObject *sender)
334{
335 Abstract3DController::handleSeriesVisibilityChangedBySender(sender);
336
337 // Visibility changes may require disabling slicing,
338 // so just reset selection to ensure everything is still valid.
339 setSelectedBar(position: m_selectedBar, series: m_selectedBarSeries, enterSlice: false);
340}
341
342void Bars3DController::handlePendingClick()
343{
344 // This function is called while doing the sync, so it is okay to query from renderer
345 QPoint position = m_renderer->clickedPosition();
346 QBar3DSeries *series = static_cast<QBar3DSeries *>(m_renderer->clickedSeries());
347
348 setSelectedBar(position, series, enterSlice: true);
349
350 Abstract3DController::handlePendingClick();
351
352 m_renderer->resetClickedStatus();
353}
354
355QPoint Bars3DController::invalidSelectionPosition()
356{
357 static QPoint invalidSelectionPos(-1, -1);
358 return invalidSelectionPos;
359}
360
361void Bars3DController::setAxisX(QAbstract3DAxis *axis)
362{
363 Abstract3DController::setAxisX(axis);
364 handleDataColumnLabelsChanged();
365}
366
367void Bars3DController::setAxisZ(QAbstract3DAxis *axis)
368{
369 Abstract3DController::setAxisZ(axis);
370 handleDataRowLabelsChanged();
371}
372
373void Bars3DController::setPrimarySeries(QBar3DSeries *series)
374{
375 if (!series) {
376 if (m_seriesList.size())
377 series = static_cast<QBar3DSeries *>(m_seriesList.at(i: 0));
378 } else if (!m_seriesList.contains(t: series)) {
379 // Add nonexistent series.
380 addSeries(series);
381 }
382
383 if (m_primarySeries != series) {
384 m_primarySeries = series;
385 handleDataRowLabelsChanged();
386 handleDataColumnLabelsChanged();
387 emit primarySeriesChanged(series: m_primarySeries);
388 }
389}
390
391QBar3DSeries *Bars3DController::primarySeries() const
392{
393 return m_primarySeries;
394}
395
396void Bars3DController::addSeries(QAbstract3DSeries *series)
397{
398 insertSeries(index: m_seriesList.size(), series);
399}
400
401void Bars3DController::removeSeries(QAbstract3DSeries *series)
402{
403 bool wasVisible = (series && series->d_ptr->m_controller == this && series->isVisible());
404
405 Abstract3DController::removeSeries(series);
406
407 if (m_selectedBarSeries == series)
408 setSelectedBar(position: invalidSelectionPosition(), series: 0, enterSlice: false);
409
410 if (wasVisible)
411 adjustAxisRanges();
412
413 // If primary series is removed, reset it to default
414 if (series == m_primarySeries) {
415 if (m_seriesList.size())
416 m_primarySeries = static_cast<QBar3DSeries *>(m_seriesList.at(i: 0));
417 else
418 m_primarySeries = 0;
419
420 handleDataRowLabelsChanged();
421 handleDataColumnLabelsChanged();
422
423 emit primarySeriesChanged(series: m_primarySeries);
424 }
425}
426
427void Bars3DController::insertSeries(int index, QAbstract3DSeries *series)
428{
429 Q_ASSERT(series && series->type() == QAbstract3DSeries::SeriesTypeBar);
430
431 int oldSize = m_seriesList.size();
432
433 Abstract3DController::insertSeries(index, series);
434
435 if (oldSize != m_seriesList.size()) {
436 QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(series);
437 if (!oldSize) {
438 m_primarySeries = barSeries;
439 handleDataRowLabelsChanged();
440 handleDataColumnLabelsChanged();
441 }
442
443 if (barSeries->selectedBar() != invalidSelectionPosition())
444 setSelectedBar(position: barSeries->selectedBar(), series: barSeries, enterSlice: false);
445
446 if (!oldSize)
447 emit primarySeriesChanged(series: m_primarySeries);
448 }
449}
450
451QList<QBar3DSeries *> Bars3DController::barSeriesList()
452{
453 QList<QAbstract3DSeries *> abstractSeriesList = seriesList();
454 QList<QBar3DSeries *> barSeriesList;
455 foreach (QAbstract3DSeries *abstractSeries, abstractSeriesList) {
456 QBar3DSeries *barSeries = qobject_cast<QBar3DSeries *>(object: abstractSeries);
457 if (barSeries)
458 barSeriesList.append(t: barSeries);
459 }
460
461 return barSeriesList;
462}
463
464void Bars3DController::handleAxisRangeChangedBySender(QObject *sender)
465{
466 // Data window changed
467 if (sender == m_axisX || sender == m_axisZ) {
468 if (sender == m_axisX)
469 handleDataColumnLabelsChanged();
470 if (sender == m_axisZ)
471 handleDataRowLabelsChanged();
472 }
473
474 Abstract3DController::handleAxisRangeChangedBySender(sender);
475
476 // Update selected bar - may be moved offscreen
477 setSelectedBar(position: m_selectedBar, series: m_selectedBarSeries, enterSlice: false);
478}
479
480void Bars3DController::setMultiSeriesScaling(bool uniform)
481{
482 m_isMultiSeriesUniform = uniform;
483
484 m_changeTracker.multiSeriesScalingChanged = true;
485 emitNeedRender();
486}
487
488bool Bars3DController::multiSeriesScaling() const
489{
490 return m_isMultiSeriesUniform;
491}
492
493void Bars3DController::setBarSpecs(GLfloat thicknessRatio, const QSizeF &spacing, bool relative)
494{
495 m_barThicknessRatio = thicknessRatio;
496 m_barSpacing = spacing;
497 m_isBarSpecRelative = relative;
498
499 m_changeTracker.barSpecsChanged = true;
500 emitNeedRender();
501}
502
503GLfloat Bars3DController::barThickness()
504{
505 return m_barThicknessRatio;
506}
507
508QSizeF Bars3DController::barSpacing()
509{
510 return m_barSpacing;
511}
512
513bool Bars3DController::isBarSpecRelative()
514{
515 return m_isBarSpecRelative;
516}
517
518void Bars3DController::setFloorLevel(float level)
519{
520 m_floorLevel = level;
521 m_isDataDirty = true;
522 m_changeTracker.floorLevelChanged = true;
523 emitNeedRender();
524}
525
526float Bars3DController::floorLevel() const
527{
528 return m_floorLevel;
529}
530
531void Bars3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode)
532{
533 if (mode.testFlag(flag: QAbstract3DGraph::SelectionSlice)
534 && (mode.testFlag(flag: QAbstract3DGraph::SelectionRow)
535 == mode.testFlag(flag: QAbstract3DGraph::SelectionColumn))) {
536 qWarning(msg: "Must specify one of either row or column selection mode in conjunction with slicing mode.");
537 } else {
538 QAbstract3DGraph::SelectionFlags oldMode = selectionMode();
539
540 Abstract3DController::setSelectionMode(mode);
541
542 if (mode != oldMode) {
543 // Refresh selection upon mode change to ensure slicing is correctly updated
544 // according to series the visibility.
545 setSelectedBar(position: m_selectedBar, series: m_selectedBarSeries, enterSlice: true);
546
547 // Special case: Always deactivate slicing when changing away from slice
548 // automanagement, as this can't be handled in setSelectedBar.
549 if (!mode.testFlag(flag: QAbstract3DGraph::SelectionSlice)
550 && oldMode.testFlag(flag: QAbstract3DGraph::SelectionSlice)) {
551 scene()->setSlicingActive(false);
552 }
553 }
554 }
555}
556
557void Bars3DController::setSelectedBar(const QPoint &position, QBar3DSeries *series, bool enterSlice)
558{
559 // If the selection targets non-existent bar, clear selection instead.
560 QPoint pos = position;
561
562 // Series may already have been removed, so check it before setting the selection.
563 if (!m_seriesList.contains(t: series))
564 series = 0;
565
566 adjustSelectionPosition(pos, series);
567
568 if (selectionMode().testFlag(flag: QAbstract3DGraph::SelectionSlice)) {
569 // If the selected bar is outside data window, or there is no visible selected bar,
570 // disable slicing.
571 if (pos.x() < m_axisZ->min() || pos.x() > m_axisZ->max()
572 || pos.y() < m_axisX->min() || pos.y() > m_axisX->max()
573 || !series->isVisible()) {
574 scene()->setSlicingActive(false);
575 } else if (enterSlice) {
576 scene()->setSlicingActive(true);
577 }
578 emitNeedRender();
579 }
580
581 if (pos != m_selectedBar || series != m_selectedBarSeries) {
582 bool seriesChanged = (series != m_selectedBarSeries);
583 m_selectedBar = pos;
584 m_selectedBarSeries = series;
585 m_changeTracker.selectedBarChanged = true;
586
587 // Clear selection from other series and finally set new selection to the specified series
588 foreach (QAbstract3DSeries *otherSeries, m_seriesList) {
589 QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(otherSeries);
590 if (barSeries != m_selectedBarSeries)
591 barSeries->dptr()->setSelectedBar(invalidSelectionPosition());
592 }
593 if (m_selectedBarSeries)
594 m_selectedBarSeries->dptr()->setSelectedBar(m_selectedBar);
595
596 if (seriesChanged)
597 emit selectedSeriesChanged(series: m_selectedBarSeries);
598
599 emitNeedRender();
600 }
601}
602
603void Bars3DController::clearSelection()
604{
605 setSelectedBar(position: invalidSelectionPosition(), series: 0, enterSlice: false);
606}
607
608void Bars3DController::adjustAxisRanges()
609{
610 QCategory3DAxis *categoryAxisZ = static_cast<QCategory3DAxis *>(m_axisZ);
611 QCategory3DAxis *categoryAxisX = static_cast<QCategory3DAxis *>(m_axisX);
612 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(m_axisY);
613
614 bool adjustZ = (categoryAxisZ && categoryAxisZ->isAutoAdjustRange());
615 bool adjustX = (categoryAxisX && categoryAxisX->isAutoAdjustRange());
616 bool adjustY = (valueAxis && categoryAxisX && categoryAxisZ && valueAxis->isAutoAdjustRange());
617
618 if (adjustZ || adjustX || adjustY) {
619 int maxRowCount = 0;
620 int maxColumnCount = 0;
621 float minValue = 0.0f;
622 float maxValue = 0.0f;
623
624 // First figure out row and column counts
625 int seriesCount = m_seriesList.size();
626 if (adjustZ || adjustX) {
627 for (int series = 0; series < seriesCount; series++) {
628 const QBar3DSeries *barSeries =
629 static_cast<QBar3DSeries *>(m_seriesList.at(i: series));
630 if (barSeries->isVisible()) {
631 const QBarDataProxy *proxy = barSeries->dataProxy();
632
633 if (adjustZ && proxy) {
634 int rowCount = proxy->rowCount();
635 if (rowCount)
636 rowCount--;
637
638 maxRowCount = qMax(a: maxRowCount, b: rowCount);
639 }
640
641 if (adjustX && proxy) {
642 const QBarDataArray *array = proxy->array();
643 int columnCount = 0;
644 for (int i = 0; i < array->size(); i++) {
645 if (columnCount < array->at(i)->size())
646 columnCount = array->at(i)->size();
647 }
648 if (columnCount)
649 columnCount--;
650
651 maxColumnCount = qMax(a: maxColumnCount, b: columnCount);
652 }
653 }
654 }
655 // Call private implementations of setRange to avoid unsetting auto adjust flag
656 if (adjustZ)
657 categoryAxisZ->dptr()->setRange(min: 0.0f, max: float(maxRowCount), suppressWarnings: true);
658 if (adjustX)
659 categoryAxisX->dptr()->setRange(min: 0.0f, max: float(maxColumnCount), suppressWarnings: true);
660 }
661
662 // Now that we know the row and column ranges, figure out the value axis range
663 if (adjustY) {
664 for (int series = 0; series < seriesCount; series++) {
665 const QBar3DSeries *barSeries =
666 static_cast<QBar3DSeries *>(m_seriesList.at(i: series));
667 if (barSeries->isVisible()) {
668 const QBarDataProxy *proxy = barSeries->dataProxy();
669 if (adjustY && proxy) {
670 QPair<GLfloat, GLfloat> limits =
671 proxy->dptrc()->limitValues(startRow: categoryAxisZ->min(),
672 startColumn: categoryAxisZ->max(),
673 rowCount: categoryAxisX->min(),
674 columnCount: categoryAxisX->max());
675 if (!series) {
676 // First series initializes the values
677 minValue = limits.first;
678 maxValue = limits.second;
679 } else {
680 minValue = qMin(a: minValue, b: limits.first);
681 maxValue = qMax(a: maxValue, b: limits.second);
682 }
683 }
684 }
685 }
686
687 if (maxValue < 0.0f)
688 maxValue = 0.0f;
689 if (minValue > 0.0f)
690 minValue = 0.0f;
691 if (minValue == 0.0f && maxValue == 0.0f) {
692 // Only zero value values in data set, set range to something.
693 minValue = 0.0f;
694 maxValue = 1.0f;
695 }
696 valueAxis->dptr()->setRange(min: minValue, max: maxValue, suppressWarnings: true);
697 }
698 }
699}
700
701// Invalidate selection position if outside data for the series
702void Bars3DController::adjustSelectionPosition(QPoint &pos, const QBar3DSeries *series)
703{
704 const QBarDataProxy *proxy = 0;
705 if (series)
706 proxy = series->dataProxy();
707
708 if (!proxy)
709 pos = invalidSelectionPosition();
710
711 if (pos != invalidSelectionPosition()) {
712 int maxRow = proxy->rowCount() - 1;
713 int maxCol = (pos.x() <= maxRow && pos.x() >= 0 && proxy->rowAt(rowIndex: pos.x()))
714 ? proxy->rowAt(rowIndex: pos.x())->size() - 1 : -1;
715
716 if (pos.x() < 0 || pos.x() > maxRow || pos.y() < 0 || pos.y() > maxCol)
717 pos = invalidSelectionPosition();
718 }
719}
720
721QAbstract3DAxis *Bars3DController::createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation)
722{
723 QAbstract3DAxis *defaultAxis = 0;
724
725 if (orientation == QAbstract3DAxis::AxisOrientationY)
726 defaultAxis = createDefaultValueAxis();
727 else
728 defaultAxis = createDefaultCategoryAxis();
729
730 return defaultAxis;
731}
732
733QT_END_NAMESPACE_DATAVISUALIZATION
734

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