KDChartCartesianCoordinatePlane.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2011 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     // calculate diagram origin in plane coordinates:
00425     QPointF coordinateOrigin = QPointF ( logicalTopLeft.x() * -diagramXUnitInCoordinatePlane,
00426                                          logicalTopLeft.y() * -diagramYUnitInCoordinatePlane );
00427     coordinateOrigin += physicalArea.topLeft();
00428 
00429     d->coordinateTransformation.originTranslation = coordinateOrigin;
00430 
00431     // As in the first quadrant of the coordinate system, the origin is the bottom left, not top left.
00432     // This origin is then the top left point of the resulting diagramRect for our coordinateTransformation.
00433     const QRectF normalizedLogArea = logArea.normalized();
00434     d->coordinateTransformation.diagramRect = QRectF( normalizedLogArea.bottomLeft(), normalizedLogArea.topRight() );
00435 
00436     d->coordinateTransformation.isoScaleX = scaleX;
00437     d->coordinateTransformation.isoScaleY = scaleY;
00438 
00439     // the plane area might have changed, so the zoom values might also be changed
00440     handleFixedDataCoordinateSpaceRelation( physicalArea );
00441 
00442     update();
00443 }
00444 
00445 void CartesianCoordinatePlane::setFixedDataCoordinateSpaceRelation( bool fixed )
00446 {
00447     d->fixedDataCoordinateSpaceRelation = fixed;
00448     d->fixedDataCoordinateSpaceRelationOldSize = QRectF();
00449     /*
00450     //TODO(khz): We need to discuss if we want to do this:
00451     if( ! fixed ){
00452         bool bChanged = false;
00453         if( doneSetZoomFactorY( 1.0 ) )
00454             bChanged = true;
00455         if( doneSetZoomFactorX( 1.0 ) )
00456             bChanged = true;
00457         if( doneSetZoomCenter( QPointF(0.5, 0.5) ) )
00458             bChanged = true;
00459         if( bChanged ){
00460             emit propertiesChanged();
00461         }
00462     }
00463     */
00464 }
00465 
00466 bool CartesianCoordinatePlane::hasFixedDataCoordinateSpaceRelation() const
00467 {
00468     return d->fixedDataCoordinateSpaceRelation;
00469 }
00470 
00471 void CartesianCoordinatePlane::setXAxisStartAtZero(bool fixedStart)
00472 {
00473     if(d->xAxisStartAtZero == fixedStart)
00474         return;
00475 
00476     d->xAxisStartAtZero = fixedStart;
00477 }
00478 
00479 bool CartesianCoordinatePlane::xAxisStartAtZero() const
00480 {
00481     return d->xAxisStartAtZero;
00482 }
00483 
00484 void CartesianCoordinatePlane::handleFixedDataCoordinateSpaceRelation( const QRectF& geometry )
00485 {
00486     // is the feature enabled?
00487     if( !d->fixedDataCoordinateSpaceRelation )
00488         return;
00489 
00490     // is the new geometry ok?
00491     if( geometry.height() < 1 || geometry.width() < 1 )
00492         return;
00493 
00494     // if the size was changed, we calculate new zoom settings
00495     if( d->fixedDataCoordinateSpaceRelationOldSize != geometry && !d->fixedDataCoordinateSpaceRelationOldSize.isNull() )
00496     {
00497         const double newZoomX = zoomFactorX() * d->fixedDataCoordinateSpaceRelationOldSize.width() / geometry.width();
00498         const double newZoomY = zoomFactorY() * d->fixedDataCoordinateSpaceRelationOldSize.height() / geometry.height();
00499 
00500         const QPointF oldCenter = zoomCenter();
00501         const QPointF newCenter = QPointF( oldCenter.x() * geometry.width() / d->fixedDataCoordinateSpaceRelationOldSize.width(),
00502                                            oldCenter.y() * geometry.height() / d->fixedDataCoordinateSpaceRelationOldSize.height() );
00503 
00504         // Use these internal methods to avoid sending
00505         // the propertiesChanged signal three times:
00506         bool bChanged = false;
00507         if( doneSetZoomFactorY( newZoomY ) )
00508             bChanged = true;
00509         if( doneSetZoomFactorX( newZoomX ) )
00510             bChanged = true;
00511         if( doneSetZoomCenter( newCenter ) )
00512             bChanged = true;
00513         if( bChanged ){
00514             emit propertiesChanged();
00515         }
00516     }
00517 
00518     d->fixedDataCoordinateSpaceRelationOldSize = geometry;
00519 }
00520 
00521 const QPointF CartesianCoordinatePlane::translate( const QPointF& diagramPoint ) const
00522 {
00523     // Note: We do not test if the point lays inside of the data area,
00524     //       but we just apply the transformation calculations to the point.
00525     //       This allows for basic calculations done by the user, see e.g.
00526     //       the file  examples/Lines/BubbleChart/mainwindow.cpp
00527     return  d->coordinateTransformation.translate ( diagramPoint );
00528 }
00529 
00530 const QPointF CartesianCoordinatePlane::translateBack( const QPointF& screenPoint ) const
00531 {
00532     return  d->coordinateTransformation.translateBack ( screenPoint );
00533 }
00534 
00535 void CartesianCoordinatePlane::setIsometricScaling ( bool onOff )
00536 {
00537     if ( d->isometricScaling != onOff )
00538     {
00539         d->isometricScaling = onOff;
00540         layoutDiagrams();
00541         emit propertiesChanged();
00542     }
00543 }
00544 
00545 bool CartesianCoordinatePlane::doesIsometricScaling () const
00546 {
00547     return d->isometricScaling;
00548 }
00549 
00550 bool CartesianCoordinatePlane::doneSetZoomFactorX( double factor )
00551 {
00552     const bool done = ( d->coordinateTransformation.zoom.xFactor != factor );
00553     if( done ){
00554         d->coordinateTransformation.zoom.xFactor = factor;
00555         if( d->autoAdjustGridToZoom )
00556             d->grid->setNeedRecalculate();
00557     }
00558     return done;
00559 }
00560 
00561 bool CartesianCoordinatePlane::doneSetZoomFactorY( double factor )
00562 {
00563     const bool done = ( d->coordinateTransformation.zoom.yFactor != factor );
00564     if( done ){
00565         d->coordinateTransformation.zoom.yFactor = factor;
00566         if( d->autoAdjustGridToZoom )
00567             d->grid->setNeedRecalculate();
00568     }
00569     return done;
00570 }
00571 
00572 bool CartesianCoordinatePlane::doneSetZoomCenter( const QPointF& point )
00573 {
00574     const bool done = ( d->coordinateTransformation.zoom.center() != point );
00575     if( done ){
00576         d->coordinateTransformation.zoom.setCenter( point );
00577         if( d->autoAdjustGridToZoom )
00578             d->grid->setNeedRecalculate();
00579     }
00580     return done;
00581 }
00582 
00583 void CartesianCoordinatePlane::setZoomFactors( double factorX, double factorY )
00584 {
00585     if( doneSetZoomFactorX( factorX ) || doneSetZoomFactorY( factorY ) ){
00586         emit propertiesChanged();
00587     }
00588 }
00589 
00590 void CartesianCoordinatePlane::setZoomFactorX( double factor )
00591 {
00592     if( doneSetZoomFactorX( factor ) ){
00593         emit propertiesChanged();
00594     }
00595 }
00596 
00597 void CartesianCoordinatePlane::setZoomFactorY( double factor )
00598 {
00599     if( doneSetZoomFactorY( factor ) ){
00600         emit propertiesChanged();
00601     }
00602 }
00603 
00604 void CartesianCoordinatePlane::setZoomCenter( const QPointF& point )
00605 {
00606     if( doneSetZoomCenter( point ) ){
00607         emit propertiesChanged();
00608     }
00609 }
00610 
00611 QPointF CartesianCoordinatePlane::zoomCenter() const
00612 {
00613     return d->coordinateTransformation.zoom.center();
00614 }
00615 
00616 double CartesianCoordinatePlane::zoomFactorX() const
00617 {
00618     return d->coordinateTransformation.zoom.xFactor;
00619 }
00620 
00621 double CartesianCoordinatePlane::zoomFactorY() const
00622 {
00623     return d->coordinateTransformation.zoom.yFactor;
00624 }
00625 
00626 
00627 CartesianCoordinatePlane::AxesCalcMode CartesianCoordinatePlane::axesCalcModeY() const
00628 {
00629     return d->coordinateTransformation.axesCalcModeY;
00630 }
00631 
00632 CartesianCoordinatePlane::AxesCalcMode CartesianCoordinatePlane::axesCalcModeX() const
00633 {
00634     return d->coordinateTransformation.axesCalcModeX;
00635 }
00636 
00637 void CartesianCoordinatePlane::setAxesCalcModes( AxesCalcMode mode )
00638 {
00639     if( d->coordinateTransformation.axesCalcModeY != mode ||
00640         d->coordinateTransformation.axesCalcModeX != mode ){
00641         d->coordinateTransformation.axesCalcModeY = mode;
00642         d->coordinateTransformation.axesCalcModeX = mode;
00643         emit propertiesChanged();
00644         emit viewportCoordinateSystemChanged();
00645         Q_FOREACH( AbstractDiagram* diag, diagrams() )
00646         slotLayoutChanged( diag );
00647     }
00648 }
00649 
00650 void CartesianCoordinatePlane::setAxesCalcModeY( AxesCalcMode mode )
00651 {
00652     if( d->coordinateTransformation.axesCalcModeY != mode ){
00653         d->coordinateTransformation.axesCalcModeY = mode;
00654         emit propertiesChanged();
00655         setGridNeedsRecalculate();
00656         emit viewportCoordinateSystemChanged();
00657     }
00658 }
00659 
00660 void CartesianCoordinatePlane::setAxesCalcModeX( AxesCalcMode mode )
00661 {
00662     if( d->coordinateTransformation.axesCalcModeX != mode ){
00663         d->coordinateTransformation.axesCalcModeX = mode;
00664         emit propertiesChanged();
00665         emit viewportCoordinateSystemChanged();
00666     }
00667 }
00668 
00669 void CartesianCoordinatePlane::setHorizontalRange( const QPair< qreal, qreal > & range )
00670 {
00671     if ( d->horizontalMin != range.first || d->horizontalMax != range.second ) {
00672         d->autoAdjustHorizontalRangeToData = 100;
00673         d->horizontalMin = range.first;
00674         d->horizontalMax = range.second;
00675         layoutDiagrams();
00676         emit propertiesChanged();
00677     }
00678 }
00679 
00680 void CartesianCoordinatePlane::setVerticalRange( const QPair< qreal, qreal > & range )
00681 {
00682 
00683     if ( d->verticalMin != range.first || d->verticalMax != range.second ) {
00684         d->autoAdjustVerticalRangeToData = 100;
00685         d->verticalMin = range.first;
00686         d->verticalMax = range.second;
00687         layoutDiagrams();
00688         emit propertiesChanged();
00689     }
00690 }
00691 
00692 QPair< qreal, qreal > CartesianCoordinatePlane::horizontalRange( ) const
00693 {
00694     return QPair<qreal, qreal>( d->horizontalMin, d->horizontalMax );
00695 }
00696 
00697 QPair< qreal, qreal > CartesianCoordinatePlane::verticalRange( ) const
00698 {
00699     return QPair<qreal, qreal>( d->verticalMin, d->verticalMax );
00700 }
00701 
00702 void CartesianCoordinatePlane::adjustRangesToData()
00703 {
00704     const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() );
00705     d->horizontalMin = dataBoundingRect.left();
00706     d->horizontalMax = dataBoundingRect.right();
00707     d->verticalMin = dataBoundingRect.top();
00708     d->verticalMax = dataBoundingRect.bottom();
00709     layoutDiagrams();
00710     emit propertiesChanged();
00711 }
00712 
00713 void CartesianCoordinatePlane::adjustHorizontalRangeToData()
00714 {
00715     const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() );
00716     d->horizontalMin = dataBoundingRect.left();
00717     d->horizontalMax = dataBoundingRect.right();
00718     layoutDiagrams();
00719     emit propertiesChanged();
00720 }
00721 
00722 void CartesianCoordinatePlane::adjustVerticalRangeToData()
00723 {
00724     const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() );
00725     d->verticalMin = dataBoundingRect.bottom();
00726     d->verticalMax = dataBoundingRect.top();
00727     layoutDiagrams();
00728     emit propertiesChanged();
00729 }
00730 
00731 void CartesianCoordinatePlane::setAutoAdjustHorizontalRangeToData( unsigned int percentEmpty )
00732 {
00733     if ( d->autoAdjustHorizontalRangeToData != percentEmpty )
00734     {
00735         d->autoAdjustHorizontalRangeToData = percentEmpty;
00736         d->horizontalMin = 0.0;
00737         d->horizontalMax = 0.0;
00738         layoutDiagrams();
00739         emit propertiesChanged();
00740     }
00741 }
00742 
00743 void CartesianCoordinatePlane::setAutoAdjustVerticalRangeToData( unsigned int percentEmpty )
00744 {
00745     if ( d->autoAdjustVerticalRangeToData != percentEmpty )
00746     {
00747         d->autoAdjustVerticalRangeToData = percentEmpty;
00748         d->verticalMin = 0.0;
00749         d->verticalMax = 0.0;
00750         layoutDiagrams();
00751         emit propertiesChanged();
00752     }
00753 }
00754 
00755 unsigned int CartesianCoordinatePlane::autoAdjustHorizontalRangeToData() const
00756 {
00757     return d->autoAdjustHorizontalRangeToData;
00758 }
00759 
00760 unsigned int CartesianCoordinatePlane::autoAdjustVerticalRangeToData() const
00761 {
00762     return d->autoAdjustVerticalRangeToData;
00763 }
00764 
00765 void CartesianCoordinatePlane::setGridAttributes(
00766     Qt::Orientation orientation,
00767     const GridAttributes& a )
00768 {
00769     if( orientation == Qt::Horizontal )
00770         d->gridAttributesHorizontal = a;
00771     else
00772         d->gridAttributesVertical = a;
00773     setHasOwnGridAttributes( orientation, true );
00774     update();
00775     emit propertiesChanged();
00776 }
00777 
00778 void CartesianCoordinatePlane::resetGridAttributes(
00779     Qt::Orientation orientation )
00780 {
00781     setHasOwnGridAttributes( orientation, false );
00782     update();
00783 }
00784 
00785 const GridAttributes CartesianCoordinatePlane::gridAttributes(
00786     Qt::Orientation orientation ) const
00787 {
00788     if( hasOwnGridAttributes( orientation ) ){
00789         if( orientation == Qt::Horizontal )
00790             return d->gridAttributesHorizontal;
00791         else
00792             return d->gridAttributesVertical;
00793     }else{
00794         return globalGridAttributes();
00795     }
00796 }
00797 
00798 void CartesianCoordinatePlane::setHasOwnGridAttributes(
00799     Qt::Orientation orientation, bool on )
00800 {
00801     if( orientation == Qt::Horizontal )
00802         d->hasOwnGridAttributesHorizontal = on;
00803     else
00804         d->hasOwnGridAttributesVertical = on;
00805     emit propertiesChanged();
00806 }
00807 
00808 bool CartesianCoordinatePlane::hasOwnGridAttributes(
00809     Qt::Orientation orientation ) const
00810 {
00811     return
00812         ( orientation == Qt::Horizontal )
00813         ? d->hasOwnGridAttributesHorizontal
00814         : d->hasOwnGridAttributesVertical;
00815 }
00816 
00817 void CartesianCoordinatePlane::setAutoAdjustGridToZoom( bool autoAdjust )
00818 {
00819     if( d->autoAdjustGridToZoom != autoAdjust ){
00820         d->autoAdjustGridToZoom = autoAdjust;
00821         d->grid->setNeedRecalculate();
00822         emit propertiesChanged();
00823     }
00824 }
00825 
00826 #if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE)
00827 const
00828 #endif
00829 bool CartesianCoordinatePlane::autoAdjustGridToZoom() const
00830 {
00831     return d->autoAdjustGridToZoom;
00832 }
00833 
00834 AbstractCoordinatePlane* CartesianCoordinatePlane::sharedAxisMasterPlane( QPainter* painter )
00835 {
00836     CartesianCoordinatePlane* plane = this;
00837     AbstractCartesianDiagram* diag = dynamic_cast< AbstractCartesianDiagram* >( plane->diagram() );
00838     const CartesianAxis* sharedAxis = 0;
00839     if( diag != 0 )
00840     {
00841         const CartesianAxisList axes = diag->axes();
00842         KDAB_FOREACH( const CartesianAxis* a, axes )
00843         {
00844             CartesianCoordinatePlane* p = const_cast< CartesianCoordinatePlane* >(
00845                                               dynamic_cast< const CartesianCoordinatePlane* >( a->coordinatePlane() ) );
00846             if( p != 0 && p != this )
00847             {
00848                 plane = p;
00849                 sharedAxis = a;
00850             }
00851         }
00852     }
00853 
00854     if( plane == this || painter == 0 )
00855         return plane;
00856 
00857     const QPointF zero = QPointF( 0, 0 );
00858     const QPointF tenX = QPointF( 10, 0 );
00859     const QPointF tenY = QPointF( 0, 10 );
00860 
00861 
00862     if( sharedAxis->isOrdinate() )
00863     {
00864         painter->translate( translate( zero ).x(), 0.0 );
00865         const qreal factor = (translate( tenX ) - translate( zero ) ).x() / ( plane->translate( tenX ) - plane->translate( zero ) ).x();
00866         painter->scale( factor, 1.0 );
00867         painter->translate( -plane->translate( zero ).x(), 0.0 );
00868     }
00869     if( sharedAxis->isAbscissa() )
00870     {
00871         painter->translate( 0.0, translate( zero ).y() );
00872         const qreal factor = (translate( tenY ) - translate( zero ) ).y() / ( plane->translate( tenY ) - plane->translate( zero ) ).y();
00873         painter->scale( 1.0, factor );
00874         painter->translate( 0.0, -plane->translate( zero ).y() );
00875     }
00876 
00877 
00878     return plane;
00879 }
00880 
00881 void CartesianCoordinatePlane::setHorizontalRangeReversed( bool reverse )
00882 {
00883     if( d->reverseHorizontalPlane == reverse )
00884         return;
00885 
00886     d->reverseHorizontalPlane = reverse;
00887     layoutDiagrams();
00888     emit propertiesChanged();
00889 }
00890 
00891 bool CartesianCoordinatePlane::isHorizontalRangeReversed() const
00892 {
00893     return d->reverseHorizontalPlane;
00894 }
00895 
00896 void CartesianCoordinatePlane::setVerticalRangeReversed( bool reverse )
00897 {
00898     if( d->reverseVerticalPlane == reverse )
00899         return;
00900 
00901     d->reverseVerticalPlane = reverse;
00902     layoutDiagrams();
00903     emit propertiesChanged();
00904 }
00905 
00906 bool CartesianCoordinatePlane::isVerticalRangeReversed() const
00907 {
00908     return d->reverseVerticalPlane;
00909 }
00910 
00911 QRectF CartesianCoordinatePlane::visibleDataRange() const
00912 {
00913     QRectF result;
00914 
00915     const QRectF drawArea = drawingArea();
00916 
00917     result.setTopLeft( translateBack( drawArea.topLeft() ) );
00918     result.setBottomRight( translateBack( drawArea.bottomRight() ) );
00919 
00920     return result;
00921 }
00922 
00923 void CartesianCoordinatePlane::setGeometry( const QRect& rectangle )
00924 {
00925     if( rectangle == geometry() )
00926         return;
00927 
00928     AbstractCoordinatePlane::setGeometry( rectangle );
00929     Q_FOREACH( AbstractDiagram* diagram, diagrams() ) {
00930         diagram->resize( drawingArea().size() );
00931     }
00932 }
 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/