pulsaranimationwidget.cpp 28.3 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

    // 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;
Oliver Bock's avatar
Oliver Bock committed
80
    m_pulsarRadius = 1.0;
81 82 83 84 85
    m_pulsarSpinAxisInclination = 0.0;
    m_pulsarMagneticAxisInclination = 60.0;
    m_pulsarSemiMajorAxis = 5.0;
    // initial spin frequency of 0.5 Hz
    m_pulsarRotationDelta = (360.0 * 0.5) / m_framesPerSecond;
86 87 88
    // beam properties (keep this order!)
    m_pulsarBeamLength = 3.0f;
    setPulsarBeamAngle(30);
Oliver Bock's avatar
Oliver Bock committed
89 90 91
    // initial companion is "Neutron Star"
    m_companionMass = 1.4;
    m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;
92

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

            // draw spin axis
            if(m_showRotationAxes) {
332 333
                glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
                glColor3f(1.0f, 1.0f, 1.0f);
334 335
                glPushMatrix();
                {
336 337
                    glTranslatef(0.0, 0.0, -4.0);
                    gluCylinder(m_quadricPulsarSpinAxis, 0.020, 0.020, 8.0, 32, 1);
338 339
                }
                glPopMatrix();
340 341 342 343 344 345 346 347 348 349 350 351 352
                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);
353
            }
354

355 356 357 358
            // draw sphere, with proper lighting
            glEnable(GL_LIGHTING);
            glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
Oliver Bock's avatar
Oliver Bock committed
359
            gluSphere(m_quadricPulsar, m_pulsarRadius, 32, 32);
360
            glDisable(GL_LIGHTING);
361 362
        }
        glPopMatrix();
363

364
        // first cone
365
        glPushMatrix();
366 367 368 369 370 371 372 373 374
        {
            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) {
375 376
                glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
                glColor3f(1.0f, 1.0f, 0.0f);
377 378
                glPushMatrix();
                {
379 380
                    glTranslatef(0.0, 0.0, -4.0);
                    gluCylinder(m_quadricPulsarMagneticAxis, 0.020, 0.020, 8.0, 32, 1);
381 382
                }
                glPopMatrix();
383 384 385 386 387 388 389 390 391 392 393 394 395
                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);
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 423 424 425 426 427 428 429 430 431 432 433 434
            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);
435

436
            glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
437 438
        }
        glPopMatrix();
439

440
        // second cone (pointing towards from camera)
441 442 443 444
        glPushMatrix();
        {
            glRotatef(-90.0, 1.0, 0.0, 0.0);
            glRotatef(-m_pulsarSpinAxisInclination, 0.0, 1.0, 0.0);
445

446 447
            glRotatef(m_pulsarRotationAngle - 90.0, 0.0, 0.0, 1.0);
            glRotatef(m_pulsarMagneticAxisInclination, 1.0, 0.0, 0.0);
448

449
            glTranslatef(0.0, 0.0, -m_pulsarBeamLength);
450

451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
            // 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);
466

467 468 469 470 471
                    // Specify the next vertex for the triangle fan
                    glVertex2f(x, y);
                }
            }
            glEnd();
472

473 474 475
            // 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);
476

477 478 479 480 481 482 483 484 485
            // 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);
486 487 488
        }
        glPopMatrix();
    }
489 490
    glPopMatrix();

491 492 493
    // back to solid rendering
    glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);

494 495 496
    // draw orbital planes
    if(m_cameraInteraction || m_showOrbits) {
        glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);
497
        glEnable(GL_LIGHTING);
498 499 500 501 502 503

        static double sizeOffset = 0.25;

        // companion's plane (only if not identical with pulsar's)
        if(m_companionSemiMajorAxis != m_pulsarSemiMajorAxis) {
            glPushMatrix();
504 505 506 507 508 509 510 511 512 513 514 515 516 517
            {
                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);
518 519 520 521 522 523
            }
            glPopMatrix();
        }

        // pulsar's plane
        glPushMatrix();
524 525 526 527 528 529 530
        {
            glRotatef(90.0, 1.0, 0.0, 0.0);
            gluDisk(m_quadricPulsarOrbitPlane,
                    m_pulsarSemiMajorAxis - sizeOffset,
                    m_pulsarSemiMajorAxis + sizeOffset,
                    64, 1);
        }
531
        glPopMatrix();
532 533

        glDisable(GL_LIGHTING);
534
    }
Oliver Bock's avatar
Oliver Bock committed
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563

    // 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
564 565
}

Oliver Bock's avatar
Oliver Bock committed
566 567
void PulsarAnimationWidget::runAnimation()
{
568
    m_frameTimer.start(qRound(1000.0 / m_framesPerSecond));
Oliver Bock's avatar
Oliver Bock committed
569
}
Oliver Bock's avatar
Oliver Bock committed
570

Oliver Bock's avatar
Oliver Bock committed
571 572
void PulsarAnimationWidget::pauseAnimation()
{
573
    m_frameTimer.stop();
Oliver Bock's avatar
Oliver Bock committed
574 575 576 577
}

void PulsarAnimationWidget::stopAnimation()
{
578 579
    m_frameTimer.stop();
    resetParameters();
Oliver Bock's avatar
Oliver Bock committed
580

581
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
582 583
}

Oliver Bock's avatar
Oliver Bock committed
584
void PulsarAnimationWidget::updateFrame()
Oliver Bock's avatar
Oliver Bock committed
585
{
586 587 588 589 590 591 592 593 594 595 596 597 598
    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);
599 600
}

601 602
void PulsarAnimationWidget::showOrbits(bool enabled)
{
603
    m_showOrbits = enabled;
604

605
    updateGL();
606 607
}

Oliver Bock's avatar
Oliver Bock committed
608 609
void PulsarAnimationWidget::showRotationAxes(bool enabled)
{
610
    m_showRotationAxes = enabled;
Oliver Bock's avatar
Oliver Bock committed
611

612
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
613 614
}

615 616
void PulsarAnimationWidget::mousePressEvent(QMouseEvent *event)
{
617
    Q_UNUSED(event);
618

619 620
    m_cameraInteraction = true;
    updateGL();
621 622
}

623 624
void PulsarAnimationWidget::mouseMoveEvent(QMouseEvent *event)
{
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
    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);
652 653 654 655
}

void PulsarAnimationWidget::mouseReleaseEvent(QMouseEvent *event)
{
656
    Q_UNUSED(event);
657

658 659 660
    m_mouseLastX = 0;
    m_mouseLastY = 0;
    m_cameraInteraction = false;
661

662
    updateGL();
663 664
}

Oliver Bock's avatar
Oliver Bock committed
665 666
void PulsarAnimationWidget::showEvent(QShowEvent *event)
{
667
    Q_UNUSED(event);
Oliver Bock's avatar
Oliver Bock committed
668

669 670
    // update and propagate pulse profile
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
671 672
}

Oliver Bock's avatar
Oliver Bock committed
673
void PulsarAnimationWidget::updateCameraPosition(const double angleH, const double angleV, const double zoom)
674
{
675 676 677
    m_cameraPosX = sin(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
    m_cameraPosY = sin(angleV * deg2rad) * zoom;
    m_cameraPosZ = cos(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
678

679
    updatePulseProfile();
680

681
    updateGL();
682 683
}

Oliver Bock's avatar
Oliver Bock committed
684 685
void PulsarAnimationWidget::setFramePerSecond(const unsigned int fps)
{
686
    m_framesPerSecond = fps;
Oliver Bock's avatar
Oliver Bock committed
687 688
}

Oliver Bock's avatar
Oliver Bock committed
689
void PulsarAnimationWidget::setPulsarSemiMajorAxis(const double length)
Oliver Bock's avatar
Oliver Bock committed
690
{
691 692 693 694
    m_pulsarSemiMajorAxis = length;
    m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;
    updateOrbitPeriod();
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
695

696
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
697
}
698

Oliver Bock's avatar
Oliver Bock committed
699
void PulsarAnimationWidget::setCompanionMass(const double mass)
700
{
701 702 703
    m_companionMass = mass;
    updateOrbitRadii();
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
704

705
    updateGL();
706 707
}

Oliver Bock's avatar
Oliver Bock committed
708
void PulsarAnimationWidget::setPulsarMass(const double mass)
709
{
710 711 712
    m_pulsarMass = mass;
    updateOrbitRadii();
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
713

714
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
715 716
}

Oliver Bock's avatar
Oliver Bock committed
717
void PulsarAnimationWidget::setPulsarSpinFrequency(const double frequency)
Oliver Bock's avatar
Oliver Bock committed
718
{
719 720
    m_pulsarRotationDelta = (360.0 * frequency) / m_framesPerSecond;
    updatePulseProfile();
721
}
722

723 724
void PulsarAnimationWidget::setPulsarSpinAxisInclination(const int degrees)
{
725 726
    m_pulsarSpinAxisInclination = degrees;
    updatePulseProfile();
727

728
    updateGL();
729 730
}

731 732
void PulsarAnimationWidget::setPulsarMagneticAxisInclination(const int degrees)
{
733 734
    m_pulsarMagneticAxisInclination = degrees;
    updatePulseProfile();
735

736
    updateGL();
737
}
738

739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
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
756 757
void PulsarAnimationWidget::updateOrbitPeriod()
{
758 759 760
    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
761

762 763
    // visual correction factor (increase orbital momentum)
    double visualCorrection = 1e-8;
Oliver Bock's avatar
Oliver Bock committed
764

765
    m_orbitRotationDelta  = (360.0 / (m_orbitalPeriod*visualCorrection)) / m_framesPerSecond;
Oliver Bock's avatar
Oliver Bock committed
766 767 768 769
}

void PulsarAnimationWidget::updateOrbitRadii()
{
770 771 772 773
    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
774

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

777
    emit pulsarSemiMajorAxisUpdated(m_pulsarSemiMajorAxis);
Oliver Bock's avatar
Oliver Bock committed
778 779
}

780 781
void PulsarAnimationWidget::resetParameters()
{
782 783 784
    m_pulsarRotationAngle = 0.0;
    m_orbitRotationAngle = 0.0;
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
785

786
    emit pulsarAnimationStep(m_pulsarRotationAngle);
787
}
788 789 790

void PulsarAnimationWidget::updatePulseProfile()
{
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
    // 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);
823
}