/* mandelbrotset.m
   mandelbrot set generator optimized for
   mobile use
   by airbit AG, 15-jun-2008/bj

   usage:
   if in idle mode -> zoom frame is displayed
   "5" or gokey: compute fractal from the range in the zoom frame
   "2" or upkey: move zoomframe up
   "8" or downkey: move zoomframe down
   "4" or leftkey: move zoomframe left
   "6" or rightkey: move zoomframe right
   "1": enlarge zoomframe
   "3": decrease zoomframe
   "7": shift color palette to brighter
   "9": shift color palette to darker
   "0": save current graph to .jpg file
   "*": toggle frame color
   "#": exit/end program
   if in compute mode -> fractal is being drawed
   gokey: stop calculation and drawing when current detail level is finished 
*/
use ui, array as a, math as m, graph as g, time, audio

const detail=400; // maximum number of iterations, as higher as better the quality, but takes more time to calculate
const zooms=10; // max number of zoom levels for frame
const grid=2; // how many pixels one step shifts the frame
// declare and init globals
g.scale(false);
scr=g.full(false);
g.size(scr[0], scr[1]);
g.bg(g.black);
g.clear();
mbca=array.create(scr[0], 0);
for i=0 to len(mbca)-1 do
  mbca[i]=array.create(scr[1], 0)
end;
function colors(cs=1) forward;
cola=colors();
colc=1600*m.log(detail/3)/detail;

// scale fractal to screen
scale=scr[0]/scr[1];
ex=(4*scale)/2;
sx=-ex;
sy=-2.5;
ey=1.5;

zoom=1;
acuteness=1;
fx=0; fy=0; fw=0; fh=0; fox=0; foy=0;
sf=[null,null,null,null];
fname="mb_"+time.str(time.get(), "YYYY-MM-DD_hh-mm-ss_");
calcs=0;
greenframe=true;

function mandelbrot(sx, ex, sy, ey, det, cola, colc, psize)
  for j=0 to ..scr[1]-1 by psize do
    for i=0 to ..scr[0]-1 by psize do
      iter=0;
      x0=ex-(ex-sx)/..scr[0]*(i+psize/2);
      y0=ey-(ey-sy)/..scr[1]*(j+psize/2);
      //flip axis
      temp=y0;
      y0=x0;
      x0=temp;
      x=x0;
      y=y0;
      maxiter=det;
      //interior check
      //small circle
      if (x+1)*(x+1)+y*y<0.0625 then
        iter=maxiter
      end;
      //big cardioide
      d=256*(x*x+y*y)*(x*x+y*y)-96*(x*x+y*y)+32*x-3;
      if d<=0 then
        iter=maxiter
      end;
      //interior check finished
      //escape time algorithm
      while (x*x+y*y<4) and (iter<maxiter) do
        xt=x*x-y*y+x0;
        yt=2*x*y+y0;
        x=xt;
        y=yt;
        iter++
      end;
      //coloring routine
      if iter=maxiter then
        color=0;
        colbase=0
      else
        colbase=(iter+(m.log(m.log(2))-m.log(m.log(m.sqrt(x*x+y*y))))/m.log(2));
        if colbase>0 then
          color=cola[(colbase*colc)%(255*6)]
        else
          color=0;
          colbase=0
        end
      end;
      g.pen(color);
      g.brush(color);
      g.rect(i, j, psize, psize);
      g.show();
      // save result
      ..mbca[i][j] = colbase;
    end
  end
end

function colorize(colc, psize)
  for j=0 to ..scr[1]-1 by psize do
    for i=0 to ..scr[0]-1 by psize do
      color=..cola[(..mbca[i][j]*colc)%(255*6)];
      g.pen(color);
      g.brush(color);
      g.rect(i, j, psize, psize);
      g.show()
    end
  end;
  while ui.cmd(0) # null do end;
  ..calcs++
end

function calcmandel(sx, ex, sy, ey, colc)
  for i=16 to 0 by -4 do
    if i = 0 then
      mandelbrot(sx, ex, sy, ey, ..detail, ..cola, colc, 1);
      ..acuteness=1
    else
      mandelbrot(sx, ex, sy, ey, ..detail, ..cola, colc, i);
      ..acuteness=i
    end;
    if ui.cmd(100)=ui.gokey then break end
  end;
  while ui.cmd(0) # null do end;
  ..calcs++
end;

function restoreframe()
  if ..sf[0] # null then
    g.put(..fx, ..fy, ..sf[0]);
    g.put(..fx, ..fy, ..sf[1]);
    g.put(..fx+..fw-1, ..fy, ..sf[2]);
    g.put(..fx, ..fy+..fh-1, ..sf[3])
  end; 
end;

function drawframe(zoom=1)
  ..fw=(..scr[0]/..zooms)*(..zooms-zoom);
  ..fx=..fox+(..scr[0]-..fw)/2;
  ..fh=(..scr[1]/..zooms)*(..zooms-zoom);
  ..fy=..foy+(..scr[1]-..fh)/2;
  ..sf[0]=g.get(..fx, ..fy, ..fw, 1);        //  -
  ..sf[1]=g.get(..fx, ..fy, 1, ..fh);        // |
  ..sf[2]=g.get(..fx+..fw-1, ..fy, 1, ..fh); //   |
  ..sf[3]=g.get(..fx, ..fy+..fh-1, ..fw, 1); //  _
  g.brush(false);
  if ..greenframe then
    g.pen(g.green)
  else
    g.pen(g.black)
  end;
  g.rect(..fx, ..fy, ..fw, ..fh);
  g.show()
end

function colors(cs=1) // color array
  cola=a.create(255*6, 0);
  ind=0;
  col=0;
  for b=1 to 255 by cs do
    col+=1;
    cola[ind]=col;
    ind++
  end;
  for g=1 to 255 by cs do
    col+=256;
    cola[ind]=col;
    ind++
  end;
  for r=1 to 255 by cs do
    col+=65536;
    cola[ind]=col;
    ind++
  end;
  for b=255 to 1 by -cs do
    col-=1;
    cola[ind]=col;
    ind++
  end;
  for g=255 to 1 by -cs do
    col-=256;
    cola[ind]=col;
    ind++
  end;
  for r=255 to 1 by -cs do
    col-=65536;
    cola[ind]=col;
    ind++
  end;
  return cola
end;

// ---------------
// prep screen log
cls();
print "mandelbrotset start:";
print date();
print "==========================";
print 'by airbit AG';

ui.keys(false);
calcmandel(sx, ex, sy, ey, colc);
drawframe(zoom);
//main loop
do
  cmd=ui.cmd(10);
  if isnum(cmd) then
    restoreframe();
    case char(cmd)
      in "3" : if zoom < zooms-1 then zoom++ end;
      in "1" : if zoom > 1 then zoom-- end;
      in "7" :
        colc=colc*2;
        colorize(colc, acuteness)
      in "9" :
        colc=colc/2;
        colorize(colc, acuteness)
      in "0" :
        g.save(fname+calcs+".jpg");
        audio.beep()
      in "*" : greenframe = not greenframe
    end;
    if cmd = ui.rightkey or char(cmd) = "6" then
      if fx+fw < scr[0] then fox+=2 end;
    elsif cmd = ui.leftkey or char(cmd) = "4" then
      if fx > 0 then fox-=grid end;
    elsif cmd = ui.upkey or char(cmd) = "2" then
      if fy > 0 then foy-=grid end;
    elsif cmd = ui.downkey or char(cmd) = "8" then
      if fy+fh < scr[1] then foy+=grid end;
    elsif cmd = ui.gokey or char(cmd) = "5" then
      // calculate mapping for zoom
      ext=ex;
      sxt=sx;
      eyt=ey;
      syt=sy;
      ex=ext-(ext-sxt)/scr[0]*(fx);
      ey=eyt-(eyt-syt)/scr[1]*fy;
      sx=ext-(ext-sxt)/scr[0]*(fx+fw);
      sy=eyt-(eyt-syt)/scr[1]*(fy+fh);
      calcmandel(sx, ex, sy, ey, colc)
    end;
    drawframe(zoom);
  end;
until isnum(cmd) and char(cmd) = "#";
g.hide();
print "mandelbrotset stopped:";
print date();
print calcs, "fractal(s) computed"
