/// <summary> /// Check and return polygon intersections /// </summary> /// <param name="polygon1"></param> /// <param name="polygon2"></param> /// <param name="intersections"></param> /// <returns></returns> private static bool VerticesIntersect(Vertices polygon1, Vertices polygon2, out List <EdgeIntersectInfo> intersections) { intersections = new List <EdgeIntersectInfo>(); // Iterate through polygon1's edges for (int i = 0; i < polygon1.Count; i++) { // Get edge vertices Vector2 p1 = polygon1[i]; Vector2 p2 = polygon1[polygon1.NextIndex(i)]; // Get intersections between this edge and polygon2 for (int j = 0; j < polygon2.Count; j++) { Vector2 point; Vector2 p3 = polygon2[j]; Vector2 p4 = polygon2[polygon2.NextIndex(j)]; // Check if the edges intersect if (LineTools.LineIntersect(p1, p2, p3, p4, true, true, out point)) { // Here, we round the returned intersection point to its nearest whole number. // This prevents floating point anomolies where 99.9999-> is returned instead of 100. point = new Vector2((float)Math.Round(point.X, 0), (float)Math.Round(point.Y, 0)); // Record the intersection intersections.Add(new EdgeIntersectInfo(new Edge(p1, p2), new Edge(p3, p4), point)); } } } // true if any intersections were found. return(intersections.Count > 0); }
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 } Vector2 intersectionPoint; if (LineTools.LineIntersect(At(i, vertices), At(j, vertices), At(k, vertices), At(k + 1, vertices), out intersectionPoint)) { return(false); } } 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 } //if(QLineF(at(i), at(j)).intersect(QLineF(at(k), at(k + 1)), NULL) == QLineF::BoundedIntersection) { if (LineTools.LineIntersect(At(i, vertices), At(j, vertices), At(k, vertices), At(k + 1, vertices)) != Vector2.Zero) { return(false); } } return(true); }
/// <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 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; Vector2 p; Vector2 lowerInt = new Vector2(); Vector2 upperInt = new Vector2(); // 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) { Vector2 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 > Settings.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); }
/// <summary> /// Calculates all intersections between two polygons. /// </summary> /// <param name="polygon1">The first polygon.</param> /// <param name="polygon2">The second polygon.</param> /// <param name="slicedPoly1">Returns the first polygon with added intersection points.</param> /// <param name="slicedPoly2">Returns the second polygon with added intersection points.</param> private static void CalculateIntersections(Vertices polygon1, Vertices polygon2, out Vertices slicedPoly1, out Vertices slicedPoly2) { slicedPoly1 = new Vertices(polygon1); slicedPoly2 = new Vertices(polygon2); // Iterate through polygon1's edges for (int i = 0; i < polygon1.Count; i++) { // Get edge vertices Vector2 a = polygon1[i]; Vector2 b = polygon1[polygon1.NextIndex(i)]; // Get intersections between this edge and polygon2 for (int j = 0; j < polygon2.Count; j++) { Vector2 c = polygon2[j]; Vector2 d = polygon2[polygon2.NextIndex(j)]; Vector2 intersectionPoint; // Check if the edges intersect if (LineTools.LineIntersect(a, b, c, d, out intersectionPoint)) { // calculate alpha values for sorting multiple intersections points on a edge float alpha; // Insert intersection point into first polygon alpha = GetAlpha(a, b, intersectionPoint); if (alpha > 0f && alpha < 1f) { int index = slicedPoly1.IndexOf(a) + 1; while (index < slicedPoly1.Count && GetAlpha(a, b, slicedPoly1[index]) <= alpha) { ++index; } slicedPoly1.Insert(index, intersectionPoint); } // Insert intersection point into second polygon alpha = GetAlpha(c, d, intersectionPoint); if (alpha > 0f && alpha < 1f) { int index = slicedPoly2.IndexOf(c) + 1; while (index < slicedPoly2.Count && GetAlpha(c, d, slicedPoly2[index]) <= alpha) { ++index; } slicedPoly2.Insert(index, intersectionPoint); } } } } // Check for very small edges for (int i = 0; i < slicedPoly1.Count; ++i) { int iNext = slicedPoly1.NextIndex(i); //If they are closer than the distance remove vertex if ((slicedPoly1[iNext] - slicedPoly1[i]).sqrMagnitude <= ClipperEpsilonSquared) { slicedPoly1.RemoveAt(i); --i; } } for (int i = 0; i < slicedPoly2.Count; ++i) { int iNext = slicedPoly2.NextIndex(i); //If they are closer than the distance remove vertex if ((slicedPoly2[iNext] - slicedPoly2[i]).sqrMagnitude <= ClipperEpsilonSquared) { slicedPoly2.RemoveAt(i); --i; } } }
private static List <Vertices> TriangulatePolygon(Vertices vertices) { List <Vertices> list = new List <Vertices>(); Vector2 lowerInt = new Vector2(); Vector2 upperInt = new Vector2(); // intersection points int lowerIndex = 0, upperIndex = 0; Vertices lowerPoly, upperPoly; for (int i = 0; i < vertices.Count; ++i) { if (Reflex(i, vertices)) { float upperDist; float lowerDist = upperDist = float.MaxValue; for (int j = 0; j < vertices.Count; ++j) { // if line intersects with an edge float d; Vector2 p; 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) { Vector2 p = ((lowerInt + upperInt) / 2); lowerPoly = Copy(i, upperIndex, vertices); lowerPoly.Add(p); upperPoly = Copy(lowerIndex, i, vertices); upperPoly.Add(p); } 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(TriangulatePolygon(lowerPoly)); list.AddRange(TriangulatePolygon(upperPoly)); return(list); } } // polygon is already convex if (vertices.Count > Settings.MaxPolygonVertices) { lowerPoly = Copy(0, vertices.Count / 2, vertices); upperPoly = Copy(vertices.Count / 2, 0, vertices); list.AddRange(TriangulatePolygon(lowerPoly)); list.AddRange(TriangulatePolygon(upperPoly)); } else { list.Add(vertices); } return(list); }
/// <summary> /// Precondition: Counter Clockwise polygon /// 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 Settings.MaxPolygonVertices. /// </summary> /// <param name="vertices"></param> /// <returns></returns> public static List <Vertices> ConvexPartition(Vertices vertices) { List <Vertices> list = new List <Vertices>(); float d, dist1, dist2; Vector2 ip; Vector2 ip1 = new Vector2(); Vector2 ip2 = new Vector2(); // intersection points int ind1 = 0, ind2 = 0; Vertices poly1, poly2; for (int i = 0; i < vertices.Count; ++i) { if (Reflex(i, vertices)) { dist1 = dist2 = float.MaxValue; // std::numeric_limits<qreal>::max(); for (int j = 0; j < vertices.Count; ++j) { if (Left(At(i - 1, vertices), At(i, vertices), At(j, vertices)) && RightOn(At(i - 1, vertices), At(i, vertices), At(j - 1, vertices))) { // if ray (i-1)->(i) intersects with edge (j, j-1) //QLineF(at(i - 1), at(i)).intersect(QLineF(at(j), at(j - 1)), ip); ip = 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), ip)) { // intersection point isn't caused by backwards ray d = SquareDist(At(i, vertices), ip); if (d < dist1) { // take the closest intersection so we know it isn't blocked by another edge dist1 = d; ind1 = j; ip1 = ip; } } } if (Left(At(i + 1, vertices), At(i, vertices), At(j + 1, vertices)) && RightOn(At(i + 1, vertices), At(i, vertices), At(j, vertices))) { // if ray (i+1)->(i) intersects with edge (j+1, j) //QLineF(at(i + 1), at(i)).intersect(QLineF(at(j), at(j + 1)), ip); ip = 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), ip)) { d = SquareDist(At(i, vertices), ip); if (d < dist2) { dist2 = d; ind2 = j; ip2 = ip; } } } } if (ind1 == (ind2 + 1) % vertices.Count) { // no vertices in range Vector2 sp = ((ip1 + ip2) / 2); poly1 = Copy(i, ind2, vertices); poly1.Add(sp); poly2 = Copy(ind1, i, vertices); poly2.Add(sp); } else { double highestScore = 0, bestIndex = ind1; while (ind2 < ind1) { ind2 += vertices.Count; } for (int j = ind1; j <= ind2; ++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; } } } poly1 = Copy(i, (int)bestIndex, vertices); poly2 = Copy((int)bestIndex, i, vertices); } list.AddRange(ConvexPartition(poly1)); list.AddRange(ConvexPartition(poly2)); return(list); } } // polygon is already convex if (vertices.Count > Settings.MaxPolygonVertices) { poly1 = Copy(0, vertices.Count / 2, vertices); poly2 = Copy(vertices.Count / 2, 0, vertices); list.AddRange(ConvexPartition(poly1)); list.AddRange(ConvexPartition(poly2)); } 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); } return(list); }