Category Archives: matrix

~20,000 Rollovers

Actionscript:
  1. [SWF(width = 500, height = 500, frameRate = 30)]
  2.  
  3. var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xFFFFFF);
  4.  
  5. var indexCanvas:BitmapData = new BitmapData(stage.stage.stageWidth, stage.stageHeight, false,
  6.                                             0xFFFFFF);
  7. addChild(new Bitmap(canvas));
  8.  
  9. var s:Shape = new Shape();
  10.  
  11. var lineData:Array = [];
  12. var dataIndex:int = 0;
  13.  
  14. trace(0xFFFFFF - 1)
  15. var totalLines:int = 20000;
  16. var iterations:int = 9;
  17. var linesPerIter:int = totalLines / iterations;
  18.  
  19. var xp:int = stage.stageWidth / 2;
  20. var yp:int = stage.stageHeight / 2;
  21.  
  22. var stepAmt:Number = 60;
  23. var halfStepAmt:Number = stepAmt / 2;
  24.  
  25. addEventListener(Event.ENTER_FRAME, onDraw);
  26. function onDraw(evt:Event):void {
  27.      if (lineData.length <totalLines){
  28.         generateData(linesPerIter);
  29.      }else{
  30.         stage.quality = "high";
  31.         addChild(s);
  32.         s.x = 0;
  33.         s.y = 0;
  34.          
  35.         removeEventListener(Event.ENTER_FRAME, onDraw);
  36.         addEventListener(Event.ENTER_FRAME, onRun);
  37.      }
  38. }
  39.  
  40. function onRun(evt:Event):void {
  41.    var currentIndex:int = indexCanvas.getPixel(mouseX, mouseY);
  42.    var currentLine:Array = lineData[currentIndex];
  43.    
  44.    s.graphics.clear();
  45.    if (currentIndex != 0xFFFFFF){
  46.           s.graphics.lineStyle(3, 0xFF0000);
  47.           s.graphics.moveTo(currentLine[0], currentLine[1]);
  48.           s.graphics.lineTo(currentLine[2], currentLine[3]);  
  49.    }
  50. }
  51.  
  52. function generateData(num:int):void{
  53.     var rxA:int, rxB:int, ryA:int, ryB:int;
  54.     var g:Graphics = s.graphics;
  55.     for (var i:int = 0; i<num; i++){
  56.         rxA = xp;
  57.         ryA = yp;
  58.        
  59.         xp += Math.round(Math.random() * stepAmt) - halfStepAmt;
  60.         yp += Math.round(Math.random() * stepAmt) - halfStepAmt;
  61.        
  62.         if (xp> stage.stageWidth){
  63.             xp = stage.stageWidth - halfStepAmt;
  64.         }else
  65.         if (xp <0){
  66.             xp = halfStepAmt;
  67.         }
  68.         if (yp> stage.stageHeight){
  69.             yp = stage.stageHeight - halfStepAmt;
  70.         }else
  71.         if (yp <0){
  72.             yp = halfStepAmt;
  73.         }
  74.        
  75.         rxB = xp;
  76.         ryB = yp;
  77.          
  78.         lineData[dataIndex] = [rxA, ryA, rxB, ryB];            
  79.         s.x = rxA;
  80.         s.y = ryA;
  81.         var endX:Number = rxB - rxA;
  82.         var endY:Number = ryB - ryA;
  83.         var m:Matrix = s.transform.matrix;
  84.         g.clear();
  85.         g.lineStyle(1, 0x000000, 0.3);
  86.  
  87.         g.lineTo(endX, endY);
  88.         stage.quality = "high";
  89.         canvas.draw(s, m);
  90.        
  91.         g.clear();
  92.         g.lineStyle(3, dataIndex);
  93.        
  94.         g.lineTo(endX, endY);
  95.         stage.quality = "low";
  96.         indexCanvas.draw(s, m);
  97.        
  98.         dataIndex++
  99.     }
  100. }

I'm working on a data visualization that contains a long path made up of approximately one million points. There is some information associated with every two sets of coordinates that needs to be displayed when the user rolls their mouse over any part of the line.

I took a little time to think about the best way to do this and came up with a few techniques. The first one I tried seems to work nicely - this snippet is the proof of concept for that first technique. I tested this snippet with 1,000,000 xy coordinates and it works nicely. It takes a little while to draw though, so for the purposes of this demo I've just included 20,000 coordinates.

Have a look at the swf over at wonderfl.net

The way this works is by drawing lines to two different BitmapData instances. I draw anti-aliased slightly transparent lines to a BitmapData instance called "canvas" (this is added to the display list) - I then draw aliased lines to a BitmapData called "indexCanvas" (this is never added to the display list) - each aliased line uses an incremental value for its color - this incremental value is also the index for a two dimensional array containing the coordinate information for the aliased line. I use getPixel() on the "indexCanvas" and use the return value as the index for the 2D array. The data from the 2D array is used to draw a red line with the graphics class. This technique enables you to have many many rollovers and all you ever have to do is call getPixel() and use the returned color value to look up info about what you're mouse is touching.

There are a few cool ways this could be repurposed and this is really only one solution to the problem of having many many things that you need to be able to rollover... there are others that don't use BitmapData at all... I may write those up in the next couple of days.

Also posted in BitmapData, Data Structures, UI, arrays, display list, graphics algorithms, misc, pixel manipulation, return values | Tagged , , | 2 Comments

drawTriangles() Cubes

Actionscript:
  1. private function addCube(xp:Number, yp:Number, zp:Number, w:Number, h:Number, leng:Number):void{
  2.             var hw:Number = w * 0.5;
  3.             var hh:Number = h * 0.5;
  4.             var hl:Number = leng * 0.5;
  5.             var xA:Number = xp - hw;
  6.             var xB:Number = hw + xp;
  7.             var yA:Number = yp - hh;
  8.             var yB:Number = hh + yp;
  9.             var zA:Number = zp - hl;
  10.             var zB:Number = hl + zp;
  11.             _verts.push(xA, yA, zA,
  12.                         xB, yA, zA,
  13.                         xA, yB, zA,
  14.                         xB, yB, zA,
  15.                         xA, yA, zB,
  16.                         xB, yA, zB,
  17.                         xA, yB, zB,
  18.                         xB, yB, zB);
  19.            
  20.             var index:int = _boxIndex * 8;
  21.             var i0:int = index, i1:int = index + 1, i2:int = index + 2;
  22.             var i3:int = index + 3,  i4:int = index + 4, i5:int = index + 5;
  23.             var i6:int = index + 6, i7:int = index + 7;
  24.             _indices.push(i0, i1, i2,
  25.                           i1, i3, i2,
  26.                           i6, i7, i4,
  27.                           i7, i5, i4,
  28.                           i1, i5, i3,
  29.                           i7, i3, i5,
  30.                           i4, i5, i0,
  31.                           i1, i0, i5,
  32.                           i2, i6, i0,
  33.                           i0, i6, i4,
  34.                           i2, i3, i6,
  35.                           i3, i7, i6);
  36.                          
  37.             _faces.push(new Face(), new Face(), new Face(),
  38.                         new Face(),  new Face(), new Face(),
  39.                         new Face(), new Face(), new Face(),
  40.                         new Face(), new Face(), new Face());
  41.             _uvts.push(Math.random(), Math.random(), 0,
  42.                        Math.random(), Math.random(), 0,
  43.                        Math.random(), Math.random(), 0,
  44.                        Math.random(), Math.random(), 0,
  45.                        Math.random(), Math.random(), 0,
  46.                        Math.random(), Math.random(), 0,
  47.                        Math.random(), Math.random(), 0,
  48.                        Math.random(), Math.random(), 0);
  49.             _boxIndex++;
  50.         }

Lately I've been posting large code snippets... so today I'm highlighting part of a larger snippet - The above code is the heart of a small experiment I created this morning. It sets up a cube for use with drawTraingles().

The rest of the code can be read here:
Cubes3D.as

Have a look at the swf here...


I also put it on wonderfl...

Also posted in 3D, Graphics, Vector, motion | Tagged , , | 6 Comments

Matrix Zoom and Pan

Actionscript:
  1. [SWF(width=600, height=600, frameRate=30)]
  2. var sw:Number = stage.stageWidth;
  3. var sh:Number = stage.stageHeight;
  4.  
  5. var s:Shape = Shape(addChild(new Shape()));
  6.  
  7. var scale:Number = 1;
  8. var scaleDest:Number = 1;
  9. var down:Boolean = false;
  10. var dx:Number = 0, dy:Number = 0, time:Number = 0;
  11.  
  12. buttonMode = true;
  13.  
  14. addInstructions();
  15. vectorTexture();
  16.  
  17. stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
  18. stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
  19. addEventListener(Event.ENTER_FRAME, onLoop);
  20.  
  21. function addInstructions():void{
  22.     var instruct:Sprite = Sprite(addChild(new Sprite()));
  23.     with (instruct.graphics) beginFill(0x666666), drawRect(0,0,270, 30);
  24.     instruct.x = instruct.y = 20;
  25.     var txt:TextField = TextField(instruct.addChild(new TextField()));
  26.     txt.defaultTextFormat = new TextFormat("Verdana", 11);
  27.     txt.x = txt.y = 5;
  28.     txt.selectable = false;
  29.     with (txt) textColor = 0xFFFFFF, autoSize = "left", text = "Click and hold to zoom, move mouse to pan";
  30. }
  31.  
  32. function vectorTexture():void{
  33.     var cols:Vector.<uint> = Vector.<uint>([0xFFFFFF, 0x000000]);
  34.     var rnd:Vector.<Number> = new Vector.<Number>(6, true);
  35.    
  36.     for(var i:int = 0 ; i<50; i++){
  37.         with(s.graphics){
  38.             lineStyle(Math.random() * 50 + 2, cols[int(Math.random()*cols.length)]);
  39.             drawCircle(Math.random() * sw, Math.random() * sh, 10 + Math.random() * Math.random() * 400);
  40.         }
  41.     }
  42.     s.graphics.lineStyle(20, 0xCCCCCC);
  43.     s.graphics.drawRect(0, 0,sw, sh);
  44. }
  45.  
  46. function onDown(evt:MouseEvent):void{ down = true; }
  47. function onUp(evt:MouseEvent):void{ down = false; }
  48. function onLoop(evt:Event):void {
  49.    if (down){
  50.      scaleDest *= 1.05;  
  51.      time = 0;
  52.    }else{
  53.      time++;
  54.      // zoom out after 30 iterations
  55.      if (time == 30){
  56.          scaleDest = 1;  
  57.      }
  58.    }
  59.    scale += (scaleDest - scale) / 4;
  60.    if (scale> 10) scale = scaleDest = 10;
  61.    
  62.    dx += (mouseX - dx) / 4;
  63.    dy += (mouseY - dy) / 4;
  64.    if (dx <0) dx = 0;
  65.    if (dy <0) dy = 0;
  66.    if (dx> sw) dx = sw;
  67.    if (dy> sh) dy = sh;
  68.    
  69.    // matrix zoom/pan
  70.    var m:Matrix = s.transform.matrix;
  71.    m.identity();
  72.    m.translate(-dx,-dy);
  73.    m.scale(scale, scale);
  74.    m.translate(dx,dy);
  75.    s.transform.matrix = m;
  76. }

I haven't been by the computer much these last two weeks - been traveling. Going back to nyc tomorrow so I'll go back to posting once a day.

This snippet uses a transformation matrix to zoom in and pan a Sprite instance. For demo purposes I filled the sprite with a few circles - but you'd likely be using this with a vector image of a map, a floor plan or some other graphic that warrants zooming and panning.

Back around flash 7 (I think) before the Matrix class was introduced we used to have to use MovieClip nesting to achieve this effect.


Have a look at the swf...


Also posted in Graphics, motion | Tagged , , | Leave a comment

Fake Light w/ UV & Quads

Actionscript:
  1. [SWF(width = 500, height=500, backgroundColor = 0x000000)]
  2. x = stage.stageWidth / 2;
  3. y = stage.stageHeight / 2;
  4. var quadNum:int = 2200;
  5. // standard Vectors for using drawTriangles
  6. var verts:Vector.<Number> = new Vector.<Number>();
  7. var pVerts:Vector.<Number>;
  8. var uvts:Vector.<Number> = new Vector.<Number>();
  9. var indices:Vector.<int> = new Vector.<int>();
  10. // needed for z-sorting
  11. var sortedIndices:Vector.<int>;
  12. var faces:Array = [];
  13. // we'll use this for tranforming points
  14. // and as the transformation matrix for our render
  15. var m:Matrix3D = new Matrix3D();
  16. // plot a quad
  17. var quad:Vector.<Number>;
  18. quad = Vector.<Number>([-10, -10, 0,
  19.                         10, -10, 0,
  20.                         -10, 10, 0,
  21.                         10, 10, 0]);
  22. // temp vect for any transformed quads
  23. var transQuad:Vector.<Number> = new Vector.<Number>();
  24. var i:int;
  25. var inc:int = 0;
  26. for (i = 0; i<quadNum; i++){
  27.     m.identity();
  28.     var s:Number = (int(Math.random()*50) == 1) ? 2 + Math.random()*2 : .1 + Math.random() * 2;
  29.     m.appendScale(s, s, 1);
  30.     m.appendRotation(90, Vector3D.Y_AXIS);
  31.     var mult:Number = 100 + Math.random()*200;
  32.     m.appendTranslation(mult, 0, 0);
  33.     m.appendRotation(Math.random()*360, Vector3D.X_AXIS);
  34.     m.appendRotation(Math.random()*360, Vector3D.Y_AXIS);
  35.     m.appendRotation(Math.random()*360, Vector3D.Z_AXIS);
  36.     m.transformVectors(quad, transQuad);
  37.     verts = verts.concat(transQuad);
  38.     faces.push(new Vector3D());
  39.     faces.push(new Vector3D());
  40.     var i4:int = i * 4;
  41.     indices.push(0 + i4, 1 + i4, 2 + i4,
  42.                  1 + i4, 3 + i4, 2 + i4);
  43.     mult /= 300;
  44.     uvts.push(mult,mult,0,
  45.               mult+.1,mult,0,
  46.               mult,mult - .1,0,
  47.               mult + .1,mult + .1,0);
  48. }
  49. sortedIndices = new Vector.<int>(indices.length, true);
  50. // create texture
  51. var tex:BitmapData = new BitmapData(400,400,false, 0x000000);
  52. var grad:Shape = new Shape();
  53. var mat:Matrix = new Matrix();
  54. mat.createGradientBox(400,400,0,0,0);
  55. with (grad.graphics){
  56.     beginGradientFill(GradientType.LINEAR, [0xFFFFFF,0x002244], [1, 1], [100, 255], mat);
  57.     drawRect(0,0,400,400);
  58. }
  59. tex.draw(grad);
  60. // create background
  61. mat.createGradientBox(1600,1200,0,-550, 0);
  62. with (grad.graphics){
  63.     beginGradientFill(GradientType.RADIAL, [0x000000, 0x001133], [1, 1], [0, 255], mat);
  64.     drawRect(0,0,500,500);
  65. }
  66. grad.x = -stage.stageWidth/2
  67. grad.y = -stage.stageHeight/2;
  68. addChild(grad);
  69. // triangles will be drawn to this
  70. var render:Shape = Shape(addChild(new Shape()));
  71. // fix all vector lengths
  72. verts.fixed = true, uvts.fixed = true, indices.fixed = true
  73. pVerts = new Vector.<Number>(verts.length/3 * 2, true);
  74. // we need these if we want perspective
  75. var persp:PerspectiveProjection = new PerspectiveProjection();
  76. persp.fieldOfView = 45;
  77. // projection matrix
  78. var proj:Matrix3D = persp.toMatrix3D();
  79. var dx:Number = 0, dy:Number = 0;
  80. addEventListener(Event.ENTER_FRAME, onLoop);
  81. function onLoop(evt:Event):void {
  82.     dx += (mouseX - dx) / 4;
  83.     dy += (mouseY - dy) / 4;
  84.     m.identity();
  85.     m.appendRotation(dy, Vector3D.X_AXIS);
  86.     m.appendRotation(dx, Vector3D.Y_AXIS);
  87.     m.appendTranslation(0,0,800);
  88.     m.append(proj);
  89.     Utils3D.projectVectors(m, verts, pVerts, uvts);
  90.     var face:Vector3D;
  91.     inc = 0;
  92.     for (var i:int = 0; i<indices.length; i+=3){
  93.         face = faces[inc];
  94.         face.x = indices[i];
  95.         face.y = indices[int(i + 1)];
  96.         face.z = indices[int(i + 2)];
  97.         var i3:int = i * 3;
  98.         face.w = (uvts[int(face.x*3 + 2)] + uvts[int(face.y*3 + 2)] + uvts[int(face.z*3 + 2)]) * 0.333333;
  99.         inc++;
  100.     }
  101.     faces.sortOn("w", Array.NUMERIC);
  102.     inc = 0;
  103.     for each (face in faces){
  104.         sortedIndices[inc++] = face.x;
  105.         sortedIndices[inc++] = face.y;
  106.         sortedIndices[inc++] = face.z;
  107.     }
  108.     render.graphics.clear();
  109.     render.graphics.beginBitmapFill(tex, null, false, false);
  110.     render.graphics.drawTriangles(pVerts, sortedIndices, uvts, TriangleCulling.NEGATIVE);
  111. }

Expanding again on the z-sorting demos from the last few days - this snippet uses UV coords and a gradient to create something that vaguely resembles a light. Unlike the previous demos, the 3D cluster in this snippet is made up of quads - requiring a slight adjustment to the depth calculation.


Have a look at the swf...

I also posted the code over at wonderfl.net...

Also posted in 3D, Graphics, Vector, misc, sortOn | Tagged , , , | 2 Comments

More z-sorting

Actionscript:
  1. [SWF(width = 500, height=500, backgroundColor = 0x333333)]
  2. x = stage.stageWidth / 2;
  3. y = stage.stageHeight / 2;
  4.  
  5.  
  6. var polyNum:int = 3000;
  7. // standard Vectors for using drawTriangles
  8. var verts:Vector.<Number> = new Vector.<Number>();
  9. var pVerts:Vector.<Number>;
  10. var uvts:Vector.<Number> = new Vector.<Number>();
  11. var indices:Vector.<int> = new Vector.<int>();
  12. // needed for z-sorting
  13. var sortedIndices:Vector.<int>;
  14. var faces:Array = [];
  15.  
  16. // we'll use this for tranforming points
  17. // and as the transormation matrix for our render
  18. var m:Matrix3D = new Matrix3D();
  19.  
  20. // plot a poly
  21. var poly:Vector.<Number>;
  22. poly = Vector.<Number>([ 0, 0, 0,
  23.                         10, 0, 0,
  24.                         0,10, 0]);
  25.  
  26. // temp vect for any transformed polygons
  27. var transPoly:Vector.<Number> = new Vector.<Number>();
  28.  
  29. var i:int;
  30. var inc:int = 0;
  31. for (i = 0; i<polyNum; i++){
  32.     m.identity();
  33.     var s:Number = (int(Math.random()*50) == 1) ? 4 + Math.random()*2 : 1 + Math.random() * 2;
  34.     m.appendScale(s, s, 1);
  35.     m.appendRotation(Math.random()*360, Vector3D.X_AXIS);
  36.     m.appendRotation(Math.random()*360, Vector3D.Y_AXIS);
  37.     m.appendRotation(Math.random()*360, Vector3D.Z_AXIS);
  38.     m.appendTranslation(200,0,0);
  39.                          
  40.     m.appendRotation(Math.random()*360, Vector3D.X_AXIS);
  41.     m.appendRotation(Math.random()*360, Vector3D.Y_AXIS);
  42.     m.appendRotation(Math.random()*360, Vector3D.Z_AXIS);
  43.    
  44.     m.transformVectors(poly, transPoly);
  45.    
  46.     verts = verts.concat(transPoly);
  47.     faces.push(new Vector3D());
  48.     indices.push(inc++, inc++, inc++);
  49.     uvts.push(Math.random(), Math.random(), 0, Math.random(), Math.random(), 0, Math.random(), Math.random(), 0);
  50. }
  51.  
  52. sortedIndices = new Vector.<int>(indices.length, true);
  53.  
  54. // create texture
  55. var tex:BitmapData = new BitmapData(400,400,false, 0x000000);
  56. var grad:Shape = new Shape();
  57. var mat:Matrix = new Matrix();
  58. mat.createGradientBox(400,400,0,0,0);
  59. with (grad.graphics){
  60.     beginGradientFill(GradientType.LINEAR, [0x000000, 0xAA0000, 0xFFFF00], [1, 1, 1], [20, 200, 255], mat);
  61.     drawRect(0,0,400,400);
  62. }
  63. tex.draw(grad);
  64.  
  65. // create background
  66. mat.createGradientBox(1600,1000,0,-550, -100);
  67. with (grad.graphics){
  68.     beginGradientFill(GradientType.RADIAL, [0x333333, 0xffffff], [1, 1], [0, 255], mat);
  69.     drawRect(0,0,500,500);
  70. }
  71. grad.x = -stage.stageWidth/2
  72. grad.y = -stage.stageHeight/2;
  73. addChild(grad);
  74.  
  75. // triangles will be drawn to this
  76. var render:Shape = Shape(addChild(new Shape()));
  77.  
  78. // fix all vector lengths
  79. verts.fixed = true, uvts.fixed = true, indices.fixed = true
  80. pVerts = new Vector.<Number>(verts.length/3 * 2, true);
  81.  
  82. // we need these if we want perspective
  83. var persp:PerspectiveProjection = new PerspectiveProjection();
  84. persp.fieldOfView = 45;
  85. // projection matrix
  86. var proj:Matrix3D = persp.toMatrix3D();
  87.  
  88. var dx:Number = 0, dy:Number = 0;
  89. addEventListener(Event.ENTER_FRAME, onLoop);
  90. function onLoop(evt:Event):void {
  91.     dx += (mouseX - dx) / 4;
  92.     dy += (mouseY - dy) / 4;
  93.     m.identity();
  94.     m.appendRotation(dy, Vector3D.X_AXIS);
  95.     m.appendRotation(dx, Vector3D.Y_AXIS);
  96.     // push everything back so its not to close
  97.     m.appendTranslation(0,0,800);
  98.     // append the projection matrix at the end
  99.     m.append(proj);
  100.    
  101.     Utils3D.projectVectors(m, verts, pVerts, uvts);
  102.    
  103.     var face:Vector3D;
  104.     inc = 0;
  105.     for (var i:int = 0; i<indices.length; i+=3){
  106.         face = faces[inc];
  107.         face.x = indices[i];
  108.         // it may look odd, but casting to an int
  109.         // when doing operations inside array sytnax
  110.         // adds a big speed boost
  111.         face.y = indices[int(i + 1)];
  112.         face.z = indices[int(i + 2)];
  113.         var i3:int = i * 3;
  114.         // get the average z position (t value) and store it in the Vector3D w property
  115.         // depending on your model, you may not need to do an average of all 3 values
  116.         face.w = (uvts[int(i3 + 2)] + uvts[int(i3 + 5)] + uvts[int(i3 + 8)]) * 0.333333;
  117.         inc++;
  118.     }
  119.      
  120.     // sort on w, so far this beats all other sorting methods for speed,
  121.     // faster than Vector.sort(), faster than any custom sort method I could find
  122.     faces.sortOn("w", Array.NUMERIC);
  123.  
  124.     // re-order indices so that faces are drawn in the correct order (back to front);
  125.     inc = 0;
  126.     for each (face in faces){
  127.         sortedIndices[inc++] = face.x;
  128.         sortedIndices[inc++] = face.y;
  129.         sortedIndices[inc++] = face.z;
  130.     }
  131.    
  132.     render.graphics.clear();
  133.     render.graphics.beginBitmapFill(tex, null, false, false);
  134.     render.graphics.drawTriangles(pVerts, sortedIndices, uvts, TriangleCulling.NONE);
  135. }

This is a slightly more advanced version of the z-sorting demo from yesterday. Here 3000 polygons are randomly arranged in a sphere formation...


Have a look at the swf...

Also posted in 3D, Graphics, Vector, misc | Tagged , , , , | 6 Comments

drawTriangles() & z-sorting

Actionscript:
  1. [SWF(width = 500, height=500, backgroundColor = 0x000000)]
  2. x = stage.stageWidth / 2;
  3. y = stage.stageHeight / 2;
  4.  
  5. // standard Vectors for using drawTriangles and projectVectors
  6. var verts:Vector.<Number> = new Vector.<Number>();
  7. var pVerts:Vector.<Number>;
  8. var uvts:Vector.<Number>;
  9. var indices:Vector.<int>;
  10. // needed for z-sorting
  11. var sortedIndices:Vector.<int>;
  12. var faces:Array = [];
  13.  
  14. // we'll use this for transforming points
  15. // and as the transformation matrix for our render
  16. var m:Matrix3D = new Matrix3D();
  17.  
  18. // plot a poly
  19. var poly:Vector.<Number>;
  20. poly = Vector.<Number>([ 0, 0, 0,
  21.                         10, 0, 0,
  22.                         0,10, 0]);
  23.  
  24. verts = verts.concat(poly);
  25. faces.push(new Vector3D());
  26.  
  27. // add two more polygons by transorming the poly Vector
  28. // and concatenating it to the verts Vector
  29. // Vector3D instances are added to the faces
  30. // Array for z-sorting
  31.  
  32. // temp vect for any transformed polygons
  33. var transPoly:Vector.<Number> = new Vector.<Number>();
  34.  
  35. m.appendRotation(10, Vector3D.Z_AXIS);
  36. m.appendTranslation(0,-2,5);
  37. m.transformVectors(poly, transPoly);
  38.  
  39. verts = verts.concat(transPoly);
  40. faces.push(new Vector3D());
  41.  
  42. m.appendRotation(10, Vector3D.Z_AXIS);
  43. m.appendTranslation(0,-2,5);
  44. m.transformVectors(poly, transPoly);
  45. verts = verts.concat(transPoly);
  46. faces.push(new Vector3D());
  47.  
  48. // hard coded indices
  49. indices = Vector.<int>([0, 1, 2, 3, 4, 5, 6, 7, 8]);
  50. sortedIndices = new Vector.<int>(indices.length, true);
  51.  
  52. // create texture
  53. var tex:BitmapData = new BitmapData(100,100,false, 0x000000);
  54. tex.fillRect(new Rectangle(0,0,50,50), 0xFF0000);
  55. tex.fillRect(new Rectangle(50,0,50,50), 0x0000FF);
  56. tex.fillRect(new Rectangle(0,50,50,50), 0x00FF00);
  57.  
  58. // hard coded uvts
  59. uvts = Vector.<Number>([0,0,0, .5,0,0, 0,.5,0,
  60.                         .53, 0, 0, 1, 0, 0, .53, .5, 0,
  61.                          0,.6,.5, 0,.6,0, 0,1, 0]);
  62.                        
  63. // fix all vector lengths
  64. verts.fixed = true, uvts.fixed = true, indices.fixed = true
  65. pVerts = new Vector.<Number>(verts.length/3 * 2, true);
  66.  
  67. // we need these if we want perspective
  68. var persp:PerspectiveProjection = new PerspectiveProjection();
  69. // projection matrix
  70. var proj:Matrix3D = persp.toMatrix3D();
  71.  
  72. addEventListener(Event.ENTER_FRAME, onLoop);
  73. function onLoop(evt:Event):void {
  74.     m.identity();
  75.     m.appendRotation(mouseY * 2, Vector3D.X_AXIS);
  76.     m.appendRotation(mouseX * 2, Vector3D.Y_AXIS);
  77.     // push everything back so its not too close
  78.     m.appendTranslation(0,0,40);
  79.     // append the projection matrix at the end
  80.     m.append(proj);
  81.    
  82.     Utils3D.projectVectors(m, verts, pVerts, uvts);
  83.    
  84.     var face:Vector3D;
  85.     var inc:int = 0;
  86.     for (var i:int = 0; i<indices.length; i+=3){
  87.         face = faces[inc];
  88.         face.x = indices[i];
  89.         // it may look odd, but casting to an int
  90.         // when doing operations inside array sytnax
  91.         // adds a big speed boost
  92.         face.y = indices[int(i + 1)];
  93.         face.z = indices[int(i + 2)];
  94.         var i3:int = i * 3;
  95.         // get the average z position (t value) and store it in the Vector3D w property
  96.         // depending on your model, you may not need to do an average of all 3 values
  97.                 // (t is the distance from the eye to the texture)
  98.         face.w = (uvts[int(i3 + 2)] + uvts[int(i3 + 5)] + uvts[int(i3 + 8)]) * 0.333333;
  99.         inc++;
  100.     }
  101.    
  102.     // sort on w, so far this beats all other sorting methods for speed,
  103.     // faster than Vector.sort(), faster than any custom sort method I could find
  104.     faces.sortOn("w", Array.NUMERIC);
  105.    
  106.     // re-order indices so that faces are drawn in the correct order (back to front);
  107.     inc = 0;
  108.     for each (face in faces){
  109.         sortedIndices[inc++] = face.x;
  110.         sortedIndices[inc++] = face.y;
  111.         sortedIndices[inc++] = face.z;
  112.     }
  113.    
  114.     graphics.clear();
  115.     graphics.beginBitmapFill(tex, null, false, false);
  116.     graphics.drawTriangles(pVerts, sortedIndices, uvts, TriangleCulling.NONE);
  117. }

So you want to be able to do some z-sorting with drawTriangles()? Well, its extremely unintuitive. Here is what you need to know:

1) Vector.sort() is slow
2) Array.sortOn() is quite fast
3) I couldn't get any custom sorting method to be faster than Array.sortOn
4) You can use the t value from uvts to do z-sorting (like in this demo)
5) If your going for speed, don't forget to use fixed Vectors (I often forget this step)
6) When working with Vectors cast any operations inside brackets to int() (my tests showed this is a huge speed boost up to 3x faster - franky I think it's horrible that you have to do this).

The swf is not very interesting because I wanted to keep it super simple, but here it is anyway...


Questions

I was told and have read that drawTriangles is slower than using drawGraphicsData and a GraphicsTrianglePath instance. I tested this extensively and found that drawGraphicsData was slower. I'm still not sure which is faster but am up for checking out any simple benchmarks that people have.

drawTriangles is supposed to be faster without adding t values to the uvts Vector. In my benchmark tests there was no real difference in speed. The t value is used to render more accurate textures - which would mean you could potentially use less triangles in your models. One would assume that not using a t value would be faster... but I couldn't confirm this. Again if someone has a good benchmark for this, please let me know.

Help

I had a great deal of help from a few people on Twitter, Katopz (Away3D contributor) of sleepydesign.com made me aware of the fact that Array.sortOn is the way to go... You can see some great demos over at Katopz's blog. While I didn't end up using a custom sorting method, Inspirit
pointed me to his article about custom Vector sorting methods which proved to be very enlightening. I was able to get some really great results for doing different types of sorting - just sorting 1D Vectors with FlashSort can be very fast (faster than Array.sortOn - but not helpful for sorting a 1D Vector of indices).

I also learned a good deal by looking at this code post by Psyark... I actually optimized that code to run about 2x faster (will post that on wonderfl.net at some point, it just needs to be cleaned up).

kevinSuttle sent me a link to The video

Also posted in 3D, Graphics, Vector | Tagged , , , , | 3 Comments

Spring PerspectiveProjection

Actionscript:
  1. var pointNum:int = 3000;
  2. x = stage.stageWidth / 2;
  3. y = stage.stageHeight / 2;
  4.  
  5. var verts:Vector.<Number>  = new Vector.<Number>();
  6. var tVerts:Vector.<Number> = new Vector.<Number>();
  7. var pVerts:Vector.<Number> = new Vector.<Number>();
  8. var uv:Vector.<Number> = new Vector.<Number>();
  9. var cmds:Vector.<int> = new Vector.<int>();
  10.  
  11. var index:int = 0;
  12. var half:Number = pointNum / 20000;
  13. for (var i:int = 0; i<pointNum; i+=3){
  14.     verts[i] = 0.08 * Math.cos(i * Math.PI / 180);
  15.     verts[i+1] = 0.08 * Math.sin(i * Math.PI / 180);
  16.     verts[i+2] =  i / 10000 - half;
  17.     cmds[index++] = 2;
  18. }
  19. cmds[0] = 1;
  20.  
  21. var proj:PerspectiveProjection = new PerspectiveProjection();
  22. proj.fieldOfView = 45;
  23. var projMat:Matrix3D = proj.toMatrix3D();
  24. var m:Matrix3D = new Matrix3D();
  25. var dx:Number = 0, dy:Number = 0;
  26. addEventListener(Event.ENTER_FRAME, onLoop);
  27. function onLoop(evt:Event):void {
  28.        m.identity();
  29.        dx += (mouseX - dx) / 4;
  30.        dy += (mouseY - dy) / 4;
  31.        m.appendRotation(dy,Vector3D.X_AXIS);
  32.        m.appendRotation(dx,Vector3D.Y_AXIS);
  33.        m.appendTranslation(0,0,0.5);
  34.        m.transformVectors(verts, tVerts);
  35.        Utils3D.projectVectors(projMat, tVerts, pVerts, uv);
  36.        graphics.clear();
  37.        graphics.lineStyle(3,0x000000);
  38.        graphics.drawPath(cmds, pVerts);
  39. }

This snippet draws a spring shape in 3D with perspective. This is the first snippet where I've made use of the PerspectiveProjection class. So if your wondering how to add perspective to your Utils3D.projectVectors code... this is one way to do it...


Have a look at the swf...

Also posted in 3D, Graphics, Video | Tagged , , | 1 Comment

Quick Pixel Sphere

Actionscript:
  1. var pointNum:int = 20000;
  2. var radius:int = 150;
  3.  
  4. var canvas:BitmapData = new BitmapData(400,400,false, 0x000000);
  5. addChild(new Bitmap(canvas));
  6. var verts:Vector.<Number>  = new Vector.<Number>();
  7. var pVerts:Vector.<Number> = new Vector.<Number>();
  8. var uv:Vector.<Number> = new Vector.<Number>();
  9.  
  10. for (var i:int = 0; i<pointNum; i+=3){
  11.     var xp:Number = Math.random() * 400 - 200;
  12.     var yp:Number = Math.random() * 400 - 200;
  13.     var zp:Number = Math.random() * 400 - 200;
  14.     var dist:Number = Math.sqrt(xp * xp + yp * yp + zp * zp);
  15.     // normalize and scale x,y,z
  16.     verts[i] = xp / dist * radius;
  17.     verts[i+1] = yp / dist * radius;
  18.     verts[i+2] = zp / dist * radius;
  19. }
  20.  
  21. var m:Matrix3D = new Matrix3D();
  22. var dx:Number = 0, dy:Number = 0;
  23. addEventListener(Event.ENTER_FRAME, onLoop);
  24. function onLoop(evt:Event):void {
  25.        m.identity();
  26.        dx += (mouseX - dx) / 4;
  27.        dy += (mouseY - dy) / 4;
  28.        m.appendRotation(dx, Vector3D.X_AXIS);
  29.        m.appendRotation(dy, Vector3D.Y_AXIS);
  30.        m.appendTranslation(200,200,0);
  31.        Utils3D.projectVectors(m, verts, pVerts, uv);
  32.        canvas.fillRect(canvas.rect, 0x000000);
  33.        for (var i:int = 0; i<pVerts.length; i+=2){
  34.          canvas.setPixel(pVerts[i], pVerts[i + 1], 0xFFFFFF);
  35.        }
  36. }

This snippet shows a quick way to randomly place a bunch of xyz coordinates on the surface of a sphere. I saw this trick in an OpenGL book a few years back - dug around my books but couldn't find it... If I find it I'll update this post.

The trick is achieved by normalizing the vector defined by each 3D coordinate...


Have a look at the swf...

Also posted in 3D, BitmapData, Math, setPixel | Tagged , , | 4 Comments

Another Matrix.transformPoint()

Actionscript:
  1. [SWF(width = 628, height=500)]
  2. var trans:Matrix = new Matrix();
  3. var pnt:Point = new Point();
  4. x = stage.stageWidth / 2;
  5. y = stage.stageHeight / 2;
  6. addEventListener(Event.ENTER_FRAME, onLoop);
  7. function onLoop(evt:Event):void {
  8.     graphics.clear();
  9.     graphics.lineStyle(10, 0x000000, 1, false, LineScaleMode.NORMAL, CapsStyle.SQUARE);
  10.     pnt.x = 0;
  11.     pnt.y = 0;
  12.     trans.identity();
  13.     trans.translate(15,15);
  14.     trans.rotate(mouseX/10 * Math.PI / 180);
  15.     graphics.moveTo(pnt.x, pnt.y);
  16.     for (var i:int = 0; i<12; i++){
  17.         pnt = trans.transformPoint(pnt);
  18.         graphics.lineTo(pnt.x, pnt.y);
  19.     }
  20. }

I was thinking about processing and OpenGL today... I always really enjoyed using matrix stuff, working with the matrix stack, using push() and pop() etc... Understanding how to use matrices is pretty important when your doing advanced graphics in actionscript. If you haven't messed with matrices, this snippet is a good place to start. It uses Matrix.transformPoint() to draw a series of connected lines that are controlled by the x location of the mouse. Back before flash 8, this kind of thing was usually done using trig or MovieClip nesting...

I posted another snippet on this topic awhile back.

Also posted in motion | 2 Comments

Utils3D.projectVectors() Lathe

Actionscript:
  1. [SWF(width = 500, height = 500, backgroundColor = 0x000000)]
  2. var halfWidth:Number = stage.stageWidth / 2;
  3. var halfHeight:Number = stage.stageHeight / 2;
  4. var loc:Vector.<Number>;
  5.  
  6. graphics.lineStyle(0, 0xFF0000);
  7. graphics.moveTo(halfWidth, 0);
  8. graphics.lineTo(halfWidth, stage.stageHeight);
  9. graphics.moveTo(0, halfHeight);
  10. graphics.lineTo(stage.stageWidth, halfHeight);
  11.  
  12. var line:Shape = Shape(addChild(new Shape()));
  13. line.x = halfWidth;
  14.  
  15. var idle:Function = function(){};
  16. var currentMode:Function = idle;
  17. stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
  18. stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
  19. addEventListener(Event.ENTER_FRAME, onLoop);
  20. function onDown(evt:MouseEvent):void{
  21.     if (contains(frame)){
  22.         removeChild(frame);
  23.         currentMode = idle;
  24.         line.graphics.clear();
  25.         return;
  26.     }
  27.     loc = new Vector.<Number>();
  28.     line.graphics.lineStyle(0,0xFFFFFF);
  29.     line.x = halfWidth
  30.     line.y = halfHeight;
  31.     line.graphics.moveTo(line.mouseX, line.mouseY);
  32.     currentMode = captureLocs;
  33.     canvas.fillRect(canvas.rect, 0x000000);
  34. }
  35. function onUp(evt:MouseEvent):void{
  36.     if (currentMode == idle) return;
  37.     setupLathe();
  38.     currentMode = showLathe;
  39. }
  40.  
  41. function onLoop(evt:Event):void{
  42.     currentMode();
  43. }
  44. function captureLocs():void{
  45.     loc.push(line.mouseX);
  46.     loc.push(line.mouseY);
  47.     loc.push(0);
  48.     line.graphics.lineTo(line.mouseX, line.mouseY);
  49. }
  50. /**
  51.  -- Lathe Stuff:
  52. */
  53. var canvas:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight,false, 0x000000);
  54. var frame:Bitmap = new Bitmap(canvas);
  55. var dx:Number=0;
  56. var dy:Number=0;
  57.  var matrix:Matrix3D = new Matrix3D();
  58. var pVerts:Vector.<Number>;
  59. var uvts:Vector.<Number>;
  60. function setupLathe():void{
  61.     addChild(frame);
  62.     pVerts =  new Vector.<Number>();
  63.     uvts = new Vector.<Number>();
  64.     var nVerts:Vector.<Number> = new Vector.<Number>();
  65.     var tVerts:Vector.<Number> = new Vector.<Number>();
  66.     matrix.identity();
  67.     var step:Number = 2;
  68.     for (var i:int = 0; i <360; i+=step){
  69.         matrix.appendRotation(step,Vector3D.Y_AXIS);
  70.         matrix.transformVectors(loc, tVerts);
  71.         nVerts = nVerts.concat(tVerts);
  72.     }
  73.     loc = nVerts.concat();
  74. }
  75. function showLathe():void{
  76.     dx += (mouseX - dx)/4;
  77.     dy += (mouseY - dy)/4;
  78.     matrix.identity();
  79.     matrix.appendRotation(dy,Vector3D.X_AXIS);
  80.     matrix.appendRotation(dx,Vector3D.Y_AXIS);
  81.     matrix.appendTranslation(halfWidth, halfHeight, 0);
  82.     Utils3D.projectVectors(matrix, loc, pVerts, uvts);
  83.     canvas.lock();
  84.     canvas.fillRect(canvas.rect, 0x000000);
  85.     var leng:int = pVerts.length;
  86.     for (var i:int = 0; i<leng; i+=2){
  87.         canvas.setPixel( pVerts[i], pVerts[i + 1], 0xFFFFFF);
  88.     }
  89.     canvas.unlock();
  90. }

This snippet allows you to create 3D lathe shapes by drawing a 2D line. This is done using Utils3D.projectVectors() and Matrix.transformVectors().

Have a look at the swf here...

Also posted in 3D, BitmapData, Math, Vector, graphics algorithms, setPixel | Tagged , , | 3 Comments