From 4c6d061455e299aa3ff116c7888ab3860d9aa489 Mon Sep 17 00:00:00 2001
From: Ben Cragg <bcvery1@gmail.com>
Date: Tue, 29 Jan 2019 11:20:21 +0000
Subject: [PATCH] added rect-circle intersection functions

---
 geometry.go      | 32 ++++++++++++++++++++++
 geometry_test.go | 70 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 102 insertions(+)

diff --git a/geometry.go b/geometry.go
index 55fbf53..b19e902 100644
--- a/geometry.go
+++ b/geometry.go
@@ -318,6 +318,16 @@ func (r Rect) Intersect(s Rect) Rect {
 	return t
 }
 
+// IntersectsCircle returns whether the Circle and the Rect intersect.
+//
+// This function will return true if:
+//  - The Rect contains the Circle, partially or fully
+//  - The Circle contains the Rect, partially of fully
+//  - An edge of the Rect is a tangent to the Circle
+func (r Rect) IntersectsCircle(c Circle) bool {
+	return c.IntersectsRect(r)
+}
+
 // Circle is a 2D circle. It is defined by two properties:
 //  - Radius float64
 //  - Center vector
@@ -470,6 +480,28 @@ func (c Circle) Intersect(d Circle) Circle {
 	}
 }
 
+// IntersectsRect returns whether the Circle and the Rect intersect.
+//
+// This function will return true if:
+//  - The Rect contains the Circle, partially or fully
+//  - The Circle contains the Rect, partially of fully
+//  - An edge of the Rect is a tangent to the Circle
+func (c Circle) IntersectsRect(r Rect) bool {
+	// Checks if the c.Center is not in the diagonal quadrants of the rectangle
+	var grownR Rect
+	if (r.Min.X <= c.Center.X && c.Center.X <= r.Max.X) || (r.Min.Y <= c.Center.Y && c.Center.Y <= r.Max.Y) {
+		// 'grow' the Rect by c.Radius in each diagonal
+		grownR = Rect{
+			Min: r.Min.Sub(V(c.Radius, c.Radius)),
+			Max: r.Max.Add(V(c.Radius, c.Radius)),
+		}
+
+		return grownR.Contains(c.Center)
+	}
+	// The center is in the diagonal quadrants
+	return c.Center.To(r.Min).Len() <= c.Radius || c.Center.To(r.Max).Len() <= c.Radius
+}
+
 // Matrix is a 2x3 affine matrix that can be used for all kinds of spatial transforms, such
 // as movement, scaling and rotations.
 //
diff --git a/geometry_test.go b/geometry_test.go
index cda1dc6..a3aa5f8 100644
--- a/geometry_test.go
+++ b/geometry_test.go
@@ -576,3 +576,73 @@ func TestCircle_Intersect(t *testing.T) {
 		})
 	}
 }
+
+func TestRect_IntersectsCircle(t *testing.T) {
+	type fields struct {
+		Min pixel.Vec
+		Max pixel.Vec
+	}
+	type args struct {
+		c pixel.Circle
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		args   args
+		want   bool
+	}{
+		{
+			name:   "Rect.IntersectsCircle(): no overlap",
+			fields: fields{Min: pixel.V(0, 0), Max: pixel.V(10, 10)},
+			args:   args{c: pixel.C(1, pixel.V(50, 50))},
+			want:   false,
+		},
+		{
+			name:   "Rect.IntersectsCircle(): circle contains rect",
+			fields: fields{Min: pixel.V(0, 0), Max: pixel.V(10, 10)},
+			args:   args{c: pixel.C(10, pixel.V(5, 5))},
+			want:   true,
+		},
+		{
+			name:   "Rect.IntersectsCircle(): rect contains circle",
+			fields: fields{Min: pixel.V(0, 0), Max: pixel.V(10, 10)},
+			args:   args{c: pixel.C(1, pixel.V(5, 5))},
+			want:   true,
+		},
+		{
+			name:   "Rect.IntersectsCircle(): circle overlaps one corner",
+			fields: fields{Min: pixel.V(0, 0), Max: pixel.V(10, 10)},
+			args:   args{c: pixel.C(1, pixel.V(0, 0))},
+			want:   true,
+		},
+		{
+			name:   "Rect.IntersectsCircle(): circle overlaps two corner",
+			fields: fields{Min: pixel.V(0, 0), Max: pixel.V(10, 10)},
+			args:   args{c: pixel.C(11, pixel.V(0, 5))},
+			want:   true,
+		},
+		{
+			name:   "Rect.IntersectsCircle(): circle overlaps one edge",
+			fields: fields{Min: pixel.V(0, 0), Max: pixel.V(10, 10)},
+			args:   args{c: pixel.C(1, pixel.V(0, 5))},
+			want:   true,
+		},
+		{
+			name:   "Rect.IntersectsCircle(): edge is tangent",
+			fields: fields{Min: pixel.V(0, 0), Max: pixel.V(10, 10)},
+			args:   args{c: pixel.C(1, pixel.V(-1, 5))},
+			want:   true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := pixel.Rect{
+				Min: tt.fields.Min,
+				Max: tt.fields.Max,
+			}
+			if got := r.IntersectsCircle(tt.args.c); got != tt.want {
+				t.Errorf("Rect.IntersectsCircle() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
-- 
GitLab