############################################################################
#
#	File:     sym4mm.icn
#
#	Subject:  Program to draw symmetrically
#
#	Author:   Ralph E. Griswold
#
#	Date:     May 2, 2001
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  This program draws with the eight symmetries of the square - 4mm
#  symmetry.
#
#  It is based on a simple drawing program by Gregg Townsend.
#
#  Pressing the left mouse button draws a point.  Dragging with the left mouse
#  button depressed draws a line.  Pressing and dragging with the middle mouse
#  depressed shows a dashed straight line, which is drawn solid when
#  the middle mouse button is released.  Dragging with the right mouse
#  button depressed erases in the vicinity of the mouse pointer.
#
#  Typing "f" toggles restriction of drawing to the "generating region"
#  which is shaded when drawing is restricted.
#
#  Typing "g" toggles the grid lines.
#
#  Typing "p" toggles the background in the generating region.
#
#  Typing "c" clears the window.
#
#  Typing "s" takes a snapshot, writing a GIF file.  File names begin with
#  a prefix, followed by three digits starting at 000 and increasing, and
#  terminated by .GIF.
#
#  Typing "q" terminates the session.
#
#  Grid lines and shading are only provided for servers that support mutable
#  colors.
#
#  The options supported are:
#
#	-w i	width of the window, default 512
#	-h i	height of the window, default 512
#	-s i	size of square, default 512x512; supersedes -w and -h
#	-p s	prefix for image files, default "sym"
#
#  Note:  Although the window does not have to be square, the application is
#  designed to work with a square window.
#
############################################################################
#
#  Requires:  Graphics
#
############################################################################
#
#  Links:  options, xio
#
############################################################################

link options
link xio

procedure main(args)
   local x, y, opts, number, w, h, prefix, curr, alt, restrict, xc, yc, grid
   local xd, yd, nonrestrict, palt, pattern, pcurr, x1, y1, x2, y2, delta

   opts := options(args, "w+h+s+p:")

   number := -1

   w := \opts["w"] | 512
   h := \opts["h"] | 512
   w := h := \opts["s"]
   prefix := \opts["p"] | "sym"

   restrict := 1			# initially restricted
   nonrestrict := &null

   WOpen("size=" || w || "," || h) | stop("*** cannot open window")

   xc := w / 2
   yc := h / 2

   w -:= 1			# adjustment for 0-origin indexing
   h -:= 1

   curr := "light blue"
   pcurr := "pink"
   alt := "white"
   palt := "white"

   Pattern("2,#01")

   if grid := NewColor(curr) then {
      drawgrid(w, h, grid)
      }

   if pattern := NewColor(pcurr) then {
      shade(w, h, pattern)
      }

   repeat case Event() of {
      "f": {
          restrict :=: nonrestrict
          Color(\pattern, pcurr :=: palt)
          }
      "q": {
         exit()
         }
      "c":  {
         EraseArea()
         if \grid then {
            drawgrid(w, h, grid)
            shade(w, h, pattern)
            }
         }
      "s":  {
         Color(\grid, "white")
         Color(\pattern, "white")
         WriteImage(prefix || right(number +:= 1, 3, 0) || ".gif")
         Color(\grid, curr)
         Color(\pattern, pcurr)
         }
      "g":  {
         Color(\grid, curr :=: alt)
         }
      "p":  {
         Color(\pattern, pcurr :=: palt)
         }
      &lpress: {
         if \restrict & ((real(&x) / (&y + 0.0001) < 1.0) | (&x > xc) |
            (&y > yc)) then next
         every DrawPoint(&x | (w - &x), &y | (h - &y))
         every DrawPoint(&y | (w - &y), &x | (h - &x))
         x := &x
         y := &y
         }
      &ldrag: {
         if \x then {			# just in case (for artificial events)
            if \restrict & ((real(x) / (y + 0.0001)  < 1.0) | (x > xc) |
               (y > yc)) then next
            DrawLine(x, y, &x, &y)
            DrawLine(w - x, y, w - &x, &y)
            DrawLine(x, h - y, &x, h - &y)
            DrawLine(w - x, h - y, w - &x, h - &y)
            DrawLine(y, x, &y, &x)
            DrawLine(w - y, x, w - &y, &x)
            DrawLine(y, h - x, &y, h - &x)
            DrawLine(w - y, h - x, w - &y, h - &x)
            }
         x := &x
         y := &y
         }
      &lrelease: {
         x := y := &null
         }
      &mpress: {
         x1 := xd := &x
         y1 := yd := &y
         WAttrib("linestyle=dashed")
         WAttrib("drawop=reverse")
         DrawLine(x1, y1, xd, yd)		# start trace line
         }
      &mdrag: {
         DrawLine(x1, y1, xd, yd)		# erase current trace line
         xd := &x
         yd := &y
         DrawLine(x1, y1, xd, yd)		# draw new trace line
         }
      &mrelease: {
         DrawLine(x1, y1, xd, yd)		# erase trace line
         WAttrib("drawop=copy")
         WAttrib("linestyle=solid")
         x2 := &x
         y2 := &y
         if \restrict then {			# adjust end points
            if ((x1 > xc) & (x2 > xc)) | ((y1 > yc) & (y2 > yc)) then next
            if x2 > x1 then {
               x1 :=: x2
               y1 :=: y2
               }
            if x1 > xc then {
               y1 := y2 + ((xc - x2) * (y1 - y2)) / (x1 - x2)
               x1 := xc
               }
            if y2 > yc then {
               x2 := x1 - ((x1 - x2) * (y1 - yc)) / (y1 - y2)
               y2 := yc
               }
            if y1 > y2 then {
               y1 :=: y2
               x1 :=: x2
               }
            if y1 > x1 then next
            if y2 > x2 then {
               delta := real(x2 - x1) / (y2 - y1)
               x2 := (x1 - y1 * delta) / (1 - delta)
               y2 := x2
               }
            }
         DrawLine(x1, y1, x2, y2)
         DrawLine(w - x1, y1, w - x2, y2)
         DrawLine(x1, h - y1, x2, h - y2)
         DrawLine(w - x1, h - y1, w - x2, h - y2)
         DrawLine(y1, x1, y2, x2)
         DrawLine(w - y1, x1, w - y2, x2)
         DrawLine(y1, h - x1, y2, h - x2)
         DrawLine(w - y1, h - x1, w - y2, h - x2)
         x := &x
         y := &y
         }
      &rpress | &rdrag: {
         every EraseArea((&x - 2) | (w - &x - 2),
            (&y - 2) | (h - &y - 2), 5, 5)
         every EraseArea((&y - 2) | (w - &y - 2),
            (&x - 2) | (h - &x - 2), 5, 5)
         }
      }
end

procedure drawgrid(w, h, grid)

   Fg(grid)
   DrawLine(0, 0, w, h)
   DrawLine(w, 0, 0, h)
   DrawLine(0, h / 2, w, h / 2)
   DrawLine(w / 2, 0, w / 2, h)
   Fg("bleck")

   return

end

procedure shade(w, h, pattern)

   Fg(pattern)
   WAttrib("fillstyle=textured")
   FillPolygon(1, 0, w / 2, 1, w / 2, h / 2, 1, 0)
   WAttrib("fillstyle=solid")
   Fg("black")

   return

end
