private void CheckEdges(SubdivisionSearch search) { foreach (SubdivisionEdge edge in search.Source.Edges.Values) { var edgeElement = new SubdivisionElement(edge); var twinElement = new SubdivisionElement(edge._twin); // SubdivisionSearch always returns lexicographically increasing half-edges PointD start = edge._origin, end = edge._twin._origin; int result = PointDComparerX.CompareEpsilon(start, end, search.Source.Epsilon); var element = (result < 0 ? edgeElement : twinElement); PointD q = new PointD((start.X + end.X) / 2, (start.Y + end.Y) / 2); Assert.AreEqual(element, search.Find(q)); // brute force search may return half-edge or its twin var found = search.Source.Find(q, 1e-10); Assert.IsTrue(found == edgeElement || found == twinElement); // brute force search also supports comparison epsilon PointD offset = GeoAlgorithms.RandomPoint(-0.1, -0.1, 0.2, 0.2); found = search.Source.Find(q + offset, 0.25); Assert.IsTrue(found == edgeElement || found == twinElement); } }
private static void RandomPointsComparer(IPointDComparer comparer) { RectD bounds = new RectD(-100, -100, 200, 200); PointD[] points = GeoAlgorithms.RandomPoints(100, bounds, comparer, 2); for (int i = 0; i < points.Length; i++) { PointD p = points[i]; Assert.IsTrue(bounds.Contains(p)); if (i > 0) { Assert.AreEqual(+1, comparer.Compare(p, points[i - 1])); } if (i < points.Length - 1) { Assert.AreEqual(-1, comparer.Compare(p, points[i + 1])); } for (int j = 0; j < points.Length; j++) { if (i == j) { continue; } double distance = p.Subtract(points[j]).LengthSquared; Assert.GreaterOrEqual(distance, 4); } } }
public void Polygon() { for (int i = 3; i < 9; i++) { var polygon = new RegularPolygon(30.0 / i, i, PolygonOrientation.OnEdge); LineD[] lines = GeoAlgorithms.ConnectPoints(true, polygon.Vertices); CheckSearch(lines); } }
private void DrawIntersections(LineD[] lines) { const double radius = 4.0; // generate new random line set if desired if (lines == null) { Size scale = new Size(OutputBox.Width - 4 * radius, OutputBox.Height - 4 * radius); int count = MersenneTwister.Default.Next(3, 20); lines = new LineD[count]; for (int i = 0; i < lines.Length; i++) { lines[i] = GeoAlgorithms.RandomLine( 2 * radius, 2 * radius, scale.Width, scale.Height); } } _lines = lines; double epsilon = (double)ToleranceUpDown.Value; _crossings = (epsilon > 0.0 ? MultiLineIntersection.FindSimple(lines, epsilon) : MultiLineIntersection.FindSimple(lines)); LinesLabel.Content = String.Format("{0}/{1}", lines.Length, _crossings.Length); OutputBox.Children.Clear(); // draw line set foreach (LineD line in lines) { var shape = new Line() { X1 = line.Start.X, Y1 = line.Start.Y, X2 = line.End.X, Y2 = line.End.Y, Stroke = Brushes.Black }; OutputBox.Children.Add(shape); } // draw intersections as hollow circles foreach (var crossing in _crossings) { var circle = new Ellipse() { Width = 2 * radius, Height = 2 * radius, Stroke = Brushes.Red }; Canvas.SetLeft(circle, crossing.Shared.X - radius); Canvas.SetTop(circle, crossing.Shared.Y - radius); OutputBox.Children.Add(circle); } }
public void RandomPoints() { RectD bounds = new RectD(-100, -100, 200, 200); PointD[] points = GeoAlgorithms.RandomPoints(100, bounds); foreach (PointD p in points) { Assert.IsTrue(bounds.Contains(p)); } }
public void Random() { LineD[] lines = new LineD[100]; for (int i = 0; i < lines.Length; i++) { lines[i] = GeoAlgorithms.RandomLine(0, 0, 1000000, 1000000); } FindBoth(lines); }
public void VoronoiTest() { int count = MersenneTwister.Default.Next(10, 100); var points = new PointD[count]; for (int i = 0; i < points.Length; i++) { points[i] = GeoAlgorithms.RandomPoint(-1000, -1000, 2000, 2000); } var results = Voronoi.FindAll(points, new RectD(-1000, -1000, 2000, 2000)); var delaunay = results.ToDelaunySubdivision(); delaunay.Validate(); var voronoi = results.ToVoronoiSubdivision(); voronoi.Source.Validate(); // compare original and subdivision’s Delaunay edges var delaunayEdges = delaunay.ToLines(); Assert.AreEqual(results.DelaunayEdges.Length, delaunayEdges.Length); foreach (LineD edge in results.DelaunayEdges) { if (PointDComparerY.CompareExact(edge.Start, edge.End) > 0) { Assert.Contains(edge.Reverse(), delaunayEdges); } else { Assert.Contains(edge, delaunayEdges); } } // compare original and subdivision’s Voronoi regions var voronoiFaces = voronoi.Source.Faces; Assert.AreEqual(results.VoronoiRegions.Length, voronoiFaces.Count - 1); foreach (var face in voronoiFaces.Values) { if (face.OuterEdge == null) { continue; } int index = voronoi.FromFace(face); PointD[] polygon = results.VoronoiRegions[index]; CollectionAssert.AreEquivalent(polygon, face.OuterEdge.CyclePolygon); PointD site = results.GeneratorSites[index]; Assert.AreNotEqual(PolygonLocation.Outside, face.OuterEdge.Locate(site)); } }
private void GeometryTest() { Stopwatch timer = new Stopwatch(); var testCases = _geometryTestCases; Output(String.Format("{0,6}", " ")); foreach (TestCase test in testCases) { Output(String.Format("{0,12}", test.Name)); } Output("\n"); const int outerLoop = 100, innerLoop = 100; const int iterations = outerLoop * innerLoop; for (int size = 10; size <= 120; size += 10) { PointD[] points = new PointD[size]; for (int i = 0; i < outerLoop; i++) { for (int j = 0; j < points.Length; j++) { points[j] = GeoAlgorithms.RandomPoint(0, 0, 1000, 1000); } foreach (TestCase test in testCases) { // trigger JIT compilation and reset ticks if (i == 0) { test.FindPoints(points); test.Ticks = 0; } timer.Restart(); for (int k = 0; k < innerLoop; k++) { test.FindPoints(points); } timer.Stop(); test.Ticks += timer.ElapsedTicks; } } Output(String.Format("{0,6}", size)); foreach (TestCase test in testCases) { Output(String.Format("{0,12:N2}", AverageMicrosecs(test.Ticks, iterations))); } Output("\n"); } Output("\nTimes are µsec averages for point sets of the indicated size.\n"); }
public PointInPolygonTest() { InitializeComponent(); _polygon = GeoAlgorithms.RandomPolygon(0.0, 0.0, OutputBox.Width, OutputBox.Height); OutputBox.Children.Add(new Polygon() { Points = _polygon.ToWpfPoints(), Stroke = Brushes.Black }); }
public void PolygonCentroid() { PointD p0 = new PointD(0, 0), p1 = new PointD(1, 1), p2 = new PointD(2, 0); PointD p3 = new PointD(0, 2), p4 = new PointD(2, 2); Assert.AreEqual(new PointD(1, 1 / 3.0), GeoAlgorithms.PolygonCentroid(p0, p1, p2)); Assert.AreEqual(new PointD(1, 1 / 3.0), GeoAlgorithms.PolygonCentroid(p2, p1, p0)); Assert.AreEqual(p1, GeoAlgorithms.PolygonCentroid(p0, p3, p4, p2)); Assert.AreEqual(p1, GeoAlgorithms.PolygonCentroid(p2, p4, p3, p0)); }
public void Empty() { var search = new SubdivisionSearch(new Subdivision()); search.Validate(); for (int i = 0; i < 10; i++) { PointD q = GeoAlgorithms.RandomPoint(-100, -100, 200, 200); Assert.IsTrue(search.Find(q).IsUnboundedFace); Assert.IsTrue(search.Source.Find(q).IsUnboundedFace); } }
private void CheckVertices(SubdivisionSearch search) { foreach (PointD vertex in search.Source.Vertices.Keys) { var element = new SubdivisionElement(vertex); Assert.AreEqual(element, search.Find(vertex)); Assert.AreEqual(element, search.Source.Find(vertex)); // brute force search also supports comparison epsilon PointD offset = GeoAlgorithms.RandomPoint(-0.1, -0.1, 0.2, 0.2); Assert.AreEqual(element, search.Source.Find(vertex + offset, 0.25)); } }
protected override void OnMouseMove(MouseEventArgs args) { base.OnMouseMove(args); args.Handled = true; PointD cursor = args.GetPosition(OutputBox).ToPointD(); // determine relative location of cursor PolygonLocation location = (_tolerance == 0m ? GeoAlgorithms.PointInPolygon(cursor, _polygon) : GeoAlgorithms.PointInPolygon(cursor, _polygon, (double)_tolerance)); LocationLabel.Content = location; }
public void PointInPolygonEpsilon() { PointD[] p = { new PointD(0, 0), new PointD(1, 1), new PointD(2, 0) }; Assert.AreEqual(PolygonLocation.Inside, GeoAlgorithms.PointInPolygon(new PointD(1.0, 0.5), p, 0.2)); Assert.AreEqual(PolygonLocation.Outside, GeoAlgorithms.PointInPolygon(new PointD(1.0, -0.5), p, 0.2)); Assert.AreEqual(PolygonLocation.Outside, GeoAlgorithms.PointInPolygon(new PointD(0.0, 0.5), p, 0.2)); Assert.AreEqual(PolygonLocation.Outside, GeoAlgorithms.PointInPolygon(new PointD(2.0, 0.5), p, 0.2)); Assert.AreEqual(PolygonLocation.Vertex, GeoAlgorithms.PointInPolygon(new PointD(1.0, 0.9), p, 0.2)); Assert.AreEqual(PolygonLocation.Vertex, GeoAlgorithms.PointInPolygon(new PointD(0.0, 0.1), p, 0.2)); Assert.AreEqual(PolygonLocation.Vertex, GeoAlgorithms.PointInPolygon(new PointD(2.1, 0.0), p, 0.2)); Assert.AreEqual(PolygonLocation.Edge, GeoAlgorithms.PointInPolygon(new PointD(1.0, -0.1), p, 0.2)); Assert.AreEqual(PolygonLocation.Edge, GeoAlgorithms.PointInPolygon(new PointD(0.6, 0.5), p, 0.2)); Assert.AreEqual(PolygonLocation.Edge, GeoAlgorithms.PointInPolygon(new PointD(1.4, 0.5), p, 0.2)); }
public void PolygonArea() { PointD p0 = new PointD(0, 0), p1 = new PointD(1, 1), p2 = new PointD(2, 0); PointD p3 = new PointD(0, 2), p4 = new PointD(2, 2); // triangles in both orientations Assert.AreEqual(-1, GeoAlgorithms.PolygonArea(p0, p1, p2)); Assert.AreEqual(+1, GeoAlgorithms.PolygonArea(p2, p1, p0)); // squares in both orientations Assert.AreEqual(-4, GeoAlgorithms.PolygonArea(p0, p3, p4, p2)); Assert.AreEqual(+4, GeoAlgorithms.PolygonArea(p2, p4, p3, p0)); // collinear points and star shape Assert.AreEqual(0, GeoAlgorithms.PolygonArea(p0, p1, p4)); Assert.AreEqual(0, GeoAlgorithms.PolygonArea(p0, p1, p3, p1, p2, p1, p4, p1)); }
public void ConnectPoints() { PointD p0 = new PointD(0, 0), p1 = new PointD(1, 1), p2 = new PointD(2, 0); CollectionAssert.AreEqual(new LineD[0], GeoAlgorithms.ConnectPoints(false)); CollectionAssert.AreEqual(new LineD[0], GeoAlgorithms.ConnectPoints(true)); CollectionAssert.AreEqual(new LineD[0], GeoAlgorithms.ConnectPoints(false, p0)); CollectionAssert.AreEqual(new LineD[0], GeoAlgorithms.ConnectPoints(true, p0)); CollectionAssert.AreEqual(new[] { new LineD(p0, p1) }, GeoAlgorithms.ConnectPoints(false, p0, p1)); CollectionAssert.AreEqual(new[] { new LineD(p0, p1), new LineD(p1, p0) }, GeoAlgorithms.ConnectPoints(true, p0, p1)); CollectionAssert.AreEqual(new[] { new LineD(p0, p1), new LineD(p1, p2) }, GeoAlgorithms.ConnectPoints(false, p0, p1, p2)); CollectionAssert.AreEqual(new[] { new LineD(p0, p1), new LineD(p1, p2), new LineD(p2, p0) }, GeoAlgorithms.ConnectPoints(true, p0, p1, p2)); }
private void DrawConvexHull(PointD[] points) { const double radius = 4.0; // generate new random point set if desired if (points == null) { Size scale = new Size(OutputBox.Width - 4 * radius, OutputBox.Height - 4 * radius); int count = MersenneTwister.Default.Next(4, 40); RectD bounds = new RectD(2 * radius, 2 * radius, scale.Width, scale.Height); points = GeoAlgorithms.RandomPoints( count, bounds, new PointDComparerY(), 2 * radius); } _points = points; PointD[] polygon = GeoAlgorithms.ConvexHull(points); OutputBox.Children.Clear(); // draw hull vertices filled, other points hollow for (int i = 0; i < points.Length; i++) { bool isVertex = polygon.Contains(points[i]); var circle = new Ellipse() { Width = 2 * radius, Height = 2 * radius, Stroke = Brushes.Black, Fill = isVertex ? Brushes.Black : null }; Canvas.SetLeft(circle, points[i].X - radius); Canvas.SetTop(circle, points[i].Y - radius); OutputBox.Children.Add(circle); } // draw edges of convex hull OutputBox.Children.Add(new Polygon() { Points = polygon.ToWpfPoints(), Stroke = Brushes.Red }); }
public static GraphManager <PointD> CreateSubdivisionManager(GraphDialog dialog) { RectD output = new RectD(0, 0, dialog.OutputBox.Width, dialog.OutputBox.Height); RectD bounds = new RectD(8, 8, output.Width - 16, output.Height - 16); PointD[] points = GeoAlgorithms.RandomPoints(40, bounds, new PointDComparerX(), 20); VoronoiResults results = Voronoi.FindAll(points, output); Subdivision division = results.ToDelaunySubdivision(output, true); var manager = new GraphManager <PointD>(dialog, 8); manager.Graph = division; manager.Renderer = new GraphRenderer <PointD>(manager); // scaling factor to keep step costs above node distance manager._scaleCost = output.Width + output.Height; return(manager); }
public void ConvexHull() { PointD p0 = new PointD(0, 0), p1 = new PointD(1, 1), p2 = new PointD(2, 0); CollectionAssert.AreEqual(new[] { p0 }, GeoAlgorithms.ConvexHull(p0)); CollectionAssert.AreEqual(new[] { p0 }, GeoAlgorithms.ConvexHull(p0, p0)); CollectionAssert.AreEqual(new[] { p0 }, GeoAlgorithms.ConvexHull(p0, p0, p0)); CollectionAssert.AreEqual(new[] { p0, p1 }, GeoAlgorithms.ConvexHull(p0, p1)); CollectionAssert.AreEqual(new[] { p1, p0 }, GeoAlgorithms.ConvexHull(p0, p1, p0)); CollectionAssert.AreEqual(new[] { p1, p0 }, GeoAlgorithms.ConvexHull(p0, p0, p1)); CollectionAssert.AreEqual(new[] { p1, p0 }, GeoAlgorithms.ConvexHull(p0, p1, p1)); CollectionAssert.AreEqual(new[] { p1, p2, p0 }, GeoAlgorithms.ConvexHull(p0, p1, p2)); CollectionAssert.AreEqual(new[] { p1, p2, p0 }, GeoAlgorithms.ConvexHull(p1, p0, p2)); PointD p3 = new PointD(1, 0); CollectionAssert.AreEqual(new[] { p1, p2, p0 }, GeoAlgorithms.ConvexHull(p3, p1, p0, p2)); }
/// <summary> /// Creates a random <see cref="Subdivision"/> with the specified number of full edges and /// comparison epsilon.</summary> /// <param name="size"> /// The number of full edges, i.e. half the number of <see cref="Subdivision.Edges"/>, in /// the returned <see cref="Subdivision"/>.</param> /// <param name="epsilon"> /// The maximum absolute difference at which two coordinates should be considered equal. /// </param> /// <returns> /// A new random <see cref="Subdivision"/> with the specified <paramref name="size"/> and /// <paramref name="epsilon"/>.</returns> private static Subdivision CreateSubdivision(int size, double epsilon) { LineD[] lines = new LineD[size]; for (int i = 0; i < size; i++) { lines[i] = GeoAlgorithms.RandomLine(0, 0, 1000, 1000); } // split random set into non-intersecting line segments var crossings = MultiLineIntersection.FindSimple(lines, epsilon); var splitLines = MultiLineIntersection.Split(lines, crossings); Array.Copy(splitLines, lines, size); // re-randomize lines to eliminate split ordering CollectionsUtility.Randomize(lines); Subdivision division = Subdivision.FromLines(lines); division.Validate(); return(division); }
private static void CheckGridDivision(PolygonGrid.SubdivisionMap map) { map.Source.Validate(); // test finding vertices with FindNearestVertex for (int i = 0; i < map.Source.Vertices.Count; i++) { PointD q = map.Source.Vertices.GetKey(i) + GeoAlgorithms.RandomPoint(-2, -2, 4, 4); Assert.AreEqual(i, map.Source.FindNearestVertex(q)); } // test GetElementVertices and face mapping for (int x = 0; x < map.Target.Size.Width; x++) { for (int y = 0; y < map.Target.Size.Height; y++) { var polygon = map.Target.GetElementVertices(x, y); var face = map.Source.FindFace(polygon, true); Assert.AreSame(face, map.ToFace(new PointI(x, y))); Assert.AreEqual(new PointI(x, y), map.FromFace(face)); } } }
public void FindNearestEpsilonOverlap() { var points = new PointD[100]; for (int i = 0; i < points.Length; i++) { points[i] = new PointD((i % 10) / 10.0, i / 10); } // unpredictable sorting since epsilon overlaps point distances var comparer = new PointDComparerY() { Epsilon = 0.2 }; Array.Sort <PointD>(points, comparer.Compare); for (int i = 0; i < points.Length; i++) { PointD q = points[i]; Assert.AreEqual(i, comparer.FindNearest(points, q)); /* * Since epsilon overlaps adjacent points, sorting is unpredictable * and we cannot know which index contains the point nearest to a * non-collection point. So we use brute force to find that index. */ q = new PointD(q.X + 0.1, q.Y - 0.1); int index = GeoAlgorithms.NearestPoint(points, q); Assert.AreEqual(index, comparer.FindNearest(points, q)); q = new PointD(q.X - 0.4, q.Y + 0.4); index = GeoAlgorithms.NearestPoint(points, q); Assert.AreEqual(index, comparer.FindNearest(points, q)); } }
private void GeometryBasicTest() { Stopwatch timer = new Stopwatch(); long polyTicks = 0, polyEpsilonTicks = 0, lineTicks = 0, lineEpsilonTicks = 0; const double epsilon = 1e-10; const int outerLoop = 10000, innerLoop = 1000; const int iterations = outerLoop * innerLoop; for (int i = 0; i < outerLoop; i++) { PointD[] polygon = GeoAlgorithms.RandomPolygon(0, 0, 1000, 1000); LineD line = GeoAlgorithms.RandomLine(0, 0, 1000, 1000); LineD line2 = GeoAlgorithms.RandomLine(0, 0, 1000, 1000); PointD q = GeoAlgorithms.RandomPoint(0, 0, 1000, 1000); // trigger JIT compilation if (i == 0) { GeoAlgorithms.PointInPolygon(q, polygon); GeoAlgorithms.PointInPolygon(q, polygon, epsilon); line.Intersect(line2); line.Intersect(line2, epsilon); } timer.Restart(); for (int j = 0; j < innerLoop; j++) { line.Intersect(line2); } timer.Stop(); lineTicks += timer.ElapsedTicks; timer.Restart(); for (int j = 0; j < innerLoop; j++) { line.Intersect(line2, epsilon); } timer.Stop(); lineEpsilonTicks += timer.ElapsedTicks; timer.Restart(); for (int j = 0; j < innerLoop; j++) { GeoAlgorithms.PointInPolygon(q, polygon); } timer.Stop(); polyTicks += timer.ElapsedTicks; timer.Restart(); for (int j = 0; j < innerLoop; j++) { GeoAlgorithms.PointInPolygon(q, polygon, epsilon); } timer.Stop(); polyEpsilonTicks += timer.ElapsedTicks; } Output(" "); Output(String.Format("{0,12}", "Exact")); Output(String.Format("{0,12}", "Epsilon")); Output("\nLine Intersection "); Output(String.Format("{0,12:N2}", 1000 * AverageMicrosecs(lineTicks, iterations))); Output(String.Format("{0,12:N2}", 1000 * AverageMicrosecs(lineEpsilonTicks, iterations))); Output("\nPoint in Polygon "); Output(String.Format("{0,12:N2}", 1000 * AverageMicrosecs(polyTicks, iterations))); Output(String.Format("{0,12:N2}", 1000 * AverageMicrosecs(polyEpsilonTicks, iterations))); Output("\nTimes are nsec averages for exact and epsilon comparisons.\n"); Output("Point in Polygon uses random polygons with 3-60 vertices.\n"); }
private void SubdivisionSearchTest(bool random) { Stopwatch timer = new Stopwatch(); var testCases = _subdivSearchTestCases; Output(String.Format("{0,6}", " ")); foreach (TestCase test in testCases) { Output(String.Format("{0,12}", test.Name)); } Output("\n"); const int outerLoop = 100, innerLoop = 200; const int iterations = outerLoop * innerLoop; PointD[] query = new PointD[innerLoop]; PolygonGrid grid = null; int sizeMin, sizeMax, sizeStep; if (random) { sizeMin = 100; sizeMax = 1200; sizeStep = 100; } else { sizeMin = 6; sizeMax = 30; sizeStep = 2; RegularPolygon polygon = new RegularPolygon(10, 4, PolygonOrientation.OnEdge); grid = new PolygonGrid(polygon); } for (int size = sizeMin; size <= sizeMax; size += sizeStep) { Subdivision division; if (random) { // create subdivision from random lines (few faces) division = CreateSubdivision(size, 1e-10); } else { // create subdivision from grid of diamonds (many faces) grid.Element = new RegularPolygon(900 / size, 4, PolygonOrientation.OnEdge); grid.Size = new SizeI(size, size); division = grid.ToSubdivision(PointD.Empty).Source; } var ordered = new SubdivisionSearch(division, true); var randomized = new SubdivisionSearch(division, false); // test cases: BruteForce, Ordered, Randomized testCases[0].FindSubdivision = (q) => division.Find(q, division.Epsilon); testCases[1].FindSubdivision = (q) => ordered.Find(q); testCases[2].FindSubdivision = (q) => randomized.Find(q); // trigger JIT compilation and reset ticks foreach (TestCase test in testCases) { test.FindSubdivision(PointD.Empty); test.Ticks = 0; } for (int j = 0; j < outerLoop; j++) { for (int k = 0; k < query.Length; k++) { query[k] = GeoAlgorithms.RandomPoint(0, 0, 1000, 1000); } foreach (TestCase test in testCases) { timer.Restart(); for (int k = 0; k < query.Length; k++) { test.FindSubdivision(query[k]); } timer.Stop(); test.Ticks += timer.ElapsedTicks; } } Output(String.Format("{0,6:N0}", division.Edges.Count / 2)); foreach (TestCase test in testCases) { Output(String.Format("{0,12:N2}", AverageMicrosecs(test.Ticks, iterations))); } Output("\n"); } Output("\nTimes are µsec averages for subdivisions of the indicated edge count,\n"); if (random) { Output("based on random line sets (few faces, completely random edges).\n"); } else { Output("based on grids of squares (many faces, strictly ordered edges).\n"); } }
private void RangeTreeTest() { Stopwatch timer = new Stopwatch(); var testCases = _rangeTreeTestCases; Output(String.Format("{0,8}", " ")); foreach (TestCase test in testCases) { Output(String.Format("{0,14}", test.Name)); } Output("\n"); // count units of size x operation in milliseconds, // rather than individual operations in microseconds const int outerLoop = 10, innerLoop = 10; const int iterations = outerLoop * innerLoop * 1000; // bounds of search space, size of point set, // range & iterations for range search const int bounds = 10000, size = 60000; const int range = size / 80, rangeIterations = size / 120; long[] addTicks = new long[testCases.Length], iterateTicks = new long[testCases.Length], searchTicks = new long[testCases.Length], rangeTicks = new long[testCases.Length], removeTicks = new long[testCases.Length]; var array = new KeyValuePair <PointD, String> [size]; for (int i = 0; i < outerLoop; i++) { // generate random spatial keys for (int j = 0; j < array.Length; j++) { var key = GeoAlgorithms.RandomPoint(0, 0, bounds, bounds); array[j] = new KeyValuePair <PointD, String>(key, null); } // trigger JIT compilation if (i == 0) { foreach (TestCase test in testCases) { test.RangeTree.Clear(); foreach (var pair in array) { test.RangeTree.Add(pair.Key, pair.Value); } } } for (int j = 0; j < innerLoop; j++) { for (int k = 0; k < testCases.Length; k++) { TestCase test = testCases[k]; test.RangeTree.Clear(); timer.Restart(); foreach (var pair in array) { test.RangeTree.Add(pair.Key, pair.Value); } timer.Stop(); addTicks[k] += timer.ElapsedTicks; double sum = 0; timer.Restart(); foreach (var pair in test.RangeTree) { sum += pair.Key.X; } timer.Stop(); iterateTicks[k] += timer.ElapsedTicks; timer.Restart(); foreach (var pair in array) { test.RangeTree.ContainsKey(pair.Key); } timer.Stop(); searchTicks[k] += timer.ElapsedTicks; /* * BraidedTree performs one-dimensional range searches within a point set * sorted by y-coordinates, using PointDComparerY. Therefore, we supply a * condition that limits x-coordinates. */ var braidedTree = test.RangeTree as BraidedTree <PointD, String>; var quadTree = test.RangeTree as QuadTree <String>; timer.Restart(); for (int l = 0; l < array.Length; l += size / rangeIterations) { PointD p = array[l].Key; RectD rect = new RectD(p.X, p.Y, range, range); if (braidedTree != null) { braidedTree.FindRange(rect.TopLeft, rect.BottomRight, n => (n.Key.X >= rect.Left && n.Key.X <= rect.Right)); } else if (quadTree != null) { quadTree.FindRange(rect); } } timer.Stop(); rangeTicks[k] += timer.ElapsedTicks; timer.Restart(); foreach (var pair in array) { test.RangeTree.Remove(pair.Key); } timer.Stop(); removeTicks[k] += timer.ElapsedTicks; } } } Output(String.Format("{0,8}", "Add")); for (int i = 0; i < testCases.Length; i++) { Output(String.Format("{0,14:N2}", AverageMicrosecs(addTicks[i], iterations))); } Output(String.Format("\n{0,8}", "Iterate")); for (int i = 0; i < testCases.Length; i++) { Output(String.Format("{0,14:N2}", AverageMicrosecs(iterateTicks[i], iterations))); } Output(String.Format("\n{0,8}", "Search")); for (int i = 0; i < testCases.Length; i++) { Output(String.Format("{0,14:N2}", AverageMicrosecs(searchTicks[i], iterations))); } Output(String.Format("\n{0,8}", "Range")); for (int i = 0; i < testCases.Length; i++) { Output(String.Format("{0,14:N2}", AverageMicrosecs(rangeTicks[i], iterations))); } Output(String.Format("\n{0,8}", "Remove")); for (int i = 0; i < testCases.Length; i++) { Output(String.Format("{0,14:N2}", AverageMicrosecs(removeTicks[i], iterations))); } const double share = range / (double)bounds; Output(String.Format("\n\nTimes are msec averages for {0:N0} random points.\n", size)); Output(String.Format("Range search performs {0} iterations on {1:0.00%} of search space.\n", rangeIterations, share * share)); }
private void NearestPointTest() { Stopwatch timer = new Stopwatch(); var testCases = _nearestPointTestCases; Output(String.Format("{0,6}", " ")); foreach (TestCase test in testCases) { Output(String.Format("{0,12}", test.Name)); } Output("\n"); const int outerLoop = 100, innerLoop = 100; const int iterations = outerLoop * innerLoop; var comparer = new PointDComparerY(); PointD[] query = new PointD[innerLoop]; for (int size = 1000; size <= 12000; size += 1000) { PointD[] points = new PointD[size]; for (int i = 0; i < points.Length; i++) { points[i] = GeoAlgorithms.RandomPoint(0, 0, 1000, 1000); } Array.Sort <PointD>(points, comparer); // trigger JIT compilation and reset ticks foreach (TestCase test in testCases) { test.FindPointIndex(comparer, points, PointD.Empty); test.Ticks = 0; } for (int j = 0; j < outerLoop; j++) { for (int k = 0; k < query.Length; k++) { query[k] = GeoAlgorithms.RandomPoint(0, 0, 1000, 1000); } foreach (TestCase test in testCases) { timer.Restart(); for (int k = 0; k < query.Length; k++) { test.FindPointIndex(comparer, points, query[k]); } timer.Stop(); test.Ticks += timer.ElapsedTicks; } } Output(String.Format("{0,6:N0}", size)); foreach (TestCase test in testCases) { Output(String.Format("{0,12:N2}", AverageMicrosecs(test.Ticks, iterations))); } Output("\n"); } Output("\nTimes are µsec averages for point arrays of the indicated size.\n"); }