mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: f70a282d078d1fee3858ab7cd10952d5613cfe6c Former-commit-id: 07e613cc7879308b9475ee3a8582a9b5db3ff121 Former-commit-id: 73fdea9a14385d10b74a0abea0ece717170ec960af/merge-core
8 changed files with 343 additions and 70 deletions
@ -0,0 +1,219 @@ |
|||||
|
namespace ImageProcessor |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Numerics; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A rotated rectangle. Adapted from the excellent sample. TODO: Refactor into a struct.
|
||||
|
/// <see href="http://www.xnadevelopment.com/tutorials/rotatedrectanglecollisions/rotatedrectanglecollisions.shtml"/>
|
||||
|
/// </summary>
|
||||
|
public class RotatedRectangle |
||||
|
{ |
||||
|
public Rectangle CollisionRectangle; |
||||
|
public float Rotation; |
||||
|
public Vector2 Origin; |
||||
|
|
||||
|
public RotatedRectangle(Rectangle theRectangle, float theInitialRotation) |
||||
|
{ |
||||
|
CollisionRectangle = theRectangle; |
||||
|
Rotation = theInitialRotation; |
||||
|
|
||||
|
//Calculate the Rectangles origin. We assume the center of the Rectangle will
|
||||
|
//be the point that we will be rotating around and we use that for the origin
|
||||
|
Origin = new Vector2((int)theRectangle.Width / 2f, (int)theRectangle.Height / 2f); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Used for changing the X and Y position of the RotatedRectangle
|
||||
|
/// </summary>
|
||||
|
/// <param name="theXPositionAdjustment"></param>
|
||||
|
/// <param name="theYPositionAdjustment"></param>
|
||||
|
public void ChangePosition(int theXPositionAdjustment, int theYPositionAdjustment) |
||||
|
{ |
||||
|
CollisionRectangle.X += theXPositionAdjustment; |
||||
|
CollisionRectangle.Y += theYPositionAdjustment; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Determines if the specfied point is contained within the rectangular region defined by
|
||||
|
/// this <see cref="RotatedRectangle"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">The x-coordinate of the given point.</param>
|
||||
|
/// <param name="y">The y-coordinate of the given point.</param>
|
||||
|
/// <returns>The <see cref="bool"/></returns>
|
||||
|
public bool Contains(int x, int y) |
||||
|
{ |
||||
|
Rectangle rectangle = new Rectangle(x, y, 1, 1); |
||||
|
return this.Intersects(new RotatedRectangle(rectangle, 0.0f)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// This intersects method can be used to check a standard XNA framework Rectangle
|
||||
|
/// object and see if it collides with a Rotated Rectangle object
|
||||
|
/// </summary>
|
||||
|
/// <param name="rectangle"></param>
|
||||
|
/// <returns></returns>
|
||||
|
public bool Intersects(Rectangle rectangle) |
||||
|
{ |
||||
|
return this.Intersects(new RotatedRectangle(rectangle, 0.0f)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Check to see if two Rotated Rectangls have collided
|
||||
|
/// </summary>
|
||||
|
/// <param name="theRectangle"></param>
|
||||
|
/// <returns></returns>
|
||||
|
public bool Intersects(RotatedRectangle theRectangle) |
||||
|
{ |
||||
|
//Calculate the Axis we will use to determine if a collision has occurred
|
||||
|
//Since the objects are rectangles, we only have to generate 4 Axis (2 for
|
||||
|
//each rectangle) since we know the other 2 on a rectangle are parallel.
|
||||
|
List<Vector2> aRectangleAxis = new List<Vector2> |
||||
|
{ |
||||
|
this.UpperRightCorner() - this.UpperLeftCorner(), |
||||
|
this.UpperRightCorner() - this.LowerRightCorner(), |
||||
|
theRectangle.UpperLeftCorner() - theRectangle.LowerLeftCorner(), |
||||
|
theRectangle.UpperLeftCorner() - theRectangle.UpperRightCorner() |
||||
|
}; |
||||
|
|
||||
|
//Cycle through all of the Axis we need to check. If a collision does not occur
|
||||
|
//on ALL of the Axis, then a collision is NOT occurring. We can then exit out
|
||||
|
//immediately and notify the calling function that no collision was detected. If
|
||||
|
//a collision DOES occur on ALL of the Axis, then there is a collision occurring
|
||||
|
//between the rotated rectangles. We know this to be true by the Seperating Axis Theorem
|
||||
|
return aRectangleAxis.All(aAxis => this.IsAxisCollision(theRectangle, aAxis)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Determines if a collision has occurred on an Axis of one of the
|
||||
|
/// planes parallel to the Rectangle
|
||||
|
/// </summary>
|
||||
|
/// <param name="theRectangle"></param>
|
||||
|
/// <param name="aAxis"></param>
|
||||
|
/// <returns></returns>
|
||||
|
private bool IsAxisCollision(RotatedRectangle theRectangle, Vector2 aAxis) |
||||
|
{ |
||||
|
//Project the corners of the Rectangle we are checking on to the Axis and
|
||||
|
//get a scalar value of that project we can then use for comparison
|
||||
|
List<int> aRectangleAScalars = new List<int> |
||||
|
{ |
||||
|
this.GenerateScalar(theRectangle.UpperLeftCorner(), aAxis), |
||||
|
this.GenerateScalar(theRectangle.UpperRightCorner(), aAxis), |
||||
|
this.GenerateScalar(theRectangle.LowerLeftCorner(), aAxis), |
||||
|
this.GenerateScalar(theRectangle.LowerRightCorner(), aAxis) |
||||
|
}; |
||||
|
|
||||
|
//Project the corners of the current Rectangle on to the Axis and
|
||||
|
//get a scalar value of that project we can then use for comparison
|
||||
|
List<int> aRectangleBScalars = new List<int> |
||||
|
{ |
||||
|
this.GenerateScalar(this.UpperLeftCorner(), aAxis), |
||||
|
this.GenerateScalar(this.UpperRightCorner(), aAxis), |
||||
|
this.GenerateScalar(this.LowerLeftCorner(), aAxis), |
||||
|
this.GenerateScalar(this.LowerRightCorner(), aAxis) |
||||
|
}; |
||||
|
|
||||
|
//Get the Maximum and Minium Scalar values for each of the Rectangles
|
||||
|
int aRectangleAMinimum = aRectangleAScalars.Min(); |
||||
|
int aRectangleAMaximum = aRectangleAScalars.Max(); |
||||
|
int aRectangleBMinimum = aRectangleBScalars.Min(); |
||||
|
int aRectangleBMaximum = aRectangleBScalars.Max(); |
||||
|
|
||||
|
//If we have overlaps between the Rectangles (i.e. Min of B is less than Max of A)
|
||||
|
//then we are detecting a collision between the rectangles on this Axis
|
||||
|
if (aRectangleBMinimum <= aRectangleAMaximum && aRectangleBMaximum >= aRectangleAMaximum) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
else if (aRectangleAMinimum <= aRectangleBMaximum && aRectangleAMaximum >= aRectangleBMaximum) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Generates a scalar value that can be used to compare where corners of
|
||||
|
/// a rectangle have been projected onto a particular axis.
|
||||
|
/// </summary>
|
||||
|
/// <param name="corner"></param>
|
||||
|
/// <param name="axis"></param>
|
||||
|
/// <returns></returns>
|
||||
|
private int GenerateScalar(Vector2 corner, Vector2 axis) |
||||
|
{ |
||||
|
// Using the formula for Vector projection. Take the corner being passed in
|
||||
|
// and project it onto the given Axis
|
||||
|
float numerator = Vector2.Dot(corner, axis); //(theRectangleCorner.X * theAxis.X) + (theRectangleCorner.Y * theAxis.Y);
|
||||
|
float denominator = Vector2.Dot(axis, axis); //(theAxis.X * theAxis.X) + (theAxis.Y * theAxis.Y);
|
||||
|
float aDivisionResult = numerator / denominator; |
||||
|
|
||||
|
Vector2 projected = new Vector2(aDivisionResult * axis.X, aDivisionResult * axis.Y); |
||||
|
|
||||
|
// Now that we have our projected Vector, calculate a scalar of that projection
|
||||
|
// that can be used to more easily do comparisons
|
||||
|
float scalar = Vector2.Dot(axis, projected); |
||||
|
return (int)scalar; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Rotate a point from a given location and adjust using the Origin we
|
||||
|
/// are rotating around
|
||||
|
/// </summary>
|
||||
|
/// <param name="thePoint"></param>
|
||||
|
/// <param name="theOrigin"></param>
|
||||
|
/// <param name="theRotation"></param>
|
||||
|
/// <returns></returns>
|
||||
|
private Vector2 RotatePoint(Vector2 thePoint, Vector2 theOrigin, float theRotation) |
||||
|
{ |
||||
|
Vector2 aTranslatedPoint = new Vector2 |
||||
|
{ |
||||
|
X = (float)(theOrigin.X |
||||
|
+ (thePoint.X - theOrigin.X) * Math.Cos(theRotation) |
||||
|
- (thePoint.Y - theOrigin.Y) * Math.Sin(theRotation)), |
||||
|
Y = (float)(theOrigin.Y |
||||
|
+ (thePoint.Y - theOrigin.Y) * Math.Cos(theRotation) |
||||
|
+ (thePoint.X - theOrigin.X) * Math.Sin(theRotation)) |
||||
|
}; |
||||
|
return aTranslatedPoint; |
||||
|
} |
||||
|
|
||||
|
public Vector2 UpperLeftCorner() |
||||
|
{ |
||||
|
Vector2 aUpperLeft = new Vector2(this.CollisionRectangle.Left, this.CollisionRectangle.Top); |
||||
|
aUpperLeft = this.RotatePoint(aUpperLeft, aUpperLeft + this.Origin, this.Rotation); |
||||
|
return aUpperLeft; |
||||
|
} |
||||
|
|
||||
|
public Vector2 UpperRightCorner() |
||||
|
{ |
||||
|
Vector2 aUpperRight = new Vector2(this.CollisionRectangle.Right, this.CollisionRectangle.Top); |
||||
|
aUpperRight = this.RotatePoint(aUpperRight, aUpperRight + new Vector2(-this.Origin.X, this.Origin.Y), this.Rotation); |
||||
|
return aUpperRight; |
||||
|
} |
||||
|
|
||||
|
public Vector2 LowerLeftCorner() |
||||
|
{ |
||||
|
Vector2 aLowerLeft = new Vector2(this.CollisionRectangle.Left, this.CollisionRectangle.Bottom); |
||||
|
aLowerLeft = this.RotatePoint(aLowerLeft, aLowerLeft + new Vector2(this.Origin.X, -this.Origin.Y), this.Rotation); |
||||
|
return aLowerLeft; |
||||
|
} |
||||
|
|
||||
|
public Vector2 LowerRightCorner() |
||||
|
{ |
||||
|
Vector2 aLowerRight = new Vector2(this.CollisionRectangle.Right, this.CollisionRectangle.Bottom); |
||||
|
aLowerRight = this.RotatePoint(aLowerRight, aLowerRight + new Vector2(-this.Origin.X, -this.Origin.Y), this.Rotation); |
||||
|
return aLowerRight; |
||||
|
} |
||||
|
|
||||
|
public int X => this.CollisionRectangle.X; |
||||
|
|
||||
|
public int Y => this.CollisionRectangle.Y; |
||||
|
|
||||
|
public int Width => this.CollisionRectangle.Width; |
||||
|
|
||||
|
public int Height => this.CollisionRectangle.Height; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue