public void WalkTree() { const int radius = 3000; var array = new KeyValuePair <PointD, CloneableType> [2 * radius]; for (int i = 0; i < array.Length; i++) { array[i] = new KeyValuePair <PointD, CloneableType>( new PointD(i - radius, radius - i), new CloneableType(String.Format("bar{0:D3} value", i))); } var tree = new QuadTree <CloneableType>( new RectD(-2 * radius, -2 * radius, 4 * radius, 4 * radius)); Assert.AreEqual(tree, tree.RootNode.Tree); Assert.AreEqual(1, tree.Nodes.Count); // test adding elements foreach (var pair in array) { tree.Add(pair.Key, pair.Value); } Assert.AreEqual(array.Length, tree.Count); Assert.AreEqual(145, tree.Nodes.Count); // test moving elements without hint node PointD offset = new PointD(0.1, 0.1); foreach (var pair in array) { tree.Move(pair.Key, pair.Key + offset); } // test moving elements with hint node QuadTreeNode <CloneableType> node = null; foreach (var pair in array) { node = tree.Move(pair.Key + offset, pair.Key, node); } Assert.AreEqual(array.Length, tree.Count); Assert.AreEqual(145, tree.Nodes.Count); // test finding elements foreach (var pair in array) { CloneableType value; Assert.IsTrue(tree.TryGetValue(pair.Key, out value)); Assert.AreEqual(pair.Value, value); node = tree.FindNode(pair.Key); Assert.AreEqual(tree, node.Tree); Assert.IsTrue(node.Data.Contains(pair)); var valueNode = tree.FindNodeByValue(pair.Value); Assert.AreEqual(node, valueNode); } // test finding elements in range var range = new RectD(-radius, 0, 2 * radius, 2 * radius); var elements = tree.FindRange(range); Assert.AreEqual(radius + 1, elements.Count); for (int i = 0; i <= radius; i++) { CloneableType value; Assert.IsTrue(elements.TryGetValue(array[i].Key, out value)); Assert.AreEqual(array[i].Value, value); } // compare range search to BraidedTree var braidedTree = new BraidedTree <PointD, CloneableType>(PointDComparerY.CompareExact); foreach (var pair in array) { braidedTree.Add(pair); } // BraidedTree sorts by y-coordinates, so we must restrict x-coordinates var braidedElements = braidedTree.FindRange(range.TopLeft, range.BottomRight, n => (n.Key.X >= range.Left && n.Key.X <= range.Right)); Assert.AreEqual(elements.Count, braidedElements.Count); foreach (var pair in elements) { CloneableType value; Assert.IsTrue(braidedElements.TryGetValue(pair.Key, out value)); Assert.AreEqual(pair.Value, value); } // test element enumeration foreach (var pair in tree) { CloneableType value; Assert.IsTrue(braidedTree.TryGetValue(pair.Key, out value)); Assert.AreEqual(value, pair.Value); braidedTree.Remove(pair.Key); } Assert.AreEqual(0, braidedTree.Count); // test removing elements foreach (var pair in array) { Assert.IsTrue(tree.Remove(pair.Key)); } Assert.AreEqual(0, tree.Count); Assert.AreEqual(1, tree.Nodes.Count); }
/// <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); } }