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 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | Bars3DController::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 | |
39 | Bars3DController::~Bars3DController() |
40 | { |
41 | } |
42 | |
43 | void 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 | |
61 | void 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 | |
130 | void 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 | |
150 | void 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 | |
164 | void 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 | |
200 | void 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 | |
229 | void 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 | |
253 | void 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 | |
279 | void 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 | |
292 | void 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 | |
307 | void Bars3DController::handleRowColorsChanged() |
308 | { |
309 | emitNeedRender(); |
310 | } |
311 | |
312 | void Bars3DController::handleAxisAutoAdjustRangeChangedInOrientation( |
313 | QAbstract3DAxis::AxisOrientation orientation, bool autoAdjust) |
314 | { |
315 | Q_UNUSED(orientation); |
316 | Q_UNUSED(autoAdjust); |
317 | adjustAxisRanges(); |
318 | } |
319 | |
320 | void 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 | |
329 | void 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 | |
342 | QPoint Bars3DController::invalidSelectionPosition() |
343 | { |
344 | static QPoint invalidSelectionPos(-1, -1); |
345 | return invalidSelectionPos; |
346 | } |
347 | |
348 | void Bars3DController::setAxisX(QAbstract3DAxis *axis) |
349 | { |
350 | Abstract3DController::setAxisX(axis); |
351 | handleDataColumnLabelsChanged(); |
352 | } |
353 | |
354 | void Bars3DController::setAxisZ(QAbstract3DAxis *axis) |
355 | { |
356 | Abstract3DController::setAxisZ(axis); |
357 | handleDataRowLabelsChanged(); |
358 | } |
359 | |
360 | void 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 | |
378 | QBar3DSeries *Bars3DController::primarySeries() const |
379 | { |
380 | return m_primarySeries; |
381 | } |
382 | |
383 | void Bars3DController::addSeries(QAbstract3DSeries *series) |
384 | { |
385 | insertSeries(index: m_seriesList.size(), series); |
386 | } |
387 | |
388 | void 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 | |
414 | void 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 | |
438 | QList<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 | |
451 | void 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 | |
467 | void Bars3DController::setMultiSeriesScaling(bool uniform) |
468 | { |
469 | m_isMultiSeriesUniform = uniform; |
470 | |
471 | m_changeTracker.multiSeriesScalingChanged = true; |
472 | emitNeedRender(); |
473 | } |
474 | |
475 | bool Bars3DController::multiSeriesScaling() const |
476 | { |
477 | return m_isMultiSeriesUniform; |
478 | } |
479 | |
480 | void 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 | |
490 | GLfloat Bars3DController::barThickness() |
491 | { |
492 | return m_barThicknessRatio; |
493 | } |
494 | |
495 | QSizeF Bars3DController::barSpacing() |
496 | { |
497 | return m_barSpacing; |
498 | } |
499 | |
500 | void Bars3DController::setBarSeriesMargin(const QSizeF &margin) |
501 | { |
502 | m_barSeriesMargin = margin; |
503 | m_changeTracker.barSeriesMarginChanged = true; |
504 | emitNeedRender(); |
505 | } |
506 | |
507 | QSizeF Bars3DController::barSeriesMargin() |
508 | { |
509 | return m_barSeriesMargin; |
510 | } |
511 | |
512 | bool Bars3DController::isBarSpecRelative() |
513 | { |
514 | return m_isBarSpecRelative; |
515 | } |
516 | |
517 | void Bars3DController::setFloorLevel(float level) |
518 | { |
519 | m_floorLevel = level; |
520 | m_isDataDirty = true; |
521 | m_changeTracker.floorLevelChanged = true; |
522 | emitNeedRender(); |
523 | } |
524 | |
525 | float Bars3DController::floorLevel() const |
526 | { |
527 | return m_floorLevel; |
528 | } |
529 | |
530 | void 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 | |
556 | void 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 | |
602 | void Bars3DController::clearSelection() |
603 | { |
604 | setSelectedBar(position: invalidSelectionPosition(), series: 0, enterSlice: false); |
605 | } |
606 | |
607 | void 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 |
701 | void 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 | |
720 | QAbstract3DAxis *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 | |
732 | QT_END_NAMESPACE |
733 |
Definitions
- Bars3DController
- ~Bars3DController
- initializeOpenGL
- synchDataToRenderer
- handleArrayReset
- handleRowsAdded
- handleRowsChanged
- handleRowsRemoved
- handleRowsInserted
- handleItemChanged
- handleDataRowLabelsChanged
- handleDataColumnLabelsChanged
- handleRowColorsChanged
- handleAxisAutoAdjustRangeChangedInOrientation
- handleSeriesVisibilityChangedBySender
- handlePendingClick
- invalidSelectionPosition
- setAxisX
- setAxisZ
- setPrimarySeries
- primarySeries
- addSeries
- removeSeries
- insertSeries
- barSeriesList
- handleAxisRangeChangedBySender
- setMultiSeriesScaling
- multiSeriesScaling
- setBarSpecs
- barThickness
- barSpacing
- setBarSeriesMargin
- barSeriesMargin
- isBarSpecRelative
- setFloorLevel
- floorLevel
- setSelectionMode
- setSelectedBar
- clearSelection
- adjustAxisRanges
- adjustSelectionPosition
Learn to use CMake with our Intro Training
Find out more