You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeedu/kig/misc/kigpainter.cpp

954 lines
28 KiB

/**
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 <tqpen.h>
#include <cmath>
#include <stack>
#include <functional>
#include <algorithm>
KigPainter::KigPainter( const ScreenInfo& si, TQPaintDevice* device,
const KigDocument& doc, bool no )
: mP ( device ),
color( TQt::blue ),
style( Qt::SolidLine ),
pointstyle( 0 ),
width( -1 ),
brushStyle( Qt::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( Qt::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( Qt::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 Qt::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 Qt::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<TQPoint>& pts,
bool winding, int index, int npoints )
{
TQPen oldpen = mP.pen();
TQBrush oldbrush = mP.brush();
setBrush( TQBrush( color, Dense4Pattern ) );
setPen( Qt::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<TQPoint>::const_iterator i = pts.begin(); i != pts.end(); ++i )
{
t.putPoints( c++, 1, i->x(), i->y() );
};
mP.tqdrawPolygon( 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 )
{
TQPen oldpen = mP.pen();
TQBrush oldbrush = mP.brush();
setBrush( TQBrush( color, SolidPattern ) );
if ( border )
setPen( TQPen( color, width == -1 ? 1 : width ) );
else
setPen( Qt::NoPen );
TQPointArray t( pts.size() );
int c = 0;
for( std::vector<Coordinate>::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<ObjectHolder*>& 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(Qt::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<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 ) );
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<int>( point.x() + radius * cos( dstartangle + dangle ) ),
static_cast<int>( 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<TQPoint> arrow;
// arrow.push_back( end );
// arrow.push_back( end + orthvect + vect );
// arrow.push_back( end + orthvect - vect );
setBrushStyle( Qt::SolidPattern );
// drawPolygon( arrow );
mP.tqdrawPolygon( 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<TQPoint> 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<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;
TQPen 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
// 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.tqcontains( 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.tqdrawPolyline( 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.tqcontains( p0 ) ) )
workstack.push( workitem( curitem.first, coordparampair( t2, p2 ),
overlaypt ) );
if ( addn || ( valid1 && sr.tqcontains( p1 ) ) )
{
curitem = workitem( coordparampair( t2, p2 ), curitem.second ,
overlaypt );
curitemok = true;
}
}
}
}
// flush the rest of the curve
mP.tqdrawPolyline( 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<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 );
TQRect rect = toScreen( krect );
mP.drawArc( rect, startangle, angle );
setWholeWinOverlay();
}
}