123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955 |
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Text;
- using FarseerPhysics.Collision;
- using Microsoft.Xna.Framework;
- namespace FarseerPhysics.Common
- {
- #if !(XBOX360)
- [DebuggerDisplay("Count = {Count} Vertices = {ToString()}")]
- #endif
- public class Vertices : List<Vector2>
- {
- public Vertices()
- {
- }
- public Vertices(int capacity)
- {
- Capacity = capacity;
- }
- public Vertices(Vector2[] vector2)
- {
- for (int i = 0; i < vector2.Length; i++)
- {
- Add(vector2[i]);
- }
- }
- public Vertices(IList<Vector2> vertices)
- {
- for (int i = 0; i < vertices.Count; i++)
- {
- Add(vertices[i]);
- }
- }
- /// <summary>
- /// Nexts the index.
- /// </summary>
- /// <param name="index">The index.</param>
- /// <returns></returns>
- public int NextIndex(int index)
- {
- if (index == Count - 1)
- {
- return 0;
- }
- return index + 1;
- }
- public Vector2 NextVertex(int index)
- {
- return this[NextIndex(index)];
- }
- /// <summary>
- /// Gets the previous index.
- /// </summary>
- /// <param name="index">The index.</param>
- /// <returns></returns>
- public int PreviousIndex(int index)
- {
- if (index == 0)
- {
- return Count - 1;
- }
- return index - 1;
- }
- public Vector2 PreviousVertex(int index)
- {
- return this[PreviousIndex(index)];
- }
- /// <summary>
- /// Gets the signed area.
- /// </summary>
- /// <returns></returns>
- public float GetSignedArea()
- {
- int i;
- float area = 0;
- for (i = 0; i < Count; i++)
- {
- int j = (i + 1) % Count;
- area += this[i].X * this[j].Y;
- area -= this[i].Y * this[j].X;
- }
- area /= 2.0f;
- return area;
- }
- /// <summary>
- /// Gets the area.
- /// </summary>
- /// <returns></returns>
- public float GetArea()
- {
- int i;
- float area = 0;
- for (i = 0; i < Count; i++)
- {
- int j = (i + 1) % Count;
- area += this[i].X * this[j].Y;
- area -= this[i].Y * this[j].X;
- }
- area /= 2.0f;
- return (area < 0 ? -area : area);
- }
- /// <summary>
- /// Gets the centroid.
- /// </summary>
- /// <returns></returns>
- public Vector2 GetCentroid()
- {
- // Same algorithm is used by Box2D
- Vector2 c = Vector2.Zero;
- float area = 0.0f;
- const float inv3 = 1.0f / 3.0f;
- Vector2 pRef = Vector2.Zero;
- for (int i = 0; i < Count; ++i)
- {
- // Triangle vertices.
- Vector2 p1 = pRef;
- Vector2 p2 = this[i];
- Vector2 p3 = i + 1 < Count ? this[i + 1] : this[0];
- Vector2 e1 = p2 - p1;
- Vector2 e2 = p3 - p1;
- float D = MathUtils.Cross(e1, e2);
- float triangleArea = 0.5f * D;
- area += triangleArea;
- // Area weighted centroid
- c += triangleArea * inv3 * (p1 + p2 + p3);
- }
- // Centroid
- c *= 1.0f / area;
- return c;
- }
- /// <summary>
- /// Gets the radius based on area.
- /// </summary>
- /// <returns></returns>
- public float GetRadius()
- {
- float area = GetSignedArea();
- double radiusSqrd = (double)area / MathHelper.Pi;
- if (radiusSqrd < 0)
- {
- radiusSqrd *= -1;
- }
- return (float)Math.Sqrt(radiusSqrd);
- }
- /// <summary>
- /// Returns an AABB for vertex.
- /// </summary>
- /// <returns></returns>
- public AABB GetCollisionBox()
- {
- AABB aabb;
- Vector2 lowerBound = new Vector2(float.MaxValue, float.MaxValue);
- Vector2 upperBound = new Vector2(float.MinValue, float.MinValue);
- for (int i = 0; i < Count; ++i)
- {
- if (this[i].X < lowerBound.X)
- {
- lowerBound.X = this[i].X;
- }
- if (this[i].X > upperBound.X)
- {
- upperBound.X = this[i].X;
- }
- if (this[i].Y < lowerBound.Y)
- {
- lowerBound.Y = this[i].Y;
- }
- if (this[i].Y > upperBound.Y)
- {
- upperBound.Y = this[i].Y;
- }
- }
- aabb.LowerBound = lowerBound;
- aabb.UpperBound = upperBound;
- return aabb;
- }
- public void Translate(Vector2 vector)
- {
- Translate(ref vector);
- }
- /// <summary>
- /// Translates the vertices with the specified vector.
- /// </summary>
- /// <param name="vector">The vector.</param>
- public void Translate(ref Vector2 vector)
- {
- for (int i = 0; i < Count; i++)
- this[i] = Vector2.Add(this[i], vector);
- }
- /// <summary>
- /// Scales the vertices with the specified vector.
- /// </summary>
- /// <param name="value">The Value.</param>
- public void Scale(ref Vector2 value)
- {
- for (int i = 0; i < Count; i++)
- this[i] = Vector2.Multiply(this[i], value);
- }
- /// <summary>
- /// Rotate the vertices with the defined value in radians.
- /// </summary>
- /// <param name="value">The amount to rotate by in radians.</param>
- public void Rotate(float value)
- {
- Matrix rotationMatrix;
- Matrix.CreateRotationZ(value, out rotationMatrix);
- for (int i = 0; i < Count; i++)
- this[i] = Vector2.Transform(this[i], rotationMatrix);
- }
- /// <summary>
- /// Assuming the polygon is simple; determines whether the polygon is convex.
- /// NOTE: It will also return false if the input contains colinear edges.
- /// </summary>
- /// <returns>
- /// <c>true</c> if it is convex; otherwise, <c>false</c>.
- /// </returns>
- public bool IsConvex()
- {
- // Ensure the polygon is convex and the interior
- // is to the left of each edge.
- for (int i = 0; i < Count; ++i)
- {
- int i1 = i;
- int i2 = i + 1 < Count ? i + 1 : 0;
- Vector2 edge = this[i2] - this[i1];
- for (int j = 0; j < Count; ++j)
- {
- // Don't check vertices on the current edge.
- if (j == i1 || j == i2)
- {
- continue;
- }
- Vector2 r = this[j] - this[i1];
- float s = edge.X * r.Y - edge.Y * r.X;
- if (s <= 0.0f)
- return false;
- }
- }
- return true;
- }
- public bool IsCounterClockWise()
- {
- //We just return true for lines
- if (Count < 3)
- return true;
- return (GetSignedArea() > 0.0f);
- }
- /// <summary>
- /// Forces counter clock wise order.
- /// </summary>
- public void ForceCounterClockWise()
- {
- if (!IsCounterClockWise())
- {
- Reverse();
- }
- }
- /// <summary>
- /// Check for edge crossings
- /// </summary>
- /// <returns></returns>
- public bool IsSimple()
- {
- for (int i = 0; i < Count; ++i)
- {
- int iplus = (i + 1 > Count - 1) ? 0 : i + 1;
- Vector2 a1 = new Vector2(this[i].X, this[i].Y);
- Vector2 a2 = new Vector2(this[iplus].X, this[iplus].Y);
- for (int j = i + 1; j < Count; ++j)
- {
- int jplus = (j + 1 > Count - 1) ? 0 : j + 1;
- Vector2 b1 = new Vector2(this[j].X, this[j].Y);
- Vector2 b2 = new Vector2(this[jplus].X, this[jplus].Y);
- Vector2 temp;
- if (LineTools.LineIntersect2(a1, a2, b1, b2, out temp))
- {
- return false;
- }
- }
- }
- return true;
- }
- //TODO: Test
- //Implementation found here: http://www.gamedev.net/community/forums/topic.asp?topic_id=548477
- public bool IsSimple2()
- {
- for (int i = 0; i < Count; ++i)
- {
- if (i < Count - 1)
- {
- for (int h = i + 1; h < Count; ++h)
- {
- // Do two vertices lie on top of one another?
- if (this[i] == this[h])
- {
- return true;
- }
- }
- }
- int j = (i + 1) % Count;
- Vector2 iToj = this[j] - this[i];
- Vector2 iTojNormal = new Vector2(iToj.Y, -iToj.X);
- // i is the first vertex and j is the second
- int startK = (j + 1) % Count;
- int endK = (i - 1 + Count) % Count;
- endK += startK < endK ? 0 : startK + 1;
- int k = startK;
- Vector2 iTok = this[k] - this[i];
- bool onLeftSide = Vector2.Dot(iTok, iTojNormal) >= 0;
- Vector2 prevK = this[k];
- ++k;
- for (; k <= endK; ++k)
- {
- int modK = k % Count;
- iTok = this[modK] - this[i];
- if (onLeftSide != Vector2.Dot(iTok, iTojNormal) >= 0)
- {
- Vector2 prevKtoK = this[modK] - prevK;
- Vector2 prevKtoKNormal = new Vector2(prevKtoK.Y, -prevKtoK.X);
- if ((Vector2.Dot(this[i] - prevK, prevKtoKNormal) >= 0) !=
- (Vector2.Dot(this[j] - prevK, prevKtoKNormal) >= 0))
- {
- return true;
- }
- }
- onLeftSide = Vector2.Dot(iTok, iTojNormal) > 0;
- prevK = this[modK];
- }
- }
- return false;
- }
- // From Eric Jordan's convex decomposition library
- /// <summary>
- /// Checks if polygon is valid for use in Box2d engine.
- /// Last ditch effort to ensure no invalid polygons are
- /// added to world geometry.
- ///
- /// Performs a full check, for simplicity, convexity,
- /// orientation, minimum angle, and volume. This won't
- /// be very efficient, and a lot of it is redundant when
- /// other tools in this section are used.
- /// </summary>
- /// <returns></returns>
- public bool CheckPolygon()
- {
- int error = -1;
- if (Count < 3 || Count > Settings.MaxPolygonVertices)
- {
- error = 0;
- }
- if (!IsConvex())
- {
- error = 1;
- }
- if (!IsSimple())
- {
- error = 2;
- }
- if (GetArea() < Settings.Epsilon)
- {
- error = 3;
- }
- //Compute normals
- Vector2[] normals = new Vector2[Count];
- Vertices vertices = new Vertices(Count);
- for (int i = 0; i < Count; ++i)
- {
- vertices.Add(new Vector2(this[i].X, this[i].Y));
- int i1 = i;
- int i2 = i + 1 < Count ? i + 1 : 0;
- Vector2 edge = new Vector2(this[i2].X - this[i1].X, this[i2].Y - this[i1].Y);
- normals[i] = MathUtils.Cross(edge, 1.0f);
- normals[i].Normalize();
- }
- //Required side checks
- for (int i = 0; i < Count; ++i)
- {
- int iminus = (i == 0) ? Count - 1 : i - 1;
- //Parallel sides check
- float cross = MathUtils.Cross(normals[iminus], normals[i]);
- cross = MathUtils.Clamp(cross, -1.0f, 1.0f);
- float angle = (float)Math.Asin(cross);
- if (angle <= Settings.AngularSlop)
- {
- error = 4;
- break;
- }
- //Too skinny check
- for (int j = 0; j < Count; ++j)
- {
- if (j == i || j == (i + 1) % Count)
- {
- continue;
- }
- float s = Vector2.Dot(normals[i], vertices[j] - vertices[i]);
- if (s >= -Settings.LinearSlop)
- {
- error = 5;
- }
- }
- Vector2 centroid = vertices.GetCentroid();
- Vector2 n1 = normals[iminus];
- Vector2 n2 = normals[i];
- Vector2 v = vertices[i] - centroid;
- Vector2 d = new Vector2();
- d.X = Vector2.Dot(n1, v); // - toiSlop;
- d.Y = Vector2.Dot(n2, v); // - toiSlop;
- // Shifting the edge inward by toiSlop should
- // not cause the plane to pass the centroid.
- if ((d.X < 0.0f) || (d.Y < 0.0f))
- {
- error = 6;
- }
- }
- if (error != -1)
- {
- Debug.WriteLine("Found invalid polygon, ");
- switch (error)
- {
- case 0:
- Debug.WriteLine(string.Format("must have between 3 and {0} vertices.\n",
- Settings.MaxPolygonVertices));
- break;
- case 1:
- Debug.WriteLine("must be convex.\n");
- break;
- case 2:
- Debug.WriteLine("must be simple (cannot intersect itself).\n");
- break;
- case 3:
- Debug.WriteLine("area is too small.\n");
- break;
- case 4:
- Debug.WriteLine("sides are too close to parallel.\n");
- break;
- case 5:
- Debug.WriteLine("polygon is too thin.\n");
- break;
- case 6:
- Debug.WriteLine("core shape generation would move edge past centroid (too thin).\n");
- break;
- default:
- Debug.WriteLine("don't know why.\n");
- break;
- }
- }
- return error != -1;
- }
- // From Eric Jordan's convex decomposition library
- /// <summary>
- /// Trace the edge of a non-simple polygon and return a simple polygon.
- ///
- /// Method:
- /// Start at vertex with minimum y (pick maximum x one if there are two).
- /// We aim our "lastDir" vector at (1.0, 0)
- /// We look at the two rays going off from our start vertex, and follow whichever
- /// has the smallest angle (in -Pi . Pi) wrt lastDir ("rightest" turn)
- /// Loop until we hit starting vertex:
- /// We add our current vertex to the list.
- /// We check the seg from current vertex to next vertex for intersections
- /// - if no intersections, follow to next vertex and continue
- /// - if intersections, pick one with minimum distance
- /// - if more than one, pick one with "rightest" next point (two possibilities for each)
- /// </summary>
- /// <param name="verts">The vertices.</param>
- /// <returns></returns>
- public Vertices TraceEdge(Vertices verts)
- {
- PolyNode[] nodes = new PolyNode[verts.Count * verts.Count];
- //overkill, but sufficient (order of mag. is right)
- int nNodes = 0;
- //Add base nodes (raw outline)
- for (int i = 0; i < verts.Count; ++i)
- {
- Vector2 pos = new Vector2(verts[i].X, verts[i].Y);
- nodes[i].Position = pos;
- ++nNodes;
- int iplus = (i == verts.Count - 1) ? 0 : i + 1;
- int iminus = (i == 0) ? verts.Count - 1 : i - 1;
- nodes[i].AddConnection(nodes[iplus]);
- nodes[i].AddConnection(nodes[iminus]);
- }
- //Process intersection nodes - horribly inefficient
- bool dirty = true;
- int counter = 0;
- while (dirty)
- {
- dirty = false;
- for (int i = 0; i < nNodes; ++i)
- {
- for (int j = 0; j < nodes[i].NConnected; ++j)
- {
- for (int k = 0; k < nNodes; ++k)
- {
- if (k == i || nodes[k] == nodes[i].Connected[j]) continue;
- for (int l = 0; l < nodes[k].NConnected; ++l)
- {
- if (nodes[k].Connected[l] == nodes[i].Connected[j] ||
- nodes[k].Connected[l] == nodes[i]) continue;
- //Check intersection
- Vector2 intersectPt;
- bool crosses = LineTools.LineIntersect(nodes[i].Position, nodes[i].Connected[j].Position,
- nodes[k].Position, nodes[k].Connected[l].Position,
- out intersectPt);
- if (crosses)
- {
- dirty = true;
- //Destroy and re-hook connections at crossing point
- PolyNode connj = nodes[i].Connected[j];
- PolyNode connl = nodes[k].Connected[l];
- nodes[i].Connected[j].RemoveConnection(nodes[i]);
- nodes[i].RemoveConnection(connj);
- nodes[k].Connected[l].RemoveConnection(nodes[k]);
- nodes[k].RemoveConnection(connl);
- nodes[nNodes] = new PolyNode(intersectPt);
- nodes[nNodes].AddConnection(nodes[i]);
- nodes[i].AddConnection(nodes[nNodes]);
- nodes[nNodes].AddConnection(nodes[k]);
- nodes[k].AddConnection(nodes[nNodes]);
- nodes[nNodes].AddConnection(connj);
- connj.AddConnection(nodes[nNodes]);
- nodes[nNodes].AddConnection(connl);
- connl.AddConnection(nodes[nNodes]);
- ++nNodes;
- goto SkipOut;
- }
- }
- }
- }
- }
- SkipOut:
- ++counter;
- }
- //Collapse duplicate points
- bool foundDupe = true;
- int nActive = nNodes;
- while (foundDupe)
- {
- foundDupe = false;
- for (int i = 0; i < nNodes; ++i)
- {
- if (nodes[i].NConnected == 0) continue;
- for (int j = i + 1; j < nNodes; ++j)
- {
- if (nodes[j].NConnected == 0) continue;
- Vector2 diff = nodes[i].Position - nodes[j].Position;
- if (diff.LengthSquared() <= Settings.Epsilon * Settings.Epsilon)
- {
- if (nActive <= 3)
- return new Vertices();
- //printf("Found dupe, %d left\n",nActive);
- --nActive;
- foundDupe = true;
- PolyNode inode = nodes[i];
- PolyNode jnode = nodes[j];
- //Move all of j's connections to i, and orphan j
- int njConn = jnode.NConnected;
- for (int k = 0; k < njConn; ++k)
- {
- PolyNode knode = jnode.Connected[k];
- Debug.Assert(knode != jnode);
- if (knode != inode)
- {
- inode.AddConnection(knode);
- knode.AddConnection(inode);
- }
- knode.RemoveConnection(jnode);
- }
- jnode.NConnected = 0;
- }
- }
- }
- }
- //Now walk the edge of the list
- //Find node with minimum y value (max x if equal)
- float minY = float.MaxValue;
- float maxX = -float.MaxValue;
- int minYIndex = -1;
- for (int i = 0; i < nNodes; ++i)
- {
- if (nodes[i].Position.Y < minY && nodes[i].NConnected > 1)
- {
- minY = nodes[i].Position.Y;
- minYIndex = i;
- maxX = nodes[i].Position.X;
- }
- else if (nodes[i].Position.Y == minY && nodes[i].Position.X > maxX && nodes[i].NConnected > 1)
- {
- minYIndex = i;
- maxX = nodes[i].Position.X;
- }
- }
- Vector2 origDir = new Vector2(1.0f, 0.0f);
- Vector2[] resultVecs = new Vector2[4 * nNodes];
- // nodes may be visited more than once, unfortunately - change to growable array!
- int nResultVecs = 0;
- PolyNode currentNode = nodes[minYIndex];
- PolyNode startNode = currentNode;
- Debug.Assert(currentNode.NConnected > 0);
- PolyNode nextNode = currentNode.GetRightestConnection(origDir);
- if (nextNode == null)
- {
- Vertices vertices = new Vertices(nResultVecs);
- for (int i = 0; i < nResultVecs; ++i)
- {
- vertices.Add(resultVecs[i]);
- }
- return vertices;
- }
- // Borked, clean up our mess and return
- resultVecs[0] = startNode.Position;
- ++nResultVecs;
- while (nextNode != startNode)
- {
- if (nResultVecs > 4 * nNodes)
- {
- Debug.Assert(false);
- }
- resultVecs[nResultVecs++] = nextNode.Position;
- PolyNode oldNode = currentNode;
- currentNode = nextNode;
- nextNode = currentNode.GetRightestConnection(oldNode);
- if (nextNode == null)
- {
- Vertices vertices = new Vertices(nResultVecs);
- for (int i = 0; i < nResultVecs; ++i)
- {
- vertices.Add(resultVecs[i]);
- }
- return vertices;
- }
- // There was a problem, so jump out of the loop and use whatever garbage we've generated so far
- }
- return new Vertices();
- }
- private class PolyNode
- {
- private const int MaxConnected = 32;
- /*
- * Given sines and cosines, tells if A's angle is less than B's on -Pi, Pi
- * (in other words, is A "righter" than B)
- */
- public PolyNode[] Connected = new PolyNode[MaxConnected];
- public int NConnected;
- public Vector2 Position;
- public PolyNode(Vector2 pos)
- {
- Position = pos;
- NConnected = 0;
- }
- private bool IsRighter(float sinA, float cosA, float sinB, float cosB)
- {
- if (sinA < 0)
- {
- if (sinB > 0 || cosA <= cosB) return true;
- else return false;
- }
- else
- {
- if (sinB < 0 || cosA <= cosB) return false;
- else return true;
- }
- }
- public void AddConnection(PolyNode toMe)
- {
- Debug.Assert(NConnected < MaxConnected);
- // Ignore duplicate additions
- for (int i = 0; i < NConnected; ++i)
- {
- if (Connected[i] == toMe) return;
- }
- Connected[NConnected] = toMe;
- ++NConnected;
- }
- public void RemoveConnection(PolyNode fromMe)
- {
- bool isFound = false;
- int foundIndex = -1;
- for (int i = 0; i < NConnected; ++i)
- {
- if (fromMe == Connected[i])
- {
- //.position == connected[i].position){
- isFound = true;
- foundIndex = i;
- break;
- }
- }
- Debug.Assert(isFound);
- --NConnected;
- for (int i = foundIndex; i < NConnected; ++i)
- {
- Connected[i] = Connected[i + 1];
- }
- }
- public PolyNode GetRightestConnection(PolyNode incoming)
- {
- if (NConnected == 0) Debug.Assert(false); // This means the connection graph is inconsistent
- if (NConnected == 1)
- {
- //b2Assert(false);
- // Because of the possibility of collapsing nearby points,
- // we may end up with "spider legs" dangling off of a region.
- // The correct behavior here is to turn around.
- return incoming;
- }
- Vector2 inDir = Position - incoming.Position;
- float inLength = inDir.Length();
- inDir.Normalize();
- Debug.Assert(inLength > Settings.Epsilon);
- PolyNode result = null;
- for (int i = 0; i < NConnected; ++i)
- {
- if (Connected[i] == incoming) continue;
- Vector2 testDir = Connected[i].Position - Position;
- float testLengthSqr = testDir.LengthSquared();
- testDir.Normalize();
- Debug.Assert(testLengthSqr >= Settings.Epsilon * Settings.Epsilon);
- float myCos = Vector2.Dot(inDir, testDir);
- float mySin = MathUtils.Cross(inDir, testDir);
- if (result != null)
- {
- Vector2 resultDir = result.Position - Position;
- resultDir.Normalize();
- float resCos = Vector2.Dot(inDir, resultDir);
- float resSin = MathUtils.Cross(inDir, resultDir);
- if (IsRighter(mySin, myCos, resSin, resCos))
- {
- result = Connected[i];
- }
- }
- else
- {
- result = Connected[i];
- }
- }
- Debug.Assert(result != null);
- return result;
- }
- public PolyNode GetRightestConnection(Vector2 incomingDir)
- {
- Vector2 diff = Position - incomingDir;
- PolyNode temp = new PolyNode(diff);
- PolyNode res = GetRightestConnection(temp);
- Debug.Assert(res != null);
- return res;
- }
- }
- public override string ToString()
- {
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < Count; i++)
- {
- builder.Append(this[i].ToString());
- if (i < Count - 1)
- {
- builder.Append(" ");
- }
- }
- return builder.ToString();
- }
- /// <summary>
- /// Projects to axis.
- /// </summary>
- /// <param name="axis">The axis.</param>
- /// <param name="min">The min.</param>
- /// <param name="max">The max.</param>
- public void ProjectToAxis(ref Vector2 axis, out float min, out float max)
- {
- // To project a point on an axis use the dot product
- float dotProduct = Vector2.Dot(axis, this[0]);
- min = dotProduct;
- max = dotProduct;
- for (int i = 0; i < Count; i++)
- {
- dotProduct = Vector2.Dot(this[i], axis);
- if (dotProduct < min)
- {
- min = dotProduct;
- }
- else
- {
- if (dotProduct > max)
- {
- max = dotProduct;
- }
- }
- }
- }
- /// <summary>
- /// Winding number test for a point in a polygon.
- /// </summary>
- /// See more info about the algorithm here: http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm
- /// <param name="point">The point to be tested.</param>
- /// <returns>-1 if the winding number is zero and the point is outside
- /// the polygon, 1 if the point is inside the polygon, and 0 if the point
- /// is on the polygons edge.</returns>
- public int PointInPolygon(ref Vector2 point)
- {
- // Winding number
- int wn = 0;
- // Iterate through polygon's edges
- for (int i = 0; i < Count; i++)
- {
- // Get points
- Vector2 p1 = this[i];
- Vector2 p2 = this[NextIndex(i)];
- // Test if a point is directly on the edge
- Vector2 edge = p2 - p1;
- float area = MathUtils.Area(ref p1, ref p2, ref point);
- if (area == 0f && Vector2.Dot(point - p1, edge) >= 0f && Vector2.Dot(point - p2, edge) <= 0f)
- {
- return 0;
- }
- // Test edge for intersection with ray from point
- if (p1.Y <= point.Y)
- {
- if (p2.Y > point.Y && area > 0f)
- {
- ++wn;
- }
- }
- else
- {
- if (p2.Y <= point.Y && area < 0f)
- {
- --wn;
- }
- }
- }
- return (wn == 0 ? -1 : 1);
- }
- /// <summary>
- /// Compute the sum of the angles made between the test point and each pair of points making up the polygon.
- /// If this sum is 2pi then the point is an interior point, if 0 then the point is an exterior point.
- /// ref: http://ozviz.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/ - Solution 2
- /// </summary>
- public bool PointInPolygonAngle(ref Vector2 point)
- {
- double angle = 0;
- // Iterate through polygon's edges
- for (int i = 0; i < Count; i++)
- {
- // Get points
- Vector2 p1 = this[i] - point;
- Vector2 p2 = this[NextIndex(i)] - point;
- angle += MathUtils.VectorAngle(ref p1, ref p2);
- }
- if (Math.Abs(angle) < Math.PI)
- {
- return false;
- }
- return true;
- }
- }
- }
|