12 #include "KDChartCartesianCoordinatePlane_p.h"
17 #include "KDChartAbstractDiagram_p.h"
21 #include "KDChartPainterSaver_p.h"
24 #include <KDABLibFakes>
26 #include <QApplication>
27 #include <QElapsedTimer>
37 CartesianCoordinatePlane::Private::Private()
42 CartesianCoordinatePlane::CartesianCoordinatePlane(
Chart *parent)
53 void CartesianCoordinatePlane::init()
61 "CartesianCoordinatePlane::addDiagram",
"Only cartesian "
62 "diagrams can be added to a cartesian coordinate plane!");
73 if (
d->bPaintIsRunning) {
76 d->bPaintIsRunning =
true;
79 if (!diags.isEmpty()) {
87 PainterSaver painterSaver(painter);
88 QRect clipRect = drawArea.toRect().adjusted(-1, -1, 1, 1);
89 QRegion clipRegion(clipRect);
90 painter->setClipRegion(clipRegion);
93 d->grid->drawGrid(&ctx);
96 for (
int i = 0; i < diags.size(); i++) {
97 if (diags[i]->isHidden()) {
100 bool doDumpPaintTime = AbstractDiagram::Private::get(diags[i])->doDumpPaintTime;
101 QElapsedTimer stopWatch;
102 if (doDumpPaintTime) {
106 PainterSaver diagramPainterSaver(painter);
107 diags[i]->paint(&ctx);
109 if (doDumpPaintTime) {
110 qDebug() <<
"Painting diagram" << i <<
"took" << stopWatch.elapsed() <<
"milliseconds";
114 d->bPaintIsRunning =
false;
129 bool bStarting =
true;
130 const auto constDiagrams =
diagrams();
134 if (bStarting || dataBoundariesPair.first.x() < minX)
135 minX = dataBoundariesPair.first.x();
136 if (bStarting || dataBoundariesPair.first.y() < minY)
137 minY = dataBoundariesPair.first.y();
138 if (bStarting || dataBoundariesPair.second.x() > maxX)
139 maxX = dataBoundariesPair.second.x();
140 if (bStarting || dataBoundariesPair.second.y() > maxY)
141 maxY = dataBoundariesPair.second.y();
145 QRectF dataBoundingRect;
146 dataBoundingRect.setBottomLeft(QPointF(minX, minY));
147 dataBoundingRect.setTopRight(QPointF(maxX, maxY));
148 return dataBoundingRect;
152 const QRectF &r,
unsigned int percentX,
unsigned int percentY)
const
156 const bool isPositive = r.left() >= 0;
157 if ((r.right() >= 0) == isPositive) {
158 qreal upperBound = qMax(r.left(), r.right());
159 qreal lowerBound = qMin(r.left(), r.right());
160 qreal innerBound = isPositive ? lowerBound : upperBound;
161 qreal outerBound = isPositive ? upperBound : lowerBound;
162 if (innerBound / outerBound * 100 <= percentX &&
d->xAxisStartAtZero) {
173 const bool isPositive = r.bottom() >= 0;
174 if ((r.top() >= 0) == isPositive) {
175 qreal upperBound = qMax(r.top(), r.bottom());
176 qreal lowerBound = qMin(r.top(), r.bottom());
177 const qreal innerBound = isPositive ? lowerBound : upperBound;
178 const qreal outerBound = isPositive ? upperBound : lowerBound;
179 if (innerBound / outerBound * 100 <= percentY) {
194 const bool bAutoAdjustHorizontalRange =
d->autoAdjustHorizontalRangeToData < 100;
195 const bool bAutoAdjustVerticalRange =
d->autoAdjustVerticalRangeToData < 100;
197 const bool bHardHorizontalRange = (!bAutoAdjustHorizontalRange) && (
d->horizontalMin !=
d->horizontalMax || (ISNAN(
d->horizontalMin) != ISNAN(
d->horizontalMax)));
198 const bool bHardVerticalRange = (!bAutoAdjustVerticalRange) && (
d->verticalMin !=
d->verticalMax || (ISNAN(
d->verticalMin) != ISNAN(
d->verticalMax)));
199 QRectF dataBoundingRect;
202 if (bHardHorizontalRange && bHardVerticalRange) {
203 dataBoundingRect.setLeft(
d->horizontalMin);
204 dataBoundingRect.setRight(
d->horizontalMax);
205 dataBoundingRect.setBottom(
d->verticalMin);
206 dataBoundingRect.setTop(
d->verticalMax);
210 if (bHardHorizontalRange) {
211 if (!ISNAN(
d->horizontalMin))
212 dataBoundingRect.setLeft(
d->horizontalMin);
213 if (!ISNAN(
d->horizontalMax))
214 dataBoundingRect.setRight(
d->horizontalMax);
216 if (bHardVerticalRange) {
217 if (!ISNAN(
d->verticalMin))
218 dataBoundingRect.setBottom(
d->verticalMin);
219 if (!ISNAN(
d->verticalMax))
220 dataBoundingRect.setTop(
d->verticalMax);
226 dataBoundingRect,
d->autoAdjustHorizontalRangeToData,
d->autoAdjustVerticalRangeToData);
227 if (bAutoAdjustHorizontalRange) {
231 if (bAutoAdjustVerticalRange) {
236 return dataBoundingRect;
245 const auto *barDiagram = qobject_cast<const BarDiagram *>(dgr);
246 const auto *stockDiagram = qobject_cast<const StockDiagram *>(dgr);
252 const Qt::Orientation diagramOrientation = barDiagram !=
nullptr ? barDiagram->orientation() : Qt::Vertical;
253 const bool diagramIsVertical = diagramOrientation == Qt::Vertical;
295 return QRectF(
areaGeometry()).adjusted(1.0, 1.0, -2.0, -2.0);
300 if (
d->dimensions.isEmpty())
307 const QRectF dataBoundingRect(pt, siz);
310 const QPointF topLeft(
d->reverseHorizontalPlane ? dataBoundingRect.right() : dataBoundingRect.left(),
311 d->reverseVerticalPlane ? dataBoundingRect.bottom() : dataBoundingRect.top());
313 const qreal width = dataBoundingRect.width() * (
d->reverseHorizontalPlane ? -1.0 : 1.0);
314 const qreal height = dataBoundingRect.height() * (
d->reverseVerticalPlane ? -1.0 : 1.0);
316 return QRectF(topLeft, QSizeF(width, height));
322 QPointF physicalTopLeft =
d->coordinateTransformation.translate(logArea.topLeft());
323 QPointF physicalBottomRight =
d->coordinateTransformation.translate(logArea.bottomRight());
325 return QRectF(physicalTopLeft, physicalBottomRight).normalized();
336 Q_ASSERT_X(
d->dimensions.count() == 2,
"CartesianCoordinatePlane::layoutDiagrams",
337 "Error: gridDimensionsList() did not return exactly two dimensions.");
349 d->coordinateTransformation.updateTransform(logArea, physicalArea);
356 d->fixedDataCoordinateSpaceRelation = fixed;
357 d->fixedDataCoordinateSpaceRelationPinnedSize = QSize();
363 return d->fixedDataCoordinateSpaceRelation;
368 if (
d->xAxisStartAtZero == fixedStart)
371 d->xAxisStartAtZero = fixedStart;
376 return d->xAxisStartAtZero;
381 if (!
d->fixedDataCoordinateSpaceRelation) {
392 if (!
d->fixedDataCoordinateSpaceRelationPinnedSize.isValid()) {
393 d->fixedDataCoordinateSpaceRelationPinnedSize =
geometry.size();
399 if (
d->fixedDataCoordinateSpaceRelationPinnedSize !=
geometry.size()) {
400 const qreal widthScaling =
d->fixedDataCoordinateSpaceRelationPinnedSize.width() /
geometry.width();
401 const qreal heightScaling =
d->fixedDataCoordinateSpaceRelationPinnedSize.height() /
geometry.height();
403 const qreal newZoomX =
d->fixedDataCoordinateSpaceRelationPinnedZoom.xFactor * widthScaling;
404 const qreal newZoomY =
d->fixedDataCoordinateSpaceRelationPinnedZoom.yFactor * heightScaling;
406 const QPointF newCenter = QPointF(
d->fixedDataCoordinateSpaceRelationPinnedZoom.xCenter / widthScaling,
407 d->fixedDataCoordinateSpaceRelationPinnedZoom.yCenter / heightScaling);
409 bool changed =
false;
427 return d->coordinateTransformation.translate(diagramPoint);
432 return d->coordinateTransformation.translateBack(screenPoint);
437 if (
d->isometricScaling != isOn) {
438 d->isometricScaling = isOn;
446 return d->isometricScaling;
451 if (
d->coordinateTransformation.zoom.xFactor == factor) {
454 d->coordinateTransformation.zoom.xFactor = factor;
455 if (
d->autoAdjustGridToZoom) {
456 d->grid->setNeedRecalculate();
463 if (
d->coordinateTransformation.zoom.yFactor == factor) {
466 d->coordinateTransformation.zoom.yFactor = factor;
467 if (
d->autoAdjustGridToZoom) {
468 d->grid->setNeedRecalculate();
475 if (
d->coordinateTransformation.zoom.center() == point) {
478 d->coordinateTransformation.zoom.setCenter(point);
479 if (
d->autoAdjustGridToZoom) {
480 d->grid->setNeedRecalculate();
519 return d->coordinateTransformation.zoom.center();
524 return d->coordinateTransformation.zoom.xFactor;
529 return d->coordinateTransformation.zoom.yFactor;
534 return d->coordinateTransformation.axesCalcModeY;
539 return d->coordinateTransformation.axesCalcModeX;
544 if (
d->coordinateTransformation.axesCalcModeY != mode ||
d->coordinateTransformation.axesCalcModeX != mode) {
545 d->coordinateTransformation.axesCalcModeY = mode;
546 d->coordinateTransformation.axesCalcModeX = mode;
549 const auto constDiagrams =
diagrams();
558 if (
d->coordinateTransformation.axesCalcModeY != mode) {
559 d->coordinateTransformation.axesCalcModeY = mode;
568 if (
d->coordinateTransformation.axesCalcModeX != mode) {
569 d->coordinateTransformation.axesCalcModeX = mode;
576 inline bool fuzzyCompare(qreal a, qreal b)
578 if (ISNAN(a) && ISNAN(b))
580 if (qFuzzyIsNull(a) && qFuzzyIsNull(b))
582 return qFuzzyCompare(a, b);
588 const bool bAutoAdjustHorizontalRange =
d->autoAdjustHorizontalRangeToData < 100;
589 if (!fuzzyCompare(
d->horizontalMin, range.first) || !fuzzyCompare(
d->horizontalMax, range.second) || bAutoAdjustHorizontalRange) {
590 d->autoAdjustHorizontalRangeToData = 100;
591 d->horizontalMin = range.first;
592 d->horizontalMax = range.second;
601 const bool bAutoAdjustVerticalRange =
d->autoAdjustVerticalRangeToData < 100;
602 if (!fuzzyCompare(
d->verticalMin, range.first) || !fuzzyCompare(
d->verticalMax, range.second) || bAutoAdjustVerticalRange) {
603 d->autoAdjustVerticalRangeToData = 100;
604 d->verticalMin = range.first;
605 d->verticalMax = range.second;
614 return QPair<qreal, qreal>(
d->horizontalMin,
d->horizontalMax);
619 return QPair<qreal, qreal>(
d->verticalMin,
d->verticalMax);
625 d->horizontalMin = dataBoundingRect.left();
626 d->horizontalMax = dataBoundingRect.right();
627 d->verticalMin = dataBoundingRect.top();
628 d->verticalMax = dataBoundingRect.bottom();
636 d->horizontalMin = dataBoundingRect.left();
637 d->horizontalMax = dataBoundingRect.right();
645 d->verticalMin = dataBoundingRect.bottom();
646 d->verticalMax = dataBoundingRect.top();
653 if (
d->autoAdjustHorizontalRangeToData != percentEmpty) {
654 d->autoAdjustHorizontalRangeToData = percentEmpty;
655 d->horizontalMin = 0.0;
656 d->horizontalMax = 0.0;
664 if (
d->autoAdjustVerticalRangeToData != percentEmpty) {
665 d->autoAdjustVerticalRangeToData = percentEmpty;
666 d->verticalMin = 0.0;
667 d->verticalMax = 0.0;
675 return d->autoAdjustHorizontalRangeToData;
680 return d->autoAdjustVerticalRangeToData;
684 Qt::Orientation orientation,
687 if (orientation == Qt::Horizontal)
688 d->gridAttributesHorizontal = a;
690 d->gridAttributesVertical = a;
691 setHasOwnGridAttributes(orientation,
true);
698 setHasOwnGridAttributes(orientation,
false);
705 if (orientation == Qt::Horizontal)
706 return d->gridAttributesHorizontal;
708 return d->gridAttributesVertical;
714 void CartesianCoordinatePlane::setHasOwnGridAttributes(Qt::Orientation orientation,
bool on)
716 if (orientation == Qt::Horizontal)
717 d->hasOwnGridAttributesHorizontal = on;
719 d->hasOwnGridAttributesVertical = on;
725 return orientation == Qt::Horizontal ?
d->hasOwnGridAttributesHorizontal
726 :
d->hasOwnGridAttributesVertical;
731 if (
d->autoAdjustGridToZoom != autoAdjust) {
732 d->autoAdjustGridToZoom = autoAdjust;
733 d->grid->setNeedRecalculate();
738 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && defined(Q_COMPILER_MANGLES_RETURN_TYPE)
744 return d->autoAdjustGridToZoom;
752 if (diag !=
nullptr) {
757 if (p !=
nullptr && p !=
this) {
764 if (plane ==
this || painter ==
nullptr)
767 const QPointF zero = QPointF(0, 0);
768 const QPointF tenX = QPointF(10, 0);
769 const QPointF tenY = QPointF(0, 10);
772 painter->translate(
translate(zero).x(), 0.0);
774 painter->scale(factor, 1.0);
775 painter->translate(-plane->
translate(zero).x(), 0.0);
778 painter->translate(0.0,
translate(zero).y());
780 painter->scale(1.0, factor);
781 painter->translate(0.0, -plane->
translate(zero).y());
789 if (
d->reverseHorizontalPlane == reverse)
792 d->reverseHorizontalPlane = reverse;
799 return d->reverseHorizontalPlane;
804 if (
d->reverseVerticalPlane == reverse)
807 d->reverseVerticalPlane = reverse;
814 return d->reverseVerticalPlane;
824 result.setBottomRight(
translateBack(drawArea.bottomRight()));
835 d->geometry = rectangle;
836 if (
d->isometricScaling) {
840 if (hfw < rectangle.height()) {
841 d->geometry.setHeight(hfw);
843 d->geometry.setWidth(qRound(qreal(rectangle.width()) * qreal(rectangle.height()) / qreal(hfw)));
849 const auto constDiagrams =
diagrams();
858 return d->isometricScaling ? Qt::Horizontal : (Qt::Horizontal | Qt::Vertical);
863 return d->isometricScaling;
872 return qRound(qreal(w) * qAbs(qreal(dataRect.height()) / qreal(dataRect.width())));
878 if (
d->isometricScaling) {
880 sh =
d->geometry.size();
QRect areaGeometry() const override
Base class for diagrams based on a cartesian coordianate system.
virtual AbstractCartesianDiagram * referenceDiagram() const
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
void setGeometry(const QRect &r) override
QSize sizeHint() const override
virtual void addDiagram(AbstractDiagram *diagram)
void viewportCoordinateSystemChanged()
void setGridNeedsRecalculate()
GridAttributes globalGridAttributes() const
AbstractDiagramList diagrams()
AbstractDiagram * diagram()
DataDimensionsList gridDimensionsList()
QRect geometry() const override
AbstractDiagram defines the interface for diagram classes.
void layoutChanged(AbstractDiagram *)
const QPair< QPointF, QPointF > dataBoundaries() const
Return the bottom left and top right data point, that the diagram will display (unless the grid adjus...
virtual void resize(const QSizeF &area)=0
int datasetDimension() const
virtual bool isOrdinate() const
virtual bool isAbscissa() const
Cartesian coordinate plane.
QRectF visibleDataRange() const
void setAxesCalcModeX(AxesCalcMode mode)
DataDimensionsList getDataDimensionsList() const override
QRectF visibleDiagramArea() const
int heightForWidth(int w) const override
bool hasOwnGridAttributes(Qt::Orientation orientation) const
void setVerticalRange(const QPair< qreal, qreal > &range)
Set the boundaries of the visible value space displayed in vertical direction.
unsigned int autoAdjustVerticalRangeToData() const
Returns the maximal allowed percent of the vertical space covered by the coordinate plane that may be...
void adjustVerticalRangeToData()
void adjustHorizontalRangeToData()
unsigned int autoAdjustHorizontalRangeToData() const
Returns the maximal allowed percent of the horizontal space covered by the coordinate plane that may ...
void layoutDiagrams() override
bool hasHeightForWidth() const override
bool doesIsometricScaling() const
void setZoomFactorY(qreal factor) override
void setZoomFactors(qreal factorX, qreal factorY) override
bool isVerticalRangeReversed() const
void setGeometry(const QRect &r) override
void adjustRangesToData()
Adjust both, horizontal and vertical range settings to the ranges covered by the model's data values.
QPointF zoomCenter() const override
const QPointF translate(const QPointF &diagramPoint) const override
bool xAxisStartAtZero() const
bool hasFixedDataCoordinateSpaceRelation() const
bool doneSetZoomFactorX(qreal factor)
virtual QRectF calculateRawDataBoundingRect() const
void setAutoAdjustGridToZoom(bool autoAdjust)
QPair< qreal, qreal > horizontalRange() const
void paint(QPainter *) override
bool isHorizontalRangeReversed() const
void setVerticalRangeReversed(bool reverse)
QPair< qreal, qreal > verticalRange() const
AbstractCoordinatePlane * sharedAxisMasterPlane(QPainter *p=nullptr) override
void setZoomFactorX(qreal factor) override
Qt::Orientations expandingDirections() const override
void setAutoAdjustVerticalRangeToData(unsigned int percentEmpty=67)
Automatically adjust vertical range settings to the ranges covered by the model's values,...
void slotLayoutChanged(AbstractDiagram *)
const GridAttributes gridAttributes(Qt::Orientation orientation) const
void addDiagram(AbstractDiagram *diagram) override
void setXAxisStartAtZero(bool fixedStart)
void setHorizontalRangeReversed(bool reverse)
QRectF logicalArea() const
QSize sizeHint() const override
void setIsometricScaling(bool onOff)
void setAxesCalcModes(AxesCalcMode mode)
const QPointF translateBack(const QPointF &screenPoint) const
void setAutoAdjustHorizontalRangeToData(unsigned int percentEmpty=67)
Automatically adjust horizontal range settings to the ranges covered by the model's values,...
qreal zoomFactorX() const override
void setZoomCenter(const QPointF ¢er) override
QRectF adjustedToMaxEmptyInnerPercentage(const QRectF &r, unsigned int percentX, unsigned int percentY) const
void setFixedDataCoordinateSpaceRelation(bool fixed)
virtual QRectF drawingArea() const
void setHorizontalRange(const QPair< qreal, qreal > &range)
Set the boundaries of the visible value space displayed in horizontal direction.
QRectF diagramArea() const
bool doneSetZoomFactorY(qreal factor)
~CartesianCoordinatePlane() override
bool autoAdjustGridToZoom() const
QRectF getRawDataBoundingRectFromDiagrams() const
void resetGridAttributes(Qt::Orientation orientation)
AxesCalcMode axesCalcModeX() const
void setGridAttributes(Qt::Orientation orientation, const GridAttributes &)
void setAxesCalcModeY(AxesCalcMode mode)
AxesCalcMode axesCalcModeY() const
void handleFixedDataCoordinateSpaceRelation(const QRectF &geometry)
qreal zoomFactorY() const override
bool doneSetZoomCenter(const QPointF ¢er)
A chart with one or more diagrams.
Helper class for one dimension of data, e.g. for the rows in a data model, or for the labels of an ax...
A set of attributes controlling the appearance of grids.
qreal gridStepWidth() const
KDChartEnums::GranularitySequence gridGranularitySequence() const
qreal gridSubStepWidth() const
Stores information about painting diagrams.
void setPainter(QPainter *painter)
void setCoordinatePlane(AbstractCoordinatePlane *plane)
void setRectangle(const QRectF &rect)
QList< AbstractDiagram * > AbstractDiagramList
QList< DataDimension > DataDimensionsList
QList< CartesianAxis * > CartesianAxisList