I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

# Animate a pendulum

(Redirected from Pendulum Animation)
Animate a pendulum
You are encouraged to solve this task according to the task description, using any language you may know.

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.

Task

Create a simple physical model of a pendulum and animate it.

## Ada

This does not use a GUI, it simply animates the pendulum and prints out the positions. If you want, you can replace the output method with graphical update methods.

X and Y are relative positions of the pendulum to the anchor.

pendulums.ads:

generic   type Float_Type is digits <>;   Gravitation : Float_Type;package Pendulums is   type Pendulum is private;   function New_Pendulum (Length : Float_Type;                          Theta0 : Float_Type) return Pendulum;   function Get_X (From : Pendulum) return Float_Type;   function Get_Y (From : Pendulum) return Float_Type;   procedure Update_Pendulum (Item : in out Pendulum; Time : in Duration);private   type Pendulum is record      Length   : Float_Type;      Theta    : Float_Type;      X        : Float_Type;      Y        : Float_Type;      Velocity : Float_Type;   end record;end Pendulums;

pendulums.adb:

with Ada.Numerics.Generic_Elementary_Functions;package body Pendulums is   package Math is new Ada.Numerics.Generic_Elementary_Functions (Float_Type);    function New_Pendulum (Length : Float_Type;                          Theta0 : Float_Type) return Pendulum is      Result : Pendulum;   begin      Result.Length   := Length;      Result.Theta    := Theta0 / 180.0 * Ada.Numerics.Pi;      Result.X        := Math.Sin (Theta0) * Length;      Result.Y        := Math.Cos (Theta0) * Length;      Result.Velocity := 0.0;      return Result;   end New_Pendulum;    function Get_X (From : Pendulum) return Float_Type is   begin      return From.X;   end Get_X;    function Get_Y (From : Pendulum) return Float_Type is   begin      return From.Y;   end Get_Y;    procedure Update_Pendulum (Item : in out Pendulum; Time : in Duration) is      Acceleration : constant Float_Type := Gravitation / Item.Length *                                            Math.Sin (Item.Theta);   begin         Item.X        := Math.Sin (Item.Theta) * Item.Length;         Item.Y        := Math.Cos (Item.Theta) * Item.Length;         Item.Velocity := Item.Velocity +                          Acceleration  * Float_Type (Time);         Item.Theta    := Item.Theta +                          Item.Velocity * Float_Type (Time);   end Update_Pendulum;end Pendulums;

example main.adb:

with Ada.Text_IO;with Ada.Calendar;with Pendulums; procedure Main is   package Float_Pendulum is new Pendulums (Float, -9.81);   use Float_Pendulum;   use type Ada.Calendar.Time;    My_Pendulum : Pendulum := New_Pendulum (10.0, 30.0);   Now, Before : Ada.Calendar.Time;begin   Before := Ada.Calendar.Clock;   loop      Delay 0.1;      Now := Ada.Calendar.Clock;      Update_Pendulum (My_Pendulum, Now - Before);      Before := Now;      -- output positions relative to origin      -- replace with graphical output if wanted      Ada.Text_IO.Put_Line (" X: " & Float'Image (Get_X (My_Pendulum)) &                            " Y: " & Float'Image (Get_Y (My_Pendulum)));   end loop;end Main;
Output:
 X:  5.00000E+00 Y:  8.66025E+00
X:  4.95729E+00 Y:  8.68477E+00
X:  4.87194E+00 Y:  8.73294E+00
X:  4.74396E+00 Y:  8.80312E+00
X:  4.57352E+00 Y:  8.89286E+00
X:  4.36058E+00 Y:  8.99919E+00
X:  4.10657E+00 Y:  9.11790E+00
X:  3.81188E+00 Y:  9.24498E+00
X:  3.47819E+00 Y:  9.37562E+00
X:  3.10714E+00 Y:  9.50504E+00
X:  2.70211E+00 Y:  9.62801E+00
X:  2.26635E+00 Y:  9.73980E+00
X:  1.80411E+00 Y:  9.83591E+00
X:  1.32020E+00 Y:  9.91247E+00
X:  8.20224E-01 Y:  9.96630E+00
X:  3.10107E-01 Y:  9.99519E+00
X: -2.03865E-01 Y:  9.99792E+00
X: -7.15348E-01 Y:  9.97438E+00
X: -1.21816E+00 Y:  9.92553E+00
X: -1.70581E+00 Y:  9.85344E+00
X: -2.17295E+00 Y:  9.76106E+00
X: -2.61452E+00 Y:  9.65216E+00
X: -3.02618E+00 Y:  9.53112E+00
X: -3.40427E+00 Y:  9.40271E+00
X: -3.74591E+00 Y:  9.27190E+00
X: -4.04873E+00 Y:  9.14373E+00
X: -4.31141E+00 Y:  9.02285E+00
X: -4.53271E+00 Y:  8.91373E+00
X: -4.71186E+00 Y:  8.82034E+00
X: -4.84868E+00 Y:  8.74587E+00
X: -4.94297E+00 Y:  8.69293E+00
X: -4.99459E+00 Y:  8.66337E+00
X: -5.00352E+00 Y:  8.65822E+00
...

## AutoHotkey

This version doesn't use an complex physics calculation - I found a faster way.

Library: GDIP
SetBatchlines,-1;settingsSizeGUI:={w:650,h:400} ;Guisizependulum:={length:300,maxangle:90,speed:2,size:30,center:{x:Sizegui.w//2,y:10}} ;pendulum length, size, center, speed and maxangle pendulum.maxangle:=pendulum.maxangle*0.01745329252p_Token:=Gdip_Startup()Gui,+LastFoundGui,show,% "w" SizeGUI.w  " h" SizeGUI.hhwnd:=WinActive()hdc:=GetDC(hwnd)start:=A_TickCount/1000G:=Gdip_GraphicsFromHDC(hdc)pBitmap:=Gdip_CreateBitmap(650, 450)G2:=Gdip_GraphicsFromImage(pBitmap)Gdip_SetSmoothingMode(G2, 4)pBrush := Gdip_BrushCreateSolid(0xff0000FF)pBrush2 := Gdip_BrushCreateSolid(0xFF777700)pPen:=Gdip_CreatePenFromBrush(pBrush2, 10)SetTimer,Update,10 Update:Gdip_GraphicsClear(G2,0xFFFFFFFF)time:=start-(A_TickCount/1000*pendulum.speed)angle:=sin(time)*pendulum.maxanglex2:=sin(angle)*pendulum.length+pendulum.center.xy2:=cos(angle)*pendulum.length+pendulum.center.yGdip_DrawLine(G2,pPen,pendulum.center.x,pendulum.center.y,x2,y2)GDIP_DrawCircle(G2,pBrush,pendulum.center.x,pendulum.center.y,15)GDIP_DrawCircle(G2,pBrush2,x2,y2,pendulum.size)Gdip_DrawImage(G, pBitmap)return GDIP_DrawCircle(g,b,x,y,r){	Gdip_FillEllipse(g, b, x-r//2,y-r//2 , r, r)} GuiClose:ExitApp

## BASIC

### BBC BASIC

      MODE 8      *FLOAT 64      VDU 23,23,4;0;0;0; : REM Set line thickness       theta = RAD(40) : REM initial displacement      g = 9.81 : REM acceleration due to gravity      l = 0.50 : REM length of pendulum in metres       REPEAT        PROCpendulum(theta, l)        WAIT 1        PROCpendulum(theta, l)        accel = - g * SIN(theta) / l / 100        speed += accel / 100        theta += speed      UNTIL FALSE      END       DEF PROCpendulum(a, l)      LOCAL pivotX, pivotY, bobX, bobY      pivotX = 640      pivotY = 800      bobX = pivotX + l * 1000 * SIN(a)      bobY = pivotY - l * 1000 * COS(a)      GCOL 3,6      LINE pivotX, pivotY, bobX, bobY      GCOL 3,11      CIRCLE FILL bobX + 24 * SIN(a), bobY - 24 * COS(a), 24      ENDPROC

### Commodore BASIC

10 GOSUB 100020 THETA = π/230 G = 9.8140 L = 0.550 SPEED = 060 PX = 2070 PY = 180 BX = PX+L*20*SIN(THETA)90 BY = PY-L*20*COS(THETA)100 PRINT CHR$(147);110 FOR X=PX TO BX STEP (BX-PX)/10120 Y=PY+(X-PX)*(BY-PY)/(BX-PX)130 PRINT CHR$(19);LEFT$(X$,X);LEFT$(Y$,Y);"."140 NEXT150 PRINT CHR$(19);LEFT$(X$,BX);LEFT$(Y$,BY);CHR$(113)160 ACCEL=G*SIN(THETA)/L/50170 SPEED=SPEED+ACCEL/10180 THETA=THETA+SPEED190 GOTO 80980 REM ** SETUP STRINGS TO BE USED **990 REM ** FOR CURSOR POSITIONING   **1000 FOR I=0 TO 39: X$= X$+CHR$(29): NEXT1010 FOR I=0 TO 24: Y$ = Y$+CHR$(17): NEXT1020 RETURN

### FreeBASIC

Const PI = 3.141592920Dim As Double theta, g, l, accel, speed, px, py, bx, bytheta = PI/2g = 9.81l = 1speed = 0px = 320py = 10Screen 17 '640x400 graphicDo    bx=px+l*300*Sin(theta)    by=py-l*300*Cos(theta)    Cls    Line (px,py)-(bx,by)    Circle (bx,by),5,,,,,F    accel=g*Sin(theta)/l/100    speed=speed+accel/100    theta=theta+speed    Draw String (0,370), "Pendulum"    Draw String (0,385), "Press any key to quit"    Sleep 10Loop Until Inkey()<>""

### IS-BASIC

100 PROGRAM "Pendulum.bas"110 LET THETA=RAD(50):LET G=9.81:LET L=.5120 CALL INIC130 CALL DRAWING140 CALL ANIMATE150 CALL RESET160 END170 DEF INIC180   CLOSE #102190   OPTION ANGLE RADIANS200   SET STATUS OFF:SET INTERRUPT STOP OFF:SET BORDER 56210   SET VIDEO MODE 1:SET VIDEO COLOR 1:SET VIDEO X 14:SET VIDEO Y 8220   FOR I=1 TO 24230     OPEN #I:"video:"240     SET #I:PALETTE 56,0,255,YELLOW250   NEXT260 END DEF270 DEF DRAWING280   LET SPD=0290   FOR I=1 TO 24300     DISPLAY #I:AT 3 FROM 1 TO 8310     SET #I:INK 2320     PLOT #I:224,280,ELLIPSE 10,10330     PLOT #I:0,280;214,280,234,280;446,280340     SET #I:INK 1350     CALL PENDULUM(THETA,L,I)360     LET ACC=-G*SIN(THETA)/L/100370     LET SPD=SPD+ACC/10.5380     LET THETA=THETA+SPD390   NEXT400 END DEF410 DEF PENDULUM(A,L,CH)420   LET PX=224:LET PY=280430   LET BX=PX+L*460*SIN(A)440   LET BY=PY-L*460*COS(A)450   PLOT #CH:PX,PY;BX,BY460   PLOT #CH:BX+24*SIN(A),BY-24*COS(A),ELLIPSE 20,20,470   SET #CH:INK 3:PLOT #CH:PAINT480 END DEF490 DEF ANIMATE500   DO510     FOR I=1 TO 24520       DISPLAY #I:AT 3 FROM 1 TO 8530     NEXT 540     FOR I=23 TO 2 STEP-1550       DISPLAY #I:AT 3 FROM 1 TO 8560     NEXT 570   LOOP UNTIL INKEY$=CHR$(27)580 END DEF590 DEF RESET600   TEXT 40:SET STATUS ON:SET INTERRUPT STOP ON:SET BORDER 0610   FOR I=24 TO 1 STEP-1620     CLOSE #I630   NEXT640 END DEF

## C

Library: GLUT
#include <stdlib.h>#include <math.h>#include <GL/glut.h>#include <GL/gl.h>#include <sys/time.h> #define length 5#define g 9.8double alpha, accl, omega = 0, E;struct timeval tv; double elappsed() {	struct timeval now;	gettimeofday(&now, 0);	int ret = (now.tv_sec - tv.tv_sec) * 1000000		+ now.tv_usec - tv.tv_usec;	tv = now;	return ret / 1.e6;} void resize(int w, int h){	glViewport(0, 0, w, h);	glMatrixMode(GL_PROJECTION);	glLoadIdentity(); 	glMatrixMode(GL_MODELVIEW);	glLoadIdentity();	glOrtho(0, w, h, 0, -1, 1);} void render(){	double x = 320 + 300 * sin(alpha), y = 300 * cos(alpha);	resize(640, 320); 	glClear(GL_COLOR_BUFFER_BIT); 	glBegin(GL_LINES);	glVertex2d(320, 0);	glVertex2d(x, y);	glEnd();	glFlush(); 	double us = elappsed();	alpha += (omega + us * accl / 2) * us;	omega += accl * us; 	/* don't let precision error go out of hand */	if (length * g * (1 - cos(alpha)) >= E) {		alpha = (alpha < 0 ? -1 : 1) * acos(1 - E / length / g);		omega = 0;	}	accl = -g / length * sin(alpha);} void init_gfx(int *c, char **v){	glutInit(c, v);	glutInitDisplayMode(GLUT_RGB);	glutInitWindowSize(640, 320);	glutIdleFunc(render);	glutCreateWindow("Pendulum");} int main(int c, char **v){	alpha = 4 * atan2(1, 1) / 2.1;	E = length * g * (1 - cos(alpha)); 	accl = -g / length * sin(alpha);	omega = 0; 	gettimeofday(&tv, 0);	init_gfx(&c, v);	glutMainLoop();	return 0;}

## C#

Library: Windows Forms
 using System;using System.Drawing;using System.Windows.Forms; class CSharpPendulum{    Form _form;    Timer _timer;     double _angle = Math.PI / 2,            _angleAccel,            _angleVelocity = 0,            _dt = 0.1;     int _length = 50;     [STAThread]    static void Main()    {        var p = new CSharpPendulum();    }     public CSharpPendulum()    {        _form = new Form() { Text = "Pendulum", Width = 200, Height = 200 };        _timer = new Timer() { Interval = 30 };         _timer.Tick += delegate(object sender, EventArgs e)        {            int anchorX = (_form.Width / 2) - 12,                anchorY = _form.Height / 4,                ballX = anchorX + (int)(Math.Sin(_angle) * _length),                ballY = anchorY + (int)(Math.Cos(_angle) * _length);             _angleAccel = -9.81 / _length * Math.Sin(_angle);            _angleVelocity += _angleAccel * _dt;            _angle += _angleVelocity * _dt;             Bitmap dblBuffer = new Bitmap(_form.Width, _form.Height);            Graphics g = Graphics.FromImage(dblBuffer);            Graphics f = Graphics.FromHwnd(_form.Handle);             g.DrawLine(Pens.Black, new Point(anchorX, anchorY), new Point(ballX, ballY));            g.FillEllipse(Brushes.Black, anchorX - 3, anchorY - 4, 7, 7);            g.FillEllipse(Brushes.DarkGoldenrod, ballX - 7, ballY - 7, 14, 14);             f.Clear(Color.White);            f.DrawImage(dblBuffer, new Point(0, 0));            };         _timer.Start();        Application.Run(_form);    }     }

## C++

Library: wxWidgets

File 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__

File 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();}

This program is tested with wxWidgets version 2.8 and 2.9. The whole project, including makefile for compiling on Linux can be download from github.

## Clojure

Clojure solution using an atom and a separate rendering thread

Library: Swing
Library: AWT
 (ns pendulum  (:import    (javax.swing JFrame)    (java.awt Canvas Graphics Color))) (def length 200)(def width (* 2 (+ 50 length)))(def height (* 3 (/ length 2)))(def dt 0.1)(def g 9.812)(def k (- (/ g length)))(def anchor-x (/ width 2))(def anchor-y (/ height 8))(def angle (atom (/ (Math/PI) 2))) (defn draw [#^Canvas canvas angle]  (let [buffer  (.getBufferStrategy canvas)        g       (.getDrawGraphics buffer)        ball-x (+ anchor-x (* (Math/sin angle) length))        ball-y (+ anchor-y (* (Math/cos angle) length))]    (try            (doto g        (.setColor Color/BLACK)        (.fillRect 0 0 width height)        (.setColor Color/RED)        (.drawLine anchor-x anchor-y ball-x ball-y)        (.setColor Color/YELLOW)        (.fillOval (- anchor-x 3) (- anchor-y 4) 7 7)        (.fillOval (- ball-x 7) (- ball-y 7) 14 14))            (finally (.dispose g)))    (if-not (.contentsLost buffer)      (.show buffer)) )) (defn start-renderer [canvas]  (->>    (fn [] (draw canvas @angle) (recur))    (new Thread)    (.start))) (defn -main [& args]  (let [frame  (JFrame. "Pendulum")        canvas (Canvas.)]     (doto frame      (.setSize width height)            (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)      (.setResizable false)      (.add canvas)      (.setVisible true))     (doto canvas      (.createBufferStrategy 2)            (.setVisible true)      (.requestFocus))     (start-renderer canvas)     (loop [v 0]            (swap! angle #(+ % (* v dt)))      (Thread/sleep 15)      (recur (+ v (* k (Math/sin @angle) dt)))) )) (-main)

## Common Lisp

An approach using closures. Physics code adapted from Ada.

Pressing the spacebar adds a pendulum.

(defvar *frame-rate* 30)(defvar *damping* 0.99 "Deceleration factor.") (defun make-pendulum (length theta0 x)  "Returns an anonymous function with enclosed state representing a pendulum."  (let* ((theta (* (/ theta0 180) pi))         (acceleration 0))    (if (< length 40) (setf length 40)) ;;avoid a divide-by-zero    (lambda ()      ;;Draws the pendulum, updating its location and speed.      (sdl:draw-line (sdl:point :x x :y 1)                     (sdl:point :x (+ (* (sin theta) length) x)                                :y (* (cos theta) length)))      (sdl:draw-filled-circle (sdl:point :x (+ (* (sin theta) length) x)                                         :y (* (cos theta) length))                              20                              :color sdl:*yellow*                              :stroke-color sdl:*white*)      ;;The magic constant approximates the speed we want for a given frame-rate.      (incf acceleration (* (sin theta) (* *frame-rate* -0.001)))      (incf theta acceleration)      (setf acceleration (* acceleration *damping*)))))  (defun main (&optional (w 640) (h 480))  (sdl:with-init ()    (sdl:window w h :title-caption "Pendulums"                :fps (make-instance 'sdl:fps-fixed))    (setf (sdl:frame-rate) *frame-rate*)    (let ((pendulums nil))      (sdl:with-events ()        (:quit-event () t)        (:idle ()               (sdl:clear-display sdl:*black*)               (mapcar #'funcall pendulums) ;;Draw all the pendulums                (sdl:update-display))        (:key-down-event (:key key)                         (cond ((sdl:key= key :sdl-key-escape)                                (sdl:push-quit-event))                               ((sdl:key= key :sdl-key-space)                                (push (make-pendulum (random (- h 100))                                                     (random 90)                                                     (round w 2))                                      pendulums))))))))

## Delphi

Library: Vcl.Forms
Translation of: C#
 unit main; interface uses  Vcl.Forms, Vcl.Graphics, Vcl.ExtCtrls; type  TForm1 = class(TForm)    procedure FormCreate(Sender: TObject);    procedure FormDestroy(Sender: TObject);  private    Timer: TTimer;    angle, angleAccel, angleVelocity, dt: double;    len: Integer;    procedure Tick(Sender: TObject);  end; var  Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject);begin Width := 200; Height := 200; DoubleBuffered := True; Timer := TTimer.Create(nil); Timer.Interval := 30; Timer.OnTimer := Tick; Caption := 'Pendulum'; // initialize angle := PI / 2; angleAccel := 0; angleVelocity := 0; dt := 0.1; len := 50;end; procedure TForm1.FormDestroy(Sender: TObject);begin Timer.Free;end; procedure TForm1.Tick(Sender: TObject);const HalfPivot = 4; HalfBall = 7;var anchorX, anchorY, ballX, ballY: Integer;begin anchorX := Width div 2 - 12; anchorY := Height div 4; ballX := anchorX + Trunc(Sin(angle) * len); ballY := anchorY + Trunc(Cos(angle) * len); angleAccel := -9.81 / len * Sin(angle); angleVelocity := angleVelocity + angleAccel * dt; angle := angle + angleVelocity * dt; with canvas do begin Pen.Color := clBlack; with Brush do begin Style := bsSolid; Color := clWhite; end; FillRect(ClientRect); MoveTo(anchorX, anchorY); LineTo(ballX, ballY); Brush.Color := clGray; Ellipse(anchorX - HalfPivot, anchorY - HalfPivot, anchorX + HalfPivot, anchorY + HalfPivot); Brush.Color := clYellow; Ellipse(ballX - HalfBall, ballY - HalfBall, ballX + HalfBall, ballY + HalfBall); end;end; end. ## E Works with: E-on-Java (Uses Java Swing for GUI. The animation logic is independent, however.) The angle ${\displaystyle \theta }$ of a pendulum with length ${\displaystyle L}$ and acceleration due to gravity ${\displaystyle g}$ with all its mass at the end and no friction/air resistance has an acceleration at any given moment of ${\displaystyle {\frac {d^{2}}{dt^{2}}}\theta =-{\frac {g}{L}}\sin \theta }$ This simulation uses this formula directly, updating the velocity from the acceleration and the position from the velocity; inaccuracy results from the finite timestep. The event flow works like this: The clock object created by the simulation steps the simulation on the specified in the interval. The simulation writes its output to angle, which is a Lamport slot which can notify of updates. The whenever set up by makeDisplayComponent listens for updates and triggers redrawing as long as interest has been expressed, which is done whenever the component actually redraws, which happens only if the component's window is still on screen. When the window is closed, additionally, the simulation itself is stopped and the application allowed to exit. (This logic is more general than necessary; it is designed to be suitable for a larger application as well.) #!/usr/bin/env runepragma.syntax("0.9") def pi := (-1.0).acos()def makeEPainter := <unsafe:com.zooko.tray.makeEPainter>def makeLamportSlot := <import:org.erights.e.elib.slot.makeLamportSlot>def whenever := <import:org.erights.e.elib.slot.whenever>def colors := <import:java.awt.makeColor> # --------------------------------------------------------------# --- Definitions def makePendulumSim(length_m :float64, gravity_mps2 :float64, initialAngle_rad :float64, timestep_ms :int) { var velocity := 0 def &angle := makeLamportSlot(initialAngle_rad) def k := -gravity_mps2/length_m def timestep_s := timestep_ms / 1000 def clock := timer.every(timestep_ms, fn _ { def acceleration := k * angle.sin() velocity += acceleration * timestep_s angle += velocity * timestep_s }) return [clock, &angle]} def makeDisplayComponent(&angle) { def c def updater := whenever([&angle], fn { c.repaint() }) bind c := makeEPainter(def paintCallback { to paintComponent(g) { try { def originX := c.getWidth() // 2 def originY := c.getHeight() // 2 def pendRadius := (originX.min(originY) * 0.95).round() def ballRadius := (originX.min(originY) * 0.04).round() def ballX := (originX + angle.sin() * pendRadius).round() def ballY := (originY + angle.cos() * pendRadius).round() g.setColor(colors.getWhite()) g.fillRect(0, 0, c.getWidth(), c.getHeight()) g.setColor(colors.getBlack()) g.fillOval(originX - 2, originY - 2, 4, 4) g.drawLine(originX, originY, ballX, ballY) g.fillOval(ballX - ballRadius, ballY - ballRadius, ballRadius * 2, ballRadius * 2) updater[] # provoke interest provided that we did get drawn (window not closed) } catch p { stderr.println(In paint callback:$p${p.eStack()}) } } }) c.setPreferredSize(<awt:makeDimension>(300, 300)) return c} # --------------------------------------------------------------# --- Application setup def [clock, &angle] := makePendulumSim(1, 9.80665, pi*99/100, 10) # Initialize AWT, move to AWT event threadwhen (currentVat.morphInto("awt")) -> { # Create the window def frame := <unsafe:javax.swing.makeJFrame>("Pendulum") frame.setContentPane(def display := makeDisplayComponent(&angle)) frame.addWindowListener(def mainWindowListener { to windowClosing(_) { clock.stop() interp.continueAtTop() } match _ {} }) frame.setLocation(50, 50) frame.pack() # Start and become visible frame.show() clock.start()} interp.blockAtTop() ## EasyLang on animate clear_screen move_pen 50 50 draw_circle 1 x = 50 + 40 * sin ang y = 50 - 40 * cos ang draw_line x y draw_circle 5 vel += sin ang / 5 ang += vel.ang = 10 ## Elm import Color exposing (..)import Collage exposing (..)import Element exposing (..)import Html exposing (..)import Time exposing (..)import Html.App exposing (program) dt = 0.01scale = 100 type alias Model = { angle : Float , angVel : Float , length : Float , gravity : Float } type Msg = Tick Time init : (Model,Cmd Msg)init = ( { angle = 3 * pi / 4 , angVel = 0.0 , length = 2 , gravity = -9.81 } , Cmd.none) update : Msg -> Model -> (Model, Cmd Msg)update _ model = let angAcc = -1.0 * (model.gravity / model.length) * sin (model.angle) angVel' = model.angVel + angAcc * dt angle' = model.angle + angVel' * dt in ( { model | angle = angle' , angVel = angVel' } , Cmd.none ) view : Model -> Html Msgview model = let endPoint = ( 0, scale * model.length ) pendulum = group [ segment ( 0, 0 ) endPoint |> traced { defaultLine | width = 2, color = red } , circle 8 |> filled blue , ngon 3 10 |> filled green |> rotate (pi/2) |> move endPoint ] in toHtml <| collage 700 500 [ pendulum |> rotate model.angle ] subscriptions : Model -> Sub Msgsubscriptions _ = Time.every (dt * second) Tick main = program { init = init , view = view , update = update , subscriptions = subscriptions } Link to live demo: http://dc25.github.io/animatedPendulumElm ## ERRE  PROGRAM PENDULUM !! for rosettacode.org! !$KEY !$INCLUDE="PC.LIB" PROCEDURE PENDULUM(A,L) PIVOTX=320 PIVOTY=0 BOBX=PIVOTX+L*500*SIN(a) BOBY=PIVOTY+L*500*COS(a) LINE(PIVOTX,PIVOTY,BOBX,BOBY,6,FALSE) CIRCLE(BOBX+24*SIN(A),BOBY+24*COS(A),27,11) PAUSE(0.01) LINE(PIVOTX,PIVOTY,BOBX,BOBY,0,FALSE) CIRCLE(BOBX+24*SIN(A),BOBY+24*COS(A),27,0)END PROCEDURE BEGIN SCREEN(9) THETA=40*p/180 ! initial displacement G=9.81 ! acceleration due to gravity L=0.5 ! length of pendulum in metres LINE(0,0,639,0,5,FALSE) LOOP PENDULUM(THETA,L) ACCEL=-G*SIN(THETA)/L/100 SPEED=SPEED+ACCEL/100 THETA=THETA+SPEED END LOOPEND PROGRAM  PC version: Ctrl+Break to stop. ## Euler Math Toolbox Euler Math Toolbox can determine the exact period of a physical pendulum. The result is then used to animate the pendulum. The following code is ready to be pasted back into Euler notebooks. >g=gearth$; l=1m;
>function f(x,y) := [y[2],-g*sin(y[1])/l]
>function h(a) := ode("f",linspace(0,a,100),[0,2])[1,-1]
>period=solve("h",2)
2.06071780729
>t=linspace(0,period,30); s=ode("f",t,[0,2])[1];
>function anim (t,s) ...
$setplot(-1,1,-1,1);$  markerstyle("o#");
$repeat$  for i=1 to cols(t)-1;
$clg;$    hold on;
$plot([0,sin(s[i])],[1,1-cos(s[i])]);$    mark([0,sin(s[i])],[1,1-cos(s[i])]);
$hold off;$    wait(t[i+1]-t[i]);
$end;$  until testkey();
$end$endfunction
>anim(t,s);
>


## Euphoria

### DOS32 version

Works with: Euphoria version 3.1.1
include graphics.einclude misc.e constant dt = 1E-3constant g = 50 sequence vcsequence suspensionatom len procedure draw_pendulum(atom color, atom len, atom alfa)    sequence point    point = (len*{sin(alfa),cos(alfa)} + suspension)    draw_line(color, {suspension, point})    ellipse(color,0,point-{10,10},point+{10,10})end procedure function wait()    atom t0    t0 = time()    while time() = t0 do        if get_key() != -1 then            return 1        end if    end while    return 0end function procedure animation()    atom alfa, omega, epsilon     if graphics_mode(18) then    end if     vc = video_config()    suspension = {vc[VC_XPIXELS]/2,vc[VC_YPIXELS]/2}    len = vc[VC_YPIXELS]/2-20     alfa = PI/2    omega = 0     while 1 do        draw_pendulum(BRIGHT_WHITE,len,alfa)        if wait() then            exit        end if        draw_pendulum(BLACK,len,alfa)        epsilon = -len*sin(alfa)*g        omega += dt*epsilon        alfa += dt*omega    end while     if graphics_mode(-1) then    end ifend procedure animation()

## F#

A nice application of F#'s support for units of measure.

open Systemopen System.Drawingopen System.Windows.Forms // define units of measurement[<Measure>] type m;  // metres[<Measure>] type s;  // seconds // a pendulum is represented as a record of physical quantitiestype Pendulum = { length   : float<m>   gravity  : float<m/s^2>   velocity : float<m/s>   angle    : float } // calculate the next state of a pendulumlet next pendulum deltaT : Pendulum =  let k = -pendulum.gravity / pendulum.length  let acceleration = k * Math.Sin pendulum.angle * 1.0<m>   let newVelocity = pendulum.velocity + acceleration * deltaT  let newAngle = pendulum.angle + newVelocity * deltaT / 1.0<m>  { pendulum with velocity = newVelocity; angle = newAngle } // paint a pendulum (using hard-coded screen coordinates)let paint pendulum (gr: System.Drawing.Graphics) =  let homeX = 160  let homeY = 50  let length = 140.0  // draw plate  gr.DrawLine( new Pen(Brushes.Gray, width=2.0f), 0, homeY, 320, homeY )  // draw pivot  gr.FillEllipse( Brushes.Gray,           homeX-5, homeY-5, 10, 10 )  gr.DrawEllipse( new Pen(Brushes.Black), homeX-5, homeY-5, 10, 10 )  // draw the pendulum itself  let x = homeX + int( length * Math.Sin pendulum.angle )  let y = homeY + int( length * Math.Cos pendulum.angle )  // draw rod  gr.DrawLine( new Pen(Brushes.Black, width=3.0f), homeX, homeY, x, y )  // draw bob  gr.FillEllipse( Brushes.Yellow,         x-15, y-15, 30, 30 )  gr.DrawEllipse( new Pen(Brushes.Black), x-15, y-15, 30, 30 ) // defines an operator "-?" that calculates the time from t2 to t1  // where t2 is optionallet (-?) (t1: DateTime) (t2: DateTime option) : float<s> =  match t2 with  | None   -> 0.0<s> // only one timepoint given -> difference is 0  | Some t -> (t1 - t).TotalSeconds * 1.0<s> // our main window is double-buffered form that reacts to paint eventstype PendulumForm() as self =   inherit Form(Width=325, Height=240, Text="Pendulum")  let mutable pendulum = { length   = 1.0<m>;                           gravity  = 9.81<m/s^2>                           velocity = 0.0<m/s>                           angle    = Math.PI / 2.0                         }  let mutable lastPaintedAt = None  let updateFreq = 0.01<s>   do self.DoubleBuffered <- true     self.Paint.Add( fun args ->       let now = DateTime.Now       let deltaT = now -? lastPaintedAt |> min 0.01<s>        lastPaintedAt <- Some now        pendulum <- next pendulum deltaT        let gr = args.Graphics       gr.Clear( Color.LightGray )       paint pendulum gr        // initiate a new paint event after a while (non-blocking)       async { do! Async.Sleep( int( 1000.0 * updateFreq / 1.0<s> ) )               self.Invalidate()            }       |> Async.Start      ) [<STAThread>]Application.Run( new PendulumForm( Visible=true ) )

## Factor

Approximation of the pendulum for small swings : theta = theta0 * cos(omega0 * t)

USING: accessors alarms arrays calendar colors.constants kernellocals math math.constants math.functions math.rectanglesmath.vectors opengl sequences system ui ui.gadgets ui.render ;IN: pendulum CONSTANT: g 9.81CONSTANT: l 20CONSTANT: theta0 0.5 : current-time ( -- time ) nano-count -9 10^ * ; : T0 ( -- T0 ) 2 pi l g / sqrt * * ;: omega0 ( -- omega0 ) 2 pi * T0 / ;: theta ( -- theta ) current-time omega0 * cos theta0 * ; : relative-xy ( theta l -- xy )     swap [ sin * ] [ cos * ] 2bi 2array ;: theta-to-xy ( origin theta l -- xy ) relative-xy v+ ; TUPLE: pendulum-gadget < gadget alarm ; : O ( gadget -- origin ) rect-bounds [ drop ] [ first 2 / ] bi* 0 2array ;: window-l ( gadget -- l ) rect-bounds [ drop ] [ second ] bi* ;: gadget-xy ( gadget -- xy ) [ O ] [ drop theta ] [ window-l ] tri theta-to-xy ; M: pendulum-gadget draw-gadget*     COLOR: black gl-color    [ O ] [ gadget-xy ] bi gl-line ; M:: pendulum-gadget graft* ( gadget -- )    [ gadget relayout-1 ]    20 milliseconds every gadget (>>alarm) ;M: pendulum-gadget ungraft* alarm>> cancel-alarm ; : <pendulum-gadget> ( -- gadget )     pendulum-gadget new     { 500 500 } >>pref-dim ;: pendulum-main ( -- )    [ <pendulum-gadget> "pendulum" open-window ] with-ui ;MAIN: pendulum-main

## FBSL

#INCLUDE <Include\Windows.inc> FBSLSETTEXT(ME, "Pendulum")FBSL.SETTIMER(ME, 1000, 10)RESIZE(ME, 0, 0, 300, 200)CENTER(ME)SHOW(ME) BEGIN EVENTS	SELECT CASE CBMSG		CASE WM_TIMER			' Request redraw			InvalidateRect(ME, NULL, FALSE)			RETURN 0		CASE WM_PAINT			Swing()		CASE WM_CLOSE			FBSL.KILLTIMER(ME, 1000)	END SELECTEND EVENTS SUB Swing()	TYPE RECT: %rcLeft, %rcTop, %rcRight, %rcBottom: END TYPE	STATIC rc AS RECT, !!acceleration, !!velocity, !!angle = M_PI_2, %pendulum = 100 	GetClientRect(ME, @rc) 	' Recalculate	DIM headX = rc.rcRight / 2, headY = rc.rcBottom / 4	DIM tailX = headX + SIN(angle) * pendulum	DIM tailY = headY + COS(angle) * pendulum 	acceleration = -9.81 / pendulum * SIN(angle)	INCR(velocity, acceleration * 0.1)(angle, velocity * 0.1) 	' Create backbuffer	CreateCompatibleDC(GetDC(ME))	SelectObject(CreateCompatibleDC, CreateCompatibleBitmap(GetDC, rc.rcRight, rc.rcBottom)) 	' Draw to backbuffer	FILLSTYLE(FILL_SOLID): FILLCOLOR(RGB(200, 200, 0))	LINE(CreateCompatibleDC, 0, 0, rc.rcRight, rc.rcBottom, GetSysColor(COLOR_BTNHILIGHT), TRUE, TRUE)	LINE(CreateCompatibleDC, 0, headY, rc.rcRight, headY, GetSysColor(COLOR_3DSHADOW))	DRAWWIDTH(3)	LINE(CreateCompatibleDC, headX, headY, tailX, tailY, RGB(200, 0, 0))	DRAWWIDTH(1)	CIRCLE(CreateCompatibleDC, headX, headY, 2, GetSysColor, 0, 360, 1, TRUE)	CIRCLE(CreateCompatibleDC, tailX, tailY, 10, GetSysColor, 0, 360, 1, FALSE) 	' Blit to window	BitBlt(GetDC, 0, 0, rc.rcRight, rc.rcBottom, CreateCompatibleDC, 0, 0, SRCCOPY)	ReleaseDC(ME, GetDC) 	' Delete backbuffer		DeleteObject(SelectObject(CreateCompatibleDC, SelectObject))	DeleteDC(CreateCompatibleDC)END SUB

Screenshot:




## Fortran

Uses system commands (gfortran) to clear the screen. An initial starting angle is allowed between 90 (to the right) and -90 degrees (to the left). It checks for incorrect inputs.

 !Implemented by Anant Dixit (October, 2014)program animated_pendulumimplicit nonedouble precision, parameter :: pi = 4.0D0*atan(1.0D0), l = 1.0D-1, dt = 1.0D-2, g = 9.8D0integer :: iodouble precision :: s_ang, c_ang, p_ang, n_ang write(*,*) 'Enter starting angle (in degrees):'do  read(*,*,iostat=io) s_ang  if(io.ne.0 .or. s_ang.lt.-90.0D0 .or. s_ang.gt.90.0D0) then    write(*,*) 'Please enter an angle between 90 and -90 degrees:'  else    exit  end ifend docall execute_command_line('cls') c_ang = s_ang*pi/180.0D0p_ang = c_ang call display(c_ang)do  call next_time_step(c_ang,p_ang,g,l,dt,n_ang)  if(abs(c_ang-p_ang).ge.0.05D0) then    call execute_command_line('cls')    call display(c_ang)  end ifend doend program subroutine next_time_step(c_ang,p_ang,g,l,dt,n_ang)double precision :: c_ang, p_ang, g, l, dt, n_angn_ang = (-g*sin(c_ang)/l)*2.0D0*dt**2 + 2.0D0*c_ang - p_angp_ang = c_angc_ang = n_angend subroutine subroutine display(c_ang)double precision :: c_angcharacter (len=*), parameter :: cfmt = '(A1)'double precision :: rx, ryinteger :: x, y, i, jrx = 45.0D0*sin(c_ang)ry = 22.5D0*cos(c_ang)x = int(rx)+51y = int(ry)+2do i = 1,32  do j = 1,100    if(i.eq.y .and. j.eq.x) then      write(*,cfmt,advance='no') 'O'    else if(i.eq.y .and. (j.eq.(x-1).or.j.eq.(x+1))) then      write(*,cfmt,advance='no') 'G'    else if(j.eq.x .and. (i.eq.(y-1).or.i.eq.(y+1))) then      write(*,cfmt,advance='no') 'G'    else if(i.eq.y .and. (j.eq.(x-2).or.j.eq.(x+2))) then      write(*,cfmt,advance='no') '#'    else if(j.eq.x .and. (i.eq.(y-2).or.i.eq.(y+2))) then      write(*,cfmt,advance='no') 'G'    else if((i.eq.(y+1).and.j.eq.(x+1)) .or. (i.eq.(y-1).and.j.eq.(x-1))) then      write(*,cfmt,advance='no') '#'    else if((i.eq.(y+1).and.j.eq.(x-1)) .or. (i.eq.(y-1).and.j.eq.(x+1))) then      write(*,cfmt,advance='no') '#'    else if(j.eq.50) then      write(*,cfmt,advance='no') '|'    else if(i.eq.2) then      write(*,cfmt,advance='no') '-'    else      write(*,cfmt,advance='no') ' '    end if  end do  write(*,*)end doend subroutine

A small preview (truncated to a few steps of the pendulum changing direction). Initial angle provided = 80 degrees.

                                                 |
-------------------------------------------------|--------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|                G
|               #G#
|              #GOG#
|               #G#
|                G
|
|
|
|
|
|
|
|

|
-------------------------------------------------|--------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|                       G
|                      #G#
|                     #GOG#
|                      #G#
|                       G
|
|
|
|
|
|
|
|
|

|
-------------------------------------------------|--------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|                            G
|                           #G#
|                          #GOG#
|                           #G#
|                            G
|
|
|
|
|
|
|
|
|
|
|

|
-------------------------------------------------|--------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|                                 G
|                                #G#
|                               #GOG#
|                                #G#
|                                 G
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
-------------------------------------------------|--------------------------------------------------
|
|
|
|
|
|
|
|
|
|                                     G
|                                    #G#
|                                   #GOG#
|                                    #G#
|                                     G
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
-------------------------------------------------|--------------------------------------------------
|
|
|
|
|
|
|
|                                       G
|                                      #G#
|                                     #GOG#
|                                      #G#
|                                       G
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
-------------------------------------------------|--------------------------------------------------
|
|
|
|
|
|                                         G
|                                        #G#
|                                       #GOG#
|                                        #G#
|                                         G
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
-------------------------------------------------|--------------------------------------------------
|
|
|
|                                          G
|                                         #G#
|                                        #GOG#
|                                         #G#
|                                          G
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|



## Groovy

Straight translation of Java solution groovified by removing explicit definitions and converting casts to Groovy as style where needed.

 import java.awt.*;import javax.swing.*; class Pendulum extends JPanel implements Runnable {     private angle = Math.PI / 2;    private length;     Pendulum(length) {        this.length = length;        setDoubleBuffered(true);    }     @Override    void paint(Graphics g) {        g.setColor(Color.WHITE);        g.fillRect(0, 0, getWidth(), getHeight());        g.setColor(Color.BLACK);        int anchorX = getWidth() / 2, anchorY = getHeight() / 4;        def ballX = anchorX + (Math.sin(angle) * length) as int;        def ballY = anchorY + (Math.cos(angle) * length) as int;        g.drawLine(anchorX, anchorY, ballX, ballY);        g.fillOval(anchorX - 3, anchorY - 4, 7, 7);        g.fillOval(ballX - 7, ballY - 7, 14, 14);    }     void run() {        def angleAccel, angleVelocity = 0, dt = 0.1;        while (true) {            angleAccel = -9.81 / length * Math.sin(angle);            angleVelocity += angleAccel * dt;            angle += angleVelocity * dt;            repaint();            try { Thread.sleep(15); } catch (InterruptedException ex) {}        }    }     @Override    Dimension getPreferredSize() {        return new Dimension(2 * length + 50, (length / 2 * 3) as int);    }     static void main(String[] args) {        def f = new JFrame("Pendulum");        def p = new Pendulum(200);        f.add(p);        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        f.pack();        f.setVisible(true);        new Thread(p).start();    }}

## Go

Using
Library: GXUI
from Github
package main import (	"github.com/google/gxui"	"github.com/google/gxui/drivers/gl"	"github.com/google/gxui/math"	"github.com/google/gxui/themes/dark"	omath "math"	"time") //Two pendulums animated//Top: Mathematical pendulum with small-angle approxmiation (not appropiate with PHI_ZERO=pi/2)//Bottom: Simulated with differential equation phi'' = g/l * sin(phi) const (	ANIMATION_WIDTH  int     = 480	ANIMATION_HEIGHT int     = 320	BALL_RADIUS      float32 = 25.0	METER_PER_PIXEL  float64 = 1.0 / 20.0	PHI_ZERO         float64 = omath.Pi * 0.5) var (	l    float64 = float64(ANIMATION_HEIGHT) * 0.5	freq float64 = omath.Sqrt(9.81 / (l * METER_PER_PIXEL))) type Pendulum interface {	GetPhi() float64} type mathematicalPendulum struct {	start time.Time} func (p *mathematicalPendulum) GetPhi() float64 {	if (p.start == time.Time{}) {		p.start = time.Now()	}	t := float64(time.Since(p.start).Nanoseconds()) / omath.Pow10(9)	return PHI_ZERO * omath.Cos(t*freq)} type numericalPendulum struct {	currentPhi float64	angAcc     float64	angVel     float64	lastTime   time.Time} func (p *numericalPendulum) GetPhi() float64 {	dt := 0.0	if (p.lastTime != time.Time{}) {		dt = float64(time.Since(p.lastTime).Nanoseconds()) / omath.Pow10(9)	}	p.lastTime = time.Now() 	p.angAcc = -9.81 / (float64(l) * METER_PER_PIXEL) * omath.Sin(p.currentPhi)	p.angVel += p.angAcc * dt	p.currentPhi += p.angVel * dt 	return p.currentPhi} func draw(p Pendulum, canvas gxui.Canvas, x, y int) {	attachment := math.Point{X: ANIMATION_WIDTH/2 + x, Y: y} 	phi := p.GetPhi()	ball := math.Point{X: x + ANIMATION_WIDTH/2 + math.Round(float32(l*omath.Sin(phi))), Y: y + math.Round(float32(l*omath.Cos(phi)))} 	line := gxui.Polygon{gxui.PolygonVertex{attachment, 0}, gxui.PolygonVertex{ball, 0}} 	canvas.DrawLines(line, gxui.DefaultPen) 	m := math.Point{int(BALL_RADIUS), int(BALL_RADIUS)}	rect := math.Rect{ball.Sub(m), ball.Add(m)}	canvas.DrawRoundedRect(rect, BALL_RADIUS, BALL_RADIUS, BALL_RADIUS, BALL_RADIUS, gxui.TransparentPen, gxui.CreateBrush(gxui.Yellow))} func appMain(driver gxui.Driver) {	theme := dark.CreateTheme(driver) 	window := theme.CreateWindow(ANIMATION_WIDTH, 2*ANIMATION_HEIGHT, "Pendulum")	window.SetBackgroundBrush(gxui.CreateBrush(gxui.Gray50)) 	image := theme.CreateImage() 	ticker := time.NewTicker(time.Millisecond * 15)	pendulum := &mathematicalPendulum{}	pendulum2 := &numericalPendulum{PHI_ZERO, 0.0, 0.0, time.Time{}} 	go func() {		for _ = range ticker.C {			canvas := driver.CreateCanvas(math.Size{ANIMATION_WIDTH, 2 * ANIMATION_HEIGHT})			canvas.Clear(gxui.White) 			draw(pendulum, canvas, 0, 0)			draw(pendulum2, canvas, 0, ANIMATION_HEIGHT) 			canvas.Complete()			driver.Call(func() {				image.SetCanvas(canvas)			})		}	}() 	window.AddChild(image) 	window.OnClose(ticker.Stop)	window.OnClose(driver.Terminate)} func main() {	gl.StartDriver(appMain)}

Library: HGL

## ZX Spectrum Basic

Translation of: ERRE

In a real Spectrum it is too slow. Use the BasinC emulator/editor at maximum speed for realistic animation.

10 OVER 1: CLS 20 LET theta=130 LET g=9.8140 LET l=0.550 LET speed=0100 LET pivotx=120110 LET pivoty=140120 LET bobx=pivotx+l*100*SIN (theta)130 LET boby=pivoty+l*100*COS (theta)140 GO SUB 1000: PAUSE 1: GO SUB 1000190 LET accel=g*SIN (theta)/l/100200 LET speed=speed+accel/100210 LET theta=theta+speed220 GO TO 1001000 PLOT pivotx,pivoty: DRAW bobx-pivotx,boby-pivoty1010 CIRCLE bobx,boby,31020 RETURN