Commit a0d359d0 authored by David Saxton's avatar David Saxton

Added parameter animator. The value of a function parameter can now be

automatically stepped through.

svn path=/trunk/KDE/kdeedu/kmplot/; revision=554985
parent 631992af
...@@ -137,3 +137,8 @@ kmplot/plotstylewidget.ui ...@@ -137,3 +137,8 @@ kmplot/plotstylewidget.ui
kmplot/parameterswidget.cpp kmplot/parameterswidget.cpp
kmplot/parameterswidget.h kmplot/parameterswidget.h
kmplot/parameterswidget.ui kmplot/parameterswidget.ui
kmplot/animateparameter.cpp
kmplot/animateparameter.h
kmplot/parameteranimator.cpp
kmplot/parameteranimator.h
kmplot/parameteranimator.ui
...@@ -23,8 +23,6 @@ TODO ...@@ -23,8 +23,6 @@ TODO
* An export dialog where you can set the size and enable/disable monocrome. * An export dialog where you can set the size and enable/disable monocrome.
* More printing options. * More printing options.
* Move Coordinate System toolbar buttons to one drop-down list * Move Coordinate System toolbar buttons to one drop-down list
* Make Appearance widget more powerful - add gradient for parameters, etc.
* Option to show extreme points of (Cartesian) functions (accessible via right-click, where else?..). This would replace the find min/max dialog box.
IN PROGRESS IN PROGRESS
========================= =========================
...@@ -33,6 +31,8 @@ IN PROGRESS ...@@ -33,6 +31,8 @@ IN PROGRESS
DONE DONE
========================= =========================
* Option to show extreme points of (Cartesian) functions (accessible via right-click, where else?..). This would replace the find min/max dialog box.
* Make Appearance widget more powerful - add gradient for parameters, etc.
* Give parameters to all types of plots * Give parameters to all types of plots
* Intrinsic plots: f(x,y) = 0 * Intrinsic plots: f(x,y) = 0
* Use a better(faster!) algorithm for drawing integrals numeric. It's not urgent anymore since the implemention of Euler's method is better now. * Use a better(faster!) algorithm for drawing integrals numeric. It's not urgent anymore since the implemention of Euler's method is better now.
......
...@@ -24,7 +24,9 @@ set(kmplotpart_PART_SRCS ...@@ -24,7 +24,9 @@ set(kmplotpart_PART_SRCS
kmplotio.cpp kmplotio.cpp
ksliderwindow.cpp ksliderwindow.cpp
parameterswidget.cpp parameterswidget.cpp
coordsconfigdialog.cpp ) coordsconfigdialog.cpp
parameteranimator.cpp
)
kde4_automoc(${kmplotpart_PART_SRCS}) kde4_automoc(${kmplotpart_PART_SRCS})
...@@ -41,6 +43,7 @@ kde4_add_ui_files(kmplotpart_PART_SRCS ...@@ -41,6 +43,7 @@ kde4_add_ui_files(kmplotpart_PART_SRCS
settingspagegeneral.ui settingspagegeneral.ui
sliderwindow.ui sliderwindow.ui
parameterswidget.ui parameterswidget.ui
parameteranimator.ui
) )
kde4_add_kcfg_files(kmplotpart_PART_SRCS settings.kcfgc ) kde4_add_kcfg_files(kmplotpart_PART_SRCS settings.kcfgc )
......
...@@ -330,6 +330,10 @@ void MainDlg::setupActions() ...@@ -330,6 +330,10 @@ void MainDlg::setupActions()
m_popupmenu->addSeparator(); m_popupmenu->addSeparator();
KAction * animateFunction = new KAction( i18n("Animate Function"), actionCollection(), "animateFunction" );
connect( animateFunction, SIGNAL(triggered(bool)), View::self(), SLOT( animateFunction() ) );
m_popupmenu->addAction( animateFunction );
View::self()->m_showFunctionExtrema = new KToggleAction( i18n( "Show Extrema" ), actionCollection(), "showExtrema" ); View::self()->m_showFunctionExtrema = new KToggleAction( i18n( "Show Extrema" ), actionCollection(), "showExtrema" );
View::self()->m_showFunctionExtrema->setIcon( KIcon( "minimum" ) ); View::self()->m_showFunctionExtrema->setIcon( KIcon( "minimum" ) );
connect( View::self()->m_showFunctionExtrema, SIGNAL(triggered(bool)), View::self(), SLOT(showExtrema(bool)) ); connect( View::self()->m_showFunctionExtrema, SIGNAL(triggered(bool)), View::self(), SLOT(showExtrema(bool)) );
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
#include "settings.h" #include "settings.h"
#include "ksliderwindow.h" #include "ksliderwindow.h"
#include "MainDlg.h" #include "MainDlg.h"
#include "parameteranimator.h"
#include "View.h" #include "View.h"
#include "viewadaptor.h" #include "viewadaptor.h"
...@@ -270,13 +270,17 @@ void View::draw( QPaintDevice * dev, PlotMedium medium ) ...@@ -270,13 +270,17 @@ void View::draw( QPaintDevice * dev, PlotMedium medium )
} }
double View::h() const
{
return qMin( (m_xmax-m_xmin)/area.width(), (m_ymax-m_ymin)/area.height() ) * 1e-1;
}
double View::value( const Plot & plot, int eq, double x, bool updateParameter ) double View::value( const Plot & plot, int eq, double x, bool updateParameter )
{ {
Function * function = plot.function(); Function * function = plot.function();
assert( function ); assert( function );
double dx = (m_xmax-m_xmin)/area.width();
if ( updateParameter ) if ( updateParameter )
plot.updateFunctionParameter(); plot.updateFunctionParameter();
...@@ -288,13 +292,13 @@ double View::value( const Plot & plot, int eq, double x, bool updateParameter ) ...@@ -288,13 +292,13 @@ double View::value( const Plot & plot, int eq, double x, bool updateParameter )
return XParser::self()->fkt( equation, x ); return XParser::self()->fkt( equation, x );
case Function::Derivative1: case Function::Derivative1:
return XParser::self()->derivative( 1, equation, x, dx ); return XParser::self()->derivative( 1, equation, x, h() );
case Function::Derivative2: case Function::Derivative2:
return XParser::self()->derivative( 2, equation, x, dx ); return XParser::self()->derivative( 2, equation, x, h() );
case Function::Integral: case Function::Integral:
return XParser::self()->integral( equation, x, dx ); return XParser::self()->integral( equation, x, h() );
} }
kWarning() << k_funcinfo << "Unknown mode!\n"; kWarning() << k_funcinfo << "Unknown mode!\n";
...@@ -684,6 +688,17 @@ void View::plotFunction(Function *ufkt, QPainter *pDC) ...@@ -684,6 +688,17 @@ void View::plotFunction(Function *ufkt, QPainter *pDC)
foreach ( Plot plot, plots ) foreach ( Plot plot, plots )
{ {
plot.updateFunctionParameter(); plot.updateFunctionParameter();
bool setAliased = false;
if ( plot.parameter.type() == Parameter::Animated )
{
// Don't use antialiasing, so that rendering is speeded up
if ( pDC->renderHints() & QPainter::Antialiasing )
{
setAliased = true;
pDC->setRenderHint( QPainter::Antialiasing, false );
}
}
pDC->setPen( penForPlot( plot, pDC->renderHints() & QPainter::Antialiasing ) ); pDC->setPen( penForPlot( plot, pDC->renderHints() & QPainter::Antialiasing ) );
...@@ -803,6 +818,9 @@ void View::plotFunction(Function *ufkt, QPainter *pDC) ...@@ -803,6 +818,9 @@ void View::plotFunction(Function *ufkt, QPainter *pDC)
x=x+dx; x=x+dx;
} }
} }
if ( setAliased )
pDC->setRenderHint( QPainter::Antialiasing, true );
} }
} }
...@@ -1598,6 +1616,8 @@ double View::pixelNormal( const Plot & plot, double x, double y ) ...@@ -1598,6 +1616,8 @@ double View::pixelNormal( const Plot & plot, double x, double y )
double dx = 0; double dx = 0;
double dy = 0; double dy = 0;
double h = this->h();
switch ( f->type() ) switch ( f->type() )
{ {
case Function::Cartesian: case Function::Cartesian:
...@@ -1608,8 +1628,8 @@ double View::pixelNormal( const Plot & plot, double x, double y ) ...@@ -1608,8 +1628,8 @@ double View::pixelNormal( const Plot & plot, double x, double y )
case Function::Implicit: case Function::Implicit:
{ {
dx = XParser::self()->partialDerivative( 1, 0, f->eq[0], x, y, 1e-5, 1e-5 ) / sx; dx = XParser::self()->partialDerivative( 1, 0, f->eq[0], x, y, h, h ) / sx;
dy = XParser::self()->partialDerivative( 0, 1, f->eq[0], x, y, 1e-5, 1e-5 ) / sy; dy = XParser::self()->partialDerivative( 0, 1, f->eq[0], x, y, h, h ) / sy;
double theta = -arctan( dy / dx ); double theta = -arctan( dy / dx );
...@@ -1623,8 +1643,8 @@ double View::pixelNormal( const Plot & plot, double x, double y ) ...@@ -1623,8 +1643,8 @@ double View::pixelNormal( const Plot & plot, double x, double y )
case Function::Polar: case Function::Polar:
{ {
double r = XParser::self()->derivative( 0, f->eq[0], x, 1e-5 ); double r = XParser::self()->derivative( 0, f->eq[0], x, h );
double dr = XParser::self()->derivative( 1, f->eq[0], x, 1e-5 ); double dr = XParser::self()->derivative( 1, f->eq[0], x, h );
dx = (dr * cos(x) - r * sin(x)) * sx; dx = (dr * cos(x) - r * sin(x)) * sx;
dy = (dr * sin(x) + r * cos(x)) * sy; dy = (dr * sin(x) + r * cos(x)) * sy;
...@@ -1633,8 +1653,8 @@ double View::pixelNormal( const Plot & plot, double x, double y ) ...@@ -1633,8 +1653,8 @@ double View::pixelNormal( const Plot & plot, double x, double y )
case Function::Parametric: case Function::Parametric:
{ {
dx = XParser::self()->derivative( 1, f->eq[0], x, 1e-5 ) * sx; dx = XParser::self()->derivative( 1, f->eq[0], x, h ) * sx;
dy = XParser::self()->derivative( 1, f->eq[1], x, 1e-5 ) * sy; dy = XParser::self()->derivative( 1, f->eq[1], x, h ) * sy;
break; break;
} }
} }
...@@ -1662,6 +1682,8 @@ double View::pixelCurvature( const Plot & plot, double x, double y ) ...@@ -1662,6 +1682,8 @@ double View::pixelCurvature( const Plot & plot, double x, double y )
double fddy = 0; double fddy = 0;
double fdxy = 0; double fdxy = 0;
double h = this->h();
switch ( f->type() ) switch ( f->type() )
{ {
case Function::Cartesian: case Function::Cartesian:
...@@ -1669,17 +1691,17 @@ double View::pixelCurvature( const Plot & plot, double x, double y ) ...@@ -1669,17 +1691,17 @@ double View::pixelCurvature( const Plot & plot, double x, double y )
fdx = sx; fdx = sx;
fddx = 0; fddx = 0;
fdy = XParser::self()->derivative( 1, f->eq[0], x, (m_xmax-m_xmin)/1e5 ) * sy; fdy = XParser::self()->derivative( 1, f->eq[0], x, h ) * sy;
fddy = XParser::self()->derivative( 2, f->eq[0], x, (m_xmax-m_xmin)/1e5 ) * sy; fddy = XParser::self()->derivative( 2, f->eq[0], x, h) * sy;
break; break;
} }
case Function::Polar: case Function::Polar:
{ {
double r = XParser::self()->derivative( 0, f->eq[0], x, 1e-5 ); double r = XParser::self()->derivative( 0, f->eq[0], x, h );
double dr = XParser::self()->derivative( 1, f->eq[0], x, 1e-5 ); double dr = XParser::self()->derivative( 1, f->eq[0], x, h );
double ddr = XParser::self()->derivative( 2, f->eq[0], x, 1e-5 ); double ddr = XParser::self()->derivative( 2, f->eq[0], x, h );
fdx = (dr * cos(x) - r * sin(x)) * sx; fdx = (dr * cos(x) - r * sin(x)) * sx;
fdy = (dr * sin(x) + r * cos(x)) * sy; fdy = (dr * sin(x) + r * cos(x)) * sy;
...@@ -1692,24 +1714,24 @@ double View::pixelCurvature( const Plot & plot, double x, double y ) ...@@ -1692,24 +1714,24 @@ double View::pixelCurvature( const Plot & plot, double x, double y )
case Function::Parametric: case Function::Parametric:
{ {
fdx = XParser::self()->derivative( 1, f->eq[0], x, 1e-5 ) * sx; fdx = XParser::self()->derivative( 1, f->eq[0], x, h ) * sx;
fdy = XParser::self()->derivative( 1, f->eq[1], x, 1e-5 ) * sy; fdy = XParser::self()->derivative( 1, f->eq[1], x, h ) * sy;
fddx = XParser::self()->derivative( 2, f->eq[0], x, 1e-5 ) * sx; fddx = XParser::self()->derivative( 2, f->eq[0], x, h ) * sx;
fddy = XParser::self()->derivative( 2, f->eq[1], x, 1e-5 ) * sy; fddy = XParser::self()->derivative( 2, f->eq[1], x, h ) * sy;
break; break;
} }
case Function::Implicit: case Function::Implicit:
{ {
fdx = XParser::self()->partialDerivative( 1, 0, f->eq[0], x, y, 1e-5, 1e-5 ) / sx; fdx = XParser::self()->partialDerivative( 1, 0, f->eq[0], x, y, h, h ) / sx;
fdy = XParser::self()->partialDerivative( 0, 1, f->eq[0], x, y, 1e-5, 1e-5 ) / sy; fdy = XParser::self()->partialDerivative( 0, 1, f->eq[0], x, y, h, h ) / sy;
fddx = XParser::self()->partialDerivative( 2, 0, f->eq[0], x, y, 1e-5, 1e-5 ) / (sx*sx); fddx = XParser::self()->partialDerivative( 2, 0, f->eq[0], x, y, h, h ) / (sx*sx);
fddy = XParser::self()->partialDerivative( 0, 2, f->eq[0], x, y, 1e-5, 1e-5 ) / (sy*sy); fddy = XParser::self()->partialDerivative( 0, 2, f->eq[0], x, y, h, h ) / (sy*sy);
fdxy = XParser::self()->partialDerivative( 1, 1, f->eq[0], x, y, 1e-5, 1e-5 ) / (sx*sy); fdxy = XParser::self()->partialDerivative( 1, 1, f->eq[0], x, y, h, h ) / (sx*sy);
break; break;
...@@ -2047,7 +2069,9 @@ double View::pixelDistance( const QPointF & pos, const Plot & plot, double x, bo ...@@ -2047,7 +2069,9 @@ double View::pixelDistance( const QPointF & pos, const Plot & plot, double x, bo
QString View::posToString( double x, double delta, PositionFormatting format, QColor color ) const QString View::posToString( double x, double delta, PositionFormatting format, QColor color ) const
{ {
assert( delta != 0.0 ); // assert( delta != 0.0 );
if ( delta == 0 )
delta = 1;
QString numberText; QString numberText;
...@@ -2929,6 +2953,17 @@ void View::mnuRemove_clicked() ...@@ -2929,6 +2953,17 @@ void View::mnuRemove_clicked()
} }
void View::animateFunction()
{
Function * f = m_currentPlot.function();
if ( !f )
return;
ParameterAnimator * anim = new ParameterAnimator( this, f );
anim->show();
}
void View::showExtrema( bool show ) void View::showExtrema( bool show )
{ {
Function * f = m_currentPlot.function(); Function * f = m_currentPlot.function();
......
...@@ -188,6 +188,7 @@ class View : public QWidget ...@@ -188,6 +188,7 @@ class View : public QWidget
void mnuRemove_clicked(); void mnuRemove_clicked();
void mnuEdit_clicked(); void mnuEdit_clicked();
void showExtrema( bool show ); void showExtrema( bool show );
void animateFunction();
///Slots for the zoom menu ///Slots for the zoom menu
void mnuZoomIn_clicked(); void mnuZoomIn_clicked();
void mnuZoomOut_clicked(); void mnuZoomOut_clicked();
...@@ -224,6 +225,10 @@ class View : public QWidget ...@@ -224,6 +225,10 @@ class View : public QWidget
virtual void focusInEvent( QFocusEvent * ); virtual void focusInEvent( QFocusEvent * );
private: private:
/**
* \return an appropriate value to use in numerical differentiation.
*/
double h() const;
/** /**
* Print out table with additional information. Only for printing. * Print out table with additional information. Only for printing.
*/ */
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <QApplication> #include <QApplication>
#include <QStyleOptionFrame> #include <QStyleOptionFrame>
#include <assert.h>
//BEGIN class EquationHighlighter //BEGIN class EquationHighlighter
EquationHighlighter::EquationHighlighter( EquationEdit * parent ) EquationHighlighter::EquationHighlighter( EquationEdit * parent )
...@@ -122,6 +123,14 @@ void EquationEdit::setInputType( InputType type ) ...@@ -122,6 +123,14 @@ void EquationEdit::setInputType( InputType type )
} }
double EquationEdit::value( )
{
assert( m_inputType == Expression ); // Can't really get a value of a function as that requires an input
return XParser::self()->eval( text() );
}
void EquationEdit::slotTextChanged( ) void EquationEdit::slotTextChanged( )
{ {
emit textChanged( text() ); emit textChanged( text() );
......
...@@ -95,6 +95,10 @@ class EquationEdit : public QTextEdit ...@@ -95,6 +95,10 @@ class EquationEdit : public QTextEdit
void setValidatePrefix( const QString & prefix ); void setValidatePrefix( const QString & prefix );
QString text() const { return toPlainText(); } QString text() const { return toPlainText(); }
/**
* Attempts to evaluate the text and return it.
*/
double value();
signals: signals:
void editingFinished(); void editingFinished();
......
...@@ -501,7 +501,9 @@ QList< Plot > Function::allPlots( ) const ...@@ -501,7 +501,9 @@ QList< Plot > Function::allPlots( ) const
bool usedParameter = false; bool usedParameter = false;
if ( m_parameters.useSlider ) // Don't use slider or list parameters if animating
if ( !m_parameters.animating && m_parameters.useSlider )
{ {
Parameter param( Parameter::Slider ); Parameter param( Parameter::Slider );
param.setSliderID( m_parameters.sliderID ); param.setSliderID( m_parameters.sliderID );
...@@ -511,7 +513,7 @@ QList< Plot > Function::allPlots( ) const ...@@ -511,7 +513,7 @@ QList< Plot > Function::allPlots( ) const
usedParameter = true; usedParameter = true;
} }
if ( m_parameters.useList ) if ( !m_parameters.animating && m_parameters.useList )
{ {
int pos = 0; int pos = 0;
foreach ( Value v, m_parameters.list ) foreach ( Value v, m_parameters.list )
...@@ -525,6 +527,12 @@ QList< Plot > Function::allPlots( ) const ...@@ -525,6 +527,12 @@ QList< Plot > Function::allPlots( ) const
} }
} }
if ( m_parameters.animating )
{
Parameter param( Parameter::Animated );
plot.parameter = param;
}
if ( !usedParameter ) if ( !usedParameter )
list << plot; list << plot;
} }
...@@ -538,6 +546,7 @@ QList< Plot > Function::allPlots( ) const ...@@ -538,6 +546,7 @@ QList< Plot > Function::allPlots( ) const
//BEGIN class ParameterSettings //BEGIN class ParameterSettings
ParameterSettings::ParameterSettings() ParameterSettings::ParameterSettings()
{ {
animating = false;
useSlider = false; useSlider = false;
sliderID = 0; sliderID = 0;
useList = false; useList = false;
...@@ -642,6 +651,12 @@ void Plot::updateFunctionParameter() const ...@@ -642,6 +651,12 @@ void Plot::updateFunctionParameter() const
k = m_function->m_parameters.list[ parameter.listPos() ].value(); k = m_function->m_parameters.list[ parameter.listPos() ].value();
break; break;
} }
case Parameter::Animated:
{
// Don't adjust the current function parameter
return;
}
} }
m_function->setParameter( k ); m_function->setParameter( k );
......
...@@ -209,6 +209,7 @@ class Equation ...@@ -209,6 +209,7 @@ class Equation
bool operator == ( const ParameterSettings & other ) const; bool operator == ( const ParameterSettings & other ) const;
bool operator != ( const ParameterSettings & other ) const { return !((*this) == other); } bool operator != ( const ParameterSettings & other ) const { return !((*this) == other); }
bool animating; ///< if true, then useSlider and useList are ignored, parameter value is assumed to be updated
bool useSlider; bool useSlider;
int sliderID; int sliderID;
bool useList; bool useList;
...@@ -223,7 +224,7 @@ class Equation ...@@ -223,7 +224,7 @@ class Equation
class Parameter class Parameter
{ {
public: public:
enum Type { Unknown, Slider, List }; enum Type { Unknown, Animated, Slider, List };
Parameter( Type type = Unknown ); Parameter( Type type = Unknown );
Type type() const { return m_type; } Type type() const { return m_type; }
......
...@@ -45,10 +45,9 @@ ...@@ -45,10 +45,9 @@
class ParameterValueList; class ParameterValueList;
KParameterEditor::KParameterEditor(XParser *m, QList<Value> *l, QWidget *parent ) KParameterEditor::KParameterEditor( QList<Value> *l, QWidget *parent )
: KDialog( parent ), : KDialog( parent ),
m_parameter(l), m_parameter(l)
m_parser(m)
{ {
setCaption( i18n( "Parameter Editor" ) ); setCaption( i18n( "Parameter Editor" ) );
setButtons( Ok | Cancel ); setButtons( Ok | Cancel );
...@@ -147,8 +146,8 @@ void KParameterEditor::saveCurrentValue() ...@@ -147,8 +146,8 @@ void KParameterEditor::saveCurrentValue()
bool KParameterEditor::checkValueValid() bool KParameterEditor::checkValueValid()
{ {
QString valueText = m_mainWidget->value->text(); QString valueText = m_mainWidget->value->text();
(double) m_parser->eval( valueText ); (double) XParser::self()->eval( valueText );
bool valid = (m_parser->parserError( false ) == 0); bool valid = (XParser::self()->parserError( false ) == 0);
m_mainWidget->valueInvalidLabel->setVisible( !valueText.isEmpty() && !valid ); m_mainWidget->valueInvalidLabel->setVisible( !valueText.isEmpty() && !valid );
return valid; return valid;
} }
...@@ -190,8 +189,8 @@ void KParameterEditor::cmdImport_clicked() ...@@ -190,8 +189,8 @@ void KParameterEditor::cmdImport_clicked()
line = stream.readLine(); line = stream.readLine();
if (line.isEmpty()) if (line.isEmpty())
continue; continue;
m_parser->eval( line ); XParser::self()->eval( line );
if ( m_parser->parserError(false) == 0) if ( XParser::self()->parserError(false) == 0)
{ {
if ( !checkTwoOfIt(line) ) if ( !checkTwoOfIt(line) )
{ {
......
...@@ -43,7 +43,7 @@ class KParameterEditor : public KDialog ...@@ -43,7 +43,7 @@ class KParameterEditor : public KDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
KParameterEditor(XParser *, QList<Value> *, QWidget *parent = 0 ); KParameterEditor( QList<Value> *, QWidget *parent = 0 );
~KParameterEditor(); ~KParameterEditor();
public slots: public slots:
...@@ -68,7 +68,6 @@ private: ...@@ -68,7 +68,6 @@ private:
/// Check so that it doesn't exist two equal values /// Check so that it doesn't exist two equal values
bool checkTwoOfIt( const QString & text); bool checkTwoOfIt( const QString & text);
QList<Value> *m_parameter; QList<Value> *m_parameter;
XParser *m_parser;
QParameterEditor * m_mainWidget; QParameterEditor * m_mainWidget;
}; };
......
/*
* KmPlot - a math. function plotter for the KDE-Desktop
*
* Copyright (C) 2006 David Saxton <david@bluehaze.org>
*
* This file is part of the KDE Project.
* KmPlot is part of the KDE-EDU Project.
*