summaryrefslogtreecommitdiffstats
path: root/libkdchart/KDChartPolarPainter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libkdchart/KDChartPolarPainter.cpp')
-rw-r--r--libkdchart/KDChartPolarPainter.cpp805
1 files changed, 805 insertions, 0 deletions
diff --git a/libkdchart/KDChartPolarPainter.cpp b/libkdchart/KDChartPolarPainter.cpp
new file mode 100644
index 0000000..41009a1
--- /dev/null
+++ b/libkdchart/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();
+}