! This is a Mandelbrot fractal zoomer I did some time ago.
! Peter De Wachter
! [email protected]

Release 1;

Constant F_BITS = 12;
Constant F_DIV = 64; ! == 2 ^ (F_BITS / 2)

Constant TEXT   = 0;
Constant STATUS = 1;

Constant C_CENTER = 1;
Constant C_LEFT   = 0;
Constant C_RIGHT  = 2;
Constant C_UP     = 2;
Constant C_DOWN   = 0;

Global max_iter = 250;
Global inf;

Global screen_width;
Global screen_height;

Global top;
Global bottom;
Global left;
Global right;
Global width;
Global height;
Global x_inc;
Global y_inc;

Array chars -> '.' '"' '-' '+' '=' '*' '#' '!' '?' 'a' '%' '&' '$' '?' '4' 64;
Array cursorpos table 2;

[ Main char;
       InitScreen();
       SetDim(Fixed(-2), Fixed(1), Fixed(-1), Fixed(1));
       inf = Fixed(4);
       DrawFractal();

       PrintStatusLine("FractalZ, copyright 1999 by Peter De Wachter.  Press h for help.");

       for (::) {
               @read_char 1 -> char;
               switch (char) {
               'z':
                       Zoom();
                       DrawFractal();
               146 to 155:
                       char = char - 146;
                       ZoomRel(char % 3, char / 3);
                       DrawFractal();
               '+':
                       max_iter++;
                       if (max_iter < 1) max_iter--;
                       PrintStatusLine("Max iterations: ", max_iter);
               '-':
                       if (max_iter > 1) max_iter--;
                       PrintStatusLine("Max iterations: ", max_iter);
               'd':
                       DrawFractal();
               'o':
                       SetDim(Fixed(-2), Fixed(1), Fixed(-1), Fixed(1));
                       DrawFractal();
               27, 'q':
                       @erase_window -1;
                       @quit;
               's', 'r':
                       fsaverest(char);
               'h':
                       Help();
               }
       }
];

[ InitScreen;
       font off;
       @buffer_mode 0;
       if ((screen_width = 0->$21) == 0)
               screen_width = 80;
       if ((screen_height = 0->$20) == 255)
               screen_height = (screen_width * 100) / 162;
       @split_window screen_height;
];

[ TextScreen;
       @split_window 1;
       @set_window TEXT;
       @erase_window TEXT;
       @buffer_mode 1;
       new_line;
];

[ SetDim l r t b oldl oldr oldt oldb tmp;
       oldt = top; oldb = bottom; oldl = left; oldr = right;

       top = t; bottom = b; left = l; right = r;
       height = bottom - top; width = right - left;
       x_inc = (width + screen_width) / screen_width;
       y_inc = (height + screen_height - 1) / (screen_height - 1);

       if ((width / x_inc < screen_width - (screen_width / 5)) ||
           (height / y_inc < screen_height - (screen_width / 5))) {
               PrintStatusLine("Can't zoom that much...");
               @read_char 1 -> tmp;
               SetDim(oldl, oldr, oldt, oldb);
       }
];

[ ZoomRel x y nb nt nl nr;
       switch (x) {
       C_LEFT:   nl = left; nr = left / 2 + right / 2;
       C_RIGHT:  nl = left / 2 + right / 2; nr = right;
       C_CENTER: nl = left / 2 + right / 2 - width / 4;
                 nr = left / 2 + right / 2 + width / 4;
       }
       switch (y) {
       C_UP:     nt = top; nb = top / 2 + bottom / 2;
       C_DOWN:   nt = top / 2 + bottom / 2; nb = bottom;
       C_CENTER: nt = top / 2 + bottom / 2 - height / 4;
                 nb = top / 2 + bottom / 2 + height / 4;
       }
       SetDim(nl, nr, nt, nb);
];

[ GetXY x y char;
       for (::) {
               @set_cursor y x;
               style reverse;
               print "+";
               style roman;
               @read_char 1 0 -> char;
               switch (char) {
               129: if (y > 2) { DrawFractalChar(x, y); y--; }
               130: if (y < screen_height) { DrawFractalChar(x, y); y++; }
               131: if (x > 1) { DrawFractalChar(x, y); x--; }
               132: if (x < screen_width) { DrawFractalChar(x, y); x++; }
               10, 13, 32, 27, 'z': jump done;
               }
       }
       .done;
       return x * 256 + y;
];

[ Zoom nl nr nt nb tmp;
       PrintStatusLine("Select upper-left point...");
       tmp = GetXY(screen_width / 2, screen_height / 2);
       nl = left + (tmp / 256 - 1) * x_inc;
       nt = top + (tmp % 256 - 2) * y_inc;

       PrintStatusLine("Select lower-right point...");
       tmp = GetXY(screen_width / 2, screen_height / 2);
       nr = left + (tmp / 256) * (x_inc - 1);
       nb = top + (tmp % 256 - 1) * (y_inc - 1);

       if (nl > nr) { tmp = nl; nl = nr; nr = tmp; }
       if (nt > nb) { tmp = nt; nt = nb; nb = tmp; }
       SetDim(nl, nr, nt, nb);
];

[ DrawFractal x y;
       @split_window 1;
       @erase_window TEXT;
       @split_window screen_height;
       PrintStatusLine("Calculating...");
       y = top;
       for (::) {
               x = left;
               while (x < right) {
                       Iterate(x, y);
                       x = x + x_inc;
               }
               y = y + y_inc;
               if (y < bottom) {
                       print "^";
               } else
                       break;
       }
       PrintStatusLine(
               "Ready.  Horizontal zoom = ",
               19200 / (width / 64),
               "%, vertical zoom = ",
               25600 / (height / 32));
];

[ DrawFractalChar x y;
       @set_cursor y x;
       Iterate(left + (x-1) * x_inc, top + (y-2) * y_inc);
];

[ Iterate x y iter cx cy xd yd x2 y2;
       iter = 0;

       cx = x; cy = y;

       xd = x / F_DIV;
       x2 = xd * xd;

       yd = y / F_DIV;
       y2 = yd * yd;

       while ((x2 + y2 < inf) && (iter < max_iter)) {
               x = x2 - y2 + cx;
               y = xd * yd * 2 + cy;
               iter++;

               xd = x / F_DIV;
               x2 = xd * xd;
               yd = y / F_DIV;
               y2 = yd * yd;
       }

       if (iter == max_iter) {
               print " ";
       } else {
               print (char) chars->(iter&15);
       }
];

[ Help tmp;
       PrintStatusLine("Help");
       TextScreen();
       style bold;
       print "FractalZ";
       style roman;
       print ", release ", (0-->$1) & $03ff, ", serial ";
       DumpHeader($12, $17);
       print ", compiled with Inform ";
       DumpHeader(60, 63);
       print ".^";

       print "^
FractalZ is a Mandelbrot-set zoomer for the Z-Machine. Who said it could only
be used for adventure games?^^

It's controls are:
^   1-9   Zoom into the lower-left (1), upper-right (9), ... corner.
^   z     Zoom into a area you choose.
^   +/-   Increase / decrease the number of iterations.
^   d     Redraw the fractal
^   o     Zoom out.
^   s     Save the current state.
^   r     Restore.
^   q     Quit.
^   h     Help (but you already knew that).^^

It has been programmed by Peter De Wachter (dewachter@@64softhome.net)
somewhere in the spring of 1999.";

       @read_char 1 -> tmp;
       InitScreen();
       DrawFractal();
];

[ Fixed x;
       @art_shift x F_BITS -> x;
       return x;
];

[ PrintStatusLine str1 num2 str3 num4;
       @set_window STATUS;
       @set_cursor 1 1;
       style reverse;

       print " ";
       if (str1 ~= nothing) { print (string) str1;
       if (num2 ~= nothing) { print num2;
       if (str3 ~= nothing) { print (string) str3;
       if (num4 ~= nothing) { print num4, "%"; } } } }

       @get_cursor cursorpos;
       spaces screen_width - cursorpos->3 + 1;

       @set_cursor 2 1;
       style roman;
];

[ fsaverest what;
       if (what == 's')
               PrintStatusLine("Saving...");
       else
               PrintStatusLine("Restoring...");
       TextScreen();
       if (what == 's')
               save next;
       else
               restore next;
       .next;
       InitScreen();
       SetDim(left, right, top, bottom);
       DrawFractal();
];

[ DumpHeader from to;  ! destroys from
       for (: from <= to: from++) print (char)0->from;
];