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

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