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 SubdivisionSearch CheckSearch(params LineD[] lines) { Subdivision division = Subdivision.FromLines(lines); division.Validate(); SubdivisionSearch search = new SubdivisionSearch(division); search.Validate(); CheckVertices(search); CheckEdges(search); PointD[] points = new[] { new PointD(+1, +1), new PointD(+1, -1), new PointD(-1, +1), new PointD(-1, -1) }; if (division.Faces.Count == 1) { foreach (PointD point in points) { Assert.IsTrue(search.Find(point).IsUnboundedFace); Assert.IsTrue(division.Find(point).IsUnboundedFace); } } else { var element = new SubdivisionElement(division.Faces[1]); foreach (PointD point in points) { Assert.AreEqual(element, search.Find(point)); Assert.AreEqual(element, division.Find(point)); } } points = new[] { new PointD(+10, +10), new PointD(+10, -10), new PointD(-10, +10), new PointD(-10, -10) }; foreach (PointD point in points) { Assert.IsTrue(search.Find(point).IsUnboundedFace); Assert.IsTrue(division.Find(point).IsUnboundedFace); } return(search); }
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); } }
private SubdivisionSearch CheckSearch(Subdivision division) { division.Validate(); SubdivisionSearch search = new SubdivisionSearch(division); search.Validate(); CheckVertices(search); CheckEdges(search); foreach (SubdivisionFace face in division.Faces.Values) { if (face.OuterEdge == null) { continue; } PointD centroid = face.OuterEdge.CycleCentroid; var element = new SubdivisionElement(face); Assert.AreEqual(element, search.Find(centroid)); Assert.AreEqual(element, division.Find(centroid)); } return(search); }
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"); } }