public void Test_S2CellId_ParentChildRelationships() { S2CellId id = S2CellId.FromFacePosLevel(3, 0x12345678, S2.kMaxCellLevel - 4); Assert.True(id.IsValid()); Assert.Equal(3UL, id.Face()); Assert.Equal(0x12345700UL, id.Pos()); Assert.Equal(S2.kMaxCellLevel - 4, id.Level()); Assert.False(id.IsLeaf()); Assert.Equal(0x12345610UL, id.ChildBegin(id.Level() + 2).Pos()); Assert.Equal(0x12345640UL, id.ChildBegin().Pos()); Assert.Equal(0x12345400UL, id.Parent().Pos()); Assert.Equal(0x12345000UL, id.Parent(id.Level() - 2).Pos()); // Check ordering of children relative to parents. Assert.True(id.ChildBegin() < id); Assert.True(id.ChildEnd() > id); Assert.Equal(id.ChildEnd(), id.ChildBegin().Next().Next().Next().Next()); Assert.Equal(id.RangeMin(), id.ChildBegin(S2.kMaxCellLevel)); Assert.Equal(id.RangeMax().Next(), id.ChildEnd(S2.kMaxCellLevel)); // Check that cells are represented by the position of their center // along the Hilbert curve. Assert.Equal(2 * id.Id, id.RangeMin().Id + id.RangeMax().Id); }
public void Test_S2CellId_CenterSiTi() { S2CellId id = S2CellId.FromFacePosLevel(3, 0x12345678, S2.kMaxCellLevel); // Check that the (si, ti) coordinates of the center end in a // 1 followed by (30 - level) 0s. // Leaf level, 30. id.CenterSiTi(out var si, out var ti); Assert.Equal(1 << 0, si & 1); Assert.Equal(1 << 0, ti & 1); // Level 29. id.Parent(S2.kMaxCellLevel - 1).CenterSiTi(out si, out ti); Assert.Equal(1 << 1, si & 3); Assert.Equal(1 << 1, ti & 3); // Level 28. id.Parent(S2.kMaxCellLevel - 2).CenterSiTi(out si, out ti); Assert.Equal(1 << 2, si & 7); Assert.Equal(1 << 2, ti & 7); // Level 20. id.Parent(S2.kMaxCellLevel - 10).CenterSiTi(out si, out ti); Assert.Equal(1 << 10, si & ((1 << 11) - 1)); Assert.Equal(1 << 10, ti & ((1 << 11) - 1)); // Level 10. id.Parent(S2.kMaxCellLevel - 20).CenterSiTi(out si, out ti); Assert.Equal(1 << 20, si & ((1 << 21) - 1)); Assert.Equal(1 << 20, ti & ((1 << 21) - 1)); // Level 0. id.Parent(0).CenterSiTi(out si, out ti); Assert.Equal(1 << 30, si & ((1U << 31) - 1)); Assert.Equal(1 << 30, ti & ((1U << 31) - 1)); }
// Computes the smallest S2Cell that covers the S2Cell range (first, last) and // adds this cell to "cell_ids". // // REQUIRES: "first" and "last" have a common ancestor. private static void CoverRange(S2CellId first, S2CellId last, List <S2CellId> cell_ids) { if (first == last) { // The range consists of a single index cell. cell_ids.Add(first); } else { // Add the lowest common ancestor of the given range. int level = first.CommonAncestorLevel(last); System.Diagnostics.Debug.Assert(level >= 0); cell_ids.Add(first.Parent(level)); } }
public void Test_S2PaddedCell_ShrinkToFit() { const int kIters = 1000; for (int iter = 0; iter < kIters; ++iter) { // Start with the desired result and work backwards. S2CellId result = S2Testing.GetRandomCellId(); R2Rect result_uv = result.BoundUV(); R2Point size_uv = result_uv.GetSize(); // Find the biggest rectangle that fits in "result" after padding. // (These calculations ignore numerical errors.) double max_padding = 0.5 * Math.Min(size_uv[0], size_uv[1]); double padding = max_padding * S2Testing.Random.RandDouble(); R2Rect max_rect = result_uv.Expanded(-padding); // Start with a random subset of the maximum rectangle. R2Point a = new(SampleInterval(max_rect[0]), SampleInterval(max_rect[1])); R2Point b = new(SampleInterval(max_rect[0]), SampleInterval(max_rect[1])); if (!result.IsLeaf()) { // If the result is not a leaf cell, we must ensure that no child of // "result" also satisfies the conditions of ShrinkToFit(). We do this // by ensuring that "rect" intersects at least two children of "result" // (after padding). int axis = S2Testing.Random.Uniform(2); double center = result.CenterUV()[axis]; // Find the range of coordinates that are shared between child cells // along that axis. R1Interval shared = new(center - padding, center + padding); double mid = SampleInterval(shared.Intersection(max_rect[axis])); a = a.SetAxis(axis, SampleInterval(new R1Interval(max_rect[axis].Lo, mid))); b = b.SetAxis(axis, SampleInterval(new R1Interval(mid, max_rect[axis].Hi))); } R2Rect rect = R2Rect.FromPointPair(a, b); // Choose an arbitrary ancestor as the S2PaddedCell. S2CellId initial_id = result.Parent(S2Testing.Random.Uniform(result.Level() + 1)); Assert.Equal(result, new S2PaddedCell(initial_id, padding).ShrinkToFit(rect)); } }
public void Test_CellTarget_VisitContainingShapes() { var index = MakeIndexOrDie( "1:1 # 1:1, 2:2 # 0:0, 0:3, 3:0 | 6:6, 6:9, 9:6 | -1:-1, -1:5, 5:-1"); // Only shapes 2 and 4 should contain a very small cell near 1:1. var cellid1 = new S2CellId(MakePointOrDie("1:1")); var target1 = new S2MinDistanceCellTarget(new S2Cell(cellid1)); Assert.True(IsSubsetOfSize(GetContainingShapes(target1, index, 1), new int[] { 2, 4 }, 1)); Assert.Equal(new int[] { 2, 4 }, GetContainingShapes(target1, index, 5)); // For a larger cell that properly contains one or more index cells, all // shapes that intersect the first such cell in S2CellId order are returned. // In the test below, this happens to again be the 1st and 3rd polygons // (whose shape_ids are 2 and 4). var cellid2 = cellid1.Parent(5); var target2 = new S2MinDistanceCellTarget(new S2Cell(cellid2)); Assert.Equal(new int[] { 2, 4 }, GetContainingShapes(target2, index, 5)); }
public void CanonicalizeCovering(List <S2CellId> covering) { // We check this on each call because of Options(). System.Diagnostics.Debug.Assert(Options_.MinLevel <= Options_.MaxLevel); // Note that when the covering parameters have their default values, almost // all of the code in this function is skipped. // If any cells are too small, or don't satisfy level_mod(), then replace // them with ancestors. if (Options_.MaxLevel < S2.kMaxCellLevel || Options_.LevelMod > 1) { for (var i = 0; i < covering.Count; i++) { var id = covering[i]; int level = id.Level(); int new_level = AdjustLevel(Math.Min(level, Options_.MaxLevel)); if (new_level != level) { covering[i] = id.Parent(new_level); } } } // Sort the cells and simplify them. S2CellUnion.Normalize(covering); // Make sure that the covering satisfies min_level() and level_mod(), // possibly at the expense of satisfying max_cells(). if (Options_.MinLevel > 0 || Options_.LevelMod > 1) { var tmp = new List <S2CellId>(); S2CellUnion.Denormalize(covering, Options_.MinLevel, Options_.LevelMod, tmp); covering.Clear(); covering.AddRange(tmp); } // If there are too many cells and the covering is very large, use the // S2RegionCoverer to compute a new covering. (This avoids possible O(n^2) // behavior of the simpler algorithm below.) var excess = covering.Count - Options_.MaxCells; if (excess <= 0 || IsCanonical(covering)) { return; } if (excess * covering.Count > 10000) { GetCovering(new S2CellUnion(covering), out covering); } else { // Repeatedly replace two adjacent cells in S2CellId order by their lowest // common ancestor until the number of cells is acceptable. while (covering.Count > Options_.MaxCells) { int best_index = -1, best_level = -1; for (int i = 0; i + 1 < covering.Count; ++i) { int level = covering[i].CommonAncestorLevel(covering[i + 1]); level = AdjustLevel(level); if (level > best_level) { best_level = level; best_index = i; } } if (best_level < Options_.MinLevel) { break; } // Replace all cells contained by the new ancestor cell. S2CellId id = covering[best_index].Parent(best_level); ReplaceCellsWithAncestor(covering, id); // Now repeatedly check whether all children of the parent cell are // present, in which case we can replace those cells with their parent. while (best_level > Options_.MinLevel) { best_level -= Options_.LevelMod; id = id.Parent(best_level); if (!ContainsAllChildren(covering, id)) { break; } ReplaceCellsWithAncestor(covering, id); } } } System.Diagnostics.Debug.Assert(IsCanonical(covering)); }
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); } }
public void Test_S2Cap_S2CellMethods() { // For each cube face, we construct some cells on // that face and some caps whose positions are relative to that face, // and then check for the expected intersection/containment results. for (var face = 0; face < 6; ++face) { // The cell consisting of the entire face. S2Cell root_cell = S2Cell.FromFace(face); // A leaf cell at the midpoint of the v=1 edge. S2Cell edge_cell = new(S2.FaceUVtoXYZ(face, 0, 1 - kEps)); // A leaf cell at the u=1, v=1 corner. S2Cell corner_cell = new(S2.FaceUVtoXYZ(face, 1 - kEps, 1 - kEps)); // Quick check for full and empty caps. Assert.True(S2Cap.Full.Contains(root_cell)); Assert.False(S2Cap.Empty.MayIntersect(root_cell)); // Check intersections with the bounding caps of the leaf cells that are // adjacent to 'corner_cell' along the Hilbert curve. Because this corner // is at (u=1,v=1), the curve stays locally within the same cube face. S2CellId first = corner_cell.Id.Advance(-3); S2CellId last = corner_cell.Id.Advance(4); for (S2CellId id = first; id < last; id = id.Next()) { S2Cell cell = new(id); Assert.Equal(id == corner_cell.Id, cell.GetCapBound().Contains(corner_cell)); Assert.Equal(id.Parent().Contains(corner_cell.Id), cell.GetCapBound().MayIntersect(corner_cell)); } var anti_face = (face + 3) % 6; // Opposite face. for (var cap_face = 0; cap_face < 6; ++cap_face) { // A cap that barely contains all of 'cap_face'. S2Point center = S2.GetNorm(cap_face); S2Cap covering = new(center, S1Angle.FromRadians(kFaceRadius + kEps)); Assert.Equal(cap_face == face, covering.Contains(root_cell)); Assert.Equal(cap_face != anti_face, covering.MayIntersect(root_cell)); Assert.Equal(center.DotProd(edge_cell.Center()) > 0.1, covering.Contains(edge_cell)); Assert.Equal(covering.MayIntersect(edge_cell), covering.Contains(edge_cell)); Assert.Equal(cap_face == face, covering.Contains(corner_cell)); Assert.Equal(center.DotProd(corner_cell.Center()) > 0, covering.MayIntersect(corner_cell)); // A cap that barely intersects the edges of 'cap_face'. S2Cap bulging = new(center, S1Angle.FromRadians(S2.M_PI_4 + kEps)); Assert.False(bulging.Contains(root_cell)); Assert.Equal(cap_face != anti_face, bulging.MayIntersect(root_cell)); Assert.Equal(cap_face == face, bulging.Contains(edge_cell)); Assert.Equal(center.DotProd(edge_cell.Center()) > 0.1, bulging.MayIntersect(edge_cell)); Assert.False(bulging.Contains(corner_cell)); Assert.False(bulging.MayIntersect(corner_cell)); // A singleton cap. S2Cap singleton = new(center, S1Angle.Zero); Assert.Equal(cap_face == face, singleton.MayIntersect(root_cell)); Assert.False(singleton.MayIntersect(edge_cell)); Assert.False(singleton.MayIntersect(corner_cell)); } } }