Compile GL screensaver
[kdeartwork.git] / kscreensaver / kdesavers / lorenz.cpp
blobb8a96c63e28c16cc4bdd9d2d18e7b8fcf36c7d34
1 //-----------------------------------------------------------------------------
2 //
3 // Lorenz - Lorenz Attractor screen saver
4 // Nicolas Brodu, brodu@kde.org, 2000
5 //
6 // Portions of code from kblankscrn and khop.
7 // See authors there.
8 //
9 // I release my code as GPL, but see the other headers and the README
11 #include <math.h>
12 #include <stdlib.h>
14 #include <qpainter.h>
15 #include <qslider.h>
16 #include <qlayout.h>
17 #include <qcolor.h>
18 #include <qlabel.h>
19 //Added by qt3to4:
20 #include <QVBoxLayout>
21 #include <QHBoxLayout>
23 #include <kapplication.h>
24 #include <klocale.h>
25 #include <kglobal.h>
26 #include <kconfig.h>
27 #include <kmessagebox.h>
28 #include <krandom.h>
30 #include "lorenz.h"
31 #include "lorenz.moc"
33 // libkscreensaver interface
34 class KLorenzSaverInterface : public KScreenSaverInterface
38 public:
39 virtual KAboutData* aboutData() {
40 return new KAboutData( "klorenz.kss", I18N_NOOP( "KLorenz" ), "2.2.0", I18N_NOOP( "KLorenz" ) );
44 virtual KScreenSaver* create( WId id )
46 return new KLorenzSaver( id );
49 virtual QDialog* setup()
51 return new KLorenzSetup();
55 int main( int argc, char *argv[] )
57 KLorenzSaverInterface kss;
58 return kScreenSaverMain( argc, argv, kss );
61 #define MINSPEED 1
62 #define MAXSPEED 1500
63 #define DEFSPEED 150
64 #define MINZROT -180
65 #define MAXZROT 180
66 #define DEFZROT 104 //100
67 #define MINYROT -180
68 #define MAXYROT 180
69 #define DEFYROT -19 //80
70 #define MINXROT -180
71 #define MAXXROT 180
72 #define DEFXROT 25 //20
73 #define MINEPOCH 1
74 #define MAXEPOCH 30000
75 #define DEFEPOCH 5800
76 #define MINCOLOR 1
77 #define MAXCOLOR 100
78 #define DEFCOLOR 20
80 //-----------------------------------------------------------------------------
81 // dialog to setup screen saver parameters
83 KLorenzSetup::KLorenzSetup( QWidget *parent, const char *name )
84 : KDialog( parent)
86 setCaption(i18n( "Setup Lorenz Attractor" ));
87 setButtons(Ok|Cancel|Default|Help);
88 setDefaultButton(Ok);
89 setModal(true);
90 readSettings();
92 setButtonText( Help, i18n( "A&bout" ) );
93 QWidget *main = new QWidget(this);
94 setMainWidget(main);
96 QHBoxLayout *tl = new QHBoxLayout( main, 0, spacingHint() );
97 QVBoxLayout *tl1 = new QVBoxLayout;
98 tl->addLayout(tl1);
100 QLabel *label = new QLabel( i18n("Speed:"), main );
101 tl1->addWidget(label);
103 sps = new QSlider(MINSPEED, MAXSPEED, 10, speed, Qt::Horizontal, main);
104 sps->setMinimumSize( 120, 20 );
105 sps->setTickPosition(QSlider::TicksBelow);
106 sps->setTickInterval(150);
107 connect( sps, SIGNAL( valueChanged( int ) ), SLOT( slotSpeed( int ) ) );
108 tl1->addWidget(sps);
110 label = new QLabel( i18n("Epoch:"), main );
111 tl1->addWidget(label);
113 eps = new QSlider(MINEPOCH, MAXEPOCH, 100, epoch, Qt::Horizontal, main);
114 eps->setMinimumSize( 120, 20 );
115 eps->setTickPosition(QSlider::TicksBelow);
116 eps->setTickInterval(3000);
117 connect( eps, SIGNAL( valueChanged( int ) ), SLOT( slotEpoch( int ) ) );
118 tl1->addWidget(eps);
120 label = new QLabel( i18n("Color rate:"), main );
121 tl1->addWidget(label);
123 crs = new QSlider(MINCOLOR, MAXCOLOR, 5, crate, Qt::Horizontal, main);
124 crs->setMinimumSize( 120, 20 );
125 crs->setTickPosition(QSlider::TicksBelow);
126 crs->setTickInterval(10);
127 connect( crs, SIGNAL( valueChanged( int ) ), SLOT( slotCRate( int ) ) );
128 tl1->addWidget(crs);
130 label = new QLabel( i18n("Rotation Z:"), main );
131 tl1->addWidget(label);
133 zrs = new QSlider(MINZROT, MAXZROT, 18, zrot, Qt::Horizontal, main);
134 zrs->setMinimumSize( 120, 20 );
135 zrs->setTickPosition(QSlider::TicksBelow);
136 zrs->setTickInterval(36);
137 connect( zrs, SIGNAL( valueChanged( int ) ), SLOT( slotZRot( int ) ) );
138 tl1->addWidget(zrs);
140 label = new QLabel( i18n("Rotation Y:"), main );
141 tl1->addWidget(label);
143 yrs = new QSlider(MINYROT, MAXYROT, 18, yrot, Qt::Horizontal, main);
144 yrs->setMinimumSize( 120, 20 );
145 yrs->setTickPosition(QSlider::TicksBelow);
146 yrs->setTickInterval(36);
147 connect( yrs, SIGNAL( valueChanged( int ) ), SLOT( slotYRot( int ) ) );
148 tl1->addWidget(yrs);
150 label = new QLabel( i18n("Rotation X:"), main );
151 tl1->addWidget(label);
153 xrs = new QSlider(MINXROT, MAXXROT, 18, xrot, Qt::Horizontal, main);
154 xrs->setMinimumSize( 120, 20 );
155 xrs->setTickPosition(QSlider::TicksBelow);
156 xrs->setTickInterval(36);
157 connect( xrs, SIGNAL( valueChanged( int ) ), SLOT( slotXRot( int ) ) );
158 tl1->addWidget(xrs);
160 preview = new QWidget( main );
161 preview->setFixedSize( 220, 165 );
162 preview->setBackgroundColor( Qt::black );
163 preview->show(); // otherwise saver does not get correct size
164 saver = new KLorenzSaver( preview->winId() );
165 tl->addWidget(preview);
168 KLorenzSetup::~KLorenzSetup()
170 delete saver;
173 // read settings from config file
174 void KLorenzSetup::readSettings()
176 KConfig *config = KGlobal::config();
177 config->setGroup( "Settings" );
179 speed = config->readNumEntry( "Speed", DEFSPEED );
180 epoch = config->readNumEntry( "Epoch", DEFEPOCH );
181 crate = config->readNumEntry( "Color Rate", DEFCOLOR );
182 zrot = config->readNumEntry( "ZRot", DEFZROT );
183 yrot = config->readNumEntry( "YRot", DEFZROT );
184 xrot = config->readNumEntry( "XRot", DEFZROT );
188 void KLorenzSetup::slotSpeed(int num)
190 speed = num;
191 if (saver) saver->setSpeed(speed);
194 void KLorenzSetup::slotEpoch(int num)
196 epoch = num;
197 if (saver) saver->setEpoch(epoch);
200 void KLorenzSetup::slotCRate(int num)
202 crate = num;
203 if (saver) saver->setCRate(crate);
206 void KLorenzSetup::slotZRot(int num)
208 zrot = num;
209 if (saver) {
210 saver->setZRot(zrot);
211 saver->updateMatrix();
212 saver->newEpoch();
216 void KLorenzSetup::slotYRot(int num)
218 yrot = num;
219 if (saver) {
220 saver->setYRot(yrot);
221 saver->updateMatrix();
222 saver->newEpoch();
226 void KLorenzSetup::slotXRot(int num)
228 xrot = num;
229 if (saver) {
230 saver->setXRot(xrot);
231 saver->updateMatrix();
232 saver->newEpoch();
236 void KLorenzSetup::slotHelp()
238 KMessageBox::about(this,i18n("Lorenz Attractor screen saver for KDE\n\nCopyright (c) 2000 Nicolas Brodu"));
241 // Ok pressed - save settings and exit
242 void KLorenzSetup::slotOk()
244 KConfig *config = KGlobal::config();
245 config->setGroup( "Settings" );
247 config->writeEntry( "Speed", speed );
248 config->writeEntry( "Epoch", epoch );
249 config->writeEntry( "Color Rate", crate );
250 config->writeEntry( "ZRot", zrot );
251 config->writeEntry( "YRot", yrot );
252 config->writeEntry( "XRot", xrot );
254 config->sync();
256 accept();
259 void KLorenzSetup::slotDefault()
261 speed = DEFSPEED;
262 epoch = DEFEPOCH;
263 crate = DEFCOLOR;
264 zrot = DEFZROT;
265 yrot = DEFYROT;
266 xrot = DEFXROT;
267 if (saver) {
268 saver->setSpeed(speed);
269 saver->setEpoch(epoch);
270 saver->setCRate(crate);
271 saver->setZRot(zrot);
272 saver->setYRot(yrot);
273 saver->setXRot(xrot);
274 saver->updateMatrix();
275 saver->newEpoch();
277 sps->setValue(speed);
278 eps->setValue(epoch);
279 crs->setValue(crate);
280 zrs->setValue(zrot);
281 yrs->setValue(yrot);
282 xrs->setValue(xrot);
284 /* // User can cancel, or save defaults?
286 KConfig *config = KGlobal::config();
287 config->setGroup( "Settings" );
289 config->writeEntry( "Speed", speed );
290 config->writeEntry( "Epoch", epoch );
291 config->writeEntry( "Color Rate", crate );
292 config->writeEntry( "ZRot", zrot );
293 config->writeEntry( "YRot", yrot );
294 config->writeEntry( "XRot", xrot );
296 config->sync();
300 //-----------------------------------------------------------------------------
303 #ifndef M_PI
304 #define M_PI 3.14159265358979323846
305 #endif
306 const double pi = M_PI;
308 // Homogeneous coordinate transform matrix
309 // I initially wrote it for a Java applet, it is inspired from a
310 // Matrix class in the JDK.
311 // Nicolas Brodu, 1998-2000
312 class Matrix3D
314 // All coefficients
315 double xx, xy, xz, xo;
316 double yx, yy, yz, yo;
317 double zx, zy, zz, zo;
318 // 0, 0, 0, 1 are implicit
319 public:
321 void unit()
323 xx=1.0; xy=0.0; xz=0.0; xo=0.0;
324 yx=0.0; yy=1.0; yz=0.0; yo=0.0;
325 zx=0.0; zy=0.0; zz=1.0; zo=0.0;
328 Matrix3D ()
330 unit();
333 // Translation
334 void translate(double x, double y, double z)
336 xo += x;
337 yo += y;
338 zo += z;
341 // Rotation, in degrees, around the Y axis
342 void rotY(double theta)
344 theta *= pi / 180;
345 double ct = cos(theta);
346 double st = sin(theta);
348 double Nxx = xx * ct + zx * st;
349 double Nxy = xy * ct + zy * st;
350 double Nxz = xz * ct + zz * st;
351 double Nxo = xo * ct + zo * st;
353 double Nzx = zx * ct - xx * st;
354 double Nzy = zy * ct - xy * st;
355 double Nzz = zz * ct - xz * st;
356 double Nzo = zo * ct - xo * st;
358 xo = Nxo;
359 xx = Nxx;
360 xy = Nxy;
361 xz = Nxz;
362 zo = Nzo;
363 zx = Nzx;
364 zy = Nzy;
365 zz = Nzz;
369 // Rotation, in degrees, around the X axis
370 void rotX(double theta)
372 theta *= pi / 180;
373 double ct = cos(theta);
374 double st = sin(theta);
376 double Nyx = yx * ct + zx * st;
377 double Nyy = yy * ct + zy * st;
378 double Nyz = yz * ct + zz * st;
379 double Nyo = yo * ct + zo * st;
381 double Nzx = zx * ct - yx * st;
382 double Nzy = zy * ct - yy * st;
383 double Nzz = zz * ct - yz * st;
384 double Nzo = zo * ct - yo * st;
386 yo = Nyo;
387 yx = Nyx;
388 yy = Nyy;
389 yz = Nyz;
390 zo = Nzo;
391 zx = Nzx;
392 zy = Nzy;
393 zz = Nzz;
397 // Rotation, in degrees, around the Z axis
398 void rotZ(double theta)
400 theta *= pi / 180;
401 double ct = cos(theta);
402 double st = sin(theta);
404 double Nyx = yx * ct + xx * st;
405 double Nyy = yy * ct + xy * st;
406 double Nyz = yz * ct + xz * st;
407 double Nyo = yo * ct + xo * st;
409 double Nxx = xx * ct - yx * st;
410 double Nxy = xy * ct - yy * st;
411 double Nxz = xz * ct - yz * st;
412 double Nxo = xo * ct - yo * st;
414 yo = Nyo;
415 yx = Nyx;
416 yy = Nyy;
417 yz = Nyz;
418 xo = Nxo;
419 xx = Nxx;
420 xy = Nxy;
421 xz = Nxz;
424 // Multiply by a projection matrix, with camera f
425 // f 0 0 0 x f*x
426 // 0 f 0 0 * y = f*y
427 // 0 0 1 f z z+f
428 // 0 0 0 1 1 1
429 // So, it it easy to find the 2D coordinates after the transform
430 // u = f*x / (z+f)
431 // v = f*y / (z+f)
432 void proj(double f)
434 xx*=f;
435 xy*=f;
436 xz*=f;
437 xo*=f;
438 yx*=f;
439 yy*=f;
440 yz*=f;
441 yo*=f;
442 zo+=f;
445 // Apply the transformation 3D => 2D
446 void transform(double x, double y, double z, double &u, double& v, double& w)
448 u = x * xx + y * xy + z * xz + xo;
449 v = x * yx + y * yy + z * yz + yo;
450 w = x * zx + y * zy + z * zz + zo;
454 KLorenzSaver::KLorenzSaver( WId id ) : KScreenSaver( id )
456 readSettings();
458 // Create a transform matrix with the parameters
459 mat = new Matrix3D();
460 updateMatrix();
462 setBackgroundColor( Qt::black );
463 newEpoch();
465 timer.start( 10 );
466 connect( &timer, SIGNAL( timeout() ), SLOT( drawOnce() ) );
469 KLorenzSaver::~KLorenzSaver()
471 delete mat;
472 mat=0;
473 timer.stop();
476 // read configuration settings from config file
477 void KLorenzSaver::readSettings()
479 KConfig *config = KGlobal::config();
480 config->setGroup( "Settings" );
482 speed = config->readNumEntry( "Speed", DEFSPEED );
483 epoch = config->readNumEntry( "Epoch", DEFEPOCH );
484 zrot = config->readNumEntry( "ZRot", DEFZROT );
485 yrot = config->readNumEntry( "YRot", DEFZROT );
486 xrot = config->readNumEntry( "XRot", DEFZROT );
488 int crate_num = config->readNumEntry( "Color Rate", DEFCOLOR );
489 crate = (double)crate_num / (double)MAXCOLOR;
492 void KLorenzSaver::setSpeed(int num)
494 speed = num;
497 void KLorenzSaver::setEpoch(int num)
499 epoch = num;
502 void KLorenzSaver::setZRot(int num)
504 zrot = num;
507 void KLorenzSaver::setYRot(int num)
509 yrot = num;
512 void KLorenzSaver::setXRot(int num)
514 xrot = num;
517 void KLorenzSaver::setCRate(int num)
519 crate = (double)num / (double)MAXCOLOR;
522 void KLorenzSaver::updateMatrix()
524 // reset matrix
525 mat->unit();
526 // Remove the mean before the rotations...
527 mat->translate(-0.95413, -0.96740, -23.60065);
528 mat->rotZ(zrot);
529 mat->rotY(yrot);
530 mat->rotX(xrot);
531 mat->translate(0, 0, 100);
532 mat->proj(1);
535 void KLorenzSaver::newEpoch()
537 // Start at a random position, somewhere around the mean
538 x = 0.95-25.0+50.0*KRandom::random() / (RAND_MAX+1.0);
539 y = 0.97-25.0+50.0*KRandom::random() / (RAND_MAX+1.0);
540 z = 23.6-25.0+50.0*KRandom::random() / (RAND_MAX+1.0);
541 // start at some random 'time' as well to have different colors
542 t = 10000.0*KRandom::random() / (RAND_MAX+1.0);
543 erase();
544 e=0; // reset epoch counter
547 // Computes the derivatives using Lorenz equations
548 static void lorenz(double x, double y, double z, double& dx, double& dy, double& dz)
550 dx = 10*(y-x);
551 dy = 28*x - y - x*z;
552 dz = x*y - z*8.0/3.0;
555 // Use a simple Runge-Kutta formula to draw a few points
556 // No need to go beyond 2nd order for a screensaver!
557 void KLorenzSaver::drawOnce()
559 double kx, ky, kz, dx, dy, dz;
560 const double h = 0.0001;
561 const double tqh = h * 3.0 / 4.0;
562 QPainter p(this);
564 for (int i=0; i<speed; i++) {
565 // Runge-Kutta formula
566 lorenz(x,y,z,dx,dy,dz);
567 lorenz(x + tqh*dx, y + tqh*dy, z + tqh*dz, kx, ky, kz);
568 x += h*(dx/3.0+2*kx/3.0);
569 y += h*(dy/3.0+2*ky/3.0);
570 z += h*(dz/3.0+2*kz/3.0);
571 // Apply transform
572 mat->transform(x,y,z,kx,ky,kz);
573 // Choose a color
574 p.setPen(
575 QColor((int)(sin(t*crate/pi)*127+128),
576 (int)(sin(t*crate/(pi-1))*127+128),
577 (int)(sin(t*crate/(pi-2))*127+128)).pixel() );
578 // Draw a point
579 p.drawPoint( (int)(kx*width()*1.5/kz)+(int)(width()/2),
580 (int)(ky*height()*1.5/kz)+(int)(height()/2));
581 t+=h;
583 if (++e>=epoch) newEpoch();