KDChartCartesianCoordinatePlane.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002  ** Copyright (C) 2007 Klaralvdalens 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 "KDChartCartesianCoordinatePlane.h"
00027 #include "KDChartCartesianCoordinatePlane_p.h"
00028 
00029 #include <QFont>
00030 #include <QList>
00031 #include <QtDebug>
00032 #include <QPainter>
00033 #include <QApplication>
00034 
00035 #include "KDChartAbstractDiagram.h"
00036 #include "KDChartAbstractCartesianDiagram.h"
00037 #include "CartesianCoordinateTransformation.h"
00038 #include "KDChartGridAttributes.h"
00039 #include "KDChartPaintContext.h"
00040 #include "KDChartPainterSaver_p.h"
00041 #include "KDChartBarDiagram.h"
00042 
00043 #include <KDABLibFakes>
00044 
00045 
00046 using namespace KDChart;
00047 
00048 #define d d_func()
00049 
00050 CartesianCoordinatePlane::Private::Private()
00051     : AbstractCoordinatePlane::Private()
00052     , bPaintIsRunning( false )
00053     , hasOwnGridAttributesHorizontal ( false )
00054     , hasOwnGridAttributesVertical ( false )
00055     // old: , initialResizeEventReceived ( false )
00056     , isometricScaling ( false )
00057     , horizontalMin(0)
00058     , horizontalMax(0)
00059     , verticalMin(0)
00060     , verticalMax(0)
00061     , autoAdjustHorizontalRangeToData(67)
00062     , autoAdjustVerticalRangeToData(  67)
00063     , autoAdjustGridToZoom( true )
00064     , fixedDataCoordinateSpaceRelation( false )
00065     , reverseVerticalPlane( false )
00066     , reverseHorizontalPlane( false )
00067 {
00068 }
00069 
00070 CartesianCoordinatePlane::CartesianCoordinatePlane ( Chart* parent )
00071     : AbstractCoordinatePlane ( new Private(), parent )
00072 {
00073     // this bloc left empty intentionally
00074 }
00075 
00076 CartesianCoordinatePlane::~CartesianCoordinatePlane()
00077 {
00078     // this bloc left empty intentionally
00079 }
00080 
00081 void CartesianCoordinatePlane::init()
00082 {
00083     // this bloc left empty intentionally
00084 }
00085 
00086 
00087 void CartesianCoordinatePlane::addDiagram ( AbstractDiagram* diagram )
00088 {
00089     Q_ASSERT_X ( dynamic_cast<AbstractCartesianDiagram*> ( diagram ),
00090                  "CartesianCoordinatePlane::addDiagram", "Only cartesian "
00091                  "diagrams can be added to a cartesian coordinate plane!" );
00092     AbstractCoordinatePlane::addDiagram ( diagram );
00093     connect ( diagram,  SIGNAL ( layoutChanged ( AbstractDiagram* ) ),
00094               SLOT ( slotLayoutChanged ( AbstractDiagram* ) ) );
00095 
00096     connect( diagram, SIGNAL( propertiesChanged() ),this, SIGNAL( propertiesChanged() ) );
00097 }
00098 
00099 
00100 void CartesianCoordinatePlane::paint ( QPainter* painter )
00101 {
00102     // prevent recursive call:
00103     //qDebug("attempt plane::paint()");
00104     if( d->bPaintIsRunning ){
00105         return;
00106     }
00107     d->bPaintIsRunning = true;
00108 
00109     //qDebug() << "start plane::paint()";
00110 
00111     AbstractDiagramList diags = diagrams();
00112     if ( !diags.isEmpty() )
00113     {
00114         PaintContext ctx;
00115         ctx.setPainter ( painter );
00116         ctx.setCoordinatePlane ( this );
00117         const QRectF drawArea( drawingArea() );
00118         ctx.setRectangle ( drawArea );
00119 
00120         // enabling clipping so that we're not drawing outside
00121         PainterSaver painterSaver( painter );
00122         QRect clipRect = drawArea.toRect().adjusted( -1, -1, 1, 1 );
00123         QRegion clipRegion( clipRect );
00124         painter->setClipRegion( clipRegion );
00125 
00126         // paint the coordinate system rulers:
00127         d->grid->drawGrid( &ctx );
00128 
00129         // paint the diagrams:
00130         for ( int i = 0; i < diags.size(); i++ )
00131         {
00132 //qDebug("  start diags[i]->paint ( &ctx );");
00133             PainterSaver diagramPainterSaver( painter );
00134             diags[i]->paint ( &ctx );
00135 //qDebug("  done: diags[i]->paint ( &ctx );");
00136         }
00137 
00138         //for debugging:
00139         //    painter->drawRect( drawArea.adjusted(4,4,-4,-4) );
00140         //    painter->drawRect( drawArea.adjusted(2,2,-2,-2) );
00141         //    painter->drawRect( drawArea );
00142     }
00143     d->bPaintIsRunning = false;
00144     //qDebug("done: plane::paint()");
00145 }
00146 
00147 
00148 void CartesianCoordinatePlane::slotLayoutChanged ( AbstractDiagram* )
00149 {
00150     // old: if ( d->initialResizeEventReceived )
00151     layoutDiagrams();
00152 }
00153 
00154 QRectF CartesianCoordinatePlane::getRawDataBoundingRectFromDiagrams() const
00155 {
00156     // determine unit of the rectangles of all involved diagrams:
00157     qreal minX, maxX, minY, maxY;
00158     bool bStarting = true;
00159     Q_FOREACH( const AbstractDiagram* diagram, diagrams() )
00160     {
00161         QPair<QPointF, QPointF> dataBoundariesPair = diagram->dataBoundaries();
00162         //qDebug() << "CartesianCoordinatePlane::getRawDataBoundingRectFromDiagrams()\ngets diagram->dataBoundaries: " << dataBoundariesPair.first << dataBoundariesPair.second;
00163         if ( bStarting || dataBoundariesPair.first.x()  < minX ) minX = dataBoundariesPair.first.x();
00164         if ( bStarting || dataBoundariesPair.first.y()  < minY ) minY = dataBoundariesPair.first.y();
00165         if ( bStarting || dataBoundariesPair.second.x() > maxX ) maxX = dataBoundariesPair.second.x();
00166         if ( bStarting || dataBoundariesPair.second.y() > maxY ) maxY = dataBoundariesPair.second.y();
00167         bStarting = false;
00168     }
00169     //qDebug() << "CartesianCoordinatePlane::getRawDataBoundingRectFromDiagrams()\nreturns data boundaries: " << QRectF( QPointF(minX, minY), QSizeF(maxX - minX, maxY - minY) );
00170     QRectF dataBoundingRect;
00171     dataBoundingRect.setBottomLeft( QPointF(minX, minY) );
00172     dataBoundingRect.setTopRight(   QPointF(maxX, maxY) );
00173     return dataBoundingRect;
00174 }
00175 
00176 
00177 QRectF CartesianCoordinatePlane::adjustedToMaxEmptyInnerPercentage(
00178         const QRectF& r, unsigned int percentX, unsigned int percentY ) const
00179 {
00180     QRectF erg( r );
00181     if( ( axesCalcModeX() != Logarithmic || r.left() < 0.0 ) && (percentX > 0) && (percentX != 100) ) {
00182         const bool isPositive = (r.left() >= 0);
00183         if( (r.right() >= 0) == isPositive ){
00184             const qreal innerBound =
00185                     isPositive ? qMin(r.left(), r.right()) : qMax(r.left(), r.right());
00186             const qreal outerBound =
00187                     isPositive ? qMax(r.left(), r.right()) : qMin(r.left(), r.right());
00188             if( innerBound / outerBound * 100 <= percentX )
00189             {
00190                 if( isPositive )
00191                     erg.setLeft( 0.0 );
00192                 else
00193                     erg.setRight( 0.0 );
00194             }
00195         }
00196     }
00197     if( ( axesCalcModeY() != Logarithmic || r.bottom() < 0.0 ) && (percentY > 0) && (percentY != 100) ) {
00198         //qDebug() << erg.bottom() << erg.top();
00199         const bool isPositive = (r.bottom() >= 0);
00200         if( (r.top() >= 0) == isPositive ){
00201             const qreal innerBound =
00202                     isPositive ? qMin(r.top(), r.bottom()) : qMax(r.top(), r.bottom());
00203             const qreal outerBound =
00204                     isPositive ? qMax(r.top(), r.bottom()) : qMin(r.top(), r.bottom());
00205             //qDebug() << innerBound << outerBound;
00206             if( innerBound / outerBound * 100 <= percentY )
00207             {
00208                 if( isPositive )
00209                     erg.setBottom( 0.0 );
00210                 else
00211                     erg.setTop( 0.0 );
00212             }
00213         }
00214         //qDebug() << erg.bottom() << erg.top() << "!!";
00215     }
00216     return erg;
00217 }
00218 
00219 
00220 QRectF CartesianCoordinatePlane::calculateRawDataBoundingRect() const
00221 {
00222     // are manually set ranges to be applied?
00223     const bool bAutoAdjustHorizontalRange = (d->autoAdjustHorizontalRangeToData < 100);
00224     const bool bAutoAdjustVerticalRange   = (d->autoAdjustVerticalRangeToData   < 100);
00225 
00226     const bool bHardHorizontalRange = (d->horizontalMin != d->horizontalMax) && ! bAutoAdjustHorizontalRange;
00227     const bool bHardVerticalRange   = (d->verticalMin   != d->verticalMax)   && ! bAutoAdjustVerticalRange;
00228     QRectF dataBoundingRect;
00229 
00230     // if custom boundaries are set on the plane, use them
00231     if ( bHardHorizontalRange && bHardVerticalRange ) {
00232         dataBoundingRect.setLeft(   d->horizontalMin );
00233         dataBoundingRect.setRight(  d->horizontalMax );
00234         dataBoundingRect.setBottom( d->verticalMin );
00235         dataBoundingRect.setTop(    d->verticalMax );
00236     }else{
00237         // determine unit of the rectangles of all involved diagrams:
00238         dataBoundingRect = getRawDataBoundingRectFromDiagrams();
00239         if ( bHardHorizontalRange ) {
00240             dataBoundingRect.setLeft(  d->horizontalMin );
00241             dataBoundingRect.setRight( d->horizontalMax );
00242         }
00243         if ( bHardVerticalRange ) {
00244             dataBoundingRect.setBottom( d->verticalMin );
00245             dataBoundingRect.setTop(    d->verticalMax );
00246         }
00247     }
00248     // recalculate the bounds, if automatic adjusting of ranges is desired AND
00249     //                         both bounds are at the same side of the zero line
00250     dataBoundingRect = adjustedToMaxEmptyInnerPercentage(
00251             dataBoundingRect, d->autoAdjustHorizontalRangeToData, d->autoAdjustVerticalRangeToData );
00252     if( bAutoAdjustHorizontalRange ){
00253         const_cast<CartesianCoordinatePlane::Private *>(d)->horizontalMin = dataBoundingRect.left();
00254         const_cast<CartesianCoordinatePlane::Private *>(d)->horizontalMax = dataBoundingRect.right();
00255     }
00256     if( bAutoAdjustVerticalRange ){
00257         const_cast<CartesianCoordinatePlane*>(this)->d->verticalMin = dataBoundingRect.bottom();
00258         const_cast<CartesianCoordinatePlane*>(this)->d->verticalMax = dataBoundingRect.top();
00259     }
00260     //qDebug() << "CartesianCoordinatePlane::calculateRawDataBoundingRect()\nreturns data boundaries: " << dataBoundingRect;
00261     return dataBoundingRect;
00262 }
00263 
00264 
00265 DataDimensionsList CartesianCoordinatePlane::getDataDimensionsList() const
00266 {
00267 
00268     DataDimensionsList l;
00269     const AbstractCartesianDiagram* dgr
00270         = diagrams().isEmpty() ? 0 : dynamic_cast<const AbstractCartesianDiagram*> (diagrams().first() );
00271     if( dgr && dgr->referenceDiagram() )
00272         dgr = dgr->referenceDiagram();
00273         const BarDiagram *barDiagram = qobject_cast< const BarDiagram* >( dgr );
00274 
00275         // note:
00276         // It does make sense to retrieve the orientation from the first diagram. This is because
00277         // a coordinate plane can either be for horizontal *or* for vertical diagrams. Both at the
00278         // same time won't work, and thus the orientation for all diagrams is the same as for the first one.
00279         const Qt::Orientation diagramOrientation = barDiagram != 0 ? barDiagram->orientation() : Qt::Vertical;
00280 
00281     const bool diagramIsVertical = diagramOrientation == Qt::Vertical;
00282 
00283 
00284     if( dgr ){
00285         const QRectF r( calculateRawDataBoundingRect() );
00286         // note:
00287         // We do *not* access d->gridAttributesHorizontal here, but
00288         // we use the getter function, to get the global attrs, if no
00289         // special ones have been set for the respective orientation.
00290         const GridAttributes gaH( gridAttributes( Qt::Horizontal ) );
00291         const GridAttributes gaV( gridAttributes( Qt::Vertical ) );
00292         // append the first dimension: for Abscissa axes
00293         l.append(
00294             DataDimension(
00295                 r.left(), r.right(),
00296                 diagramIsVertical ? ( dgr->datasetDimension() > 1 ) : true,
00297                 axesCalcModeX(),
00298                 gaH.gridGranularitySequence(),
00299                 gaH.gridStepWidth(),
00300                 gaH.gridSubStepWidth() ) );
00301         // append the second dimension: for Ordinate axes
00302         l.append(
00303             DataDimension(
00304                 r.bottom(), r.top(),
00305                 diagramIsVertical ? true : ( dgr->datasetDimension() > 1 ),
00306                 axesCalcModeY(),
00307                 gaV.gridGranularitySequence(),
00308                 gaV.gridStepWidth(),
00309                 gaV.gridSubStepWidth() ) );
00310     }else{
00311         l.append( DataDimension() ); // This gets us the default 1..0 / 1..0 grid
00312         l.append( DataDimension() ); // shown, if there is no diagram on this plane.
00313     }
00314     return l;
00315 }
00316 
00317 QRectF CartesianCoordinatePlane::drawingArea() const
00318 {
00319     // the rectangle the diagrams cover in the *plane*:
00320     // (Why -3? We save 1px on each side for the antialiased drawing, and
00321     // respect the way QPainter calculates the width of a painted rect (the
00322     // size is the rectangle size plus the pen width). This way, most clipping
00323     // for regular pens should be avoided. When pens with a penWidth or larger
00324     // than 1 are used, this may not be sufficient.
00325     const QRect rect( areaGeometry() );
00326     return QRectF ( rect.left()+1, rect.top()+1, rect.width() - 3, rect.height() - 3 );
00327 }
00328 
00329 
00330 QRectF CartesianCoordinatePlane::logicalArea() const
00331 {
00332     if ( d->dimensions.isEmpty() )
00333         return QRectF();
00334 
00335     const DataDimension dimX = d->dimensions.first();
00336     const DataDimension dimY = d->dimensions.last();
00337     const QPointF pt( qMin( dimX.start, dimX.end ), qMax( dimY.start, dimY.end ) );
00338     const QSizeF siz( qAbs( dimX.distance() ), -qAbs( dimY.distance() ) );
00339     const QRectF dataBoundingRect( pt, siz );
00340 
00341     // determine logical top left, taking the "reverse" option of
00342     // horizontal and vertical dimension into account
00343     QPointF topLeft;
00344     if( !d->reverseVerticalPlane && !d->reverseHorizontalPlane )
00345         topLeft = dataBoundingRect.topLeft();
00346     else if( d->reverseVerticalPlane && !d->reverseHorizontalPlane )
00347         topLeft = dataBoundingRect.bottomLeft();
00348     else if( d->reverseVerticalPlane && d->reverseHorizontalPlane )
00349         topLeft = dataBoundingRect.bottomRight();
00350     else if( !d->reverseVerticalPlane && d->reverseHorizontalPlane )
00351         topLeft = dataBoundingRect.topRight();
00352 
00353     const double width  = dataBoundingRect.width()  * ( d->reverseHorizontalPlane ? -1.0 : 1.0 );
00354     const double height = dataBoundingRect.height() * ( d->reverseVerticalPlane   ? -1.0 : 1.0 );
00355 
00356     return QRectF( topLeft, QSizeF( width, height ) );
00357 }
00358 
00359 QRectF CartesianCoordinatePlane::diagramArea() const
00360 {
00361     const QRectF logArea( logicalArea() );
00362     QPointF physicalTopLeft = d->coordinateTransformation.translate( logArea.topLeft() );
00363     QPointF physicalBottomRight = d->coordinateTransformation.translate( logArea.bottomRight() );
00364 
00365     return QRectF( physicalTopLeft, physicalBottomRight ).normalized();
00366 }
00367 
00368 QRectF CartesianCoordinatePlane::visibleDiagramArea() const
00369 {
00370     return diagramArea().intersected( drawingArea() );
00371 }
00372 
00373 void CartesianCoordinatePlane::layoutDiagrams()
00374 {
00375     if ( diagrams().isEmpty() )
00376     {   // FIXME evaluate what can still be prepared
00377         // FIXME decide default dimension if no diagrams are present (to make empty planes useable)
00378     }
00379 
00380     d->dimensions = gridDimensionsList();
00381     // test for programming errors: critical
00382     Q_ASSERT_X ( d->dimensions.count() == 2, "CartesianCoordinatePlane::layoutDiagrams",
00383                  "Error: gridDimensionsList() did not return exactly two dimensions." );
00384 
00385     // physical area of the plane
00386     const QRectF physicalArea( drawingArea() );
00387     // .. in contrast to the logical area
00388     const QRectF logArea( logicalArea() );
00389 
00390     d->coordinateTransformation.unitVectorX = logArea.width()  != 0 ? physicalArea.width()  / logArea.width()  : 1.0;
00391     d->coordinateTransformation.unitVectorY = logArea.height() != 0 ? physicalArea.height() / logArea.height() : 1.0;
00392 
00393     const double diagramXUnitInCoordinatePlane = d->coordinateTransformation.unitVectorX;
00394     const double diagramYUnitInCoordinatePlane = d->coordinateTransformation.unitVectorY;
00395 
00396     double scaleX;
00397     double scaleY;
00398 
00399     // calculate isometric scaling factor to maxscale the diagram into
00400     // the coordinate system:
00401     if ( d->isometricScaling )
00402     {
00403         double scale = qMin ( qAbs ( diagramXUnitInCoordinatePlane ),
00404                               qAbs ( diagramYUnitInCoordinatePlane ) );
00405 
00406         scaleX = qAbs( scale / diagramXUnitInCoordinatePlane );
00407         scaleY = qAbs( scale / diagramYUnitInCoordinatePlane );
00408     } else {
00409         scaleX = 1.0;
00410         scaleY = 1.0;
00411     }
00412 
00413     const QPointF logicalTopLeft = logArea.topLeft();
00414     // calculate diagram origin in plane coordinates:
00415     QPointF coordinateOrigin = QPointF ( logicalTopLeft.x() * -diagramXUnitInCoordinatePlane,
00416                                          logicalTopLeft.y() * -diagramYUnitInCoordinatePlane );
00417     coordinateOrigin += physicalArea.topLeft();
00418 
00419     d->coordinateTransformation.originTranslation = coordinateOrigin;
00420 
00421     // As in the first quadrant of the coordinate system, the origin is the bottom left, not top left.
00422     // This origin is then the top left point of the resulting diagramRect for our coordinateTransformation.
00423     const QRectF normalizedLogArea = logArea.normalized();
00424     d->coordinateTransformation.diagramRect = QRectF( normalizedLogArea.bottomLeft(), normalizedLogArea.topRight() );
00425 
00426     d->coordinateTransformation.isoScaleX = scaleX;
00427     d->coordinateTransformation.isoScaleY = scaleY;
00428 
00429     // the plane area might have changed, so the zoom values might also be changed
00430     handleFixedDataCoordinateSpaceRelation( physicalArea );
00431 
00432     update();
00433 }
00434 
00435 void CartesianCoordinatePlane::setFixedDataCoordinateSpaceRelation( bool fixed )
00436 {
00437     d->fixedDataCoordinateSpaceRelation = fixed;
00438     d->fixedDataCoordinateSpaceRelationOldSize = QRectF();
00439     /*
00440     //TODO(khz): We need to discuss if we want to do this:
00441     if( ! fixed ){
00442         bool bChanged = false;
00443         if( doneSetZoomFactorY( 1.0 ) )
00444             bChanged = true;
00445         if( doneSetZoomFactorX( 1.0 ) )
00446             bChanged = true;
00447         if( doneSetZoomCenter( QPointF(0.5, 0.5) ) )
00448             bChanged = true;
00449         if( bChanged ){
00450             emit propertiesChanged();
00451         }
00452     }
00453     */
00454 }
00455 
00456 bool CartesianCoordinatePlane::hasFixedDataCoordinateSpaceRelation() const
00457 {
00458     return d->fixedDataCoordinateSpaceRelation;
00459 }
00460 
00461 void CartesianCoordinatePlane::handleFixedDataCoordinateSpaceRelation( const QRectF& geometry )
00462 {
00463     // is the feature enabled?
00464     if( !d->fixedDataCoordinateSpaceRelation )
00465         return;
00466 
00467     // is the new geometry ok?
00468     if( geometry.height() < 1 || geometry.width() < 1 )
00469         return;
00470 
00471     // if the size was changed, we calculate new zoom settings
00472     if( d->fixedDataCoordinateSpaceRelationOldSize != geometry && !d->fixedDataCoordinateSpaceRelationOldSize.isNull() )
00473     {
00474         const double newZoomX = zoomFactorX() * d->fixedDataCoordinateSpaceRelationOldSize.width() / geometry.width();
00475         const double newZoomY = zoomFactorY() * d->fixedDataCoordinateSpaceRelationOldSize.height() / geometry.height();
00476 
00477         const QPointF oldCenter = zoomCenter();
00478         const QPointF newCenter = QPointF( oldCenter.x() * geometry.width() / d->fixedDataCoordinateSpaceRelationOldSize.width(),
00479                                            oldCenter.y() * geometry.height() / d->fixedDataCoordinateSpaceRelationOldSize.height() );
00480 
00481         // Use these internal methods to avoid sending
00482         // the propertiesChanged signal three times:
00483         bool bChanged = false;
00484         if( doneSetZoomFactorY( newZoomY ) )
00485             bChanged = true;
00486         if( doneSetZoomFactorX( newZoomX ) )
00487             bChanged = true;
00488         if( doneSetZoomCenter( newCenter ) )
00489             bChanged = true;
00490         if( bChanged ){
00491             emit propertiesChanged();
00492         }
00493     }
00494 
00495     d->fixedDataCoordinateSpaceRelationOldSize = geometry;
00496 }
00497 
00498 const QPointF CartesianCoordinatePlane::translate( const QPointF& diagramPoint ) const
00499 {
00500     // Note: We do not test if the point lays inside of the data area,
00501     //       but we just apply the transformation calculations to the point.
00502     //       This allows for basic calculations done by the user, see e.g.
00503     //       the file  examples/Lines/BubbleChart/mainwindow.cpp
00504     return  d->coordinateTransformation.translate ( diagramPoint );
00505 }
00506 
00507 const QPointF CartesianCoordinatePlane::translateBack( const QPointF& screenPoint ) const
00508 {
00509     return  d->coordinateTransformation.translateBack ( screenPoint );
00510 }
00511 
00512 void CartesianCoordinatePlane::setIsometricScaling ( bool onOff )
00513 {
00514     if ( d->isometricScaling != onOff )
00515     {
00516         d->isometricScaling = onOff;
00517         layoutDiagrams();
00518         emit propertiesChanged();
00519     }
00520 }
00521 
00522 bool CartesianCoordinatePlane::doesIsometricScaling () const
00523 {
00524     return d->isometricScaling;
00525 }
00526 
00527 bool CartesianCoordinatePlane::doneSetZoomFactorX( double factor )
00528 {
00529     const bool done = ( d->coordinateTransformation.zoom.xFactor != factor );
00530     if( done ){
00531         d->coordinateTransformation.zoom.xFactor = factor;
00532         if( d->autoAdjustGridToZoom )
00533             d->grid->setNeedRecalculate();
00534     }
00535     return done;
00536 }
00537 
00538 bool CartesianCoordinatePlane::doneSetZoomFactorY( double factor )
00539 {
00540     const bool done = ( d->coordinateTransformation.zoom.yFactor != factor );
00541     if( done ){
00542         d->coordinateTransformation.zoom.yFactor = factor;
00543         if( d->autoAdjustGridToZoom )
00544             d->grid->setNeedRecalculate();
00545     }
00546     return done;
00547 }
00548 
00549 bool CartesianCoordinatePlane::doneSetZoomCenter( const QPointF& point )
00550 {
00551     const bool done = ( d->coordinateTransformation.zoom.center() != point );
00552     if( done ){
00553         d->coordinateTransformation.zoom.setCenter( point );
00554         if( d->autoAdjustGridToZoom )
00555             d->grid->setNeedRecalculate();
00556     }
00557     return done;
00558 }
00559 
00560 void CartesianCoordinatePlane::setZoomFactors( double factorX, double factorY )
00561 {
00562     if( doneSetZoomFactorX( factorX ) || doneSetZoomFactorY( factorY ) ){
00563         emit propertiesChanged();
00564     }
00565 }
00566 
00567 void CartesianCoordinatePlane::setZoomFactorX( double factor )
00568 {
00569     if( doneSetZoomFactorX( factor ) ){
00570         emit propertiesChanged();
00571     }
00572 }
00573 
00574 void CartesianCoordinatePlane::setZoomFactorY( double factor )
00575 {
00576     if( doneSetZoomFactorY( factor ) ){
00577         emit propertiesChanged();
00578     }
00579 }
00580 
00581 void CartesianCoordinatePlane::setZoomCenter( const QPointF& point )
00582 {
00583     if( doneSetZoomCenter( point ) ){
00584         emit propertiesChanged();
00585     }
00586 }
00587 
00588 QPointF CartesianCoordinatePlane::zoomCenter() const
00589 {
00590     return d->coordinateTransformation.zoom.center();
00591 }
00592 
00593 double CartesianCoordinatePlane::zoomFactorX() const
00594 {
00595     return d->coordinateTransformation.zoom.xFactor;
00596 }
00597 
00598 double CartesianCoordinatePlane::zoomFactorY() const
00599 {
00600     return d->coordinateTransformation.zoom.yFactor;
00601 }
00602 
00603 
00604 CartesianCoordinatePlane::AxesCalcMode CartesianCoordinatePlane::axesCalcModeY() const
00605 {
00606     return d->coordinateTransformation.axesCalcModeY;
00607 }
00608 
00609 CartesianCoordinatePlane::AxesCalcMode CartesianCoordinatePlane::axesCalcModeX() const
00610 {
00611     return d->coordinateTransformation.axesCalcModeX;
00612 }
00613 
00614 void CartesianCoordinatePlane::setAxesCalcModes( AxesCalcMode mode )
00615 {
00616     if( d->coordinateTransformation.axesCalcModeY != mode ||
00617         d->coordinateTransformation.axesCalcModeX != mode ){
00618         d->coordinateTransformation.axesCalcModeY = mode;
00619         d->coordinateTransformation.axesCalcModeX = mode;
00620         emit propertiesChanged();
00621     }
00622 }
00623 
00624 void CartesianCoordinatePlane::setAxesCalcModeY( AxesCalcMode mode )
00625 {
00626     if( d->coordinateTransformation.axesCalcModeY != mode ){
00627         d->coordinateTransformation.axesCalcModeY = mode;
00628         emit propertiesChanged();
00629     }
00630 }
00631 
00632 void CartesianCoordinatePlane::setAxesCalcModeX( AxesCalcMode mode )
00633 {
00634     if( d->coordinateTransformation.axesCalcModeX != mode ){
00635         d->coordinateTransformation.axesCalcModeX = mode;
00636         emit propertiesChanged();
00637     }
00638 }
00639 
00640 void CartesianCoordinatePlane::setHorizontalRange( const QPair< qreal, qreal > & range )
00641 {
00642     if ( d->horizontalMin != range.first || d->horizontalMax != range.second ) {
00643         d->autoAdjustHorizontalRangeToData = 100;
00644         d->horizontalMin = range.first;
00645         d->horizontalMax = range.second;
00646         layoutDiagrams();
00647         emit propertiesChanged();
00648     }
00649 }
00650 
00651 void CartesianCoordinatePlane::setVerticalRange( const QPair< qreal, qreal > & range )
00652 {
00653 
00654     if ( d->verticalMin != range.first || d->verticalMax != range.second ) {
00655         d->autoAdjustVerticalRangeToData = 100;
00656         d->verticalMin = range.first;
00657         d->verticalMax = range.second;
00658         layoutDiagrams();
00659         emit propertiesChanged();
00660     }
00661 }
00662 
00663 QPair< qreal, qreal > CartesianCoordinatePlane::horizontalRange( ) const
00664 {
00665     return QPair<qreal, qreal>( d->horizontalMin, d->horizontalMax );
00666 }
00667 
00668 QPair< qreal, qreal > CartesianCoordinatePlane::verticalRange( ) const
00669 {
00670     return QPair<qreal, qreal>( d->verticalMin, d->verticalMax );
00671 }
00672 
00673 void CartesianCoordinatePlane::adjustRangesToData()
00674 {
00675     const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() );
00676     d->horizontalMin = dataBoundingRect.left();
00677     d->horizontalMax = dataBoundingRect.right();
00678     d->verticalMin = dataBoundingRect.top();
00679     d->verticalMax = dataBoundingRect.bottom();
00680     layoutDiagrams();
00681     emit propertiesChanged();
00682 }
00683 
00684 void CartesianCoordinatePlane::adjustHorizontalRangeToData()
00685 {
00686     const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() );
00687     d->horizontalMin = dataBoundingRect.left();
00688     d->horizontalMax = dataBoundingRect.right();
00689     layoutDiagrams();
00690     emit propertiesChanged();
00691 }
00692 
00693 void CartesianCoordinatePlane::adjustVerticalRangeToData()
00694 {
00695     const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() );
00696     d->verticalMin = dataBoundingRect.bottom();
00697     d->verticalMax = dataBoundingRect.top();
00698     layoutDiagrams();
00699     emit propertiesChanged();
00700 }
00701 
00702 void CartesianCoordinatePlane::setAutoAdjustHorizontalRangeToData( unsigned int percentEmpty )
00703 {
00704     d->autoAdjustHorizontalRangeToData = percentEmpty;
00705     d->horizontalMin = 0.0;
00706     d->horizontalMax = 0.0;
00707     layoutDiagrams();
00708     emit propertiesChanged();
00709 }
00710 
00711 void CartesianCoordinatePlane::setAutoAdjustVerticalRangeToData( unsigned int percentEmpty )
00712 {
00713     d->autoAdjustVerticalRangeToData = percentEmpty;
00714     d->verticalMin = 0.0;
00715     d->verticalMax = 0.0;
00716     layoutDiagrams();
00717     emit propertiesChanged();
00718 }
00719 
00720 unsigned int CartesianCoordinatePlane::autoAdjustHorizontalRangeToData() const
00721 {
00722     return d->autoAdjustHorizontalRangeToData;
00723 }
00724 
00725 unsigned int CartesianCoordinatePlane::autoAdjustVerticalRangeToData() const
00726 {
00727     return d->autoAdjustVerticalRangeToData;
00728 }
00729 
00730 void CartesianCoordinatePlane::setGridAttributes(
00731     Qt::Orientation orientation,
00732     const GridAttributes& a )
00733 {
00734     if( orientation == Qt::Horizontal )
00735         d->gridAttributesHorizontal = a;
00736     else
00737         d->gridAttributesVertical = a;
00738     setHasOwnGridAttributes( orientation, true );
00739     update();
00740     emit propertiesChanged();
00741 }
00742 
00743 void CartesianCoordinatePlane::resetGridAttributes(
00744     Qt::Orientation orientation )
00745 {
00746     setHasOwnGridAttributes( orientation, false );
00747     update();
00748 }
00749 
00750 const GridAttributes CartesianCoordinatePlane::gridAttributes(
00751     Qt::Orientation orientation ) const
00752 {
00753     if( hasOwnGridAttributes( orientation ) ){
00754         if( orientation == Qt::Horizontal )
00755             return d->gridAttributesHorizontal;
00756         else
00757             return d->gridAttributesVertical;
00758     }else{
00759         return globalGridAttributes();
00760     }
00761 }
00762 
00763 void CartesianCoordinatePlane::setHasOwnGridAttributes(
00764     Qt::Orientation orientation, bool on )
00765 {
00766     if( orientation == Qt::Horizontal )
00767         d->hasOwnGridAttributesHorizontal = on;
00768     else
00769         d->hasOwnGridAttributesVertical = on;
00770     emit propertiesChanged();
00771 }
00772 
00773 bool CartesianCoordinatePlane::hasOwnGridAttributes(
00774     Qt::Orientation orientation ) const
00775 {
00776     return
00777         ( orientation == Qt::Horizontal )
00778         ? d->hasOwnGridAttributesHorizontal
00779         : d->hasOwnGridAttributesVertical;
00780 }
00781 
00782 void CartesianCoordinatePlane::setAutoAdjustGridToZoom( bool autoAdjust )
00783 {
00784     if( d->autoAdjustGridToZoom != autoAdjust ){
00785         d->autoAdjustGridToZoom = autoAdjust;
00786         d->grid->setNeedRecalculate();
00787         emit propertiesChanged();
00788     }
00789 }
00790 
00791 #if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE)
00792 const
00793 #endif
00794 bool CartesianCoordinatePlane::autoAdjustGridToZoom() const
00795 {
00796     return d->autoAdjustGridToZoom;
00797 }
00798 
00799 AbstractCoordinatePlane* CartesianCoordinatePlane::sharedAxisMasterPlane( QPainter* painter )
00800 {
00801     CartesianCoordinatePlane* plane = this;
00802     AbstractCartesianDiagram* diag = dynamic_cast< AbstractCartesianDiagram* >( plane->diagram() );
00803     const CartesianAxis* sharedAxis = 0;
00804     if( diag != 0 )
00805     {
00806         const CartesianAxisList axes = diag->axes();
00807         KDAB_FOREACH( const CartesianAxis* a, axes )
00808         {
00809             CartesianCoordinatePlane* p = const_cast< CartesianCoordinatePlane* >(
00810                                               dynamic_cast< const CartesianCoordinatePlane* >( a->coordinatePlane() ) );
00811             if( p != 0 && p != this )
00812             {
00813                 plane = p;
00814                 sharedAxis = a;
00815             }
00816         }
00817     }
00818 
00819     if( plane == this || painter == 0 )
00820         return plane;
00821 
00822     const QPointF zero = QPointF( 0, 0 );
00823     const QPointF tenX = QPointF( 10, 0 );
00824     const QPointF tenY = QPointF( 0, 10 );
00825 
00826 
00827     if( sharedAxis->isOrdinate() )
00828     {
00829         painter->translate( translate( zero ).x(), 0.0 );
00830         const qreal factor = (translate( tenX ) - translate( zero ) ).x() / ( plane->translate( tenX ) - plane->translate( zero ) ).x();
00831         painter->scale( factor, 1.0 );
00832         painter->translate( -plane->translate( zero ).x(), 0.0 );
00833     }
00834     if( sharedAxis->isAbscissa() )
00835     {
00836         painter->translate( 0.0, translate( zero ).y() );
00837         const qreal factor = (translate( tenY ) - translate( zero ) ).y() / ( plane->translate( tenY ) - plane->translate( zero ) ).y();
00838         painter->scale( 1.0, factor );
00839         painter->translate( 0.0, -plane->translate( zero ).y() );
00840     }
00841 
00842 
00843     return plane;
00844 }
00845 
00846 void CartesianCoordinatePlane::setHorizontalRangeReversed( bool reverse )
00847 {
00848     if( d->reverseHorizontalPlane == reverse )
00849         return;
00850 
00851     d->reverseHorizontalPlane = reverse;
00852     layoutDiagrams();
00853     emit propertiesChanged();
00854 }
00855 
00856 bool CartesianCoordinatePlane::isHorizontalRangeReversed() const
00857 {
00858     return d->reverseHorizontalPlane;
00859 }
00860 
00861 void CartesianCoordinatePlane::setVerticalRangeReversed( bool reverse )
00862 {
00863     if( d->reverseVerticalPlane == reverse )
00864         return;
00865 
00866     d->reverseVerticalPlane = reverse;
00867     layoutDiagrams();
00868     emit propertiesChanged();
00869 }
00870 
00871 bool CartesianCoordinatePlane::isVerticalRangeReversed() const
00872 {
00873     return d->reverseVerticalPlane;
00874 }
00875 
00876 QRectF CartesianCoordinatePlane::visibleDataRange() const
00877 {
00878     QRectF result;
00879 
00880     const QRectF drawArea = drawingArea();
00881 
00882     result.setTopLeft( translateBack( drawArea.topLeft() ) );
00883     result.setBottomRight( translateBack( drawArea.bottomRight() ) );
00884 
00885     return result;
00886 }
00887 
00888 void CartesianCoordinatePlane::setGeometry( const QRect& rectangle )
00889 {
00890     if( rectangle == geometry() )
00891         return;
00892 
00893     AbstractCoordinatePlane::setGeometry( rectangle );
00894     Q_FOREACH( AbstractDiagram* diagram, diagrams() ) {
00895         diagram->resize( drawingArea().size() );
00896     }
00897 }

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