/* 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 #include #include "KDChartAxesPainter.h" #include "KDDrawText.h" #include /** \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 TQPainter 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( TQPainter* painter, KDChartTableDataBase* data, bool paint2nd, KDChartDataRegionList* regions ) { uint chart = paint2nd ? 1 : 0; TQRect 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 TQWMatrix & world = painter->worldMatrix(); ourClipRect = #if COMPAT_TQT_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 = TQMIN( _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 ); TQRect position( x, y, size, size ); TQPoint 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 = TQMAX( 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; TQMap < 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; TQMap < 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 ) ); TQFont 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 TQStringList* labelTexts = 0; ((KDChartParams*)params())->setAxisArea( KDChartAxisParams::AxisPosCircular, TQRect( 0, 0, static_cast( radiusPPU ), static_cast( radiusPPU ) ) ); double delimLen = 20.0 * minSizeP1000; // per mille of area KDChartAxisParams::AxisPos basicPos; TQPoint 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 = TQt::AlignHCenter | TQt::AlignVCenter; bool isLogarithmic = false; bool isDateTime = false; bool autoDtLabels = false; TQDateTime dtLow; TQDateTime 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 = ( TQStringList* ) paraCircular.axisLabelTexts(); if( paraCircular.axisLabelsVisible() ) { //tqDebug("\nnTxtHeight: "+TQString::number(nTxtHeight)); // calculate font size actFont = paraCircular.axisLabelsFont(); if ( paraCircular.axisLabelsFontUseRelSize() ) { //tqDebug("paraCircular.axisLabelsFontUseRelSize() is TRUE"); actFont.setPointSizeFloat( nTxtHeight ); } TQFontMetrics fm( actFont ); TQString strMax; int maxLabelsWidth = 0; for ( TQStringList::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 = TQFontMetrics( 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( TQPen( paraCircular.axisGridColor(), circularGridLineWidth ) ); painter->drawEllipse( static_cast( center.x() - currentRadiusPPU ), static_cast( center.y() - currentRadiusPPU ), static_cast( currentRadiusPPU2 ), static_cast( currentRadiusPPU2 ) ); } if( paraCircular.axisVisible() ) { painter->setPen( TQPen( paraCircular.axisLineColor(), circularLineWidth ) ); if( params()->polarDelimAtPos( KDChartEnums::PosTopCenter ) ) painter->drawArc( static_cast( center.x() - currentRadiusPPU ), static_cast( center.y() - currentRadiusPPU ), static_cast( currentRadiusPPU2 ), static_cast( currentRadiusPPU2 ), (90 - circularAxisAngle/2) * 16, circularAxisAngle * 16 ); if( params()->polarDelimAtPos( KDChartEnums::PosBottomCenter ) ) painter->drawArc( static_cast( center.x() - currentRadiusPPU ), static_cast( center.y() - currentRadiusPPU ), static_cast( currentRadiusPPU2 ), static_cast( currentRadiusPPU2 ), (270 - circularAxisAngle/2) * 16, circularAxisAngle * 16 ); if( params()->polarDelimAtPos( KDChartEnums::PosCenterRight ) ) painter->drawArc( static_cast( center.x() - currentRadiusPPU ), static_cast( center.y() - currentRadiusPPU ), static_cast( currentRadiusPPU2 ), static_cast( currentRadiusPPU2 ), (0 - circularAxisAngle/2) * 16, circularAxisAngle * 16 ); if( params()->polarDelimAtPos( KDChartEnums::PosCenterLeft ) ) painter->drawArc( static_cast( center.x() - currentRadiusPPU ), static_cast( center.y() - currentRadiusPPU ), static_cast( currentRadiusPPU2 ), static_cast( currentRadiusPPU2 ), (180 - circularAxisAngle/2) * 16, circularAxisAngle * 16 ); if( params()->polarDelimAtPos( KDChartEnums::PosTopRight ) ) painter->drawArc( static_cast( center.x() - currentRadiusPPU ), static_cast( center.y() - currentRadiusPPU ), static_cast( currentRadiusPPU2 ), static_cast( currentRadiusPPU2 ), (45 - circularAxisAngle/2) * 16, circularAxisAngle * 16 ); if( params()->polarDelimAtPos( KDChartEnums::PosBottomLeft ) ) painter->drawArc( static_cast( center.x() - currentRadiusPPU ), static_cast( center.y() - currentRadiusPPU ), static_cast( currentRadiusPPU2 ), static_cast( currentRadiusPPU2 ), (225 - circularAxisAngle/2) * 16, circularAxisAngle * 16 ); if( params()->polarDelimAtPos( KDChartEnums::PosBottomRight ) ) painter->drawArc( static_cast( center.x() - currentRadiusPPU ), static_cast( center.y() - currentRadiusPPU ), static_cast( currentRadiusPPU2 ), static_cast( currentRadiusPPU2 ), (315 - circularAxisAngle/2) * 16, circularAxisAngle * 16 ); if( params()->polarDelimAtPos( KDChartEnums::PosTopLeft ) ) painter->drawArc( static_cast( center.x() - currentRadiusPPU ), static_cast( center.y() - currentRadiusPPU ), static_cast( currentRadiusPPU2 ), static_cast( currentRadiusPPU2 ), (135 - circularAxisAngle/2) * 16, circularAxisAngle * 16 ); } if( paraCircular.axisLabelsVisible() ) { const bool rotate = params()->polarRotateCircularLabels(); painter->setPen( TQPen( paraCircular.axisLabelsColor(), circularLineWidth ) ); const TQString& txt = (*labelTexts)[ iLabel ]; if( params()->polarLabelsAtPos( KDChartEnums::PosTopCenter ) ) paintCircularAxisLabel( painter, rotate, 90, center, currentRadiusPPU, txt, TQt::AlignBottom | TQt::AlignHCenter, iLabel ); if( params()->polarLabelsAtPos( KDChartEnums::PosBottomCenter ) ) paintCircularAxisLabel( painter, rotate, 270, center, currentRadiusPPU, txt, TQt::AlignTop | TQt::AlignHCenter, iLabel ); if( params()->polarLabelsAtPos( KDChartEnums::PosCenterRight ) ) paintCircularAxisLabel( painter, rotate, 0, center, currentRadiusPPU, txt, TQt::AlignVCenter | TQt::AlignRight, iLabel ); if( params()->polarLabelsAtPos( KDChartEnums::PosCenterLeft ) ) paintCircularAxisLabel( painter, rotate, 180, center, currentRadiusPPU, txt, TQt::AlignVCenter | TQt::AlignLeft, iLabel ); if( params()->polarLabelsAtPos( KDChartEnums::PosTopRight ) ) paintCircularAxisLabel( painter, rotate, 45, center, currentRadiusPPU, txt, TQt::AlignBottom | TQt::AlignRight, iLabel ); if( params()->polarLabelsAtPos( KDChartEnums::PosBottomLeft ) ) paintCircularAxisLabel( painter, rotate, 225, center, currentRadiusPPU, txt, TQt::AlignTop | TQt::AlignLeft, iLabel ); if( params()->polarLabelsAtPos( KDChartEnums::PosBottomRight ) ) paintCircularAxisLabel( painter, rotate, 315, center, currentRadiusPPU, txt, TQt::AlignTop | TQt::AlignRight, iLabel ); if( params()->polarLabelsAtPos( KDChartEnums::PosTopLeft ) ) paintCircularAxisLabel( painter, rotate, 135, center, currentRadiusPPU, txt, TQt::AlignBottom | TQt::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 TQStringList* labelTexts = 0; bool onlyDefaultLabels = true; if( paraSagittal.axisLabelsVisible() ) { ((KDChartParams*)params())->setAxisArea( KDChartAxisParams::AxisPosSagittal, TQRect( 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; TQPoint 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 = TQt::AlignCenter; bool isLogarithmic = false; bool isDateTime = false; bool autoDtLabels = false; TQDateTime dtLow; TQDateTime 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 = ( TQStringList* ) paraSagittal.axisLabelTexts(); // calculate font size actFont = paraSagittal.axisLabelsFont(); if ( paraSagittal.axisLabelsFontUseRelSize() ) { actFont.setPointSizeFloat( nTxtHeight ); } TQFontMetrics fm( actFont ); TQString strMax; int maxLabelsWidth = 0; for ( TQStringList::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 = TQFontMetrics( 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 ); TQPoint 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( TQPen( paraSagittal.axisGridColor(), sagittalGridLineWidth ) ); painter->drawLine( center, pt1 ); } if( paraSagittal.axisVisible() ) { painter->setPen( TQPen( paraSagittal.axisLineColor(), sagittalLineWidth ) ); painter->drawLine( pt1, pt2 ); } if( paraSagittal.axisLabelsVisible() && labelTexts && labelTexts->count() > value ) { painter->setPen( TQPen( paraSagittal.axisLabelsColor(), sagittalLineWidth ) ); TQString label( onlyDefaultLabels ? TQString::number( currentAngle ) : (*labelTexts)[ value ] ); KDDrawText::drawRotatedText( painter, currentAngle+90, painter->worldMatrix().map(pt3), label, 0, TQt::AlignCenter ); } currentAngle += angleBetweenRays; } } // Now draw the data int dataLinesWidth = 0 <= params()->polarLineWidth() ? params()->polarLineWidth() : -1 * static_cast < int > ( params()->polarLineWidth() * minSizeP1000 ); painter->setBrush( TQt::NoBrush ); for ( unsigned int dataset = datasetStart; dataset <= datasetEnd; dataset++ ) { painter->setPen( TQPen( params()->dataColor( dataset ), dataLinesWidth ) ); TQPointArray points( numValues ); int totalPoints = 0; double valueTotal = 0.0; // Will only be used for Percent int angleBetweenRays = 360 / numValues; TQVariant 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 ) && TQVariant::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( radius ) ); } else drawValue = cellValue * pixelsPerUnit; // record the point for drawing the polygon later int drawAngle = value * angleBetweenRays; TQPoint drawPoint( center + polarToXY( static_cast( 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( TQPen( params()->dataColor( dataset ), dataLinesWidth ) ); } if ( regions ) { bool bMustAppendDatReg = 0 == datReg; if( bMustAppendDatReg ){ TQRect rect( TQPoint( drawPoint.x() - 1, drawPoint.y() - 1 ), TQSize( 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( TQPainter* painter, bool rotate, int txtAngle, TQPoint center, double currentRadiusPPU, const TQString& txt, int align, int step ) { if( !rotate && (0 != (align & (TQt::AlignLeft | TQt::AlignRight) ) ) ) currentRadiusPPU += center.x()*0.01; KDDrawText::drawRotatedText( painter, rotate ? txtAngle - 90 : 0, painter->worldMatrix().map(center - polarToXY( static_cast( currentRadiusPPU ), txtAngle )), txt, 0, step ? (rotate ? TQt::AlignBottom | TQt::AlignHCenter : align) : TQt::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( TQPainter* painter, KDChartParams::PolarMarkerStyle style, const TQColor& color, const TQPoint& p, uint, //dataset, uint, //value, uint, //chart, double minSizeP1000, TQRegion& 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 ); TQRect rect( TQPoint( p.x() - xsize2, p.y() - ysize2 ), TQPoint( p.x() + xsize2, p.y() + ysize2 ) ); painter->drawRect( rect ); // Don't use rect for drawing after this! rect.moveBy( _dataRect.x(), _dataRect.y() ); region = TQRegion( rect ); painter->restore(); break; } case KDChartParams::PolarMarkerDiamond: { painter->save(); painter->setBrush( color ); TQPointArray 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 = TQRegion( points ); painter->restore(); break; } case KDChartParams::PolarMarkerCircle: default: { painter->save(); painter->setBrush( color ); painter->drawEllipse( p.x() - xsize2, p.y() - ysize2, xsize, ysize ); TQPointArray 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 = TQRegion( points ); else region = TQRegion(); painter->restore(); } }; }*/ #define DEGTORAD(d) (d)*M_PI/180 TQPoint KDChartPolarPainter::polarToXY( int radius, int angle ) { double anglerad = DEGTORAD( static_cast( angle ) ); TQPoint ret( static_cast( cos( anglerad ) * radius ), static_cast( 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 */ TQString KDChartPolarPainter::fallbackLegendText( uint dataset ) const { return TQObject::tr( "Series " ) + TQString::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(); }