From 4c6d061455e299aa3ff116c7888ab3860d9aa489 Mon Sep 17 00:00:00 2001 From: Ben Cragg 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) + } + }) + } +}