void CartesianGrid::drawGrid( PaintContext* context ) { //qDebug() << "KDChart::CartesianGrid::drawGrid( PaintContext* context ) called"; CartesianCoordinatePlane* plane = dynamic_cast<CartesianCoordinatePlane*>(context->coordinatePlane()); // This plane is used for tranlating the coordinates - not for the data boundaries PainterSaver p( context->painter() ); plane = dynamic_cast< CartesianCoordinatePlane* >( plane->sharedAxisMasterPlane( context->painter() ) ); Q_ASSERT_X ( plane, "CartesianGrid::drawGrid", "Bad function call: PaintContext::coodinatePlane() NOT a cartesian plane." ); const GridAttributes gridAttrsX( plane->gridAttributes( Qt::Horizontal ) ); const GridAttributes gridAttrsY( plane->gridAttributes( Qt::Vertical ) ); //qDebug() << "OK:"; if ( !gridAttrsX.isGridVisible() && !gridAttrsY.isGridVisible() ) return; //qDebug() << "A"; // important: Need to update the calculated mData, // before we may use it! updateData( context->coordinatePlane() ); if( plane->axesCalcModeX() == KDChart::AbstractCoordinatePlane::Logarithmic && mData.first().stepWidth == 0.0 ) mData.first().stepWidth = 1.0; if( plane->axesCalcModeY() == KDChart::AbstractCoordinatePlane::Logarithmic && mData.last().stepWidth == 0.0 ) mData.last().stepWidth = 1.0; // test for programming errors: critical Q_ASSERT_X ( mData.count() == 2, "CartesianGrid::drawGrid", "Error: updateData did not return exactly two dimensions." ); // test for invalid boundaries: non-critical if( !isBoundariesValid( mData ) ) return; //qDebug() << "B"; DataDimension dimX = mData.first(); const DataDimension& dimY = mData.last(); // test for other programming errors: critical Q_ASSERT_X ( dimX.stepWidth, "CartesianGrid::drawGrid", "Error: updateData returned a Zero step width for the X grid." ); Q_ASSERT_X ( dimY.stepWidth, "CartesianGrid::drawGrid", "Error: updateData returned a Zero step width for the Y grid." ); qreal numberOfUnitLinesX = qAbs( dimX.distance() / dimX.stepWidth ) + (dimX.isCalculated ? 1.0 : 0.0); qreal numberOfUnitLinesY = qAbs( dimY.distance() / dimY.stepWidth ) + (dimY.isCalculated ? 1.0 : 0.0); //qDebug("numberOfUnitLinesX: %f numberOfUnitLinesY: %f",numberOfUnitLinesX,numberOfUnitLinesY); // do not draw a Zero size grid (and do not divide by Zero) if( numberOfUnitLinesX <= 0.0 || numberOfUnitLinesY <= 0.0 ) return; //qDebug() << "C"; const QPointF p1 = plane->translate( QPointF(dimX.start, dimY.start) ); const QPointF p2 = plane->translate( QPointF(dimX.end, dimY.end) ); //qDebug() << "dimX.isCalculated:" << dimX.isCalculated << "dimY.isCalculated:" << dimY.isCalculated; //qDebug() << "dimX.start: " << dimX.start << "dimX.end: " << dimX.end; //qDebug() << "dimY.start: " << dimY.start << "dimY.end: " << dimY.end; //qDebug() << "p1:" << p1 << " p2:" << p2; const qreal screenRangeX = qAbs ( p1.x() - p2.x() ); const qreal screenRangeY = qAbs ( p1.y() - p2.y() ); /* * let us paint the grid at a smaller resolution * the user can disable at any time * by setting the grid attribute to false * Same Value as for Cartesian Axis */ static const qreal GridLineDistanceTreshold = 4.0; // <Treshold> pixels between each grid line const qreal MinimumPixelsBetweenLines = GridLineDistanceTreshold; //qDebug() << "x step " << dimX.stepWidth << " y step " << dimY.stepWidth; //qreal unitFactorX = 1.0; // qreal unitFactorY = 1.0; //FIXME(khz): Remove this code, and do the calculation in the grid calc function if( ! dimX.isCalculated ){ while( screenRangeX / numberOfUnitLinesX <= MinimumPixelsBetweenLines ){ dimX.stepWidth *= 10.0; dimX.subStepWidth *= 10.0; numberOfUnitLinesX = qAbs( dimX.distance() / dimX.stepWidth ); } } if( dimX.subStepWidth && (screenRangeX / (dimX.distance() / dimX.subStepWidth) <= MinimumPixelsBetweenLines) ){ dimX.subStepWidth = 0.0; //qDebug() << "de-activating grid sub steps: not enough space"; } const bool drawUnitLinesX = gridAttrsX.isGridVisible() && (screenRangeX / numberOfUnitLinesX > MinimumPixelsBetweenLines); const bool drawUnitLinesY = gridAttrsY.isGridVisible() && (screenRangeY / numberOfUnitLinesY > MinimumPixelsBetweenLines); const bool isLogarithmicX = dimX.isCalculated && (dimX.calcMode == AbstractCoordinatePlane::Logarithmic ); const bool isLogarithmicY = (dimY.calcMode == AbstractCoordinatePlane::Logarithmic ); /* while ( !drawUnitLinesX ) { unitFactorX *= 10.0; drawUnitLinesX = screenRangeX / (numberOfUnitLinesX / unitFactorX) > MinimumPixelsBetweenLines; } while ( !drawUnitLinesY ) { unitFactorY *= 10.0; drawUnitLinesY = screenRangeY / (numberOfUnitLinesY / unitFactorY) > MinimumPixelsBetweenLines; } */ const bool drawSubGridLinesX = isLogarithmicX || ((dimX.subStepWidth != 0.0) && (screenRangeX / (numberOfUnitLinesX / dimX.stepWidth * dimX.subStepWidth) > MinimumPixelsBetweenLines) && gridAttrsX.isSubGridVisible()); const bool drawSubGridLinesY = isLogarithmicY || ((dimY.subStepWidth != 0.0) && (screenRangeY / (numberOfUnitLinesY / dimY.stepWidth * dimY.subStepWidth) > MinimumPixelsBetweenLines) && gridAttrsY.isSubGridVisible()); qreal minValueX = qMin( dimX.start, dimX.end ); qreal maxValueX = qMax( dimX.start, dimX.end ); qreal minValueY = qMin( dimY.start, dimY.end ); qreal maxValueY = qMax( dimY.start, dimY.end ); AbstractGrid::adjustLowerUpperRange( minValueX, maxValueX, dimX.stepWidth, true, true ); AbstractGrid::adjustLowerUpperRange( minValueY, maxValueY, dimY.stepWidth, true, true ); if ( drawSubGridLinesX ) { context->painter()->setPen( PrintingParameters::scalePen( gridAttrsX.subGridPen() ) ); qreal f = minValueX; qreal fLogSubstep = minValueX; int logSubstep = 0; while ( f <= maxValueX ) { QPointF topPoint( f, maxValueY ); QPointF bottomPoint( f, minValueY ); topPoint = plane->translate( topPoint ); bottomPoint = plane->translate( bottomPoint ); context->painter()->drawLine( topPoint, bottomPoint ); if ( isLogarithmicX ){ if( logSubstep == 9 ){ fLogSubstep *= ( fLogSubstep > 0.0 ) ? 10.0 : 0.1; if( fLogSubstep == 0.0 ) fLogSubstep = pow( 10.0, floor( log10( dimX.start ) ) ); logSubstep = 0; f = fLogSubstep; } else { f += fLogSubstep; } ++logSubstep; }else{ f += dimX.subStepWidth; } if(maxValueX == 0 && minValueX == 0) break; } } if ( drawSubGridLinesY ) { context->painter()->setPen( PrintingParameters::scalePen( gridAttrsY.subGridPen() ) ); qreal f = minValueY; qreal fLogSubstep = minValueY; int logSubstep = 0; while ( f <= maxValueY ) { //qDebug() << "sub grid line Y at" << f; QPointF leftPoint( minValueX, f ); QPointF rightPoint( maxValueX, f ); leftPoint = plane->translate( leftPoint ); rightPoint = plane->translate( rightPoint ); context->painter()->drawLine( leftPoint, rightPoint ); if ( isLogarithmicY ){ if( logSubstep == 9 ){ fLogSubstep *= ( fLogSubstep > 0.0 ) ? 10.0 : 0.1; if( fLogSubstep == 0.0 ) fLogSubstep = pow( 10.0, floor( log10( dimY.start ) ) ); logSubstep = 0; f = fLogSubstep; } else { f += fLogSubstep; } ++logSubstep; }else{ f += dimY.subStepWidth; } if(maxValueY == 0 && minValueY == 0) break; } } const bool drawXZeroLineX = dimX.isCalculated && gridAttrsX.zeroLinePen().style() != Qt::NoPen; const bool drawZeroLineY = gridAttrsY.zeroLinePen().style() != Qt::NoPen; const bool drawOuterX = gridAttrsX.isOuterLinesVisible(); const bool drawOuterY = gridAttrsY.isOuterLinesVisible(); const QPointF bottomRightPoint = plane->translate( QPointF( maxValueX, minValueY ) ); if ( drawUnitLinesX || drawXZeroLineX ) { //qDebug() << "E"; if ( drawUnitLinesX ) context->painter()->setPen( PrintingParameters::scalePen( gridAttrsX.gridPen() ) ); // const qreal minX = dimX.start; qreal f = minValueX; while ( f <= maxValueX ) { // PENDING(khz) FIXME: make draving/not drawing of Zero line more sophisticated?: const bool zeroLineHere = drawXZeroLineX && (f == 0.0); if ( drawUnitLinesX || zeroLineHere ){ //qDebug("main grid line X at: %f --------------------------",f); QPointF topPoint( f, maxValueY ); QPointF bottomPoint( f, minValueY ); topPoint = plane->translate( topPoint ); bottomPoint = plane->translate( bottomPoint ); const qreal penWidth = context->painter()->pen().widthF(); if ( zeroLineHere ) context->painter()->setPen( PrintingParameters::scalePen( gridAttrsX.zeroLinePen() ) ); if ( drawOuterX || (topPoint.x()> 2 * penWidth && topPoint.x()<bottomRightPoint.x()-2.0 * penWidth) ) context->painter()->drawLine( topPoint, bottomPoint ); if ( zeroLineHere ) context->painter()->setPen( PrintingParameters::scalePen( gridAttrsX.gridPen() ) ); } if ( isLogarithmicX ) { f *= ( f > 0.0 ) ? 10.0 : 0.1; if( f == 0.0 ) f = pow( 10.0, floor( log10( dimX.start ) ) ); } else f += dimX.stepWidth; if(maxValueX == 0 && minValueX == 0) break; } // draw the last line if not logarithmic calculation // we need the in order to get the right grid line painted // when f + dimX.stepWidth jump over maxValueX if ( ! isLogarithmicX && drawOuterX ) context->painter()->drawLine( plane->translate( QPointF( maxValueX, maxValueY ) ), plane->translate( QPointF( maxValueX, minValueY ) ) ); } if ( drawUnitLinesY || drawZeroLineY ) { //qDebug() << "F"; if ( drawUnitLinesY ) context->painter()->setPen( PrintingParameters::scalePen( gridAttrsY.gridPen() ) ); //const qreal minY = dimY.start; //qDebug("minY: %f maxValueY: %f dimY.stepWidth: %f",minY,maxValueY,dimY.stepWidth); qreal f = minValueY; while ( f <= maxValueY ) { // PENDING(khz) FIXME: make draving/not drawing of Zero line more sophisticated?: //qDebug("main grid line Y at: %f",f); const bool zeroLineHere = (f == 0.0); if ( drawUnitLinesY || zeroLineHere ){ QPointF leftPoint( minValueX, f ); QPointF rightPoint( maxValueX, f ); leftPoint = plane->translate( leftPoint ); rightPoint = plane->translate( rightPoint ); if ( zeroLineHere ) context->painter()->setPen( PrintingParameters::scalePen( gridAttrsY.zeroLinePen() ) ); if ( drawOuterY || (leftPoint.y()>2.0 && leftPoint.y()<bottomRightPoint.y()-2.0) ) context->painter()->drawLine( leftPoint, rightPoint ); if ( zeroLineHere ) context->painter()->setPen( PrintingParameters::scalePen( gridAttrsY.gridPen() ) ); } if ( isLogarithmicY ) { f *= ( f > 0.0 ) ? 10.0 : 0.1; if( f == 0.0 ) f = pow( 10.0, floor( log10( dimY.start ) ) ); } else f += dimY.stepWidth; if(maxValueY == 0 && minValueY == 0) break; } } //qDebug() << "Z"; }
DataDimensionsList CartesianGrid::calculateGrid( const DataDimensionsList& rawDataDimensions ) const { Q_ASSERT_X ( rawDataDimensions.count() == 2, "CartesianGrid::calculateGrid", "Error: calculateGrid() expects a list with exactly two entries." ); CartesianCoordinatePlane* plane = dynamic_cast<CartesianCoordinatePlane*>( mPlane ); Q_ASSERT_X ( plane, "CartesianGrid::calculateGrid", "Error: PaintContext::calculatePlane() called, but no cartesian plane set." ); DataDimensionsList l( rawDataDimensions ); // rule: Returned list is either empty, or it is providing two // valid dimensions, complete with two non-Zero step widths. if( isBoundariesValid( l ) ) { const QPointF translatedBottomLeft( plane->translateBack( plane->geometry().bottomLeft() ) ); const QPointF translatedTopRight( plane->translateBack( plane->geometry().topRight() ) ); //qDebug() << "CartesianGrid::calculateGrid() first:" << l.first().start << l.first().end << " last:" << l.last().start << l.last().end; //qDebug() << "CartesianGrid::calculateGrid() translated x:" << translatedBottomLeft.x() << translatedTopRight.x() << " y:" << translatedBottomLeft.y() << translatedTopRight.y(); //qDebug() << "CartesianGrid::calculateGrid() raw data y-range :" << l.last().end - l.last().start; //qDebug() << "CartesianGrid::calculateGrid() translated y-range:" << translatedTopRight.y() - translatedBottomLeft.y(); /* Code is obsolete. The dataset dimension of the diagram should *never* be > 1. if( l.first().isCalculated && plane->autoAdjustGridToZoom() && plane->axesCalcModeX() == CartesianCoordinatePlane::Linear && plane->zoomFactorX() > 1.0 ) { l.first().start = translatedBottomLeft.x(); l.first().end = translatedTopRight.x(); } */ const GridAttributes gridAttrsX( plane->gridAttributes( Qt::Horizontal ) ); const GridAttributes gridAttrsY( plane->gridAttributes( Qt::Vertical ) ); const DataDimension dimX = calculateGridXY( l.first(), Qt::Horizontal, gridAttrsX.adjustLowerBoundToGrid(), gridAttrsX.adjustUpperBoundToGrid() ); if( dimX.stepWidth ){ //qDebug("CartesianGrid::calculateGrid() l.last().start: %f l.last().end: %f", l.last().start, l.last().end); //qDebug(" l.first().start: %f l.first().end: %f", l.first().start, l.first().end); // one time for the min/max value const DataDimension minMaxY = calculateGridXY( l.last(), Qt::Vertical, gridAttrsY.adjustLowerBoundToGrid(), gridAttrsY.adjustUpperBoundToGrid() ); if( plane->autoAdjustGridToZoom() && plane->axesCalcModeY() == CartesianCoordinatePlane::Linear && plane->zoomFactorY() > 1.0 ) { l.last().start = translatedBottomLeft.y(); l.last().end = translatedTopRight.y(); } // and one other time for the step width const DataDimension dimY = calculateGridXY( l.last(), Qt::Vertical, gridAttrsY.adjustLowerBoundToGrid(), gridAttrsY.adjustUpperBoundToGrid() ); if( dimY.stepWidth ){ l.first().start = dimX.start; l.first().end = dimX.end; l.first().stepWidth = dimX.stepWidth; l.first().subStepWidth = dimX.subStepWidth; l.last().start = minMaxY.start; l.last().end = minMaxY.end; l.last().stepWidth = dimY.stepWidth; l.last().subStepWidth = dimY.subStepWidth; //qDebug() << "CartesianGrid::calculateGrid() final grid y-range:" << l.last().end - l.last().start << " step width:" << l.last().stepWidth << endl; // calculate some reasonable subSteps if the // user did not set the sub grid but did set // the stepWidth. // FIXME (Johannes) // the last (y) dimension is not always the dimension for the ordinate! // since there's no way to check for the orientation of this dimension here, // we cannot automatically assume substep values //if ( dimY.subStepWidth == 0 ) // l.last().subStepWidth = dimY.stepWidth/2; //else // l.last().subStepWidth = dimY.subStepWidth; } } } //qDebug() << "CartesianGrid::calculateGrid() final grid Y-range:" << l.last().end - l.last().start << " substep width:" << l.last().subStepWidth; //qDebug() << "CartesianGrid::calculateGrid() final grid X-range:" << l.first().end - l.first().start << " substep width:" << l.first().subStepWidth; return l; }