// returns 5 local max points (1 for each fingertip) and 4 local min points (spaces between fingers)
        public bool FindFingerPoints(List <Point> path, Point centroid, out List <APair <int, double> > maximums, out List <APair <int, double> > minimums)
        {
            // find distances to centroid for each path point
            List <double> distances = new List <double>();

            for (int i = 0; i < path.Count; ++i)
            {
                Point p = path[i];
                if (p != null)
                {
                    double dist = BitMatrix.Distance(p, centroid);
                    distances.Add(dist);
                }
            }

            List <double> blurredDistances = ListBlur(distances);

            maximums = FindMaximums(blurredDistances);
            if (maximums.Count != MAX_COUNT)
            {
                minimums = new List <APair <int, double> >();
                return(false);
            }

            minimums = FindMinimums(blurredDistances, maximums);
            if (minimums.Count != MIN_COUNT)
            {
                return(false);
            }

            return(true);
        }
        // calculates distances between max/min points and surface areas for each finger
        public List <double> FindFingerFeatures(BitMatrix bm, List <Point> path, Point centroid, List <APair <int, double> > maximums, List <APair <int, double> > minimums)
        {
            // get finger/hole points
            List <Point> fingers = new List <Point>();

            for (int i = 0; i < maximums.Count; ++i)
            {
                fingers.Add(path[maximums[i].First]);
            }
            List <Point> holes = new List <Point>();

            for (int i = 0; i < minimums.Count; ++i)
            {
                holes.Add(path[minimums[i].First]);
            }

            List <double> features = new List <double>
            {
                // first add left side distances
                BitMatrix.Distance(fingers[0], holes[0]),
                BitMatrix.Distance(fingers[1], holes[1]),
                BitMatrix.Distance(fingers[2], holes[2]),
                BitMatrix.Distance(fingers[3], holes[3]),

                // then add right side distances
                BitMatrix.Distance(fingers[1], holes[0]),
                BitMatrix.Distance(fingers[2], holes[1]),
                BitMatrix.Distance(fingers[3], holes[2]),
                BitMatrix.Distance(fingers[4], holes[3])
            };

            // now find points before and after first hole to calculate surface areas
            int preI = maximums[0].First * 2 - minimums[0].First;

            if (preI < 0)
            {
                preI = 0;
            }
            else if (preI > path.Count - 1)
            {
                preI = path.Count - 1;
            }

            Point preHole = path[preI];
            Point postHole;

            // find point before first hole
            while (preI >= 1 && BitMatrix.Distance(preHole, fingers[0]) < BitMatrix.Distance(fingers[0], holes[0]))
            {
                preHole = path[--preI];
            }
            while (preI < path.Count - 2 && BitMatrix.Distance(preHole, fingers[0]) > BitMatrix.Distance(fingers[0], holes[0]))
            {
                preHole = path[++preI];
            }

            // find point after last hole
            int maxId = MAX_COUNT - 1;
            int minId = MIN_COUNT - 1;
            int postI = maximums[maxId].First * 2 - minimums[minId].First;

            if (postI < 0)
            {
                postI = 0;
            }
            else if (postI > path.Count - 1)
            {
                postI = path.Count - 1;
            }
            postHole = path[postI];

            while (postI < path.Count - 2 && BitMatrix.Distance(postHole, fingers[maxId]) < BitMatrix.Distance(fingers[maxId], holes[minId]))
            {
                postHole = path[++postI];
            }
            while (postI >= 1 && BitMatrix.Distance(postHole, fingers[maxId]) > BitMatrix.Distance(fingers[maxId], holes[minId]))
            {
                postHole = path[--postI];
            }

            BitMatrix fbm = new BitMatrix(bm);

            fbm.FillArea(centroid);

            // #TODO LS generate surface area data
            //features.Add(Math.Sqrt(bm.GetSurface(fbm, preHole, holes[0])));
            //features.Add(Math.Sqrt(bm.GetSurface(fbm, holes[0], holes[1])));
            //features.Add(Math.Sqrt(bm.GetSurface(fbm, holes[1], holes[2])));
            //features.Add(Math.Sqrt(bm.GetSurface(fbm, holes[2], holes[3])));
            //features.Add(Math.Sqrt(bm.GetSurface(fbm, holes[3], postHole)));

            return(features);
        }