Back to index

kdeartwork  4.3.2
rotation.cpp
Go to the documentation of this file.
00001 
00024 #define QT_NO_COMPAT
00025 
00026 // std. C++ headers
00027 #include <cstdlib>
00028 
00029 // STL headers
00030 #include <deque>
00031 
00032 // Qt headers
00033 #include <QCheckBox>
00034 #include <QLineEdit>
00035 #include <QValidator>
00036 #include <QToolTip>
00037 #include <QResizeEvent>
00038 
00039 // KDE headers
00040 #include <KLocale>
00041 #include <KGlobal>
00042 #include <KConfig>
00043 #include <KDebug>
00044 #include <KMessageBox>
00045 
00046 // Eigen2 from KDE support
00047 #include <Eigen/Core>
00048 #include <Eigen/Geometry>
00049 // import all Eigen types, Transform and Quaternion are not part of the
00050 // namespace part published by USING_PART_OF_NAMESPACE_EIGEN
00051 using namespace Eigen;
00052 
00053 // the screen saver preview area class
00054 #include "sspreviewarea.h"
00055 
00056 #include "rotation.h"           // own interfaces
00057 #include "rotation.moc"
00058 
00060 #define KROTATION_VERSION "2.0"
00061 
00062 #ifndef M_PI
00063 #define M_PI 3.14159265358979323846
00064 #endif
00065 
00066 // libkscreensaver interface
00067 class KRotationSaverInterface : public KScreenSaverInterface
00068 {
00069    public:
00070       virtual KAboutData* aboutData()
00071       {
00072          return new KAboutData(
00073             "krotation.kss", "klock",
00074             ki18n("Simulation of a force free rotating asymmetric body"),
00075             KROTATION_VERSION,
00076             ki18n("Simulation of a force free rotating asymmetric body"));
00077       }
00078 
00080       virtual KScreenSaver* create(WId id)
00081       {
00082          return new KRotationSaver(id);
00083       }
00084 
00086       virtual QDialog* setup()
00087       {
00088          return new KRotationSetup();
00089       }
00090 };
00091 
00092 int main(int argc, char *argv[])
00093 {
00094    KRotationSaverInterface kss;
00095    return kScreenSaverMain(argc, argv, kss);
00096 }
00097 
00098 //-----------------------------------------------------------------------------
00099 // EulerOdeSolver implementation
00100 //-----------------------------------------------------------------------------
00101 
00102 EulerOdeSolver::EulerOdeSolver(
00103    const double& t,
00104    const double& dt,
00105    const double& A,
00106    const double& B,
00107    const double& C,
00108    Vector12d&    y,
00109    const double& eps)
00110    : RkOdeSolver<double,12>(t, y, dt, eps),
00111      m_A(A),
00112      m_B(B),
00113      m_C(C)
00114 {
00115 }
00116 
00117 Vector12d EulerOdeSolver::f(const double& x, const Vector12d& y) const
00118 {
00119    // unused
00120    (void)x;
00121 
00122    // vec omega in body coor. sys.: omega_body = (p, q, r)
00123    const Vector3d omega_body(y.start<3>());
00124 
00125    // body unit vectors in fixed frame coordinates
00126    Matrix3d e;
00127    for (int i=0; i<3; ++i)
00128    {
00129       e.col(i) = y.segment<3>(3+i*3);
00130    }
00131 
00132    // vec omega in global fixed coor. sys.
00133    const Vector3d omega = e * omega_body;
00134 
00135    // return vector y'
00136    Vector12d ypr;
00137 
00138    // omega_body'
00139    ypr[0] = -(m_C-m_B)/m_A * omega_body[1] * omega_body[2]; // p'
00140    ypr[1] = -(m_A-m_C)/m_B * omega_body[2] * omega_body[0]; // q'
00141    ypr[2] = -(m_B-m_A)/m_C * omega_body[0] * omega_body[1]; // r'
00142 
00143    // e1', e2', e3'
00144    for (int i=0; i<3; ++i)
00145    {
00146       ypr.segment<3>(3+i*3) = omega.cross(e.col(i));
00147    }
00148 
00149    return ypr;
00150 }
00151 //-----------------------------------------------------------------------------
00152 
00153 
00154 //-----------------------------------------------------------------------------
00155 // Rotation: screen saver widget
00156 //-----------------------------------------------------------------------------
00157 
00158 RotationGLWidget::RotationGLWidget(
00159    QWidget*              parent,
00160    const KRotationSaver& saver)
00161    : QGLWidget(parent),
00162      m_eyeR(25),
00163      m_eyeTheta(1),
00164      m_eyePhi(M_PI*0.25),
00165      m_boxSize(Vector3d::Ones()),
00166      m_fixedAxses(0),
00167      m_bodyAxses(0),
00168      m_lightR(10),
00169      m_lightTheta(M_PI/4),
00170      m_lightPhi(0),
00171      m_bodyAxsesLength(6),
00172      m_fixedAxsesLength(8),
00173      m_saver(saver)
00174 {
00175    /* Set the box sizes from the momenta of inertia.  J is the 3 vector with
00176     * momenta of inertia with respect to the 3 figure axes. */
00177 
00178    const Vector3d& J = m_saver.J();
00179 
00180    /* the default values must be valid so that w,h,d are real! */
00181    const GLfloat x2 = 6.0*(-J[0] + J[1] + J[2]);
00182    const GLfloat y2 = 6.0*( J[0] - J[1] + J[2]);
00183    const GLfloat z2 = 6.0*( J[0] + J[1] - J[2]);
00184 
00185    if ((x2>=0) && (y2>=0) && (z2>=0))
00186    {
00187       m_boxSize = Vector3d(std::sqrt(x2), std::sqrt(y2), std::sqrt(z2));
00188    }
00189    else
00190    {
00191       kError() << "parameter error";
00192    }
00193 }
00194 
00195 /* --------- protected methods ----------- */
00196 
00197 void RotationGLWidget::initializeGL(void)
00198 {
00199    qglClearColor(QColor(Qt::black)); // set color to clear the background
00200 
00201    glClearDepth(1);             // depth buffer setup
00202    glEnable(GL_DEPTH_TEST);     // depth testing
00203    glDepthFunc(GL_LEQUAL);      // type of depth test
00204 
00205    glShadeModel(GL_SMOOTH);     // smooth color shading in poygons
00206 
00207    // nice perspective calculation
00208    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
00209 
00210    // set up the light
00211    glEnable(GL_LIGHTING);
00212    glEnable(GL_LIGHT0);
00213    // set position of light0
00214    GLfloat lightPos[4]=
00215       {m_lightR * std::sin(m_lightTheta) * std::sin(m_lightPhi),
00216        m_lightR * std::sin(m_lightTheta) * std::cos(m_lightPhi),
00217        m_lightR * std::cos(m_lightTheta), 1.};
00218    glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
00219 
00220    // enable setting the material colour by glColor()
00221    glEnable(GL_COLOR_MATERIAL);
00222 
00223    // set up display lists
00224 
00225    if (m_fixedAxses == 0)
00226    {
00227       m_fixedAxses = glGenLists(1); // list to be returned
00228    }
00229    glNewList(m_fixedAxses, GL_COMPILE);
00230 
00231    // fixed coordinate system axes
00232 
00233    glPushMatrix();
00234    glLoadIdentity();
00235 
00236    // z-axis, blue
00237    qglColor(QColor(Qt::blue));
00238    myGlArrow(m_fixedAxsesLength, 0.5f, 0.03f, 0.1f);
00239 
00240    // x-axis, red
00241    qglColor(QColor(Qt::red));
00242    glRotatef(90, 0, 1, 0);
00243 
00244    myGlArrow(m_fixedAxsesLength, 0.5f, 0.03f, 0.1f);
00245 
00246    // y-axis, green
00247    qglColor(QColor(Qt::green));
00248    glLoadIdentity();
00249    glRotatef(-90, 1, 0, 0);
00250    myGlArrow(m_fixedAxsesLength, 0.5f, 0.03f, 0.1f);
00251 
00252    glPopMatrix();
00253    glEndList();
00254    // end of axes object list
00255 
00256 
00257    // box and box-axses
00258    if (m_bodyAxses == 0)
00259    {
00260       m_bodyAxses = glGenLists(1); // list to be returned
00261    }
00262    glNewList(m_bodyAxses, GL_COMPILE);
00263 
00264    // z-axis, blue
00265    qglColor(QColor(Qt::blue));
00266    myGlArrow(m_bodyAxsesLength, 0.5f, 0.03f, 0.1f);
00267 
00268    // x-axis, red
00269    qglColor(QColor(Qt::red));
00270    glPushMatrix();
00271    glRotatef(90, 0, 1, 0);
00272    myGlArrow(m_bodyAxsesLength, 0.5f, 0.03f, 0.1f);
00273    glPopMatrix();
00274 
00275    // y-axis, green
00276    qglColor(QColor(Qt::green));
00277    glPushMatrix();
00278    glRotatef(-90, 1, 0, 0);
00279    myGlArrow(m_bodyAxsesLength, 0.5f, 0.03f, 0.1f);
00280    glPopMatrix();
00281 
00282    glEndList();
00283 }
00284 
00285 void RotationGLWidget::draw_traces(void)
00286 {
00287    const std::deque<Matrix3d>&e = m_saver.e();
00288 
00289    // traces must contain at least 2 elements
00290    if (e.size() <= 1)
00291    {
00292       return;
00293    }
00294 
00295    glPushMatrix();
00296    glScalef(m_bodyAxsesLength, m_bodyAxsesLength, m_bodyAxsesLength);
00297 
00298    glEnable(GL_BLEND);
00299    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00300 
00301    for (int j=0; j<3; ++j)
00302    {
00303       if (m_saver.traceFlag(j))
00304       {
00305          // emission colour
00306          GLfloat em[4] = {0,0,0,1};
00307          em[j] = 1; // set either red, green, blue emission colour
00308 
00309          glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, em);
00310          glColor4fv(em);
00311 
00312          // set iterator of the tail part
00313          std::deque<Matrix3d>::const_iterator eit  = e.begin();
00314          std::deque<Matrix3d>::const_iterator tail =
00315             e.begin() +
00316             static_cast<std::deque<Matrix3d>::difference_type>
00317             (0.9 * e.size());
00318 
00319          glBegin(GL_LINES);
00320          for (; eit < e.end()-1; ++eit)
00321          {
00322             glVertex3f((*eit)(0,j), (*eit)(1,j), (*eit)(2,j));
00323             // decrease transparency for tail section
00324             if (eit > tail)
00325             {
00326                em[3] =
00327                   static_cast<GLfloat>
00328                   (1.0 - double(eit-tail)/(0.1 * e.size()));
00329             }
00330             glColor4fv(em);
00331             glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, em);
00332             glVertex3f((*(eit+1))(0,j), (*(eit+1))(1,j), (*(eit+1))(2,j));
00333          }
00334          glEnd();
00335       }
00336    }
00337 
00338    glDisable(GL_BLEND);
00339 
00340    glPopMatrix();
00341 }
00342 
00343 void RotationGLWidget::paintGL(void)
00344 {
00345    // clear color and depth buffer
00346    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
00347 
00348    glMatrixMode(GL_MODELVIEW);           // select modelview matrix
00349 
00350    glLoadIdentity();
00351    GLfloat const em[] = {0,0,0,1};
00352    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, em);
00353 
00354    glPushMatrix();
00355    // calculate the transform which rotates the unit z vector onto omega
00356    glLoadMatrixd(
00357       Transform<double,3>(
00358          Quaternion<double>()
00359          .setFromTwoVectors(Vector3d::UnitZ(), m_saver.omega())
00360          .toRotationMatrix())
00361       .data());
00362    // draw the white omega arrow
00363    qglColor(QColor(Qt::white));
00364    myGlArrow(7, .5f, .1f, 0.2f);
00365    glPopMatrix();
00366 
00367    // draw the fixed axes
00368    glCallList(m_fixedAxses);
00369 
00370 
00371    // create transformation/rotation matrix from the body and its unit axes
00372    glPushMatrix();
00373    glLoadMatrixd(
00374       Transform<double,3>(m_saver.e().front())
00375       .data());
00376 
00377    // draw the body unit axis
00378    glCallList(m_bodyAxses);
00379 
00380    // scaling from a cube to the rotating body
00381    glScalef(m_boxSize[0]/2, m_boxSize[1]/2, m_boxSize[2]/2);
00382 
00383    // paint box
00384    glBegin(GL_QUADS);
00385    // front (z)
00386    qglColor(QColor(Qt::blue));
00387    glNormal3f( 0,0,1);
00388    glVertex3f( 1,  1,  1);
00389    glVertex3f(-1,  1,  1);
00390    glVertex3f(-1, -1,  1);
00391    glVertex3f( 1, -1,  1);
00392    // back (-z)
00393    glNormal3f( 0,0,-1);
00394    glVertex3f( 1,  1, -1);
00395    glVertex3f(-1,  1, -1);
00396    glVertex3f(-1, -1, -1);
00397    glVertex3f( 1, -1, -1);
00398    // top (y)
00399    qglColor(QColor(Qt::green));
00400    glNormal3f( 0,1,0);
00401    glVertex3f( 1,  1,  1);
00402    glVertex3f( 1,  1, -1);
00403    glVertex3f(-1,  1, -1);
00404    glVertex3f(-1,  1,  1);
00405    // bottom (-y)
00406    glNormal3f( 0,-1,0);
00407    glVertex3f( 1, -1,  1);
00408 
00409    glVertex3f( 1, -1, -1);
00410    glVertex3f(-1, -1, -1);
00411    glVertex3f(-1, -1,  1);
00412    // left (-x)
00413    qglColor(QColor(Qt::red));
00414    glNormal3f( -1,0,0);
00415    glVertex3f(-1,  1,  1);
00416    glVertex3f(-1,  1, -1);
00417    glVertex3f(-1, -1, -1);
00418    glVertex3f(-1, -1,  1);
00419    // right (x)
00420    glNormal3f( 1,0,0);
00421    glVertex3f( 1,  1,  1);
00422    glVertex3f( 1,  1, -1);
00423    glVertex3f( 1, -1, -1);
00424    glVertex3f( 1, -1,  1);
00425    glEnd();
00426 
00427    glPopMatrix();
00428 
00429    // draw the traces
00430    draw_traces();
00431 
00432    glFlush();
00433 }
00434 
00435 void RotationGLWidget::resizeGL(int w, int h)
00436 {
00437    kDebug() << "w=" << w << ", h=" << h << "\n";
00438 
00439    // prevent division by zero
00440    if (h == 0)
00441    {
00442       return;
00443    }
00444 
00445    // set the new view port
00446    glViewport(0, 0, (GLint)w, (GLint)h);
00447 
00448    // set up projection matrix
00449    glMatrixMode(GL_PROJECTION);
00450    glLoadIdentity();
00451    // Perspective view
00452    gluPerspective(40.0f, (GLdouble)w/(GLdouble)h, 1.0, 100.0f);
00453 
00454    // Viewing transformation, position for better view
00455    // Theta is polar angle 0<Theta<Pi
00456    gluLookAt(
00457       m_eyeR * std::sin(m_eyeTheta) * std::sin(m_eyePhi),
00458       m_eyeR * std::sin(m_eyeTheta) * std::cos(m_eyePhi),
00459       m_eyeR * std::cos(m_eyeTheta),
00460       0,0,0,
00461       0,0,1);
00462 }
00463 
00464 /* --------- privat methods ----------- */
00465 
00466 void RotationGLWidget::myGlArrow(
00467    GLfloat total_length, GLfloat head_length,
00468    GLfloat base_width,   GLfloat head_width)
00469 {
00470    GLUquadricObj* const quadAx = gluNewQuadric();
00471    glPushMatrix();
00472    gluCylinder(
00473       quadAx, base_width, base_width,
00474       total_length - head_length, 10, 1);
00475    glTranslatef(0, 0, total_length - head_length);
00476    gluCylinder(quadAx, head_width, 0, head_length, 10, 1);
00477    glPopMatrix();
00478    gluDeleteQuadric(quadAx);
00479 }
00480 
00481 
00482 //-----------------------------------------------------------------------------
00483 // KRotationSaver: screen saver class
00484 //-----------------------------------------------------------------------------
00485 
00486 // class methods
00487 
00488 KRotationSaver::KRotationSaver(WId id)
00489    : KScreenSaver(id),
00490      m_solver(0),
00491      m_glArea(0),
00492      m_timer(0),
00493      m_J(4,2,3),                // fixed box sizes!
00494      m_traceLengthSeconds(sm_traceLengthSecondsDefault),
00495      m_Lz(sm_LzDefault),
00496      m_initEulerPhi(0),
00497      m_initEulerPsi(0),
00498      m_initEulerTheta(sm_initEulerThetaDefault)
00499 {
00500    // no need to set our parent widget's background here, the GL widget sets its
00501    // own background color
00502 
00503    readSettings();              // read global settings
00504 
00505    // init m_e1,m_e2,m_e3,m_omega, construct and init m_solver
00506    initData();
00507 
00508    // create gl widget w/o parent
00509    m_glArea = new RotationGLWidget(0, *this);
00510    embed(m_glArea);             // embed gl widget and resize it
00511    m_glArea->show();            // show embedded gl widget
00512 
00513    // set up cyclic timer
00514    m_timer = new QTimer(this);
00515    m_timer->start(sm_deltaT);
00516    connect(m_timer, SIGNAL(timeout()), this, SLOT(doTimeStep()));
00517 }
00518 
00519 KRotationSaver::~KRotationSaver()
00520 {
00521    m_timer->stop();
00522 
00523    // m_timer is automatically deleted with parent KRotationSaver
00524    delete m_glArea;
00525    delete m_solver;
00526 }
00527 
00528 void KRotationSaver::initData()
00529 {
00530    // rotation by phi around z = zhat axis
00531    Matrix3d et(AngleAxisd(m_initEulerPhi, Vector3d::UnitZ()));
00532    // rotation by theta around new x axis
00533    et = AngleAxisd(m_initEulerPhi, et.col(0)).toRotationMatrix() * et;
00534    // rotation by psi around new z axis
00535    et = AngleAxisd(m_initEulerPsi, et.col(2)).toRotationMatrix() * et;
00536 
00537    // set first vector in deque
00538    m_e.clear(); m_e.push_front(et);
00539 
00540    /* calc L in body frame:
00541     *
00542     * determine unit-axes of fixed frame in body coordinates, invert the
00543     * transformations above for unit vectors of the body frame */
00544 
00545    // rotation by -psi along z axis
00546    Matrix3d e_body(AngleAxisd(-m_initEulerPsi, Vector3d::UnitZ()));
00547    // rotation by -theta along new x axis
00548    e_body = AngleAxisd(-m_initEulerTheta, e_body.col(0)).toRotationMatrix() * e_body;
00549 
00550    // omega_body = L_body * J_body^(-1)
00551    // component-wise division because J_body is a diagonal matrix
00552    Vector3d omega_body = (m_Lz * e_body.col(2)).cwise() / m_J;
00553 
00554    // initial rotation vector
00555    m_omega = et * omega_body;
00556 
00557    // assemble initial y for solver
00558    Vector12d y;
00559    y.start<3>() = omega_body;
00560    // 3 basis vectors of body system in fixed coordinates
00561    y.segment<3>(3) = et.col(0);
00562    y.segment<3>(6) = et.col(1);
00563    y.segment<3>(9) = et.col(2);
00564 
00565    if (m_solver!=0)
00566    {
00567       // deleting the solver is necessary when parameters are changed in the
00568       // configuration dialog.
00569       delete m_solver;
00570    }
00571    // init solver
00572    m_solver = new EulerOdeSolver(
00573       0.0,      // t
00574       0.01,     // first dt step size estimation
00575       m_J[0], m_J[1], m_J[2], // A,B,C
00576       y,        // omega_body,e1,e2,e3
00577       1e-5);    // eps
00578 }
00579 
00580 void KRotationSaver::readSettings()
00581 {
00582    // read configuration settings from config file
00583    KConfigGroup config(KGlobal::config(), "Settings");
00584 
00585    // internal saver parameters are set to stored values or left at their
00586    // default values if stored values are out of range
00587    setTraceFlag(0, config.readEntry("x trace", sm_traceFlagDefault[0]));
00588    setTraceFlag(1, config.readEntry("y trace", sm_traceFlagDefault[1]));
00589    setTraceFlag(2, config.readEntry("z trace", sm_traceFlagDefault[2]));
00590    setRandomTraces(config.readEntry("random traces", sm_randomTracesDefault));
00591    setTraceLengthSeconds(
00592       config.readEntry("length", sm_traceLengthSecondsDefault));
00593    setLz(
00594       config.readEntry("Lz",     sm_LzDefault));
00595    setInitEulerTheta(
00596       config.readEntry("theta",  sm_initEulerThetaDefault));
00597 }
00598 
00599 void KRotationSaver::setTraceLengthSeconds(const double& t)
00600 {
00601    if ((t >= sm_traceLengthSecondsLimitLower)
00602        && (t <= sm_traceLengthSecondsLimitUpper))
00603    {
00604       m_traceLengthSeconds = t;
00605    }
00606 }
00607 
00608 void KRotationSaver::setLz(const double& Lz)
00609 {
00610    if ((Lz >= sm_LzLimitLower) && (Lz <= sm_LzLimitUpper))
00611    {
00612       m_Lz = Lz;
00613    }
00614 }
00615 
00616 void KRotationSaver::setInitEulerTheta(const double& theta)
00617 {
00618    if ((theta >= sm_initEulerThetaLimitLower)
00619        && (theta <= sm_initEulerThetaLimitUpper))
00620    {
00621       m_initEulerTheta = theta;
00622    }
00623 }
00624 
00625 //   public slots
00626 
00627 void KRotationSaver::doTimeStep()
00628 {
00629    // integrate a step ahead
00630    m_solver->integrate(0.001*sm_deltaT);
00631 
00632    // read new y
00633    Vector12d y = m_solver->Y();
00634 
00635    std::deque<Vector3d>::size_type
00636       max_vec_length =
00637       static_cast<std::deque<Vector3d>::size_type>
00638       ( m_traceLengthSeconds/(0.001*sm_deltaT) );
00639 
00640    // construct matrix from solution vector
00641    // read out new body coordinate system
00642    Matrix3d et;
00643    for (int j=0; j<3; ++j)
00644    {
00645       et.col(j) = y.segment<3>(3*j+3);
00646    }
00647 
00648    m_e.push_front(et);
00649 
00650    if (max_vec_length > 0)
00651    {
00652       m_e.push_front(et);
00653       while (m_e.size() > max_vec_length)
00654       {
00655          m_e.pop_back();
00656       }
00657    }
00658    else
00659    {
00660       // only set the 1. element
00661       m_e.front() = et;
00662       // and delete all other emements
00663       if (m_e.size() > 1)
00664       {
00665          m_e.resize(1);
00666       }
00667    }
00668 
00669    // current rotation vector omega
00670    m_omega = m_e.front() * y.start<3>();
00671 
00672    // set new random traces every 10 seconds
00673    if (m_randomTraces)
00674    {
00675       static unsigned int counter=0;
00676       ++counter;
00677       if (counter > unsigned(10.0/(0.001*sm_deltaT)))
00678       {
00679          counter=0;
00680          for (int i=0; i<3; ++i)
00681          {
00682             m_traceFlag[i] = (rand()%2==1);
00683          }
00684       }
00685    }
00686 
00687    m_glArea->updateGL();
00688 
00689    // no need to restart timer here, it is a cyclic timer
00690 }
00691 
00692 // public slot of KRotationSaver, forward resize event to public slot of glArea
00693 // to allow the resizing of the gl area withing the setup dialog
00694 void KRotationSaver::resizeGlArea(QResizeEvent* e)
00695 {
00696    m_glArea->resize(e->size());
00697 }
00698 
00699 // public static class members
00700 
00701 const double KRotationSaver::sm_traceLengthSecondsLimitLower =  0.0;
00702 const double KRotationSaver::sm_traceLengthSecondsLimitUpper = 99.0;
00703 const double KRotationSaver::sm_traceLengthSecondsDefault    =  3.0;
00704 
00705 const bool   KRotationSaver::sm_traceFlagDefault[3] = {false, false, true};
00706 const bool   KRotationSaver::sm_randomTracesDefault          = true;
00707 
00708 const double KRotationSaver::sm_LzLimitLower                 =   0.0;
00709 const double KRotationSaver::sm_LzLimitUpper                 = 500.0;
00710 const double KRotationSaver::sm_LzDefault                    =  10.0;
00711 
00712 const double KRotationSaver::sm_initEulerThetaLimitLower     =   0.0;
00713 const double KRotationSaver::sm_initEulerThetaLimitUpper     = 180.0;
00714 const double KRotationSaver::sm_initEulerThetaDefault        =   0.03;
00715 
00716 // private static class members
00717 
00718 const unsigned int KRotationSaver::sm_deltaT                 = 20;
00719 
00720 
00721 //-----------------------------------------------------------------------------
00722 // KRotationSetup: dialog to setup screen saver parameters
00723 //-----------------------------------------------------------------------------
00724 
00725 KRotationSetup::KRotationSetup(QWidget* parent)
00726    : QDialog(parent)
00727 {
00728    setupUi(this);
00729 
00730    // the dialog should block, no other control center input should be possible
00731    // until the dialog is closed
00732    setModal(true);
00733 
00734    m_lengthEdit->setValidator(
00735       new QDoubleValidator(
00736          KRotationSaver::sm_traceLengthSecondsLimitLower,
00737          KRotationSaver::sm_traceLengthSecondsLimitUpper,
00738          3, m_lengthEdit));
00739    m_LzEdit->setValidator(
00740       new QDoubleValidator(
00741          KRotationSaver::sm_LzLimitLower,
00742          KRotationSaver::sm_LzLimitUpper,
00743          3, m_LzEdit));
00744    m_thetaEdit->setValidator(
00745       new QDoubleValidator(
00746          KRotationSaver::sm_initEulerThetaLimitLower,
00747          KRotationSaver::sm_initEulerThetaLimitUpper,
00748          3, m_thetaEdit));
00749 
00750    // set tool tips of editable fields
00751    m_lengthEdit->setToolTip(
00752       ki18n("Length of traces in seconds of visibility.\nValid values from %1 to %2.")
00753       .subs(KRotationSaver::sm_traceLengthSecondsLimitLower, 0, 'f', 2)
00754       .subs(KRotationSaver::sm_traceLengthSecondsLimitUpper, 0, 'f', 2)
00755       .toString());
00756    m_LzEdit->setToolTip(
00757       ki18n("Angular momentum in z direction in arbitrary units.\nValid values from %1 to %2.")
00758       .subs(KRotationSaver::sm_LzLimitLower, 0, 'f', 2)
00759       .subs(KRotationSaver::sm_LzLimitUpper, 0, 'f', 2)
00760       .toString());
00761    m_thetaEdit->setToolTip(
00762       ki18n("Gravitational constant in arbitrary units.\nValid values from %1 to %2.")
00763       .subs(KRotationSaver::sm_initEulerThetaLimitLower, 0, 'f', 2)
00764       .subs(KRotationSaver::sm_initEulerThetaLimitUpper, 0, 'f', 2)
00765       .toString());
00766 
00767    // setting the background of m_preview widget is not necessary, it's content
00768    // is overlayed with the embedded GL widget anyway
00769    m_preview->show();    // otherwise saver does not get correct size initially
00770 
00771    // create screen saver and give it the WinID of the preview area in which it
00772    // will be embedded
00773    m_saver = new KRotationSaver(m_preview->winId());
00774 
00775    // read settings from saver and update GUI elements with these values, saver
00776    // has read settings in its constructor
00777 
00778    // set editable fields with stored values as defaults
00779    m_xTrace->setChecked(m_saver->traceFlag(0));
00780    m_yTrace->setChecked(m_saver->traceFlag(1));
00781    m_zTrace->setChecked(m_saver->traceFlag(2));
00782    m_randTraces->setChecked(m_saver->randomTraces());
00783    QString text;
00784    text.setNum(m_saver->traceLengthSeconds());
00785    m_lengthEdit->setText(text);
00786    text.setNum(m_saver->Lz());
00787    m_LzEdit->setText(text);
00788    text.setNum(m_saver->initEulerTheta());
00789    m_thetaEdit->setText(text);
00790 
00791    // if the preview area is resized it emits the resized() event which is
00792    // caught by m_saver.  The embedded GLArea is resized to fit into the preview
00793    // area.
00794    connect(m_preview,   SIGNAL(resized(QResizeEvent*)),
00795            m_saver,     SLOT(resizeGlArea(QResizeEvent*)));
00796 
00797    connect(m_okButton,    SIGNAL(clicked()),            this, SLOT(okButtonClickedSlot()));
00798    connect(m_aboutButton, SIGNAL(clicked()),            this, SLOT(aboutButtonClickedSlot()));
00799 
00800    connect(m_xTrace,      SIGNAL(toggled(bool)),        this, SLOT(xTraceToggled(bool)));
00801    connect(m_randTraces,  SIGNAL(toggled(bool)),        this, SLOT(randomTracesToggled(bool)));
00802    connect(m_yTrace,      SIGNAL(toggled(bool)),        this, SLOT(yTraceToggled(bool)));
00803    connect(m_zTrace,      SIGNAL(toggled(bool)),        this, SLOT(zTraceToggled(bool)));
00804    connect(m_lengthEdit,  SIGNAL(textChanged(QString)), this, SLOT(lengthEnteredSlot(QString)));
00805    connect(m_LzEdit,      SIGNAL(textChanged(QString)), this, SLOT(LzEnteredSlot(QString)));
00806    connect(m_thetaEdit,   SIGNAL(textChanged(QString)), this, SLOT(thetaEnteredSlot(QString)));
00807 }
00808 
00809 KRotationSetup::~KRotationSetup()
00810 {
00811    delete m_saver;
00812 }
00813 
00814 // Ok pressed - save settings and exit
00815 void KRotationSetup::okButtonClickedSlot(void)
00816 {
00817    KConfigGroup config(KGlobal::config(), "Settings");
00818    config.writeEntry("x trace",       m_saver->traceFlag(0));
00819    config.writeEntry("y trace",       m_saver->traceFlag(1));
00820    config.writeEntry("z trace",       m_saver->traceFlag(2));
00821    config.writeEntry("random traces", m_saver->randomTraces());
00822    config.writeEntry("length",        m_saver->traceLengthSeconds());
00823    config.writeEntry("Lz",            m_saver->Lz());
00824    config.writeEntry("theta",         m_saver->initEulerTheta());
00825    config.sync();
00826    accept();
00827 }
00828 
00829 void KRotationSetup::aboutButtonClickedSlot(void)
00830 {
00831    KMessageBox::about(this, i18n("\
00832 <h3>KRotation Screen Saver for KDE</h3>\
00833 <p>Simulation of a force free rotating asymmetric body</p>\
00834 <p>Copyright (c) Georg&nbsp;Drenkhahn 2004</p>\
00835 <p><tt>Georg.Drenkhahn@gmx.net</tt></p>"));
00836 }
00837 
00838 void KRotationSetup::xTraceToggled(bool state)
00839 {
00840    m_saver->setTraceFlag(0, state);
00841 }
00842 void KRotationSetup::yTraceToggled(bool state)
00843 {
00844    m_saver->setTraceFlag(1, state);
00845 }
00846 void KRotationSetup::zTraceToggled(bool state)
00847 {
00848    m_saver->setTraceFlag(2, state);
00849 }
00850 void KRotationSetup::randomTracesToggled(bool state)
00851 {
00852    m_saver->setRandomTraces(state);
00853    if (!state)
00854    {
00855       // restore settings from gui if random traces are turned off
00856       m_saver->setTraceFlag(0, m_xTrace->isChecked());
00857       m_saver->setTraceFlag(1, m_yTrace->isChecked());
00858       m_saver->setTraceFlag(2, m_zTrace->isChecked());
00859    }
00860 }
00861 void KRotationSetup::lengthEnteredSlot(const QString& s)
00862 {
00863    m_saver->setTraceLengthSeconds(s.toDouble());
00864 }
00865 void KRotationSetup::LzEnteredSlot(const QString& s)
00866 {
00867    m_saver->setLz(s.toDouble());
00868    m_saver->initData();
00869 }
00870 void KRotationSetup::thetaEnteredSlot(const QString& s)
00871 {
00872    m_saver->setInitEulerTheta(s.toDouble());
00873    m_saver->initData();
00874 }