# 1b0c963e54aa27a907e1b9fcec15cdb6c212ec77eb9b68e94a1a8f5bb49cea22 Elan 2.0.0-alpha4 valid

# After image is displayed, press: 
# - z to zoom in, x to zoom out
# - arrow keys to pan
# - g, j and y, h to change shape
# 
# Acknowledgements:
# https://mathworld.wolfram.com/JuliaSet.html
# Implemented in Elan by Charles Wicksteed
main
  variable p set to new Coords()
  while true
    variable vg set to allpoints(p)
    call displayVectorGraphics(vg)
    call p.checkkeys()
    call print($"x = {p.jx} y = {p.jy}")
  end while
end main

function allpoints(p as Coords) returns List<of VectorGraphic>
  variable vg2 set to new List<of VectorGraphic>()
  for xp in range(0, width + 1)
    for yp in range(0, height + 1)
      # scale and centre
      variable n set to onepoint(divAsFloat(divAsFloat(xp - width, 2), p.scale - p.xoff), divAsFloat(divAsFloat(yp - height, 2), p.scale - p.yoff), nmax, p)
      # colour depends on how many iterations were done for that point
      variable col set to if(n is nmax, 0xffffff, ((n*0x010201) mod 0xffffff))
      variable rect set to (new RectangleVG()).withX(divAsFloat(xp, 2)).withY(divAsFloat(yp, 2)).withWidth(0.5).withHeight(0.5).withFillColour(col).withStrokeWidth(0.25)
      set vg2 to vg2.withAppend(rect)
    end for
  end for
  return vg2
end function

function onepoint(x as Float, y as Float, maxnum as Int, p as Coords) returns Int
  variable done set to false
  variable a set to x
  variable b set to y
  variable i set to 0
  while not done
    variable c set to 2*a*b
    set a to (a*a - b*b) + p.jx
    set b to c + p.jy
    set i to i + 1
    if (i >= maxnum) or ((a*a + b*b) > 4) then
      set done to true
    end if
  end while
  return i
end function

class Coords
  constructor()
    # number of cells per unit distance on complex plane
    set this.scale to 100
    # centered on the screen to start
    set this.xoff to 0
    set this.yoff to 0
    # Julia set parameters
    set this.jx to -0.512
    set this.jy to 0.521
  end constructor

  function toString() returns String
    return ""
  end function

  property scale as Float

  property xoff as Float

  property yoff as Float

  property jx as Float

  property jy as Float

  # Arrow keys move the virtual camera
  # eg Arrow Up moves the image down
  procedure checkkeys()
    variable panstep set to 10/this.scale
    variable jstep set to 0.001
    # save some CPU by not recalculating until a parameter has been changed
    variable changed set to false
    while not changed
      variable k set to getKey()
      # loop because more than one key may have been pressed
      while not k.equals("")
        if k.equals("z") then
          set this.scale to this.scale*1.2
        elif k.equals("x") then
          set this.scale to this.scale/1.2
        elif k.equals("ArrowUp") then
          set this.yoff to this.yoff + panstep
        elif k.equals("ArrowDown") then
          set this.yoff to this.yoff - panstep
        elif k.equals("ArrowLeft") then
          set this.xoff to this.xoff + panstep
        elif k.equals("ArrowRight") then
          set this.xoff to this.xoff - panstep
        elif k.equals("g") then
          set this.jx to this.jx + jstep
        elif k.equals("j") then
          set this.jx to this.jx - jstep
        elif k.equals("y") then
          set this.jy to this.jy + jstep
        elif k.equals("h") then
          set this.jy to this.jy - jstep
          # for autocomplete in the RHS expression, don't type "property"
        else
          # ignore erroneous key presses
        end if
        # there is no harm in recalculating even if an invalid key was pressed
        set changed to true
        # another key may have been pressed
        set k to getKey()
      end while
      call sleep_ms(10)
    end while
  end procedure

end class

test test_one
  variable p set to new Coords()
  assert onepoint(0, 0, 100, p) is 100
  assert onepoint(0.5, 0.5, 100, p) is 3
end test

constant width set to 200

constant height set to 150

constant nmax set to 360
