Monthly Archives: March 2009

Closed Catmull-Rom

Actionscript:
  1. var canvas:BitmapData = new BitmapData(400,400,false, 0x000000);
  2. addChild(new Bitmap(canvas));
  3.  
  4. var pnts:Array = new Array();
  5. // make control points
  6. for (var i:int = 0; i<5; i++){
  7.     var t:Number = i * 72 * Math.PI /180;
  8.     pnts.push(dot(200 + 100 * Math.cos(t),200 +100 * Math.sin(t)));  
  9. }
  10.  
  11. addEventListener(Event.ENTER_FRAME, onLoop);
  12. function onLoop(evt:Event):void {
  13.     canvas.lock();
  14.     canvas.fillRect(canvas.rect, 0x000000);
  15.     curve(pnts);
  16.     canvas.unlock();
  17. }
  18.  
  19. function tangent(pk1:Sprite, pk_1:Sprite){
  20.     return new Point((pk1.x - pk_1.x) / 2, (pk1.y - pk_1.y) / 2);
  21. }
  22.  
  23. // all math from http://en.wikipedia.org/wiki/Cubic_Hermite_spline
  24. function curve(p:Array, res:Number=.03):void{
  25.     var px:Number = 0;
  26.     var py:Number = 0;
  27.     var pIter:int = p.length - 1;
  28.     var m:Array = [];
  29.    
  30.     m[0] = tangent(p[1] ,  p[pIter]);
  31.     for (var i:int = 1; i<pIter; i++){
  32.         m[i] = tangent(p[i + 1], p[i - 1]);
  33.     }
  34.     m[pIter] = tangent(p[0],p[pIter-1]);
  35.    
  36.     for (var t:Number = 0; t <1; t+=res){
  37.          var t_2:Number = t * t;
  38.          var _1_t:Number = 1 - t;
  39.          var _2t:Number = 2 * t;
  40.          
  41.          var h00:Number =  (1 + _2t) * (_1_t) * (_1_t);
  42.          var h10:Number =  t  * (_1_t) * (_1_t);
  43.          var h01:Number =  t_2 * (3 - _2t);
  44.          var h11:Number =  t_2 * (t - 1);
  45.          
  46.          for (var k:int = 0; k <pIter; k++){
  47.              var k1:int = k + 1;
  48.              var pk:Sprite = p[k];
  49.              var pk1:Sprite = p[k1];
  50.              var mk:Point = m[k];
  51.              var mk1:Point = m[k1];
  52.              px = h00 * pk.x + h10 * mk.x + h01 * pk1.x + h11 * mk1.x;
  53.              py = h00 * pk.y + h10 * mk.y + h01 * pk1.y + h11 * mk1.y;
  54.              canvas.setPixel(px, py, 0xFFFFFF);
  55.          }
  56.           pk = p[k];
  57.           pk1 = p[0];
  58.           mk = m[k];
  59.           mk1= m[0];
  60.          
  61.           px = h00 * pk.x + h10 * mk.x + h01 * pk1.x + h11 * mk1.x;
  62.           py = h00 * pk.y + h10 * mk.y + h01 * pk1.y + h11 * mk1.y;
  63.           canvas.setPixel(px, py, 0xFFFFFF);
  64.     }
  65. }
  66.  
  67. // draggable dot
  68. function dot(xp:Number, yp:Number, col:uint = 0xFF0000, rad:Number=4):Sprite {
  69.     var s:Sprite = Sprite(addChild(new Sprite));
  70.     s.x = xp;
  71.     s.y = yp;
  72.     with(s.graphics) beginFill(col), drawCircle(0,0,rad);
  73.     s.buttonMode = true;
  74.     s.addEventListener(MouseEvent.MOUSE_DOWN, onDrag);
  75.     return s;
  76. }
  77. function onDrag(evt:MouseEvent):void {
  78.     evt.currentTarget.startDrag()
  79. }
  80. stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
  81. function onUp(evt:MouseEvent):void{
  82.     stopDrag();
  83. }

This a variation on a post from a few days ago. This shows how to create a closed Catmull Rom spline.

See the other Catmull-Rom related Posts

Posted in Math, graphics algorithms, setPixel | Tagged , | 2 Comments

Hippopede

Actionscript:
  1. x=stage.stageWidth/2;
  2. y=stage.stageHeight/2;
  3. var xp:Number = 0, yp:Number = 0;
  4. var r:Number = 0, t:Number = 0;
  5. var speed:Number=.1;
  6.  
  7. var range:Number=.2;
  8. var b:Number = 20;
  9. var a:Number = b * range;
  10. var orbit:Number = 2 * Math.PI + speed;
  11.  
  12. graphics.lineStyle(0,0x000000);
  13.  
  14. addEventListener(Event.ENTER_FRAME, onLoop);
  15. function onLoop(evt:Event):void {
  16.     var sin:Number=Math.sin(t);
  17.     r =  Math.sqrt(4 * b * (a - b * (sin * sin)));
  18.     xp= r * Math.cos(t);
  19.     yp= r * sin;
  20.    
  21.     if (t==0) {
  22.         graphics.moveTo(xp, yp);
  23.     } else {
  24.         graphics.lineTo(xp, yp);
  25.     }
  26.  
  27.     t += speed;
  28.    
  29.     if (t> orbit) {
  30.         range += .5;
  31.         a= b * range;
  32.         t= 0;
  33.     }
  34. }

The above draws a Hippopede

Posted in Graphics, Math, motion | Tagged , | Leave a comment

Fermat’s Spiral

Actionscript:
  1. x = stage.stageWidth / 2;
  2. y = stage.stageHeight / 2;
  3.  
  4. var xp:Number = 0, yp:Number = 0;
  5. var r:Number = 0, t:Number = 0;
  6. var speed:Number = .07;
  7. var scale:Number = 20;
  8.  
  9. var plot0:Shape = Shape(addChild(new Shape()));
  10. var plot1:Shape = Shape(addChild(new Shape()));
  11. plot0.graphics.lineStyle(0,0x000000);
  12. plot1.graphics.lineStyle(0,0x000000);
  13.  
  14. addEventListener(Event.ENTER_FRAME, onLoop);
  15. function onLoop(evt:Event):void {
  16.      r =  scale * Math.sqrt(t);
  17.      xp =  r * Math.cos(t);
  18.      yp =  r * Math.sin(t);
  19.      t += speed;
  20.      
  21.     plot0.graphics.lineTo(xp, yp);
  22.     plot1.graphics.lineTo(-xp, -yp);
  23. }

This snippet draws Fermat's Spiral.

Posted in Graphics, Math, motion | Tagged , | Leave a comment

var obj:Object = new Function()

Actionscript:
  1. var Pnt:Function = function(xp:Number, yp:Number){
  2.    
  3.     var x:Number = xp;
  4.     var y:Number = yp
  5.    
  6.     this.setX = function(v:int):void{
  7.         x = v;
  8.     }
  9.     this.setY = function(v:int):void{
  10.         y = v;
  11.     }
  12.     this.getX = function():int {
  13.         return x;
  14.     }
  15.     this.getY = function():int {
  16.         return y;
  17.     }
  18. }
  19.  
  20. var p:Object = new Pnt(10,10);
  21.  
  22. trace(p.getX(), p.getY());
  23. p.setX(100);
  24. trace(p.getX());
  25.  
  26. /*
  27. outputs:
  28. 10, 10
  29. 100
  30. */

Another way to define and instantiate Objects on the timeline. Interesting, but I don't recommend it over actual classes...

Posted in OOP, Object, functions | Tagged , | Leave a comment

Interesting Function Composition

Actionscript:
  1. var multBy2Add3:Function = add(3, mult(2));
  2.  
  3. trace(multBy2Add3(10));
  4. // 10 * 2  = 20
  5. //  20 + 3 = 23
  6.  
  7. var add2AndSquare = sq(add(2));
  8.  
  9. trace(add2AndSquare(8));
  10. // 8 + 2 = 10
  11. // 10 * 10 = 100
  12.  
  13. var multBy2_3times:Function = repeat(3,mult(2));
  14.  
  15. trace(multBy2_3times(3));
  16. // 3 * 2 = 6;
  17. // 6 * 2 = 12;
  18. // 12 * 2 = 24
  19.  
  20. // you can also chain for even less readability
  21. trace(sq(mult(5,add(1)))(4));
  22. // 4 + 1 = 5
  23. // 5 * 5 = 25;
  24. // 25 * 25 = 625;
  25.  
  26. /*
  27. outputs:
  28. 12
  29. 100
  30. 24
  31. 625
  32. */
  33.  
  34. // function  composition
  35. const F:Function = function(a:*):*{return a};
  36. function mult(scalar:Number, f:Function=null):Function{
  37.     if (f == null) f = F;
  38.      return function(n:Number){
  39.         return f(n) * scalar;
  40.     }
  41. }
  42.  
  43. function add(off:Number, f:Function=null):Function{
  44.      if (f == null) f = F;
  45.      return function(n:Number){
  46.         return f(n) + off;
  47.     }
  48. }
  49. function sq(f:Function=null):Function{
  50.      if (f == null) f = F;
  51.      return function(n:Number){
  52.          var v:Number = f(n);
  53.         return  v * v;
  54.     }
  55. }
  56. function repeat(times:int, f:Function):Function {
  57.      if (f == null) f = F;
  58.      return function (n:Number){
  59.          var v:Number = n;
  60.          for (var i:int = 0; i<times; i++) v = f(v);
  61.          return v;
  62.      }
  63. }

The above shows some interesting function composition... this demo contains the following functions:

mult();
add();
sq();
repeat();

These functions are designed to be combined to create new functions.

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

ActionScript Wiki

Just wanted to post a link to Joa Ebert's ActionScript Wiki. Looks like there's some great stuff there...

Posted in misc | Tagged , | Leave a comment

k-th Order Catmull-Rom Spline

Actionscript:
  1. var canvas:BitmapData = new BitmapData(400,400,false, 0x000000);
  2. addChild(new Bitmap(canvas));
  3. var pnts:Array = new Array();
  4. // make 8 control points
  5. for (var i:int = 0; i<8; i++){
  6.     pnts.push(dot(50 + i * (30 + Math.random()*10),
  7.                              50 + i * (30 + Math.random()*10)));
  8. }
  9. addEventListener(Event.ENTER_FRAME, onLoop);
  10. function onLoop(evt:Event):void {
  11.     canvas.lock();
  12.     canvas.fillRect(canvas.rect, 0x000000);
  13.     curve(pnts);
  14.     canvas.unlock();
  15. }
  16. function tangent(pk1:Sprite, pk_1:Sprite){
  17.     return new Point((pk1.x - pk_1.x) / 2, (pk1.y - pk_1.y) / 2);
  18. }
  19. // all math from http://en.wikipedia.org/wiki/Cubic_Hermite_spline
  20. function curve(p:Array, res:Number=.03):void{
  21.     var px:Number = 0;
  22.     var py:Number = 0;
  23.     var pIter:int = p.length - 1;
  24.     var m:Array = [];
  25.     m[0] = tangent(p[1], p[0]);
  26.     for (var i:int = 1; i<pIter; i++){
  27.         m[i] = tangent(p[i + 1], p[i - 1]);
  28.     }
  29.     m[pIter] = tangent(p[pIter], p[pIter - 1]);
  30.     for (var t:Number = 0; t <1; t+=res){
  31.          var t_2:Number = t * t;
  32.          var _1_t:Number = 1 - t;
  33.          var _2t:Number = 2 * t;
  34.          var h00:Number =  (1 + _2t) * (_1_t) * (_1_t);
  35.          var h10:Number =  t  * (_1_t) * (_1_t);
  36.          var h01:Number =  t_2 * (3 - _2t);
  37.          var h11:Number =  t_2 * (t - 1);
  38.          for (var k:int = 0; k <pIter; k++){
  39.              var k1:int = k + 1;
  40.              var pk:Sprite = p[k];
  41.              var pk1:Sprite = p[k1];
  42.              var mk:Point = m[k];
  43.              var mk1:Point = m[k1];
  44.              px = h00 * pk.x + h10 * mk.x + h01 * pk1.x + h11 * mk1.x;
  45.              py = h00 * pk.y + h10 * mk.y + h01 * pk1.y + h11 * mk1.y;
  46.              canvas.setPixel(px, py, 0xFFFFFF);
  47.          }
  48.     }
  49. }
  50. // draggable dot
  51. function dot(xp:Number, yp:Number, col:uint = 0xFF0000, rad:Number=4):Sprite {
  52.     var s:Sprite = Sprite(addChild(new Sprite));
  53.     s.x = xp;
  54.     s.y = yp;
  55.     with(s.graphics) beginFill(col), drawCircle(0,0,rad);
  56.     s.buttonMode = true;
  57.     s.addEventListener(MouseEvent.MOUSE_DOWN, onDrag);
  58.     return s;
  59. }
  60. function onDrag(evt:MouseEvent):void {
  61.     evt.currentTarget.startDrag()
  62. }
  63. stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
  64. function onUp(evt:MouseEvent):void{
  65.     stopDrag();
  66. }

More Catmull-Rom stuff. This one can have any number of control points:


Check it out here:

Posted in Math, graphics algorithms, setPixel | Leave a comment

Catmull-Rom Spline

Actionscript:
  1. var canvas:BitmapData = new BitmapData(400,400,false, 0x000000);
  2. addChild(new Bitmap(canvas));
  3.  
  4. var p0:Sprite = dot(100, 100);
  5. var p1:Sprite = dot(120, 180);
  6. var p2:Sprite = dot(220, 180);
  7. var p3:Sprite = dot(250, 250);
  8.  
  9. addEventListener(Event.ENTER_FRAME, onLoop);
  10. function onLoop(evt:Event):void {
  11.     canvas.lock();  
  12.     canvas.fillRect(canvas.rect, 0x000000);
  13.  
  14.     curve(p0, p1, p2, p3);
  15.     canvas.unlock();
  16. }
  17.  
  18.  
  19. // all math from http://en.wikipedia.org/wiki/Cubic_Hermite_spline
  20. function curve(p0:Sprite, p1:Sprite, p2:Sprite, p3:Sprite, rez:Number=.02):void{
  21.     var px:Number = 0;
  22.     var py:Number = 0;
  23.     var m0:Point = tangent(p1, p0);
  24.     var m1:Point = tangent(p2, p0);
  25.     var m2:Point = tangent(p3, p1);
  26.     var m3:Point = tangent(p3, p2);
  27.    
  28.     for (var t:Number = 0; t <1; t+=rez){
  29.          var t_2:Number = t * t;
  30.          var _1_t:Number = 1 - t;
  31.          var _2t:Number = 2 * t;
  32.          
  33.          var h00:Number =  (1 + _2t) * (_1_t) * (_1_t);
  34.          var h10:Number =  t  * (_1_t) * (_1_t);
  35.          var h01:Number =  t_2 * (3 - _2t);
  36.          var h11:Number =  t_2 * (t - 1);
  37.          
  38.          px = h00 * p0.x + h10 * m0.x + h01 * p1.x + h11 * m1.x;
  39.          py = h00 * p0.y + h10 * m0.y + h01 * p1.y + h11 * m1.y;
  40.          canvas.setPixel(px, py, 0xFFFFFF);
  41.          
  42.          px = h00 * p1.x + h10 * m1.x + h01 * p2.x + h11 * m2.x;
  43.          py = h00 * p1.y + h10 * m1.y + h01 * p2.y + h11 * m2.y;
  44.          canvas.setPixel(px, py, 0xFFFFFF);
  45.          
  46.          px = h00 * p2.x + h10 * m2.x + h01 * p3.x + h11 * m3.x;
  47.          py = h00 * p2.y + h10 * m2.y + h01 * p3.y + h11 * m3.y;
  48.          canvas.setPixel(px, py, 0xFFFFFF);
  49.     }
  50. }
  51.  
  52. function tangent(pk1:Sprite, pk_1:Sprite){
  53.     return new Point((pk1.x - pk_1.x) / 2, (pk1.y - pk_1.y) / 2);
  54. }
  55.  
  56. // draggable dot
  57. function dot(xp:Number, yp:Number, col:uint = 0x507399, rad:Number=5):Sprite {
  58.     var s:Sprite = Sprite(addChild(new Sprite));
  59.     s.x = xp;
  60.     s.y = yp;
  61.     with(s.graphics) beginFill(col), drawCircle(0,0,rad);
  62.     s.buttonMode = true;
  63.     s.addEventListener(MouseEvent.MOUSE_DOWN, onDrag);
  64.     return s;
  65. }
  66. function onDrag(evt:MouseEvent):void {
  67.     evt.currentTarget.startDrag()
  68. }
  69. stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
  70. function onUp(evt:MouseEvent):void{
  71.     stopDrag();
  72. }

This is snippet draws a Catmull-Rom spline based on 4 control points. A nice feature of Catmull-Rom splines is that they always pass through each of their control points.


have a look at the swf here:

I first learned about Catmull-Rom splines somewhere in the processing forums.

I got all my info for this snippet from this wikipedia page.

There is a good deal of room for optimization here... I may post an optimized version in the future.

Posted in Math, graphics algorithms, setPixel | Tagged , | 4 Comments

Cubic Hermite Spline

Actionscript:
  1. var canvas:BitmapData = new BitmapData(400,400,false, 0x000000);
  2. addChild(new Bitmap(canvas));
  3.  
  4. // create some draggable dots
  5. var p0:Sprite = dot(100, 100);
  6. var p1:Sprite = dot(200, 200);
  7. var m0:Sprite = dot(200, 100, 0xFFFFFF, 3)
  8. var m1:Sprite = dot(100, 200, 0xFFFFFF, 3);
  9.  
  10. addEventListener(Event.ENTER_FRAME, onLoop);
  11. function onLoop(evt:Event):void {
  12.     canvas.fillRect(canvas.rect, 0x000000);
  13.     var px:Number = 0;
  14.     var py:Number = 0;
  15.     for (var t:Number = 0; t <1; t+=.01){
  16.         var t_2:Number = t * t;
  17.         var t_3:Number = t_2 * t;
  18.        
  19.         // some repetitive math for clarity
  20.         px = (2 * t_3  - 3 * t_2 + 1) * p0.x +(t_3 - 2 * t_2 + t) *
  21.                  m0.x + (-2 * t_3 + 3 * t_2) * p1.x + (t_3 - t_2) * m1.x;
  22.         py = (2 * t_3  - 3 * t_2 + 1) * p0.y +(t_3 - 2 * t_2 + t) *
  23.                  m0.y + (-2 * t_3 + 3 * t_2) * p1.y + (t_3 - t_2) * m1.y;
  24.         canvas.setPixel(px, py, 0xFF0000);
  25.     }
  26. }
  27. // draggable dot
  28. function dot(xp:Number, yp:Number, col:uint = 0x507399, rad:Number=5):Sprite {
  29.     var s:Sprite = Sprite(addChild(new Sprite()));
  30.     s.x = xp;
  31.     s.y = yp;
  32.     with(s.graphics) beginFill(col), drawCircle(0,0,rad);
  33.     s.buttonMode = true;
  34.     s.addEventListener(MouseEvent.MOUSE_DOWN, onDrag);
  35.     return s;
  36. }
  37. function onDrag(evt:MouseEvent):void {
  38.     evt.currentTarget.startDrag()
  39. }
  40. stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
  41. function onUp(evt:MouseEvent):void{
  42.     stopDrag();
  43. }

This is the start of my exploration of Cubic Hermite Splines (like Catmull–Rom)... I wrote this snippet while skimming the wikipedia article on the subject.... at first I wasn't sure if this was correct, but I added tangent calculations for a Catmull-Rom and it worked nicely... will post that tomorrow.

Posted in Math, graphics algorithms, setPixel | Tagged , | Leave a comment

more(nesting(functions(graphDrawing)));

Actionscript:
  1. [SWF(width=800, height=600)]
  2.  
  3. var canvas:Graphics;
  4. var graphData:Array = sineData();
  5.  
  6. var graph0:Shape = Shape(addChild(new Shape()));
  7. graph0.x = 50;
  8. graph0.y = 150;
  9.  
  10. var graph1:Shape = Shape(addChild(new Shape()));
  11. graph1.x = 400;
  12. graph1.y = 150;
  13.  
  14. var graph2:Shape = Shape(addChild(new Shape()));
  15. graph2.x = 50;
  16. graph2.y = 400;
  17.  
  18. // use graphData to draw 3 different looking graphs:
  19.  
  20. canvas = graph0.graphics;
  21. axis(lines(graphData));
  22.  
  23. canvas = graph1.graphics;
  24. axis(dots(graphData, 0xFF0000), 0xFFCC00, 2);
  25.  
  26. canvas = graph2.graphics;
  27. axis(dots(dots(lines(lines(graphData, 0xCCCCCC, 20))), 0x0022FF, 0, 4), 0xFF);
  28.  
  29.  
  30. // generate data
  31. function sineData():Array{
  32.     var dat:Array = new Array();
  33.     for (var i:int = 0; i<60; i++){
  34.         dat.push(new Point(i * 4,  (30 + i) * Math.sin(i * 24 * Math.PI/180)));
  35.     }            
  36.     return dat;
  37. }
  38.  
  39. // render lines
  40. function lines(dat:Array, col:uint=0x000000, thick:Number=0):Array{
  41.     canvas.lineStyle(thick, col);
  42.     canvas.moveTo(dat[0].x, dat[0].y)
  43.     for (var i:int = 1; i<dat.length; i++){
  44.          canvas.lineTo(dat[i].x, dat[i].y);
  45.     }
  46.     return dat;
  47. }
  48.  
  49. // render dots
  50. function dots(dat:Array, col:uint=0xFF0000, thick:Number=0, rad:Number=1.5):Array{
  51.     canvas.lineStyle(thick, col);
  52.     for (var i:int = 0; i<dat.length; i++){
  53.          canvas.drawCircle(dat[i].x, dat[i].y, rad);
  54.     }
  55.     return dat;
  56. }
  57.  
  58. // render graph axis
  59. function axis(dat:Array, col:uint=0x000000, thick:Number=0):Array{
  60.     var d:Array = dat.concat();
  61.     d.sortOn("y", Array.NUMERIC);
  62.     var lastIndex:int = d.length - 1;
  63.     var minY:Number = d[0].y;
  64.     var maxY:Number = d[lastIndex].y;
  65.     d.sortOn("x", Array.NUMERIC);
  66.     var minX:Number = d[0].x;
  67.     var maxX:Number = d[lastIndex].x;
  68.     canvas.lineStyle(thick, col, .2);
  69.     canvas.moveTo(minX, 0);
  70.     canvas.lineTo(maxX, 0);
  71.     canvas.lineStyle(thick, col);
  72.     canvas.moveTo(minX, minY);
  73.     canvas.lineTo(minX, maxY);
  74.     canvas.lineTo(maxX, maxY);
  75.     return dat;
  76. }

This is something I've been meaning to post for awhile. Finally had time to write it today... It contains functions that are designed to be nested for the purpose of rendering a small data set in a few different ways...

The upper left image is rendered with axis labels and lines... and it defaults to the color black (line 21):

axis(lines(graphData));

The upper right image is rendered with yellow axis and red dots (line 24):

axis(dots(graphData, 0xFF0000), 0xFFCC00, 2);

etc... (line 27)
axis(dots(dots(lines(lines(graphData, 0xCCCCCC, 20))), 0x0022FF, 0, 4), 0xFF);

Alternatively you could write each function call on one line:

lines(graphData, 0xCCCCCC, 20);
lines(graphData);
dots(graphData);
dots(graphData, 0x0022FF, 0, 4)
axis(graphData, 0xFF);

NOTE: If you think this post is insane, please read the warning page of this site...

Posted in Graphics, functions, misc | Tagged , | Leave a comment