KDChartPolarCoordinatePlane.cpp

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

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
http://www.kdab.com/
http://www.kdab.com/products/kd-chart/