kpresenter

KPrPieObject.cpp
1/* This file is part of the KDE project
2 Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
3 Copyright (C) 2005-2006 Thorsten Zachmann <zachmann@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include "KPrPieObject.h"
22#include "KPrGradient.h"
23#include "KPrUtils.h"
24#include "KPrPieObjectIface.h"
25
26#include <KoTextZoomHandler.h>
27#include <KoOasisContext.h>
28#include <KoStyleStack.h>
29#include <KoXmlNS.h>
30
31#include <kdebug.h>
32
33#include <tqregion.h>
34#include <tqpicture.h>
35#include <tqdom.h>
36#include <tqpainter.h>
37#include <tqbitmap.h>
38using namespace std;
39
40KPrPieObject::KPrPieObject()
41: KPr2DObject()
42, KPrStartEndLine( L_NORMAL, L_NORMAL )
43{
44 pieType = PT_PIE;
45 p_angle = 45 * 16;
46 p_len = 270 * 16;
47}
48
49KPrPieObject::KPrPieObject( const KoPen &_pen, const TQBrush &_brush, FillType _fillType,
50 const TQColor &_gColor1, const TQColor &_gColor2, BCType _gType,
51 PieType _pieType, int _p_angle, int _p_len,
52 LineEnd _lineBegin, LineEnd _lineEnd,
53 bool _unbalanced, int _xfactor, int _yfactor )
54: KPr2DObject( _pen, _brush, _fillType, _gColor1, _gColor2, _gType, _unbalanced, _xfactor, _yfactor )
55, KPrStartEndLine( _lineBegin, _lineEnd )
56{
57 pieType = _pieType;
58 p_angle = _p_angle;
59 p_len = _p_len;
60}
61
62DCOPObject* KPrPieObject::dcopObject()
63{
64 if ( !dcop )
65 dcop = new KPrPieObjectIface( this );
66 return dcop;
67}
68
69KPrPieObject &KPrPieObject::operator=( const KPrPieObject & )
70{
71 return *this;
72}
73
74TQDomDocumentFragment KPrPieObject::save( TQDomDocument& doc, double offset )
75{
76 TQDomDocumentFragment fragment=KPr2DObject::save(doc, offset);
77 KPrStartEndLine::save( fragment, doc );
78 if (p_angle!=720)
79 fragment.appendChild(KPrObject::createValueElement("PIEANGLE", p_angle, doc));
80 if (p_len!=1440)
81 fragment.appendChild(KPrObject::createValueElement("PIELENGTH", p_len, doc));
82 if (pieType!=PT_PIE)
83 fragment.appendChild(KPrObject::createValueElement("PIETYPE", static_cast<int>(pieType), doc));
84 return fragment;
85}
86
87bool KPrPieObject::saveOasisObjectAttributes( KPOasisSaveContext &sc ) const
88{
89 switch( pieType )
90 {
91 case PT_PIE:
92 sc.xmlWriter.addAttribute( "draw:kind", "section" );
93 break;
94 case PT_CHORD:
95 sc.xmlWriter.addAttribute( "draw:kind", "cut" );
96 break;
97 case PT_ARC:
98 sc.xmlWriter.addAttribute( "draw:kind", "arc" );
99 break;
100 default:
101 kdDebug() << " type of pie not supported" << endl;
102 }
103
104 int startangle = ( (int)p_angle / 16 );
105 sc.xmlWriter.addAttribute( "draw:start-angle", startangle );
106
107 int endangle = ( (int) p_len / 16 ) + startangle;
108 sc.xmlWriter.addAttribute( "draw:end-angle", endangle );
109
110 return true;
111}
112
113void KPrPieObject::fillStyle( KoGenStyle& styleObjectAuto, KoGenStyles& mainStyles ) const
114{
115 KPrShadowObject::fillStyle( styleObjectAuto, mainStyles );
116 if ( pieType == PT_ARC )
117 {
118 saveOasisMarkerElement( mainStyles, styleObjectAuto );
119 }
120 else
121 {
122 m_brush.saveOasisFillStyle( styleObjectAuto, mainStyles );
123 }
124}
125
126const char * KPrPieObject::getOasisElementName() const
127{
128 return ext.width() == ext.height() ? "draw:circle" : "draw:ellipse";
129}
130
131
132void KPrPieObject::loadOasis(const TQDomElement &element, KoOasisContext & context, KPrLoadingInfo *info)
133{
134 kdDebug()<<"void KPrPieObject::loadOasis(const TQDomElement &element) ***************\n";
135 KPr2DObject::loadOasis(element, context, info);
136 TQString kind = element.attributeNS( KoXmlNS::draw, "kind", TQString() );
137 if ( kind == "section" )
138 pieType = PT_PIE;
139 else if ( kind == "cut" )
140 pieType = PT_CHORD;
141 else if ( kind == "arc" )
142 pieType =PT_ARC;
143 else
144 {
145 kdDebug()<<" KPrPieObject::loadOasis(const TQDomElement &element) type indefined :"<<kind<<endl;
146 pieType = PT_PIE;
147 }
148 kdDebug()<<" type of pie object :"<<( ( pieType == PT_PIE ) ? "pie" : ( pieType == PT_CHORD )?"cut" : "arc" )<<endl;
149
150 int start = (int) ( element.attributeNS( KoXmlNS::draw, "start-angle", TQString() ).toDouble() );
151 p_angle=start*16;
152
153 int end = (int) ( element.attributeNS( KoXmlNS::draw, "end-angle", TQString() ).toDouble() );
154 if ( end < start )
155 p_len = ( ( 360 - start + end ) * 16 );
156 else
157 p_len = ( ( end - start ) * 16 );
158
159 kdDebug()<<"KPrPieObject::loadOasis(const TQDomElement &element) : p_angle :"<<p_angle<<" p_len :"<<p_len<<endl;
160 if ( pieType == PT_ARC )
161 {
162 loadOasisMarkerElement( context, "marker-start", lineBegin );
163 loadOasisMarkerElement( context, "marker-end", lineEnd );
164 }
165}
166
167double KPrPieObject::load(const TQDomElement &element)
168{
169 double offset=KPr2DObject::load(element);
170 KPrStartEndLine::load( element );
171 TQDomElement e=element.namedItem("PIEANGLE").toElement();
172 if(!e.isNull()) {
173 int tmp=0;
174 if(e.hasAttribute("value"))
175 tmp=e.attribute("value").toInt();
176 p_angle=tmp;
177 }
178 e=element.namedItem("PIELENGTH").toElement();
179 if(!e.isNull()) {
180 int tmp=0;
181 if(e.hasAttribute("value"))
182 tmp=e.attribute("value").toInt();
183 p_len=tmp;
184 }
185 else
186 p_len=1440; //necessary to reinitialise p_len
187 //I don't know who change default value
188 e=element.namedItem("PIETYPE").toElement();
189 if(!e.isNull()) {
190 int tmp=0;
191 if(e.hasAttribute("value"))
192 tmp=e.attribute("value").toInt();
193 pieType=static_cast<PieType>(tmp);
194 }
195 return offset;
196}
197
198void KPrPieObject::paint( TQPainter* _painter, KoTextZoomHandler*_zoomHandler,
199 int /* pageNum */, bool drawingShadow, bool drawContour )
200{
201 double ow = ext.width();
202 double oh = ext.height();
203 double pw = ( ( pen.style() == TQt::NoPen ) ? 1 : pen.pointWidth() ) / 2.0;
204
205 if ( drawContour ) {
206 TQPen pen3( TQt::black, 1, TQt::DotLine );
207 _painter->setPen( pen3 );
208 _painter->setRasterOp( TQt::NotXorROP );
209 }
210 else {
211 TQPen pen2 = pen.zoomedPen( _zoomHandler );
212 _painter->setPen( pen2 );
213 if ( drawingShadow || getFillType() == FT_BRUSH || !gradient )
214 {
215 _painter->setBrush( getBrush() );
216 }
217 else
218 {
219 if ( pieType != PT_ARC )
220 {
221 TQSize size( _zoomHandler->zoomSize( ext ) );
222
223 if ( m_redrawGradientPix || gradient->size() != size )
224 {
225 m_redrawGradientPix = false;
226 gradient->setSize( size );
227
228 m_gradientPix.resize ( size );
229 m_gradientPix.fill( TQt::white );
230 TQPainter p;
231 p.begin( &m_gradientPix );
232 p.drawPixmap( 0, 0, gradient->pixmap() );
233 p.end();
234
235 TQBitmap mask( size, true );
236 p.begin( &mask );
237 p.setPen( TQPen( TQt::color1 ) );
238 p.setBrush( TQBrush( TQt::color1 ) );
239 if ( pieType == PT_CHORD )
240 {
241 p.drawChord( _zoomHandler->zoomItX(pw), _zoomHandler->zoomItY(pw),
242 _zoomHandler->zoomItX(ow - 2 * pw),
243 _zoomHandler->zoomItY(oh - 2 * pw), p_angle, p_len );
244 }
245 else
246 {
247 p.drawPie( _zoomHandler->zoomItX(pw), _zoomHandler->zoomItY(pw),
248 _zoomHandler->zoomItX( ow - 2 * pw),
249 _zoomHandler->zoomItY( oh - 2 * pw), p_angle, p_len );
250 }
251 p.end();
252 m_gradientPix.setMask( mask );
253 }
254 _painter->drawPixmap( 0, 0, m_gradientPix, 0, 0, size.width(), size.height() );
255 _painter->setBrush( TQt::NoBrush );
256 }
257 }
258 if ( pieType == PT_ARC )
259 {
260 KoPointArray points( 2 );
261 setEndPoints( points );
262 KoPoint start( points.point( 0 ) );
263 KoPoint end( points.point( 1 ) );
264
265 double ys = ( ( 1 - start.x() / ( ext.width() * ext.width() / 4 ) ) * ext.height() * ext.height() / 4 ) / start.y();
266 double s_angle = 90 + ( atan( ( start.x() - 1 ) / ( start.y() - ys ) ) * 180 / M_PI );
267 if ( p_angle / 16 >= 90 && p_angle / 16 <= 270 )
268 {
269 s_angle += 180.0;
270 }
271 double ye = ( ( 1 - end.x() / ( ext.width() * ext.width() / 4 ) ) * ext.height() * ext.height() / 4 ) / end.y();
272 double e_angle = 270 + ( atan( ( end.x() - 1 ) / ( end.y() - ye ) ) * 180 / M_PI );
273 if ( ( ( p_angle + p_len ) / 16 ) % 360 >= 90 && ( ( p_angle + p_len ) / 16 ) % 360 <= 270 )
274 {
275 e_angle -= 180.0;
276 }
277
278 start = KoPoint( ext.width() / 2.0 + start.x(), ext.height() / 2.0 - start.y() );
279 end = KoPoint( ext.width() / 2.0 + end.x(), ext.height() / 2.0 - end.y() );
280
281
282
283 if ( lineBegin != L_NORMAL )
284 drawFigureWithOffset( lineBegin, _painter, start,
285 pen2.color(), int( pen.pointWidth() ), s_angle, _zoomHandler, true );
286
287 if ( lineEnd != L_NORMAL )
288 drawFigureWithOffset( lineEnd, _painter, end,
289 pen2.color(), int( pen.pointWidth() ), e_angle, _zoomHandler, false );
290 }
291 }
292 switch ( pieType )
293 {
294 case PT_PIE:
295 _painter->drawPie( _zoomHandler->zoomItX(pw), _zoomHandler->zoomItY( pw),
296 _zoomHandler->zoomItX( ow - 2 * pw),
297 _zoomHandler->zoomItY( oh - 2 * pw), p_angle, p_len );
298 break;
299 case PT_ARC:
300 _painter->drawArc( _zoomHandler->zoomItX(pw), _zoomHandler->zoomItY(pw),
301 _zoomHandler->zoomItX(ow - 2 * pw),
302 _zoomHandler->zoomItY(oh - 2 * pw), p_angle, p_len );
303 break;
304 case PT_CHORD:
305 _painter->drawChord( _zoomHandler->zoomItX(pw), _zoomHandler->zoomItY(pw),
306 _zoomHandler->zoomItX(ow - 2 * pw),
307 _zoomHandler->zoomItY(oh - 2 * pw), p_angle, p_len );
308 break;
309 default: break;
310 }
311}
312
313void KPrPieObject::flip( bool horizontal )
314{
315 KPr2DObject::flip( horizontal );
316 if ( ! horizontal )
317 {
318 p_angle = 360*16 - p_angle -p_len;
319 }
320 else
321 {
322 p_angle = 180*16 - p_angle - p_len;
323 }
324 // angle smaller 0
325 while ( p_angle < 0 ) {
326 p_angle += 360*16;
327 }
328
329}
330
331
332void KPrPieObject::setMinMax( double &min_x, double &min_y,
333 double &max_x, double &max_y, KoPoint point ) const
334{
335 double tmp_x = point.x();
336 double tmp_y = point.y();
337
338 if ( tmp_x < min_x ) {
339 min_x = tmp_x;
340 }
341 else if ( tmp_x > max_x ) {
342 max_x = tmp_x;
343 }
344
345 if ( tmp_y < min_y ) {
346 min_y = tmp_y;
347 }
348 else if ( tmp_y > max_y ) {
349 max_y = tmp_y;
350 }
351}
352
353
354/*
355 * The calculation of the real size and origin for a pie object is a little more
356 * complicated. It took me quite a whlie to get it right.
357 * Here is how it works:
358 * 1. calculate the position of the end points
359 * 2. calculate the 4 maximal points, the points with max x or y position, of the
360 * hole ellipse
361 * 3. find minimal and maximal points
362 * 4. check if the maximal points lie on the arc
363 *
364 */
365void KPrPieObject::getRealSizeAndOrig( KoSize &size, KoPoint &realOrig ) const {
366 double radius1 = size.width() / 2.0;
367 double radius2 = size.height() / 2.0;
368
369 // the rotation angle
370 double angInRad = angle * M_PI / 180;
371
372 // 1. calulate position of end points
373 KoPointArray points(2);
374 setEndPoints( points );
375
376 // rotate point
377 for ( int i = 0; i < 2; i++ ) {
378 if ( angle != 0 ) {
379 double sinus = sin( angInRad );
380 double cosinus = cos( angInRad );
381
382 double tmp_x = points.point( i ).x();
383 double tmp_y = points.point( i ).y();
384
385 double x = tmp_x * cosinus + tmp_y * sinus;
386 double y = - tmp_x * sinus + tmp_y * cosinus;
387 points.setPoint( i, x, y );
388 }
389 }
390
391 KoPoint firstPoint( points.point(0) );
392 KoPoint secondPoint( points.point(1) );
393
394 // 2. calulate maximal points
395 KoPointArray maxPoints(4);
396 if ( angle == 0 ) {
397 maxPoints.setPoint( 0, 0, radius2 );
398 maxPoints.setPoint( 1, radius1, 0 );
399 maxPoints.setPoint( 2, 0, -radius2 );
400 maxPoints.setPoint( 3, -radius1, 0 );
401 }
402 else {
403 double sinus = sin( angInRad );
404 double cosinus = cos( angInRad );
405
406 double x = sqrt( pow( radius1 * cosinus , 2 ) + pow(radius2 * sinus, 2));
407 double y = ( pow( radius2, 2 ) - pow( radius1, 2) ) * sinus * cosinus / x;
408 maxPoints.setPoint( 0, x, y );
409 maxPoints.setPoint( 1, -x, -y );
410
411 y = sqrt( pow( radius1 * sinus , 2 ) + pow(radius2 * cosinus, 2));
412 x = ( pow( radius1, 2 ) - pow( radius2, 2) ) * sinus * cosinus / y;
413 maxPoints.setPoint( 2, x, y);
414 maxPoints.setPoint( 3, -x, -y );
415 }
416
417 // 3. find minimal and maximal points
418 double min_x = firstPoint.x();
419 double min_y = firstPoint.y();
420 double max_x = firstPoint.x();
421 double max_y = firstPoint.y();
422
423 if ( pieType == PT_PIE ) {
424 KoPoint zero(0,0);
425 setMinMax( min_x, min_y, max_x, max_y, zero );
426 }
427 setMinMax( min_x, min_y, max_x, max_y, secondPoint );
428
429 /* 4. check if maximal points lie on the arc.
430 * There are three posibilities how many sections have to
431 * been checked.
432 * 1. the arc is only once on one side of the x axis
433 * 2. the arc is on both sides of the x axis
434 * 3. the arc is twice on one one side of the x axis
435 *
436 * 1) 2) 3)
437 * y y y
438 * ex|xx xx|xs s |
439 * | x x | x | e
440 * | s x | x | x
441 * ----+---- x ----+---- x ----+---- x
442 * | x | x | x
443 * | x | x | x
444 * | e | xx|xx
445 *
446 */
447 if ( firstPoint.y() >= 0 ) {
448 if ( secondPoint.y() >= 0 ) {
449 if ( firstPoint.x() > secondPoint.x() || p_len == 0 ) {
450 // 1 section
451 // f.x() <= x <= s.x() && y >= 0
452 KoPointArray::ConstIterator it( maxPoints.begin() );
453 for ( ; it != maxPoints.end(); ++it ) {
454 if ( (*it).y() >= 0 &&
455 (*it).x() <= firstPoint.x() && (*it).x() >= secondPoint.x() )
456 {
457 setMinMax( min_x, min_y, max_x, max_y, *it );
458 }
459 }
460 }
461 else {
462 // 3 sections
463 // x <= f.x() && y >= 0
464 // y < 0
465 // x >= s.x() && y >= 0
466 KoPointArray::ConstIterator it( maxPoints.begin() );
467 for ( ; it != maxPoints.end(); ++it ) {
468 if ( (*it).y() >= 0 ) {
469 if ( (*it).x() <= firstPoint.x() || (*it).x() >= secondPoint.x() ) {
470 setMinMax( min_x, min_y, max_x, max_y, *it );
471 }
472 }
473 else {
474 setMinMax( min_x, min_y, max_x, max_y, *it );
475 }
476 }
477 }
478 }
479 else {
480 // 2 sections
481 // x <= f.x() && y >= 0
482 // x <= s.x() && y < 0
483 KoPointArray::ConstIterator it( maxPoints.begin() );
484 for ( ; it != maxPoints.end(); ++it ) {
485 if ( (*it).y() >= 0 ) {
486 if ( (*it).x() <= firstPoint.x() ) {
487 setMinMax( min_x, min_y, max_x, max_y, *it );
488 }
489 }
490 else {
491 if ( (*it).x() <= secondPoint.x() ) {
492 setMinMax( min_x, min_y, max_x, max_y, *it );
493 }
494 }
495 }
496 }
497 }
498 else {
499 if ( secondPoint.y() >= 0 ) {
500 // 2 sections
501 // x >= f.x() && y < 0
502 // x >= s.x() && y >= 0
503 KoPointArray::ConstIterator it( maxPoints.begin() );
504 for ( ; it != maxPoints.end(); ++it ) {
505 if ( (*it).y() < 0 ) {
506 if ( (*it).x() >= firstPoint.x() ) {
507 setMinMax( min_x, min_y, max_x, max_y, *it );
508 }
509 }
510 else {
511 if ( (*it).x() >= secondPoint.x() ) {
512 setMinMax( min_x, min_y, max_x, max_y, *it );
513 }
514 }
515 }
516 }
517 else {
518 if ( firstPoint.x() < secondPoint.x() || p_len == 0 ) {
519 // 1 section
520 // f.x() <= x <= s.x() && y < 0
521 KoPointArray::ConstIterator it( maxPoints.begin() );
522 for ( ; it != maxPoints.end(); ++it ) {
523 if ( (*it).y() < 0 &&
524 (*it).x() >= firstPoint.x() && (*it).x() <= secondPoint.x() )
525 {
526 setMinMax( min_x, min_y, max_x, max_y, *it );
527 }
528 }
529 }
530 else {
531 // 3 sections
532 // x >= f.x() && y < 0
533 // y >= 0
534 // x <= s.x() && y < 0
535 KoPointArray::ConstIterator it( maxPoints.begin() );
536 for ( ; it != maxPoints.end(); ++it ) {
537 if ( (*it).y() < 0 ) {
538 if ( (*it).x() >= firstPoint.x() || (*it).x() <= secondPoint.x() ) {
539 setMinMax( min_x, min_y, max_x, max_y, *it );
540 }
541 }
542 else {
543 setMinMax( min_x, min_y, max_x, max_y, *it );
544 }
545 }
546 }
547 }
548 }
549
550 double mid_x = size.width() / 2;
551 double mid_y = size.height() / 2;
552
553 size.setWidth( max_x - min_x );
554 size.setHeight( max_y - min_y );
555
556 realOrig.setX( realOrig.x() + mid_x + min_x );
557 realOrig.setY( realOrig.y() + mid_y - max_y );
558}
559
560void KPrPieObject::setEndPoints( KoPointArray &points ) const
561{
562 int angles[] = { p_angle, ( p_angle + p_len ) % ( 16 * 360 ) };
563 double anglesInRad[] = { p_angle / 16.0 * M_PI / 180, ( angles[1] ) / 16.0 * M_PI / 180 };
564
565 double radius1 = ext.width() / 2.0;
566 double radius2 = ext.height() / 2.0;
567
568 double prop = radius2 / radius1;
569
570 for ( int i = 0; i < 2; i++ ) {
571 double x = 0;
572 double y = 0;
573
574 // be carefull
575 if ( angles[i] == 90 * 16 ) {
576 y = radius2;
577 }
578 else if ( angles[i] == 270 * 16 ) {
579 y = -radius2;
580 }
581 else {
582 // The real angle is not what was given. It is only ok if radius1 == radius2,
583 // otherwise it is arctan ( radius2 / radius1 tan ( angle ) )
584 double tanalpha = tan( anglesInRad[i] ) * prop;
585 x = sqrt( 1 / ( pow ( 1 / radius1, 2 ) + pow( tanalpha / radius2, 2 ) ) );
586 if ( angles[i] > 90 * 16 && angles[i] < 270 * 16 )
587 x = -x;
588 y = tanalpha * x;
589 }
590 points.setPoint( i, x, y );
591 }
592}
593
594KoSize KPrPieObject::getRealSize() const {
595 KoSize size( ext );
596 KoPoint realOrig( orig );
597 getRealSizeAndOrig( size, realOrig );
598 return size;
599}
600
601
602KoPoint KPrPieObject::getRealOrig() const {
603 KoSize size( ext );
604 KoPoint realOrig( orig );
605 getRealSizeAndOrig( size, realOrig );
606 return realOrig;
607}
Temporary information used only during loading.