diff options
Diffstat (limited to 'kscreensaver/kdesavers/lorenz.cpp')
-rw-r--r-- | kscreensaver/kdesavers/lorenz.cpp | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/kscreensaver/kdesavers/lorenz.cpp b/kscreensaver/kdesavers/lorenz.cpp new file mode 100644 index 00000000..c9017bb0 --- /dev/null +++ b/kscreensaver/kdesavers/lorenz.cpp @@ -0,0 +1,569 @@ +//----------------------------------------------------------------------------- +// +// Lorenz - Lorenz Attractor screen saver +// Nicolas Brodu, brodu@kde.org, 2000 +// +// Portions of code from kblankscrn and khop. +// See authors there. +// +// I release my code as GPL, but see the other headers and the README + +#include <math.h> +#include <stdlib.h> + +#include <qpainter.h> +#include <qslider.h> +#include <qlayout.h> +#include <qcolor.h> +#include <qlabel.h> + +#include <kapplication.h> +#include <klocale.h> +#include <kglobal.h> +#include <kconfig.h> +#include <kmessagebox.h> + +#include "lorenz.h" +#include "lorenz.moc" + +// libkscreensaver interface +extern "C" +{ + KDE_EXPORT const char *kss_applicationName = "klorenz.kss"; + KDE_EXPORT const char *kss_description = I18N_NOOP( "KLorenz" ); + KDE_EXPORT const char *kss_version = "2.2.0"; + + KDE_EXPORT KScreenSaver *kss_create( WId id ) + { + return new KLorenzSaver( id ); + } + + KDE_EXPORT QDialog *kss_setup() + { + return new KLorenzSetup(); + } +} + +#define MINSPEED 1 +#define MAXSPEED 1500 +#define DEFSPEED 150 +#define MINZROT -180 +#define MAXZROT 180 +#define DEFZROT 104 //100 +#define MINYROT -180 +#define MAXYROT 180 +#define DEFYROT -19 //80 +#define MINXROT -180 +#define MAXXROT 180 +#define DEFXROT 25 //20 +#define MINEPOCH 1 +#define MAXEPOCH 30000 +#define DEFEPOCH 5800 +#define MINCOLOR 1 +#define MAXCOLOR 100 +#define DEFCOLOR 20 + +//----------------------------------------------------------------------------- +// dialog to setup screen saver parameters +// +KLorenzSetup::KLorenzSetup( QWidget *parent, const char *name ) + : KDialogBase( parent, name, true, i18n( "Setup Lorenz Attractor" ), + Ok|Cancel|Default|Help, Ok, true ) +{ + readSettings(); + + setButtonText( Help, i18n( "A&bout" ) ); + QWidget *main = makeMainWidget(); + + QHBoxLayout *tl = new QHBoxLayout( main, 0, spacingHint() ); + QVBoxLayout *tl1 = new QVBoxLayout; + tl->addLayout(tl1); + + QLabel *label = new QLabel( i18n("Speed:"), main ); + tl1->addWidget(label); + + sps = new QSlider(MINSPEED, MAXSPEED, 10, speed, QSlider::Horizontal, main); + sps->setMinimumSize( 120, 20 ); + sps->setTickmarks(QSlider::Below); + sps->setTickInterval(150); + connect( sps, SIGNAL( valueChanged( int ) ), SLOT( slotSpeed( int ) ) ); + tl1->addWidget(sps); + + label = new QLabel( i18n("Epoch:"), main ); + tl1->addWidget(label); + + eps = new QSlider(MINEPOCH, MAXEPOCH, 100, epoch, QSlider::Horizontal, main); + eps->setMinimumSize( 120, 20 ); + eps->setTickmarks(QSlider::Below); + eps->setTickInterval(3000); + connect( eps, SIGNAL( valueChanged( int ) ), SLOT( slotEpoch( int ) ) ); + tl1->addWidget(eps); + + label = new QLabel( i18n("Color rate:"), main ); + tl1->addWidget(label); + + crs = new QSlider(MINCOLOR, MAXCOLOR, 5, crate, QSlider::Horizontal, main); + crs->setMinimumSize( 120, 20 ); + crs->setTickmarks(QSlider::Below); + crs->setTickInterval(10); + connect( crs, SIGNAL( valueChanged( int ) ), SLOT( slotCRate( int ) ) ); + tl1->addWidget(crs); + + label = new QLabel( i18n("Rotation Z:"), main ); + tl1->addWidget(label); + + zrs = new QSlider(MINZROT, MAXZROT, 18, zrot, QSlider::Horizontal, main); + zrs->setMinimumSize( 120, 20 ); + zrs->setTickmarks(QSlider::Below); + zrs->setTickInterval(36); + connect( zrs, SIGNAL( valueChanged( int ) ), SLOT( slotZRot( int ) ) ); + tl1->addWidget(zrs); + + label = new QLabel( i18n("Rotation Y:"), main ); + tl1->addWidget(label); + + yrs = new QSlider(MINYROT, MAXYROT, 18, yrot, QSlider::Horizontal, main); + yrs->setMinimumSize( 120, 20 ); + yrs->setTickmarks(QSlider::Below); + yrs->setTickInterval(36); + connect( yrs, SIGNAL( valueChanged( int ) ), SLOT( slotYRot( int ) ) ); + tl1->addWidget(yrs); + + label = new QLabel( i18n("Rotation X:"), main ); + tl1->addWidget(label); + + xrs = new QSlider(MINXROT, MAXXROT, 18, xrot, QSlider::Horizontal, main); + xrs->setMinimumSize( 120, 20 ); + xrs->setTickmarks(QSlider::Below); + xrs->setTickInterval(36); + connect( xrs, SIGNAL( valueChanged( int ) ), SLOT( slotXRot( int ) ) ); + tl1->addWidget(xrs); + + preview = new QWidget( main ); + preview->setFixedSize( 220, 165 ); + preview->setBackgroundColor( black ); + preview->show(); // otherwise saver does not get correct size + saver = new KLorenzSaver( preview->winId() ); + tl->addWidget(preview); +} + +KLorenzSetup::~KLorenzSetup() +{ + delete saver; +} + +// read settings from config file +void KLorenzSetup::readSettings() +{ + KConfig *config = KGlobal::config(); + config->setGroup( "Settings" ); + + speed = config->readNumEntry( "Speed", DEFSPEED ); + epoch = config->readNumEntry( "Epoch", DEFEPOCH ); + crate = config->readNumEntry( "Color Rate", DEFCOLOR ); + zrot = config->readNumEntry( "ZRot", DEFZROT ); + yrot = config->readNumEntry( "YRot", DEFZROT ); + xrot = config->readNumEntry( "XRot", DEFZROT ); +} + + +void KLorenzSetup::slotSpeed(int num) +{ + speed = num; + if (saver) saver->setSpeed(speed); +} + +void KLorenzSetup::slotEpoch(int num) +{ + epoch = num; + if (saver) saver->setEpoch(epoch); +} + +void KLorenzSetup::slotCRate(int num) +{ + crate = num; + if (saver) saver->setCRate(crate); +} + +void KLorenzSetup::slotZRot(int num) +{ + zrot = num; + if (saver) { + saver->setZRot(zrot); + saver->updateMatrix(); + saver->newEpoch(); + } +} + +void KLorenzSetup::slotYRot(int num) +{ + yrot = num; + if (saver) { + saver->setYRot(yrot); + saver->updateMatrix(); + saver->newEpoch(); + } +} + +void KLorenzSetup::slotXRot(int num) +{ + xrot = num; + if (saver) { + saver->setXRot(xrot); + saver->updateMatrix(); + saver->newEpoch(); + } +} + +void KLorenzSetup::slotHelp() +{ + KMessageBox::about(this,i18n("Lorenz Attractor screen saver for KDE\n\nCopyright (c) 2000 Nicolas Brodu")); +} + +// Ok pressed - save settings and exit +void KLorenzSetup::slotOk() +{ + KConfig *config = KGlobal::config(); + config->setGroup( "Settings" ); + + config->writeEntry( "Speed", speed ); + config->writeEntry( "Epoch", epoch ); + config->writeEntry( "Color Rate", crate ); + config->writeEntry( "ZRot", zrot ); + config->writeEntry( "YRot", yrot ); + config->writeEntry( "XRot", xrot ); + + config->sync(); + + accept(); +} + +void KLorenzSetup::slotDefault() +{ + speed = DEFSPEED; + epoch = DEFEPOCH; + crate = DEFCOLOR; + zrot = DEFZROT; + yrot = DEFYROT; + xrot = DEFXROT; + if (saver) { + saver->setSpeed(speed); + saver->setEpoch(epoch); + saver->setCRate(crate); + saver->setZRot(zrot); + saver->setYRot(yrot); + saver->setXRot(xrot); + saver->updateMatrix(); + saver->newEpoch(); + } + sps->setValue(speed); + eps->setValue(epoch); + crs->setValue(crate); + zrs->setValue(zrot); + yrs->setValue(yrot); + xrs->setValue(xrot); + +/* // User can cancel, or save defaults? + + KConfig *config = KGlobal::config(); + config->setGroup( "Settings" ); + + config->writeEntry( "Speed", speed ); + config->writeEntry( "Epoch", epoch ); + config->writeEntry( "Color Rate", crate ); + config->writeEntry( "ZRot", zrot ); + config->writeEntry( "YRot", yrot ); + config->writeEntry( "XRot", xrot ); + + config->sync(); +*/ +} + +//----------------------------------------------------------------------------- + + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +const double pi = M_PI; + +// Homogeneous coordinate transform matrix +// I initially wrote it for a Java applet, it is inspired from a +// Matrix class in the JDK. +// Nicolas Brodu, 1998-2000 +class Matrix3D +{ + // All coefficients + double xx, xy, xz, xo; + double yx, yy, yz, yo; + double zx, zy, zz, zo; + // 0, 0, 0, 1 are implicit +public: + + void unit() + { + xx=1.0; xy=0.0; xz=0.0; xo=0.0; + yx=0.0; yy=1.0; yz=0.0; yo=0.0; + zx=0.0; zy=0.0; zz=1.0; zo=0.0; + } + + Matrix3D () + { + unit(); + } + + // Translation + void translate(double x, double y, double z) + { + xo += x; + yo += y; + zo += z; + } + + // Rotation, in degrees, around the Y axis + void rotY(double theta) + { + theta *= pi / 180; + double ct = cos(theta); + double st = sin(theta); + + double Nxx = xx * ct + zx * st; + double Nxy = xy * ct + zy * st; + double Nxz = xz * ct + zz * st; + double Nxo = xo * ct + zo * st; + + double Nzx = zx * ct - xx * st; + double Nzy = zy * ct - xy * st; + double Nzz = zz * ct - xz * st; + double Nzo = zo * ct - xo * st; + + xo = Nxo; + xx = Nxx; + xy = Nxy; + xz = Nxz; + zo = Nzo; + zx = Nzx; + zy = Nzy; + zz = Nzz; + } + + + // Rotation, in degrees, around the X axis + void rotX(double theta) + { + theta *= pi / 180; + double ct = cos(theta); + double st = sin(theta); + + double Nyx = yx * ct + zx * st; + double Nyy = yy * ct + zy * st; + double Nyz = yz * ct + zz * st; + double Nyo = yo * ct + zo * st; + + double Nzx = zx * ct - yx * st; + double Nzy = zy * ct - yy * st; + double Nzz = zz * ct - yz * st; + double Nzo = zo * ct - yo * st; + + yo = Nyo; + yx = Nyx; + yy = Nyy; + yz = Nyz; + zo = Nzo; + zx = Nzx; + zy = Nzy; + zz = Nzz; + } + + + // Rotation, in degrees, around the Z axis + void rotZ(double theta) + { + theta *= pi / 180; + double ct = cos(theta); + double st = sin(theta); + + double Nyx = yx * ct + xx * st; + double Nyy = yy * ct + xy * st; + double Nyz = yz * ct + xz * st; + double Nyo = yo * ct + xo * st; + + double Nxx = xx * ct - yx * st; + double Nxy = xy * ct - yy * st; + double Nxz = xz * ct - yz * st; + double Nxo = xo * ct - yo * st; + + yo = Nyo; + yx = Nyx; + yy = Nyy; + yz = Nyz; + xo = Nxo; + xx = Nxx; + xy = Nxy; + xz = Nxz; + } + + // Multiply by a projection matrix, with camera f + // f 0 0 0 x f*x + // 0 f 0 0 * y = f*y + // 0 0 1 f z z+f + // 0 0 0 1 1 1 + // So, it it easy to find the 2D coordinates after the transform + // u = f*x / (z+f) + // v = f*y / (z+f) + void proj(double f) + { + xx*=f; + xy*=f; + xz*=f; + xo*=f; + yx*=f; + yy*=f; + yz*=f; + yo*=f; + zo+=f; + } + + // Apply the transformation 3D => 2D + void transform(double x, double y, double z, double &u, double& v, double& w) + { + u = x * xx + y * xy + z * xz + xo; + v = x * yx + y * yy + z * yz + yo; + w = x * zx + y * zy + z * zz + zo; + } +}; + +KLorenzSaver::KLorenzSaver( WId id ) : KScreenSaver( id ) +{ + readSettings(); + + // Create a transform matrix with the parameters + mat = new Matrix3D(); + updateMatrix(); + + colorContext = QColor::enterAllocContext(); + setBackgroundColor( black ); + newEpoch(); + + timer.start( 10 ); + connect( &timer, SIGNAL( timeout() ), SLOT( drawOnce() ) ); +} + +KLorenzSaver::~KLorenzSaver() +{ + delete mat; + mat=0; + timer.stop(); + QColor::leaveAllocContext(); + QColor::destroyAllocContext( colorContext ); +} + +// read configuration settings from config file +void KLorenzSaver::readSettings() +{ + KConfig *config = KGlobal::config(); + config->setGroup( "Settings" ); + + speed = config->readNumEntry( "Speed", DEFSPEED ); + epoch = config->readNumEntry( "Epoch", DEFEPOCH ); + zrot = config->readNumEntry( "ZRot", DEFZROT ); + yrot = config->readNumEntry( "YRot", DEFZROT ); + xrot = config->readNumEntry( "XRot", DEFZROT ); + + int crate_num = config->readNumEntry( "Color Rate", DEFCOLOR ); + crate = (double)crate_num / (double)MAXCOLOR; +} + +void KLorenzSaver::setSpeed(int num) +{ + speed = num; +} + +void KLorenzSaver::setEpoch(int num) +{ + epoch = num; +} + +void KLorenzSaver::setZRot(int num) +{ + zrot = num; +} + +void KLorenzSaver::setYRot(int num) +{ + yrot = num; +} + +void KLorenzSaver::setXRot(int num) +{ + xrot = num; +} + +void KLorenzSaver::setCRate(int num) +{ + crate = (double)num / (double)MAXCOLOR; +} + +void KLorenzSaver::updateMatrix() +{ + // reset matrix + mat->unit(); + // Remove the mean before the rotations... + mat->translate(-0.95413, -0.96740, -23.60065); + mat->rotZ(zrot); + mat->rotY(yrot); + mat->rotX(xrot); + mat->translate(0, 0, 100); + mat->proj(1); +} + +void KLorenzSaver::newEpoch() +{ + // Start at a random position, somewhere around the mean + x = 0.95-25.0+50.0*kapp->random() / (RAND_MAX+1.0); + y = 0.97-25.0+50.0*kapp->random() / (RAND_MAX+1.0); + z = 23.6-25.0+50.0*kapp->random() / (RAND_MAX+1.0); + // start at some random 'time' as well to have different colors + t = 10000.0*kapp->random() / (RAND_MAX+1.0); + erase(); + e=0; // reset epoch counter +} + +// Computes the derivatives using Lorenz equations +static void lorenz(double x, double y, double z, double& dx, double& dy, double& dz) +{ + dx = 10*(y-x); + dy = 28*x - y - x*z; + dz = x*y - z*8.0/3.0; +} + +// Use a simple Runge-Kutta formula to draw a few points +// No need to go beyond 2nd order for a screensaver! +void KLorenzSaver::drawOnce() +{ + double kx, ky, kz, dx, dy, dz; + const double h = 0.0001; + const double tqh = h * 3.0 / 4.0; + QPainter p(this); + + for (int i=0; i<speed; i++) { + // Runge-Kutta formula + lorenz(x,y,z,dx,dy,dz); + lorenz(x + tqh*dx, y + tqh*dy, z + tqh*dz, kx, ky, kz); + x += h*(dx/3.0+2*kx/3.0); + y += h*(dy/3.0+2*ky/3.0); + z += h*(dz/3.0+2*kz/3.0); + // Apply transform + mat->transform(x,y,z,kx,ky,kz); + // Choose a color + p.setPen( + QColor((int)(sin(t*crate/pi)*127+128), + (int)(sin(t*crate/(pi-1))*127+128), + (int)(sin(t*crate/(pi-2))*127+128)).pixel() ); + // Draw a point + p.drawPoint( (int)(kx*width()*1.5/kz)+(int)(width()/2), + (int)(ky*height()*1.5/kz)+(int)(height()/2)); + t+=h; + } + if (++e>=epoch) newEpoch(); +} |