kchart

KDChartRingPainter.cpp
1/*
2 KDChart - a multi-platform charting engine
3 */
4
5/****************************************************************************
6 ** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. All rights reserved.
7 **
8 ** This file is part of the KDChart library.
9 **
10 ** This file may be distributed and/or modified under the terms of the
11 ** GNU General Public License version 2 as published by the Free Software
12 ** Foundation and appearing in the file LICENSE.GPL included in the
13 ** packaging of this file.
14 **
15 ** Licensees holding valid commercial KDChart licenses may use this file in
16 ** accordance with the KDChart Commercial License Agreement provided with
17 ** the Software.
18 **
19 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
20 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21 **
22 ** See http://www.klaralvdalens-datakonsult.se/?page=products for
23 ** information about KDChart Commercial License Agreements.
24 **
25 ** Contact info@klaralvdalens-datakonsult.se if any conditions of this
26 ** licensing are not clear to you.
27 **
28 **********************************************************************/
29#include "KDChartRingPainter.h"
30#include "KDChartParams.h"
31
32#include <tqpainter.h>
33#include <tqvaluestack.h>
34
35#include <stdlib.h>
36
37#define DEGTORAD(d) (d)*M_PI/180
38
53KDChartPainter( params )
54{
55 // This constructor intentionally left blank so far; we cannot setup the
56 // geometry yet since we do not know the size of the painter.
57}
58
59
64{
65 // intentionally left blank
66}
67
68
78void KDChartRingPainter::paintData( TQPainter* painter,
80 bool paint2nd,
81 KDChartDataRegionList* regions )
82{
83 uint chart = paint2nd ? 1 : 0;
84
85 TQRect ourClipRect( _dataRect );
86
87 const TQWMatrix & world = painter->worldMatrix();
88 ourClipRect = world.mapRect( ourClipRect );
89 ourClipRect.setTop(ourClipRect.top()-1);
90 ourClipRect.setLeft(ourClipRect.left()-1);
91 ourClipRect.setBottom(ourClipRect.bottom()+1);
92 ourClipRect.setRight(ourClipRect.right()+1);
93 painter->setClipRect( ourClipRect );
94
95 uint datasetStart, datasetEnd;
96 if ( params()->neverUsedSetChartSourceMode()
97 || !params()->findDataset( KDChartParams::DataEntry,
98 datasetStart,
99 datasetEnd,
100 chart ) ) {
101 uint maxRow, maxRowMinus1;
102 switch ( data->usedRows() ) {
103 case 0:
104 return ;
105 case 1:
106 maxRow = 0;
107 maxRowMinus1 = 0;
108 break;
109 default:
110 maxRow = data->usedRows() - 1;
111 maxRowMinus1 = maxRow - 1;
112 }
113 datasetStart = paint2nd ? maxRow
114 : 0;
115 datasetEnd = paint2nd ? maxRow
116 : ( ( KDChartParams::NoType
117 == params()->additionalChartType() )
118 ? maxRow
119 : maxRowMinus1 );
120 }
121 uint datasetNum = abs( (int)( datasetEnd - datasetStart ) + 1 );
122
123
124 // Number of values: If -1, use all values, otherwise use the
125 // specified number of values.
126 if ( params()->numValues() != -1 )
127 _numValues = params()->numValues();
128 else
129 _numValues = data->usedCols();
130
131 // compute position
132 _size = TQMIN( _dataRect.width(), _dataRect.height() ); // initial size
133 // if the rings explode, we need to give them additional space =>
134 // make the basic size smaller
135 if ( params()->explode() ) {
136 double doubleSize = ( double ) _size;
137 doubleSize /= ( 1.0 + params()->explodeFactor() * 2 );
138 _size = ( int ) doubleSize;
139 }
140
141 int x = ( _dataRect.width() == _size ) ? 0 : ( ( _dataRect.width() - _size ) / 2 );
142 int y = ( _dataRect.height() == _size ) ? 0 : ( ( _dataRect.height() - _size ) / 2 );
143 _position = TQRect( x, y, _size, _size );
144 _position.moveBy( _dataRect.left(), _dataRect.top() );
145
146 // We need the row sums anyway later, so we can just as well compute them
147 // here, because we need them in case of relative ring thicknesses.
148 TQMemArray<double> rowsums;
149 double totalSum = 0.0;
150 rowsums.resize( datasetEnd+1 ); // not datasetNum!
151 for( int d1 = (int)datasetStart; d1 <= (int)datasetEnd; d1++ ) {
152 rowsums[d1] = data->rowAbsSum( d1 );
153 totalSum += rowsums[d1];
154 }
155
156 TQMemArray<int> ringthicknesses;
157 ringthicknesses.resize( datasetEnd+1 ); // not datasetNum!
158
159 // constant ring thickness
160 int ringthickness = _size / ( datasetNum * 2 );
161 // Never let the ring thickness be more than 1/10 of the size to
162 // ensure "ringness"
163 if( ringthickness > ( _size/10 ) )
164 ringthickness = _size / 10;
165
166 for( int d2 = (int)datasetStart; d2 <= (int)datasetEnd; d2++ )
167 if( params()->relativeRingThickness() ) {
168 // 50% should be the same thickness as the one used when ring
169 // thickness is constant.
170 ringthicknesses[d2] = (uint)floor( (rowsums[d2] / totalSum) *
171 ( 2.0 * (double)ringthickness ) + 0.5 );
172 } else {
173 ringthicknesses[d2] = ringthickness;
174 }
175
176 int currentouterradius = _size/2;
177
178 // Loop through all the displayable datasets; each dataset is one ring
179 for( int dataset = (int)datasetStart; dataset <= (int)datasetEnd; dataset++ ) {
180 double sectorsPerValue = 5760.0 / rowsums[dataset]; // 5760 == 16*360, number of sections in TQt circle
181 //int sectorsPerValueI = static_cast<int>( sectorsPerValue );
182 double currentstartpos = (double)params()->ringStart() * 16.0;
183 // Loop through all the values; each value is one piece on the ring.
184 TQVariant vValY;
185 for( int value = 0; value < _numValues; value++ ) {
186 // is there anything at all at this value?
187 double cellValue = 0.0;
188 if( data->cellCoord( dataset, value, vValY, 1 ) &&
189 TQVariant::Double == vValY.type() ){
190 cellValue = fabs( vValY.toDouble() );
191 // Explosion: Only explode if explosion is turned on generally
192 // and we are on the first ring. Besides, if there is a list
193 // of explodeable values, the current value must be on this
194 // list.
195
196 TQValueList<int> explodeList = params()->explodeValues();
197 bool explode = params()->explode() && // explosion is on at all
198 ( dataset == (int)datasetStart ) && // outermost ring
199 ( ( explodeList.count() == 0 ) || // either nothing on explode list
200 ( explodeList.find( value ) != explodeList.end() ) ); // or pie is on it
201
202 drawOneSegment( painter,
203 currentouterradius,
204 currentouterradius-ringthicknesses[dataset],
205 currentstartpos,
206 sectorsPerValue * cellValue,
207 dataset, value, chart, explode, regions );
208 }
209 currentstartpos += sectorsPerValue * cellValue;
210 }
211 currentouterradius -= ringthicknesses[dataset];
212 }
213}
214
215
216
217void KDChartRingPainter::drawOneSegment( TQPainter* painter,
218 uint outerRadius,
219 uint innerRadius,
220 double startAngle,
221 double angles,
222 uint dataset,
223 uint value,
224 uint chart,
225 bool explode,
226 KDChartDataRegionList* regions )
227{
228 // special case for full circle
229 if( angles == 5760.0 )
230 startAngle = 0.0;
231
232 painter->setPen( TQPen( params()->outlineDataColor(),
233 params()->outlineDataLineWidth() ) );
234 painter->setBrush( params()->dataColor( value ) );
235
236 uint outerRadius2 = outerRadius * 2;
237 uint innerRadius2 = innerRadius * 2;
238
239 TQRect drawPosition = _position;
240 if ( explode ) {
241 // need to compute a new position for each pie
242 double explodeAngle = ( startAngle + angles / 2.0 ) / 16.0;
243 double explodeAngleRad = DEGTORAD( explodeAngle );
244 double cosAngle = cos( explodeAngleRad );
245 double sinAngle = -sin( explodeAngleRad );
246
247 // find the explode factor for this particular ring segment
248 double explodeFactor = 0.0;
249 TQMap<int,double> explodeFactors = params()->explodeFactors();
250 if( !explodeFactors.contains( value ) ) // not on factors list, use default
251 explodeFactor = params()->explodeFactor();
252 else // on factors list, use segment-specific value
253 explodeFactor = explodeFactors[value];
254
255 double explodeX = explodeFactor * _size * cosAngle;
256 double explodeY = explodeFactor * _size * sinAngle;
257 drawPosition.moveBy( static_cast<int>( explodeX ), static_cast<int>( explodeY ) );
258 }
259
260 TQRect outerRect( drawPosition.x() +
261 ( drawPosition.width() - outerRadius2 ) / 2,
262 drawPosition.y() +
263 ( drawPosition.height() - outerRadius2 ) / 2,
264 outerRadius2, outerRadius2 );
265 TQRect innerRect( drawPosition.x() +
266 ( drawPosition.width() - innerRadius2 ) / 2,
267 drawPosition.y() +
268 ( drawPosition.height() - innerRadius2 ) / 2,
269 innerRadius2, innerRadius2 );
270
271 // Start with getting the points for the inner arc.
272 TQPointArray innerArc;
273 makeArc( innerArc, innerRect, startAngle, angles );
274
275 // And the points for the outer arc
276 TQPointArray outerArc;
277 makeArc( outerArc, outerRect, startAngle, angles );
278
279 // Now copy the points from the outer arc in the reverse order onto the
280 // inner arc array and draw that.
281 uint innerArcPoints = innerArc.size();
282 uint outerArcPoints = outerArc.size();
283 innerArc.resize( innerArcPoints + outerArcPoints );
284 for ( int i = outerArcPoints - 1; i >= 0; i-- ) {
285 innerArc.setPoint( innerArcPoints+outerArcPoints-i-1,
286 outerArc.point( i ) );
287 }
288
289 painter->drawPolygon( innerArc );
290 if ( regions /* && ( innerArc.size() > 2 )*/ ) {
291 KDChartDataRegion* datReg = new KDChartDataRegion( dataset,
292 value,
293 chart,
294 innerArc );
295
296 const int aA = static_cast<int>( startAngle );
297 const int aM = static_cast<int>( startAngle + angles / 2.0 );
298 const int aZ = static_cast<int>( startAngle + angles );
299
300 datReg->points[ KDChartEnums::PosTopLeft ]
301 = pointOnCircle( outerRect, aZ );
302 datReg->points[ KDChartEnums::PosTopCenter ]
303 = pointOnCircle( outerRect, aM );
304 datReg->points[ KDChartEnums::PosTopRight ]
305 = pointOnCircle( outerRect, aA );
306
307 datReg->points[ KDChartEnums::PosBottomLeft ]
308 = pointOnCircle( innerRect, aZ );
309 datReg->points[ KDChartEnums::PosBottomCenter ]
310 = pointOnCircle( innerRect, aM );
311 datReg->points[ KDChartEnums::PosBottomRight ]
312 = pointOnCircle( innerRect, aA );
313
314 datReg->points[ KDChartEnums::PosCenterLeft ]
315 = TQPoint( ( datReg->points[ KDChartEnums::PosTopLeft ].x()
316 + datReg->points[ KDChartEnums::PosBottomLeft ].x() ) / 2,
317 ( datReg->points[ KDChartEnums::PosTopLeft ].y()
318 + datReg->points[ KDChartEnums::PosBottomLeft ].y() ) / 2 );
319 datReg->points[ KDChartEnums::PosCenter ]
320 = TQPoint( ( datReg->points[ KDChartEnums::PosTopCenter ].x()
321 + datReg->points[ KDChartEnums::PosBottomCenter ].x() ) / 2,
322 ( datReg->points[ KDChartEnums::PosTopCenter ].y()
323 + datReg->points[ KDChartEnums::PosBottomCenter ].y() ) / 2 );
324 datReg->points[ KDChartEnums::PosCenterRight ]
325 = TQPoint( ( datReg->points[ KDChartEnums::PosTopRight ].x()
326 + datReg->points[ KDChartEnums::PosBottomRight ].x() ) / 2,
327 ( datReg->points[ KDChartEnums::PosTopRight ].y()
328 + datReg->points[ KDChartEnums::PosBottomRight ].y() ) / 2 );
329
330 // test the 9 positions:
331 /*
332 painter->drawEllipse( datReg->points[ KDChartEnums::PosTopLeft ].x() - 2,
333 datReg->points[ KDChartEnums::PosTopLeft ].y() - 2, 5, 5);
334 painter->drawEllipse( datReg->points[ KDChartEnums::PosCenterLeft ].x() - 2,
335 datReg->points[ KDChartEnums::PosCenterLeft ].y() - 2, 5, 5);
336 painter->drawEllipse( datReg->points[ KDChartEnums::PosBottomLeft ].x() - 2,
337 datReg->points[ KDChartEnums::PosBottomLeft ].y() - 2, 5, 5);
338
339 tqDebug( "\ncenter: (%i, %i)",
340 datReg->points[ KDChartEnums::PosCenter ].x(),
341 datReg->points[ KDChartEnums::PosCenter ].y() );
342 painter->drawEllipse( datReg->points[ KDChartEnums::PosTopCenter ].x() - 2,
343 datReg->points[ KDChartEnums::PosTopCenter ].y() - 2, 5, 5);
344 painter->drawEllipse( datReg->points[ KDChartEnums::PosCenter ].x() - 2,
345 datReg->points[ KDChartEnums::PosCenter ].y() - 2, 5, 5);
346 painter->drawEllipse( datReg->points[ KDChartEnums::PosBottomCenter].x() - 2,
347 datReg->points[ KDChartEnums::PosBottomCenter].y() - 2, 5, 5);
348
349 painter->drawRect( datReg->points[ KDChartEnums::PosCenterRight ].x() - 2,
350 datReg->points[ KDChartEnums::PosCenterRight ].y() - 2, 5, 5);
351 //painter->drawRect( datReg->points[ KDChartEnums::PosTopRight ].x() - 2,
352 // datReg->points[ KDChartEnums::PosTopRight ].y() - 2, 5, 5);
353 painter->drawRect( datReg->points[ KDChartEnums::PosBottomRight ].x() - 2,
354 datReg->points[ KDChartEnums::PosBottomRight ].y() - 2, 5, 5);
355 */
356 datReg->startAngle = static_cast<int>( startAngle );
357 datReg->angleLen = static_cast<int>( angles );
358 regions->append( datReg );
359 }
360}
361
362
376TQString KDChartRingPainter::fallbackLegendText( uint dataset ) const
377{
378 return TQObject::tr( "Item " ) + TQString::number( dataset + 1 );
379}
380
381
392{
393 return data->usedCols();
394}
Header for all common chart parameters.
The collection class used by KD Chart to store data region information.
An abstract base class that defines an interface for classes that implement chart drawing.
Bundles the display parameters of a chart.
ExplodeFactorsMap explodeFactors() const
Returns the list of explode factors.
bool relativeRingThickness() const
Returns whether the ring thickness is relative to the sum of values in the dataset that the ring repr...
int numValues() const
Returns how many of the values should be shown.
ChartType additionalChartType() const
Returns the additional chart type configured in this params object.
TQValueList< int > explodeValues() const
Returns which values are exploded in a pie or ring chart.
bool explode() const
Returns whether the pie or ring chart should be exploding or not.
int ringStart() const
Returns the starting point of the ring circle.
double explodeFactor() const
Returns the default explode factor, i.e., the explode factor used for those segments which should be ...
virtual ~KDChartRingPainter()
Destructor.
virtual void paintData(TQPainter *painter, KDChartTableDataBase *data, bool paint2nd, KDChartDataRegionList *regions=0)
Paints the actual data area.
virtual TQString fallbackLegendText(uint dataset) const
This method is a specialization that returns a fallback legend text appropriate for rings where the f...
KDChartRingPainter(KDChartParams *params)
Constructor.
virtual uint numLegendFallbackTexts(KDChartTableDataBase *data) const
This methods returns the number of elements to be shown in the legend in case fallback texts are used...
KD Chart's build-in table data for an easy way of storing data values.
virtual uint usedCols() const
Returns the number of cols in the table that actually contain data.
virtual bool cellCoord(uint _row, uint _col, TQVariant &_value, int coordinate=1) const =0
Returns one of the coordinate data value(s) stored in a cell.
virtual uint usedRows() const
Returns the number of rows in the table that actually contain data.