Click here to Skip to main content
15,897,704 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 222.1K   8.1K   144  
This program can create and recognize mouse gestures.
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Xml;
using System.Common;

namespace MouseGestures
{
	public class GestureSet
	{
		public event OnIOActivityProgress IOProgress;
		public event OnGestureCreatedDelegate GestureCreated;

		private string m_Name;
		private Gesture[] m_Gestures;

		public int Gestures { get { return m_Gestures != null ? m_Gestures.Length : 0; } }
		public Gesture this[int index] { get { return m_Gestures[index]; } }

		public GestureSet(string full_path)
		{
			Load(full_path);
		}

		public GestureSet(XmlReader reader)
		{
			Deserialize(reader);
		}

		public GestureSet(Gesture gesture, int count)
		{
			if (gesture == null)
				throw new ArgumentException("Gesture must not be null", "gesture");
			else if(count<0)
				throw new ArgumentException("Gestures to create must be 0 or a positive value", "count");

			m_Name = gesture.Name;

			if (count > 0)
				Create(gesture, count, true);
			else
				m_Gestures = new Gesture[0];
		}

		public GestureSet(string name, Gesture[] gestures)
		{
			if (gestures == null)
				throw new ArgumentException("Gestures must not be null", "gestures");

			m_Name = name;
			m_Gestures = gestures;
		}

		private void Merge(Gesture[] gestures)
		{
            if (m_Gestures == null)
                m_Gestures = new Gesture[0];

			int i;
			Gesture[] new_gestures = new Gesture[m_Gestures.Length + gestures.Length];
			for (i = 0; i < m_Gestures.Length; i++)
				new_gestures[i] = m_Gestures[i];

			for (i = 0; i < gestures.Length; i++)
				new_gestures[m_Gestures.Length + i] = gestures[i];

			m_Gestures = new_gestures;
		}

		public void Create(Gesture[] gestures, bool replace)
		{
			if (gestures == null)
				throw new ArgumentException("Gestures must not be null", "gestures");
			else if (gestures.Length < 1)
				throw new ArgumentException("Gestures must be at least 1", "gestures");

			if (replace)
				m_Gestures = gestures;
			else
				Merge(gestures);
		}

		public void Create(Gesture base_gesture, int to_create, bool replace)
		{
			if (base_gesture == null)
				throw new ArgumentException("Base gesture must not be null", "base_gesture");
			else if (to_create < 1)
				throw new ArgumentException("Gestures to create must be at least 1", "to_create");

			int i, j;
			m_Name = base_gesture.Name;
			Gesture[] gestures = new Gesture[to_create];

			float max_dx = GetMaxDeltaX(base_gesture);
			float max_dy = GetMaxDeltaY(base_gesture);
			
			float max_error = (float)Math.Min(max_dx, max_dy);
			max_error = (max_error == 0) ? ((float)Math.Max(max_dx, max_dy)) : (max_error);
			max_error /= 3f;
			
			if (max_error == 0)
				max_error = 0.05f;

			Random randomizer = new Random(DateTime.Now.Millisecond);
			PointF[] points;

			for (i = 0; i < to_create; i++)
			{
				points = new PointF[base_gesture.Points];

				for (j = 0; j < base_gesture.Points; j++)
				{
					points[j].X = base_gesture[j].X + ((2 * (float)randomizer.NextDouble() - 1) * max_error);
					points[j].Y = base_gesture[j].Y + ((2 * (float)randomizer.NextDouble() - 1) * max_error);
				}

				gestures[i] = new Gesture(base_gesture.Name + "_" + i.ToString(), points);

				if (GestureCreated != null)
					GestureCreated(gestures[i]);
			}

			if (replace)
				m_Gestures = gestures;
			else
				Merge(gestures);
		}

		private float GetMaxDeltaX(Gesture gesture)
		{
			float max = gesture[0].X - gesture[1].X, delta;
			for (int i = 2; i < gesture.Points - 1; i++)
			{
				delta = gesture[i].X - gesture[i + 1].X;
				delta = (delta > 0) ? (delta) : (-delta);
				if (delta > max)
					max = delta;
			}

			return max;
		}

		private float GetMaxDeltaY(Gesture gesture)
		{
			float max = gesture[0].Y - gesture[1].Y, delta;
			for (int i = 2; i < gesture.Points - 1; i++)
			{
				delta = gesture[i].Y - gesture[i + 1].Y;
				delta = (delta > 0) ? (delta) : (-delta);
				if (delta > max)
					max = delta;
			}

			return max;
		}

		public override string ToString()
		{
			string s = "Gesture set:\n{\n";

			for (int i = 0; i < m_Gestures.Length; i++)
				s += m_Gestures[i].ToString() + "\n";

			return s + "}";
		}

		public override bool Equals(object obj)
		{
			if (obj == this)
				return false;
			else
			{
				GestureSet set = obj as GestureSet;

				if (set == null)
					return false;
				else
				{
					if (set.m_Gestures.Length != m_Gestures.Length)
						return false;
					else
					{
						int i, j;
						bool check;

						for (i = 0; i < m_Gestures.Length; i++)	//this could be optimized using a cache of checked indices... :P
						{
							check = false;
							for (j = 0; j < set.m_Gestures.Length && !check; j++)
							{
								if (m_Gestures[i].Equals(set.m_Gestures[i]))
									check = true;
							}

							if (!check)
								return false;
						}

						return true;
					}
				}
			}
		}

		public override int GetHashCode()
		{
			int hash = 0;

			for (int i = 0; i < m_Gestures.Length; i++)
				hash += m_Gestures[i].GetHashCode();

			return hash;
		}

		public void Serialize(XmlWriter writer)
		{
			writer.WriteStartElement("gesture_set");
			writer.WriteAttributeString("name", m_Name);
			writer.WriteAttributeString("gestures", m_Gestures.Length.ToString());

			if (IOProgress != null)
				IOProgress("Gesture set main data saved...");

			for (int i = 0; i < m_Gestures.Length; i++)
			{
				if (IOProgress != null)
					IOProgress("Saving gesture data (" + i.ToString() + "/" + m_Gestures.Length.ToString() + ")...");
				m_Gestures[i].Serialize(writer);				
			}

			writer.WriteEndElement();
		}

		public void Deserialize(XmlReader reader)
		{
			try
			{
				while (reader.Read())
				{
					switch (reader.NodeType)
					{
						case XmlNodeType.Element:
							{
								if (reader.Name == "gesture_set")
								{
									m_Name = reader["name"];
									m_Gestures = new Gesture[int.Parse(reader["gestures"])];

									if (IOProgress != null)
										IOProgress("Gesture set main data loaded...");

									for (int i = 0; i < m_Gestures.Length; i++)
										m_Gestures[i] = new Gesture(reader);
								}

								break;
							}
						case XmlNodeType.EndElement:
							{
								if (reader.Name == "gesture_set")
									return;

								break;
							}
					}
				}
			}
			catch (Exception exc)
			{
				Console.WriteLine("Exception caught while deserializing gesture set data.\n\tSource: " + exc.Source + "\n\tMessage: " + exc.Message);
				m_Gestures = new Gesture[0];
				m_Name = "NULL";
			}
		}

		public bool Save(string full_path)
		{
			try
			{
				XmlTextWriter writer = new XmlTextWriter(full_path, Encoding.UTF8);
				writer.Formatting = Formatting.Indented;
				writer.WriteStartDocument();
				Serialize(writer);
				writer.WriteEndDocument();
				writer.Close();
				return true;
			}
			catch (Exception exc)
			{
				Console.WriteLine("Exception caught while saving gesture set data.\n\tSource: " + exc.Source + "\n\tMessage: " + exc.Message);
				return false;
			}
		}

		public bool Load(string full_path)
		{
			try
			{
				XmlTextReader reader = new XmlTextReader(full_path);
				Deserialize(reader);
				reader.Close();
				return true;
			}
			catch (Exception exc)
			{
				Console.WriteLine("Exception caught while loading gesture set data.\n\tSource: " + exc.Source + "\n\tMessage: " + exc.Message);
				m_Gestures = new Gesture[0];
				m_Name = "NULL";
				return false;
			}
		}
	}
}

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