/// <summary> /// Finds all intersections between the specified line segments, using a sweep line /// algorithm.</summary> /// <param name="lines"> /// An <see cref="Array"/> containing the <see cref="LineD"/> segments to intersect. /// </param> /// <returns> /// A lexicographically sorted <see cref="List{T}"/> containing the final <see /// cref="EventPoint"/> for every point of intersection between two or more <paramref /// name="lines"/>.</returns> /// <exception cref="ArgumentException"> /// <paramref name="lines"/> contains a <see cref="LineD"/> whose <see /// cref="LineD.Start"/> and <see cref="LineD.End"/> coordinates are equal.</exception> /// <exception cref="ArgumentNullException"> /// <paramref name="lines"/> is a null reference.</exception> /// <exception cref="InvalidOperationException"> /// <paramref name="lines"/> contains coordinates that caused corruption to an internal /// search structure.</exception> /// <remarks> /// <b>FindCore</b> creates the intermediate output which is further processed by one of /// the <see cref="Find"/> overloads.</remarks> internal List <EventPoint> FindCore(LineD[] lines) { BuildSchedule(lines); while (Schedule.Count > 0) { var node = Schedule.RootNode._next; Schedule.RemoveNode(node); HandleEvent(node._value); } if (SweepLine.Count > 0) { ThrowHelper.ThrowInvalidOperationException(Strings.SearchStructureCorrupted); } return(Crossings); }
/// <summary> /// Handles the specified <see cref="EventPoint"/> that was just removed from the <see /// cref="Schedule"/>.</summary> /// <param name="e"> /// The <see cref="EventPoint"/> to handle.</param> /// <remarks> /// <b>HandleEvent</b> always updates the <see cref="SweepLine"/>, and possibly the <see /// cref="Schedule"/> via <see cref="AddCrossing"/>.</remarks> private void HandleEvent(EventPoint e) { BraidedTreeNode <Int32, Int32> node, previous = null, next = null; Cursor = e.Shared; bool adding = false; // remove end point & crossing nodes for (int i = 0; i < e.Locations.Count; i++) { switch (e.Locations[i]) { case LineLocation.Start: adding = true; break; case LineLocation.End: node = SweepLine.FindNode(e.Lines[i]); if (node == null) { ThrowHelper.ThrowInvalidOperationException( Strings.SearchStructureCorrupted); } // remember surrounding lines previous = node._previous; next = node._next; SweepLine.RemoveNode(node); break; case LineLocation.Between: if (!SweepLine.Remove(e.Lines[i])) { ThrowHelper.ThrowInvalidOperationException( Strings.SearchStructureCorrupted); } adding = true; break; } } if (!adding) { // intersect remaining neighbors of removed lines if (previous._parent != null && next._parent != null) { AddCrossing(previous, next, e); } // record intersection event var lines = e.Lines; if (lines.Count < 2) { return; } /* * The sweep line algorithm would normally record TWO intersections for * overlapping lines that share the same lexicographic end point: one for the * start point, and one for the end point. So when we encounter an event that * contains only end points, we must check that its line segments arrive from at * least two different directions, and only then record an intersection. */ double slope = Slopes[lines[0]]; for (int i = 1; i < lines.Count; i++) { if (slope != Slopes[lines[i]]) { e.Normalize(Lines); Crossings.Add(e); break; } } return; } // update remaining sweep line to prepare for insertion var root = SweepLine.RootNode; for (node = root._next; node != root; node = node._next) { int index = node.Key; double slope = Slopes[index]; if (slope != Double.MaxValue) { PointD start = Lines[index].Start; Positions[index] = slope * (Cursor.Y - start.Y) + start.X; } } // (re-)insert start point & crossing nodes previous = next = null; for (int i = 0; i < e.Locations.Count; i++) { if (e.Locations[i] != LineLocation.End) { int index = e.Lines[i]; Positions[index] = Cursor.X; SweepLine.TryAddNode(index, 0, out node); // remember surrounding lines if (previous == null) { previous = node._previous; next = node._next; } } } // intersect outermost added lines with existing neighbors if (previous._parent != null) { AddCrossing(previous, previous._next, e); } if (next._parent != null) { AddCrossing(next._previous, next, e); } // record intersection event if (e.Lines.Count > 1) { e.Normalize(Lines); Crossings.Add(e); } }