Esempio n. 1
0
        /// <summary>
        /// Compute the convex hull given a set of points on a 2D plane.
        /// </summary>
        /// <param name="input">A set of points on a 2D plane.</param>
        /// <returns>The convex hull.</returns>
        public List <Vector2> Compute(List <Vector2> input)
        {
            // Three or less points is already the convex hull.
            if (input.Count <= 3)
            {
                return(new List <Vector2>(input));
            }

            // Swap the lowest y to input[0].
            SwapLowestY(input);

            // Calculate angle and distance for each vector based on the lowest point.
            // The idea is to update the vector in the dictionary if it has a lesser distance
            // to the starting point than the new vector.
            Dictionary <double, VectorAttributes> dict = new Dictionary <double, VectorAttributes>();

            foreach (Vector2 v in input)
            {
                // Create the vector angle and distance attributes.
                VectorAttributes vectorAttr = new VectorAttributes
                {
                    Vector          = v,
                    Angle           = VectorUtils.FindAngle(lowest, v),
                    DistanceSquared = VectorUtils.DistanceSquared(lowest, v)
                };

                // Add the vector to the dictionary.
                if (dict.ContainsKey(vectorAttr.Angle))
                {
                    // If the distance is greater, update the value in the dictionary.
                    if (dict[vectorAttr.Angle].DistanceSquared < vectorAttr.DistanceSquared)
                    {
                        dict[vectorAttr.Angle] = vectorAttr;
                    }
                }
                else
                {
                    // A vector for this angle doesn't exist, so add one.
                    dict.Add(vectorAttr.Angle, vectorAttr);
                }
            }

            // Create a list of the vectors.
            VectorAttributes lowestVectorAttr = new VectorAttributes
            {
                Vector          = input[0],
                Angle           = VectorUtils.FindAngle(lowest, input[0]),
                DistanceSquared = VectorUtils.DistanceSquared(lowest, input[0])
            };
            List <VectorAttributes> vectorAttributeList = new List <VectorAttributes>();

            vectorAttributeList.Add(lowestVectorAttr);
            vectorAttributeList.AddRange(dict.Values.ToList());

            // Sort remaining vectors based on angle with lowest point.
            vectorAttributeList = vectorAttributeList.OrderBy(v => v.Angle).ToList();

            // The guts of the Graham Scan algorithm.
            List <Vector2> output = new List <Vector2>();

            output.Add(vectorAttributeList[0].Vector);
            output.Add(vectorAttributeList[1].Vector);
            output.Add(vectorAttributeList[2].Vector);

            for (int i = 3; i < vectorAttributeList.Count; ++i)
            {
                while (VectorUtils.Orientation(output[output.Count - 2], output[output.Count - 1], vectorAttributeList[i].Vector) < 0)
                {
                    // Remove vectors that make a clockwise turn.
                    output.RemoveAt(output.Count - 1);
                }
                output.Add(vectorAttributeList[i].Vector);
            }

            return(output);
        }