public void testUnionSloppySuccess() { var polygons = new List <S2Polygon>(); polygons.Add(adj0); polygons.Add(adj1); var union = S2Polygon.DestructiveUnionSloppy(polygons, S1Angle.FromDegrees(0.1)); assertEquals(1, union.NumLoops); if (union.NumLoops != 1) { return; } var s2Loop = union.Loop(0); assertEquals(8, s2Loop.NumVertices); if (s2Loop.NumVertices != 8) { return; } assertPointApproximatelyEquals(s2Loop, 0, 2.0, 0.0, 0.01); assertPointApproximatelyEquals(s2Loop, 1, 1.0, 0.0, 0.01); assertPointApproximatelyEquals(s2Loop, 2, 0.0, 0.0, 0.01); assertPointApproximatelyEquals(s2Loop, 3, 0.0, 1.0, 0.01); assertPointApproximatelyEquals(s2Loop, 4, 0.0, 2.0, 0.01); assertPointApproximatelyEquals(s2Loop, 5, 1.0, 2.0, 0.01); assertPointApproximatelyEquals(s2Loop, 6, 2.0, 2.0, 0.01); assertPointApproximatelyEquals(s2Loop, 7, 2.0, 1.0, 0.01); }
private static void TestS2Polygon(string[] input_strs, string expected_str, EdgeType edge_type) { S2Builder builder = new(new Options()); S2Polygon output = new(); builder.StartLayer(new S2PolygonLayer( output, new S2PolygonLayer.Options(edge_type))); bool is_full = false; foreach (var input_str in input_strs) { if (input_str == "full") { is_full = true; } builder.AddPolygon(MakeVerbatimPolygonOrDie(input_str)); } builder.AddIsFullPolygonPredicate(IsFullPolygon(is_full)); Assert.True(builder.Build(out var error)); // The input strings in tests may not be in normalized form, so we build an // S2Polygon and convert it back to a string. S2Polygon expected = MakePolygonOrDie(expected_str); Assert.Equal(expected.ToDebugString(), output.ToDebugString()); }
private static bool InternalMakePolygon(string str, bool normalize_loops, out S2Polygon polygon) { polygon = null; if (str == "empty") { str = ""; } var loop_strs = SplitString(str, ';'); var loops = new List <S2Loop>(); foreach (var loop_str in loop_strs) { if (!MakeLoop(loop_str, out var loop)) { return(false); } // Don't normalize loops that were explicitly specified as "full". if (normalize_loops && !loop.IsFull()) { loop.Normalize(); } loops.Add(loop); } polygon = new S2Polygon(loops); return(true); }
public void testUnionSloppyFailure() { var polygons = new List <S2Polygon>(); polygons.Add(adj0); polygons.Add(unAdj); // The polygons are sufficiently far apart that this angle will not // bring them together: var union = S2Polygon.DestructiveUnionSloppy(polygons, S1Angle.FromDegrees(0.1)); assertEquals(2, union.NumLoops); }
// Add a polygon to the input geometry. public void AddPolygon(S2Polygon polygon) { for (int i = 0; i < polygon.NumLoops(); ++i) { var loop = polygon.Loop(i); // Only loops at depth 0 can contribute to the convex hull. if (loop.Depth == 0) { AddLoop(loop); } } }
public void Test_MakePolygon_ValidInput() { Assert.True(MakePolygon("-20:150, -20:151, -19:150", out var polygon)); var vertices = new[] { S2LatLng.FromDegrees(-20, 150).ToPoint(), S2LatLng.FromDegrees(-20, 151).ToPoint(), S2LatLng.FromDegrees(-19, 150).ToPoint(), }; var expected = new S2Polygon(new S2Loop(vertices)); Assert.True(polygon == expected); }
public void Test_MakeVerbatimPolygon_ValidInput() { Assert.True(MakeVerbatimPolygon("-20:150, -20:151, -19:150", out var polygon)); var vertices = new[] { new [] { -20, 150 }, new [] { -20, 151 }, new [] { -19, 150 }, }.Select(t => S2LatLng.FromDegrees(t[0], t[1]).ToPoint()); var expected = new S2Polygon(new S2Loop(vertices)); Assert.Equal(polygon, expected); }
public static string ToDebugString(this S2Polygon polygon, string loop_separator = ";\n") { if (polygon.IsEmpty()) { return("empty"); } else if (polygon.IsFull()) { return("full"); } var loops = polygon.Loops().Select(t => AppendVertices(t.Vertices, t.NumVertices)); return(string.Join(loop_separator, loops)); }
public void tryUnion(S2Polygon a, S2Polygon b) { var union = new S2Polygon(); union.InitToUnion(a, b); var polygons = new List <S2Polygon>(); polygons.Add(new S2Polygon(a)); polygons.Add(new S2Polygon(b)); var destructiveUnion = S2Polygon.DestructiveUnion(polygons); checkEqual(union, destructiveUnion); }
public void Test_IndexedS2PolygonLayer_AddsShape() { S2Builder builder = new(new Options()); MutableS2ShapeIndex index = new(); builder.StartLayer(new IndexedS2PolygonLayer(index)); string polygon_str = "0:0, 0:10, 10:0"; builder.AddPolygon(MakePolygonOrDie(polygon_str)); Assert.True(builder.Build(out _)); Assert.Equal(1, index.NumShapeIds()); S2Polygon polygon = ((S2Polygon.Shape)index.Shape(0)).Polygon; Assert.Equal(polygon_str, polygon.ToDebugString()); }
// Converts a 2-dimensional S2Shape into an S2Polygon. Each chain is converted // to an S2Loop and the vector of loops is used to construct the S2Polygon. public static S2Polygon ShapeToS2Polygon(S2Shape poly) { if (poly.IsFull()) { return new S2Polygon(S2Loop.kFull); } System.Diagnostics.Debug.Assert(poly.Dimension() == 2); List<S2Loop> loops = new(); for (int i = 0; i < poly.NumChains(); ++i) { S2.GetChainVertices(poly, i, out var vertices); loops.Add(new S2Loop(vertices)); } var output_poly = new S2Polygon(loops, initOriented: true); return output_poly; }
public void testDisjoint() { var builder = new S2PolygonBuilder(S2PolygonBuilderOptions.UndirectedXor); builder.AddPolygon(adj0); builder.AddPolygon(unAdj); var ab = new S2Polygon(); assertTrue(builder.AssemblePolygon(ab, null)); var union = new S2Polygon(); union.InitToUnion(adj0, unAdj); assertEquals(2, union.NumLoops); checkEqual(ab, union); tryUnion(adj0, unAdj); }
private void checkEqual(S2Polygon a, S2Polygon b) { var MAX_ERROR = 1e-31; if (a.IsNormalized && b.IsNormalized) { var r = a.BoundaryApproxEquals(b, MAX_ERROR); assertTrue(r); } else { var builder = new S2PolygonBuilder(S2PolygonBuilderOptions.UndirectedXor); var a2 = new S2Polygon(); var b2 = new S2Polygon(); builder.AddPolygon(a); assertTrue(builder.AssemblePolygon(a2, null)); builder.AddPolygon(b); assertTrue(builder.AssemblePolygon(b2, null)); assertTrue(a2.BoundaryApproxEquals(b2, MAX_ERROR)); } }
private bool testBuilder(TestCase test) { for (var iter = 0; iter < 200; ++iter) { // Initialize to the default options, which are changed below var options = S2PolygonBuilderOptions.DirectedXor; options.UndirectedEdges = evalTristate(test.undirectedEdges); options.XorEdges = evalTristate(test.xorEdges); // Each test has a minimum and a maximum merge distance. The merge // distance must be at least the given minimum to ensure that all expected // merging will take place, and it must be at most the given maximum to // ensure that no unexpected merging takes place. // // If the minimum and maximum values are different, we have some latitude // to perturb the vertices as long as the merge distance is adjusted // appropriately. If "p" is the maximum perturbation distance, "min" and // "max" are the min/max merge distances, and "m" is the actual merge // distance for this test, we require that // // x >= min + 2*p and x <= max - 2*p . // // This implies that p <= 0.25 * (max - min). We choose "p" so that it is // zero half of the time, and otherwise chosen randomly up to this limit. var minMerge = S1Angle.FromDegrees(test.minMerge).Radians; var maxMerge = S1Angle.FromDegrees(test.maxMerge).Radians; var r = Math.Max(0.0, 2*rand.NextDouble() - 1); var maxPerturbation = r*0.25*(maxMerge - minMerge); // Now we set the merge distance chosen randomly within the limits above // (min + 2*p and max - 2*p). Half of the time we set the merge distance // to the minimum value. r = Math.Max(0.0, 2*rand.NextDouble() - 1); options.MergeDistance = S1Angle.FromRadians( minMerge + 2*maxPerturbation + r*(maxMerge - minMerge - 4*maxPerturbation)); options.Validate = true; var builder = new S2PolygonBuilder(options); // On each iteration we randomly rotate the test case around the sphere. // This causes the S2PolygonBuilder to choose different first edges when // trying to build loops. var x = randomPoint(); var y = S2Point.Normalize(S2Point.CrossProd(x, randomPoint())); var z = S2Point.Normalize(S2Point.CrossProd(x, y)); foreach (var chain in test.chainsIn) { addChain(chain, x, y, z, maxPerturbation, builder); } var loops = new List<S2Loop>(); var unusedEdges = new List<S2Edge>(); if (test.xorEdges < 0) { builder.AssembleLoops(loops, unusedEdges); } else { var polygon = new S2Polygon(); builder.AssemblePolygon(polygon, unusedEdges); polygon.Release(loops); } var expected = new List<S2Loop>(); foreach (var loop in test.loopsOut) { var vertices = new List<S2Point>(); getVertices(loop, x, y, z, 0, vertices); expected.Add(new S2Loop(vertices)); } // We assume that the vertex locations in the expected output polygon // are separated from the corresponding vertex locations in the input // edges by at most half of the minimum merge distance. Essentially // this means that the expected output vertices should be near the // centroid of the various input vertices. var maxError = 0.5*minMerge + maxPerturbation; // Note single "|" below so that we print both sets of loops. if (findMissingLoops(loops, expected, maxError, "Actual") | findMissingLoops(expected, loops, maxError, "Expected")) { Console.Error.WriteLine( "During iteration " + iter + ", undirected: " + options.UndirectedEdges + ", xor: " + options.XorEdges + "\n\n"); return false; } if (unusedEdges.Count != test.numUnusedEdges) { Console.Error.WriteLine("Wrong number of unused edges: " + unusedEdges.Count + " (should be " + test.numUnusedEdges + ")\n"); return false; } } return true; }
private bool testBuilder(TestCase test) { for (var iter = 0; iter < 200; ++iter) { // Initialize to the default options, which are changed below var options = S2PolygonBuilderOptions.DirectedXor; options.UndirectedEdges = evalTristate(test.undirectedEdges); options.XorEdges = evalTristate(test.xorEdges); // Each test has a minimum and a maximum merge distance. The merge // distance must be at least the given minimum to ensure that all expected // merging will take place, and it must be at most the given maximum to // ensure that no unexpected merging takes place. // // If the minimum and maximum values are different, we have some latitude // to perturb the vertices as long as the merge distance is adjusted // appropriately. If "p" is the maximum perturbation distance, "min" and // "max" are the min/max merge distances, and "m" is the actual merge // distance for this test, we require that // // x >= min + 2*p and x <= max - 2*p . // // This implies that p <= 0.25 * (max - min). We choose "p" so that it is // zero half of the time, and otherwise chosen randomly up to this limit. var minMerge = S1Angle.FromDegrees(test.minMerge).Radians; var maxMerge = S1Angle.FromDegrees(test.maxMerge).Radians; var r = Math.Max(0.0, 2 * rand.NextDouble() - 1); var maxPerturbation = r * 0.25 * (maxMerge - minMerge); // Now we set the merge distance chosen randomly within the limits above // (min + 2*p and max - 2*p). Half of the time we set the merge distance // to the minimum value. r = Math.Max(0.0, 2 * rand.NextDouble() - 1); options.MergeDistance = S1Angle.FromRadians( minMerge + 2 * maxPerturbation + r * (maxMerge - minMerge - 4 * maxPerturbation)); options.Validate = true; var builder = new S2PolygonBuilder(options); // On each iteration we randomly rotate the test case around the sphere. // This causes the S2PolygonBuilder to choose different first edges when // trying to build loops. var x = randomPoint(); var y = S2Point.Normalize(S2Point.CrossProd(x, randomPoint())); var z = S2Point.Normalize(S2Point.CrossProd(x, y)); foreach (var chain in test.chainsIn) { addChain(chain, x, y, z, maxPerturbation, builder); } var loops = new List <S2Loop>(); var unusedEdges = new List <S2Edge>(); if (test.xorEdges < 0) { builder.AssembleLoops(loops, unusedEdges); } else { var polygon = new S2Polygon(); builder.AssemblePolygon(polygon, unusedEdges); polygon.Release(loops); } var expected = new List <S2Loop>(); foreach (var loop in test.loopsOut) { var vertices = new List <S2Point>(); getVertices(loop, x, y, z, 0, vertices); expected.Add(new S2Loop(vertices)); } // We assume that the vertex locations in the expected output polygon // are separated from the corresponding vertex locations in the input // edges by at most half of the minimum merge distance. Essentially // this means that the expected output vertices should be near the // centroid of the various input vertices. var maxError = 0.5 * minMerge + maxPerturbation; // Note single "|" below so that we print both sets of loops. if (findMissingLoops(loops, expected, maxError, "Actual") | findMissingLoops(expected, loops, maxError, "Expected")) { Console.Error.WriteLine( "During iteration " + iter + ", undirected: " + options.UndirectedEdges + ", xor: " + options.XorEdges + "\n\n"); return(false); } if (unusedEdges.Count != test.numUnusedEdges) { Console.Error.WriteLine("Wrong number of unused edges: " + unusedEdges.Count + " (should be " + test.numUnusedEdges + ")\n"); return(false); } } return(true); }
private void assertRelation(S2Polygon a, S2Polygon b, int contains, bool intersects) { assertEquals(a.Contains(b), contains > 0); assertEquals(b.Contains(a), contains < 0); assertEquals(a.Intersects(b), intersects); }
public void Test_FullPolygon() { var full = new S2Polygon(S2Loop.kFull); Assert.Equal("full", full.ToDebugString()); }
public void Test_EmptyPolygon() { var empty = new S2Polygon(); Assert.Equal("empty", empty.ToDebugString()); }
private void checkEqual(S2Polygon a, S2Polygon b) { var MAX_ERROR = 1e-31; if (a.IsNormalized && b.IsNormalized) { var r = a.BoundaryApproxEquals(b, MAX_ERROR); assertTrue(r); } else { var builder = new S2PolygonBuilder(S2PolygonBuilderOptions.UndirectedXor); var a2 = new S2Polygon(); var b2 = new S2Polygon(); builder.AddPolygon(a); assertTrue(builder.AssemblePolygon(a2, null)); builder.AddPolygon(b); assertTrue(builder.AssemblePolygon(b2, null)); assertTrue(a2.BoundaryApproxEquals(b2, MAX_ERROR)); } }
// As above, but does not Debug.Assert-fail on invalid input. Returns true if // conversion is successful. public static bool MakeVerbatimPolygon(string str, out S2Polygon polygon) { return(InternalMakePolygon(str, false, out polygon)); }
// As above, but does not Debug.Assert-fail on invalid input. Returns true if // conversion is successful. public static bool MakePolygon(string str, out S2Polygon polygon) { return(InternalMakePolygon(str, true, out polygon)); }
public void tryUnion(S2Polygon a, S2Polygon b) { var union = new S2Polygon(); union.InitToUnion(a, b); var polygons = new List<S2Polygon>(); polygons.Add(new S2Polygon(a)); polygons.Add(new S2Polygon(b)); var destructiveUnion = S2Polygon.DestructiveUnion(polygons); checkEqual(union, destructiveUnion); }
public void testDisjoint() { var builder = new S2PolygonBuilder(S2PolygonBuilderOptions.UndirectedXor); builder.AddPolygon(adj0); builder.AddPolygon(unAdj); var ab = new S2Polygon(); assertTrue(builder.AssemblePolygon(ab, null)); var union = new S2Polygon(); union.InitToUnion(adj0, unAdj); assertEquals(2, union.NumLoops); checkEqual(ab, union); tryUnion(adj0, unAdj); }
private void assertRelation(S2Polygon a, S2Polygon b, int contains, bool intersects) { assertEquals(a.Contains(b), contains > 0); assertEquals(b.Contains(a), contains < 0); assertEquals(a.Intersects(b), intersects); }