24 #include "KDChartCartesianCoordinatePlane_p.h"
27 #include "KDChartAbstractDiagram_p.h"
29 #include "CartesianCoordinateTransformation.h"
32 #include "KDChartPainterSaver_p.h"
36 #include <KDABLibFakes>
38 #include <QApplication>
46 using namespace KDChart;
50 CartesianCoordinatePlane::Private::Private()
52 , bPaintIsRunning( false )
53 , hasOwnGridAttributesHorizontal( false )
54 , hasOwnGridAttributesVertical( false )
55 , isometricScaling( false )
60 , autoAdjustHorizontalRangeToData( 67 )
61 , autoAdjustVerticalRangeToData( 67 )
62 , autoAdjustGridToZoom( true )
63 , fixedDataCoordinateSpaceRelation( false )
64 , xAxisStartAtZero( true )
65 , reverseVerticalPlane( false )
66 , reverseHorizontalPlane( false )
70 CartesianCoordinatePlane::CartesianCoordinatePlane (
Chart* parent )
81 void CartesianCoordinatePlane::init()
89 Q_ASSERT_X ( dynamic_cast<AbstractCartesianDiagram*> ( diagram ),
90 "CartesianCoordinatePlane::addDiagram",
"Only cartesian "
91 "diagrams can be added to a cartesian coordinate plane!" );
103 if (
d->bPaintIsRunning ) {
106 d->bPaintIsRunning =
true;
109 if ( !diags.isEmpty() )
118 PainterSaver painterSaver( painter );
119 QRect clipRect = drawArea.toRect().adjusted( -1, -1, 1, 1 );
120 QRegion clipRegion( clipRect );
121 painter->setClipRegion( clipRegion );
124 d->grid->drawGrid( &ctx );
127 for (
int i = 0; i < diags.size(); i++ )
129 if ( diags[i]->isHidden() ) {
132 bool doDumpPaintTime = AbstractDiagram::Private::get( diags[ i ] )->doDumpPaintTime;
134 if ( doDumpPaintTime ) {
138 PainterSaver diagramPainterSaver( painter );
139 diags[i]->paint ( &ctx );
141 if ( doDumpPaintTime ) {
142 qDebug() <<
"Painting diagram" << i <<
"took" << stopWatch.elapsed() <<
"milliseconds";
147 d->bPaintIsRunning =
false;
163 bool bStarting =
true;
168 if ( bStarting || dataBoundariesPair.first.x() < minX ) minX = dataBoundariesPair.first.x();
169 if ( bStarting || dataBoundariesPair.first.y() < minY ) minY = dataBoundariesPair.first.y();
170 if ( bStarting || dataBoundariesPair.second.x() > maxX ) maxX = dataBoundariesPair.second.x();
171 if ( bStarting || dataBoundariesPair.second.y() > maxY ) maxY = dataBoundariesPair.second.y();
175 QRectF dataBoundingRect;
176 dataBoundingRect.setBottomLeft( QPointF( minX, minY ) );
177 dataBoundingRect.setTopRight( QPointF( maxX, maxY ) );
178 return dataBoundingRect;
183 const QRectF& r,
unsigned int percentX,
unsigned int percentY )
const
187 const bool isPositive = r.left() >= 0;
188 if ( ( r.right() >= 0 ) == isPositive ) {
189 qreal upperBound = qMax( r.left(), r.right() );
190 qreal lowerBound = qMin( r.left(), r.right() );
191 qreal innerBound = isPositive ? lowerBound : upperBound;
192 qreal outerBound = isPositive ? upperBound : lowerBound;
193 if ( innerBound / outerBound * 100 <= percentX &&
d->xAxisStartAtZero ) {
204 const bool isPositive = r.bottom() >= 0;
205 if ( ( r.top() >= 0 ) == isPositive ) {
206 qreal upperBound = qMax( r.top(), r.bottom() );
207 qreal lowerBound = qMin( r.top(), r.bottom() );
208 const qreal innerBound = isPositive ? lowerBound : upperBound;
209 const qreal outerBound = isPositive ? upperBound : lowerBound;
210 if ( innerBound / outerBound * 100 <= percentY ) {
212 ret.setBottom( 0.0 );
226 const bool bAutoAdjustHorizontalRange =
d->autoAdjustHorizontalRangeToData < 100;
227 const bool bAutoAdjustVerticalRange =
d->autoAdjustVerticalRangeToData < 100;
229 const bool bHardHorizontalRange =
d->horizontalMin !=
d->horizontalMax && !bAutoAdjustHorizontalRange;
230 const bool bHardVerticalRange =
d->verticalMin !=
d->verticalMax && !bAutoAdjustVerticalRange;
231 QRectF dataBoundingRect;
234 if ( bHardHorizontalRange && bHardVerticalRange ) {
235 dataBoundingRect.setLeft(
d->horizontalMin );
236 dataBoundingRect.setRight(
d->horizontalMax );
237 dataBoundingRect.setBottom(
d->verticalMin );
238 dataBoundingRect.setTop(
d->verticalMax );
242 if ( bHardHorizontalRange ) {
243 dataBoundingRect.setLeft(
d->horizontalMin );
244 dataBoundingRect.setRight(
d->horizontalMax );
246 if ( bHardVerticalRange ) {
247 dataBoundingRect.setBottom(
d->verticalMin );
248 dataBoundingRect.setTop(
d->verticalMax );
254 dataBoundingRect,
d->autoAdjustHorizontalRangeToData,
d->autoAdjustVerticalRangeToData );
255 if ( bAutoAdjustHorizontalRange ) {
259 if ( bAutoAdjustVerticalRange ) {
264 return dataBoundingRect;
282 const Qt::Orientation diagramOrientation = barDiagram != 0 ? barDiagram->
orientation() : Qt::Vertical;
283 const bool diagramIsVertical = diagramOrientation == Qt::Vertical;
296 diagramIsVertical ? ( !stockDiagram && dgr->
datasetDimension() > 1 ) :
true,
325 return QRectF(
areaGeometry() ).adjusted( 1.0, 1.0, -2.0, -2.0 );
331 if (
d->dimensions.isEmpty() )
336 const QPointF pt( qMin( dimX.
start, dimX.
end ), qMax( dimY.
start, dimY.
end ) );
338 const QRectF dataBoundingRect( pt, siz );
341 const QPointF topLeft(
d->reverseHorizontalPlane ? dataBoundingRect.right() : dataBoundingRect.left(),
342 d->reverseVerticalPlane ? dataBoundingRect.bottom() : dataBoundingRect.top() );
344 const qreal width = dataBoundingRect.width() * (
d->reverseHorizontalPlane ? -1.0 : 1.0 );
345 const qreal height = dataBoundingRect.height() * (
d->reverseVerticalPlane ? -1.0 : 1.0 );
347 return QRectF( topLeft, QSizeF( width, height ) );
353 QPointF physicalTopLeft =
d->coordinateTransformation.translate( logArea.topLeft() );
354 QPointF physicalBottomRight =
d->coordinateTransformation.translate( logArea.bottomRight() );
356 return QRectF( physicalTopLeft, physicalBottomRight ).normalized();
367 Q_ASSERT_X (
d->dimensions.count() == 2,
"CartesianCoordinatePlane::layoutDiagrams",
368 "Error: gridDimensionsList() did not return exactly two dimensions." );
380 d->coordinateTransformation.updateTransform( logArea, physicalArea );
387 d->fixedDataCoordinateSpaceRelation = fixed;
388 d->fixedDataCoordinateSpaceRelationPinnedSize = QSize();
394 return d->fixedDataCoordinateSpaceRelation;
399 if (
d->xAxisStartAtZero == fixedStart)
402 d->xAxisStartAtZero = fixedStart;
407 return d->xAxisStartAtZero;
412 if ( !
d->fixedDataCoordinateSpaceRelation ) {
416 if ( !geometry.isValid() ) {
423 if ( !
d->fixedDataCoordinateSpaceRelationPinnedSize.isValid() ) {
424 d->fixedDataCoordinateSpaceRelationPinnedSize = geometry.size();
430 if (
d->fixedDataCoordinateSpaceRelationPinnedSize != geometry.size() ) {
431 const qreal widthScaling =
d->fixedDataCoordinateSpaceRelationPinnedSize.width() / geometry.width();
432 const qreal heightScaling =
d->fixedDataCoordinateSpaceRelationPinnedSize.height() / geometry.height();
434 const qreal newZoomX =
d->fixedDataCoordinateSpaceRelationPinnedZoom.xFactor * widthScaling;
435 const qreal newZoomY =
d->fixedDataCoordinateSpaceRelationPinnedZoom.yFactor * heightScaling;
437 const QPointF newCenter = QPointF(
d->fixedDataCoordinateSpaceRelationPinnedZoom.xCenter / widthScaling,
438 d->fixedDataCoordinateSpaceRelationPinnedZoom.yCenter / heightScaling );
440 bool changed =
false;
458 return d->coordinateTransformation.translate( diagramPoint );
463 return d->coordinateTransformation.translateBack( screenPoint );
468 if (
d->isometricScaling != isOn ) {
469 d->isometricScaling = isOn;
477 return d->isometricScaling;
482 if (
d->coordinateTransformation.zoom.xFactor == factor ) {
485 d->coordinateTransformation.zoom.xFactor = factor;
486 if (
d->autoAdjustGridToZoom ) {
487 d->grid->setNeedRecalculate();
494 if (
d->coordinateTransformation.zoom.yFactor == factor ) {
497 d->coordinateTransformation.zoom.yFactor = factor;
498 if (
d->autoAdjustGridToZoom ) {
499 d->grid->setNeedRecalculate();
506 if (
d->coordinateTransformation.zoom.center() == point ) {
509 d->coordinateTransformation.zoom.setCenter( point );
510 if (
d->autoAdjustGridToZoom ) {
511 d->grid->setNeedRecalculate();
550 return d->coordinateTransformation.zoom.center();
555 return d->coordinateTransformation.zoom.xFactor;
560 return d->coordinateTransformation.zoom.yFactor;
566 return d->coordinateTransformation.axesCalcModeY;
571 return d->coordinateTransformation.axesCalcModeX;
576 if (
d->coordinateTransformation.axesCalcModeY != mode ||
577 d->coordinateTransformation.axesCalcModeX != mode ) {
578 d->coordinateTransformation.axesCalcModeY = mode;
579 d->coordinateTransformation.axesCalcModeX = mode;
589 if (
d->coordinateTransformation.axesCalcModeY != mode ) {
590 d->coordinateTransformation.axesCalcModeY = mode;
599 if (
d->coordinateTransformation.axesCalcModeX != mode ) {
600 d->coordinateTransformation.axesCalcModeX = mode;
608 if (
d->horizontalMin != range.first ||
d->horizontalMax != range.second ) {
609 d->autoAdjustHorizontalRangeToData = 100;
610 d->horizontalMin = range.first;
611 d->horizontalMax = range.second;
621 if (
d->verticalMin != range.first ||
d->verticalMax != range.second ) {
622 d->autoAdjustVerticalRangeToData = 100;
623 d->verticalMin = range.first;
624 d->verticalMax = range.second;
646 d->horizontalMin = dataBoundingRect.left();
647 d->horizontalMax = dataBoundingRect.right();
648 d->verticalMin = dataBoundingRect.top();
649 d->verticalMax = dataBoundingRect.bottom();
657 d->horizontalMin = dataBoundingRect.left();
658 d->horizontalMax = dataBoundingRect.right();
666 d->verticalMin = dataBoundingRect.bottom();
667 d->verticalMax = dataBoundingRect.top();
674 if (
d->autoAdjustHorizontalRangeToData != percentEmpty )
676 d->autoAdjustHorizontalRangeToData = percentEmpty;
677 d->horizontalMin = 0.0;
678 d->horizontalMax = 0.0;
686 if (
d->autoAdjustVerticalRangeToData != percentEmpty )
688 d->autoAdjustVerticalRangeToData = percentEmpty;
689 d->verticalMin = 0.0;
690 d->verticalMax = 0.0;
698 return d->autoAdjustHorizontalRangeToData;
703 return d->autoAdjustVerticalRangeToData;
707 Qt::Orientation orientation,
710 if ( orientation == Qt::Horizontal )
711 d->gridAttributesHorizontal = a;
713 d->gridAttributesVertical = a;
714 setHasOwnGridAttributes( orientation,
true );
720 Qt::Orientation orientation )
722 setHasOwnGridAttributes( orientation,
false );
727 Qt::Orientation orientation )
const
730 if ( orientation == Qt::Horizontal )
731 return d->gridAttributesHorizontal;
733 return d->gridAttributesVertical;
739 void CartesianCoordinatePlane::setHasOwnGridAttributes(
740 Qt::Orientation orientation,
bool on )
742 if ( orientation == Qt::Horizontal )
743 d->hasOwnGridAttributesHorizontal = on;
745 d->hasOwnGridAttributesVertical = on;
750 Qt::Orientation orientation )
const
753 ( orientation == Qt::Horizontal )
754 ?
d->hasOwnGridAttributesHorizontal
755 :
d->hasOwnGridAttributesVertical;
760 if (
d->autoAdjustGridToZoom != autoAdjust ) {
761 d->autoAdjustGridToZoom = autoAdjust;
762 d->grid->setNeedRecalculate();
767 #if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE)
772 return d->autoAdjustGridToZoom;
787 if ( p != 0 && p !=
this )
795 if ( plane ==
this || painter == 0 )
798 const QPointF zero = QPointF( 0, 0 );
799 const QPointF tenX = QPointF( 10, 0 );
800 const QPointF tenY = QPointF( 0, 10 );
805 painter->translate(
translate( zero ).x(), 0.0 );
807 painter->scale( factor, 1.0 );
808 painter->translate( -plane->
translate( zero ).x(), 0.0 );
812 painter->translate( 0.0,
translate( zero ).y() );
814 painter->scale( 1.0, factor );
815 painter->translate( 0.0, -plane->
translate( zero ).y() );
824 if (
d->reverseHorizontalPlane == reverse )
827 d->reverseHorizontalPlane = reverse;
834 return d->reverseHorizontalPlane;
839 if (
d->reverseVerticalPlane == reverse )
842 d->reverseVerticalPlane = reverse;
849 return d->reverseVerticalPlane;
859 result.setBottomRight(
translateBack( drawArea.bottomRight() ) );
870 d->geometry = rectangle;
871 if (
d->isometricScaling ) {
875 if ( hfw < rectangle.height() ) {
876 d->geometry.setHeight( hfw );
878 d->geometry.setWidth( qRound( qreal( rectangle.width() ) *
879 qreal( rectangle.height() ) / qreal( hfw ) ) );
886 diagram->
resize(
d->geometry.size() );
893 return d->isometricScaling ? Qt::Horizontal : ( Qt::Horizontal | Qt::Vertical );
898 return d->isometricScaling;
907 return qRound( qreal( w ) * qAbs( qreal( dataRect.height() ) / qreal( dataRect.width() ) ) );
913 if (
d->isometricScaling ) {
915 sh =
d->geometry.size();