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>
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()
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 = (!bAutoAdjustHorizontalRange) && (
d->horizontalMin !=
d->horizontalMax || (ISNAN(
d->horizontalMin) != ISNAN(
d->horizontalMax)));
230 const bool bHardVerticalRange = (!bAutoAdjustVerticalRange) && (
d->verticalMin !=
d->verticalMax || (ISNAN(
d->verticalMin) != ISNAN(
d->verticalMax)));
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 if (!ISNAN(
d->horizontalMin))
244 dataBoundingRect.setLeft(
d->horizontalMin );
245 if (!ISNAN(
d->horizontalMax))
246 dataBoundingRect.setRight(
d->horizontalMax );
248 if ( bHardVerticalRange ) {
249 if (!ISNAN(
d->verticalMin))
250 dataBoundingRect.setBottom(
d->verticalMin );
251 if (!ISNAN(
d->verticalMax))
252 dataBoundingRect.setTop(
d->verticalMax );
258 dataBoundingRect,
d->autoAdjustHorizontalRangeToData,
d->autoAdjustVerticalRangeToData );
259 if ( bAutoAdjustHorizontalRange ) {
263 if ( bAutoAdjustVerticalRange ) {
268 return dataBoundingRect;
275 qobject_cast< const AbstractCartesianDiagram* >(
diagrams().first() );
279 const BarDiagram *barDiagram = qobject_cast< const BarDiagram* >( dgr );
280 const StockDiagram *stockDiagram = qobject_cast< const StockDiagram* >( dgr );
286 const Qt::Orientation diagramOrientation = barDiagram != 0 ? barDiagram->
orientation() : Qt::Vertical;
287 const bool diagramIsVertical = diagramOrientation == Qt::Vertical;
300 diagramIsVertical ? ( !stockDiagram && dgr->
datasetDimension() > 1 ) :
true,
329 return QRectF(
areaGeometry() ).adjusted( 1.0, 1.0, -2.0, -2.0 );
335 if (
d->dimensions.isEmpty() )
340 const QPointF pt( qMin( dimX.
start, dimX.
end ), qMax( dimY.
start, dimY.
end ) );
342 const QRectF dataBoundingRect( pt, siz );
345 const QPointF topLeft(
d->reverseHorizontalPlane ? dataBoundingRect.right() : dataBoundingRect.left(),
346 d->reverseVerticalPlane ? dataBoundingRect.bottom() : dataBoundingRect.top() );
348 const qreal width = dataBoundingRect.width() * (
d->reverseHorizontalPlane ? -1.0 : 1.0 );
349 const qreal height = dataBoundingRect.height() * (
d->reverseVerticalPlane ? -1.0 : 1.0 );
351 return QRectF( topLeft, QSizeF( width, height ) );
357 QPointF physicalTopLeft =
d->coordinateTransformation.translate( logArea.topLeft() );
358 QPointF physicalBottomRight =
d->coordinateTransformation.translate( logArea.bottomRight() );
360 return QRectF( physicalTopLeft, physicalBottomRight ).normalized();
371 Q_ASSERT_X (
d->dimensions.count() == 2,
"CartesianCoordinatePlane::layoutDiagrams",
372 "Error: gridDimensionsList() did not return exactly two dimensions." );
384 d->coordinateTransformation.updateTransform( logArea, physicalArea );
391 d->fixedDataCoordinateSpaceRelation = fixed;
392 d->fixedDataCoordinateSpaceRelationPinnedSize = QSize();
398 return d->fixedDataCoordinateSpaceRelation;
403 if (
d->xAxisStartAtZero == fixedStart)
406 d->xAxisStartAtZero = fixedStart;
411 return d->xAxisStartAtZero;
416 if ( !
d->fixedDataCoordinateSpaceRelation ) {
427 if ( !
d->fixedDataCoordinateSpaceRelationPinnedSize.isValid() ) {
428 d->fixedDataCoordinateSpaceRelationPinnedSize =
geometry.size();
434 if (
d->fixedDataCoordinateSpaceRelationPinnedSize !=
geometry.size() ) {
435 const qreal widthScaling =
d->fixedDataCoordinateSpaceRelationPinnedSize.width() /
geometry.width();
436 const qreal heightScaling =
d->fixedDataCoordinateSpaceRelationPinnedSize.height() /
geometry.height();
438 const qreal newZoomX =
d->fixedDataCoordinateSpaceRelationPinnedZoom.xFactor * widthScaling;
439 const qreal newZoomY =
d->fixedDataCoordinateSpaceRelationPinnedZoom.yFactor * heightScaling;
441 const QPointF newCenter = QPointF(
d->fixedDataCoordinateSpaceRelationPinnedZoom.xCenter / widthScaling,
442 d->fixedDataCoordinateSpaceRelationPinnedZoom.yCenter / heightScaling );
444 bool changed =
false;
462 return d->coordinateTransformation.translate( diagramPoint );
467 return d->coordinateTransformation.translateBack( screenPoint );
472 if (
d->isometricScaling != isOn ) {
473 d->isometricScaling = isOn;
481 return d->isometricScaling;
486 if (
d->coordinateTransformation.zoom.xFactor == factor ) {
489 d->coordinateTransformation.zoom.xFactor = factor;
490 if (
d->autoAdjustGridToZoom ) {
491 d->grid->setNeedRecalculate();
498 if (
d->coordinateTransformation.zoom.yFactor == factor ) {
501 d->coordinateTransformation.zoom.yFactor = factor;
502 if (
d->autoAdjustGridToZoom ) {
503 d->grid->setNeedRecalculate();
510 if (
d->coordinateTransformation.zoom.center() == point ) {
513 d->coordinateTransformation.zoom.setCenter( point );
514 if (
d->autoAdjustGridToZoom ) {
515 d->grid->setNeedRecalculate();
554 return d->coordinateTransformation.zoom.center();
559 return d->coordinateTransformation.zoom.xFactor;
564 return d->coordinateTransformation.zoom.yFactor;
570 return d->coordinateTransformation.axesCalcModeY;
575 return d->coordinateTransformation.axesCalcModeX;
580 if (
d->coordinateTransformation.axesCalcModeY != mode ||
581 d->coordinateTransformation.axesCalcModeX != mode ) {
582 d->coordinateTransformation.axesCalcModeY = mode;
583 d->coordinateTransformation.axesCalcModeX = mode;
593 if (
d->coordinateTransformation.axesCalcModeY != mode ) {
594 d->coordinateTransformation.axesCalcModeY = mode;
603 if (
d->coordinateTransformation.axesCalcModeX != mode ) {
604 d->coordinateTransformation.axesCalcModeX = mode;
611 inline bool fuzzyCompare( qreal a, qreal b )
613 if ( ISNAN(a) && ISNAN(b) )
615 if ( qFuzzyIsNull(a) && qFuzzyIsNull(b) )
617 return qFuzzyCompare( a, b );
623 const bool bAutoAdjustHorizontalRange =
d->autoAdjustHorizontalRangeToData < 100;
624 if ( !fuzzyCompare(
d->horizontalMin, range.first) || !fuzzyCompare(
d->horizontalMax, range.second) || bAutoAdjustHorizontalRange ) {
625 d->autoAdjustHorizontalRangeToData = 100;
626 d->horizontalMin = range.first;
627 d->horizontalMax = range.second;
636 const bool bAutoAdjustVerticalRange =
d->autoAdjustVerticalRangeToData < 100;
637 if ( !fuzzyCompare(
d->verticalMin, range.first) || !fuzzyCompare(
d->verticalMax, range.second) || bAutoAdjustVerticalRange ) {
638 d->autoAdjustVerticalRangeToData = 100;
639 d->verticalMin = range.first;
640 d->verticalMax = range.second;
660 d->horizontalMin = dataBoundingRect.left();
661 d->horizontalMax = dataBoundingRect.right();
662 d->verticalMin = dataBoundingRect.top();
663 d->verticalMax = dataBoundingRect.bottom();
671 d->horizontalMin = dataBoundingRect.left();
672 d->horizontalMax = dataBoundingRect.right();
680 d->verticalMin = dataBoundingRect.bottom();
681 d->verticalMax = dataBoundingRect.top();
688 if (
d->autoAdjustHorizontalRangeToData != percentEmpty )
690 d->autoAdjustHorizontalRangeToData = percentEmpty;
691 d->horizontalMin = 0.0;
692 d->horizontalMax = 0.0;
700 if (
d->autoAdjustVerticalRangeToData != percentEmpty )
702 d->autoAdjustVerticalRangeToData = percentEmpty;
703 d->verticalMin = 0.0;
704 d->verticalMax = 0.0;
712 return d->autoAdjustHorizontalRangeToData;
717 return d->autoAdjustVerticalRangeToData;
721 Qt::Orientation orientation,
724 if ( orientation == Qt::Horizontal )
725 d->gridAttributesHorizontal = a;
727 d->gridAttributesVertical = a;
728 setHasOwnGridAttributes( orientation,
true );
735 setHasOwnGridAttributes( orientation,
false );
742 if ( orientation == Qt::Horizontal )
743 return d->gridAttributesHorizontal;
745 return d->gridAttributesVertical;
751 void CartesianCoordinatePlane::setHasOwnGridAttributes( Qt::Orientation orientation,
bool on )
753 if ( orientation == Qt::Horizontal )
754 d->hasOwnGridAttributesHorizontal = on;
756 d->hasOwnGridAttributesVertical = on;
762 return orientation == Qt::Horizontal ?
d->hasOwnGridAttributesHorizontal
763 :
d->hasOwnGridAttributesVertical;
768 if (
d->autoAdjustGridToZoom != autoAdjust ) {
769 d->autoAdjustGridToZoom = autoAdjust;
770 d->grid->setNeedRecalculate();
775 #if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE)
780 return d->autoAdjustGridToZoom;
795 if ( p != 0 && p !=
this )
803 if ( plane ==
this || painter == 0 )
806 const QPointF zero = QPointF( 0, 0 );
807 const QPointF tenX = QPointF( 10, 0 );
808 const QPointF tenY = QPointF( 0, 10 );
813 painter->translate(
translate( zero ).x(), 0.0 );
815 painter->scale( factor, 1.0 );
816 painter->translate( -plane->
translate( zero ).x(), 0.0 );
820 painter->translate( 0.0,
translate( zero ).y() );
822 painter->scale( 1.0, factor );
823 painter->translate( 0.0, -plane->
translate( zero ).y() );
832 if (
d->reverseHorizontalPlane == reverse )
835 d->reverseHorizontalPlane = reverse;
842 return d->reverseHorizontalPlane;
847 if (
d->reverseVerticalPlane == reverse )
850 d->reverseVerticalPlane = reverse;
857 return d->reverseVerticalPlane;
867 result.setBottomRight(
translateBack( drawArea.bottomRight() ) );
878 d->geometry = rectangle;
879 if (
d->isometricScaling ) {
883 if ( hfw < rectangle.height() ) {
884 d->geometry.setHeight( hfw );
886 d->geometry.setWidth( qRound( qreal( rectangle.width() ) *
887 qreal( rectangle.height() ) / qreal( hfw ) ) );
901 return d->isometricScaling ? Qt::Horizontal : ( Qt::Horizontal | Qt::Vertical );
906 return d->isometricScaling;
915 return qRound( qreal( w ) * qAbs( qreal( dataRect.height() ) / qreal( dataRect.width() ) ) );
921 if (
d->isometricScaling ) {
923 sh =
d->geometry.size();