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