##// END OF EJS Templates
Fix zooming in crash...
Fix zooming in crash The bounding rect has to fit inside int limits when zooming in, meaning that the height and width both have to be within the int limits (INT_MAX). QWidget::update() uses a region (based on bounding rect) that has to be compatible with QRect. The geometry change is only done if the bounding rect fulfills this requirement. Task-number: QTRD-1907 Change-Id: I4e874de355390c5fc983ac1e0976bf0d21e2d10c Reviewed-by: Miikka Heikkinen <miikka.heikkinen@digia.com>

File last commit:

r2592:621c955c10d2
r2603:1e4c29e111d4
Show More
polarchartaxisangular.cpp
422 lines | 16.4 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) {
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());
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 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()) {
Miikka Heikkinen
Fix mingw build errors...
r2552 QRectF dummyRect;
title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0), axisGeometry().width(), Qt::Horizontal, dummyRect));
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);
}
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
Added HTML support for various text items...
r2539 QGraphicsTextItem *title = titleItem();
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());
title->setFont(axis()->titleFont());
Miikka Heikkinen
Added HTML support for various text items...
r2539 title->setDefaultTextColor(axis()->titleBrush().color());
title->setHtml(axis()->titleText());
Miikka Heikkinen
Add Polar chart support...
r2483 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