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
        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);
326
            glColor3f(0.5f, 0.5f, 1.0f);
327
328
329
330
331
332
            gluCylinder(m_quadricLHOv, 0.010, 0.010, 0.20, 32, 1);
        }
        glPopMatrix();
    }
    glPopMatrix();

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

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

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

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

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

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

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

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

400
            glDisable(GL_LIGHTING);
401
        }
402
403
        glPopMatrix();
    }
404
    glPopMatrix();
Oliver Bock's avatar
Oliver Bock committed
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423

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

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

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

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

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

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

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

465
466
    updatePulseProfile();

467
468
469
    updateGL();

    emit pulsarAnimationStep(m_pulsarRotationAngle);
470
471
}

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

476
    updateGL();
477
478
}

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

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

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

    updateGL();
}

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

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

501
502
void PulsarAnimationWidget::mouseMoveEvent(QMouseEvent *event)
{
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
529
    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);
530
531
532
533
}

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

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

540
    updateGL();
541
542
}

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

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

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

557
    updatePulseProfile();
558

559
    updateGL();
560
561
}

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

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

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

Oliver Bock's avatar
Oliver Bock committed
577
void PulsarAnimationWidget::setLHOAngle(const double degrees)
578
{
579
    m_LHOAngle = degrees + 180;
580
    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
}