Click here to Skip to main content
15,879,613 members
Articles / Multimedia / GDI+

C# Application to Create and Recognize Mouse Gestures (.NET)

Rate me:
Please Sign up or sign in to vote.
4.82/5 (39 votes)
17 Mar 2008CPOL5 min read 221.2K   8.1K   144  
This program can create and recognize mouse gestures.
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
	//Questa � la classe principale del programma, � un pannello sul quale vengono disegnate le gestures
	public class MainBoard : System.Windows.Forms.Panel
	{
		#region Private members
		private ArrayList m_Gestures=new ArrayList();	//Vettore che conterr� le gestures
		private MouseState m_MouseState=MouseState.Waiting;	//La variabile identifica lo stato del mouse
		private BoardState m_BoardState=BoardState.None;	//La variabile identifica lo stato della board
		private Pen m_Pen;	//Oggetto che permette di disegnare sulla board
		private Color m_BoardColor;	//Colore di background della board
		private int m_PatternsToCreate;	//Variabile necessaria nella fase di creazione del training set manuale
		private ArrayList m_TrainingSet=new ArrayList();	//Array per memorizzare il training set manuale
		private int m_ActualPattern;	//Contatore necessario a determinare la fine della creazione manuale del t.s.
		private float m_MaxErrors;	//La variabile contiene (in fase di training) il valore massimo dell'errore calcolato
		private ArrayList m_Points = new ArrayList();	//Array che contiene i punti campionati durante il movimento del mouse
		private ArrayList m_Errors=new ArrayList();	//Array contenente tutti gli errori caloclati in fase di training
		private double m_MinError=1d;	//Conterr� il valore minimo dell'errore
		private double m_PeakError=0d;	//Conterr� il valore massimo dell'errore
		#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
		//Gli eventi indicano rispettivamente:
		//L'inizio della fase di creazione di una gesture
		public delegate void BeginCreatingDelegate();
		public event BeginCreatingDelegate BeginCreating;

		//L'inizio della fase di training
		public delegate void BeginTrainingDelegate();
		public event BeginTrainingDelegate BeginTraining;

		//La fine della fase di creazione
		public delegate void EndCreatingDelegate();
		public event EndCreatingDelegate EndCreating;

		//La fine della fase di training
		public delegate void EndTrainingDelegate();
		public event EndTrainingDelegate EndTraining;
		#endregion

		#region Static variables
		public static int GestureMinimumPoints{get{return 15;}}	//Valore minimo per una gesture disegnata per essere utilizata
		public static int GestureMaximumPoints{get{return 100;}}	//Valore massimo dei punti di una gesture
		public static int MaximumFeatures{get{return 14;}}	//Valore massimo delle features gestite dalle reti
		#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 TransparentPanel m_UnusedPanel;
		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
		//Le seguenti funzioni servono solamente a settare i colori dei componenti o il colore e lo spessore delle linee
		#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
		//Queste funzioni gestiscono la spaziatura e il colore della griglia
		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
		//La funzione viene chiamata da un'altra form, che richiede si inizi la fase di creazione
		//manuale del training set
		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
		//La funzione viene invocata dall'esterno quando viene richiesto di inizare la fase di creazione di
		//una nuova gesture
		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();
		}

		//La funzione viene invocata per aggiungere una nuova gesture
		public bool AddNewGesture(string Name, ArrayList Points)
		{
			//Elimino dal nome i caratteri indesiderati
			string GestureName=Parser.PurgeString(Name);

			//Controllo che il nome della gesture sia valido
			if(GestureName.Length>0)
			{
				//Creo e aggiungo la gesture
				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
			{
				//Segnalo che il nome della gesture non � valiso
				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
		//Quando muovo il mouse sulla board controllo lo stato del mouse, se � in recording mode, registro il
		//punto campionato e aggiorno il messaggeio sulla barra
		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
		//Quando abbasso il tasto destro, se non sono in fase di training, metto il mouse in recording mode
		//ed inizio la procedura di registrazione della gesture (a seconda dello stato della main board
		//cancello tutti i pannelli o solo quello con la gesture appena disegnata (actual)
		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
		//Questa funzione � fodamentale per la visualizzazione delle gesture
		private void MouseUpOnBoard(object sender,System.Windows.Forms.MouseEventArgs e)
		{
			//Controllo che la board non sia inf ase di training (quando la gestione degli input � disattivata)
			if(this.m_BoardState!=BoardState.Training)
			{
				//Controllo se il mouse � nello stato di recording (sto registrando una gesture)
				if(this.m_MouseState==MouseState.Recording)
				{
					//Riporto lo sato del mouse in waiting (verr� riattivato dalla pressione del tasto dx)
					this.m_MouseState=MouseState.Waiting;

					//Aggiungo l'ultimo punto specificato negli argomenti dell'handler
					this.m_Points.Add(new PointF(e.X,e.Y));	

					//Controllo se il numero di punti � sufficiente
					if(this.m_Points.Count<MainBoard.GestureMinimumPoints)
					{
						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)
						{
							//Se ho almeno 2 punti, allora disegno la preview
							this.m_ActualGesture.InsertNewGesture(this.m_Points);
						}
					}
					else
					{
						//Se il numero dei punti � almeno sufficiente controllo se sia maggiore o minore del massimo
						if(this.m_Points.Count>MainBoard.GestureMaximumPoints)
						{
							//Se necessario riduco il numero di punti
							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)
						{
							//Se necessario aumento il numero di punti
							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"))+"]");
						}

						//Se la gesture ha almeno 2 punti, allora la inserisco nel pannello
						if(this.m_Points.Count>1)
							this.m_ActualGesture.InsertNewGesture(this.m_Points);

						//Se la board � nello stato di creazione della nuova gesture, faccio partire l'apposita funzione
						if(this.m_BoardState==BoardState.Recording)
						{
							this.ShowGestureCreation();
						}
						else if(this.m_BoardState==BoardState.TSCreating)
						{
							//Se la board � nellos tato di creazione del training set allora controllo se la
							//nuova gesture � stata inserita correttamente
							if(MessageBox.Show("Do you want to save this gesture to training set?","Save?",MessageBoxButtons.YesNo,MessageBoxIcon.Question)==DialogResult.Yes)
							{
								//Aggiorno il counter dei pattern
								this.m_ActualPattern++;
								//Creo il nuovo pattern
								Pattern p=new Pattern(Gesture.ExtractFeatures(this.m_Points));
								//Aggiungo al trainig set corrente il pattern
								this.m_TrainingSet.Add(p);

								//Se la gesture inserita � l'ultima, faccio riapparire la form di training
								//che completer� il lavoro
								if(this.m_ActualPattern==this.m_PatternsToCreate+1)
								{
									this.Parent.Enabled=false;
									this.m_CreateTrainingSetForm.Enabled=true;
									this.m_CreateTrainingSetForm.Visible=true;
							
									//Preaparo il vettore statico di apttern e vi riverso il contenuto
									//del vettore dinamico
									Pattern[] patterns=new Pattern[this.m_PatternsToCreate];
									this.m_TrainingSet.CopyTo(patterns);

									//Setto a none lo stato della board
									this.m_BoardState=BoardState.None;
									//Richiamo la funzione nel form di creazione del training set
									this.m_CreateTrainingSetForm.ImportAndUseGestures(patterns);
									this.ClearAll();
								}
								else
								{
									//Se non � ancora l'ultimo pattern allora mi limito a resettare la grafica
									//e dare i messaggi
									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
							{
								//Se si � deciso di non accettare la gesture, la posso creare di nuovo
								this.m_Message.ShowMessage("Draw again the gesture",MessageType.Info);
								this.ClearActualGesturePanel();
							}
						}
						else if(this.m_BoardState==BoardState.Verifying)
						{
							//Se mi trovo nello stato di verifica appronto un vettore che contenga
							//gli errori ad ogni epoca
							double[] Outputs=new double[this.m_Gestures.Count];

							for(int z=0; z<this.m_Gestures.Count; z++)
							{
								//Se la rete � stata addestrata calcolo le risposte della reti
								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;	//Do un valore negativo inferiore a al valore minimo dell'output della rete per gestures non addestrate
							}

                            //Dal vettore degli errori max, estraggo i 3 migliori valori di uscita (o meno se ho meno reti)
							int[] Winners=this.GetWinners(Outputs);

							//Controllo se l'output supera la threashold scelta
							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"):(""));

							//Visualizzo dei messaggi
							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));

							//Se la gesture � riconosciuta, aggiungo nel pannello delle risposte quella attuale
							if(Recognized)
								this.m_RecognizedGesture.InsertNewGesture(((Gesture)this.m_Gestures[Winners[0]]).Points);
						}
					}
					//Forzo il refresh dell'area
					this.Invalidate();
				}
			}
		}
		#endregion

		#endregion

		#region Paint hanldler
		private void PaintBoard(object sender,System.Windows.Forms.PaintEventArgs e)
		{
			//Ridisegno lo sfondo colorato e la griglia ogni volta che viene richiesto
			e.Graphics.FillRectangle(new SolidBrush(this.m_BoardColor),0,0,this.ClientSize.Width,this.ClientSize.Height);
			
			#region Draw Grid
			//Disegno la griglia secondo le spaziature impostate
			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
			
			//Controllo in che stato si trovi la board
			if(this.m_BoardState==BoardState.Training)
			{
				//Se sto facendo il training devo disegnare il grafico dell'errore
				//Calcolo il passo tra una linea e l'altra del grafico (poich� voglio che tutto il grafico
				//sia rappresentato nella finestra controllo che il numero massimo di errori (ossia il numero di epoche)
				//non sia maggiore della larghezza della finestra. Se � cos� il passo � pari al rapporto tra la
				//larghezza totale della finestra e il numero di errori; in caso contrario il passo � 1
				float Pass=0;
				if(this.m_MaxErrors>(this.Width-20))	//Invece di usare tutta la larghezza lascio un o spazio di 20 pixel
					Pass=((float)this.Width-20f)/((float)this.m_MaxErrors);
				else
					Pass=1;

				//Calcolo l'errore massimo tra quelli inseriti, scalando tutte le altezze del grafo secondo questo
				//fattore (in questa maniera tutto il grafico resta all'interno della form
				double MaxError=this.GetMaxError();
				//Altezza massima del grafico
                float BaseHight=this.Height-this.m_Message.Height;

				//Disegno una linea dal fondo al punto desiderato, utilizzabdo l'indice degli errori e il passo
				//per calcolare l'ascissa della colonna da rappresentare
				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
			{
				//Se non sono in training mode, allora devo disegnare ci� che � stato disegnato
				//dall'utente (purch� abbia disegnato almeno due punti (altrimenti non posso
				//disegnare una retta
				if(this.m_Points.Count>1)
				{
					//Converto l'array dinamico in uno statico
					PointF[] pts=new PointF[this.m_Points.Count];
					this.m_Points.CopyTo(pts,0);
					//Passo l'array alla funzione che disegna le linee
					e.Graphics.DrawLines(this.m_Pen,pts);
				
					//Se ho gi� terminato di disegnare la gesture, allora visualizzo
					//anche gli anchor points (ossia i punti realmente campionati durante il movimento del mouse)
					if(this.m_MouseState==MouseState.Waiting)
					{
						foreach (PointF p in this.m_Points)
						{
							//Per ogni punto che trovo disegno un rettangolo centrato nel punto
							e.Graphics.FillRectangle(new SolidBrush(this.m_PointsColor),p.X-1f,p.Y-1f,2,2);
						}
					}
				}
			}
		}
		#endregion

		#region ParentResize handler
		//La funzione si occupa di aggiornare la posizione o la dimensione dei componenti sulla board, ossia
		//i due pannelli e le due label
		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
		//La funzione si occupa di caricare all'interno del l'arry di gesture
		//il contenuto di un altro array proveniente dall'esterno
		public void ImportGestures(ArrayList gestures)
		{
			this.m_Gestures=gestures;
		}
		#endregion

		#region ShowMessageBar function
		//La funzione � utilizzata per scrivere sulla barra il messaggio desiderato
		//Non � una funzione necessaria, poich� esistono properties publiche
		//per accedere ai compoennti della main board, ma risulta comoda durante al stesura del codice
		public void ShowMessageToBar(string message, MessageType type)
		{
			this.m_Message.ShowMessage(message,type);
		}
		#endregion

		#region GesturesCreationForm functions
		//La funzione visualizza (una volta creata la nuova gesture) la finestra di creazione
		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();
		}

		//La funzione viene invocata alla chiusura del dialog di creazione della gesture
		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
		//La funzione permette la visualizzazione della finestra per la gestione delle gestures
		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();
		}

		//Alla chiusura della finestra ripristino lo stato della manin board
		private void OnCloseGestureManagement(object sender, System.EventArgs e)
		{
			this.ClearAll();
			this.Parent.Enabled=true;
			this.m_BoardState=BoardState.None;	//Alla chiusura della finestra riporto la board principale nello stato iniziale
			this.m_Message.ShowMessage("Gesture management: closed", MessageType.Normal);
		}
		#endregion

		#endregion

		#region Manage verifying

		#region Start Verifying
		//La funzione viene chiamata quando si vuole iniziare la verifica della rete
		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
		//Al termine della verifica questa funzione riporta lo stato della board al valore iniziale
		public void EndVerify()
		{
			this.ClearAll();
			this.m_BoardState=BoardState.None;
			this.m_Message.ShowMessage("Verifying stopped",MessageType.Normal);
		}
		#endregion

		#region Verifiyng result functions
		//La funzione calcola i migliori risultati tra gli output delle reti (variabile in ingresso)
		//restituendo un vettore di indici, ordinato in maniera tale che iol rpimo indice faccia 
		//riferimento alla rete con output maggiore
		private int[] GetWinners(double[] outputs)
		{
			//Preparo il vettore dei risultati (cerco di mostrare sempre i primi 3, nel caso in cui
			//ci siano meno gestures, allora mostro semplicemente tutti i risultati
			int BestResults=(outputs.Length>=3)?(3):(outputs.Length);
			int[] ret=new int[BestResults];
			
			//Faccio una copia degli output, per poter eseguire il sorting
			double[] TmpOut=new double[outputs.Length];
			outputs.CopyTo(TmpOut,0);

			//Creo il vettore degli indici che andr� ordinato insieme agli output
			int[] Indexes=new int[outputs.Length];
			for(int k=0; k<Indexes.Length; k++)
				Indexes[k]=k;

			//Ordino tramite bubblesort sia il vettore dei valori che gli indici
			//Le prime componenti faranno riferimento agli output minori
			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;
					}
				}
			}

			//Popolo il vettore da restituire con gli ultimi BestResults indici
			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
		//La funzione da inizio alla fase di training
		public void StartTrainingMode(int maxerrors)
		{
			this.ClearActualGesturePanel();
			this.m_MaxErrors=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
		//La funzione termina la fase di training ripristinando lo stato della main board
		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
		//Ogni volta che una gesture completa un'epoca, aggiorno il grafico dell'errore
		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
		//Ogni volta che una gesture viene completata, il vettore degli errori
		public void OnGestureTrained()
		{
			this.m_Errors=new ArrayList();
			this.m_PeakError=0d;
			this.m_MinError=1d;
			this.Invalidate(true);
		}
		#endregion

		#region ClearError function
		//La funzione pu� essere chiamata dall'esterno per pulire la board dal grafico di errore
		public void ClearError()
		{
			this.m_Errors=new ArrayList();
			this.m_PeakError=0d;
			this.m_MinError=1d;
			this.Invalidate();
		}
		#endregion

		#region GetMaxError
		//La funzione calcola il massimo valore tra gli elementi dell'array degli errori o 0
		//se non ci sono errori all'interno dell'array (per evitare sgradevoli crash)
		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
		//La funzione salva la serializzazione dei settings
		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
		//La funzione si occupa di caricare un file XML e convertirlo nei settaggi grafici del programma
		public void LoadSettings()
		{
			string SettingsPath=Environment.CurrentDirectory+"/Settings.cfg";

			//Controllo se esiste il file dei settaggi, se non esiste, mantengo i settaggi di default
			if(File.Exists(SettingsPath))
			{
				XmlTextReader TextReader = new XmlTextReader(SettingsPath);

				try
				{
					//Tento di leggere nodo per nodo tutto il file
					while(TextReader.Read())
					{
						if(TextReader.NodeType == XmlNodeType.Element && TextReader.Name == "MBColor")
						{
							string tmp=Parser.PurgeString(TextReader.ReadInnerXml());
							Color col=Parser.ParseColor(tmp);
							//Ogni volta che faccio il parsing di un colore ontrollo se � andato a buon fine
							//se il parsing � fallito per qualche motivo, il parser ritorna un colore con
							//A,R,G e B pari a 0
							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
		//La funzione converte i dati letti in un file XML
		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
		//La funzione si limita a scrivere la stringa serializzata nel file specificato dalla path
		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
		//Questa funzione riduce la rete ad un file XML contenente i dati per ricreare la rete stessa
		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
	//Questa classe � semplicemente una semplificazione della classe Ret
	//identificando un rettangolo tramite le sue dimensioni
	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
	//L'enumeratore indica i possibili stati in cui pu� trovarsi il mouse
	public enum MouseState
	{
		Recording,	//Il tasto destro � premuto e sto registrando i punti
		Waiting	//Il tasto destro non � premuto, non sto registrando i punti e resto in attesa
	}
	#endregion

	#region enum BoardState
	//L'enumeratore indica i possibili stati della main board
	public enum BoardState
	{
		None,	//Stato iniziale
		Recording,	//Stato in cui si impara una nuova gesture (potranno essere anche specificate tramite file di testo, specificando un minimo di 15 punti)
		TSCreating,	//Stato in cui la rete apprende gli esempi (manualmente)
		Verifying,	//Stato in cui si pu� controllare l'apprendimento della rete
		Training	//Stato in cui la board visualizza l'errore durante la fase di training
	}
	#endregion
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Apex s.r.l.
Italy Italy
I got my Computer Science (Engineering) Master's Degree at the Siena University (Italy), but I'm from Rieti (a small town next to Rome).
My hobbies are RPG, MMORGP, programming and 3D graphics.
At the moment I'm employed at Apex s.r.l. (Modena, Italy) as a senior software developer, working for a WPF/WCF project in Rome.

Comments and Discussions