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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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