wip
This commit is contained in:
parent
0a0c8ff110
commit
ab70533793
84
geometry.go
84
geometry.go
|
@ -318,14 +318,15 @@ func (r Rect) Intersect(s Rect) Rect {
|
|||
return t
|
||||
}
|
||||
|
||||
// IntersectsCircle returns whether the Circle and the Rect intersect.
|
||||
// IntersectsCircle returns a minimal required Vector, such that moving the circle by that vector would stop the Circle
|
||||
// and the Rect intersecting. This function returns a zero-vector if the Circle and Rect do not overlap, and if only
|
||||
// the perimeters touch.
|
||||
//
|
||||
// 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)
|
||||
func (r Rect) IntersectsCircle(c Circle) Vec {
|
||||
return c.IntersectsRect(r).Scaled(-1)
|
||||
}
|
||||
|
||||
// Circle is a 2D circle. It is defined by two properties:
|
||||
|
@ -475,24 +476,79 @@ func (c Circle) Intersect(d Circle) Circle {
|
|||
}
|
||||
}
|
||||
|
||||
// IntersectsRect returns whether the Circle and the Rect intersect.
|
||||
// IntersectsRect returns a minimal required Vector, such that moving the circle by that vector would stop the Circle
|
||||
// and the Rect intersecting. This function returns a zero-vector if the Circle and Rect do not overlap, and if only
|
||||
// the perimeters touch.
|
||||
//
|
||||
// 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 {
|
||||
func (c Circle) IntersectsRect(r Rect) Vec {
|
||||
// h and v will hold the minimum horizontal and vertical distances (respectively) to avoid overlapping
|
||||
var h, v float64
|
||||
|
||||
// Checks if the c.Center is not in the diagonal quadrants of the rectangle
|
||||
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 orthagonal
|
||||
return Rect{
|
||||
Min: r.Min.Sub(V(c.Radius, c.Radius)),
|
||||
Max: r.Max.Add(V(c.Radius, c.Radius)),
|
||||
}.Contains(c.Center)
|
||||
grown := Rect{Min: r.Min.Sub(V(c.Radius, c.Radius)), Max: r.Max.Add(V(c.Radius, c.Radius))}
|
||||
if !grown.Contains(c.Center) {
|
||||
// c.Center not close enough to overlap, return zero-vector
|
||||
return ZV
|
||||
}
|
||||
|
||||
// Get minimum distance to travel out of Rect
|
||||
rToC := r.Center().To(c.Center)
|
||||
h = c.Radius - math.Abs(rToC.X) + (r.W() / 2)
|
||||
v = c.Radius - math.Abs(rToC.Y) + (r.H() / 2)
|
||||
|
||||
if rToC.X < 0 {
|
||||
h = -h
|
||||
}
|
||||
if rToC.Y < 0 {
|
||||
v = -v
|
||||
}
|
||||
} else {
|
||||
// The center is in the diagonal quadrants
|
||||
if c.Center.To(r.Min).Len() <= c.Radius {
|
||||
// Closest to bottom-left
|
||||
cornerToCenter := r.Min.To(c.Center)
|
||||
// Get the horizontal and vertical overlaps
|
||||
h = c.Radius - math.Sqrt(math.Pow(c.Radius, 2)-math.Pow(cornerToCenter.Y, 2))
|
||||
v = -1 * (c.Radius + math.Sqrt(math.Pow(c.Radius, 2)-math.Pow(cornerToCenter.X, 2)))
|
||||
}
|
||||
if c.Center.To(r.Max).Len() <= c.Radius {
|
||||
// Closest to top-right
|
||||
cornerToCenter := r.Max.To(c.Center)
|
||||
// Get the horizontal and vertical overlaps
|
||||
h = c.Radius - math.Sqrt(math.Pow(c.Radius, 2)-math.Pow(cornerToCenter.Y, 2))
|
||||
v = c.Radius - math.Sqrt(math.Pow(c.Radius, 2)-math.Pow(cornerToCenter.X, 2))
|
||||
}
|
||||
if c.Center.To(V(r.Min.X, r.Max.Y)).Len() <= c.Radius {
|
||||
// Closest to top-left
|
||||
cornerToCenter := V(r.Min.X, r.Max.Y).To(c.Center)
|
||||
// Get the horizontal and vertical overlaps
|
||||
h = -1 * (c.Radius + math.Sqrt(math.Pow(c.Radius, 2)-math.Pow(cornerToCenter.Y, 2)))
|
||||
v = c.Radius - math.Sqrt(math.Pow(c.Radius, 2)-math.Pow(cornerToCenter.X, 2))
|
||||
}
|
||||
if c.Center.To(V(r.Max.X, r.Min.Y)).Len() <= c.Radius {
|
||||
// Closest to bottom-right
|
||||
cornerToCenter := V(r.Max.X, r.Min.Y).To(c.Center)
|
||||
// Get the horizontal and vertical overlaps
|
||||
h = -1 * (c.Radius + math.Sqrt(math.Pow(c.Radius, 2)-math.Pow(cornerToCenter.Y, 2)))
|
||||
v = -1 * (c.Radius + math.Sqrt(math.Pow(c.Radius, 2)-math.Pow(cornerToCenter.X, 2)))
|
||||
}
|
||||
}
|
||||
// The center is in the diagonal quadrants
|
||||
return c.Center.To(r.Min).Len() <= c.Radius || c.Center.To(r.Max).Len() <= c.Radius ||
|
||||
c.Center.To(V(r.Min.X, r.Max.Y)).Len() <= c.Radius || c.Center.To(V(r.Max.X, r.Min.Y)).Len() <= c.Radius
|
||||
|
||||
// No intersect
|
||||
if h == 0 && v == 0 {
|
||||
return ZV
|
||||
}
|
||||
|
||||
if math.Abs(h) > math.Abs(v) {
|
||||
// Vertical distance shorter
|
||||
return V(0, v)
|
||||
}
|
||||
return V(h, 0)
|
||||
}
|
||||
|
||||
// Matrix is a 2x3 affine matrix that can be used for all kinds of spatial transforms, such
|
||||
|
|
|
@ -553,55 +553,85 @@ func TestRect_IntersectsCircle(t *testing.T) {
|
|||
name string
|
||||
fields fields
|
||||
args args
|
||||
want bool
|
||||
want pixel.Vec
|
||||
}{
|
||||
{
|
||||
name: "Rect.IntersectsCircle(): no overlap",
|
||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||
args: args{c: pixel.C(pixel.V(50, 50), 1)},
|
||||
want: false,
|
||||
want: pixel.ZV,
|
||||
},
|
||||
{
|
||||
name: "Rect.IntersectsCircle(): circle contains rect",
|
||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||
args: args{c: pixel.C(pixel.V(5, 5), 10)},
|
||||
want: true,
|
||||
want: pixel.V(-15, 0),
|
||||
},
|
||||
{
|
||||
name: "Rect.IntersectsCircle(): rect contains circle",
|
||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||
args: args{c: pixel.C(pixel.V(5, 5), 1)},
|
||||
want: true,
|
||||
want: pixel.V(-6, 0),
|
||||
},
|
||||
{
|
||||
name: "Rect.IntersectsCircle(): circle overlaps bottom-left corner",
|
||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||
args: args{c: pixel.C(pixel.V(-.5, -.5), 1)},
|
||||
want: true,
|
||||
args: args{c: pixel.C(pixel.V(0, 0), 1)},
|
||||
want: pixel.V(1, 0),
|
||||
},
|
||||
{
|
||||
name: "Rect.IntersectsCircle(): circle overlaps top-left corner",
|
||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||
args: args{c: pixel.C(pixel.V(-.5, 10.5), 1)},
|
||||
want: true,
|
||||
args: args{c: pixel.C(pixel.V(0, 10), 1)},
|
||||
want: pixel.V(1, 0),
|
||||
},
|
||||
{
|
||||
name: "Rect.IntersectsCircle(): circle overlaps bottom-right corner",
|
||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||
args: args{c: pixel.C(pixel.V(10, 0), 1)},
|
||||
want: pixel.V(-1, 0),
|
||||
},
|
||||
{
|
||||
name: "Rect.IntersectsCircle(): circle overlaps top-right corner",
|
||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||
args: args{c: pixel.C(pixel.V(10, 10), 1)},
|
||||
want: pixel.V(-1, 0),
|
||||
},
|
||||
{
|
||||
name: "Rect.IntersectsCircle(): circle overlaps two corners",
|
||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||
args: args{c: pixel.C(pixel.V(0, 5), 6)},
|
||||
want: true,
|
||||
want: pixel.V(6, 0),
|
||||
},
|
||||
{
|
||||
name: "Rect.IntersectsCircle(): circle overlaps one edge",
|
||||
name: "Rect.IntersectsCircle(): circle overlaps left edge",
|
||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||
args: args{c: pixel.C(pixel.V(0, 5), 1)},
|
||||
want: true,
|
||||
want: pixel.V(1, 0),
|
||||
},
|
||||
{
|
||||
name: "Rect.IntersectsCircle(): circle overlaps bottom edge",
|
||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||
args: args{c: pixel.C(pixel.V(5, 0), 1)},
|
||||
want: pixel.V(0, 1),
|
||||
},
|
||||
{
|
||||
name: "Rect.IntersectsCircle(): circle overlaps right edge",
|
||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||
args: args{c: pixel.C(pixel.V(10, 5), 1)},
|
||||
want: pixel.V(-1, 0),
|
||||
},
|
||||
{
|
||||
name: "Rect.IntersectsCircle(): circle overlaps top edge",
|
||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||
args: args{c: pixel.C(pixel.V(5, 10), 1)},
|
||||
want: pixel.V(0, -1),
|
||||
},
|
||||
{
|
||||
name: "Rect.IntersectsCircle(): edge is tangent",
|
||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||
args: args{c: pixel.C(pixel.V(-1, 5), 1)},
|
||||
want: true,
|
||||
want: pixel.ZV,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
|
Loading…
Reference in New Issue