############################################################################
#
#	File:     tgdemo.icn
#
#	Subject:  Program to demonstrate turtle graphics
#
#	Author:   Gregg M. Townsend
#
#	Date:     May 31, 1994
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#     tgdemo displays various random designs in a window using the
#  turtle graphics library procedures.  Click in the window, or
#  enter a character on the keyboard, to start a new design.
#
#     The following keyboard characters have meaning:
#
#	w or W:			random walk
#	b or B:			fractal bush (looks like "desert broom")
#	s or S:			spiral design
#	p or P:			polygon design
#	t or T:			rectangular tiling
#	r or R:			radial tiling
#
#	\n, \r, \t, or SP:	choose design randomly
#	q or Q:			exit program
#
#	0:			pause drawing
#	1, ... 9:		set speed of drawing (9 is fastest)
#
############################################################################
#
#  Requires:  Version 9 graphics
#
############################################################################
#
#  Links: options, optwindw, turtle, random, graphics
#
############################################################################

link options
link optwindw
link turtle
link random
link graphics

global msec	# delay between drawing actions
global event	# interrupting event, if any

procedure main(args)
   local opts, dlist, p, e

   opts := options(args, winoptions())
   /opts["W"] := /opts["H"] := 500
   &window := optwindow(opts)

   randomize()
   dlist := [walk, bush, poly, spiral, tile, radial]
   msec := 0
   event := "\r"
   repeat {
      e := \event | Event()
      event := &null
      case e of {
         QuitEvents():				break
         "\n" | "\r" | "\t" | " ":		run(?dlist)
         &lrelease | &mrelease | &rrelease:	run(?dlist)
         "b" | "B":				run(bush)
         "w" | "W":				run(walk)
         "s" | "S":				run(spiral)
         "p" | "P":				run(poly)
         "t" | "T":				run(tile)
         "r" | "R":				run(radial)
         "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9": setdelay(e)
         }
      }
end

#  run(p) -- execute procedure p after resetting screen environment

procedure run(p)
   TReset()
   return p()
end

#  continue() -- delay and check for interrupts
#
#  Every demo should call this periodically and should exit if it fails.
#  The global "event" is set to the interrupting event and can be checked
#  to exit from recursive calls.

procedure continue()
   local evlist

   event := &null
   delay(msec)
   if *Pending() = 0 then
      return
   event := Event()
   if setdelay(event) then {
      event := &null
      return
      }
   else
      fail
end

#  setdelay(e) -- handle delay-setting event, or fail

procedure setdelay(e)
   while e === "0" do		# 0 is pause -- wait until anything else input
      e := Event()
   if type(e) == "string" & *e = 1 & (e ? any(&digits)) then {
      if e === "9" then
         msec := 0
      else
         msec := ishift(1, 12 - e)
      return
      }
   else
      fail
end


#################### drawing routines ####################


procedure walk()				# random walk
   local stepsize, maxturn, bias

   maxturn := 30
   bias := 1
   while continue() do
      every 1 to 10 do {
         TDraw(1)
         TRight(?maxturn - maxturn/2.0 + bias)
         }
end


procedure bush(n, len)				# fractal bush
   local maxturn

   if /n then {
      TSkip(-150)
      n := 4 + ?4
      len := 400 / n
      }
   maxturn := 60
   TSave()
   TRight(?maxturn - maxturn / 2.0)
   TDraw(?len)
   if n > 0 & /event then {
      continue()
      every 1 to ?4 do
         bush(n - 1, len)
      }
   TRestore()
end


procedure poly()				# regular nonconvex polygon
   local angle, side, x0, y0
   angle := 60 + ?119
   side := 200 - 100 * cos(dtor(angle))
   x0 := WAttrib("width") / 2 - side / 2
   y0 := WAttrib("height") / 2 - side / 3
   TGoto(x0, y0, 0)
   while continue() do {
      TDraw(side)
      TRight(angle)
      if abs(TX() - x0) + abs(TY() - y0) < 1 then break
      }
end


procedure spiral()				# polygon spiral
   local angle, side, incr
   angle := 30 + ?149
   incr := sqrt(4 * ?0) + 0.3
   side := 0
   while side < 1000 & continue() do {
      TDraw(side +:= incr)
      TRight(angle)
      }
end


procedure tile()
   local i, j, n, x0, y0, x, y, dx, dy, f

   n := 5
   x0 := WAttrib("width") / 2
   y0 := WAttrib("height") / 2
   dx := x0 / n
   dy := y0 / n
   f := mkfig(?10)
   x := dx / 2
   TScale(dx + dy)
   every i := 1 to n do {
      y := dy / 2
      every j := 1 to n do {
         THeading(45)
         TGoto(x0 + x, y0 + y);  every 1 to 4 do { putfig(f); TRight(90) }
         TGoto(x0 + x, y0 - y);  every 1 to 4 do { putfig(f); TRight(90) }
         TGoto(x0 - x, y0 + y);  every 1 to 4 do { putfig(f); TRight(90) }
         TGoto(x0 - x, y0 - y);  every 1 to 4 do { putfig(f); TRight(90) }
         y +:= dy
         if not continue() then
            return
         }
      x +:= dx
      }
end


procedure radial()
   local f, i, j, nrings, rwidth, fwd, circ, nfig, da

   f := mkfig(?8)
   nrings := 5
   rwidth := WAttrib("width") / (2 * nrings)
   TScale(rwidth)
   every i := 1 to nrings do {
      circ := &pi * 2 * i * rwidth
      nfig := integer(circ / 50)
      nfig := nfig / 2 + ?nfig
      da := 360.0 / nfig
      every j := 0 to nfig-1 do {
         TGoto(, , 90 - j * da)
         TSkip(i - 0.9)
         putfig(f)
         if not continue() then
            return
         }
      }
end


procedure mkfig(nseg)
   local f
   f := []
   every 1 to nseg do {
      put(f, ?0 / nseg)		# draw
      put(f, -90 + 180 * ?0)	# turn
      }
   return f
end

procedure putfig(f)
   local i
   TSave()
   every i := 1 to *f by 2 do {
      TDraw(f[i])
      TRight(f[i+1])
      }
   TRestore()
end
