pulsaranimationwidget.cpp 24.2 KB
Newer Older
Oliver Bock's avatar
Oliver Bock committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/******************************************************************************
 *   Copyright (C) 2008 by Oliver Bock                                        *
 *   oliver.bock[AT]aei.mpg.de                                                *
 *                                                                            *
 *   This file is part of PulsatingScience.                                   *
 *                                                                            *
 *   PulsatingScience is free software: you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published        *
 *   by the Free Software Foundation, version 3 of the License.               *
 *                                                                            *
 *   PulsatingScience is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the             *
 *   GNU General Public License for more details.                             *
 *                                                                            *
 *   You should have received a copy of the GNU General Public License        *
 *   along with PulsatingScience. If not, see <http://www.gnu.org/licenses/>. *
 *                                                                            *
 ******************************************************************************/

#include "pulsaranimationwidget.h"

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_quadricVirgoh = NULL;
    m_quadricVirgov = NULL;
58
    m_quadricPulsarOrbitPlane = NULL;
Oliver Bock's avatar
Oliver Bock committed
59
    m_quadricPulsar = NULL;
Oliver Bock's avatar
Oliver Bock committed
60
61
    m_quadricLLOh = NULL;
    m_quadricLLOv = 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
    // initial parameters (have to match GUI!)
    m_LHOAngle = 180;
Oliver Bock's avatar
Oliver Bock committed
80
    m_LLOAngle = -100;
Oliver Bock's avatar
Oliver Bock committed
81
    m_VirgoAngle = 165;
82
    m_sourceInclination = 0;
83

84
    m_pulsarRadius = 3.0;
85
    m_pulsarSpinAxisInclination = 0.0;
86

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

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

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

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

PulsarAnimationWidget::~PulsarAnimationWidget()
{
Oliver Bock's avatar
Oliver Bock committed
120
121
    if(m_quadricVirgoh) gluDeleteQuadric(m_quadricVirgoh);
    if(m_quadricVirgov) gluDeleteQuadric(m_quadricVirgov);
122
123
    if(m_quadricPulsarOrbitPlane) gluDeleteQuadric(m_quadricPulsarOrbitPlane);
    if(m_quadricPulsar) gluDeleteQuadric(m_quadricPulsar);
Oliver Bock's avatar
Oliver Bock committed
124
125
    if(m_quadricLLOh) gluDeleteQuadric(m_quadricLLOh);
    if(m_quadricLLOv) gluDeleteQuadric(m_quadricLLOv);
126
    if(m_quadricPulsarSpinAxis) gluDeleteQuadric(m_quadricPulsarSpinAxis);
127
    if(m_quadricPulsarSpinAxisTop1) gluDeleteQuadric(m_quadricPulsarSpinAxisTop1);
128
129
    if(m_quadricLHOv) gluDeleteQuadric(m_quadricLHOv);
    if(m_quadricLHOh) gluDeleteQuadric(m_quadricLHOh);
130
131

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

void PulsarAnimationWidget::initializeGL()
{
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);

Oliver Bock's avatar
Oliver Bock committed
165
166
    m_quadricVirgoh = gluNewQuadric();
    m_quadricVirgov = gluNewQuadric();
167
168
    m_quadricPulsarOrbitPlane = gluNewQuadric();
    m_quadricPulsar = gluNewQuadric();
Oliver Bock's avatar
Oliver Bock committed
169
170
    m_quadricLLOh = gluNewQuadric();
    m_quadricLLOv = gluNewQuadric();
171
    m_quadricPulsarSpinAxis = gluNewQuadric();
172
    m_quadricPulsarSpinAxisTop1 = gluNewQuadric();
173
174
    m_quadricLHOv = gluNewQuadric();
    m_quadricLHOh = gluNewQuadric();
175

Oliver Bock's avatar
Oliver Bock committed
176
177
    gluQuadricNormals(m_quadricVirgoh, GLU_SMOOTH);
    gluQuadricNormals(m_quadricVirgov, GLU_SMOOTH);
178
    gluQuadricNormals(m_quadricPulsarOrbitPlane, GLU_SMOOTH);
Oliver Bock's avatar
Oliver Bock committed
179
    gluQuadricNormals(m_quadricPulsar, GLU_SMOOTH);
Oliver Bock's avatar
Oliver Bock committed
180
181
    gluQuadricNormals(m_quadricLLOh, GLU_SMOOTH);
    gluQuadricNormals(m_quadricLLOv, GLU_SMOOTH);
182
    gluQuadricNormals(m_quadricPulsarSpinAxis, GLU_SMOOTH);
183
    gluQuadricNormals(m_quadricPulsarSpinAxisTop1, GLU_SMOOTH);
184
185
    gluQuadricNormals(m_quadricLHOv, GLU_SMOOTH);
    gluQuadricNormals(m_quadricLHOh, GLU_SMOOTH);
186

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

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

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

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

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

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

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

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

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

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

243
244
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

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

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

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

265
266
267
268
269
270
271
272
273
274
275
276
    // 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)
277
            glColor3f(1.0, 1.0, 1.0f);
278
279
280
281
282
283
284
285
286
287
            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
288
    // draw earth
289
    glPushMatrix();
290
291
292
    {
        glRotatef(130, 0.0, 1.0, 0.0);
        glPushMatrix();
293
294
        {
            glRotatef(90, 1.0, 0.0, 0.0);
295
296
297
298
299
300
            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);
301

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

308
309
310
311
312
313
314
    // 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);
315
        glRotatef(m_LHOAngle, 0.0, 0.0, 1.0);
316
317
318
319
320
321
322
323
324
325
326
        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);
327
            glColor3f(0.5f, 0.5f, 1.0f);
328
329
330
331
332
333
            gluCylinder(m_quadricLHOv, 0.010, 0.010, 0.20, 32, 1);
        }
        glPopMatrix();
    }
    glPopMatrix();

Oliver Bock's avatar
Oliver Bock committed
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
    // draw LLO
    glPushMatrix();
    {
        glColor3f(1.0f, 1.0f, 1.0f);

        glRotatef(-2, 0.0, 1.0, 0.0);
        glRotatef(5, 1.0, 0.0, 0.0);
        glRotatef(-60, 1.0, 1.0, 0.0);
        glTranslatef(0.0, 0.0, m_pulsarRadius);
        glRotatef(m_LLOAngle, 0.0, 0.0, 1.0);
        glPushMatrix();
        {
            glRotatef(90, 0.0, 1.0, 0.0);
            gluCylinder(m_quadricLLOh, 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);
            glColor3f(1.0f, 0.5f, 0.5f);
            gluCylinder(m_quadricLLOv, 0.010, 0.010, 0.20, 32, 1);
        }
        glPopMatrix();
    }
    glPopMatrix();

Oliver Bock's avatar
Oliver Bock committed
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
    // draw Virgo
    glPushMatrix();
    {
        glColor3f(1.0f, 1.0f, 1.0f);

        glRotatef(50, 0.0, 1.0, 0.0);
        glRotatef(-43, 1.0, 0.0, 0.0);
        glTranslatef(0.0, 0.0, m_pulsarRadius);
        glRotatef(m_VirgoAngle, 0.0, 0.0, 1.0);
        glPushMatrix();
        {
            glRotatef(90, 0.0, 1.0, 0.0);
            gluCylinder(m_quadricVirgoh, 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);
            glColor3f(0.5f, 1.0f, 0.5f);
            gluCylinder(m_quadricVirgov, 0.010, 0.010, 0.20, 32, 1);
        }
        glPopMatrix();
    }
    glPopMatrix();

389
    // draw source
390
    glPushMatrix();
391
    {
392
393
        glTranslatef(3.0,
                     3.0,
394
395
                     -3.0);

396
        glRotatef(m_sourceInclination, 0.0, 1.0, 0.0);
397
        glRotatef(-45, 0.0, 1.0, 0.0);
398
        glRotatef(90, 1.0, 0.0, 0.0);
399
400

        glPushMatrix();
401
        {
402
403
            glRotatef(m_pulsarSpinAxisInclination, 1.0, 0.0, 0.0);
            glRotatef(-45, 1.0, 0.0, 0.0);
404

Oliver Bock's avatar
Oliver Bock committed
405
406
407
408
            // draw source plane normal
            glColor3f(1.0f, 0.0f, 0.0f);
            glPushMatrix();
            {
409
410
                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
411
412
413
414
            }
            glPopMatrix();
            glPushMatrix();
            {
415
                glTranslatef(0.0, 0.0, -1.0);
Oliver Bock's avatar
Oliver Bock committed
416
417
418
419
420
                gluDisk(m_quadricPulsarSpinAxisTop1, 0, 0.020, 32, 8);
            }
            glPopMatrix();
            glPushMatrix();
            {
421
                glTranslatef(0.0, 0.0, 0.5f);
Oliver Bock's avatar
Oliver Bock committed
422
                glBegin(GL_TRIANGLE_FAN);
Oliver Bock's avatar
Oliver Bock committed
423
                {
Oliver Bock's avatar
Oliver Bock committed
424
425
                    // Pinnacle of cone is shared vertex for fan, moved up z-axis
                    // to produce a cone instead of a circle
426
                    glVertex3f(0.0f, 0.0f, 0.5f);
Oliver Bock's avatar
Oliver Bock committed
427
428
429
430

                    // 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
431
                    {
Oliver Bock's avatar
Oliver Bock committed
432
                        // Calculate x and y position of the next vertex
433
434
                        x = 0.1f * sin(angle);
                        y = 0.1f * cos(angle);
Oliver Bock's avatar
Oliver Bock committed
435
436
437

                        // Specify the next vertex for the triangle fan
                        glVertex2f(x, y);
Oliver Bock's avatar
Oliver Bock committed
438
439
                    }
                }
Oliver Bock's avatar
Oliver Bock committed
440
                glEnd();
441
            }
Oliver Bock's avatar
Oliver Bock committed
442
            glPopMatrix();
443

444
445
            // draw disc
            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);
446
447
            glEnable(GL_LIGHTING);

448
            glPushMatrix();
449
            {
450
451
                gluDisk(m_quadricPulsarOrbitPlane,
                        0,
452
                        1.0,
453
                        64, 1);
454
455
456
            }
            glPopMatrix();

457
            glDisable(GL_LIGHTING);
458
        }
459
460
        glPopMatrix();
    }
461
    glPopMatrix();
Oliver Bock's avatar
Oliver Bock committed
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480

    // 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
481
            renderText(10, 25, -100, QString("Copyright %1 2017").arg(QChar(0x00A9)), font);
482
            renderText(10, 10, -100, tr("Max Planck Institute for Gravitational Physics"), font);
Oliver Bock's avatar
Oliver Bock committed
483
484
485
486
487
488
489
490

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

Oliver Bock's avatar
Oliver Bock committed
493
494
void PulsarAnimationWidget::runAnimation()
{
495
    m_frameTimer.start(qRound(1000.0 / m_framesPerSecond));
Oliver Bock's avatar
Oliver Bock committed
496
}
Oliver Bock's avatar
Oliver Bock committed
497

Oliver Bock's avatar
Oliver Bock committed
498
499
void PulsarAnimationWidget::pauseAnimation()
{
500
    m_frameTimer.stop();
Oliver Bock's avatar
Oliver Bock committed
501
502
503
504
}

void PulsarAnimationWidget::stopAnimation()
{
505
506
    m_frameTimer.stop();
    resetParameters();
Oliver Bock's avatar
Oliver Bock committed
507

508
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
509
510
}

Oliver Bock's avatar
Oliver Bock committed
511
void PulsarAnimationWidget::updateFrame()
Oliver Bock's avatar
Oliver Bock committed
512
{
513
514
515
516
517
518
519
520
521
    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;
    }

522
523
    updatePulseProfile();

524
525
526
    updateGL();

    emit pulsarAnimationStep(m_pulsarRotationAngle);
527
528
}

529
530
void PulsarAnimationWidget::showOrbits(bool enabled)
{
531
    m_showOrbits = enabled;
532

533
    updateGL();
534
535
}

Oliver Bock's avatar
Oliver Bock committed
536
537
void PulsarAnimationWidget::showRotationAxes(bool enabled)
{
538
    m_showRotationAxes = enabled;
Oliver Bock's avatar
Oliver Bock committed
539

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

543
544
545
546
547
548
549
void PulsarAnimationWidget::showPulseFlashes(bool enabled)
{
    m_showPulseFlashes = enabled;

    updateGL();
}

550
551
void PulsarAnimationWidget::mousePressEvent(QMouseEvent *event)
{
552
    Q_UNUSED(event);
553

554
555
    m_cameraInteraction = true;
    updateGL();
556
557
}

558
559
void PulsarAnimationWidget::mouseMoveEvent(QMouseEvent *event)
{
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
    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);
587
588
589
590
}

void PulsarAnimationWidget::mouseReleaseEvent(QMouseEvent *event)
{
591
    Q_UNUSED(event);
592

593
594
595
    m_mouseLastX = 0;
    m_mouseLastY = 0;
    m_cameraInteraction = false;
596

597
    updateGL();
598
599
}

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

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

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

614
    updatePulseProfile();
615

616
    updateGL();
617
618
}

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

624
void PulsarAnimationWidget::setSourceInclination(const double degrees)
Oliver Bock's avatar
Oliver Bock committed
625
{
626
    m_sourceInclination = degrees;
627
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
628

629
    updateGL();
Oliver Bock's avatar
Oliver Bock committed
630
}
631

Oliver Bock's avatar
Oliver Bock committed
632
void PulsarAnimationWidget::setLHOAngle(const double degrees)
633
{
634
    m_LHOAngle = degrees + 180;
635
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
636

637
    updateGL();
638
639
}

640
void PulsarAnimationWidget::setLLOAngle(const double degrees)
641
{
Oliver Bock's avatar
Oliver Bock committed
642
    m_LLOAngle = degrees - 100;
643
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
644
645

    updateGL();
Oliver Bock's avatar
Oliver Bock committed
646
647
}

648
void PulsarAnimationWidget::setVirgoAngle(const int degrees)
Oliver Bock's avatar
Oliver Bock committed
649
{
Oliver Bock's avatar
Oliver Bock committed
650
    m_VirgoAngle = degrees + 165;
651
    updatePulseProfile();
652

653
    updateGL();
654
655
}

656
void PulsarAnimationWidget::setPulsarSpinAxisInclination(const int degrees)
657
{
658
    m_pulsarSpinAxisInclination = degrees;
659
    updatePulseProfile();
660

661
    updateGL();
662
}
663

664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
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();
}

681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
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
697
698
void PulsarAnimationWidget::updateOrbitPeriod()
{
699
700
701
    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
702

703
704
    // visual correction factor (increase orbital momentum)
    double visualCorrection = 1e-8;
Oliver Bock's avatar
Oliver Bock committed
705

706
    m_orbitRotationDelta  = (360.0 / (m_orbitalPeriod*visualCorrection)) / m_framesPerSecond;
Oliver Bock's avatar
Oliver Bock committed
707
708
709
710
}

void PulsarAnimationWidget::updateOrbitRadii()
{
711
712
713
714
    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
715

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

718
    emit pulsarSemiMajorAxisUpdated(m_pulsarSemiMajorAxis);
Oliver Bock's avatar
Oliver Bock committed
719
720
}

721
722
void PulsarAnimationWidget::resetParameters()
{
723
724
725
    m_pulsarRotationAngle = 0.0;
    m_orbitRotationAngle = 0.0;
    updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
726

727
    emit pulsarAnimationStep(m_pulsarRotationAngle);
728
}
729
730
731

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

735
736
737
738
739
    // 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;
740
    const double    deltaPhiOrb     = deg2rad * deltaPhiRot * m_orbitRotationDelta / pulsarRotationDelta;
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
    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);
767
}