9#include "KChartCartesianGrid.h"
10#include "KChartAbstractCartesianDiagram.h"
11#include "KChartPaintContext.h"
12#include "KChartPainterSaver_p.h"
13#include "KChartPrintingParameters.h"
14#include "KChartFrameAttributes.h"
15#include "KChartCartesianAxis_p.h"
16#include "KChartMath_p.h"
19#include <QPainterPath>
23CartesianGrid::CartesianGrid()
28CartesianGrid::~CartesianGrid()
32int CartesianGrid::minimalSteps()
const
37void CartesianGrid::setMinimalSteps(
int minsteps)
39 m_minsteps = minsteps;
42int CartesianGrid::maximalSteps()
const
47void CartesianGrid::setMaximalSteps(
int maxsteps)
49 m_maxsteps = maxsteps;
57 if ( !gridAttrsX.isGridVisible() && !gridAttrsX.isSubGridVisible() &&
58 !gridAttrsY.isGridVisible() && !gridAttrsY.isSubGridVisible() ) {
63 PainterSaver painterSaver( p );
65 plane = qobject_cast< CartesianCoordinatePlane* >( plane->
sharedAxisMasterPlane( context->painter() ) );
66 Q_ASSERT_X ( plane,
"CartesianGrid::drawGrid",
67 "Bad function call: PaintContext::coodinatePlane() NOT a cartesian plane." );
71 Q_ASSERT_X ( mDataDimensions.
count() == 2,
"CartesianGrid::drawGrid",
72 "Error: updateData did not return exactly two dimensions." );
79 const bool isLogarithmicX = dimX.calcMode == AbstractCoordinatePlane::Logarithmic;
80 const bool isLogarithmicY = dimY.calcMode == AbstractCoordinatePlane::Logarithmic;
82 qreal minValueX = qMin( dimX.start, dimX.end );
83 qreal maxValueX = qMax( dimX.start, dimX.end );
84 qreal minValueY = qMin( dimY.start, dimY.end );
85 qreal maxValueY = qMax( dimY.start, dimY.end );
87 bool adjustXLower = !isLogarithmicX && gridAttrsX.adjustLowerBoundToGrid();
88 bool adjustXUpper = !isLogarithmicX && gridAttrsX.adjustUpperBoundToGrid();
89 bool adjustYLower = !isLogarithmicY && gridAttrsY.adjustLowerBoundToGrid();
90 bool adjustYUpper = !isLogarithmicY && gridAttrsY.adjustUpperBoundToGrid();
95 if ( plane->frameAttributes().isVisible() ) {
96 const qreal radius = plane->frameAttributes().cornerRadius();
121 for (
int i = 0; i < 2; i++ ) {
122 XySwitch xy( i == 1 );
124 bool hasMajorLines = gridAttrs.isGridVisible();
125 bool hasMinorLines = hasMajorLines && gridAttrs.isSubGridVisible();
126 if ( !hasMajorLines && !hasMinorLines ) {
131 const bool drawZeroLine = dimension.isCalculated && gridAttrs.zeroLinePen().
style() !=
Qt::NoPen;
136 TickIterator it( xy.isY, dimension, gridAttrs.linesOnAnnotations(),
137 hasMajorLines, hasMinorLines, plane );
138 for ( ; !it.isAtEnd(); ++it ) {
139 if ( !gridAttrs.isOuterLinesVisible() &&
140 ( it.areAlmostEqual( it.position(), xy( minValueX, minValueY ) ) ||
141 it.areAlmostEqual( it.position(), xy( maxValueX, maxValueY ) ) ) ) {
144 xy.lvalue( lineStart.
rx(), lineStart.
ry() ) = it.position();
145 xy.lvalue( lineEnd.
rx(), lineEnd.
ry() ) = it.position();
148 if ( ISNAN( transLineStart.
x() ) || ISNAN( transLineStart.
y() ) ||
149 ISNAN( transLineEnd.
x() ) || ISNAN( transLineEnd.
y() ) ) {
153 if ( it.position() == 0.0 && drawZeroLine ) {
154 p->
setPen( PrintingParameters::scalePen( gridAttrsX.zeroLinePen() ) );
155 }
else if ( it.type() == TickIterator::MinorTick ) {
156 p->
setPen( PrintingParameters::scalePen( gridAttrs.subGridPen() ) );
158 p->
setPen( PrintingParameters::scalePen( gridAttrs.gridPen() ) );
160 p->
drawLine( transLineStart, transLineEnd );
168 Q_ASSERT_X ( rawDataDimensions.
count() == 2,
"CartesianGrid::calculateGrid",
169 "Error: calculateGrid() expects a list with exactly two entries." );
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;
193 gridAttrsX.adjustLowerBoundToGrid(),
194 gridAttrsX.adjustUpperBoundToGrid() );
195 if ( dimX.stepWidth ) {
202 gridAttrsY.adjustLowerBoundToGrid(),
203 gridAttrsY.adjustUpperBoundToGrid() );
206 && plane->axesCalcModeY() == CartesianCoordinatePlane::Linear
209 l.last().start = translatedBottomLeft.y();
210 l.last().end = translatedTopRight.y();
215 gridAttrsY.adjustLowerBoundToGrid(),
216 gridAttrsY.adjustUpperBoundToGrid() );
217 if ( dimY.stepWidth ) {
218 l.first().start = dimX.start;
219 l.first().end = dimX.end;
220 l.first().stepWidth = dimX.stepWidth;
221 l.first().subStepWidth = dimX.subStepWidth;
222 l.last().start = minMaxY.start;
223 l.last().end = minMaxY.end;
224 l.last().stepWidth = dimY.stepWidth;
225 l.last().subStepWidth = dimY.subStepWidth;
243 qDebug() << Q_FUNC_INFO <<
"final grid X-range:" << l.first().start <<
"->" << l.first().end
244 <<
" substep width:" << l.first().subStepWidth;
245 qDebug() << Q_FUNC_INFO <<
"final grid Y-range:" << l.last().start <<
"->" << l.last().end
246 <<
" substep width:" << l.last().subStepWidth;
251qreal fastPow10(
int x )
255 for (
int i = 1; i <= x; ++i )
258 for (
int i = -1; i >= x; --i )
265#define trunc(x) ((int)(x))
271 bool adjustLower,
bool adjustUpper )
const
281 if ( dim.isCalculated && dim.start != dim.end ) {
282 if ( dim.calcMode == AbstractCoordinatePlane::Linear ) {
284 if ( dim.stepWidth == 0.0 ) {
286 switch ( dim.sequence ) {
287 case KChartEnums::GranularitySequence_10_20:
288 granularities << 1.0 << 2.0;
290 case KChartEnums::GranularitySequence_10_50:
291 granularities << 1.0 << 5.0;
293 case KChartEnums::GranularitySequence_25_50:
294 granularities << 2.5 << 5.0;
296 case KChartEnums::GranularitySequence_125_25:
297 granularities << 1.25 << 2.5;
299 case KChartEnums::GranularitySequenceIrregular:
300 granularities << 1.0 << 1.25 << 2.0 << 2.5 << 5.0;
305 dim.start, dim.end, granularities, orientation,
306 dim.stepWidth, dim.subStepWidth,
307 adjustLower, adjustUpper );
313 adjustLower, adjustUpper );
320 const qreal minRaw = qMin( dim.start, dim.end );
321 const int minLog = -
static_cast<int>(trunc( log10( -minRaw ) ) );
323 min = qMin( minRaw, -std::numeric_limits< qreal >::epsilon() );
325 min = -fastPow10( -(minLog-1) );
328 const qreal maxRaw = qMin( -std::numeric_limits< qreal >::epsilon(), qMax( dim.start, dim.end ) );
329 const int maxLog = -
static_cast<int>(ceil( log10( -maxRaw ) ) );
332 else if ( fastPow10( -maxLog ) < maxRaw )
333 max = -fastPow10( -(maxLog+1) );
335 max = -fastPow10( -maxLog );
340 dim.stepWidth = -pow( 10.0, ceil( log10( qAbs( max - min ) / 10.0 ) ) );
346 const qreal minRaw = qMax( qMin( dim.start, dim.end ), qreal( 0.0 ) );
347 const int minLog =
static_cast<int>(trunc( log10( minRaw ) ) );
348 if ( minLog <= 0 && dim.end < 1.0 )
349 min = qMax( minRaw, std::numeric_limits< qreal >::epsilon() );
350 else if ( minLog <= 0 )
351 min = qMax( qreal(0.00001), dim.start );
353 min = fastPow10( minLog-1 );
357 const bool zeroBound = dim.start == 0.0 || dim.end == 0.0;
360 const qreal maxRaw = qMax( qMax( dim.start, dim.end ), qreal( 0.0 ) );
361 const int maxLog =
static_cast<int>(ceil( log10( maxRaw ) ) );
364 else if ( fastPow10( maxLog ) < maxRaw )
365 max = fastPow10( maxLog+1 );
367 max = fastPow10( maxLog );
368 if ( adjustLower || zeroBound )
370 if ( adjustUpper || zeroBound )
372 dim.stepWidth = pow( 10.0, ceil( log10( qAbs( max - min ) / 10.0 ) ) );
378 dim.stepWidth = dim.stepWidth ? dim.stepWidth : 1.0;
384static void calculateSteps(
386 int minSteps,
int maxSteps,
388 qreal& steps, qreal& stepWidth,
389 bool adjustLower,
bool adjustUpper )
397 for (
int i = 0; i <= lastIdx; ++i ) {
398 const qreal testStepWidth =
list.
at(lastIdx - i) * fastPow10( power );
400 qreal
start = qMin( start_, end_ );
401 qreal
end = qMax( start_, end_ );
406 const qreal testDistance = qAbs(end -
start);
407 const qreal testSteps = testDistance / testStepWidth;
410 if ( (minSteps <= testSteps) && (testSteps <= maxSteps)
411 && ( (steps == 0.0) || (testDistance <= distance) ) ) {
413 stepWidth = testStepWidth;
421void CartesianGrid::calculateStepWidth(
422 qreal start_, qreal end_,
425 qreal& stepWidth, qreal& subStepWidth,
426 bool adjustLower,
bool adjustUpper )
const
428 Q_UNUSED( orientation );
430 Q_ASSERT_X ( granularities.
count(),
"CartesianGrid::calculateStepWidth",
431 "Error: The list of GranularitySequence values is empty." );
435 const qreal
start = qMin( start_, end_);
436 const qreal
end = qMax( start_, end_);
442 while (
list.
last() * fastPow10( power ) < distance ) {
450 for (
int dec = -1;
dec == -1 || fastPow10( dec + 1 ) >=
distance; --
dec )
451 for (
int i = 0; i < count; ++i )
452 testList <<
list.
at(i) * fastPow10( dec );
457 calculateSteps(
start, end, testList, m_minsteps, m_maxsteps, power,
459 adjustLower, adjustUpper );
461 }
while ( steps == 0.0 );
468 if ( subStepWidth == 0.0 ) {
469 if ( stepWidth ==
list.
first() * fastPow10( power ) ) {
470 subStepWidth =
list.
last() * fastPow10( power-1 );
472 }
else if ( stepWidth ==
list.
first() * fastPow10( power-1 ) ) {
473 subStepWidth =
list.
last() * fastPow10( power-2 );
477 for (
int i = 1; i <
list.
count(); ++i ) {
478 if ( stepWidth ==
list.
at( i ) * fastPow10( power ) ) {
479 subStepWidth = smallerStepWidth * fastPow10( power );
482 if ( stepWidth ==
list.
at( i ) * fastPow10( power-1 ) ) {
483 subStepWidth = smallerStepWidth * fastPow10( power-1 );
486 smallerStepWidth =
list.
at( i );
QRect geometry() const override
pure virtual in QLayoutItem
Abstract base class for grid classes: cartesian, polar, ...
DataDimensionsList updateData(AbstractCoordinatePlane *plane)
Returns the cached result of data calculation.
static void adjustLowerUpperRange(qreal &start, qreal &end, qreal stepWidth, bool adjustLower, bool adjustUpper)
Adjusts start and/or end so that they are a multiple of stepWidth.
static bool isBoundariesValid(const QRectF &r)
Checks whether both coordinates of r are valid according to isValueValid.
Cartesian coordinate plane.
unsigned int autoAdjustVerticalRangeToData() const
Returns the maximal allowed percent of the vertical space covered by the coordinate plane that may be...
unsigned int autoAdjustHorizontalRangeToData() const
Returns the maximal allowed percent of the horizontal space covered by the coordinate plane that may ...
const QPointF translate(const QPointF &diagramPoint) const override
Translate the given point in value space coordinates to a position in pixel space.
AbstractCoordinatePlane * sharedAxisMasterPlane(QPainter *p=nullptr) override
reimpl
const GridAttributes gridAttributes(Qt::Orientation orientation) const
bool autoAdjustGridToZoom() const
Return the status of the built-in grid adjusting feature.
qreal zoomFactorY() const override
void drawGrid(PaintContext *context) override
Doing the actual drawing.
Helper class for one dimension of data, e.g.
A set of attributes controlling the appearance of grids.
Stores information about painting diagrams.
Q_SCRIPTABLE Q_NOREPLY void start()
KIOCORE_EXPORT QStringList list(const QString &fileClass)
const QList< QKeySequence > & end()
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
const_reference at(qsizetype i) const const
qsizetype count() const const
void drawLine(const QLine &line)
void setClipPath(const QPainterPath &path, Qt::ClipOperation operation)
void setPen(Qt::PenStyle style)
Qt::PenStyle style() const const
QPoint bottomLeft() const const
QPoint topRight() const const
QTextStream & dec(QTextStream &stream)