Monthly Archives: March 2009

Torus 3D

Actionscript:
  1. stage.frameRate = 30;
  2. const TWO_PI:Number = Math.PI * 2;
  3. var centerX:Number = 200;
  4. var centerY:Number = 200;
  5. var p:Array = new Array();
  6. var zpos:Number;
  7. var xpos:Number;
  8. var ypos:Number;
  9. var depth:Number;
  10. var canvas:BitmapData = new BitmapData(400,400,true,0xFF000000);
  11. addChild(new Bitmap(canvas));
  12.  
  13. var dy:Number = 0
  14. var dx:Number = 0;
  15.  
  16. addEventListener(Event.ENTER_FRAME, onLoop);
  17. function onLoop(evt:Event):void {
  18.     canvas.fillRect(canvas.rect, 0xFF000000);
  19.      
  20.      dx += (mouseX / 100 - dx)/12;
  21.      dy += (mouseY / 100 - dy)/12;
  22.      var xp:Number, yp:Number, zp:Number;
  23.      
  24.      canvas.lock();
  25.      for (var a:Number =0; a <TWO_PI; a+=.08){
  26.           for (var b:Number =0; b <TWO_PI; b+=.08){
  27.               xp = (70 + 40 * Math.cos(a)) * Math.cos(b) ;
  28.               yp = (70 + 40 * Math.cos(a)) * Math.sin(b);
  29.               zp =  40 * Math.sin(a);
  30.               calc3D(xp, yp, zp, dx, dy);
  31.               convert3D();
  32.               canvas.setPixel(p[0], p[1], 0xFFFFFF);
  33.           }
  34.      }
  35.      canvas.unlock();
  36. }
  37. function calc3D(px:Number, py:Number, pz:Number, rotX:Number=0, rotY:Number=0):void {
  38.     // I first learned this from code by Andries Odendaal - www.wireframe.co.za
  39.     zpos=pz*Math.cos(rotX)-px*Math.sin(rotX) ;
  40.     xpos=pz*Math.sin(rotX)+px*Math.cos(rotX) ;
  41.     ypos=py*Math.cos(rotY)-zpos*Math.sin(rotY) ;
  42.     zpos=py*Math.sin(rotY)+zpos*Math.cos(rotY);
  43. }
  44. function convert3D():void {
  45.     depth = 1/((zpos/200)+1);
  46.     p[0] =  xpos * depth + centerX;
  47.     p[1] =  ypos * depth + centerY;
  48. }

This code draws a rotating 3D torus using setPixel().

Posted in 3D, BitmapData | Tagged , | Leave a comment

Flashvars

Actionscript:
  1. // variable is passed into the swf via Javascript (most likely swfObject)
  2. // myswf.swf?myFlashVar="a new value"
  3.  
  4. var myFlashVar:String = "Some Default Value";
  5. if (root.loaderInfo.parameters.myFlashVar){
  6.    myFlashVar = root.loaderInfo.parameters.myFlashVar;
  7. }

Getting at flashvars in AS3 requires a bit more typing than it did in AS2. Above is a technique I find myself using from time to time. Usually for paths to xml or dynamic callouts (large buttons with text, an image and a link that change frequently).

Posted in dynamic, misc | Tagged , | Leave a comment

Low Precision Sine and Cosine

Actionscript:
  1. var t:Number=0, cos:Number, sin:Number;
  2.  
  3. var canvas:BitmapData = new BitmapData(400,400,false, 0x000000);
  4. addChild(new Bitmap(canvas));
  5.  
  6. var shape:Shape = new Shape();
  7. with(shape.graphics) beginFill(0x568338, .2), drawCircle(0,0,10);
  8.  
  9. addEventListener(Event.ENTER_FRAME, onLoop);
  10. function onLoop(evt:Event):void {
  11.    
  12.          t += .1;
  13.         // -- low precision sine/cosine
  14.         //always wrap input angle to -PI..PI
  15.         if (t <-3.14159265){
  16.             t += 6.28318531;
  17.         }else{
  18.         if (t>  3.14159265)
  19.             t -= 6.28318531;
  20.         }
  21.        
  22.         //compute sine
  23.         if (t <0){
  24.             sin = 1.27323954 * t + .405284735 * t * t;
  25.         }else{
  26.             sin = 1.27323954 * t - 0.405284735 * t * t;
  27.         }
  28.        
  29.         //compute cosine: sin(t + PI/2) = cos(t)
  30.         t += 1.57079632;
  31.         if (t>  3.14159265){
  32.             t -= 6.28318531;
  33.         }
  34.        
  35.         if (t <0){
  36.             cos = 1.27323954 * t + 0.405284735 * t * t
  37.         }else{
  38.             cos = 1.27323954 * t - 0.405284735 * t * t;
  39.         }
  40.         t -= 1.57079632;
  41.        
  42.         // move the shape
  43.         shape.x = 200 + 100 * cos;
  44.         shape.y = 200 + 100 * sin;
  45.        
  46.         // draw to the canvas
  47.         canvas.draw(shape, shape.transform.matrix);
  48. }

This snippet draws a circle using low precision sine and cosine... you'll notice that its not a perfect looking circle:


Back in January I saw this blog post by Michael Baczynski over at http://lab.polygonal.de/. The blog post describes a technique for fast sine and cosine approximation - (I highly recommend giving it a read - very fun stuff).

It's worth noting that there is a higher precision sine and cosine that will likely draw a better looking circle but will be about half as fast. According to the original post ... the low precision technique is approximately 14x faster than using Math.cos()/Math.sin().

There some other really great posts over at polygonal go check them out.

Posted in Math, motion | Tagged , | 2 Comments

Unique Array Comparison

Actionscript:
  1. function uniqueCompare(arr:Array, compare:Function):void {
  2.     var leng:int = arr.length;
  3.     for (var i:int = 0; i<leng; i++){
  4.         for (var j:int = i + 1; j<leng; j++){
  5.             compare(arr[i], arr[j]);
  6.         }
  7.     }
  8. }
  9.  
  10. var numbers:Array = [1, 2, 3, 4, 5];
  11.  
  12. uniqueCompare(numbers, compareCallback);
  13.  
  14. function compareCallback(a:*, b:*):void {
  15.     trace("compare " + a + " to " + b);
  16. }
  17.  
  18. /*
  19. outputs:
  20. compare 1 to 2
  21. compare 1 to 3
  22. compare 1 to 4
  23. compare 1 to 5
  24. compare 2 to 3
  25. compare 2 to 4
  26. compare 2 to 5
  27. compare 3 to 4
  28. compare 3 to 5
  29. compare 4 to 5
  30. */

This snippet shows a function called uniqueCompare() that compares all elements in an array to all other elements in an array without repeating a comparison.

Posted in arrays, misc | Tagged , | Leave a comment

Object.constructor()

Actionscript:
  1. var Box:Function = {
  2.     constructor : function(color:uint, w:Number, h:Number):void {
  3.         this.color = color;
  4.         this.s = new Shape();
  5.         with(this.s.graphics) beginFill(this.color), drawRect(0,0,w,h);
  6.         addChild(this.s);
  7.        
  8.         this.setLoc = function(x:Number, y:Number):void{
  9.             this.s.x = x;
  10.             this.s.y = y;
  11.        }
  12.     }
  13. }.constructor;
  14.  
  15. var box0:Object = new Box(0xFF0000, 100, 100);
  16.  
  17. box0.setLoc(100, 10);
  18.  
  19. var box1:Object = new Box(0x000000, 50, 50);
  20.  
  21. box1.setLoc(210, 10);

This snippet makes use of Object.constructor() to allow creation of Object instances using the new keyword. This is for fun only, I don't recommend this over actual Classes.

Posted in OOP, dynamic, functions, misc | Tagged , | Leave a comment

Circle of Particles #2

Actionscript:
  1. [SWF(width = 400, height = 400)];
  2. var canvas:BitmapData = new BitmapData(400,400, false, 0x000000);
  3. var eraser:BitmapData = new BitmapData(400,400, true, 0x11000000);
  4. addChild(new Bitmap(canvas));
  5.  
  6. var particles:Array = new Array();
  7. for (var i:int = 0; i <500; i++){
  8.     particles.push(makeParticle());
  9. }
  10.  
  11. addEventListener(Event.ENTER_FRAME, onLoop);
  12. function onLoop(evt:Event):void {
  13.     canvas.copyPixels(eraser, eraser.rect, new Point(0,0), null, null, true);
  14.     for (var i:int = 0; i <particles.length; i++){
  15.         particles[i]();
  16.     }
  17. }
  18.  
  19. function makeParticle():Function {
  20.     var dx:Number, dy:Number;
  21.     var x:Number = 200;
  22.     var y:Number = 200;
  23.     var vx:Number = Math.random() * 4 - 2;
  24.     var vy:Number = Math.random() * 4 - 2;
  25.     var ang:Number;
  26.     return function():void {
  27.               x += vx;
  28.               y += vy;
  29.               dx = 200 - x;
  30.               dy=  200 - y;
  31.                if (Math.sqrt((dx * dx) + (dy * dy))> 130){
  32.                 ang = Math.atan2(dy, dx) / Math.PI * 180;
  33.                  vx = Math.cos(ang);
  34.                  vy = Math.sin(ang);
  35.                }
  36.                canvas.setPixel(x, y, 0xFFFFFF);
  37.     }
  38. }

This is an interesting variation on yesterdays post. The result will looks like this...


Click to view swf...

I also decided to do a processing port of this with 3,000 particles:

Processing Version

The processing version eventually evolved into this:


Click for enlarged version...

Posted in BitmapData, motion, setPixel | Tagged , , | 2 Comments

Circle of Particles

Actionscript:
  1. var canvas:BitmapData = new BitmapData(400,400, false, 0xFFFFFF);
  2. addChild(new Bitmap(canvas));
  3. var eraser:BitmapData = new BitmapData(400,400, true, 0x33FFFFFF);
  4.  
  5. var particles:Array = new Array();
  6. for (var i:int = 0; i <1000; i++){
  7.     particles.push(makeParticle());
  8. }
  9.  
  10. addEventListener(Event.ENTER_FRAME, onLoop);
  11. function onLoop(evt:Event):void {
  12.     canvas.copyPixels(eraser, eraser.rect, new Point(0,0), null, null, true);
  13.     for (var i:int = 0; i <particles.length; i++){
  14.         particles[i]();
  15.     }
  16. }
  17.  
  18. function makeParticle():Function {
  19.     var dx:Number, dy:Number;
  20.     var x:Number = 200;
  21.     var y:Number = 200;
  22.     var vx:Number = Math.random() * 4 - 2;
  23.     var vy:Number = Math.random() * 4 - 2;
  24.     return function():void {
  25.               x += vx;
  26.               y += vy;
  27.               dx = x - 200;
  28.               dy= y - 200;
  29.                if (Math.sqrt((dx * dx) + (dy * dy))> 100){
  30.                  vx *= -1;
  31.                  vy *= -1;
  32.                }
  33.                canvas.setPixel(x, y, 0x000000);
  34.     }
  35. }

This creates a circular area filled with moving particles/pixels. It makes use of yesterdays functional programming techniques.

Posted in BitmapData, functions, motion, setPixel | Tagged , | Leave a comment

Functional Pseudo-Objects

Actionscript:
  1. var moverNum:int = 40;
  2. var movers:Array = new Array();
  3.  
  4. for (var i:int = 0; i<moverNum; i++){
  5.     movers.push(makeMover());
  6. }
  7.  
  8. addEventListener(Event.ENTER_FRAME, onLoop);
  9. function onLoop(evt:Event):void {
  10.     for (var i:int = 0; i<moverNum; i++){
  11.         movers[i]();
  12.     }
  13. }
  14.  
  15. function makeMover():Function{
  16.     // mover vars & setup
  17.     var xVel:Number = Math.random() * 5 + 1;
  18.     var right:Number = stage.stageWidth + 30;
  19.     var s:Shape = Shape(addChild(new Shape()));
  20.     with(s.graphics) beginFill(0xFF0000), drawCircle(0,0,5);
  21.     s.x = Math.random() * stage.stageWidth;
  22.     s.y = Math.random() * stage.stageHeight;
  23.     // return a "run" function
  24.     return function():void {
  25.          s.x += xVel;
  26.          if (s.x> right){
  27.             s.x = -30;
  28.          }
  29.     }
  30. }

This snippet creates 40 shapes that move to the right at a random velocity. When they animate off the right hand side of the stage they return from the left.

The makeMover() function has a series of variable definitions related to the x velocity and position of a Shape. The makeMover() function then returns an anonymous function that contains some logic to move the Shape "s". The anonymous function has access to the makeMover() functions temporary variables...

On the main loop we run all of the anonymous functions - animating all the shapes by their random x velocities.

It would be interesting to write the same program using classes and test the for speed differences. My assumption is that anonymous functions are expensive, but it would still be interesting to see...

Posted in functions, motion | Tagged , | Leave a comment

Bezier Intersections (not a snippet)

Actionscript:
  1. var resolution:Number = .03;
  2. var pointNum:int = Math.ceil(1 / resolution);
  3.  
  4. var bezA:Array = new Array();
  5. populateArray(bezA);
  6. var a:Sprite = dot(100, 200);
  7. var b:Sprite = dot(200, 100);
  8. var c:Sprite = dot(300, 200);
  9.  
  10. var bezB:Array = new Array();
  11. populateArray(bezB);
  12. var d:Sprite = dot(300, 100, 0xCCCC00);
  13. var e:Sprite = dot(120, 130, 0xCCCC00);
  14. var f:Sprite = dot(200, 300, 0xCCCC00);
  15.  
  16. addEventListener(Event.ENTER_FRAME, onLoop);
  17. function onLoop(evt:Event):void {
  18.       with(graphics){
  19.           clear();
  20.           lineStyle(0, 0x000000);
  21.        
  22.           // calc and draw bezier points
  23.           drawBezier(bezA, a, b, c);
  24.           drawBezier(bezB, d, e, f);
  25.          
  26.           // calc collisions
  27.           var intersections:Array = calculateIntersection(bezA, bezB);
  28.          
  29.            // draw collisions
  30.            beginFill(0xFF0000);
  31.            if (intersections.length> 0){
  32.                 for (var i:int = 0; i<intersections.length; i++){
  33.                     drawCircle(intersections[i].x, intersections[i].y, 3);
  34.                 }
  35.            }
  36.            endFill();
  37.      }
  38. }
  39.  
  40. function populateArray(a:Array):void {
  41.     for (var i:int = 0; i<pointNum; i++){
  42.         a.push(new Point());
  43.     }
  44. }
  45.  
  46. function drawBezier(bez:Array, a:Sprite, b:Sprite, c:Sprite):void{
  47.      with(graphics){
  48.          bezier(bez, a.x, a.y, b.x, b.y, c.x, c.y);
  49.          var leng:Number = bez.length;
  50.          moveTo(bez[0].x, bez[0].y);
  51.          for (var i:int = 1; i<leng; i++){
  52.              lineTo(bez[i].x, bez[i].y);
  53.          }
  54.      }
  55. }
  56.  
  57. function bezier(bez:Array, x1:Number, y1:Number, x2:Number, y2:Number, x3:Number, y3:Number):void {
  58.             var b:Number, a2:Number, ab2:Number, b2:Number;
  59.             var pnt:Point;
  60.             var inc:int = 0;
  61.             for (var a:Number = 0; a <=1; a+=resolution) {
  62.                 b= 1 - a;
  63.                 a2 = a * a;
  64.                 ab2 = a * b * 2;
  65.                 b2 = b * b;
  66.                 pnt = bez[inc];
  67.                 pnt.x = a2 * x1 + ab2 * x2  + b2 * x3;
  68.                 pnt.y = a2 * y1 + ab2 * y2 + b2 * y3;      
  69.                 inc++;
  70.             }
  71. }
  72.  
  73. function calculateIntersection(bezA:Array, bezB:Array):Array {
  74.     var intersections:Array = new Array();
  75.     var ip:Point;
  76.     var aLength:int = bezA.length;
  77.     var bLength:int = bezB.length;
  78.     var p1:Point, p2:Point, p3:Point, p4:Point;
  79.    
  80.     // compare all line segments and check for
  81.     // intersections
  82.     for (var i:int = 1; i<aLength; i++){
  83.         p1 = bezA[i - 1];
  84.         p2 = bezA[i];
  85.         for (var j:int = 1; j<bLength; j++){
  86.             p3 = bezB[j - 1];
  87.             p4 = bezB[j];
  88.             ip = intersection(p1, p2, p3, p4);
  89.             if (ip){
  90.                 intersections.push(ip.clone());
  91.             }
  92.         }
  93.     }
  94.     return intersections;
  95. }
  96.  
  97. var ip:Point = new Point();
  98.  
  99. function intersection(p1:Point, p2:Point, p3:Point, p4:Point):Point {
  100.      
  101.     var nx:Number, ny:Number, dn:Number;
  102.     var x4_x3:Number = p4.x - p3.x;
  103.     var pre2:Number = p4.y - p3.y;
  104.     var pre3:Number = p2.x - p1.x;
  105.     var pre4:Number = p2.y - p1.y;
  106.     var pre5:Number = p1.y - p3.y;
  107.     var pre6:Number = p1.x - p3.x;
  108.    
  109.     nx = x4_x3 * pre5 - pre2 * pre6;
  110.     ny = pre3 * pre5 - pre4 * pre6;
  111.     dn = pre2 * pre3 - x4_x3 * pre4;
  112.    
  113.     if (dn == 0){
  114.         return null
  115.     }
  116.     nx /= dn;
  117.     ny /= dn;
  118.     // has intersection
  119.     if(nx>= 0 && nx <= 1 && ny>= 0 && ny <= 1){
  120.         ny = p1.y + nx * pre4;
  121.         nx = p1.x + nx * pre3;
  122.         ip.x = nx;
  123.         ip.y = ny;
  124.     }else{
  125.          return null;
  126.     }
  127.     return ip
  128. }
  129.  
  130. // draggable dot
  131. function dot(xp:Number, yp:Number, col:uint = 0x507399, noDrag:Boolean = false):Sprite {
  132.     var s:Sprite = Sprite(addChild(new Sprite));
  133.     s.x = xp;
  134.     s.y = yp;
  135.     with(s.graphics) beginFill(col), drawCircle(0,0,5);
  136.     if (!noDrag){
  137.         s.buttonMode = true;
  138.        s.addEventListener(MouseEvent.MOUSE_DOWN, onDrag);
  139.     }
  140.     return s;
  141. }
  142. function onDrag(evt:MouseEvent):void {
  143.     evt.currentTarget.startDrag()
  144. }
  145.  
  146. stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
  147. function onUp(evt:MouseEvent):void{
  148.     stopDrag();
  149. }

This is not a snippet, but it uses some functions that could be considered snippets on their own. Take a look at the swf here:

Bezier Intersection Demo

This demo show a brute force method for getting the coordinates of all intersections between two given quadtratic bezier curves. The approach is pretty simple, each bezier is actually made up of a series of line segments. The resolution variable determines how many line segments make up one bezier curve. By default I use a resolution of 0.03 (~34 lines)... the higher the resolution decimal value, the fewer the number of line segments. I then use a slight variation on yesterdays line segment intersection function to test all existing line segments for intersections.

This is a brute for approach with lots of room for improvement. But in the scenario that you only need to check a few bezier curves for intersection this approach works quite nicely. I don't recommend this approach if your trying to create a complex vector drawing tool with boolean vector operations etc...

It's easy to get an idea of the different approaches to this problem with a little googling. One example I found is from Graphics Gems:

http://tog.acm.org/GraphicsGems/gemsiv/curve_isect/

it's been awhile, but I believe this website also contains information related to this topic:

http://algorithmist.wordpress.com/

Posted in Math, bezier | Tagged , | Leave a comment

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 | Tagged , | Leave a comment