SeparatingAxisTheorem 1.0.7
/**
* Copyright WLAD ( http://wonderfl.net/user/WLAD )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/8Dfp
*/
/// @mxmlc -o bin/SeparatingAxisTheorem.swf -swf-version 20
package
{
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.geom.Point;
import flash.text.TextField;
import flash.text.TextFormat;
/** @author vladik.voina@gmail.com */
[SWF( width="800", height="800", backgroundColor="#000000", frameRate="60" )]
public class SeparatingAxisTheorem extends Sprite
{
public function SeparatingAxisTheorem()
{
// STAGE INIT
stage.scaleMode = "noScale";
stage.align = "tl";
stage.color = 0x0;
graphics.beginFill(0);
graphics.drawRect(0,0,1000,1000);
graphics.endFill();
// VARS
var mp:Vec = new Vec(0,0); // mouse point
var mo:Vec = new Vec(0,0); // old mouse point
var dm:Vec = new Vec(0, 0); // delta mouse
var sz:Vec = new Vec(stage.stageWidth, stage.stageHeight); // stage zise
var sz2:Vec = new Vec(sz.x>>1,sz.y>>1); // half stage zise
var N:Vec; // Rectangle 1 mid point ( position )
var M:Vec; // Rectangle 2 mid point ( position )
var NM:Vec; // vetor position to position
var rmp:Vec; // Vec between rectangles mid points
var A:Vec, B:Vec, C:Vec, D:Vec, E:Vec, F:Vec, G:Vec, H:Vec;// rectangle edges by order
var Ax:Vec, Bx:Vec, Cx:Vec, Dx:Vec, Ex:Vec, Fx:Vec, Gx:Vec, Hx:Vec;// rectangle edges projected on x axis
var Ay:Vec, By:Vec, Cy:Vec, Dy:Vec, Ey:Vec, Fy:Vec, Gy:Vec, Hy:Vec;// rectangle edges projected on y axis
var ld:Boolean = false; // left mouse down
var rd:Boolean = false; // right mouse down
var sd:Boolean = false; // shift key down
var cd:Boolean = false; // ctrl key down
var axisX:Vec; // temp x axis
var axisY:Vec; // temp y axis
// DISPLAY
var r1:Rect = new Rect( 150, 180, 45 );
var r2:Rect = new Rect( 100, 200, 0 );
addChild( r1 ); r1.x = 100; r1.y = 250;
addChild( r2 ); r2.x = 280; r2.y = 300;
Draw.init( this );
// UI
var tp:Vec = new Vec();
var out:Function = function(s:String,c:uint=0x494949):void {
var ts:int = Draw.textSize+4;
Draw.altTarget = graphics; Draw.textCenter = false;
Draw.text(s,tp,c); tp.y += ts;
Draw.altTarget = null; Draw.textCenter = true;
};
out('SeparatingAxisTheorem 1.0.7');
out('Use left or/and right mouse to move objects');
out('Holding Shift & L/R mouse will scale object');
out('Holding Ctrl & L/R mouse will rotate object');
out('Middle mouse to enter fullscreen');
// EVENTS
var mouse:Function = function( e:* ):void {
if ( e.type == 'middleMouseDown' ) stage.displayState = 'fullScreen';
else if ( e.type == 'mouseDown' ) ld = true;
else if ( e.type == 'mouseUp' ) ld = false;
else if ( e.type == 'rightMouseDown' ) rd = true;
else if ( e.type == 'rightMouseUp' ) rd = false;
sd = e.shiftKey;cd = e.ctrlKey;
};stage.addEventListener('mouseDown', mouse );
stage.addEventListener('mouseUp', mouse );
stage.addEventListener('rightMouseUp', mouse );
stage.addEventListener('rightMouseDown', mouse );
stage.addEventListener('middleMouseDown', mouse );
stage.addEventListener('enterFrame', function( e:* ):void {
dm = new Vec( mouseX - mp.x, mouseY - mp.y );
mo = mp.clone();
mp = new Vec( mouseX, mouseY );
if ( ld ) {
if ( cd ) r1.rotation += mp.clone().subtract(N).degrees - mo.clone().subtract(N).degrees;
else if ( sd ) r1.grow(dm.x, dm.y);
else r1.offset( dm.x, dm.y );
}
if ( rd ) {
if ( cd ) r2.rotation += mp.clone().subtract(M).degrees - mo.clone().subtract(M).degrees;
else if ( sd ) r2.grow(dm.x, dm.y);
else r2.offset( dm.x, dm.y );
}
// READ VALUES
var collision:Boolean = true;
N = r1.pos; M = r2.pos;
A = r1.UL; B = r1.UR;
C = r1.LR; D = r1.LL;
E = r2.UL; F = r2.UR;
G = r2.LR; H = r2.LL;
var V1:Vector.<Vec> = new <Vec>[ A,B,C,D ];
var V2:Vector.<Vec> = new <Vec>[ E,F,G,H ];
/////////////////////////////////////////
/////////////////////////////////////////
// SeparatingAxisTheorem BEGIN
/////////////////////////////////////////
/////////////////////////////////////////
axisX = B.copy.subtract( A );
axisY = D.copy.subtract( A );
if ( areAxisSeparated( V1, V2, axisX ) ) collision = false;
if ( areAxisSeparated( V1, V2, axisY ) ) collision = false;
axisX = F.clone().subtract( E );
axisY = H.clone().subtract( E );
if ( areAxisSeparated( V1, V2, axisX ) ) collision = false;
if ( areAxisSeparated( V1, V2, axisY ) ) collision = false;
/////////////////////////////////////////
/////////////////////////////////////////
// SeparatingAxisTheorem END
/////////////////////////////////////////
/////////////////////////////////////////
// DRAW CALCULATION
rmp = Vec.P2V(
Point.interpolate(
N.toPoint(),
M.toPoint(),
0.5
)
);
axisX = B.copy.subtract( A );
axisY = D.copy.subtract( A );
Ex = axisX.projection( E.copy.subtract( A ) );
Fx = axisX.projection( F.copy.subtract( A ) );
Gx = axisX.projection( G.copy.subtract( A ) );
Hx = axisX.projection( H.copy.subtract( A ) );
Ey = axisY.projection( E.copy.subtract( A ) );
Fy = axisY.projection( F.copy.subtract( A ) );
Gy = axisY.projection( G.copy.subtract( A ) );
Hy = axisY.projection( H.copy.subtract( A ) );
axisX = F.clone().subtract( E );
axisY = H.clone().subtract( E );
Ax = axisX.projection( A.copy.subtract( E ) );
Bx = axisX.projection( B.copy.subtract( E ) );
Cx = axisX.projection( C.copy.subtract( E ) );
Dx = axisX.projection( D.copy.subtract( E ) );
Ay = axisY.projection( A.copy.subtract( E ) );
By = axisY.projection( B.copy.subtract( E ) );
Cy = axisY.projection( C.copy.subtract( E ) );
Dy = axisY.projection( D.copy.subtract( E ) );
Draw.clear();
Draw.line( A, B, 0x484A49, 2 );
Draw.line( A, D, 0x484A49, 2 );
Draw.line( E, F, 0x484A49, 2 );
Draw.line( E, H, 0x484A49, 2 );
Draw.point( Ex.copy.add( A ), 0xFA1CFF, 2 );
Draw.point( Fx.copy.add( A ), 0xFA1CFF, 2 );
Draw.point( Gx.copy.add( A ), 0xFA1CFF, 2 );
Draw.point( Hx.copy.add( A ), 0xFA1CFF, 2 );
Draw.point( Ey.copy.add( A ), 0xFA1CFF, 2 );
Draw.point( Fy.copy.add( A ), 0xFA1CFF, 2 );
Draw.point( Gy.copy.add( A ), 0xFA1CFF, 2 );
Draw.point( Hy.copy.add( A ), 0xFA1CFF, 2 );
Draw.point( Ax.copy.add( E ), 0xFAFF1C, 2 );
Draw.point( Bx.copy.add( E ), 0xFAFF1C, 2 );
Draw.point( Cx.copy.add( E ), 0xFAFF1C, 2 );
Draw.point( Dx.copy.add( E ), 0xFAFF1C, 2 );
Draw.point( Ay.copy.add( E ), 0xFAFF1C, 2 );
Draw.point( By.copy.add( E ), 0xFAFF1C, 2 );
Draw.point( Cy.copy.add( E ), 0xFAFF1C, 2 );
Draw.point( Dy.copy.add( E ), 0xFAFF1C, 2 );
Draw.text( "A", A, collision ? 0xFF0000 : 0xFAFF1C );
Draw.text( "B", B, collision ? 0xFF0000 : 0xFAFF1C );
Draw.text( "C", C, collision ? 0xFF0000 : 0xFAFF1C );
Draw.text( "D", D, collision ? 0xFF0000 : 0xFAFF1C );
Draw.text( "E", E, collision ? 0xFF0000 : 0xFA1CFF );
Draw.text( "F", F, collision ? 0xFF0000 : 0xFA1CFF );
Draw.text( "G", G, collision ? 0xFF0000 : 0xFA1CFF );
Draw.text( "H", H, collision ? 0xFF0000 : 0xFA1CFF );
Draw.text( "N", N, collision ? 0xFF0000 : 0x6AD8E3 );
Draw.text( "M", M, collision ? 0xFF0000 : 0x6AD8E3 );
Draw.point( rmp, 0x1E79E8 );
});
}
/// return vec were vec.x is Min and vec.y is Max
static public function vvMM( vv:Vector.<Vec>, axis:Vec ):Vec
{
var minProj:Number = vv[0].dot(axis); var minDot:int = 1;
var maxProj:Number = vv[0].dot(axis); var maxDot:int = 1;
for ( var i:int = 1; i < 4; i++ )
{
var proj:Number = vv[ i ].dot(axis);
if (minProj > proj) { minProj = proj; minDot = i; }
if (maxProj < proj) { maxProj = proj; maxDot = i; }
}
return new Vec( minProj, maxProj );
}
static public function areAxisSeparated( v1:Vector.<Vec>, v2:Vector.<Vec>, axis:Vec ):Boolean
{
var V1:Vec = vvMM( v1, axis );
var V2:Vec = vvMM( v2, axis );
return V2.y < V1.x || V1.y < V2.x;
}
}
}
var r2d:Number = 180 / Math.PI;
var d2r:Number = Math.PI / 180
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFormat;
class Vec {
// Example: http://wonderfl.net/c/4Zft
static public function PerpendicularProjection( V:Vec, U:Vec ):Vec
{return U.clone().subtract( V.projection( U ) );}
// Convert a line between two points into a vector
static public function PP2V(a:Vec, b:Vec):Vec{return new Vec(b.x - a.x, b.y - a.y);}
// Conver point to vector
static public function P2V(p:Point):Vec{return new Vec(p.x,p.y);}
// Angle between two vectors in radians
static public function RadiansBetween( A:Vec, B:Vec):Number{return Math.acos( A.clone().normalise().dot( B.clone().normalise() ) );}
static public function zero():Vec{return new Vec();}
// @author http://wonderfl.net/user/Vladik
public var x:Number, y:Number;
public function Vec(x:Number = 0, y:Number = 0){this.x = x;this.y = y;}
public function get length():Number{return Math.sqrt(x * x + y * y);}
public function set length(value:Number):void{length == 0 ? scale(0) : scale(value / length);}
public function set degrees(value:Number):void{radians = value * d2r;}
public function get degrees():Number{return radians * r2d;}
public function set radians(value:Number):void{var f:Number = length;x = Math.cos(value) * f;y = Math.sin(value) * f;}
public function scale(n:Number):Vec { x *= n; y *= n; return this; }
public function clone():Vec { return new Vec(x, y); }
public function get copy():Vec { return clone(); }
public function get radians():Number { return Math.atan2(y, x); }
public function dot( v:Vec ):Number { return x * v.x + y * v.y; }
public function get point():Vec { return new Vec(x, y); }
//Projection method credits: http://jccc-mpg.wikidot.com/vector-projection
public function projection( u:Vec ):Vec{var l:Number = length;return clone().scale( u.dot( this ) / (l * l) );}
public function normalise():Vec{scale(1);return this;}
//public function distToVec( p:Vec ):Number {return p.length * Math.sin( Vec.RadiansBetween( this, Vec.P2V(p) ) );}
public function render( g:Graphics, o:Vec = null, color:uint = 0xFF0000, thikness:uint = 3 ):void
{if (o == null) o = new Vec(); g.beginFill( color ); g.lineStyle( thikness, color); g.moveTo( o.x, o.y); var B:Vec = new Vec(o.x + x, o.y + y); g.lineTo( B.x, B.y);
var v:Vec = Vec.PP2V( o, B );v.length = 7;v.degrees -= 45 + 180;g.lineTo( B.x + v.x, B.y + v.y );
v.degrees += 90;g.lineTo( B.x + v.x, B.y + v.y );g.lineTo( B.x, B.y );g.endFill();}
public function add( V:Vec ):Vec{this.x += V.x;this.y += V.y;return this;}
public function subtract(V:Vec):Vec { this.x -= V.x; this.y -= V.y; return this; }
public function toString():String{return "["+x.toFixed(2)+","+y.toFixed(2)+"]";}
public function toPoint():Point{return new Point(x, y);}
public function rotate(rad:Number):Vec { radians += rad; return this; }
public function invert():Vec { var t:Number = x; x = -y; y = -t; return this; }
}
class Rect extends Sprite
{
private var w:Number = 0;
private var h:Number = 0;
private var w2:Number = 0;
private var h2:Number = 0;
private var _rect:Rectangle;
public function Rect( w:Number, h:Number, r:Number ):void
{
rotation = r;
grow( w, h );
}
public function grow( dw:Number, dh:Number ):void
{
w += dw;
h += dh;
w2 = w * .5;
h2 = h * .5;
render();
}
public function offset( dx:Number, dy:Number ):void
{
x += dx;
y += dy;
}
private function render():void
{
graphics.clear();
graphics.lineStyle(5, 0x7E7E7E, 1 , true);
graphics.drawRect( -w / 2, -h / 2, w, h );
}
public function get pos():Vec { return new Vec( x, y ); }
public function get points():Vector.<Vec>
{
return Vector.<Vec>([ LL, LR, UL, UR ]);
}
public function get angle():Number { return rotation * d2r; }
public function get UL():Vec { return new Vec( -w2, -h2 ).rotate( angle ).add( pos ); }
public function get LL():Vec { return new Vec( -w2, h2 ).rotate( angle ).add( pos ); }
public function get UR():Vec { return new Vec( w2, -h2 ).rotate( angle ).add( pos ); }
public function get LR():Vec { return new Vec( w2, h2 ).rotate( angle ).add( pos ); }
}
class Draw
{
static public var textCenter:Boolean = true;
static public var altTarget:Graphics = null;
static public var textSize:int = 18;
static public var canvas:Shape;
static public function get g():Graphics { return altTarget ? altTarget : canvas.graphics; }
static private var tf:TextField;
static public function init( s:Sprite ):void
{
tf = new TextField();
tf.autoSize = 'left';
tf.multiline = false;
tf.wordWrap = false;
tf.defaultTextFormat = new TextFormat('Arial', textSize, false);
canvas = new Shape();
s.addChild( canvas );
}
static public function clear():void { g.clear(); }
static public function circle( p:Vec, radius:Number, color:uint = 0xFF0000, thikness:Number = 1 ):void
{
g.lineStyle( thikness, color );
g.drawCircle( p.x, p.y, radius );
}
static public function point( p:Vec, color:uint = 0xFF0000, size:Number = 5 ):void
{
g.lineStyle();
g.beginFill( color );
g.drawCircle( p.x, p.y , size );
g.endFill();
}
static public function points( v:Vector.<Vec>, color:uint = 0xFF0000, size:Number = 5 ):void
{
for ( var i:int = 0; i < v.length; i++) point( v[i], color, size );
}
static public function line( a:Vec, b:Vec, color:uint = 0xFF0000, thikness:Number = 1 ):void
{
g.lineStyle( thikness, color );
var l:Vec = b.copy.subtract( a );
l.length = 2000 ;
g.moveTo( a.x - l.x, a.y - l.y );
g.lineTo( a.x + l.x, a.y + l.y );
}
static public function lineH( y:Number, color:uint = 0xFF0000, thikness:Number = 1 ):void
{
g.lineStyle( thikness, color );
g.moveTo( -1000, y );
g.lineTo( 1000, y );
}
static public function lineV( x:Number, color:uint = 0xFF0000, thikness:Number = 1 ):void
{
g.lineStyle( thikness, color );
g.moveTo( x, -1000 );
g.lineTo( x, 1000 );
}
static public function text( s:String, p:Vec, color:uint = 0xFFFFFF ):void
{
tf.htmlText = "<font color='#" + color.toString(16).toUpperCase() + "' >" + s + "</font>";
var bd:BitmapData = new BitmapData(tf.width, tf.height, false, 0xFF000000);bd.draw(tf);
var q:Vec = textCenter ? p.clone().subtract( new Vec( tf.width / 2, tf.height / 2 ) ) : p;
g.lineStyle();
g.beginBitmapFill(bd,new Matrix(1,0,0,1,q.x,q.y));
g.drawRect(q.x, q.y, tf.width, tf.height);
g.endFill();
}
}