private ContourPolygon ConnectEdges() { var result = new ContourPolygon(); var resultEvents = ResultEvents .Where(it => (it.IsStart && it.InResult) || (!it.IsStart && it.OtherEvent.InResult)).ToList(); // Due to overlapping edges the resultEvents list can be not wholly sorted var sorted = false; while (!sorted) { sorted = true; for (int i = 0; i < resultEvents.Count; i++) { if (i + 1 < resultEvents.Count && SweepEvent.CompareTo(resultEvents[i], resultEvents[i + 1]) == 1) { var tmp = resultEvents[i]; resultEvents[i] = resultEvents[i + 1]; resultEvents[i + 1] = tmp; sorted = false; } } } // We cannot do a foreach because we need to set PositionInResult for (int i = 0; i < resultEvents.Count; i++) { var resultEvent = resultEvents[i]; resultEvent.PositionInResult = i; } foreach (var resultEvent in resultEvents) { if (!resultEvent.IsStart) { var tmp = resultEvent.PositionInResult; resultEvent.PositionInResult = resultEvent.OtherEvent.PositionInResult; resultEvent.OtherEvent.PositionInResult = tmp; } } var processed = new BitArray(resultEvents.Count); var depth = new List <int>(); var holeOf = new List <int>(); for (int i = 0; i < resultEvents.Count; i++) { if (processed[i]) { continue; } var contour = new Contour(); result.Add(contour); var contourId = result.NumberOfContours - 1; depth.Add(0); holeOf.Add(-1); if (resultEvents[i].PreviousInResult != null) { var lowerContourId = resultEvents[i].PreviousInResult.ContourId; if (!resultEvents[i].PreviousInResult.ResultInOut) { result[lowerContourId].AddHole(contourId); holeOf[contourId] = lowerContourId; depth[contourId] = depth[lowerContourId] + 1; contour.External = false; } else if (!result[lowerContourId].External) { result[holeOf[lowerContourId]].AddHole(contourId); holeOf[contourId] = holeOf[lowerContourId]; depth[contourId] = depth[lowerContourId]; contour.External = false; } } var pos = i; var initial = resultEvents[i].Point; contour.AddVertex(initial); while (pos >= i) { processed[pos] = true; if (resultEvents[pos].IsStart) { resultEvents[pos].ResultInOut = false; resultEvents[pos].ContourId = contourId; } else { resultEvents[pos].OtherEvent.ResultInOut = true; resultEvents[pos].OtherEvent.ContourId = contourId; } pos = resultEvents[pos].PositionInResult; processed[pos] = true; contour.AddVertex(resultEvents[pos].Point); pos = NextPos(pos, resultEvents, processed, i); } pos = pos == -1 ? i : pos; processed[pos] = processed[resultEvents[pos].PositionInResult] = true; resultEvents[pos].OtherEvent.ResultInOut = true; resultEvents[pos].OtherEvent.ContourId = contourId; if ((depth[contourId] & 1) != 0) { contour.ChangeOrientation(); } } return(result); }
private void HandleEvent(IBST <SweepEvent> events, IBST <StatusItem> status, SweepEvent ev) { ResultEvents.Add(ev); // Optimization 2 if ((Operation == OperationType.Intersection && ev.Point.x > RightBound) || (Operation == OperationType.Difference && ev.Point.x > SubjectBoundingBox.xMax)) { // We need to connect edges now, so just clear all events. This will result in us immediately // going to ConnectEdges() since there are no more events to handle. InitializeEvents(new List <SweepEvent>()); return; } if (ev.IsStart) // The line segment must be inserted into status { ev.StatusItem = new StatusItem(ev); if (!status.Insert(ev.StatusItem)) { throw new ArgumentException("Failed to insert into state"); } StatusItem prev; var prevFound = status.FindNextSmallest(ev.StatusItem, out prev); ComputeFields(ev, prev, prevFound); StatusItem next; if (status.FindNextBiggest(ev.StatusItem, out next)) { // Process a possible intersection between "ev" and its next neighbor in status if (PossibleIntersection(ev, next.SweepEvent, events) == 2) { ComputeFields(ev, prev, prevFound); ComputeFields(next.SweepEvent, ev.StatusItem, true); } } // Process a possible intersection between "ev" and its previous neighbor in status if (prevFound) { if (PossibleIntersection(prev.SweepEvent, ev, events) == 2) { StatusItem prevprev; var prevprevFound = status.FindNextSmallest(prev, out prevprev); ComputeFields(prev.SweepEvent, prevprev, prevprevFound); ComputeFields(ev, prev, prevFound); } } } else { // The line segment must be removed from status ev = ev.OtherEvent; // We work with the left event StatusItem prev, next; var prevFound = status.FindNextSmallest(ev.StatusItem, out prev); var nextFound = status.FindNextBiggest(ev.StatusItem, out next); // Delete line segment associated to "ev" from status and check for intersection between the neighbors of "ev" in status status.Delete(ev.StatusItem); if (nextFound && prevFound) { PossibleIntersection(prev.SweepEvent, next.SweepEvent, events); } } }