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