You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
806 lines
35 KiB
806 lines
35 KiB
15 years ago
|
/* -*- 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();
|
||
|
}
|