SVN_SILENT made messages (.desktop file)
[kdeartwork.git] / kscreensaver / kdesavers / lorenz.cpp
blob1678a34f217db05276570fa392a0fe5a7408401f
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 <qcolormap.h>
19 #include <qlabel.h>
21 #include <kapplication.h>
22 #include <klocale.h>
23 #include <kglobal.h>
24 #include <kconfig.h>
25 #include <kmessagebox.h>
26 #include <krandom.h>
28 #include "lorenz.h"
29 #include "lorenz.moc"
31 // libkscreensaver interface
32 class KLorenzSaverInterface : public KScreenSaverInterface
36 public:
37 virtual KAboutData* aboutData() {
38 return new KAboutData( "klorenz.kss", 0, ki18n( "KLorenz" ), "2.2.0", ki18n( "KLorenz" ) );
42 virtual KScreenSaver* create( WId id )
44 return new KLorenzSaver( id );
47 virtual QDialog* setup()
49 return new KLorenzSetup();
53 int main( int argc, char *argv[] )
55 KLorenzSaverInterface kss;
56 return kScreenSaverMain( argc, argv, kss );
59 #define MINSPEED 1
60 #define MAXSPEED 1500
61 #define DEFSPEED 150
62 #define MINZROT -180
63 #define MAXZROT 180
64 #define DEFZROT 104 //100
65 #define MINYROT -180
66 #define MAXYROT 180
67 #define DEFYROT -19 //80
68 #define MINXROT -180
69 #define MAXXROT 180
70 #define DEFXROT 25 //20
71 #define MINEPOCH 1
72 #define MAXEPOCH 30000
73 #define DEFEPOCH 5800
74 #define MINCOLOR 1
75 #define MAXCOLOR 100
76 #define DEFCOLOR 20
78 //-----------------------------------------------------------------------------
79 // dialog to setup screen saver parameters
81 KLorenzSetup::KLorenzSetup( QWidget *parent )
82 : KDialog( parent)
84 setCaption(i18n( "Setup Lorenz Attractor" ));
85 setButtons(Ok|Cancel|Default|Help);
86 setDefaultButton(Ok);
87 setModal(true);
88 readSettings();
90 setButtonText( Help, i18n( "A&bout" ) );
91 QWidget *main = new QWidget(this);
92 setMainWidget(main);
94 QHBoxLayout *tl = new QHBoxLayout( main );
95 tl->setSpacing( spacingHint() );
96 QVBoxLayout *tl1 = new QVBoxLayout;
97 tl->addLayout(tl1);
99 QLabel *label = new QLabel( i18n("Speed:"), main );
100 tl1->addWidget(label);
102 sps = new QSlider(Qt::Horizontal, main);
103 sps->setMinimum(MINSPEED);
104 sps->setMaximum(MAXSPEED);
105 sps->setPageStep(10);
106 sps->setValue(speed);
107 sps->setMinimumSize( 120, 20 );
108 sps->setTickPosition(QSlider::TicksBelow);
109 sps->setTickInterval(150);
110 connect( sps, SIGNAL( valueChanged( int ) ), SLOT( slotSpeed( int ) ) );
111 tl1->addWidget(sps);
113 label = new QLabel( i18n("Epoch:"), main );
114 tl1->addWidget(label);
116 eps = new QSlider(Qt::Horizontal, main);
117 eps->setMinimum(MINEPOCH);
118 eps->setMaximum(MAXEPOCH);
119 eps->setPageStep(100);
120 eps->setValue(epoch);
121 eps->setMinimumSize( 120, 20 );
122 eps->setTickPosition(QSlider::TicksBelow);
123 eps->setTickInterval(3000);
124 connect( eps, SIGNAL( valueChanged( int ) ), SLOT( slotEpoch( int ) ) );
125 tl1->addWidget(eps);
127 label = new QLabel( i18n("Color rate:"), main );
128 tl1->addWidget(label);
130 crs = new QSlider(Qt::Horizontal, main);
131 crs->setMinimum(MINCOLOR);
132 crs->setMaximum(MAXCOLOR);
133 crs->setPageStep(5);
134 crs->setValue(crate);
135 crs->setMinimumSize( 120, 20 );
136 crs->setTickPosition(QSlider::TicksBelow);
137 crs->setTickInterval(10);
138 connect( crs, SIGNAL( valueChanged( int ) ), SLOT( slotCRate( int ) ) );
139 tl1->addWidget(crs);
141 label = new QLabel( i18n("Rotation Z:"), main );
142 tl1->addWidget(label);
144 zrs = new QSlider(Qt::Horizontal, main);
145 zrs->setMinimum(MINZROT);
146 zrs->setMaximum(MAXZROT);
147 zrs->setPageStep(18);
148 zrs->setValue(zrot);
149 zrs->setMinimumSize( 120, 20 );
150 zrs->setTickPosition(QSlider::TicksBelow);
151 zrs->setTickInterval(36);
152 connect( zrs, SIGNAL( valueChanged( int ) ), SLOT( slotZRot( int ) ) );
153 tl1->addWidget(zrs);
155 label = new QLabel( i18n("Rotation Y:"), main );
156 tl1->addWidget(label);
158 yrs = new QSlider(Qt::Horizontal, main);
159 yrs->setMinimum(MINYROT);
160 yrs->setMaximum(MAXYROT);
161 yrs->setPageStep(18);
162 yrs->setValue(yrot);
163 yrs->setMinimumSize( 120, 20 );
164 yrs->setTickPosition(QSlider::TicksBelow);
165 yrs->setTickInterval(36);
166 connect( yrs, SIGNAL( valueChanged( int ) ), SLOT( slotYRot( int ) ) );
167 tl1->addWidget(yrs);
169 label = new QLabel( i18n("Rotation X:"), main );
170 tl1->addWidget(label);
172 xrs = new QSlider(Qt::Horizontal, main);
173 xrs->setMinimum(MINXROT);
174 xrs->setMaximum(MAXXROT);
175 xrs->setPageStep(18);
176 xrs->setValue(xrot);
177 xrs->setMinimumSize( 120, 20 );
178 xrs->setTickPosition(QSlider::TicksBelow);
179 xrs->setTickInterval(36);
180 connect( xrs, SIGNAL( valueChanged( int ) ), SLOT( slotXRot( int ) ) );
181 tl1->addWidget(xrs);
183 preview = new QWidget( main );
184 preview->setFixedSize( 220, 165 );
186 QPalette palette;
187 palette.setColor( preview->backgroundRole(), Qt::black );
188 preview->setPalette( palette );
190 preview->show(); // otherwise saver does not get correct size
191 saver = new KLorenzSaver( preview->winId() );
192 tl->addWidget(preview);
193 connect(this,SIGNAL(okClicked()),this,SLOT(slotOk()));
194 connect(this,SIGNAL(helpClicked()),this,SLOT(slotHelp()));
195 connect(this,SIGNAL(defaultClicked()),this,SLOT(slotDefault()));
198 KLorenzSetup::~KLorenzSetup()
200 delete saver;
203 // read settings from config file
204 void KLorenzSetup::readSettings()
206 KConfigGroup config(KGlobal::config(), "Settings");
208 speed = config.readEntry( "Speed", DEFSPEED );
209 epoch = config.readEntry( "Epoch", DEFEPOCH );
210 crate = config.readEntry( "Color Rate", DEFCOLOR );
211 zrot = config.readEntry( "ZRot", DEFZROT );
212 yrot = config.readEntry( "YRot", DEFZROT );
213 xrot = config.readEntry( "XRot", DEFZROT );
217 void KLorenzSetup::slotSpeed(int num)
219 speed = num;
220 if (saver) saver->setSpeed(speed);
223 void KLorenzSetup::slotEpoch(int num)
225 epoch = num;
226 if (saver) saver->setEpoch(epoch);
229 void KLorenzSetup::slotCRate(int num)
231 crate = num;
232 if (saver) saver->setCRate(crate);
235 void KLorenzSetup::slotZRot(int num)
237 zrot = num;
238 if (saver) {
239 saver->setZRot(zrot);
240 saver->updateMatrix();
241 saver->newEpoch();
245 void KLorenzSetup::slotYRot(int num)
247 yrot = num;
248 if (saver) {
249 saver->setYRot(yrot);
250 saver->updateMatrix();
251 saver->newEpoch();
255 void KLorenzSetup::slotXRot(int num)
257 xrot = num;
258 if (saver) {
259 saver->setXRot(xrot);
260 saver->updateMatrix();
261 saver->newEpoch();
265 void KLorenzSetup::slotHelp()
267 KMessageBox::about(this,i18n("Lorenz Attractor screen saver for KDE\n\nCopyright (c) 2000 Nicolas Brodu"));
270 // Ok pressed - save settings and exit
271 void KLorenzSetup::slotOk()
273 KConfigGroup config(KGlobal::config(), "Settings");
275 config.writeEntry( "Speed", speed );
276 config.writeEntry( "Epoch", epoch );
277 config.writeEntry( "Color Rate", crate );
278 config.writeEntry( "ZRot", zrot );
279 config.writeEntry( "YRot", yrot );
280 config.writeEntry( "XRot", xrot );
282 config.sync();
284 accept();
287 void KLorenzSetup::slotDefault()
289 speed = DEFSPEED;
290 epoch = DEFEPOCH;
291 crate = DEFCOLOR;
292 zrot = DEFZROT;
293 yrot = DEFYROT;
294 xrot = DEFXROT;
295 if (saver) {
296 saver->setSpeed(speed);
297 saver->setEpoch(epoch);
298 saver->setCRate(crate);
299 saver->setZRot(zrot);
300 saver->setYRot(yrot);
301 saver->setXRot(xrot);
302 saver->updateMatrix();
303 saver->newEpoch();
305 sps->setValue(speed);
306 eps->setValue(epoch);
307 crs->setValue(crate);
308 zrs->setValue(zrot);
309 yrs->setValue(yrot);
310 xrs->setValue(xrot);
312 /* // User can cancel, or save defaults?
314 KSharedConfig::Ptr config = KGlobal::config();
315 config.setGroup( "Settings" );
317 config.writeEntry( "Speed", speed );
318 config.writeEntry( "Epoch", epoch );
319 config.writeEntry( "Color Rate", crate );
320 config.writeEntry( "ZRot", zrot );
321 config.writeEntry( "YRot", yrot );
322 config.writeEntry( "XRot", xrot );
324 config.sync();
328 //-----------------------------------------------------------------------------
331 #ifndef M_PI
332 #define M_PI 3.14159265358979323846
333 #endif
334 const double pi = M_PI;
336 // Homogeneous coordinate transform matrix
337 // I initially wrote it for a Java applet, it is inspired from a
338 // Matrix class in the JDK.
339 // Nicolas Brodu, 1998-2000
340 class Matrix3D
342 // All coefficients
343 double xx, xy, xz, xo;
344 double yx, yy, yz, yo;
345 double zx, zy, zz, zo;
346 // 0, 0, 0, 1 are implicit
347 public:
349 void unit()
351 xx=1.0; xy=0.0; xz=0.0; xo=0.0;
352 yx=0.0; yy=1.0; yz=0.0; yo=0.0;
353 zx=0.0; zy=0.0; zz=1.0; zo=0.0;
356 Matrix3D ()
358 unit();
361 // Translation
362 void translate(double x, double y, double z)
364 xo += x;
365 yo += y;
366 zo += z;
369 // Rotation, in degrees, around the Y axis
370 void rotY(double theta)
372 theta *= pi / 180;
373 double ct = cos(theta);
374 double st = sin(theta);
376 double Nxx = xx * ct + zx * st;
377 double Nxy = xy * ct + zy * st;
378 double Nxz = xz * ct + zz * st;
379 double Nxo = xo * ct + zo * st;
381 double Nzx = zx * ct - xx * st;
382 double Nzy = zy * ct - xy * st;
383 double Nzz = zz * ct - xz * st;
384 double Nzo = zo * ct - xo * st;
386 xo = Nxo;
387 xx = Nxx;
388 xy = Nxy;
389 xz = Nxz;
390 zo = Nzo;
391 zx = Nzx;
392 zy = Nzy;
393 zz = Nzz;
397 // Rotation, in degrees, around the X axis
398 void rotX(double theta)
400 theta *= pi / 180;
401 double ct = cos(theta);
402 double st = sin(theta);
404 double Nyx = yx * ct + zx * st;
405 double Nyy = yy * ct + zy * st;
406 double Nyz = yz * ct + zz * st;
407 double Nyo = yo * ct + zo * st;
409 double Nzx = zx * ct - yx * st;
410 double Nzy = zy * ct - yy * st;
411 double Nzz = zz * ct - yz * st;
412 double Nzo = zo * ct - yo * st;
414 yo = Nyo;
415 yx = Nyx;
416 yy = Nyy;
417 yz = Nyz;
418 zo = Nzo;
419 zx = Nzx;
420 zy = Nzy;
421 zz = Nzz;
425 // Rotation, in degrees, around the Z axis
426 void rotZ(double theta)
428 theta *= pi / 180;
429 double ct = cos(theta);
430 double st = sin(theta);
432 double Nyx = yx * ct + xx * st;
433 double Nyy = yy * ct + xy * st;
434 double Nyz = yz * ct + xz * st;
435 double Nyo = yo * ct + xo * st;
437 double Nxx = xx * ct - yx * st;
438 double Nxy = xy * ct - yy * st;
439 double Nxz = xz * ct - yz * st;
440 double Nxo = xo * ct - yo * st;
442 yo = Nyo;
443 yx = Nyx;
444 yy = Nyy;
445 yz = Nyz;
446 xo = Nxo;
447 xx = Nxx;
448 xy = Nxy;
449 xz = Nxz;
452 // Multiply by a projection matrix, with camera f
453 // f 0 0 0 x f*x
454 // 0 f 0 0 * y = f*y
455 // 0 0 1 f z z+f
456 // 0 0 0 1 1 1
457 // So, it it easy to find the 2D coordinates after the transform
458 // u = f*x / (z+f)
459 // v = f*y / (z+f)
460 void proj(double f)
462 xx*=f;
463 xy*=f;
464 xz*=f;
465 xo*=f;
466 yx*=f;
467 yy*=f;
468 yz*=f;
469 yo*=f;
470 zo+=f;
473 // Apply the transformation 3D => 2D
474 void transform(double x, double y, double z, double &u, double& v, double& w)
476 u = x * xx + y * xy + z * xz + xo;
477 v = x * yx + y * yy + z * yz + yo;
478 w = x * zx + y * zy + z * zz + zo;
482 KLorenzSaver::KLorenzSaver( WId id ) : KScreenSaver( id )
484 readSettings();
486 // Create a transform matrix with the parameters
487 mat = new Matrix3D();
488 updateMatrix();
491 QPalette palette;
492 palette.setColor( backgroundRole(), Qt::black );
493 setPalette( palette );
495 newEpoch();
497 timer.start( 10 );
498 connect( &timer, SIGNAL( timeout() ), SLOT( update() ) );
501 KLorenzSaver::~KLorenzSaver()
503 delete mat;
504 mat=0;
505 timer.stop();
508 // read configuration settings from config file
509 void KLorenzSaver::readSettings()
511 KConfigGroup config(KGlobal::config(), "Settings");
513 speed = config.readEntry( "Speed", DEFSPEED );
514 epoch = config.readEntry( "Epoch", DEFEPOCH );
515 zrot = config.readEntry( "ZRot", DEFZROT );
516 yrot = config.readEntry( "YRot", DEFZROT );
517 xrot = config.readEntry( "XRot", DEFZROT );
519 int crate_num = config.readEntry( "Color Rate", DEFCOLOR );
520 crate = (double)crate_num / (double)MAXCOLOR;
523 void KLorenzSaver::setSpeed(int num)
525 speed = num;
528 void KLorenzSaver::setEpoch(int num)
530 epoch = num;
533 void KLorenzSaver::setZRot(int num)
535 zrot = num;
538 void KLorenzSaver::setYRot(int num)
540 yrot = num;
543 void KLorenzSaver::setXRot(int num)
545 xrot = num;
548 void KLorenzSaver::setCRate(int num)
550 crate = (double)num / (double)MAXCOLOR;
553 void KLorenzSaver::updateMatrix()
555 // reset matrix
556 mat->unit();
557 // Remove the mean before the rotations...
558 mat->translate(-0.95413, -0.96740, -23.60065);
559 mat->rotZ(zrot);
560 mat->rotY(yrot);
561 mat->rotX(xrot);
562 mat->translate(0, 0, 100);
563 mat->proj(1);
566 void KLorenzSaver::newEpoch()
568 // Start at a random position, somewhere around the mean
569 x = 0.95-25.0+50.0*KRandom::random() / (RAND_MAX+1.0);
570 y = 0.97-25.0+50.0*KRandom::random() / (RAND_MAX+1.0);
571 z = 23.6-25.0+50.0*KRandom::random() / (RAND_MAX+1.0);
572 // start at some random 'time' as well to have different colors
573 t = 10000.0*KRandom::random() / (RAND_MAX+1.0);
574 e=0; // reset epoch counter
577 // Computes the derivatives using Lorenz equations
578 static void lorenz(double x, double y, double z, double& dx, double& dy, double& dz)
580 dx = 10*(y-x);
581 dy = 28*x - y - x*z;
582 dz = x*y - z*8.0/3.0;
585 // Use a simple Runge-Kutta formula to draw a few points
586 // No need to go beyond 2nd order for a screensaver!
587 void KLorenzSaver::paintEvent(QPaintEvent *)
589 setAttribute( Qt::WA_NoSystemBackground ); // Only after initial clear
591 double kx, ky, kz, dx, dy, dz;
592 const double h = 0.0001;
593 const double tqh = h * 3.0 / 4.0;
594 QPainter p(this);
596 if ( !e )
597 p.fillRect(rect(), p.background());
599 QColormap cmap = QColormap::instance();
600 for (int i=0; i<speed; i++) {
601 // Runge-Kutta formula
602 lorenz(x,y,z,dx,dy,dz);
603 lorenz(x + tqh*dx, y + tqh*dy, z + tqh*dz, kx, ky, kz);
604 x += h*(dx/3.0+2*kx/3.0);
605 y += h*(dy/3.0+2*ky/3.0);
606 z += h*(dz/3.0+2*kz/3.0);
607 // Apply transform
608 mat->transform(x,y,z,kx,ky,kz);
609 // Choose a color
610 p.setPen(
611 cmap.pixel(QColor((int)(sin(t*crate/pi)*127+128),
612 (int)(sin(t*crate/(pi-1))*127+128),
613 (int)(sin(t*crate/(pi-2))*127+128))) );
614 // Draw a point
615 p.drawPoint( (int)(kx*width()*1.5/kz)+(int)(width()/2),
616 (int)(ky*height()*1.5/kz)+(int)(height()/2));
617 t+=h;
619 if (++e>=epoch)
620 newEpoch();