public SweepEvent(Point p, bool isLeft, PolygonType polygonType, SweepEvent otherSweepEvent, EdgeType edgeType) { this.p = p; this.isLeft = isLeft; this.polygonType = polygonType; this.otherSE = otherSweepEvent; this.edgeType = edgeType; }
public bool Equals(SweepEvent e2) { bool equal = isLeft == e2.isLeft && polygonType == e2.polygonType && inOut == e2.inOut && edgeType == e2.edgeType && inside == e2.inside && Point.EqualsBoth(p, e2.p); if (!equal) return false; return otherSE.isLeft == e2.otherSE.isLeft && otherSE.polygonType == e2.otherSE.polygonType && otherSE.inOut == e2.otherSE.inOut && otherSE.edgeType == e2.otherSE.edgeType && otherSE.inside == e2.otherSE.inside && Point.EqualsBoth(otherSE.p, e2.otherSE.p); }
public void Remove(SweepEvent key) { int keyIndex = eventSet.IndexOf (key); if (keyIndex == -1) return; eventSet.RemoveAt (keyIndex); }
// Should only be called by segmentCompare private bool CompareSweepEvent(SweepEvent e1, SweepEvent e2) { if (e1.p.x > e2.p.x) // Different x coordinate return true; if (e2.p.x > e1.p.x) // Different x coordinate return false; if (e1.p != e2.p) // Different points, but same x coordinate. The event with lower y coordinate is processed first return e1.p.y > e2.p.y; if (e1.isLeft != e2.isLeft) // Same point, but one is a left endpoint and the other a right endpoint. The right endpoint is processed first return e1.isLeft; // Same point, both events are left endpoints or both are right endpoints. The event associate to the bottom segment is processed first return e1.isAbove (e2.otherSE.p); }
public int Insert(SweepEvent item) { int count = eventSet.Count; if (count == 0) { eventSet.Add (item); return 0; } eventSet.Add (null); // Expand the Vector by one. int i = count - 1; while (i >= 0 && SegmentCompare(item, eventSet[i])) { eventSet [i + 1] = eventSet [i]; i--; } eventSet [i + 1] = item; return i + 1; }
private bool SegmentCompare(SweepEvent e1, SweepEvent e2) { if (e1.Equals(e2)) return false; if (signedArea (e1.p, e1.otherSE.p, e2.p) != 0 || signedArea (e1.p, e1.otherSE.p, e2.otherSE.p) != 0) { // Segments are not collinear // If they share their left endpoint use the right endpoint to sort if (Point.EqualsBoth(e1.p, e2.p)) return e1.isBelow (e2.otherSE.p); if (CompareSweepEvent (e1, e2)) return e2.isAbove (e1.p); return e1.isBelow (e2.p); } if (Point.EqualsBoth(e1.p, e2.p)) // Segments colinear return false; return CompareSweepEvent (e1, e2); }
// The ordering is reversed because push and pop are faster. int CompareSweepEvent(SweepEvent e1, SweepEvent e2) { if (e1.Equals(e2)) return 0; if (e1.p.x - e2.p.x > Point.PRECISION) // Different x coordinate return -1; if (e1.p.x - e2.p.x < -Point.PRECISION) // Different x coordinate return 1; if ( e1.p.y - e2.p.y > Point.PRECISION) // Different points, but same x coordinate. The event with lower y coordinate is processed first return -1; if ( e1.p.y - e2.p.y < -Point.PRECISION ) // Different points, but same x coordinate. The event with lower y coordinate is processed first return 1; if (e1.isLeft != e2.isLeft) // Same point, but one is a left endpoint and the other a right endpoint. The right endpoint is processed first return (e1.isLeft) ? -1 : 1; // Same point, both events are left endpoints or both are right endpoints. The event associate to the bottom segment is processed first bool isAbove = e1.isAbove (e2.otherSE.p); return isAbove ? -1 : 1; }
public void Enqueue(SweepEvent obj) { if (!sorted) { elements.Add (obj); pointer = elements.Count-1; return; } // If already sorted use insertionSort on the inserted item. // int count = pointer = elements.Count; if (pointer < 0) { elements.Add (obj); pointer = 0; return; } elements.Add (null); // Expand the Vector by one. int i = pointer; pointer++; while (i >= 0 && CompareSweepEvent(obj, elements[i]) == -1) { elements [i + 1] = elements [i]; i--; } elements [i + 1] = obj; }
void PossibleIntersection(SweepEvent e1, SweepEvent e2) { IntersectResult intData = FindIntersection(e1.segment, e2.segment); int numIntersections = intData.max; Point ip1 = intData.point1; if (numIntersections == 0) { return; } if (numIntersections == 1 && (Point.EqualsBoth(e1.p, e2.p) || Point.EqualsBoth(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.EqualsBoth(e1.p, ip1) && !Point.EqualsBoth(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.EqualsBoth(e2.p, ip1) && !Point.EqualsBoth(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.EqualsBoth(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.EqualsBoth(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); }
Polygon ComputeInternal(PolygonOp operation) { Polygon result = null; sortedEvents = new List <SweepEvent>(); // Init event queue eventQueue = new EventQueue(); // 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. for (int k = 0; k < subject.contours.Count; k++) { Contour sCont = subject.contours[k]; int sContPointsCount = sCont.points.Count; for (int pParse1 = 0; pParse1 < sContPointsCount; pParse1++) { ProcessSegment(sCont.GetSegment(pParse1), PolygonType.SUBJECT); } } for (int k = 0; k < clipping.contours.Count; k++) { Contour cCont = clipping.contours[k]; int cContPointsCount = cCont.points.Count; for (int pParse2 = 0; pParse2 < cContPointsCount; 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(); double MINMAX_X = Math.Min(subjectBB.right, clippingBB.right) + Point.PRECISION; SweepEvent prev, next; int panicCounter = 0; // This is a safety check to prevent infinite loops (very rare but could happen due to floating-point issues with a high number of points) while (!eventQueue.isEmpty) { if (panicCounter++ > 10000) { 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 > subjectBB.right + Point.PRECISION)) { return(connector.ToPolygonFromLargestLineStrip()); } 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); } } } return(connector.ToPolygonFromLargestLineStrip()); }
public SweepEvent(Point p, bool isLeft, PolygonType polygonType, SweepEvent otherSweepEvent) : this(p, isLeft, polygonType, otherSweepEvent, EdgeType.NORMAL) { }
bool Sec(SweepEvent e1, SweepEvent e2) { // Different x coordinate if ( e1.p.x - e2.p.x > Point.PRECISION || e1.p.x - e2.p.x < -Point.PRECISION ) { return e1.p.x > e2.p.x; } // Same x coordinate. The event with lower y coordinate is processed first if ( e1.p.y - e2.p.y > Point.PRECISION || e1.p.y - e2.p.y < -Point.PRECISION) { return e1.p.y > e2.p.y; } // Same point, but one is a left endpoint and the other a right endpoint. The right endpoint is processed first if (e1.isLeft != e2.isLeft) { return e1.isLeft; } // Same point, both events are left endpoints or both are right endpoints. The event associate to the bottom segment is processed first return e1.isAbove(e2.otherSE.p); }
void ProcessSegment(Segment segment, PolygonType polygonType) { if (Point.EqualsBoth(segment.start, segment.end)) return; SweepEvent e1 = new SweepEvent(segment.start, true, polygonType); SweepEvent e2 = new SweepEvent(segment.end, true, polygonType, e1); e1.otherSE = e2; if (e1.p.x < e2.p.x - Point.PRECISION ) { e2.isLeft = false; } else if (e1.p.x > e2.p.x + Point.PRECISION) { e1.isLeft = false; } else if (e1.p.y < e2.p.y - Point.PRECISION) { // the segment is vertical. The bottom endpoint is processed before the top endpoint e2.isLeft = false; } else { e1.isLeft = false; } // Pushing it so the que is sorted from left to right, with object on the left // having the highest priority. eventQueue.Enqueue(e1); eventQueue.Enqueue(e2); }
void PossibleIntersection(SweepEvent e1, SweepEvent e2) { IntersectResult intData = FindIntersection(e1.segment, e2.segment); int numIntersections = intData.max; Point ip1 = intData.point1; if (numIntersections == 0) return; if (numIntersections == 1 && (Point.EqualsBoth(e1.p, e2.p) || Point.EqualsBoth(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.EqualsBoth(e1.p,ip1) && !Point.EqualsBoth(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.EqualsBoth(e2.p, ip1) && !Point.EqualsBoth(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.EqualsBoth(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.EqualsBoth(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); }
void DivideSegment(SweepEvent e, Point p) { // "Right event" of the "left line segment" resulting from dividing e (the line segment associated to e) SweepEvent r = new SweepEvent(p, false, e.polygonType, e, e.edgeType); // "Left event" of the "right line segment" resulting from dividing e (the line segment associated to e) SweepEvent l = new SweepEvent(p, true, e.polygonType, e.otherSE, e.otherSE.edgeType); if (Sec(l, e.otherSE)) { // avoid a rounding error. The left event would be processed after the right event e.otherSE.isLeft = true; e.isLeft = false; } e.otherSE.otherSE = l; e.otherSE = r; eventQueue.Enqueue(l); eventQueue.Enqueue(r); }
public SweepEvent(Point p, bool isLeft, PolygonType polygonType, SweepEvent otherSweepEvent) : this(p,isLeft,polygonType,otherSweepEvent, EdgeType.NORMAL) { }