// File "SAT.as"
package
{
import Box2D.Common.Math.b2Vec2;
import flash.display.*;
import flash.geom.*;
import flash.events.*;
import flash.utils.Timer;
/**
* SAT.as
* Created On: 12/03/2012 17:46
*/
[SWF(backgroundColor="0x000000", frameRate="60", width="800", height="600")]
public class SAT extends Sprite
{
private static var square:Vector.<b2Vec2>;
private static var canvas:Sprite;
private var A:Shape2D;
private var B:Shape2D;
public function SAT()
{
// SPRITE TO DRAW SHAPES TO:
canvas = new Sprite();
// SHAPE OF A:
A = new Shape2D(Vector.<b2Vec2>([
new b2Vec2( -100, -100),
new b2Vec2( -100, 100),
new b2Vec2( 100, 100),
new b2Vec2( 100, -100)]), new b2Vec2(550, 300), 45);
// SHAPE OF B:
B = new Shape2D(Vector.<b2Vec2>([
new b2Vec2( -200, -50),
new b2Vec2( -200, 50),
new b2Vec2( 200, 50),
new b2Vec2( 200, -50)]), new b2Vec2(250, 300), 175);
// ADD CANVAS TO STAGE:
stage.addChild(canvas);
// DRAW A & B:
A.draw(canvas, false);
B.draw(canvas, false);
collides(A, B);
/*
// TIMER THAT TRIGGERS FUNCTION 'ROTATE' EVERY 10MS:
var t:Timer = new Timer(10);
t.addEventListener(TimerEvent.TIMER, rotate);
t.start();
*/
}
public function rotate(e:TimerEvent):void
{
// INCREMENT ANGLES:
A.angle++;
B.angle++;
// CLEAR GRAPHICS:
canvas.graphics.clear();
// FILL SHAPE IF IT'S CURRENTLY COLLIDING:
if (collides(A, B))
{
A.draw(canvas, true);
B.draw(canvas, true);
} else {
A.draw(canvas, false);
B.draw(canvas, false);
}
}
public function collides(A:Shape2D, B:Shape2D):Boolean
{
var test1:Number; // numbers to use to test for overlap
var test2:Number;
var testNum:Number; // number to test if its the new max/min
var min1:Number; // current smallest(shape 1)
var max1:Number; // current largest(shape 1)
var min2:Number; // current smallest(shape 2)
var max2:Number; // current largest(shape 2)
var axis:b2Vec2; // the normal axis for projection
var offset:Number;
var vectorOffset:b2Vec2;
var vectors1:Vector.<b2Vec2>; // the points
var vectors2:Vector.<b2Vec2>; // the points
vectors1 = A.getOBB().concat(); // these functions are in my polygon class, all they do is return a Vector.<b2Vec2> of the vertices of the polygon
vectors2 = B.getOBB().concat();
/* SHOULDN'T NEED THIS AS BOTH VECTORS CONSIST OF 4 OBJECTS.
* ALSO B2VEC2 DOESN'T HAVE A TRUNCATE FUNCTION.
*
// add a little padding to make the test work correctly
if (vectors1.length == 2)
{
var temp:b2Vec2 = new b2Vec2(-(vectors1[1].y - vectors1[0].y), vectors1[1].x - vectors1[0].x);
temp.truncate(0.0000000001);
vectors1.push(vectors1[1].add(temp));
}
if (vectors2.length == 2)
{
temp = new b2Vec2(-(vectors2[1].y - vectors2[0].y), vectors2[1].x - vectors2[0].x);
temp.truncate(0.0000000001);
vectors2.push(vectors2[1].add(temp));
}
*/
// find vertical offset
vectorOffset = new b2Vec2(A.position.x - B.position.x, A.position.y - B.position.y);
// loop to begin projection
for (var i:int = 0; i < vectors1.length; i++)
{
// get the normal axis, and begin projection
axis = findNormalAxis(vectors1, i);
trace("AXIS:", axis.x, axis.y);
// project polygon1
min1 = dotProduct(axis, vectors1[0]);
max1 = min1;//set max and min equal
for (var j:int = 1; j < vectors1.length; j++)
{
testNum = dotProduct(axis, vectors1[j]);// project each point
if (testNum < min1) min1 = testNum; // test for new smallest
if (testNum > max1) max1 = testNum; // test for new largest
}
trace("MIN1:", min1);
trace("MAX1:", max1);
// project polygon2
min2 = dotProduct(axis, vectors2[0]);
max2 = min2; // set 2's max and min
for (j = 1; j < vectors2.length; j++)
{
testNum = dotProduct(axis, vectors2[j]);// project the point
if (testNum < min2) min2 = testNum; // test for new min
if (testNum > max2) max2 = testNum; // test for new max
}
trace("MIN2:", min2);
trace("MAX2:", max2);
// apply the offset to each max/min(no need for each point, max and min are all that matter)
offset = dotProduct(axis, vectorOffset);// calculate offset
min1 += offset; // apply offset
max1 += offset; // apply offset
trace("MIN1OFFSET:", min1);
trace("MAX1OFFSET:", max1);
// and test if they are touching
test1 = min1 - max2; // test min1 and max2
test2 = min2 - max1; // test min2 and max1
trace("TEST1:", test1);
trace("TEST2:", test2);
trace("TEST1 > 0 || TEST2 > 0:", test1 > 0 || test2 > 0);
if (test1 > 0 || test2 > 0)
{ //if they are greater than 0, there is a gap
trace("RETURN: FALSE");
return false;//just quit
}
}
//if you're here, there is a collision
var seperation:b2Vec2 = new b2Vec2(axis.x*((max2-min1)*-1), axis.y*((max2-min1)*-1)); //return the separation, apply it to a polygon to separate the two shapes.
return true;
}
private function dotProduct(vecA_:b2Vec2, vecB_:b2Vec2):Number
{
return (vecA_.x * vecB_.x + vecA_.y * vecB_.y);
}
private function findNormalAxis(verts:Vector.<b2Vec2>, index:int):b2Vec2
{
var vector1:b2Vec2 = verts[index];
var vector2:b2Vec2 = (index >= verts.length - 1) ? verts[0] : verts[index + 1]; //make sure you get a real vertex, not one that is outside the length of the vector.
var normalAxis:b2Vec2 = new b2Vec2( -(vector2.y - vector1.y), vector2.x - vector1.x);//take the two vertices, make a line out of them, and find the normal of the line
normalAxis.Normalize(); //normalize the line(set its length to 1)
return normalAxis;
}
}
}