/// <summary> /// Adds a new gesture to library and then saves it to the xml. /// The trick here is that we don't reload the newly saved xml. /// It would have been a waste of resources. Instead, we just add /// the new gesture to the list of gestures (the library). /// </summary> /// <param name="gesture">The gesture to add</param> /// <returns>True if addition is succesful</returns> public bool AddGesture(Gesture gesture) { // Create the xml node to add to the xml file XmlElement rootElement = gestureLibrary.DocumentElement; XmlElement gestureNode = gestureLibrary.CreateElement("gesture"); gestureNode.SetAttribute("name", gesture.Name); foreach (Vector2 v in gesture.Points) { XmlElement gesturePoint = gestureLibrary.CreateElement("point"); gesturePoint.SetAttribute("x", v.x.ToString()); gesturePoint.SetAttribute("y", v.y.ToString()); gestureNode.AppendChild(gesturePoint); } // Append the node to xml file contents rootElement.AppendChild(gestureNode); try { // Add the new gesture to the list of gestures this.Library.Add(gesture); // Save the file if it is not the web player, because // web player cannot have write permissions. #if !UNITY_WEBPLAYER && !UNITY_EDITOR FileTools.Write(persistentLibraryPath, gestureLibrary.OuterXml); #elif UNITY_EDITOR && !UNITY_WEBPLAYER FileTools.Write(resourcesPath, gestureLibrary.OuterXml); #endif return true; } catch (Exception e) { Debug.Log(e.Message); return false; } }
/// <summary> /// Gets the average distance between two gestures at a specified angle /// </summary> /// <param name="gesture">Gesture to compare to</param> /// <param name="angle">The angle to rotate this gesture</param> /// <returns>Distance between two gestures</returns> public float GetDistanceAtAngle(Gesture gesture, float angle) { List<Vector2> newPoints = this.RotateBy(angle); return Gesture.GetDistanceBetweenPaths(newPoints, gesture.Points); }
/// <summary> /// $1 algorithm works like this: /// - Take a gesture: with any number of points /// - Resample: so that it has exactly NUMBER_OF_POINTS number of points which are equidistant to each other /// - Rotate: based on the indicative angle (angle between the centroid and the first point) /// - Scale and translate: so that it can fit into a predefined sized square /// - Find the most optimized angle by Golden Section Search so that it has the best score /// /// Resampling, rotating, scaling and translating ensures that gestures are somewhat "more" similar. By doing these /// operations, we make them easier to compare. After these operations, the distance between two gestures /// (which is the average distance between each points: d = sum(d1, d2, ..., dn) / n) is calculated. The gesture /// with the least distance is the result and this result is converted to a score between [0, 1]. /// /// To get the best score, each gesture is rotated to the best indicative angle which is found by using /// Golden Section Search. /// </summary> /// <param name="gesture">Gesture to compare</param> /// <param name="a">Negative angle range</param> /// <param name="b">Positive angle range</param> /// <param name="threshold">Angle precision</param> /// <returns>Score</returns> public float DollarOneAlgorithm(Gesture gesture, float a, float b, float threshold) { float x1 = this.PHI * a + (1f - this.PHI) * b; float f1 = this.GetDistanceAtAngle(gesture, x1); float x2 = (1f - this.PHI) * a + this.PHI * b; float f2 = this.GetDistanceAtAngle(gesture, x2); while (Mathf.Abs(b - a) > threshold) { if (f1 < f2) { b = x2; x2 = x1; f2 = f1; x1 = this.PHI * a + (1f - this.PHI) * b; f1 = this.GetDistanceAtAngle(gesture, x1); } else { a = x1; x1 = x2; f1 = f2; x2 = (1f - this.PHI) * a + this.PHI * b; f2 = this.GetDistanceAtAngle(gesture, x2); } } return Mathf.Min(f1, f2); }
/// <summary> /// Loads the library from an XML file /// </summary> public void LoadLibrary() { // Uses the XML file in resources folder if it is webplayer or the editor. string xmlContents = ""; string floatSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; #if !UNITY_WEBPLAYER && !UNITY_EDITOR xmlContents = FileTools.Read(persistentLibraryPath); #else xmlContents = Resources.Load<TextAsset>(libraryName).text; #endif gestureLibrary.LoadXml(xmlContents); // Get "gesture" elements XmlNodeList xmlGestureList = gestureLibrary.GetElementsByTagName("gesture"); // Parse "gesture" elements and add them to library foreach (XmlNode xmlGestureNode in xmlGestureList) { string gestureName = xmlGestureNode.Attributes.GetNamedItem("name").Value; XmlNodeList xmlPoints = xmlGestureNode.ChildNodes; List<Vector2> gesturePoints = new List<Vector2>(); foreach (XmlNode point in xmlPoints) { Vector2 gesturePoint = new Vector2(); gesturePoint.x = (float)System.Convert.ToDouble(point.Attributes.GetNamedItem("x").Value.Replace(",", floatSeparator).Replace(".", floatSeparator)); gesturePoint.y = (float)System.Convert.ToDouble(point.Attributes.GetNamedItem("y").Value.Replace(",", floatSeparator).Replace(".", floatSeparator)); gesturePoints.Add(gesturePoint); } Gesture gesture = new Gesture(gesturePoints, gestureName); library.Add(gesture); } }
private void OnClickRemoveGesture() { currentLibrary.Gestures.Remove(currentGesture); currentGesture = null; EditorUtility.SetDirty(currentLibrary); }
private void DrawInstructions() { GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUILayout.Label("Draw your gesture, enter a name in the text field and then click 'Add'"); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); newGestureName = GUILayout.TextField(newGestureName, 32, GUILayout.Width(200)); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); if (newGestureName == "") { GUI.enabled = false; } if (GUILayout.Button("Add", GUILayout.Width(100))) { Gesture g = new Gesture(points.ToArray(), newGestureName); currentLibrary.Gestures.Add(g); ClearGesture(); isAddingGesture = false; EditorUtility.SetDirty(currentLibrary); } GUI.enabled = true; if (points.Count == 0) { GUI.enabled = false; } if (GUILayout.Button("Clear", GUILayout.Width(100))) { ClearGesture(); EditorUtility.SetDirty(currentLibrary); } GUI.enabled = true; if (GUILayout.Button("Cancel", GUILayout.Width(100))) { ClearGesture(); isAddingGesture = false; EditorUtility.SetDirty(currentLibrary); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); }
public void SaveToLibraryGesture(Gesture gesture) { }