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 }

Generated on Mon Sep 17 16:16:50 2007 for KD Chart 2 by  doxygen 1.5.1