summaryrefslogtreecommitdiffstats
path: root/kig/misc/coordinate_system.cpp
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce599e4f9f94b4eb00c1b5edb85bce5431ab3df2 (patch)
treed3bb9f5d25a2dc09ca81adecf39621d871534297 /kig/misc/coordinate_system.cpp
downloadtdeedu-ce599e4f9f94b4eb00c1b5edb85bce5431ab3df2.tar.gz
tdeedu-ce599e4f9f94b4eb00c1b5edb85bce5431ab3df2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdeedu@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kig/misc/coordinate_system.cpp')
-rw-r--r--kig/misc/coordinate_system.cpp720
1 files changed, 720 insertions, 0 deletions
diff --git a/kig/misc/coordinate_system.cpp b/kig/misc/coordinate_system.cpp
new file mode 100644
index 00000000..dd5181c2
--- /dev/null
+++ b/kig/misc/coordinate_system.cpp
@@ -0,0 +1,720 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 2002 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 "coordinate_system.h"
+
+#include "../kig/kig_document.h"
+#include "../kig/kig_view.h"
+
+#include "common.h"
+#include "coordinate.h"
+#include "goniometry.h"
+#include "kigpainter.h"
+
+#include <qpainter.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <knumvalidator.h>
+
+#include <string>
+#include <math.h>
+
+class CoordinateValidator
+ : public QValidator
+{
+ bool mpolar;
+#ifdef KIG_USE_KDOUBLEVALIDATOR
+ KDoubleValidator mdv;
+#else
+ KFloatValidator mdv;
+#endif
+ mutable QRegExp mre;
+public:
+ CoordinateValidator( bool polar );
+ ~CoordinateValidator();
+ State validate ( QString & input, int & pos ) const;
+ void fixup ( QString & input ) const;
+};
+
+
+CoordinateValidator::CoordinateValidator( bool polar )
+ : QValidator( 0, 0 ), mpolar( polar ), mdv( 0, 0 ),
+ mre( polar ? "\\(? ?([0-9.,+-]+); ?([0-9.,+-]+) ?°? ?\\)?"
+ : "\\(? ?([0-9.,+-]+); ?([0-9.,+-]+) ?\\)?" )
+{
+}
+
+CoordinateValidator::~CoordinateValidator()
+{
+}
+
+QValidator::State CoordinateValidator::validate( QString & input, int & pos ) const
+{
+ QString tinput = input;
+ if ( tinput[tinput.length() - 1 ] == ')' ) tinput.truncate( tinput.length() - 1 );
+ if ( mpolar )
+ {
+ if ( tinput[tinput.length() - 1 ] == ' ' ) tinput.truncate( tinput.length() - 1 );
+ if ( tinput[tinput.length() - 1 ] == '°' ) tinput.truncate( tinput.length() - 1 );
+ };
+ if( tinput[tinput.length() - 1 ] == ' ' ) tinput.truncate( tinput.length() - 1 );
+ if ( tinput[0] == '(' ) tinput = tinput.mid( 1 );
+ if( tinput[0] == ' ' ) tinput = tinput.mid( 1 );
+ int scp = tinput.find( ';' );
+ if ( scp == -1 ) return mdv.validate( tinput, pos ) == Invalid ? Invalid : Valid;
+ else
+ {
+ QString p1 = tinput.left( scp );
+ QString p2 = tinput.mid( scp + 1 );
+
+ State ret = Acceptable;
+
+ int boguspos = 0;
+ ret = kigMin( ret, mdv.validate( p1, boguspos ) );
+
+ boguspos = 0;
+ ret = kigMin( ret, mdv.validate( p2, boguspos ) );
+
+ return ret;
+ };
+}
+
+void CoordinateValidator::fixup( QString & input ) const
+{
+ int nsc = input.contains( ';' );
+ if ( nsc > 1 )
+ {
+ // where is the second ';'
+ int i = input.find( ';' );
+ i = input.find( ';', i );
+ input = input.left( i );
+ };
+ // now the string has at most one semicolon left..
+ int sc = input.find( ';' );
+ if ( sc == -1 )
+ {
+ sc = input.length();
+ KLocale* l = KGlobal::locale();
+ if ( mpolar )
+ input.append( QString::fromLatin1( ";" ) + l->positiveSign() +
+ QString::fromLatin1( "0°" ) );
+ else
+ input.append( QString::fromLatin1( ";" ) + l->positiveSign() +
+ QString::fromLatin1( "0" ) + l->decimalSymbol() +
+ QString::fromLatin1( "0" ) );
+ };
+ mre.exactMatch( input );
+ QString ds1 = mre.cap( 1 );
+ mdv.fixup( ds1 );
+ QString ds2 = mre.cap( 2 );
+ mdv.fixup( ds2 );
+ input = ds1 + QString::fromLatin1( "; " ) + ds2;
+}
+
+EuclideanCoords::EuclideanCoords()
+{
+}
+
+QString EuclideanCoords::fromScreen( const Coordinate& p, const KigDocument& d ) const
+{
+ // i used to use the widget size here, but that's no good idea,
+ // since an object isn't asked to recalc every time the widget size
+ // changes.. might be a good idea to do that, but well, maybe some
+ // other time :)
+ Rect sr = d.suggestedRect();
+ double m = kigMax( sr.width(), sr.height() );
+ int l = kigMax( 0, (int) ( 3 - log10( m ) ) );
+ QString xs = KGlobal::locale()->formatNumber( p.x, l );
+ QString ys = KGlobal::locale()->formatNumber( p.y, l );
+ return QString::fromLatin1( "( %1; %2 )" ).arg( xs ).arg( ys );
+}
+
+Coordinate EuclideanCoords::toScreen(const QString& s, bool& ok) const
+{
+ QRegExp r( "\\(? ?([0-9.,+-]+); ?([0-9.,+-]+) ?\\)?" );
+ ok = ( r.search(s) == 0 );
+ if (ok)
+ {
+ QString xs = r.cap(1);
+ QString ys = r.cap(2);
+ KLocale* l = KGlobal::locale();
+ double x = l->readNumber( xs, &ok );
+ if ( ! ok ) x = xs.toDouble( &ok );
+ if ( ! ok ) return Coordinate();
+ double y = l->readNumber( ys, &ok );
+ if ( ! ok ) y = ys.toDouble( &ok );
+ if ( ! ok ) return Coordinate();
+ return Coordinate( x, y );
+ }
+ return Coordinate();
+}
+
+/**
+ * copied and adapted from a ( public domain ) function i found in the
+ * first Graphics Gems book. Credits to Paul S. Heckbert, who wrote
+ * the "Nice number for graph labels" gem.
+ * find a "nice" number approximately equal to x. We look for
+ * 1, 2 or 5, multiplied by a power of 10.
+ */
+static double nicenum( double x, bool round )
+{
+ int exp = (int) log10( x );
+ double f = x/pow( 10., exp );
+ double nf;
+ if ( round )
+ {
+ if ( f < 1.5 ) nf = 1.;
+ else if ( f < 3. ) nf = 2.;
+ else if ( f < 7. ) nf = 5.;
+ else nf = 10.;
+ }
+ else
+ {
+ if ( f <= 1. ) nf = 1.;
+ else if ( f <= 2. ) nf = 2.;
+ else if ( f <= 5. ) nf = 5.;
+ else nf = 10.;
+ };
+ return nf * pow( 10., exp );
+}
+
+void EuclideanCoords::drawGrid( KigPainter& p, bool showgrid, bool showaxes ) const
+{
+ p.setWholeWinOverlay();
+
+ // this instruction in not necessary, but there is a little
+ // optimization when there are no grid and no axes.
+ if ( !( showgrid || showaxes ) )
+ return;
+
+ // this function is inspired upon ( public domain ) code from the
+ // first Graphics Gems book. Credits to Paul S. Heckbert, who wrote
+ // the "Nice number for graph labels" gem.
+
+ const double hmax = ceil( p.window().right() );
+ const double hmin = floor( p.window().left() );
+ const double vmax = ceil( p.window().top() );
+ const double vmin = floor( p.window().bottom() );
+
+ // the number of intervals we would like to have:
+ // we try to have one of them per 40 pixels or so..
+ const int ntick = static_cast<int>(
+ kigMax( hmax - hmin, vmax - vmin ) / p.pixelWidth() / 40. ) + 1;
+
+ double hrange = nicenum( hmax - hmin, false );
+ double vrange = nicenum( vmax - vmin, false );
+ const double newrange = kigMin( hrange, vrange );
+ hrange = newrange;
+ vrange = newrange;
+
+ const double hd = nicenum( hrange / ( ntick - 1 ), true );
+ const double vd = nicenum( vrange / ( ntick - 1 ), true );
+
+ const double hgraphmin = ceil( hmin / hd) * hd;
+ const double hgraphmax = floor( hmax / hd ) * hd;
+ const double vgraphmin = ceil( vmin / vd ) * vd;
+ const double vgraphmax = floor( vmax / vd ) * vd;
+
+ const int hnfrac = kigMax( (int) - floor( log10( hd ) ), 0 );
+ const int vnfrac = kigMax( (int) - floor( log10( vd ) ), 0 );
+
+ /****** the grid lines ******/
+ if ( showgrid )
+ {
+ p.setPen( QPen( lightGray, 0, DotLine ) );
+ // vertical lines...
+ for ( double i = hgraphmin; i <= hgraphmax + hd/2; i += hd )
+ p.drawSegment( Coordinate( i, vgraphmin ),
+ Coordinate( i, vgraphmax ) );
+ // horizontal lines...
+ for ( double i = vgraphmin; i <= vgraphmax + vd/2; i += vd )
+ p.drawSegment( Coordinate( hgraphmin, i ),
+ Coordinate( hgraphmax, i ) );
+ }
+
+ /****** the axes ******/
+ if ( showaxes )
+ {
+ p.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) );
+ // x axis
+ p.drawSegment( Coordinate( hmin, 0 ), Coordinate( hmax, 0 ) );
+ // y axis
+ p.drawSegment( Coordinate( 0, vmin ), Coordinate( 0, vmax ) );
+
+ /****** the numbers ******/
+
+ // x axis
+ for( double i = hgraphmin; i <= hgraphmax + hd / 2; i += hd )
+ {
+ // we skip 0 since that would look stupid... (the axes going
+ // through the 0 etc. )
+ if( fabs( i ) < 1e-8 ) continue;
+
+ p.drawText(
+ Rect( Coordinate( i, 0 ), hd, -2*vd ).normalized(),
+ KGlobal::locale()->formatNumber( i, hnfrac ),
+ AlignLeft | AlignTop
+ );
+ };
+ // y axis...
+ for ( double i = vgraphmin; i <= vgraphmax + vd/2; i += vd )
+ {
+ if( fabs( i ) < 1e-8 ) continue;
+ p.drawText ( Rect( Coordinate( 0, i ), 2*hd, vd ).normalized(),
+ KGlobal::locale()->formatNumber( i, vnfrac ),
+ AlignBottom | AlignLeft
+ );
+ };
+ // arrows on the ends of the axes...
+ p.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) );
+ p.setBrush( QBrush( Qt::gray ) );
+ std::vector<Coordinate> a;
+
+ // the arrow on the right end of the X axis...
+ a.reserve( 3 );
+ double u = p.pixelWidth();
+ a.push_back( Coordinate( hmax - 6 * u, -3 * u) );
+ a.push_back( Coordinate( hmax, 0 ) );
+ a.push_back( Coordinate( hmax - 6 * u, 3 * u ) );
+ p.drawArea( a );
+// p.drawPolygon( a, true );
+
+ // the arrow on the top end of the Y axis...
+ a.clear();
+ a.reserve( 3 );
+ a.push_back( Coordinate( 3 * u, vmax - 6 * u ) );
+ a.push_back( Coordinate( 0, vmax ) );
+ a.push_back( Coordinate( -3 * u, vmax - 6 * u ) );
+ p.drawArea( a );
+// p.drawPolygon( a, true );
+ }; // if( showaxes )
+}
+
+QString EuclideanCoords::coordinateFormatNotice() const
+{
+ return i18n( "Enter coordinates in the following format: \"x;y\",\n"
+ "where x is the x coordinate, and y is the y coordinate." );
+}
+
+QString EuclideanCoords::coordinateFormatNoticeMarkup() const
+{
+ return i18n( "Enter coordinates in the following format: <b>\"x;y\"</b>, "
+ "where x is the x coordinate, and y is the y coordinate." );
+}
+
+EuclideanCoords::~EuclideanCoords()
+{
+}
+
+CoordinateSystem::~CoordinateSystem()
+{
+}
+
+CoordinateSystem::CoordinateSystem()
+{
+}
+
+PolarCoords::PolarCoords()
+{
+}
+
+PolarCoords::~PolarCoords()
+{
+}
+
+QString PolarCoords::fromScreen( const Coordinate& pt, const KigDocument& d ) const
+{
+ Rect sr = d.suggestedRect();
+ double m = kigMax( sr.width(), sr.height() );
+ int l = kigMax( 0, (int) ( 3 - log10( m ) ) );
+
+ double r = pt.length();
+ double theta = Goniometry::convert( atan2( pt.y, pt.x ), Goniometry::Rad, Goniometry::Deg );
+
+ QString rs = KGlobal::locale()->formatNumber( r, l );
+ QString ts = KGlobal::locale()->formatNumber( theta, 0 );
+
+ return QString::fromLatin1("( %1; %2° )").arg( rs ).arg( ts );
+}
+
+QString PolarCoords::coordinateFormatNotice() const
+{
+ // \xCE\xB8 is utf8 for the greek theta sign..
+ return i18n( "Enter coordinates in the following format: \"r; \xCE\xB8°\",\n"
+ "where r and \xCE\xB8 are the polar coordinates." );
+}
+
+QString PolarCoords::coordinateFormatNoticeMarkup() const
+{
+ // \xCE\xB8 is utf8 for the greek theta sign..
+ return i18n( "Enter coordinates in the following format: <b>\"r; \xCE\xB8°\"</b>, "
+ "where r and \xCE\xB8 are the polar coordinates." );
+}
+
+Coordinate PolarCoords::toScreen(const QString& s, bool& ok) const
+{
+ QRegExp regexp("\\(? ?([0-9.,+-]+); ?([0-9.,+-]+) ?°? ?\\)?" );
+ ok = ( regexp.search( s ) == 0 );
+ if (ok)
+ {
+ QString rs = regexp.cap( 1 );
+ double r = KGlobal::locale()->readNumber( rs, &ok );
+ if ( ! ok ) r = rs.toDouble( &ok );
+ if ( ! ok ) return Coordinate();
+ QString ts = regexp.cap( 2 );
+ double theta = KGlobal::locale()->readNumber( ts, &ok );
+ if ( ! ok ) theta = ts.toDouble( &ok );
+ if ( ! ok ) return Coordinate();
+ theta *= M_PI;
+ theta /= 180;
+ return Coordinate( cos( theta ) * r, sin( theta ) * r );
+ }
+ else return Coordinate();
+}
+
+void PolarCoords::drawGrid( KigPainter& p, bool showgrid, bool showaxes ) const
+{
+ p.setWholeWinOverlay();
+
+ // this instruction in not necessary, but there is a little
+ // optimization when there are no grid and no axes.
+ if ( !( showgrid || showaxes ) )
+ return;
+
+ // we multiply by sqrt( 2 ) cause we don't want to miss circles in
+ // the corners, that intersect with the axes outside of the
+ // screen..
+
+ const double hmax = M_SQRT2*p.window().right();
+ const double hmin = M_SQRT2*p.window().left();
+ const double vmax = M_SQRT2*p.window().top();
+ const double vmin = M_SQRT2*p.window().bottom();
+
+ // the intervals:
+ // we try to have one of them per 40 pixels or so..
+ const int ntick = static_cast<int>(
+ kigMax( hmax - hmin, vmax - vmin ) / p.pixelWidth() / 40 ) + 1;
+
+ const double hrange = nicenum( hmax - hmin, false );
+ const double vrange = nicenum( vmax - vmin, false );
+
+ const double hd = nicenum( hrange / ( ntick - 1 ), true );
+ const double vd = nicenum( vrange / ( ntick - 1 ), true );
+
+ const double hgraphmin = floor( hmin / hd) * hd;
+ const double hgraphmax = ceil( hmax / hd ) * hd;
+ const double vgraphmin = floor( vmin / vd ) * vd;
+ const double vgraphmax = ceil( vmax / vd ) * vd;
+
+ const int hnfrac = kigMax( (int) - floor( log10( hd ) ), 0 );
+ const int vnfrac = kigMax( (int) - floor( log10( vd ) ), 0 );
+ const int nfrac = kigMax( hnfrac, vnfrac );
+
+ /****** the grid lines ******/
+ if ( showgrid )
+ {
+ double d = kigMin( hd, vd );
+ double begin = kigMin( kigAbs( hgraphmin ), kigAbs( vgraphmin ) );
+ if ( kigSgn( hgraphmin ) != kigSgn( hgraphmax ) && kigSgn( vgraphmin ) != kigSgn( vgraphmax ) )
+ begin = d;
+ double end = kigMax( hgraphmax, vgraphmax );
+
+ // we also want the circles that don't fit entirely in the
+ // screen..
+ Coordinate c( 0, 0 );
+ p.setPen( QPen( lightGray, 0, DotLine ) );
+ for ( double i = begin; i <= end + d / 2; i += d )
+ drawGridLine( p, c, fabs( i ) );
+ }
+
+ /****** the axes ******/
+ if ( showaxes )
+ {
+ p.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) );
+ // x axis
+ p.drawSegment( Coordinate( hmin, 0 ), Coordinate( hmax, 0 ) );
+ // y axis
+ p.drawSegment( Coordinate( 0, vmin ), Coordinate( 0, vmax ) );
+
+ /****** the numbers ******/
+
+ // x axis
+ for( double i = hgraphmin; i <= hgraphmax + hd / 2; i += hd )
+ {
+ // we skip 0 since that would look stupid... (the axes going
+ // through the 0 etc. )
+ if( fabs( i ) < 1e-8 ) continue;
+
+ QString is = KGlobal::locale()->formatNumber( fabs( i ), nfrac );
+ p.drawText(
+ Rect( Coordinate( i, 0 ), hd, -2*vd ).normalized(),
+ is, AlignLeft | AlignTop );
+ };
+ // y axis...
+ for ( double i = vgraphmin; i <= vgraphmax + vd / 2; i += vd )
+ {
+ if( fabs( i ) < 1e-8 ) continue;
+
+ QString is = KGlobal::locale()->formatNumber( fabs( i ), nfrac );
+
+ p.drawText ( Rect( Coordinate( 0, i ), hd, vd ).normalized(),
+ is, AlignBottom | AlignLeft
+ );
+ };
+ // arrows on the ends of the axes...
+ p.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) );
+ p.setBrush( QBrush( Qt::gray ) );
+ std::vector<Coordinate> a;
+
+ // the arrow on the right end of the X axis...
+ a.reserve( 3 );
+ double u = p.pixelWidth();
+ a.push_back( Coordinate( hmax - 6 * u, -3 * u) );
+ a.push_back( Coordinate( hmax, 0 ) );
+ a.push_back( Coordinate( hmax - 6 * u, 3 * u ) );
+// p.drawPolygon( a, true );
+ p.drawArea( a );
+
+ // the arrow on the top end of the Y axis...
+ a.clear();
+ a.reserve( 3 );
+ a.push_back( Coordinate( 3 * u, vmax - 6 * u ) );
+ a.push_back( Coordinate( 0, vmax ) );
+ a.push_back( Coordinate( -3 * u, vmax - 6 * u ) );
+// p.drawPolygon( a, true );
+ p.drawArea( a );
+ }; // if( showaxes )
+}
+
+QValidator* EuclideanCoords::coordinateValidator() const
+{
+ return new CoordinateValidator( false );
+}
+
+QValidator* PolarCoords::coordinateValidator() const
+{
+ return new CoordinateValidator( true );
+}
+
+QStringList CoordinateSystemFactory::names()
+{
+ QStringList ret;
+ ret << i18n( "&Euclidean" )
+ << i18n( "&Polar" );
+ return ret;
+}
+
+CoordinateSystem* CoordinateSystemFactory::build( int which )
+{
+ if ( which == Euclidean )
+ return new EuclideanCoords;
+ else if ( which == Polar )
+ return new PolarCoords;
+ else return 0;
+}
+
+static const char euclideanTypeString[] = "Euclidean";
+static const char polarTypeString[] = "Polar";
+
+CoordinateSystem* CoordinateSystemFactory::build( const char* type )
+{
+ if ( std::string( euclideanTypeString ) == type )
+ return new EuclideanCoords;
+ if ( std::string( polarTypeString ) == type )
+ return new PolarCoords;
+ else return 0;
+}
+
+const char* EuclideanCoords::type() const
+{
+ return euclideanTypeString;
+}
+
+const char* PolarCoords::type() const
+{
+ return polarTypeString;
+}
+
+int EuclideanCoords::id() const
+{
+ return CoordinateSystemFactory::Euclidean;
+}
+
+int PolarCoords::id() const
+{
+ return CoordinateSystemFactory::Polar;
+}
+
+QString CoordinateSystemFactory::setCoordinateSystemStatement( int id )
+{
+ switch( id )
+ {
+ case Euclidean:
+ return i18n( "Set Euclidean Coordinate System" );
+ case Polar:
+ return i18n( "Set Polar Coordinate System" );
+ default:
+ assert( false );
+ return QString::null;
+ }
+}
+
+Coordinate EuclideanCoords::snapToGrid( const Coordinate& c,
+ const KigWidget& w ) const
+{
+ Rect rect = w.showingRect();
+ // we recalc the interval stuff since there is no way to cache it..
+
+ // this function is again inspired upon ( public domain ) code from
+ // the first Graphics Gems book. Credits to Paul S. Heckbert, who
+ // wrote the "Nice number for graph labels" gem.
+
+ const double hmax = rect.right();
+ const double hmin = rect.left();
+ const double vmax = rect.top();
+ const double vmin = rect.bottom();
+
+ // the number of intervals we would like to have:
+ // we try to have one of them per 40 pixels or so..
+ const int ntick = static_cast<int>(
+ kigMax( hmax - hmin, vmax - vmin ) / w.pixelWidth() / 40. ) + 1;
+
+ const double hrange = nicenum( hmax - hmin, false );
+ const double vrange = nicenum( vmax - vmin, false );
+
+ const double hd = nicenum( hrange / ( ntick - 1 ), true );
+ const double vd = nicenum( vrange / ( ntick - 1 ), true );
+
+ const double hgraphmin = ceil( hmin / hd) * hd;
+ const double vgraphmin = ceil( vmin / vd ) * vd;
+
+ const double nx = qRound( ( c.x - hgraphmin ) / hd ) * hd + hgraphmin;
+ const double ny = qRound( ( c.y - vgraphmin ) / vd ) * vd + vgraphmin;
+ return Coordinate( nx, ny );
+}
+
+Coordinate PolarCoords::snapToGrid( const Coordinate& c,
+ const KigWidget& w ) const
+{
+ // we reuse the drawGrid code to find
+
+ // we multiply by sqrt( 2 ) cause we don't want to miss circles in
+ // the corners, that intersect with the axes outside of the
+ // screen..
+
+ Rect r = w.showingRect();
+
+ const double hmax = M_SQRT2 * r.right();
+ const double hmin = M_SQRT2 * r.left();
+ const double vmax = M_SQRT2 * r.top();
+ const double vmin = M_SQRT2 * r.bottom();
+
+ // the intervals:
+ // we try to have one of them per 40 pixels or so..
+ const int ntick = static_cast<int>(
+ kigMax( hmax - hmin, vmax - vmin ) / w.pixelWidth() / 40 ) + 1;
+
+ const double hrange = nicenum( hmax - hmin, false );
+ const double vrange = nicenum( vmax - vmin, false );
+
+ const double hd = nicenum( hrange / ( ntick - 1 ), true );
+ const double vd = nicenum( vrange / ( ntick - 1 ), true );
+
+ double d = kigMin( hd, vd );
+
+ double dist = c.length();
+ double ndist = qRound( dist / d ) * d;
+ return c.normalize( ndist );
+}
+
+void PolarCoords::drawGridLine( KigPainter& p, const Coordinate& c,
+ double r ) const
+{
+ Rect rect = p.window();
+
+ struct iterdata_t
+ {
+ int xd;
+ int yd;
+ Coordinate ( Rect::*point )() const;
+ Coordinate ( Rect::*oppositepoint )() const;
+ double horizAngle;
+ double vertAngle;
+ };
+
+ static const iterdata_t iterdata[] =
+ {
+ { +1, +1, &Rect::topRight, &Rect::bottomLeft, 0, M_PI/2 },
+ { -1, +1, &Rect::topLeft, &Rect::bottomRight, M_PI, M_PI / 2 },
+ { -1, -1, &Rect::bottomLeft, &Rect::topRight, M_PI, 3*M_PI/2 },
+ { +1, -1, &Rect::bottomRight, &Rect::topLeft, 2*M_PI, 3*M_PI/2 }
+ };
+ for ( int i = 0; i < 4; ++i )
+ {
+ int xd = iterdata[i].xd;
+ int yd = iterdata[i].yd;
+ Coordinate point = ( rect.*iterdata[i].point )();
+ Coordinate opppoint = ( rect.*iterdata[i].oppositepoint )();
+ double horizangle = iterdata[i].horizAngle;
+ double vertangle = iterdata[i].vertAngle;
+
+ if ( ( c.x - point.x )*xd > 0 || ( c.y - point.y )*yd > 0 )
+ continue;
+ if ( ( c.x - opppoint.x )*-xd > r || ( c.y - opppoint.y )*-yd > r )
+ continue;
+
+ int posdir = xd*yd;
+ double hd = ( point.x - c.x )*xd;
+ assert( hd >= 0 );
+ if ( hd < r )
+ {
+ double anglediff = acos( hd/r );
+ horizangle += posdir * anglediff;
+ }
+
+ hd = ( c.x - opppoint.x )*-xd;
+ if ( hd >= 0 )
+ {
+ double anglediff = asin( hd/r );
+ vertangle -= posdir * anglediff;
+ }
+
+ double vd = ( point.y - c.y )*yd;
+ assert( vd >= 0 );
+ if ( vd < r )
+ {
+ double anglediff = acos( vd/r );
+ vertangle -= posdir * anglediff;
+ }
+
+ vd = ( c.y - opppoint.y ) * -xd;
+ if ( vd >= 0 )
+ {
+ double anglediff = asin( hd/r );
+ horizangle += posdir * anglediff;
+ }
+
+ p.drawArc( c, r, kigMin( horizangle, vertangle ), kigMax( horizangle, vertangle ) );
+ }
+// p.drawCircle( c, r );
+}