Monthly Archives: June 2009

XML to ActionScript #2

Actionscript:
  1. var script:XML=<code>
  2.  
  3.    <make reference="blur" class="flash.filters.BlurFilter">
  4.      <set blurX="10" blurY="10" />
  5.    </make>
  6.    
  7.    <make reference="filts" class="Array">
  8.      <call method="push">
  9.        <arg reference="blur" />
  10.      </call>
  11.    </make>
  12.    
  13.    <make reference="mat" class="flash.geom.Matrix">
  14.      <call method="rotate" args="1" />
  15.      <set tx="100" ty="100" />
  16.    </make>
  17.    
  18.    <make reference="box" class="flash.display.Sprite">
  19.        <setRef filters="filts" transform.matrix="mat"/>
  20.        <call method="graphics.beginFill" args="0x000000" />
  21.        <call method="graphics.drawRect" args="-50,-50,100,100" />
  22.    </make>
  23.    
  24.    <call method="addChild">
  25.      <arg reference="box"/>
  26.    </call>
  27.    
  28.    <make reference="tf" class="flash.text.TextFormat">
  29.     <set font="_sans" size="12" color="0xFFFFFF" />
  30.    </make>
  31.  
  32.    <make reference="txt" class="flash.text.TextField">
  33.      <setRef defaultTextFormat="tf" />
  34.      <set autoSize="center" text="XML to AS3" />
  35.    </make>
  36.  
  37.    <make reference="circle" class="flash.display.Sprite">
  38.        <set x="300" y="300" />
  39.        <call method="graphics.beginFill" args="0xFF0000" />
  40.        <call method="graphics.drawCircle" args="0,0,100" />
  41.        <call method="addChild">
  42.          <arg reference="txt" />
  43.        </call>
  44.    </make>
  45.    
  46.    <call method="addChild">
  47.      <arg reference="circle"/>
  48.    </call>
  49.  
  50. </code>
  51.  
  52. // parse and run
  53. runCode(this, script);
  54.  
  55. function runCode(target:*, code:XML):void{
  56.     var children:XMLList = code.children();
  57.     for (var i:int = 0; i<children.length(); i++){
  58.         var child:XML = children[i];
  59.         var type:String = child.name();
  60.         if (type == "call"){
  61.             runMethod(target, child);
  62.         }else if (type == "set"){
  63.             setProp(target, child);
  64.         }else if (type == "setRef"){
  65.             setRefProp(target, child);
  66.         }else if (type == "make"){
  67.             makeInstance(child);
  68.         }
  69.     }
  70. }
  71.  
  72. function makeInstance(code:XML):void{
  73.     var ClassRef:Class = getDefinitionByName(code.@["class"]) as Class;
  74.     // nothing can be passed to the class constructor
  75.     // its not possible with function.apply
  76.     var instance:* = this[code.@reference] = new ClassRef();
  77.     runCode(instance, code);
  78. }
  79.  
  80. // set a property to a reference
  81. function setRefProp(target:*, code:XML):void{
  82.     var attributes:XMLList = code.attributes();
  83.     for (var i:int = 0; i<attributes.length(); i++){
  84.         var prop:String = attributes[i].name();
  85.         var o:Object = dotSyntax(target, prop, 1)
  86.         if (prop.indexOf(".") != -1){
  87.           o.obj[o.prop] = dotSyntax(this, attributes[i]);
  88.         }else{
  89.           o.obj[o.prop] = this[attributes[i]];
  90.         }
  91.     }
  92. }
  93.  
  94. // set a property to a value such as a Number, Boolean, String etc...
  95. function setProp(target:*, code:XML):void{
  96.     var attributes:XMLList = code.attributes();
  97.     for (var i:int = 0; i<attributes.length(); i++){
  98.         var prop:String = attributes[i].name();
  99.         var o:Object = dotSyntax(target, prop, 1)
  100.         o.obj[o.prop] = valueType(attributes[i]);
  101.     }
  102. }
  103.  
  104. function runMethod(target:*, code:XML):void{
  105.     var i:int = 0;
  106.     // get a reference to the function
  107.     var method:Function = dotSyntax(target, code.@method);
  108.     // call the function if there are no arguments
  109.     var attributeArgsLength:int = code.@args.toXMLString().length;
  110.     var childArgsLength:int = code.arg.length()
  111.     if (attributeArgsLength == 0 && childArgsLength == 0){
  112.         method();
  113.     }else{
  114.      
  115.      var args:Array;
  116.      if (attributeArgsLength> 0){
  117.          args = code.@args.toString().split(",");
  118.      } else {
  119.         args = [];
  120.      }
  121.    
  122.      for (i = 0; i<childArgsLength; i++){
  123.           var val:String = code.arg[i].@value;
  124.           if (val){
  125.             args.push(val);
  126.           }
  127.       }
  128.       for (i = 0; i<args.length; i++){
  129.           if (args[i] != ""){
  130.             args[i] = valueType(args[i]);
  131.           }
  132.       }
  133.       for (i = 0; i<code.arg.length(); i++){
  134.          var rs:String = code.arg[i].@reference;
  135.          if (rs){
  136.              args.push(dotSyntax(this, rs));  
  137.          }
  138.       }
  139.      
  140.       // run the function
  141.       method.apply(null,args);
  142.     }
  143. }
  144.  
  145. // parse dot syntax and return the last property or method
  146. function dotSyntax(target:*, str:String, offset:int = 0):*{
  147.     var path:Array = str.split(".");
  148.     var curr:* = target;
  149.     for (var i:int = 0; i<path.length - offset; i++){
  150.         curr = curr[path[i]]
  151.     }
  152.     if (offset != 0){
  153.         return {obj:curr, prop:path[i]}
  154.     }
  155.     return curr;
  156. }
  157.  
  158. function valueType(val:*):* {
  159.     if (isNaN(Number(val))) {
  160.            // remove leading and trailing white
  161.            // remove "" around strings
  162.            val = val.replace(/^s+|s+$/g,"").replace(/^"|"$/g,"");
  163.            //  see if it's a boolean
  164.            if (val == "true"){
  165.                val = true;
  166.            }else if (val == "false"){
  167.                val = false
  168.            }
  169.     } else {
  170.         val = Number(val);
  171.     }
  172.     return val;
  173. }

WARNING: There area few small bugs in this snippet. If you'd like to use this, check out the AsXML mini-library

This is the next version of yesterdays snippet. As you can see it isn't really a snippet anymore... This code parses a specifically formatted xml file to allow the following features to be achieved from XML at runtime:

1) call methods of the main timeline
2) read and write properties on the main timeline
3) instantiate classes on the main timeline
4) call methods on these classes
5) read and write properties on these classes

By implement those five feature a great deal becomes possible. The above XML creates this somewhat crappy looking thing:

While that doesn't look like much, it's actually doing quite a bit... It has a BlurFilter, an Array (for the filters property), two Sprites, a TextFormat and a TextField. addChild() is called on the main timeline and on one of the sprites (to nest the TextField in the circle).

Uses For This

You could use this to create levels for a simple game.

You could generate this XML based on user input to create e-cards and mini-apps.

If you have a medium sized app you could use it to create a sort of advanced config file that helps to ease your pain as the client decides they need 10 subtly different versions of the app. Every time the client decides they need a subtly different version you'd just need to create a different config file. THIS is probably the thing that I'll be using it for - could be a huge time saver...

Uses With Libraries

This could be used with TweenLite and/or Papervision. You could just add a bunch of import statements to the timeline or to a dynamic document class. Then you'd be able to do some basic authoring from the XML. I think I'll post an example of this either tonight or tomorrow.

I did originally start writing this thinking about QuickBox2D - but I already have an editor for QuickBox2D that just needs some UI (currently its key controlled). So I don't really need it. The QuickBox2D editor generates an ActionScript file - which makes sense because usually you need to go in and manually do a bunch of logic that couldn't really be done easily with an editor. That said, this will work with Box2D or QuickBox2D as an XML format.

The Downside

The downside is that this is dynamic and is therefore untyped. All class instances created by reading the XML will be dynamically typed. For something where your only adding 10 classes this way, its no big deal - but for a game editor it could be a problem - at the very least there are interesting advanced techniques implemented in this snippet that could be repurposed for lots of different things.

More Examples

Tonight or tomorrow I'll upload a few examples - it would be fun to do a TweenLite/Papervision example... would also be good to show what I mean by an advanced config for a medium sized app.

Posted in XML, dynamic, external data, instantiation | Tagged , , | 2 Comments

XML to ActionScript

Actionscript:
  1. // this xml is inline but could easily be in an external file
  2. var script:XML=<code>
  3.  
  4.   <!-- // call some methods of the main timeline graphics property -->
  5.   <call method="graphics.beginFill" args="0xFF0000" />
  6.    
  7.   <call method="graphics.drawRect" args="0, 0">
  8.      <!-- // use this to access properties of the main movie -->
  9.      <!-- // and use them as arguments -->
  10.      <arg reference="stage.stageWidth" />
  11.      <arg reference="stage.stageHeight" />
  12.   </call>
  13.  
  14.   <call method="graphics.endFill" args=""/>
  15.  
  16.   <call method="graphics.beginFill" args="0x0000FF">
  17.      <!-- //regular non-reference arguments can be passed like this -->
  18.      <arg value="0.5" />
  19.   </call>
  20.  
  21.   <call method="graphics.drawCircle" args="100,100,20" />
  22.  
  23.   <call method="customFunction" args="hello">
  24.      <arg reference="root.loaderInfo.bytesTotal" />
  25.   </call>
  26.  
  27. </code>
  28.  
  29. function customFunction(a:String, b:String):void{
  30.     trace("I am a custom function called from xml");
  31.     trace("Here is an argument: " + a);
  32.     trace("Here is another argument, (total bytes of this swf): " + b);
  33. }
  34.  
  35. // parse and run
  36. runCode(script);
  37.  
  38. function runCode(code:XML):void{
  39.     var children:XMLList = code.children();
  40.     for (var i:int = 0; i<children.length(); i++){
  41.         var child:XML = children[i];
  42.         var type:String = child.name();
  43.         if (type == "call"){
  44.             runMethod(child);
  45.         }
  46.     }
  47. }
  48.  
  49. function runMethod(code:XML):void{
  50.     var i:int = 0;
  51.     // get a reference to the function
  52.     var method:Function = dotSyntax(code.@method);
  53.     // call the function if there are no arguments
  54.     if (code.@args.toXMLString().length == 0 && code.arg.length() == 0){
  55.         method();
  56.     }else{
  57.      
  58.      var args:Array = code.@args.split(",");
  59.      var childArgsLength:int = code.arg.length()
  60.      for (i = 0; i<childArgsLength; i++){
  61.           // add another one with dot syntax
  62.           var val:String = code.arg[i].@value;
  63.           if (val){
  64.             args.push(val);
  65.           }
  66.       }
  67.       for (i = 0; i<args.length; i++){
  68.           if (args[i] != ""){
  69.             args[i] = valueType(args[i]);
  70.           }
  71.       }
  72.       for (i = 0; i<code.arg.length(); i++){
  73.          var rs:String = code.arg[i].@reference;
  74.          if (rs){
  75.              var ref:* = dotSyntax(rs);
  76.              args.push(ref);  
  77.          }
  78.       }
  79.       // run the function
  80.       method.apply(null,args);
  81.     }
  82. }
  83.  
  84. // parse dot syntax and return the last property or method
  85. function dotSyntax(str:String):*{
  86.     var path:Array = str.split(".");
  87.     var curr:* = this;
  88.     for (var i:int = 0; i<path.length; i++){
  89.         curr = curr[path[i]]
  90.     }
  91.     return curr;
  92. }
  93.  
  94. function valueType(val:*):* {
  95.     if (isNaN(Number(val))) {
  96.            // remove leading and trailing white
  97.            // remove "" around strings
  98.            val = val.replace(/^s+|s+$/g,"").replace(/^"|"$/g,"");
  99.            //  see if it's a boolean
  100.            if (val == "true"){
  101.                val = true;
  102.            }else if (val == "false"){
  103.                val = false
  104.            }
  105.     } else {
  106.         val = Number(val);
  107.     }
  108.     return val;
  109. }

WARNING: There area few small bugs in this snippet. If you'd like to use this, check out the AsXML mini-library

A few days ago I had the idea to write some code that would parse ActionScript from XML. The features that I realized would be possible are:

1) call methods of the main timeline
2) read and write properties on the main timeline
3) instantiate classes on the main timeline
4) call methods on these classes
5) read and write properties on these classes

So this morning I wrote the first part. This snippet has the ability to read an XML file and call methods accordingly. It also has the ability to read properties of the main timeline and pass them to these methods.

The XML in this snippet draws a box using the stageWidth and stageHeight. It draws a transparent circle and then calls a custom function. The custom function gets passed the root.loaderInfo.bytesTotal property.

I've done something similar a few times where I specifically targeted the Graphics API. This takes that to the next level by potentially working with any ActionScript classes (excluding Vector maybe... need to think about that).

I'll post another version with more features tomorrow. At the very least it will have class instantiation.

Posted in XML, dynamic, external data, string manipulation | Tagged , , | 6 Comments

Random Guitar Tablature

Actionscript:
  1. // number of notes in chord
  2. var noteNum:int = 3;
  3. var lowNote:Number = 0;
  4. var highNote:Number = 4;
  5. // delay between chords
  6. var delay:int = 2000;
  7. ////////////////////////////////////////
  8. var chord:String = "";
  9. highNote += 1;
  10. var tab:TextField = TextField(addChild(new TextField()));
  11. tab.x = tab.y = 20;
  12. tab.defaultTextFormat = new TextFormat("Courier", 12);
  13. tab.multiline = true;
  14. tab.width = 200;
  15.  
  16. changeChord();
  17. setInterval(changeChord, delay);
  18.  
  19. function changeChord():void{
  20.     var strings:Array = [];
  21.     strings[0] = "e|---------------\n"
  22.     strings[1] = "B|---------------\n"
  23.     strings[2] = "G|---------------\n"
  24.     strings[3] = "D|---------------\n"
  25.     strings[4] = "A|---------------\n"
  26.     strings[5] = "E|---------------\n"
  27.    
  28.     for (var i:int = 0; i<3; i++){
  29.         var place:int = 5 + i * 4;
  30.         var choices:Array = [0,1,2,3,4,5];
  31.         for (var j:int = 0; j<noteNum; j++){
  32.             var ii:int = int(Math.random()*choices.length);
  33.             var index:int = choices[ii];
  34.             strings[index] = strings[index].slice(0, place) + (int(Math.random()*highNote)+lowNote) +  strings[index].substring(place+1);
  35.             choices.splice(ii, 1);
  36.         }
  37.     }
  38.     chord = strings.join("");
  39.     tab.text = chord;
  40. }

I'm working on a small program to help me practice guitar. It randomly generates guitar tabs. The idea for the program is similar to a program that helps you learn to type (like Mavis Beacon).

I wrote this snippet today as a proof of concept - just to help me start thinking about what kind of features I want the program to have. There are settings at the top so that you can tweak the number of notes in the chord and the delay between chords etc....

It generates three chords at a time... here are some stills:

Posted in random, string manipulation, strings | Tagged , , | 6 Comments

Random Negative or Positive

Actionscript:
  1. for (var i:int = 0; i<10; i++){
  2.   // randomly set a variable to -1 or positive 1
  3.   trace(int(Math.random()*2) - 1 | 1);
  4. }
  5.  
  6. /* outputs something like this
  7. 1
  8. 1
  9. -1
  10. 1
  11. -1
  12. -1
  13. 1
  14. 1
  15. -1
  16. -1
  17. */

The past few days I found myself using this one-liner a few times. Just a quick way to randomly get -1 or 1.

I was brainstorming today and wrote a weird/bad inline version of an old post:

var rand:Number = [-3, 2, 1, 1, 1, 0.5][int(Math.random() * 6)];

I need to install a related posts plugin for WordPress - would be nice if this post were related to my old post about Tausworthe random seeds.

Been posting medium/large snippets and snippets the relate to QuickBox2D - so today I figured I'd get back to basics...

Posted in one-liners, random | Tagged , , | 2 Comments

QuickBox2D FRIM

Actionscript:
  1. import com.actionsnippet.qbox.*;
  2. import Box2D.Common.Math.*;
  3.  
  4. // try altering your frame rate
  5. [SWF(backgroundColor=0x000000, width=700, height=600, frameRate=30)]
  6. // try setting frim to false
  7. var sim:QuickBox2D = new QuickBox2D(this, {iterations:20, timeStep:1 / 60, frim:true});
  8.  
  9. sim.setDefault({fillColor:0x003366, lineAlpha:0});
  10. sim.createStageWalls();
  11.  
  12. sim.addBox({x:10, y:18, height:3, density:0});
  13.  
  14. sim.setDefault({fillColor:0x004466, lineColor:0x2B80F5});
  15.  
  16. var cVel:b2Vec2 = new b2Vec2();
  17.  
  18. for (var i:int = 0; i<32; i++){
  19.     var c:QuickObject = sim.addCircle({x:13 + i % 8, y:10 + int(i / 8), friction:0.01, radius:0.2 + Math.random()*0.3, angularDamping:1});
  20.     cVel.x = Math.random() * 4 - 2;
  21.     cVel.y = Math.random() * 4 - 2;
  22.     c.body.SetLinearVelocity(cVel);
  23. }
  24.  
  25. var box:QuickObject = sim.addBox({x:3, y:16, width:2, height: 2});
  26. var boxVel:b2Vec2 = new b2Vec2(15, -25);
  27.  
  28. sim.start();
  29.  
  30. // shoot the box from left to right
  31. setTimeout(positionBox, 200);
  32. function positionBox():void{
  33.     // reset box position
  34.     box.x = 3, box.y = 16;
  35.     setTimeout(shootBox, 500);
  36. }
  37. function shootBox():void{
  38.     box.body.SetLinearVelocity(boxVel);
  39.     box.body.SetAngularVelocity(10);
  40.     // shoot the box again
  41.     setTimeout(positionBox, 4000);
  42. }

You'll need to download the latest version of QuickBox2D in order to run this snippet (alpha 107 as of this post). I recommend checking out the demo below first though...

The most notable feature of this release is FRIM (frame rate independent motion). FRIM is used to attempt to keep the speed of the Box2D simulation constant no matter what frame rate the swf is running at. This is good because frame rates will vary from browser to browser and from computer to computer.

Mr.Doob suggested that I implement this feature in QuickBox2D. If you haven't seen his site I highly recommend checking it out - really great stuff there.

I created a demo to illustrate the FRIM feature... this demo is basically the same as the above snippet with a few extra buttons added to control frame rate and toggle FRIM.


Take a look at the demo here...

While this new feature seems to be working, I believe there still may be room for improvement. Please let me know if the demo doesn't work correctly on your machine. As of now, I've only tested it on my 2.5 Ghz Intel Core 2 Duo.

Also, any suggestions to improve the clarity of this demo are appreciated... I found it kind of hard to think of a good demo to show FRIM in action. The original demo really didn't make much sense... so I gave Rich Shupe a call and he helped me come up with the idea for the current demo.

UPDATE:
I just updated the demo so that the simulation restarts when you change the frame rate (as per Mr.doob's suggestion below)

Posted in Box2D, QuickBox2D, motion | Tagged , , , , | 9 Comments

QuickBox2D w/ Key Controls

Actionscript:
  1. import com.actionsnippet.qbox.*;
  2. import Box2D.Common.Math.*
  3.  
  4. [SWF(backgroundColor=0xEFEFEF, width=700, height=600, frameRate=60)]
  5.  
  6. var sim:QuickBox2D = new QuickBox2D(this);
  7.  
  8. sim.setDefault({lineColor:0xCC0000, fillColor:0xCC0000});
  9.  
  10. // create compound shape (two circles and a box for the character)
  11. var charParts:Array = [];
  12. // x and y position are now relative to center of compound shape
  13. charParts[0] = sim.addBox({x:0, y:0, width:1, height:2});
  14. charParts[1] = sim.addCircle({x:0, y:-1, radius:0.5});
  15. charParts[2] = sim.addCircle({x:0, y:1, radius:0.5});
  16. var char:QuickObject = sim.addGroup({objects:charParts, x:2, y:2.5, allowSleep:false, angularDamping:0.8, linearDamping:1.5});
  17.  
  18. // vector for linear velocity of character
  19. var charVel:b2Vec2 = new b2Vec2();
  20.  
  21. // angular velocity of character
  22. var charVelAng:Number = 1;
  23. char.body.SetAngularVelocity(charVelAng);
  24.  
  25. // world/platforms
  26. sim.setDefault({lineColor:0x666666, fillColor:0x666666, height:0.5, density:0});
  27. sim.createStageWalls();
  28. sim.addBox({x:3, y:5, width:5});
  29. sim.addBox({x:11, y:5, width:5});
  30. sim.addBox({x:8, y:9, width:8});
  31. sim.addBox({x:4, y:13, width:8});
  32. sim.addCircle({x:16, y:8, radius:2});
  33. sim.addCircle({x:12, y:15, radius:2});
  34.  
  35. // falling circles
  36. sim.setDefault({lineColor:0x2870B5, fillColor:0x2870B5});
  37. for (var i:int = 0; i<15; i++){
  38.     sim.addCircle({x:5 + i, y:2, radius:0.25 ,density:1});
  39. }
  40.  
  41. sim.start();
  42.  
  43. // key controls
  44. addEventListener(Event.ENTER_FRAME, onLoop);
  45. function onLoop(evt:Event):void {
  46.     charVel = char.body.GetLinearVelocity();
  47.     charVelAng =  char.body.GetAngularVelocity();
  48.    
  49.     if (key[Keyboard.RIGHT]){
  50.         charVel.x += 1
  51.         char.body.SetLinearVelocity(charVel);
  52.        
  53.         charVelAng += 1;
  54.         char.body.SetAngularVelocity(charVelAng);
  55.     }
  56.     if (key[Keyboard.LEFT]){
  57.         charVel.x -=1;
  58.         char.body.SetLinearVelocity(charVel);
  59.        
  60.         charVelAng -= 1;
  61.         char.body.SetAngularVelocity(charVelAng);
  62.     }
  63.     if (key[Keyboard.UP]){
  64.          charVel.y = -10;
  65.          char.body.SetLinearVelocity(charVel);
  66.            
  67.          charVelAng *= 0.8;
  68.          char.body.SetAngularVelocity(charVelAng);
  69.     }
  70. }
  71. // basic key setup
  72. var key:Object = new Object();
  73. stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyPressed);
  74. stage.addEventListener(KeyboardEvent.KEY_UP, onKeyReleased);
  75. function onKeyPressed(evt:KeyboardEvent):void {
  76.     key[evt.keyCode] = true;
  77.     key.keyCode = evt.keyCode;
  78. }
  79. function onKeyReleased(evt:KeyboardEvent):void { key[evt.keyCode] = false}

This snippet shows one way to go about doing key controls using QuickBox2D

Take a look at the swf here...

This works with the current version of QuickBox2D.... tomorrow I'll be uploading the new version of QuickBox2D which supports FRIM (frame rate independent motion) and contains a few additional minor tweaks and fixes.

Posted in Box2D, QuickBox2D, keys, motion | Tagged , , , | 1 Comment

x and y Coordinates in 1D Array / Vector

Actionscript:
  1. var canvas:BitmapData=new BitmapData(400,400,false,0x000000);
  2. addChild(new Bitmap(canvas));
  3. var pix:Vector.<uint>=canvas.getVector(canvas.rect);
  4.  
  5. canvas.lock();
  6. for (var i:int = 0; i<300; i++) {
  7.     var xp:int=50+i;
  8.     var yp:int=50+i/2;
  9.     // target x and y coords in 1D array
  10.     pix[xp+yp*400]=0xFFFFFF;
  11. }
  12. canvas.setVector(canvas.rect, pix);
  13. canvas.unlock();

This snippet shows how to target x and y coordinates in a 1D Array / Vector. This can be useful sometimes when working with setVector().

This is sort of like re-inventing setPixel().... and for that reason is kind of pointless - that said, it's interesting to know. I first learned about this technique from using processing.

Posted in BitmapData, Vector, arrays, pixel manipulation, setPixel | Tagged , , | Leave a comment

IGraphicsData Example

Actionscript:
  1. // note the high framerate for testing purposes
  2. [SWF(width = 800, height = 600, frameRate=60)]
  3. // red, yellow, blue
  4. var fills:Vector.<IGraphicsData> = Vector.<IGraphicsData>([new GraphicsSolidFill(0xFF0000),  new GraphicsSolidFill(0xFFCC00), new GraphicsSolidFill(0x0033FF)]);
  5.  
  6. var stroke:IGraphicsData = new GraphicsStroke();
  7.  
  8. var cmds:Vector.<int> = new Vector.<int>();
  9. var ci:int = 0;
  10. var dat:Vector.<Number> = new Vector.<Number>();
  11. var di:int = 0;
  12. var igraph:Vector.<IGraphicsData> = new Vector.<IGraphicsData>();
  13. var ig:int  = 0;
  14. var path:Vector.<GraphicsPath> = new Vector.<GraphicsPath>();
  15. var boxNum:int = 1500;
  16. var locX:Vector.<Number> = new Vector.<Number>(boxNum);
  17. var locY:Vector.<Number> = new Vector.<Number>(boxNum);
  18. var velX:Vector.<Number> = new Vector.<Number>(boxNum);
  19. var velY:Vector.<Number> = new Vector.<Number>(boxNum);
  20. var cols:int = 50;
  21. for (var i:int = 0; i<boxNum; i++){
  22.     path[i] = new GraphicsPath(new Vector.<int>(), new Vector.<Number>());
  23.     path[i].winding = GraphicsPathWinding.NON_ZERO;
  24.     var theta:Number = i * Math.PI/180;
  25.     var sin:Number = Math.cos(theta);
  26.     var cos:Number =  Math.sin(theta);
  27.     var r:Number = Math.random()*100;
  28.     var vr:Number = r * 0.05;
  29.     velX[i] = vr * cos;
  30.     velY[i] = vr * sin;
  31.     locX[i] = 400 + r * cos;
  32.     locY[i] = 300 + r * sin;
  33. }
  34.  
  35. // box vars
  36. var xp:Number, yp:Number, size:Number, hs:Number;
  37. // corners
  38. var x0:Number, y0:Number, x1:Number, y1:Number;
  39.  
  40. addEventListener(Event.ENTER_FRAME, onLoop);
  41. function onLoop(evt:Event):void {
  42.     graphics.clear();
  43.     ig = 0;
  44.     for (i= 0; i<boxNum; i++){
  45.         // inline function:
  46.         locX[i] += velX[i];
  47.         locY[i] += velY[i];
  48.         xp = locX[i];
  49.         yp = locY[i];
  50.         if (xp <0 || xp> 800){
  51.             velX[i] *= -1;
  52.         }
  53.         if (yp <0 || yp> 600){
  54.             velY[i] *= -1;
  55.         }
  56.         size = 10 + i % 10;
  57.         hs = size * 0.5;
  58.         x0 = xp - hs, y0 = yp - hs;
  59.         x1 = xp + hs, y1 = yp + hs;
  60.         ci = 0;
  61.         di = 0;
  62.         cmds = path[i].commands;
  63.         dat = path[i].data;
  64.         // GraphicsPathCommand.MOVE_TO
  65.         cmds[ci++] = 1;
  66.         dat[di++] = x0 , dat[di++] = y0;
  67.         // GraphicsPathCommand.LINE_TO
  68.         cmds[ci++] = 2;
  69.         dat[di++] = x1 , dat[di++] = y0;
  70.         // GraphicsPathCommand.LINE_TO
  71.         cmds[ci++] = 2;
  72.         dat[di++] = x1 , dat[di++] = y1;
  73.         // GraphicsPathCommand.LINE_TO
  74.         cmds[ci++] = 2;
  75.         dat[di++] = x0 , dat[di++] = y1;
  76.         // end inline function
  77.          
  78.         path[i].commands = cmds;
  79.         path[i].data = dat;
  80.         igraph[ig++] = fills[i % 3];
  81.         igraph[ig++] = path[i];
  82.     }
  83.     // everything is drawn with one function call
  84.     graphics.drawGraphicsData(igraph);
  85. }

I haven't spent much time with IGraphicsData. It's a very cool feature and I think its going to take me awhile to realize its full potential. Since I have to start somewhere I decided to write this snippet to show that it is rather fast. I also recalled reading some benchmark info for the new fp10 graphics stuff over at bytearray.org.... pretty nice info there...

This snippet is pretty optimized, there are a few areas (such as the recurring population of the cmds Vector) that could be optimized more but are left this way for SOME flexibility.

Posted in Graphics, Vector, misc, motion | Tagged , , | Leave a comment

Vectorpark Style Cylinder

Actionscript:
  1. const TWO_PI:Number = Math.PI * 2;
  2. const PI_HALF_PI:Number = Math.PI + Math.PI / 2;
  3. x = stage.stageWidth / 2;
  4. y = stage.stageHeight / 2;
  5. var runCylinder:Function = makeCylinder(0,0, 100, 300, 0xFF0000, 0xFFCC00);
  6. var dx:Number = 0;
  7. var dy:Number = 0;
  8. addEventListener(Event.ENTER_FRAME, onLoop);
  9. function onLoop(evt:Event):void {
  10.     dx += (mouseX / 50 - dx) / 4;
  11.     dy += (mouseY - dy) / 4;
  12.     runCylinder(dx, dy);
  13. }
  14. function makeCylinder(x:Number, y:Number, size:Number, length:Number, colA:uint, colB:uint):Function{
  15.     var s:Sprite = Sprite(addChild(new Sprite()));
  16.     s.x = x;
  17.     s.y = y;
  18.     var acol:uint = colB;
  19.     var bcol:uint = colB;
  20.     var halfLength:Number = length / 2;
  21.     var capA:Sprite = Sprite(s.addChild(new Sprite()));
  22.     var box:Sprite = Sprite(s.addChild(new Sprite()));
  23.     var w:Number = size * 2;
  24.     var h:Number = length;
  25.     with (box.graphics)  beginFill(colA), drawRect(-w/2, -h/2, w,h);
  26.     var capB:Sprite = Sprite(s.addChild(new Sprite()));
  27.     return function(theta:Number, rot:Number){
  28.         theta %= TWO_PI;
  29.         if (theta <0){
  30.             theta = TWO_PI - -theta;
  31.         }
  32.         var scale:Number = Math.cos(theta);
  33.         if (theta> 1.57){
  34.             acol = colA;
  35.             s.addChild(capB);
  36.             bcol = colB;
  37.         } else {
  38.             acol = colB;
  39.             bcol = colA;
  40.             s.addChild(capA);
  41.         }
  42.         if (theta> PI_HALF_PI){
  43.             acol = colB;
  44.             bcol = colA;
  45.             s.addChild(capA);
  46.         }
  47.        
  48.         var sin:Number = Math.sin(theta);
  49.         box.scaleY = sin;
  50.         capA.y = halfLength * sin;
  51.         capB.y = -capA.y;
  52.         with(capA.graphics) clear(), beginFill(acol), scaleYcircle(capA.graphics,size, scale);
  53.         with(capB.graphics) clear(), beginFill(bcol), scaleYcircle(capB.graphics,size, scale);
  54.         s.rotation = rot;
  55.     }
  56. }
  57. // original circle function by senocular (www.senocular.com) from here http://www.actionscript.org/forums/showthread.php3?s=&threadid=30328
  58. // circle that can be scaled on the y axis
  59. function scaleYcircle(g:Graphics, r:Number, s:Number = 1):void {
  60.     var c1:Number = r * (Math.SQRT2 - 1);
  61.     var c2:Number = r * Math.SQRT2 / 2;
  62.     var rs:Number = r * s, c1s:Number = c1 * s, c2s:Number = c2 * s;
  63.     var x_r:Number =  -r, y_r:Number = -rs, x_c2:Number =  -c2;
  64.     var y_c2:Number =  -c2s, x_c1:Number =  -c1, y_c1:Number =  -c1s
  65.     g.moveTo(r, 0), g.curveTo(r, c1s, c2, c2s);
  66.     g.curveTo(c1, rs, 0, rs), g.curveTo(x_c1,rs, x_c2, c2s);
  67.     g.curveTo(x_r, c1s, x_r, 0), g.curveTo(x_r,y_c1,x_c2,y_c2);
  68.     g.curveTo(x_c1,y_r,0,y_r), g.curveTo(c1,y_r,c2,y_c2);
  69.     g.curveTo(r,y_c1,r,0);
  70. }

This is similar to the last post I did.... inspired by some of the great stuff over at VectorPark.com... this snippet creates a 3D cylinder out of two circles and a box...

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

Vectorpark Style Hemisphere

Actionscript:
  1. const TWO_PI:Number = Math.PI * 2;
  2. const PI_HALF_PI:Number = Math.PI + Math.PI / 2;
  3. x = stage.stageWidth / 2;
  4. y = stage.stageHeight / 2;
  5.  
  6. var runHemisphere:Function = makeHemisphere(0,0, 100,0xFF0000, 0xFFCC00);
  7. var dx:Number = 0;
  8. var dy:Number = 0;
  9.  
  10. addEventListener(Event.ENTER_FRAME, onLoop);
  11. function onLoop(evt:Event):void {
  12.     dx += (mouseX / 50 - dx) / 4;
  13.     dy += (mouseY - dy) / 4;
  14.     runHemisphere(dx, dy);
  15. }
  16. function makeHemisphere(x:Number, y:Number, size:Number, colA:uint, colB:uint):Function{
  17.     var s:Sprite = Sprite(addChild(new Sprite()));
  18.     s.x = x;
  19.     s.y = y;
  20.     var bg:Sprite = Sprite(s.addChild(new Sprite()));
  21.    
  22.     with (bg.graphics) clear(), beginFill(colA), halfCircle(bg.graphics, 0,0 , size);
  23.     var scol:uint = colB;
  24.     var slice:Sprite = Sprite(s.addChild(new Sprite()));
  25.     return function(theta:Number, rot:Number){
  26.          theta %= TWO_PI;
  27.         if (theta <0){
  28.             theta = TWO_PI - -theta;
  29.         }
  30.         var scale:Number = Math.cos(theta);
  31.         if (theta> 1.57){
  32.             scol = colA;
  33.         } else {
  34.             scol = colB;
  35.         }
  36.         if (theta> Math.PI){
  37.             bg.rotation = 180;
  38.         }else{
  39.             bg.rotation = 0;
  40.         }
  41.         if (theta> PI_HALF_PI){
  42.             scol = colB;
  43.         }
  44.         with(slice.graphics) clear(), beginFill(scol), scaleYcircle(slice.graphics,size, scale);
  45.         s.rotation = rot;
  46.     }
  47. }
  48. // original circle function by senocular (www.senocular.com) from here http://www.actionscript.org/forums/showthread.php3?s=&threadid=30328
  49. function halfCircle(g:Graphics, x:Number,y:Number,r:Number):void {
  50.     var c1:Number = r * (Math.SQRT2 - 1);
  51.     var c2:Number = r * Math.SQRT2 / 2;
  52.     var xr:Number = x + r, yr:Number = y + r;
  53.     var x_r:Number = x - r, yc1:Number = y + c1;
  54.     var yc2:Number = y + c2, xc1:Number = x + c1, xc2:Number = x + c2;
  55.     var x_c1:Number = x - c1;
  56.     g.moveTo(xr, y);
  57.     g.curveTo(xr, yc1, xc2, yc2);
  58.     g.curveTo(xc1, yr, x, yr);
  59.     g.curveTo(x - c1,yr, x - c2, yc2);
  60.     g.curveTo(x_r, yc1, x_r, y);
  61. }
  62. // circle that can be scaled on the y axis
  63. function scaleYcircle(g:Graphics, r:Number, s:Number = 1):void {
  64.     var c1:Number = r * (Math.SQRT2 - 1);
  65.     var c2:Number = r * Math.SQRT2 / 2;
  66.     var rs:Number = r * s, c1s:Number = c1 * s, c2s:Number = c2 * s;
  67.     var x_r:Number =  -r, y_r:Number = -rs, x_c2:Number =  -c2;
  68.     var y_c2:Number =  -c2s, x_c1:Number =  -c1, y_c1:Number =  -c1s
  69.     g.moveTo(r, 0), g.curveTo(r, c1s, c2, c2s);
  70.     g.curveTo(c1, rs, 0, rs), g.curveTo(x_c1,rs, x_c2, c2s);
  71.     g.curveTo(x_r, c1s, x_r, 0), g.curveTo(x_r,y_c1,x_c2,y_c2);
  72.     g.curveTo(x_c1,y_r,0,y_r), g.curveTo(c1,y_r,c2,y_c2);
  73.     g.curveTo(r,y_c1,r,0);
  74. }

The above snippet draws a 3D hemisphere using a technique I first saw at vectorpark.com. Vectorpark.com has been around for years and it's an amazing site... if you've never seen it before I highly recommend spending some time there...

When I decided to post the half-circle snippet yesterday... I knew I would be writing this today. It's one of those things I've always thought about doing, but never gotten around to. I show vectorpark.com to my animation class (almost no coding in that class) and we emulate some of the 3D techniques for simple primitives with pure timeline animation. The above is the first time I've tried to emulate one of these techniques with code.

I was pleasantly surprised when visiting vectorpark.com this morning... Patrick Smith has posted some new work there. Really excellent stuff...

UPDATE:
Katopz converted this to a doc class and put it up on wonderfl...

Posted in 3D, Graphics | Tagged , , | 6 Comments