diff --git a/shape.go b/shape.go index df6f799..0624bac 100755 --- a/shape.go +++ b/shape.go @@ -671,16 +671,46 @@ func (cp *ConvexPolygon) calculateMTV(contactSet *ContactSet, otherShape IShape) } + // If the direction from target to source points opposite to the separation, invert the separation vector. + if cp.Center().Sub(other.Center()).Dot(smallest) < 0 { + smallest = smallest.Invert() + } + case *Circle: verts := append([]Vector{}, cp.Transformed()...) - // The center point of a contact could also be closer than the verts, particularly if we're testing from a Circle to another Shape. - verts = append(verts, contactSet.Center) center := other.position sort.Slice(verts, func(i, j int) bool { return verts[i].Sub(center).Magnitude() < verts[j].Sub(center).Magnitude() }) - smallest = Vector{center.X - verts[0].X, center.Y - verts[0].Y} - smallest = smallest.Unit().Scale(smallest.Magnitude() - other.radius) + axis := Vector{center.X - verts[0].X, center.Y - verts[0].Y} + pa := cp.Project(axis) + pb := other.Project(axis) + overlap := pa.Overlap(pb) + if overlap <= 0 { + return Vector{}, false + } + smallest = axis.Unit().Scale(overlap) + + for _, axis := range cp.SATAxes() { + pa := cp.Project(axis) + pb := other.Project(axis) + + overlap := pa.Overlap(pb) + + if overlap <= 0 { + return Vector{}, false + } + + if smallest.Magnitude() > overlap { + smallest = axis.Scale(overlap) + } + + } + + // If the direction from target to source points opposite to the separation, invert the separation vector + if cp.Center().Sub(other.position).Dot(smallest) < 0 { + smallest = smallest.Invert() + } } @@ -830,6 +860,21 @@ func (circle *Circle) Bounds() (Vector, Vector) { return Vector{circle.position.X - circle.radius, circle.position.Y - circle.radius}, Vector{circle.position.X + circle.radius, circle.position.Y + circle.radius} } +func (circle *Circle) Project(axis Vector) Projection { + axis = axis.Unit() + projectedCenter := axis.Dot(circle.position) + projectedRadius := axis.Magnitude() * circle.radius * circle.scale + + min := projectedCenter - projectedRadius + max := projectedCenter + projectedRadius + + if min > max { + min, max = max, min + } + + return Projection{min, max} +} + // Intersection tests to see if a Circle intersects with the other given Shape. dx and dy are delta movement variables indicating // movement to be applied before the intersection check (thereby allowing you to see if a Shape would collide with another if it // were in a different relative location). If an Intersection is found, a ContactSet will be returned, giving information regarding @@ -1188,7 +1233,7 @@ func (projection Projection) Overlapping(other Projection) bool { // Overlap returns the amount that a Projection is overlapping with the other, provided Projection. Credit to https://dyn4j.org/2010/01/sat/#sat-nointer func (projection Projection) Overlap(other Projection) float64 { - return math.Min(projection.Max, other.Max) - math.Max(projection.Min, other.Min) + return math.Min(projection.Max - other.Min, other.Max - projection.Min) } // IsInside returns whether the Projection is wholly inside of the other, provided Projection.