KissCount/lib/libkdchart/src/KDChartCartesianCoordinatePlane.cpp

925 lines
31 KiB
C++
Raw Normal View History

2012-01-28 15:54:17 +01:00
/****************************************************************************
** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved.
**
** This file is part of the KD Chart library.
**
** Licensees holding valid commercial KD Chart licenses may use this file in
** accordance with the KD Chart Commercial License Agreement provided with
** the Software.
**
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 and version 3 as published by the
** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** Contact info@kdab.com if any conditions of this licensing are not
** clear to you.
**
**********************************************************************/
#include "KDChartCartesianCoordinatePlane.h"
#include "KDChartCartesianCoordinatePlane_p.h"
#include <QFont>
#include <QList>
#include <QtDebug>
#include <QPainter>
#include <QApplication>
#include "KDChartAbstractDiagram.h"
#include "KDChartAbstractCartesianDiagram.h"
#include "CartesianCoordinateTransformation.h"
#include "KDChartGridAttributes.h"
#include "KDChartPaintContext.h"
#include "KDChartPainterSaver_p.h"
#include "KDChartBarDiagram.h"
#include <KDABLibFakes>
using namespace KDChart;
#define d d_func()
CartesianCoordinatePlane::Private::Private()
: AbstractCoordinatePlane::Private()
, bPaintIsRunning( false )
, hasOwnGridAttributesHorizontal ( false )
, hasOwnGridAttributesVertical ( false )
// old: , initialResizeEventReceived ( false )
, isometricScaling ( false )
, horizontalMin(0)
, horizontalMax(0)
, verticalMin(0)
, verticalMax(0)
, autoAdjustHorizontalRangeToData(67)
, autoAdjustVerticalRangeToData( 67)
, autoAdjustGridToZoom( true )
, fixedDataCoordinateSpaceRelation( false )
, xAxisStartAtZero(true)
, reverseVerticalPlane( false )
, reverseHorizontalPlane( false )
{
}
CartesianCoordinatePlane::CartesianCoordinatePlane ( Chart* parent )
: AbstractCoordinatePlane ( new Private(), parent )
{
// this bloc left empty intentionally
}
CartesianCoordinatePlane::~CartesianCoordinatePlane()
{
// this bloc left empty intentionally
}
void CartesianCoordinatePlane::init()
{
// this bloc left empty intentionally
}
void CartesianCoordinatePlane::addDiagram ( AbstractDiagram* diagram )
{
Q_ASSERT_X ( dynamic_cast<AbstractCartesianDiagram*> ( diagram ),
"CartesianCoordinatePlane::addDiagram", "Only cartesian "
"diagrams can be added to a cartesian coordinate plane!" );
AbstractCoordinatePlane::addDiagram ( diagram );
connect ( diagram, SIGNAL ( layoutChanged ( AbstractDiagram* ) ),
SLOT ( slotLayoutChanged ( AbstractDiagram* ) ) );
connect( diagram, SIGNAL( propertiesChanged() ),this, SIGNAL( propertiesChanged() ) );
}
void CartesianCoordinatePlane::paint ( QPainter* painter )
{
// prevent recursive call:
//qDebug("attempt plane::paint()");
if( d->bPaintIsRunning ){
return;
}
d->bPaintIsRunning = true;
//qDebug() << "start plane::paint()";
AbstractDiagramList diags = diagrams();
if ( !diags.isEmpty() )
{
PaintContext ctx;
ctx.setPainter ( painter );
ctx.setCoordinatePlane ( this );
const QRectF drawArea( drawingArea() );
ctx.setRectangle ( drawArea );
// enabling clipping so that we're not drawing outside
PainterSaver painterSaver( painter );
QRect clipRect = drawArea.toRect().adjusted( -1, -1, 1, 1 );
QRegion clipRegion( clipRect );
painter->setClipRegion( clipRegion );
// paint the coordinate system rulers:
d->grid->drawGrid( &ctx );
// paint the diagrams:
for ( int i = 0; i < diags.size(); i++ )
{
if ( diags[i]->isHidden() ) {
continue;
}
//qDebug(" start diags[i]->paint ( &ctx );");
PainterSaver diagramPainterSaver( painter );
diags[i]->paint ( &ctx );
//qDebug(" done: diags[i]->paint ( &ctx );");
}
//for debugging:
// painter->drawRect( drawArea.adjusted(4,4,-4,-4) );
// painter->drawRect( drawArea.adjusted(2,2,-2,-2) );
// painter->drawRect( drawArea );
}
d->bPaintIsRunning = false;
//qDebug("done: plane::paint()");
}
void CartesianCoordinatePlane::slotLayoutChanged ( AbstractDiagram* )
{
// old: if ( d->initialResizeEventReceived )
layoutDiagrams();
}
QRectF CartesianCoordinatePlane::getRawDataBoundingRectFromDiagrams() const
{
// determine unit of the rectangles of all involved diagrams:
qreal minX = 0;
qreal maxX = 0;
qreal minY = 0;
qreal maxY = 0
;
bool bStarting = true;
Q_FOREACH( const AbstractDiagram* diagram, diagrams() )
{
QPair<QPointF, QPointF> dataBoundariesPair = diagram->dataBoundaries();
//qDebug() << "CartesianCoordinatePlane::getRawDataBoundingRectFromDiagrams()\ngets diagram->dataBoundaries: " << dataBoundariesPair.first << dataBoundariesPair.second;
if ( bStarting || dataBoundariesPair.first.x() < minX ) minX = dataBoundariesPair.first.x();
if ( bStarting || dataBoundariesPair.first.y() < minY ) minY = dataBoundariesPair.first.y();
if ( bStarting || dataBoundariesPair.second.x() > maxX ) maxX = dataBoundariesPair.second.x();
if ( bStarting || dataBoundariesPair.second.y() > maxY ) maxY = dataBoundariesPair.second.y();
bStarting = false;
}
//qDebug() << "CartesianCoordinatePlane::getRawDataBoundingRectFromDiagrams()\nreturns data boundaries: " << QRectF( QPointF(minX, minY), QSizeF(maxX - minX, maxY - minY) );
QRectF dataBoundingRect;
dataBoundingRect.setBottomLeft( QPointF(minX, minY) );
dataBoundingRect.setTopRight( QPointF(maxX, maxY) );
return dataBoundingRect;
}
QRectF CartesianCoordinatePlane::adjustedToMaxEmptyInnerPercentage(
const QRectF& r, unsigned int percentX, unsigned int percentY ) const
{
QRectF erg( r );
if( ( axesCalcModeX() != Logarithmic || r.left() < 0.0 ) && (percentX > 0) && (percentX != 100) ) {
const bool isPositive = (r.left() >= 0);
if( (r.right() >= 0) == isPositive ){
const qreal innerBound =
isPositive ? qMin(r.left(), r.right()) : qMax(r.left(), r.right());
const qreal outerBound =
isPositive ? qMax(r.left(), r.right()) : qMin(r.left(), r.right());
if( innerBound / outerBound * 100 <= percentX )
{
if(d->xAxisStartAtZero)
{
if( isPositive )
erg.setLeft( 0.0 );
else
erg.setRight( 0.0 );
}
}
}
}
if( ( axesCalcModeY() != Logarithmic || r.bottom() < 0.0 ) && (percentY > 0) && (percentY != 100) ) {
//qDebug() << erg.bottom() << erg.top();
const bool isPositive = (r.bottom() >= 0);
if( (r.top() >= 0) == isPositive ){
const qreal innerBound =
isPositive ? qMin(r.top(), r.bottom()) : qMax(r.top(), r.bottom());
const qreal outerBound =
isPositive ? qMax(r.top(), r.bottom()) : qMin(r.top(), r.bottom());
//qDebug() << innerBound << outerBound;
if( innerBound / outerBound * 100 <= percentY )
{
if( isPositive )
erg.setBottom( 0.0 );
else
erg.setTop( 0.0 );
}
}
//qDebug() << erg.bottom() << erg.top() << "!!";
}
return erg;
}
QRectF CartesianCoordinatePlane::calculateRawDataBoundingRect() const
{
// are manually set ranges to be applied?
const bool bAutoAdjustHorizontalRange = (d->autoAdjustHorizontalRangeToData < 100);
const bool bAutoAdjustVerticalRange = (d->autoAdjustVerticalRangeToData < 100);
const bool bHardHorizontalRange = (d->horizontalMin != d->horizontalMax) && ! bAutoAdjustHorizontalRange;
const bool bHardVerticalRange = (d->verticalMin != d->verticalMax) && ! bAutoAdjustVerticalRange;
QRectF dataBoundingRect;
// if custom boundaries are set on the plane, use them
if ( bHardHorizontalRange && bHardVerticalRange ) {
dataBoundingRect.setLeft( d->horizontalMin );
dataBoundingRect.setRight( d->horizontalMax );
dataBoundingRect.setBottom( d->verticalMin );
dataBoundingRect.setTop( d->verticalMax );
}else{
// determine unit of the rectangles of all involved diagrams:
dataBoundingRect = getRawDataBoundingRectFromDiagrams();
if ( bHardHorizontalRange ) {
dataBoundingRect.setLeft( d->horizontalMin );
dataBoundingRect.setRight( d->horizontalMax );
}
if ( bHardVerticalRange ) {
dataBoundingRect.setBottom( d->verticalMin );
dataBoundingRect.setTop( d->verticalMax );
}
}
// recalculate the bounds, if automatic adjusting of ranges is desired AND
// both bounds are at the same side of the zero line
dataBoundingRect = adjustedToMaxEmptyInnerPercentage(
dataBoundingRect, d->autoAdjustHorizontalRangeToData, d->autoAdjustVerticalRangeToData );
if( bAutoAdjustHorizontalRange ){
const_cast<CartesianCoordinatePlane::Private *>(d)->horizontalMin = dataBoundingRect.left();
const_cast<CartesianCoordinatePlane::Private *>(d)->horizontalMax = dataBoundingRect.right();
}
if( bAutoAdjustVerticalRange ){
const_cast<CartesianCoordinatePlane*>(this)->d->verticalMin = dataBoundingRect.bottom();
const_cast<CartesianCoordinatePlane*>(this)->d->verticalMax = dataBoundingRect.top();
}
//qDebug() << "CartesianCoordinatePlane::calculateRawDataBoundingRect()\nreturns data boundaries: " << dataBoundingRect;
return dataBoundingRect;
}
DataDimensionsList CartesianCoordinatePlane::getDataDimensionsList() const
{
DataDimensionsList l;
const AbstractCartesianDiagram* dgr
= diagrams().isEmpty() ? 0 : dynamic_cast<const AbstractCartesianDiagram*> (diagrams().first() );
if( dgr && dgr->referenceDiagram() )
dgr = dgr->referenceDiagram();
const BarDiagram *barDiagram = qobject_cast< const BarDiagram* >( dgr );
// note:
// It does make sense to retrieve the orientation from the first diagram. This is because
// a coordinate plane can either be for horizontal *or* for vertical diagrams. Both at the
// same time won't work, and thus the orientation for all diagrams is the same as for the first one.
const Qt::Orientation diagramOrientation = barDiagram != 0 ? barDiagram->orientation() : Qt::Vertical;
const bool diagramIsVertical = diagramOrientation == Qt::Vertical;
if( dgr ){
const QRectF r( calculateRawDataBoundingRect() );
// note:
// We do *not* access d->gridAttributesHorizontal here, but
// we use the getter function, to get the global attrs, if no
// special ones have been set for the respective orientation.
const GridAttributes gaH( gridAttributes( Qt::Horizontal ) );
const GridAttributes gaV( gridAttributes( Qt::Vertical ) );
// append the first dimension: for Abscissa axes
l.append(
DataDimension(
r.left(), r.right(),
diagramIsVertical ? ( dgr->datasetDimension() > 1 ) : true,
axesCalcModeX(),
gaH.gridGranularitySequence(),
gaH.gridStepWidth(),
gaH.gridSubStepWidth() ) );
// append the second dimension: for Ordinate axes
l.append(
DataDimension(
r.bottom(), r.top(),
diagramIsVertical ? true : ( dgr->datasetDimension() > 1 ),
axesCalcModeY(),
gaV.gridGranularitySequence(),
gaV.gridStepWidth(),
gaV.gridSubStepWidth() ) );
}else{
l.append( DataDimension() ); // This gets us the default 1..0 / 1..0 grid
l.append( DataDimension() ); // shown, if there is no diagram on this plane.
}
return l;
}
QRectF CartesianCoordinatePlane::drawingArea() const
{
// the rectangle the diagrams cover in the *plane*:
// (Why -3? We save 1px on each side for the antialiased drawing, and
// respect the way QPainter calculates the width of a painted rect (the
// size is the rectangle size plus the pen width). This way, most clipping
// for regular pens should be avoided. When pens with a penWidth or larger
// than 1 are used, this may not be sufficient.
const QRect rect( areaGeometry() );
return QRectF ( rect.left()+1, rect.top()+1, rect.width() - 3, rect.height() - 3 );
}
QRectF CartesianCoordinatePlane::logicalArea() const
{
if ( d->dimensions.isEmpty() )
return QRectF();
const DataDimension dimX = d->dimensions.first();
const DataDimension dimY = d->dimensions.last();
const QPointF pt( qMin( dimX.start, dimX.end ), qMax( dimY.start, dimY.end ) );
const QSizeF siz( qAbs( dimX.distance() ), -qAbs( dimY.distance() ) );
const QRectF dataBoundingRect( pt, siz );
// determine logical top left, taking the "reverse" option of
// horizontal and vertical dimension into account
QPointF topLeft;
if( !d->reverseVerticalPlane && !d->reverseHorizontalPlane )
topLeft = dataBoundingRect.topLeft();
else if( d->reverseVerticalPlane && !d->reverseHorizontalPlane )
topLeft = dataBoundingRect.bottomLeft();
else if( d->reverseVerticalPlane && d->reverseHorizontalPlane )
topLeft = dataBoundingRect.bottomRight();
else if( !d->reverseVerticalPlane && d->reverseHorizontalPlane )
topLeft = dataBoundingRect.topRight();
const double width = dataBoundingRect.width() * ( d->reverseHorizontalPlane ? -1.0 : 1.0 );
const double height = dataBoundingRect.height() * ( d->reverseVerticalPlane ? -1.0 : 1.0 );
return QRectF( topLeft, QSizeF( width, height ) );
}
QRectF CartesianCoordinatePlane::diagramArea() const
{
const QRectF logArea( logicalArea() );
QPointF physicalTopLeft = d->coordinateTransformation.translate( logArea.topLeft() );
QPointF physicalBottomRight = d->coordinateTransformation.translate( logArea.bottomRight() );
return QRectF( physicalTopLeft, physicalBottomRight ).normalized();
}
QRectF CartesianCoordinatePlane::visibleDiagramArea() const
{
return diagramArea().intersected( drawingArea() );
}
void CartesianCoordinatePlane::layoutDiagrams()
{
if ( diagrams().isEmpty() )
{ // FIXME evaluate what can still be prepared
// FIXME decide default dimension if no diagrams are present (to make empty planes useable)
}
d->dimensions = gridDimensionsList();
// test for programming errors: critical
Q_ASSERT_X ( d->dimensions.count() == 2, "CartesianCoordinatePlane::layoutDiagrams",
"Error: gridDimensionsList() did not return exactly two dimensions." );
// physical area of the plane
const QRectF physicalArea( drawingArea() );
// .. in contrast to the logical area
const QRectF logArea( logicalArea() );
d->coordinateTransformation.unitVectorX = logArea.width() != 0 ? physicalArea.width() / logArea.width() : 1.0;
d->coordinateTransformation.unitVectorY = logArea.height() != 0 ? physicalArea.height() / logArea.height() : 1.0;
const double diagramXUnitInCoordinatePlane = d->coordinateTransformation.unitVectorX;
const double diagramYUnitInCoordinatePlane = d->coordinateTransformation.unitVectorY;
double scaleX;
double scaleY;
// calculate isometric scaling factor to maxscale the diagram into
// the coordinate system:
if ( d->isometricScaling )
{
double scale = qMin ( qAbs ( diagramXUnitInCoordinatePlane ),
qAbs ( diagramYUnitInCoordinatePlane ) );
scaleX = qAbs( scale / diagramXUnitInCoordinatePlane );
scaleY = qAbs( scale / diagramYUnitInCoordinatePlane );
} else {
scaleX = 1.0;
scaleY = 1.0;
}
const QPointF logicalTopLeft = logArea.topLeft();
// calculate diagram origin in plane coordinates:
QPointF coordinateOrigin = QPointF ( logicalTopLeft.x() * -diagramXUnitInCoordinatePlane,
logicalTopLeft.y() * -diagramYUnitInCoordinatePlane );
coordinateOrigin += physicalArea.topLeft();
d->coordinateTransformation.originTranslation = coordinateOrigin;
// As in the first quadrant of the coordinate system, the origin is the bottom left, not top left.
// This origin is then the top left point of the resulting diagramRect for our coordinateTransformation.
const QRectF normalizedLogArea = logArea.normalized();
d->coordinateTransformation.diagramRect = QRectF( normalizedLogArea.bottomLeft(), normalizedLogArea.topRight() );
d->coordinateTransformation.isoScaleX = scaleX;
d->coordinateTransformation.isoScaleY = scaleY;
// the plane area might have changed, so the zoom values might also be changed
handleFixedDataCoordinateSpaceRelation( physicalArea );
update();
}
void CartesianCoordinatePlane::setFixedDataCoordinateSpaceRelation( bool fixed )
{
d->fixedDataCoordinateSpaceRelation = fixed;
d->fixedDataCoordinateSpaceRelationOldSize = QRectF();
/*
//TODO(khz): We need to discuss if we want to do this:
if( ! fixed ){
bool bChanged = false;
if( doneSetZoomFactorY( 1.0 ) )
bChanged = true;
if( doneSetZoomFactorX( 1.0 ) )
bChanged = true;
if( doneSetZoomCenter( QPointF(0.5, 0.5) ) )
bChanged = true;
if( bChanged ){
emit propertiesChanged();
}
}
*/
}
bool CartesianCoordinatePlane::hasFixedDataCoordinateSpaceRelation() const
{
return d->fixedDataCoordinateSpaceRelation;
}
void CartesianCoordinatePlane::setXAxisStartAtZero(bool fixedStart)
{
if(d->xAxisStartAtZero == fixedStart)
return;
d->xAxisStartAtZero = fixedStart;
}
bool CartesianCoordinatePlane::xAxisStartAtZero() const
{
return d->xAxisStartAtZero;
}
void CartesianCoordinatePlane::handleFixedDataCoordinateSpaceRelation( const QRectF& geometry )
{
// is the feature enabled?
if( !d->fixedDataCoordinateSpaceRelation )
return;
// is the new geometry ok?
if( geometry.height() < 1 || geometry.width() < 1 )
return;
// if the size was changed, we calculate new zoom settings
if( d->fixedDataCoordinateSpaceRelationOldSize != geometry && !d->fixedDataCoordinateSpaceRelationOldSize.isNull() )
{
const double newZoomX = zoomFactorX() * d->fixedDataCoordinateSpaceRelationOldSize.width() / geometry.width();
const double newZoomY = zoomFactorY() * d->fixedDataCoordinateSpaceRelationOldSize.height() / geometry.height();
const QPointF oldCenter = zoomCenter();
const QPointF newCenter = QPointF( oldCenter.x() * geometry.width() / d->fixedDataCoordinateSpaceRelationOldSize.width(),
oldCenter.y() * geometry.height() / d->fixedDataCoordinateSpaceRelationOldSize.height() );
// Use these internal methods to avoid sending
// the propertiesChanged signal three times:
bool bChanged = false;
if( doneSetZoomFactorY( newZoomY ) )
bChanged = true;
if( doneSetZoomFactorX( newZoomX ) )
bChanged = true;
if( doneSetZoomCenter( newCenter ) )
bChanged = true;
if( bChanged ){
emit propertiesChanged();
}
}
d->fixedDataCoordinateSpaceRelationOldSize = geometry;
}
const QPointF CartesianCoordinatePlane::translate( const QPointF& diagramPoint ) const
{
// Note: We do not test if the point lays inside of the data area,
// but we just apply the transformation calculations to the point.
// This allows for basic calculations done by the user, see e.g.
// the file examples/Lines/BubbleChart/mainwindow.cpp
return d->coordinateTransformation.translate ( diagramPoint );
}
const QPointF CartesianCoordinatePlane::translateBack( const QPointF& screenPoint ) const
{
return d->coordinateTransformation.translateBack ( screenPoint );
}
void CartesianCoordinatePlane::setIsometricScaling ( bool onOff )
{
if ( d->isometricScaling != onOff )
{
d->isometricScaling = onOff;
layoutDiagrams();
emit propertiesChanged();
}
}
bool CartesianCoordinatePlane::doesIsometricScaling () const
{
return d->isometricScaling;
}
bool CartesianCoordinatePlane::doneSetZoomFactorX( double factor )
{
const bool done = ( d->coordinateTransformation.zoom.xFactor != factor );
if( done ){
d->coordinateTransformation.zoom.xFactor = factor;
if( d->autoAdjustGridToZoom )
d->grid->setNeedRecalculate();
}
return done;
}
bool CartesianCoordinatePlane::doneSetZoomFactorY( double factor )
{
const bool done = ( d->coordinateTransformation.zoom.yFactor != factor );
if( done ){
d->coordinateTransformation.zoom.yFactor = factor;
if( d->autoAdjustGridToZoom )
d->grid->setNeedRecalculate();
}
return done;
}
bool CartesianCoordinatePlane::doneSetZoomCenter( const QPointF& point )
{
const bool done = ( d->coordinateTransformation.zoom.center() != point );
if( done ){
d->coordinateTransformation.zoom.setCenter( point );
if( d->autoAdjustGridToZoom )
d->grid->setNeedRecalculate();
}
return done;
}
void CartesianCoordinatePlane::setZoomFactors( double factorX, double factorY )
{
if( doneSetZoomFactorX( factorX ) || doneSetZoomFactorY( factorY ) ){
emit propertiesChanged();
}
}
void CartesianCoordinatePlane::setZoomFactorX( double factor )
{
if( doneSetZoomFactorX( factor ) ){
emit propertiesChanged();
}
}
void CartesianCoordinatePlane::setZoomFactorY( double factor )
{
if( doneSetZoomFactorY( factor ) ){
emit propertiesChanged();
}
}
void CartesianCoordinatePlane::setZoomCenter( const QPointF& point )
{
if( doneSetZoomCenter( point ) ){
emit propertiesChanged();
}
}
QPointF CartesianCoordinatePlane::zoomCenter() const
{
return d->coordinateTransformation.zoom.center();
}
double CartesianCoordinatePlane::zoomFactorX() const
{
return d->coordinateTransformation.zoom.xFactor;
}
double CartesianCoordinatePlane::zoomFactorY() const
{
return d->coordinateTransformation.zoom.yFactor;
}
CartesianCoordinatePlane::AxesCalcMode CartesianCoordinatePlane::axesCalcModeY() const
{
return d->coordinateTransformation.axesCalcModeY;
}
CartesianCoordinatePlane::AxesCalcMode CartesianCoordinatePlane::axesCalcModeX() const
{
return d->coordinateTransformation.axesCalcModeX;
}
void CartesianCoordinatePlane::setAxesCalcModes( AxesCalcMode mode )
{
if( d->coordinateTransformation.axesCalcModeY != mode ||
d->coordinateTransformation.axesCalcModeX != mode ){
d->coordinateTransformation.axesCalcModeY = mode;
d->coordinateTransformation.axesCalcModeX = mode;
emit propertiesChanged();
}
}
void CartesianCoordinatePlane::setAxesCalcModeY( AxesCalcMode mode )
{
if( d->coordinateTransformation.axesCalcModeY != mode ){
d->coordinateTransformation.axesCalcModeY = mode;
emit propertiesChanged();
}
}
void CartesianCoordinatePlane::setAxesCalcModeX( AxesCalcMode mode )
{
if( d->coordinateTransformation.axesCalcModeX != mode ){
d->coordinateTransformation.axesCalcModeX = mode;
emit propertiesChanged();
}
}
void CartesianCoordinatePlane::setHorizontalRange( const QPair< qreal, qreal > & range )
{
if ( d->horizontalMin != range.first || d->horizontalMax != range.second ) {
d->autoAdjustHorizontalRangeToData = 100;
d->horizontalMin = range.first;
d->horizontalMax = range.second;
layoutDiagrams();
emit propertiesChanged();
}
}
void CartesianCoordinatePlane::setVerticalRange( const QPair< qreal, qreal > & range )
{
if ( d->verticalMin != range.first || d->verticalMax != range.second ) {
d->autoAdjustVerticalRangeToData = 100;
d->verticalMin = range.first;
d->verticalMax = range.second;
layoutDiagrams();
emit propertiesChanged();
}
}
QPair< qreal, qreal > CartesianCoordinatePlane::horizontalRange( ) const
{
return QPair<qreal, qreal>( d->horizontalMin, d->horizontalMax );
}
QPair< qreal, qreal > CartesianCoordinatePlane::verticalRange( ) const
{
return QPair<qreal, qreal>( d->verticalMin, d->verticalMax );
}
void CartesianCoordinatePlane::adjustRangesToData()
{
const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() );
d->horizontalMin = dataBoundingRect.left();
d->horizontalMax = dataBoundingRect.right();
d->verticalMin = dataBoundingRect.top();
d->verticalMax = dataBoundingRect.bottom();
layoutDiagrams();
emit propertiesChanged();
}
void CartesianCoordinatePlane::adjustHorizontalRangeToData()
{
const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() );
d->horizontalMin = dataBoundingRect.left();
d->horizontalMax = dataBoundingRect.right();
layoutDiagrams();
emit propertiesChanged();
}
void CartesianCoordinatePlane::adjustVerticalRangeToData()
{
const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() );
d->verticalMin = dataBoundingRect.bottom();
d->verticalMax = dataBoundingRect.top();
layoutDiagrams();
emit propertiesChanged();
}
void CartesianCoordinatePlane::setAutoAdjustHorizontalRangeToData( unsigned int percentEmpty )
{
if ( d->autoAdjustHorizontalRangeToData != percentEmpty )
{
d->autoAdjustHorizontalRangeToData = percentEmpty;
d->horizontalMin = 0.0;
d->horizontalMax = 0.0;
layoutDiagrams();
emit propertiesChanged();
}
}
void CartesianCoordinatePlane::setAutoAdjustVerticalRangeToData( unsigned int percentEmpty )
{
if ( d->autoAdjustVerticalRangeToData != percentEmpty )
{
d->autoAdjustVerticalRangeToData = percentEmpty;
d->verticalMin = 0.0;
d->verticalMax = 0.0;
layoutDiagrams();
emit propertiesChanged();
}
}
unsigned int CartesianCoordinatePlane::autoAdjustHorizontalRangeToData() const
{
return d->autoAdjustHorizontalRangeToData;
}
unsigned int CartesianCoordinatePlane::autoAdjustVerticalRangeToData() const
{
return d->autoAdjustVerticalRangeToData;
}
void CartesianCoordinatePlane::setGridAttributes(
Qt::Orientation orientation,
const GridAttributes& a )
{
if( orientation == Qt::Horizontal )
d->gridAttributesHorizontal = a;
else
d->gridAttributesVertical = a;
setHasOwnGridAttributes( orientation, true );
update();
emit propertiesChanged();
}
void CartesianCoordinatePlane::resetGridAttributes(
Qt::Orientation orientation )
{
setHasOwnGridAttributes( orientation, false );
update();
}
const GridAttributes CartesianCoordinatePlane::gridAttributes(
Qt::Orientation orientation ) const
{
if( hasOwnGridAttributes( orientation ) ){
if( orientation == Qt::Horizontal )
return d->gridAttributesHorizontal;
else
return d->gridAttributesVertical;
}else{
return globalGridAttributes();
}
}
void CartesianCoordinatePlane::setHasOwnGridAttributes(
Qt::Orientation orientation, bool on )
{
if( orientation == Qt::Horizontal )
d->hasOwnGridAttributesHorizontal = on;
else
d->hasOwnGridAttributesVertical = on;
emit propertiesChanged();
}
bool CartesianCoordinatePlane::hasOwnGridAttributes(
Qt::Orientation orientation ) const
{
return
( orientation == Qt::Horizontal )
? d->hasOwnGridAttributesHorizontal
: d->hasOwnGridAttributesVertical;
}
void CartesianCoordinatePlane::setAutoAdjustGridToZoom( bool autoAdjust )
{
if( d->autoAdjustGridToZoom != autoAdjust ){
d->autoAdjustGridToZoom = autoAdjust;
d->grid->setNeedRecalculate();
emit propertiesChanged();
}
}
#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE)
const
#endif
bool CartesianCoordinatePlane::autoAdjustGridToZoom() const
{
return d->autoAdjustGridToZoom;
}
AbstractCoordinatePlane* CartesianCoordinatePlane::sharedAxisMasterPlane( QPainter* painter )
{
CartesianCoordinatePlane* plane = this;
AbstractCartesianDiagram* diag = dynamic_cast< AbstractCartesianDiagram* >( plane->diagram() );
const CartesianAxis* sharedAxis = 0;
if( diag != 0 )
{
const CartesianAxisList axes = diag->axes();
KDAB_FOREACH( const CartesianAxis* a, axes )
{
CartesianCoordinatePlane* p = const_cast< CartesianCoordinatePlane* >(
dynamic_cast< const CartesianCoordinatePlane* >( a->coordinatePlane() ) );
if( p != 0 && p != this )
{
plane = p;
sharedAxis = a;
}
}
}
if( plane == this || painter == 0 )
return plane;
const QPointF zero = QPointF( 0, 0 );
const QPointF tenX = QPointF( 10, 0 );
const QPointF tenY = QPointF( 0, 10 );
if( sharedAxis->isOrdinate() )
{
painter->translate( translate( zero ).x(), 0.0 );
const qreal factor = (translate( tenX ) - translate( zero ) ).x() / ( plane->translate( tenX ) - plane->translate( zero ) ).x();
painter->scale( factor, 1.0 );
painter->translate( -plane->translate( zero ).x(), 0.0 );
}
if( sharedAxis->isAbscissa() )
{
painter->translate( 0.0, translate( zero ).y() );
const qreal factor = (translate( tenY ) - translate( zero ) ).y() / ( plane->translate( tenY ) - plane->translate( zero ) ).y();
painter->scale( 1.0, factor );
painter->translate( 0.0, -plane->translate( zero ).y() );
}
return plane;
}
void CartesianCoordinatePlane::setHorizontalRangeReversed( bool reverse )
{
if( d->reverseHorizontalPlane == reverse )
return;
d->reverseHorizontalPlane = reverse;
layoutDiagrams();
emit propertiesChanged();
}
bool CartesianCoordinatePlane::isHorizontalRangeReversed() const
{
return d->reverseHorizontalPlane;
}
void CartesianCoordinatePlane::setVerticalRangeReversed( bool reverse )
{
if( d->reverseVerticalPlane == reverse )
return;
d->reverseVerticalPlane = reverse;
layoutDiagrams();
emit propertiesChanged();
}
bool CartesianCoordinatePlane::isVerticalRangeReversed() const
{
return d->reverseVerticalPlane;
}
QRectF CartesianCoordinatePlane::visibleDataRange() const
{
QRectF result;
const QRectF drawArea = drawingArea();
result.setTopLeft( translateBack( drawArea.topLeft() ) );
result.setBottomRight( translateBack( drawArea.bottomRight() ) );
return result;
}
void CartesianCoordinatePlane::setGeometry( const QRect& rectangle )
{
if( rectangle == geometry() )
return;
AbstractCoordinatePlane::setGeometry( rectangle );
Q_FOREACH( AbstractDiagram* diagram, diagrams() ) {
diagram->resize( drawingArea().size() );
}
}