# KDChartPolarCoordinatePlane.cpp

Go to the documentation of this file.

    00001 /****************************************************************************
    00002  ** Copyright (C) 2006 Klar�vdalens Datakonsult AB.  All rights reserved.
    00003  **
    00004  ** This file is part of the KD Chart library.
    00005  **
    00006  ** This file may be distributed and/or modified under the terms of the
    00007  ** GNU General Public License version 2 as published by the Free Software
    00008  ** Foundation and appearing in the file LICENSE.GPL included in the
    00009  ** packaging of this file.
    00010  **
    00011  ** Licensees holding valid commercial KD Chart licenses may use this file in
    00012  ** accordance with the KD Chart Commercial License Agreement provided with
    00013  ** the Software.
    00014  **
    00015  ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
    00016  ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
    00017  **
    00018  ** See http://www.kdab.net/kdchart for
    00019  **   information about KDChart Commercial License Agreements.
    00020  **
    00021  ** Contact info@kdab.net if any conditions of this
    00022  ** licensing are not clear to you.
    00023  **
    00024  **********************************************************************/
    00025
    00026 #include <math.h>
    00027
    00028 #include <QFont>
    00029 #include <QList>
    00030 #include <QtDebug>
    00031 #include <QPainter>
    00032
    00033 #include "KDChartChart.h"
    00034 #include "KDChartPaintContext.h"
    00035 #include "KDChartAbstractDiagram.h"
    00036 #include "KDChartAbstractPolarDiagram.h"
    00037 #include "KDChartPolarCoordinatePlane.h"
    00038 #include "KDChartPolarCoordinatePlane_p.h"
    00039 #include "KDChartPainterSaver_p.h"
    00040
    00041 #include <KDABLibFakes>
    00042
    00043 using namespace KDChart;
    00044
    00045 #define d d_func()
    00046
    00047 /*
    00048 #ifndef M_PI
    00049 #define M_PI 3.14159265358979323846
    00050 #endif
    00051 #define DEGTORAD(d) (d)*M_PI/180
    00052
    00053 struct PolarCoordinatePlane::CoordinateTransformation
    00054 {
    00055     // represents the distance of the diagram coordinate origin to the
    00056     // origin of the coordinate plane space:
    00057     QPointF originTranslation;
    00058     double radiusUnit;
    00059     double angleUnit;
    00060
    00061     ZoomParameters zoom;
    00062
    00063     static QPointF polarToCartesian( double R, double theta )
    00064     {
    00065         return QPointF( R * cos( DEGTORAD( theta  ) ), R * sin( DEGTORAD( theta ) ) );
    00066     }
    00067
    00068     inline const QPointF translate( const QPointF& diagramPoint ) const
    00069     {
    00070         // calculate the polar coordinates
    00071         const double x = diagramPoint.x() * radiusUnit;
    00072         const double y = ( diagramPoint.y() * angleUnit) - 90;
    00073         // convert to cartesian coordinates
    00074         QPointF cartesianPoint = polarToCartesian( x, y );
    00075         cartesianPoint.setX( cartesianPoint.x() * zoom.xFactor );
    00076         cartesianPoint.setY( cartesianPoint.y() * zoom.yFactor );
    00077
    00078         QPointF newOrigin = originTranslation;
    00079         double minOrigin = qMin( newOrigin.x(), newOrigin.y() );
    00080         newOrigin.setX( newOrigin.x() + minOrigin * ( 1 - zoom.xCenter * 2 ) * zoom.xFactor );
    00081         newOrigin.setY( newOrigin.y() + minOrigin * ( 1 - zoom.yCenter * 2 ) * zoom.yFactor );
    00082
    00083         return newOrigin + cartesianPoint;
    00084     }
    00085
    00086     inline const QPointF translatePolar( const QPointF& diagramPoint ) const
    00087     {
    00088         return QPointF( diagramPoint.x() * angleUnit, diagramPoint.y() * radiusUnit );
    00089     }
    00090 };
    00091
    00092 class PolarCoordinatePlane::Private
    00093 {
    00094 public:
    00095     Private()
    00096         :currentTransformation(0),
    00097         initialResizeEventReceived(false )
    00098         {}
    00099
    00100
    00101     // the coordinate plane will calculate coordinate transformations for all
    00102     // diagrams and store them here:
    00103     CoordinateTransformationList coordinateTransformations;
    00104     // when painting, this pointer selects the coordinate transformation for
    00105     // the current diagram:
    00106     CoordinateTransformation* currentTransformation;
    00107     // the reactangle occupied by the diagrams, in plane coordinates
    00108     QRectF contentRect;
    00109     // true after the first resize event came in
    00110     bool initialResizeEventReceived;
    00111 };
    00112 */
    00113
    00114 PolarCoordinatePlane::PolarCoordinatePlane ( Chart* parent )
    00115     : AbstractCoordinatePlane ( new Private(), parent )
    00116 {
    00117     // this bloc left empty intentionally
    00118 }
    00119
    00120 PolarCoordinatePlane::~PolarCoordinatePlane()
    00121 {
    00122     // this bloc left empty intentionally
    00123 }
    00124
    00125 void PolarCoordinatePlane::init()
    00126 {
    00127     // this bloc left empty intentionally
    00128 }
    00129
    00130 void PolarCoordinatePlane::addDiagram ( AbstractDiagram* diagram )
    00131 {
    00132     Q_ASSERT_X ( dynamic_cast<AbstractPolarDiagram*> ( diagram ),
    00133                  "PolarCoordinatePlane::addDiagram", "Only polar"
    00134                  "diagrams can be added to a polar coordinate plane!" );
    00135     AbstractCoordinatePlane::addDiagram ( diagram );
    00136     connect ( diagram,  SIGNAL ( layoutChanged ( AbstractDiagram* ) ),
    00137               SLOT ( slotLayoutChanged ( AbstractDiagram* ) ) );
    00138
    00139 }
    00140
    00141 void PolarCoordinatePlane::paint ( QPainter* painter )
    00142 {
    00143     AbstractDiagramList diags = diagrams();
    00144     if ( d->coordinateTransformations.size() == diags.size() )
    00145     {
    00146         PaintContext ctx;
    00147         ctx.setPainter ( painter );
    00148         ctx.setCoordinatePlane ( this );
    00149         ctx.setRectangle ( geometry() /*d->contentRect*/ );
    00150
    00151         // paint the coordinate system rulers:
    00152         d->currentTransformation = & ( d->coordinateTransformations[0] );
    00153         d->grid->drawGrid( &ctx );
    00154
    00155         // paint the diagrams:
    00156         for ( int i = 0; i < diags.size(); i++ )
    00157         {
    00158             d->currentTransformation = & ( d->coordinateTransformations[i] );
    00159             PainterSaver painterSaver( painter );
    00160             diags[i]->paint ( &ctx );
    00161         }
    00162         d->currentTransformation = 0;
    00163     } // else: diagrams have not been set up yet
    00164 }
    00165
    00166 /*
    00167 void PolarCoordinatePlane::paintEvent ( QPaintEvent* )
    00168 {
    00169     AbstractDiagramList diags = diagrams();
    00170     if ( d->coordinateTransformations.size() == diags.size() )
    00171     {
    00172         QPainter painter ( this );
    00173         PaintContext ctx;
    00174         ctx.setPainter ( &painter );
    00175         ctx.setCoordinatePlane ( this );
    00176         ctx.setRectangle ( d->contentRect );
    00177
    00178         // paint the coordinate system rulers:
    00179         d->grid->drawGrid( &ctx );
    00180
    00181         // paint the diagrams:
    00182         for ( int i = 0; i < diags.size(); i++ )
    00183         {
    00184             d->currentTransformation = & ( d->coordinateTransformations[i] );
    00185             PainterSaver painterSaver( &painter );
    00186             diags[i]->paint ( &ctx );
    00187         }
    00188         d->currentTransformation = 0;
    00189     } // else: diagrams have not been set up yet
    00190 }
    00191 */
    00192 /*
    00193 void PolarCoordinatePlane::paintGrid( PaintContext* ctx )
    00194 {
    00195     if ( d->coordinateTransformations.size () <= 0 ) return;
    00196
    00197     // FIXME: we paint the rulers to the settings of the first diagram for now:
    00198     AbstractPolarDiagram* dgr = dynamic_cast<AbstractPolarDiagram*> (diagrams().first() );
    00199     Q_ASSERT ( dgr ); // only polar diagrams are allowed here
    00200
    00201     ctx->painter()->setPen ( QColor ( Qt::lightGray ) );
    00202     QPointF origin = translate( QPointF( 0,0 ) );
    00203     const int numberOfSpokes = ( int ) ( 360 / d->currentTransformation->angleUnit );
    00204     const double r = dgr->dataBoundaries().second.y(); // use the full extents
    00205     for ( int i = 0; i < numberOfSpokes ; ++i ) {
    00206         ctx->painter()->drawLine( origin, d->currentTransformation->translate( QPointF( r, i ) ) );
    00207     }
    00208     const int numberOfGridRings = ( int ) dgr->numberOfGridRings();
    00209     for ( int i = 0; i < numberOfGridRings; ++i ) {
    00210         const double rad = ( ( i + 1) * r / numberOfGridRings );
    00211
    00212         if ( rad == 0 )
    00213             continue;
    00214
    00215         QRectF rect;
    00216         QPointF topLeftPoint;
    00217         QPointF bottomRightPoint;
    00218
    00219         topLeftPoint = d->currentTransformation->translate( QPointF( rad, 0 ) );
    00220         topLeftPoint.setX( d->currentTransformation->translate( QPointF( rad, 90 / d->currentTransformation->angleUnit ) ).x() );
    00221         bottomRightPoint = d->currentTransformation->translate( QPointF( rad, 180 / d->currentTransformation->angleUnit ) );
    00222         bottomRightPoint.setX( d->currentTransformation->translate( QPointF( rad, 270 / d->currentTransformation->angleUnit ) ).x() );
    00223
    00224         rect.setTopLeft( topLeftPoint );
    00225         rect.setBottomRight( bottomRightPoint );
    00226
    00227         ctx->painter()->drawEllipse( rect );
    00228     }
    00229 }
    00230 */
    00231
    00232 void PolarCoordinatePlane::resizeEvent ( QResizeEvent* )
    00233 {
    00234     d->initialResizeEventReceived = true;
    00235     layoutDiagrams();
    00236 }
    00237
    00238 void PolarCoordinatePlane::layoutDiagrams()
    00239 {
    00240     // the rectangle the diagrams cover in the *plane*:
    00241     // (Why -3? We save 1px on each side for the antialiased drawing, and
    00242     // respect the way QPainter calculates the width of a painted rect (the
    00243     // size is the rectangle size plus the pen width). This way, most clipping
    00244     // for regular pens should be avoided. When pens with a penWidth or larger
    00245     // than 1 are used, this may not b sufficient.
    00246     const QRect rect( areaGeometry() );
    00247     d->contentRect = QRectF ( 1, 1, rect.width() - 3, rect.height() - 3 );
    00248
    00249     // FIXME distribute space according to options:
    00250     const qreal oldStartPosition = startPosition();
    00251     d->coordinateTransformations.clear();
    00252     Q_FOREACH( AbstractDiagram* diagram, diagrams() )
    00253         {
    00254             AbstractPolarDiagram *polarDiagram = dynamic_cast<AbstractPolarDiagram*>( diagram );
    00255             Q_ASSERT( polarDiagram );
    00256             QPair<QPointF, QPointF> dataBoundariesPair = polarDiagram->dataBoundaries();
    00257
    00258             const double angleUnit = 360 / polarDiagram->valueTotals();
    00259 //qDebug() << "--------------------------------------------------------";
    00260             const double radius = dataBoundariesPair.second.y();
    00261 //qDebug() << radius <<"="<<dataBoundariesPair.second.y();
    00262             const double diagramWidth = radius * 2; // == height
    00263             const double planeWidth = d->contentRect.width();
    00264             const double planeHeight = d->contentRect.height();
    00265             const double radiusUnit = qMin( planeWidth, planeHeight ) / diagramWidth;
    00266 //qDebug() << radiusUnit <<"=" << "qMin( "<<planeWidth<<","<< planeHeight <<") / "<<diagramWidth;
    00267             QPointF coordinateOrigin = QPointF ( planeWidth / 2, planeHeight / 2 );
    00268             coordinateOrigin += d->contentRect.topLeft();
    00269
    00270             CoordinateTransformation diagramTransposition;
    00271             diagramTransposition.originTranslation = coordinateOrigin;
    00272             diagramTransposition.radiusUnit = radiusUnit;
    00273             diagramTransposition.angleUnit = angleUnit;
    00274             diagramTransposition.startPosition = oldStartPosition;
    00275             diagramTransposition.zoom = ZoomParameters();
    00276             d->coordinateTransformations.append( diagramTransposition );
    00277         }
    00278 }
    00279
    00280 const QPointF PolarCoordinatePlane::translate( const QPointF& diagramPoint ) const
    00281 {
    00282     Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::translate",
    00283                  "Only call translate() from within paint()." );
    00284     return  d->currentTransformation->translate ( diagramPoint );
    00285 }
    00286
    00287 const QPointF PolarCoordinatePlane::translatePolar( const QPointF& diagramPoint ) const
    00288 {
    00289     Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::translate",
    00290                  "Only call translate() from within paint()." );
    00291     return  d->currentTransformation->translatePolar ( diagramPoint );
    00292 }
    00293
    00294 qreal PolarCoordinatePlane::angleUnit() const
    00295 {
    00296     Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::angleUnit",
    00297                  "Only call angleUnit() from within paint()." );
    00298     return  d->currentTransformation->angleUnit;
    00299 }
    00300
    00301 qreal PolarCoordinatePlane::radiusUnit() const
    00302 {
    00303     Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::radiusUnit",
    00304                  "Only call radiusUnit() from within paint()." );
    00305     return  d->currentTransformation->radiusUnit;
    00306 }
    00307
    00308 void PolarCoordinatePlane::slotLayoutChanged ( AbstractDiagram* )
    00309 {
    00310     if ( d->initialResizeEventReceived ) layoutDiagrams();
    00311 }
    00312
    00313 void PolarCoordinatePlane::setStartPosition( qreal degrees )
    00314 {
    00315     Q_ASSERT_X ( diagram(), "PolarCoordinatePlane::setStartPosition",
    00316                  "setStartPosition() needs a diagram to be associated to the plane." );
    00317     d->coordinateTransformations[0].startPosition = degrees;
    00318 }
    00319
    00320 qreal PolarCoordinatePlane::startPosition() const
    00321 {
    00322     return d->coordinateTransformations.size()
    00323         ?  d->coordinateTransformations[0].startPosition
    00324         :  0.0;
    00325 }
    00326
    00327 double PolarCoordinatePlane::zoomFactorX() const
    00328 {
    00329     return d->coordinateTransformations[0].zoom.xFactor;
    00330 }
    00331
    00332 double PolarCoordinatePlane::zoomFactorY() const
    00333 {
    00334     return d->coordinateTransformations[0].zoom.yFactor;
    00335 }
    00336
    00337 void PolarCoordinatePlane::setZoomFactorX( double factor )
    00338 {
    00339     d->coordinateTransformations[0].zoom.xFactor = factor;
    00340 }
    00341
    00342 void PolarCoordinatePlane::setZoomFactorY( double factor )
    00343 {
    00344     d->coordinateTransformations[0].zoom.yFactor = factor;
    00345 }
    00346
    00347 QPointF PolarCoordinatePlane::zoomCenter() const
    00348 {
    00349     return QPointF( d->coordinateTransformations[0].zoom.xCenter, d->coordinateTransformations[0].zoom.yCenter );
    00350 }
    00351
    00352 void PolarCoordinatePlane::setZoomCenter( QPointF center )
    00353 {
    00354     d->coordinateTransformations[0].zoom.xCenter = center.x();
    00355     d->coordinateTransformations[0].zoom.yCenter = center.y();
    00356 }
    00357
    00358 DataDimensionsList PolarCoordinatePlane::getDataDimensionsList() const
    00359 {
    00360     DataDimensionsList l;
    00361
    00362     //FIXME(khz): do the real calculation
    00363
    00364     return l;
    00365 }
    00366
    00367 void KDChart::PolarCoordinatePlane::setGridAttributes(
    00368     bool circular,
    00369     const GridAttributes& a )
    00370 {
    00371     if( circular )
    00372         d->gridAttributesCircular = a;
    00373     else
    00374         d->gridAttributesSagittal = a;
    00375     setHasOwnGridAttributes( circular, true );
    00376     update();
    00377     emit propertiesChanged();
    00378 }
    00379
    00380 void KDChart::PolarCoordinatePlane::resetGridAttributes(
    00381     bool circular )
    00382 {
    00383     setHasOwnGridAttributes( circular, false );
    00384     update();
    00385 }
    00386
    00387 const GridAttributes KDChart::PolarCoordinatePlane::gridAttributes(
    00388     bool circular ) const
    00389 {
    00390     if( hasOwnGridAttributes( circular ) ){
    00391         if( circular )
    00392             return d->gridAttributesCircular;
    00393         else
    00394             return d->gridAttributesSagittal;
    00395     }else{
    00396         return globalGridAttributes();
    00397     }
    00398 }
    00399
    00400 void KDChart::PolarCoordinatePlane::setHasOwnGridAttributes(
    00401     bool circular, bool on )
    00402 {
    00403     if( circular )
    00404         d->hasOwnGridAttributesCircular = on;
    00405     else
    00406         d->hasOwnGridAttributesSagittal = on;
    00407     emit propertiesChanged();
    00408 }
    00409
    00410 bool KDChart::PolarCoordinatePlane::hasOwnGridAttributes(
    00411     bool circular ) const
    00412 {
    00413     return
    00414         ( circular )
    00415         ? d->hasOwnGridAttributesCircular
    00416         : d->hasOwnGridAttributesSagittal;
    00417 }