public void Test_LoopTestBase_GetCurvature() { Assert.Equal(-S2.M_2_PI, S2.GetCurvature(full_)); Assert.Equal(S2.M_2_PI, S2.GetCurvature(v_loop_)); CheckCurvatureInvariants(v_loop_); // This curvature should be computed exactly. Assert.Equal(0, S2.GetCurvature(north_hemi3_)); CheckCurvatureInvariants(north_hemi3_); Assert2.Near(0, S2.GetCurvature(west_hemi_), 1e-15); CheckCurvatureInvariants(west_hemi_); // We don't have an easy way to estimate the curvature of these loops, but // we can still check that the expected invariants hold. CheckCurvatureInvariants(candy_cane_); CheckCurvatureInvariants(three_leaf_clover_); Assert2.DoubleEqual(S2.M_2_PI, S2.GetCurvature(line_triangle_)); CheckCurvatureInvariants(line_triangle_); Assert2.DoubleEqual(S2.M_2_PI, S2.GetCurvature(skinny_chevron_)); CheckCurvatureInvariants(skinny_chevron_); // Build a narrow spiral loop starting at the north pole. This is designed // to test that the error in GetCurvature is linear in the number of // vertices even when the partial sum of the curvatures gets very large. // The spiral consists of two "arms" defining opposite sides of the loop. // This is a pathological loop that contains many long parallel edges. int kArmPoints = 10000; // Number of vertices in each "arm" double kArmRadius = 0.01; // Radius of spiral. S2PointLoopSpan spiral = new(2 * kArmPoints); spiral[kArmPoints] = new S2Point(0, 0, 1); for (int i = 0; i < kArmPoints; ++i) { double angle = (S2.M_2_PI / 3) * i; double x = Math.Cos(angle); double y = Math.Sin(angle); double r1 = i * kArmRadius / kArmPoints; double r2 = (i + 1.5) * kArmRadius / kArmPoints; spiral[kArmPoints - i - 1] = new S2Point(r1 * x, r1 * y, 1).Normalize(); spiral[kArmPoints + i] = new S2Point(r2 * x, r2 * y, 1).Normalize(); } // Check that GetCurvature() is consistent with GetArea() to within the // error bound of the former. We actually use a tiny fraction of the // worst-case error bound, since the worst case only happens when all the // roundoff errors happen in the same direction and this test is not // designed to achieve that. The error in GetArea() can be ignored for the // purposes of this test since it is generally much smaller. Assert2.Near( S2.M_2_PI - S2.GetArea(spiral), S2.GetCurvature(spiral), 0.01 * S2.GetCurvatureMaxError(spiral)); }
private static void TestAreaConsistentWithCurvature(S2PointLoopSpan loop) { // Check that the area computed using GetArea() is consistent with the loop // curvature. According to the Gauss-Bonnet theorem, the area of the loop // equals 2*Pi minus its curvature. double area = S2.GetArea(loop); double gauss_area = S2.M_2_PI - S2.GetCurvature(loop); // The error bound below is sufficient for current tests but not guaranteed. _ = loop.ToDebugString(); Assert.True(Math.Abs(area - gauss_area) <= 1e-14); }
// Check that the curvature is *identical* when the vertex order is // rotated, and that the sign is inverted when the vertices are reversed. private static void CheckCurvatureInvariants(S2PointLoopSpan loop_in) { S2.LoopOrder order_in = S2.GetCanonicalLoopOrder(loop_in); var loop = loop_in.ToList(); double expected = S2.GetCurvature(loop); for (int i = 0; i < loop.Count; ++i) { loop.Reverse(); Assert.Equal((expected == S2.M_2_PI) ? expected : -expected, S2.GetCurvature(loop)); ExpectSameOrder(loop_in, order_in, loop, S2.GetCanonicalLoopOrder(loop)); loop.Reverse(); loop.RotateInPlace(1); Assert.Equal(expected, S2.GetCurvature(loop)); ExpectSameOrder(loop_in, order_in, loop, S2.GetCanonicalLoopOrder(loop)); } }