summaryrefslogtreecommitdiffstats
path: root/kig/misc/kigpainter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kig/misc/kigpainter.cpp')
-rw-r--r--kig/misc/kigpainter.cpp953
1 files changed, 953 insertions, 0 deletions
diff --git a/kig/misc/kigpainter.cpp b/kig/misc/kigpainter.cpp
new file mode 100644
index 00000000..e2b2f440
--- /dev/null
+++ b/kig/misc/kigpainter.cpp
@@ -0,0 +1,953 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 2002-2003 Dominique Devriese <devriese@kde.org>
+
+ 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 <qpen.h>
+
+#include <cmath>
+#include <stack>
+#include <functional>
+#include <algorithm>
+
+KigPainter::KigPainter( const ScreenInfo& si, QPaintDevice* device,
+ const KigDocument& doc, bool no )
+ : mP ( device ),
+ color( Qt::blue ),
+ style( Qt::SolidLine ),
+ pointstyle( 0 ),
+ width( -1 ),
+ brushStyle( Qt::NoBrush ),
+ brushColor( Qt::blue ),
+ mdoc( doc ),
+ msi( si ),
+ mNeedOverlay( no ),
+ overlayenlarge( 0 )
+{
+ mP.setBackgroundColor( Qt::white );
+}
+
+KigPainter::~KigPainter()
+{
+}
+
+void KigPainter::drawRect( const Rect& r )
+{
+ Rect rt = r.normalized();
+ QRect qr = toScreen(rt);
+ qr.normalize();
+ mP.drawRect(qr);
+ if( mNeedOverlay ) mOverlay.push_back( qr );
+}
+
+void KigPainter::drawRect( const QRect& 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 );
+ QRect qr = toScreen( r );
+ mP.drawEllipse ( qr );
+ if( mNeedOverlay ) circleOverlay( center, radius );
+}
+
+void KigPainter::drawSegment( const Coordinate& from, const Coordinate& to )
+{
+ QPoint 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( QPen( color, 1, style ) );
+ switch ( pointstyle )
+ {
+ case 0:
+ {
+ double radius = twidth * pixelWidth();
+ setBrushStyle( Qt::SolidPattern );
+ Coordinate rad( radius, radius );
+ rad /= 2;
+ Coordinate tl = p - rad;
+ Coordinate br = p + rad;
+ Rect r( tl, br );
+ QRect qr = toScreen( r );
+ mP.drawEllipse( qr );
+ if( mNeedOverlay ) mOverlay.push_back( qr );
+ break;
+ }
+ case 1:
+ {
+ double radius = twidth * pixelWidth();
+ setBrushStyle( Qt::NoBrush );
+ Coordinate rad( radius, radius );
+ rad /= 2;
+ Coordinate tl = p - rad;
+ Coordinate br = p + rad;
+ Rect r( tl, br );
+ QRect 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 );
+ QRect qr = toScreen( r );
+ mP.drawRect( qr );
+ mP.fillRect( qr, QBrush( color, Qt::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 );
+ QRect 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 );
+ QRect qr = toScreen( r );
+ mP.setPen( QPen( color, 2 ) );
+ mP.drawLine( qr.topLeft(), qr.bottomRight() );
+ mP.drawLine( qr.topRight(), qr.bottomLeft() );
+ if( mNeedOverlay ) mOverlay.push_back( qr );
+ break;
+ }
+ }
+ mP.setPen( QPen( 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 QString s, int textFlags, int len )
+{
+ QRect 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 QRect& r, const QString s, int textFlags, int len )
+{
+ // kdDebug() << Rect::fromQRect( mP.boundingRect( r, textFlags, s, len ) ) << endl;
+ QRect 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 QString s,
+ int f, int l ) const
+{
+ QRect 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 QColor& c )
+{
+ color = c;
+ mP.setPen( QPen( color, width == -1 ? 1 : width, style ) );
+}
+
+void KigPainter::setStyle( const PenStyle c )
+{
+ style = c;
+ mP.setPen( QPen( color, width == -1 ? 1 : width, style ) );
+}
+
+void KigPainter::setWidth( const int c )
+{
+ width = c;
+ if (c > 0) overlayenlarge = c - 1;
+ mP.setPen( QPen( color, width == -1 ? 1 : width, style ) );
+}
+
+void KigPainter::setPointStyle( const int p )
+{
+ pointstyle = p;
+}
+
+void KigPainter::setPen( const QPen& p )
+{
+ color = p.color();
+ width = p.width();
+ style = p.style();
+ mP.setPen(p);
+}
+
+void KigPainter::setBrush( const QBrush& b )
+{
+ brushStyle = b.style();
+ brushColor = b.color();
+ mP.setBrush( b );
+}
+
+void KigPainter::setBrushStyle( const BrushStyle c )
+{
+ brushStyle = c;
+ mP.setBrush( QBrush( brushColor, brushStyle ) );
+}
+
+void KigPainter::setBrushColor( const QColor& c )
+{
+ brushColor = c;
+ mP.setBrush( QBrush( brushColor, brushStyle ) );
+}
+
+bool KigPainter::getNightVision( ) const
+{
+ return mdoc.getNightVision();
+}
+
+QColor KigPainter::getColor() const
+{
+ return color;
+}
+
+/*
+static void setContains( QRect& r, const QPoint& 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<QPoint>& pts,
+ bool winding, int index, int npoints )
+{
+ QPen oldpen = mP.pen();
+ QBrush oldbrush = mP.brush();
+ setBrush( QBrush( color, Dense4Pattern ) );
+ setPen( Qt::NoPen );
+ // i know this isn't really fast, but i blame it all on Qt with its
+ // stupid container classes... what's wrong with the STL ?
+ QPointArray t( pts.size() );
+ int c = 0;
+ for( std::vector<QPoint>::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<Coordinate>& pts, bool border )
+{
+ QPen oldpen = mP.pen();
+ QBrush oldbrush = mP.brush();
+ setBrush( QBrush( color, SolidPattern ) );
+ if ( border )
+ setPen( QPen( color, width == -1 ? 1 : width ) );
+ else
+ setPen( Qt::NoPen );
+ QPointArray t( pts.size() );
+ int c = 0;
+ for( std::vector<Coordinate>::const_iterator i = pts.begin(); i != pts.end(); ++i )
+ {
+ QPoint 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;
+}
+
+QPoint 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<ObjectHolder*>& os, bool sel )
+{
+ drawObjects( os.begin(), os.end(), sel );
+}
+
+void KigPainter::drawFilledRect( const QRect& r )
+{
+ QPen pen( Qt::black, 1, Qt::DotLine );
+ setPen( pen );
+ setBrush( QBrush( Qt::cyan, Dense6Pattern ) );
+ drawRect( r.normalize() );
+}
+
+void KigPainter::drawTextStd( const QPoint& p, const QString& 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(QPen(Qt::blue, 1, SolidLine));
+ setBrush(Qt::NoBrush);
+ drawText( Rect(
+ msi.fromScreen(p), window().bottomRight()
+ ).normalized(), s, tf );
+
+}
+
+QRect KigPainter::toScreen( const Rect r ) const
+{
+ return msi.toScreen( r );
+}
+
+QRect KigPainter::toScreenEnlarge( const Rect r ) const
+{
+ if ( overlayenlarge == 0 ) return msi.toScreen( r );
+
+ QRect 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 QString s )
+{
+ int tf = AlignLeft | AlignTop | DontClip | WordBreak;
+ drawText( c, s, tf);
+}
+
+void KigPainter::drawText( const Coordinate p, const QString 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 QString s )
+{
+ int tf = AlignLeft | AlignTop | DontClip | WordBreak;
+ return boundingRect( c, s, tf );
+}
+
+const Rect KigPainter::boundingRect( const Coordinate& c, const QString s,
+ int f, int l ) const
+{
+ return boundingRect( Rect( c, mP.window().right(), mP.window().top() ),
+ s, f, l );
+}
+
+Coordinate KigPainter::fromScreen( const QPoint& p ) const
+{
+ return msi.fromScreen( p );
+}
+
+Rect KigPainter::fromScreen( const QRect& 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<double,Coordinate> 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<int>( Goniometry::convert( 16 * dstartangle, Goniometry::Rad, Goniometry::Deg ) );
+ const int angle = static_cast<int>( Goniometry::convert( 16 * dangle, Goniometry::Rad, Goniometry::Deg ) );
+
+ QPoint point = toScreen( cpoint );
+
+// int radius = mP.window().width() / 5;
+ int radius = 50;
+ QRect surroundingRect( 0, 0, radius*2, radius*2 );
+ surroundingRect.moveCenter( point );
+
+ mP.drawArc( surroundingRect, startangle, angle );
+
+ // now for the arrow...
+ QPoint end( static_cast<int>( point.x() + radius * cos( dstartangle + dangle ) ),
+ static_cast<int>( point.y() - radius * sin( dstartangle + dangle ) ) );
+ QPoint vect = (end - point);
+ double vectlen = sqrt( float( vect.x() * vect.x() + vect.y() * vect.y() ) );
+ QPoint orthvect( -vect.y(), vect.x() );
+ vect = vect * 6 / vectlen;
+ orthvect = orthvect * 6 / vectlen;
+
+ QPointArray arrow( 3 );
+ arrow.setPoint( 0, end );
+ arrow.setPoint( 1, end + orthvect + vect );
+ arrow.setPoint( 2, end + orthvect - vect );
+// std::vector<QPoint> arrow;
+// arrow.push_back( end );
+// arrow.push_back( end + orthvect + vect );
+// arrow.push_back( end + orthvect - vect );
+
+ setBrushStyle( Qt::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<Coordinate>& pts,
+ bool winding, int index, int npoints )
+{
+ using namespace std;
+ vector<QPoint> 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( QPen( color, width == -1 ? 1 : width, Qt::SolidLine ) );
+ drawSegment( b, c );
+ drawSegment( b, d );
+ // setting again the original style
+ mP.setPen( QPen( 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<ObjectImp*> 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<PointImp*>( 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;
+
+ QPen pen = mP.pen();
+
+ // this stack contains pairs of Coordinates ( parameter intervals )
+ // that we still need to process:
+ std::stack<workitem> 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<Rect> 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
+ // QPainter::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.
+ QPointArray 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
+ QPoint tp0 = toScreen(p0);
+ QPoint tp1 = toScreen(p1);
+ QPoint 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 QString& s, bool needframe )
+{
+ QPen oldpen = mP.pen();
+ QBrush oldbrush = mP.brush();
+ if ( needframe )
+ {
+ // inspired upon kgeo, thanks to Marc Bartsch, i've taken some of
+ // his code too..
+ setPen( QPen( Qt::black, 1 ) );
+ setBrush( QBrush( QColor( 255, 255, 222 ) ) );
+ drawRect( frame );
+ setPen( QPen( QColor( 197, 194, 197 ), 1, Qt::SolidLine ) );
+
+ QRect qr = toScreen( frame );
+
+ mP.drawLine( qr.topLeft(), qr.topRight() );
+ mP.drawLine( qr.topLeft(), qr.bottomLeft() );
+ };
+ setPen( oldpen );
+ setBrush( oldbrush );
+ drawText( frame, s, Qt::AlignVCenter | Qt::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<int>( Goniometry::convert( 16 * dstartangle, Goniometry::Rad, Goniometry::Deg ) );
+ const int angle = static_cast<int>( 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 );
+ QRect rect = toScreen( krect );
+
+ mP.drawArc( rect, startangle, angle );
+ setWholeWinOverlay();
+ }
+}
+