// If the number of points is less than the grainSize, the Run method // simply finds the lower hull by going through all points from right to // left, as described in the sequential convex hull algorithm. // Otherwise we split the points in two and find the lower hulls // concurrently. // Finally we merge the two lower hulls and return the lower hull. public override List <Point> Run() { int length = end - start + 1; if (length < grainSize) { List <Point> L = new List <Point>(length); int j = end; while (j > end - 2 && j >= start) { L.Add(points[j]); j--; } for (int i = end - 2; i >= start; i--) { while (L.Count > 1 && STL.Turn(L[L.Count - 2], L[L.Count - 1], points[i]) <= 0) { L.RemoveAt(L.Count - 1); } L.Add(points[i]); } return(L); } else { int middle = (length / 2) + start; List <Point>[] res = Parallel.Gather( new LowerHull(points, start, middle, grainSize), new LowerHull(points, middle + 1, end, grainSize)); return(MergeLowerHulls(res[0], res[1])); } }
// Find the convex hull public List <Point> ConvexHull(List <Point> points) { int length = points.Count; if (length < 3) { return(points); } // Sort the points points.Sort(); // Find upper hull List <Point> U = new List <Point>(length); U.Insert(0, points[0]); U.Insert(1, points[1]); for (int i = 2; i < length; i++) { while (U.Count > 1 && STL.Turn(U[U.Count - 2], U[U.Count - 1], points[i]) <= 0) { U.RemoveAt(U.Count - 1); } U.Add(points[i]); } // Find lower hull List <Point> L = new List <Point>(length); L.Insert(0, points[length - 1]); L.Insert(1, points[length - 2]); for (int j = length - 3; j >= 0; j--) { while (L.Count > 1 && STL.Turn(L[L.Count - 2], L[L.Count - 1], points[j]) <= 0) { L.RemoveAt(L.Count - 1); } L.Add(points[j]); } // Combine upper hull and lower hull U.RemoveAt(U.Count - 1); U.AddRange(L); U.RemoveAt(U.Count - 1); return(U); }
// MergeLowerHulls merge two lower hulls in O(n) time. private List <Point> MergeLowerHulls(List <Point> P1, List <Point> P2) { int lengthP1 = P1.Count; int lengthP2 = P2.Count; int currentP1Index = 0; int currentP2Index = lengthP2 - 1; bool done = false; while (!done) { done = true; if (currentP2Index > 0 && STL.Turn(P1[currentP1Index], P2[currentP2Index], P2[currentP2Index - 1]) > 0) { done = false; currentP2Index--; while (currentP2Index > 0 && !LowerTangent(P1[currentP1Index], P2[currentP2Index], P2[currentP2Index - 1], P2[currentP2Index + 1])) { currentP2Index--; } } if (currentP1Index < lengthP1 - 1 && STL.Turn(P2[currentP2Index], P1[currentP1Index], P1[currentP1Index + 1]) < 0) { done = false; currentP1Index++; while (currentP1Index < lengthP1 - 1 && !LowerTangent(P2[currentP2Index], P1[currentP1Index], P1[currentP1Index + 1], P1[currentP1Index - 1])) { currentP1Index++; } } } List <Point> L = new List <Point>(); L.AddRange(P2.GetRange(0, currentP2Index + 1)); L.AddRange(P1.GetRange(currentP1Index, lengthP1 - currentP1Index)); return(L); }
// If the number of points is less than the grainSize, the Run method // simply finds the upper hull by going through all points from left to // right, as described in the sequential convex hull algorithm. // Otherwise we split the points in two and find the upper hulls // concurrently. // Finally we merge the two upper hulls and return the upper hull. public override List <Point> Run() { int length = end - start + 1; if (length < grainSize) { List <Point> U = new List <Point>(length); int j = start; while (j < start + 2 && j <= end) { U.Add(points[j]); j++; } for (int i = start + 2; i <= end; i++) { while (U.Count > 1 && STL.Turn(U[U.Count - 2], U[U.Count - 1], points[i]) <= 0) { U.RemoveAt(U.Count - 1); } U.Add(points[i]); } return(U); } else { int middle = (length / 2) + start; // The Parallel.Gather is similar to the Parallel.Run method. It runs the submitted // tasks concurrently, gather the result from each task and returns array containing // all results. List <Point>[] res = Parallel.Gather( new UpperHull(points, start, middle, grainSize), new UpperHull(points, middle + 1, end, grainSize)); return(MergeUpperHulls(res[0], res[1])); } }
// The main algorithm is fairly simple. We simply // sort the points and find the lower and upper hulls // concurrently. Finally we combine them and return // the points in the convex hull. public List <Point> ConvexHull(List <Point> points) { int length = points.Count; // Sort the points, see the STL class // for more information on the // parallel quicksort implementation. STL.QuickSort(points, 10); // Find Upper and Lower hull // The Parallel.Gather is similar to the Parallel.Run method. It runs the submitted // tasks concurrently, gather the result from each task and returns array containing // all results. List <Point>[] LU = Parallel.Gather( new UpperHull(points, 0, length - 1, 10), new LowerHull(points, 0, length - 1, 10)); // Combine Upper and Lower hull LU[0].RemoveAt(LU[0].Count - 1); LU[0].AddRange(LU[1]); LU[0].RemoveAt(LU[0].Count - 1); return(LU[0]); }
// Returns true if the tangent p0p1 is the lower tangent. // p0p1 is the tangent, p2 and p3 are the neighbors of p1. // p2 is the next point in the lower hull and p2 is the previous point. private bool LowerTangent(Point p0, Point p1, Point p2, Point p3) { return(!(STL.Turn(p0, p1, p2) < 0 && STL.Turn(p0, p1, p3) > 0)); }