CirclesLoveLines step 5
* @Revision
* - Lets keep the circle on the line!
* - Lets map the collision normal!
* - Lets check for collision of circleX on line AB!
* - Lets project pointX along line AB!
* - Lets Make a point X that is the mouse!
* - Lets project a line segment from lineA to circlaA!
*
* @author Mario Gonzalez
* @see http://onedayitwillmake.com/
* @note: This series of experiments is being created because.
/**
* Copyright onedayitwillmake ( http://wonderfl.net/user/onedayitwillmake )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/slf7
*/
// forked from onedayitwillmake's Step 5
// forked from onedayitwillmake's forked from: forked from: forked from: forked from: forked from: CirclesLoveLines step 1
// forked from onedayitwillmake'sforked from: forked from: forked from: CirclesLoveLines step 1
// forked from onedayitwillmake'sforked from: forked from: forked from: CirclesLoveLines step 1
/**
* CirclesLoveLines step 5
* @Revision
* - Lets keep the circle on the line!
* - Lets map the collision normal!
* - Lets check for collision of circleX on line AB!
* - Lets project pointX along line AB!
* - Lets Make a point X that is the mouse!
* - Lets project a line segment from lineA to circlaA!
*
* @author Mario Gonzalez
* @see http://onedayitwillmake.com/
* @note: This series of experiments is being created because.
*/
package
{
import flash.display.GradientType;
import flash.display.SpreadMethod;
import flash.display.Sprite;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.geom.Matrix;
[SWF(frameRate="60", width="465", height="465", backgroundColor="#00ffcc")]
public class CircleLineSegmentTest extends Sprite
{
private var A :Vector2D; // Point A
private var B :Vector2D; // Point B
//private var V :Vector2D; // Vector AB
private var X :Vector2D; // Point X
//private var W :Vector2D; // Vector AX
//Graphical representation of elements above
private var lineAB :Line // Line segment between points AB
private var circleX :Circle // Circle at Point X
//Visual display of stuff
private var lineAX :Line // Line segment between points AX
private var circleXCollisionNormal :Circle // Point X projected along LineAB
public function CircleLineSegmentTest():void
{
stage.scaleMode = StageScaleMode.NO_SCALE;
addChild(createBG(0x500149, 0x000040));
A = new Vector2D(Math.random() * stage.stageWidth + 20 - 10, Math.random() * stage.stageHeight + 20 - 10);
B = new Vector2D(Math.random() * stage.stageWidth + 20 - 10, Math.random() * stage.stageHeight + 10 - 10);
lineAB = new Line(A, B, 0xff0000);
addChild(lineAB);
lineAX = new Line(new Vector2D(), new Vector2D, 0xffffff);
addChild(lineAX);
// Create circleXonAB
circleX = new Circle(new Vector2D(), 50);
addChild(circleX);
circleXCollisionNormal = new Circle(new Vector2D(), 5, 0xEC7553);
addChild(circleXCollisionNormal);
addEventListener(Event.ENTER_FRAME, loop);
}
private function loop(e:Event):void
{
//Move the circle to where the mouse is
var mouseVector:Vector2D = new Vector2D(stage.mouseX, stage.mouseY);
var p:Vector2D = findClosestPointOnLineSegment(mouseVector, lineAB);
//Place the circle on the line if it's touching it
if(p.distanceRough(mouseVector) <= circleX._radius * circleX._radius)
{
lineAX.A = p;
lineAX.B = circleX._center;
lineAX._isDirty = true;
circleX._center = mouseVector;
var angle:Number = Math.atan2(circleX._center.y - p.y, circleX._center.x - p.x);
var nx:Number = p.x + Math.cos(angle) * (circleX._radius);
var ny:Number = p.y + Math.sin(angle) * (circleX._radius);
circleXCollisionNormal.visible = true;
lineAX.visible = true;
circleX._drawPoint.x = circleXCollisionNormal.x = nx;
circleX._drawPoint.y = circleXCollisionNormal.y = ny;
}
else
{
circleXCollisionNormal.visible = false;
lineAX.visible = false;
circleX._center = circleX._drawPoint = mouseVector;
}
circleX._isDirty = true;
}
/**
* Find T.
* T is Point X (the mouse position), projected along Line AB
* T is a number between -Infinity and Infinity where 0.0 and 1.0 represent A and B respectively.
* Being the case, normally you only want T, clamped to 0.0 - 1.0.
*/
private function findClosestPointOnLineSegment(X:Vector2D, line:Line):Vector2D
{
X = new Vector2D(stage.mouseX, stage.mouseY);
var W:Vector2D = X.minus(line.A);
var t:Number = W.dot(line.V) / line.V.dot(line.V);
return A.plus(line.V.times(clamp(0,1,t)))
}
/**
* Creates the gradient background used in the experiment
*/
private function createBG(topColor:uint, bottomColor:uint):Sprite
{
var matr:Matrix = new Matrix();
matr.createGradientBox(465, 465, Math.PI / 2, 0, 0);
var bg:Sprite = new Sprite();
bg.graphics.beginGradientFill
(
GradientType.LINEAR,
[topColor, bottomColor], //colors
[1, 1], //alphas
[0, 255], //ratios
matr, //matrix
SpreadMethod.PAD
);
bg.graphics.drawRect(0, 0, 465, 465);
bg.graphics.endFill();
return bg;
}
/**
* Clamps a numerical value between, min and max.
* @param min Lowest value
* @param max Highest value
* @param val Test against
* @return
*
*/
public static function clamp(min:Number, max:Number, val:Number):Number
{
return Math.max(min, Math.min(max, val))
}
}
}
internal class Line extends Shape
{
public var A:Vector2D, B:Vector2D; //A |----| B
public var V:Vector2D; // A |-->--| B - V is the direction of line AB
//Draw
public var _color :uint = 0x666666;
public var __isDirty :Boolean = false;
public function Line(a:Vector2D, b:Vector2D, color:uint = 0x666666):void
{
A = a;
B = b;
V = B.minus(A);
_color = color;
_isDirty = true;
}
public function draw():void
{
resetGraphics();
graphics.moveTo(A.x, A.y);
graphics.lineTo(B.x, B.y);
}
public function resetGraphics():void
{
graphics.clear();
graphics.lineStyle(2, _color, 0.75);
}
public function get _isDirty():Boolean { return __isDirty };
public function set _isDirty(value:Boolean):void
{
__isDirty = value;
draw();
}
}
internal class Circle extends Sprite
{
public var _center :Vector2D; //Where we REALLY are
public var _drawPoint :Vector2D; //Where we draw ourselves
public var _radius :int;
//Draw
private var __isDirty :Boolean = false;
public function Circle(center:Vector2D, radius:int, color:uint = 0xB8F1FE):void
{
_center = center;
_drawPoint = center;
_radius = radius;
//Draw
graphics.beginFill(color, 0.5)
graphics.drawCircle(0, 0, _radius * 0.25);
graphics.drawCircle(0, 0, _radius * 0.8);
graphics.drawCircle(0, 0, _radius);
graphics.endFill();
}
public function set _isDirty(value:Boolean):void
{
if(value == true)
{
x = _drawPoint.x;
y = _drawPoint.y
}
}
}
import flash.geom.Point;
import flash.display.Shape;
import flash.display.Sprite;
internal class Vector2D
{
public var x:Number, y:Number
public function Vector2D(x1:Number = 0, y1:Number = 0):void
{
x = x1;
y = y1;
}
public function minus(from:Vector2D):Vector2D
{
return new Vector2D(x - from.x, y - from.y);
}
public function plus(from:Vector2D):Vector2D
{
return new Vector2D(x + from.x, y + from.y);
}
public function times(z:Number):Vector2D
{
return new Vector2D(x * z, y * z);
}
//proj(w,v) = dot(w,v) / dot(v,v)
public function project(from:Vector2D):Number
{
return dot(from) / from.dot(from);
}
// w.x * v.x + w.y * v.y
public function dot(from:Vector2D):Number
{
return x * from.x + y * from.y;
}
public function get length():Number
{
return Math.sqrt((x * x) + (y * y));
}
public function distance(from:Vector2D):Number
{
return Math.sqrt((x - from.x) * (x - from.x) + (y - from.y) * (y - from.y))
}
public function distanceRough(from:Vector2D):Number
{
return (x - from.x) * (x - from.x) + (y - from.y) * (y - from.y);
}
}