C# Syntax Example
// Finding a convex hull in the plane
// This program requires .Net version 2.0.
// Peter Sestoft (sestoft@dina.kvl.dk) * Java 2000-10-07, GC# 2001-10-27
using System;
// ------------------------------------------------------------
// Find the convex hull of a point set in the plane
// An implementation of Graham's (1972) point elimination algorithm,
// as modified by Andrew (1979) to find lower and upper hull separately.
// This implementation correctly handles duplicate points, and
// multiple points with the same x-coordinate.
// 1. Sort the points lexicographically by increasing (x,y), thus
// finding also a leftmost point L and a rightmost point R.
// 2. Partition the point set into two lists, upper and lower, according as
// point is above or below the segment LR. The upper list begins with
// L and ends with R; the lower list begins with R and ends with L.
// 3. Traverse the point lists clockwise, eliminating all but the extreme
// points (thus eliminating also duplicate points).
// 4. Eliminate L from lower and R from upper, if necessary.
// 5. Join the point lists (in clockwise order) in an array.
class Convexhull {
public static Point[] convexhull(Point[] pts) {
// Sort points lexicographically by increasing (x, y)
int N = pts.Length;
Polysort.Quicksort<Point>(pts);
Point left = pts[0], right = pts[N-1];
// Partition into lower hull and upper hull
CDLL<Point> lower = new CDLL<Point>(left), upper = new CDLL<Point>(left);
for (int i=0; i<N; i++) {
double det = Point.Area2(left, right, pts[i]);
if (det > 0)
upper = upper.Append(new CDLL<Point>(pts[i]));
else if (det < 0)
lower = lower.Prepend(new CDLL<Point>(pts[i]));
}
lower = lower.Prepend(new CDLL<Point>(right));
upper = upper.Append(new CDLL<Point>(right)).Next;
// Eliminate points not on the hull
eliminate(lower);
eliminate(upper);
// Eliminate duplicate endpoints
if (lower.Prev.val.Equals(upper.val))
lower.Prev.Delete();
if (upper.Prev.val.Equals(lower.val))
upper.Prev.Delete();
// Join the lower and upper hull
Point[] res = new Point[lower.Size() + upper.Size()];
lower.CopyInto(res, 0);
upper.CopyInto(res, lower.Size());
return res;
}
// Graham's scan
private static void eliminate(CDLL<Point> start) {
CDLL<Point> v = start, w = start.Prev;
bool fwd = false;
while (v.Next != start || !fwd) {
if (v.Next == w)
fwd = true;
if (Point.Area2(v.val, v.Next.val, v.Next.Next.val) < 0) // right turn
v = v.Next;
else { // left turn or straight
v.Next.Delete();
v = v.Prev;
}
}
}
}
// ------------------------------------------------------------
// Points in the plane
class Point : Ordered<Point> {
private static readonly Random rnd = new Random();
public double x, y;
public Point(double x, double y) {
this.x = x; this.y = y;
}
public override string ToString() {
return "(" + x + ", " + y + ")";
}
public static Point Random(int w, int h) {
return new Point(rnd.Next(w), rnd.Next(h));
}
public bool Equals(Point p2) {
return x == p2.x && y == p2.y;
}
public override bool Less(Ordered<Point> o2) {
Point p2 = (Point)o2;
return x < p2.x || x == p2.x && y < p2.y;
}
// Twice the signed area of the triangle (p0, p1, p2)
public static double Area2(Point p0, Point p1, Point p2) {
return p0.x * (p1.y-p2.y) + p1.x * (p2.y-p0.y) + p2.x * (p0.y-p1.y);
}
}
// ------------------------------------------------------------
// Circular doubly linked lists of T
class CDLL<T> {
private CDLL<T> prev, next; // not null, except in deleted elements
public T val;
// A new CDLL node is a one-element circular list
public CDLL(T val) {
this.val = val; next = prev = this;
}
public CDLL<T> Prev {
get { return prev; }
}
public CDLL<T> Next {
get { return next; }
}
// Delete: adjust the remaining elements, make this one point nowhere
public void Delete() {
next.prev = prev; prev.next = next;
next = prev = null;
}
public CDLL<T> Prepend(CDLL<T> elt) {
elt.next = this; elt.prev = prev; prev.next = elt; prev = elt;
return elt;
}
public CDLL<T> Append(CDLL<T> elt) {
elt.prev = this; elt.next = next; next.prev = elt; next = elt;
return elt;
}
public int Size() {
int count = 0;
CDLL<T> node = this;
do {
count++;
node = node.next;
} while (node != this);
return count;
}
public void PrintFwd() {
CDLL<T> node = this;
do {
Console.WriteLine(node.val);
node = node.next;
} while (node != this);
Console.WriteLine();
}
public void CopyInto(T[] vals, int i) {
CDLL<T> node = this;
do {
vals[i++] = node.val; // still, implicit checkcasts at runtime
node = node.next;
} while (node != this);
}
}
// ------------------------------------------------------------
class Polysort {
private static void swap<T>(T[] arr, int s, int t) {
T tmp = arr[s]; arr[s] = arr[t]; arr[t] = tmp;
}
// Typed OO-style quicksort a la Hoare/Wirth
private static void qsort<T>(Ordered<T>[] arr, int a, int b) {
// sort arr[a..b]
if (a < b) {
int i = a, j = b;
Ordered<T> x = arr[(i+j) / 2];
do {
while (arr[i].Less(x)) i++;
while (x.Less(arr[j])) j--;
if (i <= j) {
swap< Ordered<T> >(arr, i, j);
i++; j--;
}
} while (i <= j);
qsort<T>(arr, a, j);
qsort<T>(arr, i, b);
}
}
public static void Quicksort<T>(Ordered<T>[] arr) {
qsort<T>(arr, 0, arr.Length-1);
}
}
public abstract class Ordered<T> {
public abstract bool Less(Ordered<T> that);
}
// ------------------------------------------------------------
class TestCH {
static void Main(string[] args) {
if (args.Length == 1) {
int N = int.Parse(args[0]);
Point[] pts = new Point[N];
for (int i=0; i<N; i++)
pts[i] = Point.Random(500, 500);
Point[] chpts = Convexhull.convexhull(pts);
Console.WriteLine("Area is " + area(chpts));
print(chpts);
} else
Console.WriteLine("Usage: TestCH <pointcount>\n");
}
// The centroid of a point set
public static Point centroid(Point[] pts) {
int N = pts.Length;
double sumx = 0, sumy = 0;
for (int i=0; i<N; i++) {
sumx += pts[i].x;
sumy += pts[i].y;
}
return new Point(sumx/N, sumy/N);
}
// The area of a polygon (represented by an array of ordered vertices)
public static double area(Point[] pts) {
int N = pts.Length;
Point centr = centroid(pts);
double area2 = 0;
for (int i=0; i<N; i++)
area2 += Point.Area2(centr, pts[i], pts[(i+1)%N]);
return Math.Abs(area2/2);
}
public static void print(Point[] pts) {
int N = pts.Length;
for (int i=0; i<N; i++)
Console.WriteLine(pts[i]);
}
}