]> Cypherpunks repositories - gostls13.git/commitdiff
image: fix Rectangle.Overlaps and Rectangle.Union for empty rectangles.
authorNigel Tao <nigeltao@golang.org>
Tue, 17 Feb 2015 00:15:06 +0000 (11:15 +1100)
committerNigel Tao <nigeltao@golang.org>
Tue, 17 Feb 2015 03:40:33 +0000 (03:40 +0000)
Fixes #9895.

Change-Id: I37d78ced9ff8196e32d299504908a1c41ad4592d
Reviewed-on: https://go-review.googlesource.com/4990
Reviewed-by: Rob Pike <r@golang.org>
src/image/geom.go
src/image/geom_test.go [new file with mode: 0644]

index 6ebaf67da846d55d2297445ba62cf72f8511092f..7c5601065345851ed78ab5a80b2421b2510281dc 100644 (file)
@@ -164,6 +164,12 @@ func (r Rectangle) Intersect(s Rectangle) Rectangle {
 
 // Union returns the smallest rectangle that contains both r and s.
 func (r Rectangle) Union(s Rectangle) Rectangle {
+       if r.Empty() {
+               return s
+       }
+       if s.Empty() {
+               return r
+       }
        if r.Min.X > s.Min.X {
                r.Min.X = s.Min.X
        }
@@ -192,7 +198,8 @@ func (r Rectangle) Eq(s Rectangle) bool {
 
 // Overlaps reports whether r and s have a non-empty intersection.
 func (r Rectangle) Overlaps(s Rectangle) bool {
-       return r.Min.X < s.Max.X && s.Min.X < r.Max.X &&
+       return !r.Empty() && !s.Empty() &&
+               r.Min.X < s.Max.X && s.Min.X < r.Max.X &&
                r.Min.Y < s.Max.Y && s.Min.Y < r.Max.Y
 }
 
diff --git a/src/image/geom_test.go b/src/image/geom_test.go
new file mode 100644 (file)
index 0000000..5bbd8a9
--- /dev/null
@@ -0,0 +1,103 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package image
+
+import (
+       "fmt"
+       "testing"
+)
+
+func TestRectangle(t *testing.T) {
+       // in checks that every point in f is in g.
+       in := func(f, g Rectangle) error {
+               if !f.In(g) {
+                       return fmt.Errorf("f=%s, f.In(%s): got false, want true", f, g)
+               }
+               for y := f.Min.Y; y < f.Max.Y; y++ {
+                       for x := f.Min.X; x < f.Max.X; x++ {
+                               p := Point{x, y}
+                               if !p.In(g) {
+                                       return fmt.Errorf("p=%s, p.In(%s): got false, want true", p, g)
+                               }
+                       }
+               }
+               return nil
+       }
+
+       rects := []Rectangle{
+               Rect(0, 0, 10, 10),
+               Rect(1, 2, 3, 4),
+               Rect(4, 6, 10, 10),
+               Rect(2, 3, 12, 5),
+               Rect(-1, -2, 0, 0),
+               Rect(-1, -2, 4, 6),
+               Rect(-10, -20, 30, 40),
+               Rect(8, 8, 8, 8),
+               Rect(88, 88, 88, 88),
+               Rect(6, 5, 4, 3),
+       }
+
+       // The intersection should be the largest rectangle a such that every point
+       // in a is both in r and in s.
+       for _, r := range rects {
+               for _, s := range rects {
+                       a := r.Intersect(s)
+                       if err := in(a, r); err != nil {
+                               t.Errorf("Intersect: r=%s, s=%s, a=%s, a not in r: %v", r, s, a, err)
+                       }
+                       if err := in(a, s); err != nil {
+                               t.Errorf("Intersect: r=%s, s=%s, a=%s, a not in s: %v", r, s, a, err)
+                       }
+                       if a.Empty() == r.Overlaps(s) {
+                               t.Errorf("Intersect: r=%s, s=%s, a=%s: empty=%t same as overlaps=%t",
+                                       r, s, a, a.Empty(), r.Overlaps(s))
+                       }
+                       largerThanA := [4]Rectangle{a, a, a, a}
+                       largerThanA[0].Min.X--
+                       largerThanA[1].Min.Y--
+                       largerThanA[2].Max.X++
+                       largerThanA[3].Max.Y++
+                       for i, b := range largerThanA {
+                               if b.Empty() {
+                                       // b isn't actually larger than a.
+                                       continue
+                               }
+                               if in(b, r) == nil && in(b, s) == nil {
+                                       t.Errorf("Intersect: r=%s, s=%s, a=%s, b=%s, i=%d: intersection could be larger",
+                                               r, s, a, b, i)
+                               }
+                       }
+               }
+       }
+
+       // The union should be the smallest rectangle a such that every point in r
+       // is in a and every point in s is in a.
+       for _, r := range rects {
+               for _, s := range rects {
+                       a := r.Union(s)
+                       if err := in(r, a); err != nil {
+                               t.Errorf("Union: r=%s, s=%s, a=%s, r not in a: %v", r, s, a, err)
+                       }
+                       if err := in(s, a); err != nil {
+                               t.Errorf("Union: r=%s, s=%s, a=%s, s not in a: %v", r, s, a, err)
+                       }
+                       if a.Empty() {
+                               // You can't get any smaller than a.
+                               continue
+                       }
+                       smallerThanA := [4]Rectangle{a, a, a, a}
+                       smallerThanA[0].Min.X++
+                       smallerThanA[1].Min.Y++
+                       smallerThanA[2].Max.X--
+                       smallerThanA[3].Max.Y--
+                       for i, b := range smallerThanA {
+                               if in(r, b) == nil && in(s, b) == nil {
+                                       t.Errorf("Union: r=%s, s=%s, a=%s, b=%s, i=%d: union could be smaller",
+                                               r, s, a, b, i)
+                               }
+                       }
+               }
+       }
+}