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

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 "KChartNormalBarDiagram_p.h"
0010 
0011 #include <QModelIndex>
0012 
0013 #include "KChartBarDiagram.h"
0014 #include "KChartTextAttributes.h"
0015 #include "KChartAttributesModel.h"
0016 #include "KChartAbstractCartesianDiagram.h"
0017 
0018 using namespace KChart;
0019 using namespace std;
0020 
0021 NormalBarDiagram::NormalBarDiagram( BarDiagram* d )
0022     : BarDiagramType( d )
0023 {
0024 }
0025 
0026 BarDiagram::BarType NormalBarDiagram::type() const
0027 {
0028     return BarDiagram::Normal;
0029 }
0030 
0031 const QPair<QPointF, QPointF> NormalBarDiagram::calculateDataBoundaries() const
0032 {
0033     const int rowCount = compressor().modelDataRows();
0034     const int colCount = compressor().modelDataColumns();
0035 
0036     const qreal xMin = 0.0;
0037     const qreal xMax = rowCount;
0038     qreal yMin = 0.0;
0039     qreal yMax = 0.0;
0040 
0041     qreal usedDepth = 0;
0042 
0043     bool isFirst = true;
0044     for ( int column = 0; column < colCount; ++column ) {
0045         for ( int row = 0; row < rowCount; ++row ) {
0046             const CartesianDiagramDataCompressor::CachePosition position( row, column );
0047             const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
0048             const qreal value = ISNAN( point.value ) ? 0.0 : point.value;
0049 
0050             QModelIndex sourceIndex = attributesModel()->mapToSource( point.index );
0051             ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( sourceIndex );
0052 
0053             if ( threeDAttrs.isEnabled() )
0054                 usedDepth = qMax( usedDepth, threeDAttrs.depth() );
0055 
0056             // this is always true yMin can be 0 in case all values
0057             // are the same
0058             // same for yMax it can be zero if all values are negative
0059             if ( isFirst ) {
0060                 yMin = value;
0061                 yMax = value;
0062                 isFirst = false;
0063             } else {
0064                 yMin = qMin( yMin, value );
0065                 yMax = qMax( yMax, value );
0066             }
0067         }
0068     }
0069 
0070     // special cases
0071     if ( yMax == yMin ) {
0072         if ( yMin == 0.0 ) {
0073             yMax = 0.1; // we need at least a range
0074         } else if ( yMax < 0.0 ) {
0075             yMax = 0.0; // extend the range to zero
0076         } else if ( yMin > 0.0 ) {
0077             yMin = 0.0; // dito
0078         }
0079     }
0080 
0081     return QPair< QPointF, QPointF >( QPointF( xMin, yMin ), QPointF( xMax, yMax ) );
0082 }
0083 
0084 void NormalBarDiagram::paint( PaintContext* ctx )
0085 {
0086     reverseMapper().clear();
0087 
0088     const QPair<QPointF,QPointF> boundaries = diagram()->dataBoundaries(); // cached
0089 
0090     const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ;
0091     const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second );
0092 
0093     const int rowCount = attributesModel()->rowCount(attributesModelRootIndex());
0094     const int colCount = attributesModel()->columnCount(attributesModelRootIndex());
0095 
0096     BarAttributes ba = diagram()->barAttributes();
0097     ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes();
0098 
0099     //we need some margin (hence the 2.5) for the three dimensional depth
0100     const qreal threeDepthMargin = ( threeDAttrs.isEnabled() ) ? 2.5 * threeDAttrs.depth() : 0;
0101 
0102     qreal barWidth = 0;
0103     qreal maxDepth = 0;
0104     qreal width = boundRight.x() - boundLeft.x() - threeDepthMargin;
0105     qreal groupWidth = width / rowCount;
0106     qreal spaceBetweenBars = 0;
0107     qreal spaceBetweenGroups = 0;
0108 
0109     if ( ba.useFixedBarWidth() ) {
0110 
0111         barWidth = ba.fixedBarWidth();
0112         groupWidth += barWidth;
0113 
0114         // Pending Michel set a min and max value for the groupWidth
0115         // related to the area.width
0116         if ( groupWidth < 0 )
0117             groupWidth = 0;
0118 
0119         if ( groupWidth  * rowCount > width )
0120             groupWidth = width / rowCount;
0121     }
0122 
0123     // maxLimit: allow the space between bars to be larger until area.width()
0124     // is covered by the groups.
0125     qreal maxLimit = rowCount * ( groupWidth + ( ( colCount - 1 ) * ba.fixedDataValueGap() ) );
0126 
0127     //Pending Michel: FixMe
0128     if ( ba.useFixedDataValueGap() ) {
0129         if ( width > maxLimit ) {
0130             spaceBetweenBars += ba.fixedDataValueGap();
0131         } else {
0132             spaceBetweenBars = ( ( width / rowCount ) - groupWidth ) / ( colCount - 1 );
0133         }
0134     }
0135 
0136     if ( ba.useFixedValueBlockGap() ) {
0137         spaceBetweenGroups += ba.fixedValueBlockGap();
0138     }
0139 
0140     calculateValueAndGapWidths( rowCount, colCount, groupWidth,
0141                                 barWidth, spaceBetweenBars, spaceBetweenGroups );
0142 
0143     LabelPaintCache lpc;
0144 
0145     for ( int row = 0; row < rowCount; ++row ) {
0146         qreal offset = -groupWidth / 2 + spaceBetweenGroups / 2;
0147 
0148         if ( ba.useFixedDataValueGap() ) {
0149             if ( spaceBetweenBars > 0 ) {
0150                 if ( width > maxLimit ) {
0151                     offset -= ba.fixedDataValueGap();
0152                 } else {
0153                     offset -= ( ( width / rowCount ) - groupWidth ) / ( colCount - 1 );
0154                 }
0155             } else {
0156                 offset += barWidth / 2;
0157             }
0158         }
0159 
0160         for ( int column = 0; column < colCount; ++column ) {
0161             // paint one group
0162             const CartesianDiagramDataCompressor::CachePosition position( row,  column );
0163             const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
0164             const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index );
0165             const qreal value = point.value;//attributesModel()->data( sourceIndex ).toReal();
0166             if ( ! point.hidden && !ISNAN( value ) ) {
0167                 QPointF topPoint = ctx->coordinatePlane()->translate( QPointF( point.key + 0.5, value ) );
0168                 QPointF bottomPoint =  ctx->coordinatePlane()->translate( QPointF( point.key, 0 ) );
0169 
0170                 if ( threeDAttrs.isEnabled() ) {
0171                     const qreal usedDepth = threeDAttrs.depth() / 4;
0172                     topPoint.setY( topPoint.y() + usedDepth + 1.0 );
0173                 }
0174 
0175                 const qreal barHeight = bottomPoint.y() - topPoint.y();
0176                 topPoint.setX( topPoint.x() + offset );
0177                 const QRectF rect( topPoint, QSizeF( barWidth, barHeight ) );
0178                 m_private->addLabel( &lpc, sourceIndex, nullptr, PositionPoints( rect ), Position::North,
0179                                      Position::South, point.value );
0180                 paintBars( ctx, sourceIndex, rect, maxDepth );
0181             }
0182             offset += barWidth + spaceBetweenBars;
0183         }
0184     }
0185     m_private->paintDataValueTextsAndMarkers( ctx, lpc, false );
0186 }