/// <summary> /// Converts a string in the format returned by ToString() to an S2CellId. /// Returns S2CellId.None if the string could not be parsed. /// /// The method name includes "Debug" in order to avoid possible confusion /// with FromToken() above. /// </summary> private static S2CellId S2CellId_FromDebugString(string str) { // This function is reasonably efficient, but is only intended for use in // tests. var level = str.Length - 2; if (level < 0 || level > S2.kMaxCellLevel) { return(S2CellId.None); } var face = (int)char.GetNumericValue(str[0]); if (face < 0 || face > 5 || str[1] != '/') { return(S2CellId.None); } var id = S2CellId.FromFace(face); for (var i = 2; i < str.Length; i++) { var child_pos = (int)char.GetNumericValue(str[i]); if (child_pos < 0 || child_pos > 3) { return(S2CellId.None); } id = id.Child(child_pos); } return(id); }
public void Test_S2CellId_Advance() { S2CellId id = S2CellId.FromFacePosLevel(3, 0x12345678, S2.kMaxCellLevel - 4); // Check basic properties of advance(). Assert.Equal(S2CellId.End(0), S2CellId.Begin(0).Advance(7)); Assert.Equal(S2CellId.End(0), S2CellId.Begin(0).Advance(12)); Assert.Equal(S2CellId.Begin(0), S2CellId.End(0).Advance(-7)); Assert.Equal(S2CellId.Begin(0), S2CellId.End(0).Advance(-12000000)); int num_level_5_cells = 6 << (2 * 5); Assert.Equal(S2CellId.End(5).Advance(500 - num_level_5_cells), S2CellId.Begin(5).Advance(500)); Assert.Equal(id.Next().ChildBegin(S2.kMaxCellLevel), id.ChildBegin(S2.kMaxCellLevel).Advance(256)); Assert.Equal(S2CellId.FromFacePosLevel(5, 0, S2.kMaxCellLevel), S2CellId.FromFacePosLevel(1, 0, S2.kMaxCellLevel) .Advance((long)(4UL << (2 * S2.kMaxCellLevel)))); // Check basic properties of advance_wrap(). Assert.Equal(S2CellId.FromFace(1), S2CellId.Begin(0).AdvanceWrap(7)); Assert.Equal(S2CellId.Begin(0), S2CellId.Begin(0).AdvanceWrap(12)); Assert.Equal(S2CellId.FromFace(4), S2CellId.FromFace(5).AdvanceWrap(-7)); Assert.Equal(S2CellId.Begin(0), S2CellId.Begin(0).AdvanceWrap(-12000000)); Assert.Equal(S2CellId.Begin(5).AdvanceWrap(6644), S2CellId.Begin(5).AdvanceWrap(-11788)); Assert.Equal(id.Next().ChildBegin(S2.kMaxCellLevel), id.ChildBegin(S2.kMaxCellLevel).AdvanceWrap(256)); Assert.Equal(S2CellId.FromFacePosLevel(1, 0, S2.kMaxCellLevel), S2CellId.FromFacePosLevel(5, 0, S2.kMaxCellLevel) .AdvanceWrap((long)(2UL << (2 * S2.kMaxCellLevel)))); }
public void Test_S2CellId_ToString() { Assert.Equal("3/", S2CellId.FromFace(3).ToString()); Assert.Equal("4/000000000000000000000000000000", S2CellId.FromFace(4).RangeMin().ToString()); Assert.Equal("Invalid: 0000000000000000", S2CellId.None.ToString()); }
public void Test_S2CellUnion_FromMinMax() { // Check the very first leaf cell and face cell. S2CellId face1_id = S2CellId.FromFace(0); TestFromMinMax(face1_id.RangeMin(), face1_id.RangeMin()); TestFromMinMax(face1_id.RangeMin(), face1_id.RangeMax()); // Check the very last leaf cell and face cell. S2CellId face5_id = S2CellId.FromFace(5); TestFromMinMax(face5_id.RangeMin(), face5_id.RangeMax()); TestFromMinMax(face5_id.RangeMax(), face5_id.RangeMax()); // Check random ranges of leaf cells. for (int iter = 0; iter < 100; ++iter) { S2CellId x = S2Testing.GetRandomCellId(S2.kMaxCellLevel); S2CellId y = S2Testing.GetRandomCellId(S2.kMaxCellLevel); if (x > y) { var tmp = x; x = y; y = tmp; } TestFromMinMax(x, y); } }
public void Test_S2CellId_FromFace() { for (int face = 0; face < 6; ++face) { Assert.Equal(S2CellId.FromFacePosLevel(face, 0, 0), S2CellId.FromFace(face)); } }
public void Test_S2CellUnion_ToStringOneCell() { Assert.Equal(new S2CellUnion(new List <S2CellId> { S2CellId.FromFace(1) }).ToString(), "Size:1 S2CellIds:3"); }
public void Test_S2CellId_Containment() { // Test contains() and intersects(). var parent_map = new Dictionary <S2CellId, S2CellId>(); var cells = new List <S2CellId>(); for (int face = 0; face < 6; ++face) { ExpandCell(S2CellId.FromFace(face), cells, parent_map); } foreach (var end_id in cells) { foreach (var begin_id in cells) { bool contained = true; for (var id = begin_id; id != end_id; id = parent_map[id]) { if (!parent_map.ContainsKey(id)) { contained = false; break; } } Assert.Equal(contained, end_id.Contains(begin_id)); Assert.Equal(contained, begin_id >= end_id.RangeMin() && begin_id <= end_id.RangeMax()); Assert.Equal(end_id.Intersects(begin_id), end_id.Contains(begin_id) || begin_id.Contains(end_id)); } } }
public void Test_S2CellUnion_FromBeginEnd() { // Since FromMinMax() is implemented in terms of FromBeginEnd(), we // focus on test cases that generate an empty range. S2CellId initial_id = S2CellId.FromFace(3); // Test an empty range before the minimum S2CellId. S2CellUnion cell_union = new(new List <S2CellId> { initial_id }); S2CellId id_begin = S2CellId.Begin(S2.kMaxCellLevel); cell_union.InitFromBeginEnd(id_begin, id_begin); Assert.True(cell_union.IsEmpty()); // Test an empty range after the maximum S2CellId. cell_union = new S2CellUnion(new List <S2CellId> { initial_id }); S2CellId id_end = S2CellId.End(S2.kMaxCellLevel); cell_union.InitFromBeginEnd(id_end, id_end); Assert.True(cell_union.IsEmpty()); // Test the full sphere. cell_union = S2CellUnion.FromBeginEnd(id_begin, id_end); Assert.Equal(6, cell_union.Size()); foreach (S2CellId id in cell_union) { Assert.True(id.IsFace()); } }
public void Test_S2CellUnion_ToStringTwoCells() { Assert.Equal( new S2CellUnion(new List <S2CellId> { S2CellId.FromFace(1), S2CellId.FromFace(2) }).ToString(), "Size:2 S2CellIds:3,5"); }
public void Test_MakeCellUnion_ValidInput() { Assert.True(MakeCellUnion("1/3, 4/", out var cellUnion)); var expected = new S2CellUnion(new List <S2CellId> { S2CellId.FromFace(1).Child(3), S2CellId.FromFace(4) }); Assert.Equal(cellUnion, expected); }
public void Test_EncodedS2CellIdVector_SixFaceCells() { List <S2CellId> ids = new(); for (int face = 0; face < 6; ++face) { ids.Add(S2CellId.FromFace(face)); } TestEncodedS2CellIdVector(ids, 8); }
public void Test_S2CellUnion_S2CellIdConstructor() { var face1_id = S2CellId.FromFace(1); var face1_union = new S2CellUnion(new List <S2CellId> { face1_id }); Assert.Equal(1, face1_union.Size()); Assert.Equal(face1_id, face1_union.CellId(0)); }
public void Test_S2CellUnion_WorksInContainers() { var ids = new List <S2CellId> { S2CellId.FromFace(1) }; var union_vector = new List <S2CellUnion> { new S2CellUnion(ids) }; Assert.Equal(ids, union_vector.Last().CellIds); }
public void Test_S2CellUnion_ToStringOver500Cells() { List <S2CellId> ids = new(); new S2CellUnion(new List <S2CellId> { S2CellId.FromFace(1) }).Denormalize(6, 1, ids); // 4096 cells var result = S2CellUnion.FromVerbatim(ids).ToString(); Assert.Equal(result.Count(t => t == ','), 500); Assert.Equal(result[^ 4..], ",...");
public void Test_S2Cell_TestFaces() { var edge_counts = new Dictionary <S2Point, int>(); var vertex_counts = new Dictionary <S2Point, int>(); for (int face = 0; face < 6; ++face) { S2CellId id = S2CellId.FromFace(face); S2Cell cell = new(id); Assert.Equal(id, cell.Id); Assert.Equal(face, cell.Face); Assert.Equal(0, cell.Level); // Top-level faces have alternating orientations to get RHS coordinates. Assert.Equal(face & S2.kSwapMask, cell.Orientation); Assert.False(cell.IsLeaf()); for (int k = 0; k < 4; ++k) { var key = cell.EdgeRaw(k); if (edge_counts.ContainsKey(key)) { edge_counts[key] += 1; } else { edge_counts[key] = 1; } var key2 = cell.VertexRaw(k); if (vertex_counts.ContainsKey(key2)) { vertex_counts[key2] += 1; } else { vertex_counts[key2] = 1; } Assert2.DoubleEqual(0.0, cell.VertexRaw(k).DotProd(key)); Assert2.DoubleEqual(0.0, cell.VertexRaw(k + 1).DotProd(key)); Assert2.DoubleEqual(1.0, cell.VertexRaw(k).CrossProd(cell.VertexRaw(k + 1)) .Normalize().DotProd(cell.Edge(k))); } } // Check that edges have multiplicity 2 and vertices have multiplicity 3. foreach (var p in edge_counts) { Assert.Equal(2, p.Value); } foreach (var p in vertex_counts) { Assert.Equal(3, p.Value); } }
public void Test_S2CellUnion_EmptyAndNonEmptyBooleanOps() { S2CellUnion empty_cell_union = new(); S2CellId face1_id = S2CellId.FromFace(1); S2CellUnion non_empty_cell_union = new(new List <S2CellId> { face1_id }); // Contains(...) Assert.False(empty_cell_union.Contains(face1_id)); Assert.True(non_empty_cell_union.Contains(face1_id)); Assert.True(empty_cell_union.Contains(empty_cell_union)); Assert.True(non_empty_cell_union.Contains(empty_cell_union)); Assert.False(empty_cell_union.Contains(non_empty_cell_union)); Assert.True(non_empty_cell_union.Contains(non_empty_cell_union)); // Intersects(...) Assert.False(empty_cell_union.Intersects(face1_id)); Assert.True(non_empty_cell_union.Intersects(face1_id)); Assert.False(empty_cell_union.Intersects(empty_cell_union)); Assert.False(non_empty_cell_union.Intersects(empty_cell_union)); Assert.False(empty_cell_union.Intersects(non_empty_cell_union)); Assert.True(non_empty_cell_union.Intersects(non_empty_cell_union)); // Union(...) Assert.Equal(empty_cell_union, empty_cell_union.Union(empty_cell_union)); Assert.Equal(non_empty_cell_union, non_empty_cell_union.Union(empty_cell_union)); Assert.Equal(non_empty_cell_union, empty_cell_union.Union(non_empty_cell_union)); Assert.Equal(non_empty_cell_union, non_empty_cell_union.Union(non_empty_cell_union)); // Intersection(...) Assert.Equal(empty_cell_union, empty_cell_union.Intersection(face1_id)); Assert.Equal(non_empty_cell_union, non_empty_cell_union.Intersection(face1_id)); Assert.Equal(empty_cell_union, empty_cell_union.Intersection(empty_cell_union)); Assert.Equal(empty_cell_union, non_empty_cell_union.Intersection(empty_cell_union)); Assert.Equal(empty_cell_union, empty_cell_union.Intersection(non_empty_cell_union)); Assert.Equal(non_empty_cell_union, non_empty_cell_union.Intersection(non_empty_cell_union)); // Difference(...) Assert.Equal(empty_cell_union, empty_cell_union.Difference(empty_cell_union)); Assert.Equal(non_empty_cell_union, non_empty_cell_union.Difference(empty_cell_union)); Assert.Equal(empty_cell_union, empty_cell_union.Difference(non_empty_cell_union)); Assert.Equal(new S2CellUnion(), non_empty_cell_union.Difference(non_empty_cell_union)); }
public void Test_S2ConvexHullQuery_NonConvexPoints() { // Generate a point set such that the only convex region containing them is // the entire sphere. In other words, you can generate any point on the // sphere by repeatedly linearly interpolating between the points. (The // four points of a tetrahedron would also work, but this is easier.) S2ConvexHullQuery query = new(); for (int face = 0; face < 6; ++face) { query.AddPoint(S2CellId.FromFace(face).ToPoint()); } var result = (query.GetConvexHull()); Assert.True(result.IsFull()); }
public void Test_S2CellUnion_Release() { S2CellId face1_id = S2CellId.FromFace(1); S2CellUnion face1_union = new(new List <S2CellId> { face1_id }); Assert.Equal(1, face1_union.Size()); Assert.Equal(face1_id, face1_union.CellId(0)); var released = face1_union.Release(); Assert.True(1 == released.Count); Assert.Equal(face1_id, released[0]); Assert.Equal(0, face1_union.Size()); }
public void Test_S2CellUnion_Clear() { S2CellId face1_id = S2CellId.FromFace(1); S2CellUnion face1_union = new(new List <S2CellId> { face1_id }); Assert.Equal(1, face1_union.Size()); Assert.True(1 == face1_union.CellIds.Count); Assert.True(1 <= face1_union.CellIds.Capacity); face1_union.Clear(); Assert.Equal(0, face1_union.Size()); Assert.True(0 == face1_union.CellIds.Count); Assert.Equal(0, face1_union.CellIds.Capacity); }
public void Test_S2CellId_FromDebugString() { Assert.Equal(S2CellId.FromFace(3), S2CellId.FromDebugString("3/")); Assert.Equal(S2CellId.FromFace(0).Child(2).Child(1), S2CellId.FromDebugString("0/21")); Assert.Equal(S2CellId.FromFace(4).RangeMin(), S2CellId.FromDebugString("4/000000000000000000000000000000")); Assert.Equal(S2CellId.None, S2CellId.FromDebugString("4/0000000000000000000000000000000")); Assert.Equal(S2CellId.None, S2CellId.FromDebugString("")); Assert.Equal(S2CellId.None, S2CellId.FromDebugString("7/")); Assert.Equal(S2CellId.None, S2CellId.FromDebugString(" /")); Assert.Equal(S2CellId.None, S2CellId.FromDebugString("3:0")); Assert.Equal(S2CellId.None, S2CellId.FromDebugString("3/ 12")); Assert.Equal(S2CellId.None, S2CellId.FromDebugString("3/1241")); }
public void Test_VisitCells_QueryEdgeOnFaceBoundary() { int kIters = 100; for (int iter = 0; iter < kIters; ++iter) { _logger.WriteLine("Iteration " + iter); // Choose an edge AB such that B is nearly on the edge between two S2 cube // faces, and such that the result of clipping AB to the face that nominally // contains B (according to S2.GetFace) is empty when no padding is used. int a_face, b_face; S2Point a, b; R2Point a_uv, b_uv; do { a_face = S2Testing.Random.Uniform(6); a = S2.FaceUVtoXYZ(a_face, S2Testing.Random.UniformDouble(-1, 1), S2Testing.Random.UniformDouble(-1, 1)).Normalize(); b_face = S2.GetUVWFace(a_face, 0, 1); // Towards positive u-axis var uTmp = 1 - S2Testing.Random.Uniform(2) * 0.5 * S2.DoubleEpsilon; b = S2.FaceUVtoXYZ(b_face, uTmp, S2Testing.Random.UniformDouble(-1, 1)).Normalize(); } while (S2.GetFace(b) != b_face || S2EdgeClipping.ClipToFace(a, b, b_face, out a_uv, out b_uv)); // Verify that the clipping result is non-empty when a padding of // S2EdgeClipping.kFaceClipErrorUVCoord is used instead. Assert.True(S2EdgeClipping.ClipToPaddedFace(a, b, b_face, S2EdgeClipping.kFaceClipErrorUVCoord, out a_uv, out b_uv)); // Create an S2ShapeIndex containing a single edge BC, where C is on the // same S2 cube face as B (which is different than the face containing A). S2Point c = S2.FaceUVtoXYZ(b_face, S2Testing.Random.UniformDouble(-1, 1), S2Testing.Random.UniformDouble(-1, 1)).Normalize(); MutableS2ShapeIndex index = new(); index.Add(new S2Polyline.OwningShape( new S2Polyline(new S2Point[] { b, c }))); // Check that the intersection between AB and BC is detected when the face // containing BC is specified as a root cell. (Note that VisitCells() // returns false only if the CellVisitor returns false, and otherwise // returns true.) S2CrossingEdgeQuery query = new(index); S2PaddedCell root = new(S2CellId.FromFace(b_face), 0); Assert.False(query.VisitCells(a, b, root, (S2ShapeIndexCell x) => false)); } }
public void Test_VisitIntersectingShapes_Polygons() { MutableS2ShapeIndex index = new(); S2Cap center_cap = new(new(1, 0, 0), S1Angle.FromRadians(0.5)); S2Testing.Fractal fractal = new(); for (int i = 0; i < 10; ++i) { fractal.SetLevelForApproxMaxEdges(3 * 64); S2Point center = S2Testing.SamplePoint(center_cap); index.Add(new S2Loop.Shape( fractal.MakeLoop(S2Testing.GetRandomFrameAt(center), S1Angle.FromRadians(S2Testing.Random.RandDouble())))); } // Also add a big polygon containing most of the polygons above to ensure // that we test containment of cells that are ancestors of index cells. index.Add(NewPaddedCell(S2CellId.FromFace(0), 0)); new VisitIntersectingShapesTest(index).Run(); }
public void Test_S2CellUnion_LeafCellsCovered() { S2CellUnion cell_union = new(); Assert.Equal(0UL, cell_union.LeafCellsCovered()); var ids = new List <S2CellId> { // One leaf cell on face 0. S2CellId.FromFace(0).ChildBegin(S2.kMaxCellLevel) }; cell_union = new S2CellUnion(ids); Assert.Equal(1UL, cell_union.LeafCellsCovered()); // Face 0 itself (which includes the previous leaf cell). ids.Add(S2CellId.FromFace(0)); cell_union = new S2CellUnion(ids); Assert.Equal(1UL << 60, cell_union.LeafCellsCovered()); // Five faces. cell_union.Expand(0); Assert.Equal(5UL << 60, cell_union.LeafCellsCovered()); // Whole world. cell_union.Expand(0); Assert.Equal(6UL << 60, cell_union.LeafCellsCovered()); // Add some disjoint cells. ids.Add(S2CellId.FromFace(1).ChildBegin(1)); ids.Add(S2CellId.FromFace(2).ChildBegin(2)); ids.Add(S2CellId.FromFace(2).ChildEnd(2).Prev()); ids.Add(S2CellId.FromFace(3).ChildBegin(14)); ids.Add(S2CellId.FromFace(4).ChildBegin(27)); ids.Add(S2CellId.FromFace(4).ChildEnd(15).Prev()); ids.Add(S2CellId.FromFace(5).ChildBegin(30)); cell_union = new S2CellUnion(ids); UInt64 expected = 1UL + (1UL << 6) + (1UL << 30) + (1UL << 32) + (2UL << 56) + (1UL << 58) + (1UL << 60); Assert.Equal(expected, cell_union.LeafCellsCovered()); }
public void Test_S2CellId_GetCommonAncestorLevel() { // Two identical cell ids. Assert.Equal(0, S2CellId.FromFace(0) .CommonAncestorLevel(S2CellId.FromFace(0))); Assert.Equal(30, S2CellId.FromFace(0).ChildBegin(30).CommonAncestorLevel(S2CellId.FromFace(0).ChildBegin(30))); // One cell id is a descendant of the other. Assert.Equal(0, S2CellId.FromFace(0).ChildBegin(30).CommonAncestorLevel(S2CellId.FromFace(0))); Assert.Equal(0, S2CellId.FromFace(5).CommonAncestorLevel(S2CellId.FromFace(5).ChildEnd(30).Prev())); // Two cells that have no common ancestor. Assert.Equal(-1, S2CellId.FromFace(0).CommonAncestorLevel(S2CellId.FromFace(5))); Assert.Equal(-1, S2CellId.FromFace(2).ChildBegin(30).CommonAncestorLevel(S2CellId.FromFace(3).ChildEnd(20))); // Two cells that have a common ancestor distinct from both of them. Assert.Equal(8, S2CellId.FromFace(5).ChildBegin(9).Next().ChildBegin(15).CommonAncestorLevel( S2CellId.FromFace(5).ChildBegin(9).ChildBegin(20))); Assert.Equal(1, S2CellId.FromFace(0).ChildBegin(2).ChildBegin(30). CommonAncestorLevel( S2CellId.FromFace(0).ChildBegin(2).Next().ChildBegin(5))); }
public void Test_S2RegionEncodeDecodeTest_S2CellUnion() { S2CellUnion cu_empty = new(); S2CellUnion cu_face1 = new(new List <S2CellId> { S2CellId.FromFace(1) }); // Cell ids taken from S2CellUnion EncodeDecode test. S2CellUnion cu_latlngs = S2CellUnion.FromNormalized(new List <S2CellId> { new S2CellId(0x33), new S2CellId(0x8e3748fab), new S2CellId(0x91230abcdef83427), }); var cu = TestEncodeDecode(kEncodedCellUnionEmpty, cu_empty); Assert.Equal(cu_empty, cu); cu = TestEncodeDecode(kEncodedCellUnionFace1, cu_face1); Assert.Equal(cu_face1, cu); cu = TestEncodeDecode(kEncodedCellUnionFromCells, cu_latlngs); Assert.Equal(cu_latlngs, cu); }
public void Test_S2CellId_Tokens() { // Test random cell ids at all levels. for (int i = 0; i < 10000; ++i) { var id = S2Testing.GetRandomCellId(); var token = id.ToToken(); Assert.True(token.Length <= 16); Assert.Equal(id, S2CellId.FromToken(token)); Assert.Equal(id, S2CellId.FromToken(token)); } // Check that invalid cell ids can be encoded, and round-trip is // the identity operation. string token2 = S2CellId.None.ToToken(); Assert.Equal(S2CellId.None, S2CellId.FromToken(token2)); Assert.Equal(S2CellId.None, S2CellId.FromToken(token2)); // Sentinel is invalid. token2 = S2CellId.Sentinel.ToToken(); Assert.Equal(S2CellId.FromToken(token2), S2CellId.Sentinel); Assert.Equal(S2CellId.FromToken(token2), S2CellId.Sentinel); // Check an invalid face. token2 = S2CellId.FromFace(7).ToToken(); Assert.Equal(S2CellId.FromToken(token2), S2CellId.FromFace(7)); Assert.Equal(S2CellId.FromToken(token2), S2CellId.FromFace(7)); // Check that supplying tokens with non-alphanumeric characters // returns S2CellId.None. Assert.Equal(S2CellId.None, S2CellId.FromToken("876b e99")); Assert.Equal(S2CellId.None, S2CellId.FromToken("876bee99\n")); Assert.Equal(S2CellId.None, S2CellId.FromToken("876[ee99")); Assert.Equal(S2CellId.None, S2CellId.FromToken(" 876bee99")); }
public void Test_MakeCellId_ValidInput() { Assert.True(MakeCellId("3/", out var cellId)); Assert.Equal(cellId, S2CellId.FromFace(3)); }
public void Test_S2CellId_Neighbors() { // Check the edge neighbors of face 1. var out_faces = new[] { 5, 3, 2, 0 }; var face_nbrs = new S2CellId[4]; S2CellId.FromFace(1).EdgeNeighbors(face_nbrs); for (int i = 0; i < 4; ++i) { Assert.True(face_nbrs[i].IsFace()); Assert.Equal(out_faces[i], (int)face_nbrs[i].Face()); } // Check the edge neighbors of the corner cells at all levels. This case is // trickier because it requires projecting onto adjacent faces. const int kMaxIJ = S2CellId.kMaxSize - 1; for (int level = 1; level <= S2.kMaxCellLevel; ++level) { S2CellId id2 = S2CellId.FromFaceIJ(1, 0, 0).Parent(level); var nbrs2 = new S2CellId[4]; id2.EdgeNeighbors(nbrs2); // These neighbors were determined manually using the face and axis // relationships defined in s2coords.cc. int size_ij = S2CellId.SizeIJ(level); Assert.Equal(S2CellId.FromFaceIJ(5, kMaxIJ, kMaxIJ).Parent(level), nbrs2[0]); Assert.Equal(S2CellId.FromFaceIJ(1, size_ij, 0).Parent(level), nbrs2[1]); Assert.Equal(S2CellId.FromFaceIJ(1, 0, size_ij).Parent(level), nbrs2[2]); Assert.Equal(S2CellId.FromFaceIJ(0, kMaxIJ, 0).Parent(level), nbrs2[3]); } // Check the vertex neighbors of the center of face 2 at level 5. var nbrs = new List <S2CellId>(); new S2CellId(new S2Point(0, 0, 1)).AppendVertexNeighbors(5, nbrs); nbrs.Sort(); for (int i = 0; i < 4; ++i) { Assert.Equal(S2CellId.FromFaceIJ( 2, (1 << 29) - ((i < 2) ? 1 : 0), (1 << 29) - ((i == 0 || i == 3) ? 1 : 0)) .Parent(5), nbrs[i]); } nbrs.Clear(); // Check the vertex neighbors of the corner of faces 0, 4, and 5. S2CellId id1 = S2CellId.FromFacePosLevel(0, 0, S2.kMaxCellLevel); id1.AppendVertexNeighbors(0, nbrs); nbrs.Sort(); Assert.Equal(3, nbrs.Count); Assert.Equal(S2CellId.FromFace(0), nbrs[0]); Assert.Equal(S2CellId.FromFace(4), nbrs[1]); Assert.Equal(S2CellId.FromFace(5), nbrs[2]); // Check that AppendAllNeighbors produces results that are consistent // with AppendVertexNeighbors for a bunch of random cells. for (var i = 0; i < 1000; ++i) { S2CellId id2 = S2Testing.GetRandomCellId(); if (id2.IsLeaf()) { id2 = id2.Parent(); } // TestAllNeighbors computes approximately 2**(2*(diff+1)) cell ids, // so it's not reasonable to use large values of "diff". int max_diff = Math.Min(5, S2.kMaxCellLevel - id2.Level() - 1); int level = id2.Level() + S2Testing.Random.Uniform(max_diff + 1); TestAllNeighbors(id2, level); } }