open System open System.Drawing open System.Windows.Forms // define units of measurement [] type m; // metres [] type s; // seconds // a pendulum is represented as a record of physical quantities type Pendulum = { length : float gravity : float velocity : float angle : float } // calculate the next state of a pendulum let next pendulum deltaT : Pendulum = let k = -pendulum.gravity / pendulum.length let acceleration = k * Math.Sin pendulum.angle * 1.0 let newVelocity = pendulum.velocity + acceleration * deltaT let newAngle = pendulum.angle + newVelocity * deltaT / 1.0 { 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 optional let (-?) (t1: DateTime) (t2: DateTime option) : float = match t2 with | None -> 0.0 // only one timepoint given -> difference is 0 | Some t -> (t1 - t).TotalSeconds * 1.0 // our main window is double-buffered form that reacts to paint events type PendulumForm() as self = inherit Form(Width=325, Height=240, Text="Pendulum") let mutable pendulum = { length = 1.0; gravity = 9.81 velocity = 0.0 angle = Math.PI / 2.0 } let mutable lastPaintedAt = None let updateFreq = 0.01 do self.DoubleBuffered <- true self.Paint.Add( fun args -> let now = DateTime.Now let deltaT = now -? lastPaintedAt |> min 0.01 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 ) ) self.Invalidate() } |> Async.Start ) [] Application.Run( new PendulumForm( Visible=true ) )