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 
40 QT_BEGIN_NAMESPACE_DATAVISUALIZATION
41 
Bars3DController(QRect boundRect,Q3DScene * scene)42 Bars3DController::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 
~Bars3DController()62 Bars3DController::~Bars3DController()
63 {
64 }
65 
initializeOpenGL()66 void 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 
synchDataToRenderer()84 void 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(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(m_changedRows);
116         m_changeTracker.rowsChanged = false;
117         m_changedRows.clear();
118     }
119 
120     if (m_changeTracker.itemChanged) {
121         m_renderer->updateItems(m_changedItems);
122         m_changeTracker.itemChanged = false;
123         m_changedItems.clear();
124     }
125 
126     if (m_changeTracker.multiSeriesScalingChanged) {
127         m_renderer->updateMultiSeriesScaling(m_isMultiSeriesUniform);
128         m_changeTracker.multiSeriesScalingChanged = false;
129     }
130 
131     if (m_changeTracker.barSpecsChanged) {
132         m_renderer->updateBarSpecs(m_barThicknessRatio, m_barSpacing, 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(m_selectedBar, 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 
handleArrayReset()148 void Bars3DController::handleArrayReset()
149 {
150     QBar3DSeries *series;
151     if (qobject_cast<QBarDataProxy *>(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(series))
162         m_changedSeriesList.append(series);
163     // Clear selection unless still valid
164     setSelectedBar(m_selectedBar, m_selectedBarSeries, false);
165     emitNeedRender();
166 }
167 
handleRowsAdded(int startIndex,int count)168 void 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(series))
178         m_changedSeriesList.append(series);
179     emitNeedRender();
180 }
181 
handleRowsChanged(int startIndex,int count)182 void 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(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(j);
194             if (oldChangeItem.row == candidate && series == oldChangeItem.series) {
195                 newItem = false;
196                 break;
197             }
198         }
199         if (newItem) {
200             ChangeRow newChangeItem = {series, candidate};
201             m_changedRows.append(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(m_selectedBar, m_selectedBarSeries, false);
214         emitNeedRender();
215     }
216 }
217 
handleRowsRemoved(int startIndex,int count)218 void 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(QPoint(selectedRow, m_selectedBar.y()), m_selectedBarSeries, false);
234         }
235     }
236 
237     if (series->isVisible()) {
238         adjustAxisRanges();
239         m_isDataDirty = true;
240     }
241     if (!m_changedSeriesList.contains(series))
242         m_changedSeriesList.append(series);
243 
244     emitNeedRender();
245 }
246 
handleRowsInserted(int startIndex,int count)247 void 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(QPoint(selectedRow, m_selectedBar.y()), m_selectedBarSeries, false);
258         }
259     }
260 
261     if (series->isVisible()) {
262         adjustAxisRanges();
263         m_isDataDirty = true;
264     }
265     if (!m_changedSeriesList.contains(series))
266         m_changedSeriesList.append(series);
267 
268     emitNeedRender();
269 }
270 
handleItemChanged(int rowIndex,int columnIndex)271 void 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, candidate};
286         m_changedItems.append(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 
handleDataRowLabelsChanged()297 void 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(min, count);
306         static_cast<QCategory3DAxis *>(m_axisZ)->dptr()->setDataLabels(subList);
307     }
308 }
309 
handleDataColumnLabelsChanged()310 void 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(min, count);
320         }
321         static_cast<QCategory3DAxis *>(m_axisX)->dptr()->setDataLabels(subList);
322     }
323 }
324 
handleAxisAutoAdjustRangeChangedInOrientation(QAbstract3DAxis::AxisOrientation orientation,bool autoAdjust)325 void Bars3DController::handleAxisAutoAdjustRangeChangedInOrientation(
326         QAbstract3DAxis::AxisOrientation orientation, bool autoAdjust)
327 {
328     Q_UNUSED(orientation)
329     Q_UNUSED(autoAdjust)
330     adjustAxisRanges();
331 }
332 
handleSeriesVisibilityChangedBySender(QObject * sender)333 void 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(m_selectedBar, m_selectedBarSeries, false);
340 }
341 
handlePendingClick()342 void 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, true);
349 
350     Abstract3DController::handlePendingClick();
351 
352     m_renderer->resetClickedStatus();
353 }
354 
invalidSelectionPosition()355 QPoint Bars3DController::invalidSelectionPosition()
356 {
357     static QPoint invalidSelectionPos(-1, -1);
358     return invalidSelectionPos;
359 }
360 
setAxisX(QAbstract3DAxis * axis)361 void Bars3DController::setAxisX(QAbstract3DAxis *axis)
362 {
363     Abstract3DController::setAxisX(axis);
364     handleDataColumnLabelsChanged();
365 }
366 
setAxisZ(QAbstract3DAxis * axis)367 void Bars3DController::setAxisZ(QAbstract3DAxis *axis)
368 {
369     Abstract3DController::setAxisZ(axis);
370     handleDataRowLabelsChanged();
371 }
372 
setPrimarySeries(QBar3DSeries * series)373 void Bars3DController::setPrimarySeries(QBar3DSeries *series)
374 {
375     if (!series) {
376         if (m_seriesList.size())
377             series = static_cast<QBar3DSeries *>(m_seriesList.at(0));
378     } else if (!m_seriesList.contains(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(m_primarySeries);
388     }
389 }
390 
primarySeries() const391 QBar3DSeries *Bars3DController::primarySeries() const
392 {
393     return m_primarySeries;
394 }
395 
addSeries(QAbstract3DSeries * series)396 void Bars3DController::addSeries(QAbstract3DSeries *series)
397 {
398     insertSeries(m_seriesList.size(), series);
399 }
400 
removeSeries(QAbstract3DSeries * series)401 void 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(invalidSelectionPosition(), 0, 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(0));
417         else
418             m_primarySeries = 0;
419 
420         handleDataRowLabelsChanged();
421         handleDataColumnLabelsChanged();
422 
423         emit primarySeriesChanged(m_primarySeries);
424     }
425 }
426 
insertSeries(int index,QAbstract3DSeries * series)427 void 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(barSeries->selectedBar(), barSeries, false);
445 
446         if (!oldSize)
447             emit primarySeriesChanged(m_primarySeries);
448     }
449 }
450 
barSeriesList()451 QList<QBar3DSeries *> Bars3DController::barSeriesList()
452 {
453     QList<QAbstract3DSeries *> abstractSeriesList = seriesList();
454     QList<QBar3DSeries *> barSeriesList;
455     foreach (QAbstract3DSeries *abstractSeries, abstractSeriesList) {
456         QBar3DSeries *barSeries = qobject_cast<QBar3DSeries *>(abstractSeries);
457         if (barSeries)
458             barSeriesList.append(barSeries);
459     }
460 
461     return barSeriesList;
462 }
463 
handleAxisRangeChangedBySender(QObject * sender)464 void 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(m_selectedBar, m_selectedBarSeries, false);
478 }
479 
setMultiSeriesScaling(bool uniform)480 void Bars3DController::setMultiSeriesScaling(bool uniform)
481 {
482     m_isMultiSeriesUniform = uniform;
483 
484     m_changeTracker.multiSeriesScalingChanged = true;
485     emitNeedRender();
486 }
487 
multiSeriesScaling() const488 bool Bars3DController::multiSeriesScaling() const
489 {
490     return m_isMultiSeriesUniform;
491 }
492 
setBarSpecs(GLfloat thicknessRatio,const QSizeF & spacing,bool relative)493 void 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 
barThickness()503 GLfloat Bars3DController::barThickness()
504 {
505     return m_barThicknessRatio;
506 }
507 
barSpacing()508 QSizeF Bars3DController::barSpacing()
509 {
510     return m_barSpacing;
511 }
512 
isBarSpecRelative()513 bool Bars3DController::isBarSpecRelative()
514 {
515     return m_isBarSpecRelative;
516 }
517 
setFloorLevel(float level)518 void Bars3DController::setFloorLevel(float level)
519 {
520     m_floorLevel = level;
521     m_isDataDirty = true;
522     m_changeTracker.floorLevelChanged = true;
523     emitNeedRender();
524 }
525 
floorLevel() const526 float Bars3DController::floorLevel() const
527 {
528     return m_floorLevel;
529 }
530 
setSelectionMode(QAbstract3DGraph::SelectionFlags mode)531 void Bars3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode)
532 {
533     if (mode.testFlag(QAbstract3DGraph::SelectionSlice)
534             && (mode.testFlag(QAbstract3DGraph::SelectionRow)
535                 == mode.testFlag(QAbstract3DGraph::SelectionColumn))) {
536         qWarning("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(m_selectedBar, m_selectedBarSeries, 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(QAbstract3DGraph::SelectionSlice)
550                     && oldMode.testFlag(QAbstract3DGraph::SelectionSlice)) {
551                 scene()->setSlicingActive(false);
552             }
553         }
554     }
555 }
556 
setSelectedBar(const QPoint & position,QBar3DSeries * series,bool enterSlice)557 void 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(series))
564         series = 0;
565 
566     adjustSelectionPosition(pos, series);
567 
568     if (selectionMode().testFlag(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(m_selectedBarSeries);
598 
599         emitNeedRender();
600     }
601 }
602 
clearSelection()603 void Bars3DController::clearSelection()
604 {
605     setSelectedBar(invalidSelectionPosition(), 0, false);
606 }
607 
adjustAxisRanges()608 void 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(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(maxRowCount, 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(maxColumnCount, columnCount);
652                     }
653                 }
654             }
655             // Call private implementations of setRange to avoid unsetting auto adjust flag
656             if (adjustZ)
657                 categoryAxisZ->dptr()->setRange(0.0f, float(maxRowCount), true);
658             if (adjustX)
659                 categoryAxisX->dptr()->setRange(0.0f, float(maxColumnCount), 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(series));
667                 if (barSeries->isVisible()) {
668                     const QBarDataProxy *proxy = barSeries->dataProxy();
669                     if (adjustY && proxy) {
670                         QPair<GLfloat, GLfloat> limits =
671                                 proxy->dptrc()->limitValues(categoryAxisZ->min(),
672                                                             categoryAxisZ->max(),
673                                                             categoryAxisX->min(),
674                                                             categoryAxisX->max());
675                         if (!series) {
676                             // First series initializes the values
677                             minValue = limits.first;
678                             maxValue = limits.second;
679                         } else {
680                             minValue = qMin(minValue, limits.first);
681                             maxValue = qMax(maxValue, 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(minValue, maxValue, true);
697         }
698     }
699 }
700 
701 // Invalidate selection position if outside data for the series
adjustSelectionPosition(QPoint & pos,const QBar3DSeries * series)702 void 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(pos.x()))
714                 ? proxy->rowAt(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 
createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation)721 QAbstract3DAxis *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 
733 QT_END_NAMESPACE_DATAVISUALIZATION
734