moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kig / misc / kigpainter.cpp
blobbb433253d4f0f7fb1b82c1788f84e390e4b0e0c5
1 /**
2 This file is part of Kig, a KDE program for Interactive Geometry...
3 Copyright (C) 2002-2003 Dominique Devriese <devriese@kde.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program 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
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18 USA
19 **/
21 #include "kigpainter.h"
23 #include "../kig/kig_view.h"
24 #include "../kig/kig_document.h"
25 #include "../misc/goniometry.h"
26 #include "../objects/object_holder.h"
27 #include "../objects/curve_imp.h"
28 #include "../objects/point_imp.h"
29 #include "object_hierarchy.h"
30 #include "common.h"
31 #include "conic-common.h"
32 #include "cubic-common.h"
33 #include "coordinate_system.h"
35 #include <qpen.h>
37 #include <cmath>
38 #include <stack>
39 #include <functional>
40 #include <algorithm>
42 KigPainter::KigPainter( const ScreenInfo& si, QPaintDevice* device,
43 const KigDocument& doc, bool no )
44 : mP ( device ),
45 color( Qt::blue ),
46 style( Qt::SolidLine ),
47 pointstyle( 0 ),
48 width( -1 ),
49 brushStyle( Qt::NoBrush ),
50 brushColor( Qt::blue ),
51 mdoc( doc ),
52 msi( si ),
53 mNeedOverlay( no ),
54 overlayenlarge( 0 )
56 mP.setBackgroundColor( Qt::white );
59 KigPainter::~KigPainter()
63 void KigPainter::drawRect( const Rect& r )
65 Rect rt = r.normalized();
66 QRect qr = toScreen(rt);
67 qr.normalize();
68 mP.drawRect(qr);
69 if( mNeedOverlay ) mOverlay.push_back( qr );
72 void KigPainter::drawRect( const QRect& r )
74 mP.drawRect(r);
75 if( mNeedOverlay ) mOverlay.push_back( r );
78 void KigPainter::drawCircle( const Coordinate& center, const double radius )
80 Coordinate bottomLeft = center - Coordinate(radius, radius);
81 Coordinate topRight = center + Coordinate(radius, radius);
82 Rect r( bottomLeft, topRight );
83 QRect qr = toScreen( r );
84 mP.drawEllipse ( qr );
85 if( mNeedOverlay ) circleOverlay( center, radius );
88 void KigPainter::drawSegment( const Coordinate& from, const Coordinate& to )
90 QPoint tF = toScreen(from), tT = toScreen(to);
91 mP.drawLine( tF, tT );
92 if( mNeedOverlay ) segmentOverlay( from, to );
95 void KigPainter::drawFatPoint( const Coordinate& p )
97 int twidth = width == -1 ? 5 : width;
98 mP.setPen( QPen( color, 1, style ) );
99 switch ( pointstyle )
101 case 0:
103 double radius = twidth * pixelWidth();
104 setBrushStyle( Qt::SolidPattern );
105 Coordinate rad( radius, radius );
106 rad /= 2;
107 Coordinate tl = p - rad;
108 Coordinate br = p + rad;
109 Rect r( tl, br );
110 QRect qr = toScreen( r );
111 mP.drawEllipse( qr );
112 if( mNeedOverlay ) mOverlay.push_back( qr );
113 break;
115 case 1:
117 double radius = twidth * pixelWidth();
118 setBrushStyle( Qt::NoBrush );
119 Coordinate rad( radius, radius );
120 rad /= 2;
121 Coordinate tl = p - rad;
122 Coordinate br = p + rad;
123 Rect r( tl, br );
124 QRect qr = toScreen( r );
125 mP.drawEllipse( qr );
126 if( mNeedOverlay ) mOverlay.push_back( qr );
127 break;
129 case 2:
131 double radius = twidth * pixelWidth();
132 Coordinate rad( radius, radius );
133 rad /= 2;
134 Coordinate tl = p - rad;
135 Coordinate br = p + rad;
136 Rect r( tl, br );
137 QRect qr = toScreen( r );
138 mP.drawRect( qr );
139 mP.fillRect( qr, QBrush( color, Qt::SolidPattern ) );
140 if( mNeedOverlay ) mOverlay.push_back( qr );
141 break;
143 case 3:
145 double radius = twidth * pixelWidth();
146 Coordinate rad( radius, radius );
147 rad /= 2;
148 Coordinate tl = p - rad;
149 Coordinate br = p + rad;
150 Rect r( tl, br );
151 QRect qr = toScreen( r );
152 mP.drawRect( qr );
153 if( mNeedOverlay ) mOverlay.push_back( qr );
154 break;
156 case 4:
158 double radius = twidth * pixelWidth();
159 Coordinate rad( radius, radius );
160 rad /= 2;
161 Coordinate tl = p - rad;
162 Coordinate br = p + rad;
163 Rect r( tl, br );
164 QRect qr = toScreen( r );
165 mP.setPen( QPen( color, 2 ) );
166 mP.drawLine( qr.topLeft(), qr.bottomRight() );
167 mP.drawLine( qr.topRight(), qr.bottomLeft() );
168 if( mNeedOverlay ) mOverlay.push_back( qr );
169 break;
172 mP.setPen( QPen( color, twidth, style ) );
175 void KigPainter::drawPoint( const Coordinate& p )
177 mP.drawPoint( toScreen(p) );
178 if( mNeedOverlay ) pointOverlay( p );
181 void KigPainter::drawLine( const Coordinate& p1, const Coordinate& p2 )
183 drawLine( LineData( p1, p2 ) );
186 void KigPainter::drawText( const Rect p, const QString s, int textFlags, int len )
188 QRect t = toScreen(p);
189 int tf = textFlags;
190 t.moveBy( 2, 2 );
191 t.setWidth( t.width() - 4 );
192 t.setHeight( t.height() - 4 );
193 mP.drawText( t, tf, s, len );
194 if( mNeedOverlay ) textOverlay( t, s, tf, len );
197 void KigPainter::textOverlay( const QRect& r, const QString s, int textFlags, int len )
199 // kdDebug() << Rect::fromQRect( mP.boundingRect( r, textFlags, s, len ) ) << endl;
200 QRect newr( mP.boundingRect( r, textFlags, s, len ) );
201 newr.moveBy( -2, -2 );
202 newr.setWidth( newr.width() + 4 );
203 newr.setHeight( newr.height() + 4 );
204 mOverlay.push_back( newr );
207 const Rect KigPainter::boundingRect( const Rect& r, const QString s,
208 int f, int l ) const
210 QRect qr = mP.boundingRect( toScreen( r ), f, s, l );
211 qr.moveBy( -2, -2 );
212 qr.setWidth( qr.width() + 4 );
213 qr.setHeight( qr.height() + 4 );
214 return fromScreen( qr );
217 void KigPainter::setColor( const QColor& c )
219 color = c;
220 mP.setPen( QPen( color, width == -1 ? 1 : width, style ) );
223 void KigPainter::setStyle( const PenStyle c )
225 style = c;
226 mP.setPen( QPen( color, width == -1 ? 1 : width, style ) );
229 void KigPainter::setWidth( const int c )
231 width = c;
232 if (c > 0) overlayenlarge = c - 1;
233 mP.setPen( QPen( color, width == -1 ? 1 : width, style ) );
236 void KigPainter::setPointStyle( const int p )
238 pointstyle = p;
241 void KigPainter::setPen( const QPen& p )
243 color = p.color();
244 width = p.width();
245 style = p.style();
246 mP.setPen(p);
249 void KigPainter::setBrush( const QBrush& b )
251 brushStyle = b.style();
252 brushColor = b.color();
253 mP.setBrush( b );
256 void KigPainter::setBrushStyle( const BrushStyle c )
258 brushStyle = c;
259 mP.setBrush( QBrush( brushColor, brushStyle ) );
262 void KigPainter::setBrushColor( const QColor& c )
264 brushColor = c;
265 mP.setBrush( QBrush( brushColor, brushStyle ) );
268 bool KigPainter::getNightVision( ) const
270 return mdoc.getNightVision();
273 QColor KigPainter::getColor() const
275 return color;
279 static void setContains( QRect& r, const QPoint& p )
281 if ( r.left() > p.x() ) r.setLeft( p.x() );
282 if ( r.right() < p.x() ) r.setRight( p.x() );
283 // this is correct, i think. In qt the bottom has the highest y
284 // coord...
285 if ( r.bottom() > p.y() ) r.setBottom( p.y() );
286 if ( r.top() < p.y() ) r.setTop( p.y() );
290 void KigPainter::drawPolygon( const std::vector<QPoint>& pts,
291 bool winding, int index, int npoints )
293 QPen oldpen = mP.pen();
294 QBrush oldbrush = mP.brush();
295 setBrush( QBrush( color, Dense4Pattern ) );
296 setPen( Qt::NoPen );
297 // i know this isn't really fast, but i blame it all on Qt with its
298 // stupid container classes... what's wrong with the STL ?
299 QPointArray t( pts.size() );
300 int c = 0;
301 for( std::vector<QPoint>::const_iterator i = pts.begin(); i != pts.end(); ++i )
303 t.putPoints( c++, 1, i->x(), i->y() );
305 mP.drawPolygon( t, winding, index, npoints );
306 setPen( oldpen );
307 setBrush( oldbrush );
308 if( mNeedOverlay ) mOverlay.push_back( t.boundingRect() );
311 void KigPainter::drawArea( const std::vector<Coordinate>& pts, bool border )
313 QPen oldpen = mP.pen();
314 QBrush oldbrush = mP.brush();
315 setBrush( QBrush( color, SolidPattern ) );
316 if ( border )
317 setPen( QPen( color, width == -1 ? 1 : width ) );
318 else
319 setPen( Qt::NoPen );
320 QPointArray t( pts.size() );
321 int c = 0;
322 for( std::vector<Coordinate>::const_iterator i = pts.begin(); i != pts.end(); ++i )
324 QPoint p = toScreen( *i );
325 t.putPoints( c++, 1, p.x(), p.y() );
327 mP.drawPolygon( t );
328 setPen( oldpen );
329 setBrush( oldbrush );
330 if( mNeedOverlay ) mOverlay.push_back( t.boundingRect() );
333 Rect KigPainter::window()
335 return msi.shownRect();
338 void KigPainter::circleOverlayRecurse( const Coordinate& centre,
339 double radiussq,
340 const Rect& cr )
342 Rect currentRect = cr.normalized();
344 if( !currentRect.intersects( window() ) ) return;
346 // this code is an adaptation of Marc Bartsch's code, from KGeo
347 Coordinate tl = currentRect.topLeft();
348 Coordinate br = currentRect.bottomRight();
349 Coordinate tr = currentRect.topRight();
350 Coordinate bl = currentRect.bottomLeft();
351 Coordinate c = currentRect.center();
353 // mp: we compute the minimum and maximum distance from the center
354 // of the circle and this rect
355 double distxmin = 0, distxmax = 0, distymin = 0, distymax = 0;
356 if ( centre.x >= tr.x ) distxmin = centre.x - tr.x;
357 if ( centre.x <= bl.x ) distxmin = bl.x - centre.x;
358 if ( centre.y >= tr.y ) distymin = centre.y - tr.y;
359 if ( centre.y <= bl.y ) distymin = bl.y - centre.y;
360 distxmax = fabs(centre.x - c.x) + currentRect.width()/2;
361 distymax = fabs(centre.y - c.y) + currentRect.height()/2;
362 // this should take into account the thickness of the line...
363 distxmin -= pixelWidth();
364 if (distxmin < 0) distxmin = 0;
365 distxmax += pixelWidth();
366 distymin -= pixelWidth();
367 if (distymin < 0) distymin = 0;
368 distymax += pixelWidth();
369 double distminsq = distxmin*distxmin + distymin*distymin;
370 double distmaxsq = distxmax*distxmax + distymax*distymax;
372 // if the circle doesn't touch this rect, we return
373 // too far from the centre
374 if (distminsq > radiussq) return;
376 // too near to the centre
377 if (distmaxsq < radiussq) return;
379 // the rect contains some of the circle
380 // -> if it's small enough, we keep it
381 if( currentRect.width() < overlayRectSize() ) {
382 mOverlay.push_back( toScreenEnlarge( currentRect) );
383 } else {
384 // this func works recursive: we subdivide the current rect, and if
385 // it is of a good size, we keep it, otherwise we handle it again
386 double width = currentRect.width() / 2;
387 double height = currentRect.height() / 2;
388 Rect r1 ( c, -width, -height);
389 r1.normalize();
390 circleOverlayRecurse(centre, radiussq, r1);
391 Rect r2 ( c, width, -height);
392 r2.normalize();
393 circleOverlayRecurse(centre, radiussq, r2);
394 Rect r3 ( c, -width, height);
395 r3.normalize();
396 circleOverlayRecurse(centre, radiussq, r3);
397 Rect r4 ( c, width, height);
398 r4.normalize();
399 circleOverlayRecurse(centre, radiussq, r4);
403 void KigPainter::circleOverlay( const Coordinate& centre, double radius )
405 double t = radius + pixelWidth();
406 Coordinate r( t, t );
407 Coordinate bottomLeft = centre - r;
408 Coordinate topRight = centre + r;
409 Rect rect( bottomLeft, topRight );
410 circleOverlayRecurse ( centre , radius*radius, rect );
413 void KigPainter::segmentOverlay( const Coordinate& p1, const Coordinate& p2 )
415 // this code is based upon what Marc Bartsch wrote for KGeo
417 // some stuff we may need:
418 Coordinate p3 = p2 - p1;
419 Rect border = window();
420 // double length = p3.length();
421 // mp: using the l-infinity distance is more natural here
422 double length = fabs(p3.x);
423 if ( fabs( p3.y ) > length ) length = fabs( p3.y );
424 if ( length < pixelWidth() )
426 // hopefully prevent SIGZERO's
427 mOverlay.push_back( toScreen( Rect( p1, p2 ) ) );
428 return;
430 p3 *= overlayRectSize();
431 p3 /= length;
433 int counter = 0;
435 Rect r(p1, p2);
436 r.normalize();
438 for (;;) {
439 Rect tR( Coordinate( 0, 0 ), overlayRectSize(), overlayRectSize() );
440 Coordinate tP = p1+p3*counter;
441 tR.setCenter(tP);
442 if (!tR.intersects(r))
444 //kdDebug()<< "stopped after "<< counter << " passes." << endl;
445 break;
447 if (tR.intersects(border)) mOverlay.push_back( toScreenEnlarge( tR ) );
448 if (++counter > 100)
450 kdDebug()<< k_funcinfo << "counter got too big :( " << endl;
451 break;
456 double KigPainter::overlayRectSize()
458 return 20 * pixelWidth();
461 void KigPainter::pointOverlay( const Coordinate& p1 )
463 Rect r( p1, 3*pixelWidth(), 3*pixelWidth());
464 r.setCenter( p1 );
465 mOverlay.push_back( toScreen( r) );
468 double KigPainter::pixelWidth()
470 return msi.pixelWidth();
473 void KigPainter::setWholeWinOverlay()
475 mOverlay.clear();
476 mOverlay.push_back( mP.viewport() );
477 // don't accept any more overlay's...
478 mNeedOverlay = false;
481 QPoint KigPainter::toScreen( const Coordinate p ) const
483 return msi.toScreen( p );
486 void KigPainter::drawGrid( const CoordinateSystem& c, bool showGrid, bool showAxes )
488 c.drawGrid( *this, showGrid, showAxes );
489 setWholeWinOverlay();
492 void KigPainter::drawObject( const ObjectHolder* o, bool ss )
494 o->draw( *this, ss );
497 void KigPainter::drawObjects( const std::vector<ObjectHolder*>& os, bool sel )
499 drawObjects( os.begin(), os.end(), sel );
502 void KigPainter::drawFilledRect( const QRect& r )
504 QPen pen( Qt::black, 1, Qt::DotLine );
505 setPen( pen );
506 setBrush( QBrush( Qt::cyan, Dense6Pattern ) );
507 drawRect( r.normalize() );
510 void KigPainter::drawTextStd( const QPoint& p, const QString& s )
512 if ( ! s ) return;
513 // tf = text formatting flags
514 int tf = AlignLeft | AlignTop | DontClip | WordBreak;
515 // we need the rect where we're going to paint text
516 setPen(QPen(Qt::blue, 1, SolidLine));
517 setBrush(Qt::NoBrush);
518 drawText( Rect(
519 msi.fromScreen(p), window().bottomRight()
520 ).normalized(), s, tf );
524 QRect KigPainter::toScreen( const Rect r ) const
526 return msi.toScreen( r );
529 QRect KigPainter::toScreenEnlarge( const Rect r ) const
531 if ( overlayenlarge == 0 ) return msi.toScreen( r );
533 QRect qr = msi.toScreen( r );
534 qr.moveBy ( -overlayenlarge, -overlayenlarge );
535 int w = qr.width();
536 int h = qr.height();
537 qr.setWidth (w + 2*overlayenlarge);
538 qr.setHeight (h + 2*overlayenlarge);
539 return qr;
542 void KigPainter::drawSimpleText( const Coordinate& c, const QString s )
544 int tf = AlignLeft | AlignTop | DontClip | WordBreak;
545 drawText( c, s, tf);
548 void KigPainter::drawText( const Coordinate p, const QString s,
549 int textFlags, int len )
551 drawText( Rect( p, mP.window().right(), mP.window().top() ),
552 s, textFlags, len );
554 const Rect KigPainter::simpleBoundingRect( const Coordinate& c, const QString s )
556 int tf = AlignLeft | AlignTop | DontClip | WordBreak;
557 return boundingRect( c, s, tf );
560 const Rect KigPainter::boundingRect( const Coordinate& c, const QString s,
561 int f, int l ) const
563 return boundingRect( Rect( c, mP.window().right(), mP.window().top() ),
564 s, f, l );
567 Coordinate KigPainter::fromScreen( const QPoint& p ) const
569 return msi.fromScreen( p );
572 Rect KigPainter::fromScreen( const QRect& r ) const
574 return msi.fromScreen( r );
577 void KigPainter::drawRay( const Coordinate& a, const Coordinate& b )
579 Coordinate tb = b;
580 calcRayBorderPoints( a, tb, window() );
581 drawSegment( a, tb );
584 typedef std::pair<double,Coordinate> coordparampair;
586 struct workitem
588 workitem( coordparampair f, coordparampair s, Rect *o) :
589 first(f), second(s), overlay(o) {};
590 coordparampair first;
591 coordparampair second;
592 Rect *overlay;
595 void KigPainter::drawLine( const LineData& d )
597 if ( d.a != d.b )
599 LineData l = calcBorderPoints( d, window() );
600 drawSegment( l.a, l.b );
604 void KigPainter::drawSegment( const LineData& d )
606 drawSegment( d.a, d.b );
609 void KigPainter::drawRay( const LineData& d )
611 drawRay( d.a, d.b );
614 void KigPainter::drawAngle( const Coordinate& cpoint, const double dstartangle,
615 const double dangle )
617 // convert to 16th of degrees...
618 const int startangle = static_cast<int>( Goniometry::convert( 16 * dstartangle, Goniometry::Rad, Goniometry::Deg ) );
619 const int angle = static_cast<int>( Goniometry::convert( 16 * dangle, Goniometry::Rad, Goniometry::Deg ) );
621 QPoint point = toScreen( cpoint );
623 // int radius = mP.window().width() / 5;
624 int radius = 50;
625 QRect surroundingRect( 0, 0, radius*2, radius*2 );
626 surroundingRect.moveCenter( point );
628 mP.drawArc( surroundingRect, startangle, angle );
630 // now for the arrow...
631 QPoint end( static_cast<int>( point.x() + radius * cos( dstartangle + dangle ) ),
632 static_cast<int>( point.y() - radius * sin( dstartangle + dangle ) ) );
633 QPoint vect = (end - point);
634 double vectlen = sqrt( float( vect.x() * vect.x() + vect.y() * vect.y() ) );
635 QPoint orthvect( -vect.y(), vect.x() );
636 vect = vect * 6 / vectlen;
637 orthvect = orthvect * 6 / vectlen;
639 QPointArray arrow( 3 );
640 arrow.setPoint( 0, end );
641 arrow.setPoint( 1, end + orthvect + vect );
642 arrow.setPoint( 2, end + orthvect - vect );
643 // std::vector<QPoint> arrow;
644 // arrow.push_back( end );
645 // arrow.push_back( end + orthvect + vect );
646 // arrow.push_back( end + orthvect - vect );
648 setBrushStyle( Qt::SolidPattern );
649 // drawPolygon( arrow );
650 mP.drawPolygon( arrow, false, 0, -1 );
652 // if ( mNeedOverlay ) mOverlay.push_back( toScreen( r ) );
653 setWholeWinOverlay(); //mp: ugly! why not compute a correct overlay?
654 // mOverlay.push_back( arrow.boundingRect() );
657 void KigPainter::drawPolygon( const std::vector<Coordinate>& pts,
658 bool winding, int index, int npoints )
660 using namespace std;
661 vector<QPoint> points;
662 for ( uint i = 0; i < pts.size(); ++i )
663 points.push_back( toScreen( pts[i] ) );
664 drawPolygon( points, winding, index, npoints );
667 void KigPainter::drawVector( const Coordinate& a, const Coordinate& b )
669 // bugfix...
670 if ( a == b ) return;
671 // the segment
672 drawSegment( a, b );
673 // the arrows...
674 Coordinate dir = b - a;
675 Coordinate perp( dir.y, -dir.x );
676 double length = perp.length();
677 perp *= 10* pixelWidth();
678 perp /= length;
679 dir *= 10 * pixelWidth();
680 dir /= length;
681 Coordinate c = b - dir + perp;
682 Coordinate d = b - dir - perp;
683 drawSegment( b, c );
684 drawSegment( b, d );
687 /* *** this function is commented out ***
688 inline Coordinate locusGetCoord( double p, const CurveImp* curve, const ObjectHierarchy& h,
689 bool& valid, const KigDocument& doc )
691 Coordinate pt = curve->getPoint( p, valid, doc );
692 if ( ! valid ) return Coordinate();
693 PointImp pimp( pt );
694 Args args;
695 args.push_back( &pimp );
696 std::vector<ObjectImp*> calced = h.calc( args, doc );
697 assert( calced.size() == 1 );
698 ObjectImp* o = calced.front();
699 Coordinate ret;
700 if ( o->inherits( ObjectImp::ID_PointImp ) )
702 valid = true;
703 ret = static_cast<PointImp*>( o )->coordinate();
705 else
706 valid = false;
707 delete o;
708 return ret;
712 class CurveImpPointCalcer
714 const CurveImp* curve;
715 public:
716 CurveImpPointCalcer( const CurveImp* c )
717 : curve( c )
720 static const double endinterval;
721 inline const Coordinate getPoint( double param, const KigDocument& d ) const {
722 return curve->getPoint( param, d );
726 const double CurveImpPointCalcer::endinterval = 1.;
728 void KigPainter::drawCurve( const CurveImp* curve )
730 // we manage our own overlay
731 bool tNeedOverlay = mNeedOverlay;
732 mNeedOverlay = false;
734 QPen pen = mP.pen();
736 // this stack contains pairs of Coordinates ( parameter intervals )
737 // that we still need to process:
738 std::stack<workitem> workstack;
739 // mp: this stack contains all the generated overlays:
740 // the strategy for generating the overlay structure is the same
741 // recursive-like used to draw the segments: a new rectangle is
742 // generated whenever the length of a segment becomes lower than
743 // overlayRectSize(), or if the segment would be drawn anyway
744 // to avoid strange things from happening we impose that the distance
745 // in parameter space be less than a threshold before generating
746 // any overlay.
748 // The third parameter in workitem is a pointer into a stack of
749 // all generated rectangles (in real coordinate space); if 0
750 // there is no rectangles associated to that segment yet.
752 // Using the final mOverlay stack would be much more efficient, but
753 // 1. needs transformations into window space
754 // 2. would be more difficult to drop rectangles not intersecting
755 // the window.
756 std::stack<Rect> overlaystack;
758 // mp: the original version in which an initial set of 20 intervals
759 // were pushed onto the stack is replaced by a single interval and
760 // by forcing subdivision till h < hmax (with more or less the same
761 // final result).
762 // First push the [0,1] interval into the stack:
764 Coordinate coo1 = curve->getPoint( 0., mdoc );
765 Coordinate coo2 = curve->getPoint( 1., mdoc );
766 workstack.push( workitem(
767 coordparampair( 0., coo1 ),
768 coordparampair( 1., coo2 ),
769 0 ) );
771 // maxlength is the square of the maximum size that we allow
772 // between two points..
773 double maxlength = 1.5 * pixelWidth();
774 maxlength *= maxlength;
775 // error squared is required to be less that sigma (half pixel)
776 double sigma = maxlength/4;
777 // distance between two parameter values cannot be too small
778 double hmin = 3e-5;
779 // distance between two parameter values cannot be too large
780 double hmax = 1./40;
781 double hmaxoverlay = 1./8;
783 int count = 1; // the number of segments we've already
784 // visited...
785 static const int maxnumberofpoints = 1000;
787 const Rect& sr = window();
789 // what this algorithm does is approximating the curve with a set of
790 // segments. we don't draw the individual segments, but use
791 // QPainter::drawPolyline() so that the line styles work properly.
792 // Possibly there are performance advantages as well ? this array
793 // is a buffer of the polyline approximation of the part of the
794 // curve that we are currently processing.
795 QPointArray curpolyline( 1000 );
796 int curpolylinenextfree = 0;
798 // we don't use recursion, but a stack based approach for efficiency
799 // concerns...
800 while ( ! workstack.empty() && count < maxnumberofpoints )
802 workitem curitem = workstack.top();
803 workstack.pop();
804 bool curitemok = true;
805 while ( curitemok && count++ < maxnumberofpoints )
807 double t0 = curitem.first.first;
808 double t1 = curitem.second.first;
809 Coordinate p0 = curitem.first.second;
810 bool valid0 = p0.valid();
811 Coordinate p1 = curitem.second.second;
812 bool valid1 = p1.valid();
814 // we take the middle parameter of the two previous points...
815 double t2 = ( t0 + t1 ) / 2;
816 double h = fabs( t1 - t0 ) /2;
818 // if exactly one of the two endpoints is invalid, then
819 // we prefer to find an internal value of the parameter
820 // separating valid points from invalid points. We use
821 // a bisection strategy (this is not implemented yet!)
822 // if ( ( valid0 && ! valid1 ) || ( valid1 && ! valid0 ) )
823 // {
824 // while ( h >= hmin )
825 // {
826 // .......................................
827 // }
828 // }
830 Rect *overlaypt = curitem.overlay;
831 Coordinate p2 = curve->getPoint( t2, mdoc );
832 bool allvalid = p2.valid() && valid0 && valid1;
833 bool dooverlay = ! overlaypt && h < hmaxoverlay && valid0 && valid1
834 && fabs( p0.x - p1.x ) <= overlayRectSize()
835 && fabs( p0.y - p1.y ) <= overlayRectSize();
836 bool addn = sr.contains( p2 ) || h >= hmax;
837 // estimated error between the curve and the segments
838 double errsq = 1e21;
839 if ( allvalid ) errsq = (0.5*p0 + 0.5*p1 - p2).squareLength();
840 errsq /= 4;
841 curitemok = false;
842 // bool dodraw = allvalid && h < hmax && ( errsq < sigma || h < hmin );
843 bool dodraw = allvalid && h < hmax && errsq < sigma;
844 if ( tNeedOverlay && ( dooverlay || dodraw ) )
846 Rect newoverlay( p0, p1 );
847 overlaystack.push( newoverlay );
848 overlaypt = &overlaystack.top();
850 if ( overlaypt ) overlaypt->setContains( p2 );
851 if ( dodraw )
853 // draw the two segments
854 QPoint tp0 = toScreen(p0);
855 QPoint tp1 = toScreen(p1);
856 QPoint tp2 = toScreen(p2);
857 if ( curpolylinenextfree > 0 && curpolyline[curpolylinenextfree - 1] != tp1 )
859 // flush the current part of the curve
860 mP.drawPolyline( curpolyline, 0, curpolylinenextfree );
861 curpolylinenextfree = 0;
863 if ( curpolylinenextfree == 0 )
864 curpolyline[curpolylinenextfree++] = tp1;
865 curpolyline[curpolylinenextfree++] = tp2;
866 curpolyline[curpolylinenextfree++] = tp0;
868 else if ( h >= hmin ) // we do not continue to subdivide indefinitely!
870 // push into stack in order to process both subintervals
871 if ( addn || ( valid0 && sr.contains( p0 ) ) )
872 workstack.push( workitem( curitem.first, coordparampair( t2, p2 ),
873 overlaypt ) );
874 if ( addn || ( valid1 && sr.contains( p1 ) ) )
876 curitem = workitem( coordparampair( t2, p2 ), curitem.second ,
877 overlaypt );
878 curitemok = true;
883 // flush the rest of the curve
884 mP.drawPolyline( curpolyline, 0, curpolylinenextfree );
885 curpolylinenextfree = 0;
887 if ( ! workstack.empty () )
888 kdDebug() << "Stack not empty in KigPainter::drawCurve!\n" << endl;
889 assert ( tNeedOverlay || overlaystack.empty() );
890 if ( tNeedOverlay )
892 Rect border = window();
893 while ( ! overlaystack.empty() )
895 Rect overlay = overlaystack.top();
896 overlaystack.pop();
897 if (overlay.intersects( border ))
898 mOverlay.push_back( toScreenEnlarge( overlay ) );
901 mNeedOverlay = tNeedOverlay;
904 void KigPainter::drawTextFrame( const Rect& frame,
905 const QString& s, bool needframe )
907 QPen oldpen = mP.pen();
908 QBrush oldbrush = mP.brush();
909 if ( needframe )
911 // inspired upon kgeo, thanks to Marc Bartsch, i've taken some of
912 // his code too..
913 setPen( QPen( Qt::black, 1 ) );
914 setBrush( QBrush( QColor( 255, 255, 222 ) ) );
915 drawRect( frame );
916 setPen( QPen( QColor( 197, 194, 197 ), 1, Qt::SolidLine ) );
918 QRect qr = toScreen( frame );
920 mP.drawLine( qr.topLeft(), qr.topRight() );
921 mP.drawLine( qr.topLeft(), qr.bottomLeft() );
923 setPen( oldpen );
924 setBrush( oldbrush );
925 drawText( frame, s, Qt::AlignVCenter | Qt::AlignLeft );
928 void KigPainter::drawArc( const Coordinate& center, const double radius,
929 const double dstartangle, const double dangle )
931 // convert to 16th of degrees...
932 const int startangle = static_cast<int>( Goniometry::convert( 16 * dstartangle, Goniometry::Rad, Goniometry::Deg ) );
933 const int angle = static_cast<int>( Goniometry::convert( 16 * dangle, Goniometry::Rad, Goniometry::Deg ) );
935 Rect krect( 0, 0, 2*radius, 2*radius );
936 krect.setCenter( center );
937 QRect rect = toScreen( krect );
939 mP.drawArc( rect, startangle, angle );
940 setWholeWinOverlay();