One of the concepts behind Linux (or Unix) design is to execute small programs which process each others outputs to deliver final result. In this article I will present how to create a small animation which uses several Linux utilities.

Wave

Wave

Animation uses well know POV-Ray raytracing tool to generate 3D z = sin(x, y) function. First we need to generate a frame of the animation sequence.

record Point
{
    x -> float;
    y -> float;
    z -> float;
}

func print_point(p -> Point) -> int
{
    prints("<" + p.x + ", " + p.y + ", " + p.z + ">");
    0
}

func print_rect(data[D1, D2] -> Point, x -> int, y -> int) -> int
{
    prints("triangle { ");
      print_point(data[x, y]);
      prints(", ");
      print_point(data[x + 1, y]);
      prints(", ");
      print_point(data[x + 1, y + 1]);
    prints(" }\n");

    prints("triangle { ");
      print_point(data[x, y]);
      prints(", ");
      print_point(data[x + 1, y + 1]);
      prints(", ");
      print_point(data[x, y + 1]);
    prints(" }\n");
    
    0
}

func print_wave(data[D1, D2] -> Point) -> int
{
    var x = 0;
    var y = 0;
    
    for (x = 0; x < D1 - 1; x = x + 1)
    {
        for (y = 0; y < D2 - 1; y = y + 1)
        {
            print_rect(data, x, y)
        }
    };
    
    0
}

func head() -> int
{
  prints("\
  camera { \
    location <0, -16, -12> \
    look_at <0, 0, 0> \
    rotate <0, 0, 30> \
  } \
  light_source { <0, 0, -32> color rgb<1, 1, 1> } \
  mesh { \n");

  0
}

func tail() -> int
{
  prints("\
  texture { \
      pigment { color rgb<0.9, 0.9, 0.9> } \
      finish { ambient 0.2 diffuse 0.7 } \
    } \
  }\n");

  0
}

func get_wave(phi -> float, size -> int) -> [_, _] -> Point
{
    var x = 0;
    var y = 0;
    var x_v = 0.0;
    var y_v = 0.0;
    var z_v = 0.0;
    var tab = {[ size, size ]} -> Point;

    for (x = 0; x < size; x = x + 1)
    {
        for (y = 0; y < size; y = y + 1)
        {
            tab[x, y] = Point(0.0, 0.0, 0.0);

            x_v = -2.0 * 3.141 + 4.0 * 3.141 / size * x;
            y_v = -2.0 * 3.141 + 4.0 * 3.141 / size * y;
            z_v = sin(phi + sqrt(x_v * x_v + y_v * y_v));

            tab[x, y].x = x_v;
            tab[x, y].y = y_v;
            tab[x, y].z = z_v
        }
    };
    
    tab
}

func generate(phi -> float) -> int
{
    var data = get_wave(phi, 24);

    head();
    print_wave(data);
    tail();

    10
}


func main(phi -> int) -> int
{
    generate(2.0 * 3.141 / 32.0 * phi);
    0
}

The above listsing uses Never programming language to generate Povray scene description. 3D data is calculated in function get_wave. Sin function values range from -2pi to 2pi both for x and y arguments. Next the data is output as Povray mesh by functions head, print_wave and tail. Function main takes an argument which specifies frame number. Frame number is an animation offset used in sin function.

Povray, bash and convert

To generate animation several frames need to be combined together.

for f in {0..63}
do
never -m 50000 -f wave.nev $f > wave_$f.pov
done

First 64 frames are generated which pass offset from 0 to 4*pi which gives full animation cycle.

Next frames are processed with Povray to generate graphics.

for f in wave*.pov
do
povray $f
done

And last they are combined together with convert tool.

convert -delay 10 -loop 0 wave_{0..63}.png wave.gif

I hope you liked this tutorial!