Commit b4280a16 authored by David Saxton's avatar David Saxton

Initial code for implicit functions.

svn path=/trunk/KDE/kdeedu/kmplot/; revision=552651
parent 1646c63f
......@@ -6,53 +6,56 @@
<version>$VERSION$</version>
<projectmanagement>KDevCustomProject</projectmanagement>
<primarylanguage>C</primarylanguage>
<ignoreparts>
<part>kdevrbdebugger</part>
</ignoreparts>
<ignoreparts/>
<projectdirectory>.</projectdirectory>
<absoluteprojectpath>false</absoluteprojectpath>
<description/>
<description></description>
<secondaryLanguages>
<language>C++</language>
</secondaryLanguages>
<versioncontrol/>
<versioncontrol></versioncontrol>
</general>
<kdevcustomproject>
<run>
<mainprogram>kmplot/kmplot</mainprogram>
<directoryradio>executable</directoryradio>
<directoryradio>build</directoryradio>
<customdirectory>/</customdirectory>
<programargs/>
<programargs></programargs>
<terminal>false</terminal>
<autocompile>true</autocompile>
<envvars/>
</run>
<build>
<buildtool>make</buildtool>
<builddir/>
<builddir>../../kdeedu-build/kmplot/</builddir>
</build>
<make>
<abortonerror>false</abortonerror>
<numberofjobs>1</numberofjobs>
<dontact>false</dontact>
<makebin/>
<makebin></makebin>
<selectedenvironment>default</selectedenvironment>
<environments>
<default/>
<default>
<envvar value="-O0 -g3" name="CXXFLAGS" />
</default>
</environments>
<makeoptions/>
<makeoptions></makeoptions>
<prio>0</prio>
<defaulttarget/>
<defaulttarget></defaulttarget>
</make>
<general>
<activedir/>
</general>
</kdevcustomproject>
<kdevdebugger>
<general>
<dbgshell/>
<programargs/>
<gdbpath/>
<configGdbScript/>
<runShellScript/>
<runGdbScript/>
<dbgshell></dbgshell>
<programargs></programargs>
<gdbpath></gdbpath>
<configGdbScript></configGdbScript>
<runShellScript></runShellScript>
<runGdbScript></runGdbScript>
<breakonloadinglibs>true</breakonloadinglibs>
<separatetty>false</separatetty>
<floatingtoolbar>false</floatingtoolbar>
......@@ -139,12 +142,12 @@
<headerCompletionDelay>250</headerCompletionDelay>
</codecompletion>
<qt>
<used>false</used>
<version>3</version>
<root/>
<used>true</used>
<version>4</version>
<root>/home/kde4/qt-copy</root>
</qt>
<creategettersetter>
<prefixGet/>
<prefixGet></prefixGet>
<prefixSet>set</prefixSet>
<prefixVariable>m_,_</prefixVariable>
<parameterName>theValue</parameterName>
......@@ -153,10 +156,7 @@
</creategettersetter>
</kdevcppsupport>
<kdevfileview>
<groups>
<hidenonprojectfiles>false</hidenonprojectfiles>
<hidenonlocation>false</hidenonlocation>
</groups>
<groups/>
<tree>
<hidepatterns>*.o,*.lo,CVS,*.moc,*~</hidepatterns>
<hidenonprojectfiles>false</hidenonprojectfiles>
......@@ -179,9 +179,9 @@
</kdevvisualadvance>
<kdevdocumentation>
<projectdoc>
<docsystem/>
<docurl/>
<usermanualurl/>
<docsystem></docsystem>
<docurl></docurl>
<usermanualurl></usermanualurl>
</projectdoc>
</kdevdocumentation>
<ctagspart>
......
......@@ -299,6 +299,11 @@ void MainDlg::setupActions()
newPolar->setIcon( KIcon("newpolar") );
connect( newPolar, SIGNAL(triggered(bool)), m_functionEditor, SLOT( createPolar() ) );
m_newPlotMenu->addAction( newPolar );
KAction * newImplicit = new KAction( i18n( "Implicit Plot" ), actionCollection(), "newimplicit" );
newImplicit->setIcon( KIcon("newimplicit") );
connect( newImplicit, SIGNAL(triggered(bool)), m_functionEditor, SLOT( createImplicit() ) );
m_newPlotMenu->addAction( newImplicit );
//END new plots menu
......
......@@ -222,9 +222,7 @@ void View::draw( QPaintDevice * dev, PlotMedium medium )
// Antialiasing slows down rendering a lot, so turn it off if we are
// sliding the view about
DC.setRenderHint( QPainter::Antialiasing, m_zoomMode != Translating );
// DC.setRenderHint( QPainter::Antialiasing, false );
// if ( m_zoomMode != Translating )
// kDebug() << "##############################\n";
DC.setClipping( true );
DC.setClipRect( CDiagr::self()->plotArea() );
foreach ( Function * ufkt, XParser::self()->m_ufkt )
......@@ -232,7 +230,10 @@ void View::draw( QPaintDevice * dev, PlotMedium medium )
if ( stop_calculating )
break;
plotFunction(ufkt, &DC);
if ( ufkt->type() == Function::Implicit )
plotImplicit( ufkt, & DC );
else
plotFunction(ufkt, &DC);
}
DC.setClipping( false );
......@@ -299,6 +300,19 @@ QPointF View::realValue( const Plot & plot, double x, bool updateParameter )
double Y = value( plot, 1, x, updateParameter );
return QPointF( X, Y );
}
case Function::Implicit:
{
// Can only calculate the value when either x or y is fixed.
assert( function->m_implicitMode != Function::UnfixedXY );
double val = value( plot, 0, x, updateParameter );;
if ( function->m_implicitMode == Function::FixedX )
return QPointF( function->x, val );
else
return QPointF( val, function->y );
}
}
kWarning() << k_funcinfo << "Unknown function type!\n";
......@@ -322,6 +336,7 @@ double View::getXmin( Function * function )
min = -M_PI;
break;
case Function::Implicit:
case Function::Cartesian:
min = m_xmin;
break;
......@@ -351,6 +366,7 @@ double View::getXmax( Function * function )
max = M_PI;
break;
case Function::Implicit:
case Function::Cartesian:
max = m_xmax;
break;
......@@ -364,8 +380,128 @@ double View::getXmax( Function * function )
}
void View::plotImplicit( Function * function, QPainter * painter )
{
assert( function->type() == Function::Implicit );
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;
{
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 );
// 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;
// tangent to the curve
double tx = -dy/l;
double ty = dx/l;
// kDebug() << "x="<<x<<" y="<<y<<" tx="<<tx<<" ty="<<ty<<endl;
double * coord = 0;
x += tx;
y += ty;
function->x = x;
function->y = y;
if ( qAbs(tx) > qAbs(ty) )
{
function->m_implicitMode = Function::FixedX;
coord = & y;
}
else
{
function->m_implicitMode = Function::FixedY;
coord = & x;
}
bool found = findRoot( coord, plot );
if ( !found )
break;
if ( x < m_xmin || x > m_xmax || y < m_ymin || y > m_ymax )
break;
QPointF next = CDiagr::self()->toPixel( QPointF( x, y ), CDiagr::ClipInfinite );
painter->drawLine( prev, next );
prev = next;
#if 0
QPointF start( x, y );
QPointF end( x+tx, y+ty );
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 ) );
painter->setBrush( painter->pen().color() );
painter->drawEllipse( rect );
#endif
}
}
}
}
}
void View::plotFunction(Function *ufkt, QPainter *pDC)
{
// should use plotImplicit for implicit functions
assert( ufkt->type() != Function::Implicit );
double dmin = getXmin( ufkt );
double dmax = getXmax( ufkt );
......@@ -720,6 +856,7 @@ void View::getMinMax( int koord, QString &mini, QString &maxi )
case 3:
mini="0.0";
maxi="10.0";
break;
}
}
......@@ -737,25 +874,7 @@ void View::setpi(QString *s)
QList< QPointF > View::findStationaryPoints( const Plot & plot )
{
Plot plot2 = plot;
switch ( plot.plotMode )
{
case Function::Integral:
plot2.plotMode = Function::Derivative0;
break;
case Function::Derivative0:
plot2.plotMode = Function::Derivative1;
break;
case Function::Derivative1:
plot2.plotMode = Function::Derivative2;
break;
case Function::Derivative2:
kWarning() << k_funcinfo << "Can't handle this yet!\n";
break;
}
plot2.differentiate();
QList< double > roots = findRoots( plot2 );
......@@ -807,7 +926,7 @@ bool View::findRoot( double *x0, const Plot & plot )
int max_k = 200; // maximum number of iterations
double max_y = 1e-14; // the largest value of y which is deemed a root found
double y = m_crosshairPosition.y();
double y = value( plot, 0, *x0, false );
bool tooBig = true;
// kDebug() << "Initial: ("<<*x0<<","<<y<<")\n";
......@@ -1065,7 +1184,6 @@ QPointF View::getPlotUnderMouse()
Plot bestPlot;
int best_id = -1;
double best_distance = 1e30; // a nice large number
QPointF best_cspos;
......
......@@ -163,22 +163,22 @@ class View : public QWidget
*/
QPointF realValue( const Plot & plot, double x, bool updateParameter );
public slots:
/// Called when the user want to cancel the drawing
void stopDrawing();
/// A slider window has been closed
void slidersWindowClosed();
/// Called when the graph should be updated
void drawPlot();
///Slots for the three first items in popup menu
void mnuHide_clicked();
void mnuRemove_clicked();
void mnuEdit_clicked();
///Slots for the zoom menu
void mnuZoomIn_clicked();
void mnuZoomOut_clicked();
void mnuTrig_clicked();
public slots:
/// Called when the user want to cancel the drawing
void stopDrawing();
/// A slider window has been closed
void slidersWindowClosed();
/// Called when the graph should be updated
void drawPlot();
///Slots for the three first items in popup menu
void mnuHide_clicked();
void mnuRemove_clicked();
void mnuEdit_clicked();
///Slots for the zoom menu
void mnuZoomIn_clicked();
void mnuZoomOut_clicked();
void mnuTrig_clicked();
protected slots:
void paintEvent(QPaintEvent *);
......@@ -216,9 +216,13 @@ public slots:
*/
void drawHeaderTable(QPainter *);
/**
* Draw the function plots.
* Draw the function plots (other than implicit).
*/
void plotFunction(Function *ufkt, QPainter*);
/**
* Draw an implicit function.
*/
void plotImplicit( Function * function, QPainter * );
/**
* \return whether should draw the pixel from the given line length,
* according to the given pen style (used in plotfkt).
......
......@@ -162,7 +162,7 @@ Equation::~ Equation()
}
QString Equation::fname( ) const
QString Equation::name( ) const
{
if ( m_fstr.isEmpty() )
return QString();
......@@ -176,48 +176,22 @@ QString Equation::fname( ) const
}
QString Equation::fvar( ) const
QStringList Equation::parameters( ) const
{
if ( m_fstr.isEmpty() )
return QString();
int p1 = m_fstr.indexOf( '(' );
if ( p1 == -1 )
return QString();
int p2 = m_fstr.indexOf( ',' );
if ( p2 == -1 )
p2 = m_fstr.indexOf( ')' );
if ( p2 == -1 )
return QString();
return m_fstr.mid( p1+1, p2-p1-1 );
}
QString Equation::fpar( ) const
{
if ( m_fstr.isEmpty() )
return QString();
int p1 = m_fstr.indexOf( ',' );
if ( p1 == -1 )
{
// no parameter
return QString();
}
int p2 = m_fstr.indexOf( ')' );
if ( p2 == -1 )
return QString();
if ( (p1 == -1) || (p2 == -1) )
return QStringList();
return m_fstr.mid( p1+1, p2-p1-1 );
QString parameters = m_fstr.mid( p1+1, p2-p1-1 );
return parameters.split( ',' );
}
bool Equation::setFstr( const QString & fstr, bool force )
{
// kDebug() << k_funcinfo << "fstr: "<<fstr<<endl;
if ( force )
{
m_fstr = fstr;
......@@ -228,7 +202,7 @@ bool Equation::setFstr( const QString & fstr, bool force )
if ( !XParser::self()->isFstrValid( fstr ) )
{
XParser::self()->parserError( false );
// kDebug() << k_funcinfo << "invalid fstr\n";
kDebug() << k_funcinfo << "invalid fstr\n";
return false;
}
......@@ -286,7 +260,12 @@ Equation & Equation::operator =( const Equation & other )
Function::Function( Type type )
: m_type( type )
{
eq[1] = 0;
x = y = 0;
m_implicitMode = UnfixedXY;
eq[0] = eq[1] = 0;
usecustomxmin = false;
usecustomxmax = false;
switch ( m_type )
{
......@@ -294,8 +273,6 @@ Function::Function( Type type )
eq[0] = new Equation( Equation::Cartesian, this );
dmin.updateExpression( QString("-")+QChar(960) );
dmax.updateExpression( QChar(960) );
usecustomxmin = false;
usecustomxmax = false;
break;
case Polar:
......@@ -314,6 +291,10 @@ Function::Function( Type type )
usecustomxmin = true;
usecustomxmax = true;
break;
case Implicit:
eq[0] = new Equation( Equation::Implicit, this );
break;
}
id = 0;
......@@ -390,13 +371,13 @@ QString Function::prettyName( Function::PMode mode ) const
return eq[0]->fstr();
case Function::Derivative1:
return eq[0]->fname() + '\'';
return eq[0]->name() + '\'';
case Function::Derivative2:
return eq[0]->fname() + "\'\'";
return eq[0]->name() + "\'\'";
case Function::Integral:
return eq[0]->fname().toUpper();
return eq[0]->name().toUpper();
}
kWarning() << k_funcinfo << "Unknown mode!\n";
......@@ -464,6 +445,9 @@ QString Function::typeToString( Type type )
case Polar:
return "polar";
case Implicit:
return "implicit";
}
kWarning() << "Unknown type " << type << endl;
......@@ -482,6 +466,9 @@ Function::Type Function::stringToType( const QString & type )
if ( type == "polar" )
return Polar;
if ( type == "implicit" )
return Implicit;
kWarning() << "Unknown type " << type << endl;
return Cartesian;
}
......@@ -643,4 +630,50 @@ void Plot::updateFunctionParameter() const
m_function->setParameter( k );
}
void Plot::differentiate()
{
switch ( plotMode )
{
case Function::Integral:
plotMode = Function::Derivative0;
break;
case Function::Derivative0:
plotMode = Function::Derivative1;
break;
case Function::Derivative1:
plotMode = Function::Derivative2;
break;
case Function::Derivative2:
kWarning() << k_funcinfo << "Can't handle this yet!\n";
break;
}
}
void Plot::integrate()
{
switch ( plotMode )
{
case Function::Integral:
kWarning() << k_funcinfo << "Can't handle this yet!\n";
break;
case Function::Derivative0:
plotMode = Function::Integral;
break;
case Function::Derivative1:
plotMode = Function::Derivative0;
break;
case Function::Derivative2:
plotMode = Function::Derivative1;
break;
}
}
//END class Plot
......@@ -114,7 +114,8 @@ class Equation
Cartesian,
ParametricX,
ParametricY,
Polar
Polar,
Implicit
};
Equation( Type type, Function * parent );
......@@ -147,18 +148,12 @@ class Equation
* @return the name of the function, e.g. for the cartesian function
* f(x)=x^2, this would return "f".
*/
QString fname() const; ///< Name of the function.
QString name() const;
/**
* @return the dummy variable of the function, e.g. for the cartesian
* function f(x,k)=(x+k)(x-k), this would return "x".
* \return a list of parameters, e.g. {x} for "f(x)=y", and {x,y,k} for
* "f(x,y,k)=(x+k)(y+k)".
*/
QString fvar() const;
/**
* @return the parameter if one exists, e.g. for the cartesian function