KD Chart 2
[rev.2.5]
|
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 }