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 const auto items = arrowItems();
285 for (QGraphicsItem *item : items) {
286 if (first) {
287 first = false;
288 // First arrow item is the outer circle of axis
289 static_cast<QGraphicsEllipseItem *>(item)->setPen(pen);
290 } else {
291 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
292 }
293 }
294}
295
296void PolarChartAxisAngular::handleGridPenChanged(const QPen &pen)
297{
298 const auto items = gridItems();
299 for (QGraphicsItem *item : items)
300 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
301}
302
303void PolarChartAxisAngular::handleMinorArrowPenChanged(const QPen &pen)
304{
305 const auto items = minorArrowItems();
306 for (QGraphicsItem *item : items)
307 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
308}
309
310void PolarChartAxisAngular::handleMinorGridPenChanged(const QPen &pen)
311{
312 const auto items = minorGridItems();
313 for (QGraphicsItem *item : items)
314 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
315}
316
317void PolarChartAxisAngular::handleGridLineColorChanged(const QColor &color)
318{
319 const auto items = gridItems();
320 for (QGraphicsItem *item : items) {
321 QGraphicsLineItem *lineItem = static_cast<QGraphicsLineItem *>(item);
322 QPen pen = lineItem->pen();
323 pen.setColor(color);
324 lineItem->setPen(pen);
325 }
326}
327
328void PolarChartAxisAngular::handleMinorGridLineColorChanged(const QColor &color)
329{
330 const auto items = minorGridItems();
331 for (QGraphicsItem *item : items) {
332 QGraphicsLineItem *lineItem = static_cast<QGraphicsLineItem *>(item);
333 QPen pen = lineItem->pen();
334 pen.setColor(color);
335 lineItem->setPen(pen);
336 }
337}
338
339QSizeF PolarChartAxisAngular::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
340{
341 Q_UNUSED(which);
342 Q_UNUSED(constraint);
343 return QSizeF(-1, -1);
344}
345
346qreal PolarChartAxisAngular::preferredAxisRadius(const QSizeF &maxSize)
347{
348 qreal radius = maxSize.height() / 2.0;
349 if (maxSize.width() < maxSize.height())
350 radius = maxSize.width() / 2.0;
351
352 if (axis()->labelsVisible()) {
353 QList<qreal> layout = calculateLayout();
354 if (layout.isEmpty())
355 return radius;
356
357 createAxisLabels(layout);
358 QStringList labelList = labels();
359 QFont font = axis()->labelsFont();
360
361 QRectF maxRect;
362 maxRect.setSize(maxSize);
363 maxRect.moveCenter(p: QPointF(0.0, 0.0));
364
365 // This is a horrible way to find out the maximum radius for angular axis and its labels.
366 // It just increments the radius down until everyhing fits the constraint size.
367 // Proper way would be to actually calculate it but this seems to work reasonably fast as it is.
368 bool nextTickVisible = false;
369 for (int i = 0; i < layout.size(); ) {
370 if ((i == layout.size() - 1)
371 || layout.at(i: i + 1) < 0.0
372 || layout.at(i: i + 1) > 360.0) {
373 nextTickVisible = false;
374 } else {
375 nextTickVisible = true;
376 }
377
378 qreal labelCoordinate = layout.at(i);
379 qreal labelVisible;
380
381 if (intervalAxis()) {
382 qreal farEdge;
383 if (i == (layout.size() - 1))
384 farEdge = 360.0;
385 else
386 farEdge = qMin(a: qreal(360.0), b: layout.at(i: i + 1));
387
388 // Adjust the labelCoordinate to show it if next tick is visible
389 if (nextTickVisible)
390 labelCoordinate = qMax(a: qreal(0.0), b: labelCoordinate);
391
392 labelCoordinate = (labelCoordinate + farEdge) / 2.0;
393 }
394
395 if (labelCoordinate < 0.0 || labelCoordinate > 360.0)
396 labelVisible = false;
397 else
398 labelVisible = true;
399
400 if (!labelVisible) {
401 i++;
402 continue;
403 }
404
405 QRectF boundingRect = ChartPresenter::textBoundingRect(font: axis()->labelsFont(), text: labelList.at(i), angle: axis()->labelsAngle());
406 QPointF labelPoint = QLineF::fromPolar(length: radius + tickWidth(), angle: 90.0 - labelCoordinate).p2();
407
408 boundingRect = moveLabelToPosition(angularCoordinate: labelCoordinate, labelPoint, labelRect: boundingRect);
409 QRectF intersectRect = maxRect.intersected(r: boundingRect);
410 if (boundingRect.isEmpty() || intersectRect == boundingRect) {
411 i++;
412 } else {
413 qreal reduction(0.0);
414 // If there is no intersection, reduce by smallest dimension of label rect to be on the safe side
415 if (intersectRect.isEmpty()) {
416 reduction = qMin(a: boundingRect.height(), b: boundingRect.width());
417 } else {
418 // Approximate needed radius reduction is the amount label rect exceeds max rect in either dimension.
419 // Could be further optimized by figuring out the proper math how to calculate exact needed reduction.
420 reduction = qMax(a: boundingRect.height() - intersectRect.height(),
421 b: boundingRect.width() - intersectRect.width());
422 }
423 // Typically the approximated reduction is little low, so add one
424 radius -= (reduction + 1.0);
425
426 if (radius < 1.0) // safeguard
427 return 1.0;
428 }
429 }
430 }
431
432 if (!axis()->titleText().isEmpty() && axis()->isTitleVisible()) {
433 QRectF titleRect = ChartPresenter::textBoundingRect(font: axis()->titleFont(), text: axis()->titleText());
434
435 radius -= titlePadding() + (titleRect.height() / 2.0);
436 if (radius < 1.0) // safeguard
437 return 1.0;
438 }
439
440 return radius;
441}
442
443QRectF PolarChartAxisAngular::moveLabelToPosition(qreal angularCoordinate, QPointF labelPoint, QRectF labelRect) const
444{
445 if (angularCoordinate == 0.0)
446 labelRect.moveCenter(p: labelPoint + QPointF(0, -labelRect.height() / 2.0));
447 else if (angularCoordinate < 90.0)
448 labelRect.moveBottomLeft(p: labelPoint);
449 else if (angularCoordinate == 90.0)
450 labelRect.moveCenter(p: labelPoint + QPointF(labelRect.width() / 2.0 + 2.0, 0)); // +2 so that it does not hit the radial axis
451 else if (angularCoordinate < 180.0)
452 labelRect.moveTopLeft(p: labelPoint);
453 else if (angularCoordinate == 180.0)
454 labelRect.moveCenter(p: labelPoint + QPointF(0, labelRect.height() / 2.0));
455 else if (angularCoordinate < 270.0)
456 labelRect.moveTopRight(p: labelPoint);
457 else if (angularCoordinate == 270.0)
458 labelRect.moveCenter(p: labelPoint + QPointF(-labelRect.width() / 2.0 - 2.0, 0)); // -2 so that it does not hit the radial axis
459 else if (angularCoordinate < 360.0)
460 labelRect.moveBottomRight(p: labelPoint);
461 else
462 labelRect.moveCenter(p: labelPoint + QPointF(0, -labelRect.height() / 2.0));
463 return labelRect;
464}
465
466void PolarChartAxisAngular::updateMinorTickGeometry()
467{
468 if (!axis())
469 return;
470
471 QList<qreal> layout = ChartAxisElement::layout();
472 int minorTickCount = 0;
473 qreal tickAngle = 0.0;
474 QList<qreal> minorTickAngles;
475 switch (axis()->type()) {
476 case QAbstractAxis::AxisTypeValue: {
477 const QValueAxis *valueAxis = qobject_cast<QValueAxis *>(object: axis());
478
479 minorTickCount = valueAxis->minorTickCount();
480
481 if (valueAxis->tickCount() >= 2)
482 tickAngle = layout.at(i: 1) - layout.at(i: 0);
483
484 for (int i = 0; i < minorTickCount; ++i) {
485 const qreal ratio = (1.0 / qreal(minorTickCount + 1)) * qreal(i + 1);
486 minorTickAngles.append(t: tickAngle * ratio);
487 }
488 break;
489 }
490 case QAbstractAxis::AxisTypeLogValue: {
491 const QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(object: axis());
492 const qreal base = logValueAxis->base();
493 const qreal logBase = qLn(v: base);
494
495 minorTickCount = logValueAxis->minorTickCount();
496 if (minorTickCount < 0)
497 minorTickCount = qMax(a: qFloor(v: base) - 2, b: 0);
498
499 // Two "virtual" ticks are required to make sure that all minor ticks
500 // are displayed properly (even for the partially visible segments of
501 // the chart).
502 if (layout.size() >= 2) {
503 // Calculate tickAngle as a difference between visible ticks
504 // whenever it is possible. Virtual ticks will not be correctly
505 // positioned when the layout is animating.
506 tickAngle = layout.at(i: 1) - layout.at(i: 0);
507 layout.prepend(t: layout.at(i: 0) - tickAngle);
508 layout.append(t: layout.at(i: layout.size() - 1) + tickAngle);
509 } else {
510 const qreal logMax = qLn(v: logValueAxis->max());
511 const qreal logMin = qLn(v: logValueAxis->min());
512 const qreal logExtraMaxTick = qLn(v: qPow(x: base, y: qFloor(v: logMax / logBase) + 1.0));
513 const qreal logExtraMinTick = qLn(v: qPow(x: base, y: qCeil(v: logMin / logBase) - 1.0));
514 const qreal edge = qMin(a: logMin, b: logMax);
515 const qreal delta = 360.0 / qAbs(t: logMax - logMin);
516 const qreal extraMaxTick = edge + (logExtraMaxTick - edge) * delta;
517 const qreal extraMinTick = edge + (logExtraMinTick - edge) * delta;
518
519 // Calculate tickAngle using one (if layout.size() == 1) or two
520 // (if layout.size() == 0) "virtual" ticks. In both cases animation
521 // will not work as expected. This should be fixed later.
522 layout.prepend(t: extraMinTick);
523 layout.append(t: extraMaxTick);
524 tickAngle = layout.at(i: 1) - layout.at(i: 0);
525 }
526
527 const qreal minorTickStepValue = qFabs(v: base - 1.0) / qreal(minorTickCount + 1);
528 for (int i = 0; i < minorTickCount; ++i) {
529 const qreal x = minorTickStepValue * qreal(i + 1) + 1.0;
530 const qreal minorTickAngle = tickAngle * (qLn(v: x) / logBase);
531 minorTickAngles.append(t: minorTickAngle);
532 }
533 break;
534 }
535 default:
536 // minor ticks are not supported
537 break;
538 }
539
540 if (minorTickCount < 1 || tickAngle == 0.0 || minorTickAngles.size() != minorTickCount)
541 return;
542
543 const QPointF axisCenter = axisGeometry().center();
544 const qreal axisRadius = axisGeometry().height() / 2.0;
545
546 for (int i = 0; i < layout.size() - 1; ++i) {
547 for (int j = 0; j < minorTickCount; ++j) {
548 const int minorItemIndex = i * minorTickCount + j;
549 QGraphicsLineItem *minorGridLineItem =
550 static_cast<QGraphicsLineItem *>(minorGridItems().at(i: minorItemIndex));
551 QGraphicsLineItem *minorArrowLineItem =
552 static_cast<QGraphicsLineItem *>(minorArrowItems().at(i: minorItemIndex));
553 if (!minorGridLineItem || !minorArrowLineItem)
554 continue;
555
556 const qreal minorTickAngle = 90.0 - layout.at(i) - minorTickAngles.value(i: j, defaultValue: 0.0);
557
558 const QPointF minorArrowLinePt1 = QLineF::fromPolar(length: axisRadius - tickWidth() + 1,
559 angle: minorTickAngle).p2();
560 const QPointF minorArrowLinePt2 = QLineF::fromPolar(length: axisRadius + tickWidth() - 1,
561 angle: minorTickAngle).p2();
562
563 QLineF minorGridLine = QLineF::fromPolar(length: axisRadius, angle: minorTickAngle);
564 minorGridLine.translate(point: axisCenter);
565 minorGridLineItem->setLine(minorGridLine);
566
567 QLineF minorArrowLine(minorArrowLinePt1, minorArrowLinePt2);
568 minorArrowLine.translate(point: axisCenter);
569 minorArrowLineItem->setLine(minorArrowLine);
570
571 // check if the minor grid line and the minor axis arrow should be shown
572 const bool minorGridLineVisible = (minorTickAngle >= -270.0 && minorTickAngle <= 90.0);
573 minorGridLineItem->setVisible(minorGridLineVisible);
574 minorArrowLineItem->setVisible(minorGridLineVisible);
575 }
576 }
577}
578
579void PolarChartAxisAngular::updateMinorTickItems()
580{
581 int currentCount = minorArrowItems().size();
582 int expectedCount = 0;
583 if (axis()->type() == QAbstractAxis::AxisTypeValue) {
584 QValueAxis *valueAxis = qobject_cast<QValueAxis *>(object: axis());
585 expectedCount = valueAxis->minorTickCount() * (valueAxis->tickCount() - 1);
586 expectedCount = qMax(a: expectedCount, b: 0);
587 } else if (axis()->type() == QAbstractAxis::AxisTypeLogValue) {
588 QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(object: axis());
589
590 int minorTickCount = logValueAxis->minorTickCount();
591 if (minorTickCount < 0)
592 minorTickCount = qMax(a: qFloor(v: logValueAxis->base()) - 2, b: 0);
593
594 expectedCount = minorTickCount * (logValueAxis->tickCount() + 1);
595 expectedCount = qMax(a: expectedCount, b: logValueAxis->minorTickCount());
596 } else {
597 // minor ticks are not supported
598 return;
599 }
600
601 int diff = expectedCount - currentCount;
602 if (diff > 0) {
603 for (int i = 0; i < diff; ++i) {
604 QGraphicsLineItem *minorGridLineItem = new QGraphicsLineItem(this);
605 minorGridLineItem->setPen(axis()->minorGridLinePen());
606 minorGridGroup()->addToGroup(item: minorGridLineItem);
607
608 QGraphicsLineItem *minorArrowLineItem = new QGraphicsLineItem(this);
609 minorArrowLineItem->setPen(axis()->linePen());
610 minorArrowGroup()->addToGroup(item: minorArrowLineItem);
611 }
612 } else {
613 QList<QGraphicsItem *> minorGridItemsList = minorGridItems();
614 QList<QGraphicsItem *> minorArrowItemsList = minorArrowItems();
615 for (int i = 0; i > diff; --i) {
616 if (!minorGridItemsList.isEmpty())
617 delete minorGridItemsList.takeLast();
618
619 if (!minorArrowItemsList.isEmpty())
620 delete minorArrowItemsList.takeLast();
621 }
622 }
623}
624
625QT_END_NAMESPACE
626
627#include "moc_polarchartaxisangular_p.cpp"
628

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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