Mandelbrot Set in Awk

This week I travelled by train, which meant I had some time to kill. And what better way to do it than playing with pretty colors in the shell. One thing led to another and finally to:

Awkbrot! Mandelbrot sets generated by awk, with pretty terminal colors. ;-)

As the name implies awkbrot is written in awk, it stems from an earlier attempt to do the same in Bash, but the integer only arithmetic in Bash got on my nerves.

The fun thing of awkbrot is that all math with complex numbers is abstracted nicely in some functions:

  • complex() takes a real and imaginary part and creates a complex number
  • c_add() takes two complex numbers and adds them together
  • c_mul() multiplies two complex numbers
  • im_part() and re_part() return the imaginary or the real part of a complex number, respectively.

That makes the definition of the function z -> z2 + c as simple as:

1
2
3
4
# z -> z * z + c;
function mandelbrot(z, c) {
        return c_add(c_mul(z, z), c);
}

Internally awkbrot uses the notation ai+b for complex numbers, actually it’s just syntactic sugar, the functions re_part() and im_part() just use the i+ as a separator and take substrings around it. ;)

Colors are drawn with hardcoded terminal escapes:

1
2
3
4
5
6
# set pixel
function pset(x, y, color) {
        color=substr("0462351", color%7 + 1, 1);
        printf("\033[%i;%iH", y, x);
        printf("\033[0;30;4%im ", color);
};

The line color=substr("0462351", color%7 + 1, 1); makes a decent palette for the solarized colorscheme that I use on my terminals.

Setting the font size of your terminal smaller makes for a higher resolution image, like so:

The full source of awkbrot is included below:

awkbrot (awkbrot.awk) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#!/usr/bin/env awk -f

# awkbrot - mandelbrot in awk
# Hessel Schut, hessel@isquared.nl, 2012-11-17

# set pixel
function pset(x, y, color) {
  color=substr("0462351", color%7 + 1, 1);
  printf("\033[%i;%iH", y, x);
  printf("\033[0;30;4%im ", color);
};

# create complex number
function complex(im, re) {
  return im"i+"re;
}

# real part
function re_part(c) {
  rs=index(c, "i+")+2;
  return substr(c, rs, length(c) - rs + 2);
}

# imaginary part
function im_part(c) {
  return substr(c, 1, index(c, "i+") - 1);
}

# add complex number
function c_add(c1, c2) {
  im = im_part(c1) + im_part(c2);
  re = re_part(c1) + re_part(c2);
  return complex(im, re);
}

# multiply complex number
function c_mul(c1, c2) {
  im = im_part(c1) * re_part(c2) + re_part(c1) * im_part(c2);
  re = re_part(c1) * re_part(c2) - im_part(c1) * im_part(c2);
  return complex(im, re);
}

# z -> z * z + c;
function mandelbrot(z, c) {
  return c_add(c_mul(z, z), c);
}

# term width 
function get_width() {
  t="tput cols";
  t | getline w;
  close(t);
  return w;
}

# term height 
function get_height() {
  t="tput lines";
  t | getline h;
  close(t);
  return h;
}

BEGIN {
  w=get_width()
  h=get_height()

  ci=-1;
  for(y=1; y <= h; y++) {
      cr=-2.5;
      for(x=1; x <= w; x++) {
          z="0i+0"; i=0;
          while (re_part(z)**2 + im_part(z)**2 < 4 && i < 1000) {
              z=mandelbrot(z, complex(ci, cr));
              i++;
          };
          pset(x, y, i%9  - 1);
          cr+=3.5/w;
      };
      ci+=2/h;
  };
}

{ }

END {
  print("\033[0;37;40m\033[H\033[J");
}

Comments