/// <summary> /// Adds an intersection <see cref="EventPoint"/> to the <see cref="Schedule"/> if the /// two specified <see cref="SweepLine"/> nodes indicate a line crossing.</summary> /// <param name="a"> /// The first <see cref="SweepLine"/> node to examine.</param> /// <param name="b"> /// The second <see cref="SweepLine"/> node to examine.</param> /// <param name="e"> /// The current <see cref="EventPoint"/> which receives a detected crossing that occurs /// exactly at the <see cref="Cursor"/>.</param> /// <remarks> /// If the <see cref="Schedule"/> already contains an <see cref="EventPoint"/> for the /// computed intersection, <b>AddCrossing</b> adds the indicated lines to the existing /// <see cref="EventPoint"/> if they are not already present.</remarks> private void AddCrossing(BraidedTreeNode <Int32, Int32> a, BraidedTreeNode <Int32, Int32> b, EventPoint e) { int aIndex = a.Key, bIndex = b.Key; LineIntersection c = Lines[aIndex].Intersect(Lines[bIndex]); // ignore crossings that involve only start or end points, // as those line events have been scheduled during initialization if ((c.First == LineLocation.Between && LineIntersection.Contains(c.Second)) || (LineIntersection.Contains(c.First) && c.Second == LineLocation.Between)) { // quit if crossing occurs before cursor PointD p = c.Shared.Value; int result = PointDComparerY.CompareExact(Cursor, p); if (result > 0) { return; } // update schedule if crossing occurs after cursor if (result < 0) { BraidedTreeNode <PointD, EventPoint> node; Schedule.TryAddNode(p, new EventPoint(p), out node); e = node._value; } // add crossing to current or scheduled event point e.TryAddLines(aIndex, c.First, bIndex, c.Second); } }
/// <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); } }