diff options
Diffstat (limited to 'kchart/kdchart/KDChartPolarPainter.cpp')
-rw-r--r-- | kchart/kdchart/KDChartPolarPainter.cpp | 805 |
1 files changed, 805 insertions, 0 deletions
diff --git a/kchart/kdchart/KDChartPolarPainter.cpp b/kchart/kdchart/KDChartPolarPainter.cpp new file mode 100644 index 00000000..41009a10 --- /dev/null +++ b/kchart/kdchart/KDChartPolarPainter.cpp @@ -0,0 +1,805 @@ +/* -*- Mode: C++ -*- + KDChart - a multi-platform charting engine + */ + +/**************************************************************************** + ** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. All rights reserved. + ** + ** This file is part of the KDChart library. + ** + ** This file may be distributed and/or modified under the terms of the + ** GNU General Public License version 2 as published by the Free Software + ** Foundation and appearing in the file LICENSE.GPL included in the + ** packaging of this file. + ** + ** Licensees holding valid commercial KDChart licenses may use this file in + ** accordance with the KDChart Commercial License Agreement provided with + ** the Software. + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ** See http://www.klaralvdalens-datakonsult.se/?page=products for + ** information about KDChart Commercial License Agreements. + ** + ** Contact info@klaralvdalens-datakonsult.se if any conditions of this + ** licensing are not clear to you. + ** + **********************************************************************/ +#include "KDChartPolarPainter.h" +#include <KDChartParams.h> +#include <KDChartAxisParams.h> +#include "KDChartAxesPainter.h" +#include "KDDrawText.h" + +#include <qpainter.h> + +/** + \class KDChartPolarPainter KDChartPolarPainter.h + + \brief A chart painter implementation that can paint polar charts. +*/ + +/** + Constructor. Sets up internal data structures as necessary. + + \param params the KDChartParams structure that defines the chart + \param data the data that will be displayed as a chart +*/ +KDChartPolarPainter::KDChartPolarPainter( KDChartParams* params ) : + KDChartPainter( params ) +{ + // This constructor intentionally left blank so far; we cannot setup the + // geometry yet since we do not know the size of the painter. +} + + +/** + Destructor. +*/ +KDChartPolarPainter::~KDChartPolarPainter() +{ + // intentionally left blank +} + + +/** + Paints the actual data area. Data regions will only be added if \a + regions is not 0 and the chart is configured to be drawn with + markers. + + \param painter the QPainter onto which the chart should be painted + \param data the data that will be displayed as a chart + \param paint2nd specifies whether the main chart or the additional chart is to be drawn now + \param regions a pointer to a list of regions that will be filled + with regions representing the data segments, if not null +*/ +void KDChartPolarPainter::paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions ) +{ + uint chart = paint2nd ? 1 : 0; + + QRect ourClipRect( _dataRect ); + ourClipRect.setBottom( ourClipRect.bottom() - 1 ); // protect axes + ourClipRect.setLeft( ourClipRect.left() + 1 ); + ourClipRect.setRight( ourClipRect.right() - 1 ); + // + // PENDING(khz) adjust the clip rect if neccessary... + // + + const QWMatrix & world = painter->worldMatrix(); + ourClipRect = +#if COMPAT_QT_VERSION >= 0x030000 + world.mapRect( ourClipRect ); +#else + world.map( ourClipRect ); +#endif + + painter->setClipRect( ourClipRect ); + + + uint datasetStart, datasetEnd; + findChartDatasets( data, paint2nd, chart, datasetStart, datasetEnd ); + + + painter->translate( _dataRect.x(), _dataRect.y() ); + + + // Number of values: If -1, use all values, otherwise use the + // specified number of values. + int numValues = 0; + if ( params()->numValues() != -1 ) + numValues = params()->numValues(); + else + numValues = data->usedCols(); + + // compute position + int size = QMIN( _dataRect.width(), _dataRect.height() ); // initial size + + const double minSizeP1000 = size / 1000.0; + + int x = ( _dataRect.width() == size ) ? 0 : ( ( _dataRect.width() - size ) / 2 ); + int y = ( _dataRect.height() == size ) ? 0 : ( ( _dataRect.height() - size ) / 2 ); + QRect position( x, y, size, size ); + + QPoint center( position.width() / 2 + position.x(), + position.height() / 2 + position.y() ); + + + double maxValue; + switch ( params()->polarChartSubType() ) { + case KDChartParams::PolarNormal: + maxValue = data->maxValue(); + break; + case KDChartParams::PolarPercent: + maxValue = 100.0; + break; + default: + maxValue = QMAX( data->maxColSum(), 0.0 ); + } + + double pixelsPerUnit = 0.0; + // the / 2 in the next line is there because we need the space in + // both directions + pixelsPerUnit = (position.height() / maxValue / 2) * 1000 / 1250; + + QMap < int, double > currentValueSums; + if ( params()->polarChartSubType() == KDChartParams::PolarStacked + || params()->polarChartSubType() == KDChartParams::PolarPercent ) + // this array is only used for stacked and percent polar + // charts, no need to waste time initializing it for normal + // ones + for ( int value = 0; value < numValues; value++ ) + currentValueSums[ value ] = 0.0; + QMap < int, double > totalValueSums; + + + /* + axes schema: use AxisPosSagittal for sagittal 'axis' lines + use AxisPosCircular for circular 'axis' + */ + const KDChartAxisParams & paraSagittal = params()->axisParams( KDChartAxisParams::AxisPosSagittal ); + const KDChartAxisParams & paraCircular = params()->axisParams( KDChartAxisParams::AxisPosCircular ); + + int sagittalLineWidth = 0 <= paraSagittal.axisLineWidth() + ? paraSagittal.axisLineWidth() + : -1 * static_cast < int > ( paraSagittal.axisLineWidth() + * minSizeP1000 ); + ( ( KDChartAxisParams& ) paraSagittal ).setAxisTrueLineWidth( sagittalLineWidth ); + int sagittalGridLineWidth + = ( KDCHART_AXIS_GRID_AUTO_LINEWIDTH + == paraSagittal.axisGridLineWidth() ) + ? sagittalLineWidth + : ( ( 0 <= paraSagittal.axisGridLineWidth() ) + ? paraSagittal.axisGridLineWidth() + : -1 * static_cast < int > ( paraSagittal.axisGridLineWidth() + * minSizeP1000 ) ); + + int circularLineWidth = 0 <= paraCircular.axisLineWidth() + ? paraCircular.axisLineWidth() + : -1 * static_cast < int > ( paraCircular.axisLineWidth() + * minSizeP1000 ); + ( ( KDChartAxisParams& ) paraCircular ).setAxisTrueLineWidth( circularLineWidth ); + int circularGridLineWidth + = ( KDCHART_AXIS_GRID_AUTO_LINEWIDTH + == paraCircular.axisGridLineWidth() ) + ? circularLineWidth + : ( ( 0 <= paraCircular.axisGridLineWidth() ) + ? paraCircular.axisGridLineWidth() + : -1 * static_cast < int > ( paraCircular.axisGridLineWidth() + * minSizeP1000 ) ); + + QFont actFont; + int labels = 0; + double currentRadiusPPU = position.height() / 2.0; + + // draw the "axis" circles + if( paraCircular.axisShowGrid() + || paraCircular.axisVisible() + || paraCircular.axisLabelsVisible() ) { + + double radiusPPU = maxValue * pixelsPerUnit; + double pDelimDelta = 0.0; + + // calculate label texts + QStringList* labelTexts = 0; + ((KDChartParams*)params())->setAxisArea( KDChartAxisParams::AxisPosCircular, + QRect( 0, + 0, + static_cast<int>( radiusPPU ), + static_cast<int>( radiusPPU ) ) ); + + double delimLen = 20.0 * minSizeP1000; // per mille of area + KDChartAxisParams::AxisPos basicPos; + QPoint orig, dest; + double dDummy; + double nSubDelimFactor = 0.0; + double nTxtHeight = 0.0; + double pTextsX = 0.0; + double pTextsY = 0.0; + double pTextsW = 0.0; + double pTextsH = 0.0; + int textAlign = Qt::AlignHCenter | Qt::AlignVCenter; + bool isLogarithmic = false; + bool isDateTime = false; + bool autoDtLabels = false; + QDateTime dtLow; + QDateTime dtHigh; + KDChartAxisParams::ValueScale dtDeltaScale; + KDChartAxesPainter::calculateLabelTexts( + painter, + *data, + *params(), + KDChartAxisParams::AxisPosCircular, + minSizeP1000, + delimLen, + // start of reference parameters + basicPos, + orig, + dest, + dDummy,dDummy,dDummy,dDummy, + nSubDelimFactor, + pDelimDelta, + nTxtHeight, + pTextsX, + pTextsY, + pTextsW, + pTextsH, + textAlign, + isLogarithmic, + isDateTime, + autoDtLabels, + dtLow, + dtHigh, + dtDeltaScale ); + labelTexts = ( QStringList* ) paraCircular.axisLabelTexts(); + if( paraCircular.axisLabelsVisible() ) { +//qDebug("\nnTxtHeight: "+QString::number(nTxtHeight)); + // calculate font size + actFont = paraCircular.axisLabelsFont(); + if ( paraCircular.axisLabelsFontUseRelSize() ) { +//qDebug("paraCircular.axisLabelsFontUseRelSize() is TRUE"); + actFont.setPointSizeFloat( nTxtHeight ); + } + QFontMetrics fm( actFont ); + QString strMax; + int maxLabelsWidth = 0; + for ( QStringList::Iterator it = labelTexts->begin(); + it != labelTexts->end(); + ++it ) { + if ( fm.width( *it ) > maxLabelsWidth ) { + maxLabelsWidth = fm.width( *it ); + strMax = *it; + } + } + while ( fm.width( strMax ) > pTextsW + && 6.0 < nTxtHeight ) { + nTxtHeight -= 0.5; + actFont.setPointSizeFloat( nTxtHeight ); + fm = QFontMetrics( actFont ); + } + painter->setFont( actFont ); + } + + double radiusDelta = pDelimDelta; + + labels = labelTexts + ? labelTexts->count() + : 0; + if( labels ) + currentRadiusPPU = -radiusDelta; + for( int iLabel = 0; iLabel < labels; ++iLabel ) { + //while( currentRadius < maxValue ) { + //double currentRadiusPPU = currentRadius; + currentRadiusPPU += radiusDelta; + double currentRadiusPPU2 = currentRadiusPPU * 2; + int circularAxisAngle = ( currentRadiusPPU != 0.0 ) ? ( static_cast < int > (4.0 * radiusPPU / currentRadiusPPU) ) : 0; + if( paraCircular.axisShowGrid() ) { + painter->setPen( QPen( paraCircular.axisGridColor(), + circularGridLineWidth ) ); + painter->drawEllipse( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ) ); + } + if( paraCircular.axisVisible() ) { + painter->setPen( QPen( paraCircular.axisLineColor(), + circularLineWidth ) ); + if( params()->polarDelimAtPos( KDChartEnums::PosTopCenter ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (90 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + if( params()->polarDelimAtPos( KDChartEnums::PosBottomCenter ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (270 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + + if( params()->polarDelimAtPos( KDChartEnums::PosCenterRight ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (0 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + if( params()->polarDelimAtPos( KDChartEnums::PosCenterLeft ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (180 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + + if( params()->polarDelimAtPos( KDChartEnums::PosTopRight ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (45 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + if( params()->polarDelimAtPos( KDChartEnums::PosBottomLeft ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (225 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + + if( params()->polarDelimAtPos( KDChartEnums::PosBottomRight ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (315 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + if( params()->polarDelimAtPos( KDChartEnums::PosTopLeft ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (135 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + } + if( paraCircular.axisLabelsVisible() ) { + const bool rotate = params()->polarRotateCircularLabels(); + painter->setPen( QPen( paraCircular.axisLabelsColor(), + circularLineWidth ) ); + const QString& txt = (*labelTexts)[ iLabel ]; + if( params()->polarLabelsAtPos( KDChartEnums::PosTopCenter ) ) + paintCircularAxisLabel( painter, rotate, 90, center, currentRadiusPPU, txt, + Qt::AlignBottom | Qt::AlignHCenter, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosBottomCenter ) ) + paintCircularAxisLabel( painter, rotate, 270, center, currentRadiusPPU, txt, + Qt::AlignTop | Qt::AlignHCenter, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosCenterRight ) ) + paintCircularAxisLabel( painter, rotate, 0, center, currentRadiusPPU, txt, + Qt::AlignVCenter | Qt::AlignRight, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosCenterLeft ) ) + paintCircularAxisLabel( painter, rotate, 180, center, currentRadiusPPU, txt, + Qt::AlignVCenter | Qt::AlignLeft, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosTopRight ) ) + paintCircularAxisLabel( painter, rotate, 45, center, currentRadiusPPU, txt, + Qt::AlignBottom | Qt::AlignRight, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosBottomLeft ) ) + paintCircularAxisLabel( painter, rotate, 225, center, currentRadiusPPU, txt, + Qt::AlignTop | Qt::AlignLeft, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosBottomRight ) ) + paintCircularAxisLabel( painter, rotate, 315, center, currentRadiusPPU, txt, + Qt::AlignTop | Qt::AlignRight, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosTopLeft ) ) + paintCircularAxisLabel( painter, rotate, 135, center, currentRadiusPPU, txt, + Qt::AlignBottom | Qt::AlignLeft, iLabel ); + } + } + } + + + double circularSpan = params()->polarChartSubType() == KDChartParams::PolarPercent + ? 100.0 + : paraCircular.trueAxisHigh() - paraCircular.trueAxisLow(); + double radius = currentRadiusPPU; + if( !labels + || params()->polarChartSubType() == KDChartParams::PolarPercent ) + radius = (position.width() / 2.0) * 1000.0 / 1250.0; + + if( params()->polarChartSubType() != KDChartParams::PolarPercent ) + pixelsPerUnit = labels ? currentRadiusPPU / circularSpan + : (position.height() / maxValue / 2.0) * 1000.0 / 1250.0; + else + pixelsPerUnit = (position.height() / 100.0 / 2.0) * 1000.0 / 1250.0; + + // draw the sagittal grid and axis lines + if( paraSagittal.axisShowGrid() + || paraSagittal.axisVisible() + || paraSagittal.axisLabelsVisible() ) { + + // calculate label texts + QStringList* labelTexts = 0; + bool onlyDefaultLabels = true; + if( paraSagittal.axisLabelsVisible() ) { + ((KDChartParams*)params())->setAxisArea( KDChartAxisParams::AxisPosSagittal, + QRect( 0, + 0, + static_cast < int > ( 2.0 * M_PI * radius ), + static_cast < int > ( 0.5 * radius ) ) ); + double delimLen = 20.0 * minSizeP1000; // per mille of area + KDChartAxisParams::AxisPos basicPos; + QPoint orig, dest; + double dDummy; + double nSubDelimFactor = 0.0; + double pDelimDelta = 0.0; + double nTxtHeight = 0.0; + double pTextsX = 0.0; + double pTextsY = 0.0; + double pTextsW = 0.0; + double pTextsH = 0.0; + int textAlign = Qt::AlignCenter; + bool isLogarithmic = false; + bool isDateTime = false; + bool autoDtLabels = false; + QDateTime dtLow; + QDateTime dtHigh; + KDChartAxisParams::ValueScale dtDeltaScale; + KDChartAxesPainter::calculateLabelTexts( + painter, + *data, + *params(), + KDChartAxisParams::AxisPosSagittal, + minSizeP1000, + delimLen, + // start of reference parameters + basicPos, + orig, + dest, + dDummy,dDummy,dDummy,dDummy, + nSubDelimFactor, + pDelimDelta, + nTxtHeight, + pTextsX, + pTextsY, + pTextsW, + pTextsH, + textAlign, + isLogarithmic, + isDateTime, + autoDtLabels, + dtLow, + dtHigh, + dtDeltaScale ); + labelTexts = ( QStringList* ) paraSagittal.axisLabelTexts(); + // calculate font size + actFont = paraSagittal.axisLabelsFont(); + if ( paraSagittal.axisLabelsFontUseRelSize() ) { + actFont.setPointSizeFloat( nTxtHeight ); + } + QFontMetrics fm( actFont ); + QString strMax; + int maxLabelsWidth = 0; + for ( QStringList::Iterator it = labelTexts->begin(); + it != labelTexts->end(); + ++it ) { + if ( fm.width( *it ) > maxLabelsWidth ) { + maxLabelsWidth = fm.width( *it ); + strMax = *it; + } + if ( !(*it).startsWith( "Item ") ) + onlyDefaultLabels = false; + } + while ( fm.width( strMax ) > pTextsW && 6.0 < nTxtHeight ) { + nTxtHeight -= 0.5; + actFont.setPointSizeFloat( nTxtHeight ); + fm = QFontMetrics( actFont ); + } + painter->setFont( actFont ); + } + + int currentAngle = params()->polarZeroDegreePos(); + if( -360 > currentAngle + || 360 < currentAngle ) + currentAngle = 0; + if( 0 > currentAngle ) + currentAngle += 360; + int r1 = static_cast < int > ( radius * 1050 / 1000 ); + int r2 = static_cast < int > ( radius * 1100 / 1000 ); + int r3 = static_cast < int > ( radius * 1175 / 1000 ); + QPoint pt1, pt2, pt3; + uint nLabels = labelTexts->count(); + int angleBetweenRays = 360 / nLabels; + for( uint value = 0; value < nLabels; ++value ) { + pt1 = center + polarToXY( r1, currentAngle ); + pt2 = center + polarToXY( r2, currentAngle ); + pt3 = center + polarToXY( r3, currentAngle ); + + //pt3 = painter->worldMatrix().map( pt3 ); + + if( paraSagittal.axisShowGrid() ) { + painter->setPen( QPen( paraSagittal.axisGridColor(), + sagittalGridLineWidth ) ); + painter->drawLine( center, pt1 ); + } + if( paraSagittal.axisVisible() ) { + painter->setPen( QPen( paraSagittal.axisLineColor(), + sagittalLineWidth ) ); + painter->drawLine( pt1, pt2 ); + } + if( paraSagittal.axisLabelsVisible() + && labelTexts + && labelTexts->count() > value ) { + painter->setPen( QPen( paraSagittal.axisLabelsColor(), + sagittalLineWidth ) ); + QString label( onlyDefaultLabels + ? QString::number( currentAngle ) + : (*labelTexts)[ value ] ); + + KDDrawText::drawRotatedText( painter, + currentAngle+90, + painter->worldMatrix().map(pt3), + label, + 0, + Qt::AlignCenter ); + } + currentAngle += angleBetweenRays; + } + } + + + // Now draw the data + int dataLinesWidth = 0 <= params()->polarLineWidth() + ? params()->polarLineWidth() + : -1 * static_cast < int > ( params()->polarLineWidth() + * minSizeP1000 ); + painter->setBrush( Qt::NoBrush ); + for ( unsigned int dataset = datasetStart; dataset <= datasetEnd; dataset++ ) { + painter->setPen( QPen( params()->dataColor( dataset ), + dataLinesWidth ) ); + QPointArray points( numValues ); + int totalPoints = 0; + double valueTotal = 0.0; // Will only be used for Percent + int angleBetweenRays = 360 / numValues; + QVariant vValY; + for ( int value = 0; value < numValues; value++ ) { + if( params()->polarChartSubType() == KDChartParams::PolarPercent ) + valueTotal = data->colAbsSum( value ); + // the value determines the angle, the dataset only the color + if( data->cellCoord( dataset, value, vValY, 1 ) && + QVariant::Double == vValY.type() ){ + const double cellValue = vValY.toDouble(); + double drawValue; + if ( params()->polarChartSubType() == KDChartParams::PolarStacked ) + drawValue = ( cellValue + currentValueSums[ value ] ) * pixelsPerUnit; + else if( params()->polarChartSubType() == KDChartParams::PolarPercent ) { + drawValue = ( ( cellValue + currentValueSums[ value ] ) + / valueTotal * static_cast<double>( radius ) ); + } else + drawValue = cellValue * pixelsPerUnit; + + // record the point for drawing the polygon later + int drawAngle = value * angleBetweenRays; + QPoint drawPoint( center + polarToXY( static_cast<int>( drawValue ), + drawAngle ) ); + points.setPoint( totalPoints, drawPoint ); + totalPoints++; + KDChartDataRegion* datReg = 0; + // the marker can be drawn now + if( params()->polarMarker() ) { + int xsize = params()->polarMarkerSize().width(); + int ysize = params()->polarMarkerSize().height(); + datReg = drawMarker( painter, + params(), + _areaWidthP1000, _areaHeightP1000, + _dataRect.x(), _dataRect.y(), + params()->polarMarkerStyle( dataset ), + params()->dataColor( dataset ), + drawPoint, + dataset, value, chart, + regions, + xsize ? &xsize : 0, + ysize ? &ysize : 0 ); + painter->setPen( QPen( params()->dataColor( dataset ), + dataLinesWidth ) ); + } + if ( regions ) { + bool bMustAppendDatReg = 0 == datReg; + if( bMustAppendDatReg ){ + QRect rect( QPoint( drawPoint.x() - 1, + drawPoint.y() - 1 ), + QSize( 3, 3 ) ); + datReg = new KDChartDataRegion( dataset, + value, + chart, + rect ); + } + datReg->points[ KDChartEnums::PosTopLeft ] = + drawPoint + _dataRect.topLeft(); + + datReg->points[ KDChartEnums::PosTopCenter ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosTopRight ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosBottomLeft ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosBottomCenter ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosBottomRight ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosCenterLeft ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosCenter ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosCenterRight ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + /* + // test the center positions: + painter->drawEllipse( datReg->points[ KDChartEnums::PosCenterLeft ].x() - 2, + datReg->points[ KDChartEnums::PosCenterLeft ].y() - 2, 5, 5); + */ + datReg->startAngle = drawAngle; + datReg->angleLen = drawAngle; + if( bMustAppendDatReg ) + regions->append( datReg ); + } + // calculate running sum for stacked and percent + if ( params()->polarChartSubType() == KDChartParams::PolarStacked || + params()->polarChartSubType() == KDChartParams::PolarPercent ) + currentValueSums[ value ] += cellValue; + } + } + painter->drawPolygon( points ); + } + + painter->translate( -_dataRect.x(), -_dataRect.y() ); +} + + +/* + Helper methode being called by KDChartPolarPainter::paintData() +*/ +void KDChartPolarPainter::paintCircularAxisLabel( QPainter* painter, + bool rotate, + int txtAngle, + QPoint center, + double currentRadiusPPU, + const QString& txt, + int align, + int step ) +{ + if( !rotate && (0 != (align & (Qt::AlignLeft | Qt::AlignRight) ) ) ) + currentRadiusPPU += center.x()*0.01; + KDDrawText::drawRotatedText( + painter, + rotate ? txtAngle - 90 : 0, + painter->worldMatrix().map(center - polarToXY( static_cast<int>( currentRadiusPPU ), txtAngle )), + txt, + 0, + step + ? (rotate ? Qt::AlignBottom | Qt::AlignHCenter : align) + : Qt::AlignCenter, + false,0,false, + false ); +} + + +/*! + Draws the marker for one data point according to the specified style. + + \param painter the painter to draw on + \param style what kind of marker is drawn (square, diamond or circle) + \param color the color in which to draw the marker + \param p the center of the marker + \param dataset the dataset which this marker represents + \param value the value which this marker represents + \param regions a list of regions for data points, a new region for the new + marker will be appended to this list if it is not 0 +*//* +void KDChartPolarPainter::drawMarker( QPainter* painter, + KDChartParams::PolarMarkerStyle style, + const QColor& color, + const QPoint& p, + uint, //dataset, + uint, //value, + uint, //chart, + double minSizeP1000, + QRegion& region ) +{ + int xsize = params()->polarMarkerSize().width(); + if ( 0 > xsize ) + xsize = -1 * static_cast < int > ( xsize * minSizeP1000 ); + int ysize = params()->polarMarkerSize().height(); + if ( 0 > ysize ) + ysize = -1 * static_cast < int > ( ysize * minSizeP1000 ); + int xsize2 = xsize / 2; + int ysize2 = ysize / 2; + painter->setPen( color ); + switch ( style ) { + case KDChartParams::PolarMarkerSquare: { + painter->save(); + painter->setBrush( color ); + QRect rect( QPoint( p.x() - xsize2, p.y() - ysize2 ), QPoint( p.x() + xsize2, p.y() + ysize2 ) ); + painter->drawRect( rect ); + // Don't use rect for drawing after this! + rect.moveBy( _dataRect.x(), _dataRect.y() ); + region = QRegion( rect ); + painter->restore(); + break; + } + case KDChartParams::PolarMarkerDiamond: { + painter->save(); + painter->setBrush( color ); + QPointArray points( 4 ); + points.setPoint( 0, p.x() - xsize2, p.y() ); + points.setPoint( 1, p.x(), p.y() - ysize2 ); + points.setPoint( 2, p.x() + xsize2, p.y() ); + points.setPoint( 3, p.x(), p.y() + ysize2 ); + painter->drawPolygon( points ); + // Don't use points for drawing after this! + points.translate( _dataRect.x(), _dataRect.y() ); + region = QRegion( points ); + painter->restore(); + break; + } + case KDChartParams::PolarMarkerCircle: + default: { + painter->save(); + painter->setBrush( color ); + painter->drawEllipse( p.x() - xsize2, p.y() - ysize2, xsize, ysize ); + QPointArray points; + points.makeEllipse( p.x() - xsize2, p.y() - ysize2, xsize, ysize ); + // Don't use points for drawing after this! + points.translate( _dataRect.x(), _dataRect.y() ); + if( points.size() > 0 ) + region = QRegion( points ); + else + region = QRegion(); + painter->restore(); + } + }; +}*/ + +#define DEGTORAD(d) (d)*M_PI/180 + +QPoint KDChartPolarPainter::polarToXY( int radius, int angle ) +{ + double anglerad = DEGTORAD( static_cast<double>( angle ) ); + QPoint ret( static_cast<int>( cos( anglerad ) * radius ), + static_cast<int>( sin( anglerad ) * radius ) ); + return ret; +} + + +/** + This method is a specialization that returns a fallback legend text + appropriate for polar charts where the fallbacks should come from + the values, not from the datasets. + + This method is only used when automatic legends are used, because + manual and first-column legends do not need fallback texts. + + \param uint dataset the dataset number for which to generate a + fallback text + \return the fallback text to use for describing the specified + dataset in the legend +*/ +QString KDChartPolarPainter::fallbackLegendText( uint dataset ) const +{ + return QObject::tr( "Series " ) + QString::number( dataset + 1 ); +} + + +/** + This methods returns the number of elements to be shown in the + legend in case fallback texts are used. + + This method is only used when automatic legends are used, because + manual and first-column legends do not need fallback texts. + + \return the number of fallback texts to use +*/ +uint KDChartPolarPainter::numLegendFallbackTexts( KDChartTableDataBase* data ) const +{ + return data->usedRows(); +} |