pulsaranimationwidget.cpp 22.9 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
    m_quadricPulsarSpinAxisTop1 = NULL;
64
65
    m_quadricLHOv = NULL;
    m_quadricLHOh = NULL;
66
67
68

    // initialize texture pointers
    m_backgroundTexture = 0;
69
    m_beamTexture = 0;
70
71
72
73
74
75
76
77

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

78
79
80
81
82
    // initial parameters (have to match GUI!)
    m_LHOAngle = 180;
    m_LLOAngle = 0;
    m_VirgoAngle = 0;

83
    m_pulsarRadius = 3.0;
84
    m_pulsarSpinAxisInclination = 0.0;
85

86
87
    // initial spin frequency of 0.5 Hz
    m_pulsarRotationDelta = (360.0 * 0.5) / m_framesPerSecond;
88
89
90
    // beam properties (keep this order!)
    m_pulsarBeamLength = 3.0f;
    setPulsarBeamAngle(30);
Oliver Bock's avatar
Oliver Bock committed
91
92
93
    // initial companion is "Neutron Star"
    m_companionMass = 1.4;
    m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;
94

95
96
97
98
99
100
    // update orbital period (based on settings above)
    updateOrbitPeriod();

    // initial view features
    m_showOrbits = false;
    m_showRotationAxes = false;
101
    m_showPulseFlashes = true;
102
103
104
    m_cameraInteraction = false;

    // initial view settings
105
106
    m_mouseAngleH = 0.0;
    m_mouseAngleV = 0.0;
107
    m_cameraZoom = 100.0;
108
109
110
111
112
113
114
    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
115
116
117
118
}

PulsarAnimationWidget::~PulsarAnimationWidget()
{
119
120
121
122
    if(m_quadricCompanionOrbitPlane) gluDeleteQuadric(m_quadricCompanionOrbitPlane);
    if(m_quadricCompanion) gluDeleteQuadric(m_quadricCompanion);
    if(m_quadricPulsarOrbitPlane) gluDeleteQuadric(m_quadricPulsarOrbitPlane);
    if(m_quadricPulsar) gluDeleteQuadric(m_quadricPulsar);
123
124
    if(m_quadricPulsarCone1Shell) gluDeleteQuadric(m_quadricPulsarCone1Shell);
    if(m_quadricPulsarCone2Shell) gluDeleteQuadric(m_quadricPulsarCone2Shell);
125
    if(m_quadricPulsarSpinAxis) gluDeleteQuadric(m_quadricPulsarSpinAxis);
126
    if(m_quadricPulsarSpinAxisTop1) gluDeleteQuadric(m_quadricPulsarSpinAxisTop1);
127
128
    if(m_quadricLHOv) gluDeleteQuadric(m_quadricLHOv);
    if(m_quadricLHOh) gluDeleteQuadric(m_quadricLHOh);
129
130

    if(m_backgroundTexture) deleteTexture(m_backgroundTexture);
131
    if(m_beamTexture) deleteTexture(m_beamTexture);
Oliver Bock's avatar
Oliver Bock committed
132
133
134
135
}

void PulsarAnimationWidget::initializeGL()
{
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
165
166
167
    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();
168
169
    m_quadricPulsarCone1Shell = gluNewQuadric();
    m_quadricPulsarCone2Shell = gluNewQuadric();
170
    m_quadricPulsarSpinAxis = gluNewQuadric();
171
    m_quadricPulsarSpinAxisTop1 = gluNewQuadric();
172
173
    m_quadricLHOv = gluNewQuadric();
    m_quadricLHOh = gluNewQuadric();
174

Oliver Bock's avatar
Oliver Bock committed
175
176
    gluQuadricNormals(m_quadricCompanionOrbitPlane, GLU_SMOOTH);
    gluQuadricNormals(m_quadricCompanion, GLU_SMOOTH);
177
    gluQuadricNormals(m_quadricPulsarOrbitPlane, GLU_SMOOTH);
Oliver Bock's avatar
Oliver Bock committed
178
    gluQuadricNormals(m_quadricPulsar, GLU_SMOOTH);
179
180
    gluQuadricNormals(m_quadricPulsarCone1Shell, GLU_SMOOTH);
    gluQuadricNormals(m_quadricPulsarCone2Shell, GLU_SMOOTH);
181
    gluQuadricNormals(m_quadricPulsarSpinAxis, GLU_SMOOTH);
182
    gluQuadricNormals(m_quadricPulsarSpinAxisTop1, GLU_SMOOTH);
183
184
    gluQuadricNormals(m_quadricLHOv, GLU_SMOOTH);
    gluQuadricNormals(m_quadricLHOh, GLU_SMOOTH);
185

Oliver Bock's avatar
Oliver Bock committed
186
187
188
189
    // query max texture size (estimate)
    GLint maxTextureSize;
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);

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

213
    // prepare and check beam texture
214
    QImage beamTexture(":/textures/resources/World-Map-7.jpg");
215

Oliver Bock's avatar
Oliver Bock committed
216
    // bind textures
Oliver Bock's avatar
Oliver Bock committed
217
    m_backgroundTexture = bindTexture(backgroundTexture, GL_TEXTURE_2D, GL_RGBA);
218
    m_beamTexture = bindTexture(beamTexture, GL_TEXTURE_2D, GL_RGBA);
219

220
    // use mipmapped textures
221
222
    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
223
224
225
226
}

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

229
230
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
Oliver Bock's avatar
Oliver Bock committed
231

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

234
235
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
Oliver Bock's avatar
Oliver Bock committed
236
237
238
239
}

void PulsarAnimationWidget::paintGL()
{
240
241
    GLfloat x,y,angle;

242
243
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

244
245
    // TODO: should be located elsewhere
    static GLfloat no_mat[] = {0.0, 0.0, 0.0, 1.0};
246
    static GLfloat mat_diffuse[] = {0.0, 0.0, 0.5, 1.0};
247
248
249
250
251
252
253
254
255
256
    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);

257
258
259
260
261
262
263
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

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

264
265
266
267
268
269
270
271
272
273
274
275
    // 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)
276
            glColor3f(1.0, 1.0, 1.0f);
277
278
279
280
281
282
283
284
285
286
            glTranslatef(0.0, 0.0, -501.0);

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

Oliver Bock's avatar
Oliver Bock committed
287
    // draw earth
288
    glPushMatrix();
289
290
291
    {
        glRotatef(130, 0.0, 1.0, 0.0);
        glPushMatrix();
292
293
        {
            glRotatef(90, 1.0, 0.0, 0.0);
294
295
296
297
298
299
            glRotatef(180, 1.0, 1.0, 0.0);

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

301
302
303
            gluSphere(m_quadricPulsar, m_pulsarRadius, 32, 32);
        }
        glPopMatrix();
304
305
    }
    glPopMatrix();
306

307
308
309
310
311
312
313
    // draw LHO
    glPushMatrix();
    {
        glColor3f(1.0f, 1.0f, 1.0f);

        glRotatef(-81, 1.0, 1.0, 0.0);
        glTranslatef(0.0, 0.0, m_pulsarRadius);
314
        glRotatef(m_LHOAngle, 0.0, 0.0, 1.0);
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
        glPushMatrix();
        {
            glRotatef(90, 0.0, 1.0, 0.0);
            gluCylinder(m_quadricLHOh, 0.010, 0.010, 0.20, 32, 1);
        }
        glPopMatrix();

        glPushMatrix();
        {
            glRotatef(90, 0.0, 1.0, 0.0);
            glRotatef(90, 1.0, 0.0, 0.0);
            gluCylinder(m_quadricLHOv, 0.010, 0.010, 0.20, 32, 1);
        }
        glPopMatrix();
    }
    glPopMatrix();

332
    // draw source
333
    glPushMatrix();
334
    {
335
336
        glTranslatef(3.0,
                     3.0,
337
338
                     -3.0);

339
        glRotatef(-45, 0.0, 1.0, 0.0);
340
        glRotatef(90, 1.0, 0.0, 0.0);
341
342

        glPushMatrix();
343
        {
344
345
            glRotatef(m_pulsarSpinAxisInclination, 1.0, 0.0, 0.0);
            glRotatef(-45, 1.0, 0.0, 0.0);
346

Oliver Bock's avatar
Oliver Bock committed
347
348
349
350
            // draw source plane normal
            glColor3f(1.0f, 0.0f, 0.0f);
            glPushMatrix();
            {
351
352
                glTranslatef(0.0, 0.0, -1.0);
                gluCylinder(m_quadricPulsarSpinAxis, 0.020, 0.020, 1.5, 32, 1);
Oliver Bock's avatar
Oliver Bock committed
353
354
355
356
            }
            glPopMatrix();
            glPushMatrix();
            {
357
                glTranslatef(0.0, 0.0, -1.0);
Oliver Bock's avatar
Oliver Bock committed
358
359
360
361
362
                gluDisk(m_quadricPulsarSpinAxisTop1, 0, 0.020, 32, 8);
            }
            glPopMatrix();
            glPushMatrix();
            {
363
                glTranslatef(0.0, 0.0, 0.5f);
Oliver Bock's avatar
Oliver Bock committed
364
                glBegin(GL_TRIANGLE_FAN);
Oliver Bock's avatar
Oliver Bock committed
365
                {
Oliver Bock's avatar
Oliver Bock committed
366
367
                    // Pinnacle of cone is shared vertex for fan, moved up z-axis
                    // to produce a cone instead of a circle
368
                    glVertex3f(0.0f, 0.0f, 0.5f);
Oliver Bock's avatar
Oliver Bock committed
369
370
371
372

                    // 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))
Oliver Bock's avatar
Oliver Bock committed
373
                    {
Oliver Bock's avatar
Oliver Bock committed
374
                        // Calculate x and y position of the next vertex
375
376
                        x = 0.1f * sin(angle);
                        y = 0.1f * cos(angle);
Oliver Bock's avatar
Oliver Bock committed
377
378
379

                        // Specify the next vertex for the triangle fan
                        glVertex2f(x, y);
Oliver Bock's avatar
Oliver Bock committed
380
381
                    }
                }
Oliver Bock's avatar
Oliver Bock committed
382
                glEnd();
383
            }
Oliver Bock's avatar
Oliver Bock committed
384
            glPopMatrix();
385

386
387
            // draw disc
            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);
388
389
            glEnable(GL_LIGHTING);

390
            glPushMatrix();
391
            {
392
393
                gluDisk(m_quadricPulsarOrbitPlane,
                        0,
394
                        1.0,
395
                        64, 1);
396
397
398
            }
            glPopMatrix();

399
            glDisable(GL_LIGHTING);
400
        }
401
402
        glPopMatrix();
    }
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

    // 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));
Oliver Bock's avatar
Oliver Bock committed
423
            renderText(10, 25, -100, QString("Copyright %1 2017").arg(QChar(0x00A9)), font);
424
            renderText(10, 10, -100, tr("Max Planck Institute for Gravitational Physics"), font);
Oliver Bock's avatar
Oliver Bock committed
425
426
427
428
429
430
431
432

            // 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
    m_pulsarRotationAngle += m_pulsarRotationDelta;
    if(m_pulsarRotationAngle >   360.0) {
        m_pulsarRotationAngle -= 360.0;
    }
    m_orbitRotationAngle += m_orbitRotationDelta;
    if(m_orbitRotationAngle >   360.0) {
        m_orbitRotationAngle -= 360.0;
    }

464
465
    updatePulseProfile();

466
467
468
    updateGL();

    emit pulsarAnimationStep(m_pulsarRotationAngle);
469
470
}

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

475
    updateGL();
476
477
}

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

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

485
486
487
488
489
490
491
void PulsarAnimationWidget::showPulseFlashes(bool enabled)
{
    m_showPulseFlashes = enabled;

    updateGL();
}

492
493
void PulsarAnimationWidget::mousePressEvent(QMouseEvent *event)
{
494
    Q_UNUSED(event);
495

496
497
    m_cameraInteraction = true;
    updateGL();
498
499
}

500
501
void PulsarAnimationWidget::mouseMoveEvent(QMouseEvent *event)
{
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
    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);
529
530
531
532
}

void PulsarAnimationWidget::mouseReleaseEvent(QMouseEvent *event)
{
533
    Q_UNUSED(event);
534

535
536
537
    m_mouseLastX = 0;
    m_mouseLastY = 0;
    m_cameraInteraction = false;
538

539
    updateGL();
540
541
}

Oliver Bock's avatar
Oliver Bock committed
542
543
void PulsarAnimationWidget::showEvent(QShowEvent *event)
{
544
    Q_UNUSED(event);
Oliver Bock's avatar
Oliver Bock committed
545

546
547
    // update and propagate pulse profile
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
548
549
}

Oliver Bock's avatar
Oliver Bock committed
550
void PulsarAnimationWidget::updateCameraPosition(const double angleH, const double angleV, const double zoom)
551
{
552
553
554
    m_cameraPosX = sin(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
    m_cameraPosY = sin(angleV * deg2rad) * zoom;
    m_cameraPosZ = cos(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
555

556
    updatePulseProfile();
557

558
    updateGL();
559
560
}

Oliver Bock's avatar
Oliver Bock committed
561
562
void PulsarAnimationWidget::setFramePerSecond(const unsigned int fps)
{
563
    m_framesPerSecond = fps;
Oliver Bock's avatar
Oliver Bock committed
564
565
}

Oliver Bock's avatar
Oliver Bock committed
566
void PulsarAnimationWidget::setPulsarSemiMajorAxis(const double length)
Oliver Bock's avatar
Oliver Bock committed
567
{
568
569
570
571
    m_pulsarSemiMajorAxis = length;
    m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;
    updateOrbitPeriod();
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
572

573
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
574
}
575

576
void PulsarAnimationWidget::setLLHOAngle(const double degrees)
577
{
578
    m_LHOAngle = degrees + 180;
579
580
    updateOrbitRadii();
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
581

582
    updateGL();
583
584
}

585
void PulsarAnimationWidget::setLLOAngle(const double degrees)
586
{
587
    m_LHOAngle = degrees;
588
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
589
590
}

591
void PulsarAnimationWidget::setVirgoAngle(const int degrees)
Oliver Bock's avatar
Oliver Bock committed
592
{
593
    m_VirgoAngle = degrees;
594
    updatePulseProfile();
595

596
    updateGL();
597
598
}

599
void PulsarAnimationWidget::setPulsarSpinAxisInclination(const int degrees)
600
{
601
    m_pulsarSpinAxisInclination = degrees;
602
    updatePulseProfile();
603

604
    updateGL();
605
}
606

607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
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();
}

624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
void PulsarAnimationWidget::getCameraPosition(double& cameraAngleH, double& cameraAngleV, double& cameraZoom)
{
    cameraAngleH = m_mouseAngleH;
    cameraAngleV = m_mouseAngleV;
    cameraZoom = m_cameraZoom;
}

void PulsarAnimationWidget::resetCameraPosition(const double angleH, const double angleV, const double zoom)
{
    m_mouseAngleH = angleH;
    m_mouseAngleV = angleV;
    m_cameraZoom = zoom;

    updateCameraPosition(m_mouseAngleH, m_mouseAngleV, m_cameraZoom);
}

Oliver Bock's avatar
Oliver Bock committed
640
641
void PulsarAnimationWidget::updateOrbitPeriod()
{
642
643
644
    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
645

646
647
    // visual correction factor (increase orbital momentum)
    double visualCorrection = 1e-8;
Oliver Bock's avatar
Oliver Bock committed
648

649
    m_orbitRotationDelta  = (360.0 / (m_orbitalPeriod*visualCorrection)) / m_framesPerSecond;
Oliver Bock's avatar
Oliver Bock committed
650
651
652
653
}

void PulsarAnimationWidget::updateOrbitRadii()
{
654
655
656
657
    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
658

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

661
    emit pulsarSemiMajorAxisUpdated(m_pulsarSemiMajorAxis);
Oliver Bock's avatar
Oliver Bock committed
662
663
}

664
665
void PulsarAnimationWidget::resetParameters()
{
666
667
668
    m_pulsarRotationAngle = 0.0;
    m_orbitRotationAngle = 0.0;
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
669

670
    emit pulsarAnimationStep(m_pulsarRotationAngle);
671
}
672
673
674

void PulsarAnimationWidget::updatePulseProfile()
{
675
676
677
    // avoid division by zero (keep profile visible if pulsar doesn't spin)
    double pulsarRotationDelta = m_pulsarRotationDelta == 0.0 ? 0.01 : m_pulsarRotationDelta;

678
679
680
681
682
    // 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;
683
    const double    deltaPhiOrb     = deg2rad * deltaPhiRot * m_orbitRotationDelta / pulsarRotationDelta;
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
    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);
710
}