private static void TestIntervalOps(S1Interval x, S1Interval y, string expected_relation, S1Interval expected_union, S1Interval expected_intersection) { // Test all of the interval operations on the given pair of intervals. // "expected_relation" is a sequence of "T" and "F" characters corresponding // to the expected results of Contains(), InteriorContains(), Intersects(), // and InteriorIntersects() respectively. Assert.Equal(x.Contains(y), expected_relation[0] == 'T'); Assert.Equal(x.InteriorContains(y), expected_relation[1] == 'T'); Assert.Equal(x.Intersects(y), expected_relation[2] == 'T'); Assert.Equal(x.InteriorIntersects(y), expected_relation[3] == 'T'); // bounds() returns a reference to a member variable, so we need to // make a copy when invoking it on a temporary object. Assert.Equal(R2Point.FromCoords(x.Union(y).Bounds()).Bounds(), expected_union.Bounds()); Assert.Equal(R2Point.FromCoords(x.Intersection(y).Bounds()).Bounds(), expected_intersection.Bounds()); Assert.Equal(x.Contains(y), x.Union(y) == x); Assert.Equal(x.Intersects(y), !x.Intersection(y).IsEmpty()); if (y.Lo == y.Hi) { var r = S1Interval.AddPoint(x, y.Lo); Assert.Equal(r.Bounds(), expected_union.Bounds()); } }
public void Test_S1IntervalTestBase_Contains() { // Contains(double), InteriorContains(double) Assert.True(!empty.Contains(0) && !empty.Contains(Math.PI) && !empty.Contains(-Math.PI)); Assert.True(!empty.InteriorContains(Math.PI) && !empty.InteriorContains(-Math.PI)); Assert.True(full.Contains(0) && full.Contains(Math.PI) && full.Contains(-Math.PI)); Assert.True(full.InteriorContains(Math.PI) && full.InteriorContains(-Math.PI)); Assert.True(quad12.Contains(0) && quad12.Contains(Math.PI) && quad12.Contains(-Math.PI)); Assert.True(quad12.InteriorContains(S2.M_PI_2) && !quad12.InteriorContains(0)); Assert.True(!quad12.InteriorContains(Math.PI) && !quad12.InteriorContains(-Math.PI)); Assert.True(quad23.Contains(S2.M_PI_2) && quad23.Contains(-S2.M_PI_2)); Assert.True(quad23.Contains(Math.PI) && quad23.Contains(-Math.PI)); Assert.True(!quad23.Contains(0)); Assert.True(!quad23.InteriorContains(S2.M_PI_2) && !quad23.InteriorContains(-S2.M_PI_2)); Assert.True(quad23.InteriorContains(Math.PI) && quad23.InteriorContains(-Math.PI)); Assert.True(!quad23.InteriorContains(0)); Assert.True(pi.Contains(Math.PI) && pi.Contains(-Math.PI) && !pi.Contains(0)); Assert.True(!pi.InteriorContains(Math.PI) && !pi.InteriorContains(-Math.PI)); Assert.True(mipi.Contains(Math.PI) && mipi.Contains(-Math.PI) && !mipi.Contains(0)); Assert.True(!mipi.InteriorContains(Math.PI) && !mipi.InteriorContains(-Math.PI)); Assert.True(zero.Contains(0) && !zero.InteriorContains(0)); }
// Returns the minimum distance from X to the latitude line segment defined by // the given latitude and longitude interval. private static S1Angle GetDistance(S2LatLng x, S1Angle lat, S1Interval interval) { Assert.True(x.IsValid()); Assert.True(interval.IsValid()); // Is X inside the longitude interval? if (interval.Contains(x.LngRadians)) { return(S1Angle.FromRadians((x.Lat() - lat).Abs())); } // Return the distance to the closer endpoint. return(new[] { x.GetDistance(new S2LatLng(lat, S1Angle.FromRadians(interval.Lo))), x.GetDistance(new S2LatLng(lat, S1Angle.FromRadians(interval.Hi))) }.Min()); }
/** * Returns the minimum distance from X to the latitude line segment defined by * the given latitude and longitude interval. */ private static S1Angle getDistance(S2LatLng x, S1Angle lat, S1Interval interval) { assertTrue(x.IsValid); assertTrue(interval.IsValid); // Is X inside the longitude interval? if (interval.Contains(x.Lng.Radians)) { return(S1Angle.FromRadians(Math.Abs(x.Lat.Radians - lat.Radians))); } // Return the distance to the closer endpoint. return(S1Angle.Min(x.GetDistance(new S2LatLng(lat, S1Angle.FromRadians(interval.Lo))), x.GetDistance(new S2LatLng(lat, S1Angle.FromRadians(interval.Hi))))); }
/** * Returns the minimum distance from X to the latitude line segment defined by * the given latitude and longitude interval. */ private static S1Angle getDistance(S2LatLng x, S1Angle lat, S1Interval interval) { assertTrue(x.IsValid); assertTrue(interval.IsValid); // Is X inside the longitude interval? if (interval.Contains(x.Lng.Radians)) return S1Angle.FromRadians(Math.Abs(x.Lat.Radians - lat.Radians)); // Return the distance to the closer endpoint. return S1Angle.Min(x.GetDistance(new S2LatLng(lat, S1Angle.FromRadians(interval.Lo))), x.GetDistance(new S2LatLng(lat, S1Angle.FromRadians(interval.Hi)))); }
private void TestFaceClipping(S2Point a_raw, S2Point b_raw) { S2Point a = a_raw.Normalize(); S2Point b = b_raw.Normalize(); // First we test GetFaceSegments. FaceSegmentVector segments = new(); GetFaceSegments(a, b, segments); int n = segments.Count; Assert.True(n >= 1); var msg = new StringBuilder($"\nA={a_raw}\nB={b_raw}\nN={S2.RobustCrossProd(a, b)}\nSegments:\n"); int i1 = 0; foreach (var s in segments) { msg.AppendLine($"{i1++}: face={s.face}, a={s.a}, b={s.b}"); } _logger.WriteLine(msg.ToString()); R2Rect biunit = new(new R1Interval(-1, 1), new R1Interval(-1, 1)); var kErrorRadians = kFaceClipErrorRadians; // The first and last vertices should approximately equal A and B. Assert.True(a.Angle(S2.FaceUVtoXYZ(segments[0].face, segments[0].a)) <= kErrorRadians); Assert.True(b.Angle(S2.FaceUVtoXYZ(segments[n - 1].face, segments[n - 1].b)) <= kErrorRadians); S2Point norm = S2.RobustCrossProd(a, b).Normalize(); S2Point a_tangent = norm.CrossProd(a); S2Point b_tangent = b.CrossProd(norm); for (int i = 0; i < n; ++i) { // Vertices may not protrude outside the biunit square. Assert.True(biunit.Contains(segments[i].a)); Assert.True(biunit.Contains(segments[i].b)); if (i == 0) { continue; } // The two representations of each interior vertex (on adjacent faces) // must correspond to exactly the same S2Point. Assert.NotEqual(segments[i - 1].face, segments[i].face); Assert.Equal(S2.FaceUVtoXYZ(segments[i - 1].face, segments[i - 1].b), S2.FaceUVtoXYZ(segments[i].face, segments[i].a)); // Interior vertices should be in the plane containing A and B, and should // be contained in the wedge of angles between A and B (i.e., the dot // products with a_tangent and b_tangent should be non-negative). S2Point p = S2.FaceUVtoXYZ(segments[i].face, segments[i].a).Normalize(); Assert.True(Math.Abs(p.DotProd(norm)) <= kErrorRadians); Assert.True(p.DotProd(a_tangent) >= -kErrorRadians); Assert.True(p.DotProd(b_tangent) >= -kErrorRadians); } // Now we test ClipToPaddedFace (sometimes with a padding of zero). We do // this by defining an (x,y) coordinate system for the plane containing AB, // and converting points along the great circle AB to angles in the range // [-Pi, Pi]. We then accumulate the angle intervals spanned by each // clipped edge; the union over all 6 faces should approximately equal the // interval covered by the original edge. double padding = S2Testing.Random.OneIn(10) ? 0.0 : 1e-10 * Math.Pow(1e-5, S2Testing.Random.RandDouble()); S2Point x_axis = a, y_axis = a_tangent; S1Interval expected_angles = new(0, a.Angle(b)); S1Interval max_angles = expected_angles.Expanded(kErrorRadians); S1Interval actual_angles = new(); for (int face = 0; face < 6; ++face) { if (ClipToPaddedFace(a, b, face, padding, out var a_uv, out var b_uv)) { S2Point a_clip = S2.FaceUVtoXYZ(face, a_uv).Normalize(); S2Point b_clip = S2.FaceUVtoXYZ(face, b_uv).Normalize(); Assert.True(Math.Abs(a_clip.DotProd(norm)) <= kErrorRadians); Assert.True(Math.Abs(b_clip.DotProd(norm)) <= kErrorRadians); if (a_clip.Angle(a) > kErrorRadians) { Assert2.DoubleEqual(1 + padding, Math.Max(Math.Abs(a_uv[0]), Math.Abs(a_uv[1]))); } if (b_clip.Angle(b) > kErrorRadians) { Assert2.DoubleEqual(1 + padding, Math.Max(Math.Abs(b_uv[0]), Math.Abs(b_uv[1]))); } double a_angle = Math.Atan2(a_clip.DotProd(y_axis), a_clip.DotProd(x_axis)); double b_angle = Math.Atan2(b_clip.DotProd(y_axis), b_clip.DotProd(x_axis)); // Rounding errors may cause b_angle to be slightly less than a_angle. // We handle this by constructing the interval with FromPointPair(), // which is okay since the interval length is much less than Math.PI. S1Interval face_angles = S1Interval.FromPointPair(a_angle, b_angle); Assert.True(max_angles.Contains(face_angles)); actual_angles = actual_angles.Union(face_angles); } } Assert.True(actual_angles.Expanded(kErrorRadians).Contains(expected_angles)); }