// Return the smallest cell that contains all descendants of this cell whose // bounds intersect "rect". For algorithms that use recursive subdivision // to find the cells that intersect a particular object, this method can be // used to skip all the initial subdivision steps where only one child needs // to be expanded. // // Note that this method is not the same as returning the smallest cell that // contains the intersection of this cell with "rect". Because of the // padding, even if one child completely contains "rect" it is still // possible that a neighboring child also intersects "rect". // // REQUIRES: bound().Intersects(rect) public S2CellId ShrinkToFit(R2Rect rect) { System.Diagnostics.Debug.Assert(Bound.Intersects(rect)); // Quick rejection test: if "rect" contains the center of this cell along // either axis, then no further shrinking is possible. int ij_size = S2CellId.SizeIJ(Level); if (Level == 0) { // Fast path (most calls to this function start with a face cell). if (rect[0].Contains(0) || rect[1].Contains(0)) { return(Id); } } else { if (rect[0].Contains(S2.STtoUV(S2.SiTitoST((uint)(2 * ij_lo_[0] + ij_size)))) || rect[1].Contains(S2.STtoUV(S2.SiTitoST((uint)(2 * ij_lo_[1] + ij_size))))) { return(Id); } } // Otherwise we expand "rect" by the given padding() on all sides and find // the range of coordinates that it spans along the i- and j-axes. We then // compute the highest bit position at which the min and max coordinates // differ. This corresponds to the first cell level at which at least two // children intersect "rect". // Increase the padding to compensate for the error in S2Coords.UVtoST(). // (The constant below is a provable upper bound on the additional error.) R2Rect padded = rect.Expanded(Padding + 1.5 * S2.DoubleEpsilon); var ij_min = new int[2]; // Min i- or j- coordinate spanned by "padded" var ij_xor = new int[2]; // XOR of the min and max i- or j-coordinates for (int d = 0; d < 2; ++d) { ij_min[d] = Math.Max(ij_lo_[d], S2.STtoIJ(S2.UVtoST(padded[d][0]))); int ij_max = Math.Min(ij_lo_[d] + ij_size - 1, S2.STtoIJ(S2.UVtoST(padded[d][1]))); ij_xor[d] = ij_min[d] ^ ij_max; } // Compute the highest bit position where the two i- or j-endpoints differ, // and then choose the cell level that includes both of these endpoints. So // if both pairs of endpoints are equal we choose kMaxLevel; if they differ // only at bit 0, we choose (kMaxLevel - 1), and so on. var level_msb = ((ij_xor[0] | ij_xor[1]) << 1) + 1; var level = S2.kMaxCellLevel - BitsUtils.FindMSBSetNonZero((uint)level_msb); if (level <= Level) { return(Id); } return(S2CellId.FromFaceIJ((int)Id.Face(), ij_min[0], ij_min[1]).Parent(level)); }
// Given an edge AB and a rectangle "clip", verify that IntersectsRect(), // ClipEdge(), and ClipEdgeBound() produce consistent results. private static void TestClipEdge(R2Point a, R2Point b, R2Rect clip) { // A bound for the error in edge clipping plus the error in the // IntersectsRect calculation below. double kError = kEdgeClipErrorUVDist + kIntersectsRectErrorUVDist; if (!ClipEdge(a, b, clip, out var a_clipped, out var b_clipped)) { Assert.False(IntersectsRect(a, b, clip.Expanded(-kError))); }
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)); } }