800 lines
28 KiB
C#
800 lines
28 KiB
C#
using System.Collections.Generic;
|
|
using FarseerPhysics.Collision;
|
|
using Microsoft.Xna.Framework;
|
|
|
|
namespace FarseerPhysics.Common
|
|
{
|
|
// Ported by Matthew Bettcher - Feb 2011
|
|
|
|
/*
|
|
Copyright (c) 2010, Luca Deltodesco
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification, are permitted
|
|
provided that the following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above copyright notice, this list of conditions
|
|
and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright notice, this list of
|
|
conditions and the following disclaimer in the documentation and/or other materials provided
|
|
with the distribution.
|
|
* Neither the name of the nape project nor the names of its contributors may be used to endorse
|
|
or promote products derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
public static class MarchingSquares
|
|
{
|
|
/// <summary>
|
|
/// Marching squares over the given domain using the mesh defined via the dimensions
|
|
/// (wid,hei) to build a set of polygons such that f(x,y) less than 0, using the given number
|
|
/// 'bin' for recursive linear inteprolation along cell boundaries.
|
|
///
|
|
/// if 'comb' is true, then the polygons will also be composited into larger possible concave
|
|
/// polygons.
|
|
/// </summary>
|
|
/// <param name="domain"></param>
|
|
/// <param name="cellWidth"></param>
|
|
/// <param name="cellHeight"></param>
|
|
/// <param name="f"></param>
|
|
/// <param name="lerpCount"></param>
|
|
/// <param name="combine"></param>
|
|
/// <returns></returns>
|
|
public static List<Vertices> DetectSquares(AABB domain, float cellWidth, float cellHeight, sbyte[,] f,
|
|
int lerpCount, bool combine)
|
|
{
|
|
CxFastList<GeomPoly> ret = new CxFastList<GeomPoly>();
|
|
|
|
List<Vertices> verticesList = new List<Vertices>();
|
|
|
|
//NOTE: removed assignments as they were not used.
|
|
List<GeomPoly> polyList;
|
|
GeomPoly gp;
|
|
|
|
int xn = (int)(domain.Extents.X * 2 / cellWidth);
|
|
bool xp = xn == (domain.Extents.X * 2 / cellWidth);
|
|
int yn = (int)(domain.Extents.Y * 2 / cellHeight);
|
|
bool yp = yn == (domain.Extents.Y * 2 / cellHeight);
|
|
if (!xp) xn++;
|
|
if (!yp) yn++;
|
|
|
|
sbyte[,] fs = new sbyte[xn + 1, yn + 1];
|
|
GeomPolyVal[,] ps = new GeomPolyVal[xn + 1, yn + 1];
|
|
|
|
//populate shared function lookups.
|
|
for (int x = 0; x < xn + 1; x++)
|
|
{
|
|
int x0;
|
|
if (x == xn) x0 = (int)domain.UpperBound.X;
|
|
else x0 = (int)(x * cellWidth + domain.LowerBound.X);
|
|
for (int y = 0; y < yn + 1; y++)
|
|
{
|
|
int y0;
|
|
if (y == yn) y0 = (int)domain.UpperBound.Y;
|
|
else y0 = (int)(y * cellHeight + domain.LowerBound.Y);
|
|
fs[x, y] = f[x0, y0];
|
|
}
|
|
}
|
|
|
|
//generate sub-polys and combine to scan lines
|
|
for (int y = 0; y < yn; y++)
|
|
{
|
|
float y0 = y * cellHeight + domain.LowerBound.Y;
|
|
float y1;
|
|
if (y == yn - 1) y1 = domain.UpperBound.Y;
|
|
else y1 = y0 + cellHeight;
|
|
GeomPoly pre = null;
|
|
for (int x = 0; x < xn; x++)
|
|
{
|
|
float x0 = x * cellWidth + domain.LowerBound.X;
|
|
float x1;
|
|
if (x == xn - 1) x1 = domain.UpperBound.X;
|
|
else x1 = x0 + cellWidth;
|
|
|
|
gp = new GeomPoly();
|
|
|
|
int key = MarchSquare(f, fs, ref gp, x, y, x0, y0, x1, y1, lerpCount);
|
|
if (gp.Length != 0)
|
|
{
|
|
if (combine && pre != null && (key & 9) != 0)
|
|
{
|
|
combLeft(ref pre, ref gp);
|
|
gp = pre;
|
|
}
|
|
else
|
|
ret.Add(gp);
|
|
ps[x, y] = new GeomPolyVal(gp, key);
|
|
}
|
|
else
|
|
gp = null;
|
|
pre = gp;
|
|
}
|
|
}
|
|
if (!combine)
|
|
{
|
|
polyList = ret.GetListOfElements();
|
|
|
|
foreach (GeomPoly poly in polyList)
|
|
{
|
|
verticesList.Add(new Vertices(poly.Points.GetListOfElements()));
|
|
}
|
|
|
|
return verticesList;
|
|
}
|
|
|
|
//combine scan lines together
|
|
for (int y = 1; y < yn; y++)
|
|
{
|
|
int x = 0;
|
|
while (x < xn)
|
|
{
|
|
GeomPolyVal p = ps[x, y];
|
|
|
|
//skip along scan line if no polygon exists at this point
|
|
if (p == null)
|
|
{
|
|
x++;
|
|
continue;
|
|
}
|
|
|
|
//skip along if current polygon cannot be combined above.
|
|
if ((p.Key & 12) == 0)
|
|
{
|
|
x++;
|
|
continue;
|
|
}
|
|
|
|
//skip along if no polygon exists above.
|
|
GeomPolyVal u = ps[x, y - 1];
|
|
if (u == null)
|
|
{
|
|
x++;
|
|
continue;
|
|
}
|
|
|
|
//skip along if polygon above cannot be combined with.
|
|
if ((u.Key & 3) == 0)
|
|
{
|
|
x++;
|
|
continue;
|
|
}
|
|
|
|
float ax = x * cellWidth + domain.LowerBound.X;
|
|
float ay = y * cellHeight + domain.LowerBound.Y;
|
|
|
|
CxFastList<Vector2> bp = p.GeomP.Points;
|
|
CxFastList<Vector2> ap = u.GeomP.Points;
|
|
|
|
//skip if it's already been combined with above polygon
|
|
if (u.GeomP == p.GeomP)
|
|
{
|
|
x++;
|
|
continue;
|
|
}
|
|
|
|
//combine above (but disallow the hole thingies
|
|
CxFastListNode<Vector2> bi = bp.Begin();
|
|
while (Square(bi.Elem().Y - ay) > Settings.Epsilon || bi.Elem().X < ax) bi = bi.Next();
|
|
|
|
//NOTE: Unused
|
|
//Vector2 b0 = bi.elem();
|
|
Vector2 b1 = bi.Next().Elem();
|
|
if (Square(b1.Y - ay) > Settings.Epsilon)
|
|
{
|
|
x++;
|
|
continue;
|
|
}
|
|
|
|
bool brk = true;
|
|
CxFastListNode<Vector2> ai = ap.Begin();
|
|
while (ai != ap.End())
|
|
{
|
|
if (VecDsq(ai.Elem(), b1) < Settings.Epsilon)
|
|
{
|
|
brk = false;
|
|
break;
|
|
}
|
|
ai = ai.Next();
|
|
}
|
|
if (brk)
|
|
{
|
|
x++;
|
|
continue;
|
|
}
|
|
|
|
CxFastListNode<Vector2> bj = bi.Next().Next();
|
|
if (bj == bp.End()) bj = bp.Begin();
|
|
while (bj != bi)
|
|
{
|
|
ai = ap.Insert(ai, bj.Elem()); // .clone()
|
|
bj = bj.Next();
|
|
if (bj == bp.End()) bj = bp.Begin();
|
|
u.GeomP.Length++;
|
|
}
|
|
//u.p.simplify(float.Epsilon,float.Epsilon);
|
|
//
|
|
ax = x + 1;
|
|
while (ax < xn)
|
|
{
|
|
GeomPolyVal p2 = ps[(int)ax, y];
|
|
if (p2 == null || p2.GeomP != p.GeomP)
|
|
{
|
|
ax++;
|
|
continue;
|
|
}
|
|
p2.GeomP = u.GeomP;
|
|
ax++;
|
|
}
|
|
ax = x - 1;
|
|
while (ax >= 0)
|
|
{
|
|
GeomPolyVal p2 = ps[(int)ax, y];
|
|
if (p2 == null || p2.GeomP != p.GeomP)
|
|
{
|
|
ax--;
|
|
continue;
|
|
}
|
|
p2.GeomP = u.GeomP;
|
|
ax--;
|
|
}
|
|
ret.Remove(p.GeomP);
|
|
p.GeomP = u.GeomP;
|
|
|
|
x = (int)((bi.Next().Elem().X - domain.LowerBound.X) / cellWidth) + 1;
|
|
//x++; this was already commented out!
|
|
}
|
|
}
|
|
|
|
polyList = ret.GetListOfElements();
|
|
|
|
foreach (GeomPoly poly in polyList)
|
|
{
|
|
verticesList.Add(new Vertices(poly.Points.GetListOfElements()));
|
|
}
|
|
|
|
return verticesList;
|
|
}
|
|
|
|
#region Private Methods
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
/** Linearly interpolate between (x0 to x1) given a value at these coordinates (v0 and v1)
|
|
such as to approximate value(return) = 0
|
|
**/
|
|
|
|
private static int[] _lookMarch = {
|
|
0x00, 0xE0, 0x38, 0xD8, 0x0E, 0xEE, 0x36, 0xD6, 0x83, 0x63, 0xBB, 0x5B, 0x8D,
|
|
0x6D, 0xB5, 0x55
|
|
};
|
|
|
|
private static float Lerp(float x0, float x1, float v0, float v1)
|
|
{
|
|
float dv = v0 - v1;
|
|
float t;
|
|
if (dv * dv < Settings.Epsilon)
|
|
t = 0.5f;
|
|
else t = v0 / dv;
|
|
return x0 + t * (x1 - x0);
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
/** Recursive linear interpolation for use in marching squares **/
|
|
|
|
private static float Xlerp(float x0, float x1, float y, float v0, float v1, sbyte[,] f, int c)
|
|
{
|
|
float xm = Lerp(x0, x1, v0, v1);
|
|
if (c == 0)
|
|
return xm;
|
|
|
|
sbyte vm = f[(int)xm, (int)y];
|
|
|
|
if (v0 * vm < 0)
|
|
return Xlerp(x0, xm, y, v0, vm, f, c - 1);
|
|
|
|
return Xlerp(xm, x1, y, vm, v1, f, c - 1);
|
|
}
|
|
|
|
/** Recursive linear interpolation for use in marching squares **/
|
|
|
|
private static float Ylerp(float y0, float y1, float x, float v0, float v1, sbyte[,] f, int c)
|
|
{
|
|
float ym = Lerp(y0, y1, v0, v1);
|
|
if (c == 0)
|
|
return ym;
|
|
|
|
sbyte vm = f[(int)x, (int)ym];
|
|
|
|
if (v0 * vm < 0)
|
|
return Ylerp(y0, ym, x, v0, vm, f, c - 1);
|
|
|
|
return Ylerp(ym, y1, x, vm, v1, f, c - 1);
|
|
}
|
|
|
|
/** Square value for use in marching squares **/
|
|
|
|
private static float Square(float x)
|
|
{
|
|
return x * x;
|
|
}
|
|
|
|
private static float VecDsq(Vector2 a, Vector2 b)
|
|
{
|
|
Vector2 d = a - b;
|
|
return d.X * d.X + d.Y * d.Y;
|
|
}
|
|
|
|
private static float VecCross(Vector2 a, Vector2 b)
|
|
{
|
|
return a.X * b.Y - a.Y * b.X;
|
|
}
|
|
|
|
/** Look-up table to relate polygon key with the vertices that should be used for
|
|
the sub polygon in marching squares
|
|
**/
|
|
|
|
/** Perform a single celled marching square for for the given cell defined by (x0,y0) (x1,y1)
|
|
using the function f for recursive interpolation, given the look-up table 'fs' of
|
|
the values of 'f' at cell vertices with the result to be stored in 'poly' given the actual
|
|
coordinates of 'ax' 'ay' in the marching squares mesh.
|
|
**/
|
|
|
|
private static int MarchSquare(sbyte[,] f, sbyte[,] fs, ref GeomPoly poly, int ax, int ay, float x0, float y0,
|
|
float x1, float y1, int bin)
|
|
{
|
|
//key lookup
|
|
int key = 0;
|
|
sbyte v0 = fs[ax, ay];
|
|
if (v0 < 0) key |= 8;
|
|
sbyte v1 = fs[ax + 1, ay];
|
|
if (v1 < 0) key |= 4;
|
|
sbyte v2 = fs[ax + 1, ay + 1];
|
|
if (v2 < 0) key |= 2;
|
|
sbyte v3 = fs[ax, ay + 1];
|
|
if (v3 < 0) key |= 1;
|
|
|
|
int val = _lookMarch[key];
|
|
if (val != 0)
|
|
{
|
|
CxFastListNode<Vector2> pi = null;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
Vector2 p;
|
|
if ((val & (1 << i)) != 0)
|
|
{
|
|
if (i == 7 && (val & 1) == 0)
|
|
poly.Points.Add(p = new Vector2(x0, Ylerp(y0, y1, x0, v0, v3, f, bin)));
|
|
else
|
|
{
|
|
if (i == 0) p = new Vector2(x0, y0);
|
|
else if (i == 2) p = new Vector2(x1, y0);
|
|
else if (i == 4) p = new Vector2(x1, y1);
|
|
else if (i == 6) p = new Vector2(x0, y1);
|
|
|
|
else if (i == 1) p = new Vector2(Xlerp(x0, x1, y0, v0, v1, f, bin), y0);
|
|
else if (i == 5) p = new Vector2(Xlerp(x0, x1, y1, v3, v2, f, bin), y1);
|
|
|
|
else if (i == 3) p = new Vector2(x1, Ylerp(y0, y1, x1, v1, v2, f, bin));
|
|
else p = new Vector2(x0, Ylerp(y0, y1, x0, v0, v3, f, bin));
|
|
|
|
pi = poly.Points.Insert(pi, p);
|
|
}
|
|
poly.Length++;
|
|
}
|
|
}
|
|
//poly.simplify(float.Epsilon,float.Epsilon);
|
|
}
|
|
return key;
|
|
}
|
|
|
|
/** Used in polygon composition to composit polygons into scan lines
|
|
Combining polya and polyb into one super-polygon stored in polya.
|
|
**/
|
|
|
|
private static void combLeft(ref GeomPoly polya, ref GeomPoly polyb)
|
|
{
|
|
CxFastList<Vector2> ap = polya.Points;
|
|
CxFastList<Vector2> bp = polyb.Points;
|
|
CxFastListNode<Vector2> ai = ap.Begin();
|
|
CxFastListNode<Vector2> bi = bp.Begin();
|
|
|
|
Vector2 b = bi.Elem();
|
|
CxFastListNode<Vector2> prea = null;
|
|
while (ai != ap.End())
|
|
{
|
|
Vector2 a = ai.Elem();
|
|
if (VecDsq(a, b) < Settings.Epsilon)
|
|
{
|
|
//ignore shared vertex if parallel
|
|
if (prea != null)
|
|
{
|
|
Vector2 a0 = prea.Elem();
|
|
b = bi.Next().Elem();
|
|
|
|
Vector2 u = a - a0;
|
|
//vec_new(u); vec_sub(a.p.p, a0.p.p, u);
|
|
Vector2 v = b - a;
|
|
//vec_new(v); vec_sub(b.p.p, a.p.p, v);
|
|
float dot = VecCross(u, v);
|
|
if (dot * dot < Settings.Epsilon)
|
|
{
|
|
ap.Erase(prea, ai);
|
|
polya.Length--;
|
|
ai = prea;
|
|
}
|
|
}
|
|
|
|
//insert polyb into polya
|
|
bool fst = true;
|
|
CxFastListNode<Vector2> preb = null;
|
|
while (!bp.Empty())
|
|
{
|
|
Vector2 bb = bp.Front();
|
|
bp.Pop();
|
|
if (!fst && !bp.Empty())
|
|
{
|
|
ai = ap.Insert(ai, bb);
|
|
polya.Length++;
|
|
preb = ai;
|
|
}
|
|
fst = false;
|
|
}
|
|
|
|
//ignore shared vertex if parallel
|
|
ai = ai.Next();
|
|
Vector2 a1 = ai.Elem();
|
|
ai = ai.Next();
|
|
if (ai == ap.End()) ai = ap.Begin();
|
|
Vector2 a2 = ai.Elem();
|
|
Vector2 a00 = preb.Elem();
|
|
Vector2 uu = a1 - a00;
|
|
//vec_new(u); vec_sub(a1.p, a0.p, u);
|
|
Vector2 vv = a2 - a1;
|
|
//vec_new(v); vec_sub(a2.p, a1.p, v);
|
|
float dot1 = VecCross(uu, vv);
|
|
if (dot1 * dot1 < Settings.Epsilon)
|
|
{
|
|
ap.Erase(preb, preb.Next());
|
|
polya.Length--;
|
|
}
|
|
|
|
return;
|
|
}
|
|
prea = ai;
|
|
ai = ai.Next();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CxFastList from nape physics
|
|
|
|
#region Nested type: CxFastList
|
|
|
|
/// <summary>
|
|
/// Designed as a complete port of CxFastList from CxStd.
|
|
/// </summary>
|
|
internal class CxFastList<T>
|
|
{
|
|
// first node in the list
|
|
private CxFastListNode<T> _head;
|
|
private int _count;
|
|
|
|
/// <summary>
|
|
/// Iterator to start of list (O(1))
|
|
/// </summary>
|
|
public CxFastListNode<T> Begin()
|
|
{
|
|
return _head;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Iterator to end of list (O(1))
|
|
/// </summary>
|
|
public CxFastListNode<T> End()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns first element of list (O(1))
|
|
/// </summary>
|
|
public T Front()
|
|
{
|
|
return _head.Elem();
|
|
}
|
|
|
|
/// <summary>
|
|
/// add object to list (O(1))
|
|
/// </summary>
|
|
public CxFastListNode<T> Add(T value)
|
|
{
|
|
CxFastListNode<T> newNode = new CxFastListNode<T>(value);
|
|
if (_head == null)
|
|
{
|
|
newNode._next = null;
|
|
_head = newNode;
|
|
_count++;
|
|
return newNode;
|
|
}
|
|
newNode._next = _head;
|
|
_head = newNode;
|
|
|
|
_count++;
|
|
|
|
return newNode;
|
|
}
|
|
|
|
/// <summary>
|
|
/// remove object from list, returns true if an element was removed (O(n))
|
|
/// </summary>
|
|
public bool Remove(T value)
|
|
{
|
|
CxFastListNode<T> head = _head;
|
|
CxFastListNode<T> prev = _head;
|
|
|
|
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
|
|
|
|
if (head != null)
|
|
{
|
|
if (value != null)
|
|
{
|
|
do
|
|
{
|
|
// if we are on the value to be removed
|
|
if (comparer.Equals(head._elt, value))
|
|
{
|
|
// then we need to patch the list
|
|
// check to see if we are removing the _head
|
|
if (head == _head)
|
|
{
|
|
_head = head._next;
|
|
_count--;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// were not at the head
|
|
prev._next = head._next;
|
|
_count--;
|
|
return true;
|
|
}
|
|
}
|
|
// cache the current as the previous for the next go around
|
|
prev = head;
|
|
head = head._next;
|
|
} while (head != null);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// pop element from head of list (O(1)) Note: this does not return the object popped!
|
|
/// There is good reason to this, and it regards the Alloc list variants which guarantee
|
|
/// objects are released to the object pool. You do not want to retrieve an element
|
|
/// through pop or else that object may suddenly be used by another piece of code which
|
|
/// retrieves it from the object pool.
|
|
/// </summary>
|
|
public CxFastListNode<T> Pop()
|
|
{
|
|
return Erase(null, _head);
|
|
}
|
|
|
|
/// <summary>
|
|
/// insert object after 'node' returning an iterator to the inserted object.
|
|
/// </summary>
|
|
public CxFastListNode<T> Insert(CxFastListNode<T> node, T value)
|
|
{
|
|
if (node == null)
|
|
{
|
|
return Add(value);
|
|
}
|
|
CxFastListNode<T> newNode = new CxFastListNode<T>(value);
|
|
CxFastListNode<T> nextNode = node._next;
|
|
newNode._next = nextNode;
|
|
node._next = newNode;
|
|
|
|
_count++;
|
|
|
|
return newNode;
|
|
}
|
|
|
|
/// <summary>
|
|
/// removes the element pointed to by 'node' with 'prev' being the previous iterator,
|
|
/// returning an iterator to the element following that of 'node' (O(1))
|
|
/// </summary>
|
|
public CxFastListNode<T> Erase(CxFastListNode<T> prev, CxFastListNode<T> node)
|
|
{
|
|
// cache the node after the node to be removed
|
|
CxFastListNode<T> nextNode = node._next;
|
|
if (prev != null)
|
|
prev._next = nextNode;
|
|
else if (_head != null)
|
|
_head = _head._next;
|
|
else
|
|
return null;
|
|
|
|
_count--;
|
|
return nextNode;
|
|
}
|
|
|
|
/// <summary>
|
|
/// whether the list is empty (O(1))
|
|
/// </summary>
|
|
public bool Empty()
|
|
{
|
|
if (_head == null)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// computes size of list (O(n))
|
|
/// </summary>
|
|
public int Size()
|
|
{
|
|
CxFastListNode<T> i = Begin();
|
|
int count = 0;
|
|
|
|
do
|
|
{
|
|
count++;
|
|
} while (i.Next() != null);
|
|
|
|
return count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// empty the list (O(1) if CxMixList, O(n) otherwise)
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
CxFastListNode<T> head = _head;
|
|
while (head != null)
|
|
{
|
|
CxFastListNode<T> node2 = head;
|
|
head = head._next;
|
|
node2._next = null;
|
|
}
|
|
_head = null;
|
|
_count = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// returns true if 'value' is an element of the list (O(n))
|
|
/// </summary>
|
|
public bool Has(T value)
|
|
{
|
|
return (Find(value) != null);
|
|
}
|
|
|
|
// Non CxFastList Methods
|
|
public CxFastListNode<T> Find(T value)
|
|
{
|
|
// start at head
|
|
CxFastListNode<T> head = _head;
|
|
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
|
|
if (head != null)
|
|
{
|
|
if (value != null)
|
|
{
|
|
do
|
|
{
|
|
if (comparer.Equals(head._elt, value))
|
|
{
|
|
return head;
|
|
}
|
|
head = head._next;
|
|
} while (head != _head);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if (head._elt == null)
|
|
{
|
|
return head;
|
|
}
|
|
head = head._next;
|
|
} while (head != _head);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public List<T> GetListOfElements()
|
|
{
|
|
List<T> list = new List<T>();
|
|
|
|
CxFastListNode<T> iter = Begin();
|
|
|
|
if (iter != null)
|
|
{
|
|
do
|
|
{
|
|
list.Add(iter._elt);
|
|
iter = iter._next;
|
|
} while (iter != null);
|
|
}
|
|
return list;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Nested type: CxFastListNode
|
|
|
|
internal class CxFastListNode<T>
|
|
{
|
|
internal T _elt;
|
|
internal CxFastListNode<T> _next;
|
|
|
|
public CxFastListNode(T obj)
|
|
{
|
|
_elt = obj;
|
|
}
|
|
|
|
public T Elem()
|
|
{
|
|
return _elt;
|
|
}
|
|
|
|
public CxFastListNode<T> Next()
|
|
{
|
|
return _next;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Internal Stuff
|
|
|
|
#region Nested type: GeomPoly
|
|
|
|
internal class GeomPoly
|
|
{
|
|
public int Length;
|
|
public CxFastList<Vector2> Points;
|
|
|
|
public GeomPoly()
|
|
{
|
|
Points = new CxFastList<Vector2>();
|
|
Length = 0;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Nested type: GeomPolyVal
|
|
|
|
private class GeomPolyVal
|
|
{
|
|
/** Associated polygon at coordinate **/
|
|
/** Key of original sub-polygon **/
|
|
public int Key;
|
|
public GeomPoly GeomP;
|
|
|
|
public GeomPolyVal(GeomPoly geomP, int K)
|
|
{
|
|
GeomP = geomP;
|
|
Key = K;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|
|
} |