##// END OF EJS Templates
Fix crash with boxplotttester...
Fix crash with boxplotttester The test application clears the second set in the first series. It is checked that the set exists before clearing it. Change-Id: I6fc7aa812a359b2227930852c02e9b6029a7303e Reviewed-by: Mika Salmela <mika.salmela@digia.com>

File last commit:

r2608:001bc2f70487
r2630:c58d5a10ac65
Show More
polarchartaxisangular.cpp
432 lines | 16.9 KiB | text/x-c | CppLexer
/ src / axis / polarchartaxisangular.cpp
Miikka Heikkinen
Add Polar chart support...
r2483 /****************************************************************************
**
** Copyright (C) 2013 Digia Plc
** All rights reserved.
** For any questions to Digia, please use contact form at http://qt.digia.com
**
Miikka Heikkinen
Qt Commercial -> Qt Enterprise...
r2574 ** This file is part of the Qt Enterprise Charts Add-on.
Miikka Heikkinen
Add Polar chart support...
r2483 **
** $QT_BEGIN_LICENSE$
Miikka Heikkinen
Qt Commercial -> Qt Enterprise...
r2574 ** Licensees holding valid Qt Enterprise licenses may use this file in
** accordance with the Qt Enterprise License Agreement provided with the
Miikka Heikkinen
Add Polar chart support...
r2483 ** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.
**
** If you have questions regarding the use of this file, please use
** contact form at http://qt.digia.com
** $QT_END_LICENSE$
**
****************************************************************************/
#include "polarchartaxisangular_p.h"
#include "chartpresenter_p.h"
#include "abstractchartlayout_p.h"
#include "qabstractaxis.h"
#include "qabstractaxis_p.h"
#include <QDebug>
Miikka Heikkinen
Optimize polar chart radius calculation a bit....
r2542 #include <qmath.h>
Miikka Heikkinen
Fix text item margins...
r2592 #include <QTextDocument>
Miikka Heikkinen
Add Polar chart support...
r2483
QTCOMMERCIALCHART_BEGIN_NAMESPACE
PolarChartAxisAngular::PolarChartAxisAngular(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis)
: PolarChartAxis(axis, item, intervalAxis)
{
}
PolarChartAxisAngular::~PolarChartAxisAngular()
{
}
void PolarChartAxisAngular::updateGeometry()
{
QGraphicsLayoutItem::updateGeometry();
const QVector<qreal> &layout = this->layout();
if (layout.isEmpty())
return;
createAxisLabels(layout);
QStringList labelList = labels();
QPointF center = axisGeometry().center();
QList<QGraphicsItem *> arrowItemList = arrowItems();
QList<QGraphicsItem *> gridItemList = gridItems();
QList<QGraphicsItem *> labelItemList = labelItems();
QList<QGraphicsItem *> shadeItemList = shadeItems();
Miikka Heikkinen
Added HTML support for various text items...
r2539 QGraphicsTextItem *title = titleItem();
Miikka Heikkinen
Add Polar chart support...
r2483
QGraphicsEllipseItem *axisLine = static_cast<QGraphicsEllipseItem *>(arrowItemList.at(0));
axisLine->setRect(axisGeometry());
qreal radius = axisGeometry().height() / 2.0;
QRectF previousLabelRect;
QRectF firstLabelRect;
qreal labelHeight = 0;
bool firstShade = true;
bool nextTickVisible = false;
if (layout.size())
nextTickVisible = !(layout.at(0) < 0.0 || layout.at(0) > 360.0);
for (int i = 0; i < layout.size(); ++i) {
qreal angularCoordinate = layout.at(i);
QGraphicsLineItem *gridLineItem = static_cast<QGraphicsLineItem *>(gridItemList.at(i));
QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem *>(arrowItemList.at(i + 1));
Miikka Heikkinen
Added HTML support for various text items...
r2539 QGraphicsTextItem *labelItem = static_cast<QGraphicsTextItem *>(labelItemList.at(i));
Miikka Heikkinen
Add Polar chart support...
r2483 QGraphicsPathItem *shadeItem = 0;
if (i == 0)
shadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at(0));
else if (i % 2)
shadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at((i / 2) + 1));
// Ignore ticks outside valid range
bool currentTickVisible = nextTickVisible;
if ((i == layout.size() - 1)
|| layout.at(i + 1) < 0.0
|| layout.at(i + 1) > 360.0) {
nextTickVisible = false;
} else {
nextTickVisible = true;
}
qreal labelCoordinate = angularCoordinate;
qreal labelVisible = currentTickVisible;
if (intervalAxis()) {
qreal farEdge;
if (i == (layout.size() - 1))
farEdge = 360.0;
else
Miikka Heikkinen
Fix some warnings for android build....
r2504 farEdge = qMin(qreal(360.0), layout.at(i + 1));
Miikka Heikkinen
Add Polar chart support...
r2483
// Adjust the labelCoordinate to show it if next tick is visible
if (nextTickVisible)
Miikka Heikkinen
Fix some warnings for android build....
r2504 labelCoordinate = qMax(qreal(0.0), labelCoordinate);
Miikka Heikkinen
Add Polar chart support...
r2483
labelCoordinate = (labelCoordinate + farEdge) / 2.0;
// Don't display label once the category gets too small near the axis
if (labelCoordinate < 5.0 || labelCoordinate > 355.0)
labelVisible = false;
else
labelVisible = true;
}
// Need this also in label calculations, so determine it first
QLineF tickLine(QLineF::fromPolar(radius - tickWidth(), 90.0 - angularCoordinate).p2(),
QLineF::fromPolar(radius + tickWidth(), 90.0 - angularCoordinate).p2());
tickLine.translate(center);
// Angular axis label
if (axis()->labelsVisible() && labelVisible) {
Titta Heikkala
Fix multiline alignment...
r2607 QRectF boundingRect = ChartPresenter::textBoundingRect(axis()->labelsFont(),
labelList.at(i),
axis()->labelsAngle());
labelItem->setTextWidth(boundingRect.width());
Miikka Heikkinen
Added HTML support for various text items...
r2539 labelItem->setHtml(labelList.at(i));
Miikka Heikkinen
Add Polar chart support...
r2483 const QRectF &rect = labelItem->boundingRect();
QPointF labelCenter = rect.center();
labelItem->setTransformOriginPoint(labelCenter.x(), labelCenter.y());
boundingRect.moveCenter(labelCenter);
QPointF positionDiff(rect.topLeft() - boundingRect.topLeft());
QPointF labelPoint;
if (intervalAxis()) {
QLineF labelLine = QLineF::fromPolar(radius + tickWidth(), 90.0 - labelCoordinate);
labelLine.translate(center);
labelPoint = labelLine.p2();
} else {
labelPoint = tickLine.p2();
}
QRectF labelRect = moveLabelToPosition(labelCoordinate, labelPoint, boundingRect);
labelItem->setPos(labelRect.topLeft() + positionDiff);
// Store height for title calculations
qreal labelClearance = axisGeometry().top() - labelRect.top();
labelHeight = qMax(labelHeight, labelClearance);
// Label overlap detection
if (i && (previousLabelRect.intersects(labelRect) || firstLabelRect.intersects(labelRect))) {
labelVisible = false;
} else {
// Store labelRect for future comparison. Some area is deducted to make things look
// little nicer, as usually intersection happens at label corner with angular labels.
labelRect.adjust(-2.0, -4.0, -2.0, -4.0);
if (firstLabelRect.isEmpty())
firstLabelRect = labelRect;
previousLabelRect = labelRect;
labelVisible = true;
}
}
labelItem->setVisible(labelVisible);
if (!currentTickVisible) {
gridLineItem->setVisible(false);
tickItem->setVisible(false);
if (shadeItem)
shadeItem->setVisible(false);
continue;
}
// Angular grid line
QLineF gridLine = QLineF::fromPolar(radius, 90.0 - angularCoordinate);
gridLine.translate(center);
gridLineItem->setLine(gridLine);
gridLineItem->setVisible(true);
// Tick
tickItem->setLine(tickLine);
tickItem->setVisible(true);
// Shades
if (i % 2 || (i == 0 && !nextTickVisible)) {
QPainterPath path;
path.moveTo(center);
if (i == 0) {
// If first tick is also the last, we need to custom fill the first partial arc
// or it won't get filled.
path.arcTo(axisGeometry(), 90.0 - layout.at(0), layout.at(0));
path.closeSubpath();
} else {
qreal nextCoordinate;
if (!nextTickVisible) // Last visible tick
nextCoordinate = 360.0;
else
nextCoordinate = layout.at(i + 1);
qreal arcSpan = angularCoordinate - nextCoordinate;
path.arcTo(axisGeometry(), 90.0 - angularCoordinate, arcSpan);
path.closeSubpath();
// Add additional arc for first shade item if there is a partial arc to be filled
if (firstShade) {
QGraphicsPathItem *specialShadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at(0));
if (layout.at(i - 1) > 0.0) {
QPainterPath specialPath;
specialPath.moveTo(center);
specialPath.arcTo(axisGeometry(), 90.0 - layout.at(i - 1), layout.at(i - 1));
specialPath.closeSubpath();
specialShadeItem->setPath(specialPath);
specialShadeItem->setVisible(true);
} else {
specialShadeItem->setVisible(false);
}
}
}
shadeItem->setPath(path);
shadeItem->setVisible(true);
firstShade = false;
}
}
// Title, centered above the chart
QString titleText = axis()->titleText();
if (!titleText.isEmpty() && axis()->isTitleVisible()) {
Titta Heikkala
Fix multiline alignment...
r2607 QRectF truncatedRect;
Titta Heikkala
Fix long labels visibility for QBarChart...
r2604 qreal availableTitleHeight = axisGeometry().height() - labelPadding() - titlePadding() * 2.0;
qreal minimumLabelHeight = ChartPresenter::textBoundingRect(axis()->labelsFont(), "...").height();
availableTitleHeight -= minimumLabelHeight;
title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0),
axisGeometry().width(), availableTitleHeight,
Titta Heikkala
Fix multiline alignment...
r2607 truncatedRect));
title->setTextWidth(truncatedRect.width());
Miikka Heikkinen
Add Polar chart support...
r2483
Miikka Heikkinen
Fix multiline axis label positioning....
r2534 QRectF titleBoundingRect = title->boundingRect();
Miikka Heikkinen
Add Polar chart support...
r2483 QPointF titleCenter = center - titleBoundingRect.center();
Miikka Heikkinen
Fix multiline axis titles truncation...
r2540 title->setPos(titleCenter.x(), axisGeometry().top() - titlePadding() * 2.0 - titleBoundingRect.height() - labelHeight);
Miikka Heikkinen
Add Polar chart support...
r2483 }
}
Qt::Orientation PolarChartAxisAngular::orientation() const
{
return Qt::Horizontal;
}
void PolarChartAxisAngular::createItems(int count)
{
if (arrowItems().count() == 0) {
// angular axis line
QGraphicsEllipseItem *arrow = new QGraphicsEllipseItem(presenter()->rootItem());
arrow->setPen(axis()->linePen());
arrowGroup()->addToGroup(arrow);
}
Titta Heikkala
Fix setting the axis title...
r2608 QGraphicsTextItem *title = titleItem();
title->setFont(axis()->titleFont());
title->setDefaultTextColor(axis()->titleBrush().color());
title->setHtml(axis()->titleText());
Miikka Heikkinen
Add Polar chart support...
r2483 for (int i = 0; i < count; ++i) {
QGraphicsLineItem *arrow = new QGraphicsLineItem(presenter()->rootItem());
QGraphicsLineItem *grid = new QGraphicsLineItem(presenter()->rootItem());
Miikka Heikkinen
Added HTML support for various text items...
r2539 QGraphicsTextItem *label = new QGraphicsTextItem(presenter()->rootItem());
Miikka Heikkinen
Fix text item margins...
r2592 label->document()->setDocumentMargin(ChartPresenter::textMargin());
Miikka Heikkinen
Add Polar chart support...
r2483 arrow->setPen(axis()->linePen());
grid->setPen(axis()->gridLinePen());
label->setFont(axis()->labelsFont());
Miikka Heikkinen
Added HTML support for various text items...
r2539 label->setDefaultTextColor(axis()->labelsBrush().color());
Miikka Heikkinen
Add Polar chart support...
r2483 label->setRotation(axis()->labelsAngle());
arrowGroup()->addToGroup(arrow);
gridGroup()->addToGroup(grid);
labelGroup()->addToGroup(label);
if (gridItems().size() == 1 || (((gridItems().size() + 1) % 2) && gridItems().size() > 0)) {
QGraphicsPathItem *shade = new QGraphicsPathItem(presenter()->rootItem());
shade->setPen(axis()->shadesPen());
shade->setBrush(axis()->shadesBrush());
shadeGroup()->addToGroup(shade);
}
}
}
void PolarChartAxisAngular::handleArrowPenChanged(const QPen &pen)
{
bool first = true;
foreach (QGraphicsItem *item, arrowItems()) {
if (first) {
first = false;
// First arrow item is the outer circle of axis
static_cast<QGraphicsEllipseItem *>(item)->setPen(pen);
} else {
static_cast<QGraphicsLineItem *>(item)->setPen(pen);
}
}
}
void PolarChartAxisAngular::handleGridPenChanged(const QPen &pen)
{
foreach (QGraphicsItem *item, gridItems())
static_cast<QGraphicsLineItem *>(item)->setPen(pen);
}
QSizeF PolarChartAxisAngular::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
Q_UNUSED(which);
Q_UNUSED(constraint);
return QSizeF(-1, -1);
}
qreal PolarChartAxisAngular::preferredAxisRadius(const QSizeF &maxSize)
{
qreal radius = maxSize.height() / 2.0;
if (maxSize.width() < maxSize.height())
radius = maxSize.width() / 2.0;
if (axis()->labelsVisible()) {
QVector<qreal> layout = calculateLayout();
if (layout.isEmpty())
return radius;
createAxisLabels(layout);
QStringList labelList = labels();
QFont font = axis()->labelsFont();
QRectF maxRect;
maxRect.setSize(maxSize);
maxRect.moveCenter(QPointF(0.0, 0.0));
// This is a horrible way to find out the maximum radius for angular axis and its labels.
// It just increments the radius down until everyhing fits the constraint size.
// Proper way would be to actually calculate it but this seems to work reasonably fast as it is.
bool nextTickVisible = false;
for (int i = 0; i < layout.size(); ) {
if ((i == layout.size() - 1)
|| layout.at(i + 1) < 0.0
|| layout.at(i + 1) > 360.0) {
nextTickVisible = false;
} else {
nextTickVisible = true;
}
qreal labelCoordinate = layout.at(i);
qreal labelVisible;
if (intervalAxis()) {
qreal farEdge;
if (i == (layout.size() - 1))
farEdge = 360.0;
else
Miikka Heikkinen
Fix some warnings for android build....
r2504 farEdge = qMin(qreal(360.0), layout.at(i + 1));
Miikka Heikkinen
Add Polar chart support...
r2483
// Adjust the labelCoordinate to show it if next tick is visible
if (nextTickVisible)
Miikka Heikkinen
Fix some warnings for android build....
r2504 labelCoordinate = qMax(qreal(0.0), labelCoordinate);
Miikka Heikkinen
Add Polar chart support...
r2483
labelCoordinate = (labelCoordinate + farEdge) / 2.0;
}
if (labelCoordinate < 0.0 || labelCoordinate > 360.0)
labelVisible = false;
else
labelVisible = true;
if (!labelVisible) {
i++;
continue;
}
Miikka Heikkinen
Added HTML support for various text items...
r2539 QRectF boundingRect = ChartPresenter::textBoundingRect(axis()->labelsFont(), labelList.at(i), axis()->labelsAngle());
Miikka Heikkinen
Add Polar chart support...
r2483 QPointF labelPoint = QLineF::fromPolar(radius + tickWidth(), 90.0 - labelCoordinate).p2();
boundingRect = moveLabelToPosition(labelCoordinate, labelPoint, boundingRect);
Miikka Heikkinen
Optimize polar chart radius calculation a bit....
r2542 QRectF intersectRect = maxRect.intersected(boundingRect);
if (boundingRect.isEmpty() || intersectRect == boundingRect) {
Miikka Heikkinen
Add Polar chart support...
r2483 i++;
} else {
Miikka Heikkinen
Optimize polar chart radius calculation a bit....
r2542 qreal reduction(0.0);
// If there is no intersection, reduce by smallest dimension of label rect to be on the safe side
if (intersectRect.isEmpty()) {
reduction = qMin(boundingRect.height(), boundingRect.width());
} else {
// Approximate needed radius reduction is the amount label rect exceeds max rect in either dimension.
// Could be further optimized by figuring out the proper math how to calculate exact needed reduction.
reduction = qMax(boundingRect.height() - intersectRect.height(),
boundingRect.width() - intersectRect.width());
}
// Typically the approximated reduction is little low, so add one
radius -= (reduction + 1.0);
Miikka Heikkinen
Add Polar chart support...
r2483 if (radius < 1.0) // safeguard
return 1.0;
}
}
}
if (!axis()->titleText().isEmpty() && axis()->isTitleVisible()) {
Miikka Heikkinen
Added HTML support for various text items...
r2539 QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(), axis()->titleText());
Miikka Heikkinen
Fix multi-line axis titles....
r2530
Miikka Heikkinen
Fix multiline axis label positioning....
r2534 radius -= titlePadding() + (titleRect.height() / 2.0);
Miikka Heikkinen
Add Polar chart support...
r2483 if (radius < 1.0) // safeguard
return 1.0;
}
return radius;
}
QRectF PolarChartAxisAngular::moveLabelToPosition(qreal angularCoordinate, QPointF labelPoint, QRectF labelRect) const
{
if (angularCoordinate == 0.0)
labelRect.moveCenter(labelPoint + QPointF(0, -labelRect.height() / 2.0));
else if (angularCoordinate < 90.0)
labelRect.moveBottomLeft(labelPoint);
else if (angularCoordinate == 90.0)
labelRect.moveCenter(labelPoint + QPointF(labelRect.width() / 2.0 + 2.0, 0)); // +2 so that it does not hit the radial axis
else if (angularCoordinate < 180.0)
labelRect.moveTopLeft(labelPoint);
else if (angularCoordinate == 180.0)
labelRect.moveCenter(labelPoint + QPointF(0, labelRect.height() / 2.0));
else if (angularCoordinate < 270.0)
labelRect.moveTopRight(labelPoint);
else if (angularCoordinate == 270.0)
labelRect.moveCenter(labelPoint + QPointF(-labelRect.width() / 2.0 - 2.0, 0)); // -2 so that it does not hit the radial axis
else if (angularCoordinate < 360.0)
labelRect.moveBottomRight(labelPoint);
else
labelRect.moveCenter(labelPoint + QPointF(0, -labelRect.height() / 2.0));
return labelRect;
}
#include "moc_polarchartaxisangular_p.cpp"
QTCOMMERCIALCHART_END_NAMESPACE