
/* 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).
 */

/* DrawingPrimitives.cs
 *
 * Drawing primitives: Abstract circle and line creation methods.
 *
 * (C) Copyright 2004 -- Sebastian Nowozin (nowozin@cs.tu-berlin.de)
 * Parts (C) Copyright 1988 -- Steve Cunningham and Tim Worsham
 */

using System;
using System.Collections;


// Improved Bresenham integer-only algorithm with minimal special case
// handling. By Steve Cunningham and Tim Worsham, October 1988, CSU Stanislaus
// (Appendix A from http://www.cs.csustan.edu/~rsc/SDSU/Interpolation.pdf)
//
// slightly modified for its use here

public class BresenhamLine
{
	// collect all points lying on the line between (x1,y1) to (x2,y2).
	public static ArrayList DrawLine (int x1, int y1, int x2, int y2)
	{
		int minX = 0;
		int minY = 0;

		if (x1 < minX)
			minX = x1;
		if (x2 < minX)
			minX = x2;
		if (y1 < minY)
			minY = y1;
		if (y2 < minY)
			minY = y2;

		if (minX < 0 || minY < 0) {
			minX = -minX;
			minY = -minY;

			// transpose into positive coordinates
			ArrayList points = DrawLineInternal (x1 + minX, y1 + minY,
				x2 + minX, y2 + minY);
			foreach (Point point in points) {
				point.x -= minX;
				point.y -= minY;
			}

			return (points);
		}

		return (DrawLineInternal (x1, y1, x2, y2));
	}

	// collect all points lying on the line between (x1,y1) to (x2,y2).
	private static ArrayList DrawLineInternal (int x1, int y1, int x2, int y2)
	{
		int dx, dy, bx, by, xsign, ysign, p, const1, const2;
		int sign;

		bx = x1;
		by = y1;
		dx = (x2 - x1);
		dy = (y2 - y1);
		xsign = (dx >= 0) ? 1 : -1;
		ysign = (dy >= 0) ? 1 : -1;

		ArrayList points = new ArrayList ();

		// horizontal line?
		if (dy == 0) {
			points.Add (new Point (bx, by));

			while (bx != x2) {
				bx += xsign;
				points.Add (new Point (bx, by));
			}
		} else if (dx == 0) {
			// vertical line?
			points.Add (new Point (bx, by));

			while (by != y2) {
				by += ysign;
				points.Add (new Point (bx, by));
			}
		} else {
			// stock Bresenham algorithm
			if (dx < 0)
				dx = -dx;
			if (dy < 0)
				dy = -dy;

			points.Add (new Point (bx, by));

			// line more vertical than horizontal
			if (dx < dy) {
				p = 2 * dx - dy;
				const1 = 2 * dx;
				const2 = 2 * (dx - dy);

				while (by != y2) {
					by += ysign;

					if (p < 0) {
						p += const1;
					} else {
						p += const2;
						bx += xsign;
					}

					points.Add (new Point (bx, by));
				}
			} else {
				// line more horizontal than vertical
				p = 2 * dy - dx;
				const2 = 2 * (dy - dx);
				const1 = 2 * dy;

				while (bx != x2) {
					bx += xsign;

					if (p < 0) {
						p += const1;
					} else {
						p += const2;
						by += ysign;
					}

					points.Add (new Point (bx, by));
				}
			}
		}

		return (points);
	}
}


public class Circle
{
	// holds Point objects
	ArrayList circlePoints = null;
	ArrayList[] circlePointsSegments;
	public ArrayList CirclePoints {
		get {
			return (circlePoints);
		}
	}

	public Circle ()
	{
	}

	// algorithm slightly modified from:
	// http://www.cs.unc.edu/~davemc/Class/136/Lecture10/circle.html
	//
	// radius is given in pixels
	// afterwards, circlePoints holds the pixels in clockwise order, starting
	//   from the eastmost one (y = 0, x = radius) pixel.
	public ArrayList DrawWithRadius (int radius)
	{
		circlePointsSegments = new ArrayList[8];
		for (int n = 0 ; n < 8 ; ++n)
			circlePointsSegments[n] = new ArrayList ();

        int x = 0;
		int y = radius;
		int p = (5 - radius*4) / 4;

		AddEightPoints (x, y);
		while (x < y) {
			x += 1;

			if (p < 0) {
				p += 2 * x + 1;
			} else {
				y -= 1;
				p += 2 * (x-y) + 1;
			}

			AddEightPoints (x, y);
		}

		// join all 8-symmetric parts, some after inversion
		circlePoints = new ArrayList ();
		circlePointsSegments[0].Reverse ();
		circlePointsSegments[2].Reverse ();
		circlePointsSegments[4].Reverse ();
		circlePointsSegments[6].Reverse ();

		int[] shuffleArray = { 1, 0, 3, 2, 5, 4, 7, 6 };
		for (int n = 0 ; n < 8 ; ++n) {
			circlePoints.AddRange ((ICollection)
				circlePointsSegments[shuffleArray[n]]);
		}

		// delete duplicate points, including the first-last pair
		ArrayList cleanCopy = new ArrayList ();
		Point last = (Point) null;
		foreach (Point point in circlePoints) {
			if (last == null || last.x != point.x || last.y != point.y)
				cleanCopy.Add (new Point (point.x, point.y));

			last = point;
		}
		Point first = (Point) cleanCopy[0];
		if (first.x == last.x && first.y == last.y)
			cleanCopy.RemoveAt (cleanCopy.Count - 1);

		circlePoints = cleanCopy;

		return (circlePoints);
	}

	private void AddEightPoints (int x, int y)
	{
		circlePointsSegments[0].Add (new Point (x, y));
		circlePointsSegments[1].Add (new Point (y, x));
		circlePointsSegments[2].Add (new Point (-y, x));
		circlePointsSegments[3].Add (new Point (-x, y));
		circlePointsSegments[4].Add (new Point (-x, -y));
		circlePointsSegments[5].Add (new Point (-y, -x));
		circlePointsSegments[6].Add (new Point (y, -x));
		circlePointsSegments[7].Add (new Point (x, -y));
	}

}


// relative points to the circle center.
public sealed class Point
{
	private Point ()
	{
	}

	public Point (int x, int y)
	{
		this.x = x;
		this.y = y;
	}

	public int x;
	public int y;
}


