pulsaranimationwidget.cpp 27.8 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"

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

Oliver Bock's avatar
Oliver Bock committed
25
PulsarAnimationWidget::PulsarAnimationWidget(QWidget *parent) :
26 27 28
    QGLWidget(QGLFormat(QGL::AlphaChannel | QGL::SampleBuffers), parent),
    m_frameTimer(),
    m_pulseProfile(360, 0.0)
Oliver Bock's avatar
Oliver Bock committed
29
{
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
    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
53
    connect(&m_frameTimer, SIGNAL(timeout()), this, SLOT(updateFrame()));
Oliver Bock's avatar
Oliver Bock committed
54

Oliver Bock's avatar
Oliver Bock committed
55
    // initialize quadric pointers
Oliver Bock's avatar
Oliver Bock committed
56 57
    m_quadricCompanionOrbitPlane = NULL;
    m_quadricCompanion = NULL;
58
    m_quadricPulsarOrbitPlane = NULL;
Oliver Bock's avatar
Oliver Bock committed
59
    m_quadricPulsar = NULL;
60 61
    m_quadricPulsarCone1Shell = NULL;
    m_quadricPulsarCone2Shell = NULL;
62
    m_quadricPulsarSpinAxis = NULL;
63 64
    m_quadricPulsarSpinAxisTop1 = NULL;
    m_quadricPulsarSpinAxisTop2 = NULL;
65 66 67 68
    m_quadricPulsarMagneticAxis = NULL;

    // initialize texture pointers
    m_backgroundTexture = 0;
69
    m_beamTexture = 0;
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

    // initial render timing settings
    m_framesPerSecond = 25;
    m_pulsarRotationDelta = 0.0;
    m_orbitRotationDelta = 0.0;
    m_pulsarRotationAngle = 0.0;
    m_orbitRotationAngle = 0.0;

    // initial binary system parameters (have to match GUI!)
    m_pulsarMass = 1.4;
    // initial companion is "Neutron Star"
    m_companionMass = 1.4;
    m_pulsarSpinAxisInclination = 0.0;
    m_pulsarMagneticAxisInclination = 60.0;
    m_pulsarSemiMajorAxis = 5.0;
    m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;
    // initial spin frequency of 0.5 Hz
    m_pulsarRotationDelta = (360.0 * 0.5) / m_framesPerSecond;
88 89 90 91 92
    // beam properties (keep this order!)
    m_pulsarBeamLength = 3.0f;
    setPulsarBeamAngle(30);


93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
    // update orbital period (based on settings above)
    updateOrbitPeriod();

    // initial view features
    m_showOrbits = false;
    m_showRotationAxes = false;
    m_cameraInteraction = false;

    // initial view settings
    m_mouseAngleH = 90.0;
    m_mouseAngleV = 30.0;
    m_cameraZoom = 150.0;
    m_cameraZoomLBound = 10.0;
    m_cameraZoomUBound = 4500.0;
    m_mouseLastX = 0;
    m_mouseLastY = 0;

    // update camera based on settings above
    updateCameraPosition(m_mouseAngleH, m_mouseAngleV, m_cameraZoom);
Oliver Bock's avatar
Oliver Bock committed
112 113 114 115
}

PulsarAnimationWidget::~PulsarAnimationWidget()
{
116 117 118 119
    if(m_quadricCompanionOrbitPlane) gluDeleteQuadric(m_quadricCompanionOrbitPlane);
    if(m_quadricCompanion) gluDeleteQuadric(m_quadricCompanion);
    if(m_quadricPulsarOrbitPlane) gluDeleteQuadric(m_quadricPulsarOrbitPlane);
    if(m_quadricPulsar) gluDeleteQuadric(m_quadricPulsar);
120 121
    if(m_quadricPulsarCone1Shell) gluDeleteQuadric(m_quadricPulsarCone1Shell);
    if(m_quadricPulsarCone2Shell) gluDeleteQuadric(m_quadricPulsarCone2Shell);
122
    if(m_quadricPulsarSpinAxis) gluDeleteQuadric(m_quadricPulsarSpinAxis);
123 124
    if(m_quadricPulsarSpinAxisTop1) gluDeleteQuadric(m_quadricPulsarSpinAxisTop1);
    if(m_quadricPulsarSpinAxisTop2) gluDeleteQuadric(m_quadricPulsarSpinAxisTop2);
125 126 127
    if(m_quadricPulsarMagneticAxis) gluDeleteQuadric(m_quadricPulsarMagneticAxis);

    if(m_backgroundTexture) deleteTexture(m_backgroundTexture);
128
    if(m_beamTexture) deleteTexture(m_beamTexture);
Oliver Bock's avatar
Oliver Bock committed
129 130 131 132
}

void PulsarAnimationWidget::initializeGL()
{
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 161 162 163 164
    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, 3.0, 1.0};
    GLfloat spot_direction[] = {0.0, 0.0, -1.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);

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

    m_quadricCompanionOrbitPlane = gluNewQuadric();
    m_quadricCompanion = gluNewQuadric();
    m_quadricPulsarOrbitPlane = gluNewQuadric();
    m_quadricPulsar = gluNewQuadric();
165 166
    m_quadricPulsarCone1Shell = gluNewQuadric();
    m_quadricPulsarCone2Shell = gluNewQuadric();
167
    m_quadricPulsarSpinAxis = gluNewQuadric();
168 169
    m_quadricPulsarSpinAxisTop1 = gluNewQuadric();
    m_quadricPulsarSpinAxisTop2 = gluNewQuadric();
170
    m_quadricPulsarMagneticAxis = gluNewQuadric();
171

Oliver Bock's avatar
Oliver Bock committed
172 173
    gluQuadricNormals(m_quadricCompanionOrbitPlane, GLU_SMOOTH);
    gluQuadricNormals(m_quadricCompanion, GLU_SMOOTH);
174
    gluQuadricNormals(m_quadricPulsarOrbitPlane, GLU_SMOOTH);
Oliver Bock's avatar
Oliver Bock committed
175
    gluQuadricNormals(m_quadricPulsar, GLU_SMOOTH);
176 177
    gluQuadricNormals(m_quadricPulsarCone1Shell, GLU_SMOOTH);
    gluQuadricNormals(m_quadricPulsarCone2Shell, GLU_SMOOTH);
178
    gluQuadricNormals(m_quadricPulsarSpinAxis, GLU_SMOOTH);
179 180
    gluQuadricNormals(m_quadricPulsarSpinAxisTop1, GLU_SMOOTH);
    gluQuadricNormals(m_quadricPulsarSpinAxisTop2, GLU_SMOOTH);
181
    gluQuadricNormals(m_quadricPulsarMagneticAxis, GLU_SMOOTH);
182

Oliver Bock's avatar
Oliver Bock committed
183 184 185 186
    // query max texture size (estimate)
    GLint maxTextureSize;
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);

Oliver Bock's avatar
Oliver Bock committed
187 188 189 190 191
    // 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
192
    // prepare and check background texture
Oliver Bock's avatar
Oliver Bock committed
193
    QImage backgroundTexture(":/textures/resources/texture_background_carina.png");
Oliver Bock's avatar
Oliver Bock committed
194
    if(backgroundTexture.width() != backgroundTexture.height()) {
195
        qWarning() << msgShape.arg(tr("Background"));
Oliver Bock's avatar
Oliver Bock committed
196 197
    }
    else {
198 199 200 201 202 203
        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
204 205
    }
    if(backgroundTexture.width() > maxTextureSize) {
206 207
        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
208 209
    }

210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
    // prepare and check beam texture
    QImage beamTexture(":/textures/resources/texture_beam.png");
    if(beamTexture.width() != beamTexture.height()) {
        qWarning() << msgShape.arg(tr("Beam"));
    }
    else {
        double integer = 0.0;
        double fraction = 0.0;
        fraction = modf(log(beamTexture.width()) / log(2.0), &integer);
        if(fraction > 0.0) {
            qWarning() << msgPower.arg(tr("Beam"));
        }
    }
    if(beamTexture.width() > maxTextureSize) {
        qWarning() << msgSize.arg(tr("beam").arg(maxTextureSize).arg(maxTextureSize));
        beamTexture = beamTexture.scaled(maxTextureSize, maxTextureSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    }

Oliver Bock's avatar
Oliver Bock committed
228
    // bind textures
Oliver Bock's avatar
Oliver Bock committed
229
    m_backgroundTexture = bindTexture(backgroundTexture, GL_TEXTURE_2D, GL_RGBA);
230
    m_beamTexture = bindTexture(beamTexture, GL_TEXTURE_2D, GL_RGBA);
231

232
    // use mipmapped textures
233 234
    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
235 236 237 238
}

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

241 242
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
Oliver Bock's avatar
Oliver Bock committed
243

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

246 247
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
Oliver Bock's avatar
Oliver Bock committed
248 249 250 251
}

void PulsarAnimationWidget::paintGL()
{
252 253
    GLfloat x,y,angle;

254 255
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

256 257 258 259 260 261 262 263 264 265 266 267 268
    // 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};
    static GLfloat translucent[] = {1.0, 1.0, 1.0, 0.33};

    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);

269 270 271 272 273 274 275
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

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

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
    // save current state (the following is using parallel projection)
    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(0.5, 0.5, 0.5f);
            glTranslatef(0.0, 0.0, -501.0);
            drawTexture(QPointF(0.0, 0.0), m_backgroundTexture);

            // restore original state
            glMatrixMode(GL_PROJECTION);
        }
        glPopMatrix();
        glMatrixMode(GL_MODELVIEW);
    }
    glPopMatrix();

300 301
    // draw companion
    glPushMatrix();
302 303 304 305
    {
        glTranslatef(sin((m_orbitRotationAngle + 180.0) * deg2rad) * m_companionSemiMajorAxis,
                     0.0,
                     cos((m_orbitRotationAngle + 180.0) * deg2rad) * m_companionSemiMajorAxis);
306 307 308

        // draw, with proper lighting
        glEnable(GL_LIGHTING);
309
        gluSphere(m_quadricCompanion, 1.0, 32, 32);
310
        glDisable(GL_LIGHTING);
311
    }
312 313
    glPopMatrix();

314 315 316
    // enable wireframe mode
    glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);

317 318
    // draw pulsar
    glPushMatrix();
319 320 321 322
    {
        glTranslatef(sin(m_orbitRotationAngle * deg2rad) * m_pulsarSemiMajorAxis,
                     0.0,
                     cos(m_orbitRotationAngle * deg2rad) * m_pulsarSemiMajorAxis);
323 324

        glPushMatrix();
325 326 327
        {
            glRotatef(m_pulsarSpinAxisInclination, 0.0, 0.0, 1.0);
            glRotatef(m_pulsarRotationAngle, 0.0, 1.0, 0.0);
328
            glRotatef(90, 0.0, 0.0, 0.0);
329 330 331 332 333 334 335 336

            // draw spin axis
            if(m_showRotationAxes) {
                glPushMatrix();
                {
                    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);
                    glTranslatef(0.0, 0.0, -5.0);
                    gluCylinder(m_quadricPulsarSpinAxis, 0.05, 0.05, 10.0, 32, 1);
337 338
                    glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
                    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
339 340 341
                }
                glPopMatrix();
            }
342

343 344 345 346
            // draw sphere, with proper lighting
            glEnable(GL_LIGHTING);
            glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
347
            gluSphere(m_quadricPulsar, 1.0, 32, 32);
348
            glDisable(GL_LIGHTING);
349 350
        }
        glPopMatrix();
351

352
        // first cone
353
        glPushMatrix();
354 355 356 357 358 359 360 361 362
        {
            glRotatef(90.0, 1.0, 0.0, 0.0);
            glRotatef(m_pulsarSpinAxisInclination, 0.0, 1.0, 0.0);

            glRotatef(-m_pulsarRotationAngle - 90.0, 0.0, 0.0, 1.0);
            glRotatef(-m_pulsarMagneticAxisInclination, 1.0, 0.0, 0.0);

            // draw magnetic axis (for both cones)
            if(m_showRotationAxes) {
363 364
                glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
                glColor3f(1.0f, 1.0f, 0.0f);
365 366
                glPushMatrix();
                {
367 368
                    glTranslatef(0.0, 0.0, -4.0);
                    gluCylinder(m_quadricPulsarMagneticAxis, 0.020, 0.020, 8.0, 32, 1);
369 370
                }
                glPopMatrix();
371 372 373 374 375 376 377 378 379 380 381 382 383
                glPushMatrix();
                {
                    glTranslatef(0.0, 0.0, -4.0);
                    gluDisk(m_quadricPulsarSpinAxisTop1, 0, 0.020, 32, 8);
                }
                glPopMatrix();
                glPushMatrix();
                {
                    glTranslatef(0.0, 0.0, 4.0);
                    gluDisk(m_quadricPulsarSpinAxisTop2, 0, 0.020, 32, 8);
                }
                glPopMatrix();
                glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
384
            }
385

386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
            glTranslatef(0.0, 0.0, -m_pulsarBeamLength);

            // 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
                glColor4f(1.0f, 1.0f, 0.0f, 0.33f);
                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)
                for(angle = 0.0f; angle < (2.0f*PI); angle += (PI/32.0f))
                {
                    // 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 "base" (textured bottom to visualize beam intensity)
            glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
            glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

            // create texture coordinates and enable texturing
            gluQuadricTexture(m_quadricPulsarCone1Shell, GL_TRUE);
            glBindTexture(GL_TEXTURE_2D, m_beamTexture);
            glEnable(GL_TEXTURE_2D);

            gluDisk(m_quadricPulsarCone1Shell, 0, m_pulsarBeamOuterRadius, 32, 1);

            // disable texturing
            glDisable(GL_TEXTURE_2D);
423

424
            glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
425 426
        }
        glPopMatrix();
427

428
        // second cone (pointing towards from camera)
429 430 431 432
        glPushMatrix();
        {
            glRotatef(-90.0, 1.0, 0.0, 0.0);
            glRotatef(-m_pulsarSpinAxisInclination, 0.0, 1.0, 0.0);
433

434 435
            glRotatef(m_pulsarRotationAngle - 90.0, 0.0, 0.0, 1.0);
            glRotatef(m_pulsarMagneticAxisInclination, 1.0, 0.0, 0.0);
436

437
            glTranslatef(0.0, 0.0, -m_pulsarBeamLength);
438

439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
            // draw second 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
                glColor4f(1.0f, 1.0f, 0.0f, 0.33f);
                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)
                for(angle = 0.0f; angle < (2.0f*PI); angle += (PI/32.0f))
                {
                    // Calculate x and y position of the next vertex
                    x = m_pulsarBeamOuterRadius * sin(angle);
                    y = m_pulsarBeamOuterRadius * cos(angle);
454

455 456 457 458 459
                    // Specify the next vertex for the triangle fan
                    glVertex2f(x, y);
                }
            }
            glEnd();
460

461 462 463
            // draw second cone's "base" (textured bottom to visualize beam intensity)
            glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
            glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
464

465 466 467 468 469 470 471 472 473
            // create texture coordinates and enable texturing
            gluQuadricTexture(m_quadricPulsarCone2Shell, GL_TRUE);
            glBindTexture(GL_TEXTURE_2D, m_beamTexture);
            glEnable(GL_TEXTURE_2D);

            gluDisk(m_quadricPulsarCone2Shell, 0, m_pulsarBeamOuterRadius, 32, 1);

            // disable texturing
            glDisable(GL_TEXTURE_2D);
474 475 476
        }
        glPopMatrix();
    }
477 478
    glPopMatrix();

479 480 481
    // back to solid rendering
    glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);

482 483 484
    // draw orbital planes
    if(m_cameraInteraction || m_showOrbits) {
        glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);
485
        glEnable(GL_LIGHTING);
486 487 488 489 490 491

        static double sizeOffset = 0.25;

        // companion's plane (only if not identical with pulsar's)
        if(m_companionSemiMajorAxis != m_pulsarSemiMajorAxis) {
            glPushMatrix();
492 493 494 495 496 497 498 499 500 501 502 503 504 505
            {
                glRotatef(90.0, 1.0, 0.0, 0.0);

                // separate them slightly in case of overlap
                // based on ugly mass diff check (no collision detection)
                // single known problematic pair: (m_c=1.4, m_p=1.5)
                if((int)(10*(m_pulsarMass-m_companionMass)+0.5) == 1){
                    glTranslatef(0.0, 0.0, 0.01);
                }

                gluDisk(m_quadricCompanionOrbitPlane,
                        m_companionSemiMajorAxis - sizeOffset,
                        m_companionSemiMajorAxis + sizeOffset,
                        64, 1);
506 507 508 509 510 511
            }
            glPopMatrix();
        }

        // pulsar's plane
        glPushMatrix();
512 513 514 515 516 517 518
        {
            glRotatef(90.0, 1.0, 0.0, 0.0);
            gluDisk(m_quadricPulsarOrbitPlane,
                    m_pulsarSemiMajorAxis - sizeOffset,
                    m_pulsarSemiMajorAxis + sizeOffset,
                    64, 1);
        }
519
        glPopMatrix();
520 521

        glDisable(GL_LIGHTING);
522
    }
Oliver Bock's avatar
Oliver Bock committed
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551

    // save current state (the following is using parallel projection)
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    {
        glLoadIdentity();
        glOrtho(0, width(), 0, height(), 0.1, 501.0);
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        {
            glLoadIdentity();

            // draw copyright info last (appears in front of everything)
            glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
            QFont font;
            font.setPointSize(11);
            font.setBold(true);
            font.setFamily("Arial");
            font.setStyleStrategy((QFont::StyleStrategy) (QFont::OpenGLCompatible | QFont::PreferQuality));
            renderText(10, 25, -100, QString::fromLocal8Bit("Copyright © 2009-2011"), font);
            renderText(10, 10, -100, QString::fromLocal8Bit("Max-Planck-Insitut für Gravitationsphysik"), font);

            // restore original state
            glMatrixMode(GL_PROJECTION);
        }
        glPopMatrix();
        glMatrixMode(GL_MODELVIEW);
    }
    glPopMatrix();
Oliver Bock's avatar
Oliver Bock committed
552 553
}

Oliver Bock's avatar
Oliver Bock committed
554 555
void PulsarAnimationWidget::runAnimation()
{
556
    m_frameTimer.start(qRound(1000.0 / m_framesPerSecond));
Oliver Bock's avatar
Oliver Bock committed
557
}
Oliver Bock's avatar
Oliver Bock committed
558

Oliver Bock's avatar
Oliver Bock committed
559 560
void PulsarAnimationWidget::pauseAnimation()
{
561
    m_frameTimer.stop();
Oliver Bock's avatar
Oliver Bock committed
562 563 564 565
}

void PulsarAnimationWidget::stopAnimation()
{
566 567
    m_frameTimer.stop();
    resetParameters();
Oliver Bock's avatar
Oliver Bock committed
568

569
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
570 571
}

Oliver Bock's avatar
Oliver Bock committed
572
void PulsarAnimationWidget::updateFrame()
Oliver Bock's avatar
Oliver Bock committed
573
{
574 575 576 577 578 579 580 581 582 583 584 585 586
    m_pulsarRotationAngle += m_pulsarRotationDelta;
    if(m_pulsarRotationAngle >   360.0) {
        m_pulsarRotationAngle -= 360.0;
        updatePulseProfile();
    }
    m_orbitRotationAngle += m_orbitRotationDelta;
    if(m_orbitRotationAngle >   360.0) {
        m_orbitRotationAngle -= 360.0;
    }

    updateGL();

    emit pulsarAnimationStep(m_pulsarRotationAngle);
587 588
}

589 590
void PulsarAnimationWidget::showOrbits(bool enabled)
{
591
    m_showOrbits = enabled;
592

593
    updateGL();
594 595
}

Oliver Bock's avatar
Oliver Bock committed
596 597
void PulsarAnimationWidget::showRotationAxes(bool enabled)
{
598
    m_showRotationAxes = enabled;
Oliver Bock's avatar
Oliver Bock committed
599

600
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
601 602
}

603 604
void PulsarAnimationWidget::mousePressEvent(QMouseEvent *event)
{
605
    Q_UNUSED(event);
606

607 608
    m_cameraInteraction = true;
    updateGL();
609 610
}

611 612
void PulsarAnimationWidget::mouseMoveEvent(QMouseEvent *event)
{
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
    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);
640 641 642 643
}

void PulsarAnimationWidget::mouseReleaseEvent(QMouseEvent *event)
{
644
    Q_UNUSED(event);
645

646 647 648
    m_mouseLastX = 0;
    m_mouseLastY = 0;
    m_cameraInteraction = false;
649

650
    updateGL();
651 652
}

Oliver Bock's avatar
Oliver Bock committed
653 654
void PulsarAnimationWidget::showEvent(QShowEvent *event)
{
655
    Q_UNUSED(event);
Oliver Bock's avatar
Oliver Bock committed
656

657 658
    // update and propagate pulse profile
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
659 660
}

Oliver Bock's avatar
Oliver Bock committed
661
void PulsarAnimationWidget::updateCameraPosition(const double angleH, const double angleV, const double zoom)
662
{
663 664 665
    m_cameraPosX = sin(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
    m_cameraPosY = sin(angleV * deg2rad) * zoom;
    m_cameraPosZ = cos(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
666

667
    updatePulseProfile();
668

669
    updateGL();
670 671
}

Oliver Bock's avatar
Oliver Bock committed
672 673
void PulsarAnimationWidget::setFramePerSecond(const unsigned int fps)
{
674
    m_framesPerSecond = fps;
Oliver Bock's avatar
Oliver Bock committed
675 676
}

Oliver Bock's avatar
Oliver Bock committed
677
void PulsarAnimationWidget::setPulsarSemiMajorAxis(const double length)
Oliver Bock's avatar
Oliver Bock committed
678
{
679 680 681 682
    m_pulsarSemiMajorAxis = length;
    m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;
    updateOrbitPeriod();
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
683

684
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
685
}
686

Oliver Bock's avatar
Oliver Bock committed
687
void PulsarAnimationWidget::setCompanionMass(const double mass)
688
{
689 690 691
    m_companionMass = mass;
    updateOrbitRadii();
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
692

693
    updateGL();
694 695
}

Oliver Bock's avatar
Oliver Bock committed
696
void PulsarAnimationWidget::setPulsarMass(const double mass)
697
{
698 699 700
    m_pulsarMass = mass;
    updateOrbitRadii();
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
701

702
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
703 704
}

Oliver Bock's avatar
Oliver Bock committed
705
void PulsarAnimationWidget::setPulsarSpinFrequency(const double frequency)
Oliver Bock's avatar
Oliver Bock committed
706
{
707 708
    m_pulsarRotationDelta = (360.0 * frequency) / m_framesPerSecond;
    updatePulseProfile();
709
}
710

711 712
void PulsarAnimationWidget::setPulsarSpinAxisInclination(const int degrees)
{
713 714
    m_pulsarSpinAxisInclination = degrees;
    updatePulseProfile();
715

716
    updateGL();
717 718
}

719 720
void PulsarAnimationWidget::setPulsarMagneticAxisInclination(const int degrees)
{
721 722
    m_pulsarMagneticAxisInclination = degrees;
    updatePulseProfile();
723

724
    updateGL();
725
}
726

727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
void PulsarAnimationWidget::setPulsarBeamAngle(const int degrees)
{
    double beamTexturePeakCorrectionFactor = 0.83;
    double correctedOuterRadius;

    // compute visual radius
    m_pulsarBeamOuterRadius = tan(deg2rad * degrees * 0.5f) * m_pulsarBeamLength;

    // compute corrected angle for pulse profile
    correctedOuterRadius = m_pulsarBeamOuterRadius * beamTexturePeakCorrectionFactor;
    m_pulsarBeamAngle = 2 * atan(correctedOuterRadius / m_pulsarBeamLength) * 180.0/PI;

    updatePulseProfile();

    updateGL();
}

Oliver Bock's avatar
Oliver Bock committed
744 745
void PulsarAnimationWidget::updateOrbitPeriod()
{
746 747 748
    m_orbitalPeriod = 3.1553e7 * sqrt(
                                    (pow(m_pulsarSemiMajorAxis, 3.0) * pow(m_pulsarMass+m_companionMass, 2.0)) / pow(m_companionMass, 3.0)
                                 );
Oliver Bock's avatar
Oliver Bock committed
749

750 751
    // visual correction factor (increase orbital momentum)
    double visualCorrection = 1e-8;
Oliver Bock's avatar
Oliver Bock committed
752

753
    m_orbitRotationDelta  = (360.0 / (m_orbitalPeriod*visualCorrection)) / m_framesPerSecond;
Oliver Bock's avatar
Oliver Bock committed
754 755 756 757
}

void PulsarAnimationWidget::updateOrbitRadii()
{
758 759 760 761
    m_pulsarSemiMajorAxis = 1.0015e-5 * pow(
                                            (pow(m_orbitalPeriod, 2.0) * pow(m_companionMass, 3.0)) / pow(m_pulsarMass+m_companionMass, 2.0),
                                            1.0/3.0
                                        );
Oliver Bock's avatar
Oliver Bock committed
762

763
    m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;
Oliver Bock's avatar
Oliver Bock committed
764

765
    emit pulsarSemiMajorAxisUpdated(m_pulsarSemiMajorAxis);
Oliver Bock's avatar
Oliver Bock committed
766 767
}

768 769
void PulsarAnimationWidget::resetParameters()
{
770 771 772
    m_pulsarRotationAngle = 0.0;
    m_orbitRotationAngle = 0.0;
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
773

774
    emit pulsarAnimationStep(m_pulsarRotationAngle);
775
}
776 777 778

void PulsarAnimationWidget::updatePulseProfile()
{
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
    // prepare parameters (e.g. convert to radians where necessary)
    const double    i               = deg2rad * m_pulsarSpinAxisInclination;
    const double    y               = deg2rad * m_pulsarMagneticAxisInclination;
    double          phiOrb          = deg2rad * (m_orbitRotationAngle + 90.0);
    const double    deltaPhiRot     = deg2rad * 1.0;
    const double    deltaPhiOrb     = deg2rad * deltaPhiRot * m_orbitRotationDelta / m_pulsarRotationDelta;
    const double    rp              = m_pulsarSemiMajorAxis;
    const double    xk              = -m_cameraPosZ;
    const double    yk              = -m_cameraPosX;
    const double    zk              = m_cameraPosY;
    const double    cam             = pow(xk, 2.0) + pow(yk, 2.0) + pow(zk, 2.0);
    const double    alpha           = deg2rad * (90.0 - m_mouseAngleH);
    const double    delta           = deg2rad * m_mouseAngleV;
    const double    gaussProfile    = 0.012337;

    for(int x = 0; x < 360; ++x) {
        // determine angle between pulsar's magnetic axis and line of sight
        phiOrb += deltaPhiOrb;
        const double phiRot = x * deltaPhiRot;

        double a = -sin(y) * sin(phiRot) * (xk + rp * cos(phiOrb)) \
                   + (cos(i) * sin(y) * cos(phiRot) + sin(i) * cos(y)) * (yk + rp * sin(phiOrb)) \
                   - (sin(i) * sin(y) * cos(phiRot) - cos(i) * cos(y)) * zk;

        double b = sqrt(pow(rp,2.0) + cam - (2.0 * sqrt(cam) * rp * cos(delta) * sin(alpha + phiOrb)));

        // determine and store pulse amplitude
        m_pulseProfile[x] = exp(-2.0 * (1.0 - fabs(a/b)) / gaussProfile);
    }

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