#region Written by Tamas Budavari (budavari@jhu.edu)
/**
 * SphericalLib.Convex
 * 
 * SphericalLib is a .NET library to deal with geometry on the unit sphere.
 * It is intented to be used for complex survey footprint descriptions in
 * standalone applications as well as in web services and perhaps Yukon.
 * 
 * Written by Tamas Budavari (budavari@jhu.edu)
 * See bottom of file for revision history
 * 
 * Current revision:
 *   ID:          $Id: Convex.cs,v 1.1.1.1 2006/11/14 02:56:39 gupchup Exp $
 *   Revision:    $Revision: 1.1.1.1 $
 *   Date:        $Date: 2006/11/14 02:56:39 $
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Xml.Serialization;
#endregion

namespace Spherical
{
	/// <summary>
	/// Convex is the intersection of a set of halfspaces
	/// </summary>
    [Serializable, XmlRoot(Namespace = Const.NameSpace)]
    [CLSCompliant(true)]
	public class Convex
    {
        #region Private members and keyword definitions

        private string name;
        private bool simplified;
        private List<Halfspace> halfspaceList;
        private List<Patch> patchList;
        private double? area;

		private static readonly string keyword = "CONVEX";
        private static readonly string keywordPoly = "POLY";
        public static string Keyword { get { return keyword; } }
        public static string KeywordPoly { get { return keywordPoly; } }

        #endregion
        #region Public properties

        [XmlAttribute("Name")]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        [XmlIgnore]
        public ReadOnlyCollection<Halfspace> HalfspaceList
        {
            get { return halfspaceList.AsReadOnly(); }
        }

        [XmlArray(ElementName="HalfspaceList", Namespace=Const.NameSpace)]
        public Halfspace[] x___DoNotEverTouchMe___
        {
            get { return halfspaceList.ToArray(); }
            set
            {
                throw new ApplicationException
                ("x__DoNotEverTouchMe__ should not be touched");
            }
        }

        [XmlIgnore]
        public bool Simplified
        {
            get { return simplified; }
            set
            {
                if (simplified == true && value == false)
                {
                    simplified = false;
                    patchList = null;
                    area = null;
                }
                else
                    simplified = value;
            }
        }

        public double? Area
        {
            get
            {
                if (!simplified)
                {
                    return null;
                }
                else if (area == null)
                {
                    this.area = 0;
                    // add up area of patches
                    if (this.halfspaceList != null)
                    {
                        this.area = 0;
                        foreach (Patch p in patchList)
                            this.area += p.Area;
                        // negative -> positive
                        if (this.area < 0 || (this.area < double.Epsilon && this.halfspaceList[0].Cos0 < -1 + Const.Epsilon))
                            this.area += Const.WholeSphereInSquareDegree;
                    }
                }
                return area;
            }
            set 
            {
                throw new ApplicationException
                    ("Convex.Area.set: should not be used");
            }
        }

        [XmlIgnore]
        public ReadOnlyCollection<Patch> PatchList
        {
            get
            {
                if (simplified == false)
                    throw new AccessViolationException
                        ("Convex.PatchList: not accessible until simplified");
                if (patchList == null)
                    return null;
                else
                    return patchList.AsReadOnly();
            }

        }

        // Extended sign property
        public ESign ESign
        {
            get
            {
                ESign s = this.halfspaceList[0].ESign;
                foreach (Halfspace h in this.halfspaceList)
                    if (s != h.ESign) return ESign.Mixed;
                return s;
            }
        }

        #endregion
		#region Constructors and static cloning

		/// <summary>
		/// Create an empty convex
		/// </summary>
		public Convex() 
		{
            halfspaceList = new List<Halfspace>();
            name = null;
            this.Simplified = false;
		}

        /// <summary>
        /// Create an empty convex with given capacity
        /// </summary>
        /// <param name="capacity"></param>
        public Convex(int capacity)
        {
            halfspaceList = new List<Halfspace>(capacity);
            name = null;
            this.Simplified = false;
        }

        /// <summary>
        /// Create an empty convex with given name and capacity
        /// </summary>
        /// <param name="name"></param>
        /// <param name="capacity"></param>
        public Convex(string name, int capacity)
        {
            this.name = name;
            halfspaceList = new List<Halfspace>(capacity);
            this.Simplified = false;
        }

		/// <summary>
		/// Create convex with a single halfspace in it
		/// </summary>
		/// <param name="halfSpace"></param>
		public Convex(Halfspace halfspace)
		{
            halfspaceList = new List<Halfspace>(1);
			halfspaceList.Add(halfspace);
            name = null;
            this.Simplified = false;
		}

        /// <summary>
        /// Create convex from a collection of halfspaces
        /// </summary>
        /// <param name="halfSpaces"></param>
        public Convex(IEnumerable<Halfspace> halfspaces)
        {
            halfspaceList = new List<Halfspace>(4);
            halfspaceList.AddRange(halfspaces);
            name = null;
            this.Simplified = false;
        }

		/// <summary>
		/// Copy constructor (clone halfspaces)
		/// </summary>
		/// <param name="convex"></param>
		public Convex(Convex convex)
		{
            this.halfspaceList = new List<Halfspace>(convex.halfspaceList); 
			this.name = convex.name;
            this.area = convex.area;
            this.simplified = convex.simplified;
            if (convex.patchList == null)
                this.patchList = null;
            else
                this.patchList = new List<Patch>(convex.patchList); // perhaps clone patches, too...
		}

		/// <summary>
		/// Create a polygon convex from a list of points that are in a specified order.
		/// </summary>
		/// <param name="list"></param>
		/// <param name="order"></param>
		public Convex(List<Cartesian> list, PointOrder order)
		{
            name = null;
            this.Simplified = false;

			if (list.Count < 3) 
			{
				throw new ArgumentException ("Not enough points in list for a polygon!");
			}
			// preprocess if needed
			if (order == PointOrder.CW) 
			{
				// reverse order
				list.Reverse();
			}
			else if (order == PointOrder.Safe) 
			{
				// check for order of first 3 point and reverse if right and not left
				Halfspace h = new Halfspace(list[0].Cross(list[1],true),0);
				if (!h.Covers(list[2],Const.Tolerance)) list.Reverse();
			}
			else if (order == PointOrder.Random)
			{
				// run convex hull?
				throw new ArgumentException("not implemented");
			}

			// Assuming they are in 'left order' and within a half sphere
			this.halfspaceList = new List<Halfspace>(list.Count);
			for (int i=1; i<list.Count; i++)
			{
				// left is inside: center = p_{i-1} x p_{i} (normalized)
				Halfspace h = new Halfspace(list[i-1].Cross(list[i],true),0);
				this.halfspaceList.Add(h);
			}
			// close polygon by connecting last point 'list[-1]' with first 'list[0]'
			this.halfspaceList.Add(new Halfspace(list[list.Count-1].Cross(list[0],true),0));
		}

        /// <summary>
        /// Return clone of given convex
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public static Convex Clone(Convex c)
        {
            return new Convex(c);
        }

        /// <summary>
        /// Set patchlist (if you really know what you are doing)
        /// <remarks>Used by IO routines</remarks>
        /// </summary>
        /// <param name="collection"></param>
        public void SetPatches(List<Patch> collection)
        {
            this.patchList = collection;
            this.area = null;
            this.Simplified = true;
        }

        /// <summary>
        /// Set area (if you really know what you are doing)
        /// <remarks>Used by IO routines</remarks>
        /// </summary>
        public void SetArea(double a)
        {
            this.area = a;
        }

        #endregion
        #region HalfspaceList management

        public void Add(Halfspace h)
        {
            this.Simplified = false;
            this.halfspaceList.Add(h);
        }

        public void AddRange(IEnumerable<Halfspace> collection)
        {
            this.Simplified = false;
            this.halfspaceList.AddRange(collection);
        }

        public void RemoveRange(int index, int count)
        {
            this.Simplified = false;
            this.halfspaceList.RemoveRange(index, count);
        }

        [XmlIgnore]
        public int Capacity
        {
            get { return halfspaceList.Capacity; }
            set { halfspaceList.Capacity = value; }
        }

        public int Count
        {
            get { return halfspaceList.Count; }
        }

        public bool TrueForAll(Predicate<Halfspace> match)
        {
            return halfspaceList.TrueForAll(match);
        }

        public bool Exists(Predicate<Halfspace> match)
        {
            return halfspaceList.Exists(match);
        }

        public void RemoveAt(int index)
        {
            this.Simplified = false;
            halfspaceList.RemoveAt(index);
        }

        public void Sort(Comparison<Halfspace> comparison)
        {
            this.halfspaceList.Sort(comparison);
        }

        public void Clear()
        {
            this.Simplified = false;
            this.halfspaceList.Clear();
        }

        #endregion
        #region Geometric relations

        /// <summary>
        /// Intersect this convex with an halfspace. Simply adds halfspace to list.
        /// </summary>
        /// <param name="h"></param>
        public void Intersect(Halfspace h)
        {
            this.Simplified = false;
            this.halfspaceList.Add(h);
        }

        /// <summary>
        /// Intersect this convex with another. Simply adds clones of the other's halfspaces.
        /// </summary>
        /// <param name="c"></param>
        public void Intersect(Convex c)
        {
            this.Simplified = false;
            int cap = this.halfspaceList.Count + c.halfspaceList.Count;
            if (this.halfspaceList.Capacity < cap)
                this.halfspaceList.Capacity = cap;
            this.halfspaceList.AddRange(c.halfspaceList);
        }

        /// <summary>
        /// Difference of this convex and another: X = I\C (in I but not in C)
        /// Algorithm: X = (I&~h1)|(I&h1&~h2)|(I&h1&h2&~h3)|... , where C=h1&h2&...
        /// </summary>
        /// <param name="convex"></param>
        /// <returns></returns>
        public Region Difference(Convex convex)
        {
            Region diff = new Region();
            // loop on halfspaces of convex 'C'
            for (int i = 0; i < convex.halfspaceList.Count; i++)
            {
                Convex tmp = new Convex(this); // clone me
                for (int j = 0; j < i; j++)
                {
                    tmp.Intersect(convex.halfspaceList[j]);
                }
                tmp.Intersect(convex.halfspaceList[i].Inverse());
                // Union: diff = diff | tmp
                diff.Union(tmp);
            }
            return diff;
        }

        /// <summary>
        /// Increase angles of all halfspaces by the given amount
        /// and return this
        /// </summary>
        /// <param name="arcmin"></param>
        public Convex Grow(double arcmin)
        {
            this.Simplified = false;
            //foreach (Halfspace h in halfspaceList)
            //    h.Grow(arcmin);
            for (int i = 0; i < halfspaceList.Count; i++)
            {
                Halfspace h = halfspaceList[i];
                halfspaceList[i] = h.Grow(arcmin);
            }
            return this;
        }

		/// <summary>
		/// Check if convex covers a point (x,y,z) unit vector
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="z"></param>
		/// <returns></returns>
		public bool Covers(double x, double y, double z, double tolerance)
		{
			// empty means no coverage (and not all sky)
			if (halfspaceList.Count==0) 
			{ 
				return false;
			}
			foreach (Halfspace h in this.halfspaceList) 
			{
				if(!h.Covers(x,y,z,tolerance)) 
				{
					return false;
				}
			}
			return true;
		}		
		
		/// <summary>
		/// Check if convex covers a point
		/// </summary>
		/// <param name="p"></param>
		/// <returns></returns>
		public bool Covers(Cartesian p, double tolerance)
		{
            return Covers(p.X, p.Y, p.Z, tolerance);
		}

        /// <summary>
        /// Return indicies of halfspaces that cover and that mask the give point
        /// </summary>
        /// <param name="p"></param>
        /// <param name="tolerance"></param>
        /// <param name="covers"></param>
        /// <param name="masks"></param>
        public void IndexCoversMasks(Cartesian p, double tolerance, out List<int> covers, out List<int> masks)
        {
            covers = new List<int>(halfspaceList.Count);
            masks = new List<int>(halfspaceList.Count);
            for (int i = 0; i < halfspaceList.Count; i++)
                if (halfspaceList[i].Covers(p.X, p.Y, p.Z, tolerance))
                    covers.Add(i);
                else
                    masks.Add(i);
        }

		/// <summary>
		/// Check if convex covers a halfspace
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="z"></param>
		/// <returns></returns>
		public bool Covers(Halfspace h, double tolerance)
		{
			// empty means no coverage (and not all sky)
			if (halfspaceList.Count==0) 
			{ 
				return false;
			}
			foreach (Halfspace g in this.halfspaceList) 
			{
				if(!g.Covers(h,tolerance)) 
				{
					return false;
				}
			}
			return true;
		}

        /// <summary>
        /// Check if this covers a list of points
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public bool Covers(IEnumerable<Cartesian> list, double tolerance)
        {
            foreach (Cartesian p in list)
                if (!this.Covers(p.X, p.Y, p.Z, tolerance))
                    return false;
            return true;
        }

        /// <summary>
        /// Count how many points are covered
        /// </summary>
        /// <param name="arr"></param>
        /// <returns></returns>
        public int CoversHowMany(IEnumerable<Cartesian> arr, double tolerance)
        {
            int n = 0;
            foreach (Cartesian p in arr)
                if (Covers(p.X, p.Y, p.Z, tolerance))
                    n++;
            return n;
        }

        /// <summary>
        /// Count how many points are excluded by this
        /// </summary>
        /// <param name="arr"></param>
        /// <returns></returns>
        public int MasksHowMany(IEnumerable<Cartesian> arr, double tolerance)
        {
            int n = 0;
            foreach (Cartesian p in arr)
                if (!Covers(p.X, p.Y, p.Z, tolerance))
                    n++;
            return n;
        }

        /// <summary>
        /// Count how many roots are excluded by this constraint
        /// </summary>
        /// <param name="arr"></param>
        /// <returns></returns>
        internal int MasksHowMany(IEnumerable<Root> arr, double tolerance)
        {
            int n = 0;
            foreach (Root r in arr)
                if (!Covers(r.Point, tolerance))
                    n++;
            return n;
        }


		#endregion
        #region Simplification (simple and full)

        /// <summary>
        /// Simplify description of convex by removing redundant halfspaces. 
        /// Return 'false' if empty, 'true' otherwise.
        /// </summary>
        public bool SimpleSimplify(double tolerance)
        {
            List<int> iDelete = new List<int>();
            bool empty = false, disjoint, same, covers;

            // if empty, done
            if (halfspaceList.Count == 0)
            {
                return false;
            }

            // if only one halfspace but infinitesimal, empty it and done
            if (halfspaceList.Count == 1 && halfspaceList[0].Cos0 > 1 - tolerance)
            {
                halfspaceList.RemoveAt(0);
                return false;
            }

            // check pairwise relations
            for (int i = 0; i < halfspaceList.Count; i++)
            {
                Halfspace Hi = halfspaceList[i];
                for (int j = 0; j < halfspaceList.Count; j++)
                {
                    if (i == j || iDelete.Contains(j)) continue;
                    Halfspace Hj = halfspaceList[j];
                    // mark 'i' for removal and break continue with next halfspace
                    covers = Hi.Covers(Hj, tolerance);
                    same = Hi.Same(Hj, tolerance);
                    if (same || covers)
                    {
                        iDelete.Add(i);
                        break;
                    }
                    // empty convex if disjoint
                    disjoint = Hi.Disjoint(Hj, tolerance);
                    same = Hi.Same(Hj.Inverse(), tolerance);
                    if (Hi.Cos0 > 1 || disjoint || same)
                    {
                        empty = true;
                        break;
                    }
                }
                if (empty) break;
            }

            // if empty - purge halfspaces
            if (empty)
            {
                halfspaceList.Clear();
            }
            else // remove redundant halfspaces 
            {
                for (int i = iDelete.Count - 1; i >= 0; i--)
                    halfspaceList.RemoveAt(iDelete[i]);

#if true
                if (halfspaceList.Count == 0)
                    throw new ApplicationException
                        ("Convex.SimpleSimplify(): no halfspaces left?");
#else
                empty = true;
#endif
            }
            return !empty;
        }

        public bool Simplify()
        {
            return Simplify(true, Const.Tolerance, Const.MaximumIteration);
        }

        public bool Simplify(double tolerance)
        {
            return Simplify(true, tolerance, Const.MaximumIteration);
        }

        public bool Simplify(bool simple_simplify, double tolerance, int iteration)
        {
            // if already simplified, nothing to do
            if (this.simplified) return true;

            // trivial simplification
            if (simple_simplify)
            {
                bool not_empty = this.SimpleSimplify(tolerance);
                if (!not_empty)
                {
                    this.simplified = true;
                    this.area = 0;
                    return false;
                }
            }

            // build patches and fully simplify convex
            PatchFactory f = new PatchFactory(this, tolerance);
            patchList = f.DerivePatches(tolerance, iteration, true);
            this.simplified = true;

            if (this.halfspaceList == null || this.halfspaceList.Count == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        #endregion
        #region Smart methods using patches and the kind

        /// <summary>
        /// (Re)calculate the area using a given pivot/center point
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public double CalcArea(Cartesian c)
        {
            double area = 0;
            foreach (Patch p in patchList)
                area += p.CalcArea(c);
            if (area < 0 || (area < Const.Epsilon && this.halfspaceList[0].Cos0 < -1 + Const.Epsilon))
                area += Const.WholeSphereInSquareDegree;
            return area;
        }

        /// <summary>
        /// Check if the minimal enclosing circles of the patches
        /// contain the given point (within the given tolerance)
        /// </summary>
        /// <param name="x"></param>
        /// <param name="tolerance"></param>
        /// <returns></returns>
        public bool MecCovers(Cartesian x, double tolerance)
        {
            foreach (Patch p in patchList)
            {
                if (p.Mec.Covers(x, tolerance))
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Check if the given point is covered but first test the minimal
        /// enclosing circles of the patches. Uses the given tolerance.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="tolerance"></param>
        /// <returns></returns>
        public bool SmartCovers(Cartesian x, double tolerance)
        {
            if (this.MecCovers(x, tolerance) && this.Covers(x, tolerance))
                return true;
            else
                return false;
        }

        /// <summary>
        /// Check to see if the minimal enclosing circles of the patches are disjoint
        /// </summary>
        /// <param name="c"></param>
        /// <param name="tolerance"></param>
        /// <returns></returns>
        public bool MecDisjoint(Convex c, double tolerance)
        {
            foreach (Patch p in this.patchList)
                foreach (Patch q in c.patchList)
                    if (!p.Mec.Disjoint(q.Mec, tolerance))
                        return false;
            return true;
        }

        /// <summary>
        /// Intersect this with another, return 'false' if empty, 'true' otherwise
        /// </summary>
        /// <param name="c"></param>
        public bool SmartIntersect(Convex c, double tolerance, int iteration)
        {
            if (this.MecDisjoint(c, tolerance))
            {
                this.halfspaceList.Clear();
                this.patchList = null;
                this.area = 0;
                this.simplified = true;
                return false;
            }
            else
            {
                this.Intersect(c);
                return this.Simplify(true, tolerance, iteration);
            }
        }

        #endregion
        #region Area comparison methods

        /// <summary>
        /// Area comparison function to be used for sorting, etc. (ascending)
        /// </summary>
        /// <param name="c"></param>
        /// <param name="k"></param>
        /// <returns></returns>
        public static int ComparisonAreaAscending(Convex c, Convex k)
        {
            return c.Area.Value.CompareTo(k.Area.Value);
        }

        /// <summary>
        /// Area comparison function to be used for sorting, etc. (descending)
        /// </summary>
        /// <param name="c"></param>
        /// <param name="k"></param>
        /// <returns></returns>
        public static int ComparisonAreaDescending(Convex c, Convex k)
        {
            return k.Area.Value.CompareTo(c.Area.Value);
        }

        #endregion
        #region Parse and ParsePoly methods

        /// <summary>
        /// Parse a string of 'CONVEX x1 y1 z1 d1 x2... dN' or "POLY ra1 dec1 ra2 dec2 ..." into a Convex.
        /// </summary>
        /// <param name="repr"></param>
        /// <returns></returns>
        public static Convex Parse(string repr, bool normalize)
        {
            char[] sep = new char[] { ' ' };

            // only spaces (hopefully)
            string proc = repr.Trim().Replace(',', ' ').Replace("\r\n", " ").Replace('\n', ' ').Replace('\t', ' ');

            // replace duplicate spaces with a single one
            string trim = "";
            while (trim != proc)
            {
                trim = proc;
                proc = trim.Replace("  ", " ");
            }

            string[] t = trim.Split(sep);
            if (t[0] == Convex.keywordPoly)
            {
                return new Convex(Convex.ParsePoly(repr), PointOrder.Safe);
            }
            if (t[0] != Convex.keyword) throw new ArgumentException
                  ("Convex.Parse(): keyword does not match");
            if (t.Length % 4 != 1) throw new ArgumentException
                  (String.Format("Convex.Parse(): # of input values in string are wrong: {0}" + t.Length));

            // read halfspaces
            Convex c = new Convex();
            double x, y, z, cos0;
            for (int i = 1; i < t.Length; i += 4)
            {
                x = double.Parse(t[i]);
                y = double.Parse(t[i + 1]);
                z = double.Parse(t[i + 2]);
                cos0 = double.Parse(t[i + 3]);
                c.halfspaceList.Add(new Halfspace(x, y, z, normalize, cos0));
            }
            return c;
        }

        /// <summary>
        /// Parse string "POLY ra1 dec1 ra2 dec2 ra3 dec3 ... raN decN" and return point list
        /// </summary>
        /// <param name="repr"></param>
        /// <returns></returns>
        public static List<Cartesian> ParsePoly(string repr)
        {
            // separators
            char[] spc = new char[] { ' ' };
            char[] cma = new char[] { ',' };

            // only spaces (hopefully)
            string proc = repr.Trim().Replace(',', ' ').Replace("\r\n", " ").Replace('\n', ' ').Replace('\t', ' ');

            // replace duplicate spaces with a single one
            string trim = "";
            while (trim != proc)
            {
                trim = proc;
                proc = trim.Replace("  ", " ");
            }

            // get values
            string[] tok = trim.Split(spc);
            if (tok[0] != "POLY")
                throw new ArgumentException("ParsePoly(): wrong keyword");

            // read List<Point>
            List<Cartesian> pts = new List<Cartesian>(tok.Length / 2);
            double ra, dec;
            for (int i = 1; i < tok.Length; i += 2)
            {
                ra = double.Parse(tok[i]);
                dec = double.Parse(tok[i + 1]);
                pts.Add(new Cartesian(ra, dec));
            }
            return pts;
        }
        #endregion
        #region ToString and revision
        //------------------------------------------------------------------------

        /// <summary>
        /// String representation: "CONVEX x1 y1 z1 d1 x2... dN"
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            string r = Convex.keyword;
            foreach (Halfspace h in halfspaceList)
                r += String.Format(" {0}", h);
            return r;
        }

        /// <summary>
        /// Revision from CVS
        /// </summary>
        public static string Revision
        {
            get { return "$Revision: 1.1.1.1 $"; }
        }

        #endregion
    }
}

#region Revision History
/* Revision History

        $Log: Convex.cs,v $
        Revision 1.1.1.1  2006/11/14 02:56:39  gupchup
        no message

        Revision 1.13  2006/05/22 18:55:54  budavari
        BinaryFormatter now works with Region and co

        Revision 1.12  2006/05/17 13:45:08  budavari
        Misc string and comment changes

        Revision 1.11  2006/04/24 15:19:30  budavari
        String representations w/o newline

        Revision 1.10  2006/04/19 16:51:01  budavari
        EVerything is now CLS compliant

        Revision 1.9  2006/03/31 15:27:00  budavari
        small additions and typo fix

        Revision 1.8  2006/03/21 22:02:36  budavari
        SetArea and Grow fix

        Revision 1.7  2006/03/20 16:26:13  budavari
        Get rid of infinitesimal convex, e.g. CONVEX 1 0 0 1

        Revision 1.6  2006/03/08 20:55:20  budavari
        SmartIntersect() does the right thing when disjoint MECs

        Revision 1.5  2006/03/08 19:49:08  gyuri
        Attributes of static fields in safe assemblies must be marked  readonly in Visual C#

        Revision 1.4  2006/03/07 21:19:47  budavari
        Fixed bug relevant for making convexes disjoint

        Revision 1.3  2006/02/28 16:26:12  budavari
        Major revision: smart objects w/ properties

        Revision 1.2  2006/01/25 19:05:08  budavari
        Bugfix in SimpleSimplify() and new custom serialization attribute

        Revision 1.1  2006/01/24 15:56:05  budavari
        Re-structured directory layout to accommodate more projects

        Revision 1.22  2006/01/19 18:10:27  budavari
        Extended sign property added

        Revision 1.21  2006/01/09 21:11:02  gyuri
        Added an extra space at the end of ToString() to make it work better for output

        Revision 1.20  2005/12/29 14:28:09  budavari
        Cosmetics nothing big

        Revision 1.19  2005/12/20 02:16:43  budavari
        Difference() added, Parse() takes bool normalize

        Revision 1.18  2005/12/04 16:50:55  budavari
        Small features and fixes. Also added Outline class - not function, yet

        Revision 1.17  2005/12/02 21:32:30  budavari
        member variables do start with small letter ;-) XML still the same

        Revision 1.16  2005/12/01 22:34:57  budavari
        Area struct added and fully functional

        Revision 1.15  2005/11/30 20:50:27  budavari
        Cosmetics... #region specifications etc added

        Revision 1.14  2005/11/30 20:04:24  budavari
        Dilate() renamed to Gro() per Alex's request

        Revision 1.13  2005/11/30 19:50:37  budavari
        Dilate() methods added. Also explicit tolerance required everywhere for clarity

        Revision 1.12  2005/11/30 15:32:37  budavari
        Header changes

        Revision 1.11  2005/11/18 22:51:40  budavari
        Boolean operations are done for now: Intersect(), Union() and Difference()

        Revision 1.10  2005/11/18 05:58:09  budavari
        Working on boolean operations on regions...

        Revision 1.9  2005/11/17 01:58:54  budavari
        Done with the convex stuff... Hurray!!! It can even deal with the "orange
        slices" to some extent: the problem is the area - coming up next...

        Revision 1.8  2005/11/16 18:56:59  budavari
        Only degenerate roots are missing... Also added sample convex static class.

        Revision 1.7  2005/11/15 22:20:20  budavari
        Using Generics - almost there

        Revision 1.6  2005/11/15 15:25:36  budavari
        Everything is a class, things are coming together for area computation, etc. Will moved to C# 2.0

        Revision 1.5  2005/11/05 17:47:44  budavari
        Fixed a bug in Convex caused by renaming things and did some tidying up...

        Revision 1.4  2005/11/04 22:49:36  budavari
        Re-write to have Point and Halfspace as struct again and other tinkerings. XML is still fine...

        Revision 1.3  2005/11/03 15:30:33  budavari
        Nothing special, just cosmetics...

        Revision 1.2  2005/11/03 15:22:01  budavari
        Fairly major revision to make sure the objects serialize to XML nicely:
          - Added separate containers of halfspaces and convexes
          - Customized XML output for compactness and friendly proxy interface
          - Implemented simple .Parse() methods taking cartesion representation

        Revision 1.1.1.1  2005/10/28 14:13:43  budavari
        Initial import of the SphericalLib library. It is pretty complete in terms
        of the classes but lacking the simplication and other advanced methods.
		
*/
#endregion