void PossibleIntersection(SweepEvent e1, SweepEvent e2) { if (e1.polygonType == e2.polygonType) { return; } // if ((e1->pl == e2->pl) ) // Uncomment these two lines if self-intersecting polygons are not allowed // return false; IntersectResult intData = FindIntersection(e1.segment, e2.segment); int numIntersections = intData.max; Vector2 ip1 = intData.point1; if (numIntersections == 0) { return; } if (numIntersections == 1 && (Point.PointEquals(e1.p, e2.p) || Point.PointEquals(e1.otherSE.p, e2.otherSE.p))) { return; // the line segments intersect at an endpoint of both line segments } if (numIntersections == 2 && e1.polygonType == e2.polygonType) { return; // the line segments overlap, but they belong to the same polygon } // The line segments associated to e1 and e2 intersect if (numIntersections == 1) { if (!Point.PointEquals(e1.p, ip1) && !Point.PointEquals(e1.otherSE.p, ip1)) { DivideSegment(e1, ip1); // if ip1 is not an endpoint of the line segment associated to e1 then divide "e1" } if (!Point.PointEquals(e2.p, ip1) && !Point.PointEquals(e2.otherSE.p, ip1)) { DivideSegment(e2, ip1); // if ip1 is not an endpoint of the line segment associated to e2 then divide "e2" } return; } // The line segments overlap sortedEvents.Clear(); if (Point.PointEquals(e1.p, e2.p)) { sortedEvents.Add(null); } else if (Sec(e1, e2)) { sortedEvents.Add(e2); sortedEvents.Add(e1); } else { sortedEvents.Add(e1); sortedEvents.Add(e2); } if (Point.PointEquals(e1.otherSE.p, e2.otherSE.p)) { sortedEvents.Add(null); } else if (Sec(e1.otherSE, e2.otherSE)) { sortedEvents.Add(e2.otherSE); sortedEvents.Add(e1.otherSE); } else { sortedEvents.Add(e1.otherSE); sortedEvents.Add(e2.otherSE); } if (sortedEvents.Count == 2) // are both line segments equal? { e1.edgeType = e1.otherSE.edgeType = EdgeType.NON_CONTRIBUTING; e2.edgeType = e2.otherSE.edgeType = ((e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION); return; } if (sortedEvents.Count == 3) // the line segments share an endpoint { sortedEvents[1].edgeType = sortedEvents[1].otherSE.edgeType = EdgeType.NON_CONTRIBUTING; if (sortedEvents[0] != null) // is the right endpoint the shared point? { sortedEvents[0].otherSE.edgeType = (e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION; } else // the shared point is the left endpoint { sortedEvents[2].otherSE.edgeType = (e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION; } DivideSegment(sortedEvents[0] != null ? sortedEvents[0] : sortedEvents[2].otherSE, sortedEvents[1].p); return; } if (!sortedEvents[0].Equals(sortedEvents[3].otherSE)) { // no segment includes totally the otherSE one sortedEvents[1].edgeType = EdgeType.NON_CONTRIBUTING; sortedEvents[2].edgeType = (e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION; DivideSegment(sortedEvents[0], sortedEvents[1].p); DivideSegment(sortedEvents[1], sortedEvents[2].p); return; } // one line segment includes the other one sortedEvents[1].edgeType = sortedEvents[1].otherSE.edgeType = EdgeType.NON_CONTRIBUTING; DivideSegment(sortedEvents[0], sortedEvents[1].p); sortedEvents[3].otherSE.edgeType = (e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION; DivideSegment(sortedEvents[3].otherSE, sortedEvents[2].p); }
public SweepEvent(Vector2 p, bool isLeft, PolygonType polygonType, SweepEvent otherSweepEvent) : this(p, isLeft, polygonType, otherSweepEvent, EdgeType.NORMAL) { }
Polygon ComputeInternal(PolygonOp operation) { Polygon result = null; sortedEvents = new List <SweepEvent>(); // Test 1 for trivial result case if (subject.contours.Count * clipping.contours.Count == 0) { if (operation == PolygonOp.DIFFERENCE) { result = subject; } else if (operation == PolygonOp.UNION || operation == PolygonOp.XOR) { result = (subject.contours.Count == 0) ? clipping : subject; } return(result); } // Test 2 for trivial result case Rectangle subjectBB = subject.boundingBox; Rectangle clippingBB = clipping.boundingBox; if (!subjectBB.Intersects(clippingBB)) { if (operation == PolygonOp.DIFFERENCE) { result = subject; } if (operation == PolygonOp.UNION || operation == PolygonOp.XOR) { result = subject; foreach (Contour c in clipping.contours) { result.AddContour(c); } } return(result); } // Add each segment to the eventQueue, sorted from left to right. foreach (Contour sCont in subject.contours) { for (int pParse1 = 0; pParse1 < sCont.points.Count; pParse1++) { ProcessSegment(sCont.GetSegment(pParse1), PolygonType.SUBJECT); } } foreach (Contour cCont in clipping.contours) { for (int pParse2 = 0; pParse2 < cCont.points.Count; pParse2++) { ProcessSegment(cCont.GetSegment(pParse2), PolygonType.CLIPPING); } } Connector connector = new Connector(); // This is the SweepLine. That is, we go through all the polygon edges // by sweeping from left to right. SweepEventSet S = new SweepEventSet(); float MINMAX_X = Mathf.Min(subjectBB.right, clippingBB.right) + (float)Point.PRECISION; float SUBJECTBBright = subjectBB.right + (float)Point.PRECISION; SweepEvent prev, next; int panicCounter = 0; while (!eventQueue.isEmpty) { if (panicCounter++ > 100000) { Debug.Log("PANIC!!"); break; } prev = null; next = null; SweepEvent e = eventQueue.Dequeue(); if ((operation == PolygonOp.INTERSECTION && e.p.x > MINMAX_X) || (operation == PolygonOp.DIFFERENCE && e.p.x > SUBJECTBBright)) { return(connector.ToPolygon()); // could result in several pieces //FromLargestLineStrip(); } if (operation == PolygonOp.UNION && e.p.x > MINMAX_X) { // add all the non-processed line segments to the result if (!e.isLeft) { connector.Add(e.segment); } while (!eventQueue.isEmpty) { e = eventQueue.Dequeue(); if (!e.isLeft) { connector.Add(e.segment); } } return(connector.ToPolygonFromLargestLineStrip()); } if (e.isLeft) // the line segment must be inserted into S { int pos = S.Insert(e); prev = (pos > 0) ? S.eventSet[pos - 1] : null; next = (pos < S.eventSet.Count - 1) ? S.eventSet[pos + 1] : null; if (prev == null) { e.inside = e.inOut = false; } else if (prev.edgeType != EdgeType.NORMAL) { if (pos - 2 < 0) // e overlaps with prev // Not sure how to handle the case when pos - 2 < 0, but judging // from the C++ implementation this looks like how it should be handled. { e.inside = e.inOut = false; if (prev.polygonType != e.polygonType) { e.inside = true; } else { e.inOut = true; } } else { SweepEvent prevTwo = S.eventSet[pos - 2]; if (prev.polygonType == e.polygonType) { e.inOut = !prev.inOut; e.inside = !prevTwo.inOut; } else { e.inOut = !prevTwo.inOut; e.inside = !prev.inOut; } } } else if (e.polygonType == prev.polygonType) { e.inside = prev.inside; e.inOut = !prev.inOut; } else { e.inside = !prev.inOut; e.inOut = prev.inside; } // Process a possible intersection between "e" and its next neighbor in S if (next != null) { PossibleIntersection(e, next); } // Process a possible intersection between "e" and its previous neighbor in S if (prev != null) { PossibleIntersection(prev, e); } } else // the line segment must be removed from S // Get the next and previous line segments to "e" in S { int otherPos = -1; for (int evt = 0; evt < S.eventSet.Count; evt++) { if (e.otherSE.Equals(S.eventSet[evt])) { otherPos = evt; break; } } if (otherPos != -1) { prev = (otherPos > 0) ? S.eventSet[otherPos - 1] : null; next = (otherPos < S.eventSet.Count - 1) ? S.eventSet[otherPos + 1] : null; } switch (e.edgeType) { case EdgeType.NORMAL: switch (operation) { case PolygonOp.INTERSECTION: if (e.otherSE.inside) { connector.Add(e.segment); } break; case PolygonOp.UNION: if (!e.otherSE.inside) { connector.Add(e.segment); } break; case PolygonOp.DIFFERENCE: if ((e.polygonType == PolygonType.SUBJECT && !e.otherSE.inside) || (e.polygonType == PolygonType.CLIPPING && e.otherSE.inside)) { connector.Add(e.segment); } break; case PolygonOp.XOR: connector.Add(e.segment); break; } break; case EdgeType.SAME_TRANSITION: if (operation == PolygonOp.INTERSECTION || operation == PolygonOp.UNION) { connector.Add(e.segment); } break; case EdgeType.DIFFERENT_TRANSITION: if (operation == PolygonOp.DIFFERENCE) { connector.Add(e.segment); } break; } if (otherPos != -1) { S.Remove(S.eventSet[otherPos]); } if (next != null && prev != null) { PossibleIntersection(prev, next); } } } if (operation == PolygonOp.DIFFERENCE || operation == PolygonOp.XOR) { return(connector.ToPolygon()); } else { return(connector.ToPolygonFromLargestLineStrip()); } }