SVN_SILENT made messages (.desktop file)
[kdeartwork.git] / kscreensaver / kdesavers / rotation.cpp
blobb1eb1adf744abb1c79465310688248159c509235
1 /** @file
3 * KRotation screen saver for KDE
5 * The screen saver displays a physically realistic simulation of a force free
6 * rotating asymmetric body. The equations of motion for such a rotation, the
7 * Euler equations, are integrated numerically by the Runge-Kutta method.
9 * Copyright (C) 2004 Georg Drenkhahn, Georg.Drenkhahn@gmx.net
11 * This program is free software; you can redistribute it and/or modify it under
12 * the terms of the GNU General Public License as published by the Free Software
13 * Foundation; either version 2 of the License or (at your option) version 3 or
14 * any later version accepted by the membership of KDE e.V. (or its successor
15 * approved by the membership of KDE e.V.), which shall act as a proxy defined
16 * in Section 14 of version 3 of the license.
18 * This program is distributed in the hope that it will be useful, but WITHOUT
19 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
21 * details.
24 #define QT_NO_COMPAT
26 // std. C++ headers
27 #include <cstdlib>
29 // STL headers
30 #include <deque>
32 // Qt headers
33 #include <QCheckBox>
34 #include <QLineEdit>
35 #include <QValidator>
36 #include <QToolTip>
37 #include <QResizeEvent>
39 // KDE headers
40 #include <KLocale>
41 #include <KGlobal>
42 #include <KConfig>
43 #include <KDebug>
44 #include <KMessageBox>
46 // Eigen2 from KDE support
47 #include <Eigen/Core>
48 #include <Eigen/Geometry>
49 // import all Eigen types, Transform and Quaternion are not part of the
50 // namespace part published by USING_PART_OF_NAMESPACE_EIGEN
51 using namespace Eigen;
53 // the screen saver preview area class
54 #include "sspreviewarea.h"
56 #include "rotation.h" // own interfaces
57 #include "rotation.moc"
59 /** Version number of this screen saver */
60 #define KROTATION_VERSION "2.0"
62 #ifndef M_PI
63 #define M_PI 3.14159265358979323846
64 #endif
66 // libkscreensaver interface
67 class KRotationSaverInterface : public KScreenSaverInterface
69 public:
70 virtual KAboutData* aboutData()
72 return new KAboutData(
73 "krotation.kss", 0,
74 ki18n("Simulation of a force free rotating asymmetric body"),
75 KROTATION_VERSION,
76 ki18n("Simulation of a force free rotating asymmetric body"));
79 /** function to create screen saver object */
80 virtual KScreenSaver* create(WId id)
82 return new KRotationSaver(id);
85 /** function to create setup dialog for screen saver */
86 virtual QDialog* setup()
88 return new KRotationSetup();
92 int main(int argc, char *argv[])
94 KRotationSaverInterface kss;
95 return kScreenSaverMain(argc, argv, kss);
98 //-----------------------------------------------------------------------------
99 // EulerOdeSolver implementation
100 //-----------------------------------------------------------------------------
102 EulerOdeSolver::EulerOdeSolver(
103 const double& t,
104 const double& dt,
105 const double& A,
106 const double& B,
107 const double& C,
108 Vector12d& y,
109 const double& eps)
110 : RkOdeSolver<double,12>(t, y, dt, eps),
111 m_A(A),
112 m_B(B),
113 m_C(C)
117 Vector12d EulerOdeSolver::f(const double& x, const Vector12d& y) const
119 // unused
120 (void)x;
122 // vec omega in body coor. sys.: omega_body = (p, q, r)
123 const Vector3d omega_body(y.start<3>());
125 // body unit vectors in fixed frame coordinates
126 Matrix3d e;
127 for (int i=0; i<3; ++i)
129 e.col(i) = y.segment<3>(3+i*3);
132 // vec omega in global fixed coor. sys.
133 const Vector3d omega = e * omega_body;
135 // return vector y'
136 Vector12d ypr;
138 // omega_body'
139 ypr[0] = -(m_C-m_B)/m_A * omega_body[1] * omega_body[2]; // p'
140 ypr[1] = -(m_A-m_C)/m_B * omega_body[2] * omega_body[0]; // q'
141 ypr[2] = -(m_B-m_A)/m_C * omega_body[0] * omega_body[1]; // r'
143 // e1', e2', e3'
144 for (int i=0; i<3; ++i)
146 ypr.segment<3>(3+i*3) = omega.cross(e.col(i));
149 return ypr;
151 //-----------------------------------------------------------------------------
154 //-----------------------------------------------------------------------------
155 // Rotation: screen saver widget
156 //-----------------------------------------------------------------------------
158 RotationGLWidget::RotationGLWidget(
159 QWidget* parent,
160 const KRotationSaver& saver)
161 : QGLWidget(parent),
162 m_eyeR(25),
163 m_eyeTheta(1),
164 m_eyePhi(M_PI*0.25),
165 m_boxSize(Vector3d::Ones()),
166 m_fixedAxses(0),
167 m_bodyAxses(0),
168 m_lightR(10),
169 m_lightTheta(M_PI/4),
170 m_lightPhi(0),
171 m_bodyAxsesLength(6),
172 m_fixedAxsesLength(8),
173 m_saver(saver)
175 /* Set the box sizes from the momenta of inertia. J is the 3 vector with
176 * momenta of inertia with respect to the 3 figure axes. */
178 const Vector3d& J = m_saver.J();
180 /* the default values must be valid so that w,h,d are real! */
181 const GLfloat x2 = 6.0*(-J[0] + J[1] + J[2]);
182 const GLfloat y2 = 6.0*( J[0] - J[1] + J[2]);
183 const GLfloat z2 = 6.0*( J[0] + J[1] - J[2]);
185 if ((x2>=0) && (y2>=0) && (z2>=0))
187 m_boxSize = Vector3d(std::sqrt(x2), std::sqrt(y2), std::sqrt(z2));
189 else
191 kError() << "parameter error";
195 /* --------- protected methods ----------- */
197 void RotationGLWidget::initializeGL(void)
199 qglClearColor(QColor(Qt::black)); // set color to clear the background
201 glClearDepth(1); // depth buffer setup
202 glEnable(GL_DEPTH_TEST); // depth testing
203 glDepthFunc(GL_LEQUAL); // type of depth test
205 glShadeModel(GL_SMOOTH); // smooth color shading in poygons
207 // nice perspective calculation
208 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
210 // set up the light
211 glEnable(GL_LIGHTING);
212 glEnable(GL_LIGHT0);
213 // set position of light0
214 GLfloat lightPos[4]=
215 {m_lightR * std::sin(m_lightTheta) * std::sin(m_lightPhi),
216 m_lightR * std::sin(m_lightTheta) * std::cos(m_lightPhi),
217 m_lightR * std::cos(m_lightTheta), 1.};
218 glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
220 // enable setting the material colour by glColor()
221 glEnable(GL_COLOR_MATERIAL);
223 // set up display lists
225 if (m_fixedAxses == 0)
227 m_fixedAxses = glGenLists(1); // list to be returned
229 glNewList(m_fixedAxses, GL_COMPILE);
231 // fixed coordinate system axes
233 glPushMatrix();
234 glLoadIdentity();
236 // z-axis, blue
237 qglColor(QColor(Qt::blue));
238 myGlArrow(m_fixedAxsesLength, 0.5f, 0.03f, 0.1f);
240 // x-axis, red
241 qglColor(QColor(Qt::red));
242 glRotatef(90, 0, 1, 0);
244 myGlArrow(m_fixedAxsesLength, 0.5f, 0.03f, 0.1f);
246 // y-axis, green
247 qglColor(QColor(Qt::green));
248 glLoadIdentity();
249 glRotatef(-90, 1, 0, 0);
250 myGlArrow(m_fixedAxsesLength, 0.5f, 0.03f, 0.1f);
252 glPopMatrix();
253 glEndList();
254 // end of axes object list
257 // box and box-axses
258 if (m_bodyAxses == 0)
260 m_bodyAxses = glGenLists(1); // list to be returned
262 glNewList(m_bodyAxses, GL_COMPILE);
264 // z-axis, blue
265 qglColor(QColor(Qt::blue));
266 myGlArrow(m_bodyAxsesLength, 0.5f, 0.03f, 0.1f);
268 // x-axis, red
269 qglColor(QColor(Qt::red));
270 glPushMatrix();
271 glRotatef(90, 0, 1, 0);
272 myGlArrow(m_bodyAxsesLength, 0.5f, 0.03f, 0.1f);
273 glPopMatrix();
275 // y-axis, green
276 qglColor(QColor(Qt::green));
277 glPushMatrix();
278 glRotatef(-90, 1, 0, 0);
279 myGlArrow(m_bodyAxsesLength, 0.5f, 0.03f, 0.1f);
280 glPopMatrix();
282 glEndList();
285 void RotationGLWidget::draw_traces(void)
287 const std::deque<Matrix3d>&e = m_saver.e();
289 // traces must contain at least 2 elements
290 if (e.size() <= 1)
292 return;
295 glPushMatrix();
296 glScalef(m_bodyAxsesLength, m_bodyAxsesLength, m_bodyAxsesLength);
298 glEnable(GL_BLEND);
299 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
301 for (int j=0; j<3; ++j)
303 if (m_saver.traceFlag(j))
305 // emission colour
306 GLfloat em[4] = {0,0,0,1};
307 em[j] = 1; // set either red, green, blue emission colour
309 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, em);
310 glColor4fv(em);
312 // set iterator of the tail part
313 std::deque<Matrix3d>::const_iterator eit = e.begin();
314 std::deque<Matrix3d>::const_iterator tail =
315 e.begin() +
316 static_cast<std::deque<Matrix3d>::difference_type>
317 (0.9 * e.size());
319 glBegin(GL_LINES);
320 for (; eit < e.end()-1; ++eit)
322 glVertex3f((*eit)(0,j), (*eit)(1,j), (*eit)(2,j));
323 // decrease transparency for tail section
324 if (eit > tail)
326 em[3] =
327 static_cast<GLfloat>
328 (1.0 - double(eit-tail)/(0.1 * e.size()));
330 glColor4fv(em);
331 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, em);
332 glVertex3f((*(eit+1))(0,j), (*(eit+1))(1,j), (*(eit+1))(2,j));
334 glEnd();
338 glDisable(GL_BLEND);
340 glPopMatrix();
343 void RotationGLWidget::paintGL(void)
345 // clear color and depth buffer
346 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
348 glMatrixMode(GL_MODELVIEW); // select modelview matrix
350 glLoadIdentity();
351 GLfloat const em[] = {0,0,0,1};
352 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, em);
354 glPushMatrix();
355 // calculate the transform which rotates the unit z vector onto omega
356 glLoadMatrixd(
357 Transform<double,3>(
358 Quaternion<double>()
359 .setFromTwoVectors(Vector3d::UnitZ(), m_saver.omega())
360 .toRotationMatrix())
361 .data());
362 // draw the white omega arrow
363 qglColor(QColor(Qt::white));
364 myGlArrow(7, .5f, .1f, 0.2f);
365 glPopMatrix();
367 // draw the fixed axes
368 glCallList(m_fixedAxses);
371 // create transformation/rotation matrix from the body and its unit axes
372 glPushMatrix();
373 glLoadMatrixd(
374 Transform<double,3>(m_saver.e().front())
375 .data());
377 // draw the body unit axis
378 glCallList(m_bodyAxses);
380 // scaling from a cube to the rotating body
381 glScalef(m_boxSize[0]/2, m_boxSize[1]/2, m_boxSize[2]/2);
383 // paint box
384 glBegin(GL_QUADS);
385 // front (z)
386 qglColor(QColor(Qt::blue));
387 glNormal3f( 0,0,1);
388 glVertex3f( 1, 1, 1);
389 glVertex3f(-1, 1, 1);
390 glVertex3f(-1, -1, 1);
391 glVertex3f( 1, -1, 1);
392 // back (-z)
393 glNormal3f( 0,0,-1);
394 glVertex3f( 1, 1, -1);
395 glVertex3f(-1, 1, -1);
396 glVertex3f(-1, -1, -1);
397 glVertex3f( 1, -1, -1);
398 // top (y)
399 qglColor(QColor(Qt::green));
400 glNormal3f( 0,1,0);
401 glVertex3f( 1, 1, 1);
402 glVertex3f( 1, 1, -1);
403 glVertex3f(-1, 1, -1);
404 glVertex3f(-1, 1, 1);
405 // bottom (-y)
406 glNormal3f( 0,-1,0);
407 glVertex3f( 1, -1, 1);
409 glVertex3f( 1, -1, -1);
410 glVertex3f(-1, -1, -1);
411 glVertex3f(-1, -1, 1);
412 // left (-x)
413 qglColor(QColor(Qt::red));
414 glNormal3f( -1,0,0);
415 glVertex3f(-1, 1, 1);
416 glVertex3f(-1, 1, -1);
417 glVertex3f(-1, -1, -1);
418 glVertex3f(-1, -1, 1);
419 // right (x)
420 glNormal3f( 1,0,0);
421 glVertex3f( 1, 1, 1);
422 glVertex3f( 1, 1, -1);
423 glVertex3f( 1, -1, -1);
424 glVertex3f( 1, -1, 1);
425 glEnd();
427 glPopMatrix();
429 // draw the traces
430 draw_traces();
432 glFlush();
435 void RotationGLWidget::resizeGL(int w, int h)
437 kDebug() << "w=" << w << ", h=" << h << "\n";
439 // prevent division by zero
440 if (h == 0)
442 return;
445 // set the new view port
446 glViewport(0, 0, (GLint)w, (GLint)h);
448 // set up projection matrix
449 glMatrixMode(GL_PROJECTION);
450 glLoadIdentity();
451 // Perspective view
452 gluPerspective(40.0f, (GLdouble)w/(GLdouble)h, 1.0, 100.0f);
454 // Viewing transformation, position for better view
455 // Theta is polar angle 0<Theta<Pi
456 gluLookAt(
457 m_eyeR * std::sin(m_eyeTheta) * std::sin(m_eyePhi),
458 m_eyeR * std::sin(m_eyeTheta) * std::cos(m_eyePhi),
459 m_eyeR * std::cos(m_eyeTheta),
460 0,0,0,
461 0,0,1);
464 /* --------- privat methods ----------- */
466 void RotationGLWidget::myGlArrow(
467 GLfloat total_length, GLfloat head_length,
468 GLfloat base_width, GLfloat head_width)
470 GLUquadricObj* const quadAx = gluNewQuadric();
471 glPushMatrix();
472 gluCylinder(
473 quadAx, base_width, base_width,
474 total_length - head_length, 10, 1);
475 glTranslatef(0, 0, total_length - head_length);
476 gluCylinder(quadAx, head_width, 0, head_length, 10, 1);
477 glPopMatrix();
478 gluDeleteQuadric(quadAx);
482 //-----------------------------------------------------------------------------
483 // KRotationSaver: screen saver class
484 //-----------------------------------------------------------------------------
486 // class methods
488 KRotationSaver::KRotationSaver(WId id)
489 : KScreenSaver(id),
490 m_solver(0),
491 m_glArea(0),
492 m_timer(0),
493 m_J(4,2,3), // fixed box sizes!
494 m_traceLengthSeconds(sm_traceLengthSecondsDefault),
495 m_Lz(sm_LzDefault),
496 m_initEulerPhi(0),
497 m_initEulerPsi(0),
498 m_initEulerTheta(sm_initEulerThetaDefault)
500 // no need to set our parent widget's background here, the GL widget sets its
501 // own background color
503 readSettings(); // read global settings
505 // init m_e1,m_e2,m_e3,m_omega, construct and init m_solver
506 initData();
508 // create gl widget w/o parent
509 m_glArea = new RotationGLWidget(0, *this);
510 embed(m_glArea); // embed gl widget and resize it
511 m_glArea->show(); // show embedded gl widget
513 // set up cyclic timer
514 m_timer = new QTimer(this);
515 m_timer->start(sm_deltaT);
516 connect(m_timer, SIGNAL(timeout()), this, SLOT(doTimeStep()));
519 KRotationSaver::~KRotationSaver()
521 m_timer->stop();
523 // m_timer is automatically deleted with parent KRotationSaver
524 delete m_glArea;
525 delete m_solver;
528 void KRotationSaver::initData()
530 // rotation by phi around z = zhat axis
531 Matrix3d et(AngleAxisd(m_initEulerPhi, Vector3d::UnitZ()));
532 // rotation by theta around new x axis
533 et = AngleAxisd(m_initEulerPhi, et.col(0)).toRotationMatrix() * et;
534 // rotation by psi around new z axis
535 et = AngleAxisd(m_initEulerPsi, et.col(2)).toRotationMatrix() * et;
537 // set first vector in deque
538 m_e.clear(); m_e.push_front(et);
540 /* calc L in body frame:
542 * determine unit-axes of fixed frame in body coordinates, invert the
543 * transformations above for unit vectors of the body frame */
545 // rotation by -psi along z axis
546 Matrix3d e_body(AngleAxisd(-m_initEulerPsi, Vector3d::UnitZ()));
547 // rotation by -theta along new x axis
548 e_body = AngleAxisd(-m_initEulerTheta, e_body.col(0)).toRotationMatrix() * e_body;
550 // omega_body = L_body * J_body^(-1)
551 // component-wise division because J_body is a diagonal matrix
552 Vector3d omega_body = (m_Lz * e_body.col(2)).cwise() / m_J;
554 // initial rotation vector
555 m_omega = et * omega_body;
557 // assemble initial y for solver
558 Vector12d y;
559 y.start<3>() = omega_body;
560 // 3 basis vectors of body system in fixed coordinates
561 y.segment<3>(3) = et.col(0);
562 y.segment<3>(6) = et.col(1);
563 y.segment<3>(9) = et.col(2);
565 if (m_solver!=0)
567 // deleting the solver is necessary when parameters are changed in the
568 // configuration dialog.
569 delete m_solver;
571 // init solver
572 m_solver = new EulerOdeSolver(
573 0.0, // t
574 0.01, // first dt step size estimation
575 m_J[0], m_J[1], m_J[2], // A,B,C
576 y, // omega_body,e1,e2,e3
577 1e-5); // eps
580 void KRotationSaver::readSettings()
582 // read configuration settings from config file
583 KConfigGroup config(KGlobal::config(), "Settings");
585 // internal saver parameters are set to stored values or left at their
586 // default values if stored values are out of range
587 setTraceFlag(0, config.readEntry("x trace", sm_traceFlagDefault[0]));
588 setTraceFlag(1, config.readEntry("y trace", sm_traceFlagDefault[1]));
589 setTraceFlag(2, config.readEntry("z trace", sm_traceFlagDefault[2]));
590 setRandomTraces(config.readEntry("random traces", sm_randomTracesDefault));
591 setTraceLengthSeconds(
592 config.readEntry("length", sm_traceLengthSecondsDefault));
593 setLz(
594 config.readEntry("Lz", sm_LzDefault));
595 setInitEulerTheta(
596 config.readEntry("theta", sm_initEulerThetaDefault));
599 void KRotationSaver::setTraceLengthSeconds(const double& t)
601 if ((t >= sm_traceLengthSecondsLimitLower)
602 && (t <= sm_traceLengthSecondsLimitUpper))
604 m_traceLengthSeconds = t;
608 void KRotationSaver::setLz(const double& Lz)
610 if ((Lz >= sm_LzLimitLower) && (Lz <= sm_LzLimitUpper))
612 m_Lz = Lz;
616 void KRotationSaver::setInitEulerTheta(const double& theta)
618 if ((theta >= sm_initEulerThetaLimitLower)
619 && (theta <= sm_initEulerThetaLimitUpper))
621 m_initEulerTheta = theta;
625 // public slots
627 void KRotationSaver::doTimeStep()
629 // integrate a step ahead
630 m_solver->integrate(0.001*sm_deltaT);
632 // read new y
633 Vector12d y = m_solver->Y();
635 std::deque<Vector3d>::size_type
636 max_vec_length =
637 static_cast<std::deque<Vector3d>::size_type>
638 ( m_traceLengthSeconds/(0.001*sm_deltaT) );
640 // construct matrix from solution vector
641 // read out new body coordinate system
642 Matrix3d et;
643 for (int j=0; j<3; ++j)
645 et.col(j) = y.segment<3>(3*j+3);
648 m_e.push_front(et);
650 if (max_vec_length > 0)
652 m_e.push_front(et);
653 while (m_e.size() > max_vec_length)
655 m_e.pop_back();
658 else
660 // only set the 1. element
661 m_e.front() = et;
662 // and delete all other emements
663 if (m_e.size() > 1)
665 m_e.resize(1);
669 // current rotation vector omega
670 m_omega = m_e.front() * y.start<3>();
672 // set new random traces every 10 seconds
673 if (m_randomTraces)
675 static unsigned int counter=0;
676 ++counter;
677 if (counter > unsigned(10.0/(0.001*sm_deltaT)))
679 counter=0;
680 for (int i=0; i<3; ++i)
682 m_traceFlag[i] = (rand()%2==1);
687 m_glArea->updateGL();
689 // no need to restart timer here, it is a cyclic timer
692 // public slot of KRotationSaver, forward resize event to public slot of glArea
693 // to allow the resizing of the gl area withing the setup dialog
694 void KRotationSaver::resizeGlArea(QResizeEvent* e)
696 m_glArea->resize(e->size());
699 // public static class members
701 const double KRotationSaver::sm_traceLengthSecondsLimitLower = 0.0;
702 const double KRotationSaver::sm_traceLengthSecondsLimitUpper = 99.0;
703 const double KRotationSaver::sm_traceLengthSecondsDefault = 3.0;
705 const bool KRotationSaver::sm_traceFlagDefault[3] = {false, false, true};
706 const bool KRotationSaver::sm_randomTracesDefault = true;
708 const double KRotationSaver::sm_LzLimitLower = 0.0;
709 const double KRotationSaver::sm_LzLimitUpper = 500.0;
710 const double KRotationSaver::sm_LzDefault = 10.0;
712 const double KRotationSaver::sm_initEulerThetaLimitLower = 0.0;
713 const double KRotationSaver::sm_initEulerThetaLimitUpper = 180.0;
714 const double KRotationSaver::sm_initEulerThetaDefault = 0.03;
716 // private static class members
718 const unsigned int KRotationSaver::sm_deltaT = 20;
721 //-----------------------------------------------------------------------------
722 // KRotationSetup: dialog to setup screen saver parameters
723 //-----------------------------------------------------------------------------
725 KRotationSetup::KRotationSetup(QWidget* parent)
726 : QDialog(parent)
728 setupUi(this);
730 // the dialog should block, no other control center input should be possible
731 // until the dialog is closed
732 setModal(true);
734 m_lengthEdit->setValidator(
735 new QDoubleValidator(
736 KRotationSaver::sm_traceLengthSecondsLimitLower,
737 KRotationSaver::sm_traceLengthSecondsLimitUpper,
738 3, m_lengthEdit));
739 m_LzEdit->setValidator(
740 new QDoubleValidator(
741 KRotationSaver::sm_LzLimitLower,
742 KRotationSaver::sm_LzLimitUpper,
743 3, m_LzEdit));
744 m_thetaEdit->setValidator(
745 new QDoubleValidator(
746 KRotationSaver::sm_initEulerThetaLimitLower,
747 KRotationSaver::sm_initEulerThetaLimitUpper,
748 3, m_thetaEdit));
750 // set tool tips of editable fields
751 m_lengthEdit->setToolTip(
752 ki18n("Length of traces in seconds of visibility.\nValid values from %1 to %2.")
753 .subs(KRotationSaver::sm_traceLengthSecondsLimitLower, 0, 'f', 2)
754 .subs(KRotationSaver::sm_traceLengthSecondsLimitUpper, 0, 'f', 2)
755 .toString());
756 m_LzEdit->setToolTip(
757 ki18n("Angular momentum in z direction in arbitrary units.\nValid values from %1 to %2.")
758 .subs(KRotationSaver::sm_LzLimitLower, 0, 'f', 2)
759 .subs(KRotationSaver::sm_LzLimitUpper, 0, 'f', 2)
760 .toString());
761 m_thetaEdit->setToolTip(
762 ki18n("Gravitational constant in arbitrary units.\nValid values from %1 to %2.")
763 .subs(KRotationSaver::sm_initEulerThetaLimitLower, 0, 'f', 2)
764 .subs(KRotationSaver::sm_initEulerThetaLimitUpper, 0, 'f', 2)
765 .toString());
767 // setting the background of m_preview widget is not necessary, it's content
768 // is overlayed with the embedded GL widget anyway
769 m_preview->show(); // otherwise saver does not get correct size initially
771 // create screen saver and give it the WinID of the preview area in which it
772 // will be embedded
773 m_saver = new KRotationSaver(m_preview->winId());
775 // read settings from saver and update GUI elements with these values, saver
776 // has read settings in its constructor
778 // set editable fields with stored values as defaults
779 m_xTrace->setChecked(m_saver->traceFlag(0));
780 m_yTrace->setChecked(m_saver->traceFlag(1));
781 m_zTrace->setChecked(m_saver->traceFlag(2));
782 m_randTraces->setChecked(m_saver->randomTraces());
783 QString text;
784 text.setNum(m_saver->traceLengthSeconds());
785 m_lengthEdit->setText(text);
786 text.setNum(m_saver->Lz());
787 m_LzEdit->setText(text);
788 text.setNum(m_saver->initEulerTheta());
789 m_thetaEdit->setText(text);
791 // if the preview area is resized it emits the resized() event which is
792 // caught by m_saver. The embedded GLArea is resized to fit into the preview
793 // area.
794 connect(m_preview, SIGNAL(resized(QResizeEvent*)),
795 m_saver, SLOT(resizeGlArea(QResizeEvent*)));
797 connect(m_okButton, SIGNAL(clicked()), this, SLOT(okButtonClickedSlot()));
798 connect(m_aboutButton, SIGNAL(clicked()), this, SLOT(aboutButtonClickedSlot()));
800 connect(m_xTrace, SIGNAL(toggled(bool)), this, SLOT(xTraceToggled(bool)));
801 connect(m_randTraces, SIGNAL(toggled(bool)), this, SLOT(randomTracesToggled(bool)));
802 connect(m_yTrace, SIGNAL(toggled(bool)), this, SLOT(yTraceToggled(bool)));
803 connect(m_zTrace, SIGNAL(toggled(bool)), this, SLOT(zTraceToggled(bool)));
804 connect(m_lengthEdit, SIGNAL(textChanged(QString)), this, SLOT(lengthEnteredSlot(QString)));
805 connect(m_LzEdit, SIGNAL(textChanged(QString)), this, SLOT(LzEnteredSlot(QString)));
806 connect(m_thetaEdit, SIGNAL(textChanged(QString)), this, SLOT(thetaEnteredSlot(QString)));
809 KRotationSetup::~KRotationSetup()
811 delete m_saver;
814 // Ok pressed - save settings and exit
815 void KRotationSetup::okButtonClickedSlot(void)
817 KConfigGroup config(KGlobal::config(), "Settings");
818 config.writeEntry("x trace", m_saver->traceFlag(0));
819 config.writeEntry("y trace", m_saver->traceFlag(1));
820 config.writeEntry("z trace", m_saver->traceFlag(2));
821 config.writeEntry("random traces", m_saver->randomTraces());
822 config.writeEntry("length", m_saver->traceLengthSeconds());
823 config.writeEntry("Lz", m_saver->Lz());
824 config.writeEntry("theta", m_saver->initEulerTheta());
825 config.sync();
826 accept();
829 void KRotationSetup::aboutButtonClickedSlot(void)
831 KMessageBox::about(this, i18n("\
832 <h3>KRotation Screen Saver for KDE</h3>\
833 <p>Simulation of a force free rotating asymmetric body</p>\
834 <p>Copyright (c) Georg&nbsp;Drenkhahn 2004</p>\
835 <p><tt>Georg.Drenkhahn@gmx.net</tt></p>"));
838 void KRotationSetup::xTraceToggled(bool state)
840 m_saver->setTraceFlag(0, state);
842 void KRotationSetup::yTraceToggled(bool state)
844 m_saver->setTraceFlag(1, state);
846 void KRotationSetup::zTraceToggled(bool state)
848 m_saver->setTraceFlag(2, state);
850 void KRotationSetup::randomTracesToggled(bool state)
852 m_saver->setRandomTraces(state);
853 if (!state)
855 // restore settings from gui if random traces are turned off
856 m_saver->setTraceFlag(0, m_xTrace->isChecked());
857 m_saver->setTraceFlag(1, m_yTrace->isChecked());
858 m_saver->setTraceFlag(2, m_zTrace->isChecked());
861 void KRotationSetup::lengthEnteredSlot(const QString& s)
863 m_saver->setTraceLengthSeconds(s.toDouble());
865 void KRotationSetup::LzEnteredSlot(const QString& s)
867 m_saver->setLz(s.toDouble());
868 m_saver->initData();
870 void KRotationSetup::thetaEnteredSlot(const QString& s)
872 m_saver->setInitEulerTheta(s.toDouble());
873 m_saver->initData();