UltimateZoneBuilder/Source/Core/Geometry/EarClipPolygon.cs

195 lines
4.7 KiB
C#

#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Drawing;
#endregion
namespace CodeImp.DoomBuilder.Geometry
{
public sealed class EarClipPolygon : LinkedList<EarClipVertex>
{
#region ================== Variables
// Tree variables
private List<EarClipPolygon> children;
private bool inner;
#endregion
#region ================== Properties
public List<EarClipPolygon> Children { get { return children; } }
public bool Inner { get { return inner; } set { inner = value; } }
#endregion
#region ================== Constructors
// Constructor
internal EarClipPolygon()
{
// Initialize
children = new List<EarClipPolygon>();
}
// Constructor
internal EarClipPolygon(EarClipPolygon p, EarClipVertex add) : base(p)
{
// Initialize
base.AddLast(add);
children = new List<EarClipPolygon>();
}
#endregion
#region ================== Methods
// This merges a polygon into this one
public void Add(EarClipPolygon p)
{
// Initialize
foreach(EarClipVertex v in p) base.AddLast(v);
}
// This calculates the area
public float CalculateArea()
{
// Multiply the x coordinate of each vertex by the y coordinate of the next vertex.
// Multiply the y coordinate of each vertex by the x coordinate of the next vertex.
// Subtract these.
float result = 0.0f;
int firstcalculated = 0;
LinkedListNode<EarClipVertex> n1 = base.First;
while(firstcalculated < 2)
{
LinkedListNode<EarClipVertex> n2 = n1.Next ?? base.First;
float a = n1.Value.Position.x * n2.Value.Position.y;
float b = n1.Value.Position.y * n2.Value.Position.x;
result += a - b;
n1 = n2;
if(n2 == base.First) firstcalculated++;
}
return Math.Abs(result / 2.0f);
}
// This creates a bounding box from the outer polygon
public RectangleF CreateBBox()
{
float left = float.MaxValue;
float right = float.MinValue;
float top = float.MaxValue;
float bottom = float.MinValue;
foreach(EarClipVertex v in this)
{
if(v.Position.x < left) left = v.Position.x;
if(v.Position.x > right) right = v.Position.x;
if(v.Position.y < top) top = v.Position.y;
if(v.Position.y > bottom) bottom = v.Position.y;
}
return new RectangleF(left, top, right - left, bottom - top);
}
// Point inside the polygon?
// See: http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/
public bool Intersect(Vector2D p)
{
Vector2D v1 = base.Last.Value.Position;
Vector2D v2;
LinkedListNode<EarClipVertex> n = base.First;
uint c = 0;
// Go for all vertices
while(n != null)
{
// Get next vertex
v2 = n.Value.Position;
// Determine min/max values
float miny = Math.Min(v1.y, v2.y);
float maxy = Math.Max(v1.y, v2.y);
float maxx = Math.Max(v1.x, v2.x);
// Check for intersection
if((p.y > miny) && (p.y <= maxy))
{
if(p.x <= maxx)
{
if(v1.y != v2.y)
{
float xint = (p.y - v1.y) * (v2.x - v1.x) / (v2.y - v1.y) + v1.x;
if((v1.x == v2.x) || (p.x <= xint)) c++;
}
}
}
// Move to next
v1 = v2;
n = n.Next;
}
// Inside this polygon?
if((c & 0x00000001UL) != 0)
{
// Check if not inside the children
foreach(EarClipPolygon child in children)
{
// Inside this child? Then it is not inside this polygon.
if(child.Intersect(p)) return false;
}
// Inside polygon!
return true;
}
else
{
// Not inside the polygon
return false;
}
}
// This inserts a polygon if it is a child of this one
public bool InsertChild(EarClipPolygon p)
{
// Polygon must have at least 1 vertex
if(p.Count == 0) return false;
// Check if it can be inserted at a lower level
foreach(EarClipPolygon child in children)
{
if(child.InsertChild(p)) return true;
}
// Check if it can be inserted here
if(this.Intersect(p.First.Value.Position))
{
// Make the polygon the inverse of this one
p.Inner = !inner;
children.Add(p);
return true;
}
// Can't insert it as a child
return false;
}
#endregion
}
}