wip
This commit is contained in:
parent
ba4f417559
commit
8650692efa
82
geometry.go
82
geometry.go
|
@ -310,14 +310,15 @@ func (r Rect) Intersect(s Rect) Rect {
|
||||||
return t
|
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:
|
// This function will return true if:
|
||||||
// - The Rect contains the Circle, partially or fully
|
// - The Rect contains the Circle, partially or fully
|
||||||
// - The Circle contains the Rect, partially of 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) Vec {
|
||||||
func (r Rect) IntersectsCircle(c Circle) bool {
|
return c.IntersectsRect(r).Scaled(-1)
|
||||||
return c.IntersectsRect(r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Circle is a 2D circle. It is defined by two properties:
|
// Circle is a 2D circle. It is defined by two properties:
|
||||||
|
@ -467,24 +468,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:
|
// This function will return true if:
|
||||||
// - The Rect contains the Circle, partially or fully
|
// - The Rect contains the Circle, partially or fully
|
||||||
// - The Circle contains the Rect, partially of 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) Vec {
|
||||||
func (c Circle) IntersectsRect(r Rect) bool {
|
// 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
|
// 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) {
|
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
|
// 'grow' the Rect by c.Radius in each orthagonal
|
||||||
return Rect{
|
grown := Rect{Min: r.Min.Sub(V(c.Radius, c.Radius)), Max: r.Max.Add(V(c.Radius, c.Radius))}
|
||||||
Min: r.Min.Sub(V(c.Radius, c.Radius)),
|
if !grown.Contains(c.Center) {
|
||||||
Max: r.Max.Add(V(c.Radius, c.Radius)),
|
// c.Center not close enough to overlap, return zero-vector
|
||||||
}.Contains(c.Center)
|
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
|
// The center is in the diagonal quadrants
|
||||||
return c.Center.To(r.Min).Len() <= c.Radius || c.Center.To(r.Max).Len() <= c.Radius ||
|
if c.Center.To(r.Min).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
|
// 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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
// Matrix is a 2x3 affine matrix that can be used for all kinds of spatial transforms, such
|
||||||
|
|
|
@ -469,55 +469,85 @@ func TestRect_IntersectsCircle(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
fields fields
|
fields fields
|
||||||
args args
|
args args
|
||||||
want bool
|
want pixel.Vec
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Rect.IntersectsCircle(): no overlap",
|
name: "Rect.IntersectsCircle(): no overlap",
|
||||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||||
args: args{c: pixel.C(pixel.V(50, 50), 1)},
|
args: args{c: pixel.C(pixel.V(50, 50), 1)},
|
||||||
want: false,
|
want: pixel.ZV,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Rect.IntersectsCircle(): circle contains rect",
|
name: "Rect.IntersectsCircle(): circle contains rect",
|
||||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||||
args: args{c: pixel.C(pixel.V(5, 5), 10)},
|
args: args{c: pixel.C(pixel.V(5, 5), 10)},
|
||||||
want: true,
|
want: pixel.V(-15, 0),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Rect.IntersectsCircle(): rect contains circle",
|
name: "Rect.IntersectsCircle(): rect contains circle",
|
||||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||||
args: args{c: pixel.C(pixel.V(5, 5), 1)},
|
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",
|
name: "Rect.IntersectsCircle(): circle overlaps bottom-left corner",
|
||||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||||
args: args{c: pixel.C(pixel.V(-.5, -.5), 1)},
|
args: args{c: pixel.C(pixel.V(0, 0), 1)},
|
||||||
want: true,
|
want: pixel.V(1, 0),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Rect.IntersectsCircle(): circle overlaps top-left corner",
|
name: "Rect.IntersectsCircle(): circle overlaps top-left corner",
|
||||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||||
args: args{c: pixel.C(pixel.V(-.5, 10.5), 1)},
|
args: args{c: pixel.C(pixel.V(0, 10), 1)},
|
||||||
want: true,
|
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",
|
name: "Rect.IntersectsCircle(): circle overlaps two corners",
|
||||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||||
args: args{c: pixel.C(pixel.V(0, 5), 6)},
|
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)},
|
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||||
args: args{c: pixel.C(pixel.V(0, 5), 1)},
|
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",
|
name: "Rect.IntersectsCircle(): edge is tangent",
|
||||||
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
fields: fields{Min: pixel.ZV, Max: pixel.V(10, 10)},
|
||||||
args: args{c: pixel.C(pixel.V(-1, 5), 1)},
|
args: args{c: pixel.C(pixel.V(-1, 5), 1)},
|
||||||
want: true,
|
want: pixel.ZV,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
Loading…
Reference in New Issue