fbpx

C++: Animate a Pendulum

Bjarne-stroustrup
 

One good way of making an animation is by simulating a physical system and illustrating the variables in that system using a dynamically changing graphical display. The classic such physical system is a simple gravity pendulum.

For this task, create a simple physical model of a pendulum and animate it.

Library: wxWidgets

wxPendulumDlg.hpp

#ifndef __wxPendulumDlg_h__
#define __wxPendulumDlg_h__

// ---------------------
/// @author Martin Ettl
/// @date   2013-02-03
// ---------------------

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include <wx/wx.h>
#include <wx/dialog.h>
#else
#include <wx/wxprec.h>
#endif
#include <wx/timer.h>
#include <wx/dcbuffer.h>
#include <cmath>

class wxPendulumDlgApp : public wxApp
{
public:
	bool OnInit();
	int OnExit();
};

class wxPendulumDlg : public wxDialog
{
public:

	wxPendulumDlg(wxWindow *parent, wxWindowID id = 1, const wxString &title = wxT("wxPendulum"), 
	const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, 
	long style = wxSUNKEN_BORDER | wxCAPTION | wxRESIZE_BORDER | wxSYSTEM_MENU | wxDIALOG_NO_PARENT | wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxCLOSE_BOX);

	virtual ~wxPendulumDlg();

	// Event handler
	void wxPendulumDlgPaint(wxPaintEvent& event);
	void wxPendulumDlgSize(wxSizeEvent& event);
	void OnTimer(wxTimerEvent& event);

private:

	// a pointer to a timer object
	wxTimer *m_timer;

	unsigned int m_uiLength;
	double  	 m_Angle;
	double       m_AngleVelocity;

	enum wxIDs
	{
		ID_WXTIMER1 = 1001,
		ID_DUMMY_VALUE_ 
	};

	void OnClose(wxCloseEvent& event);
	void CreateGUIControls();

	DECLARE_EVENT_TABLE()
};

#endif // __wxPendulumDlg_h__

wxPendulumDlg.cpp

// ---------------------
/// @author Martin Ettl
/// @date   2013-02-03
// ---------------------

#include "wxPendulumDlg.hpp"
#include <wx/pen.h>

IMPLEMENT_APP(wxPendulumDlgApp)

bool wxPendulumDlgApp::OnInit()
{
	wxPendulumDlg* dialog = new wxPendulumDlg(NULL);
	SetTopWindow(dialog);
	dialog->Show(true);
	return true;
}

int wxPendulumDlgApp::OnExit()
{
	return 0;
}

BEGIN_EVENT_TABLE(wxPendulumDlg, wxDialog)
EVT_CLOSE(wxPendulumDlg::OnClose)
EVT_SIZE(wxPendulumDlg::wxPendulumDlgSize)
EVT_PAINT(wxPendulumDlg::wxPendulumDlgPaint)
EVT_TIMER(ID_WXTIMER1, wxPendulumDlg::OnTimer)
END_EVENT_TABLE()

wxPendulumDlg::wxPendulumDlg(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &position, const wxSize& size, long style)
: wxDialog(parent, id, title, position, size, style)
{
	CreateGUIControls();
}

wxPendulumDlg::~wxPendulumDlg()
{
}

void wxPendulumDlg::CreateGUIControls()
{
	SetIcon(wxNullIcon);
	SetSize(8, 8, 509, 412);
	Center();

	m_uiLength = 200;
	m_Angle    = M_PI/2.;
	m_AngleVelocity = 0;

	m_timer = new wxTimer();
	m_timer->SetOwner(this, ID_WXTIMER1);
	m_timer->Start(20);
}

void wxPendulumDlg::OnClose(wxCloseEvent& WXUNUSED(event))
{
	Destroy();
}

void wxPendulumDlg::wxPendulumDlgPaint(wxPaintEvent& WXUNUSED(event))
{
	SetBackgroundStyle(wxBG_STYLE_CUSTOM);
	wxBufferedPaintDC dc(this);

	// Get window dimensions
	wxSize sz = GetClientSize();
	// determine the center of the canvas
	const wxPoint center(wxPoint(sz.x / 2, sz.y / 2));

	// create background color
	wxColour powderblue = wxColour(176,224,230);

	// draw powderblue background
	dc.SetPen(powderblue);
	dc.SetBrush(powderblue);
	dc.DrawRectangle(0, 0, sz.x, sz.y);

	// draw lines
	wxPen Pen(*wxBLACK_PEN);
	Pen.SetWidth(1);
	dc.SetPen(Pen);
	dc.SetBrush(*wxBLACK_BRUSH);

	double angleAccel, dt = 0.15;

	angleAccel = (-9.81 / m_uiLength) * sin(m_Angle);
	m_AngleVelocity += angleAccel * dt;
	m_Angle += m_AngleVelocity * dt;

	int anchorX = sz.x / 2, anchorY = sz.y / 4;
	int ballX = anchorX + (int)(sin(m_Angle) * m_uiLength);
	int ballY = anchorY + (int)(cos(m_Angle) * m_uiLength);
	dc.DrawLine(anchorX, anchorY, ballX, ballY);

	dc.SetBrush(*wxGREY_BRUSH);
	dc.DrawEllipse(anchorX - 3, anchorY - 4, 7, 7);

	dc.SetBrush(wxColour(255,255,0)); // yellow
	dc.DrawEllipse(ballX - 7, ballY - 7, 20, 20);
}

void wxPendulumDlg::wxPendulumDlgSize(wxSizeEvent& WXUNUSED(event))
{
	Refresh();
}

void wxPendulumDlg::OnTimer(wxTimerEvent& WXUNUSED(event))
{
	// force refresh
	Refresh();
}

SOURCE

Content is available under GNU Free Documentation License 1.2.