Back to index

kdeartwork  4.3.2
lorenz.cpp
Go to the documentation of this file.
00001 //-----------------------------------------------------------------------------
00002 //
00003 // Lorenz - Lorenz Attractor screen saver
00004 //   Nicolas Brodu, brodu@kde.org, 2000
00005 //
00006 // Portions of code from kblankscrn and khop.
00007 //   See authors there.
00008 //
00009 // I release my code as GPL, but see the other headers and the README
00010 
00011 #include <math.h>
00012 #include <stdlib.h>
00013 
00014 #include <qpainter.h>
00015 #include <qslider.h>
00016 #include <qlayout.h>
00017 #include <qcolor.h>
00018 #include <qcolormap.h>
00019 #include <qlabel.h>
00020 
00021 #include <kapplication.h>
00022 #include <klocale.h>
00023 #include <kglobal.h>
00024 #include <kconfig.h>
00025 #include <kmessagebox.h>
00026 #include <krandom.h>
00027 
00028 #include "lorenz.h"
00029 #include "lorenz.moc"
00030 
00031 // libkscreensaver interface
00032 class KLorenzSaverInterface : public KScreenSaverInterface
00033 {
00034 
00035 
00036 public:
00037     virtual KAboutData* aboutData() {
00038         return new KAboutData( "klorenz.kss", "klock", ki18n( "KLorenz" ), "2.2.0", ki18n( "KLorenz" ) );
00039     }
00040 
00041 
00042     virtual KScreenSaver* create( WId id )
00043     {
00044         return new KLorenzSaver( id );
00045     }
00046 
00047     virtual QDialog* setup()
00048     {
00049         return new KLorenzSetup();
00050     }
00051 };
00052 
00053 int main( int argc, char *argv[] )
00054 {
00055     KLorenzSaverInterface kss;
00056     return kScreenSaverMain( argc, argv, kss );
00057 }
00058 
00059 #define MINSPEED 1
00060 #define MAXSPEED 1500
00061 #define DEFSPEED 150
00062 #define MINZROT -180
00063 #define MAXZROT 180
00064 #define DEFZROT 104 //100
00065 #define MINYROT -180
00066 #define MAXYROT 180
00067 #define DEFYROT -19 //80
00068 #define MINXROT -180
00069 #define MAXXROT 180
00070 #define DEFXROT 25 //20
00071 #define MINEPOCH 1
00072 #define MAXEPOCH 30000
00073 #define DEFEPOCH 5800
00074 #define MINCOLOR 1
00075 #define MAXCOLOR 100
00076 #define DEFCOLOR 20
00077 
00078 //-----------------------------------------------------------------------------
00079 // dialog to setup screen saver parameters
00080 //
00081 KLorenzSetup::KLorenzSetup( QWidget *parent )
00082     : KDialog( parent)
00083 {
00084        setCaption(i18n( "Setup Lorenz Attractor" ));
00085        setButtons(Ok|Cancel|Default|Help);
00086        setDefaultButton(Ok);
00087     setModal(true);
00088        readSettings();
00089 
00090     setButtonText( Help, i18n( "A&bout" ) );
00091     QWidget *main = new QWidget(this);
00092        setMainWidget(main);
00093 
00094     QHBoxLayout *tl = new QHBoxLayout( main );
00095     tl->setSpacing( spacingHint() );
00096     QVBoxLayout *tl1 = new QVBoxLayout;
00097     tl->addLayout(tl1);
00098 
00099     QLabel *label = new QLabel( i18n("Speed:"), main );
00100     tl1->addWidget(label);
00101 
00102     sps = new QSlider(Qt::Horizontal, main);
00103     sps->setMinimum(MINSPEED);
00104     sps->setMaximum(MAXSPEED);
00105     sps->setPageStep(10);
00106     sps->setValue(speed);
00107     sps->setMinimumSize( 120, 20 );
00108     sps->setTickPosition(QSlider::TicksBelow);
00109     sps->setTickInterval(150);
00110     connect( sps, SIGNAL( valueChanged( int ) ), SLOT( slotSpeed( int ) ) );
00111     tl1->addWidget(sps);
00112 
00113     label = new QLabel( i18n("Epoch:"), main );
00114     tl1->addWidget(label);
00115 
00116     eps = new QSlider(Qt::Horizontal, main);
00117     eps->setMinimum(MINEPOCH);
00118     eps->setMaximum(MAXEPOCH);
00119     eps->setPageStep(100);
00120     eps->setValue(epoch);
00121     eps->setMinimumSize( 120, 20 );
00122     eps->setTickPosition(QSlider::TicksBelow);
00123     eps->setTickInterval(3000);
00124     connect( eps, SIGNAL( valueChanged( int ) ), SLOT( slotEpoch( int ) ) );
00125     tl1->addWidget(eps);
00126 
00127     label = new QLabel( i18n("Color rate:"), main );
00128     tl1->addWidget(label);
00129 
00130     crs = new QSlider(Qt::Horizontal, main);
00131     crs->setMinimum(MINCOLOR);
00132     crs->setMaximum(MAXCOLOR);
00133     crs->setPageStep(5);
00134     crs->setValue(crate);
00135     crs->setMinimumSize( 120, 20 );
00136     crs->setTickPosition(QSlider::TicksBelow);
00137     crs->setTickInterval(10);
00138     connect( crs, SIGNAL( valueChanged( int ) ), SLOT( slotCRate( int ) ) );
00139     tl1->addWidget(crs);
00140 
00141     label = new QLabel( i18n("Rotation Z:"), main );
00142     tl1->addWidget(label);
00143 
00144     zrs = new QSlider(Qt::Horizontal, main);
00145     zrs->setMinimum(MINZROT);
00146     zrs->setMaximum(MAXZROT);
00147     zrs->setPageStep(18);
00148     zrs->setValue(zrot);
00149     zrs->setMinimumSize( 120, 20 );
00150     zrs->setTickPosition(QSlider::TicksBelow);
00151     zrs->setTickInterval(36);
00152     connect( zrs, SIGNAL( valueChanged( int ) ), SLOT( slotZRot( int ) ) );
00153     tl1->addWidget(zrs);
00154 
00155     label = new QLabel( i18n("Rotation Y:"), main );
00156     tl1->addWidget(label);
00157 
00158     yrs = new QSlider(Qt::Horizontal, main);
00159     yrs->setMinimum(MINYROT);
00160     yrs->setMaximum(MAXYROT);
00161     yrs->setPageStep(18);
00162     yrs->setValue(yrot);
00163     yrs->setMinimumSize( 120, 20 );
00164     yrs->setTickPosition(QSlider::TicksBelow);
00165     yrs->setTickInterval(36);
00166     connect( yrs, SIGNAL( valueChanged( int ) ), SLOT( slotYRot( int ) ) );
00167     tl1->addWidget(yrs);
00168 
00169     label = new QLabel( i18n("Rotation X:"), main );
00170     tl1->addWidget(label);
00171 
00172     xrs = new QSlider(Qt::Horizontal, main);
00173     xrs->setMinimum(MINXROT);
00174     xrs->setMaximum(MAXXROT);
00175     xrs->setPageStep(18);
00176     xrs->setValue(xrot);
00177     xrs->setMinimumSize( 120, 20 );
00178     xrs->setTickPosition(QSlider::TicksBelow);
00179     xrs->setTickInterval(36);
00180     connect( xrs, SIGNAL( valueChanged( int ) ), SLOT( slotXRot( int ) ) );
00181     tl1->addWidget(xrs);
00182 
00183     preview = new QWidget( main );
00184     preview->setFixedSize( 220, 165 );
00185     {
00186         QPalette palette;
00187         palette.setColor( preview->backgroundRole(), Qt::black );
00188         preview->setPalette( palette );
00189        preview->setAutoFillBackground(true);
00190     }
00191     preview->show();    // otherwise saver does not get correct size
00192     saver = new KLorenzSaver( preview->winId() );
00193     tl->addWidget(preview);
00194     connect(this,SIGNAL(okClicked()),this,SLOT(slotOk()));
00195     connect(this,SIGNAL(helpClicked()),this,SLOT(slotHelp()));
00196     connect(this,SIGNAL(defaultClicked()),this,SLOT(slotDefault()));
00197 }
00198 
00199 KLorenzSetup::~KLorenzSetup()
00200 {
00201     delete saver;
00202 }
00203 
00204 // read settings from config file
00205 void KLorenzSetup::readSettings()
00206 {
00207     KConfigGroup config(KGlobal::config(), "Settings");
00208 
00209     speed = config.readEntry( "Speed", DEFSPEED );
00210     epoch = config.readEntry( "Epoch", DEFEPOCH );
00211     crate = config.readEntry( "Color Rate", DEFCOLOR );
00212     zrot = config.readEntry( "ZRot", DEFZROT );
00213     yrot = config.readEntry( "YRot", DEFZROT );
00214     xrot = config.readEntry( "XRot", DEFZROT );
00215 }
00216 
00217 
00218 void KLorenzSetup::slotSpeed(int num)
00219 {
00220     speed = num;
00221     if (saver) saver->setSpeed(speed);
00222 }
00223 
00224 void KLorenzSetup::slotEpoch(int num)
00225 {
00226     epoch = num;
00227     if (saver) saver->setEpoch(epoch);
00228 }
00229 
00230 void KLorenzSetup::slotCRate(int num)
00231 {
00232     crate = num;
00233     if (saver) saver->setCRate(crate);
00234 }
00235 
00236 void KLorenzSetup::slotZRot(int num)
00237 {
00238     zrot = num;
00239     if (saver) {
00240         saver->setZRot(zrot);
00241         saver->updateMatrix();
00242         saver->newEpoch();
00243     }
00244 }
00245 
00246 void KLorenzSetup::slotYRot(int num)
00247 {
00248     yrot = num;
00249     if (saver) {
00250         saver->setYRot(yrot);
00251         saver->updateMatrix();
00252         saver->newEpoch();
00253     }
00254 }
00255 
00256 void KLorenzSetup::slotXRot(int num)
00257 {
00258     xrot = num;
00259     if (saver) {
00260         saver->setXRot(xrot);
00261         saver->updateMatrix();
00262         saver->newEpoch();
00263     }
00264 }
00265 
00266 void KLorenzSetup::slotHelp()
00267 {
00268     KMessageBox::about(this,i18n("Lorenz Attractor screen saver for KDE\n\nCopyright (c) 2000 Nicolas Brodu"));
00269 }
00270 
00271 // Ok pressed - save settings and exit
00272 void KLorenzSetup::slotOk()
00273 {
00274     KConfigGroup config(KGlobal::config(), "Settings");
00275 
00276     config.writeEntry( "Speed", speed );
00277     config.writeEntry( "Epoch", epoch );
00278     config.writeEntry( "Color Rate", crate );
00279     config.writeEntry( "ZRot", zrot );
00280     config.writeEntry( "YRot", yrot );
00281     config.writeEntry( "XRot", xrot );
00282 
00283     config.sync();
00284 
00285     accept();
00286 }
00287 
00288 void KLorenzSetup::slotDefault()
00289 {
00290     speed = DEFSPEED;
00291     epoch = DEFEPOCH;
00292     crate = DEFCOLOR;
00293     zrot = DEFZROT;
00294     yrot = DEFYROT;
00295     xrot = DEFXROT;
00296     if (saver) {
00297         saver->setSpeed(speed);
00298         saver->setEpoch(epoch);
00299         saver->setCRate(crate);
00300         saver->setZRot(zrot);
00301         saver->setYRot(yrot);
00302         saver->setXRot(xrot);
00303         saver->updateMatrix();
00304         saver->newEpoch();
00305     }
00306     sps->setValue(speed);
00307     eps->setValue(epoch);
00308     crs->setValue(crate);
00309     zrs->setValue(zrot);
00310     yrs->setValue(yrot);
00311     xrs->setValue(xrot);
00312 
00313 /*  // User can cancel, or save defaults?
00314 
00315     KSharedConfig::Ptr config = KGlobal::config();
00316     config.setGroup( "Settings" );
00317 
00318     config.writeEntry( "Speed", speed );
00319     config.writeEntry( "Epoch", epoch );
00320     config.writeEntry( "Color Rate", crate );
00321     config.writeEntry( "ZRot", zrot );
00322     config.writeEntry( "YRot", yrot );
00323     config.writeEntry( "XRot", xrot );
00324 
00325     config.sync();
00326 */
00327 }
00328 
00329 //-----------------------------------------------------------------------------
00330 
00331 
00332 #ifndef M_PI
00333 #define M_PI 3.14159265358979323846
00334 #endif
00335 const double pi = M_PI;
00336 
00337 // Homogeneous coordinate transform matrix
00338 // I initially wrote it for a Java applet, it is inspired from a
00339 // Matrix class in the JDK.
00340 // Nicolas Brodu, 1998-2000
00341 class Matrix3D
00342 {
00343     // All coefficients
00344     double xx, xy, xz, xo;
00345     double yx, yy, yz, yo;
00346     double zx, zy, zz, zo;
00347     // 0, 0, 0, 1 are implicit
00348 public:
00349 
00350     void unit()
00351     {
00352         xx=1.0; xy=0.0; xz=0.0; xo=0.0;
00353         yx=0.0; yy=1.0; yz=0.0; yo=0.0;
00354         zx=0.0; zy=0.0; zz=1.0; zo=0.0;
00355     }
00356 
00357     Matrix3D ()
00358     {
00359         unit();
00360     }
00361 
00362     // Translation
00363     void translate(double x, double y, double z)
00364     {
00365         xo += x;
00366         yo += y;
00367         zo += z;
00368     }
00369 
00370     // Rotation, in degrees, around the Y axis
00371     void rotY(double theta)
00372     {
00373         theta *= pi / 180;
00374         double ct = cos(theta);
00375         double st = sin(theta);
00376 
00377         double Nxx = xx * ct + zx * st;
00378         double Nxy = xy * ct + zy * st;
00379         double Nxz = xz * ct + zz * st;
00380         double Nxo = xo * ct + zo * st;
00381 
00382         double Nzx = zx * ct - xx * st;
00383         double Nzy = zy * ct - xy * st;
00384         double Nzz = zz * ct - xz * st;
00385         double Nzo = zo * ct - xo * st;
00386 
00387         xo = Nxo;
00388         xx = Nxx;
00389         xy = Nxy;
00390         xz = Nxz;
00391         zo = Nzo;
00392         zx = Nzx;
00393         zy = Nzy;
00394         zz = Nzz;
00395     }
00396 
00397 
00398     // Rotation, in degrees, around the X axis
00399     void rotX(double theta)
00400     {
00401         theta *= pi / 180;
00402         double ct = cos(theta);
00403         double st = sin(theta);
00404 
00405         double Nyx = yx * ct + zx * st;
00406         double Nyy = yy * ct + zy * st;
00407         double Nyz = yz * ct + zz * st;
00408         double Nyo = yo * ct + zo * st;
00409 
00410         double Nzx = zx * ct - yx * st;
00411         double Nzy = zy * ct - yy * st;
00412         double Nzz = zz * ct - yz * st;
00413         double Nzo = zo * ct - yo * st;
00414 
00415         yo = Nyo;
00416         yx = Nyx;
00417         yy = Nyy;
00418         yz = Nyz;
00419         zo = Nzo;
00420         zx = Nzx;
00421         zy = Nzy;
00422         zz = Nzz;
00423     }
00424 
00425 
00426     // Rotation, in degrees, around the Z axis
00427     void rotZ(double theta)
00428     {
00429         theta *= pi / 180;
00430         double ct = cos(theta);
00431         double st = sin(theta);
00432 
00433         double Nyx = yx * ct + xx * st;
00434         double Nyy = yy * ct + xy * st;
00435         double Nyz = yz * ct + xz * st;
00436         double Nyo = yo * ct + xo * st;
00437 
00438         double Nxx = xx * ct - yx * st;
00439         double Nxy = xy * ct - yy * st;
00440         double Nxz = xz * ct - yz * st;
00441         double Nxo = xo * ct - yo * st;
00442 
00443         yo = Nyo;
00444         yx = Nyx;
00445         yy = Nyy;
00446         yz = Nyz;
00447         xo = Nxo;
00448         xx = Nxx;
00449         xy = Nxy;
00450         xz = Nxz;
00451     }
00452 
00453     // Multiply by a projection matrix, with camera f
00454     // f 0 0 0   x   f*x
00455     // 0 f 0 0 * y = f*y
00456     // 0 0 1 f   z   z+f
00457     // 0 0 0 1   1   1
00458     // So, it it easy to find the 2D coordinates after the transform
00459     //  u = f*x / (z+f)
00460     //  v = f*y / (z+f)
00461     void proj(double f)
00462     {
00463         xx*=f;
00464         xy*=f;
00465         xz*=f;
00466         xo*=f;
00467         yx*=f;
00468         yy*=f;
00469         yz*=f;
00470         yo*=f;
00471         zo+=f;
00472     }
00473 
00474     // Apply the transformation 3D => 2D
00475     void transform(double x, double y, double z, double &u, double& v, double& w)
00476     {
00477         u = x * xx + y * xy + z * xz + xo;
00478         v = x * yx + y * yy + z * yz + yo;
00479         w = x * zx + y * zy + z * zz + zo;
00480     }
00481 };
00482 
00483 KLorenzSaver::KLorenzSaver( WId id ) : KScreenSaver( id )
00484 {
00485     readSettings();
00486 
00487     // Create a transform matrix with the parameters
00488     mat = new Matrix3D();
00489     updateMatrix();
00490 
00491     {
00492         QPalette palette;
00493         palette.setColor( backgroundRole(), Qt::black );
00494         setPalette( palette );
00495        setAutoFillBackground(true);
00496     }
00497     newEpoch();
00498 
00499     timer.start( 10 );
00500     connect( &timer, SIGNAL( timeout() ), SLOT( update() ) );
00501     show();
00502 }
00503 
00504 KLorenzSaver::~KLorenzSaver()
00505 {
00506     delete mat;
00507     mat=0;
00508     timer.stop();
00509 }
00510 
00511 // read configuration settings from config file
00512 void KLorenzSaver::readSettings()
00513 {
00514     KConfigGroup config(KGlobal::config(), "Settings");
00515 
00516     speed = config.readEntry( "Speed", DEFSPEED );
00517     epoch = config.readEntry( "Epoch", DEFEPOCH );
00518     zrot = config.readEntry( "ZRot", DEFZROT );
00519     yrot = config.readEntry( "YRot", DEFZROT );
00520     xrot = config.readEntry( "XRot", DEFZROT );
00521 
00522     int crate_num = config.readEntry( "Color Rate", DEFCOLOR );
00523     crate = (double)crate_num / (double)MAXCOLOR;
00524 }
00525 
00526 void KLorenzSaver::setSpeed(int num)
00527 {
00528     speed = num;
00529 }
00530 
00531 void KLorenzSaver::setEpoch(int num)
00532 {
00533     epoch = num;
00534 }
00535 
00536 void KLorenzSaver::setZRot(int num)
00537 {
00538     zrot = num;
00539 }
00540 
00541 void KLorenzSaver::setYRot(int num)
00542 {
00543     yrot = num;
00544 }
00545 
00546 void KLorenzSaver::setXRot(int num)
00547 {
00548     xrot = num;
00549 }
00550 
00551 void KLorenzSaver::setCRate(int num)
00552 {
00553     crate = (double)num / (double)MAXCOLOR;
00554 }
00555 
00556 void KLorenzSaver::updateMatrix()
00557 {
00558     // reset matrix
00559     mat->unit();
00560     // Remove the mean before the rotations...
00561     mat->translate(-0.95413, -0.96740, -23.60065);
00562     mat->rotZ(zrot);
00563     mat->rotY(yrot);
00564     mat->rotX(xrot);
00565     mat->translate(0, 0, 100);
00566     mat->proj(1);
00567 }
00568 
00569 void KLorenzSaver::newEpoch()
00570 {
00571     // Start at a random position, somewhere around the mean
00572     x = 0.95-25.0+50.0*KRandom::random() / (RAND_MAX+1.0);
00573     y = 0.97-25.0+50.0*KRandom::random() / (RAND_MAX+1.0);
00574     z = 23.6-25.0+50.0*KRandom::random() / (RAND_MAX+1.0);
00575     // start at some random 'time' as well to have different colors
00576     t = 10000.0*KRandom::random() / (RAND_MAX+1.0);
00577     e=0; // reset epoch counter
00578 }
00579 
00580 // Computes the derivatives using Lorenz equations
00581 static void lorenz(double x, double y, double z, double& dx, double& dy, double& dz)
00582 {
00583     dx = 10*(y-x);
00584     dy = 28*x - y - x*z;
00585     dz = x*y - z*8.0/3.0;
00586 }
00587 
00588 // Use a simple Runge-Kutta formula to draw a few points
00589 // No need to go beyond 2nd order for a screensaver!
00590 void KLorenzSaver::paintEvent(QPaintEvent *)
00591 {
00592     setAttribute( Qt::WA_NoSystemBackground ); // Only after initial clear
00593 
00594     double kx, ky, kz, dx, dy, dz;
00595     const double h = 0.0001;
00596     const double tqh = h * 3.0 / 4.0;
00597     QPainter p(this);
00598 
00599     if ( !e )
00600         p.fillRect(rect(), p.background());
00601 
00602     QColormap cmap = QColormap::instance();
00603     for (int i=0; i<speed; i++) {
00604         // Runge-Kutta formula
00605         lorenz(x,y,z,dx,dy,dz);
00606         lorenz(x + tqh*dx, y + tqh*dy, z + tqh*dz, kx, ky, kz);
00607         x += h*(dx/3.0+2*kx/3.0);
00608         y += h*(dy/3.0+2*ky/3.0);
00609         z += h*(dz/3.0+2*kz/3.0);
00610         // Apply transform
00611         mat->transform(x,y,z,kx,ky,kz);
00612         // Choose a color
00613         p.setPen(
00614             cmap.pixel(QColor((int)(sin(t*crate/pi)*127+128),
00615                               (int)(sin(t*crate/(pi-1))*127+128),
00616                               (int)(sin(t*crate/(pi-2))*127+128))) );
00617         // Draw a point
00618         p.drawPoint( (int)(kx*width()*1.5/kz)+(int)(width()/2),
00619                      (int)(ky*height()*1.5/kz)+(int)(height()/2));
00620         t+=h;
00621     }
00622     if (++e>=epoch)
00623         newEpoch();
00624 }