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);
            }
        }
예제 #2
0
        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");
        }
예제 #3
0
        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 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));
            }
        }
        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);
            }
        }
예제 #6
0
        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));
                }
            }
        }
예제 #7
0
        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));
        }
예제 #8
0
        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");
        }
예제 #9
0
        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");
        }
예제 #10
0
        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");
            }
        }