KD Chart 2 [rev.2.4]

KDChartCartesianCoordinatePlane.cpp

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

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
http://www.kdab.com/
http://www.kdab.com/products/kd-chart/