KD Chart 2  [rev.2.5]
KDChartPolarCoordinatePlane.cpp
Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2012 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 PolarCoordinatePlane::PolarCoordinatePlane ( Chart* parent )
00048     : AbstractCoordinatePlane ( new Private(), parent )
00049 {
00050     // this bloc left empty intentionally
00051 }
00052 
00053 PolarCoordinatePlane::~PolarCoordinatePlane()
00054 {
00055     // this bloc left empty intentionally
00056 }
00057 
00058 void PolarCoordinatePlane::init()
00059 {
00060     // this bloc left empty intentionally
00061 }
00062 
00063 void PolarCoordinatePlane::addDiagram ( AbstractDiagram* diagram )
00064 {
00065     Q_ASSERT_X ( dynamic_cast<AbstractPolarDiagram*> ( diagram ),
00066                  "PolarCoordinatePlane::addDiagram", "Only polar"
00067                  "diagrams can be added to a polar coordinate plane!" );
00068     AbstractCoordinatePlane::addDiagram ( diagram );
00069     connect ( diagram,  SIGNAL ( layoutChanged ( AbstractDiagram* ) ),
00070               SLOT ( slotLayoutChanged ( AbstractDiagram* ) ) );
00071 
00072 }
00073 
00074 void PolarCoordinatePlane::paint ( QPainter* painter )
00075 {
00076     AbstractDiagramList diags = diagrams();
00077     if ( d->coordinateTransformations.size() != diags.size() ) {
00078         // diagrams have not been set up yet
00079         return;
00080     }
00081      // need at least one so d->currentTransformation can be a valid pointer
00082     Q_ASSERT( !d->coordinateTransformations.isEmpty() );
00083 
00084     PaintContext ctx;
00085     ctx.setPainter ( painter );
00086     ctx.setCoordinatePlane ( this );
00087     ctx.setRectangle ( geometry() /*d->contentRect*/ );
00088 
00089     // 1. ask (only!) PolarDiagrams if they need additional space for data labels / data comments
00090 
00091     const qreal oldZoomX = zoomFactorX();
00092     const qreal oldZoomY = zoomFactorY();
00093     d->newZoomX = oldZoomX;
00094     d->newZoomY = oldZoomY;
00095     for ( int i = 0; i < diags.size(); i++ ) {
00096         d->currentTransformation = & ( d->coordinateTransformations[i] );
00097         qreal zoomX;
00098         qreal zoomY;
00099         PolarDiagram* polarDia = dynamic_cast<PolarDiagram*> ( diags[i] );
00100         if ( polarDia ) {
00101             polarDia->paint( &ctx, true, zoomX, zoomY );
00102             d->newZoomX = qMin( d->newZoomX, zoomX );
00103             d->newZoomY = qMin( d->newZoomY, zoomY );
00104         }
00105     }
00106 
00107     if ( d->newZoomX != oldZoomX || d->newZoomY != oldZoomY ) {
00108         //qDebug() << "new zoom:" << d->newZoomY << " old zoom:" << oldZoomY;
00109         d->currentTransformation = 0; // not painting anymore until we get called again
00110         QMetaObject::invokeMethod( this, "adjustZoomAndRepaint", Qt::QueuedConnection );
00111         return;
00112     }
00113 
00114     // 2. there was room enough for the labels, so start drawing
00115 
00116     // paint the coordinate system rulers:
00117     d->currentTransformation = &d->coordinateTransformations.first();
00118     d->grid->drawGrid( &ctx );
00119 
00120     // paint the diagrams which will re-use their DataValueTextInfoList(s) filled in step 1:
00121     for ( int i = 0; i < diags.size(); i++ ) {
00122         d->currentTransformation = & ( d->coordinateTransformations[i] );
00123         PainterSaver painterSaver( painter );
00124         PolarDiagram* polarDia = dynamic_cast<PolarDiagram*>( diags[i] );
00125         if ( polarDia ) {
00126             qreal dummy1, dummy2;
00127             polarDia->paint( &ctx, false, dummy1, dummy2 );
00128         } else {
00129             diags[i]->paint( &ctx );
00130         }
00131     }
00132     d->currentTransformation = 0;
00133 }
00134 
00135 
00136 void PolarCoordinatePlane::adjustZoomAndRepaint()
00137 {
00138     const qreal newZoom = qMin(d->newZoomX, d->newZoomY);
00139     setZoomFactors(newZoom, newZoom);
00140     update();
00141 }
00142 
00143 
00144 void PolarCoordinatePlane::resizeEvent ( QResizeEvent* )
00145 {
00146     d->initialResizeEventReceived = true;
00147     layoutDiagrams();
00148 }
00149 
00150 void PolarCoordinatePlane::layoutDiagrams()
00151 {
00152     // the rectangle the diagrams cover in the *plane*:
00153     // (Why -3? We save 1px on each side for the antialiased drawing, and
00154     // respect the way QPainter calculates the width of a painted rect (the
00155     // size is the rectangle size plus the pen width). This way, most clipping
00156     // for regular pens should be avoided. When pens with a penWidth or larger
00157     // than 1 are used, this may not b sufficient.
00158     const QRect rect( areaGeometry() );
00159     d->contentRect = QRectF ( 1, 1, rect.width() - 3, rect.height() - 3 );
00160 
00161     const ZoomParameters zoom = d->coordinateTransformations.isEmpty() ? ZoomParameters()
00162                                                                        : d->coordinateTransformations.front().zoom;
00163     // FIXME distribute space according to options:
00164     const qreal oldStartPosition = startPosition();
00165     d->coordinateTransformations.clear();
00166     Q_FOREACH( AbstractDiagram* diagram, diagrams() )
00167         {
00168             AbstractPolarDiagram *polarDiagram = dynamic_cast<AbstractPolarDiagram*>( diagram );
00169             Q_ASSERT( polarDiagram );
00170             QPair<QPointF, QPointF> dataBoundariesPair = polarDiagram->dataBoundaries();
00171 
00172             const qreal angleUnit = 360 / polarDiagram->valueTotals();
00173 //qDebug() << "--------------------------------------------------------";
00174             const qreal radius = qAbs( dataBoundariesPair.first.y() ) + dataBoundariesPair.second.y();
00175 //qDebug() << radius <<"="<<dataBoundariesPair.second.y();
00176             const qreal diagramWidth = radius * 2; // == height
00177             const qreal planeWidth = d->contentRect.width();
00178             const qreal planeHeight = d->contentRect.height();
00179             const qreal radiusUnit = qMin( planeWidth, planeHeight ) / diagramWidth;
00180 //qDebug() << radiusUnit <<"=" << "qMin( "<<planeWidth<<","<< planeHeight <<") / "<<diagramWidth;
00181             QPointF coordinateOrigin = QPointF ( planeWidth / 2, planeHeight / 2 );
00182             coordinateOrigin += d->contentRect.topLeft();
00183 
00184             CoordinateTransformation diagramTransposition;
00185             diagramTransposition.originTranslation = coordinateOrigin;
00186             diagramTransposition.radiusUnit = radiusUnit;
00187             diagramTransposition.angleUnit = angleUnit;
00188             diagramTransposition.startPosition = oldStartPosition;
00189             diagramTransposition.zoom = zoom;
00190             diagramTransposition.minValue = dataBoundariesPair.first.y() < 0 ? dataBoundariesPair.first.y() : 0.0;
00191             d->coordinateTransformations.append( diagramTransposition );
00192         }
00193 }
00194 
00195 const QPointF PolarCoordinatePlane::translate( const QPointF& diagramPoint ) const
00196 {
00197     Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::translate",
00198                  "Only call translate() from within paint()." );
00199     return  d->currentTransformation->translate ( diagramPoint );
00200 }
00201 
00202 const QPointF PolarCoordinatePlane::translatePolar( const QPointF& diagramPoint ) const
00203 {
00204     Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::translate",
00205                  "Only call translate() from within paint()." );
00206     return  d->currentTransformation->translatePolar ( diagramPoint );
00207 }
00208 
00209 qreal PolarCoordinatePlane::angleUnit() const
00210 {
00211     Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::angleUnit",
00212                  "Only call angleUnit() from within paint()." );
00213     return  d->currentTransformation->angleUnit;
00214 }
00215 
00216 qreal PolarCoordinatePlane::radiusUnit() const
00217 {
00218     Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::radiusUnit",
00219                  "Only call radiusUnit() from within paint()." );
00220     return  d->currentTransformation->radiusUnit;
00221 }
00222 
00223 void PolarCoordinatePlane::slotLayoutChanged ( AbstractDiagram* )
00224 {
00225     if ( d->initialResizeEventReceived ) layoutDiagrams();
00226 }
00227 
00228 void PolarCoordinatePlane::setStartPosition( qreal degrees )
00229 {
00230     Q_ASSERT_X ( diagram(), "PolarCoordinatePlane::setStartPosition",
00231                  "setStartPosition() needs a diagram to be associated to the plane." );
00232     for( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
00233                                                 it != d->coordinateTransformations.end();
00234                                                 ++it )
00235     {
00236         CoordinateTransformation& trans = *it;
00237         trans.startPosition = degrees;
00238     }
00239 }
00240 
00241 qreal PolarCoordinatePlane::startPosition() const
00242 {
00243     return d->coordinateTransformations.isEmpty()
00244         ? 0.0
00245         :  d->coordinateTransformations.first().startPosition;
00246 }
00247 
00248 qreal PolarCoordinatePlane::zoomFactorX() const
00249 {
00250     return d->coordinateTransformations.isEmpty()
00251         ? 1.0
00252         : d->coordinateTransformations.first().zoom.xFactor;
00253 }
00254 
00255 qreal PolarCoordinatePlane::zoomFactorY() const
00256 {
00257     return d->coordinateTransformations.isEmpty()
00258         ? 1.0
00259         : d->coordinateTransformations.first().zoom.yFactor;
00260 }
00261 
00262 void PolarCoordinatePlane::setZoomFactors( qreal factorX, qreal factorY )
00263 {
00264     setZoomFactorX( factorX );
00265     setZoomFactorY( factorY );
00266 }
00267 
00268 void PolarCoordinatePlane::setZoomFactorX( qreal factor )
00269 {
00270     for( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
00271                                                 it != d->coordinateTransformations.end();
00272                                                 ++it )
00273     {
00274         CoordinateTransformation& trans = *it;
00275         trans.zoom.xFactor = factor;
00276     }
00277 }
00278 
00279 void PolarCoordinatePlane::setZoomFactorY( qreal factor )
00280 {
00281     for( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
00282                                                 it != d->coordinateTransformations.end();
00283                                                 ++it )
00284     {
00285         CoordinateTransformation& trans = *it;
00286         trans.zoom.yFactor = factor;
00287     }
00288 }
00289 
00290 QPointF PolarCoordinatePlane::zoomCenter() const
00291 {
00292     return d->coordinateTransformations.isEmpty()
00293         ? QPointF( 0.5, 0.5 )
00294         : QPointF( d->coordinateTransformations.first().zoom.xCenter, d->coordinateTransformations.first().zoom.yCenter );
00295 }
00296 
00297 void PolarCoordinatePlane::setZoomCenter( const QPointF& center )
00298 {
00299     for( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
00300                                                 it != d->coordinateTransformations.end();
00301                                                 ++it )
00302     {
00303         CoordinateTransformation& trans = *it;
00304         trans.zoom.xCenter = center.x();
00305         trans.zoom.yCenter = center.y();
00306     }
00307 }
00308 
00309 DataDimensionsList PolarCoordinatePlane::getDataDimensionsList() const
00310 {
00311     DataDimensionsList l;
00312 
00313     //FIXME(khz): do the real calculation
00314 
00315     return l;
00316 }
00317 
00318 void KDChart::PolarCoordinatePlane::setGridAttributes(
00319     bool circular,
00320     const GridAttributes& a )
00321 {
00322     if( circular )
00323         d->gridAttributesCircular = a;
00324     else
00325         d->gridAttributesSagittal = a;
00326     setHasOwnGridAttributes( circular, true );
00327     update();
00328     emit propertiesChanged();
00329 }
00330 
00331 void KDChart::PolarCoordinatePlane::resetGridAttributes(
00332     bool circular )
00333 {
00334     setHasOwnGridAttributes( circular, false );
00335     update();
00336 }
00337 
00338 const GridAttributes KDChart::PolarCoordinatePlane::gridAttributes(
00339     bool circular ) const
00340 {
00341     if( hasOwnGridAttributes( circular ) ){
00342         if( circular )
00343             return d->gridAttributesCircular;
00344         else
00345             return d->gridAttributesSagittal;
00346     }else{
00347         return globalGridAttributes();
00348     }
00349 }
00350 
00351 QRectF KDChart::PolarCoordinatePlane::Private::contentsRect( const KDChart::PolarCoordinatePlane* plane )
00352 {
00353     QRectF contentsRect;
00354     QPointF referencePointAtTop = plane->translate( QPointF( 1, 0 ) );
00355     QPointF temp = plane->translate( QPointF( 0, 0 ) ) - referencePointAtTop;
00356     const qreal offset = temp.y();
00357     referencePointAtTop.setX( referencePointAtTop.x() - offset );
00358     contentsRect.setTopLeft( referencePointAtTop );
00359     contentsRect.setBottomRight( referencePointAtTop + QPointF( 2.0 * offset, 2.0 * offset) );
00360     return contentsRect;
00361 }
00362 
00363 void KDChart::PolarCoordinatePlane::setHasOwnGridAttributes(
00364     bool circular, bool on )
00365 {
00366     if( circular )
00367         d->hasOwnGridAttributesCircular = on;
00368     else
00369         d->hasOwnGridAttributesSagittal = on;
00370     emit propertiesChanged();
00371 }
00372 
00373 bool KDChart::PolarCoordinatePlane::hasOwnGridAttributes(
00374     bool circular ) const
00375 {
00376     return
00377         ( circular )
00378         ? d->hasOwnGridAttributesCircular
00379         : d->hasOwnGridAttributesSagittal;
00380 }
 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/