Tag Archives: actionscript

Line Intersection Part 2 (optimized)

Actionscript:
  1. // optimized line to line intersection test:
  2. // from wikipedia: http://en.wikipedia.org/wiki/Line-line_intersection
  3. // and http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
  4. var ip:Point = new Point();
  5.  
  6. function intersection(p1:Sprite, p2:Sprite, p3:Sprite, p4:Sprite):Point {
  7.     var nx:Number, ny:Number, dn:Number;
  8.     var x4_x3:Number = p4.x - p3.x;
  9.     var y4_y3:Number = p4.y - p3.y;
  10.     var x2_x1:Number = p2.x - p1.x;
  11.     var y2_y1:Number = p2.y - p1.y;
  12.     var x1_x3:Number = p1.x - p3.x;
  13.     var y1_y3:Number = p1.y - p3.y;
  14.    
  15.     nx = x4_x3 * y1_y3 - y4_y3 * x1_x3;
  16.     ny = x2_x1 * y1_y3 - y2_y1 * x1_x3;
  17.     dn = y4_y3 * x2_x1 - x4_x3 * y2_y1;
  18.    
  19.     nx /= dn;
  20.     ny /= dn;
  21.    
  22.     // has intersection
  23.     if(nx>= 0 && nx <= 1 && ny>= 0 && ny <= 1){
  24.         ny = p1.y + nx * y2_y1;
  25.         nx = p1.x + nx * x2_x1;
  26.         ip.x = nx;
  27.         ip.y = ny;
  28.     }else{
  29.         // no intersection
  30.         ip.x = ip.y = -1000;
  31.     }
  32.     return ip
  33. }
  34.  
  35.  
  36. //
  37. // test out the function:
  38. //
  39.  
  40. stage.frameRate = 30;
  41. var a:Sprite = dot();
  42. a.x = a.y = 100;
  43.  
  44. var b:Sprite = dot();
  45. b.x = b.y = 200;
  46.  
  47. var c:Sprite = dot();
  48. c.x = 200
  49. c.y = 100;
  50.  
  51. var d:Sprite = dot();
  52. d.x = 100
  53. d.y = 200;
  54.  
  55. var inter:Sprite = dot(0xFF0000,true);
  56.  
  57. addEventListener(Event.ENTER_FRAME, onLoop);
  58. function onLoop(evt:Event):void {
  59.     var p:Point = intersection(a, b, c, d);
  60.     inter.x = p.x;
  61.     inter.y = p.y
  62.    
  63.     with(graphics){
  64.         clear();
  65.         lineStyle(0,0x000000);
  66.         moveTo(a.x, a.y);
  67.         lineTo(b.x, b.y);
  68.         moveTo(c.x, c.y);
  69.         lineTo(d.x,d.y);
  70.     }
  71. }
  72.  
  73. // draggable dot
  74. function dot(col:uint = 0x507399, noDrag:Boolean = false):Sprite {
  75.     var s:Sprite = Sprite(addChild(new Sprite));
  76.     with(s.graphics) beginFill(col), drawCircle(0,0,5);
  77.     if (!noDrag){
  78.         s.buttonMode = true;
  79.        s.addEventListener(MouseEvent.MOUSE_DOWN, onDrag);
  80.     }
  81.     return s;
  82. }
  83. function onDrag(evt:MouseEvent):void {
  84.     evt.currentTarget.startDrag()
  85. }
  86.  
  87. stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
  88. function onUp(evt:MouseEvent):void{
  89.     stopDrag();
  90. }

This is a significantly optimized version of yesterdays post. The function intersection() takes four sprites as its arguments. The four arguments could be considered starting points and ending points for two line segments.

The bottleneck in yesterdays post looked something like this;

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);

nx and ny are the numerators for two fractions both with dn as their denominator.... after taking a look at this page:

http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/

We can simplify things a bit before we start wrapping repetitive math operations up in variables:

Actionscript:
  1. nx = (p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x);
  2. // ny = (p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x);
  3. dn = (p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y);
  4. nx /= dn;
  5. // ny /= dn
  6. ny = p1.y + nx * (p2.y - p1.y);
  7. nx = p1.x + nx * (p2.x - p1.x);
  8. return new Point(nx, ny);

It's important to note here that if we didn't intend to calculate line segments we could exclude the initial calculation of ny.

At this point we're ready to get rid of duplicate math operations by wrapping them in variables. In this case this only includes subtraction:

Actionscript:
  1. var x4_x3:Number=p4.x-p3.x;
  2. var y4_y3:Number=p4.y-p3.y;
  3. var x2_x1:Number=p2.x-p1.x;
  4. var y2_y1:Number=p2.y-p1.y;
  5. var x1_x3:Number=p1.x-p3.x;
  6. var y1_y3:Number=p1.y-p3.y;
  7.  
  8. nx=x4_x3*y1_y3-y4_y3*x1_x3;
  9. ny=x2_x1*y1_y3-y2_y1*x1_x3;
  10. dn=y4_y3*x2_x1-x4_x3*y2_y1;
  11.  
  12. nx/=dn;
  13. ny/=dn;
  14.  
  15. ny=p1.y+nx*y2_y1;
  16. nx=p1.x+nx*x2_x1;
  17.  
  18. return new Point(nx, ny);

After wrapping repetitive math into variables the only optimization left to be made is related to the instantiation of a new Point(). Instantiation of objects is an expensive process, so it's better to define the point once outside of the intersection() function definition.

In the end our function looks like this:

Actionscript:
  1. var ip:Point = new Point();
  2.  
  3. function intersection(p1:Sprite, p2:Sprite, p3:Sprite, p4:Sprite):Point {
  4.     var nx:Number, ny:Number, dn:Number;
  5.     var x4_x3:Number = p4.x - p3.x;
  6.     var pre2:Number = p4.y - p3.y;
  7.     var pre3:Number = p2.x - p1.x;
  8.     var pre4:Number = p2.y - p1.y;
  9.     var pre5:Number = p1.y - p3.y;
  10.     var pre6:Number = p1.x - p3.x;
  11.     nx = x4_x3 * pre5 - pre2 * pre6;
  12.     ny = pre3 * pre5 - pre4 * pre6;
  13.     dn = pre2 * pre3 - x4_x3 * pre4;
  14.     nx /= dn;
  15.     ny /= dn;
  16.     // has intersection
  17.     if(nx>= 0 && nx <= 1 && ny>= 0 && ny <= 1){
  18.         ny = p1.y + nx * pre4;
  19.         nx = p1.x + nx * pre3;
  20.         ip.x = nx;
  21.         ip.y = ny;
  22.     }else{
  23.         // no intersection
  24.         ip.x = ip.y = -1000;
  25.     }
  26.     return ip
  27. }

The final version contains a conditional that checks the end values of nx and ny, if they are between 0 and 1 then we know that there is an intersection between the segments.

Note: Optimized code usually breaks some best practices... as best practices are usually geared toward code flexibility and readability... and not speed.

Posted in Math, misc | Also tagged | Leave a comment

Line Intersection Part 1 (unoptimized)

Actionscript:
  1. // completely unoptimized line to line intersection test:
  2. // from wikipedia: http://en.wikipedia.org/wiki/Line-line_intersection
  3. function intersection(p1:Sprite, p2:Sprite, p3:Sprite, p4:Sprite):Point {
  4.     var nx:Number, ny:Number, dn:Number;
  5.     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);
  6.     dn = (p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x);
  7.     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);
  8.     return new Point(nx / dn, ny / dn);
  9. }
  10.  
  11. //
  12. // test out the function:
  13. //
  14.  
  15. stage.frameRate = 30;
  16. var a:Sprite = dot();
  17. a.x = a.y = 100;
  18.  
  19. var b:Sprite = dot();
  20. b.x = b.y = 200;
  21.  
  22. var c:Sprite = dot();
  23. c.x = 200
  24. c.y = 100;
  25.  
  26. var d:Sprite = dot();
  27. d.x = 100
  28. d.y = 200;
  29.  
  30. var inter:Sprite = dot(0xFF0000,true);
  31.  
  32. addEventListener(Event.ENTER_FRAME, onLoop);
  33. function onLoop(evt:Event):void {
  34.     var p:Point = intersection(a, b, c, d);
  35.     inter.x = p.x;
  36.     inter.y = p.y
  37.    
  38.     with(graphics){
  39.         clear();
  40.         lineStyle(0,0x000000);
  41.         moveTo(a.x, a.y);
  42.         lineTo(b.x, b.y);
  43.         moveTo(c.x, c.y);
  44.         lineTo(d.x,d.y);
  45.     }
  46. }
  47.  
  48. // draggable dot
  49. function dot(col:uint = 0x507399, noDrag:Boolean = false):Sprite {
  50.     var s:Sprite = Sprite(addChild(new Sprite));
  51.     with(s.graphics) beginFill(col), drawCircle(0,0,5);
  52.     if (!noDrag){
  53.         s.buttonMode = true;
  54.        s.addEventListener(MouseEvent.MOUSE_DOWN, onDrag);
  55.     }
  56.     return s;
  57. }
  58. function onDrag(evt:MouseEvent):void {
  59.     evt.currentTarget.startDrag()
  60. }
  61.  
  62. stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
  63. function onUp(evt:MouseEvent):void{
  64.     stopDrag();
  65. }

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...

Posted in Math, misc | Also tagged | Leave a comment

Bresenham line

Actionscript:
  1. var canvas:BitmapData = Bitmap(addChild(new Bitmap(new BitmapData(400,400, false, 0x000000)))).bitmapData;
  2.  
  3. drawLine(10,10,100,90, 0xFF0000);
  4. drawLine(100,90,60,80, 0xFF0000);
  5. drawLine(100,90,95,60, 0xFF0000);
  6.    
  7. for (var i:int = 0; i<100; i+=1){
  8.     drawLine(i *4, 100 + i, 200, 390);
  9. }
  10. // code ported from here:
  11. // http://www.edepot.com/linebenchmark.html
  12. function drawLine(x1:int, y1:int, x2:int, y2:int, col:uint = 0xFFFFFF){
  13.     var x:int, y:int;
  14.     var dx:int, dy:int;
  15.     var incx:int , incy:int
  16.     var balance:int;
  17.  
  18.     if (x2>= x1){
  19.         dx = x2 - x1;
  20.         incx = 1;
  21.     }else{
  22.         dx = x1 - x2;
  23.         incx = -1;
  24.     }
  25.  
  26.     if (y2>= y1){
  27.         dy = y2 - y1;
  28.         incy = 1;
  29.     }else{
  30.         dy = y1 - y2;
  31.         incy = -1;
  32.     }
  33.  
  34.     x = x1;
  35.     y = y1;
  36.  
  37.     if (dx>= dy){
  38.         dy <<= 1;
  39.         balance = dy - dx;
  40.         dx <<= 1;
  41.  
  42.         while (x != x2){
  43.             canvas.setPixel(x, y, col);
  44.             if (balance>= 0){
  45.                 y += incy;
  46.                 balance -= dx;
  47.             }
  48.             balance += dy;
  49.             x += incx;
  50.         }
  51.         canvas.setPixel(x, y, col);
  52.     }else{
  53.         dx <<= 1;
  54.         balance = dx - dy;
  55.         dy <<= 1;
  56.  
  57.         while (y != y2){
  58.             canvas.setPixel(x, y, col);
  59.             if (balance>= 0){
  60.                 x += incx;
  61.                 balance -= dy;
  62.             }
  63.             balance += dx;
  64.             y += incy;
  65.         }
  66.         canvas.setPixel(x, y, col);
  67.     }
  68. }

This snippet shows Brensenham's line drawing algorithm. I ported this implementation from here... all the line algorithms in that link are easy to port to actionscript. I've messed with them all at some point.

Tomorrow I'm going to post a super slow line drawing algorithm... so I figured I'd post a fast line drawing algorithm today.

Posted in BitmapData, graphics algorithms, setPixel | Also tagged | Leave a comment

Harmonic Series

Actionscript:
  1. var harmonic:Number = 0;
  2. var k:Number = 1;
  3. addEventListener(Event.ENTER_FRAME, onLoop);
  4. function onLoop(evt:Event):void {
  5.      harmonic += 1 / k;
  6.      trace("+");
  7.      trace("1 / " + k + "              =              " + harmonic);
  8.      k+=1;
  9. }

Read more about the harmonic series at wikipedia.


1 / 1 = 1
+
1 / 2 = 1.5
+
1 / 3 = 1.8333333333333333
+
1 / 4 = 2.083333333333333
+
...

Posted in Math | Also tagged , | Leave a comment