Commit 76db3667 authored by David Saxton's avatar David Saxton

More work on plotting implicit functions: now mostly works well.

svn path=/trunk/KDE/kdeedu/kmplot/; revision=553698
parent b4280a16
......@@ -133,6 +133,7 @@ MainDlg::MainDlg(QWidget *parentWidget, QObject *parent, const QStringList& ) :
(void) new View( m_readonly, m_modified, m_popupmenu, parentWidget, actionCollection() );
connect( View::self(), SIGNAL( setStatusBarText(const QString &)), this, SLOT( setReadOnlyStatusBarText(const QString &) ) );
m_functionEditor = 0;
if ( !m_readonly )
{
m_functionEditor = new FunctionEditor( m_newPlotMenu, parentWidget );
......
......@@ -384,115 +384,164 @@ void View::plotImplicit( Function * function, QPainter * painter )
{
assert( function->type() == Function::Implicit );
// The viewable area is divided up into square*squares squares, and the curve
// is traced around in each square.
// NOTE this should agree with the value in plotImplicitInSquare
int squares = 20;
#if 0
painter->setPen( Qt::black );
for ( double i = 0; i <= squares; ++i )
{
double x = m_xmin + i * (m_xmax-m_xmin)/squares;
double y = m_ymin + i * (m_ymax-m_ymin)/squares;
painter->drawLine( CDiagr::self()->toPixel( QPointF( m_xmin, y ), CDiagr::ClipInfinite ), CDiagr::self()->toPixel( QPointF( m_xmax, y ), CDiagr::ClipInfinite ) );
painter->drawLine( CDiagr::self()->toPixel( QPointF( x, m_ymin ), CDiagr::ClipInfinite ), CDiagr::self()->toPixel( QPointF( x, m_ymax ), CDiagr::ClipInfinite ) );
}
#endif
const QList< Plot > plots = function->allPlots();
foreach ( Plot plot, plots )
{
plot.updateFunctionParameter();
Plot differentiated = plot;
differentiated.differentiate();
painter->setPen( penForPlot( plot, painter->renderHints() & QPainter::Antialiasing ) );
#if 0
for ( double x = m_xmin; x <= m_xmax; x += (m_xmax-m_xmin)/40 )
{
for ( double y = m_ymin; y <= m_ymax; y += (m_ymax-m_ymin)/40 )
{
double var[3] = { x, y, function->k };
double value = XParser::self()->fkt( function->eq[0], var );
// QString text('0');
QString text;
// if ( value > 0 )
// text = '.';
// else if ( value < 0 )
// text = '-';
// painter->drawText( CDiagr::self()->toPixel( QPointF( x, y ), CDiagr::ClipInfinite ), text );
}
}
#endif
for ( double _y = m_ymin; _y <= m_ymax; _y += (m_ymax-m_ymin)/20 )
// double _y = 2;
for ( double y = m_ymin; y <= m_ymax; y += (m_ymax-m_ymin)/squares )
{
function->y = _y;
function->y = y;
function->m_implicitMode = Function::FixedY;
QList<double> roots = findRoots( plot );
foreach ( double x, roots )
{
double y = _y;
QPointF prev = CDiagr::self()->toPixel( QPointF( x, y ), CDiagr::ClipInfinite );
plotImplicitInSquare( plot, painter, x, y );
}
for ( double x = m_xmin; x <= m_xmax; x += (m_xmax-m_xmin)/squares )
{
function->x = x;
function->m_implicitMode = Function::FixedX;
QList<double> roots = findRoots( plot );
foreach ( double y, roots )
plotImplicitInSquare( plot, painter, x, y );
}
}
}
void View::plotImplicitInSquare( const Plot & plot, QPainter * painter, double x, double y )
{
// NOTE this should agree with the value in plotImplicit
int squares = 20;
plot.updateFunctionParameter();
Plot differentiated = plot;
differentiated.differentiate();
#if 0
painter->save();
painter->setPen( Qt::green );
QPointF tl = CDiagr::self()->toPixel( QPointF( x, y ), CDiagr::ClipInfinite ) - QPoint( 1, 1 );
painter->drawRect( QRectF( tl, QSizeF( 3, 3 ) ) );
painter->restore();
#endif
// Use a square around the root to bound the tracing
// To start with, assume that tracing will go down,right. But this
// might not be so, so firstTrace is set to true, and then the upper/lower
// boundaries may be adjusted depending on where the tracing ends up
double x_prop = (x-m_xmin)/(m_xmax-m_xmin);
double x_lower = (qRound( x_prop * squares ) / double(squares))*(m_xmax-m_xmin) + m_xmin;
double x_upper = x_lower + (m_xmax-m_xmin)/squares;
double y_prop = (y-m_ymin)/(m_ymax-m_ymin);
double y_lower = (qRound( y_prop * squares ) / double(squares))*(m_ymax-m_ymin) + m_ymin;
double y_upper = y_lower + (m_ymax-m_ymin)/squares;
bool firstTrace = true;
double segment_length = 4; // the number of pixels long each segment of the trace should be
double segment_x = (m_xmax-m_xmin) * segment_length / area.width();
double segment_y = (m_ymax-m_ymin) * segment_length / area.height();
QPointF prev = CDiagr::self()->toPixel( QPointF( x, y ), CDiagr::ClipInfinite );
// Now trace around the curve from the point...
for ( int i = 0; i < 40; ++i )
// while ( true )
{
// (dx, dy) is perpendicular to curve
function->y = y;
function->m_implicitMode = Function::FixedY;
double dx = value( differentiated, 0, x, false );
function->x = x;
function->m_implicitMode = Function::FixedX;
double dy = value( differentiated, 0, y, false );
QPointF p1 = CDiagr::self()->toPixel( QPointF( x, y ), CDiagr::ClipInfinite ) * painter->matrix();
QPointF p2 = CDiagr::self()->toPixel( QPointF( x+dx, y+dy ), CDiagr::ClipInfinite ) * painter->matrix();
double l = QLineF( p1, p2 ).length() / 4; // (1 is the number of pixels long the tangent line should be)
if ( l == 0 )
break;
// Now trace around the curve from the point...
for ( int i = 0; i < 40; ++i ) // allow a maximum of 40 traces
{
// (dx, dy) is perpendicular to curve
// tangent to the curve
double tx = -dy/l;
double ty = dx/l;
// kDebug() << "x="<<x<<" y="<<y<<" tx="<<tx<<" ty="<<ty<<endl;
plot.function()->y = y;
plot.function()->m_implicitMode = Function::FixedY;
double dx = value( differentiated, 0, x, false );
double * coord = 0;
plot.function()->x = x;
plot.function()->m_implicitMode = Function::FixedX;
double dy = value( differentiated, 0, y, false );
x += tx;
y += ty;
QPointF p1 = CDiagr::self()->toPixel( QPointF( x, y ), CDiagr::ClipInfinite ) * painter->matrix();
QPointF p2 = CDiagr::self()->toPixel( QPointF( x+dx, y+dy ), CDiagr::ClipInfinite ) * painter->matrix();
double l = QLineF( p1, p2 ).length() / segment_length;
function->x = x;
function->y = y;
if ( l == 0 )
break;
if ( qAbs(tx) > qAbs(ty) )
{
function->m_implicitMode = Function::FixedX;
coord = & y;
}
else
{
function->m_implicitMode = Function::FixedY;
coord = & x;
}
// tangent to the curve
double tx = -dy/l;
double ty = dx/l;
bool found = findRoot( coord, plot );
if ( !found )
break;
double * coord = 0;
if ( x < m_xmin || x > m_xmax || y < m_ymin || y > m_ymax )
break;
x += tx;
y += ty;
QPointF next = CDiagr::self()->toPixel( QPointF( x, y ), CDiagr::ClipInfinite );
painter->drawLine( prev, next );
prev = next;
#if 0
plot.function()->x = x;
plot.function()->y = y;
QPointF start( x, y );
QPointF end( x+tx, y+ty );
if ( qAbs(tx) > qAbs(ty) )
{
plot.function()->m_implicitMode = Function::FixedX;
coord = & y;
}
else
{
plot.function()->m_implicitMode = Function::FixedY;
coord = & x;
}
painter->drawLine( CDiagr::self()->toPixel( start, CDiagr::ClipInfinite ), CDiagr::self()->toPixel( end, CDiagr::ClipInfinite ) );
QRectF rect( CDiagr::self()->toPixel( QPointF( x, y ), CDiagr::ClipInfinite ) - QPointF( 2, 2 ), QSizeF( 4, 4 ) );
bool found = findRoot( coord, plot );
if ( !found )
break;
painter->setBrush( painter->pen().color() );
painter->drawEllipse( rect );
#endif
}
if ( firstTrace )
{
if ( x < x_lower )
{
x_lower -= (m_xmax-m_xmin)/squares;
x_upper -= (m_xmax-m_xmin)/squares;
}
if ( y < y_lower )
{
y_lower -= (m_ymax-m_ymin)/squares;
y_upper -= (m_ymax-m_ymin)/squares;
}
// adjust the boundaries to allow for overlap
x_lower -= segment_x;
x_upper += segment_x;
y_lower -= segment_y;
y_upper += segment_y;
firstTrace = false;
}
if ( x < x_lower || x > x_upper || y < y_lower || y > y_upper )
break;
QPointF next = CDiagr::self()->toPixel( QPointF( x, y ), CDiagr::ClipInfinite );
painter->drawLine( prev, next );
prev = next;
}
}
......@@ -887,7 +936,7 @@ QList< QPointF > View::findStationaryPoints( const Plot & plot )
}
QList< double > View::findRoots( const Plot & plot )
QList< double > View::findRoots( const Plot & plot/*, RootAccuracy accuracy*/ )
{
Equation * eq = plot.function()->eq[0];
......@@ -905,7 +954,9 @@ QList< double > View::findRoots( const Plot & plot )
for ( double x = min; x < max; x += dx )
{
double x0 = x;
bool found = findRoot( & x0, plot );
bool found = findRoot( & x0, plot/*, accuracy*/ );
if ( x0 < m_xmin || x0 > m_xmax )
found = false;
bool differentRoot = (qAbs(x0-prevX) > (dx/2)) || roots.isEmpty();
......@@ -920,11 +971,25 @@ QList< double > View::findRoots( const Plot & plot )
}
bool View::findRoot( double *x0, const Plot & plot )
bool View::findRoot( double *x0, const Plot & plot/*, RootAccuracy accuracy*/ )
{
int k = 0; // iteration count
int max_k = 200; // maximum number of iterations
double max_y = 1e-14; // the largest value of y which is deemed a root found
int max_k; // maximum number of iterations
double max_y; // the largest value of y which is deemed a root found
// if ( accuracy == PreciseRoot )
if ( true )
{
max_k = 200;
max_y = 1e-14;
}
else
{
// Rough root
max_k = 10;
max_y = 1e-10;
}
double y = value( plot, 0, *x0, false );
bool tooBig = true;
......
......@@ -223,6 +223,11 @@ class View : public QWidget
* Draw an implicit function.
*/
void plotImplicit( Function * function, QPainter * );
/**
* Used by plotImplicit to draw the plot in the square associated with
* the given point.
*/
void plotImplicitInSquare( const Plot & plot, QPainter *, double x, double y );
/**
* \return whether should draw the pixel from the given line length,
* according to the given pen style (used in plotfkt).
......@@ -236,12 +241,22 @@ class View : public QWidget
QPen penForPlot( const Plot & plot, bool antialias ) const;
/// Gets the greek pi symbol.
void setpi(QString *);
#if 0
/**
* Used in findRoot.
*/
enum RootAccuracy
{
PreciseRoot, ///< Will potential spend a long time finding a root to a high degree of accuracy
RoughRoot ///< Won't spend too long making a root accurate, giving up quickly if failed to find root
};
#endif
/**
* Used in trace mode. Attempts to find the root of equation \p eq near
* \p x (which is then set to the exact root if found).
* \returns whether a root was found.
*/
bool findRoot( double * x, const Plot & plot );
bool findRoot( double * x, const Plot & plot/*, RootAccuracy accuracy*/ );
/**
* Finds the list of points (in function coordinates) at which the
* derivative of the given plot is zero in the range of the currently
......@@ -252,7 +267,7 @@ class View : public QWidget
* Find all roots (at which the given plot is zero) in the range of the
* currently viewable segment of the plot.
*/
QList<double> findRoots( const Plot & plot );
QList<double> findRoots( const Plot & plot/*, RootAccuracy accuracy*/ );
///return the inverted color
void invertColor(QColor &, QColor &);
/// Changes the text in the statusbar
......
......@@ -43,8 +43,10 @@
#include <dbus/qdbus.h>
KmPlot::KmPlot( KCmdLineArgs* args)
: KParts::MainWindow( 0L, "KmPlot" )
: KParts::MainWindow()
{
setObjectName( "KmPlot" );
// set the shell's ui resource file
setXMLFile("kmplot_shell.rc");
// then, setup our actions
......
......@@ -1385,6 +1385,7 @@ void ExpressionSanitizer::fixExpression( QString * str, int pos )
append( ')' );
}
/// \todo should strip white space instead
remove( ' ' );
m_map.insert( 0, 0 );
......@@ -1499,7 +1500,10 @@ void ExpressionSanitizer::fixExpression( QString * str, int pos )
i++;
}
}
/// \todo should strip white space instead
remove(" " );
QString str_end = str->mid(pos);
str_end = str_end.replace(m_decimalSymbol, "."); //replace the locale decimal symbol with a '.'
str->truncate(pos);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment