/// <summary> /// Removes any points in the polygon which are collinear with its nearest neighbors. /// </summary> /// <param name="polygon">The polygon to search for collinear points</param> private Polygon RemoveCollinearPoints(Polygon polygon) { if (polygon.Count <= 3) { return(polygon); } Polygon result = new Polygon(); for (int i = 0; i < polygon.Count; i++) { Microsoft.Xna.Framework.Vector2 prev = polygon.At(i - 1); Microsoft.Xna.Framework.Vector2 current = polygon[i]; Microsoft.Xna.Framework.Vector2 next = polygon.At(i + 1); // Skip adding this point to the result if it is collinear with its neighbors. if (LineAlgorithms.AreCollinear(prev, current, next)) { continue; } result.Add(current); } return(result); }
List <Event> sortedEvents = new List <Event>(); // sorted list of event pointers public EventQueue(Polygon polygon) { nextIndex = 0; eventCount = 2 * polygon.Count; // 2 vertex events for each edge events.AddRange(Enumerable.Range(0, eventCount).Select(i => new Event())); sortedEvents.AddRange(events); // Initialize event queue with edge segment endpoints for (int i = 0; i < polygon.Count; i++) { // Initialize data for edge i // The original C code had either an issue or an undocumented assumption here: // they used polygon[i + 1] which could result in an out of memory error, unless // the next point was automatically returned. sortedEvents[2 * i].Edge = i; sortedEvents[2 * i + 1].Edge = i; sortedEvents[2 * i].Vertex = polygon.At(i); sortedEvents[2 * i + 1].Vertex = polygon.At(i + 1); // determine type if (XYOrder(polygon.At(i), polygon.At(i + 1)) <= 0) { sortedEvents[2 * i].EventType = EventType.Left; sortedEvents[2 * i + 1].EventType = EventType.Right; } else { sortedEvents[2 * i].EventType = EventType.Right; sortedEvents[2 * i + 1].EventType = EventType.Left; } } // Sort Eq[] by increasing x and y sortedEvents.Sort(EventComparison); }
public SweepLineSegment Add(Event E) { SweepLineSegment segToAdd = new SweepLineSegment { Edge = E.Edge }; // if it is being added, then it must be a LEFT edge event // but need to determine which endpoint is the left one Microsoft.Xna.Framework.Vector2 v1 = polygon.At(segToAdd.Edge); Microsoft.Xna.Framework.Vector2 v2 = polygon.At(segToAdd.Edge + 1); // determine which is leftmost if (XYOrder(v1, v2) < 0) { segToAdd.LeftVertex = v1; segToAdd.RightVertex = v2; } else { segToAdd.LeftVertex = v2; segToAdd.RightVertex = v1; } segToAdd.Above = null; segToAdd.Below = null; // add a node to the balanced binary tree BinaryTreeNode <SweepLineSegment> nd = tree.Add(segToAdd); BinaryTreeNode <SweepLineSegment> nx = nd.Next(); BinaryTreeNode <SweepLineSegment> np = nd.Previous(); if (nx != null) { segToAdd.Above = nx.Value; segToAdd.Above.Below = segToAdd; } if (np != null) { segToAdd.Below = np.Value; segToAdd.Below.Above = segToAdd; } return(segToAdd); }
/// <summary> /// Copies a range of vertices from a polygon with wrap-around on the indices. /// </summary> /// <param name="polygon"></param> /// <param name="start"></param> /// <param name="end"></param> /// <returns></returns> public static Polygon CopyRange(this Polygon polygon, int start, int end) { Polygon p = new Polygon(); while (end < start) { end += polygon.Count; } for (; start <= end; ++start) { p.Add(polygon.At(start)); } return(p); }
/// <summary> /// Decomposes a concave polygon into a set of convex polygons. /// </summary> /// <param name="polygon"></param> /// <returns></returns> public IReadOnlyList <Polygon> BuildConvexDecomposition(Polygon polygon) { //We force it to CCW as it is a precondition in this algorithm. polygon = ForceCounterClockWise(polygon); List <Polygon> list = new List <Polygon>(); // YOGESH : Convex Partition can not happen if there are less than 3, ie 2,1 vertices if (polygon.Count < 3) { return(list); } double d = 0.0; double lowerDist, upperDist; Microsoft.Xna.Framework.Vector2 p; Microsoft.Xna.Framework.Vector2 lowerInt = new Microsoft.Xna.Framework.Vector2(); Microsoft.Xna.Framework.Vector2 upperInt = new Microsoft.Xna.Framework.Vector2(); // intersection points int lowerIndex = 0, upperIndex = 0; Polygon lowerPoly, upperPoly; // Go thru all Verices until we find a reflex vertex i. // Extend the edges incident at i until they hit an edge // Find BEST vertex within the range, that the partitioning chord // A polygon can be broken into convex regions by eliminating all reflex vertices // Eliminating two reflex vertices with one diagonal is better than eliminating just one // A reflex vertex can only be removed if the diagonal connecting to it is within the range given by extending its neighbouring edges; // otherwise, its angle is only reduced for (int i = 0; i < polygon.Count; i++) { Microsoft.Xna.Framework.Vector2 prev = polygon.At(i - 1); Microsoft.Xna.Framework.Vector2 on = polygon.At(i); Microsoft.Xna.Framework.Vector2 next = polygon.At(i + 1); if (IsReflex(prev, on, next)) { lowerDist = double.MaxValue; upperDist = double.MaxValue; for (int j = 0; j < polygon.Count; j++) { // YOGESH: if any of j line's endpoints matches with reflex i, skip if ((i == j) || (i == NormalizeIndex(j - 1, polygon.Count)) || (i == NormalizeIndex(j + 1, polygon.Count))) { continue; // no self and prev and next, for testing } // testing incoming edge: // if line coming into i vertex (i-1 to i) has j vertex of the test-line on left // AND have j-i on right, then they will be intersecting Microsoft.Xna.Framework.Vector2 iPrev = polygon.At(i - 1); Microsoft.Xna.Framework.Vector2 iSelf = polygon.At(i); Microsoft.Xna.Framework.Vector2 jSelf = polygon.At(j); Microsoft.Xna.Framework.Vector2 jPrev = polygon.At(j - 1); bool leftOK = Left(iPrev, iSelf, jSelf); bool rightOK = Right(iPrev, iSelf, jPrev); bool leftOnOK = LineAlgorithms.AreCollinear(iPrev, iSelf, jSelf); // YOGESH: cached into variables for better debugging bool rightOnOK = LineAlgorithms.AreCollinear(iPrev, iSelf, jPrev); // YOGESH: cached into variables for better debugging if (leftOnOK || rightOnOK) // YOGESH: Checked "ON" condition as well, collinearity { // lines are colinear, they can not be overlapping as polygon is simple // find closest point which is not internal to incoming line i , i -1 d = Microsoft.Xna.Framework.Vector2.DistanceSquared(iSelf, jSelf); // this lower* is the point got from incoming edge into the i vertex, // lowerInt incoming edge intersection point // lowerIndex incoming edge intersection edge if (d < lowerDist) { // keep only the closest intersection lowerDist = d; lowerInt = jSelf; lowerIndex = j - 1; } d = Microsoft.Xna.Framework.Vector2.DistanceSquared(iSelf, jPrev); // this lower* is the point got from incoming edge into the i vertex, // lowerInt incoming edge intersection point // lowerIndex incoming edge intersection edge if (d < lowerDist) { // keep only the closest intersection lowerDist = d; lowerInt = jPrev; lowerIndex = j; } } else if (leftOK && rightOK) // YOGESH: Intersection in-between. Bayazit had ON condition in built here, which I have taken care above. { // find the point of intersection var intersection = LineAlgorithms.LineSegmentIntersection( polygon.At(i - 1), polygon.At(i), polygon.At(j), polygon.At(j - 1)) ?.IntersectionPoint; if (intersection != null) { p = intersection.Value; // make sure it's inside the poly, if (Right(polygon.At(i + 1), polygon.At(i), p)) { d = Microsoft.Xna.Framework.Vector2.DistanceSquared(polygon.At(i), p); // this lower* is the point got from incoming edge into the i vertex, // lowerInt incoming edge intersection point // lowerIndex incoming edge intersection edge if (d < lowerDist) { // keep only the closest intersection lowerDist = d; lowerInt = p; lowerIndex = j; } } } } // testing outgoing edge: // if line outgoing from i vertex (i to i+1) has j vertex of the test-line on right // AND has j+1 on left, they they will be intersecting Microsoft.Xna.Framework.Vector2 iNext = polygon.At(i + 1); Microsoft.Xna.Framework.Vector2 jNext = polygon.At(j + 1); bool leftOKn = Left(iNext, iSelf, jNext); bool rightOKn = Right(iNext, iSelf, jSelf); bool leftOnOKn = LineAlgorithms.AreCollinear(iNext, iSelf, jNext); // YOGESH: cached into variables for better debugging bool rightOnOKn = LineAlgorithms.AreCollinear(iNext, iSelf, jSelf); if (leftOnOKn || rightOnOKn) // YOGESH: Checked "ON" condition as well, collinearity { // lines are colinear, they can not be overlapping as polygon is simple // find closest point which is not internal to incoming line i , i -1 d = Microsoft.Xna.Framework.Vector2.DistanceSquared(iSelf, jNext); // this upper* is the point got from outgoing edge into the i vertex, // upperInt outgoing edge intersection point // upperIndex outgoing edge intersection edge if (d < upperDist) { // keep only the closest intersection upperDist = d; upperInt = jNext; upperIndex = j + 1; } d = Microsoft.Xna.Framework.Vector2.DistanceSquared(polygon.At(i), polygon.At(j)); // this upper* is the point got from outgoing edge into the i vertex, // upperInt outgoing edge intersection point // upperIndex outgoing edge intersection edge if (d < upperDist) { // keep only the closest intersection upperDist = d; upperInt = jSelf; upperIndex = j; } } else if (leftOKn && rightOKn) // YOGESH: Intersection in-between. Bayazit had ON condition in built here, which I have taken care above. { var intersection = LineAlgorithms.LineSegmentIntersection( polygon.At(i + 1), polygon.At(i), polygon.At(j), polygon.At(j + 1)) ?.IntersectionPoint; if (intersection != null) { p = intersection.Value; if (Left(polygon.At(i - 1), polygon.At(i), p)) { d = Microsoft.Xna.Framework.Vector2.DistanceSquared(polygon.At(i), p); // this upper* is the point got from outgoing edge from the i vertex, // upperInt outgoing edge intersection point // upperIndex outgoing edge intersection edge if (d < upperDist) { upperDist = d; upperIndex = j; upperInt = p; } } } } } // YOGESH: If no vertices in the range, lets not choose midpoint but closet point of that segment //// if there are no vertices to connect to, choose a point in the middle if (lowerIndex == (upperIndex + 1) % polygon.Count) { Microsoft.Xna.Framework.Vector2 sp = ((lowerInt + upperInt) / 2); lowerPoly = polygon.CopyRange(i, upperIndex); lowerPoly.Add(sp); upperPoly = polygon.CopyRange(lowerIndex, i); upperPoly.Add(sp); } else { //find vertex to connect to double highestScore = 0, bestIndex = lowerIndex; while (upperIndex < lowerIndex) { upperIndex += polygon.Count; } // go throuh all the vertices between the range of lower and upper for (int j = lowerIndex; j <= upperIndex; ++j) { if (CanSee(polygon, i, j)) { double score = 1 / (Microsoft.Xna.Framework.Vector2.DistanceSquared(polygon.At(i), polygon.At(j)) + 1); // if another vertex is Reflex, choosing it has highest score Microsoft.Xna.Framework.Vector2 prevj = polygon.At(j - 1); Microsoft.Xna.Framework.Vector2 onj = polygon.At(j); Microsoft.Xna.Framework.Vector2 nextj = polygon.At(j + 1); if (IsReflex(prevj, onj, nextj)) { if (RightOrOn(polygon.At(j - 1), polygon.At(j), polygon.At(i)) && LeftOrOn(polygon.At(j + 1), polygon.At(j), polygon.At(i))) { score += 3; } else { score += 2; } } else { score += 1; } if (score > highestScore) { bestIndex = j; highestScore = score; } } } // YOGESH : Pending: if there are 2 vertices as 'bestIndex', its better to disregard both and put midpoint (M case) lowerPoly = polygon.CopyRange(i, (int)bestIndex); upperPoly = polygon.CopyRange((int)bestIndex, i); } // solve smallest poly first (SAW in Bayazit's C++ code) if (lowerPoly.Count < upperPoly.Count) { list.AddRange(BuildConvexDecomposition(lowerPoly)); list.AddRange(BuildConvexDecomposition(upperPoly)); } else { list.AddRange(BuildConvexDecomposition(upperPoly)); list.AddRange(BuildConvexDecomposition(lowerPoly)); } return(list); } } // polygon is already convex if (polygon.Count > MaxPolygonVertices) { lowerPoly = polygon.CopyRange(0, polygon.Count / 2); upperPoly = polygon.CopyRange(polygon.Count / 2, 0); list.AddRange(BuildConvexDecomposition(lowerPoly)); list.AddRange(BuildConvexDecomposition(upperPoly)); } else { list.Add(polygon); } // The polygons are not guaranteed to be without collinear points. We remove // them to be sure. for (int i = 0; i < list.Count; i++) { list[i] = RemoveCollinearPoints(list[i]); } return(list); }
/// <summary> /// Returns true if vertex j can be seen from vertex i without any obstructions. /// </summary> /// <param name="polygon"></param> /// <param name="i"></param> /// <param name="j"></param> /// <returns></returns> private bool CanSee(Polygon polygon, int i, int j) { Microsoft.Xna.Framework.Vector2 prev = polygon.At(i - 1); Microsoft.Xna.Framework.Vector2 on = polygon.At(i); Microsoft.Xna.Framework.Vector2 next = polygon.At(i + 1); if (IsReflex(prev, on, next)) { if (LeftOrOn(polygon.At(i), polygon.At(i - 1), polygon.At(j)) && RightOrOn(polygon.At(i), polygon.At(i + 1), polygon.At(j))) { return(false); } } else { if (RightOrOn(polygon.At(i), polygon.At(i + 1), polygon.At(j)) || LeftOrOn(polygon.At(i), polygon.At(i - 1), polygon.At(j))) { return(false); } } Microsoft.Xna.Framework.Vector2 prevj = polygon.At(j - 1); Microsoft.Xna.Framework.Vector2 onj = polygon.At(j); Microsoft.Xna.Framework.Vector2 nextj = polygon.At(j + 1); if (IsReflex(prevj, onj, nextj)) { if (LeftOrOn(polygon.At(j), polygon.At(j - 1), polygon.At(i)) && RightOrOn(polygon.At(j), polygon.At(j + 1), polygon.At(i))) { return(false); } } else { if (RightOrOn(polygon.At(j), polygon.At(j + 1), polygon.At(i)) || LeftOrOn(polygon.At(j), polygon.At(j - 1), polygon.At(i))) { return(false); } } for (int k = 0; k < polygon.Count; ++k) { // YOGESH : changed from Line-Line intersection to Segment-Segment Intersection Microsoft.Xna.Framework.Vector2 p1 = polygon.At(i); Microsoft.Xna.Framework.Vector2 p2 = polygon.At(j); Microsoft.Xna.Framework.Vector2 q1 = polygon.At(k); Microsoft.Xna.Framework.Vector2 q2 = polygon.At(k + 1); // ignore incident edges if (p1.Equals(q1) || p1.Equals(q2) || p2.Equals(q1) || p2.Equals(q2)) { continue; } var intersection = LineAlgorithms.LineSegmentIntersection(p1, p2, q1, q2); if (intersection != null && intersection.WithinFirstSegment && intersection.WithinSecondSegment) { Microsoft.Xna.Framework.Vector2 intPoint = intersection.IntersectionPoint; // intPoint is not any of the j line then false, else continue. Intersection has to be interior to qualify s 'false' from here if ((!intPoint.Equals(polygon.At(k))) || (!intPoint.Equals(polygon.At(k + 1)))) { return(false); } } } return(true); }