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

#include "pulsaranimationwidget.h"

Oliver Bock's avatar
Oliver Bock committed
23
const double PulsarAnimationWidget::deg2rad = PI/180.0;
24

Oliver Bock's avatar
Oliver Bock committed
25
PulsarAnimationWidget::PulsarAnimationWidget(QWidget *parent) :
Oliver Bock's avatar
Oliver Bock committed
26
	QGLWidget(QGLFormat(QGL::AlphaChannel | QGL::SampleBuffers), parent),
27
	m_frameTimer(),
Oliver Bock's avatar
Oliver Bock committed
28
	m_pulseProfile(360, 0.0)
Oliver Bock's avatar
Oliver Bock committed
29
{
Oliver Bock's avatar
Oliver Bock committed
30
	QString msgThis = tr("3D animation");
Oliver Bock's avatar
Oliver Bock committed
31
	if(!format().directRendering()) {
Oliver Bock's avatar
Oliver Bock committed
32 33
		QString msg = tr("Sorry, no direct rendering support for %1...");
		qWarning() << msg.arg(msgThis);
Oliver Bock's avatar
Oliver Bock committed
34 35
	}
	if(!format().doubleBuffer()) {
Oliver Bock's avatar
Oliver Bock committed
36 37
		QString msg = tr("Sorry, no double buffering support for %1...");
		qWarning() << msg.arg(msgThis);
Oliver Bock's avatar
Oliver Bock committed
38
	}
39
	if(!format().rgba()) {
Oliver Bock's avatar
Oliver Bock committed
40 41
		QString msg = tr("Sorry, no RGBA support for %1...");
		qWarning() << msg.arg(msgThis);
42 43
	}
	if(!format().alpha()) {
Oliver Bock's avatar
Oliver Bock committed
44 45
		QString msg = tr("Sorry, no alpha channel support for %1...");
		qWarning() << msg.arg(msgThis);
46
	}
Oliver Bock's avatar
Oliver Bock committed
47
	if(!format().sampleBuffers()) {
Oliver Bock's avatar
Oliver Bock committed
48 49
		QString msg = tr("Sorry, no multisampling support for %1...");
		qWarning() << msg.arg(msgThis);
Oliver Bock's avatar
Oliver Bock committed
50
	}
51

Oliver Bock's avatar
Oliver Bock committed
52
	// 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_quadricPulsarCone1 = NULL;
	m_quadricPulsarCone2 = NULL;
62
	m_quadricPulsarSpinAxis = NULL;
63
	m_quadricPulsarMagneticAxis = NULL;
Oliver Bock's avatar
Oliver Bock committed
64

Oliver Bock's avatar
Oliver Bock committed
65
	// initialize texture pointers
66 67
	m_pulsarTexture = 0;
	m_backgroundTexture = 0;
68

Oliver Bock's avatar
Oliver Bock committed
69
	// initial render timing settings
Oliver Bock's avatar
Oliver Bock committed
70
	m_framesPerSecond = 25;
Oliver Bock's avatar
Oliver Bock committed
71 72
	m_pulsarRotationDelta = 0.0;
	m_orbitRotationDelta = 0.0;
73 74
	m_pulsarRotationAngle = 0.0;
	m_orbitRotationAngle = 0.0;
75

Oliver Bock's avatar
Oliver Bock committed
76
	// initial binary system parameters (have to match GUI!)
Oliver Bock's avatar
Oliver Bock committed
77
	m_pulsarMass = 1.4;
Oliver Bock's avatar
Oliver Bock committed
78
	// initial companion is "Neutron Star"
Oliver Bock's avatar
Oliver Bock committed
79 80
	m_companionMass = 1.4;
	m_pulsarSpinAxisInclination = 0.0;
Oliver Bock's avatar
Oliver Bock committed
81
	m_pulsarMagneticAxisInclination = 60.0;
Oliver Bock's avatar
Oliver Bock committed
82
	m_pulsarSemiMajorAxis = 5.0;
Oliver Bock's avatar
Oliver Bock committed
83 84
	m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;
	// initial spin frequency of 0.5 Hz
Oliver Bock's avatar
Oliver Bock committed
85
	m_pulsarRotationDelta = (360.0 * 0.5) / m_framesPerSecond;
Oliver Bock's avatar
Oliver Bock committed
86 87
	// update orbital period (based on settings above)
	updateOrbitPeriod();
Oliver Bock's avatar
Oliver Bock committed
88

Oliver Bock's avatar
Oliver Bock committed
89
	// initial view features
90
	m_showOrbits = false;
Oliver Bock's avatar
Oliver Bock committed
91
	m_showRotationAxes = false;
92
	m_cameraInteraction = false;
93

Oliver Bock's avatar
Oliver Bock committed
94
	// initial view settings
Oliver Bock's avatar
Oliver Bock committed
95 96
	m_mouseAngleH = 90.0;
	m_mouseAngleV = 30.0;
97 98 99
	m_cameraZoom = 150.0;
	m_cameraZoomLBound = 10.0;
	m_cameraZoomUBound = 4500.0;
Oliver Bock's avatar
Oliver Bock committed
100 101
	m_mouseLastX = 0;
	m_mouseLastY = 0;
102

Oliver Bock's avatar
Oliver Bock committed
103
	// update camera based on settings above
104
	updateCameraPosition(m_mouseAngleH, m_mouseAngleV, m_cameraZoom);
Oliver Bock's avatar
Oliver Bock committed
105 106 107 108
}

PulsarAnimationWidget::~PulsarAnimationWidget()
{
Oliver Bock's avatar
Oliver Bock committed
109 110
	if(m_quadricCompanionOrbitPlane) gluDeleteQuadric(m_quadricCompanionOrbitPlane);
	if(m_quadricCompanion) gluDeleteQuadric(m_quadricCompanion);
111
	if(m_quadricPulsarOrbitPlane) gluDeleteQuadric(m_quadricPulsarOrbitPlane);
Oliver Bock's avatar
Oliver Bock committed
112 113 114
	if(m_quadricPulsar) gluDeleteQuadric(m_quadricPulsar);
	if(m_quadricPulsarCone1) gluDeleteQuadric(m_quadricPulsarCone1);
	if(m_quadricPulsarCone2) gluDeleteQuadric(m_quadricPulsarCone2);
115
	if(m_quadricPulsarSpinAxis) gluDeleteQuadric(m_quadricPulsarSpinAxis);
116
	if(m_quadricPulsarMagneticAxis) gluDeleteQuadric(m_quadricPulsarMagneticAxis);
117 118 119

	if(m_pulsarTexture) deleteTexture(m_pulsarTexture);
	if(m_backgroundTexture) deleteTexture(m_backgroundTexture);
Oliver Bock's avatar
Oliver Bock committed
120 121 122 123
}

void PulsarAnimationWidget::initializeGL()
{
Oliver Bock's avatar
Oliver Bock committed
124
	glClearColor(0.0, 0.0, 0.0, 0.0);
Oliver Bock's avatar
Oliver Bock committed
125
	glClearDepth(1.0);
Oliver Bock's avatar
Oliver Bock committed
126
	glDepthFunc(GL_LEQUAL);
127 128 129
	glEnable(GL_DEPTH_TEST);

	glShadeModel(GL_SMOOTH);
Oliver Bock's avatar
Oliver Bock committed
130 131
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

Oliver Bock's avatar
Oliver Bock committed
132 133 134 135 136
	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};
Oliver Bock's avatar
Oliver Bock committed
137 138 139 140 141

	glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecular);
	glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
142
	glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 50.0);
Oliver Bock's avatar
Oliver Bock committed
143
	glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction);
144
	glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 10.0);
Oliver Bock's avatar
Oliver Bock committed
145 146 147 148

	glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHTING);
Oliver Bock's avatar
Oliver Bock committed
149

150
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
151
	glEnable(GL_BLEND);
152

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

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

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

Oliver Bock's avatar
Oliver Bock committed
175 176 177 178 179
    // 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
180 181 182
    // prepare and check pulsar texture
    QImage pulsarTexture(":/textures/resources/texture_pulsar.png");
    if(pulsarTexture.width() != pulsarTexture.height()) {
Oliver Bock's avatar
Oliver Bock committed
183
    	qWarning() << msgShape.arg(tr("Pulsar"));
Oliver Bock's avatar
Oliver Bock committed
184 185 186 187 188 189
    }
    else {
    	double integer = 0.0;
    	double fraction = 0.0;
    	fraction = modf(log(pulsarTexture.width()) / log(2.0), &integer);
    	if(fraction > 0.0) {
Oliver Bock's avatar
Oliver Bock committed
190
    		qWarning() << msgPower.arg(tr("Pulsar"));
Oliver Bock's avatar
Oliver Bock committed
191 192 193
    	}
    }
    if(pulsarTexture.width() > maxTextureSize) {
Oliver Bock's avatar
Oliver Bock committed
194
    	qWarning() << msgSize.arg(tr("pulsar").arg(maxTextureSize).arg(maxTextureSize));
Oliver Bock's avatar
Oliver Bock committed
195 196 197 198 199 200
    	pulsarTexture = pulsarTexture.scaled(maxTextureSize, maxTextureSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    }

    // prepare and check background texture
    QImage backgroundTexture(":/textures/resources/texture_background_carina.jpg");
    if(backgroundTexture.width() != backgroundTexture.height()) {
Oliver Bock's avatar
Oliver Bock committed
201
    	qWarning() << msgShape.arg(tr("Background"));
Oliver Bock's avatar
Oliver Bock committed
202 203 204 205 206 207
    }
    else {
    	double integer = 0.0;
    	double fraction = 0.0;
    	fraction = modf(log(backgroundTexture.width()) / log(2.0), &integer);
    	if(fraction > 0.0) {
Oliver Bock's avatar
Oliver Bock committed
208
    		qWarning() << msgPower.arg(tr("Background"));
Oliver Bock's avatar
Oliver Bock committed
209 210 211
    	}
    }
    if(backgroundTexture.width() > maxTextureSize) {
Oliver Bock's avatar
Oliver Bock committed
212
    	qWarning() << msgSize.arg(tr("background").arg(maxTextureSize).arg(maxTextureSize));
Oliver Bock's avatar
Oliver Bock committed
213 214 215 216 217 218
    	backgroundTexture = backgroundTexture.scaled(maxTextureSize, maxTextureSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    }

    // bind textures
    m_pulsarTexture = bindTexture(pulsarTexture, GL_TEXTURE_2D, GL_RGBA);
    m_backgroundTexture = bindTexture(backgroundTexture, GL_TEXTURE_2D, GL_RGB);
219

220
    // use mipmapped textures
221 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
    // 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
227 228 229 230
}

void PulsarAnimationWidget::resizeGL(int w, int h)
{
Oliver Bock's avatar
Oliver Bock committed
231 232 233 234 235
	glViewport(0, 0, w, h);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

236
	gluPerspective(4.5, (GLfloat)w / (GLfloat)h, 0.1, 5000.0);
Oliver Bock's avatar
Oliver Bock committed
237 238 239 240 241 242 243 244 245 246 247 248

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void PulsarAnimationWidget::paintGL()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

249 250
	gluLookAt(m_cameraPosX, m_cameraPosY, m_cameraPosZ,
			  0.0, 0.0, 0.0,
251
			  0.0, 1.0, 0.0);
Oliver Bock's avatar
Oliver Bock committed
252 253

	// TODO: should be located elsewhere
Oliver Bock's avatar
Oliver Bock committed
254 255 256 257 258
	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};
259

Oliver Bock's avatar
Oliver Bock committed
260 261 262 263 264
	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);
Oliver Bock's avatar
Oliver Bock committed
265

266
	// draw companion
Oliver Bock's avatar
Oliver Bock committed
267
	glPushMatrix();
Oliver Bock's avatar
Oliver Bock committed
268 269 270 271
		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);
Oliver Bock's avatar
Oliver Bock committed
272 273
	glPopMatrix();

274
	// draw pulsar
Oliver Bock's avatar
Oliver Bock committed
275
	glPushMatrix();
Oliver Bock's avatar
Oliver Bock committed
276
		glTranslatef(sin(m_orbitRotationAngle * deg2rad) * m_pulsarSemiMajorAxis,
Oliver Bock's avatar
Oliver Bock committed
277
					 0.0,
Oliver Bock's avatar
Oliver Bock committed
278
					 cos(m_orbitRotationAngle * deg2rad) * m_pulsarSemiMajorAxis);
279 280

		glPushMatrix();
Oliver Bock's avatar
Oliver Bock committed
281 282
			glRotatef(m_pulsarSpinAxisInclination, 0.0, 0.0, 1.0);
			glRotatef(m_pulsarRotationAngle, 0.0, 1.0, 0.0);
283

284
			// draw spin axis
Oliver Bock's avatar
Oliver Bock committed
285
			if(m_showRotationAxes) {
286 287
				glPushMatrix();
					glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);
Oliver Bock's avatar
Oliver Bock committed
288 289 290
					glRotatef(90.0, 1.0, 0.0, 0.0);
					glTranslatef(0.0, 0.0, -5.0);
					gluCylinder(m_quadricPulsarSpinAxis, 0.05, 0.05, 10.0, 32, 1);
291 292 293
				glPopMatrix();
			}

294 295 296 297 298
			// create texture coordinates and enable texturing
	        glEnable(GL_TEXTURE_GEN_S);
	        glEnable(GL_TEXTURE_GEN_T);
	        glBindTexture(GL_TEXTURE_2D, m_pulsarTexture);
	        glEnable(GL_TEXTURE_2D);
299 300 301

			glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
			glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
Oliver Bock's avatar
Oliver Bock committed
302
			gluSphere(m_quadricPulsar, 1.0, 32, 32);
303

304
			// disable texturing
305 306 307
	        glDisable(GL_TEXTURE_GEN_S);
	        glDisable(GL_TEXTURE_GEN_T);
	        glDisable(GL_TEXTURE_2D);
308 309
		glPopMatrix();

310
		// TODO: should be located elsewhere
Oliver Bock's avatar
Oliver Bock committed
311 312 313
		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};
314 315 316

		glMaterialfv(GL_FRONT, GL_SPECULAR, coneSpecular);

317
		// first cone
318
		glPushMatrix();
Oliver Bock's avatar
Oliver Bock committed
319 320
			glRotatef(90.0, 1.0, 0.0, 0.0);
			glRotatef(m_pulsarSpinAxisInclination, 0.0, 1.0, 0.0);
321

Oliver Bock's avatar
Oliver Bock committed
322 323
			glRotatef(-m_pulsarRotationAngle - 90.0, 0.0, 0.0, 1.0);
			glRotatef(-m_pulsarMagneticAxisInclination, 1.0, 0.0, 0.0);
324

325
			// draw magnetic axis (for both cones)
Oliver Bock's avatar
Oliver Bock committed
326
			if(m_showRotationAxes) {
327 328
				glPushMatrix();
					glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);
Oliver Bock's avatar
Oliver Bock committed
329 330
					glTranslatef(0.0, 0.0, -5.0);
					gluCylinder(m_quadricPulsarMagneticAxis, 0.05, 0.05, 10.0, 32, 1);
331
				glPopMatrix();
332 333
			}

Oliver Bock's avatar
Oliver Bock committed
334
			glTranslatef(0.0, 0.0, -4.0);
335 336 337

			glMaterialfv(GL_FRONT, GL_AMBIENT, coneAmbient);
			glMaterialfv(GL_FRONT, GL_DIFFUSE, coneDiffuse);
Oliver Bock's avatar
Oliver Bock committed
338
			gluCylinder(m_quadricPulsarCone1, 0.475, 0.0, 3.0, 32, 32);
339 340
		glPopMatrix();

341
		// second cone
342
		glPushMatrix();
Oliver Bock's avatar
Oliver Bock committed
343 344
			glRotatef(-90.0, 1.0, 0.0, 0.0);
			glRotatef(-m_pulsarSpinAxisInclination, 0.0, 1.0, 0.0);
345

Oliver Bock's avatar
Oliver Bock committed
346 347
			glRotatef(m_pulsarRotationAngle - 90.0, 0.0, 0.0, 1.0);
			glRotatef(m_pulsarMagneticAxisInclination, 1.0, 0.0, 0.0);
348

Oliver Bock's avatar
Oliver Bock committed
349
			glTranslatef(0.0, 0.0, -4.0);
350 351 352

			glMaterialfv(GL_FRONT, GL_AMBIENT, coneAmbient);
			glMaterialfv(GL_FRONT, GL_DIFFUSE, coneDiffuse);
Oliver Bock's avatar
Oliver Bock committed
353
			gluCylinder(m_quadricPulsarCone2, 0.475, 0.0, 3.0, 32, 32);
354
		glPopMatrix();
Oliver Bock's avatar
Oliver Bock committed
355
	glPopMatrix();
356

357 358 359 360
	// save current state
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
Oliver Bock's avatar
Oliver Bock committed
361
	glOrtho(0, width(), 0, height(), 0.1, 501.0);
362 363 364 365 366 367
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	// draw backdrop (independent parallel projection)
	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_specular);
Oliver Bock's avatar
Oliver Bock committed
368 369
	glTranslatef(0.0, 0.0, -501.0);
	drawTexture(QPointF(0.0, 0.0), m_backgroundTexture);
370 371 372 373 374 375 376

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

Oliver Bock's avatar
Oliver Bock committed
377
	// draw orbital planes
378
	if(m_cameraInteraction || m_showOrbits) {
379
		glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);
380

Oliver Bock's avatar
Oliver Bock committed
381
		static double sizeOffset = 0.25;
382 383 384 385

		// companion's plane (only if not identical with pulsar's)
		if(m_companionSemiMajorAxis != m_pulsarSemiMajorAxis) {
			glPushMatrix();
Oliver Bock's avatar
Oliver Bock committed
386
				glRotatef(90.0, 1.0, 0.0, 0.0);
387 388 389 390

				// 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)
Oliver Bock's avatar
Oliver Bock committed
391 392
				if((int)(10*(m_pulsarMass-m_companionMass)+0.5) == 1){
					glTranslatef(0.0, 0.0, 0.01);
393 394 395 396 397 398 399 400 401 402
				}

				gluDisk(m_quadricCompanionOrbitPlane,
						m_companionSemiMajorAxis - sizeOffset,
						m_companionSemiMajorAxis + sizeOffset,
						64, 1);
			glPopMatrix();
		}

		// pulsar's plane
Oliver Bock's avatar
Oliver Bock committed
403
		glPushMatrix();
Oliver Bock's avatar
Oliver Bock committed
404
			glRotatef(90.0, 1.0, 0.0, 0.0);
405
			gluDisk(m_quadricPulsarOrbitPlane,
406 407
					m_pulsarSemiMajorAxis - sizeOffset,
					m_pulsarSemiMajorAxis + sizeOffset,
408 409
					64, 1);
		glPopMatrix();
410
	}
Oliver Bock's avatar
Oliver Bock committed
411 412
}

Oliver Bock's avatar
Oliver Bock committed
413 414
void PulsarAnimationWidget::runAnimation()
{
Oliver Bock's avatar
Oliver Bock committed
415
	m_frameTimer.start(qRound(1000.0 / m_framesPerSecond));
Oliver Bock's avatar
Oliver Bock committed
416
}
Oliver Bock's avatar
Oliver Bock committed
417

Oliver Bock's avatar
Oliver Bock committed
418 419 420 421 422 423 424 425
void PulsarAnimationWidget::pauseAnimation()
{
	m_frameTimer.stop();
}

void PulsarAnimationWidget::stopAnimation()
{
	m_frameTimer.stop();
426
	resetParameters();
Oliver Bock's avatar
Oliver Bock committed
427 428 429 430

	updateGL();
}

Oliver Bock's avatar
Oliver Bock committed
431
void PulsarAnimationWidget::updateFrame()
Oliver Bock's avatar
Oliver Bock committed
432
{
Oliver Bock's avatar
Oliver Bock committed
433
	m_pulsarRotationAngle += m_pulsarRotationDelta;
Oliver Bock's avatar
Oliver Bock committed
434 435
	if(m_pulsarRotationAngle > 360.0) {
		m_pulsarRotationAngle -= 360.0;
436
		updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
437
	}
Oliver Bock's avatar
Oliver Bock committed
438
	m_orbitRotationAngle += m_orbitRotationDelta;
Oliver Bock's avatar
Oliver Bock committed
439 440
	if(m_orbitRotationAngle > 360.0) {
		m_orbitRotationAngle -= 360.0;
Oliver Bock's avatar
Oliver Bock committed
441 442
	}

Oliver Bock's avatar
Oliver Bock committed
443
	updateGL();
Oliver Bock's avatar
Oliver Bock committed
444

445
	emit pulsarAnimationStep(m_pulsarRotationAngle);
446 447
}

448 449 450 451 452 453 454
void PulsarAnimationWidget::showOrbits(bool enabled)
{
	m_showOrbits = enabled;

	updateGL();
}

Oliver Bock's avatar
Oliver Bock committed
455 456 457 458 459 460 461
void PulsarAnimationWidget::showRotationAxes(bool enabled)
{
	m_showRotationAxes = enabled;

	updateGL();
}

462 463
void PulsarAnimationWidget::mousePressEvent(QMouseEvent *event)
{
464 465
	Q_UNUSED(event);

466 467 468 469
	m_cameraInteraction = true;
	updateGL();
}

470 471 472 473 474
void PulsarAnimationWidget::mouseMoveEvent(QMouseEvent *event)
{
	Qt::MouseButtons buttons = event->buttons();
	if((buttons & Qt::LeftButton) == Qt::LeftButton) {
		if(m_mouseLastX != 0) {
Oliver Bock's avatar
Oliver Bock committed
475
			m_mouseAngleH += (m_mouseLastX - event->x());
Oliver Bock's avatar
Oliver Bock committed
476
			m_mouseAngleH = m_mouseAngleH < 360 ? m_mouseAngleH : 0;
Oliver Bock's avatar
Oliver Bock committed
477
			m_mouseAngleH = m_mouseAngleH >= 0 ? m_mouseAngleH : 359;
478 479
		}
		if(m_mouseLastY != 0) {
Oliver Bock's avatar
Oliver Bock committed
480
			m_mouseAngleV -= (m_mouseLastY - event->y());
Oliver Bock's avatar
Oliver Bock committed
481 482
			m_mouseAngleV = m_mouseAngleV < 90 ? m_mouseAngleV : 90;
			m_mouseAngleV = m_mouseAngleV > -90 ? m_mouseAngleV : -90;
483 484 485 486 487 488 489
		}

		m_mouseLastX = event->x();
		m_mouseLastY = event->y();
	}
	else if((buttons & Qt::RightButton) == Qt::RightButton) {
		if(m_mouseLastY != 0) {
Oliver Bock's avatar
Oliver Bock committed
490
			m_cameraZoom -= (m_mouseLastY - event->y());
Oliver Bock's avatar
Oliver Bock committed
491
			m_cameraZoom = m_cameraZoom >= m_cameraZoomLBound ? m_cameraZoom : m_cameraZoomLBound;
492
			m_cameraZoom = m_cameraZoom >= m_cameraZoomUBound ? m_cameraZoomUBound : m_cameraZoom;
493 494 495 496 497 498 499 500 501 502
		}

		m_mouseLastY = event->y();
	}

	updateCameraPosition(m_mouseAngleH, m_mouseAngleV, m_cameraZoom);
}

void PulsarAnimationWidget::mouseReleaseEvent(QMouseEvent *event)
{
503 504
	Q_UNUSED(event);

505 506
	m_mouseLastX = 0;
	m_mouseLastY = 0;
507
	m_cameraInteraction = false;
508

509
	updateGL();
510 511
}

Oliver Bock's avatar
Oliver Bock committed
512 513 514 515 516 517 518 519
void PulsarAnimationWidget::showEvent(QShowEvent *event)
{
	Q_UNUSED(event);

	// update and propagate pulse profile
	updatePulseProfile();
}

Oliver Bock's avatar
Oliver Bock committed
520
void PulsarAnimationWidget::updateCameraPosition(const int angleH, const int angleV, const double zoom)
521
{
522
	m_cameraPosX = sin(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
523
	m_cameraPosY = sin(angleV * deg2rad) * zoom;
Oliver Bock's avatar
Oliver Bock committed
524
	m_cameraPosZ = cos(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
525

526 527
	updatePulseProfile();

528 529 530
	updateGL();
}

Oliver Bock's avatar
Oliver Bock committed
531 532 533 534 535
void PulsarAnimationWidget::setFramePerSecond(const unsigned int fps)
{
	m_framesPerSecond = fps;
}

Oliver Bock's avatar
Oliver Bock committed
536
void PulsarAnimationWidget::setPulsarSemiMajorAxis(const double length)
Oliver Bock's avatar
Oliver Bock committed
537
{
Oliver Bock's avatar
Oliver Bock committed
538 539 540
	m_pulsarSemiMajorAxis = length;
	m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;
	updateOrbitPeriod();
541
	updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
542 543

	updateGL();
Oliver Bock's avatar
Oliver Bock committed
544
}
545

Oliver Bock's avatar
Oliver Bock committed
546
void PulsarAnimationWidget::setCompanionMass(const double mass)
547
{
Oliver Bock's avatar
Oliver Bock committed
548 549
	m_companionMass = mass;
	updateOrbitRadii();
550
	updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
551 552

	updateGL();
553 554
}

Oliver Bock's avatar
Oliver Bock committed
555
void PulsarAnimationWidget::setPulsarMass(const double mass)
556
{
Oliver Bock's avatar
Oliver Bock committed
557 558
	m_pulsarMass = mass;
	updateOrbitRadii();
559
	updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
560 561 562 563

	updateGL();
}

Oliver Bock's avatar
Oliver Bock committed
564
void PulsarAnimationWidget::setPulsarSpinFrequency(const double frequency)
Oliver Bock's avatar
Oliver Bock committed
565
{
Oliver Bock's avatar
Oliver Bock committed
566
	m_pulsarRotationDelta = (360.0 * frequency) / m_framesPerSecond;
567
	updatePulseProfile();
568
}
569

570 571 572
void PulsarAnimationWidget::setPulsarSpinAxisInclination(const int degrees)
{
	m_pulsarSpinAxisInclination = degrees;
573
	updatePulseProfile();
574 575 576 577

	updateGL();
}

578 579 580
void PulsarAnimationWidget::setPulsarMagneticAxisInclination(const int degrees)
{
	m_pulsarMagneticAxisInclination = degrees;
Oliver Bock's avatar
Oliver Bock committed
581
	updatePulseProfile();
582 583 584

	updateGL();
}
585

Oliver Bock's avatar
Oliver Bock committed
586 587 588
void PulsarAnimationWidget::updateOrbitPeriod()
{
	m_orbitalPeriod = 3.1553e7 * sqrt(
Oliver Bock's avatar
Oliver Bock committed
589 590
						(pow(m_pulsarSemiMajorAxis, 3.0) * pow(m_pulsarMass+m_companionMass, 2.0)) /
						 pow(m_companionMass, 3.0)
Oliver Bock's avatar
Oliver Bock committed
591 592 593 594 595 596 597 598 599 600 601
					  );

	// visual correction factor (increase orbital momentum)
	double visualCorrection = 1e-8;

	m_orbitRotationDelta  = (360.0 / (m_orbitalPeriod*visualCorrection)) / m_framesPerSecond;
}

void PulsarAnimationWidget::updateOrbitRadii()
{
	m_pulsarSemiMajorAxis = 1.0015e-5 * pow(
Oliver Bock's avatar
Oliver Bock committed
602 603 604
								(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
605 606 607 608 609 610 611
							);

	m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;

	emit pulsarSemiMajorAxisUpdated(m_pulsarSemiMajorAxis);
}

612 613
void PulsarAnimationWidget::resetParameters()
{
Oliver Bock's avatar
Oliver Bock committed
614 615
	m_pulsarRotationAngle = 0.0;
	m_orbitRotationAngle = 0.0;
616
	updatePulseProfile();
Oliver Bock's avatar
Oliver Bock committed
617

618
	emit pulsarAnimationStep(m_pulsarRotationAngle);
619
}
620 621 622

void PulsarAnimationWidget::updatePulseProfile()
{
Oliver Bock's avatar
Oliver Bock committed
623 624 625
	// prepare parameters (e.g. convert to radians where necessary)
	const double	i				= deg2rad * m_pulsarSpinAxisInclination;
	const double	y				= deg2rad * m_pulsarMagneticAxisInclination;
Oliver Bock's avatar
Oliver Bock committed
626
	double			phiOrb			= deg2rad * (m_orbitRotationAngle + 90.0);
Oliver Bock's avatar
Oliver Bock committed
627 628 629 630 631 632
	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;
Oliver Bock's avatar
Oliver Bock committed
633 634
	const double	cam				= pow(xk, 2.0) + pow(yk, 2.0) + pow(zk, 2.0);
	const double	alpha			= deg2rad * (90.0 - m_mouseAngleH);
Oliver Bock's avatar
Oliver Bock committed
635 636
	const double	delta			= deg2rad * m_mouseAngleV;
	const double	gaussProfile	= 0.012337;
637 638 639

	for(int x = 0; x < 360; ++x) {
		// determine angle between pulsar's magnetic axis and line of sight
Oliver Bock's avatar
Oliver Bock committed
640
		phiOrb += deltaPhiOrb;
641 642 643 644 645 646
		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;

Oliver Bock's avatar
Oliver Bock committed
647
		double b = sqrt(pow(rp,2.0) + cam - (2.0 * sqrt(cam) * rp * cos(delta) * sin(alpha + phiOrb)));
648

Oliver Bock's avatar
Oliver Bock committed
649 650
		// determine and store pulse amplitude
		m_pulseProfile[x] = exp(-2.0 * (1.0 - fabs(a/b)) / gaussProfile);
651 652 653 654 655
	}

	// propagate new profile
	emit pulseProfileUpdated(m_pulseProfile);
}