private bool SearchForOutstandingVertex(Vertices hullArea, out FVector2 outstanding) { FVector2 outstandingResult = FVector2.Zero; bool found = false; if (hullArea.Count > 2) { int hullAreaLastPoint = hullArea.Count - 1; FVector2 tempVector1; FVector2 tempVector2 = hullArea[0]; FVector2 tempVector3 = hullArea[hullAreaLastPoint]; // Search between the first and last hull point. for (int i = 1; i < hullAreaLastPoint; i++) { tempVector1 = hullArea[i]; // Check if the distance is over the one that's tolerable. if (LineTools.DistanceBetweenPointAndLineSegment(ref tempVector1, ref tempVector2, ref tempVector3) >= _hullTolerance) { outstandingResult = hullArea[i]; found = true; break; } } } outstanding = outstandingResult; return(found); }
private bool DistanceToHullAcceptable(Vertices polygon, FVector2 point, bool higherDetail) { if (polygon == null) { throw new ArgumentNullException("polygon", "'polygon' can't be null."); } if (polygon.Count < 3) { throw new ArgumentException("'polygon.Count' can't be less then 3."); } FVector2 edgeVertex2 = polygon[polygon.Count - 1]; FVector2 edgeVertex1; if (higherDetail) { for (int i = 0; i < polygon.Count; i++) { edgeVertex1 = polygon[i]; if (LineTools.DistanceBetweenPointAndLineSegment(ref point, ref edgeVertex1, ref edgeVertex2) <= _hullTolerance || LineTools.DistanceBetweenPointAndPoint(ref point, ref edgeVertex1) <= _hullTolerance) { return(false); } edgeVertex2 = polygon[i]; } return(true); } else { for (int i = 0; i < polygon.Count; i++) { edgeVertex1 = polygon[i]; if (LineTools.DistanceBetweenPointAndLineSegment(ref point, ref edgeVertex1, ref edgeVertex2) <= _hullTolerance) { return(false); } edgeVertex2 = polygon[i]; } return(true); } }
private static bool CanSee(int i, int j, Vertices vertices) { if (Reflex(i, vertices)) { if (LeftOn(At(i, vertices), At(i - 1, vertices), At(j, vertices)) && RightOn(At(i, vertices), At(i + 1, vertices), At(j, vertices))) { return(false); } } else { if (RightOn(At(i, vertices), At(i + 1, vertices), At(j, vertices)) || LeftOn(At(i, vertices), At(i - 1, vertices), At(j, vertices))) { return(false); } } if (Reflex(j, vertices)) { if (LeftOn(At(j, vertices), At(j - 1, vertices), At(i, vertices)) && RightOn(At(j, vertices), At(j + 1, vertices), At(i, vertices))) { return(false); } } else { if (RightOn(At(j, vertices), At(j + 1, vertices), At(i, vertices)) || LeftOn(At(j, vertices), At(j - 1, vertices), At(i, vertices))) { return(false); } } for (int k = 0; k < vertices.Count; ++k) { if ((k + 1) % vertices.Count == i || k == i || (k + 1) % vertices.Count == j || k == j) { continue; // ignore incident edges } FVector2 intersectionPoint; if (LineTools.LineIntersect(At(i, vertices), At(j, vertices), At(k, vertices), At(k + 1, vertices), out intersectionPoint)) { return(false); } } return(true); }
/// <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; FVector2 a1 = new FVector2(this[i].X, this[i].Y); FVector2 a2 = new FVector2(this[iplus].X, this[iplus].Y); for (int j = i + 1; j < Count; ++j) { int jplus = (j + 1 > Count - 1) ? 0 : j + 1; FVector2 b1 = new FVector2(this[j].X, this[j].Y); FVector2 b2 = new FVector2(this[jplus].X, this[jplus].Y); FVector2 temp; if (LineTools.LineIntersect2(a1, a2, b1, b2, out temp)) { return(false); } } } return(true); }
private bool SplitPolygonEdge(Vertices polygon, FVector2 coordInsideThePolygon, out int vertex1Index, out int vertex2Index) { FVector2 slope; int nearestEdgeVertex1Index = 0; int nearestEdgeVertex2Index = 0; bool edgeFound = false; float shortestDistance = float.MaxValue; bool edgeCoordFound = false; FVector2 foundEdgeCoord = FVector2.Zero; List <float> xCoords = SearchCrossingEdges(polygon, (int)coordInsideThePolygon.Y); vertex1Index = 0; vertex2Index = 0; foundEdgeCoord.Y = coordInsideThePolygon.Y; if (xCoords != null && xCoords.Count > 1 && xCoords.Count % 2 == 0) { float distance; for (int i = 0; i < xCoords.Count; i++) { if (xCoords[i] < coordInsideThePolygon.X) { distance = coordInsideThePolygon.X - xCoords[i]; if (distance < shortestDistance) { shortestDistance = distance; foundEdgeCoord.X = xCoords[i]; edgeCoordFound = true; } } } if (edgeCoordFound) { shortestDistance = float.MaxValue; int edgeVertex2Index = polygon.Count - 1; int edgeVertex1Index; for (edgeVertex1Index = 0; edgeVertex1Index < polygon.Count; edgeVertex1Index++) { FVector2 tempVector1 = polygon[edgeVertex1Index]; FVector2 tempVector2 = polygon[edgeVertex2Index]; distance = LineTools.DistanceBetweenPointAndLineSegment(ref foundEdgeCoord, ref tempVector1, ref tempVector2); if (distance < shortestDistance) { shortestDistance = distance; nearestEdgeVertex1Index = edgeVertex1Index; nearestEdgeVertex2Index = edgeVertex2Index; edgeFound = true; } edgeVertex2Index = edgeVertex1Index; } if (edgeFound) { slope = polygon[nearestEdgeVertex2Index] - polygon[nearestEdgeVertex1Index]; slope.Normalize(); FVector2 tempVector = polygon[nearestEdgeVertex1Index]; distance = LineTools.DistanceBetweenPointAndPoint(ref tempVector, ref foundEdgeCoord); vertex1Index = nearestEdgeVertex1Index; vertex2Index = nearestEdgeVertex1Index + 1; polygon.Insert(nearestEdgeVertex1Index, distance * slope + polygon[vertex1Index]); polygon.Insert(nearestEdgeVertex1Index, distance * slope + polygon[vertex2Index]); return(true); } } } return(false); }
/// <summary> /// Decompose the polygon into several smaller non-concave polygon. /// If the polygon is already convex, it will return the original polygon, unless it is over Box2D.Settings.MaxPolygonVertices. /// Precondition: Counter Clockwise polygon /// </summary> /// <param name="vertices"></param> /// <returns></returns> public static List <Vertices> ConvexPartition(Vertices vertices) { //We force it to CCW as it is a precondition in this algorithm. vertices.ForceCounterClockWise(); List <Vertices> list = new List <Vertices>(); float d, lowerDist, upperDist; FVector2 p; FVector2 lowerInt = new FVector2(); FVector2 upperInt = new FVector2(); // intersection points int lowerIndex = 0, upperIndex = 0; Vertices lowerPoly, upperPoly; for (int i = 0; i < vertices.Count; ++i) { if (Reflex(i, vertices)) { lowerDist = upperDist = float.MaxValue; // std::numeric_limits<qreal>::max(); for (int j = 0; j < vertices.Count; ++j) { // if line intersects with an edge if (Left(At(i - 1, vertices), At(i, vertices), At(j, vertices)) && RightOn(At(i - 1, vertices), At(i, vertices), At(j - 1, vertices))) { // find the point of intersection p = LineTools.LineIntersect(At(i - 1, vertices), At(i, vertices), At(j, vertices), At(j - 1, vertices)); if (Right(At(i + 1, vertices), At(i, vertices), p)) { // make sure it's inside the poly d = SquareDist(At(i, vertices), p); if (d < lowerDist) { // keep only the closest intersection lowerDist = d; lowerInt = p; lowerIndex = j; } } } if (Left(At(i + 1, vertices), At(i, vertices), At(j + 1, vertices)) && RightOn(At(i + 1, vertices), At(i, vertices), At(j, vertices))) { p = LineTools.LineIntersect(At(i + 1, vertices), At(i, vertices), At(j, vertices), At(j + 1, vertices)); if (Left(At(i - 1, vertices), At(i, vertices), p)) { d = SquareDist(At(i, vertices), p); if (d < upperDist) { upperDist = d; upperIndex = j; upperInt = p; } } } } // if there are no vertices to connect to, choose a point in the middle if (lowerIndex == (upperIndex + 1) % vertices.Count) { FVector2 sp = ((lowerInt + upperInt) / 2); lowerPoly = Copy(i, upperIndex, vertices); lowerPoly.Add(sp); upperPoly = Copy(lowerIndex, i, vertices); upperPoly.Add(sp); } else { double highestScore = 0, bestIndex = lowerIndex; while (upperIndex < lowerIndex) { upperIndex += vertices.Count; } for (int j = lowerIndex; j <= upperIndex; ++j) { if (CanSee(i, j, vertices)) { double score = 1 / (SquareDist(At(i, vertices), At(j, vertices)) + 1); if (Reflex(j, vertices)) { if (RightOn(At(j - 1, vertices), At(j, vertices), At(i, vertices)) && LeftOn(At(j + 1, vertices), At(j, vertices), At(i, vertices))) { score += 3; } else { score += 2; } } else { score += 1; } if (score > highestScore) { bestIndex = j; highestScore = score; } } } lowerPoly = Copy(i, (int)bestIndex, vertices); upperPoly = Copy((int)bestIndex, i, vertices); } list.AddRange(ConvexPartition(lowerPoly)); list.AddRange(ConvexPartition(upperPoly)); return(list); } } // polygon is already convex if (vertices.Count > Box2D.Settings.b2_maxPolygonVertices) { lowerPoly = Copy(0, vertices.Count / 2, vertices); upperPoly = Copy(vertices.Count / 2, 0, vertices); list.AddRange(ConvexPartition(lowerPoly)); list.AddRange(ConvexPartition(upperPoly)); } else { list.Add(vertices); } //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] = SimplifyTools.CollinearSimplify(list[i], 0); } //Remove empty vertice collections for (int i = list.Count - 1; i >= 0; i--) { if (list[i].Count == 0) { list.RemoveAt(i); } } return(list); }
// 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) { FVector2 pos = new FVector2(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 FVector2 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; } FVector2 diff = nodes[i].Position - nodes[j].Position; if (diff.LengthSquared() <= Box2D.Settings.b2_epsilon * Box2D.Settings.b2_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; } } FVector2 origDir = new FVector2(1.0f, 0.0f); FVector2[] resultVecs = new FVector2[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()); }