13 #include "KDChartCartesianAxis_p.h"
16 #include "KDChartPainterSaver_p.h"
21 #include <KDABLibFakes>
23 #include <QPainterPath>
44 m_minsteps = minsteps;
54 m_maxsteps = maxsteps;
59 auto *plane = qobject_cast<CartesianCoordinatePlane *>(context->
coordinatePlane());
60 const GridAttributes gridAttrsX(plane->gridAttributes(Qt::Horizontal));
61 const GridAttributes gridAttrsY(plane->gridAttributes(Qt::Vertical));
66 QPainter *p = context->
painter();
67 PainterSaver painterSaver(p);
69 plane = qobject_cast<CartesianCoordinatePlane *>(plane->sharedAxisMasterPlane(context->
painter()));
70 Q_ASSERT_X(plane,
"CartesianGrid::drawGrid",
71 "Bad function call: PaintContext::coodinatePlane() NOT a cartesian plane.");
76 "Error: updateData did not return exactly two dimensions.");
86 qreal minValueX = qMin(dimX.
start, dimX.
end);
87 qreal maxValueX = qMax(dimX.
start, dimX.
end);
88 qreal minValueY = qMin(dimY.
start, dimY.
end);
89 qreal maxValueY = qMax(dimY.
start, dimY.
end);
99 if (plane->frameAttributes().isVisible()) {
100 const qreal radius = plane->frameAttributes().cornerRadius();
102 path.addRoundedRect(QRectF(plane->translate(QPointF(minValueX, minValueY)),
103 plane->translate(QPointF(maxValueX, maxValueY))),
105 context->
painter()->setClipPath(path);
125 for (
int i = 0; i < 2; i++) {
130 if (!hasMajorLines && !hasMinorLines) {
137 QPointF lineStart = QPointF(minValueX, minValueY);
138 QPointF lineEnd = QPointF(maxValueX, maxValueY);
141 hasMajorLines, hasMinorLines, plane);
142 for (; !it.isAtEnd(); ++it) {
143 if (!gridAttrs.
isOuterLinesVisible() && (it.areAlmostEqual(it.position(), xy(minValueX, minValueY)) || it.areAlmostEqual(it.position(), xy(maxValueX, maxValueY)))) {
146 xy.lvalue(lineStart.rx(), lineStart.ry()) = it.position();
147 xy.lvalue(lineEnd.rx(), lineEnd.ry()) = it.position();
148 QPointF transLineStart = plane->translate(lineStart);
149 QPointF transLineEnd = plane->translate(lineEnd);
150 if (ISNAN(transLineStart.x()) || ISNAN(transLineStart.y()) || ISNAN(transLineEnd.x()) || ISNAN(transLineEnd.y())) {
154 if (it.position() == 0.0 && drawZeroLine) {
156 }
else if (it.type() == TickIterator::MinorTick) {
161 p->drawLine(transLineStart, transLineEnd);
168 Q_ASSERT_X(rawDataDimensions.count() == 2,
"CartesianGrid::calculateGrid",
169 "Error: calculateGrid() expects a list with exactly two entries.");
171 auto *plane = qobject_cast<CartesianCoordinatePlane *>(
mPlane);
172 Q_ASSERT_X(plane,
"CartesianGrid::calculateGrid",
173 "Error: PaintContext::calculatePlane() called, but no cartesian plane set.");
177 qDebug() << Q_FUNC_INFO <<
"initial grid X-range:" << l.first().start <<
"->" << l.first().end
178 <<
" substep width:" << l.first().subStepWidth;
179 qDebug() << Q_FUNC_INFO <<
"initial grid Y-range:" << l.last().start <<
"->" << l.last().end
180 <<
" substep width:" << l.last().subStepWidth;
185 const QPointF translatedBottomLeft(plane->translateBack(plane->geometry().bottomLeft()));
186 const QPointF translatedTopRight(plane->translateBack(plane->geometry().topRight()));
188 const GridAttributes gridAttrsX(plane->gridAttributes(Qt::Horizontal));
189 const GridAttributes gridAttrsY(plane->gridAttributes(Qt::Vertical));
191 const DataDimension dimX = calculateGridXY(l.first(), Qt::Horizontal,
192 gridAttrsX.adjustLowerBoundToGrid(),
193 gridAttrsX.adjustUpperBoundToGrid());
199 const DataDimension minMaxY = calculateGridXY(l.last(), Qt::Vertical,
200 gridAttrsY.adjustLowerBoundToGrid(),
201 gridAttrsY.adjustUpperBoundToGrid());
203 if (plane->autoAdjustGridToZoom()
205 && plane->zoomFactorY() > 1.0) {
206 l.last().start = translatedBottomLeft.y();
207 l.last().end = translatedTopRight.y();
210 const DataDimension dimY = calculateGridXY(l.last(), Qt::Vertical,
211 gridAttrsY.adjustLowerBoundToGrid(),
212 gridAttrsY.adjustUpperBoundToGrid());
214 l.first().start = dimX.
start;
215 l.first().end = dimX.
end;
218 l.last().start = minMaxY.
start;
219 l.last().end = minMaxY.
end;
239 qDebug() << Q_FUNC_INFO <<
"final grid X-range:" << l.first().start <<
"->" << l.first().end
240 <<
" substep width:" << l.first().subStepWidth;
241 qDebug() << Q_FUNC_INFO <<
"final grid Y-range:" << l.last().start <<
"->" << l.last().end
242 <<
" substep width:" << l.last().subStepWidth;
251 for (
int i = 1; i <= x; ++i)
254 for (
int i = -1; i >= x; --i)
261 #define trunc(x) (( int )(x))
266 Qt::Orientation orientation,
267 bool adjustLower,
bool adjustUpper)
const
270 if ((orientation == Qt::Vertical && plane->autoAdjustVerticalRangeToData() >= 100) || (orientation == Qt::Horizontal && plane->autoAdjustHorizontalRangeToData() >= 100)) {
276 if (dim.isCalculated && dim.start != dim.end) {
279 if (dim.stepWidth == 0.0) {
280 QList<qreal> granularities;
281 switch (dim.sequence) {
283 granularities << 1.0 << 2.0;
286 granularities << 1.0 << 5.0;
289 granularities << 2.5 << 5.0;
292 granularities << 1.25 << 2.5;
295 granularities << 1.0 << 1.25 << 2.0 << 2.5 << 5.0;
300 dim.start, dim.end, granularities, orientation,
301 dim.stepWidth, dim.subStepWidth,
302 adjustLower, adjustUpper);
308 adjustLower, adjustUpper);
314 const qreal minRaw = qMin(dim.start, dim.end);
315 const int minLog = -
static_cast<int>(trunc(log10(-minRaw)));
317 min = qMin(minRaw, -std::numeric_limits<qreal>::epsilon());
322 const qreal maxRaw = qMin(-std::numeric_limits<qreal>::epsilon(), qMax(dim.start, dim.end));
323 const int maxLog = -
static_cast<int>(ceil(log10(-maxRaw)));
334 dim.stepWidth = -pow(10.0, ceil(log10(qAbs(max - min) / 10.0)));
339 const qreal minRaw = qMax(qMin(dim.start, dim.end), qreal(0.0));
340 const int minLog =
static_cast<int>(trunc(log10(minRaw)));
341 if (minLog <= 0 && dim.end < 1.0)
342 min = qMax(minRaw, std::numeric_limits<qreal>::epsilon());
343 else if (minLog <= 0)
344 min = qMax(qreal(0.00001), dim.start);
350 const bool zeroBound = dim.start == 0.0 || dim.end == 0.0;
353 const qreal maxRaw = qMax(qMax(dim.start, dim.end), qreal(0.0));
354 const int maxLog =
static_cast<int>(ceil(log10(maxRaw)));
361 if (adjustLower || zeroBound)
363 if (adjustUpper || zeroBound)
365 dim.stepWidth = pow(10.0, ceil(log10(qAbs(max - min) / 10.0)));
371 dim.stepWidth = dim.stepWidth ? dim.stepWidth : 1.0;
377 qreal start_, qreal end_,
const QList<qreal> &list,
378 int minSteps,
int maxSteps,
380 qreal &steps, qreal &stepWidth,
381 bool adjustLower,
bool adjustUpper)
385 qreal distance = 0.0;
388 const int lastIdx = list.count() - 1;
389 for (
int i = 0; i <= lastIdx; ++i) {
390 const qreal testStepWidth = list.at(lastIdx - i) *
fastPow10(power);
392 qreal start = qMin(start_, end_);
393 qreal end = qMax(start_, end_);
398 const qreal testDistance = qAbs(end - start);
399 const qreal testSteps = testDistance / testStepWidth;
402 if ((minSteps <= testSteps) && (testSteps <= maxSteps)
403 && ((steps == 0.0) || (testDistance <= distance))) {
405 stepWidth = testStepWidth;
406 distance = testDistance;
412 void CartesianGrid::calculateStepWidth(
413 qreal start_, qreal end_,
414 const QList<qreal> &granularities,
415 Qt::Orientation orientation,
416 qreal &stepWidth, qreal &subStepWidth,
417 bool adjustLower,
bool adjustUpper)
const
419 Q_UNUSED(orientation);
421 Q_ASSERT_X(granularities.count(),
"CartesianGrid::calculateStepWidth",
422 "Error: The list of GranularitySequence values is empty.");
423 QList<qreal> list(granularities);
424 std::sort(list.begin(), list.end());
426 const qreal start = qMin(start_, end_);
427 const qreal end = qMax(start_, end_);
428 const qreal distance = end - start;
433 while (list.last() *
fastPow10(power) < distance) {
438 const int count = list.count();
439 QList<qreal> testList;
441 for (
int dec = -1; dec == -1 ||
fastPow10(dec + 1) >= distance; --dec)
442 for (
int i = 0; i < count; ++i)
448 calculateSteps(start, end, testList, m_minsteps, m_maxsteps, power,
450 adjustLower, adjustUpper);
452 }
while (steps == 0.0);
459 if (subStepWidth == 0.0) {
460 if (stepWidth == list.first() *
fastPow10(power)) {
461 subStepWidth = list.last() *
fastPow10(power - 1);
463 }
else if (stepWidth == list.first() *
fastPow10(power - 1)) {
464 subStepWidth = list.last() *
fastPow10(power - 2);
467 qreal smallerStepWidth = list.first();
468 for (
int i = 1; i < list.count(); ++i) {
469 if (stepWidth == list.at(i) *
fastPow10(power)) {
470 subStepWidth = smallerStepWidth *
fastPow10(power);
473 if (stepWidth == list.at(i) *
fastPow10(power - 1)) {
474 subStepWidth = smallerStepWidth *
fastPow10(power - 1);
477 smallerStepWidth = list.at(i);
static void calculateSteps(qreal start_, qreal end_, const QList< qreal > &list, int minSteps, int maxSteps, int power, qreal &steps, qreal &stepWidth, bool adjustLower, bool adjustUpper)
@ GranularitySequence_125_25
@ GranularitySequence_25_50
@ GranularitySequenceIrregular
@ GranularitySequence_10_20
@ GranularitySequence_10_50
DataDimensionsList updateData(AbstractCoordinatePlane *plane)
Returns the cached result of data calculation.
DataDimensionsList mDataDimensions
AbstractCoordinatePlane * mPlane
static void adjustLowerUpperRange(qreal &start, qreal &end, qreal stepWidth, bool adjustLower, bool adjustUpper)
static bool isBoundariesValid(const QRectF &r)
Cartesian coordinate plane.
void drawGrid(PaintContext *context) override
~CartesianGrid() override
void setMaximalSteps(int maxsteps)
void setMinimalSteps(int minsteps)
Helper class for one dimension of data, e.g. for the rows in a data model, or for the labels of an ax...
AbstractCoordinatePlane::AxesCalcMode calcMode
A set of attributes controlling the appearance of grids.
bool isOuterLinesVisible() const
bool linesOnAnnotations() const
bool isSubGridVisible() const
bool isGridVisible() const
bool adjustLowerBoundToGrid() const
bool adjustUpperBoundToGrid() const
Stores information about painting diagrams.
AbstractCoordinatePlane * coordinatePlane() const
QPainter * painter() const
static QPen scalePen(const QPen &pen)
QList< DataDimension > DataDimensionsList