using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Drawing.Drawing2D;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Threading;
namespace MouseGesture
{
#region MainBoard class
//This class is the panel where every gesture will be drawn
public class MainBoard : System.Windows.Forms.Panel
{
#region Private members
private ArrayList m_Gestures=new ArrayList(); //This array will contain every gesture
private MouseState m_MouseState=MouseState.Waiting; //Identifies the mouse state
private BoardState m_BoardState=BoardState.None; //Identifies the board state
private Pen m_Pen; //This object will be used to draw on the board
private Color m_BoardColor; //Board Background color
private int m_PatternsToCreate; //This member stores the value of patterns to create during the training set creation
private ArrayList m_TrainingSet=new ArrayList(); //Stores the manual generated training set
private int m_ActualPattern; //This member is used to store how many gestures have been manually created until now
private float m_CurrentMaxError; //During the training, this member stores the current MaxError generated by the Neural Net
private ArrayList m_Points = new ArrayList(); //This array stores the coords of every point of the gesture drawn
private ArrayList m_Errors=new ArrayList(); //Stores every error generated during the training phase by a neural net
private double m_MinError=1d; //Min error value
private double m_PeakError=0d; //Max error value
#endregion
#region Public properties
public Color BoardColor{get{return this.m_BoardColor;} set{this.m_BoardColor=(Color)value; this.Invalidate();}}
public BoardState BState{get{return this.m_BoardState;}}
public ArrayList KnownGestures{get{return this.m_Gestures;}}
#endregion
#region Events and delegates
//This event is invoked at the beginning of gesture creation phase
public delegate void BeginCreatingDelegate();
public event BeginCreatingDelegate BeginCreating;
//This event is invoked at the beginning of training phase
public delegate void BeginTrainingDelegate();
public event BeginTrainingDelegate BeginTraining;
//This event is invoked at the end of gesture creation phase
public delegate void EndCreatingDelegate();
public event EndCreatingDelegate EndCreating;
//This event is invoked at the end of training phase
public delegate void EndTrainingDelegate();
public event EndTrainingDelegate EndTraining;
#endregion
#region Static variables
public static int GestureMinimumPoints{get{return 15;}} //Min value of gesture points to be used or recognized
public static int GestureMaximumPoints{get{return 100;}} //Max value for gesture points (it's important because is used to determinate how many features can be used)
public static int MaximumFeatures{get{return 14;}} //Number of features used as inputs by neural nets (MUST be less or equal than GestureMaximumPoints)
#endregion
#region Visual objects
private TransparentPanel m_ActualGesture;
public TransparentPanel ActualGesture{get{return this.m_ActualGesture;}}
private TransparentPanel m_RecognizedGesture;
public TransparentPanel RecognizedGesture{get{return this.m_RecognizedGesture;}}
private TransparentLabel m_Message;
public TransparentLabel MessageBar{get{return this.m_Message;}}
private TransparentLabel m_Infos;
public TransparentLabel InfoPanel{get{return this.m_Infos;}}
private GesturesManagementForm m_ManagementDialog;
private CreateTrainingSetForm m_CreateTrainingSetForm;
#endregion
#region Contructor
public MainBoard()
{
this.m_BoardColor=Color.White;
this.m_Pen = new Pen(Color.Red,1);
this.m_GridPen = new Pen(Color.LightGray,1);
this.m_GridSpaces = new GridSpaces(15,15);
this.m_BoardState=BoardState.None;
this.m_PointsColor=Color.Blue;
this.InitializeComponents();
this.LinkHandlers();
this.SetStyle(ControlStyles.AllPaintingInWmPaint|ControlStyles.UserPaint|ControlStyles.DoubleBuffer,true);
this.LoadSettings();
}
#endregion
#region Board appearence
//These functions are used to customize the appearence of the board
#region Pen Get/Set thickness & color
public void SetPenColor(Color color)
{
this.m_Pen.Color=color;
this.Invalidate();
}
public Color GetPenColor()
{
return this.m_Pen.Color;
}
public void SetPenThickness(float thickness)
{
this.m_Pen.Width=thickness;
this.Invalidate();
}
public double GetPenThickness()
{
return this.m_Pen.Width;
}
#endregion
#region Points color
private Color m_PointsColor;
public Color PointsColor{get{return this.m_PointsColor;} set{this.m_PointsColor=value; this.Invalidate();}}
#endregion
#region GridPen Get/Set thickness & color
private Pen m_GridPen;
public void SetGridPenColor(Color color)
{
this.m_GridPen.Color=color;
this.Invalidate();
}
public Color GetGridPenColor()
{
return this.m_GridPen.Color;
}
public void SetGridPenThickness(float thickness)
{
this.m_GridPen.Width=thickness;
this.Invalidate();
}
public double GetGridPenThickness()
{
return this.m_GridPen.Width;
}
#endregion
#region Grid Spaces Get/Set
//These function are used to get/set spaces between two lines of the grid
private GridSpaces m_GridSpaces;
public int GetGridVerticalSpace()
{
return this.m_GridSpaces.Height;
}
public int GetGridHorizontalSpace()
{
return this.m_GridSpaces.Width;
}
public int GridVerticalSpace{set{this.m_GridSpaces.Height=value;}}
public int GridHorizontalSpace{set{this.m_GridSpaces.Width=value;}}
public void SetGridSpaces(int width, int height)
{
this.m_GridSpaces.Width=width;
this.m_GridSpaces.Height=height;
this.Invalidate();
}
#endregion
#endregion
#region Create training set
//This function is invoked by outside (another form) to start the manual training set creation
public void CreateTrainingSet(int tot, CreateTrainingSetForm form)
{
this.ClearActualGesturePanel();
this.Parent.Enabled=true;
this.m_ActualPattern=1;
this.m_PatternsToCreate=tot;
this.m_CreateTrainingSetForm=form;
this.m_Infos.ShowMessage("Gesture 1/"+tot,MessageType.Normal);
this.m_Message.ShowMessage("Draw a gesture for the training set",MessageType.Info);
this.m_BoardState=BoardState.TSCreating;
this.m_TrainingSet=new ArrayList();
}
#endregion
#region Manage new mouse gesture
//This function is invoked by outside (another form) to start the manual gesture creation
public void CreateGesture()
{
this.m_Message.ShowMessage("Draw a new mouse gesture",MessageType.Info);
this.m_BoardState=BoardState.Recording;
if(this.BeginCreating!=null)
this.BeginCreating();
}
//This function is used to add a new gesture to the gesture array
public bool AddNewGesture(string Name, ArrayList Points)
{
//Purging the name (avoiding bad or indesidered chars)
string GestureName=Parser.PurgeString(Name);
//Check to see if purge has erased all the chars
if(GestureName.Length>0)
{
//Gesture creation and add
Gesture NewGesture=new Gesture(Name,"./Gestures/"+Name+".mgf",Points);
this.m_Gestures.Add(NewGesture);
NewGesture.SaveGesture(false,false);
this.m_Message.ShowMessage("New mouse gesture added",MessageType.Info);
return true;
}
else
{
//If the name is invalid, show an error message
MessageBox.Show("The name for the gesture is not valid. Please, re-enter the name to save the gesture properly.","Warning!",MessageBoxButtons.OK,MessageBoxIcon.Warning);
this.m_Message.ShowMessage("Mouse gesture name invalid",MessageType.Error);
return false;
}
}
#endregion
#region Clear Board and Panel
public void ClearAll()
{
this.m_Points=new ArrayList();
this.m_ActualGesture.ClearPanel();
this.m_RecognizedGesture.ClearPanel();
this.m_Infos.ShowMessage("");
this.Invalidate();
}
public void ClearActualGesturePanel()
{
this.m_Points=new ArrayList();
this.m_ActualGesture.ClearPanel();
this.Invalidate();
}
#endregion
#region Custom initialization
public void InitializeComponents()
{
this.m_ActualGesture=new TransparentPanel();
this.m_ActualGesture.Parent=this;
this.m_ActualGesture.SetBounds(this.Right-this.m_ActualGesture.Width-10,10,100,100);
this.m_ActualGesture.Show();
this.m_RecognizedGesture=new TransparentPanel();
this.m_RecognizedGesture.Parent=this;
this.m_RecognizedGesture.SetBounds(this.Right-this.m_ActualGesture.Width-10,130,100,100);
this.m_RecognizedGesture.Show();
this.m_Message=new TransparentLabel();
this.m_Message.Parent=this;
this.m_Message.SetBounds(0,this.Bottom-16,this.Width,16);
this.m_Message.Show();
this.m_Infos=new TransparentLabel();
this.m_Infos.Parent=this;
this.m_Infos.SetBounds(10,10,100,100);
this.m_Infos.Transparency=20;
this.m_Infos.Show();
}
#endregion
#region Insert gesture in RecognizedGesture
public void InsertRecognizedGesture(ArrayList points)
{
this.m_RecognizedGesture.InsertNewGesture(points);
}
#endregion
#region Handlers Linking
public void LinkHandlers()
{
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.MouseUpOnBoard);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(MouseMoveOnBoard);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.MouseDownOnBoard);
this.Paint += new System.Windows.Forms.PaintEventHandler(this.PaintBoard);
}
#endregion
#region Event handlers
#region Mouse Events handlers
#region MouseMove handler
//Every time the mouse moves, I need to check if his state is "recording". If so, I must store
//the current position (in 2D coords)
private void MouseMoveOnBoard(object sender,System.Windows.Forms.MouseEventArgs e)
{
if(this.m_MouseState==MouseState.Recording)
{
this.m_Points.Add(new PointF(e.X,e.Y));
this.m_Message.ShowMessage("Points: "+this.m_Points.Count,MessageType.Normal);
this.Invalidate();
}
}
#endregion
#region MouseDown handler
//If the key pressed is RightButton, and the board is not in the training phase,
//the recording gesture phase starts (every panel, or only some of them, will be
//cleared... it depends on the board state).
private void MouseDownOnBoard(object sender,System.Windows.Forms.MouseEventArgs e)
{
if(e.Button==MouseButtons.Right && this.m_BoardState!=BoardState.Training)
{
this.m_MouseState=MouseState.Recording;
if(this.m_BoardState==BoardState.TSCreating)
this.ClearActualGesturePanel();
else
this.ClearAll();
}
}
#endregion
#region MouseUp handler
//This function controls one the most important part of the board functions
//At the end of the recording phase, the right button must be released, so gesture recognizing, or
//creation must be managed here
private void MouseUpOnBoard(object sender,System.Windows.Forms.MouseEventArgs e)
{
//Check to see which button has been released
if(e.Button==MouseButtons.Right)
{
//During the training phase the board must not recive any input by user
if(this.m_BoardState!=BoardState.Training)
{
//Check to see if the mouse is in recording mode
if(this.m_MouseState==MouseState.Recording)
{
//Mouse state will be set at "Waiting", since the recording phase has been stopped
this.m_MouseState=MouseState.Waiting;
//Adding the last point of the gesture (in this way I'm sure there are at least 2 points
//the begininning point, and the ending one
this.m_Points.Add(new PointF(e.X,e.Y));
//Check to see if there are enough points
if(this.m_Points.Count<MainBoard.GestureMinimumPoints)
{
//Show a message to signal there are too few points
this.m_Message.ShowMessage("Points: "+this.m_Points.Count+" [Too few points! Please, draw the gesture again]",MessageType.Error);
if(this.m_Points.Count>1)
{
//If there are less than minimum points, but at least 2 points, draw the gesture
this.m_ActualGesture.InsertNewGesture(this.m_Points);
}
}
else
{
//If there are enough points, check to see if they are less then or more than the maximum value
if(this.m_Points.Count>MainBoard.GestureMaximumPoints)
{
//If necessary invoke the points-reduction function
this.m_Message.ShowMessage("Points: "+this.m_Points.Count+" [There are too many points. This gesture will be simplified]",MessageType.Info);
int ExcessivePoints=GestureRegularization.ReduceGesturePoints(this.m_Points);
this.m_Message.ShowMessage(this.m_Message.Text+" [Removed "+ExcessivePoints+" point"+((ExcessivePoints==1)?(""):("s"))+"]");
}
else if(this.m_Points.Count<MainBoard.GestureMaximumPoints)
{
//If necessary invoke the points-expansion function
this.m_Message.ShowMessage("Points: "+this.m_Points.Count,MessageType.Normal);
int GapPoints=GestureRegularization.ExpandGesturePoints(this.m_Points);
this.m_Message.ShowMessage(this.m_Message.Text+" [Added "+GapPoints+" point"+((GapPoints==1)?(""):("s"))+"]");
}
//Check to avoid crashed if "unfortunally" gesture reduction... reduces too much! :P (now could be removed...)
if(this.m_Points.Count>1)
this.m_ActualGesture.InsertNewGesture(this.m_Points);
//Check the board state to invoke appropriate method
if(this.m_BoardState==BoardState.Recording)
{
//If board was in creation mode, show the preview/confirm form
this.ShowGestureCreation();
}
else if(this.m_BoardState==BoardState.TSCreating)
{
//If the board was in training set creation mode, prompt a message, asking if the user accepts this gesture
if(MessageBox.Show("Do you want to save this gesture to training set?","Save?",MessageBoxButtons.YesNo,MessageBoxIcon.Question)==DialogResult.Yes)
{
//Pattern counter update
this.m_ActualPattern++;
//New pattern cretion
Pattern p=new Pattern(Gesture.ExtractFeatures(this.m_Points));
//Pattern will be added to the training set
this.m_TrainingSet.Add(p);
//If the added pattern is the last one, shows the training set creation form
if(this.m_ActualPattern==this.m_PatternsToCreate+1)
{
this.Parent.Enabled=false;
this.m_CreateTrainingSetForm.Enabled=true;
this.m_CreateTrainingSetForm.Visible=true;
//Convert from a dynamic arraylist to a normal array
Pattern[] patterns=new Pattern[this.m_PatternsToCreate];
this.m_TrainingSet.CopyTo(patterns);
//Set to "None" the board state
this.m_BoardState=BoardState.None;
//Import the training set into the training set creation form
this.m_CreateTrainingSetForm.ImportAndUseGestures(patterns);
this.ClearAll();
}
else
{
//If the pattern created is not the last one, than simply show a message o the infos and the message bar
//and clean up actual gesture panel
this.m_Infos.ShowMessage("Gesture "+this.m_ActualPattern+"/"+this.m_PatternsToCreate);
this.m_Message.ShowMessage("Draw another gesture for the training set",MessageType.Info);
this.ClearActualGesturePanel();
}
}
else
{
//If the gesture has not been accepted, user must redraw it!
this.m_Message.ShowMessage("Draw again the gesture",MessageType.Info);
this.ClearActualGesturePanel();
}
}
else if(this.m_BoardState==BoardState.Verifying)
{
//If the board is in verifying mode I have to record every output from every neural net
double[] Outputs=new double[this.m_Gestures.Count];
for(int z=0; z<this.m_Gestures.Count; z++)
{
//Get neural net output, only if it is a trained net
if(((Gesture)this.m_Gestures[z]).Trained)
Outputs[z]=((Gesture)this.m_Gestures[z]).Net.Run(Gesture.ExtractFeatures(this.m_Points));
else
Outputs[z]=-1; //If not trained, the value returned by the net is -1 (less than every possible output (they are always between 0 and 1)
}
//Extract 3 or less (it depends on the gesture number) from output vector
int[] Winners=this.GetWinners(Outputs);
//Threashold check, to see if the gesture with the highest value is recognized
bool Recognized=(Outputs[Winners[0]]>Neuron.Threashold);
string msg="";
for(int p=0; p<Winners.Length; p++)
msg+=((Gesture)this.m_Gestures[Winners[p]]).Name+": "+((float)Outputs[Winners[p]])+((p!=Winners.Length-1)?("\n\n"):(""));
//Show messages
this.m_Infos.ShowMessage(msg,(Recognized)?(MessageType.Info):(MessageType.Error));
this.m_Message.ShowMessage((Recognized)?("Mouse gesture recognized"):("Mouse gesture unknown"),(Recognized)?(MessageType.Info):(MessageType.Error));
//If gesture have been recognized, then show the "prototype" on the Recognized gesture panel
if(Recognized)
this.m_RecognizedGesture.InsertNewGesture(((Gesture)this.m_Gestures[Winners[0]]).Points);
}
}
//Force refresh
this.Invalidate();
}
}
}
}
#endregion
#endregion
#region Paint hanldler
private void PaintBoard(object sender,System.Windows.Forms.PaintEventArgs e)
{
//Redraw the background (there is another way, I could simply use the Background color property... :P)
e.Graphics.FillRectangle(new SolidBrush(this.m_BoardColor),0,0,this.ClientSize.Width,this.ClientSize.Height);
#region Draw Grid
//Draw grid with spaces defined previously
for(int i=1; i<=(int)(this.ClientSize.Width/this.m_GridSpaces.Width); i++)
{
e.Graphics.DrawLine(this.m_GridPen,new PointF(i*this.m_GridSpaces.Width,0),new PointF(i*this.m_GridSpaces.Width,this.ClientSize.Height));
}
for(int j=1; j<=(int)(this.ClientSize.Height/this.m_GridSpaces.Height); j++)
{
e.Graphics.DrawLine(this.m_GridPen,new PointF(0,j*this.m_GridSpaces.Height),new PointF(this.ClientSize.Width,j*this.m_GridSpaces.Height));
}
#endregion
//Check the board status
if(this.m_BoardState==BoardState.Training)
{
//During the training phase, the error graph shold be drawn
//We must adjust the pass between 2 lines of the graph, to avoid the graph to be larger than
//the client size.
//To avoid this, the pass should be equals to the ratio between client width
//and number of errors... obviusly if errors are less than width, pass will be 1...
float Pass=0;
if(this.m_CurrentMaxError>(this.Width-20)) //Use a 20 pixel space to avoid the graph to be too near to the end of the form
Pass=((float)this.Width-20f)/((float)this.m_CurrentMaxError);
else
Pass=1;
//Retrieve max error among al errors, scalingscalando every height of the graph according to
//this value (so every error is inside the form)
double MaxError=this.GetMaxError();
//Graph max height
float BaseHight=this.Height-this.m_Message.Height;
//Draw a line for each error, using pass calculated before to retireve x-axis component,
//and scaling heights with MaxError
for(int h=0; h<this.m_Errors.Count; h++)
{
float y=BaseHight-(BaseHight-120)*((float)(((double)this.m_Errors[h])/(double)MaxError));
e.Graphics.DrawLine(this.m_Pen,(float) h*Pass, (float) this.Height-this.m_Message.Height, (float)h*Pass, y);
}
}
else
{
//If not in training mode, I need to show all that is drawn by user,
//only if he drew at least 2 points (needed to draw a line)
if(this.m_Points.Count>1)
{
//Convertyng dynamic array in constant array
PointF[] pts=new PointF[this.m_Points.Count];
this.m_Points.CopyTo(pts,0);
//Draw lines by DrawLines function
e.Graphics.DrawLines(this.m_Pen,pts);
//If user-drawing has ended, the program draws anchor ponints
//(points retrieved by mouse polling)
if(this.m_MouseState==MouseState.Waiting)
{
foreach (PointF p in this.m_Points)
{
//For each anchor point draw a rectangle centered in point
e.Graphics.FillRectangle(new SolidBrush(this.m_PointsColor),p.X-1f,p.Y-1f,2,2);
}
}
}
}
}
#endregion
#region ParentResize handler
//On parent resize, main board layout will be regenerated, according to new parent size
public void OnParentResize(object sender,System.EventArgs e)
{
this.Size=this.Parent.ClientSize;
this.m_ActualGesture.SetBounds(this.Right-this.m_ActualGesture.Width-10, 10,this.m_ActualGesture.Width,this.m_ActualGesture.Height);
this.m_RecognizedGesture.SetBounds(this.Right-this.m_ActualGesture.Width-10, 130,this.m_ActualGesture.Width,this.m_ActualGesture.Height);
this.m_Message.SetBounds(0,this.Bottom-this.m_Message.Height,this.Width,this.m_Message.Height);
this.m_Infos.SetBounds(10,10,100,100);
this.Invalidate(true);
}
#endregion
#endregion
#region Gesture management
#region Gestures importing
//This function set up know-gestures array
public void ImportGestures(ArrayList gestures)
{
this.m_Gestures=gestures;
}
#endregion
#region ShowMessageBar function
//The function is used to allow other form to put messages on the message bar.
//Even if panels and bars have public properties, and could be used to show messages,
//this function is another method to do that (I had to use this method before deciding to
//give public access to panels/bats for cusumizing appearence)
public void ShowMessageToBar(string message, MessageType type)
{
this.m_Message.ShowMessage(message,type);
}
#endregion
#region GesturesCreationForm functions
//This function shows the gesture creation form
private void ShowGestureCreation()
{
GestureCreationForm CreationDialog=new GestureCreationForm(this,this.m_Points);
CreationDialog.Closed+=new EventHandler(this.OnCloseGestureCreation);
this.m_Message.ShowMessage("Gesture creation: open", MessageType.Normal);
this.Parent.Enabled=false;
CreationDialog.Show();
}
//This handler is called at gesture creation form close
private void OnCloseGestureCreation(object sender, System.EventArgs e)
{
this.m_ManagementDialog.Visible=true;
this.m_ManagementDialog.Enabled=true;
this.m_ManagementDialog.RefreshGestureList();
this.m_Message.ShowMessage("Gesture creation: closed", MessageType.Normal);
if(this.EndCreating!=null)
this.EndCreating();
}
#endregion
#region GesturesManagementForm functions
//The function shows gesture management form
public void ShowGesturesManagement()
{
this.m_ManagementDialog=new GesturesManagementForm(this);
this.m_ManagementDialog.Closed+=new EventHandler(this.OnCloseGestureManagement);
this.ClearAll();
this.m_Message.ShowMessage("Gesture management: open", MessageType.Normal);
this.Invalidate();
this.m_ManagementDialog.ShowDialog();
}
//This handler is called at gesture management form close
private void OnCloseGestureManagement(object sender, System.EventArgs e)
{
this.ClearAll();
this.Parent.Enabled=true;
this.m_BoardState=BoardState.None;
this.m_Message.ShowMessage("Gesture management: closed", MessageType.Normal);
}
#endregion
#endregion
#region Manage verifying
#region Start Verifying
//This function is called at the beginning of the verifying phase
public void StartVerify()
{
this.ClearAll();
this.m_BoardState=BoardState.Verifying;
this.m_Message.ShowMessage("Draw a gesture to verify current net",MessageType.Info);
}
#endregion
#region End Verifying
//This function is called at the end of the verifying phase
public void EndVerify()
{
this.ClearAll();
this.m_BoardState=BoardState.None;
this.m_Message.ShowMessage("Verifying stopped",MessageType.Normal);
}
#endregion
#region Verifiyng result functions
//The function gets the in input the outputs of neural nets, and returns an array of
//indexes, ordered by output values, from the highest (the first) to the lowest (the last)
//(This function is awful... maybe better using quck sort and avoid final resorting... :P)
private int[] GetWinners(double[] outputs)
{
//Creating BestResults array (trying to show 3 best results, or less if
//there are few gestures)
int BestResults=(outputs.Length>=3)?(3):(outputs.Length);
int[] ret=new int[BestResults];
//Copy of neural net outputs (used to sort values)
double[] TmpOut=new double[outputs.Length];
outputs.CopyTo(TmpOut,0);
//Creating index array
int[] Indexes=new int[outputs.Length];
for(int k=0; k<Indexes.Length; k++)
Indexes[k]=k;
//Sorting by bubble sort (from lowest to highest)
for(int i=0; i<TmpOut.Length-1; i++)
{
for(int j=i; j<TmpOut.Length; j++)
{
if(TmpOut[i]>TmpOut[j])
{
double tmp=TmpOut[i];
int tmpIndex=Indexes[i];
TmpOut[i]=TmpOut[j];
Indexes[i]=Indexes[j];
TmpOut[j]=tmp;
Indexes[j]=tmpIndex;
}
}
}
//Creating output vector, ordered from highest, to lowest
for(int y=0; y<ret.Length; y++)
ret[y]=Indexes[(Indexes.Length-1)-y];
return ret;
}
#endregion
#endregion
#region Manage training
#region StartTrainingMode function
//This function starts traingin phase
public void StartTrainingMode(int maxerrors)
{
this.ClearActualGesturePanel();
this.m_CurrentMaxError=maxerrors;
this.m_BoardState=BoardState.Training;
if(this.BeginTraining!=null)
this.BeginTraining();
this.m_Infos.ShowMessage("Epoch: "+this.m_Errors.Count,MessageType.Normal);
}
#endregion
#region EndTrainingMode function
//This functions ends treaining phase
public void EndTrainingMode()
{
this.ClearAll();
this.m_BoardState=BoardState.None;
this.m_Message.ShowMessage("Training form: closed",MessageType.Normal);
if(this.EndTraining!=null)
this.EndTraining();
}
#endregion
#region OnEpochEnded handler
//Every time the neural net completes an epoch, it's needed to refresh the
//error graph
public void OnEpochEnded(TrainedEventArgs args)
{
this.m_Errors.Add(args.Error);
if(this.m_PeakError<args.Error)
this.m_PeakError=args.Error;
if(this.m_MinError>args.Error)
this.m_MinError=args.Error;
this.m_Infos.ShowMessage("Epoch: "+this.m_Errors.Count+"\n\nVal: "+((float)args.Error)+"\n\nMin: "+(float)this.m_MinError+"\n\nMax: "+(float)this.m_PeakError,MessageType.Normal);
this.Invalidate(true);
}
#endregion
#region OnGestureTrained handler
//When a gesture is trained, every structure used
//to draw the graph, must be reinitialized
public void OnGestureTrained()
{
this.m_Errors=new ArrayList();
this.m_PeakError=0d;
this.m_MinError=1d;
this.Invalidate(true);
}
#endregion
#region ClearError function
//Clears the main board, from error graph
public void ClearError()
{
this.m_Errors=new ArrayList();
this.m_PeakError=0d;
this.m_MinError=1d;
this.Invalidate();
}
#endregion
#region GetMaxError
//This function returns the max value stored
//in errors array (if array is empty, returns 0)
public double GetMaxError()
{
if(this.m_Errors.Count>0)
{
double ret=(double)this.m_Errors[0];
for(int i=1; i<this.m_Errors.Count; i++)
{
if(((double)this.m_Errors[i])>ret)
ret=((double)this.m_Errors[i]);
}
return ret;
}
return 0;
}
#endregion
#endregion
#region Settings management
#region SaveSettings function
//Save settings function
public void SaveSettings()
{
string SettingsPath=Environment.CurrentDirectory+"/Settings.cfg";
if(File.Exists(SettingsPath))
{
try
{
StreamWriter sr = new StreamWriter(SettingsPath,false);
sr.Write(this.SerializeSettings());
sr.Close();
}
catch
{
MessageBox.Show("Can't write \'"+SettingsPath+"\'. File could be already opened by another application.","Warning!",MessageBoxButtons.OK,MessageBoxIcon.Warning);
return;
}
}
else
{
StreamWriter sr = File.CreateText(SettingsPath);
sr.Write(this.SerializeSettings());
sr.Close();
}
}
#endregion
#region LoadSettings function
//Load settings function
public void LoadSettings()
{
string SettingsPath=Environment.CurrentDirectory+"/Settings.cfg";
//Check if settings file exists
if(File.Exists(SettingsPath))
{
XmlTextReader TextReader = new XmlTextReader(SettingsPath);
try
{
//Try to read the file
while(TextReader.Read())
{
if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "MBColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
//Try to parse the color. If parsing fails, function returns
//RGBA=(0,0,0,0); in such a case, is used the default color
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.BoardColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "MBGridPenColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.SetGridPenColor(col);
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "MBGridHor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
this.GridHorizontalSpace=int.Parse(tmp);
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "MBGridVer")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
this.GridVerticalSpace=int.Parse(tmp);
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "MBLineColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.SetPenColor(col);
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "MBLineThickness")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
this.SetPenThickness(float.Parse(tmp));
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "MBPtsColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.PointsColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "APColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.ActualGesture.BaseColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "APTransparency")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
this.ActualGesture.Transparency=int.Parse(tmp);
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "APStdColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.ActualGesture.StandardColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "APStartColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.ActualGesture.StartColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "APEndColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.ActualGesture.EndColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "RPColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.RecognizedGesture.BaseColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "RPTransparency")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
this.RecognizedGesture.Transparency=int.Parse(tmp);
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "RPStdColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.RecognizedGesture.StandardColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "RPStartColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.RecognizedGesture.StartColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "RPEndColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.RecognizedGesture.EndColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "MPColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.MessageBar.BaseColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "MPTransparency")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
this.MessageBar.Transparency=int.Parse(tmp);
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "MPNormColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.MessageBar.NormalColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "MPErrorColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.MessageBar.ErrorColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "IPColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.InfoPanel.BaseColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "IPTransparency")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
this.InfoPanel.Transparency=int.Parse(tmp);
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "IPNormColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.InfoPanel.NormalColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "IPInfoColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.InfoPanel.InfoColor=col;
}
else if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "IPErrorColor")
{
string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
Color col=Parser.ParseColor(tmp);
if(col.A!=0 && col.R!=0 && col.G!=0 && col.B!=0)
this.InfoPanel.ErrorColor=col;
}
}
TextReader.Close();
}
catch(XmlException)
{
MessageBox.Show("XML parsing error while reading \'"+SettingsPath+"\'. The gesture will be ignored!","Warning!",MessageBoxButtons.OK,MessageBoxIcon.Error);
TextReader.Close();
}
}
}
#endregion
#region Serialize settings
//Settings XML file creation
private string SerializeSettings()
{
string ret="<Settings>\n";
//Board panel
ret+="<MainBoard>\n";
ret+="<MBColor>\n"+this.BoardColor.ToString()+"\n</MBColor>\n";
ret+="<MBGridPenColor>\n"+this.GetGridPenColor().ToString()+"\n</MBGridPenColor>\n";
ret+="<MBGridHor>\n"+this.GetGridHorizontalSpace().ToString()+"\n</MBGridHor>\n";
ret+="<MBGridVer>\n"+this.GetGridVerticalSpace().ToString()+"\n</MBGridVer>\n";
ret+="<MBLineColor>\n"+this.GetPenColor().ToString()+"\n</MBLineColor>\n";
ret+="<MBLineThickness>\n"+this.GetPenThickness().ToString()+"\n</MBLineThickness>\n";
ret+="<MBPtsColor>\n"+this.PointsColor.ToString()+"\n</MBPtsColor>\n";
ret+="</MainBoard>\n";
//Actual panel
ret+="<ActualPanel>\n";
ret+="<APColor>\n"+this.ActualGesture.BaseColor+"\n</APColor>";
ret+="<APTransparency>\n"+this.ActualGesture.Transparency+"\n</APTransparency>\n";
ret+="<APStdColor>\n"+this.ActualGesture.StandardColor+"\n</APStdColor>\n";
ret+="<APStartColor>\n"+this.ActualGesture.StartColor+"\n</APStartColor>\n";
ret+="<APEndColor>\n"+this.ActualGesture.EndColor+"\n</APEndColor>\n";
ret+="</ActualPanel>\n";
//Recognized panel
ret+="<RecogPanel>\n";
ret+="<RPColor>\n"+this.RecognizedGesture.BaseColor+"\n</RPColor>";
ret+="<RPTransparency>\n"+this.RecognizedGesture.Transparency+"\n</RPTransparency>\n";
ret+="<RPStdColor>\n"+this.RecognizedGesture.StandardColor+"\n</RPStdColor>\n";
ret+="<RPStartColor>\n"+this.RecognizedGesture.StartColor+"\n</RPStartColor>\n";
ret+="<RPEndColor>\n"+this.RecognizedGesture.EndColor+"\n</RPEndColor>\n";
ret+="</RecogPanel>\n";
//Message panel
ret+="<MessagePanel>\n";
ret+="<MPColor>\n"+this.MessageBar.BaseColor+"\n</MPColor>\n";
ret+="<MPTransparency>\n"+this.MessageBar.Transparency+"\n</MPTransparency>\n";
ret+="<MPNormColor>\n"+this.MessageBar.NormalColor+"\n</MPNormColor>\n";
ret+="<MPInfoColor>\n"+this.MessageBar.InfoColor+"\n</MPInfoColor>\n";
ret+="<MPErrorColor>\n"+this.MessageBar.ErrorColor+"\n</MPErrorColor>\n";
ret+="</MessagePanel>\n";
//Info panel
ret+="<InfoPanel>\n";
ret+="<IPColor>\n"+this.InfoPanel.BaseColor+"\n</IPColor>\n";
ret+="<IPTransparency>\n"+this.InfoPanel.Transparency+"\n</IPTransparency>\n";
ret+="<IPNormColor>\n"+this.InfoPanel.NormalColor+"\n</IPNormColor>\n";
ret+="<IPInfoColor>\n"+this.InfoPanel.InfoColor+"\n</IPInfoColor>\n";
ret+="<IPErrorColor>\n"+this.InfoPanel.ErrorColor+"\n</IPErrorColor>\n";
ret+="</InfoPanel>\n";
ret+="</Settings>";
return ret;
}
#endregion
#endregion
#region FullNet management
#region SaveFullNet function
//Creates a full net file
public void SaveFullNet(string path)
{
if(File.Exists(path))
{
try
{
StreamWriter sr = new StreamWriter(path,false);
sr.Write(this.SerializeFullNet());
sr.Close();
}
catch
{
MessageBox.Show("Can't write \'"+path+"\'. File could be already opened by another application.","Warning!",MessageBoxButtons.OK,MessageBoxIcon.Warning);
return;
}
}
else
{
StreamWriter sr = File.CreateText(path);
sr.Write(this.SerializeFullNet());
sr.Close();
}
}
#endregion
#region SerializeFullNet function
//Create string to bes tored in the XML full net file
private string SerializeFullNet()
{
string str="";
str+="<FullNet>\n";
for(int i=0; i<this.KnownGestures.Count; i++)
{
if(((Gesture)this.KnownGestures[i]).Trained)
{
str+="<GestureAndNet>\n";
str+=((Gesture)this.KnownGestures[i]).ToMGFstring(true,false)+"\n";
str+=((Gesture)this.KnownGestures[i]).Net.ToMGNstring()+"\n";
str+="</GestureAndNet>\n";
}
}
str+="</FullNet>";
return str;
}
#endregion
#endregion
}
#endregion
#region GridSpaces class
//This class is a simpler version of the Size class
public class GridSpaces
{
public int Width;
public int Height;
public GridSpaces(int width, int height)
{
this.Width=width;
this.Height=height;
}
}
#endregion
#region enum MouseState
//This enumerator shows all possible mouse states for this program
public enum MouseState
{
Recording, //RightButton down and user drawing gesture
Waiting //RightButton up, the program doesn't record gesture points
}
#endregion
#region enum BoardState
//This enumerator shows every state of the MainBoard
public enum BoardState
{
None, //Initial state
Recording, //In this state program can learn new gestures
TSCreating, //In this state user can import manually examples
Verifying, //In this state users can check if net answers correctly
Training //In this state MainBoard shows the training error praph
}
#endregion
}