if r.Dy() > dy {
r.Max.Y = r.Min.Y + dy
}
-
- // TODO(nigeltao): Clip r to dst's bounding box, and handle the case when sp or mp has negative X or Y.
- // TODO(nigeltao): Ensure that r is well formed, i.e. r.Max.X >= r.Min.X and likewise for Y.
+ r = r.Intersect(dst.Bounds())
+ if r.Empty() {
+ return
+ }
// Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation.
if dst0, ok := dst.(*image.RGBA); ok {
}
// A Rectangle contains the points with Min.X <= X < Max.X, Min.Y <= Y < Max.Y.
+// It is well-formed if Min.X <= Max.X and likewise for Y. Points are always
+// well-formed. A rectangle's methods always return well-formed outputs for
+// well-formed inputs.
type Rectangle struct {
Min, Max Point
}
}
}
-// Inset returns the rectangle r inset by n, which may be negative.
+// Inset returns the rectangle r inset by n, which may be negative. If either
+// of r's dimensions is less than 2*n then an empty rectangle near the center
+// of r will be returned.
func (r Rectangle) Inset(n int) Rectangle {
- return Rectangle{
- Point{r.Min.X + n, r.Min.Y + n},
- Point{r.Max.X - n, r.Max.Y - n},
+ if r.Dx() < 2*n {
+ r.Min.X = (r.Min.X + r.Max.X) / 2
+ r.Max.X = r.Min.X
+ } else {
+ r.Min.X += n
+ r.Max.X -= n
+ }
+ if r.Dy() < 2*n {
+ r.Min.Y = (r.Min.Y + r.Max.Y) / 2
+ r.Max.Y = r.Min.Y
+ } else {
+ r.Min.Y += n
+ r.Max.Y -= n
+ }
+ return r
+}
+
+// Intersect returns the largest rectangle contained by both r and s. If the
+// two rectangles do not overlap then the zero rectangle will be returned.
+func (r Rectangle) Intersect(s Rectangle) Rectangle {
+ if r.Min.X < s.Min.X {
+ r.Min.X = s.Min.X
+ }
+ if r.Min.Y < s.Min.Y {
+ r.Min.Y = s.Min.Y
+ }
+ if r.Max.X > s.Max.X {
+ r.Max.X = s.Max.X
+ }
+ if r.Max.Y > s.Max.Y {
+ r.Max.Y = s.Max.Y
}
+ if r.Min.X > r.Max.X || r.Min.Y > r.Max.Y {
+ return ZR
+ }
+ return r
+}
+
+// Union returns the smallest rectangle that contains both r and s.
+func (r Rectangle) Union(s Rectangle) Rectangle {
+ if r.Min.X > s.Min.X {
+ r.Min.X = s.Min.X
+ }
+ if r.Min.Y > s.Min.Y {
+ r.Min.Y = s.Min.Y
+ }
+ if r.Max.X < s.Max.X {
+ r.Max.X = s.Max.X
+ }
+ if r.Max.Y < s.Max.Y {
+ r.Max.Y = s.Max.Y
+ }
+ return r
}
// Empty returns whether the rectangle contains no points.
p.Y >= r.Min.Y && p.Y < r.Max.Y
}
-// Canon returns the canonical version of r. The returned rectangle has
-// minimum and maximum coordinates swapped if necessary so that Min.X <= Max.X
-// and Min.Y <= Max.Y.
+// Canon returns the canonical version of r. The returned rectangle has minimum
+// and maximum coordinates swapped if necessary so that it is well-formed.
func (r Rectangle) Canon() Rectangle {
if r.Max.X < r.Min.X {
r.Min.X, r.Max.X = r.Max.X, r.Min.X