How to resolve the algorithm Animate a pendulum step by step in the Nim programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Animate a pendulum step by step in the Nim programming language

Table of Contents

Problem Statement

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.

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

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Animate a pendulum step by step in the Nim programming language

Source code in the nim programming language

# Pendulum simulation.

import math
import times

import opengl
import opengl/glut

var
  # Simulation variables.
  lg: float         # Pendulum length.
  g: float          # Gravity (should be positive).
  currTime: Time    # Current time.
  theta0: float     # Initial angle.
  theta: float      # Current angle.
  omega: float      # Angular velocity = derivative of theta.
  accel: float      # Angular acceleration = derivative of omega.
  e: float          # Total energy.

#---------------------------------------------------------------------------------------------------

proc initSimulation(length, gravitation, start: float) =
  ## Initialize the simulation.

  lg = length
  g = gravitation
  currTime = getTime()
  theta0 = start                    # Initial angle for which omega = 0.
  theta = start
  omega = 0
  accel = -g / lg * sin(theta0)
  e = g * lg * (1 - cos(theta0))    # Total energy = potential energy when starting.

#---------------------------------------------------------------------------------------------------

proc elapsed(): float =
  ## Return the elapsed time since previous call, expressed in seconds.

  let nextTime = getTime()
  result = (nextTime - currTime).inMicroseconds.float / 1e6
  currTime = nextTime

#---------------------------------------------------------------------------------------------------

proc resize(w, h: GLsizei) =
  ## Resize the window.

  glViewport(0, 0, w, h)
  glMatrixMode(GL_PROJECTION)
  glLoadIdentity()

  glMatrixMode(GL_MODELVIEW)
  glLoadIdentity()
  glOrtho(0, GLdouble(w), GLdouble(h), 0, -1, 1)

#---------------------------------------------------------------------------------------------------

proc render() {.cdecl.} =
  ## Render the window.

  # Compute the position of the mass.
  var x = 320 + 300 * sin(theta)
  var y = 300 * cos(theta)

  resize(640, 320)
  glClear(GL_COLOR_BUFFER_BIT)

  # Draw the line from pivot to mass.
  glBegin(GL_LINES)
  glVertex2d(320, 0)
  glVertex2d(x, y)
  glEnd()
  glFlush()

  # Update theta and omega.
  let dt = elapsed()
  theta += (omega + dt * accel / 2) * dt
  omega += accel * dt

  # If, due to computation errors, potential energy is greater than total energy,
  # reset theta to ±theta0 and omega to 0.
  if lg * g * (1 - cos(theta)) >= e:
    theta = sgn(theta).toFloat * theta0
    omega = 0

  accel = -g / lg * sin(theta)

#---------------------------------------------------------------------------------------------------

proc initGfx(argc: ptr cint; argv: pointer) =
  ## Initialize OpenGL rendering.

  glutInit(argc, argv)
  glutInitDisplayMode(GLUT_RGB)
  glutInitWindowSize(640, 320)
  glutIdleFunc(render)
  discard glutCreateWindow("Pendulum")
  glutDisplayFunc(render)
  loadExtensions()

#———————————————————————————————————————————————————————————————————————————————————————————————————

initSimulation(length = 5, gravitation = 9.81, start = PI / 3)

var argc: cint = 0
initGfx(addr(argc), nil)
glutMainLoop()


# Pendulum simulation.

import math
import times

import gintro/[gobject, gdk, gtk, gio, cairo]
import gintro/glib except Pi

type

  # Description of the simulation.
  Simulation = ref object
    area: DrawingArea       # Drawing area.
    length: float           # Pendulum length.
    g: float                # Gravity (should be positive).
    time: Time              # Current time.
    theta0: float           # initial angle.
    theta: float            # Current angle.
    omega: float            # Angular velocity = derivative of theta.
    accel: float            # Angular acceleration = derivative of omega.
    e: float                # Total energy.

#---------------------------------------------------------------------------------------------------

proc newSimulation(area: DrawingArea; length, g, theta0: float): Simulation {.noInit.} =
  ## Allocate and initialize the simulation object.

  new(result)
  result.area = area
  result.length = length
  result.g = g
  result.time = getTime()
  result.theta0 = theta0
  result.theta = theta0
  result.omega = 0
  result.accel = -g / length * sin(theta0)
  result.e = g * length * (1 - cos(theta0))    # Total energy = potential energy when starting.

#---------------------------------------------------------------------------------------------------

template toFloat(dt: Duration): float = dt.inNanoseconds.float / 1e9

#---------------------------------------------------------------------------------------------------

const Origin = (x: 320.0, y: 100.0)   # Pivot coordinates.
const Scale = 300                     # Coordinates scaling constant.

proc draw(sim: Simulation; context: cairo.Context) =
  ## Draw the pendulum.

  # Compute coordinates in drawing area.
  let x = Origin.x + sin(sim.theta) * Scale
  let y = Origin.y + cos(sim.theta) * Scale

  # Clear the region.
  context.moveTo(0, 0)
  context.setSource(0.0, 0.0, 0.0)
  context.paint()

  # Draw pendulum.
  context.moveTo(Origin.x, Origin.y)
  context.setSource(0.3, 1.0, 0.3)
  context.lineTo(x, y)
  context.stroke()

  # Draw pivot.
  context.setSource(0.3, 0.3, 1.0)
  context.arc(Origin.x, Origin.y, 8, 0, 2 * Pi)
  context.fill()

  # Draw mass.
  context.setSource(1.0, 0.3, 0.3)
  context.arc(x, y, 8, 0, 2 * Pi)
  context.fill()

#---------------------------------------------------------------------------------------------------

proc update(sim: Simulation): gboolean =
  ## Update the simulation state.

  # compute time interval.
  let nextTime = getTime()
  let dt = (nextTime - sim.time).toFloat
  sim.time = nextTime

  # Update theta and omega.
  sim.theta += (sim.omega + dt * sim.accel / 2) * dt
  sim.omega += sim.accel * dt

  # If, due to computation errors, potential energy is greater than total energy,
  # reset theta to ±theta0 and omega to 0.
  if sim.length * sim.g * (1 - cos(sim.theta)) >= sim.e:
    sim.theta = sgn(sim.theta).toFloat * sim.theta0
    sim.omega = 0

  # Compute acceleration.
  sim.accel = -sim.g / sim.length * sin(sim.theta)

  result = gboolean(1)

  sim.draw(sim.area.window.cairoCreate())

#---------------------------------------------------------------------------------------------------

proc activate(app: Application) =
  ## Activate the application.

  let window = app.newApplicationWindow()
  window.setSizeRequest(640, 480)
  window.setTitle("Pendulum simulation")

  let area = newDrawingArea()
  window.add(area)

  let sim = newSimulation(area, length = 5, g = 9.81, theta0 = PI / 3)

  timeoutAdd(10, update, sim)

  window.showAll()

#———————————————————————————————————————————————————————————————————————————————————————————————————

let app = newApplication(Application, "Rosetta.pendulum")
discard app.connect("activate", activate)
discard app.run()


  

You may also check:How to resolve the algorithm Filter step by step in the Wren programming language
You may also check:How to resolve the algorithm Dot product step by step in the OCaml programming language
You may also check:How to resolve the algorithm Variable size/Get step by step in the IDL programming language
You may also check:How to resolve the algorithm Compiler/lexical analyzer step by step in the Go programming language
You may also check:How to resolve the algorithm Program termination step by step in the Factor programming language