123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800 |
- 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
- }
- }
|