Esempio n. 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";
}
void NormalLineDiagram::paint( PaintContext* ctx )
{
    reverseMapper().clear();
    Q_ASSERT( dynamic_cast<CartesianCoordinatePlane*>( ctx->coordinatePlane() ) );
    CartesianCoordinatePlane* plane = static_cast<CartesianCoordinatePlane*>( ctx->coordinatePlane() );
    const int columnCount = compressor().modelDataColumns();
    const int rowCount = compressor().modelDataRows();
    if ( columnCount == 0 || rowCount == 0 ) return; // maybe blank out the area?

// FIXME integrate column index retrieval to compressor:
// the compressor should only pass through visiblel columns
//    int maxFound = 0;
//     {   // find the last column number that is not hidden
//         for( int column =  datasetDimension() - 1;
//              column <  columnCount;
//              column += datasetDimension() )
//             if( ! diagram()->isHidden( column ) )
//                 maxFound = column;
//     }
//    maxFound = columnCount;
    // ^^^ temp

    // Reverse order of data sets?
    bool rev = diagram()->reverseDatasetOrder();
    DataValueTextInfoList textInfoList;
    LineAttributesInfoList lineList;
    for( int column = rev ? columnCount - 1 : 0;
         rev ? (column >= 0) : (column < columnCount);
         rev ? --column : ++column ) {
        LineAttributes laPreviousCell;
        CartesianDiagramDataCompressor::DataPoint lastPoint;
        qreal lastAreaBoundingValue = 0;

        // Get min. y value, used as lower or upper bounding for area highlighting
        const qreal minYValue = qMin(plane->visibleDataRange().bottom(), plane->visibleDataRange().top());

        CartesianDiagramDataCompressor::CachePosition previousCellPosition;
        for ( int row = 0; row < rowCount; ++row ) {
            const CartesianDiagramDataCompressor::CachePosition position( row, column );
            // get where to draw the line from:
            CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );

            const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index );

            const LineAttributes laCell = diagram()->lineAttributes( sourceIndex );
            const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy();

            // lower or upper bounding for the highlighted area
            qreal areaBoundingValue;
            if ( laCell.areaBoundingDataset() != -1 ) {
                const CartesianDiagramDataCompressor::CachePosition areaBoundingCachePosition( row, laCell.areaBoundingDataset() );
                areaBoundingValue = compressor().data( areaBoundingCachePosition ).value;
            } else
                // Use min. y value (i.e. zero line in most cases) if no bounding dataset is set
                areaBoundingValue = minYValue;

            if( ISNAN( point.value ) )
            {
                switch( policy )
                {
                case LineAttributes::MissingValuesAreBridged:
                    // we just bridge both values
                    continue;
                case LineAttributes::MissingValuesShownAsZero:
                    // set it to zero
                    point.value = 0.0;
                    break;
                case LineAttributes::MissingValuesHideSegments:
                    // they're just hidden
                    break;
                default:
                    break;
                    // hm....
                }
            }

            // area corners, a + b are the line ends:
            const QPointF a( plane->translate( QPointF( diagram()->centerDataPoints() ? lastPoint.key + 0.5 : lastPoint.key, lastPoint.value ) ) );
            const QPointF b( plane->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, point.value ) ) );
            const QPointF c( plane->translate( QPointF( diagram()->centerDataPoints() ? lastPoint.key + 0.5 : lastPoint.key, lastAreaBoundingValue ) ) );
            const QPointF d( plane->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, areaBoundingValue ) ) );
            // add the line to the list:
           // add data point labels:
            const PositionPoints pts = PositionPoints( b, a, d, c );
            // if necessary, add the area to the area list:
            QList<QPolygonF> areas;
            if ( !ISNAN( point.value ) && !ISNAN( lastPoint.value ) && laCell.displayArea() ) {
                areas << ( QPolygonF() << a << b << d << c );//polygon;
            }
            // add the pieces to painting if this is not hidden:
            if ( ! point.hidden && !ISNAN( point.value ) )
            {
                appendDataValueTextInfoToList( diagram(), textInfoList, sourceIndex, &position,
                                               pts, Position::NorthWest, Position::SouthWest,
                                               point.value );
                paintAreas( ctx, attributesModel()->mapToSource( lastPoint.index ), areas, laCell.transparency() );
                // position 0 is not really painted, since it takes two points to make a line :-)
                if( row > 0 && !ISNAN( lastPoint.value ) )
                    lineList.append( LineAttributesInfo( sourceIndex, a, b ) );
            }

            // wrap it up:
            previousCellPosition = position;
            laPreviousCell = laCell;
            lastAreaBoundingValue = areaBoundingValue;
            lastPoint = point;
        }

    }

    LineAttributes::MissingValuesPolicy policy = LineAttributes::MissingValuesAreBridged; //unused
    paintElements( ctx, textInfoList, lineList, policy );
}
Esempio n. 3
0
void NormalLineDiagram::paint( PaintContext* ctx )
{
    reverseMapper().clear();
    Q_ASSERT( dynamic_cast<CartesianCoordinatePlane*>( ctx->coordinatePlane() ) );
    CartesianCoordinatePlane* plane = static_cast<CartesianCoordinatePlane*>( ctx->coordinatePlane() );
    const int columnCount = compressor().modelDataColumns();
    const int rowCount = compressor().modelDataRows();
    if ( columnCount == 0 || rowCount == 0 ) return; // maybe blank out the area?

    // Reverse order of data sets?
    bool rev = diagram()->reverseDatasetOrder();
    LabelPaintCache lpc;
    LineAttributesInfoList lineList;

    const int step = rev ? -1 : 1;
    const int end = rev ? -1 : columnCount;
    for ( int column = rev ? columnCount - 1 : 0; column != end; column += step ) {
        LineAttributes laPreviousCell;
        CartesianDiagramDataCompressor::DataPoint lastPoint;
        qreal lastAreaBoundingValue = 0;

        // Get min. y value, used as lower or upper bounding for area highlighting
        const qreal minYValue = qMin(plane->visibleDataRange().bottom(), plane->visibleDataRange().top());

        CartesianDiagramDataCompressor::CachePosition previousCellPosition;
        for ( int row = 0; row < rowCount; ++row ) {
            const CartesianDiagramDataCompressor::CachePosition position( row, column );
            // get where to draw the line from:
            CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
            if ( point.hidden ) {
                continue;
            }

            const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index );

            const LineAttributes laCell = diagram()->lineAttributes( sourceIndex );
            const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy();

            // lower or upper bounding for the highlighted area
            qreal areaBoundingValue;
            if ( laCell.areaBoundingDataset() != -1 ) {
                const CartesianDiagramDataCompressor::CachePosition areaBoundingCachePosition( row, laCell.areaBoundingDataset() );
                areaBoundingValue = compressor().data( areaBoundingCachePosition ).value;
            } else {
                // Use min. y value (i.e. zero line in most cases) if no bounding dataset is set
                areaBoundingValue = minYValue;
            }

            if ( ISNAN( point.value ) )
            {
                switch ( policy )
                {
                case LineAttributes::MissingValuesAreBridged:
                    // we just bridge both values
                    continue;
                case LineAttributes::MissingValuesShownAsZero:
                    // set it to zero
                    point.value = 0.0;
                    break;
                case LineAttributes::MissingValuesHideSegments:
                    // they're just hidden
                    break;
                default:
                    break;
                    // hm....
                }
            }

            if ( !ISNAN( point.value ) ) {
                // area corners, a + b are the line ends:
                const qreal offset = diagram()->centerDataPoints() ? 0.5 : 0;
                const QPointF a( plane->translate( QPointF( lastPoint.key + offset, lastPoint.value ) ) );
                const QPointF b( plane->translate( QPointF( point.key + offset, point.value ) ) );
                const QPointF c( plane->translate( QPointF( lastPoint.key + offset, lastAreaBoundingValue ) ) );
                const QPointF d( plane->translate( QPointF( point.key + offset, areaBoundingValue ) ) );
                const PositionPoints pts = PositionPoints( b, a, d, c );

                // add label
                m_private->addLabel( &lpc, sourceIndex, &position, pts, Position::NorthWest,
                                     Position::NorthWest, point.value );

                // add line and area, if switched on and we have a current and previous value
                if ( !ISNAN( lastPoint.value ) ) {
                    lineList.append( LineAttributesInfo( sourceIndex, a, b ) );

                    if ( laCell.displayArea() ) {
                        QList<QPolygonF> areas;
                        areas << ( QPolygonF() << a << b << d << c );
                        PaintingHelpers::paintAreas( m_private, ctx, attributesModel()->mapToSource( lastPoint.index ),
                                                     areas, laCell.transparency() );
                    }
                }
            }

            previousCellPosition = position;
            laPreviousCell = laCell;
            lastAreaBoundingValue = areaBoundingValue;
            lastPoint = point;
        }
    }

    // paint the lines
    PaintingHelpers::paintElements( m_private, ctx, lpc, lineList );
}