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 <QtGui/qtextdocument.h>
34#include <private/chartpresenter_p.h>
35#include <private/polarchartaxisangular_p.h>
36
37QT_CHARTS_BEGIN_NAMESPACE
38
39PolarChartAxisAngular::PolarChartAxisAngular(QAbstractAxis *axis, QGraphicsItem *item,
40 bool intervalAxis)
41 : PolarChartAxis(axis, item, intervalAxis)
42{
43}
44
45PolarChartAxisAngular::~PolarChartAxisAngular()
46{
47}
48
49void PolarChartAxisAngular::updateGeometry()
50{
51 QGraphicsLayoutItem::updateGeometry();
52
53 const QVector<qreal> &layout = this->layout();
54 if (layout.isEmpty() && axis()->type() != QAbstractAxis::AxisTypeLogValue)
55 return;
56
57 createAxisLabels(layout);
58 QStringList labelList = labels();
59 QPointF center = axisGeometry().center();
60 QList<QGraphicsItem *> arrowItemList = arrowItems();
61 QList<QGraphicsItem *> gridItemList = gridItems();
62 QList<QGraphicsItem *> labelItemList = labelItems();
63 QList<QGraphicsItem *> shadeItemList = shadeItems();
64 QList<QGraphicsItem *> minorGridItemList = minorGridItems();
65 QList<QGraphicsItem *> minorArrowItemList = minorArrowItems();
66 QGraphicsTextItem *title = titleItem();
67
68 QGraphicsEllipseItem *axisLine = static_cast<QGraphicsEllipseItem *>(arrowItemList.at(i: 0));
69 axisLine->setRect(axisGeometry());
70
71 qreal radius = axisGeometry().height() / 2.0;
72
73 QRectF previousLabelRect;
74 QRectF firstLabelRect;
75
76 qreal labelHeight = 0;
77
78 bool firstShade = true;
79 bool nextTickVisible = false;
80 if (layout.size())
81 nextTickVisible = !(layout.at(i: 0) < 0.0 || layout.at(i: 0) > 360.0);
82
83 for (int i = 0; i < layout.size(); ++i) {
84 qreal angularCoordinate = layout.at(i);
85
86 QGraphicsLineItem *gridLineItem = static_cast<QGraphicsLineItem *>(gridItemList.at(i));
87 QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem *>(arrowItemList.at(i: i + 1));
88 QGraphicsTextItem *labelItem = static_cast<QGraphicsTextItem *>(labelItemList.at(i));
89 QGraphicsPathItem *shadeItem = 0;
90 if (i == 0)
91 shadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at(i: 0));
92 else if (i % 2)
93 shadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at(i: (i / 2) + 1));
94
95 // Ignore ticks outside valid range
96 bool currentTickVisible = nextTickVisible;
97 if ((i == layout.size() - 1)
98 || layout.at(i: i + 1) < 0.0
99 || layout.at(i: i + 1) > 360.0) {
100 nextTickVisible = false;
101 } else {
102 nextTickVisible = true;
103 }
104
105 qreal labelCoordinate = angularCoordinate;
106 bool labelVisible = currentTickVisible;
107 if (intervalAxis()) {
108 qreal farEdge;
109 if (i == (layout.size() - 1))
110 farEdge = 360.0;
111 else
112 farEdge = qMin(a: qreal(360.0), b: layout.at(i: i + 1));
113
114 // Adjust the labelCoordinate to show it if next tick is visible
115 if (nextTickVisible)
116 labelCoordinate = qMax(a: qreal(0.0), b: labelCoordinate);
117
118 bool centeredLabel = true;
119 if (axis()->type() == QAbstractAxis::AxisTypeCategory) {
120 QCategoryAxis *categoryAxis = static_cast<QCategoryAxis *>(axis());
121 if (categoryAxis->labelsPosition() == QCategoryAxis::AxisLabelsPositionOnValue)
122 centeredLabel = false;
123 }
124 if (centeredLabel) {
125 labelCoordinate = (labelCoordinate + farEdge) / 2.0;
126 // Don't display label once the category gets too small near the axis
127 if (labelCoordinate < 5.0 || labelCoordinate > 355.0)
128 labelVisible = false;
129 else
130 labelVisible = true;
131 } else {
132 labelVisible = nextTickVisible;
133 labelCoordinate = farEdge;
134 }
135 }
136
137 // Need this also in label calculations, so determine it first
138 QLineF tickLine(QLineF::fromPolar(length: radius - tickWidth(), angle: 90.0 - angularCoordinate).p2(),
139 QLineF::fromPolar(length: radius + tickWidth(), angle: 90.0 - angularCoordinate).p2());
140 tickLine.translate(point: center);
141
142 // Angular axis label
143 if (axis()->labelsVisible() && labelVisible) {
144 QRectF boundingRect = ChartPresenter::textBoundingRect(font: axis()->labelsFont(),
145 text: labelList.at(i),
146 angle: axis()->labelsAngle());
147 labelItem->setTextWidth(boundingRect.width());
148 labelItem->setHtml(labelList.at(i));
149 const QRectF &rect = labelItem->boundingRect();
150 QPointF labelCenter = rect.center();
151 labelItem->setTransformOriginPoint(ax: labelCenter.x(), ay: labelCenter.y());
152 boundingRect.moveCenter(p: labelCenter);
153 QPointF positionDiff(rect.topLeft() - boundingRect.topLeft());
154
155 QPointF labelPoint;
156 if (intervalAxis()) {
157 QLineF labelLine = QLineF::fromPolar(length: radius + tickWidth(), angle: 90.0 - labelCoordinate);
158 labelLine.translate(point: center);
159 labelPoint = labelLine.p2();
160 } else {
161 labelPoint = tickLine.p2();
162 }
163
164 QRectF labelRect = moveLabelToPosition(angularCoordinate: labelCoordinate, labelPoint, labelRect: boundingRect);
165 labelItem->setPos(labelRect.topLeft() + positionDiff);
166
167 // Store height for title calculations
168 qreal labelClearance = axisGeometry().top() - labelRect.top();
169 labelHeight = qMax(a: labelHeight, b: labelClearance);
170
171 // Label overlap detection
172 if (i && (previousLabelRect.intersects(r: labelRect) || firstLabelRect.intersects(r: labelRect))) {
173 labelVisible = false;
174 } else {
175 // Store labelRect for future comparison. Some area is deducted to make things look
176 // little nicer, as usually intersection happens at label corner with angular labels.
177 labelRect.adjust(xp1: -2.0, yp1: -4.0, xp2: -2.0, yp2: -4.0);
178 if (firstLabelRect.isEmpty())
179 firstLabelRect = labelRect;
180
181 previousLabelRect = labelRect;
182 labelVisible = true;
183 }
184 }
185
186 labelItem->setVisible(labelVisible);
187 if (!currentTickVisible) {
188 gridLineItem->setVisible(false);
189 tickItem->setVisible(false);
190 if (shadeItem)
191 shadeItem->setVisible(false);
192 continue;
193 }
194
195 // Angular grid line
196 QLineF gridLine = QLineF::fromPolar(length: radius, angle: 90.0 - angularCoordinate);
197 gridLine.translate(point: center);
198 gridLineItem->setLine(gridLine);
199 gridLineItem->setVisible(true);
200
201 // Tick
202 tickItem->setLine(tickLine);
203 tickItem->setVisible(true);
204
205 // Shades
206 if (i % 2 || (i == 0 && !nextTickVisible)) {
207 QPainterPath path;
208 path.moveTo(p: center);
209 if (i == 0) {
210 // If first tick is also the last, we need to custom fill the first partial arc
211 // or it won't get filled.
212 path.arcTo(rect: axisGeometry(), startAngle: 90.0 - layout.at(i: 0), arcLength: layout.at(i: 0));
213 path.closeSubpath();
214 } else {
215 qreal nextCoordinate;
216 if (!nextTickVisible) // Last visible tick
217 nextCoordinate = 360.0;
218 else
219 nextCoordinate = layout.at(i: i + 1);
220 qreal arcSpan = angularCoordinate - nextCoordinate;
221 path.arcTo(rect: axisGeometry(), startAngle: 90.0 - angularCoordinate, arcLength: arcSpan);
222 path.closeSubpath();
223
224 // Add additional arc for first shade item if there is a partial arc to be filled
225 if (firstShade) {
226 QGraphicsPathItem *specialShadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at(i: 0));
227 if (layout.at(i: i - 1) > 0.0) {
228 QPainterPath specialPath;
229 specialPath.moveTo(p: center);
230 specialPath.arcTo(rect: axisGeometry(), startAngle: 90.0 - layout.at(i: i - 1), arcLength: layout.at(i: i - 1));
231 specialPath.closeSubpath();
232 specialShadeItem->setPath(specialPath);
233 specialShadeItem->setVisible(true);
234 } else {
235 specialShadeItem->setVisible(false);
236 }
237 }
238 }
239 shadeItem->setPath(path);
240 shadeItem->setVisible(true);
241 firstShade = false;
242 }
243 }
244
245 updateMinorTickGeometry();
246
247 // Title, centered above the chart
248 QString titleText = axis()->titleText();
249 if (!titleText.isEmpty() && axis()->isTitleVisible()) {
250 QRectF truncatedRect;
251 qreal availableTitleHeight = axisGeometry().height() - labelPadding() - titlePadding() * 2.0;
252 qreal minimumLabelHeight = ChartPresenter::textBoundingRect(font: axis()->labelsFont(),
253 QStringLiteral("...")).height();
254 availableTitleHeight -= minimumLabelHeight;
255 title->setHtml(ChartPresenter::truncatedText(font: axis()->titleFont(), text: titleText, angle: qreal(0.0),
256 maxWidth: axisGeometry().width(), maxHeight: availableTitleHeight,
257 boundingRect&: truncatedRect));
258 title->setTextWidth(truncatedRect.width());
259
260 QRectF titleBoundingRect = title->boundingRect();
261 QPointF titleCenter = center - titleBoundingRect.center();
262 title->setPos(ax: titleCenter.x(), ay: axisGeometry().top() - titlePadding() * 2.0 - titleBoundingRect.height() - labelHeight);
263 }
264}
265
266Qt::Orientation PolarChartAxisAngular::orientation() const
267{
268 return Qt::Horizontal;
269}
270
271void PolarChartAxisAngular::createItems(int count)
272{
273 if (arrowItems().count() == 0) {
274 // angular axis line
275 QGraphicsEllipseItem *arrow = new QGraphicsEllipseItem(presenter()->rootItem());
276 arrow->setPen(axis()->linePen());
277 arrowGroup()->addToGroup(item: arrow);
278 }
279
280 QGraphicsTextItem *title = titleItem();
281 title->setFont(axis()->titleFont());
282 title->setDefaultTextColor(axis()->titleBrush().color());
283 title->setHtml(axis()->titleText());
284
285 for (int i = 0; i < count; ++i) {
286 QGraphicsLineItem *arrow = new QGraphicsLineItem(presenter()->rootItem());
287 QGraphicsLineItem *grid = new QGraphicsLineItem(presenter()->rootItem());
288 QGraphicsTextItem *label = new QGraphicsTextItem(presenter()->rootItem());
289 label->document()->setDocumentMargin(ChartPresenter::textMargin());
290 arrow->setPen(axis()->linePen());
291 grid->setPen(axis()->gridLinePen());
292 label->setFont(axis()->labelsFont());
293 label->setDefaultTextColor(axis()->labelsBrush().color());
294 label->setRotation(axis()->labelsAngle());
295 arrowGroup()->addToGroup(item: arrow);
296 gridGroup()->addToGroup(item: grid);
297 labelGroup()->addToGroup(item: label);
298 if (gridItems().size() == 1 || (((gridItems().size() + 1) % 2) && gridItems().size() > 0)) {
299 QGraphicsPathItem *shade = new QGraphicsPathItem(presenter()->rootItem());
300 shade->setPen(axis()->shadesPen());
301 shade->setBrush(axis()->shadesBrush());
302 shadeGroup()->addToGroup(item: shade);
303 }
304 }
305}
306
307void PolarChartAxisAngular::handleArrowPenChanged(const QPen &pen)
308{
309 bool first = true;
310 foreach (QGraphicsItem *item, arrowItems()) {
311 if (first) {
312 first = false;
313 // First arrow item is the outer circle of axis
314 static_cast<QGraphicsEllipseItem *>(item)->setPen(pen);
315 } else {
316 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
317 }
318 }
319}
320
321void PolarChartAxisAngular::handleGridPenChanged(const QPen &pen)
322{
323 foreach (QGraphicsItem *item, gridItems())
324 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
325}
326
327void PolarChartAxisAngular::handleMinorArrowPenChanged(const QPen &pen)
328{
329 foreach (QGraphicsItem *item, minorArrowItems()) {
330 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
331 }
332}
333
334void PolarChartAxisAngular::handleMinorGridPenChanged(const QPen &pen)
335{
336 foreach (QGraphicsItem *item, minorGridItems())
337 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
338}
339
340void PolarChartAxisAngular::handleGridLineColorChanged(const QColor &color)
341{
342 foreach (QGraphicsItem *item, gridItems()) {
343 QGraphicsLineItem *lineItem = static_cast<QGraphicsLineItem *>(item);
344 QPen pen = lineItem->pen();
345 pen.setColor(color);
346 lineItem->setPen(pen);
347 }
348}
349
350void PolarChartAxisAngular::handleMinorGridLineColorChanged(const QColor &color)
351{
352 foreach (QGraphicsItem *item, minorGridItems()) {
353 QGraphicsLineItem *lineItem = static_cast<QGraphicsLineItem *>(item);
354 QPen pen = lineItem->pen();
355 pen.setColor(color);
356 lineItem->setPen(pen);
357 }
358}
359
360QSizeF PolarChartAxisAngular::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
361{
362 Q_UNUSED(which);
363 Q_UNUSED(constraint);
364 return QSizeF(-1, -1);
365}
366
367qreal PolarChartAxisAngular::preferredAxisRadius(const QSizeF &maxSize)
368{
369 qreal radius = maxSize.height() / 2.0;
370 if (maxSize.width() < maxSize.height())
371 radius = maxSize.width() / 2.0;
372
373 if (axis()->labelsVisible()) {
374 QVector<qreal> layout = calculateLayout();
375 if (layout.isEmpty())
376 return radius;
377
378 createAxisLabels(layout);
379 QStringList labelList = labels();
380 QFont font = axis()->labelsFont();
381
382 QRectF maxRect;
383 maxRect.setSize(maxSize);
384 maxRect.moveCenter(p: QPointF(0.0, 0.0));
385
386 // This is a horrible way to find out the maximum radius for angular axis and its labels.
387 // It just increments the radius down until everyhing fits the constraint size.
388 // Proper way would be to actually calculate it but this seems to work reasonably fast as it is.
389 bool nextTickVisible = false;
390 for (int i = 0; i < layout.size(); ) {
391 if ((i == layout.size() - 1)
392 || layout.at(i: i + 1) < 0.0
393 || layout.at(i: i + 1) > 360.0) {
394 nextTickVisible = false;
395 } else {
396 nextTickVisible = true;
397 }
398
399 qreal labelCoordinate = layout.at(i);
400 qreal labelVisible;
401
402 if (intervalAxis()) {
403 qreal farEdge;
404 if (i == (layout.size() - 1))
405 farEdge = 360.0;
406 else
407 farEdge = qMin(a: qreal(360.0), b: layout.at(i: i + 1));
408
409 // Adjust the labelCoordinate to show it if next tick is visible
410 if (nextTickVisible)
411 labelCoordinate = qMax(a: qreal(0.0), b: labelCoordinate);
412
413 labelCoordinate = (labelCoordinate + farEdge) / 2.0;
414 }
415
416 if (labelCoordinate < 0.0 || labelCoordinate > 360.0)
417 labelVisible = false;
418 else
419 labelVisible = true;
420
421 if (!labelVisible) {
422 i++;
423 continue;
424 }
425
426 QRectF boundingRect = ChartPresenter::textBoundingRect(font: axis()->labelsFont(), text: labelList.at(i), angle: axis()->labelsAngle());
427 QPointF labelPoint = QLineF::fromPolar(length: radius + tickWidth(), angle: 90.0 - labelCoordinate).p2();
428
429 boundingRect = moveLabelToPosition(angularCoordinate: labelCoordinate, labelPoint, labelRect: boundingRect);
430 QRectF intersectRect = maxRect.intersected(r: boundingRect);
431 if (boundingRect.isEmpty() || intersectRect == boundingRect) {
432 i++;
433 } else {
434 qreal reduction(0.0);
435 // If there is no intersection, reduce by smallest dimension of label rect to be on the safe side
436 if (intersectRect.isEmpty()) {
437 reduction = qMin(a: boundingRect.height(), b: boundingRect.width());
438 } else {
439 // Approximate needed radius reduction is the amount label rect exceeds max rect in either dimension.
440 // Could be further optimized by figuring out the proper math how to calculate exact needed reduction.
441 reduction = qMax(a: boundingRect.height() - intersectRect.height(),
442 b: boundingRect.width() - intersectRect.width());
443 }
444 // Typically the approximated reduction is little low, so add one
445 radius -= (reduction + 1.0);
446
447 if (radius < 1.0) // safeguard
448 return 1.0;
449 }
450 }
451 }
452
453 if (!axis()->titleText().isEmpty() && axis()->isTitleVisible()) {
454 QRectF titleRect = ChartPresenter::textBoundingRect(font: axis()->titleFont(), text: axis()->titleText());
455
456 radius -= titlePadding() + (titleRect.height() / 2.0);
457 if (radius < 1.0) // safeguard
458 return 1.0;
459 }
460
461 return radius;
462}
463
464QRectF PolarChartAxisAngular::moveLabelToPosition(qreal angularCoordinate, QPointF labelPoint, QRectF labelRect) const
465{
466 if (angularCoordinate == 0.0)
467 labelRect.moveCenter(p: labelPoint + QPointF(0, -labelRect.height() / 2.0));
468 else if (angularCoordinate < 90.0)
469 labelRect.moveBottomLeft(p: labelPoint);
470 else if (angularCoordinate == 90.0)
471 labelRect.moveCenter(p: labelPoint + QPointF(labelRect.width() / 2.0 + 2.0, 0)); // +2 so that it does not hit the radial axis
472 else if (angularCoordinate < 180.0)
473 labelRect.moveTopLeft(p: labelPoint);
474 else if (angularCoordinate == 180.0)
475 labelRect.moveCenter(p: labelPoint + QPointF(0, labelRect.height() / 2.0));
476 else if (angularCoordinate < 270.0)
477 labelRect.moveTopRight(p: labelPoint);
478 else if (angularCoordinate == 270.0)
479 labelRect.moveCenter(p: labelPoint + QPointF(-labelRect.width() / 2.0 - 2.0, 0)); // -2 so that it does not hit the radial axis
480 else if (angularCoordinate < 360.0)
481 labelRect.moveBottomRight(p: labelPoint);
482 else
483 labelRect.moveCenter(p: labelPoint + QPointF(0, -labelRect.height() / 2.0));
484 return labelRect;
485}
486
487void PolarChartAxisAngular::updateMinorTickGeometry()
488{
489 if (!axis())
490 return;
491
492 QVector<qreal> layout = ChartAxisElement::layout();
493 int minorTickCount = 0;
494 qreal tickAngle = 0.0;
495 QVector<qreal> minorTickAngles;
496 switch (axis()->type()) {
497 case QAbstractAxis::AxisTypeValue: {
498 const QValueAxis *valueAxis = qobject_cast<QValueAxis *>(object: axis());
499
500 minorTickCount = valueAxis->minorTickCount();
501
502 if (valueAxis->tickCount() >= 2)
503 tickAngle = layout.at(i: 1) - layout.at(i: 0);
504
505 for (int i = 0; i < minorTickCount; ++i) {
506 const qreal ratio = (1.0 / qreal(minorTickCount + 1)) * qreal(i + 1);
507 minorTickAngles.append(t: tickAngle * ratio);
508 }
509 break;
510 }
511 case QAbstractAxis::AxisTypeLogValue: {
512 const QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(object: axis());
513 const qreal base = logValueAxis->base();
514 const qreal logBase = qLn(v: base);
515
516 minorTickCount = logValueAxis->minorTickCount();
517 if (minorTickCount < 0)
518 minorTickCount = qMax(a: int(qFloor(v: base) - 2.0), b: 0);
519
520 // Two "virtual" ticks are required to make sure that all minor ticks
521 // are displayed properly (even for the partially visible segments of
522 // the chart).
523 if (layout.size() >= 2) {
524 // Calculate tickAngle as a difference between visible ticks
525 // whenever it is possible. Virtual ticks will not be correctly
526 // positioned when the layout is animating.
527 tickAngle = layout.at(i: 1) - layout.at(i: 0);
528 layout.prepend(t: layout.at(i: 0) - tickAngle);
529 layout.append(t: layout.at(i: layout.size() - 1) + tickAngle);
530 } else {
531 const qreal logMax = qLn(v: logValueAxis->max());
532 const qreal logMin = qLn(v: logValueAxis->min());
533 const qreal logExtraMaxTick = qLn(v: qPow(x: base, y: qFloor(v: logMax / logBase) + 1.0));
534 const qreal logExtraMinTick = qLn(v: qPow(x: base, y: qCeil(v: logMin / logBase) - 1.0));
535 const qreal edge = qMin(a: logMin, b: logMax);
536 const qreal delta = 360.0 / qAbs(t: logMax - logMin);
537 const qreal extraMaxTick = edge + (logExtraMaxTick - edge) * delta;
538 const qreal extraMinTick = edge + (logExtraMinTick - edge) * delta;
539
540 // Calculate tickAngle using one (if layout.size() == 1) or two
541 // (if layout.size() == 0) "virtual" ticks. In both cases animation
542 // will not work as expected. This should be fixed later.
543 layout.prepend(t: extraMinTick);
544 layout.append(t: extraMaxTick);
545 tickAngle = layout.at(i: 1) - layout.at(i: 0);
546 }
547
548 const qreal minorTickStepValue = qFabs(v: base - 1.0) / qreal(minorTickCount + 1);
549 for (int i = 0; i < minorTickCount; ++i) {
550 const qreal x = minorTickStepValue * qreal(i + 1) + 1.0;
551 const qreal minorTickAngle = tickAngle * (qLn(v: x) / logBase);
552 minorTickAngles.append(t: minorTickAngle);
553 }
554 break;
555 }
556 default:
557 // minor ticks are not supported
558 break;
559 }
560
561 if (minorTickCount < 1 || tickAngle == 0.0 || minorTickAngles.count() != minorTickCount)
562 return;
563
564 const QPointF axisCenter = axisGeometry().center();
565 const qreal axisRadius = axisGeometry().height() / 2.0;
566
567 for (int i = 0; i < layout.size() - 1; ++i) {
568 for (int j = 0; j < minorTickCount; ++j) {
569 const int minorItemIndex = i * minorTickCount + j;
570 QGraphicsLineItem *minorGridLineItem =
571 static_cast<QGraphicsLineItem *>(minorGridItems().at(i: minorItemIndex));
572 QGraphicsLineItem *minorArrowLineItem =
573 static_cast<QGraphicsLineItem *>(minorArrowItems().at(i: minorItemIndex));
574 if (!minorGridLineItem || !minorArrowLineItem)
575 continue;
576
577 const qreal minorTickAngle = 90.0 - layout.at(i) - minorTickAngles.value(i: j, defaultValue: 0.0);
578
579 const QPointF minorArrowLinePt1 = QLineF::fromPolar(length: axisRadius - tickWidth() + 1,
580 angle: minorTickAngle).p2();
581 const QPointF minorArrowLinePt2 = QLineF::fromPolar(length: axisRadius + tickWidth() - 1,
582 angle: minorTickAngle).p2();
583
584 QLineF minorGridLine = QLineF::fromPolar(length: axisRadius, angle: minorTickAngle);
585 minorGridLine.translate(point: axisCenter);
586 minorGridLineItem->setLine(minorGridLine);
587
588 QLineF minorArrowLine(minorArrowLinePt1, minorArrowLinePt2);
589 minorArrowLine.translate(point: axisCenter);
590 minorArrowLineItem->setLine(minorArrowLine);
591
592 // check if the minor grid line and the minor axis arrow should be shown
593 const bool minorGridLineVisible = (minorTickAngle >= -270.0 && minorTickAngle <= 90.0);
594 minorGridLineItem->setVisible(minorGridLineVisible);
595 minorArrowLineItem->setVisible(minorGridLineVisible);
596 }
597 }
598}
599
600void PolarChartAxisAngular::updateMinorTickItems()
601{
602 int currentCount = minorArrowItems().size();
603 int expectedCount = 0;
604 if (axis()->type() == QAbstractAxis::AxisTypeValue) {
605 QValueAxis *valueAxis = qobject_cast<QValueAxis *>(object: axis());
606 expectedCount = valueAxis->minorTickCount() * (valueAxis->tickCount() - 1);
607 expectedCount = qMax(a: expectedCount, b: 0);
608 } else if (axis()->type() == QAbstractAxis::AxisTypeLogValue) {
609 QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(object: axis());
610
611 int minorTickCount = logValueAxis->minorTickCount();
612 if (minorTickCount < 0)
613 minorTickCount = qMax(a: int(qFloor(v: logValueAxis->base()) - 2.0), b: 0);
614
615 expectedCount = minorTickCount * (logValueAxis->tickCount() + 1);
616 expectedCount = qMax(a: expectedCount, b: logValueAxis->minorTickCount());
617 } else {
618 // minor ticks are not supported
619 return;
620 }
621
622 int diff = expectedCount - currentCount;
623 if (diff > 0) {
624 for (int i = 0; i < diff; ++i) {
625 QGraphicsLineItem *minorGridLineItem = new QGraphicsLineItem(this);
626 minorGridLineItem->setPen(axis()->minorGridLinePen());
627 minorGridGroup()->addToGroup(item: minorGridLineItem);
628
629 QGraphicsLineItem *minorArrowLineItem = new QGraphicsLineItem(this);
630 minorArrowLineItem->setPen(axis()->linePen());
631 minorArrowGroup()->addToGroup(item: minorArrowLineItem);
632 }
633 } else {
634 QList<QGraphicsItem *> minorGridItemsList = minorGridItems();
635 QList<QGraphicsItem *> minorArrowItemsList = minorArrowItems();
636 for (int i = 0; i > diff; --i) {
637 if (!minorGridItemsList.isEmpty())
638 delete minorGridItemsList.takeLast();
639
640 if (!minorArrowItemsList.isEmpty())
641 delete minorArrowItemsList.takeLast();
642 }
643 }
644}
645
646QT_CHARTS_END_NAMESPACE
647
648#include "moc_polarchartaxisangular_p.cpp"
649

source code of qtcharts/src/charts/axis/polarchartaxisangular.cpp