kchart

KDChartPiePainter.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 "KDChartEnums.h"
30#include "KDChartPiePainter.h"
31#include "KDChartParams.h"
32
33#include <tqpainter.h>
34#include <tqvaluestack.h>
35#include <tqmessagebox.h>
36
37#define DEGTORAD(d) (d)*M_PI/180
38
39#include <math.h>
40
48
55KDChartPainter( params )
56{
57 // This constructor intentionally left blank so far; we cannot setup the
58 // geometry yet since we do not know the size of the painter.
59}
60
61
66{
67 // intentionally left blank
68}
69
70//static bool bHelp=true;
71
81void KDChartPiePainter::paintData( TQPainter* painter,
83 bool paint2nd,
84 KDChartDataRegionList* regions )
85{
86//bHelp=true;
87 uint chart = paint2nd ? 1 : 0;
88
89 TQRect ourClipRect( _dataRect );
90 ourClipRect.addCoords( -1,-1,1,1 );
91
92 const TQWMatrix & world = painter->worldMatrix();
93 ourClipRect = world.mapRect( ourClipRect );
94 painter->setClipRect( ourClipRect );
95
96 // find which dataset to paint
97 uint dataset;
98 if ( !params()->findDataset( KDChartParams::DataEntry
99 ,
100 dataset, dataset ) ) {
101 return ; // nothing to draw
102 }
103
104 if ( dataset == KDCHART_ALL_DATASETS )
105 // setChartSourceMode() has not been used (or all datasets have been
106 // configured to be used); use the first dataset by
107 // default
108 dataset = 0;
109
110
111 // Number of values: If -1, use all values, otherwise use the
112 // specified number of values.
113 if ( params()->numValues() != -1 )
114 _numValues = params()->numValues();
115 else
116 _numValues = data->usedCols();
117
118 _startAngles.resize( _numValues );
119 _angleLens.resize( _numValues );
120
121 // compute position
122 _size = TQMIN( _dataRect.width(), _dataRect.height() ); // initial size
123 // if the pies explode, we need to give them additional space =>
124 // make the basic size smaller
125 if ( params()->explode() ) {
126 double doubleSize = ( double ) _size;
127 doubleSize /= ( 1.0 + params()->explodeFactor() * 2 );
128 _size = ( int ) doubleSize;
129 }
130
131 int sizeFor3DEffect = 0;
132 if ( !params()->threeDPies() ) {
133
134 int x = ( _dataRect.width() == _size ) ? 0 : ( ( _dataRect.width() - _size ) / 2 );
135 int y = ( _dataRect.height() == _size ) ? 0 : ( ( _dataRect.height() - _size ) / 2 );
136 _position = TQRect( x, y, _size, _size );
137 _position.moveBy( _dataRect.left(), _dataRect.top() );
138 } else {
139 // threeD: width is the maximum possible width; height is 1/2 of that
140 int x = ( _dataRect.width() == _size ) ? 0 : ( ( _dataRect.width() - _size ) / 2 );
141 int height = _size;
142 // make sure that the height plus the threeDheight is not more than the
143 // available size
144 if ( params()->threeDPieHeight() >= 0 ) {
145 // positive pie height: absolute value
146 sizeFor3DEffect = params()->threeDPieHeight();
147 height = _size - sizeFor3DEffect;
148 } else {
149 // negative pie height: relative value
150 sizeFor3DEffect = -( int ) ( ( ( double ) params()->threeDPieHeight() / 100.0 ) * ( double ) height );
151 height = _size - sizeFor3DEffect;
152 }
153 int y = ( _dataRect.height() == height ) ? 0 : ( ( _dataRect.height() - height - sizeFor3DEffect ) / 2 );
154
155 _position = TQRect( _dataRect.left() + x, _dataRect.top() + y,
156 _size, height );
157 // _position.moveBy( _dataRect.left(), _dataRect.top() );
158 }
159
160 double sum = data->rowAbsSum( dataset );
161 if( sum==0 ) //nothing to draw
162 return;
163 double sectorsPerValue = 5760.0 / sum; // 5760 == 16*360, number of sections in TQt circle
164
165 int currentValue = params()->pieStart() * 16;
166 bool atLeastOneValue = false; // guard against completely empty tables
167 TQVariant vValY;
168 for ( int value = 0; value < _numValues; value++ ) {
169 // is there anything at all at this value
170 /* see above for meaning of 16 */
171
172 if( data->cellCoord( dataset, value, vValY, 1 ) &&
173 TQVariant::Double == vValY.type() ){
174 _startAngles[ value ] = currentValue;
175 const double cellValue = fabs( vValY.toDouble() );
176 _angleLens[ value ] = ( int ) floor( cellValue * sectorsPerValue + 0.5 );
177 atLeastOneValue = true;
178 } else { // mark as non-existent
179 _angleLens[ value ] = 0;
180 if ( value > 0 )
181 _startAngles[ value ] = _startAngles[ value - 1 ];
182 else
183 _startAngles[ value ] = currentValue;
184 }
185
186 currentValue = _startAngles[ value ] + _angleLens[ value ];
187 }
188
189 // If there was no value at all, bail out, to avoid endless loops
190 // later on (e.g. in findPieAt()).
191 if( !atLeastOneValue )
192 return;
193
194
195 // Find the backmost pie which is at +90° and needs to be drawn
196 // first
197 int backmostpie = findPieAt( 90 * 16 );
198 // Find the frontmost pie (at -90°/+270°) that should be drawn last
199 int frontmostpie = findPieAt( 270 * 16 );
200 // and put the backmost pie on the TODO stack to initialize it,
201 // but only if it is not the frontmostpie
202 TQValueStack < int > todostack;
203 if ( backmostpie != frontmostpie )
204 todostack.push( backmostpie );
205 else {
206 // Otherwise, try to find something else
207 int leftOfCurrent = findLeftPie( backmostpie );
208 if ( leftOfCurrent != frontmostpie ) {
209 todostack.push( leftOfCurrent );
210 } else {
211 int rightOfCurrent = findRightPie( backmostpie );
212 if ( rightOfCurrent != frontmostpie ) {
213 todostack.push( rightOfCurrent );
214 }
215 }
216 // If we get here, there was nothing else, and we will bail
217 // out of the while loop below.
218 }
219
220 // The list with pies that have already been drawn
221
222 TQValueList < int > donelist;
223
224 // Draw pies until the todostack is empty or only the frontmost
225 // pie is there
226 while ( !todostack.isEmpty() &&
227 !( ( todostack.count() == 1 ) &&
228 ( ( todostack.top() == frontmostpie ) ) ) ) {
229 // The while loop cannot be cancelled if frontmostpie is on
230 // top of the stack, but this is also backmostpie (can happen
231 // when one of the pies covers more than 1/2 of the circle. In
232 // this case, we need to find something else to put on the
233 // stack to get things going.
234
235 // take one pie from the stack
236 int currentpie = todostack.pop();
237 // if this pie was already drawn, ignore it
238 if ( donelist.find( currentpie ) != donelist.end() )
239 continue;
240
241 // If this pie is the frontmost pie, put it back, but at the
242 // second position (otherwise, there would be an endless
243 // loop). If this pie is the frontmost pie, there must be at
244 // least one other pie, otherwise the loop would already have
245 // been terminated by the loop condition.
246 if ( currentpie == frontmostpie ) {
247 Q_ASSERT( !todostack.isEmpty() );
248 // TQValueStack::exchange() would be nice here...
249 int secondpie = todostack.pop();
250 if ( currentpie == secondpie )
251 // no need to have the second pie twice on the stack,
252 // forget about one instance and take the third
253 // instead
254 if ( todostack.isEmpty() )
255 break; // done anyway
256 else
257 secondpie = todostack.pop();
258 todostack.push( currentpie );
259 todostack.push( secondpie );
260 continue;
261 }
262
263 // When we get here, we can just draw the pie and proceed.
264 drawOnePie( painter, data, dataset, currentpie, chart,
265 sizeFor3DEffect,
266 regions );
267
268 // Mark the pie just drawn as done.
269 donelist.append( currentpie );
270
271 // Now take the pie to the left and to the right, check
272 // whether these have not been painted already, and put them
273 // on the stack.
274 int leftOfCurrent = findLeftPie( currentpie );
275 if ( donelist.find( leftOfCurrent ) == donelist.end() )
276 todostack.push( leftOfCurrent );
277 int rightOfCurrent = findRightPie( currentpie );
278 if ( donelist.find( rightOfCurrent ) == donelist.end() )
279 todostack.push( rightOfCurrent );
280 }
281
282 // now only the frontmost pie is left to draw
283 drawOnePie( painter, data, dataset, frontmostpie, chart,
284 sizeFor3DEffect,
285 regions );
286}
287
288
299void KDChartPiePainter::drawOnePie( TQPainter* painter,
300 KDChartTableDataBase* /*data*/,
301 uint dataset, uint pie, uint chart,
302 uint threeDPieHeight,
303 KDChartDataRegionList* regions )
304{
305 // Is there anything to draw at all?
306 int angleLen = _angleLens[ ( int ) pie ];
307 if ( angleLen ) {
308 int startAngle = _startAngles[ ( int ) pie ];
309
310 KDChartDataRegion* datReg = 0;
311 TQRegion* region = 0;
312 bool mustDeleteRegion = false;
313 if ( regions ){
314 region = new TQRegion();
315 mustDeleteRegion = true;
316 }
317
318 TQRect drawPosition = _position;
319 if ( params()->explode() ) {
320 // need to compute a new position for each or some of the pie
321 TQValueList<int> explodeList = params()->explodeValues();
322 if( explodeList.count() == 0 || // nothing on list, explode all
323 explodeList.find( pie ) != explodeList.end() ) {
324 double explodeAngle = ( startAngle + angleLen / 2 ) / 16;
325 double explodeAngleRad = DEGTORAD( explodeAngle );
326 double cosAngle = cos( explodeAngleRad );
327 double sinAngle = -sin( explodeAngleRad );
328
329 // find the explode factor for this particular pie
330 double explodeFactor = 0.0;
331 TQMap<int,double> explodeFactors = params()->explodeFactors();
332 if( !explodeFactors.contains( pie ) ) // not on factors list, use default
333 explodeFactor = params()->explodeFactor();
334 else // on factors list, use segment-specific value
335 explodeFactor = explodeFactors[pie];
336
337 double explodeX = explodeFactor * _size * cosAngle;
338 double explodeY = explodeFactor * _size * sinAngle;
339 drawPosition.moveBy( static_cast<int>( explodeX ), static_cast<int>( explodeY ) );
340 } else
341 drawPosition = _position;
342 } else
343 drawPosition = _position;
344
345 // The 3D effect needs to be drawn first because it could
346 // otherwise partly hide the pie itself.
347 if ( params()->threeDPies() ) {
348 draw3DEffect( painter, drawPosition, dataset, pie, chart,
349 threeDPieHeight,
350 params()->explode(), region );
351 }
352
353 painter->setBrush( params()->dataColor( pie ) );
354 if ( angleLen == 5760 ) {
355 // full circle, avoid nasty line in the middle
356 painter->drawEllipse( drawPosition );
357 if ( regions ) {
358 TQPointArray hitregion;
359 hitregion.makeEllipse( drawPosition.x(), drawPosition.y(),
360 drawPosition.width(),
361 drawPosition.height() );
362 datReg = new KDChartDataRegion( region->unite( TQRegion( hitregion ) ),
363 dataset,
364 pie,
365 chart );
366 datReg->points[ KDChartEnums::PosCenter ]
367 = drawPosition.center();
368 datReg->points[ KDChartEnums::PosCenterRight ]
369 = pointOnCircle( drawPosition, 0 );
370 datReg->points[ KDChartEnums::PosTopRight ]
371 = pointOnCircle( drawPosition, 720 );
372 datReg->points[ KDChartEnums::PosTopCenter ]
373 = pointOnCircle( drawPosition, 1440 );
374 datReg->points[ KDChartEnums::PosTopLeft ]
375 = pointOnCircle( drawPosition, 2160 );
376 datReg->points[ KDChartEnums::PosCenterLeft ]
377 = pointOnCircle( drawPosition, 2880 );
378 datReg->points[ KDChartEnums::PosBottomLeft ]
379 = pointOnCircle( drawPosition, 3600 );
380 datReg->points[ KDChartEnums::PosBottomCenter ]
381 = pointOnCircle( drawPosition, 4320 );
382 datReg->points[ KDChartEnums::PosBottomRight ]
383 = pointOnCircle( drawPosition, 5040 );
384 datReg->startAngle = 2880;
385 datReg->angleLen = 5760;
386 regions->append( datReg );
387 }
388 } else {
389 // draw the top of this piece
390 // Start with getting the points for the arc.
391 const int arcPoints = angleLen;
392 TQPointArray collect(arcPoints+2);
393 int i=0;
394 for ( ; i<=angleLen; ++i){
395 collect.setPoint(i, pointOnCircle( drawPosition, startAngle+i ));
396 }
397 // Adding the center point of the piece.
398 collect.setPoint(i, drawPosition.center() );
399
400
401
402 painter->drawPolygon( collect );
403
404//if( bHelp ){
405// painter->drawPolyline( collect );
406//bHelp=false;
407//}
408
409
410
411 if ( regions ) {
412 TQPointArray hitregion;
413 hitregion.makeArc( drawPosition.x(), drawPosition.y(),
414 drawPosition.width(),
415 drawPosition.height(),
416 ( int ) startAngle, ( int ) angleLen );
417 hitregion.resize( hitregion.size() + 1 );
418 hitregion.setPoint( hitregion.size() - 1,
419 drawPosition.center() );
420 datReg = new KDChartDataRegion( region->unite( TQRegion( hitregion ) ),
421 dataset,
422 pie,
423 chart );
424
425 datReg->points[ KDChartEnums::PosTopLeft ]
426 = pointOnCircle( drawPosition, startAngle + angleLen );
427 datReg->points[ KDChartEnums::PosTopCenter ]
428 = pointOnCircle( drawPosition, startAngle + angleLen / 2 );
429 datReg->points[ KDChartEnums::PosTopRight ]
430 = pointOnCircle( drawPosition, startAngle );
431
432 datReg->points[ KDChartEnums::PosBottomLeft ] = drawPosition.center();
433 datReg->points[ KDChartEnums::PosBottomCenter ]
434 = datReg->points[ KDChartEnums::PosBottomLeft ];
435 datReg->points[ KDChartEnums::PosBottomRight ]
436 = datReg->points[ KDChartEnums::PosBottomLeft ];
437
438 datReg->points[ KDChartEnums::PosCenterLeft ]
439 = TQPoint( ( datReg->points[ KDChartEnums::PosTopLeft ].x()
440 + datReg->points[ KDChartEnums::PosBottomLeft ].x() ) / 2,
441 ( datReg->points[ KDChartEnums::PosTopLeft ].y()
442 + datReg->points[ KDChartEnums::PosBottomLeft ].y() ) / 2 );
443 datReg->points[ KDChartEnums::PosCenter ]
444 = TQPoint( ( datReg->points[ KDChartEnums::PosTopCenter ].x()
445 + datReg->points[ KDChartEnums::PosBottomCenter ].x() ) / 2,
446 ( datReg->points[ KDChartEnums::PosTopCenter ].y()
447 + datReg->points[ KDChartEnums::PosBottomCenter ].y() ) / 2 );
448 datReg->points[ KDChartEnums::PosCenterRight ]
449 = TQPoint( ( datReg->points[ KDChartEnums::PosTopRight ].x()
450 + datReg->points[ KDChartEnums::PosBottomRight ].x() ) / 2,
451 ( datReg->points[ KDChartEnums::PosTopRight ].y()
452 + datReg->points[ KDChartEnums::PosBottomRight ].y() ) / 2 );
453
454 datReg->startAngle = startAngle;
455 datReg->angleLen = angleLen;
456 regions->append( datReg );
457 }
458 }
459 if( mustDeleteRegion )
460 delete region;
461 }
462}
463
464
477void KDChartPiePainter::draw3DEffect( TQPainter* painter,
478 const TQRect& drawPosition,
479 uint dataset, uint pie, uint chart,
480 uint threeDHeight,
481 bool /*explode*/,
482 TQRegion* region )
483{
484 // NOTE: We cannot optimize away drawing some of the effects (even
485 // when not exploding), because some of the pies might be left out
486 // in future versions which would make some of the normally hidden
487 // pies visible. Complex hidden-line algorithms would be much more
488 // expensive than just drawing for nothing.
489
490 // No need to save the brush, will be changed on return from this
491 // method anyway.
492 painter->setBrush( TQBrush( params()->dataShadow1Color( pie ),
493 params()->shadowPattern() ) );
494
495 int startAngle = _startAngles[ ( int ) pie ];
496 int endAngle = startAngle + _angleLens[ ( int ) pie ];
497 // Normalize angles
498 while ( startAngle >= 5760 )
499 startAngle -= 5760;
500 while ( endAngle >= 5760 )
501 endAngle -= 5760;
502 Q_ASSERT( startAngle >= 0 && startAngle <= 360 * 16 );
503 Q_ASSERT( endAngle >= 0 && endAngle <= 360 * 16 );
504
505 //int centerY = drawPosition.center().y();
506
507 if ( startAngle == endAngle ||
508 startAngle == endAngle - 5760 ) { // full circle
509 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
510 threeDHeight, 2880, 5760, region );
511 } else if ( startAngle <= 90 * 16 ) {
512 if ( endAngle <= 90 * 16 ) {
513 if ( startAngle <= endAngle ) {
515 drawStraightEffectSegment( painter, drawPosition, dataset,
516 pie, chart, threeDHeight, startAngle,
517 region );
518 } else {
520 drawStraightEffectSegment( painter, drawPosition, dataset,
521 pie, chart, threeDHeight, startAngle,
522 region );
523 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
524 threeDHeight, 2880, 5760, region );
525 }
526 } else if ( endAngle <= 180 * 16 ) {
529 drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
530 threeDHeight, startAngle, region );
531 drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
532 threeDHeight, endAngle, region );
533 } else if ( endAngle <= 270 * 16 ) {
535 drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
536 threeDHeight, startAngle,
537 region );
538 drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
539 threeDHeight, endAngle, region );
540 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
541 threeDHeight, 2880, endAngle, region );
542 } else { // 270*16 < endAngle < 360*16
545 drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
546 threeDHeight, startAngle, region );
547 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
548 threeDHeight, 2880, endAngle, region );
549 }
550 } else if ( startAngle <= 180 * 16 ) {
551 if ( endAngle <= 90 * 16 ) {
552 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
553 threeDHeight, 2880, 5760, region );
554 } else if ( endAngle <= 180 * 16 ) {
555 if ( startAngle <= endAngle ) {
558 drawStraightEffectSegment( painter, drawPosition, dataset,
559 pie, chart, threeDHeight, endAngle,
560 region );
561 } else {
564 drawStraightEffectSegment( painter, drawPosition, dataset,
565 pie, chart, threeDHeight, endAngle,
566 region );
567 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
568 threeDHeight, 2880, 5760, region );
569 }
570 } else if ( endAngle <= 270 * 16 ) {
571 drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
572 threeDHeight, endAngle, region );
573 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
574 threeDHeight, 2880, endAngle, region );
575 } else { // 270*16 < endAngle < 360*16
576 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
577 threeDHeight, 2880, endAngle, region );
578 }
579 } else if ( startAngle <= 270 * 16 ) {
580 if ( endAngle <= 90 * 16 ) {
581 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
582 threeDHeight, startAngle, 5760, region );
583 } else if ( endAngle <= 180 * 16 ) {
584 drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
585 threeDHeight, endAngle, region );
586 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
587 threeDHeight, startAngle, 5760, region );
588 } else if ( endAngle <= 270 * 16 ) {
589 if ( startAngle <= endAngle ) {
592 drawStraightEffectSegment( painter, drawPosition, dataset,
593 pie, chart, threeDHeight, endAngle,
594 region );
595 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
596 threeDHeight, startAngle, endAngle,
597 region );
598 } else {
601 drawStraightEffectSegment( painter, drawPosition, dataset,
602 pie, chart, threeDHeight, endAngle,
603 region );
604 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
605 threeDHeight, 2880, endAngle,
606 region );
607 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
608 threeDHeight, startAngle, 5760,
609 region );
610 }
611 } else { // 270*16 < endAngle < 360*16
612 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
613 threeDHeight, startAngle, endAngle,
614 region );
615 }
616 } else { // 270*16 < startAngle < 360*16
617 if ( endAngle <= 90 * 16 ) {
618 drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
619 threeDHeight, startAngle, region );
620 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
621 threeDHeight, startAngle, 5760, region );
622 } else if ( endAngle <= 180 * 16 ) {
623 drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
624 threeDHeight, startAngle, region );
625 drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
626 threeDHeight, endAngle, region );
627 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
628 threeDHeight, startAngle, 5760, region );
629 } else if ( endAngle <= 270 * 16 ) {
630 drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
631 threeDHeight, startAngle, region );
632 drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
633 threeDHeight, endAngle, region );
634 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
635 threeDHeight, 2880, endAngle, region );
636 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
637 threeDHeight, startAngle, 5760, region );
638 } else { // 270*16 < endAngle < 360*16
639 if ( startAngle <= endAngle ) {
642 drawStraightEffectSegment( painter, drawPosition, dataset,
643 pie, chart, threeDHeight, startAngle,
644 region );
645 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
646 threeDHeight, startAngle, endAngle,
647 region );
648 } else {
651 drawStraightEffectSegment( painter, drawPosition, dataset,
652 pie, chart, threeDHeight, startAngle,
653 region );
654 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
655 threeDHeight, startAngle, 5760,
656 region );
657 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
658 threeDHeight, 2880, endAngle, region );
659 }
660 }
661 }
662}
663
664
676 const TQRect& rect,
677 uint /*dataset*/, uint /*pie*/, uint /*chart*/,
678 int threeDHeight,
679 int angle,
680 TQRegion* region )
681{
682 TQPoint center = rect.center();
683 TQPointArray points( 4 );
684 TQPoint circlePoint = pointOnCircle( rect, angle );
685 points.setPoint( 0, center );
686 points.setPoint( 1, circlePoint );
687 points.setPoint( 2, circlePoint.x(), circlePoint.y() + threeDHeight );
688 points.setPoint( 3, center.x(),
689 center.y() + threeDHeight );
690 painter->drawPolygon( points );
691 if ( region )
692 *region += TQRegion( points );
693}
694
695
708 const TQRect& rect,
709 uint /*dataset*/, uint /*pie*/, uint /*chart*/,
710 int threeDHeight,
711 int startAngle,
712 int endAngle,
713 TQRegion* region )
714{
715 // Start with getting the points for the inner arc.
716 const int startA = TQMIN(startAngle, endAngle);
717 const int endA = TQMAX(startAngle, endAngle);
718 const int arcPoints = endA-startA+1;
719 TQPointArray collect(arcPoints * 2);
720 for ( int angle=endA; angle>=startA; --angle){
721 collect.setPoint(endA-angle, pointOnCircle( rect, angle ));
722 }
723
724 // Now copy these arcs again into the same array, but in the
725 // opposite direction and moved down by the 3D height.
726 for ( int i = arcPoints - 1; i >= 0; --i ) {
727 TQPoint pointOnFirstArc = collect.point( i );
728 pointOnFirstArc.setY( pointOnFirstArc.y() + threeDHeight );
729 collect.setPoint( arcPoints * 2 - i - 1, pointOnFirstArc );
730 }
731 painter->drawPolygon( collect );
732 if ( region )
733 *region += TQRegion( collect );
734}
735
736
745{
746 for ( int i = 0; i < _numValues; i++ ) {
747 int endseg = _startAngles[ i ] + _angleLens[ i ];
748 if ( ( _startAngles[ i ] <= angle ) &&
749 ( endseg >= angle ) )
750 // found!
751 return i;
752 }
753
754 // If we have not found it, try wrap around
755 return findPieAt( angle + 5760 );
756}
757
758
767{
768 if ( pie == 0 )
769 if ( _numValues > 1 )
770 return _numValues - 1;
771 else
772 return 0;
773 else {
774 return pie - 1;
775 }
776}
777
778
787{
788 int rightpie = pie + 1;
789 if ( rightpie == _numValues )
790 rightpie = 0;
791 return rightpie;
792}
793
794
807TQString KDChartPiePainter::fallbackLegendText( uint dataset ) const
808{
809 return TQObject::tr( "Item " ) + TQString::number( dataset + 1 );
810}
811
812
823{
824 return data->usedCols();
825}
Definition of global enums.
Header for all common chart parameters.
The collection class used by KD Chart to store data region information.
Bundles the display parameters of a chart.
virtual void drawOnePie(TQPainter *painter, KDChartTableDataBase *data, uint dataset, uint pie, uint chart, uint threeDPieHeight, KDChartDataRegionList *regions=0)
Internal method that draws one of the pies in a pie chart.
void drawStraightEffectSegment(TQPainter *painter, const TQRect &rect, uint dataset, uint pie, uint chart, int threeDHeight, int angle, TQRegion *region=0)
Internal method that draws a segment with a straight 3D effect.
virtual void draw3DEffect(TQPainter *painter, const TQRect &drawPosition, uint dataset, uint pie, uint chart, uint threeDPieHeight, bool explode, TQRegion *region=0)
Internal method that draws the shadow creating the 3D effect of a pie.
virtual TQString fallbackLegendText(uint dataset) const
This method is a specialization that returns a fallback legend text appropriate for pies that do not ...
uint findRightPie(uint pie)
Internal method that finds the pie that is located to the right of the pie specified by pie.
uint findPieAt(int angle)
Internal method that finds the pie that is located at the position specified by angle.
virtual ~KDChartPiePainter()
Destructor.
virtual void paintData(TQPainter *painter, KDChartTableDataBase *data, bool paint2nd, KDChartDataRegionList *regions=0)
Paints the actual data area.
KDChartPiePainter(KDChartParams *params)
Constructor.
uint findLeftPie(uint pie)
Internal method that finds the pie that is located to the left of the pie specified by pie.
void drawArcEffectSegment(TQPainter *painter, const TQRect &rect, uint dataset, uint pie, uint chart, int threeDHeight, int startAngle, int endAngle, TQRegion *region=0)
Internal method that draws a segment with an arc 3D effect.
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.