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
}