Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

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 }

Generated on Thu May 10 11:06:25 2007 for KD Chart 2 by doxygen 1.3.6