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>
33 // libkscreensaver interface
34 class KLorenzSaverInterface
: public KScreenSaverInterface
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
);
66 #define DEFZROT 104 //100
69 #define DEFYROT -19 //80
72 #define DEFXROT 25 //20
74 #define MAXEPOCH 30000
80 //-----------------------------------------------------------------------------
81 // dialog to setup screen saver parameters
83 KLorenzSetup::KLorenzSetup( QWidget
*parent
, const char *name
)
86 setCaption(i18n( "Setup Lorenz Attractor" ));
87 setButtons(Ok
|Cancel
|Default
|Help
);
92 setButtonText( Help
, i18n( "A&bout" ) );
93 QWidget
*main
= new QWidget(this);
96 QHBoxLayout
*tl
= new QHBoxLayout( main
, 0, spacingHint() );
97 QVBoxLayout
*tl1
= new QVBoxLayout
;
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 ) ) );
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 ) ) );
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 ) ) );
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 ) ) );
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 ) ) );
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 ) ) );
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()
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
)
191 if (saver
) saver
->setSpeed(speed
);
194 void KLorenzSetup::slotEpoch(int num
)
197 if (saver
) saver
->setEpoch(epoch
);
200 void KLorenzSetup::slotCRate(int num
)
203 if (saver
) saver
->setCRate(crate
);
206 void KLorenzSetup::slotZRot(int num
)
210 saver
->setZRot(zrot
);
211 saver
->updateMatrix();
216 void KLorenzSetup::slotYRot(int num
)
220 saver
->setYRot(yrot
);
221 saver
->updateMatrix();
226 void KLorenzSetup::slotXRot(int num
)
230 saver
->setXRot(xrot
);
231 saver
->updateMatrix();
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
);
259 void KLorenzSetup::slotDefault()
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();
277 sps
->setValue(speed
);
278 eps
->setValue(epoch
);
279 crs
->setValue(crate
);
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 );
300 //-----------------------------------------------------------------------------
304 #define M_PI 3.14159265358979323846
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
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
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;
334 void translate(double x
, double y
, double z
)
341 // Rotation, in degrees, around the Y axis
342 void rotY(double theta
)
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
;
369 // Rotation, in degrees, around the X axis
370 void rotX(double theta
)
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
;
397 // Rotation, in degrees, around the Z axis
398 void rotZ(double theta
)
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
;
424 // Multiply by a projection matrix, with camera f
429 // So, it it easy to find the 2D coordinates after the transform
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
)
458 // Create a transform matrix with the parameters
459 mat
= new Matrix3D();
462 setBackgroundColor( Qt::black
);
466 connect( &timer
, SIGNAL( timeout() ), SLOT( drawOnce() ) );
469 KLorenzSaver::~KLorenzSaver()
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
)
497 void KLorenzSaver::setEpoch(int num
)
502 void KLorenzSaver::setZRot(int num
)
507 void KLorenzSaver::setYRot(int num
)
512 void KLorenzSaver::setXRot(int num
)
517 void KLorenzSaver::setCRate(int num
)
519 crate
= (double)num
/ (double)MAXCOLOR
;
522 void KLorenzSaver::updateMatrix()
526 // Remove the mean before the rotations...
527 mat
->translate(-0.95413, -0.96740, -23.60065);
531 mat
->translate(0, 0, 100);
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);
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
)
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;
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);
572 mat
->transform(x
,y
,z
,kx
,ky
,kz
);
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() );
579 p
.drawPoint( (int)(kx
*width()*1.5/kz
)+(int)(width()/2),
580 (int)(ky
*height()*1.5/kz
)+(int)(height()/2));
583 if (++e
>=epoch
) newEpoch();