By Zevan | March 13, 2009
Actionscript:
-
// completely unoptimized line to line intersection test:
-
// from wikipedia: http://en.wikipedia.org/wiki/Line-line_intersection
-
function intersection(p1:Sprite, p2:Sprite, p3:Sprite, p4:Sprite):Point {
-
var nx:Number, ny:Number, dn:Number;
-
nx = (p1.x * p2.y - p1.y * p2.x) * (p3.x - p4.x) - (p1.x - p2.x) * (p3.x * p4.y - p3.y * p4.x);
-
dn = (p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x);
-
ny = (p1.x * p2.y - p1.y * p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x * p4.y - p3.y * p4.x);
-
return new Point(nx / dn, ny / dn);
-
}
-
-
//
-
// test out the function:
-
//
-
-
stage.frameRate = 30;
-
var a:Sprite = dot();
-
a.x = a.y = 100;
-
-
var b:Sprite = dot();
-
b.x = b.y = 200;
-
-
var c:Sprite = dot();
-
c.x = 200
-
c.y = 100;
-
-
var d:Sprite = dot();
-
d.x = 100
-
d.y = 200;
-
-
var inter:Sprite = dot(0xFF0000,true);
-
-
addEventListener(Event.ENTER_FRAME, onLoop);
-
function onLoop(evt:Event):void {
-
var p:Point = intersection(a, b, c, d);
-
inter.x = p.x;
-
inter.y = p.y
-
-
with(graphics){
-
clear();
-
lineStyle(0,0x000000);
-
moveTo(a.x, a.y);
-
lineTo(b.x, b.y);
-
moveTo(c.x, c.y);
-
lineTo(d.x,d.y);
-
}
-
}
-
-
// draggable dot
-
function dot(col:uint = 0x507399, noDrag:Boolean = false):Sprite {
-
var s:Sprite = Sprite(addChild(new Sprite));
-
with(s.graphics) beginFill(col), drawCircle(0,0,5);
-
if (!noDrag){
-
s.buttonMode = true;
-
s.addEventListener(MouseEvent.MOUSE_DOWN, onDrag);
-
}
-
return s;
-
}
-
function onDrag(evt:MouseEvent):void {
-
evt.currentTarget.startDrag()
-
}
-
-
stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
-
function onUp(evt:MouseEvent):void{
-
stopDrag();
-
}
This snippet shows the first step toward a usable line to line intersection test. While this demo is fully functional, there is some room for optimization within the intersection() function itself...
From Equations to Code
When I go directly from an equation to code I like to try to make the code resemble the equation... test to make sure it works and then add optimizations. I do this to keep things clear for myself and also so that I have a step by step process to show my students.
The optimizations and additional features that I'll add to this function tomorrow will make it look very different from the original equation from wikipedia. Some things that I'll do:
1) tweak the equation to use fewer operators (it has 12 multiplications right now)
2) wrap duplicate mathmatical operations in variables
3) add the ability to test for line segments rather than infinite lines
4) move any object instantiation outside the function
etc...
Also posted in Math | Tagged actionscript, flash |
By Zevan | March 12, 2009
Actionscript:
-
var canvas:BitmapData = Bitmap(addChild(new Bitmap(new BitmapData(400, 400, false, 0x000000)))).bitmapData;
-
-
function line(x1:Number, y1:Number, x2:Number, y2:Number, res:int=10):void{
-
var dx:Number = x2 - x1;
-
var dy:Number = y2 - y1;
-
var dist:Number = Math.sqrt((dx * dx) + (dy * dy));
-
var step:Number = 1 / (dist / res);
-
for (var i:Number = 0; i<=1; i+= step){
-
// lerp : a + (b - a) * f
-
canvas.setPixel(x1 + dx * i, y1 + dy * i, 0xFFFFFF);
-
}
-
}
-
-
addEventListener(Event.ENTER_FRAME, onLoop);
-
function onLoop(evt:Event):void {
-
canvas.fillRect(canvas.rect, 0x000000);
-
line(100 , 100, mouseX, mouseY,1);
-
line(100 + 50, 100, mouseX+ 50, mouseY,5);
-
}
Yesterday I posted an implementation of the Bresenham Line Algorithm. Today I'm posting a comparatively slow way to draw a line with setPixel(). This snippet uses lerp and the Pythagorean theorem. It works nicely for small numbers of lines, its easy to draw dotted lines with it and its easy to explain. In a real app where you needed to use setPixel() to draw a line you should use one of the fast algorithms like Wu or Bresenham.
I didn't originally write this snippet to use set pixel... a few weeks ago I wrote something very similar to calculate a set of x y coords between two given points. In the program I used it in speed wasn't an issue (as I only needed to run the function one time). I've needed this kind of function many times before in games and small apps...
This was the original:
Actionscript:
-
function calculatePoints(x1:Number, y1:Number, x2:Number, y2:Number, res:int=10):Array{
-
var points:Array = new Array();
-
var dx:Number = x2 - x1;
-
var dy:Number = y2 - y1;
-
var dist:Number = Math.sqrt((dx * dx) + (dy * dy));
-
var step:Number = 1 / (dist / res);
-
for (var i:Number = 0; i<=1; i+= step){
-
points.push(new Point(x1 + dx * i, y1 + dy * i));
-
}
-
return points;
-
}
-
-
trace(calculatePoints(0,0,100,0,10));
-
/* outputs:
-
(x=0, y=0),(x=10, y=0),(x=20, y=0),(x=30.000000000000004, y=0),(x=40, y=0),(x=50, y=0),(x=60, y=0),(x=70, y=0),(x=80, y=0),(x=89.99999999999999, y=0),(x=99.99999999999999, y=0)
-
*/
...and another version to allow you to specify the number of points to calculate rather than the pixel interval at which they should be calculated:
Actionscript:
-
function calculatePoints(x1:Number, y1:Number, x2:Number, y2:Number, pointNum:int=10):Array{
-
var points:Array = new Array();
-
var step:Number = 1 / (pointNum + 1);
-
for (var i:Number = 0; i<=1; i+= step){
-
points.push(new Point(x1 + (x2 - x1) * i, y1 + (y2 - y1) * i));
-
}
-
return points;
-
}
-
-
trace(calculatePoints(0,30,30,0,1));
-
/* outputs:
-
(x=0, y=30),(x=15, y=15),(x=30, y=0)
-
*/
This last version isn't perfect, sometimes the pointNum will be off by 1, I may fix that in a future post.
By Zevan | February 27, 2009
Actionscript:
-
function randomize(a:*, b:*):int{
-
return Math.round(Math.random()*8) - 4;
-
}
-
-
var i:int;
-
var fruits:Array;
-
-
trace("Math.random()");
-
for (i = 0; i<4; i++){
-
// reset fruits array:
-
fruits = ["apple", "grape","pear","cherry"];
-
fruits.sort(randomize);
-
trace(fruits);
-
}
-
-
-
// seeds
-
var s1:Number= 0xFF00FF;
-
var s2:Number = 0xCCCCCC;
-
var s3:Number= 0xFF00F0;
-
-
function tRandomize(a:*, b:*):int{
-
return Math.round(rand()*8) - 4;
-
}
-
-
trace("\nTausworthe rand()");
-
for (i= 0; i<4; i++){
-
fruits = ["apple", "grape","pear","cherry"];
-
fruits.sort(tRandomize);
-
trace(fruits);
-
}
-
// from www.ams.org/mcom/1996-65-213/S0025-5718-96-00696-5/S0025-5718-96-00696-5.pdf
-
function rand():Number {
-
s1=((s1&4294967294)<<12)^(((s1<<13)^s1)>>19);
-
s2=((s2&4294967288)<<4)^(((s2<<2)^s2)>>25);
-
s3=((s3&4294967280)<<17)^(((s3<<3)^s3)>>11);
-
var r:Number = (s1^s2^s3) * 2.3283064365e-10;
-
r = (r<0) ? r+=1 : r;
-
return r;
-
}
-
-
/*
-
outputs:
-
Math.random()
-
grape,apple,pear,cherry
-
pear,cherry,apple,grape
-
grape,apple,pear,cherry
-
grape,apple,cherry,pear
-
-
Tausworthe rand()
-
apple,grape,pear,cherry
-
cherry,grape,pear,apple
-
grape,apple,cherry,pear
-
grape,pear,apple,cherry
-
*/
The above shows how to randomly sort or shuffle an array. This is useful in games. To achieve this I made use of the compareFunction argument of Array.sort(). Most sorting algorithms go through the array and compare values until the desired sort order is achieved. The compareFunction argument is a function that takes two values a and b and returns an integer that is negative positive or zero... see this info from the docs:
* A negative return value specifies that A appears before B in the sorted sequence.
* A return value of 0 specifies that A and B have the same sort order.
* A positive return value specifies that A appears after B in the sorted sequence.
So in the case of a randomizing an array you simply need to return a random int -1, 0 or 1. This is what I've done in the past (Math.round()*2) -1) ... but when I was writing this snippet it seemed like 0 caused less variation in the output of the array so I made the range from -4 to 4 instead. This could have just been my imagination, but it seems like having less chance of a zero caused the arrays to be a bit more shuffled.
The reason I also included a version that uses Tausworthe is because of the easy seeding. In some cases you may want to use seeded randomness to sort an array.
UPDATE:
Was digging around about this and found a much faster method for randomizing arrays... not a big deal if you have small arrays, but if you need to randomize 1000's of values this method is much faster than using Array.sort()
Also posted in arrays | Tagged actionscript, flash |
By Zevan | February 24, 2009
Actionscript:
-
var input:String = "a, b, c";
-
-
var words:Array = input.split(", ");
-
var max:String = "";
-
var maxD:String = (words.length - 1).toString();
-
for (var i:int = 0; i<words.length; i++){
-
max += maxD;
-
}
-
var maxInt:int = parseInt(max,words.length);
-
-
for(i = 0; i<=maxInt; i++){
-
var indices:String = i.toString(words.length);
-
var r:String = "";
-
var k:int=0;
-
for (var j:int = 0; j<indices.length; j++){
-
r += words[parseInt(indices.charAt(j))] +" ";
-
k++;
-
}
-
while(k <words.length) {
-
r = words[0] +" "+ r;
-
k++;
-
}
-
trace(r);
-
}
-
trace(i, " variations");
Like many things on this site, I coded this rather quickly and it can probably be cleaned up...
Setting the input variable of the above snippet to "a, b" will output:
a a
a b
b a
b b
4 variations
Setting the input to "a, b, c" will output:
a a a
a a b
a a c
a b a
a b b
a b c
a c a
a c b
a c c
b a a
b a b
b a c
b b a
b b b
b b c
b c a
b c b
b c c
c a a
c a b
c a c
c b a
c b b
c b c
c c a
c c b
c c c
27 variations
I created this to work with words... inputs like "bread, breath, blobs, backwards". Be careful because you can quickly get millions of outputs:
1 to the power of 1 is 1
2 to the power of 2 is 4
3 to the power of 3 is 27
4 to the power of 4 is 256
5 to the power of 5 is 3125
6 to the power of 6 is 46,656
7 to the power of 7 is 823,543
8 to the power of 8 is 16,777,216
etc...