1 //-----------------------------------------------------------------------------
3 // Lorenz - Lorenz Attractor screen saver
4 // Nicolas Brodu, brodu@kde.org, 2000
6 // Portions of code from kblankscrn and khop.
9 // I release my code as GPL, but see the other headers and the README
20 #include <QVBoxLayout>
21 #include <QHBoxLayout>
23 #include <kapplication.h>
27 #include <kmessagebox.h>
32 // libkscreensaver interface
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();
55 #define DEFZROT 104 //100
58 #define DEFYROT -19 //80
61 #define DEFXROT 25 //20
63 #define MAXEPOCH 30000
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 )
78 setButtonText( Help
, i18n( "A&bout" ) );
79 QWidget
*main
= makeMainWidget();
81 QHBoxLayout
*tl
= new QHBoxLayout( main
, 0, spacingHint() );
82 QVBoxLayout
*tl1
= new QVBoxLayout
;
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 ) ) );
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 ) ) );
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 ) ) );
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 ) ) );
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 ) ) );
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 ) ) );
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()
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
)
176 if (saver
) saver
->setSpeed(speed
);
179 void KLorenzSetup::slotEpoch(int num
)
182 if (saver
) saver
->setEpoch(epoch
);
185 void KLorenzSetup::slotCRate(int num
)
188 if (saver
) saver
->setCRate(crate
);
191 void KLorenzSetup::slotZRot(int num
)
195 saver
->setZRot(zrot
);
196 saver
->updateMatrix();
201 void KLorenzSetup::slotYRot(int num
)
205 saver
->setYRot(yrot
);
206 saver
->updateMatrix();
211 void KLorenzSetup::slotXRot(int num
)
215 saver
->setXRot(xrot
);
216 saver
->updateMatrix();
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
);
244 void KLorenzSetup::slotDefault()
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();
262 sps
->setValue(speed
);
263 eps
->setValue(epoch
);
264 crs
->setValue(crate
);
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 );
285 //-----------------------------------------------------------------------------
289 #define M_PI 3.14159265358979323846
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
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
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;
319 void translate(double x
, double y
, double z
)
326 // Rotation, in degrees, around the Y axis
327 void rotY(double theta
)
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
;
354 // Rotation, in degrees, around the X axis
355 void rotX(double theta
)
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
;
382 // Rotation, in degrees, around the Z axis
383 void rotZ(double theta
)
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
;
409 // Multiply by a projection matrix, with camera f
414 // So, it it easy to find the 2D coordinates after the transform
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
)
443 // Create a transform matrix with the parameters
444 mat
= new Matrix3D();
447 colorContext
= QColor::enterAllocContext();
448 setBackgroundColor( black
);
452 connect( &timer
, SIGNAL( timeout() ), SLOT( drawOnce() ) );
455 KLorenzSaver::~KLorenzSaver()
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
)
485 void KLorenzSaver::setEpoch(int num
)
490 void KLorenzSaver::setZRot(int num
)
495 void KLorenzSaver::setYRot(int num
)
500 void KLorenzSaver::setXRot(int num
)
505 void KLorenzSaver::setCRate(int num
)
507 crate
= (double)num
/ (double)MAXCOLOR
;
510 void KLorenzSaver::updateMatrix()
514 // Remove the mean before the rotations...
515 mat
->translate(-0.95413, -0.96740, -23.60065);
519 mat
->translate(0, 0, 100);
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);
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
)
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;
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);
560 mat
->transform(x
,y
,z
,kx
,ky
,kz
);
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() );
567 p
.drawPoint( (int)(kx
*width()*1.5/kz
)+(int)(width()/2),
568 (int)(ky
*height()*1.5/kz
)+(int)(height()/2));
571 if (++e
>=epoch
) newEpoch();