Qt3to4
[kdeartwork.git] / kscreensaver / kdesavers / lorenz.cpp
blob30765dcb108c1f87c09133dd776f0e06bcc95e67
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>
29 #include "lorenz.h"
30 #include "lorenz.moc"
32 // libkscreensaver interface
33 extern "C"
35 KDE_EXPORT const char *kss_applicationName = "klorenz.kss";
36 KDE_EXPORT const char *kss_description = I18N_NOOP( "KLorenz" );
37 KDE_EXPORT const char *kss_version = "2.2.0";
39 KDE_EXPORT KScreenSaver *kss_create( WId id )
41 return new KLorenzSaver( id );
44 KDE_EXPORT QDialog *kss_setup()
46 return new KLorenzSetup();
50 #define MINSPEED 1
51 #define MAXSPEED 1500
52 #define DEFSPEED 150
53 #define MINZROT -180
54 #define MAXZROT 180
55 #define DEFZROT 104 //100
56 #define MINYROT -180
57 #define MAXYROT 180
58 #define DEFYROT -19 //80
59 #define MINXROT -180
60 #define MAXXROT 180
61 #define DEFXROT 25 //20
62 #define MINEPOCH 1
63 #define MAXEPOCH 30000
64 #define DEFEPOCH 5800
65 #define MINCOLOR 1
66 #define MAXCOLOR 100
67 #define DEFCOLOR 20
69 //-----------------------------------------------------------------------------
70 // dialog to setup screen saver parameters
72 KLorenzSetup::KLorenzSetup( QWidget *parent, const char *name )
73 : KDialogBase( parent, name, true, i18n( "Setup Lorenz Attractor" ),
74 Ok|Cancel|Default|Help, Ok, true )
76 readSettings();
78 setButtonText( Help, i18n( "A&bout" ) );
79 QWidget *main = makeMainWidget();
81 QHBoxLayout *tl = new QHBoxLayout( main, 0, spacingHint() );
82 QVBoxLayout *tl1 = new QVBoxLayout;
83 tl->addLayout(tl1);
85 QLabel *label = new QLabel( i18n("Speed:"), main );
86 tl1->addWidget(label);
88 sps = new QSlider(MINSPEED, MAXSPEED, 10, speed, QSlider::Horizontal, main);
89 sps->setMinimumSize( 120, 20 );
90 sps->setTickmarks(QSlider::Below);
91 sps->setTickInterval(150);
92 connect( sps, SIGNAL( valueChanged( int ) ), SLOT( slotSpeed( int ) ) );
93 tl1->addWidget(sps);
95 label = new QLabel( i18n("Epoch:"), main );
96 tl1->addWidget(label);
98 eps = new QSlider(MINEPOCH, MAXEPOCH, 100, epoch, QSlider::Horizontal, main);
99 eps->setMinimumSize( 120, 20 );
100 eps->setTickmarks(QSlider::Below);
101 eps->setTickInterval(3000);
102 connect( eps, SIGNAL( valueChanged( int ) ), SLOT( slotEpoch( int ) ) );
103 tl1->addWidget(eps);
105 label = new QLabel( i18n("Color rate:"), main );
106 tl1->addWidget(label);
108 crs = new QSlider(MINCOLOR, MAXCOLOR, 5, crate, QSlider::Horizontal, main);
109 crs->setMinimumSize( 120, 20 );
110 crs->setTickmarks(QSlider::Below);
111 crs->setTickInterval(10);
112 connect( crs, SIGNAL( valueChanged( int ) ), SLOT( slotCRate( int ) ) );
113 tl1->addWidget(crs);
115 label = new QLabel( i18n("Rotation Z:"), main );
116 tl1->addWidget(label);
118 zrs = new QSlider(MINZROT, MAXZROT, 18, zrot, QSlider::Horizontal, main);
119 zrs->setMinimumSize( 120, 20 );
120 zrs->setTickmarks(QSlider::Below);
121 zrs->setTickInterval(36);
122 connect( zrs, SIGNAL( valueChanged( int ) ), SLOT( slotZRot( int ) ) );
123 tl1->addWidget(zrs);
125 label = new QLabel( i18n("Rotation Y:"), main );
126 tl1->addWidget(label);
128 yrs = new QSlider(MINYROT, MAXYROT, 18, yrot, QSlider::Horizontal, main);
129 yrs->setMinimumSize( 120, 20 );
130 yrs->setTickmarks(QSlider::Below);
131 yrs->setTickInterval(36);
132 connect( yrs, SIGNAL( valueChanged( int ) ), SLOT( slotYRot( int ) ) );
133 tl1->addWidget(yrs);
135 label = new QLabel( i18n("Rotation X:"), main );
136 tl1->addWidget(label);
138 xrs = new QSlider(MINXROT, MAXXROT, 18, xrot, QSlider::Horizontal, main);
139 xrs->setMinimumSize( 120, 20 );
140 xrs->setTickmarks(QSlider::Below);
141 xrs->setTickInterval(36);
142 connect( xrs, SIGNAL( valueChanged( int ) ), SLOT( slotXRot( int ) ) );
143 tl1->addWidget(xrs);
145 preview = new QWidget( main );
146 preview->setFixedSize( 220, 165 );
147 preview->setBackgroundColor( black );
148 preview->show(); // otherwise saver does not get correct size
149 saver = new KLorenzSaver( preview->winId() );
150 tl->addWidget(preview);
153 KLorenzSetup::~KLorenzSetup()
155 delete saver;
158 // read settings from config file
159 void KLorenzSetup::readSettings()
161 KConfig *config = KGlobal::config();
162 config->setGroup( "Settings" );
164 speed = config->readNumEntry( "Speed", DEFSPEED );
165 epoch = config->readNumEntry( "Epoch", DEFEPOCH );
166 crate = config->readNumEntry( "Color Rate", DEFCOLOR );
167 zrot = config->readNumEntry( "ZRot", DEFZROT );
168 yrot = config->readNumEntry( "YRot", DEFZROT );
169 xrot = config->readNumEntry( "XRot", DEFZROT );
173 void KLorenzSetup::slotSpeed(int num)
175 speed = num;
176 if (saver) saver->setSpeed(speed);
179 void KLorenzSetup::slotEpoch(int num)
181 epoch = num;
182 if (saver) saver->setEpoch(epoch);
185 void KLorenzSetup::slotCRate(int num)
187 crate = num;
188 if (saver) saver->setCRate(crate);
191 void KLorenzSetup::slotZRot(int num)
193 zrot = num;
194 if (saver) {
195 saver->setZRot(zrot);
196 saver->updateMatrix();
197 saver->newEpoch();
201 void KLorenzSetup::slotYRot(int num)
203 yrot = num;
204 if (saver) {
205 saver->setYRot(yrot);
206 saver->updateMatrix();
207 saver->newEpoch();
211 void KLorenzSetup::slotXRot(int num)
213 xrot = num;
214 if (saver) {
215 saver->setXRot(xrot);
216 saver->updateMatrix();
217 saver->newEpoch();
221 void KLorenzSetup::slotHelp()
223 KMessageBox::about(this,i18n("Lorenz Attractor screen saver for KDE\n\nCopyright (c) 2000 Nicolas Brodu"));
226 // Ok pressed - save settings and exit
227 void KLorenzSetup::slotOk()
229 KConfig *config = KGlobal::config();
230 config->setGroup( "Settings" );
232 config->writeEntry( "Speed", speed );
233 config->writeEntry( "Epoch", epoch );
234 config->writeEntry( "Color Rate", crate );
235 config->writeEntry( "ZRot", zrot );
236 config->writeEntry( "YRot", yrot );
237 config->writeEntry( "XRot", xrot );
239 config->sync();
241 accept();
244 void KLorenzSetup::slotDefault()
246 speed = DEFSPEED;
247 epoch = DEFEPOCH;
248 crate = DEFCOLOR;
249 zrot = DEFZROT;
250 yrot = DEFYROT;
251 xrot = DEFXROT;
252 if (saver) {
253 saver->setSpeed(speed);
254 saver->setEpoch(epoch);
255 saver->setCRate(crate);
256 saver->setZRot(zrot);
257 saver->setYRot(yrot);
258 saver->setXRot(xrot);
259 saver->updateMatrix();
260 saver->newEpoch();
262 sps->setValue(speed);
263 eps->setValue(epoch);
264 crs->setValue(crate);
265 zrs->setValue(zrot);
266 yrs->setValue(yrot);
267 xrs->setValue(xrot);
269 /* // User can cancel, or save defaults?
271 KConfig *config = KGlobal::config();
272 config->setGroup( "Settings" );
274 config->writeEntry( "Speed", speed );
275 config->writeEntry( "Epoch", epoch );
276 config->writeEntry( "Color Rate", crate );
277 config->writeEntry( "ZRot", zrot );
278 config->writeEntry( "YRot", yrot );
279 config->writeEntry( "XRot", xrot );
281 config->sync();
285 //-----------------------------------------------------------------------------
288 #ifndef M_PI
289 #define M_PI 3.14159265358979323846
290 #endif
291 const double pi = M_PI;
293 // Homogeneous coordinate transform matrix
294 // I initially wrote it for a Java applet, it is inspired from a
295 // Matrix class in the JDK.
296 // Nicolas Brodu, 1998-2000
297 class Matrix3D
299 // All coefficients
300 double xx, xy, xz, xo;
301 double yx, yy, yz, yo;
302 double zx, zy, zz, zo;
303 // 0, 0, 0, 1 are implicit
304 public:
306 void unit()
308 xx=1.0; xy=0.0; xz=0.0; xo=0.0;
309 yx=0.0; yy=1.0; yz=0.0; yo=0.0;
310 zx=0.0; zy=0.0; zz=1.0; zo=0.0;
313 Matrix3D ()
315 unit();
318 // Translation
319 void translate(double x, double y, double z)
321 xo += x;
322 yo += y;
323 zo += z;
326 // Rotation, in degrees, around the Y axis
327 void rotY(double theta)
329 theta *= pi / 180;
330 double ct = cos(theta);
331 double st = sin(theta);
333 double Nxx = xx * ct + zx * st;
334 double Nxy = xy * ct + zy * st;
335 double Nxz = xz * ct + zz * st;
336 double Nxo = xo * ct + zo * st;
338 double Nzx = zx * ct - xx * st;
339 double Nzy = zy * ct - xy * st;
340 double Nzz = zz * ct - xz * st;
341 double Nzo = zo * ct - xo * st;
343 xo = Nxo;
344 xx = Nxx;
345 xy = Nxy;
346 xz = Nxz;
347 zo = Nzo;
348 zx = Nzx;
349 zy = Nzy;
350 zz = Nzz;
354 // Rotation, in degrees, around the X axis
355 void rotX(double theta)
357 theta *= pi / 180;
358 double ct = cos(theta);
359 double st = sin(theta);
361 double Nyx = yx * ct + zx * st;
362 double Nyy = yy * ct + zy * st;
363 double Nyz = yz * ct + zz * st;
364 double Nyo = yo * ct + zo * st;
366 double Nzx = zx * ct - yx * st;
367 double Nzy = zy * ct - yy * st;
368 double Nzz = zz * ct - yz * st;
369 double Nzo = zo * ct - yo * st;
371 yo = Nyo;
372 yx = Nyx;
373 yy = Nyy;
374 yz = Nyz;
375 zo = Nzo;
376 zx = Nzx;
377 zy = Nzy;
378 zz = Nzz;
382 // Rotation, in degrees, around the Z axis
383 void rotZ(double theta)
385 theta *= pi / 180;
386 double ct = cos(theta);
387 double st = sin(theta);
389 double Nyx = yx * ct + xx * st;
390 double Nyy = yy * ct + xy * st;
391 double Nyz = yz * ct + xz * st;
392 double Nyo = yo * ct + xo * st;
394 double Nxx = xx * ct - yx * st;
395 double Nxy = xy * ct - yy * st;
396 double Nxz = xz * ct - yz * st;
397 double Nxo = xo * ct - yo * st;
399 yo = Nyo;
400 yx = Nyx;
401 yy = Nyy;
402 yz = Nyz;
403 xo = Nxo;
404 xx = Nxx;
405 xy = Nxy;
406 xz = Nxz;
409 // Multiply by a projection matrix, with camera f
410 // f 0 0 0 x f*x
411 // 0 f 0 0 * y = f*y
412 // 0 0 1 f z z+f
413 // 0 0 0 1 1 1
414 // So, it it easy to find the 2D coordinates after the transform
415 // u = f*x / (z+f)
416 // v = f*y / (z+f)
417 void proj(double f)
419 xx*=f;
420 xy*=f;
421 xz*=f;
422 xo*=f;
423 yx*=f;
424 yy*=f;
425 yz*=f;
426 yo*=f;
427 zo+=f;
430 // Apply the transformation 3D => 2D
431 void transform(double x, double y, double z, double &u, double& v, double& w)
433 u = x * xx + y * xy + z * xz + xo;
434 v = x * yx + y * yy + z * yz + yo;
435 w = x * zx + y * zy + z * zz + zo;
439 KLorenzSaver::KLorenzSaver( WId id ) : KScreenSaver( id )
441 readSettings();
443 // Create a transform matrix with the parameters
444 mat = new Matrix3D();
445 updateMatrix();
447 colorContext = QColor::enterAllocContext();
448 setBackgroundColor( black );
449 newEpoch();
451 timer.start( 10 );
452 connect( &timer, SIGNAL( timeout() ), SLOT( drawOnce() ) );
455 KLorenzSaver::~KLorenzSaver()
457 delete mat;
458 mat=0;
459 timer.stop();
460 QColor::leaveAllocContext();
461 QColor::destroyAllocContext( colorContext );
464 // read configuration settings from config file
465 void KLorenzSaver::readSettings()
467 KConfig *config = KGlobal::config();
468 config->setGroup( "Settings" );
470 speed = config->readNumEntry( "Speed", DEFSPEED );
471 epoch = config->readNumEntry( "Epoch", DEFEPOCH );
472 zrot = config->readNumEntry( "ZRot", DEFZROT );
473 yrot = config->readNumEntry( "YRot", DEFZROT );
474 xrot = config->readNumEntry( "XRot", DEFZROT );
476 int crate_num = config->readNumEntry( "Color Rate", DEFCOLOR );
477 crate = (double)crate_num / (double)MAXCOLOR;
480 void KLorenzSaver::setSpeed(int num)
482 speed = num;
485 void KLorenzSaver::setEpoch(int num)
487 epoch = num;
490 void KLorenzSaver::setZRot(int num)
492 zrot = num;
495 void KLorenzSaver::setYRot(int num)
497 yrot = num;
500 void KLorenzSaver::setXRot(int num)
502 xrot = num;
505 void KLorenzSaver::setCRate(int num)
507 crate = (double)num / (double)MAXCOLOR;
510 void KLorenzSaver::updateMatrix()
512 // reset matrix
513 mat->unit();
514 // Remove the mean before the rotations...
515 mat->translate(-0.95413, -0.96740, -23.60065);
516 mat->rotZ(zrot);
517 mat->rotY(yrot);
518 mat->rotX(xrot);
519 mat->translate(0, 0, 100);
520 mat->proj(1);
523 void KLorenzSaver::newEpoch()
525 // Start at a random position, somewhere around the mean
526 x = 0.95-25.0+50.0*kapp->random() / (RAND_MAX+1.0);
527 y = 0.97-25.0+50.0*kapp->random() / (RAND_MAX+1.0);
528 z = 23.6-25.0+50.0*kapp->random() / (RAND_MAX+1.0);
529 // start at some random 'time' as well to have different colors
530 t = 10000.0*kapp->random() / (RAND_MAX+1.0);
531 erase();
532 e=0; // reset epoch counter
535 // Computes the derivatives using Lorenz equations
536 static void lorenz(double x, double y, double z, double& dx, double& dy, double& dz)
538 dx = 10*(y-x);
539 dy = 28*x - y - x*z;
540 dz = x*y - z*8.0/3.0;
543 // Use a simple Runge-Kutta formula to draw a few points
544 // No need to go beyond 2nd order for a screensaver!
545 void KLorenzSaver::drawOnce()
547 double kx, ky, kz, dx, dy, dz;
548 const double h = 0.0001;
549 const double tqh = h * 3.0 / 4.0;
550 QPainter p(this);
552 for (int i=0; i<speed; i++) {
553 // Runge-Kutta formula
554 lorenz(x,y,z,dx,dy,dz);
555 lorenz(x + tqh*dx, y + tqh*dy, z + tqh*dz, kx, ky, kz);
556 x += h*(dx/3.0+2*kx/3.0);
557 y += h*(dy/3.0+2*ky/3.0);
558 z += h*(dz/3.0+2*kz/3.0);
559 // Apply transform
560 mat->transform(x,y,z,kx,ky,kz);
561 // Choose a color
562 p.setPen(
563 QColor((int)(sin(t*crate/pi)*127+128),
564 (int)(sin(t*crate/(pi-1))*127+128),
565 (int)(sin(t*crate/(pi-2))*127+128)).pixel() );
566 // Draw a point
567 p.drawPoint( (int)(kx*width()*1.5/kz)+(int)(width()/2),
568 (int)(ky*height()*1.5/kz)+(int)(height()/2));
569 t+=h;
571 if (++e>=epoch) newEpoch();