
/* autopano-sift, Automatic panorama image creation
 * Copyright (C) 2004 -- Sebastian Nowozin
 *
 * This program is free software released under the GNU General Public
 * License, which is included in this software package (doc/LICENSE).
 */

/* AreaFilter.cs
 *
 * Area maximizing match filtration using convex hull and polygon-area
 * maximization.
 *
 * (C) Copyright 2004 -- Sebastian Nowozin (nowozin@cs.tu-berlin.de)
 *
 * Convex hull code based on Ken Clarksons original C implementation.
 * Polygon area formula by Paul Bourke.
 */

using System;
using System.Collections;


public class
AreaFilter
{

	public static void Main (string[] args)
	{
		ArrayList al = new ArrayList ();
		Random rnd = new Random ();

		for (int n = 0 ; n < 10 ; ++n) {
			FilterPoint p = new FilterPoint ();

			p.x = rnd.NextDouble () * 400.0;
			p.y = rnd.NextDouble () * 400.0;

			al.Add (p);
		}
		foreach (FilterPoint p in al)
			Console.WriteLine ("{0} {1} # GNUPLOT1", p.x, p.y);

		AreaFilter conv = new AreaFilter ();
		ArrayList hull = conv.CreateConvexHull (al);

		Console.WriteLine ("\nhull: {0} elements", hull.Count);
		foreach (FilterPoint p in hull)
			Console.WriteLine ("{0} {1} # GNUPLOT2", p.x, p.y);

		Console.WriteLine ("\npolygon area: {0}", conv.PolygonArea (hull));
	}

	// Measure the absolute area of a non self-intersecting polygon.
	// Formula by Paul Bourke
	// (http://astronomy.swin.edu.au/~pbourke/geometry/polyarea/)
	//
	// The input points must be ordered (clock- or counter-clockwise). The
	// polygon is closed automatically between the first and the last point,
	// so there should not be two same points in the polygon point set.
	public double PolygonArea (ArrayList orderedPoints)
	{
		double A = 0.0;

		for (int n = 0 ; n < orderedPoints.Count ; ++n) {
			FilterPoint p0 = (FilterPoint) orderedPoints[n];
			FilterPoint p1 = (FilterPoint) orderedPoints[(n + 1) % orderedPoints.Count];

			A += p0.x * p1.y - p1.x * p0.y;
		}

		A *= 0.5;

		return (Math.Abs (A));
	}

	// Create the convex hull of a point set.
	public ArrayList CreateConvexHull (ArrayList points)
	{
		return (CreateConvexHull ((FilterPoint[]) points.ToArray (typeof (FilterPoint))));
	}

	public ArrayList CreateConvexHull (FilterPoint[] points)
	{
		int chn = CreateHull (points);

		ArrayList hull = new ArrayList ();
		for (int k = 0 ; k < chn ; ++k)
			hull.Add (points[k]);

		return (hull);
	}

	public class CompareLow : IComparer {
		public int Compare (object o1, object o2)
		{
			FilterPoint p1 = (FilterPoint) o1;
			FilterPoint p2 = (FilterPoint) o2;

			double v = p1.x - p2.x;
			if (v > 0)
				return (1);
			if (v < 0)
				return (-1);

			v = p2.y - p1.y;
			if (v > 0)
				return (1);
			if (v < 0)
				return (-1);

			// Equal point
			return (0);
		}
	}

	public class CompareHigh : IComparer {
		CompareLow cl;

		public CompareHigh ()
		{
			cl = new CompareLow ();
		}

		public int Compare (object o1, object o2)
		{
			return (cl.Compare (o2, o1));
		}
	}


	private bool ccw (FilterPoint[] points, int i, int j, int k)
	{
		double a = points[i].x - points[j].x;
		double b = points[i].y - points[j].y;
		double c = points[k].x - points[j].x;
		double d = points[k].y - points[j].y;

		return ((a * d - b * c) <= 0.0);
	}

	private int MakeChain (FilterPoint[] points, IComparer comp)
	{
		System.Array.Sort (points, comp);

		int s = 1;
		int pCount = points.Length;
		for (int i = 2 ; i < pCount ; ++i) {
			int j;
			for (j = s ; j >= 1 && ccw (points, i, j, j - 1) ; --j)
				;

			s = j + 1;

			// Swap
			FilterPoint t = points[s];
			points[s] = points[i];
			points[i] = t;
		}

		return (s);
	}

	private int CreateHull (FilterPoint[] points)
	{
		int u = MakeChain (points, new CompareLow ());

		if (points.Length == 0)
			return (0);
		/*
		for (int k = 0 ; k < u ; ++k)
			Console.WriteLine ("point {0}: {1} {2}", k, points[k].x, points[k].y);
			*/

		FilterPoint[] pointsHigh = new FilterPoint[points.Length + 1 - u];
		//Console.WriteLine ("points.Length = {0}, u = {1}", points.Length, u);
		System.Array.Copy (points, u, pointsHigh, 0, points.Length - u);
		pointsHigh[pointsHigh.Length - 1] = points[0];

		int h = MakeChain (pointsHigh, new CompareHigh ());

		for (int p = u ; p < points.Length ; ++p)
			points[p] = pointsHigh[p - u];

		/*
		Console.WriteLine ("h = {0}, u = {1}", h, u);
		for (int k = 0 ; k < (h + u) ; ++k)
			Console.WriteLine ("point {0}: {1} {2}", k, points[k].x, points[k].y);
			*/

		return (h + u);
	}
}

