#!/usr/local/bin/wish -f
#
#  /*-----------------------------------------------------------------------*\
#  |                                                                         |
#  |                  Robotics 95  --  Final Course Project                  |
#  |                                                                         |
#  |                    By :  Omri Weisman & Ziv Pollack                     |
#  |                                                                         |
#  |               NNUGA - Neural Network Using Genetic Algorithms           |
#  |                                                                         |
#  \*-----------------------------------------------------------------------*/
#  
#   File name : nnuga
#
#   NNUGA is a neural network which can learn to classify a set of x:y vectors
#   by dividing the plain into regions. The learning method is an evolutionary
#   algorithms.
#
#   When the window opens, you can create positive points on the board by clicking
#   the left mouse button, and negative points by clicking the right mouse button.
#   When you're finished placing points, you can click on the "Start" button to
#   allow the network to try and learn your input points, generalize its knowledge,
#   and show you the result by drawing the line on the board. The line represents the
#   border which seperates the plane into regions - regions where the NN's output
#   will be True and regions where the NN's output will be False.
#
#   This file is the Tk interface of the program. It uses a program written in C
#   to perform the actual NN learning, and acts as its GUI.
#


#   Constants
#
set LIMIT 150       ;#  Limit of points allowed


#   Main Window
#
wm title . "NNUGA"
wm minsize . 560 550
#option add *activeBackground  green

  
#   Headers
#  
frame .header1 -borderwidth 5; pack .header1
label .header1.l -text "NNUGA - Neural Network Using Genetic Algorithms" ; pack .header1.l
frame .header2 -borderwidth 5 ; pack .header2
label .header2.l -text "By Omri Weisman and Ziv Pollack" ; pack .header2.l


#   Buttons
#
frame  .buttons -borderwidth 15 ; pack .buttons -side bottom -fill x
button .buttons.quit -text "  Quit  " -command exit
button .buttons.start -text "  Start  " -command Start
button .buttons.clear -text "  Clear  " -command Clear
entry  .buttons.mousepos -width 7 -bd 2 -relief raised -textvariable mousepos 
entry  .buttons.statusbar -width 30 -bd 2 -relief raised -textvariable status
pack   .buttons.start .buttons.clear .buttons.statusbar .buttons.mousepos -side left
pack   .buttons.quit -side right


#   The Clipboard
#   This is a frame that holds the board and the rulers
#
frame .clip ; pack .clip


#   The Board
#   This is the board upon which the points are drawn
#
canvas .clip.board -width 360 -height 360 -bd 5 -relief raised -bg white
pack .clip.board -anchor e
bind .clip.board <Any-Motion>  {set mousepos "%x %y"}   ;#  Show mouse position when moved
bind .clip.board <ButtonPress-1> {Button1 %x %y}        ;#  Create positive point
bind .clip.board <ButtonPress-3> {Button3 %x %y}        ;#  Create negative point
bind .clip.board <Leave>  {set mousepos ""}


#   Vertical Ruler
#   Just a graphic feature
#
canvas .clip.c1 -width 2.5c -height 11.5c
pack .clip.c1 -side left -before .clip.board
.clip.c1 create line 1.5c 0c 1c 0c 1c 10c 1.5c 10c
for {set i 0} {$i < 10} {incr i} {
  set x [expr $i+0]
  .clip.c1 create line 1c ${x}c 1.4c ${x}c
  .clip.c1 create line 1c $x.25c 1.2c $x.25c
  .clip.c1 create line 1c $x.5c 1.3c $x.5c
  .clip.c1 create line 1c $x.75c 1.2c $x.75c
  .clip.c1 create text 1.4c $x.4c -text [expr 35*$i] -anchor sw
}


#   vertical frame on right side,
#   to balance with the ruler on the left
#
frame .clip.c3 -width 2.5c -height 11.5c
pack .clip.c3 -side right -before .clip.board
  

#   Horizental Ruler
#   Another graphic feature
#
canvas .clip.c2 -width 10.1c -height 1.5c
pack .clip.c2 -side bottom -after .clip.board
.clip.c2 create line 0c 0.5c 0c 1c 10c 1c 10c 0.5c
for {set i 0} {$i < 10} {incr i} {
  set x [expr $i+0]
  .clip.c2 create line ${x}c 1c ${x}c 0.6c
  .clip.c2 create line $x.25c 1c $x.25c 0.8c
  .clip.c2 create line $x.5c 1c $x.5c 0.7c
  .clip.c2 create line $x.75c 1c $x.75c 0.8c
  .clip.c2 create text $x.01c .5c -text [expr 35*$i] -anchor sw
}


#   Global Variables
#
set list1 ""      ;#  List of points for which the result should be True
set list2 ""      ;#  List of points for which the result should be False
set mark_list ""  ;#  List of graphic marks that should be deleted when "Clear" is requested
set line_list ""  ;#  List of seperator lines that are created after the NN has learnt
set counter 0     ;#  Counts how many points are there


#   Procedure Start
#   This procedure starts the calculation. It saves the points to a temporary
#   file, calls upon the helper program to perform the actual learning and
#   receives its output from the standard output. If the result is 4 numbers,
#   they are interpreted as 2 x-y pairs and a line is drawn, otherwise it is
#   assumed that some problem occured.
#
proc Start {} {
  global list1 list2 line_list
  say "Starting..."
  set fileID [open /tmp/nn-input w 0644]        ;#  Write data into file
  puts $fileID [expr [llength $list1]/2]
  foreach item $list1 { puts $fileID $item }
  puts $fileID [expr [llength $list2]/2]
  foreach item $list2 { puts $fileID $item }
  close $fileID
  foreach item $line_list { .clip.board delete $item }  ;# Delete old lines
  catch {exec nnuga.out >@ stdout} result               ;# Execute the C program
  set fileID [open /tmp/nn-output r]                    ;# Open result file
  gets $fileID result
  close $fileID
  set length [llength $result]
  for {set i 0} {$i < $length } {incr i 2} {
    lappend line_list [.clip.board create line\
                         [lindex $result $i]\
                         [lindex $result [expr $i+1]]\
                         [expr [lindex $result $i]+1]\
                         [expr [lindex $result [expr $i+1]]+1]]
    }
  say "Finished"
  }


#   Procedure Button1
#   This procedure creates a new positive point at the location x,y when the
#   left mouse button is clicked. It also draws a '+' there.
#
proc Button1 {x y} {
  global mark_list list1 counter LIMIT
  if { $counter >= $LIMIT } {
    say "No more points allowed"
  } else {
    set line1 [.clip.board create line [expr $x-3] $y [expr $x+4] $y]
    set line2 [.clip.board create line $x [expr $y-3] $x [expr $y+4]]
    lappend mark_list $line1 $line2
    lappend list1 $x $y
    incr counter 1
    say "Created Positive input at $x:$y"
    }
  }


#   Procedure Button3
#   This procedure creates a new negative point at the location x,y when the
#   right mouse button is clicked. It also draws a '-' there.
#
proc Button3 {x y} {
  global mark_list list2 counter LIMIT
  if { $counter >= $LIMIT } {
    say "No more points allowed"
  } else {
    set line1 [.clip.board create line [expr $x-3] $y [expr $x+4] $y]
    lappend mark_list $line1
    lappend list2 $x $y
    incr counter 1
    say "Created Negative input at $x:$y"
    }
  }


#   Procedure Clear
#   Clear all marks from the board and reset variables
#
proc Clear {} {
  global list1 list2 mark_list line_list counter
  foreach item [concat $mark_list $line_list] { .clip.board delete $item }
  set list1 ""
  set list2 ""
  set mark_list ""
  set counter 0
  say ""
  }


#    Procedure Say
#    print a message on the status bar.
#
proc say {message} {
  global status
  set status $message
  }
