-
Notifications
You must be signed in to change notification settings - Fork 0
/
RotatedRectangle.cs
213 lines (188 loc) · 9.11 KB
/
RotatedRectangle.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
namespace RedMan
{
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 / 2, (int)theRectangle.Height/2);
}
/// <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>
/// 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="theRectangle"></param>
/// <returns></returns>
public bool Intersects(Rectangle theRectangle)
{
return Intersects(new RotatedRectangle(theRectangle, 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>();
aRectangleAxis.Add(UpperRightCorner() - UpperLeftCorner());
aRectangleAxis.Add(UpperRightCorner() - LowerRightCorner());
aRectangleAxis.Add(theRectangle.UpperLeftCorner() - theRectangle.LowerLeftCorner());
aRectangleAxis.Add(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
foreach (Vector2 aAxis in aRectangleAxis)
{
if (!IsAxisCollision(theRectangle, aAxis))
{
return false;
}
}
return true;
}
/// <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>();
aRectangleAScalars.Add(GenerateScalar(theRectangle.UpperLeftCorner(), aAxis));
aRectangleAScalars.Add(GenerateScalar(theRectangle.UpperRightCorner(), aAxis));
aRectangleAScalars.Add(GenerateScalar(theRectangle.LowerLeftCorner(), aAxis));
aRectangleAScalars.Add(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>();
aRectangleBScalars.Add(GenerateScalar(UpperLeftCorner(), aAxis));
aRectangleBScalars.Add(GenerateScalar(UpperRightCorner(), aAxis));
aRectangleBScalars.Add(GenerateScalar(LowerLeftCorner(), aAxis));
aRectangleBScalars.Add(GenerateScalar(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="theRectangleCorner"></param>
/// <param name="theAxis"></param>
/// <returns></returns>
private int GenerateScalar(Vector2 theRectangleCorner, Vector2 theAxis)
{
//Using the formula for Vector projection. Take the corner being passed in
//and project it onto the given Axis
float aNumerator = (theRectangleCorner.X * theAxis.X) + (theRectangleCorner.Y * theAxis.Y);
float aDenominator = (theAxis.X * theAxis.X) + (theAxis.Y * theAxis.Y);
float aDivisionResult = aNumerator / aDenominator;
Vector2 aCornerProjected = new Vector2(aDivisionResult * theAxis.X, aDivisionResult * theAxis.Y);
//Now that we have our projected Vector, calculate a scalar of that projection
//that can be used to more easily do comparisons
float aScalar = (theAxis.X * aCornerProjected.X) + (theAxis.Y * aCornerProjected.Y);
return (int)aScalar;
}
/// <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();
aTranslatedPoint.X = (float)(theOrigin.X + (thePoint.X - theOrigin.X) * Math.Cos(theRotation)
- (thePoint.Y - theOrigin.Y) * Math.Sin(theRotation));
aTranslatedPoint.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(CollisionRectangle.Left, CollisionRectangle.Top);
aUpperLeft = RotatePoint(aUpperLeft, aUpperLeft + Origin, Rotation);
return aUpperLeft;
}
public Vector2 UpperRightCorner()
{
Vector2 aUpperRight = new Vector2(CollisionRectangle.Right, CollisionRectangle.Top);
aUpperRight = RotatePoint(aUpperRight, aUpperRight + new Vector2(-Origin.X, Origin.Y), Rotation);
return aUpperRight;
}
public Vector2 LowerLeftCorner()
{
Vector2 aLowerLeft = new Vector2(CollisionRectangle.Left, CollisionRectangle.Bottom);
aLowerLeft = RotatePoint(aLowerLeft, aLowerLeft + new Vector2(Origin.X, -Origin.Y), Rotation);
return aLowerLeft;
}
public Vector2 LowerRightCorner()
{
Vector2 aLowerRight = new Vector2(CollisionRectangle.Right, CollisionRectangle.Bottom);
aLowerRight = RotatePoint(aLowerRight, aLowerRight + new Vector2(-Origin.X, -Origin.Y), Rotation);
return aLowerRight;
}
public int X
{
get { return CollisionRectangle.X; }
}
public int Y
{
get { return CollisionRectangle.Y; }
}
public int Width
{
get { return CollisionRectangle.Width; }
}
public int Height
{
get { return CollisionRectangle.Height; }
}
}
}