File indexing completed on 2024-12-15 04:02:28

0001 /*
0002  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
0003  *
0004  * This file is part of the KD Chart library.
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "KChartBarDiagram_p.h"
0010 
0011 #include "KChartDataValueAttributes.h"
0012 #include "KChartPainterSaver_p.h"
0013 
0014 using namespace KChart;
0015 
0016 BarDiagram::Private::Private( const Private& rhs )
0017     : AbstractCartesianDiagram::Private( rhs )
0018 {
0019 }
0020 
0021 void BarDiagram::BarDiagramType::paintBars( PaintContext* ctx, const QModelIndex& index, const QRectF& bar, qreal maxDepth )
0022 {
0023     PainterSaver painterSaver( ctx->painter() );
0024 
0025     //Pending Michel: configure threeDBrush settings - shadowColor etc...
0026     QBrush indexBrush( diagram()->brush( index ) );
0027     QPen indexPen( diagram()->pen( index ) );
0028 
0029     ctx->painter()->setRenderHint( QPainter::Antialiasing, diagram()->antiAliasing() );
0030     ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( index );
0031     if ( threeDAttrs.isEnabled() ) {
0032         indexBrush = threeDAttrs.threeDBrush( indexBrush, bar );
0033     }
0034     ctx->painter()->setBrush( indexBrush );
0035     ctx->painter()->setPen( PrintingParameters::scalePen( indexPen ) );
0036 
0037     if ( threeDAttrs.isEnabled() ) {
0038         if ( maxDepth ) {
0039             threeDAttrs.setDepth( -maxDepth );
0040         }
0041         //fixme adjust the painting to reasonable depth value
0042         const qreal usedDepth = threeDAttrs.depth() * ( type() == BarDiagram::Normal ? 0.25 : 1.0 );
0043 
0044         const QRectF isoRect = bar.translated( usedDepth, -usedDepth );
0045         // we need to find out if the height is negative
0046         // and in this case paint it up and down
0047         QPolygonF topPoints;
0048         if ( isoRect.height() < 0 ) {
0049             if ( !( type() == BarDiagram::Stacked && index.column() != 0 ) ) {
0050                 // fix it when several negative stacked values
0051                 topPoints << isoRect.bottomLeft() << isoRect.bottomRight()
0052                           << bar.bottomRight() << bar.bottomLeft();
0053             }
0054         } else {
0055             reverseMapper().addRect( index.row(), index.column(), isoRect );
0056             ctx->painter()->drawRect( isoRect );
0057             if ( !( type() == BarDiagram::Percent && isoRect.height() == 0 ) ) {
0058                 topPoints << bar.topLeft() << bar.topRight() << isoRect.topRight() << isoRect.topLeft();
0059             }
0060         }
0061 
0062         bool noClippingForTop = false;
0063         if ( !topPoints.isEmpty() ) {
0064             // Draw the top, if at least one of the top's points is
0065             // either inside or near at the edge of the coordinate plane:
0066             bool drawIt = false;
0067             bool hasPointOutside = false;
0068             const QRectF r( ctx->rectangle().adjusted( 0, -1, 1, 0 ) );
0069             for ( QPointF pt : qAsConst(topPoints) ) {
0070                 if ( r.contains( pt ) ) {
0071                     drawIt = true;
0072                 } else {
0073                     hasPointOutside = true;
0074                 }
0075             }
0076             if ( drawIt ) {
0077                 const PainterSaver p( ctx->painter() );
0078                 noClippingForTop = hasPointOutside && ctx->painter()->hasClipping();
0079                 if ( noClippingForTop ) {
0080                     ctx->painter()->setClipping( false );
0081                 }
0082                 reverseMapper().addPolygon( index.row(), index.column(), topPoints );
0083                 ctx->painter()->drawPolygon( topPoints );
0084             }
0085         }
0086 
0087         if ( bar.height() != 0 ) {
0088             const PainterSaver p( ctx->painter() );
0089             if ( noClippingForTop ) {
0090                 ctx->painter()->setClipping( false );
0091             }
0092             QPolygonF sidePoints;
0093             sidePoints << bar.topRight() << isoRect.topRight()
0094                        << isoRect.bottomRight() << bar.bottomRight();
0095             reverseMapper().addPolygon( index.row(), index.column(), sidePoints );
0096             ctx->painter()->drawPolygon( sidePoints );
0097         }
0098     }
0099 
0100     if ( bar.height() != 0 ) {
0101         reverseMapper().addRect( index.row(), index.column(), bar );
0102         ctx->painter()->drawRect( bar );
0103     }
0104 }
0105 
0106 AttributesModel* BarDiagram::BarDiagramType::attributesModel() const
0107 {
0108     return m_private->attributesModel;
0109 }
0110 
0111 QModelIndex BarDiagram::BarDiagramType::attributesModelRootIndex() const
0112 {
0113     return diagram()->attributesModelRootIndex();
0114 }
0115 
0116 BarDiagram* BarDiagram::BarDiagramType::diagram() const
0117 {
0118     return static_cast< BarDiagram* >( m_private->diagram );
0119 }
0120 
0121 void BarDiagram::BarDiagramType::calculateValueAndGapWidths( int rowCount, int colCount,
0122                                              qreal groupWidth,
0123                                              qreal& outBarWidth,
0124                                              qreal& outSpaceBetweenBars,
0125                                              qreal& outSpaceBetweenGroups )
0126 {
0127 
0128     Q_UNUSED( rowCount );
0129     BarAttributes ba = diagram()->barAttributes();
0130 
0131     // Pending Michel Fixme
0132     /* We are colCount groups to paint. Each group is centered around the
0133      * horizontal point position on the grid. The full area covers the
0134      * values -1 to colCount + 1. A bar has a relative width of one unit,
0135      * the gaps between bars are 0.5 wide, and the gap between groups is
0136      * also one unit, by default. */
0137 
0138     qreal units;
0139     if ( type() == Normal ) {
0140         units = colCount // number of bars in group * 1.0
0141                 + (colCount-1) * ba.barGapFactor() // number of bar gaps
0142                 + 1 * ba.groupGapFactor(); // number of group gaps
0143     } else {
0144         units = 1 + 1 * ba.groupGapFactor();
0145     }
0146 
0147     qreal unitWidth = groupWidth / units;
0148 
0149     if ( !ba.useFixedBarWidth() ) {
0150         outBarWidth = unitWidth;
0151     }
0152 
0153     outSpaceBetweenBars += unitWidth * ba.barGapFactor();
0154 
0155     // Pending Michel - minLimit: allow space between bars to be reduced until the bars are displayed next to each other.
0156     // is that what we want?
0157     // sebsauer; in the case e.g. CartesianCoordinatePlane::setHorizontalRangeReversed(true) was
0158     // used to reverse the values, we deal with negative outSpaceBetweenBars and unitWidth here
0159     // and since that's correct we don't like to lose e.g. the spacing here.
0160     //if ( outSpaceBetweenBars < 0 )
0161     //    outSpaceBetweenBars = 0;
0162 
0163     outSpaceBetweenGroups += unitWidth * ba.groupGapFactor();
0164 }
0165 
0166 ReverseMapper& BarDiagram::BarDiagramType::reverseMapper()
0167 {
0168     return m_private->reverseMapper;
0169 }
0170 
0171 CartesianDiagramDataCompressor& BarDiagram::BarDiagramType::compressor() const
0172 {
0173     return m_private->compressor;
0174 }