KDChartPolarCoordinatePlane.cpp

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

Generated on Thu Mar 4 23:19:12 2010 for KD Chart 2 by  doxygen 1.5.4