Example #1
0
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";
}
Example #2
0
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;
}