20 #include "KChartCartesianGrid.h" 21 #include "KChartAbstractCartesianDiagram.h" 22 #include "KChartPaintContext.h" 23 #include "KChartPainterSaver_p.h" 24 #include "KChartPrintingParameters.h" 25 #include "KChartFrameAttributes.h" 26 #include "KChartCartesianAxis_p.h" 27 #include "KChartMath_p.h" 30 #include <QPainterPath> 34 CartesianGrid::CartesianGrid()
39 CartesianGrid::~CartesianGrid()
43 int CartesianGrid::minimalSteps()
const 48 void CartesianGrid::setMinimalSteps(
int minsteps)
50 m_minsteps = minsteps;
53 int CartesianGrid::maximalSteps()
const 58 void CartesianGrid::setMaximalSteps(
int maxsteps)
60 m_maxsteps = maxsteps;
68 if ( !gridAttrsX.isGridVisible() && !gridAttrsX.isSubGridVisible() &&
69 !gridAttrsY.isGridVisible() && !gridAttrsY.isSubGridVisible() ) {
74 PainterSaver painterSaver( p );
77 Q_ASSERT_X ( plane,
"CartesianGrid::drawGrid",
78 "Bad function call: PaintContext::coodinatePlane() NOT a cartesian plane." );
81 updateData( context->coordinatePlane() );
82 Q_ASSERT_X ( mDataDimensions.count() == 2,
"CartesianGrid::drawGrid",
83 "Error: updateData did not return exactly two dimensions." );
84 if ( !isBoundariesValid( mDataDimensions ) ) {
90 const bool isLogarithmicX = dimX.calcMode == AbstractCoordinatePlane::Logarithmic;
91 const bool isLogarithmicY = dimY.calcMode == AbstractCoordinatePlane::Logarithmic;
93 qreal minValueX = qMin( dimX.start, dimX.end );
94 qreal maxValueX = qMax( dimX.start, dimX.end );
95 qreal minValueY = qMin( dimY.start, dimY.end );
96 qreal maxValueY = qMax( dimY.start, dimY.end );
98 bool adjustXLower = !isLogarithmicX && gridAttrsX.adjustLowerBoundToGrid();
99 bool adjustXUpper = !isLogarithmicX && gridAttrsX.adjustUpperBoundToGrid();
100 bool adjustYLower = !isLogarithmicY && gridAttrsY.adjustLowerBoundToGrid();
101 bool adjustYUpper = !isLogarithmicY && gridAttrsY.adjustUpperBoundToGrid();
106 if ( plane->frameAttributes().isVisible() ) {
107 const qreal radius = plane->frameAttributes().cornerRadius();
132 for (
int i = 0; i < 2; i++ ) {
133 XySwitch xy( i == 1 );
135 bool hasMajorLines = gridAttrs.isGridVisible();
136 bool hasMinorLines = hasMajorLines && gridAttrs.isSubGridVisible();
137 if ( !hasMajorLines && !hasMinorLines ) {
142 const bool drawZeroLine = dimension.isCalculated && gridAttrs.zeroLinePen().
style() !=
Qt::NoPen;
147 TickIterator it( xy.isY, dimension, gridAttrs.linesOnAnnotations(),
148 hasMajorLines, hasMinorLines, plane );
149 for ( ; !it.isAtEnd(); ++it ) {
150 if ( !gridAttrs.isOuterLinesVisible() &&
151 ( it.areAlmostEqual( it.position(), xy( minValueX, minValueY ) ) ||
152 it.areAlmostEqual( it.position(), xy( maxValueX, maxValueY ) ) ) ) {
155 xy.lvalue( lineStart.
rx(), lineStart.
ry() ) = it.position();
156 xy.lvalue( lineEnd.
rx(), lineEnd.
ry() ) = it.position();
159 if ( ISNAN( transLineStart.
x() ) || ISNAN( transLineStart.
y() ) ||
160 ISNAN( transLineEnd.
x() ) || ISNAN( transLineEnd.
y() ) ) {
164 if ( it.position() == 0.0 && drawZeroLine ) {
165 p->
setPen( PrintingParameters::scalePen( gridAttrsX.zeroLinePen() ) );
166 }
else if ( it.type() == TickIterator::MinorTick ) {
167 p->
setPen( PrintingParameters::scalePen( gridAttrs.subGridPen() ) );
169 p->
setPen( PrintingParameters::scalePen( gridAttrs.gridPen() ) );
171 p->
drawLine( transLineStart, transLineEnd );
179 Q_ASSERT_X ( rawDataDimensions.
count() == 2,
"CartesianGrid::calculateGrid",
180 "Error: calculateGrid() expects a list with exactly two entries." );
183 Q_ASSERT_X ( plane,
"CartesianGrid::calculateGrid",
184 "Error: PaintContext::calculatePlane() called, but no cartesian plane set." );
188 qDebug() << Q_FUNC_INFO <<
"initial grid X-range:" << l.
first().start <<
"->" << l.
first().end
189 <<
" substep width:" << l.
first().subStepWidth;
190 qDebug() << Q_FUNC_INFO <<
"initial grid Y-range:" << l.
last().start <<
"->" << l.
last().end
191 <<
" substep width:" << l.
last().subStepWidth;
195 if ( isBoundariesValid( l ) ) {
196 const QPointF translatedBottomLeft( plane->translateBack( plane->geometry().bottomLeft() ) );
197 const QPointF translatedTopRight( plane->translateBack( plane->geometry().topRight() ) );
204 gridAttrsX.adjustLowerBoundToGrid(),
205 gridAttrsX.adjustUpperBoundToGrid() );
206 if ( dimX.stepWidth ) {
213 gridAttrsY.adjustLowerBoundToGrid(),
214 gridAttrsY.adjustUpperBoundToGrid() );
216 if ( plane->autoAdjustGridToZoom()
217 && plane->axesCalcModeY() == CartesianCoordinatePlane::Linear
218 && plane->zoomFactorY() > 1.0 )
220 l.
last().start = translatedBottomLeft.
y();
221 l.
last().end = translatedTopRight.
y();
226 gridAttrsY.adjustLowerBoundToGrid(),
227 gridAttrsY.adjustUpperBoundToGrid() );
228 if ( dimY.stepWidth ) {
229 l.
first().start = dimX.start;
230 l.
first().end = dimX.end;
231 l.
first().stepWidth = dimX.stepWidth;
232 l.
first().subStepWidth = dimX.subStepWidth;
233 l.
last().start = minMaxY.start;
234 l.
last().end = minMaxY.end;
235 l.
last().stepWidth = dimY.stepWidth;
236 l.
last().subStepWidth = dimY.subStepWidth;
254 qDebug() << Q_FUNC_INFO <<
"final grid X-range:" << l.
first().start <<
"->" << l.
first().end
255 <<
" substep width:" << l.
first().subStepWidth;
256 qDebug() << Q_FUNC_INFO <<
"final grid Y-range:" << l.
last().start <<
"->" << l.
last().end
257 <<
" substep width:" << l.
last().subStepWidth;
262 qreal fastPow10(
int x )
266 for (
int i = 1; i <= x; ++i )
269 for (
int i = -1; i >= x; --i )
276 #define trunc(x) ((int)(x)) 282 bool adjustLower,
bool adjustUpper )
const 292 if ( dim.isCalculated && dim.start != dim.end ) {
293 if ( dim.calcMode == AbstractCoordinatePlane::Linear ) {
295 if ( dim.stepWidth == 0.0 ) {
297 switch ( dim.sequence ) {
298 case KChartEnums::GranularitySequence_10_20:
299 granularities << 1.0 << 2.0;
301 case KChartEnums::GranularitySequence_10_50:
302 granularities << 1.0 << 5.0;
304 case KChartEnums::GranularitySequence_25_50:
305 granularities << 2.5 << 5.0;
307 case KChartEnums::GranularitySequence_125_25:
308 granularities << 1.25 << 2.5;
310 case KChartEnums::GranularitySequenceIrregular:
311 granularities << 1.0 << 1.25 << 2.0 << 2.5 << 5.0;
316 dim.start, dim.end, granularities, orientation,
317 dim.stepWidth, dim.subStepWidth,
318 adjustLower, adjustUpper );
324 adjustLower, adjustUpper );
331 const qreal minRaw = qMin( dim.start, dim.end );
332 const int minLog = -
static_cast<int>(trunc( log10( -minRaw ) ) );
334 min = qMin( minRaw, -std::numeric_limits< qreal >::epsilon() );
336 min = -fastPow10( -(minLog-1) );
339 const qreal maxRaw = qMin( -std::numeric_limits< qreal >::epsilon(), qMax( dim.start, dim.end ) );
340 const int maxLog = -
static_cast<int>(ceil( log10( -maxRaw ) ) );
343 else if ( fastPow10( -maxLog ) < maxRaw )
344 max = -fastPow10( -(maxLog+1) );
346 max = -fastPow10( -maxLog );
351 dim.stepWidth = -pow( 10.0, ceil( log10( qAbs( max - min ) / 10.0 ) ) );
357 const qreal minRaw = qMax( qMin( dim.start, dim.end ), qreal( 0.0 ) );
358 const int minLog =
static_cast<int>(trunc( log10( minRaw ) ) );
359 if ( minLog <= 0 && dim.end < 1.0 )
360 min = qMax( minRaw, std::numeric_limits< qreal >::epsilon() );
361 else if ( minLog <= 0 )
362 min = qMax( qreal(0.00001), dim.start );
364 min = fastPow10( minLog-1 );
368 const bool zeroBound = dim.start == 0.0 || dim.end == 0.0;
371 const qreal maxRaw = qMax( qMax( dim.start, dim.end ), qreal( 0.0 ) );
372 const int maxLog =
static_cast<int>(ceil( log10( maxRaw ) ) );
375 else if ( fastPow10( maxLog ) < maxRaw )
376 max = fastPow10( maxLog+1 );
378 max = fastPow10( maxLog );
379 if ( adjustLower || zeroBound )
381 if ( adjustUpper || zeroBound )
383 dim.stepWidth = pow( 10.0, ceil( log10( qAbs( max - min ) / 10.0 ) ) );
389 dim.stepWidth = dim.stepWidth ? dim.stepWidth : 1.0;
395 static void calculateSteps(
397 int minSteps,
int maxSteps,
399 qreal& steps, qreal& stepWidth,
400 bool adjustLower,
bool adjustUpper )
404 qreal distance = 0.0;
407 const int lastIdx = list.
count()-1;
408 for (
int i = 0; i <= lastIdx; ++i ) {
409 const qreal testStepWidth = list.
at(lastIdx - i) * fastPow10( power );
411 qreal start = qMin( start_, end_ );
412 qreal end = qMax( start_, end_ );
417 const qreal testDistance = qAbs(end - start);
418 const qreal testSteps = testDistance / testStepWidth;
421 if ( (minSteps <= testSteps) && (testSteps <= maxSteps)
422 && ( (steps == 0.0) || (testDistance <= distance) ) ) {
424 stepWidth = testStepWidth;
425 distance = testDistance;
432 void CartesianGrid::calculateStepWidth(
433 qreal start_, qreal end_,
436 qreal& stepWidth, qreal& subStepWidth,
437 bool adjustLower,
bool adjustUpper )
const 439 Q_UNUSED( orientation );
441 Q_ASSERT_X ( granularities.
count(),
"CartesianGrid::calculateStepWidth",
442 "Error: The list of GranularitySequence values is empty." );
444 std::sort(list.begin(), list.end());
446 const qreal start = qMin( start_, end_);
447 const qreal end = qMax( start_, end_);
448 const qreal distance = end - start;
453 while ( list.last() * fastPow10( power ) < distance ) {
458 const int count = list.count();
461 for (
int dec = -1; dec == -1 || fastPow10( dec + 1 ) >= distance; --dec )
462 for (
int i = 0; i < count; ++i )
463 testList << list.
at(i) * fastPow10( dec );
468 calculateSteps( start, end, testList, m_minsteps, m_maxsteps, power,
470 adjustLower, adjustUpper );
472 }
while ( steps == 0.0 );
479 if ( subStepWidth == 0.0 ) {
480 if ( stepWidth == list.first() * fastPow10( power ) ) {
481 subStepWidth = list.last() * fastPow10( power-1 );
483 }
else if ( stepWidth == list.first() * fastPow10( power-1 ) ) {
484 subStepWidth = list.last() * fastPow10( power-2 );
487 qreal smallerStepWidth = list.first();
488 for (
int i = 1; i < list.count(); ++i ) {
489 if ( stepWidth == list.at( i ) * fastPow10( power ) ) {
490 subStepWidth = smallerStepWidth * fastPow10( power );
493 if ( stepWidth == list.at( i ) * fastPow10( power-1 ) ) {
494 subStepWidth = smallerStepWidth * fastPow10( power-1 );
497 smallerStepWidth = list.at( i );
Helper class for one dimension of data, e.g.
Qt::PenStyle style() const const
void setClipPath(const QPainterPath &path, Qt::ClipOperation operation)
const T & at(int i) const const
void addRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode)
void drawLine(const QLineF &line)
const GridAttributes gridAttributes(Qt::Orientation orientation) const
int count(const T &value) const const
Stores information about painting diagrams.
void setPen(const QColor &color)
unsigned int autoAdjustVerticalRangeToData() const
Returns the maximal allowed percent of the vertical space covered by the coordinate plane that may be...
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.
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
void drawGrid(PaintContext *context) override
Doing the actual drawing.
A set of attributes controlling the appearance of grids.
Cartesian coordinate plane.
T qobject_cast(QObject *object)
Abstract base class for grid classes: cartesian, polar, ...
unsigned int autoAdjustHorizontalRangeToData() const
Returns the maximal allowed percent of the horizontal space covered by the coordinate plane that may ...