pulsaranimationwidget.cpp 24.2 KB
Newer Older
Oliver Bock's avatar
Oliver Bock committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/******************************************************************************
 *   Copyright (C) 2008 by Oliver Bock                                        *
 *   oliver.bock[AT]aei.mpg.de                                                *
 *                                                                            *
 *   This file is part of PulsatingScience.                                   *
 *                                                                            *
 *   PulsatingScience is free software: you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published        *
 *   by the Free Software Foundation, version 3 of the License.               *
 *                                                                            *
 *   PulsatingScience 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 PulsatingScience. If not, see <http://www.gnu.org/licenses/>. *
 *                                                                            *
 ******************************************************************************/

#include "pulsaranimationwidget.h"

23 24 25
// workaround for lack of C99 support in C++
#define isnan(x) ((x) != (x))

Oliver Bock's avatar
Oliver Bock committed
26
const double PulsarAnimationWidget::deg2rad = PI/180.0;
27

Oliver Bock's avatar
Oliver Bock committed
28
PulsarAnimationWidget::PulsarAnimationWidget(QWidget *parent) :
29 30 31
        QGLWidget(QGLFormat(QGL::AlphaChannel | QGL::SampleBuffers), parent),
        m_frameTimer(),
        m_pulseProfile(360, 0.0)
Oliver Bock's avatar
Oliver Bock committed
32
{
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
    QString msgThis = tr("3D animation");
    if(!format().directRendering()) {
        QString msg = tr("Sorry, no direct rendering support for %1...");
        qWarning() << msg.arg(msgThis);
    }
    if(!format().doubleBuffer()) {
        QString msg = tr("Sorry, no double buffering support for %1...");
        qWarning() << msg.arg(msgThis);
    }
    if(!format().rgba()) {
        QString msg = tr("Sorry, no RGBA support for %1...");
        qWarning() << msg.arg(msgThis);
    }
    if(!format().alpha()) {
        QString msg = tr("Sorry, no alpha channel support for %1...");
        qWarning() << msg.arg(msgThis);
    }
    if(!format().sampleBuffers()) {
        QString msg = tr("Sorry, no multisampling support for %1...");
        qWarning() << msg.arg(msgThis);
    }

    // connect primary rendering timer to local callback
Oliver Bock's avatar
Oliver Bock committed
56
    connect(&m_frameTimer, SIGNAL(timeout()), this, SLOT(updateFrame()));
Oliver Bock's avatar
Oliver Bock committed
57

Oliver Bock's avatar
Oliver Bock committed
58
    // initialize quadric pointers
Oliver Bock's avatar
Oliver Bock committed
59
    m_quadricPulsar = NULL;
60
    m_quadricPulsarSpinAxis = NULL;
Oliver Bock's avatar
Oliver Bock committed
61 62
    m_quadricPulsarSpinAxisTop1 = NULL;
    m_quadricPulsarSpinAxisTop2 = NULL;
63
    m_quadricPulsarMagneticAxis = NULL;
64 65
    m_quadricPulsarConeRim1 = NULL;
    m_quadricPulsarConeRim2 = NULL;
66
    m_quadricLineOfSight = NULL;
67 68 69
    m_quadricLineOfSightTop = NULL;
    m_quadricLineOfSightCone = NULL;
    m_quadricLineOfSightConeBase = NULL;
Oliver Bock's avatar
Oliver Bock committed
70

71 72
    // initialize texture pointers
    m_backgroundTexture = 0;
73

74 75 76 77
    // initial render timing settings
    m_framesPerSecond = 25;
    m_pulsarRotationDelta = 0.0;
    m_pulsarRotationAngle = 0.0;
78

79
    m_pulsarRadius = 1.0;
Oliver Bock's avatar
Oliver Bock committed
80 81
    m_pulsarBeamLength = 3.0f;
    m_pulsarBeamRimSize = 0.1f;
82 83
    setPulsarSpinAxisInclination(0);
    setPulsarMagneticAxisInclination(5);
Oliver Bock's avatar
Oliver Bock committed
84 85 86 87
    setPulsarBeamAngle(20);


    // initial spin frequency of 0.5 Hz
88
    m_pulsarRotationDelta = (360.0 * 0.5) / m_framesPerSecond;
Oliver Bock's avatar
Oliver Bock committed
89

90 91 92
    // initial view features
    m_showRotationAxes = false;
    m_cameraInteraction = false;
93

94
    // initial view settings
Oliver Bock's avatar
Oliver Bock committed
95 96 97
    m_mouseAngleH = 0.0;
    m_mouseAngleV = 0.0;
    m_cameraZoom = 30.0;
98 99 100 101
    m_cameraZoomLBound = 10.0;
    m_cameraZoomUBound = 4500.0;
    m_mouseLastX = 0;
    m_mouseLastY = 0;
102

103 104
    // update camera based on settings above
    updateCameraPosition(m_mouseAngleH, m_mouseAngleV, m_cameraZoom);
Oliver Bock's avatar
Oliver Bock committed
105 106 107 108
}

PulsarAnimationWidget::~PulsarAnimationWidget()
{
109 110 111 112 113 114 115
    if(m_quadricPulsar) gluDeleteQuadric(m_quadricPulsar);
    if(m_quadricPulsarConeRim1) gluDeleteQuadric(m_quadricPulsarConeRim1);
    if(m_quadricPulsarConeRim2) gluDeleteQuadric(m_quadricPulsarConeRim2);
    if(m_quadricPulsarSpinAxis) gluDeleteQuadric(m_quadricPulsarSpinAxis);
    if(m_quadricPulsarSpinAxisTop1) gluDeleteQuadric(m_quadricPulsarSpinAxisTop1);
    if(m_quadricPulsarSpinAxisTop2) gluDeleteQuadric(m_quadricPulsarSpinAxisTop2);
    if(m_quadricPulsarMagneticAxis) gluDeleteQuadric(m_quadricPulsarMagneticAxis);
Oliver Bock's avatar
Oliver Bock committed
116
    if(m_quadricLineOfSight) gluDeleteQuadric(m_quadricLineOfSight);
117 118 119
    if(m_quadricLineOfSightTop) gluDeleteQuadric(m_quadricLineOfSightTop);
    if(m_quadricLineOfSightCone) gluDeleteQuadric(m_quadricLineOfSightCone);
    if(m_quadricLineOfSightConeBase) gluDeleteQuadric(m_quadricLineOfSightConeBase);
120
    if(m_backgroundTexture) deleteTexture(m_backgroundTexture);
Oliver Bock's avatar
Oliver Bock committed
121 122 123 124
}

void PulsarAnimationWidget::initializeGL()
{
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClearDepth(1.0);
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);

    glShadeModel(GL_SMOOTH);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

    GLfloat LightAmbient[] = {0.3, 0.3, 0.3, 1.0};
    GLfloat LightDiffuse[] = {1.0, 1.0, 1.0, 1.0};
    GLfloat LightSpecular[] = {1.0, 1.0, 1.0, 1.0};
    GLfloat LightPosition[] = {0.0, 0.0, 0.0, 1.0};
    GLfloat spot_direction[] = {0.0, 0.0, 0.0};

    glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecular);
    glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
    glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 50.0);
    glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction);
    glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 10.0);

    glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
    glEnable(GL_LIGHT0);
    //        glEnable(GL_LIGHTING);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    m_quadricPulsar = gluNewQuadric();
    m_quadricPulsarConeRim1 = gluNewQuadric();
    m_quadricPulsarConeRim2 = gluNewQuadric();
    m_quadricPulsarSpinAxis = gluNewQuadric();
    m_quadricPulsarSpinAxisTop1 = gluNewQuadric();
    m_quadricPulsarSpinAxisTop2 = gluNewQuadric();
    m_quadricPulsarMagneticAxis = gluNewQuadric();
161
    m_quadricLineOfSight = gluNewQuadric();
162 163 164
    m_quadricLineOfSightTop = gluNewQuadric();
    m_quadricLineOfSightCone = gluNewQuadric();
    m_quadricLineOfSightConeBase = gluNewQuadric();
165

Oliver Bock's avatar
Oliver Bock committed
166
    gluQuadricNormals(m_quadricPulsar, GLU_SMOOTH);
167 168
    gluQuadricNormals(m_quadricPulsarConeRim1, GLU_SMOOTH);
    gluQuadricNormals(m_quadricPulsarConeRim2, GLU_SMOOTH);
169
    gluQuadricNormals(m_quadricPulsarSpinAxis, GLU_SMOOTH);
170 171
    gluQuadricNormals(m_quadricPulsarSpinAxisTop1, GLU_SMOOTH);
    gluQuadricNormals(m_quadricPulsarSpinAxisTop2, GLU_SMOOTH);
172
    gluQuadricNormals(m_quadricPulsarMagneticAxis, GLU_SMOOTH);
173
    gluQuadricNormals(m_quadricLineOfSight, GLU_SMOOTH);
174 175 176
    gluQuadricNormals(m_quadricLineOfSightTop, GLU_SMOOTH);
    gluQuadricNormals(m_quadricLineOfSightCone, GLU_SMOOTH);
    gluQuadricNormals(m_quadricLineOfSightConeBase, GLU_SMOOTH);
177

Oliver Bock's avatar
Oliver Bock committed
178 179 180 181
    // query max texture size (estimate)
    GLint maxTextureSize;
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);

Oliver Bock's avatar
Oliver Bock committed
182 183 184 185 186
    // prepare local messages
    QString msgShape = tr("%1 texture shape not quadratic!");
    QString msgPower = tr("%1 texture dimensions not a power of 2!");
    QString msgSize = tr("Maximum texture size exceeded! Scaling down %1 texture to %2x%3...");

Oliver Bock's avatar
Oliver Bock committed
187
    // prepare and check background texture
Oliver Bock's avatar
Oliver Bock committed
188
    QImage backgroundTexture(":/textures/resources/texture_background_carina.png");
Oliver Bock's avatar
Oliver Bock committed
189
    if(backgroundTexture.width() != backgroundTexture.height()) {
190
        qWarning() << msgShape.arg(tr("Background"));
Oliver Bock's avatar
Oliver Bock committed
191 192
    }
    else {
193 194 195 196 197 198
        double integer = 0.0;
        double fraction = 0.0;
        fraction = modf(log(backgroundTexture.width()) / log(2.0), &integer);
        if(fraction > 0.0) {
            qWarning() << msgPower.arg(tr("Background"));
        }
Oliver Bock's avatar
Oliver Bock committed
199 200
    }
    if(backgroundTexture.width() > maxTextureSize) {
201 202
        qWarning() << msgSize.arg(tr("background").arg(maxTextureSize).arg(maxTextureSize));
        backgroundTexture = backgroundTexture.scaled(maxTextureSize, maxTextureSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
Oliver Bock's avatar
Oliver Bock committed
203 204 205
    }

    // bind textures
Oliver Bock's avatar
Oliver Bock committed
206
    m_backgroundTexture = bindTexture(backgroundTexture, GL_TEXTURE_2D, GL_RGBA);
207

208
    // use mipmapped textures
209 210 211
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);

Oliver Bock's avatar
Oliver Bock committed
212 213 214
    // enable 2D mapping (s/t coordinates, sphere map won't rotate texture!)
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
Oliver Bock's avatar
Oliver Bock committed
215 216 217 218
}

void PulsarAnimationWidget::resizeGL(int w, int h)
{
219
    glViewport(0, 0, w, h);
Oliver Bock's avatar
Oliver Bock committed
220

221 222
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
Oliver Bock's avatar
Oliver Bock committed
223

224
    gluPerspective(4.5, (GLfloat)w / (GLfloat)h, 0.1, 5000.0);
Oliver Bock's avatar
Oliver Bock committed
225

226 227
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
Oliver Bock's avatar
Oliver Bock committed
228 229 230 231
}

void PulsarAnimationWidget::paintGL()
{
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
    GLfloat x,y,angle;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    gluLookAt(m_cameraPosX, m_cameraPosY, m_cameraPosZ,
              0.0, 0.0, 0.0,
              0.0, 1.0, 0.0);

    // TODO: should be located elsewhere
    static GLfloat no_mat[] = {0.0, 0.0, 0.0, 1.0};
    static GLfloat mat_diffuse[] = {0.5, 0.5, 0.5, 1.0};
    static GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
    static GLfloat low_shininess[] = {2.5};

    glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);

    // let's draw everything as wireframe model
    glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);

    // draw scence
    glPushMatrix();
    {
261 262 263 264 265 266
        //draw line of sight (observer)
        if(m_showRotationAxes) {
            glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
            glColor3f(0.33f, 0.33f, 1.0f);
            glPushMatrix();
            {
267
                glTranslatef(0.0, 0.0, m_pulsarRadius);
268 269 270 271 272 273 274
                gluCylinder(m_quadricLineOfSightCone, 0, 0.1, 0.5, 32, 1);
            }
            glPopMatrix();
            glPushMatrix();
            {
                glTranslatef(0.0, 0.0, m_pulsarRadius + 0.5);
                gluDisk(m_quadricLineOfSightConeBase, 0, 0.1, 32, 8);
275 276 277 278
            }
            glPopMatrix();
            glPushMatrix();
            {
279 280
                glTranslatef(0.0, 0.0, m_pulsarRadius + 0.5);
                gluCylinder(m_quadricLineOfSight, 0.025, 0.025, 3.51, 32, 1);
281 282 283 284
            }
            glPopMatrix();
            glPushMatrix();
            {
285 286
                glTranslatef(0.0, 0.0, m_pulsarRadius + 4.01);
                gluDisk(m_quadricLineOfSightTop, 0, 0.025, 32, 8);
287 288 289 290 291
            }
            glPopMatrix();
            glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
        }

292 293 294 295 296
        // draw pulsar
        glPushMatrix();
        {
            glRotatef(-m_pulsarSpinAxisInclination, 1.0, 0.0, 0.0);
            glRotatef(m_pulsarRotationAngle, 0.0, 0.0, 1.0);
297

298 299 300 301 302 303 304
            // draw spin axis
            if(m_showRotationAxes) {
                glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
                glColor3f(1.0f, 1.0f, 1.0f);
                glPushMatrix();
                {
                    glTranslatef(0.0, 0.0, -5.0);
305
                    gluCylinder(m_quadricPulsarSpinAxis, 0.020, 0.020, 10.0, 32, 1);
306 307 308 309 310
                }
                glPopMatrix();
                glPushMatrix();
                {
                    glTranslatef(0.0, 0.0, -5.0);
311
                    gluDisk(m_quadricPulsarSpinAxisTop1, 0, 0.020, 32, 8);
312 313 314 315 316
                }
                glPopMatrix();
                glPushMatrix();
                {
                    glTranslatef(0.0, 0.0, 5.0);
317
                    gluDisk(m_quadricPulsarSpinAxisTop2, 0, 0.020, 32, 8);
318 319 320 321
                }
                glPopMatrix();
                glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
            }
Oliver Bock's avatar
Oliver Bock committed
322

323 324
            // gives pulsar spherical appearance
            glEnable(GL_LIGHTING);
Oliver Bock's avatar
Oliver Bock committed
325

326 327
            glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
328
            gluSphere(m_quadricPulsar, m_pulsarRadius, 32, 32);
Oliver Bock's avatar
Oliver Bock committed
329

330 331 332
            glDisable(GL_LIGHTING);
        }
        glPopMatrix();
333

334 335 336 337
        // TODO: should be located elsewhere
        static GLfloat coneAmbient[] = {1.0, 1.0, 0.0, 1.0};
        static GLfloat coneDiffuse[] = {1.0, 1.0, 0.0, 1.0};
        static GLfloat coneSpecular[] = {1.0, 1.0, 0.5, 1.0};
Oliver Bock's avatar
Oliver Bock committed
338

339
        glMaterialfv(GL_FRONT, GL_SPECULAR, coneSpecular);
Oliver Bock's avatar
Oliver Bock committed
340

341 342 343
        // first cone
        glPushMatrix();
        {
344
            glRotatef(-m_pulsarSpinAxisInclination, 1.0, 0.0, 0.0);
345

346
            glRotatef(m_pulsarRotationAngle - 90.0, 0.0, 0.0, 1.0);
347
            glRotatef(m_pulsarMagneticAxisInclination, 0.0, 1.0, 0.0);
348

349 350 351 352 353 354 355
            // draw magnetic axis (for both cones)
            if(m_showRotationAxes) {
                glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
                glColor3f(1.0f, 1.0f, 0.0f);
                glPushMatrix();
                {
                    glTranslatef(0.0, 0.0, -5.0);
356
                    gluCylinder(m_quadricPulsarMagneticAxis, 0.020, 0.020, 10.0, 32, 1);
357 358 359 360 361
                }
                glPopMatrix();
                glPushMatrix();
                {
                    glTranslatef(0.0, 0.0, -5.0);
362
                    gluDisk(m_quadricPulsarSpinAxisTop1, 0, 0.020, 32, 8);
363 364 365 366 367
                }
                glPopMatrix();
                glPushMatrix();
                {
                    glTranslatef(0.0, 0.0, 5.0);
368
                    gluDisk(m_quadricPulsarSpinAxisTop2, 0, 0.020, 32, 8);
369 370 371 372 373
                }
                glPopMatrix();
                glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
            }

374
            glTranslatef(0.0, 0.0, -m_pulsarBeamLength);
375 376 377 378 379 380 381 382 383 384 385 386 387 388

            glMaterialfv(GL_FRONT, GL_AMBIENT, coneAmbient);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, coneDiffuse);

            // draw first cone's outer layer
            glBegin(GL_TRIANGLE_FAN);
            {
                // Pinnacle of cone is shared vertex for fan, moved up z-axis
                // to produce a cone instead of a circle
                glColor3f(1.0f, 1.0f, 0.0f);
                glVertex3f(0.0f, 0.0f, m_pulsarBeamLength);

                // Loop around in a circle and specify even points along the circle
                // as the vertices of the triangle fan (32 sections)
Oliver Bock's avatar
Oliver Bock committed
389
                for(angle = 0.0f; angle < (2.0f*PI); angle += (PI/32.0f))
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
                {
                    // Calculate x and y position of the next vertex
                    x = m_pulsarBeamOuterRadius * sin(angle);
                    y = m_pulsarBeamOuterRadius * cos(angle);

                    // Specify the next vertex for the triangle fan
                    glVertex2f(x, y);
                }
            }
            glEnd();

            // draw first cone's inner layer (do we need/want this?)
            glBegin(GL_TRIANGLE_FAN);
            {
                glColor3f(0.66f, 0.66f, 0.0f);
                glVertex3f(0.0f, 0.0f, m_pulsarBeamLength);
Oliver Bock's avatar
Oliver Bock committed
406
                for(angle = 0.0f; angle < (2.0f*PI); angle += (PI/32.0f)) {
407 408 409 410 411 412 413 414 415 416 417 418
                    x = m_pulsarBeamInnerRadius * sin(angle);
                    y = m_pulsarBeamInnerRadius * cos(angle);
                    glVertex2f(x, y);
                }
            }
            glEnd();

            // draw first cone's "rim" (disk topping gap between both cones)
            glColor3f(1.0f, 1.0f, 0.5f);
            gluDisk(m_quadricPulsarConeRim1, m_pulsarBeamInnerRadius, m_pulsarBeamOuterRadius, 32, 8);
        }
        glPopMatrix();
419

420 421 422 423 424
        // second cone
        glPushMatrix();
        {
            glRotatef(180.0, 1.0, 0.0, 0.0);
            glRotatef(-m_pulsarSpinAxisInclination, 1.0, 0.0, 0.0);
425

426
            glRotatef(-m_pulsarRotationAngle - 90.0, 0.0, 0.0, 1.0);
427
            glRotatef(m_pulsarMagneticAxisInclination, 0.0, 1.0, 0.0);
428

429
            glTranslatef(0.0, 0.0, -m_pulsarBeamLength);
430 431 432 433 434 435 436 437 438 439 440 441 442 443

            glMaterialfv(GL_FRONT, GL_AMBIENT, coneAmbient);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, coneDiffuse);

            // draw first cone's outer layer
            glBegin(GL_TRIANGLE_FAN);
            {
                // Pinnacle of cone is shared vertex for fan, moved up z-axis
                // to produce a cone instead of a circle
                glColor3f(1.0f, 1.0f, 0.0f);
                glVertex3f(0.0f, 0.0f, 3.0f);

                // Loop around in a circle and specify even points along the circle
                // as the vertices of the triangle fan (32 sections)
Oliver Bock's avatar
Oliver Bock committed
444
                for(angle = 0.0f; angle < (2.0f*PI); angle += (PI/32.0f))
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
                {
                    // Calculate x and y position of the next vertex
                    x = m_pulsarBeamOuterRadius * sin(angle);
                    y = m_pulsarBeamOuterRadius * cos(angle);

                    // Specify the next vertex for the triangle fan
                    glVertex2f(x, y);
                }
            }
            glEnd();

            // draw first cone's inner layer (do we need/want this?)
            glBegin(GL_TRIANGLE_FAN);
            {
                glColor3f(0.66f, 0.66f, 0.0f);
                glVertex3f(0.0f, 0.0f, 3.0f);
Oliver Bock's avatar
Oliver Bock committed
461
                for(angle = 0.0f; angle < (2.0f*PI); angle += (PI/32.0f)) {
462 463 464 465 466 467 468 469 470 471 472
                    x = m_pulsarBeamInnerRadius * sin(angle);
                    y = m_pulsarBeamInnerRadius * cos(angle);
                    glVertex2f(x, y);
                }
            }
            glEnd();

            // draw first cone's "rim" (disk topping gap between both cones)
            glColor3f(1.0f, 1.0f, 0.5f);
            gluDisk(m_quadricPulsarConeRim2, m_pulsarBeamInnerRadius, m_pulsarBeamOuterRadius, 32, 8);
        }
473
        glPopMatrix();
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
    }
    glPopMatrix();

    glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);

    // save current state
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    {
        glLoadIdentity();
        glOrtho(0, width(), 0, height(), 0.1, 501.0);
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        {
            glLoadIdentity();

            // draw backdrop (independent parallel projection)
            glColor3f(1.0f, 1.0f, 1.0f);
            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_specular);
            glTranslatef(0.0, 0.0, -501.0);
            drawTexture(QPointF(0.0, 0.0), m_backgroundTexture);

            // restore original state
            glMatrixMode(GL_PROJECTION);
        }
499
        glPopMatrix();
500 501 502
        glMatrixMode(GL_MODELVIEW);
    }
    glPopMatrix();
Oliver Bock's avatar
Oliver Bock committed
503 504
}

Oliver Bock's avatar
Oliver Bock committed
505 506
void PulsarAnimationWidget::runAnimation()
{
507
    m_frameTimer.start(qRound(1000.0 / m_framesPerSecond));
Oliver Bock's avatar
Oliver Bock committed
508
}
Oliver Bock's avatar
Oliver Bock committed
509

Oliver Bock's avatar
Oliver Bock committed
510 511
void PulsarAnimationWidget::pauseAnimation()
{
512
    m_frameTimer.stop();
Oliver Bock's avatar
Oliver Bock committed
513 514 515 516
}

void PulsarAnimationWidget::stopAnimation()
{
517 518
    m_frameTimer.stop();
    resetParameters();
Oliver Bock's avatar
Oliver Bock committed
519

520
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
521 522
}

523 524 525 526 527 528 529 530 531
void PulsarAnimationWidget::resetViewpoint()
{
    m_mouseAngleH = 0.0;
    m_mouseAngleV = 0.0;
    m_cameraZoom = 30.0;

    updateCameraPosition(m_mouseAngleH, m_mouseAngleV, m_cameraZoom);
}

Oliver Bock's avatar
Oliver Bock committed
532
void PulsarAnimationWidget::updateFrame()
Oliver Bock's avatar
Oliver Bock committed
533
{
534 535 536 537 538
    m_pulsarRotationAngle += m_pulsarRotationDelta;
    if(m_pulsarRotationAngle > 360.0) {
        m_pulsarRotationAngle -= 360.0;
        updatePulseProfile();
    }
Oliver Bock's avatar
Oliver Bock committed
539

540
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
541

542
    emit pulsarAnimationStep(m_pulsarRotationAngle);
543 544
}

Oliver Bock's avatar
Oliver Bock committed
545 546
void PulsarAnimationWidget::showRotationAxes(bool enabled)
{
547
    m_showRotationAxes = enabled;
Oliver Bock's avatar
Oliver Bock committed
548

549
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
550 551
}

552 553
void PulsarAnimationWidget::mousePressEvent(QMouseEvent *event)
{
554
    Q_UNUSED(event);
555

556 557
    m_cameraInteraction = true;
    updateGL();
558 559
}

560 561
void PulsarAnimationWidget::mouseMoveEvent(QMouseEvent *event)
{
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
    Qt::MouseButtons buttons = event->buttons();
    if((buttons & Qt::LeftButton) == Qt::LeftButton) {
        if(m_mouseLastX != 0) {
            m_mouseAngleH += (m_mouseLastX - event->x());
            m_mouseAngleH = m_mouseAngleH < 360 ? m_mouseAngleH : 0;
            m_mouseAngleH = m_mouseAngleH >= 0 ? m_mouseAngleH : 359;
        }
        if(m_mouseLastY != 0) {
            m_mouseAngleV -= (m_mouseLastY - event->y());
            m_mouseAngleV = m_mouseAngleV < 90 ? m_mouseAngleV : 90;
            m_mouseAngleV = m_mouseAngleV > -90 ? m_mouseAngleV : -90;
        }

        m_mouseLastX = event->x();
        m_mouseLastY = event->y();
    }
    else if((buttons & Qt::RightButton) == Qt::RightButton) {
        if(m_mouseLastY != 0) {
            m_cameraZoom -= (m_mouseLastY - event->y());
            m_cameraZoom = m_cameraZoom >= m_cameraZoomLBound ? m_cameraZoom : m_cameraZoomLBound;
            m_cameraZoom = m_cameraZoom >= m_cameraZoomUBound ? m_cameraZoomUBound : m_cameraZoom;
        }

        m_mouseLastY = event->y();
    }

    updateCameraPosition(m_mouseAngleH, m_mouseAngleV, m_cameraZoom);
589 590 591 592
}

void PulsarAnimationWidget::mouseReleaseEvent(QMouseEvent *event)
{
593
    Q_UNUSED(event);
594

595 596 597
    m_mouseLastX = 0;
    m_mouseLastY = 0;
    m_cameraInteraction = false;
598

599
    updateGL();
600 601
}

Oliver Bock's avatar
Oliver Bock committed
602 603
void PulsarAnimationWidget::showEvent(QShowEvent *event)
{
604
    Q_UNUSED(event);
Oliver Bock's avatar
Oliver Bock committed
605

606 607
    // update and propagate pulse profile
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
608 609
}

Oliver Bock's avatar
Oliver Bock committed
610
void PulsarAnimationWidget::updateCameraPosition(const double angleH, const double angleV, const double zoom)
611
{
Oliver Bock's avatar
Oliver Bock committed
612 613
    m_cameraPosX = sin(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
    m_cameraPosY = sin(angleV * deg2rad) * zoom;
614
    m_cameraPosZ = cos(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
615

616
    updateGL();
617

618
    qDebug("Camera (x,y,z,+,h,v): %f, %f, %f, %f, %f, %f", m_cameraPosX, m_cameraPosY, m_cameraPosZ, zoom, angleH, angleV);
619 620
}

Oliver Bock's avatar
Oliver Bock committed
621 622
void PulsarAnimationWidget::setFramePerSecond(const unsigned int fps)
{
623
    m_framesPerSecond = fps;
Oliver Bock's avatar
Oliver Bock committed
624 625
}

Oliver Bock's avatar
Oliver Bock committed
626
void PulsarAnimationWidget::setPulsarSpinFrequency(const double frequency)
Oliver Bock's avatar
Oliver Bock committed
627
{
628 629
    m_pulsarRotationDelta = (360.0 * frequency) / m_framesPerSecond;
    updatePulseProfile();
630
}
631

632 633
void PulsarAnimationWidget::setPulsarSpinAxisInclination(const int degrees)
{
634 635
    m_pulsarSpinAxisInclination = degrees;
    updatePulseProfile();
636

637
    updateGL();
638 639
}

640 641
void PulsarAnimationWidget::setPulsarMagneticAxisInclination(const int degrees)
{
642 643
    m_pulsarMagneticAxisInclination = degrees;
    updatePulseProfile();
644

645
    updateGL();
646
}
647

Oliver Bock's avatar
Oliver Bock committed
648 649
void PulsarAnimationWidget::setPulsarBeamAngle(const int degrees)
{
650 651
    m_pulsarBeamAngle = degrees;

652
    m_pulsarBeamOuterRadius = tan(deg2rad * degrees * 0.5f) * m_pulsarBeamLength + m_pulsarBeamRimSize * 0.5f;
Oliver Bock's avatar
Oliver Bock committed
653
    m_pulsarBeamInnerRadius = m_pulsarBeamOuterRadius - m_pulsarBeamRimSize;
Oliver Bock's avatar
Oliver Bock committed
654
    if(m_pulsarBeamInnerRadius < 0.0) m_pulsarBeamInnerRadius = 0.0;
Oliver Bock's avatar
Oliver Bock committed
655 656 657 658 659
    updatePulseProfile();

    updateGL();
}

660 661
void PulsarAnimationWidget::resetParameters()
{
662 663
    m_pulsarRotationAngle = 0.0;
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
664

665
    emit pulsarAnimationStep(m_pulsarRotationAngle);
666
}
667 668 669

void PulsarAnimationWidget::updatePulseProfile()
{
670 671
    // prepare parameters (e.g. convert to radians where necessary)
    const double    deltaPhiRot     = deg2rad * 1.0;
672 673 674 675 676
    const double    gamma           = deg2rad * m_pulsarSpinAxisInclination;
    const double    alpha           = deg2rad * m_pulsarMagneticAxisInclination;
    const double    beta            = alpha - gamma;
    const double    rho             = deg2rad * m_pulsarBeamAngle * 0.5;
    const double    gaussProfile    = 0.04;
677

678 679 680
    const double    t1              = pow(sin(rho*0.5), 2) - pow(sin(beta*0.5), 2);
    const double    t2              = sin(alpha) * sin(beta+alpha);
    const double    W               = 2 * asin(sqrt( t1 / t2 ));
681

682
    qDebug("alpha: %f, beta: %f, rho: %f, t1: %f, t2: %f, W: %f", alpha*180/PI, beta*180/PI, rho*180/PI, t1*180/PI, t2*180/PI, W*180/PI);
683

684 685 686
    for(int x = 0; x < 360; ++x) {
        // update pulsar rotation angle
        const double phiRot = x * deltaPhiRot;
687 688

        // determine and store pulse amplitude
689 690 691 692 693 694
        if(!isnan(W)){
            m_pulseProfile[x] = exp(-2.0 * pow(phiRot -W, 2) / gaussProfile) + exp(-2.0 * pow(phiRot + W - 2.0 * PI, 2) / gaussProfile);
        }
        else {
            m_pulseProfile[x] = 0.0;
        }
695 696 697 698
    }

    // propagate new profile
    emit pulseProfileUpdated(m_pulseProfile);
699
}