+++ /dev/null
-/** @file
- *
- * KPendulum screen saver for KDE
- *
- * The screen saver displays a physically realistic simulation of a two-part
- * pendulum.
- *
- * Copyright (C) 2004 Georg Drenkhahn, Georg.Drenkhahn@gmx.net
- *
- * 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) version 3 or
- * any later version accepted by the membership of KDE e.V. (or its successor
- * approved by the membership of KDE e.V.), which shall act as a proxy defined
- * in Section 14 of version 3 of the license.
- *
- * 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.
- */
-
-#define QT_NO_COMPAT
-
-// std. C++ headers
-#include <cstdlib>
-
-// Qt headers
-#include <QLineEdit>
-#include <QSpinBox>
-#include <QValidator>
-#include <QColorDialog>
-#include <QPushButton>
-#include <QToolTip>
-#include <QtGui/qevent.h>
-
-// KDE headers
-#include <KLocale>
-#include <KGlobal>
-#include <KConfig>
-#include <KDebug>
-#include <KMessageBox>
-
-// Eigen2 from KDE support
-#include <Eigen/Core>
-#include <Eigen/Geometry>
-// import most common Eigen types
-using namespace Eigen;
-
-// the screen saver preview area class
-#include "sspreviewarea.h"
-
-#include "pendulum.h" // own interfaces
-#include <kcolordialog.h>
-#include "moc_pendulum.cpp"
-
-#define KPENDULUM_VERSION "2.0"
-
-#ifndef M_PI
-#define M_PI 3.14159265358979323846
-#endif
-
-// libkscreensaver interface
-class KPendulumSaverInterface : public KScreenSaverInterface
-{
- public:
- /// aboutdata instance for libkscreensaver interface
- virtual KAboutData* aboutData()
- {
- return new KAboutData(
- "kpendulum.kss", 0,
- ki18n("Simulation of a two-part pendulum"),
- KPENDULUM_VERSION,
- ki18n("Simulation of a two-part pendulum"));
- }
-
- /// function to create screen saver object
- virtual KScreenSaver* create(WId id)
- {
- return new KPendulumSaver(id);
- }
-
- /// function to create setup dialog for screen saver
- virtual QDialog* setup()
- {
- return new KPendulumSetup();
- }
-};
-
-int main( int argc, char *argv[] )
-{
- KPendulumSaverInterface kss;
- return kScreenSaverMain(argc, argv, kss);
-}
-
-//-----------------------------------------------------------------------------
-// PendulumOdeSolver
-//-----------------------------------------------------------------------------
-
-PendulumOdeSolver::PendulumOdeSolver(
- const double& t,
- const double& dt,
- const Vector4d& y,
- const double& eps,
- const double& m1,
- const double& m2,
- const double& l1,
- const double& l2,
- const double& g)
- : RkOdeSolver<double,4>(t, y, dt, eps),
- // constants for faster numeric calculation, derived from m1,m2,l1,l2,g
- m_A(1.0/(m2*l1*l1)),
- m_B1(m2*l1*l2),
- m_B(1.0/m_B1),
- m_C((m1+m2)/(m2*m2*l2*l2)),
- m_D(g*(m1+m2)*l1),
- m_E(g*m2*l2),
- m_M((m1+m2)/m2)
-{
-}
-
-Vector4d PendulumOdeSolver::f(const double& x, const Vector4d& y) const
-{
- (void)x; // unused
-
- const double& q1 = y[0];
- const double& q2 = y[1];
- const double& p1 = y[2];
- const double& p2 = y[3];
-
- const double cosDq = std::cos(q1-q2);
- const double iden = 1.0/(m_M - cosDq*cosDq); // invers denominator
- const double dq1dt = (m_A*p1 - m_B*cosDq*p2)*iden;
- const double dq2dt = (m_C*p2 - m_B*cosDq*p1)*iden;
-
- Vector4d ypr;
- ypr[0] = dq1dt;
- ypr[1] = dq2dt;
-
- const double K = m_B1 * dq1dt*dq2dt * std::sin(q1-q2);
- ypr[2] = -K - m_D * std::sin(q1);
- ypr[3] = K - m_E * std::sin(q2);
-
- return ypr;
-}
-
-//-----------------------------------------------------------------------------
-// Rotation: screen saver widget
-//-----------------------------------------------------------------------------
-
-PendulumGLWidget::PendulumGLWidget(QWidget* parent)
- : QGLWidget(parent),
- m_eyeR(30), // eye coordinates (polar)
- m_eyeTheta(M_PI*0.45),
- m_eyePhi(0),
- m_lightR(m_eyeR), // light coordinates (polar)
- m_lightTheta(M_PI*0.25),
- m_lightPhi(M_PI*0.25),
- m_quadM1(gluNewQuadric()),
- m_barColor(KPendulumSaver::sm_barColorDefault),
- m_m1Color(KPendulumSaver::sm_m1ColorDefault),
- m_m2Color(KPendulumSaver::sm_m2ColorDefault)
-{
-}
-
-PendulumGLWidget::~PendulumGLWidget(void)
-{
- gluDeleteQuadric(m_quadM1);
-}
-
-void PendulumGLWidget::setEyePhi(double phi)
-{
- m_eyePhi = phi;
- while (m_eyePhi < 0)
- {
- m_eyePhi += 2.*M_PI;
- }
- while (m_eyePhi > 2*M_PI)
- {
- m_eyePhi -= 2.*M_PI;
- }
-
- // get the view port
- GLint vp[4];
- glGetIntegerv(GL_VIEWPORT, vp);
-
- // calc new perspective, a resize event is simulated here
- resizeGL(static_cast<int>(vp[2]), static_cast<int>(vp[3]));
-}
-
-void PendulumGLWidget::setAngles(const double& q1, const double& q2)
-{
- m_ang1 = static_cast<GLfloat>(q1*180./M_PI);
- m_ang2 = static_cast<GLfloat>(q2*180./M_PI);
-}
-
-void PendulumGLWidget::setMasses(const double& m1, const double& m2)
-{
- m_sqrtm1 = static_cast<GLfloat>(std::sqrt(m1));
- m_sqrtm2 = static_cast<GLfloat>(std::sqrt(m2));
-}
-
-void PendulumGLWidget::setLengths(const double& l1, const double& l2)
-{
- m_l1 = static_cast<GLfloat>(l1);
- m_l2 = static_cast<GLfloat>(l2);
-}
-
-void PendulumGLWidget::setBarColor(const QColor& c)
-{
- if (c.isValid())
- {
- m_barColor = c;
- }
-}
-
-void PendulumGLWidget::setM1Color(const QColor& c)
-{
- if (c.isValid())
- {
- m_m1Color = c;
- }
-}
-void PendulumGLWidget::setM2Color(const QColor& c)
-{
- if (c.isValid())
- {
- m_m2Color = c;
- }
-}
-
-/* --------- protected methods ----------- */
-
-void PendulumGLWidget::initializeGL(void)
-{
- qglClearColor(QColor(Qt::black)); // set color to clear the background
-
- glClearDepth(1); // depth buffer setup
- glEnable(GL_DEPTH_TEST); // depth testing
- glDepthFunc(GL_LEQUAL); // type of depth test
-
- glShadeModel(GL_SMOOTH); // smooth color shading in poygons
-
- // nice perspective calculation
- glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
-
- // set up the light
- glEnable(GL_LIGHTING);
- glEnable(GL_LIGHT0);
- glEnable(GL_LIGHT1);
-
- glMatrixMode(GL_MODELVIEW); // select modelview matrix
- glLoadIdentity();
- // set position of light0
- GLfloat lightPos[4]=
- {GLfloat(m_lightR * std::sin(m_lightTheta) * std::sin(m_lightPhi)),
- GLfloat(m_lightR * std::sin(m_lightTheta) * std::cos(m_lightPhi)),
- GLfloat(m_lightR * std::cos(m_lightTheta)),
- 0};
- glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
- // set position of light1
- lightPos[0] = m_lightR * std::sin(m_lightTheta) * std::sin(m_lightPhi+M_PI);
- lightPos[1] = m_lightR * std::sin(m_lightTheta) * std::cos(m_lightPhi+M_PI);
- glLightfv(GL_LIGHT1, GL_POSITION, lightPos);
-
- // only for lights #>0
- GLfloat spec[] = {1,1,1,1};
- glLightfv(GL_LIGHT1, GL_SPECULAR, spec);
- glLightfv(GL_LIGHT1, GL_DIFFUSE, spec);
-
- // enable setting the material colour by glColor()
- glEnable(GL_COLOR_MATERIAL);
-
- GLfloat emi[4] = {.13, .13, .13, 1};
- glMaterialfv(GL_FRONT, GL_EMISSION, emi);
-}
-
-void PendulumGLWidget::paintGL(void)
-{
- // clear color and depth buffer
- glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
-
- glMatrixMode(GL_MODELVIEW); // select modelview matrix
-
- glLoadIdentity();
-
- const GLfloat width = 2.0;
- const GLfloat masswidth = 1.0;
- const int noOfSlices = 20;
-
- // top axis, left (x>0)
- glTranslatef(0.5*width, 0, 0);
- glRotatef(90, 0, 1, 0);
- qglColor(m_barColor);
- gluCylinder(m_quadM1, 0.2, 0.2, 5, 10, 1);
- gluSphere(m_quadM1, 0.2, 10, 10);
- // top axis, right
- glLoadIdentity();
- glTranslatef(-0.5*width, 0, 0);
- glRotatef(-90, 0, 1, 0);
- gluCylinder(m_quadM1, 0.2, 0.2, 5, 10, 1);
- gluSphere(m_quadM1, 0.2, 10, 10);
- // 1. part, left
- glLoadIdentity();
- glRotatef(m_ang1, 1, 0, 0);
- glPushMatrix();
- glTranslatef(0.5*width, 0, -m_l1);
- gluCylinder(m_quadM1, 0.2, 0.2, m_l1, 10, 1);
- glPopMatrix();
-
- // 1. part, right
- glPushMatrix();
- glTranslatef(-0.5*width, 0, -m_l1);
- gluCylinder(m_quadM1, 0.2, 0.2, m_l1, 10, 1);
- // 1. part, bottom
- glRotatef(90, 0, 1, 0);
- gluSphere(m_quadM1, 0.2, 10, 10); // bottom corner 1
- gluCylinder(m_quadM1, 0.2, 0.2, width, 10, 1); // connection
- glTranslatef(0, 0, 0.5*(width-masswidth));
- qglColor(m_m1Color);
- gluCylinder(m_quadM1, m_sqrtm1, m_sqrtm1, masswidth, noOfSlices, 1); // mass 1
- gluQuadricOrientation(m_quadM1, GLU_INSIDE);
- gluDisk(m_quadM1, 0, m_sqrtm1, noOfSlices, 1); // bottom of mass
- gluQuadricOrientation(m_quadM1, GLU_OUTSIDE);
- glTranslatef(0, 0, masswidth);
- gluDisk(m_quadM1, 0, m_sqrtm1, noOfSlices, 1); // top of mass
-
- glTranslatef(0, 0, 0.5*(width-masswidth));
- qglColor(m_barColor);
- gluSphere(m_quadM1, 0.2, 10, 10); // bottom corner 2
- glPopMatrix();
-
- // 2. pendulum bar
- glLoadIdentity();
- glTranslatef(0, m_l1*std::sin(m_ang1*M_PI/180.), -m_l1*std::cos(m_ang1*M_PI/180.));
- glRotatef(m_ang2, 1, 0, 0);
- glTranslatef(0, 0, -m_l2);
- qglColor(m_barColor);
- gluCylinder(m_quadM1, 0.2, 0.2, m_l2, 10, 1);
-
- // mass 2
- glRotatef(90, 0, 1, 0);
- glTranslatef(0, 0, -0.5*masswidth);
- qglColor(m_m2Color);
- gluCylinder(m_quadM1, m_sqrtm2, m_sqrtm2, masswidth, noOfSlices, 1);
- gluQuadricOrientation(m_quadM1, GLU_INSIDE);
- gluDisk(m_quadM1, 0, m_sqrtm2, noOfSlices, 1); // bottom of mass
- gluQuadricOrientation(m_quadM1, GLU_OUTSIDE);
- glTranslatef(0, 0, masswidth);
- gluDisk(m_quadM1, 0, m_sqrtm2, noOfSlices, 1); // top of mass
-
- glFlush();
-}
-
-void PendulumGLWidget::resizeGL(int w, int h)
-{
- kDebug() << "w=" << w << ", h=" << h << "\n";
-
- // prevent a divide by zero
- if (h == 0)
- {
- return;
- }
-
- // set the new view port
- glViewport(0, 0, static_cast<GLint>(w), static_cast<GLint>(h));
-
- // set up projection matrix
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- // Perspective view
- gluPerspective(
- 40.0f,
- static_cast<GLdouble>(w)/static_cast<GLdouble>(h),
- 1.0, 100.0f);
-
- // Viewing transformation, position for better view
- // Theta is polar angle 0<Theta<Pi
- gluLookAt(
- m_eyeR * std::sin(m_eyeTheta) * std::sin(m_eyePhi),
- m_eyeR * std::sin(m_eyeTheta) * std::cos(m_eyePhi),
- m_eyeR * std::cos(m_eyeTheta),
- 0,0,0,
- 0,0,1);
-}
-
-//-----------------------------------------------------------------------------
-// KPendulumSaver: screen saver class
-//-----------------------------------------------------------------------------
-
-// class methods
-
-KPendulumSaver::KPendulumSaver(WId id) :
- KScreenSaver(id),
- m_solver(0),
- m_massRatio(sm_massRatioDefault),
- m_lengthRatio(sm_lengthRatioDefault),
- m_g(sm_gDefault),
- m_E(sm_EDefault),
- m_persChangeInterval(sm_persChangeIntervalDefault)
-{
- // no need to set our parent widget's background here, the GL widget sets its
- // own background color
-
-
- m_glArea = new PendulumGLWidget(); // create gl widget w/o parent
- m_glArea->setEyePhi(sm_eyePhiDefault);
- readSettings(); // read global settings into pars
-
-
- // init m_glArea with read settings, construct and init m_solver
- initData();
-
- embed(m_glArea); // embed gl widget and resize it
- m_glArea->show(); // show embedded gl widget
-
- // set up and start cyclic timer
- m_timer = new QTimer(this);
- m_timer->start(sm_deltaT);
- connect(m_timer, SIGNAL(timeout()), this, SLOT(doTimeStep()));
-}
-
-KPendulumSaver::~KPendulumSaver()
-{
- m_timer->stop();
-
- // m_timer is automatically deleted with parent KPendulumSaver
- delete m_solver;
- delete m_glArea;
-}
-
-
-void KPendulumSaver::readSettings()
-{
- // read configuration settings from config file
- KConfigGroup config(KGlobal::config(), "Settings");
-
- // internal saver parameters are set to stored values or left at their
- // default values if stored values are out of range
- setMassRatio(
- config.readEntry(
- "mass ratio",
- KPendulumSaver::sm_massRatioDefault));
- setLengthRatio(
- config.readEntry(
- "length ratio",
- KPendulumSaver::sm_lengthRatioDefault));
- setG(
- config.readEntry(
- "g",
- KPendulumSaver::sm_gDefault));
- setE(
- config.readEntry(
- "E",
- KPendulumSaver::sm_EDefault));
- setPersChangeInterval(
- config.readEntry(
- "perspective change interval",
- (uint)KPendulumSaver::sm_persChangeIntervalDefault));
-
- // set the colours
- setBarColor(config.readEntry("bar color", sm_barColorDefault));
- setM1Color( config.readEntry("m1 color", sm_m1ColorDefault));
- setM2Color( config.readEntry("m2 color", sm_m2ColorDefault));
-}
-
-void KPendulumSaver::initData()
-{
- const double m1plusm2 = 2; // m1+m2
- const double m2 = m_massRatio * m1plusm2;
- const double m1 = m1plusm2 - m2;
- m_glArea->setMasses(m1, m2);
- m_glArea->setAngles(0, 0);
-
- const double l1plusl2 = 9; // l1+l2
- const double l2 = m_lengthRatio * l1plusl2;
- const double l1 = l1plusl2 - l2;
- m_glArea->setLengths(l1, l2);
-
- // kinetic energy of m2 and m1
- const double kin_energy = m_E * m_g * (l1*m1 + (m1+m2)*(l1+l2));
- // angular velocity for 1. and 2. pendulum
- const double qp = std::sqrt(2.*kin_energy/((m1+m2)*l1*l1 + m2*l2*l2 + m2*l1*l2));
-
- // assemble initial y for solver
- Vector4d y;
- y[0] = 0; // q1
- y[1] = 0; // q2
- y[2] = (m1+m2)*l1*l1*qp + 0.5*m2*l1*l2*qp; // p1
- y[3] = m2*l2*l2*qp + 0.5*m2*l1*l2*qp; // p2
-
- // delete old solver
- if (m_solver!=0)
- {
- delete m_solver;
- }
- // init new solver
- m_solver = new PendulumOdeSolver(
- 0.0, // t
- 0.01, // first dt step size estimation
- y,
- 1e-5, // eps
- m1,
- m2,
- l1,
- l2,
- m_g);
-}
-
-
-void KPendulumSaver::setBarColor(const QColor& c)
-{
- m_glArea->setBarColor(c);
-}
-QColor KPendulumSaver::barColor(void) const
-{
- return m_glArea->barColor();
-}
-
-void KPendulumSaver::setM1Color(const QColor& c)
-{
- m_glArea->setM1Color(c);
-}
-QColor KPendulumSaver::m1Color(void) const
-{
- return m_glArea->m1Color();
-}
-
-void KPendulumSaver::setM2Color(const QColor& c)
-{
- m_glArea->setM2Color(c);
-}
-QColor KPendulumSaver::m2Color(void) const
-{
- return m_glArea->m2Color();
-}
-
-
-void KPendulumSaver::setMassRatio(const double& massRatio)
-{
- // range check is not necessary in normal operation because validators check
- // the values at input. But the validators do not check for corrupted
- // settings read from disk.
- if ((massRatio >= sm_massRatioLimitLower)
- && (massRatio <= sm_massRatioLimitUpper)
- && (m_massRatio != massRatio))
- {
- m_massRatio = massRatio;
- if (m_timer!=0)
- {
- initData();
- }
- }
-}
-
-void KPendulumSaver::setLengthRatio(const double& lengthRatio)
-{
- if ((lengthRatio >= sm_lengthRatioLimitLower)
- && (lengthRatio <= sm_lengthRatioLimitUpper)
- && (m_lengthRatio != lengthRatio))
- {
- m_lengthRatio = lengthRatio;
- if (m_timer!=0)
- {
- initData();
- }
- }
-}
-
-void KPendulumSaver::setG(const double& g)
-{
- if ((g >= sm_gLimitLower)
- && (g <= sm_gLimitUpper)
- && (m_g != g))
- {
- m_g = g;
- if (m_timer!=0)
- {
- initData();
- }
- }
-}
-
-void KPendulumSaver::setE(const double& E)
-{
- if ((E >= sm_ELimitLower)
- && (E <= sm_ELimitUpper)
- && (m_E != E))
- {
- m_E = E;
- if (m_timer!=0)
- {
- initData();
- }
- }
-}
-
-void KPendulumSaver::setPersChangeInterval(
- const unsigned int& persChangeInterval)
-{
- if ((persChangeInterval >= sm_persChangeIntervalLimitLower)
- && (persChangeInterval <= sm_persChangeIntervalLimitUpper)
- && (m_persChangeInterval != persChangeInterval))
- {
- m_persChangeInterval = persChangeInterval;
- // do not restart simulation here
- }
-}
-
-void KPendulumSaver::doTimeStep()
-{
- /* time (in seconds) of perspective change.
- * - t<0: no change yet
- * - t=0: change starts
- * - 0<t<moving time: change takes place
- * - t=moving time: end of the change */
- static double persChangeTime = -5;
-
- // integrate a step ahead
- m_solver->integrate(0.001 * sm_deltaT);
-
- // read new y from solver
- const Vector4d& y = m_solver->Y();
-
- // tell glArea the new coordinates/angles of the pendulum
- m_glArea->setAngles(y[0], y[1]);
-
- // handle perspective change
- persChangeTime += 0.001 * sm_deltaT;
- if (persChangeTime > 0)
- {
- // phi value at the start of a perspective change
- static double eyePhi0 = sm_eyePhiDefault;
- // phi value at the end of a perspective change
- static double eyePhi1 = 0.75*M_PI;
- static double deltaEyePhi = eyePhi1-eyePhi0;
-
- // movement acceleration/deceleration
- const double a = 3;
- // duration of the change period
- const double movingTime = 2.*std::sqrt(std::abs(deltaEyePhi)/a);
-
- // new current phi of eye
- double eyePhi = persChangeTime < 0.5*movingTime ?
- // accelerating phase
- eyePhi0 + (deltaEyePhi>0?1:-1)
- * 0.5*a*persChangeTime*persChangeTime:
- // decellerating phase
- eyePhi1 - (deltaEyePhi>0?1:-1)
- * 0.5*a*(movingTime-persChangeTime)*(movingTime-persChangeTime);
-
- if (persChangeTime > movingTime)
- { // perspective change has finished
- // set new time till next change
- persChangeTime = -double(m_persChangeInterval);
- eyePhi0 = eyePhi = eyePhi1;
- // find new phi value with angleLimit < phi < Pi-angleLimit or
- // Pi+angleLimit < phi < 2*Pi-angleLimit
- const double angleLimit = M_PI*0.2;
- for (eyePhi1 = 0;
- (eyePhi1<angleLimit)
- || ((eyePhi1<M_PI+angleLimit) && (eyePhi1>M_PI-angleLimit))
- || (eyePhi1>2*M_PI-angleLimit);
- eyePhi1 = double(qrand())/RAND_MAX * 2*M_PI)
- {
- }
- // new delta phi for next change
- deltaEyePhi = eyePhi1 - eyePhi0;
- // find shortest perspective change
- if (deltaEyePhi < -M_PI)
- {
- deltaEyePhi += 2*M_PI;
- }
- }
-
- m_glArea->setEyePhi(eyePhi); // set new perspective
- }
-
- m_glArea->updateGL(); // repaint scenery
-
- // restarting timer not necessary here, it is a cyclic timer
-}
-
-// public slot of KPendulumSaver, forward resize event to public slot of glArea
-// to allow the resizing of the gl area withing the setup dialog
-void KPendulumSaver::resizeGlArea(QResizeEvent* e)
-{
- m_glArea->resize(e->size());
-}
-
-// public static class member variables
-
-const QColor KPendulumSaver::sm_barColorDefault(255, 255, 127);
-const QColor KPendulumSaver::sm_m1ColorDefault( 170, 0, 127);
-const QColor KPendulumSaver::sm_m2ColorDefault( 85, 170, 127);
-
-const double KPendulumSaver::sm_massRatioLimitLower = 0.01;
-const double KPendulumSaver::sm_massRatioLimitUpper = 0.99;
-const double KPendulumSaver::sm_massRatioDefault = 0.5;
-
-const double KPendulumSaver::sm_lengthRatioLimitLower = 0.01;
-const double KPendulumSaver::sm_lengthRatioLimitUpper = 0.99;
-const double KPendulumSaver::sm_lengthRatioDefault = 0.5;
-
-const double KPendulumSaver::sm_gLimitLower = 0.1;
-const double KPendulumSaver::sm_gLimitUpper = 300.0;
-const double KPendulumSaver::sm_gDefault = 40.0;
-
-const double KPendulumSaver::sm_ELimitLower = 0.0;
-const double KPendulumSaver::sm_ELimitUpper = 5.0;
-const double KPendulumSaver::sm_EDefault = 1.2;
-
-const unsigned int KPendulumSaver::sm_persChangeIntervalLimitLower = 5;
-const unsigned int KPendulumSaver::sm_persChangeIntervalLimitUpper = 600;
-const unsigned int KPendulumSaver::sm_persChangeIntervalDefault = 15;
-
-// private static class member variables
-
-const unsigned int KPendulumSaver::sm_deltaT = 20;
-const double KPendulumSaver::sm_eyePhiDefault = 0.25 * M_PI;
-
-//-----------------------------------------------------------------------------
-// KPendulumSetup: dialog to setup screen saver parameters
-//-----------------------------------------------------------------------------
-
-KPendulumSetup::KPendulumSetup(QWidget* parent)
- : KDialog(parent)
-{
-
- // the dialog should block, no other control center input should be possible
- // until the dialog is closed
- setModal(true);
- setCaption(i18n( "KPendulum Setup" ));
- setButtons(Ok|Cancel|Help);
- setDefaultButton(Ok);
- setButtonText( Help, i18n( "A&bout" ) );
- QWidget *main = new QWidget(this);
- setMainWidget(main);
- cfg = new PendulumWidget();
- cfg->setupUi( main );
-
- // create input validators
- cfg->m_mEdit->setValidator(
- new QDoubleValidator(
- KPendulumSaver::sm_massRatioLimitLower,
- KPendulumSaver::sm_massRatioLimitUpper,
- 5, cfg->m_mEdit));
- cfg->m_lEdit->setValidator(
- new QDoubleValidator(
- KPendulumSaver::sm_lengthRatioLimitLower,
- KPendulumSaver::sm_lengthRatioLimitUpper,
- 5, cfg->m_lEdit));
- cfg->m_gEdit->setValidator(
- new QDoubleValidator(
- KPendulumSaver::sm_gLimitLower,
- KPendulumSaver::sm_gLimitUpper,
- 5, cfg->m_gEdit));
- cfg->m_eEdit->setValidator(
- new QDoubleValidator(
- KPendulumSaver::sm_ELimitLower,
- KPendulumSaver::sm_ELimitUpper,
- 5, cfg->m_eEdit));
-
- // set input limits for the perspective change interval time
- cfg->m_persSpinBox->setMinimum(KPendulumSaver::sm_persChangeIntervalLimitLower);
- cfg->m_persSpinBox->setMaximum(KPendulumSaver::sm_persChangeIntervalLimitUpper);
-
- // set tool tips of editable fields
- cfg->m_mEdit->setToolTip(
- ki18n("Ratio of 2nd mass to sum of both masses.\nValid values from %1 to %2.")
- .subs(KPendulumSaver::sm_massRatioLimitLower, 0, 'f', 2)
- .subs(KPendulumSaver::sm_massRatioLimitUpper, 0, 'f', 2)
- .toString());
- cfg->m_lEdit->setToolTip(
- ki18n("Ratio of 2nd pendulum part length to the sum of both part lengths.\nValid values from %1 to %2.")
- .subs(KPendulumSaver::sm_lengthRatioLimitLower, 0, 'f', 2)
- .subs(KPendulumSaver::sm_lengthRatioLimitUpper, 0, 'f', 2)
- .toString());
- cfg->m_gEdit->setToolTip(
- ki18n("Gravitational constant in arbitrary units.\nValid values from %1 to %2.")
- .subs(KPendulumSaver::sm_gLimitLower, 0, 'f', 2)
- .subs(KPendulumSaver::sm_gLimitUpper, 0, 'f', 2)
- .toString());
- cfg->m_eEdit->setToolTip(
- ki18n("Energy in units of the maximum potential energy of the given configuration.\nValid values from %1 to %2.")
- .subs(KPendulumSaver::sm_ELimitLower, 0, 'f', 2)
- .subs(KPendulumSaver::sm_ELimitUpper, 0, 'f', 2)
- .toString());
- cfg->m_persSpinBox->setToolTip(
- ki18n("Time in seconds after which a random perspective change occurs.\nValid values from %1 to %2.")
- .subs(KPendulumSaver::sm_persChangeIntervalLimitLower)
- .subs(KPendulumSaver::sm_persChangeIntervalLimitUpper)
- .toString());
-
- // init preview area
- QPalette palette;
- palette.setColor(cfg->m_preview->backgroundRole(), Qt::black);
- cfg->m_preview->setPalette(palette);
- cfg->m_preview->setAutoFillBackground(true);
- cfg->m_preview->show(); // otherwise saver does not get correct size
-
- // create saver and give it the WinID of the preview area
- m_saver = new KPendulumSaver(cfg->m_preview->winId());
-
- // read settings from saver and update GUI elements with these values, saver
- // has read settings in its constructor
-
- // set editable fields with stored values as defaults
- QString text;
- text.setNum(m_saver->massRatio());
- cfg->m_mEdit->setText(text);
- text.setNum(m_saver->lengthRatio());
- cfg->m_lEdit->setText(text);
- text.setNum(m_saver->g());
- cfg->m_gEdit->setText(text);
- text.setNum(m_saver->E());
- cfg->m_eEdit->setText(text);
-
- cfg->m_persSpinBox->setValue(m_saver->persChangeInterval());
-
- palette.setColor(cfg->m_barColorButton->backgroundRole(), m_saver->barColor());
- cfg->m_barColorButton->setPalette(palette);
- palette.setColor(cfg->m_m1ColorButton->backgroundRole(), m_saver->m1Color());
- cfg->m_m1ColorButton->setPalette(palette);
- palette.setColor(cfg->m_m2ColorButton->backgroundRole(), m_saver->m2Color());
- cfg->m_m2ColorButton->setPalette(palette);
-
- // if the preview area is resized it emits the resized() event which is
- // caught by m_saver. The embedded GLArea is resized to fit into the preview
- // area.
- connect(cfg->m_preview, SIGNAL(resized(QResizeEvent*)),
- m_saver, SLOT(resizeGlArea(QResizeEvent*)));
-
- connect(this, SIGNAL(okClicked()), this, SLOT(okButtonClickedSlot()));
- connect(this, SIGNAL(helpClicked()), this, SLOT(aboutButtonClickedSlot()));
-
- connect(cfg->m_lEdit, SIGNAL(lostFocus()), this, SLOT(lEditLostFocusSlot()));
- connect(cfg->m_gEdit, SIGNAL(lostFocus()), this, SLOT(gEditLostFocusSlot()));
- connect(cfg->m_eEdit, SIGNAL(lostFocus()), this, SLOT(eEditLostFocusSlot()));
- connect(cfg->m_persSpinBox, SIGNAL(valueChanged(int)), this, SLOT(persChangeEnteredSlot(int)));
- connect(cfg->m_mEdit, SIGNAL(lostFocus()), this, SLOT(mEditLostFocusSlot()));
- connect(cfg->m_barColorButton, SIGNAL(clicked()), this, SLOT(barColorButtonClickedSlot()));
- connect(cfg->m_m1ColorButton, SIGNAL(clicked()), this, SLOT(m1ColorButtonClickedSlot()));
- connect(cfg->m_m2ColorButton, SIGNAL(clicked()), this, SLOT(m2ColorButtonClickedSlot()));
-}
-
-KPendulumSetup::~KPendulumSetup()
-{
- delete m_saver;
- delete cfg;
-}
-
-// Ok pressed - save settings and exit
-void KPendulumSetup::okButtonClickedSlot()
-{
- KConfigGroup config(KGlobal::config(), "Settings");
-
- config.writeEntry("mass ratio", m_saver->massRatio());
- config.writeEntry("length ratio", m_saver->lengthRatio());
- config.writeEntry("g", m_saver->g());
- config.writeEntry("E", m_saver->E());
- config.writeEntry("perspective change interval",
- m_saver->persChangeInterval());
- config.writeEntry("bar color", m_saver->barColor());
- config.writeEntry("m1 color", m_saver->m1Color());
- config.writeEntry("m2 color", m_saver->m2Color());
-
- config.sync();
- accept();
-}
-
-void KPendulumSetup::aboutButtonClickedSlot()
-{
- KMessageBox::about(this, i18n("\
-<h3>KPendulum Screen Saver for KDE</h3>\
-<p>Simulation of a two-part pendulum</p>\
-<p>Copyright (c) Georg Drenkhahn 2004</p>\
-<p><tt>Georg.Drenkhahn@gmx.net</tt></p>"));
-}
-
-void KPendulumSetup::mEditLostFocusSlot(void)
-{
- if (cfg->m_mEdit->hasAcceptableInput())
- {
- m_saver->setMassRatio(cfg->m_mEdit->text().toDouble());
- }
- else
- { // write current setting back into input field
- QString text;
- text.setNum(m_saver->massRatio());
- cfg->m_mEdit->setText(text);
- }
-}
-void KPendulumSetup::lEditLostFocusSlot(void)
-{
- if (cfg->m_lEdit->hasAcceptableInput())
- {
- m_saver->setLengthRatio(cfg->m_lEdit->text().toDouble());
- }
- else
- { // write current setting back into input field
- QString text;
- text.setNum(m_saver->lengthRatio());
- cfg->m_lEdit->setText(text);
- }
-}
-void KPendulumSetup::gEditLostFocusSlot(void)
-{
- if (cfg->m_gEdit->hasAcceptableInput())
- {
- m_saver->setG(cfg->m_gEdit->text().toDouble());
- }
- else
- { // write current setting back into input field
- QString text;
- text.setNum(m_saver->g());
- cfg->m_gEdit->setText(text);
- }
-}
-void KPendulumSetup::eEditLostFocusSlot(void)
-{
- if (cfg->m_eEdit->hasAcceptableInput())
- {
- m_saver->setE(cfg->m_eEdit->text().toDouble());
- }
- else
- { // write current setting back into input field
- QString text;
- text.setNum(m_saver->E());
- cfg->m_eEdit->setText(text);
- }
-}
-void KPendulumSetup::persChangeEnteredSlot(int t)
-{
- m_saver->setPersChangeInterval(t);
-}
-
-void KPendulumSetup::barColorButtonClickedSlot(void)
-{
- QColor color = m_saver->barColor();
- if ( KColorDialog::getColor( color, this) )
- {
- if (color.isValid())
- {
- m_saver->setBarColor(color);
- QPalette palette;
- palette.setColor(cfg->m_barColorButton->backgroundRole(), color);
- cfg->m_barColorButton->setPalette(palette);
- }
- }
-}
-
-void KPendulumSetup::m1ColorButtonClickedSlot(void)
-{
- QColor color =m_saver->m1Color();
- if ( KColorDialog::getColor( color, this) )
- {
- if (color.isValid())
- {
- m_saver->setM1Color(color);
- QPalette palette;
- palette.setColor(cfg->m_m1ColorButton->backgroundRole(), color);
- cfg->m_m1ColorButton->setPalette(palette);
- }
- }
-}
-void KPendulumSetup::m2ColorButtonClickedSlot(void)
-{
- QColor color = m_saver->m2Color();
- if ( KColorDialog::getColor( color, this) )
- {
- if (color.isValid())
- {
- m_saver->setM2Color(color);
- QPalette palette;
- palette.setColor(cfg->m_m2ColorButton->backgroundRole(), color);
- cfg->m_m2ColorButton->setPalette(palette);
- }
- }
-}
+++ /dev/null
-/*============================================================================
- *
- * KPendulum screen saver for KDE
- *
- * The screen saver displays a physically realistic simulation of a two-part
- * pendulum.
- *
- * Copyright (C) 2004 Georg Drenkhahn, Georg.Drenkhahn@gmx.net
- *
- * 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) version 3 or
- * any later version accepted by the membership of KDE e.V. (or its successor
- * approved by the membership of KDE e.V.), which shall act as a proxy defined
- * in Section 14 of version 3 of the license.
- *
- * 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.
- *
- *============================================================================*/
-
-#ifndef PENDULUM_H
-#define PENDULUM_H
-
-// STL headers
-#include <valarray>
-
-// Qt headers
-#include <QWidget>
-#include <QTimer>
-#include <QGL>
-
-// GL headers
-#include <GL/glu.h>
-#include <GL/gl.h>
-
-// KDE headers
-#include <kscreensaver.h>
-#include <kdialog.h>
-
-#include "rkodesolver.h"
-
-// KPendulumSetupUi
-#include "ui_pendulumcfg.h"
-
-//--------------------------------------------------------------------
-
-/** @brief ODE solver for the Pendulum equations */
-class PendulumOdeSolver : public RkOdeSolver<double,4>
-{
- public:
- /** @brief Constuctor for the RK solver of the pendulum equation of motion
- * @param t initial time in seconds
- * @param dt initial time increment in seconds, just a hint for solver
- * @param y generalized coordinates of pendulum system
- * @param eps relative precision
- * @param m1 mass of upper pendulum
- * @param m2 mass of lower pendulum
- * @param l1 length of upper pendulum
- * @param l2 length of lower pendulum
- * @param g gravitational constant */
- PendulumOdeSolver(
- const double& t,
- const double& dt,
- const Vector4d& y,
- const double& eps,
- const double& m1,
- const double& m2,
- const double& l1,
- const double& l2,
- const double& g);
-
- protected:
- /** @brief ODE function for the pendulum equation of motion system
- * @param x time
- * @param y generalized coordinates of pendulum system
- * @return derivation dy/dx */
- Vector4d f(const double& x, const Vector4d& y) const;
-
- private:
- /** These private variables contain constants for faster numeric calculation.
- * They are derived from the constructor arguments m1,m2,l1,l2,g. */
- const double m_A, m_B1, m_B, m_C, m_D, m_E, m_M;
-};
-
-
-//--------------------------------------------------------------------
-
-/** @brief GL widget class for the KPendulum screen saver
- *
- * Class implements QGLWidget to display the KPendulum screen saver. */
-class PendulumGLWidget : public QGLWidget
-{
- Q_OBJECT
-
- public:
- /** @brief Constructor of KPendulum's GL widget
- * @param parent parent widget, passed to QGLWidget's constructor */
- PendulumGLWidget(QWidget* parent=0);
- /** @brief Destructor of KPendulum's GL widget */
- ~PendulumGLWidget(void);
-
- /** @brief Set phi angle of viewpoint
- * @param phi angle in sterad */
- void setEyePhi(double phi);
- /** @brief Set angles of pendulum configuration
- * @param q1 angle of 1. pendulum in sterad
- * @param q2 angle of 2. pendulum in sterad */
- void setAngles(const double& q1, const double& q2);
- /** @brief Set masses of pendulum configuration
- * @param m1 mass of 1. pendulum
- * @param m2 mass of 2. pendulum */
- void setMasses(const double& m1, const double& m2);
- /** @brief Set lengths of pendulum configuration
- * @param l1 length of 1. pendulum
- * @param l2 length of 2. pendulum */
- void setLengths(const double& l1, const double& l2);
-
- /* accessors for colour settings */
-
- /** @brief set color of the bars
- * @param c color */
- void setBarColor(const QColor& c);
- /** @brief get color of the bars
- * @return color */
- inline QColor barColor(void) const {return m_barColor;}
- /** @brief set color of mass 1
- * @param c color */
- void setM1Color(const QColor& c);
- /** @brief get color of mass 1
- * @return color */
- inline QColor m1Color(void) const {return m_m1Color;}
- /** @brief set color of mass 2
- * @param c color */
- void setM2Color(const QColor& c);
- /** @brief get color of mass 2
- * @return color */
- inline QColor m2Color(void) const {return m_m2Color;}
-
- protected:
- /** paint the GL view */
- virtual void paintGL();
- /** resize the gl view */
- virtual void resizeGL(int w, int h);
- /** setup the GL environment */
- virtual void initializeGL();
-
- private: // Private attributes
- /** Eye position distance from coordinate zero point */
- GLfloat m_eyeR;
- /** Eye position theta angle from z axis in sterad */
- double m_eyeTheta;
- /** Eye position phi angle (longitude) in sterad */
- double m_eyePhi;
- /** Light position distance from coordinate zero point */
- GLfloat m_lightR;
- /** Light position theta angle from z axis in sterad */
- double m_lightTheta;
- /** Light position phi angle (longitude) in sterad */
- double m_lightPhi;
-
- /** 1. pendulum's angle, degree */
- GLfloat m_ang1;
- /** 2. pendulum's angle, degree */
- GLfloat m_ang2;
-
- /** 1. pendulum's square root of mass */
- GLfloat m_sqrtm1;
- /** 2. pendulum's square root of mass */
- GLfloat m_sqrtm2;
-
- /** 1. pendulum's length */
- GLfloat m_l1;
- /** 2. pendulum's length */
- GLfloat m_l2;
-
- /** Pointer to a quadric object used in the rendering function paintGL() */
- GLUquadricObj* const m_quadM1;
-
- /** color of the pendulum bars */
- QColor m_barColor;
- /** color of the 1. mass */
- QColor m_m1Color;
- /** color of the 2. mass */
- QColor m_m2Color;
-};
-
-//--------------------------------------------------------------------
-
-/** @brief Main class of the KPendulum screen saver
- *
- * This class implements KScreenSaver for the KPendulum screen saver. */
-class KPendulumSaver : public KScreenSaver
-{
- Q_OBJECT
-
- public:
- /** @brief Constructor of the KPendulum screen saver object
- * @param drawable Id of the window in which the screen saver is drawed
- *
- * Initial settings are read from disk, the GL widget is set up and displayed
- * and the eq. of motion solver is started. */
- KPendulumSaver(WId drawable);
- /** @brief Destructor of the KPendulum screen saver object
- *
- * Only KPendulumSaver::solver is destoyed. */
- ~KPendulumSaver();
-
- /** read the saved settings from disk */
- void readSettings();
- /** init physical quantities, set up the GL area and (re)start the ode
- * solver. Called if new parameters are specified in the setup dialog and at
- * startup. */
- void initData();
-
- /* accessors for PendulumGLWidget member variables */
-
- /** Set the displayed bar color of the pendulum */
- void setBarColor(const QColor& c);
- /** Get the displayed bar color of the pendulum */
- QColor barColor(void) const;
-
- /** Set the displayed color of the 1. pendulum mass */
- void setM1Color(const QColor& c);
- /** Get the displayed color of the 1. pendulum mass */
- QColor m1Color(void) const;
-
- /** Set the displayed color of the 2. pendulum mass */
- void setM2Color(const QColor& c);
- /** Get the displayed color of the 2. pendulum mass */
- QColor m2Color(void) const;
-
- /* accessors for own member variables */
-
- /** Set the mass ratio of the pendulum system. @sa
- * KPendulumSaver::m_massRatio */
- void setMassRatio(const double& massRatio);
- /** Get the mass ratio of the pendulum system. @sa
- * KPendulumSaver::m_massRatio */
- inline double massRatio(void) const
- {
- return m_massRatio;
- }
-
- /** Set the length ratio of the pendulum system. @sa
- * KPendulumSaver::m_lengthRatio */
- void setLengthRatio(const double& lengthRatio);
- /** Get the length ratio of the pendulum system. @sa
- * KPendulumSaver::m_lengthRatio */
- inline double lengthRatio(void) const
- {
- return m_lengthRatio;
- }
-
- /** Set the gravitational constant. @sa KPendulumSaver::m_g */
- void setG(const double& g);
- /** Get the gravitational constant. @sa KPendulumSaver::m_g */
- inline double g(void) const
- {
- return m_g;
- }
-
- /** Set the total energy. @sa KPendulumSaver::m_E */
- void setE(const double& E);
- /** Get the total energy. @sa KPendulumSaver::m_E */
- inline double E(void) const
- {
- return m_E;
- }
-
- /** Set the time interval for the periodic perspective change. @sa
- * KPendulumSaver::m_persChangeInterval */
- void setPersChangeInterval(const unsigned int& persChangeInterval);
- /** Get the time interval for the periodic perspective change. @sa
- * KPendulumSaver::m_persChangeInterval */
- inline unsigned int persChangeInterval(void) const
- {
- return m_persChangeInterval;
- }
-
- /* public static class member variables */
-
- static const QColor sm_barColorDefault;
- static const QColor sm_m1ColorDefault;
- static const QColor sm_m2ColorDefault;
-
- /** lower, upper limits (inclusive) and default values for the setup
- * parameter massRatio */
- static const double sm_massRatioLimitUpper;
- static const double sm_massRatioLimitLower;
- static const double sm_massRatioDefault;
-
- /** lower, upper limits (inclusive) and default values for the setup
- * parameter lengthRatio */
- static const double sm_lengthRatioLimitLower;
- static const double sm_lengthRatioLimitUpper;
- static const double sm_lengthRatioDefault;
-
- /** lower, upper limits (inclusive) and default values for the setup
- * parameter g */
- static const double sm_gLimitLower;
- static const double sm_gLimitUpper;
- static const double sm_gDefault;
-
- /** lower, upper limits (inclusive) and default values for the setup
- * parameter E */
- static const double sm_ELimitLower;
- static const double sm_ELimitUpper;
- static const double sm_EDefault;
-
- /** lower, upper limits (inclusive) and default values for the setup
- * parameter persChangeInterval */
- static const unsigned int sm_persChangeIntervalLimitLower;
- static const unsigned int sm_persChangeIntervalLimitUpper;
- static const unsigned int sm_persChangeIntervalDefault;
-
- public slots:
- /** slot is called if integration should proceed by ::deltaT */
- void doTimeStep();
- /** slot is called if setup dialog changes in size and the GL are should be
- * adjusted */
- void resizeGlArea(QResizeEvent* e);
-
- private:
- /** Time step size for the integration in milliseconds. 20 ms corresponds to
- * a frame rate of 50 fps. */
- static const unsigned int sm_deltaT;
- /** Default angle phi for the eye to look onto the pendulum */
- static const double sm_eyePhiDefault;
-
- /** The ode solver which is used to integrate the equations of motion */
- PendulumOdeSolver* m_solver;
- /** Gl widget of simulation */
- PendulumGLWidget* m_glArea;
- /** Timer for the real time integration of the eqs. of motion */
- QTimer* m_timer;
-
- // persistent configurtion settings
-
- /** Mass ratio m2/(m1+m2) of the pendulum masses. Value is determined by the
- * setup dialog. Variable is accessed by setMassRatio() and massRatio(). */
- double m_massRatio;
- /** Length ratio l2/(l1+l2) of the pendulums. Value is determined by the
- * setup dialog. Variable is accessed by setLengthRatio() and
- * lengthRatio(). */
- double m_lengthRatio;
- /** Gravitational constant (in arbitrary units). Value is determined by the
- * setup dialog. Variable is accessed by setG() and g(). */
- double m_g;
- /** Total energy of the system in units of the maximum possible potential
- * energy. Value is determined by the setup dialog. Variable is accessed by
- * setE() and E(). */
- double m_E;
- /** Time interval after which a new perspective changed happens. Value is
- * determined by the setup dialog. Variable is accessed by
- * setPersChangeInterval() and persChangeInterval(). */
- unsigned int m_persChangeInterval;
-};
-
-//--------------------------------------------------------------------
-
-class PendulumWidget : public QWidget, public Ui::PendulumWidget
-{
-public:
- PendulumWidget( QWidget *parent = 0L ) : QWidget( parent ) {
- setupUi( this );
- }
-};
-
-/** @brief KPendulum screen saver setup dialog.
- *
- * This class handles the KPendulum screen saver setup dialog. */
-class KPendulumSetup : public KDialog
-{
- Q_OBJECT
-
- public:
- /** @brief Constructor for the KPendulum screen saver setup dialog
- * @param parent Pointer to the parent widget, passed to KPendulumSetupUi
- *
- * The dialog box is set up and the screen saver object KPendulumSetup::saver
- * is instantiated. */
- KPendulumSetup(QWidget* parent = 0);
- /** @brief Destructor of the KPendulum screen saver setup dialog
- *
- * Only KPendulumSetup::saver is deleted. */
- ~KPendulumSetup(void);
-
- public slots:
- /** slot for the "OK" button: save settings and exit */
- void okButtonClickedSlot(void);
- /** slot for the "About" button: show the About dialog */
- void aboutButtonClickedSlot(void);
-
- /** slot is called if the mass ratio edit field looses its focus. If the
- * input is acceptable KPendulumSaver::setMassRatio() is called. */
- void mEditLostFocusSlot(void);
- /** slot is called if the length ratio edit field looses its focus. If the
- * input is acceptable KPendulumSaver::setLengthRatio() is called. */
- void lEditLostFocusSlot(void);
- /** slot is called if the gravitational constant edit field looses its focus.
- * If the input is acceptable KPendulumSaver::setG() is called. */
- void gEditLostFocusSlot(void);
- /** slot is called if the energy edit field looses its focus. If the input
- * is acceptable KPendulumSaver::setE() is called. */
- void eEditLostFocusSlot(void);
- /** slot is called if the perspective change interval spin box changed. If
- * the input is acceptable KPendulumSaver::setPersChangeInterval() is
- * called. */
- void persChangeEnteredSlot(int t);
-
- /** slot is called if the bar color button was clicked. A color dialog is
- * opened and the result is given to KPendulumSaver::setBarColor(). */
- void barColorButtonClickedSlot(void);
- /** slot is called if the mass 1 color button was clicked. A color dialog is
- * opened and the result is given to KPendulumSaver::setM1Color(). */
- void m1ColorButtonClickedSlot(void);
- /** slot is called if the mass 2 color button was clicked. A color dialog is
- * opened and the result is given to KPendulumSaver::setM2Color(). */
- void m2ColorButtonClickedSlot(void);
-
- private:
- /** Pointer to the screen saver object. Its member KPendulumSaver::glArea is
- * displayed in the preview area */
- KPendulumSaver* m_saver;
- PendulumWidget *cfg;
-};
-
-#endif
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>PendulumWidget</class>
- <widget class="QWidget" name="PendulumWidget">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>464</width>
- <height>384</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
- <horstretch>2</horstretch>
- <verstretch>2</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>1200</width>
- <height>900</height>
- </size>
- </property>
- <property name="baseSize">
- <size>
- <width>400</width>
- <height>250</height>
- </size>
- </property>
- <layout class="QVBoxLayout">
- <property name="sizeConstraint">
- <enum>QLayout::SetMinimumSize</enum>
- </property>
- <item>
- <layout class="QHBoxLayout">
- <item>
- <layout class="QVBoxLayout">
- <item>
- <layout class="QHBoxLayout">
- <item>
- <widget class="QLabel" name="textLabel1">
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Plain</enum>
- </property>
- <property name="text">
- <string><p align="center">
-m<sub>2</sub><br>
------------<br>
-m<sub>1</sub>+m<sub>2</sub>
-</p></string>
- </property>
- <property name="wordWrap">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLineEdit" name="m_mEdit">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximumSize">
- <size>
- <width>60</width>
- <height>32767</height>
- </size>
- </property>
- <property name="maxLength">
- <number>5</number>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout">
- <item>
- <widget class="QLabel" name="textLabel2">
- <property name="text">
- <string><p align="center">
-l<sub>2</sub><br>
-------<br>
-l<sub>1</sub>+l<sub>2</sub>
-</p></string>
- </property>
- <property name="wordWrap">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLineEdit" name="m_lEdit">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximumSize">
- <size>
- <width>60</width>
- <height>32767</height>
- </size>
- </property>
- <property name="maxLength">
- <number>5</number>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout">
- <item>
- <widget class="QLabel" name="textLabel3">
- <property name="text">
- <string>g</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <property name="wordWrap">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLineEdit" name="m_gEdit">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximumSize">
- <size>
- <width>60</width>
- <height>32767</height>
- </size>
- </property>
- <property name="maxLength">
- <number>5</number>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout">
- <item>
- <widget class="QLabel" name="textLabel4">
- <property name="text">
- <string>E</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <property name="wordWrap">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLineEdit" name="m_eEdit">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximumSize">
- <size>
- <width>60</width>
- <height>32767</height>
- </size>
- </property>
- <property name="maxLength">
- <number>5</number>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout">
- <item>
- <widget class="QLabel" name="textLabel2_2">
- <property name="toolTip">
- <string>specify the time in seconds after which a random perspective change occurs</string>
- </property>
- <property name="text">
- <string>Perspective<br>
-Change [s]</string>
- </property>
- <property name="wordWrap">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="KIntSpinBox" name="m_persSpinBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>60</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>60</width>
- <height>32767</height>
- </size>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout">
- <item>
- <widget class="QPushButton" name="m_barColorButton">
- <property name="maximumSize">
- <size>
- <width>40</width>
- <height>32767</height>
- </size>
- </property>
- <property name="text">
- <string>Bars</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="m_m1ColorButton">
- <property name="maximumSize">
- <size>
- <width>40</width>
- <height>32767</height>
- </size>
- </property>
- <property name="text">
- <string>M1</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="m_m2ColorButton">
- <property name="maximumSize">
- <size>
- <width>40</width>
- <height>32767</height>
- </size>
- </property>
- <property name="text">
- <string>M2</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <spacer name="spacer4">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::MinimumExpanding</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </item>
- <item>
- <widget class="SsPreviewArea" name="m_preview" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>2</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>260</width>
- <height>260</height>
- </size>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- <customwidgets>
- <customwidget>
- <class>KIntSpinBox</class>
- <extends>QSpinBox</extends>
- <header>knuminput.h</header>
- </customwidget>
- <customwidget>
- <class>SsPreviewArea</class>
- <extends>QWidget</extends>
- <header location="global">sspreviewarea.h</header>
- </customwidget>
- </customwidgets>
- <includes>
- <include location="local">sspreviewarea.h</include>
- </includes>
- <resources/>
- <connections/>
-</ui>
+++ /dev/null
-/** @file
- *
- * Ordinary differential equation solver using the Runge-Kutta method.
- *
- * Copyright (C) 2004 Georg Drenkhahn, Georg.Drenkhahn@gmx.net
- *
- * 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) version 3 or
- * any later version accepted by the membership of KDE e.V. (or its successor
- * approved by the membership of KDE e.V.), which shall act as a proxy defined
- * in Section 14 of version 3 of the license.
- *
- * 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.
- */
-
-#ifndef RKODESOLVER_H
-#define RKODESOLVER_H
-
-#include <KDebug>
-
-/* vector and matrix classes */
-#include <Eigen/Core>
-/* import most common Eigen types */
-using namespace Eigen;
-
-
-/** @brief Solver class to integrate a first-order ordinary differential
- * equation (ODE) by means of a 6. order Runge-Kutta method.
- *
- * See the article about the Cash-Karp method
- * (http://en.wikipedia.org/wiki/Cash%E2%80%93Karp_method) for details on this
- * algorithm.
- *
- * The ODE system must be given as the derivative
- * dy/dx = f(x,y)
- * with x in R and y in R^n.
- *
- * Within this class the function f() is a pure virtual function, which must be
- * reimplemented in a derived class. */
-template<typename T, int D>
-class RkOdeSolver
-{
- public:
- EIGEN_MAKE_ALIGNED_OPERATOR_NEW
-
- /** @brief Constructor
- * @param x Initial integration parameter
- * @param y Initial function values of function to integrate
- * @param dx Initial guess for step size. Will be automatically adjusted to
- * guarantee required precision. @a dx > 0
- * @param eps Relative precision. @a eps > 0.
- *
- * Initialises the solver with start conditions. */
- RkOdeSolver(
- const T& x,// = 0.0,
- const Matrix<T,D,1>& y,// = Matrix<T,D,1>::Zero(),
- const T& dx = 0.0,
- const T& eps = 1e-6);
-
- /** @brief Destructor */
- virtual ~RkOdeSolver(void);
-
- /** @brief Integrates the ordinary differential equation from the current x
- * value to x+@a dx.
- * @param dx x-interval size to integrate over starting from x. dx may be
- * negative.
- *
- * The integration is performed by calling rkStepCheck() repeatedly until the
- * desired x value is reached. */
- void integrate(const T& dx);
-
- // Accessors
-
- /** @brief Get current x value.
- * @return Reference of x value. */
- const T& X(void) const;
- /** @brief Set current x value.
- * @param a The value to be set. */
- void X(const T& a);
-
- /** @brief Get current y value.
- * @return Reference of y vector. */
- const Matrix<T,D,1>& Y(void) const;
- /** @brief Set current y values.
- * @param a The vector to be set. */
- void Y(const Matrix<T,D,1>& a);
-
- /** @brief Get current dy/dx value.
- * @return Reference of dy/dx vector. */
- const Matrix<T,D,1>& dYdX(void) const;
-
- /** @brief Get current estimated step size dX.
- * @return Reference of dX value. */
- const T& dX(void) const;
- /** @brief Set estimated step size dX.
- * @param a The value to be set. */
- void dX(const T& a);
-
- /** @brief Get current presision.
- * @return Reference of precision value. */
- const T& Eps(void) const;
- /** @brief Set estimated presision.
- * @param a The value to be set. */
- void Eps(const T& a);
-
- protected:
- /** @brief ODE function
- * @param x Integration value
- * @param y Function value
- * @return Derivation
- *
- * This purely virtual function returns the value of dy/dx for the given
- * parameter values of x and y. This function is integrated by the
- * Runge-Kutta algorithm. */
- virtual Matrix<T,D,1>
- f(const T& x, const Matrix<T,D,1>& y) const = 0;
-
- private:
- /** @brief Perform one integration step with a tolerable relative error given
- * by ::mErr.
- * @param dx Maximal step size, may be positive or negative depending on
- * integration direction.
- * @return Flag indicating if made absolute integration step was equal |@a dx
- * | (true) less than |@a dx | (false).
- *
- * A new estimate for the maximum next step size is saved to ::m_step. The
- * new values for x, y and f are saved in ::m_x, ::m_y and ::m_dydx. */
- bool rkStepCheck(const T& dx);
-
- /** @brief Perform one Runge-Kutta integration step forward with step size
- * ::m_step
- * @param dx Step size relative to current x value.
- * @param yerr Reference to vector in which the estimated error made in y is
- * returned.
- * @return The y value after the step at x+@a dx.
- *
- * Stored current x,y values are not adjusted. */
- Matrix<T,D,1> rkStep(
- const T& dx, Matrix<T,D,1>& yerr) const;
-
- /** current x value */
- T m_x;
- /** current y value */
- Matrix<T,D,1> m_y;
- /** current value of dy/dx */
- Matrix<T,D,1> m_dydx;
-
- /** allowed relative error */
- T m_eps;
- /** estimated step size for next Runge-Kutta step */
- T m_step;
-};
-
-// inline accessors
-
-template<typename T, int D>
-inline const T&
-RkOdeSolver<T, D>::X(void) const
-{
- return m_x;
-}
-
-template<typename T, int D>
-inline void
-RkOdeSolver<T, D>::X(const T &a)
-{
- m_x = a;
-}
-
-template<typename T, int D>
-inline const Matrix<T,D,1>&
-RkOdeSolver<T, D>::Y(void) const
-{
- return m_y;
-}
-
-template<typename T, int D>
-inline void
-RkOdeSolver<T, D>::Y(const Matrix<T,D,1>& a)
-{
- m_y = a;
-}
-
-template<typename T, int D>
-inline const Matrix<T,D,1>&
-RkOdeSolver<T, D>::dYdX(void) const
-{
- return m_dydx;
-}
-
-template<typename T, int D>
-inline const T&
-RkOdeSolver<T, D>::dX(void) const
-{
- return m_step;
-}
-
-template<typename T, int D>
-inline const T&
-RkOdeSolver<T, D>::Eps(void) const
-{
- return m_eps;
-}
-
-
-template<typename T, int D>
-RkOdeSolver<T, D>::RkOdeSolver(
- const T& x,
- const Matrix<T,D,1>& y,
- const T& dx,
- const T& eps)
- : m_x(x)
-{
- Y(y);
- dX(dx);
- Eps(eps);
-}
-
-// virtual dtor
-template<typename T, int D>
-RkOdeSolver<T, D>::~RkOdeSolver(void)
-{
-}
-
-// accessors
-
-template<typename T, int D>
-void
-RkOdeSolver<T, D>::dX(const T& a)
-{
- if (a <= 0.0)
- {
- kError() << "RkOdeSolver: dx must be > 0";
- m_step = 0.001; // a very arbitrary value
- return;
- }
-
- m_step = a;
-}
-
-template<typename T, int D>
-void
-RkOdeSolver<T, D>::Eps(const T& a)
-{
- if (a <= 0.0)
- {
- kError() << "RkOdeSolver: eps must be > 0";
- m_eps = 1e-5; // a very arbitrary value
- return;
- }
-
- m_eps = a;
-}
-
-// public member functions
-
-template<typename T, int D>
-void
-RkOdeSolver<T, D>::integrate(const T& deltaX)
-{
- if (deltaX == 0)
- {
- return; // nothing to integrate
- }
-
- // init dydx
- m_dydx = f(m_x, m_y);
-
- static const unsigned int maxiter = 10000;
- const T x2 = m_x + deltaX;
-
- unsigned int iter;
- for (iter=0;
- iter<maxiter && !rkStepCheck(x2-m_x);
- ++iter)
- {
- }
-
- if (iter > maxiter)
- {
- kWarning()
- << "RkOdeSolver: More than " << maxiter
- << " iterations in RkOdeSolver::integrate" << endl;
- }
-}
-
-
-// private member functions
-
-template<typename T, int D>
-bool
-RkOdeSolver<T, D>::rkStepCheck(const T& dx_requested)
-{
- static const T safety = 0.9;
- static const T pshrnk = -0.25;
- static const T pgrow = -0.2;
-
- // reduce step size by no more than a factor 10
- static const T shrinkLimit = 0.1;
- // enlarge step size by no more than a factor 5
- static const T growthLimit = 5.0;
- // errmax_sl = 6561.0
- static const T errmax_sl = std::pow(shrinkLimit/safety, 1.0/pshrnk);
- // errmax_gl = 1.89e-4
- static const T errmax_gl = std::pow(growthLimit/safety, 1.0/pgrow);
-
- static const unsigned int maxiter = 100;
-
- if (dx_requested == 0)
- {
- return true; // integration done
- }
-
- Matrix<T,D,1> ytmp, yerr, t;
-
- bool stepSizeWasMaximal;
- T dx;
- if (std::abs(dx_requested) > m_step)
- {
- stepSizeWasMaximal = true;
- dx = dx_requested>0 ? m_step : -m_step;
- }
- else
- {
- stepSizeWasMaximal = false;
- dx = dx_requested;
- }
-
- // generic scaling factor
- // |y| + |dx * dy/dx| + 1e-15
- Matrix<T,D,1> yscal
- = (m_y.array().abs() + (dx*m_dydx).array().abs()).array()
- + 1e-15;
-
- unsigned int iter = 0;
- T errmax = 0;
- do
- {
- if (errmax >= 1.0)
- {
- // reduce step size
- dx *= errmax<errmax_sl ? safety * pow(errmax, pshrnk) : shrinkLimit;
- stepSizeWasMaximal = true;
- if (m_x == m_x + dx)
- {
- // stepsize below numerical resolution
- kError()
- << "RkOdeSolver: stepsize underflow in rkStepCheck"
- << endl;
- }
- // new dx -> update scaling vector
- yscal
- = (m_y.array().abs()
- + (dx*m_dydx).array().abs()).array()
- + 1e-15;
- }
-
- ytmp = rkStep(dx, yerr); // try to make a step forward
- t = (yerr.array() / yscal.array()).abs(); // calc the error vector
- errmax = t.maxCoeff()/m_eps; // calc the rel. maximal error
- ++iter;
- } while ((iter < maxiter) && (errmax >= 1.0));
-
- if (iter >= maxiter)
- {
- kError()
- << "RkOdeSolver: too many iterations in rkStepCheck()";
- }
-
- if (stepSizeWasMaximal)
- {
- // estimate next step size if used step size was maximal
- m_step =
- std::abs(dx)
- * (errmax>errmax_gl ? safety * pow(errmax, pgrow) : growthLimit);
- }
- m_x += dx; // make step forward
- m_y = ytmp; // save new function values
- m_dydx = f(m_x,m_y); // and update derivatives
-
- return (std::abs(dx) < std::abs(dx_requested));
-}
-
-template<typename T, int D>
-Matrix<T,D,1>
-RkOdeSolver<T, D>::rkStep(const T& dx, Matrix<T,D,1>& yerr) const
-{
- static const T
- a2=0.2, a3=0.3, a4=0.6, a5=1.0, a6=0.875,
- b21=0.2,
- b31=3.0/40.0, b32=9.0/40.0,
- b41=0.3, b42=-0.9, b43=1.2,
- b51=-11.0/54.0, b52=2.5, b53=-70.0/27.0, b54=35.0/27.0,
- b61=1631.0/55296.0, b62=175.0/512.0, b63=575.0/13824.0,
- b64=44275.0/110592.0, b65=253.0/4096.0,
- c1=37.0/378.0, c3=250.0/621.0, c4=125.0/594.0, c6=512.0/1771.0,
- dc1=c1-2825.0/27648.0, dc3=c3-18575.0/48384.0,
- dc4=c4-13525.0/55296.0, dc5=-277.0/14336.0, dc6=c6-0.25;
-
- Matrix<T,D,1> ak2 = f(m_x + a2*dx,
- m_y + dx*b21*m_dydx); // 2. step
- Matrix<T,D,1> ak3 = f(m_x + a3*dx,
- m_y + dx*(b31*m_dydx + b32*ak2)); // 3.step
- Matrix<T,D,1> ak4 = f(m_x + a4*dx,
- m_y + dx*(b41*m_dydx + b42*ak2
- + b43*ak3)); // 4.step
- Matrix<T,D,1> ak5 = f(m_x + a5*dx,
- m_y + dx*(b51*m_dydx + b52*ak2
- + b53*ak3 + b54*ak4)); // 5.step
- Matrix<T,D,1> ak6 = f(m_x + a6*dx,
- m_y + dx*(b61*m_dydx + b62*ak2
- + b63*ak3 + b64*ak4
- + b65*ak5)); // 6.step
- yerr = dx*(dc1*m_dydx + dc3*ak3 + dc4*ak4 + dc5*ak5 + dc6*ak6);
- return m_y + dx*( c1*m_dydx + c3*ak3 + c4*ak4 + c6*ak6);
-}
-
-#endif