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

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