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 ); }
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 ); }