Skip to content

Commit e46a1d5

Browse files
authored
Return early in surfaceIntegral if there are less than 3 vertices. (#201)
There's no area to integrate in these cases. Mirrors the logic from cpp at https://github.com/google/s2geometry/blob/9a43f6ac20950e59a182f498bcd3e9aa8fbe55ec/src/s2/s2loop_measures.h#L298 Addresses #197
1 parent 92ad70d commit e46a1d5

File tree

2 files changed

+59
-20
lines changed

2 files changed

+59
-20
lines changed

s2/loop.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,10 @@ func (l *Loop) ContainsNested(other *Loop) bool {
10051005
//
10061006
// Any changes to this method may need corresponding changes to surfaceIntegralPoint as well.
10071007
func (l *Loop) surfaceIntegralFloat64(f func(a, b, c Point) float64) float64 {
1008+
if len(l.vertices) < 3 {
1009+
// If the loop has less than 3 vertices, there's no interior.
1010+
return 0
1011+
}
10081012
// We sum f over a collection T of oriented triangles, possibly
10091013
// overlapping. Let the sign of a triangle be +1 if it is CCW and -1
10101014
// otherwise, and let the sign of a point x be the sum of the signs of the
@@ -1092,6 +1096,10 @@ func (l *Loop) surfaceIntegralFloat64(f func(a, b, c Point) float64) float64 {
10921096
func (l *Loop) surfaceIntegralPoint(f func(a, b, c Point) Point) Point {
10931097
const maxLength = math.Pi - 1e-5
10941098
var sum r3.Vector
1099+
if len(l.vertices) < 3 {
1100+
// If the loop has less than 3 vertices, there's no interior.
1101+
return Point{sum}
1102+
}
10951103

10961104
origin := l.Vertex(0)
10971105
for i := 1; i+1 < len(l.vertices); i++ {

s2/loop_test.go

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,28 +1584,59 @@ func TestLoopTurningAngle(t *testing.T) {
15841584
}
15851585

15861586
func TestLoopAreaAndCentroid(t *testing.T) {
1587-
var p Point
1588-
1589-
if got, want := EmptyLoop().Area(), 0.0; got != want {
1590-
t.Errorf("EmptyLoop.Area() = %v, want %v", got, want)
1591-
}
1592-
if got, want := FullLoop().Area(), 4*math.Pi; got != want {
1593-
t.Errorf("FullLoop.Area() = %v, want %v", got, want)
1594-
}
1595-
if got := EmptyLoop().Centroid(); !p.ApproxEqual(got) {
1596-
t.Errorf("EmptyLoop.Centroid() = %v, want %v", got, p)
1597-
}
1598-
if got := FullLoop().Centroid(); !p.ApproxEqual(got) {
1599-
t.Errorf("FullLoop.Centroid() = %v, want %v", got, p)
1600-
}
1601-
1602-
if got, want := northHemi.Area(), 2*math.Pi; !float64Eq(got, want) {
1603-
t.Errorf("northHemi.Area() = %v, want %v", got, want)
1587+
tests := []struct {
1588+
name string
1589+
loop *Loop
1590+
wantArea float64
1591+
wantCentroid Point
1592+
}{
1593+
{
1594+
name: "EmptyLoop",
1595+
loop: EmptyLoop(),
1596+
wantArea: 0.0,
1597+
wantCentroid: Point{},
1598+
},
1599+
{
1600+
name: "FullLoop",
1601+
loop: FullLoop(),
1602+
wantArea: 4 * math.Pi,
1603+
wantCentroid: Point{},
1604+
},
1605+
{
1606+
name: "northHemi",
1607+
loop: northHemi,
1608+
wantArea: 2 * math.Pi,
1609+
wantCentroid: Point{}, // Centroid of a hemisphere is (0,0,0)
1610+
},
1611+
{
1612+
name: "eastHemi",
1613+
loop: eastHemi,
1614+
wantArea: 2 * math.Pi,
1615+
wantCentroid: Point{}, // Centroid of a hemisphere is (0,0,0)
1616+
},
1617+
{
1618+
name: "lineTriangle",
1619+
loop: lineTriangle,
1620+
wantArea: 0,
1621+
wantCentroid: Point{},
1622+
},
1623+
{
1624+
name: "twoPoints",
1625+
loop: LoopFromPoints([]Point{PointFromLatLng(LatLngFromDegrees(0, 0)), PointFromLatLng(LatLngFromDegrees(0, 1))}),
1626+
wantArea: 0,
1627+
wantCentroid: Point{},
1628+
},
16041629
}
16051630

1606-
eastHemiArea := eastHemi.Area()
1607-
if eastHemiArea < 2*math.Pi-1e-12 || eastHemiArea > 2*math.Pi+1e-12 {
1608-
t.Errorf("eastHemi.Area() = %v, want between [%v, %v]", eastHemiArea, 2*math.Pi-1e-12, 2*math.Pi+1e-12)
1631+
for _, test := range tests {
1632+
t.Run(test.name, func(t *testing.T) {
1633+
if got := test.loop.Area(); !float64Near(got, test.wantArea, epsilon) {
1634+
t.Errorf("Area() = %v, want %v", got, test.wantArea)
1635+
}
1636+
if got := test.loop.Centroid(); !got.ApproxEqual(test.wantCentroid) {
1637+
t.Errorf("Centroid() = %v, want %v", got, test.wantCentroid)
1638+
}
1639+
})
16091640
}
16101641

16111642
// Construct spherical caps of random height, and approximate their boundary

0 commit comments

Comments
 (0)