// Construct the child of "parent" with the given (i,j) index. The four // child cells have indices of (0,0), (0,1), (1,0), (1,1), where the i and j // indices correspond to increasing u- and v-values respectively. public S2PaddedCell(S2PaddedCell parent, int i, int j) { ij_lo_ = new int[2]; Padding = parent.Padding; Level = parent.Level + 1; // Compute the position and orientation of the child incrementally from the // orientation of the parent. int pos = S2.kIJtoPos[parent.orientation_][2 * i + j]; Id = parent.Id.Child(pos); int ij_size = S2CellId.SizeIJ(Level); ij_lo_[0] = parent.ij_lo_[0] + i * ij_size; ij_lo_[1] = parent.ij_lo_[1] + j * ij_size; orientation_ = parent.orientation_ ^ S2.kPosToOrientation[pos]; // For each child, one corner of the bound is taken directly from the parent // while the diagonally opposite corner is taken from middle(). var middle = parent.Middle; var x = new double[] { parent.Bound[0][0], parent.Bound[0][1] }; var y = new double[] { parent.Bound[1][0], parent.Bound[1][1] }; x[1 - i] = middle[0][1 - i]; y[1 - j] = middle[1][1 - j]; Bound = new R2Rect(new R1Interval(x), new R1Interval(y)); }
public void Test_R2Rect_ConstructorsAndAccessors() { // Check various constructors and accessor methods. R2Rect r = new(new R2Point(0.1, 0), new R2Point(0.25, 1)); Assert.Equal(0.1, r.X.Lo); Assert.Equal(0.25, r.X.Hi); Assert.Equal(0.0, r.Y.Lo); Assert.Equal(1.0, r.Y.Hi); Assert.Equal(0.1, r[0][0]); Assert.Equal(0.25, r[0][1]); Assert.Equal(0.0, r[1][0]); Assert.Equal(1.0, r[1][1]); Assert.Equal(new R1Interval(0.1, 0.25), r.X); Assert.Equal(new R1Interval(0, 1), r.Y); Assert.Equal(new R1Interval(0.1, 0.25), r[0]); Assert.Equal(new R1Interval(0, 1), r[1]); r = new R2Rect(new R1Interval(3, 4), new R1Interval(5, 6)); Assert.Equal(new R1Interval(3, 4), r[0]); Assert.Equal(new R1Interval(5, 6), r[1]); Assert.Equal(r, r); Assert.NotEqual(r, R2Rect.Empty); R2Rect r2 = new(); Assert.True(r2.IsEmpty()); Assert.Equal(r2, R2Rect.Empty); }
private readonly int orientation_; // Hilbert curve orientation of this cell (see s2coords.h) #region Constructors // Construct an S2PaddedCell for the given cell id and padding. public S2PaddedCell(S2CellId id, double padding) { ij_lo_ = new int[2]; Id = id; Padding = padding; if (Id.IsFace()) { // Fast path for constructing a top-level face (the most common case). double limit = 1 + padding; Bound = new R2Rect(new R1Interval(-limit, limit), new R1Interval(-limit, limit)); middle_ = new R2Rect(new R1Interval(-padding, padding), new R1Interval(-padding, padding)); ij_lo_[0] = ij_lo_[1] = 0; orientation_ = (int)(Id.Face() & 1); Level = 0; } else { var ij = new int[2]; id.ToFaceIJOrientation(out ij[0], out ij[1], out orientation_, true); Level = id.Level(); Bound = S2CellId.IJLevelToBoundUV(ij, Level).Expanded(padding); int ij_size = S2CellId.SizeIJ(Level); ij_lo_[0] = ij[0] & -ij_size; ij_lo_[1] = ij[1] & -ij_size; } }
private static void TestIntervalOps(R2Rect x, R2Rect y, string expected_rexion, R2Rect expected_union, R2Rect expected_intersection) { // Test all of the interval operations on the given pair of intervals. // "expected_rexion" is a sequence of "T" and "F" characters corresponding // to the expected results of Contains(), InteriorContains(), Intersects(), // and InteriorIntersects() respectively. Assert.Equal(expected_rexion[0] == 'T', x.Contains(y)); Assert.Equal(expected_rexion[1] == 'T', x.InteriorContains(y)); Assert.Equal(expected_rexion[2] == 'T', x.Intersects(y)); Assert.Equal(expected_rexion[3] == 'T', x.InteriorIntersects(y)); Assert.Equal(x.Union(y) == x, x.Contains(y)); Assert.Equal(!x.Intersection(y).IsEmpty(), x.Intersects(y)); Assert.Equal(expected_union, x.Union(y)); Assert.Equal(expected_intersection, x.Intersection(y)); R2Rect r = x; r = r.AddRect(y); Assert.Equal(expected_union, r); if (y.GetSize() == new R2Point(0, 0)) { r = x; r = r.AddPoint(y.Lo()); Assert.Equal(expected_union, r); } }
public void Test_R2Rect_FromCenterSize() { // FromCenterSize() Assert.True(R2Rect.FromCenterSize(new R2Point(0.3, 0.5), new R2Point(0.2, 0.4)). ApproxEquals(new R2Rect(new R2Point(0.2, 0.3), new R2Point(0.4, 0.7)))); Assert.True(R2Rect.FromCenterSize(new R2Point(1, 0.1), new R2Point(0, 2)). ApproxEquals(new R2Rect(new R2Point(1, -0.9), new R2Point(1, 1.1)))); }
// Given a point P representing a possibly clipped endpoint A of an edge AB, // verify that "clip" contains P, and that if clipping occurred (i.e., P != A) // then P is on the boundary of "clip". private static void CheckPointOnBoundary(R2Point p, R2Point a, R2Rect clip) { Assert.True(clip.Contains(p)); if (p != a) { Assert.False(clip.Contains(new R2Point(MathUtils.NextAfter(p[0], a[0]), MathUtils.NextAfter(p[1], a[1])))); } }
public void Test_R2Rect_EmptyRectangles() { // Test basic properties of empty rectangles. R2Rect empty = R2Rect.Empty; Assert.True(empty.IsValid()); Assert.True(empty.IsEmpty()); Assert.Equal(empty, empty); }
// 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_R2Rect_FromPoint() { // FromPoint(), FromPointPair() R2Rect d1 = new(new R2Point(0.1, 0), new R2Point(0.25, 1)); Assert.Equal(new R2Rect(d1.Lo(), d1.Lo()), R2Rect.FromPoint(d1.Lo())); Assert.Equal(new R2Rect(new R2Point(0.15, 0.3), new R2Point(0.35, 0.9)), R2Rect.FromPointPair(new R2Point(0.15, 0.9), new R2Point(0.35, 0.3))); Assert.Equal(new R2Rect(new R2Point(0.12, 0), new R2Point(0.83, 0.5)), R2Rect.FromPointPair(new R2Point(0.83, 0), new R2Point(0.12, 0.5))); }
private static void CompareS2CellToPadded(S2Cell cell, S2PaddedCell pcell, double padding) { Assert.Equal(cell.Id, pcell.Id); Assert.Equal(cell.Level, pcell.Level); Assert.Equal(padding, pcell.Padding); Assert.Equal(cell.BoundUV.Expanded(padding), pcell.Bound); var center_uv = cell.Id.CenterUV(); Assert.Equal(R2Rect.FromPoint(center_uv).Expanded(padding), pcell.Middle); Assert.Equal(cell.Center(), pcell.GetCenter()); }
public void Test_R2Rect_AddPoint() { // AddPoint() R2Point sw1 = new(0, 0.25); R2Point ne1 = new(0.5, 0.75); R2Rect r1 = new(sw1, ne1); R2Rect r2 = R2Rect.Empty; r2 = r2.AddPoint(new R2Point(0, 0.25)); r2 = r2.AddPoint(new R2Point(0.5, 0.25)); r2 = r2.AddPoint(new R2Point(0, 0.75)); r2 = r2.AddPoint(new R2Point(0.1, 0.4)); Assert.Equal(r1, r2); }
public void Test_R2Rect_IntervalOperations() { // Contains(R2Rect), InteriorContains(R2Rect), // Intersects(), InteriorIntersects(), Union(), Intersection(). // // Much more testing of these methods is done in s1interval_test // and r1interval_test. R2Rect empty = R2Rect.Empty; R2Point sw1 = new(0, 0.25); R2Point ne1 = new(0.5, 0.75); R2Rect r1 = new(sw1, ne1); R2Rect r1_mid = new(new R2Point(0.25, 0.5), new R2Point(0.25, 0.5)); R2Rect r_sw1 = new(sw1, sw1); R2Rect r_ne1 = new(ne1, ne1); TestIntervalOps(r1, r1_mid, "TTTT", r1, r1_mid); TestIntervalOps(r1, r_sw1, "TFTF", r1, r_sw1); TestIntervalOps(r1, r_ne1, "TFTF", r1, r_ne1); Assert.Equal(new R2Rect(new R2Point(0, 0.25), new R2Point(0.5, 0.75)), r1); TestIntervalOps(r1, new R2Rect(new R2Point(0.45, 0.1), new R2Point(0.75, 0.3)), "FFTT", new R2Rect(new R2Point(0, 0.1), new R2Point(0.75, 0.75)), new R2Rect(new R2Point(0.45, 0.25), new R2Point(0.5, 0.3))); TestIntervalOps(r1, new R2Rect(new R2Point(0.5, 0.1), new R2Point(0.7, 0.3)), "FFTF", new R2Rect(new R2Point(0, 0.1), new R2Point(0.7, 0.75)), new R2Rect(new R2Point(0.5, 0.25), new R2Point(0.5, 0.3))); TestIntervalOps(r1, new R2Rect(new R2Point(0.45, 0.1), new R2Point(0.7, 0.25)), "FFTF", new R2Rect(new R2Point(0, 0.1), new R2Point(0.7, 0.75)), new R2Rect(new R2Point(0.45, 0.25), new R2Point(0.5, 0.25))); TestIntervalOps(new R2Rect(new R2Point(0.1, 0.2), new R2Point(0.1, 0.3)), new R2Rect(new R2Point(0.15, 0.7), new R2Point(0.2, 0.8)), "FFFF", new R2Rect(new R2Point(0.1, 0.2), new R2Point(0.2, 0.8)), empty); // Check that the intersection of two rectangles that overlap in x but not y // is valid, and vice versa. TestIntervalOps(new R2Rect(new R2Point(0.1, 0.2), new R2Point(0.4, 0.5)), new R2Rect(new R2Point(0, 0), new R2Point(0.2, 0.1)), "FFFF", new R2Rect(new R2Point(0, 0), new R2Point(0.4, 0.5)), empty); TestIntervalOps(new R2Rect(new R2Point(0, 0), new R2Point(0.1, 0.3)), new R2Rect(new R2Point(0.2, 0.1), new R2Point(0.3, 0.4)), "FFFF", new R2Rect(new R2Point(0, 0), new R2Point(0.3, 0.4)), empty); }
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)); } }