summaryrefslogtreecommitdiffstats
path: root/kscreensaver/kdesavers/rotation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kscreensaver/kdesavers/rotation.cpp')
-rw-r--r--kscreensaver/kdesavers/rotation.cpp825
1 files changed, 825 insertions, 0 deletions
diff --git a/kscreensaver/kdesavers/rotation.cpp b/kscreensaver/kdesavers/rotation.cpp
new file mode 100644
index 00000000..38098cb0
--- /dev/null
+++ b/kscreensaver/kdesavers/rotation.cpp
@@ -0,0 +1,825 @@
+//============================================================================
+//
+// KRotation screen saver for KDE
+//
+// The screen saver displays a physically realistic simulation of a force free
+// rotating asymmetric body. The equations of motion for such a rotation, the
+// Euler equations, are integrated numerically by the Runge-Kutta method.
+//
+// Developed by Georg Drenkhahn, georg-d@users.sourceforge.net
+//
+// $Id$
+//
+/*
+ * Copyright (C) 2004 Georg Drenkhahn
+ *
+ * KRotation is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ * KRotation is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+//============================================================================
+
+// std. C++ headers
+#include <cstdlib>
+// STL
+#include <deque>
+// Qt headers
+#include <qcheckbox.h>
+#include <qlineedit.h>
+#include <qvalidator.h>
+#include <qtooltip.h>
+// KDE headers
+#include <klocale.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+
+#include "sspreviewarea.h"
+
+// rotation.moc includes rotation.h
+#include "rotation.moc"
+
+/** Version number of this screen saver */
+#define KROTATION_VERSION "1.1"
+
+// libkscreensaver interface
+extern "C"
+{
+ /** application name for the libkscreensaver interface */
+ KDE_EXPORT const char *kss_applicationName = "krotation.kss";
+ /** application description for the libkscreensaver interface */
+ KDE_EXPORT const char *kss_description =
+ I18N_NOOP("Simulation of a force free rotating asymmetric body");
+ /** application version for the libkscreensaver interface */
+ KDE_EXPORT const char *kss_version = KROTATION_VERSION;
+
+ /** function to create screen saver object */
+ KDE_EXPORT KScreenSaver* kss_create(WId id)
+ {
+ return new KRotationSaver(id);
+ }
+
+ /** function to create setup dialog for screen saver */
+ KDE_EXPORT QDialog* kss_setup()
+ {
+ return new KRotationSetup();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// EulerOdeSolver implementation
+//-----------------------------------------------------------------------------
+
+EulerOdeSolver::EulerOdeSolver(
+ const double &t_,
+ const double &dt_,
+ const double &A_,
+ const double &B_,
+ const double &C_,
+ std::valarray<double> &y_,
+ const double &eps_)
+ : RkOdeSolver<double>(t_,y_,dt_,eps_),
+ A(A_), B(B_), C(C_)
+{
+}
+
+std::valarray<double> EulerOdeSolver::f(
+ const double &x,
+ const std::valarray<double> &y) const
+{
+ // unused
+ (void)x;
+
+ // vec omega in body coor. sys.: omega_body = (p, q, r)
+ const vec3<double> omega_body(y[std::slice(0,3,1)]);
+ // body unit vectors in fixed frame coordinates
+ const vec3<double> e1(y[std::slice(3,3,1)]);
+ const vec3<double> e2(y[std::slice(6,3,1)]);
+ const vec3<double> e3(y[std::slice(9,3,1)]);
+
+ // don't use "const vec3<double>&" here because slice_array must be
+ // value-copied to vec3<double>.
+
+ // vec omega in global fixed coor. sys.
+ vec3<double> omega(
+ omega_body[0] * e1
+ + omega_body[1] * e2
+ + omega_body[2] * e3);
+
+ // return vector y'
+ std::valarray<double> ypr(y.size());
+
+ // omega_body'
+ ypr[0] = -(C-B)/A * omega_body[1] * omega_body[2]; // p'
+ ypr[1] = -(A-C)/B * omega_body[2] * omega_body[0]; // q'
+ ypr[2] = -(B-A)/C * omega_body[0] * omega_body[1]; // r'
+
+ // e1', e2', e3'
+ ypr[std::slice(3,3,1)] = vec3<double>::crossprod(omega, e1);
+ ypr[std::slice(6,3,1)] = vec3<double>::crossprod(omega, e2);
+ ypr[std::slice(9,3,1)] = vec3<double>::crossprod(omega, e3);
+
+ return ypr;
+}
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Rotation: screen saver widget
+//-----------------------------------------------------------------------------
+
+RotationGLWidget::RotationGLWidget(
+ QWidget* parent, const char* name,
+ const vec3<double>& _omega,
+ const std::deque<vec3<double> >& e1_,
+ const std::deque<vec3<double> >& e2_,
+ const std::deque<vec3<double> >& e3_,
+ const vec3<double>& J)
+ : QGLWidget(parent, name),
+ eyeR(25), eyeTheta(1), eyePhi(M_PI*0.25),
+ boxSize(1,1,1),
+ fixedAxses(0),
+ bodyAxses(0),
+ lightR(10), lightTheta(M_PI/4), lightPhi(0),
+ bodyAxsesLength(6),
+ fixedAxsesLength(8),
+ omega(_omega),
+ e1(e1_),
+ e2(e2_),
+ e3(e3_)
+{
+ // set up initial rotation matrix as unit matrix, only non-constant elements
+ // are set later on
+ for (int i=0; i<16; i++)
+ rotmat[i] = ((i%5)==0) ? 1:0;
+
+ // Set the box sizes from the momenta of inertia. J is the 3 vector with
+ // momenta of inertia with respect to the 3 figure axes.
+
+ // the default values must be valid so that w,h,d are real!
+ GLfloat
+ x2 = 6.0*(-J[0] + J[1] + J[2]),
+ y2 = 6.0*( J[0] - J[1] + J[2]),
+ z2 = 6.0*( J[0] + J[1] - J[2]);
+
+ if (x2>=0 && y2>=0 && z2>=0)
+ {
+ boxSize = vec3<double>(sqrt(x2), sqrt(y2), sqrt(z2));
+ }
+ else
+ {
+ kdDebug() << "parameter error" << endl;
+ boxSize = vec3<double>(1, 1, 1);
+ }
+}
+
+/* --------- protected methods ----------- */
+
+void RotationGLWidget::initializeGL(void)
+{
+ qglClearColor(QColor(black)); // set color to clear the background
+
+ glClearDepth(1); // depth buffer setup
+ glEnable(GL_DEPTH_TEST); // depth testing
+ glDepthFunc(GL_LEQUAL); // type of depth test
+
+ glShadeModel(GL_SMOOTH); // smooth color shading in poygons
+
+ // nice perspective calculation
+ glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+
+ // set up the light
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ // set positon of light0
+ GLfloat lightPos[4]=
+ {lightR * sin(lightTheta) * sin(lightPhi),
+ lightR * sin(lightTheta) * cos(lightPhi),
+ lightR * cos(lightTheta), 1.};
+ glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
+
+ // enable setting the material colour by glColor()
+ glEnable(GL_COLOR_MATERIAL);
+
+ // set up display lists
+
+ if (fixedAxses == 0)
+ fixedAxses = glGenLists(1); // list to be returned
+ glNewList(fixedAxses, GL_COMPILE);
+
+ // fixed coordinate system axes
+
+ glPushMatrix();
+ glLoadIdentity();
+
+ // z-axis, blue
+ qglColor(QColor(blue));
+ myGlArrow(fixedAxsesLength, 0.5f, 0.03f, 0.1f);
+
+ // x-axis, red
+ qglColor(QColor(red));
+ glRotatef(90, 0, 1, 0);
+
+ myGlArrow(fixedAxsesLength, 0.5f, 0.03f, 0.1f);
+
+ // y-axis, green
+ qglColor(QColor(green));
+ glLoadIdentity();
+ glRotatef(-90, 1, 0, 0);
+ myGlArrow(fixedAxsesLength, 0.5f, 0.03f, 0.1f);
+
+ glPopMatrix();
+ glEndList();
+ // end of axes object list
+
+
+ // box and box-axses
+ if (bodyAxses == 0)
+ bodyAxses = glGenLists(1); // list to be returned
+ glNewList(bodyAxses, GL_COMPILE);
+
+ // z-axis, blue
+ qglColor(QColor(blue));
+ myGlArrow(bodyAxsesLength, 0.5f, 0.03f, 0.1f);
+
+ // x-axis, red
+ qglColor(QColor(red));
+ glPushMatrix();
+ glRotatef(90, 0, 1, 0);
+ myGlArrow(bodyAxsesLength, 0.5f, 0.03f, 0.1f);
+ glPopMatrix();
+
+ // y-axis, green
+ qglColor(QColor(green));
+ glPushMatrix();
+ glRotatef(-90, 1, 0, 0);
+ myGlArrow(bodyAxsesLength, 0.5f, 0.03f, 0.1f);
+ glPopMatrix();
+
+ glEndList();
+}
+
+void RotationGLWidget::draw_traces(void)
+{
+ if (e1.size()==0 && e2.size()==0 && e3.size()==0)
+ return;
+
+ glPushMatrix();
+ glScalef(bodyAxsesLength, bodyAxsesLength, bodyAxsesLength);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ for (int j=0; j<3; ++j)
+ {
+ const std::deque<vec3<double> >& e =
+ j==0 ? e1 : j==1 ? e2 : e3;
+
+ // trace must contain at least 2 elements
+ if (e.size() > 1)
+ {
+ // emission colour
+ GLfloat em[4] = {0,0,0,1};
+ em[j] = 1; // set either red, green, blue emission colour
+
+ glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, em);
+ glColor4fv(em);
+
+ // set iterator of the tail part
+ std::deque<vec3<double> >::const_iterator eit = e.begin();
+ std::deque<vec3<double> >::const_iterator tail =
+ e.begin() +
+ static_cast<std::deque<vec3<double> >::difference_type>
+ (0.9*e.size());
+
+ glBegin(GL_LINES);
+ for (; eit < e.end()-1; ++eit)
+ {
+ glVertex3f((*eit)[0], (*eit)[1], (*eit)[2]);
+ // decrease transparency for tail section
+ if (eit > tail)
+ em[3] =
+ static_cast<GLfloat>
+ (1.0 - double(eit-tail)/(0.1*e.size()));
+ glColor4fv(em);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, em);
+ glVertex3f((*(eit+1))[0], (*(eit+1))[1], (*(eit+1))[2]);
+ }
+ glEnd();
+ }
+ }
+
+ glDisable(GL_BLEND);
+
+ glPopMatrix();
+}
+
+void RotationGLWidget::paintGL(void)
+{
+ // clear color and depth buffer
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+ glMatrixMode(GL_MODELVIEW); // select modelview matrix
+
+ glLoadIdentity();
+ GLfloat const em[] = {0,0,0,1};
+ glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, em);
+
+ // omega vector
+ vec3<double> rotvec =
+ vec3<double>::crossprod(vec3<double>(0,0,1), omega).normalize();
+ GLfloat rotdeg =
+ 180./M_PI * vec3<double>::angle(vec3<double>(0,0,1), omega);
+ glPushMatrix();
+ glRotatef(rotdeg, rotvec[0], rotvec[1], rotvec[2]);
+ qglColor(QColor(white));
+ myGlArrow(7, .5f, .1f, 0.2f);
+ glPopMatrix();
+
+ // fixed axes
+ glCallList(fixedAxses);
+
+ glPushMatrix();
+
+ // set up variable part of rotation matrix for body
+ // set gl body rotation matrix from e1,e2,e3
+ const vec3<double>& e1b = e1.front();
+ const vec3<double>& e2b = e2.front();
+ const vec3<double>& e3b = e3.front();
+
+ rotmat[0] = e1b[0];
+ rotmat[1] = e1b[1];
+ rotmat[2] = e1b[2];
+ rotmat[4] = e2b[0];
+ rotmat[5] = e2b[1];
+ rotmat[6] = e2b[2];
+ rotmat[8] = e3b[0];
+ rotmat[9] = e3b[1];
+ rotmat[10] = e3b[2];
+
+ glMultMatrixf(rotmat);
+
+ glCallList(bodyAxses);
+
+ glScalef(boxSize[0]/2, boxSize[1]/2, boxSize[2]/2);
+
+ // paint box
+ glBegin(GL_QUADS);
+ // front (z)
+ qglColor(QColor(blue));
+ glNormal3f( 0,0,1);
+ glVertex3f( 1, 1, 1);
+ glVertex3f(-1, 1, 1);
+ glVertex3f(-1, -1, 1);
+ glVertex3f( 1, -1, 1);
+ // back (-z)
+ glNormal3f( 0,0,-1);
+ glVertex3f( 1, 1, -1);
+ glVertex3f(-1, 1, -1);
+ glVertex3f(-1, -1, -1);
+ glVertex3f( 1, -1, -1);
+ // top (y)
+ qglColor(QColor(green));
+ glNormal3f( 0,1,0);
+ glVertex3f( 1, 1, 1);
+ glVertex3f( 1, 1, -1);
+ glVertex3f(-1, 1, -1);
+ glVertex3f(-1, 1, 1);
+ // bottom (-y)
+ glNormal3f( 0,-1,0);
+ glVertex3f( 1, -1, 1);
+
+ glVertex3f( 1, -1, -1);
+ glVertex3f(-1, -1, -1);
+ glVertex3f(-1, -1, 1);
+ // left (-x)
+ qglColor(QColor(red));
+ glNormal3f( -1,0,0);
+ glVertex3f(-1, 1, 1);
+ glVertex3f(-1, 1, -1);
+ glVertex3f(-1, -1, -1);
+ glVertex3f(-1, -1, 1);
+ // right (x)
+ glNormal3f( 1,0,0);
+ glVertex3f( 1, 1, 1);
+ glVertex3f( 1, 1, -1);
+ glVertex3f( 1, -1, -1);
+ glVertex3f( 1, -1, 1);
+ glEnd();
+
+ // traces
+ glPopMatrix();
+ draw_traces ();
+
+ glFlush();
+}
+
+void RotationGLWidget::resizeGL(int w, int h)
+{
+ // Prevent a divide by zero
+ if (h == 0) h = 1;
+
+ // set the new view port
+ glViewport(0, 0, (GLint)w, (GLint)h);
+
+ // set up projection matrix
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ // Perspective view
+ gluPerspective(40.0f, (GLdouble)w/(GLdouble)h, 1.0, 100.0f);
+
+ // Viewing transformation, position for better view
+ // Theta is polar angle 0<Theta<Pi
+ gluLookAt(
+ eyeR * sin(eyeTheta) * sin(eyePhi),
+ eyeR * sin(eyeTheta) * cos(eyePhi),
+ eyeR * cos(eyeTheta),
+ 0,0,0,
+ 0,0,1);
+}
+
+/* --------- privat methods ----------- */
+
+void RotationGLWidget::myGlArrow(
+ GLfloat total_length, GLfloat head_length,
+ GLfloat base_width, GLfloat head_width)
+{
+ GLUquadricObj* quadAx = gluNewQuadric();
+ glPushMatrix();
+ gluCylinder(quadAx, base_width, base_width,
+ total_length-head_length, 10, 1);
+ glTranslatef(0, 0, total_length-head_length);
+ gluCylinder(quadAx, head_width, 0, head_length, 10, 1);
+ glPopMatrix();
+ gluDeleteQuadric(quadAx);
+}
+
+
+//-----------------------------------------------------------------------------
+// KRotationSaver: screen saver class
+//-----------------------------------------------------------------------------
+
+KRotationSaver::KRotationSaver(WId id)
+ : KScreenSaver(id),
+ J(4,2,3), // fixed box sizes!
+ initEulerPhi(0),
+ initEulerPsi(0),
+ solver(0),
+ glArea(0),
+ timer(0),
+ m_traceLengthSeconds(traceLengthSecondsDefault),
+ m_Lz(LzDefault),
+ m_initEulerTheta(initEulerThetaDefault)
+{
+ readSettings(); // read global settings
+ initData(); // init e1,e2,e3,omega,solver
+
+ setEraseColor(black);
+ erase(); // erase area
+ glArea = new RotationGLWidget(
+ this, 0, omega, e1, e2, e3, J); // create gl widget
+ embed(glArea); // embed gl widget and resize it
+ glArea->show(); // show gl widget
+
+ timer = new QTimer(this);
+ timer->start(deltaT, TRUE);
+ connect(timer, SIGNAL(timeout()), this, SLOT(doTimeStep()));
+}
+
+KRotationSaver::~KRotationSaver()
+{
+ // time, rotation are automatically deleted with parent KRotationSaver
+ delete solver;
+}
+
+void KRotationSaver::initData()
+{
+ // reset coordiante system
+ vec3<double> e1t(1,0,0), e2t(0,1,0), e3t(0,0,1);
+ // rotation by phi around z = zhat axis
+ e1t.rotate(initEulerPhi*e3t);
+ e2t.rotate(initEulerPhi*e3t);
+ // rotation by theta around new x axis
+ e2t.rotate(m_initEulerTheta*e1t);
+ e3t.rotate(m_initEulerTheta*e1t);
+ // rotation by psi around new z axis
+ e1t.rotate(initEulerPsi*e3t);
+ e2t.rotate(initEulerPsi*e3t);
+ // set first vector in deque
+ e1.clear(); e1.push_front(e1t);
+ e2.clear(); e2.push_front(e2t);
+ e3.clear(); e3.push_front(e3t);
+
+ // calc L in body frame: 1. determine z-axis of fixed frame in body
+ // coordinates, undo the transformations for unit z vector of the body frame
+
+ // calc omega_body from ...
+ vec3<double> e1_body(1,0,0), e3_body(0,0,1);
+ // rotation by -psi along z axis
+ e1_body.rotate(-initEulerPsi*e3_body);
+ // rotation by -theta along new x axis
+ e3_body.rotate(-m_initEulerTheta*e1_body);
+ // omega_body = L_body * J_body^(-1)
+ vec3<double> omega_body = e3_body * m_Lz;
+ omega_body /= J;
+
+ // assemble initial y for solver
+ std::valarray<double> y(12);
+ y[std::slice(0,3,1)] = omega_body;
+ // 3 basis vectors of body system in fixed coordinates
+ y[std::slice(3,3,1)] = e1t;
+ y[std::slice(6,3,1)] = e2t;
+ y[std::slice(9,3,1)] = e3t;
+
+ // initial rotation vector
+ omega
+ = omega_body[0]*e1t
+ + omega_body[1]*e2t
+ + omega_body[2]*e3t;
+
+ if (solver!=0) delete solver;
+ // init solver
+ solver = new EulerOdeSolver(
+ 0.0, // t
+ 0.01, // first dt step size estimation
+ J[0], J[1], J[2], // A,B,C
+ y, // omega_body,e1,e2,e3
+ 1e-5); // eps
+}
+
+void KRotationSaver::readSettings()
+{
+ // read configuration settings from config file
+ KConfig *config = KGlobal::config();
+ config->setGroup("Settings");
+
+ // internal saver parameters are set to stored values or left at their
+ // default values if stored values are out of range
+ setTraceFlag(0, config->readBoolEntry("x trace", traceFlagDefault[0]));
+ setTraceFlag(1, config->readBoolEntry("y trace", traceFlagDefault[1]));
+ setTraceFlag(2, config->readBoolEntry("z trace", traceFlagDefault[2]));
+ setRandomTraces(config->readBoolEntry("random traces", randomTracesDefault));
+ setTraceLengthSeconds(
+ config->readDoubleNumEntry("length", traceLengthSecondsDefault));
+ setLz(
+ config->readDoubleNumEntry("Lz", LzDefault));
+ setInitEulerTheta(
+ config->readDoubleNumEntry("theta", initEulerThetaDefault));
+}
+
+void KRotationSaver::setTraceLengthSeconds(const double& t)
+{
+ if (t >= traceLengthSecondsLimitLower
+ && t <= traceLengthSecondsLimitUpper)
+ {
+ m_traceLengthSeconds = t;
+ }
+}
+
+const double KRotationSaver::traceLengthSecondsLimitLower = 0.0;
+const double KRotationSaver::traceLengthSecondsLimitUpper = 99.0;
+const double KRotationSaver::traceLengthSecondsDefault = 3.0;
+
+const bool KRotationSaver::traceFlagDefault[3] = {false, false, true};
+
+void KRotationSaver::setLz(const double& Lz)
+{
+ if (Lz >= LzLimitLower && Lz <= LzLimitUpper)
+ {
+ m_Lz = Lz;
+ }
+}
+
+const double KRotationSaver::LzLimitLower = 0.0;
+const double KRotationSaver::LzLimitUpper = 500.0;
+const double KRotationSaver::LzDefault = 10.0;
+
+void KRotationSaver::setInitEulerTheta(const double& theta)
+{
+ if (theta >= initEulerThetaLimitLower
+ && theta <= initEulerThetaLimitUpper)
+ {
+ m_initEulerTheta = theta;
+ }
+}
+
+const double KRotationSaver::initEulerThetaLimitLower = 0.0;
+const double KRotationSaver::initEulerThetaLimitUpper = 180.0;
+const double KRotationSaver::initEulerThetaDefault = 0.03;
+
+// public slots
+
+void KRotationSaver::doTimeStep()
+{
+ // integrate a step ahead
+ solver->integrate(0.001*deltaT);
+
+ // read new y
+ std::valarray<double> y = solver->Y();
+
+ std::deque<vec3<double> >::size_type
+ max_vec_length =
+ static_cast<std::deque<vec3<double> >::size_type>
+ ( m_traceLengthSeconds/(0.001*deltaT) );
+
+ for (int j=0; j<3; ++j)
+ {
+ std::deque<vec3<double> >& e =
+ j==0 ? e1 :
+ j==1 ? e2 : e3;
+
+ // read out new body coordinate system
+ if (m_traceFlag[j] == true
+ && max_vec_length > 0)
+ {
+ e.push_front(y[std::slice(3*j+3, 3, 1)]);
+ while (e.size() > max_vec_length)
+ {
+ e.pop_back();
+ }
+ }
+ else
+ {
+ // only set the 1. element
+ e.front() = y[std::slice(3*j+3, 3, 1)];
+ // and delete all other emements
+ if (e.size() > 1)
+ e.resize(1);
+ }
+ }
+
+ // current rotation vector omega
+ omega = y[0]*e1.front() + y[1]*e2.front() + y[2]*e3.front();
+
+ // set new random traces every 10 seconds
+ if (m_randomTraces==true)
+ {
+ static unsigned int counter=0;
+ ++counter;
+ if (counter > unsigned(10.0/(0.001*deltaT)))
+ {
+ counter=0;
+ for (int i=0; i<3; ++i)
+ m_traceFlag[i] = rand()%2==1 ? true : false;
+ }
+ }
+
+ glArea->updateGL();
+ timer->start(deltaT, TRUE); // restart timer
+}
+
+// public slot of KRotationSaver, forward resize event to public slot of glArea
+// to allow the resizing of the gl area withing the setup dialog
+void KRotationSaver::resizeGlArea(QResizeEvent* e)
+{
+ glArea->resize(e->size());
+}
+
+//-----------------------------------------------------------------------------
+// KRotationSetup: dialog to setup screen saver parameters
+//-----------------------------------------------------------------------------
+
+KRotationSetup::KRotationSetup(QWidget* parent, const char* name)
+ : KRotationSetupUi(parent, name),
+ // create ssaver and give it the WinID of the preview area
+ saver(new KRotationSaver(preview->winId()))
+{
+ // the dialog should block, no other control center input should be possible
+ // until the dialog is closed
+ setModal(TRUE);
+
+ lengthEdit->setValidator(
+ new QDoubleValidator(
+ KRotationSaver::traceLengthSecondsLimitLower,
+ KRotationSaver::traceLengthSecondsLimitUpper,
+ 3, lengthEdit));
+ LzEdit->setValidator(
+ new QDoubleValidator(
+ KRotationSaver::LzLimitLower,
+ KRotationSaver::LzLimitUpper,
+ 3, LzEdit));
+ thetaEdit->setValidator(
+ new QDoubleValidator(
+ KRotationSaver::initEulerThetaLimitLower,
+ KRotationSaver::initEulerThetaLimitUpper,
+ 3, thetaEdit));
+
+ // set tool tips of editable fields
+ QToolTip::add(
+ lengthEdit,
+ i18n("Length of traces in seconds of visibility.\nValid values from %1 to %2.")
+ .arg(KRotationSaver::traceLengthSecondsLimitLower, 0, 'f', 2)
+ .arg(KRotationSaver::traceLengthSecondsLimitUpper, 0, 'f', 2));
+ QToolTip::add(
+ LzEdit,
+ i18n("Angular momentum in z direction in arbitrary units.\nValid values from %1 to %2.")
+ .arg(KRotationSaver::LzLimitLower, 0, 'f', 2)
+ .arg(KRotationSaver::LzLimitUpper, 0, 'f', 2));
+ QToolTip::add(
+ thetaEdit,
+ i18n("Gravitational constant in arbitrary units.\nValid values from %1 to %2.")
+ .arg(KRotationSaver::initEulerThetaLimitLower, 0, 'f', 2)
+ .arg(KRotationSaver::initEulerThetaLimitUpper, 0, 'f', 2));
+
+ // init preview area
+ preview->setBackgroundColor(black);
+ preview->show(); // otherwise saver does not get correct size
+
+ // read settings from saver and update GUI elements with these values, saver
+ // has read settings in its constructor
+
+ // set editable fields with stored values as defaults
+ xTrace->setChecked(saver->traceFlag(0));
+ yTrace->setChecked(saver->traceFlag(1));
+ zTrace->setChecked(saver->traceFlag(2));
+ randTraces->setChecked(saver->randomTraces());
+ QString text;
+ text.setNum(saver->traceLengthSeconds());
+ lengthEdit->validateAndSet(text,0,0,0);
+ text.setNum(saver->Lz());
+ LzEdit->validateAndSet(text,0,0,0);
+ text.setNum(saver->initEulerTheta());
+ thetaEdit->validateAndSet(text,0,0,0);
+
+ // if the preview area is resized it emmits the resized() event which is
+ // caught by the saver. The embedded GlArea is resized to fit into the
+ // preview area.
+ connect(preview, SIGNAL(resized(QResizeEvent*)),
+ saver, SLOT(resizeGlArea(QResizeEvent*)));
+}
+
+KRotationSetup::~KRotationSetup()
+{
+ delete saver;
+}
+
+// Ok pressed - save settings and exit
+void KRotationSetup::okButtonClickedSlot(void)
+{
+ KConfig* config = KGlobal::config();
+ config->setGroup("Settings");
+ config->writeEntry("x trace", saver->traceFlag(0));
+ config->writeEntry("y trace", saver->traceFlag(1));
+ config->writeEntry("z trace", saver->traceFlag(2));
+ config->writeEntry("random traces", saver->randomTraces());
+ config->writeEntry("length", saver->traceLengthSeconds());
+ config->writeEntry("Lz", saver->Lz());
+ config->writeEntry("theta", saver->initEulerTheta());
+ config->sync();
+ accept();
+}
+
+void KRotationSetup::aboutButtonClickedSlot(void)
+{
+ KMessageBox::about(this, i18n("\
+<h3>KRotation Screen Saver for KDE</h3>\
+<p>Simulation of a force free rotating asymmetric body</p>\
+<p>Copyright (c) Georg&nbsp;Drenkhahn 2004</p>\
+<p><tt>georg-d@users.sourceforge.net</tt></p>"));
+}
+
+void KRotationSetup::xTraceToggled(bool state)
+{
+ saver->setTraceFlag(0, state);
+}
+void KRotationSetup::yTraceToggled(bool state)
+{
+ saver->setTraceFlag(1, state);
+}
+void KRotationSetup::zTraceToggled(bool state)
+{
+ saver->setTraceFlag(2, state);
+}
+void KRotationSetup::randomTracesToggled(bool state)
+{
+ saver->setRandomTraces(state);
+ if (state==false)
+ {
+ // restore settings from gui if random traces are turned off
+ saver->setTraceFlag(0, xTrace->isChecked());
+ saver->setTraceFlag(1, yTrace->isChecked());
+ saver->setTraceFlag(2, zTrace->isChecked());
+ }
+}
+void KRotationSetup::lengthEnteredSlot(const QString& s)
+{
+ saver->setTraceLengthSeconds(s.toDouble());
+}
+void KRotationSetup::LzEnteredSlot(const QString& s)
+{
+ saver->setLz(s.toDouble());
+ if (saver!=0) saver->initData();
+}
+void KRotationSetup::thetaEnteredSlot(const QString& s)
+{
+ saver->setInitEulerTheta(s.toDouble());
+ if (saver!=0) saver->initData();
+}