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 }