Skip to content
Snippets Groups Projects
Select Git revision
  • 2989e95bcbf86894e3e2241b656081b685a81a6c
  • master default
2 results

test_map.py

Blame
  • Forked from finesse / pykat
    Source project has a limited visibility.
    pulsaranimationwidget.cpp 20.32 KiB
    /******************************************************************************
     *   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"
    
    const double PulsarAnimationWidget::deg2rad = PI/180.0;
    
    PulsarAnimationWidget::PulsarAnimationWidget(QWidget *parent) :
    	QGLWidget(QGLFormat(QGL::AlphaChannel | QGL::SampleBuffers), parent),
    	m_frameTimer(),
    	m_pulseProfile(360, 0.0)
    {
    	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
        connect(&m_frameTimer, SIGNAL(timeout()), this, SLOT(updateFrame()));
    
        // initialize quadric pointers
        m_quadricCompanionOrbitPlane = NULL;
        m_quadricCompanion = NULL;
        m_quadricPulsarOrbitPlane = NULL;
        m_quadricPulsar = NULL;
    	m_quadricPulsarCone1 = NULL;
    	m_quadricPulsarCone2 = NULL;
    	m_quadricPulsarSpinAxis = NULL;
    	m_quadricPulsarMagneticAxis = NULL;
    
    	// initialize texture pointers
    	m_pulsarTexture = 0;
    	m_backgroundTexture = 0;
    
    	// initial render timing settings
    	m_framesPerSecond = 25;
    	m_pulsarRotationDelta = 0.0;
    	m_orbitRotationDelta = 0.0;
    	m_pulsarRotationAngle = 0.0;
    	m_orbitRotationAngle = 0.0;
    
    	// initial binary system parameters (have to match GUI!)
    	m_pulsarMass = 1.4;
    	// initial companion is "Neutron Star"
    	m_companionMass = 1.4;
    	m_pulsarSpinAxisInclination = 0.0;
    	m_pulsarMagneticAxisInclination = 60.0;
    	m_pulsarSemiMajorAxis = 5.0;
    	m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;
    	// initial spin frequency of 0.5 Hz
    	m_pulsarRotationDelta = (360.0 * 0.5) / m_framesPerSecond;
    	// update orbital period (based on settings above)
    	updateOrbitPeriod();
    
    	// initial view features
    	m_showOrbits = false;
    	m_showRotationAxes = false;
    	m_cameraInteraction = false;
    
    	// initial view settings
    	m_mouseAngleH = 90.0;
    	m_mouseAngleV = 30.0;
    	m_cameraZoom = 150.0;
    	m_cameraZoomLBound = 10.0;
    	m_cameraZoomUBound = 4500.0;
    	m_mouseLastX = 0;
    	m_mouseLastY = 0;
    
    	// update camera based on settings above
    	updateCameraPosition(m_mouseAngleH, m_mouseAngleV, m_cameraZoom);
    }
    
    PulsarAnimationWidget::~PulsarAnimationWidget()
    {
    	if(m_quadricCompanionOrbitPlane) gluDeleteQuadric(m_quadricCompanionOrbitPlane);
    	if(m_quadricCompanion) gluDeleteQuadric(m_quadricCompanion);
    	if(m_quadricPulsarOrbitPlane) gluDeleteQuadric(m_quadricPulsarOrbitPlane);
    	if(m_quadricPulsar) gluDeleteQuadric(m_quadricPulsar);
    	if(m_quadricPulsarCone1) gluDeleteQuadric(m_quadricPulsarCone1);
    	if(m_quadricPulsarCone2) gluDeleteQuadric(m_quadricPulsarCone2);
    	if(m_quadricPulsarSpinAxis) gluDeleteQuadric(m_quadricPulsarSpinAxis);
    	if(m_quadricPulsarMagneticAxis) gluDeleteQuadric(m_quadricPulsarMagneticAxis);
    
    	if(m_pulsarTexture) deleteTexture(m_pulsarTexture);
    	if(m_backgroundTexture) deleteTexture(m_backgroundTexture);
    }
    
    void PulsarAnimationWidget::initializeGL()
    {
    	glClearColor(0.0, 0.0, 0.0, 0.0);
    	glClearDepth(1.0);
    	glDepthFunc(GL_LEQUAL);
    	glEnable(GL_DEPTH_TEST);
    
    	glShadeModel(GL_SMOOTH);
    	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    
    	GLfloat LightAmbient[] = {0.3, 0.3, 0.3, 1.0};
    	GLfloat LightDiffuse[] = {1.0, 1.0, 1.0, 1.0};
    	GLfloat LightSpecular[] = {1.0, 1.0, 1.0, 1.0};
    	GLfloat LightPosition[] = {0.0, 0.0, 3.0, 1.0};
    	GLfloat spot_direction[] = {0.0, 0.0, -1.0};
    
    	glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);
    	glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);
    	glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecular);
    	glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
    	glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 50.0);
    	glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction);
    	glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 10.0);
    
    	glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
    	glEnable(GL_LIGHT0);
    	glEnable(GL_LIGHTING);
    
    	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    	glEnable(GL_BLEND);
    
    	m_quadricCompanionOrbitPlane = gluNewQuadric();
    	m_quadricCompanion = gluNewQuadric();
    	m_quadricPulsarOrbitPlane = gluNewQuadric();
    	m_quadricPulsar = gluNewQuadric();
        m_quadricPulsarCone1 = gluNewQuadric();
        m_quadricPulsarCone2 = gluNewQuadric();
        m_quadricPulsarSpinAxis = gluNewQuadric();
        m_quadricPulsarMagneticAxis = gluNewQuadric();
    
        gluQuadricNormals(m_quadricCompanionOrbitPlane, GLU_SMOOTH);
        gluQuadricNormals(m_quadricCompanion, GLU_SMOOTH);
        gluQuadricNormals(m_quadricPulsarOrbitPlane, GLU_SMOOTH);
        gluQuadricNormals(m_quadricPulsar, GLU_SMOOTH);
        gluQuadricNormals(m_quadricPulsarCone1, GLU_SMOOTH);
        gluQuadricNormals(m_quadricPulsarCone2, GLU_SMOOTH);
        gluQuadricNormals(m_quadricPulsarSpinAxis, GLU_SMOOTH);
        gluQuadricNormals(m_quadricPulsarMagneticAxis, GLU_SMOOTH);
    
        // query max texture size (estimate)
        GLint maxTextureSize;
        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
    
        // 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...");
    
        // prepare and check pulsar texture
        QImage pulsarTexture(":/textures/resources/texture_pulsar.png");
        if(pulsarTexture.width() != pulsarTexture.height()) {
        	qWarning() << msgShape.arg(tr("Pulsar"));
        }
        else {
        	double integer = 0.0;
        	double fraction = 0.0;
        	fraction = modf(log(pulsarTexture.width()) / log(2.0), &integer);
        	if(fraction > 0.0) {
        		qWarning() << msgPower.arg(tr("Pulsar"));
        	}
        }
        if(pulsarTexture.width() > maxTextureSize) {
        	qWarning() << msgSize.arg(tr("pulsar").arg(maxTextureSize).arg(maxTextureSize));
        	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()) {
        	qWarning() << msgShape.arg(tr("Background"));
        }
        else {
        	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"));
        	}
        }
        if(backgroundTexture.width() > maxTextureSize) {
        	qWarning() << msgSize.arg(tr("background").arg(maxTextureSize).arg(maxTextureSize));
        	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);
    
        // use mipmapped textures
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    
        // 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);
    }
    
    void PulsarAnimationWidget::resizeGL(int w, int h)
    {
    	glViewport(0, 0, w, h);
    
    	glMatrixMode(GL_PROJECTION);
    	glLoadIdentity();
    
    	gluPerspective(4.5, (GLfloat)w / (GLfloat)h, 0.1, 5000.0);
    
    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();
    }
    
    void PulsarAnimationWidget::paintGL()
    {
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();
    
    	gluLookAt(m_cameraPosX, m_cameraPosY, m_cameraPosZ,
    			  0.0, 0.0, 0.0,
    			  0.0, 1.0, 0.0);
    
    	// TODO: should be located elsewhere
    	static GLfloat no_mat[] = {0.0, 0.0, 0.0, 1.0};
    	static GLfloat mat_diffuse[] = {0.5, 0.5, 0.5, 1.0};
    	static GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
    	static GLfloat low_shininess[] = {2.5};
    	static GLfloat translucent[] = {1.0, 1.0, 1.0, 0.33};
    
    	glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
    	glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    	glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    	glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);
    	glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
    
    	// draw companion
    	glPushMatrix();
    		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);
    	glPopMatrix();
    
    	// draw pulsar
    	glPushMatrix();
    		glTranslatef(sin(m_orbitRotationAngle * deg2rad) * m_pulsarSemiMajorAxis,
    					 0.0,
    					 cos(m_orbitRotationAngle * deg2rad) * m_pulsarSemiMajorAxis);
    
    		glPushMatrix();
    			glRotatef(m_pulsarSpinAxisInclination, 0.0, 0.0, 1.0);
    			glRotatef(m_pulsarRotationAngle, 0.0, 1.0, 0.0);
    
    			// draw spin axis
    			if(m_showRotationAxes) {
    				glPushMatrix();
    					glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);
    					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);
    				glPopMatrix();
    			}
    
    			// 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);
    
    			glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
    			glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    			gluSphere(m_quadricPulsar, 1.0, 32, 32);
    
    			// disable texturing
    	        glDisable(GL_TEXTURE_GEN_S);
    	        glDisable(GL_TEXTURE_GEN_T);
    	        glDisable(GL_TEXTURE_2D);
    		glPopMatrix();
    
    		// TODO: should be located elsewhere
    		static GLfloat coneAmbient[] = {1.0, 1.0, 0.0, 1.0};
    		static GLfloat coneDiffuse[] = {1.0, 1.0, 0.0, 1.0};
    		static GLfloat coneSpecular[] = {1.0, 1.0, 0.5, 1.0};
    
    		glMaterialfv(GL_FRONT, GL_SPECULAR, coneSpecular);
    
    		// first cone
    		glPushMatrix();
    			glRotatef(90.0, 1.0, 0.0, 0.0);
    			glRotatef(m_pulsarSpinAxisInclination, 0.0, 1.0, 0.0);
    
    			glRotatef(-m_pulsarRotationAngle - 90.0, 0.0, 0.0, 1.0);
    			glRotatef(-m_pulsarMagneticAxisInclination, 1.0, 0.0, 0.0);
    
    			// draw magnetic axis (for both cones)
    			if(m_showRotationAxes) {
    				glPushMatrix();
    					glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);
    					glTranslatef(0.0, 0.0, -5.0);
    					gluCylinder(m_quadricPulsarMagneticAxis, 0.05, 0.05, 10.0, 32, 1);
    				glPopMatrix();
    			}
    
    			glTranslatef(0.0, 0.0, -4.0);
    
    			glMaterialfv(GL_FRONT, GL_AMBIENT, coneAmbient);
    			glMaterialfv(GL_FRONT, GL_DIFFUSE, coneDiffuse);
    			gluCylinder(m_quadricPulsarCone1, 0.475, 0.0, 3.0, 32, 32);
    		glPopMatrix();
    
    		// second cone
    		glPushMatrix();
    			glRotatef(-90.0, 1.0, 0.0, 0.0);
    			glRotatef(-m_pulsarSpinAxisInclination, 0.0, 1.0, 0.0);
    
    			glRotatef(m_pulsarRotationAngle - 90.0, 0.0, 0.0, 1.0);
    			glRotatef(m_pulsarMagneticAxisInclination, 1.0, 0.0, 0.0);
    
    			glTranslatef(0.0, 0.0, -4.0);
    
    			glMaterialfv(GL_FRONT, GL_AMBIENT, coneAmbient);
    			glMaterialfv(GL_FRONT, GL_DIFFUSE, coneDiffuse);
    			gluCylinder(m_quadricPulsarCone2, 0.475, 0.0, 3.0, 32, 32);
    		glPopMatrix();
    	glPopMatrix();
    
    	// save current state
    	glMatrixMode(GL_PROJECTION);
    	glPushMatrix();
    	glLoadIdentity();
    	glOrtho(0, width(), 0, height(), 0.1, 501.0);
    	glMatrixMode(GL_MODELVIEW);
    	glPushMatrix();
    	glLoadIdentity();
    
    	// draw backdrop (independent parallel projection)
    	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_specular);
    	glTranslatef(0.0, 0.0, -501.0);
    	drawTexture(QPointF(0.0, 0.0), m_backgroundTexture);
    
    	// restore original state
    	glMatrixMode(GL_PROJECTION);
    	glPopMatrix();
    	glMatrixMode(GL_MODELVIEW);
    	glPopMatrix();
    
    	// draw orbital planes
    	if(m_cameraInteraction || m_showOrbits) {
    		glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);
    
    		static double sizeOffset = 0.25;
    
    		// companion's plane (only if not identical with pulsar's)
    		if(m_companionSemiMajorAxis != m_pulsarSemiMajorAxis) {
    			glPushMatrix();
    				glRotatef(90.0, 1.0, 0.0, 0.0);
    
    				// separate them slightly in case of overlap
    				// based on ugly mass diff check (no collision detection)
    				// single known problematic pair: (m_c=1.4, m_p=1.5)
    				if((int)(10*(m_pulsarMass-m_companionMass)+0.5) == 1){
    					glTranslatef(0.0, 0.0, 0.01);
    				}
    
    				gluDisk(m_quadricCompanionOrbitPlane,
    						m_companionSemiMajorAxis - sizeOffset,
    						m_companionSemiMajorAxis + sizeOffset,
    						64, 1);
    			glPopMatrix();
    		}
    
    		// pulsar's plane
    		glPushMatrix();
    			glRotatef(90.0, 1.0, 0.0, 0.0);
    			gluDisk(m_quadricPulsarOrbitPlane,
    					m_pulsarSemiMajorAxis - sizeOffset,
    					m_pulsarSemiMajorAxis + sizeOffset,
    					64, 1);
    		glPopMatrix();
    	}
    }
    
    void PulsarAnimationWidget::runAnimation()
    {
    	m_frameTimer.start(1000.0 / m_framesPerSecond);
    }
    
    void PulsarAnimationWidget::pauseAnimation()
    {
    	m_frameTimer.stop();
    }
    
    void PulsarAnimationWidget::stopAnimation()
    {
    	m_frameTimer.stop();
    	resetParameters();
    
    	updateGL();
    }
    
    void PulsarAnimationWidget::updateFrame()
    {
    	m_pulsarRotationAngle += m_pulsarRotationDelta;
    	if(m_pulsarRotationAngle > 360.0) {
    		m_pulsarRotationAngle -= 360.0;
    		updatePulseProfile();
    	}
    	m_orbitRotationAngle += m_orbitRotationDelta;
    	if(m_orbitRotationAngle > 360.0) {
    		m_orbitRotationAngle -= 360.0;
    	}
    
    	updateGL();
    
    	emit pulsarAnimationStep(m_pulsarRotationAngle);
    }
    
    void PulsarAnimationWidget::showOrbits(bool enabled)
    {
    	m_showOrbits = enabled;
    
    	updateGL();
    }
    
    void PulsarAnimationWidget::showRotationAxes(bool enabled)
    {
    	m_showRotationAxes = enabled;
    
    	updateGL();
    }
    
    void PulsarAnimationWidget::mousePressEvent(QMouseEvent *event)
    {
    	Q_UNUSED(event);
    
    	m_cameraInteraction = true;
    	updateGL();
    }
    
    void PulsarAnimationWidget::mouseMoveEvent(QMouseEvent *event)
    {
    	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);
    }
    
    void PulsarAnimationWidget::mouseReleaseEvent(QMouseEvent *event)
    {
    	Q_UNUSED(event);
    
    	m_mouseLastX = 0;
    	m_mouseLastY = 0;
    	m_cameraInteraction = false;
    
    	updateGL();
    }
    
    void PulsarAnimationWidget::showEvent(QShowEvent *event)
    {
    	Q_UNUSED(event);
    
    	// update and propagate pulse profile
    	updatePulseProfile();
    }
    
    void PulsarAnimationWidget::updateCameraPosition(const int angleH, const int angleV, const double zoom)
    {
    	m_cameraPosX = sin(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
    	m_cameraPosY = sin(angleV * deg2rad) * zoom;
    	m_cameraPosZ = cos(angleH * deg2rad) * cos(angleV * deg2rad) * zoom;
    
    	updatePulseProfile();
    
    	updateGL();
    }
    
    void PulsarAnimationWidget::setFramePerSecond(const unsigned int fps)
    {
    	m_framesPerSecond = fps;
    }
    
    void PulsarAnimationWidget::setPulsarSemiMajorAxis(const double length)
    {
    	m_pulsarSemiMajorAxis = length;
    	m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;
    	updateOrbitPeriod();
    	updatePulseProfile();
    
    	updateGL();
    }
    
    void PulsarAnimationWidget::setCompanionMass(const double mass)
    {
    	m_companionMass = mass;
    	updateOrbitRadii();
    	updatePulseProfile();
    
    	updateGL();
    }
    
    void PulsarAnimationWidget::setPulsarMass(const double mass)
    {
    	m_pulsarMass = mass;
    	updateOrbitRadii();
    	updatePulseProfile();
    
    	updateGL();
    }
    
    void PulsarAnimationWidget::setPulsarSpinFrequency(const double frequency)
    {
    	m_pulsarRotationDelta = (360.0 * frequency) / m_framesPerSecond;
    	updatePulseProfile();
    }
    
    void PulsarAnimationWidget::setPulsarSpinAxisInclination(const int degrees)
    {
    	m_pulsarSpinAxisInclination = degrees;
    	updatePulseProfile();
    
    	updateGL();
    }
    
    void PulsarAnimationWidget::setPulsarMagneticAxisInclination(const int degrees)
    {
    	m_pulsarMagneticAxisInclination = degrees;
    	updatePulseProfile();
    
    	updateGL();
    }
    
    void PulsarAnimationWidget::updateOrbitPeriod()
    {
    	m_orbitalPeriod = 3.1553e7 * sqrt(
    						(pow(m_pulsarSemiMajorAxis, 3.0) * pow(m_pulsarMass+m_companionMass, 2.0)) /
    						 pow(m_companionMass, 3.0)
    					  );
    
    	// 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(
    								(pow(m_orbitalPeriod, 2.0) * pow(m_companionMass, 3.0)) /
    								 pow(m_pulsarMass+m_companionMass, 2.0),
    								 1.0/3.0
    							);
    
    	m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis;
    
    	emit pulsarSemiMajorAxisUpdated(m_pulsarSemiMajorAxis);
    }
    
    void PulsarAnimationWidget::resetParameters()
    {
    	m_pulsarRotationAngle = 0.0;
    	m_orbitRotationAngle = 0.0;
    	updatePulseProfile();
    
    	emit pulsarAnimationStep(m_pulsarRotationAngle);
    }
    
    void PulsarAnimationWidget::updatePulseProfile()
    {
    	// 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;
    	const double	deltaPhiRot		= deg2rad * 1.0;
    	const double	deltaPhiOrb		= deg2rad * deltaPhiRot * m_orbitRotationDelta / m_pulsarRotationDelta;
    	const double	rp				= m_pulsarSemiMajorAxis;
    	const double	xk				= -m_cameraPosZ;
    	const double	yk				= -m_cameraPosX;
    	const double	zk				= m_cameraPosY;
    	const double	cam				= pow(xk, 2.0) + pow(yk, 2.0) + pow(zk, 2.0);
    	const double	alpha			= deg2rad * (90.0 - m_mouseAngleH);
    	const double	delta			= deg2rad * m_mouseAngleV;
    	const double	gaussProfile	= 0.012337;
    
    	for(int x = 0; x < 360; ++x) {
    		// determine angle between pulsar's magnetic axis and line of sight
    		phiOrb += deltaPhiOrb;
    		const double phiRot = x * deltaPhiRot;
    
    		double a = -sin(y) * sin(phiRot) * (xk + rp * cos(phiOrb)) \
    				 + (cos(i) * sin(y) * cos(phiRot) + sin(i) * cos(y)) * (yk + rp * sin(phiOrb)) \
    				 - (sin(i) * sin(y) * cos(phiRot) - cos(i) * cos(y)) * zk;
    
    		double b = sqrt(pow(rp,2.0) + cam - (2.0 * sqrt(cam) * rp * cos(delta) * sin(alpha + phiOrb)));
    
    		// determine and store pulse amplitude
    		m_pulseProfile[x] = exp(-2.0 * (1.0 - fabs(a/b)) / gaussProfile);
    	}
    
    	// propagate new profile
    	emit pulseProfileUpdated(m_pulseProfile);
    }