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 Charts 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 <QtCharts/qcategoryaxis.h> |
31 | #include <QtCharts/qlogvalueaxis.h> |
32 | #include <QtCore/qmath.h> |
33 | #include <private/chartpresenter_p.h> |
34 | #include <private/verticalaxis_p.h> |
35 | |
36 | QT_CHARTS_BEGIN_NAMESPACE |
37 | |
38 | VerticalAxis::VerticalAxis(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis) |
39 | : CartesianChartAxis(axis, item, intervalAxis) |
40 | { |
41 | } |
42 | |
43 | VerticalAxis::~VerticalAxis() |
44 | { |
45 | } |
46 | |
47 | QSizeF VerticalAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const |
48 | { |
49 | Q_UNUSED(constraint); |
50 | QSizeF sh(0, 0); |
51 | |
52 | if (axis()->titleText().isEmpty() || !titleItem()->isVisible()) |
53 | return sh; |
54 | |
55 | switch (which) { |
56 | case Qt::MinimumSize: { |
57 | QRectF titleRect = ChartPresenter::textBoundingRect(font: axis()->titleFont(), |
58 | QStringLiteral("..." )); |
59 | sh = QSizeF(titleRect.height() + (titlePadding() * 2.0), titleRect.width()); |
60 | break; |
61 | } |
62 | case Qt::MaximumSize: |
63 | case Qt::PreferredSize: { |
64 | QRectF titleRect = ChartPresenter::textBoundingRect(font: axis()->titleFont(), text: axis()->titleText()); |
65 | sh = QSizeF(titleRect.height() + (titlePadding() * 2.0), titleRect.width()); |
66 | break; |
67 | } |
68 | default: |
69 | break; |
70 | } |
71 | |
72 | return sh; |
73 | } |
74 | |
75 | void VerticalAxis::updateGeometry() |
76 | { |
77 | const QVector<qreal> &layout = ChartAxisElement::layout(); |
78 | const QVector<qreal> &dynamicMinorTicklayout = ChartAxisElement::dynamicMinorTicklayout(); |
79 | |
80 | if (layout.isEmpty() && dynamicMinorTicklayout.isEmpty() |
81 | && axis()->type() != QAbstractAxis::AxisTypeLogValue) { |
82 | return; |
83 | } |
84 | |
85 | QStringList labelList = labels(); |
86 | |
87 | QList<QGraphicsItem *> labels = labelItems(); |
88 | QList<QGraphicsItem *> arrow = arrowItems(); |
89 | QGraphicsTextItem *title = titleItem(); |
90 | |
91 | Q_ASSERT(labels.size() == labelList.size()); |
92 | Q_ASSERT(layout.size() == labelList.size()); |
93 | |
94 | const QRectF &axisRect = axisGeometry(); |
95 | const QRectF &gridRect = gridGeometry(); |
96 | |
97 | qreal height = axisRect.bottom(); |
98 | |
99 | //arrow |
100 | QGraphicsLineItem *arrowItem = static_cast<QGraphicsLineItem*>(arrow.at(i: 0)); |
101 | |
102 | //arrow position |
103 | if (axis()->alignment() == Qt::AlignLeft) |
104 | arrowItem->setLine(x1: axisRect.right(), y1: gridRect.top(), x2: axisRect.right(), y2: gridRect.bottom()); |
105 | else if (axis()->alignment() == Qt::AlignRight) |
106 | arrowItem->setLine(x1: axisRect.left(), y1: gridRect.top(), x2: axisRect.left(), y2: gridRect.bottom()); |
107 | |
108 | //title |
109 | QRectF titleBoundingRect; |
110 | QString titleText = axis()->titleText(); |
111 | qreal labelAvailableSpace = axisRect.width(); |
112 | if (!titleText.isEmpty() && titleItem()->isVisible()) { |
113 | const qreal titleAvailableSpace = |
114 | axisRect.height() - labelPadding() - (titlePadding() * 2.0); |
115 | qreal minimumLabelWidth = ChartPresenter::textBoundingRect(font: axis()->labelsFont(), |
116 | QStringLiteral("..." )).width(); |
117 | qreal titleSpace = titleAvailableSpace - minimumLabelWidth; |
118 | title->setHtml(ChartPresenter::truncatedText(font: axis()->titleFont(), text: titleText, angle: qreal(90.0), |
119 | maxWidth: titleSpace, maxHeight: gridRect.height(), |
120 | boundingRect&: titleBoundingRect)); |
121 | title->setTextWidth(titleBoundingRect.height()); |
122 | |
123 | titleBoundingRect = title->boundingRect(); |
124 | |
125 | QPointF center = gridRect.center() - titleBoundingRect.center(); |
126 | if (axis()->alignment() == Qt::AlignLeft) { |
127 | title->setPos(ax: axisRect.left() - titleBoundingRect.width() / 2.0 |
128 | + titleBoundingRect.height() / 2.0 + titlePadding(), ay: center.y()); |
129 | } else if (axis()->alignment() == Qt::AlignRight) { |
130 | title->setPos(ax: axisRect.right() - titleBoundingRect.width() / 2.0 |
131 | - titleBoundingRect.height() / 2.0 - titlePadding(), ay: center.y()); |
132 | } |
133 | |
134 | title->setTransformOriginPoint(titleBoundingRect.center()); |
135 | title->setRotation(270); |
136 | labelAvailableSpace -= titleBoundingRect.height(); |
137 | } |
138 | |
139 | QList<QGraphicsItem *> lines = gridItems(); |
140 | QList<QGraphicsItem *> shades = shadeItems(); |
141 | |
142 | for (int i = 0; i < layout.size(); ++i) { |
143 | //items |
144 | QGraphicsLineItem *gridItem = static_cast<QGraphicsLineItem *>(lines.at(i)); |
145 | QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem *>(arrow.at(i: i + 1)); |
146 | QGraphicsTextItem *labelItem = static_cast<QGraphicsTextItem *>(labels.at(i)); |
147 | |
148 | //grid line |
149 | if (axis()->isReverse()) { |
150 | gridItem->setLine(x1: gridRect.left(), y1: gridRect.top() + gridRect.bottom() - layout[i], |
151 | x2: gridRect.right(), y2: gridRect.top() + gridRect.bottom() - layout[i]); |
152 | } else { |
153 | gridItem->setLine(x1: gridRect.left(), y1: layout[i], x2: gridRect.right(), y2: layout[i]); |
154 | } |
155 | |
156 | //label text wrapping |
157 | QString text; |
158 | if (axis()->isReverse() && axis()->type() != QAbstractAxis::AxisTypeCategory) |
159 | text = labelList.at(i: labelList.count() - i - 1); |
160 | else |
161 | text = labelList.at(i); |
162 | |
163 | QRectF boundingRect; |
164 | // don't truncate empty labels |
165 | if (text.isEmpty()) { |
166 | labelItem->setHtml(text); |
167 | } else { |
168 | qreal labelHeight = (axisRect.height() / layout.count()) - (2 * labelPadding()); |
169 | QString truncatedText = |
170 | ChartPresenter::truncatedText(font: axis()->labelsFont(), text, angle: axis()->labelsAngle(), |
171 | maxWidth: labelAvailableSpace, maxHeight: labelHeight, boundingRect); |
172 | labelItem->setTextWidth(ChartPresenter::textBoundingRect(font: axis()->labelsFont(), |
173 | text: truncatedText).width()); |
174 | labelItem->setHtml(truncatedText); |
175 | } |
176 | |
177 | //label transformation origin point |
178 | const QRectF &rect = labelItem->boundingRect(); |
179 | QPointF center = rect.center(); |
180 | labelItem->setTransformOriginPoint(ax: center.x(), ay: center.y()); |
181 | qreal widthDiff = rect.width() - boundingRect.width(); |
182 | qreal heightDiff = rect.height() - boundingRect.height(); |
183 | |
184 | //ticks and label position |
185 | QPointF labelPos; |
186 | if (axis()->alignment() == Qt::AlignLeft) { |
187 | if (axis()->isReverse()) { |
188 | labelPos = QPointF(axisRect.right() - rect.width() + (widthDiff / 2.0) |
189 | - labelPadding(), |
190 | gridRect.top() + gridRect.bottom() |
191 | - layout[layout.size() - i - 1] - center.y()); |
192 | tickItem->setLine(x1: axisRect.right() - labelPadding(), |
193 | y1: gridRect.top() + gridRect.bottom() - layout[i], |
194 | x2: axisRect.right(), |
195 | y2: gridRect.top() + gridRect.bottom() - layout[i]); |
196 | } else { |
197 | labelPos = QPointF(axisRect.right() - rect.width() + (widthDiff / 2.0) |
198 | - labelPadding(), |
199 | layout[i] - center.y()); |
200 | tickItem->setLine(x1: axisRect.right() - labelPadding(), y1: layout[i], |
201 | x2: axisRect.right(), y2: layout[i]); |
202 | } |
203 | } else if (axis()->alignment() == Qt::AlignRight) { |
204 | if (axis()->isReverse()) { |
205 | tickItem->setLine(x1: axisRect.left(), |
206 | y1: gridRect.top() + gridRect.bottom() - layout[i], |
207 | x2: axisRect.left() + labelPadding(), |
208 | y2: gridRect.top() + gridRect.bottom() - layout[i]); |
209 | labelPos = QPointF(axisRect.left() + labelPadding() - (widthDiff / 2.0), |
210 | gridRect.top() + gridRect.bottom() |
211 | - layout[layout.size() - i - 1] - center.y()); |
212 | } else { |
213 | labelPos = QPointF(axisRect.left() + labelPadding() - (widthDiff / 2.0), |
214 | layout[i] - center.y()); |
215 | tickItem->setLine(x1: axisRect.left(), y1: layout[i], |
216 | x2: axisRect.left() + labelPadding(), y2: layout[i]); |
217 | } |
218 | } |
219 | |
220 | //label in between |
221 | bool forceHide = false; |
222 | bool labelOnValue = false; |
223 | if (intervalAxis() && (i + 1) != layout.size()) { |
224 | qreal lowerBound; |
225 | qreal upperBound; |
226 | if (axis()->isReverse()) { |
227 | lowerBound = qMax(a: gridRect.top() + gridRect.bottom() - layout[i + 1], |
228 | b: gridRect.top()); |
229 | upperBound = qMin(a: gridRect.top() + gridRect.bottom() - layout[i], |
230 | b: gridRect.bottom()); |
231 | } else { |
232 | lowerBound = qMin(a: layout[i], b: gridRect.bottom()); |
233 | upperBound = qMax(a: layout[i + 1], b: gridRect.top()); |
234 | } |
235 | const qreal delta = lowerBound - upperBound; |
236 | if (axis()->type() != QAbstractAxis::AxisTypeCategory) { |
237 | // Hide label in case visible part of the category at the grid edge is too narrow |
238 | if (delta < boundingRect.height() |
239 | && (lowerBound == gridRect.bottom() || upperBound == gridRect.top())) { |
240 | forceHide = true; |
241 | } else { |
242 | labelPos.setY(lowerBound - (delta / 2.0) - center.y()); |
243 | } |
244 | } else { |
245 | QCategoryAxis *categoryAxis = static_cast<QCategoryAxis *>(axis()); |
246 | if (categoryAxis->labelsPosition() == QCategoryAxis::AxisLabelsPositionCenter) { |
247 | if (delta < boundingRect.height() |
248 | && (lowerBound == gridRect.bottom() || upperBound == gridRect.top())) { |
249 | forceHide = true; |
250 | } else { |
251 | labelPos.setY(lowerBound - (delta / 2.0) - center.y()); |
252 | } |
253 | } else if (categoryAxis->labelsPosition() |
254 | == QCategoryAxis::AxisLabelsPositionOnValue) { |
255 | labelOnValue = true; |
256 | if (axis()->isReverse()) { |
257 | labelPos.setY(gridRect.top() + gridRect.bottom() |
258 | - layout[i + 1] - center.y()); |
259 | } else { |
260 | labelPos.setY(upperBound - center.y()); |
261 | } |
262 | } |
263 | } |
264 | } |
265 | |
266 | // Round to full pixel via QPoint to avoid one pixel clipping on the edge in some cases |
267 | labelItem->setPos(labelPos.toPoint()); |
268 | |
269 | //label overlap detection - compensate one pixel for rounding errors |
270 | if (axis()->isReverse()) { |
271 | if (forceHide) |
272 | labelItem->setVisible(false); |
273 | } else if (labelItem->pos().y() + boundingRect.height() > height || forceHide || |
274 | ((labelItem->pos().y() + (heightDiff / 2.0) - 1.0) > axisRect.bottom() |
275 | && !labelOnValue) || |
276 | (labelItem->pos().y() + (heightDiff / 2.0) < (axisRect.top() - 1.0) && !labelOnValue)) { |
277 | labelItem->setVisible(false); |
278 | } |
279 | else { |
280 | labelItem->setVisible(true); |
281 | height=labelItem->pos().y(); |
282 | } |
283 | |
284 | //shades |
285 | QGraphicsRectItem *shadeItem = 0; |
286 | if (i == 0) |
287 | shadeItem = static_cast<QGraphicsRectItem *>(shades.at(i: 0)); |
288 | else if (i % 2) |
289 | shadeItem = static_cast<QGraphicsRectItem *>(shades.at(i: (i / 2) + 1)); |
290 | if (shadeItem) { |
291 | qreal lowerBound; |
292 | qreal upperBound; |
293 | if (i == 0) { |
294 | if (axis()->isReverse()) { |
295 | upperBound = gridRect.top(); |
296 | lowerBound = gridRect.top() + gridRect.bottom() - layout[i]; |
297 | } else { |
298 | lowerBound = gridRect.bottom(); |
299 | upperBound = layout[0]; |
300 | } |
301 | } else { |
302 | if (axis()->isReverse()) { |
303 | upperBound = gridRect.top() + gridRect.bottom() - layout[i]; |
304 | if (i == layout.size() - 1) { |
305 | lowerBound = gridRect.bottom(); |
306 | } else { |
307 | lowerBound = qMax(a: gridRect.top() + gridRect.bottom() - layout[i + 1], |
308 | b: gridRect.top()); |
309 | } |
310 | } else { |
311 | lowerBound = layout[i]; |
312 | if (i == layout.size() - 1) |
313 | upperBound = gridRect.top(); |
314 | else |
315 | upperBound = qMax(a: layout[i + 1], b: gridRect.top()); |
316 | } |
317 | |
318 | } |
319 | if (lowerBound > gridRect.bottom()) |
320 | lowerBound = gridRect.bottom(); |
321 | if (upperBound < gridRect.top()) |
322 | upperBound = gridRect.top(); |
323 | shadeItem->setRect(ax: gridRect.left(), ay: upperBound, w: gridRect.width(), |
324 | h: lowerBound - upperBound); |
325 | if (shadeItem->rect().height() <= 0.0) |
326 | shadeItem->setVisible(false); |
327 | else |
328 | shadeItem->setVisible(true); |
329 | } |
330 | |
331 | // check if the grid line and the axis tick should be shown |
332 | const bool gridLineVisible = (gridItem->line().p1().y() >= gridRect.top() |
333 | && gridItem->line().p1().y() <= gridRect.bottom()); |
334 | gridItem->setVisible(gridLineVisible); |
335 | tickItem->setVisible(gridLineVisible); |
336 | } |
337 | |
338 | updateMinorTickGeometry(); |
339 | |
340 | // begin/end grid line in case labels between |
341 | if (intervalAxis()) { |
342 | QGraphicsLineItem *gridLine; |
343 | gridLine = static_cast<QGraphicsLineItem *>(lines.at(i: layout.size())); |
344 | gridLine->setLine(x1: gridRect.left(), y1: gridRect.top(), x2: gridRect.right(), y2: gridRect.top()); |
345 | gridLine->setVisible(true); |
346 | gridLine = static_cast<QGraphicsLineItem*>(lines.at(i: layout.size() + 1)); |
347 | gridLine->setLine(x1: gridRect.left(), y1: gridRect.bottom(), x2: gridRect.right(), y2: gridRect.bottom()); |
348 | gridLine->setVisible(true); |
349 | } |
350 | } |
351 | |
352 | void VerticalAxis::updateMinorTickGeometry() |
353 | { |
354 | if (!axis()) |
355 | return; |
356 | |
357 | QVector<qreal> layout = ChartAxisElement::layout(); |
358 | int minorTickCount = 0; |
359 | qreal tickSpacing = 0.0; |
360 | QVector<qreal> minorTickSpacings; |
361 | switch (axis()->type()) { |
362 | case QAbstractAxis::AxisTypeValue: { |
363 | const QValueAxis *valueAxis = qobject_cast<QValueAxis *>(object: axis()); |
364 | |
365 | minorTickCount = valueAxis->minorTickCount(); |
366 | |
367 | if (valueAxis->tickType() == QValueAxis::TicksFixed) { |
368 | if (valueAxis->tickCount() >= 2) |
369 | tickSpacing = layout.at(i: 0) - layout.at(i: 1); |
370 | |
371 | for (int i = 0; i < minorTickCount; ++i) { |
372 | const qreal ratio = (1.0 / qreal(minorTickCount + 1)) * qreal(i + 1); |
373 | minorTickSpacings.append(t: tickSpacing * ratio); |
374 | } |
375 | } |
376 | break; |
377 | } |
378 | case QAbstractAxis::AxisTypeLogValue: { |
379 | const QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(object: axis()); |
380 | const qreal base = logValueAxis->base(); |
381 | const qreal logBase = qLn(v: base); |
382 | |
383 | minorTickCount = logValueAxis->minorTickCount(); |
384 | if (minorTickCount < 0) |
385 | minorTickCount = qMax(a: int(qFloor(v: base) - 2.0), b: 0); |
386 | |
387 | // Two "virtual" ticks are required to make sure that all minor ticks |
388 | // are displayed properly (even for the partially visible segments of |
389 | // the chart). |
390 | if (layout.size() >= 2) { |
391 | // Calculate tickSpacing as a difference between visible ticks |
392 | // whenever it is possible. Virtual ticks will not be correctly |
393 | // positioned when the layout is animating. |
394 | tickSpacing = layout.at(i: 0) - layout.at(i: 1); |
395 | layout.prepend(t: layout.at(i: 0) + tickSpacing); |
396 | layout.append(t: layout.at(i: layout.size() - 1) - tickSpacing); |
397 | } else { |
398 | const qreal logMax = qLn(v: logValueAxis->max()); |
399 | const qreal logMin = qLn(v: logValueAxis->min()); |
400 | const qreal = qLn(v: qPow(x: base, y: qFloor(v: logMax / logBase) + 1.0)); |
401 | const qreal = qLn(v: qPow(x: base, y: qCeil(v: logMin / logBase) - 1.0)); |
402 | const qreal edge = gridGeometry().bottom(); |
403 | const qreal delta = gridGeometry().height() / qAbs(t: logMax - logMin); |
404 | const qreal = edge - (logExtraMaxTick - qMin(a: logMin, b: logMax)) * delta; |
405 | const qreal = edge - (logExtraMinTick - qMin(a: logMin, b: logMax)) * delta; |
406 | |
407 | // Calculate tickSpacing using one (if layout.size() == 1) or two |
408 | // (if layout.size() == 0) "virtual" ticks. In both cases animation |
409 | // will not work as expected. This should be fixed later. |
410 | layout.prepend(t: extraMinTick); |
411 | layout.append(t: extraMaxTick); |
412 | tickSpacing = layout.at(i: 0) - layout.at(i: 1); |
413 | } |
414 | |
415 | const qreal minorTickStepValue = qFabs(v: base - 1.0) / qreal(minorTickCount + 1); |
416 | for (int i = 0; i < minorTickCount; ++i) { |
417 | const qreal x = minorTickStepValue * qreal(i + 1) + 1.0; |
418 | const qreal minorTickSpacing = tickSpacing * (qLn(v: x) / logBase); |
419 | minorTickSpacings.append(t: minorTickSpacing); |
420 | } |
421 | break; |
422 | } |
423 | default: |
424 | // minor ticks are not supported |
425 | break; |
426 | } |
427 | |
428 | const QValueAxis *valueAxis = qobject_cast<QValueAxis *>(object: axis()); |
429 | if (valueAxis && valueAxis->tickType() == QValueAxis::TicksDynamic) { |
430 | const QVector<qreal> dynamicMinorTicklayout = ChartAxisElement::dynamicMinorTicklayout(); |
431 | const QRectF &gridRect = gridGeometry(); |
432 | const qreal deltaY = gridRect.height() / (valueAxis->max() - valueAxis->min()); |
433 | const qreal bottomPos = gridRect.bottom(); |
434 | const qreal topPos = gridRect.top(); |
435 | |
436 | for (int i = 0; i < dynamicMinorTicklayout.size(); i++) { |
437 | QGraphicsLineItem *minorGridLineItem = |
438 | static_cast<QGraphicsLineItem *>(minorGridItems().value(i)); |
439 | QGraphicsLineItem *minorArrowLineItem = |
440 | static_cast<QGraphicsLineItem *>(minorArrowItems().value(i)); |
441 | if (!minorGridLineItem || !minorArrowLineItem) |
442 | continue; |
443 | |
444 | qreal minorGridLineItemY = 0.0; |
445 | if (axis()->isReverse()) |
446 | minorGridLineItemY = topPos + dynamicMinorTicklayout.at(i) * deltaY; |
447 | else |
448 | minorGridLineItemY = bottomPos - dynamicMinorTicklayout.at(i) * deltaY; |
449 | |
450 | qreal minorArrowLineItemX1; |
451 | qreal minorArrowLineItemX2; |
452 | switch (axis()->alignment()) { |
453 | case Qt::AlignLeft: |
454 | minorArrowLineItemX1 = gridGeometry().left() - labelPadding() / 2.0; |
455 | minorArrowLineItemX2 = gridGeometry().left(); |
456 | break; |
457 | case Qt::AlignRight: |
458 | minorArrowLineItemX1 = gridGeometry().right(); |
459 | minorArrowLineItemX2 = gridGeometry().right() + labelPadding() / 2.0; |
460 | break; |
461 | default: |
462 | minorArrowLineItemX1 = 0.0; |
463 | minorArrowLineItemX2 = 0.0; |
464 | break; |
465 | } |
466 | |
467 | minorGridLineItem->setLine(x1: gridGeometry().left(), y1: minorGridLineItemY, |
468 | x2: gridGeometry().right(), y2: minorGridLineItemY); |
469 | minorArrowLineItem->setLine(x1: minorArrowLineItemX1, y1: minorGridLineItemY, |
470 | x2: minorArrowLineItemX2, y2: minorGridLineItemY); |
471 | |
472 | // check if the minor grid line and the minor axis arrow should be shown |
473 | bool minorGridLineVisible = (minorGridLineItemY >= gridGeometry().top() |
474 | && minorGridLineItemY <= gridGeometry().bottom()); |
475 | minorGridLineItem->setVisible(minorGridLineVisible); |
476 | minorArrowLineItem->setVisible(minorGridLineVisible); |
477 | } |
478 | } else { |
479 | if (minorTickCount < 1 || tickSpacing == 0.0 || minorTickSpacings.count() != minorTickCount) |
480 | return; |
481 | |
482 | for (int i = 0; i < layout.size() - 1; ++i) { |
483 | for (int j = 0; j < minorTickCount; ++j) { |
484 | const int minorItemIndex = i * minorTickCount + j; |
485 | QGraphicsLineItem *minorGridLineItem = |
486 | static_cast<QGraphicsLineItem *>(minorGridItems().value(i: minorItemIndex)); |
487 | QGraphicsLineItem *minorArrowLineItem = |
488 | static_cast<QGraphicsLineItem *>(minorArrowItems().value(i: minorItemIndex)); |
489 | if (!minorGridLineItem || !minorArrowLineItem) |
490 | continue; |
491 | |
492 | const qreal minorTickSpacing = minorTickSpacings.value(i: j, defaultValue: 0.0); |
493 | |
494 | qreal minorGridLineItemY = 0.0; |
495 | if (axis()->isReverse()) { |
496 | minorGridLineItemY = qFloor(v: gridGeometry().top() + gridGeometry().bottom() |
497 | - layout.at(i) + minorTickSpacing); |
498 | } else { |
499 | minorGridLineItemY = qCeil(v: layout.at(i) - minorTickSpacing); |
500 | } |
501 | |
502 | qreal minorArrowLineItemX1; |
503 | qreal minorArrowLineItemX2; |
504 | switch (axis()->alignment()) { |
505 | case Qt::AlignLeft: |
506 | minorArrowLineItemX1 = gridGeometry().left() - labelPadding() / 2.0; |
507 | minorArrowLineItemX2 = gridGeometry().left(); |
508 | break; |
509 | case Qt::AlignRight: |
510 | minorArrowLineItemX1 = gridGeometry().right(); |
511 | minorArrowLineItemX2 = gridGeometry().right() + labelPadding() / 2.0; |
512 | break; |
513 | default: |
514 | minorArrowLineItemX1 = 0.0; |
515 | minorArrowLineItemX2 = 0.0; |
516 | break; |
517 | } |
518 | |
519 | minorGridLineItem->setLine(x1: gridGeometry().left(), y1: minorGridLineItemY, |
520 | x2: gridGeometry().right(), y2: minorGridLineItemY); |
521 | minorArrowLineItem->setLine(x1: minorArrowLineItemX1, y1: minorGridLineItemY, |
522 | x2: minorArrowLineItemX2, y2: minorGridLineItemY); |
523 | |
524 | // check if the minor grid line and the minor axis arrow should be shown |
525 | bool minorGridLineVisible = (minorGridLineItemY >= gridGeometry().top() |
526 | && minorGridLineItemY <= gridGeometry().bottom()); |
527 | minorGridLineItem->setVisible(minorGridLineVisible); |
528 | minorArrowLineItem->setVisible(minorGridLineVisible); |
529 | } |
530 | } |
531 | } |
532 | } |
533 | |
534 | QT_CHARTS_END_NAMESPACE |
535 | |