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
18 #include <qcolormap.h>
21 #include <kapplication.h>
25 #include <kmessagebox.h>
31 // libkscreensaver interface
32 class KLorenzSaverInterface
: public KScreenSaverInterface
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
);
64 #define DEFZROT 104 //100
67 #define DEFYROT -19 //80
70 #define DEFXROT 25 //20
72 #define MAXEPOCH 30000
78 //-----------------------------------------------------------------------------
79 // dialog to setup screen saver parameters
81 KLorenzSetup::KLorenzSetup( QWidget
*parent
)
84 setCaption(i18n( "Setup Lorenz Attractor" ));
85 setButtons(Ok
|Cancel
|Default
|Help
);
90 setButtonText( Help
, i18n( "A&bout" ) );
91 QWidget
*main
= new QWidget(this);
94 QHBoxLayout
*tl
= new QHBoxLayout( main
);
95 tl
->setSpacing( spacingHint() );
96 QVBoxLayout
*tl1
= new QVBoxLayout
;
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 ) ) );
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 ) ) );
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
);
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 ) ) );
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);
149 zrs
->setMinimumSize( 120, 20 );
150 zrs
->setTickPosition(QSlider::TicksBelow
);
151 zrs
->setTickInterval(36);
152 connect( zrs
, SIGNAL( valueChanged( int ) ), SLOT( slotZRot( int ) ) );
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);
163 yrs
->setMinimumSize( 120, 20 );
164 yrs
->setTickPosition(QSlider::TicksBelow
);
165 yrs
->setTickInterval(36);
166 connect( yrs
, SIGNAL( valueChanged( int ) ), SLOT( slotYRot( int ) ) );
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);
177 xrs
->setMinimumSize( 120, 20 );
178 xrs
->setTickPosition(QSlider::TicksBelow
);
179 xrs
->setTickInterval(36);
180 connect( xrs
, SIGNAL( valueChanged( int ) ), SLOT( slotXRot( int ) ) );
183 preview
= new QWidget( main
);
184 preview
->setFixedSize( 220, 165 );
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()
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
)
220 if (saver
) saver
->setSpeed(speed
);
223 void KLorenzSetup::slotEpoch(int num
)
226 if (saver
) saver
->setEpoch(epoch
);
229 void KLorenzSetup::slotCRate(int num
)
232 if (saver
) saver
->setCRate(crate
);
235 void KLorenzSetup::slotZRot(int num
)
239 saver
->setZRot(zrot
);
240 saver
->updateMatrix();
245 void KLorenzSetup::slotYRot(int num
)
249 saver
->setYRot(yrot
);
250 saver
->updateMatrix();
255 void KLorenzSetup::slotXRot(int num
)
259 saver
->setXRot(xrot
);
260 saver
->updateMatrix();
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
);
287 void KLorenzSetup::slotDefault()
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();
305 sps
->setValue(speed
);
306 eps
->setValue(epoch
);
307 crs
->setValue(crate
);
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 );
328 //-----------------------------------------------------------------------------
332 #define M_PI 3.14159265358979323846
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
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
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;
362 void translate(double x
, double y
, double z
)
369 // Rotation, in degrees, around the Y axis
370 void rotY(double theta
)
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
;
397 // Rotation, in degrees, around the X axis
398 void rotX(double theta
)
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
;
425 // Rotation, in degrees, around the Z axis
426 void rotZ(double theta
)
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
;
452 // Multiply by a projection matrix, with camera f
457 // So, it it easy to find the 2D coordinates after the transform
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
)
486 // Create a transform matrix with the parameters
487 mat
= new Matrix3D();
492 palette
.setColor( backgroundRole(), Qt::black
);
493 setPalette( palette
);
498 connect( &timer
, SIGNAL( timeout() ), SLOT( update() ) );
501 KLorenzSaver::~KLorenzSaver()
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
)
528 void KLorenzSaver::setEpoch(int num
)
533 void KLorenzSaver::setZRot(int num
)
538 void KLorenzSaver::setYRot(int num
)
543 void KLorenzSaver::setXRot(int num
)
548 void KLorenzSaver::setCRate(int num
)
550 crate
= (double)num
/ (double)MAXCOLOR
;
553 void KLorenzSaver::updateMatrix()
557 // Remove the mean before the rotations...
558 mat
->translate(-0.95413, -0.96740, -23.60065);
562 mat
->translate(0, 0, 100);
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
)
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;
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);
608 mat
->transform(x
,y
,z
,kx
,ky
,kz
);
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))) );
615 p
.drawPoint( (int)(kx
*width()*1.5/kz
)+(int)(width()/2),
616 (int)(ky
*height()*1.5/kz
)+(int)(height()/2));