pulsaranimationwidget.cpp 22.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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
    m_quadricPulsarCone1 = NULL;
    m_quadricPulsarCone2 = NULL;
    m_quadricPulsarSpinAxis = NULL;
    m_quadricPulsarMagneticAxis = NULL;

    // initialize texture pointers
    m_backgroundTexture = 0;

    // 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;
    // 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
104 105 106 107
}

PulsarAnimationWidget::~PulsarAnimationWidget()
{
108 109 110 111 112 113 114 115 116 117
    if(m_quadricCompanionOrbitPlane) gluDeleteQuadric(m_quadricCompanionOrbitPlane);
    if(m_quadricCompanion) gluDeleteQuadric(m_quadricCompanion);
    if(m_quadricPulsarOrbitPlane) gluDeleteQuadric(m_quadricPulsarOrbitPlane);
    if(m_quadricPulsar) gluDeleteQuadric(m_quadricPulsar);
    if(m_quadricPulsarCone1) gluDeleteQuadric(m_quadricPulsarCone1);
    if(m_quadricPulsarCone2) gluDeleteQuadric(m_quadricPulsarCone2);
    if(m_quadricPulsarSpinAxis) gluDeleteQuadric(m_quadricPulsarSpinAxis);
    if(m_quadricPulsarMagneticAxis) gluDeleteQuadric(m_quadricPulsarMagneticAxis);

    if(m_backgroundTexture) deleteTexture(m_backgroundTexture);
Oliver Bock's avatar
Oliver Bock committed
118 119 120 121
}

void PulsarAnimationWidget::initializeGL()
{
122 123 124 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
    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);
    glEnable(GL_LIGHTING);

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

    m_quadricCompanionOrbitPlane = gluNewQuadric();
    m_quadricCompanion = gluNewQuadric();
    m_quadricPulsarOrbitPlane = gluNewQuadric();
    m_quadricPulsar = gluNewQuadric();
Oliver Bock's avatar
Oliver Bock committed
155 156
    m_quadricPulsarCone1 = gluNewQuadric();
    m_quadricPulsarCone2 = gluNewQuadric();
157
    m_quadricPulsarSpinAxis = gluNewQuadric();
158
    m_quadricPulsarMagneticAxis = gluNewQuadric();
159

Oliver Bock's avatar
Oliver Bock committed
160 161
    gluQuadricNormals(m_quadricCompanionOrbitPlane, GLU_SMOOTH);
    gluQuadricNormals(m_quadricCompanion, GLU_SMOOTH);
162
    gluQuadricNormals(m_quadricPulsarOrbitPlane, GLU_SMOOTH);
Oliver Bock's avatar
Oliver Bock committed
163 164 165
    gluQuadricNormals(m_quadricPulsar, GLU_SMOOTH);
    gluQuadricNormals(m_quadricPulsarCone1, GLU_SMOOTH);
    gluQuadricNormals(m_quadricPulsarCone2, GLU_SMOOTH);
166
    gluQuadricNormals(m_quadricPulsarSpinAxis, GLU_SMOOTH);
167
    gluQuadricNormals(m_quadricPulsarMagneticAxis, GLU_SMOOTH);
168

Oliver Bock's avatar
Oliver Bock committed
169 170 171 172
    // query max texture size (estimate)
    GLint maxTextureSize;
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);

Oliver Bock's avatar
Oliver Bock committed
173 174 175 176 177
    // 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
178
    // prepare and check background texture
Oliver Bock's avatar
Oliver Bock committed
179
    QImage backgroundTexture(":/textures/resources/texture_background_carina.png");
Oliver Bock's avatar
Oliver Bock committed
180
    if(backgroundTexture.width() != backgroundTexture.height()) {
181
        qWarning() << msgShape.arg(tr("Background"));
Oliver Bock's avatar
Oliver Bock committed
182 183
    }
    else {
184 185 186 187 188 189
        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
190 191
    }
    if(backgroundTexture.width() > maxTextureSize) {
192 193
        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
194 195 196
    }

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

199
    // use mipmapped textures
200 201 202
    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
203 204 205
    // 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
206 207 208 209
}

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

212 213
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
Oliver Bock's avatar
Oliver Bock committed
214

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

217 218
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
Oliver Bock's avatar
Oliver Bock committed
219 220 221 222
}

void PulsarAnimationWidget::paintGL()
{
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
    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};
    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);

    // draw companion
    glPushMatrix();
247 248 249 250 251 252
    {
        glTranslatef(sin((m_orbitRotationAngle + 180.0) * deg2rad) * m_companionSemiMajorAxis,
                     0.0,
                     cos((m_orbitRotationAngle + 180.0) * deg2rad) * m_companionSemiMajorAxis);
        gluSphere(m_quadricCompanion, 1.0, 32, 32);
    }
253 254 255 256
    glPopMatrix();

    // draw pulsar
    glPushMatrix();
257 258 259 260
    {
        glTranslatef(sin(m_orbitRotationAngle * deg2rad) * m_pulsarSemiMajorAxis,
                     0.0,
                     cos(m_orbitRotationAngle * deg2rad) * m_pulsarSemiMajorAxis);
261 262

        glPushMatrix();
263 264 265
        {
            glRotatef(m_pulsarSpinAxisInclination, 0.0, 0.0, 1.0);
            glRotatef(m_pulsarRotationAngle, 0.0, 1.0, 0.0);
266
            glRotatef(90, 0.0, 0.0, 0.0);
267 268 269 270 271 272 273 274

            // 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);
275 276
                    glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
                    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
277 278 279
                }
                glPopMatrix();
            }
280

281 282 283
            // enable wireframe mode
            glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
            // draw pulsar
284
            gluSphere(m_quadricPulsar, 1.0, 32, 32);
285 286
            // disable wireframe mode
            glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
287 288
        }
        glPopMatrix();
289

290 291 292 293
        // 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};
294

295
        // first cone
296
        glPushMatrix();
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
        {
            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) {
                glPushMatrix();
                {
                    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);
                    glTranslatef(0.0, 0.0, -5.0);
                    gluCylinder(m_quadricPulsarMagneticAxis, 0.05, 0.05, 10.0, 32, 1);
                }
                glPopMatrix();
            }
314

315
            glTranslatef(0.0, 0.0, -3.0);
316

317 318 319 320 321
            glMaterialfv(GL_FRONT, GL_AMBIENT, coneAmbient);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, coneDiffuse);
            gluCylinder(m_quadricPulsarCone1, 0.475, 0.0, 3.0, 32, 32);
        }
        glPopMatrix();
322

323 324 325 326 327
        // second cone
        glPushMatrix();
        {
            glRotatef(-90.0, 1.0, 0.0, 0.0);
            glRotatef(-m_pulsarSpinAxisInclination, 0.0, 1.0, 0.0);
328

329 330
            glRotatef(m_pulsarRotationAngle - 90.0, 0.0, 0.0, 1.0);
            glRotatef(m_pulsarMagneticAxisInclination, 1.0, 0.0, 0.0);
331

332
            glTranslatef(0.0, 0.0, -3.0);
333

334 335 336 337 338 339
            glMaterialfv(GL_FRONT, GL_AMBIENT, coneAmbient);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, coneDiffuse);
            gluCylinder(m_quadricPulsarCone2, 0.475, 0.0, 3.0, 32, 32);
        }
        glPopMatrix();
    }
340 341
    glPopMatrix();

Oliver Bock's avatar
Oliver Bock committed
342
    // save current state (the following is using parallel projection)
343 344
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
345 346 347 348 349 350 351
    {
        glLoadIdentity();
        glOrtho(0, width(), 0, height(), 0.1, 501.0);
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        {
            glLoadIdentity();
352

353 354 355 356
            // draw backdrop (independent parallel projection)
            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_specular);
            glTranslatef(0.0, 0.0, -501.0);
            drawTexture(QPointF(0.0, 0.0), m_backgroundTexture);
357

358 359 360 361 362 363
            // restore original state
            glMatrixMode(GL_PROJECTION);
        }
        glPopMatrix();
        glMatrixMode(GL_MODELVIEW);
    }
364 365 366 367 368 369 370 371 372 373 374
    glPopMatrix();

    // draw orbital planes
    if(m_cameraInteraction || m_showOrbits) {
        glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);

        static double sizeOffset = 0.25;

        // companion's plane (only if not identical with pulsar's)
        if(m_companionSemiMajorAxis != m_pulsarSemiMajorAxis) {
            glPushMatrix();
375 376 377 378 379 380 381 382 383 384 385 386 387 388
            {
                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);
389 390 391 392 393 394
            }
            glPopMatrix();
        }

        // pulsar's plane
        glPushMatrix();
395 396 397 398 399 400 401
        {
            glRotatef(90.0, 1.0, 0.0, 0.0);
            gluDisk(m_quadricPulsarOrbitPlane,
                    m_pulsarSemiMajorAxis - sizeOffset,
                    m_pulsarSemiMajorAxis + sizeOffset,
                    64, 1);
        }
402 403
        glPopMatrix();
    }
Oliver Bock's avatar
Oliver Bock committed
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

    // 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
433 434
}

Oliver Bock's avatar
Oliver Bock committed
435 436
void PulsarAnimationWidget::runAnimation()
{
437
    m_frameTimer.start(qRound(1000.0 / m_framesPerSecond));
Oliver Bock's avatar
Oliver Bock committed
438
}
Oliver Bock's avatar
Oliver Bock committed
439

Oliver Bock's avatar
Oliver Bock committed
440 441
void PulsarAnimationWidget::pauseAnimation()
{
442
    m_frameTimer.stop();
Oliver Bock's avatar
Oliver Bock committed
443 444 445 446
}

void PulsarAnimationWidget::stopAnimation()
{
447 448
    m_frameTimer.stop();
    resetParameters();
Oliver Bock's avatar
Oliver Bock committed
449

450
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
451 452
}

Oliver Bock's avatar
Oliver Bock committed
453
void PulsarAnimationWidget::updateFrame()
Oliver Bock's avatar
Oliver Bock committed
454
{
455 456 457 458 459 460 461 462 463 464 465 466 467
    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);
468 469
}

470 471
void PulsarAnimationWidget::showOrbits(bool enabled)
{
472
    m_showOrbits = enabled;
473

474
    updateGL();
475 476
}

Oliver Bock's avatar
Oliver Bock committed
477 478
void PulsarAnimationWidget::showRotationAxes(bool enabled)
{
479
    m_showRotationAxes = enabled;
Oliver Bock's avatar
Oliver Bock committed
480

481
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
482 483
}

484 485
void PulsarAnimationWidget::mousePressEvent(QMouseEvent *event)
{
486
    Q_UNUSED(event);
487

488 489
    m_cameraInteraction = true;
    updateGL();
490 491
}

492 493
void PulsarAnimationWidget::mouseMoveEvent(QMouseEvent *event)
{
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
    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);
521 522 523 524
}

void PulsarAnimationWidget::mouseReleaseEvent(QMouseEvent *event)
{
525
    Q_UNUSED(event);
526

527 528 529
    m_mouseLastX = 0;
    m_mouseLastY = 0;
    m_cameraInteraction = false;
530

531
    updateGL();
532 533
}

Oliver Bock's avatar
Oliver Bock committed
534 535
void PulsarAnimationWidget::showEvent(QShowEvent *event)
{
536
    Q_UNUSED(event);
Oliver Bock's avatar
Oliver Bock committed
537

538 539
    // update and propagate pulse profile
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
540 541
}

Oliver Bock's avatar
Oliver Bock committed
542
void PulsarAnimationWidget::updateCameraPosition(const double angleH, const double angleV, const double zoom)
543
{
544 545 546
    m_cameraPosX = sin(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
    m_cameraPosY = sin(angleV * deg2rad) * zoom;
    m_cameraPosZ = cos(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
547

548
    updatePulseProfile();
549

550
    updateGL();
551 552
}

Oliver Bock's avatar
Oliver Bock committed
553 554
void PulsarAnimationWidget::setFramePerSecond(const unsigned int fps)
{
555
    m_framesPerSecond = fps;
Oliver Bock's avatar
Oliver Bock committed
556 557
}

Oliver Bock's avatar
Oliver Bock committed
558
void PulsarAnimationWidget::setPulsarSemiMajorAxis(const double length)
Oliver Bock's avatar
Oliver Bock committed
559
{
560 561 562 563
    m_pulsarSemiMajorAxis = length;
    m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;
    updateOrbitPeriod();
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
564

565
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
566
}
567

Oliver Bock's avatar
Oliver Bock committed
568
void PulsarAnimationWidget::setCompanionMass(const double mass)
569
{
570 571 572
    m_companionMass = mass;
    updateOrbitRadii();
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
573

574
    updateGL();
575 576
}

Oliver Bock's avatar
Oliver Bock committed
577
void PulsarAnimationWidget::setPulsarMass(const double mass)
578
{
579 580 581
    m_pulsarMass = mass;
    updateOrbitRadii();
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
582

583
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
584 585
}

Oliver Bock's avatar
Oliver Bock committed
586
void PulsarAnimationWidget::setPulsarSpinFrequency(const double frequency)
Oliver Bock's avatar
Oliver Bock committed
587
{
588 589
    m_pulsarRotationDelta = (360.0 * frequency) / m_framesPerSecond;
    updatePulseProfile();
590
}
591

592 593
void PulsarAnimationWidget::setPulsarSpinAxisInclination(const int degrees)
{
594 595
    m_pulsarSpinAxisInclination = degrees;
    updatePulseProfile();
596

597
    updateGL();
598 599
}

600 601
void PulsarAnimationWidget::setPulsarMagneticAxisInclination(const int degrees)
{
602 603
    m_pulsarMagneticAxisInclination = degrees;
    updatePulseProfile();
604

605
    updateGL();
606
}
607

Oliver Bock's avatar
Oliver Bock committed
608 609
void PulsarAnimationWidget::updateOrbitPeriod()
{
610 611 612
    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
613

614 615
    // visual correction factor (increase orbital momentum)
    double visualCorrection = 1e-8;
Oliver Bock's avatar
Oliver Bock committed
616

617
    m_orbitRotationDelta  = (360.0 / (m_orbitalPeriod*visualCorrection)) / m_framesPerSecond;
Oliver Bock's avatar
Oliver Bock committed
618 619 620 621
}

void PulsarAnimationWidget::updateOrbitRadii()
{
622 623 624 625
    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
626

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

629
    emit pulsarSemiMajorAxisUpdated(m_pulsarSemiMajorAxis);
Oliver Bock's avatar
Oliver Bock committed
630 631
}

632 633
void PulsarAnimationWidget::resetParameters()
{
634 635 636
    m_pulsarRotationAngle = 0.0;
    m_orbitRotationAngle = 0.0;
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
637

638
    emit pulsarAnimationStep(m_pulsarRotationAngle);
639
}
640 641 642

void PulsarAnimationWidget::updatePulseProfile()
{
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
    // 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);
675
}