/// <summary>
        /// Compares two handdatas and tells whether they match.
        /// </summary>
        /// <param name="position">The position that should be matched.</param>
        /// <param name="current">The current data.</param>
        /// <param name="tollerance">How much each joint is allowed to deviate.</param>
        /// <returns>Whether the data matches.</returns>
        public bool AreSimilar(PositionRecord position, HandData current, double tollerance = 0)
        {
            tollerance = Math.Pow(tollerance > 0 ? tollerance : TOLLERANCE, 2);
            // Number of joints.
            int max = (int)HandData.Joint.MAX;
            // The total deviation.
            double total = 0, frac;
            // The number of non-ignored joints.
            int nr = 0;

            for (int i = 0; i < max; i++)
            {
                if (position.Ignored[i])
                {
                    continue;
                }
                // Add square deviation of this joint to the total.
                frac   = position[i].Degrees - current[i].Degrees;
                total += frac * frac;
                nr++;
            }

            // Check whether this one matches.
            if (total / nr < tollerance)
            {
                return(true);
            }
            else
            {
                return(false);
            }
        }
        /// <summary>
        /// Creates a record from the file input.
        /// </summary>
        /// <param name="name">The name of the record.</param>
        /// <param name="csv">The CSV string of values.</param>
        /// <returns></returns>
        private PositionRecord makeRecord(string name, string csv)
        {
            // Parse the name line
            bool standalone = !name.EndsWith("-");

            name = name.TrimEnd(new char[] { '-' });
            PositionRecord rv = new PositionRecord(name);

            rv.Standalone = standalone;
            // Parse the CSV line
            string[] values = csv.Split(SEPARATOR);
            if (values.Length != values.Length)
            {
                throw new ArgumentException("The CSV needs to contain exactly 22 values.", "csv");
            }
            for (int i = 0; i < values.Length; i++)
            {
                string v = values[i].Trim();
                if (v == "*")
                {
                    rv.Ignored[i] = true;
                }
                else
                {
                    rv[i].Value = Double.Parse(values[i].Trim());
                }
            }
            return(rv);
        }
        /// <summary>
        /// Parses the contents of the given file. Recognized positions will be saved in
        /// Hand.positions.
        /// </summary>
        public void Parse()
        {
            // If the file does not exist, we create it (for writing). Its contents
            // will of course be empty, so we can just exit immediately.
            if (!File.Exists(Path))
            {
                File.Create(Path);
                return;
            }
            // name and csv strings, for file input.
            string       name, csv;
            StreamReader reader = new StreamReader(Path);

            try
            {
                while (reader.Peek() != -1)
                {
                    // Read two lines. First line is always a name, possibly with the Dependency modifier....
                    name = reader.ReadLine();
                    // ... followed by the array of values
                    csv = reader.ReadLine();
                    PositionRecord record = makeRecord(name, csv);
                    AddPosition(record);
                }
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                reader.Close();
                reader.Dispose();
            }
        }
 /// <summary>
 /// Compares two handdatas and tells whether they match.
 /// </summary>
 /// <param name="position">The position that should be matched.</param>
 /// <param name="current">The current data.</param>
 /// <returns>Whether the data matches.</returns>
 public bool AreSimilar(PositionRecord position, HandData current, double tollerance = 0)
 {
     tollerance = tollerance > 0 ? tollerance : TOLLERANCE;
     int max = (int)HandData.Joint.MAX;
     for (int i = 0; i < max; i++)
     {
         if (position.Ignored[i])
             continue;
         else if (!position[i].IsSimilar(current[i], tollerance))
             return false;
     }
     return true;
 }
        /// <summary>
        /// Returns a list of matches with the given HandData object, with the precision altered by the given argument.
        /// </summary>
        /// <param name="needle">The HandData object that we have to search a match for.</param>
        /// <param name="precision">The normal precision will be multiplied by this number.
        /// Low number increases precision, high number decreases precision.</param>
        /// <returns>A List of matches. Will be empty if none were found.</returns>
        public static List <PositionRecord> GetAllMatches(PositionRecord needle, double precision)
        {
            List <PositionRecord> rv = new List <PositionRecord>();

            foreach (PositionRecord pr in positions.Values)
            {
                if (pr.IsSimilar(needle, precision))
                {
                    rv.Add(pr);
                }
            }
            return(rv);
        }
        /// <summary>
        /// Tells whether a hand's current position is similar to this one, with
        /// the precision altered by the given argument.
        /// Not to confused with IsSimilar(HandData, double) which is a less
        /// specific implementation.
        /// </summary>
        /// <param name="other">The other handposition.</param>
        /// <param name="tollerance">The factor by which the default precision should be multiplied.</param>
        /// <returns>Whether the other hand is similar to this one.</returns>
        public bool IsSimilar(PositionRecord other, double tollerance)
        {
            int max = NR_FINGERS * NR_JOINTS + 2;

            for (int i = 0; i < max; i++)
            {
                if (Ignored[i] || other.Ignored[i])
                {
                    continue;
                }
                else if (!this[i].IsSimilar(other[i], tollerance))
                {
                    return(false);
                }
            }
            return(true);
        }
        /// <summary>
        /// Saves a record to the text file.
        /// </summary>
        /// <param name="record">The record that should be saved.</param>
        public void Save(PositionRecord record)
        {
            StreamWriter writer = null;

            try
            {
                writer = new StreamWriter(Path, true);
                writer.WriteLine(record.Name + (record.Standalone ? "" : "-"));
                writer.WriteLine(record.CSV);
                AddPosition(record);
            }
            finally
            {
                if (writer != null)
                {
                    writer.Close();
                    writer.Dispose();
                }
            }
        }
 /// <summary>
 /// Adds a record to the list of known records.
 /// </summary>
 /// <param name="pr">The PositionRecord that needs to be added.</param>
 public static void AddPosition(PositionRecord pr)
 {
     positions.Add(pr.Name, pr);
 }