/** This file is part of Kig, a KDE program for Interactive Geometry... Copyright (C) 2002-2003 Dominique Devriese This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **/ #include "kigpainter.h" #include "../kig/kig_view.h" #include "../kig/kig_document.h" #include "../misc/goniometry.h" #include "../objects/object_holder.h" #include "../objects/curve_imp.h" #include "../objects/point_imp.h" #include "object_hierarchy.h" #include "common.h" #include "conic-common.h" #include "cubic-common.h" #include "coordinate_system.h" #include #include #include #include #include KigPainter::KigPainter( const ScreenInfo& si, TQPaintDevice* device, const KigDocument& doc, bool no ) : mP ( device ), color( TQt::blue ), style( TQt::SolidLine ), pointstyle( 0 ), width( -1 ), brushStyle( TQt::NoBrush ), brushColor( TQt::blue ), mdoc( doc ), msi( si ), mNeedOverlay( no ), overlayenlarge( 0 ) { mP.setBackgroundColor( TQt::white ); } KigPainter::~KigPainter() { } void KigPainter::drawRect( const Rect& r ) { Rect rt = r.normalized(); TQRect qr = toScreen(rt); qr.normalize(); mP.drawRect(qr); if( mNeedOverlay ) mOverlay.push_back( qr ); } void KigPainter::drawRect( const TQRect& r ) { mP.drawRect(r); if( mNeedOverlay ) mOverlay.push_back( r ); } void KigPainter::drawCircle( const Coordinate& center, const double radius ) { Coordinate bottomLeft = center - Coordinate(radius, radius); Coordinate topRight = center + Coordinate(radius, radius); Rect r( bottomLeft, topRight ); TQRect qr = toScreen( r ); mP.drawEllipse ( qr ); if( mNeedOverlay ) circleOverlay( center, radius ); } void KigPainter::drawSegment( const Coordinate& from, const Coordinate& to ) { TQPoint tF = toScreen(from), tT = toScreen(to); mP.drawLine( tF, tT ); if( mNeedOverlay ) segmentOverlay( from, to ); } void KigPainter::drawFatPoint( const Coordinate& p ) { int twidth = width == -1 ? 5 : width; mP.setPen( TQPen( color, 1, style ) ); switch ( pointstyle ) { case 0: { double radius = twidth * pixelWidth(); setBrushStyle( TQt::SolidPattern ); Coordinate rad( radius, radius ); rad /= 2; Coordinate tl = p - rad; Coordinate br = p + rad; Rect r( tl, br ); TQRect qr = toScreen( r ); mP.drawEllipse( qr ); if( mNeedOverlay ) mOverlay.push_back( qr ); break; } case 1: { double radius = twidth * pixelWidth(); setBrushStyle( TQt::NoBrush ); Coordinate rad( radius, radius ); rad /= 2; Coordinate tl = p - rad; Coordinate br = p + rad; Rect r( tl, br ); TQRect qr = toScreen( r ); mP.drawEllipse( qr ); if( mNeedOverlay ) mOverlay.push_back( qr ); break; } case 2: { double radius = twidth * pixelWidth(); Coordinate rad( radius, radius ); rad /= 2; Coordinate tl = p - rad; Coordinate br = p + rad; Rect r( tl, br ); TQRect qr = toScreen( r ); mP.drawRect( qr ); mP.fillRect( qr, TQBrush( color, TQt::SolidPattern ) ); if( mNeedOverlay ) mOverlay.push_back( qr ); break; } case 3: { double radius = twidth * pixelWidth(); Coordinate rad( radius, radius ); rad /= 2; Coordinate tl = p - rad; Coordinate br = p + rad; Rect r( tl, br ); TQRect qr = toScreen( r ); mP.drawRect( qr ); if( mNeedOverlay ) mOverlay.push_back( qr ); break; } case 4: { double radius = twidth * pixelWidth(); Coordinate rad( radius, radius ); rad /= 2; Coordinate tl = p - rad; Coordinate br = p + rad; Rect r( tl, br ); TQRect qr = toScreen( r ); mP.setPen( TQPen( color, 2 ) ); mP.drawLine( qr.topLeft(), qr.bottomRight() ); mP.drawLine( qr.topRight(), qr.bottomLeft() ); if( mNeedOverlay ) mOverlay.push_back( qr ); break; } } mP.setPen( TQPen( color, twidth, style ) ); } void KigPainter::drawPoint( const Coordinate& p ) { mP.drawPoint( toScreen(p) ); if( mNeedOverlay ) pointOverlay( p ); } void KigPainter::drawLine( const Coordinate& p1, const Coordinate& p2 ) { drawLine( LineData( p1, p2 ) ); } void KigPainter::drawText( const Rect p, const TQString s, int textFlags, int len ) { TQRect t = toScreen(p); int tf = textFlags; t.moveBy( 2, 2 ); t.setWidth( t.width() - 4 ); t.setHeight( t.height() - 4 ); mP.drawText( t, tf, s, len ); if( mNeedOverlay ) textOverlay( t, s, tf, len ); } void KigPainter::textOverlay( const TQRect& r, const TQString s, int textFlags, int len ) { // kdDebug() << Rect::fromTQRect( mP.boundingRect( r, textFlags, s, len ) ) << endl; TQRect newr( mP.boundingRect( r, textFlags, s, len ) ); newr.setWidth( newr.width() + 4 ); newr.setHeight( newr.height() + 4 ); mOverlay.push_back( newr ); } const Rect KigPainter::boundingRect( const Rect& r, const TQString s, int f, int l ) const { TQRect qr = mP.boundingRect( toScreen( r ), f, s, l ); qr.setWidth( qr.width() + 4 ); qr.setHeight( qr.height() + 4 ); return fromScreen( qr ); } void KigPainter::setColor( const TQColor& c ) { color = c; mP.setPen( TQPen( color, width == -1 ? 1 : width, style ) ); } void KigPainter::setStyle( const TQt::PenStyle c ) { style = c; mP.setPen( TQPen( color, width == -1 ? 1 : width, style ) ); } void KigPainter::setWidth( const int c ) { width = c; if (c > 0) overlayenlarge = c - 1; mP.setPen( TQPen( color, width == -1 ? 1 : width, style ) ); } void KigPainter::setPointStyle( const int p ) { pointstyle = p; } void KigPainter::setPen( const TQPen& p ) { color = p.color(); width = p.width(); style = p.style(); mP.setPen(p); } void KigPainter::setBrush( const TQBrush& b ) { brushStyle = b.style(); brushColor = b.color(); mP.setBrush( b ); } void KigPainter::setBrushStyle( const TQt::BrushStyle c ) { brushStyle = c; mP.setBrush( TQBrush( brushColor, brushStyle ) ); } void KigPainter::setBrushColor( const TQColor& c ) { brushColor = c; mP.setBrush( TQBrush( brushColor, brushStyle ) ); } bool KigPainter::getNightVision( ) const { return mdoc.getNightVision(); } TQColor KigPainter::getColor() const { return color; } /* static void setContains( TQRect& r, const TQPoint& p ) { if ( r.left() > p.x() ) r.setLeft( p.x() ); if ( r.right() < p.x() ) r.setRight( p.x() ); // this is correct, i think. In qt the bottom has the highest y // coord... if ( r.bottom() > p.y() ) r.setBottom( p.y() ); if ( r.top() < p.y() ) r.setTop( p.y() ); } */ void KigPainter::drawPolygon( const std::vector& pts, bool winding, int index, int npoints ) { TQPen oldpen = mP.pen(); TQBrush oldbrush = mP.brush(); setBrush( TQBrush( color, Dense4Pattern ) ); setPen( TQt::NoPen ); // i know this isn't really fast, but i blame it all on TQt with its // stupid container classes... what's wrong with the STL ? TQPointArray t( pts.size() ); int c = 0; for( std::vector::const_iterator i = pts.begin(); i != pts.end(); ++i ) { t.putPoints( c++, 1, i->x(), i->y() ); }; mP.drawPolygon( t, winding, index, npoints ); setPen( oldpen ); setBrush( oldbrush ); if( mNeedOverlay ) mOverlay.push_back( t.boundingRect() ); } void KigPainter::drawArea( const std::vector& pts, bool border ) { TQPen oldpen = mP.pen(); TQBrush oldbrush = mP.brush(); setBrush( TQBrush( color, SolidPattern ) ); if ( border ) setPen( TQPen( color, width == -1 ? 1 : width ) ); else setPen( TQt::NoPen ); TQPointArray t( pts.size() ); int c = 0; for( std::vector::const_iterator i = pts.begin(); i != pts.end(); ++i ) { TQPoint p = toScreen( *i ); t.putPoints( c++, 1, p.x(), p.y() ); } mP.drawPolygon( t ); setPen( oldpen ); setBrush( oldbrush ); if( mNeedOverlay ) mOverlay.push_back( t.boundingRect() ); } Rect KigPainter::window() { return msi.shownRect(); } void KigPainter::circleOverlayRecurse( const Coordinate& centre, double radiussq, const Rect& cr ) { Rect currentRect = cr.normalized(); if( !currentRect.intersects( window() ) ) return; // this code is an adaptation of Marc Bartsch's code, from KGeo Coordinate tl = currentRect.topLeft(); Coordinate br = currentRect.bottomRight(); Coordinate tr = currentRect.topRight(); Coordinate bl = currentRect.bottomLeft(); Coordinate c = currentRect.center(); // mp: we compute the minimum and maximum distance from the center // of the circle and this rect double distxmin = 0, distxmax = 0, distymin = 0, distymax = 0; if ( centre.x >= tr.x ) distxmin = centre.x - tr.x; if ( centre.x <= bl.x ) distxmin = bl.x - centre.x; if ( centre.y >= tr.y ) distymin = centre.y - tr.y; if ( centre.y <= bl.y ) distymin = bl.y - centre.y; distxmax = fabs(centre.x - c.x) + currentRect.width()/2; distymax = fabs(centre.y - c.y) + currentRect.height()/2; // this should take into account the thickness of the line... distxmin -= pixelWidth(); if (distxmin < 0) distxmin = 0; distxmax += pixelWidth(); distymin -= pixelWidth(); if (distymin < 0) distymin = 0; distymax += pixelWidth(); double distminsq = distxmin*distxmin + distymin*distymin; double distmaxsq = distxmax*distxmax + distymax*distymax; // if the circle doesn't touch this rect, we return // too far from the centre if (distminsq > radiussq) return; // too near to the centre if (distmaxsq < radiussq) return; // the rect contains some of the circle // -> if it's small enough, we keep it if( currentRect.width() < overlayRectSize() ) { mOverlay.push_back( toScreenEnlarge( currentRect) ); } else { // this func works recursive: we subdivide the current rect, and if // it is of a good size, we keep it, otherwise we handle it again double width = currentRect.width() / 2; double height = currentRect.height() / 2; Rect r1 ( c, -width, -height); r1.normalize(); circleOverlayRecurse(centre, radiussq, r1); Rect r2 ( c, width, -height); r2.normalize(); circleOverlayRecurse(centre, radiussq, r2); Rect r3 ( c, -width, height); r3.normalize(); circleOverlayRecurse(centre, radiussq, r3); Rect r4 ( c, width, height); r4.normalize(); circleOverlayRecurse(centre, radiussq, r4); }; } void KigPainter::circleOverlay( const Coordinate& centre, double radius ) { double t = radius + pixelWidth(); Coordinate r( t, t ); Coordinate bottomLeft = centre - r; Coordinate topRight = centre + r; Rect rect( bottomLeft, topRight ); circleOverlayRecurse ( centre , radius*radius, rect ); } void KigPainter::segmentOverlay( const Coordinate& p1, const Coordinate& p2 ) { // this code is based upon what Marc Bartsch wrote for KGeo // some stuff we may need: Coordinate p3 = p2 - p1; Rect border = window(); // double length = p3.length(); // mp: using the l-infinity distance is more natural here double length = fabs(p3.x); if ( fabs( p3.y ) > length ) length = fabs( p3.y ); if ( length < pixelWidth() ) { // hopefully prevent SIGZERO's mOverlay.push_back( toScreen( Rect( p1, p2 ) ) ); return; }; p3 *= overlayRectSize(); p3 /= length; int counter = 0; Rect r(p1, p2); r.normalize(); for (;;) { Rect tR( Coordinate( 0, 0 ), overlayRectSize(), overlayRectSize() ); Coordinate tP = p1+p3*counter; tR.setCenter(tP); if (!tR.intersects(r)) { //kdDebug()<< "stopped after "<< counter << " passes." << endl; break; } if (tR.intersects(border)) mOverlay.push_back( toScreenEnlarge( tR ) ); if (++counter > 100) { kdDebug()<< k_funcinfo << "counter got too big :( " << endl; break; } } } double KigPainter::overlayRectSize() { return 20 * pixelWidth(); } void KigPainter::pointOverlay( const Coordinate& p1 ) { Rect r( p1, 3*pixelWidth(), 3*pixelWidth()); r.setCenter( p1 ); mOverlay.push_back( toScreen( r) ); } double KigPainter::pixelWidth() { return msi.pixelWidth(); } void KigPainter::setWholeWinOverlay() { mOverlay.clear(); mOverlay.push_back( mP.viewport() ); // don't accept any more overlay's... mNeedOverlay = false; } TQPoint KigPainter::toScreen( const Coordinate p ) const { return msi.toScreen( p ); } void KigPainter::drawGrid( const CoordinateSystem& c, bool showGrid, bool showAxes ) { c.drawGrid( *this, showGrid, showAxes ); setWholeWinOverlay(); } void KigPainter::drawObject( const ObjectHolder* o, bool ss ) { o->draw( *this, ss ); } void KigPainter::drawObjects( const std::vector& os, bool sel ) { drawObjects( os.begin(), os.end(), sel ); } void KigPainter::drawFilledRect( const TQRect& r ) { TQPen pen( TQt::black, 1, TQt::DotLine ); setPen( pen ); setBrush( TQBrush( TQt::cyan, Dense6Pattern ) ); drawRect( r.normalize() ); } void KigPainter::drawTextStd( const TQPoint& p, const TQString& s ) { if ( s.isNull() ) return; // tf = text formatting flags int tf = AlignLeft | AlignTop | DontClip | WordBreak; // we need the rect where we're going to paint text setPen(TQPen(TQt::blue, 1, SolidLine)); setBrush(TQt::NoBrush); drawText( Rect( msi.fromScreen(p), window().bottomRight() ).normalized(), s, tf ); } TQRect KigPainter::toScreen( const Rect r ) const { return msi.toScreen( r ); } TQRect KigPainter::toScreenEnlarge( const Rect r ) const { if ( overlayenlarge == 0 ) return msi.toScreen( r ); TQRect qr = msi.toScreen( r ); qr.moveBy ( -overlayenlarge, -overlayenlarge ); int w = qr.width(); int h = qr.height(); qr.setWidth (w + 2*overlayenlarge); qr.setHeight (h + 2*overlayenlarge); return qr; } void KigPainter::drawSimpleText( const Coordinate& c, const TQString s ) { int tf = AlignLeft | AlignTop | DontClip | WordBreak; drawText( c, s, tf); } void KigPainter::drawText( const Coordinate p, const TQString s, int textFlags, int len ) { drawText( Rect( p, mP.window().right(), mP.window().top() ), s, textFlags, len ); } const Rect KigPainter::simpleBoundingRect( const Coordinate& c, const TQString s ) { int tf = AlignLeft | AlignTop | DontClip | WordBreak; return boundingRect( c, s, tf ); } const Rect KigPainter::boundingRect( const Coordinate& c, const TQString s, int f, int l ) const { return boundingRect( Rect( c, mP.window().right(), mP.window().top() ), s, f, l ); } Coordinate KigPainter::fromScreen( const TQPoint& p ) const { return msi.fromScreen( p ); } Rect KigPainter::fromScreen( const TQRect& r ) const { return msi.fromScreen( r ); } void KigPainter::drawRay( const Coordinate& a, const Coordinate& b ) { Coordinate tb = b; calcRayBorderPoints( a, tb, window() ); drawSegment( a, tb ); } typedef std::pair coordparampair; struct workitem { workitem( coordparampair f, coordparampair s, Rect *o) : first(f), second(s), overlay(o) {} coordparampair first; coordparampair second; Rect *overlay; }; void KigPainter::drawLine( const LineData& d ) { if ( d.a != d.b ) { LineData l = calcBorderPoints( d, window() ); drawSegment( l.a, l.b ); } } void KigPainter::drawSegment( const LineData& d ) { drawSegment( d.a, d.b ); } void KigPainter::drawRay( const LineData& d ) { drawRay( d.a, d.b ); } void KigPainter::drawAngle( const Coordinate& cpoint, const double dstartangle, const double dangle ) { // convert to 16th of degrees... const int startangle = static_cast( Goniometry::convert( 16 * dstartangle, Goniometry::Rad, Goniometry::Deg ) ); const int angle = static_cast( Goniometry::convert( 16 * dangle, Goniometry::Rad, Goniometry::Deg ) ); TQPoint point = toScreen( cpoint ); // int radius = mP.window().width() / 5; int radius = 50; TQRect surroundingRect( 0, 0, radius*2, radius*2 ); surroundingRect.moveCenter( point ); mP.drawArc( surroundingRect, startangle, angle ); // now for the arrow... TQPoint end( static_cast( point.x() + radius * cos( dstartangle + dangle ) ), static_cast( point.y() - radius * sin( dstartangle + dangle ) ) ); TQPoint vect = (end - point); double vectlen = sqrt( float( vect.x() * vect.x() + vect.y() * vect.y() ) ); TQPoint orthvect( -vect.y(), vect.x() ); vect = vect * 6 / vectlen; orthvect = orthvect * 6 / vectlen; TQPointArray arrow( 3 ); arrow.setPoint( 0, end ); arrow.setPoint( 1, end + orthvect + vect ); arrow.setPoint( 2, end + orthvect - vect ); // std::vector arrow; // arrow.push_back( end ); // arrow.push_back( end + orthvect + vect ); // arrow.push_back( end + orthvect - vect ); setBrushStyle( TQt::SolidPattern ); // drawPolygon( arrow ); mP.drawPolygon( arrow, false, 0, -1 ); // if ( mNeedOverlay ) mOverlay.push_back( toScreen( r ) ); setWholeWinOverlay(); //mp: ugly! why not compute a correct overlay? // mOverlay.push_back( arrow.boundingRect() ); } void KigPainter::drawPolygon( const std::vector& pts, bool winding, int index, int npoints ) { using namespace std; vector points; for ( uint i = 0; i < pts.size(); ++i ) points.push_back( toScreen( pts[i] ) ); drawPolygon( points, winding, index, npoints ); } void KigPainter::drawVector( const Coordinate& a, const Coordinate& b ) { // bugfix... if ( a == b ) return; // the segment drawSegment( a, b ); // the arrows... Coordinate dir = b - a; Coordinate perp( dir.y, -dir.x ); double length = perp.length(); perp *= 10* pixelWidth(); perp /= length; dir *= 10 * pixelWidth(); dir /= length; Coordinate c = b - dir + perp; Coordinate d = b - dir - perp; // draw the arrow lines with a normal style mP.setPen( TQPen( color, width == -1 ? 1 : width, TQt::SolidLine ) ); drawSegment( b, c ); drawSegment( b, d ); // setting again the original style mP.setPen( TQPen( color, width == -1 ? 1 : width, style ) ); } /* *** this function is commented out *** inline Coordinate locusGetCoord( double p, const CurveImp* curve, const ObjectHierarchy& h, bool& valid, const KigDocument& doc ) { Coordinate pt = curve->getPoint( p, valid, doc ); if ( ! valid ) return Coordinate(); PointImp pimp( pt ); Args args; args.push_back( &pimp ); std::vector calced = h.calc( args, doc ); assert( calced.size() == 1 ); ObjectImp* o = calced.front(); Coordinate ret; if ( o->inherits( ObjectImp::ID_PointImp ) ) { valid = true; ret = static_cast( o )->coordinate(); } else valid = false; delete o; return ret; }; */ class CurveImpPointCalcer { const CurveImp* curve; public: CurveImpPointCalcer( const CurveImp* c ) : curve( c ) { } static const double endinterval; inline const Coordinate getPoint( double param, const KigDocument& d ) const { return curve->getPoint( param, d ); } }; const double CurveImpPointCalcer::endinterval = 1.; void KigPainter::drawCurve( const CurveImp* curve ) { // we manage our own overlay bool tNeedOverlay = mNeedOverlay; mNeedOverlay = false; TQPen pen = mP.pen(); // this stack contains pairs of Coordinates ( parameter intervals ) // that we still need to process: std::stack workstack; // mp: this stack contains all the generated overlays: // the strategy for generating the overlay structure is the same // recursive-like used to draw the segments: a new rectangle is // generated whenever the length of a segment becomes lower than // overlayRectSize(), or if the segment would be drawn anyway // to avoid strange things from happening we impose that the distance // in parameter space be less than a threshold before generating // any overlay. // // The third parameter in workitem is a pointer into a stack of // all generated rectangles (in real coordinate space); if 0 // there is no rectangles associated to that segment yet. // // Using the final mOverlay stack would be much more efficient, but // 1. needs transformations into window space // 2. would be more difficult to drop rectangles not intersecting // the window. std::stack overlaystack; // mp: the original version in which an initial set of 20 intervals // were pushed onto the stack is replaced by a single interval and // by forcing subdivision till h < hmax (with more or less the same // final result). // First push the [0,1] interval into the stack: Coordinate coo1 = curve->getPoint( 0., mdoc ); Coordinate coo2 = curve->getPoint( 1., mdoc ); workstack.push( workitem( coordparampair( 0., coo1 ), coordparampair( 1., coo2 ), 0 ) ); // maxlength is the square of the maximum size that we allow // between two points.. double maxlength = 1.5 * pixelWidth(); maxlength *= maxlength; // error squared is required to be less that sigma (half pixel) double sigma = maxlength/4; // distance between two parameter values cannot be too small double hmin = 3e-5; // distance between two parameter values cannot be too large double hmax = 1./40; double hmaxoverlay = 1./8; int count = 1; // the number of segments we've already // visited... static const int maxnumberofpoints = 1000; const Rect& sr = window(); // what this algorithm does is approximating the curve with a set of // segments. we don't draw the individual segments, but use // TQPainter::drawPolyline() so that the line styles work properly. // Possibly there are performance advantages as well ? this array // is a buffer of the polyline approximation of the part of the // curve that we are currently processing. TQPointArray curpolyline( 1000 ); int curpolylinenextfree = 0; // we don't use recursion, but a stack based approach for efficiency // concerns... while ( ! workstack.empty() && count < maxnumberofpoints ) { workitem curitem = workstack.top(); workstack.pop(); bool curitemok = true; while ( curitemok && count++ < maxnumberofpoints ) { double t0 = curitem.first.first; double t1 = curitem.second.first; Coordinate p0 = curitem.first.second; bool valid0 = p0.valid(); Coordinate p1 = curitem.second.second; bool valid1 = p1.valid(); // we take the middle parameter of the two previous points... double t2 = ( t0 + t1 ) / 2; double h = fabs( t1 - t0 ) /2; // if exactly one of the two endpoints is invalid, then // we prefer to find an internal value of the parameter // separating valid points from invalid points. We use // a bisection strategy (this is not implemented yet!) // if ( ( valid0 && ! valid1 ) || ( valid1 && ! valid0 ) ) // { // while ( h >= hmin ) // { // ....................................... // } // } Rect *overlaypt = curitem.overlay; Coordinate p2 = curve->getPoint( t2, mdoc ); bool allvalid = p2.valid() && valid0 && valid1; bool dooverlay = ! overlaypt && h < hmaxoverlay && valid0 && valid1 && fabs( p0.x - p1.x ) <= overlayRectSize() && fabs( p0.y - p1.y ) <= overlayRectSize(); bool addn = sr.contains( p2 ) || h >= hmax; // estimated error between the curve and the segments double errsq = 1e21; if ( allvalid ) errsq = (0.5*p0 + 0.5*p1 - p2).squareLength(); errsq /= 4; curitemok = false; // bool dodraw = allvalid && h < hmax && ( errsq < sigma || h < hmin ); bool dodraw = allvalid && h < hmax && errsq < sigma; if ( tNeedOverlay && ( dooverlay || dodraw ) ) { Rect newoverlay( p0, p1 ); overlaystack.push( newoverlay ); overlaypt = &overlaystack.top(); } if ( overlaypt ) overlaypt->setContains( p2 ); if ( dodraw ) { // draw the two segments TQPoint tp0 = toScreen(p0); TQPoint tp1 = toScreen(p1); TQPoint tp2 = toScreen(p2); if ( curpolylinenextfree > 0 && curpolyline[curpolylinenextfree - 1] != tp1 ) { // flush the current part of the curve mP.drawPolyline( curpolyline, 0, curpolylinenextfree ); curpolylinenextfree = 0; } if ( curpolylinenextfree == 0 ) curpolyline[curpolylinenextfree++] = tp1; curpolyline[curpolylinenextfree++] = tp2; curpolyline[curpolylinenextfree++] = tp0; } else if ( h >= hmin ) // we do not continue to subdivide indefinitely! { // push into stack in order to process both subintervals if ( addn || ( valid0 && sr.contains( p0 ) ) ) workstack.push( workitem( curitem.first, coordparampair( t2, p2 ), overlaypt ) ); if ( addn || ( valid1 && sr.contains( p1 ) ) ) { curitem = workitem( coordparampair( t2, p2 ), curitem.second , overlaypt ); curitemok = true; } } } } // flush the rest of the curve mP.drawPolyline( curpolyline, 0, curpolylinenextfree ); curpolylinenextfree = 0; if ( ! workstack.empty () ) kdDebug() << "Stack not empty in KigPainter::drawCurve!\n" << endl; assert ( tNeedOverlay || overlaystack.empty() ); if ( tNeedOverlay ) { Rect border = window(); while ( ! overlaystack.empty() ) { Rect overlay = overlaystack.top(); overlaystack.pop(); if (overlay.intersects( border )) mOverlay.push_back( toScreenEnlarge( overlay ) ); } } mNeedOverlay = tNeedOverlay; } void KigPainter::drawTextFrame( const Rect& frame, const TQString& s, bool needframe ) { TQPen oldpen = mP.pen(); TQBrush oldbrush = mP.brush(); if ( needframe ) { // inspired upon kgeo, thanks to Marc Bartsch, i've taken some of // his code too.. setPen( TQPen( TQt::black, 1 ) ); setBrush( TQBrush( TQColor( 255, 255, 222 ) ) ); drawRect( frame ); setPen( TQPen( TQColor( 197, 194, 197 ), 1, TQt::SolidLine ) ); TQRect qr = toScreen( frame ); mP.drawLine( qr.topLeft(), qr.topRight() ); mP.drawLine( qr.topLeft(), qr.bottomLeft() ); }; setPen( oldpen ); setBrush( oldbrush ); drawText( frame, s, TQt::AlignVCenter | TQt::AlignLeft ); } void KigPainter::drawArc( const Coordinate& center, const double radius, const double dstartangle, const double dangle ) { // convert to 16th of degrees... const int startangle = static_cast( Goniometry::convert( 16 * dstartangle, Goniometry::Rad, Goniometry::Deg ) ); const int angle = static_cast( Goniometry::convert( 16 * dangle, Goniometry::Rad, Goniometry::Deg ) ); if ( angle <= 16 ) { Coordinate a = center + radius * Coordinate( cos( dstartangle ), sin( dstartangle )); Coordinate b = center + radius * Coordinate( cos( dstartangle + dangle ), sin( dstartangle + dangle )); drawSegment ( a , b ); } else { Rect krect( 0, 0, 2*radius, 2*radius ); krect.setCenter( center ); TQRect rect = toScreen( krect ); mP.drawArc( rect, startangle, angle ); setWholeWinOverlay(); } }