20
Nov/09
0

Box 2D Tutorial

If you were out at Adobe Max this year, you might have seen the huge Max Companion walls. It’s a little bit difficult to see in the video, but tweets relating to Adobe Max would come in, they would be loaded and displayed for a few seconds, then they would fall as if gravity was applied to them. The introduction of physics into this application was done via a framework called Box-2D.

What I plan to show you is how to set up a very basic application that has physics applied to it. Setting up your “physical world” is definitely the most difficult part of using Box2D. Here’s the methods I use to create the Box2D world.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public function createWorld(event:Event = null):void
{
    var ab:b2AABB = new b2AABB();  //the bounds of the physics world
    ab.lowerBound.Set(-200, -200);
    ab.upperBound.Set(200, 200);

    var gravity:b2Vec2 = new b2Vec2(0, 4.0);  //the constant force of gravity

    world = new b2World(ab, gravity, false);  //create the world

    createBorderObject(0, this.height, this.width, 100); //floor
    createBorderObject(0, -this.height, this.width, 100); //ceiling
    createBorderObject(-100, -this.height, 100, 3*this.height); //left
    createBorderObject(this.width + 2, -this.height, 100, 3*this.height); //right

    this.addEventListener( Event.ENTER_FRAME, caluclateWorld );
}

private function createBorderObject(xp:Number, yp:Number, w:Number, h:Number):void
{  //these bodies will prevent objects from getting out of our physics world and being unusable
    var the_box:b2PolygonDef = new b2PolygonDef();
    the_box.SetAsBox(w/physicsScale/2, h/physicsScale/2);
    the_box.density = 0;
    the_box.friction = .4;
    the_box.restitution = 0;

    var gbd:b2BodyDef = new b2BodyDef();
    gbd.position.Set((xp+w/2)/physicsScale, (yp+h/2)/physicsScale);

    var ground:b2Body = world.CreateBody(gbd);
    ground.CreateShape(the_box);
    ground.SetMassFromShapes();

    var rect:Canvas = new Canvas();
    rect.graphics.beginFill(0xFF00FF, 0);
    rect.graphics.drawRect(xp, yp, w, h);
    addChild(rect);
}

In order to create the world you need to set up a few things. The first thing you have to set up is the boundaries of the world, which need to extend beyond what you are actually going to use. The reason it needs to be larger than your play area, is because if any objects exit this region, they’ll become unusable to the physics world. Next you’ll need to create gravity, which doesn’t need to go to the bottom of your screen. You could easily set up gravity to go to the top of the screen or to the side to one side. The last part of the constructor is whether or not you want objects at rest to go to sleep. I’ve always set it to false, but I’m by no means an expert on all of the options.

Within my createWorld method I’ve also created four border objects. These are invisible barriers that prevent objects from reaching my physics border. Lastly, I add an ENTER_FRAME event listener so that every time flash begins to redraw this component it will calculate any changes. What follows is my calculateWorld function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private function updateWorld(e:Event):void
{
    world.Step((1/30),10);
    var bodyPos:b2Vec2;
    var bodyRot:Number;
    var renderers:ArrayCollection = getActors();
     //updates our visual components to coincide with their physics
    for( var i:Number = 0; i < renderers.length; i++ )
    {
        var renderer:Actor = renderers.getItemAt(i) as Actor;
        var body:b2Body = renderer.physicsBody;
        if( body )
        {
            bodyPos = body.GetPosition();
            renderer.rotation=0;

            var m:Matrix=renderer.transform.matrix;
            m.tx =- renderer.width/2;
            m.ty =- renderer.height/2;
            m.rotate( body.GetAngle() );   
            //m.scale(1,1);
            // Now set the position to the world position
            m.tx+=bodyPos.x*physicsScale;
            m.ty+=bodyPos.y*physicsScale;

            // ...and set the whole thing at once via the matrix.
            // ie. Update the sprite.
            renderer.transform.matrix=m;
            renderer.scaleX = 1;
            renderer.scaleY = 1;
        }
    }
}

My updateWorld function has a number of steps. First, I tell Box2D to calculate the changes, by passing in the number of refresh frequency (1/30) as well as the number of iterations to calculate. After Box2D has calculated the changes I have to loop through all of the objects in the “world” and update their renderer. The renderers are visual components that are tied to a physics body, which is necessary because the objects in the Box2D are not visible.

For the purposes of my sample, I’ve created a simple renderer which you can see here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.softwarejesus.physics
{
    import Box2D.Dynamics.b2Body;
   
    import mx.containers.Canvas;

    public class Actor extends Canvas
    {
        public var physicsBody:b2Body;
        public var shape:String;
        public function Actor() {
           
        }
       
        public function drawShape():void
        {
            if (shape == Physics.SQUARE_SHAPE) {
                this.setStyle("backgroundAlpha", 1);
                this.setStyle("backgroundColor", 0x000000);
            } else {
                this.setStyle("backgroundAlpha", 0);
                graphics.beginFill(0x000000);
                graphics.drawCircle(this.width/2, this.height/2, this.width/2);
            }
        }
    }
}

Now that you’ve seen the definition for my renderers, let’s see how objects get added to the world.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public function addShape():void
{
    if(this.selectedShape == CIRCLE_SHAPE)
        addBall();
    else
        addBox()
}

public function addBox():void
{
    var newCanvas:Actor = new Actor();
    newCanvas.width = this.diameter;
    newCanvas.height = this.diameter;
    newCanvas.x = Math.random() * ( this.width - this.diameter);
    newCanvas.y = -newCanvas.height;
    newCanvas.shape = this.selectedShape;
    newCanvas.drawShape();

    var b1:b2PolygonDef = new b2PolygonDef();
    b1.SetAsBox((diameter/2)/physicsScale , (diameter/2) / physicsScale);
    b1.density = density;
    b1.friction = friction;
    b1.restitution = restitution;

    var bd:b2BodyDef = new b2BodyDef();
    bd.position.Set( (newCanvas.x+(newCanvas.width/2))/physicsScale, (newCanvas.y+(newCanvas.height/2))/physicsScale );
    bd.angle = 0;

    var physicsBody:b2Body = world.CreateBody(bd);
    physicsBody.CreateShape(b1);
    physicsBody.SetMassFromShapes();
    physicsBody.ApplyImpulse( b2Vec2.Make(Math.floor(Math.random() * (10 -  -10)) + -10,0), b2Vec2.Make(10,5) );

    newCanvas.physicsBody = physicsBody;
    newCanvas.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
    this.addChild(newCanvas)
}

public function addBall():void
{
    var newCanvas:Actor = new Actor();
    newCanvas.width = diameter;
    newCanvas.height = diameter;
    newCanvas.x = Math.random() * ( this.width - diameter);
    newCanvas.y = -newCanvas.height;
    newCanvas.shape = selectedShape;
    newCanvas.drawShape();

    var b1:b2CircleDef = new b2CircleDef();
    b1.radius = (diameter/2)/ physicsScale;
    b1.density = density;
    b1.friction = friction;
    b1.restitution = restitution;

    var bd:b2BodyDef = new b2BodyDef();
    bd.position.Set( (newCanvas.x+(newCanvas.width/2))/physicsScale, (newCanvas.y+(newCanvas.height/2))/physicsScale );
    bd.angle = 0;

    var physicsBody:b2Body = world.CreateBody(bd);
    physicsBody.CreateShape(b1);
    physicsBody.SetMassFromShapes();
    physicsBody.ApplyImpulse( b2Vec2.Make(Math.floor(Math.random() * (10 + 10)) + -10,0), b2Vec2.Make(10,5) );

    newCanvas.physicsBody = physicsBody;
    newCanvas.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
    this.addChild(newCanvas)
}

I start by creating my renderer. Next, I create the shape that I want to add to the world, for this example I’ve limited the shapes to circles and squares for the sake of simplicity. I set a few options on the shape that determine how the shape interacts with it’s surroundings in our world. Those options are density, friction, restitution (bounciness), and size. You can see the effects of each of these options in my example below.

The last element of my functions for adding my shapes is the addition of a MOUSE_DOWN event listener. The purpose of this is to allow the user to actually interact with the objects in the world. Here’s my mouse event handlers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public function handleMouseDown(evt:MouseEvent):void
{
    var newX:int = (evt.stageX+15)/physicsScale;
    var newY:int = (evt.stageY+15)/physicsScale;
    var renderer:Actor;
    if (evt.target is Actor) {
        renderer = evt.target as Actor;
    } else {
    }
    if( renderer )
    {
        var body:b2Body = renderer.physicsBody;
        var md:b2MouseJointDef = new b2MouseJointDef();
        md.body1 = world.GetGroundBody();
        md.body2 = body;
        md.target.Set(newX, newY);
        md.maxForce = 5000.0;
        md.timeStep = (1/30);

        var newMouseJoint:b2MouseJoint = world.CreateJoint(md) as b2MouseJoint;
        mouseJoint = newMouseJoint;
        body.WakeUp();
        this.addEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
        this.addEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
    }
}

public function handleMouseMove(event:MouseEvent):void
{
    var newX:int = (event.stageX+10)/physicsScale;
    var newY:int = (event.stageY+10)/physicsScale;
    var p2:b2Vec2 = new b2Vec2(newX, newY);

    if(mouseJoint)
        mouseJoint.SetTarget(p2);

}  

public function handleMouseUp(event:MouseEvent):void    
{
    this.removeEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
    this.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
    if (mouseJoint) {
        world.DestroyJoint(mouseJoint);
        mouseJoint = null;
    }


}

When the user mouses down on one of the renderers a mouse joint is created and we listen for a mouse move event. A mouse joint is a means for us to apply an external force to our objects.

When you put all this together, you have the goofy little sample below.

Get Adobe Flash player

You can also see it here
Or download this source from here

19
Aug/09
3

Flex Configurations made easy

If you have worked on a project of any size using Flex and Cairngorm one of the issues that you might have encountered is how to allow the application to connect to the necessary services in different environments. For example with your local development you might connect to a Tomcat server running on your machine, but in the test environment it would connect to a different server. To solve this I used to have code that looked like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.softwarejesus.configuration.model
{
   import com.adobe.cairngorm.model.ModelLocator;

   public class OldModelLocator implements ModelLocator
   {
      private static var modelLocator : OldModelLocator;

      public var SERVICE_URL:String = "http://localhost:8080/sampleServices/messagebroker/amf";
//    public var SERVICE_URL:String = "http://devServer:8080/sampleServices/messagebroker/amf";

      public static function getInstance() : OldModelLocator
      {
         if ( modelLocator == null ) {
            modelLocator = new OldModelLocator;
         }

         return modelLocator;
      }
   
      public function OldModelLocator()
      {
         if ( modelLocator != null )
            throw new Error( "Only one ModelLocator instance should be instantiated" );
      }

   }
}

You’ll notice that I have different endpoints commented out in my ModelLocator. In order to create a Test build, I would un-comment the second line and rebuild the application, but I was never entirely happy with that solution.
What some coworkers and I came up with, was creating a simple configuration XML that would reside in the same directory as the generated SWF. At startup, the application would load any environment specific constants before any real work began.
Now, how did this impact my code? The only major deviation from the standard Cairngorm pattern is that we had to wait to add the Services to our application until after the application had received confirmation that the configuration has been loaded.
As you’ll see here my Services.mxml code looks exceptionally ordinary.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<ServiceLocator xmlns="com.adobe.cairngorm.business.*" xmlns:mx="http://www.adobe.com/2006/mxml">
   <mx:Script>
      <![CDATA[
         import com.softwarejesus.configuration.model.ConfigModelLocator;

         [Bindable]
         private var serviceUrl : String = ConfigModelLocator.getInstance().SERVICE_URL;
      ]]>
   </mx:Script>
   <mx:RemoteObject
      id="sampleService"
      destination="sampleService"
      showBusyCursor="true"
      endpoint="{serviceUrl}"/>
   
</ServiceLocator>

Here is how I’ve written my ModelLocator, notice that when I instantiate the model, I retrieve the configuration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.softwarejesus.configuration.model
{
   import com.adobe.cairngorm.control.CairngormEvent;
   import com.adobe.cairngorm.control.CairngormEventDispatcher;
   import com.adobe.cairngorm.model.ModelLocator;

   import flash.events.Event;
   import flash.events.IOErrorEvent;
   import flash.net.URLLoader;
   import flash.net.URLRequest;

   import mx.controls.Alert;

   public class ConfigModelLocator implements ModelLocator
   {
      private static var modelLocator : ConfigModelLocator;
      private static const CONFIG_FILE_NAME:String = "config.xml";

      public var SERVICE_URL:String;

      public static const CONFIG_LOAD_COMPLETE:String = "ConfigLoaded";

      public static function getInstance() : ConfigModelLocator
      {
         if ( modelLocator == null ) {
            modelLocator = new ConfigModelLocator;
            modelLocator.loadXML();
         }

         return modelLocator;
      }
   
      public function ConfigModelLocator()
      {
         if ( modelLocator != null )
            throw new Error( "Only one ModelLocator instance should be instantiated" );
      }

      public function loadXML():void {
         var configPath:String = CONFIG_FILE_NAME;        
         var loader:URLLoader = new URLLoader(new URLRequest(configPath));
         loader.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
         loader.addEventListener(Event.COMPLETE, handleXML);    
 
      }

      private function handleXML(event:Event):void
      {
         var xml:XML = new XML((event.currentTarget as URLLoader).data);
         SERVICE_URL  = xml.system.serviceUrl;

         CairngormEventDispatcher.getInstance().dispatchEvent(new CairngormEvent(CONFIG_LOAD_COMPLETE));
      }
       
      private function handleIOError(event:Event):void {
         Alert.show("Unable to open config.xml. Please contact the your IT representative");
      }        

   }
}

Tying it all together, in my Application file, we I initialize the app, I retrieve the model, and add a listener for when it has completed the configuration process. Once the configuration complete event has been dispatched, I know I can now add the services and get to work making Service Calls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="doInit()">
   <mx:Script>
      <![CDATA[
         import com.adobe.cairngorm.control.CairngormEventDispatcher;
         import com.softwarejesus.configuration.services.Services;
         import com.adobe.cairngorm.control.CairngormEvent;
         import com.softwarejesus.configuration.model.ConfigModelLocator;

         private function doInit():void {
            ConfigModelLocator.getInstance();    
            CairngormEventDispatcher.getInstance().addEventListener(ConfigModelLocator.CONFIG_LOAD_COMPLETE, addServices);
         }

         private function addServices(event:CairngormEvent):void {
            var serv:Services = new Services();
            addChild(serv);
         }
      ]]>
   </mx:Script>
</mx:Application>

I found this solution to be perfectly suited to address an issue I ran into on another project. In that project, the client was a manufacturer of goods, and had distributors that sold his products. Initially it was set up so that when someone logged in they could browse through the entire catalog of products. A couple of months in, he told me that each of his distributors had a finite set of products they could sell, and that none of them could sell all of the products. As a result, the distributors could not show their customer’s the app, for fear that the customer would decide to purchase something they were not authorized to sell.

The solution I came up with was to host the web services, and all the assets (and believe me, the number of assets was disgusting) centrally, and develop a swf that could be given to the distributors to host on their respective websites. Along with the swf, the customer was also given a config.xml that would determine which products were available through the application. This solution allowed me to introduce brand new functionality and all I had to do was implement one new service call.

Source

1
Jul/09
0

Cairngorm Super Classes

I’m a big fan of the Cairngorm micro-architecture for Flex, I am unapologetic and vocal about my support of it. I’m also incredibly lazy, and hate to rewrite the same two or three lines of code over and over, but I found that because of some of the design choices that were made on two of my recent projects, I was having to do just that. The biggest example of my rewriting the same code was in my commands. If you use Cairngorm, and you’re anything like me, you probably have commands that look roughly like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.softwarejesus.cairngorm.command
{
    import com.adobe.cairngorm.commands.Command;
    import com.adobe.cairngorm.control.CairngormEvent;
   
    import mx.controls.Alert;
    import mx.rpc.IResponder;
    import mx.rpc.events.FaultEvent;

    public class SimpleCommand implements Command, IResponder
    {
        public function execute(event:CairngormEvent):void
        {
            /*Do something - call a web service*/
        }
       
        public function result(data:Object):void
        {
            /*Handle the response*/
        }
       
        public function fault(info:Object):void
        {
            var event:FaultEvent = info as FaultEvent;
            Alert.show(event.fault.faultString);
            /*trace(event.fault.faultString);*/
        }
       
    }
}

So I decided I was going to run with this lazy thing and create a simple class called MyResponder which would at least remove the need to re-implement the fault method again and again. But then I noticed another pattern, but before I can show you I need to explain a little bit about how applications were structured. These applications allowed the user to open up 1 to n datagrids or charts. So I created a simple multiton model which contained data that controlled how the data was retrieved, how the data was rendered, as well as the actual result data. Here’s what models basically looked like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.softwarejesus.cairngorm.model
{
    import mx.collections.ArrayCollection;
   
    [Bindable]
    public class GenericModel
    {
        /*Criteria variables*/
        public var string1:String;
        public var date1:Date;
       
        /*Display variables*/
        public var string2:String;
        public var number1:Number;
       
        /*List of some type of objects*/
        public var resultData:ArrayCollection;
       
       
    }
}

When somebody launched a new chart or datagrid a new GenericModel was created. It was then populated with the data my app needed to render it properly, search criteria, and then a new Cairngorm Event was created to retrieve the applicable data. Depending on how the data was structured, I would have a number of events, like this one:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.softwarejesus.cairngorm.event
{
    import com.adobe.cairngorm.control.CairngormEvent;
    import com.softwarejesus.cairngorm.model.GenericModel;

    public class SpecificEvent extends CairngormEvent
    {
        public var obj:GenericModel;
        public static const EVENT_TYPE:String = "SpecificEventType";
        public function SpecificEvent(obj:GenericModel)
        {
            super(EVENT_TYPE);
            this.obj = obj;
        }
       
    }
}

I would also have a number of commands that were structured like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.softwarejesus.cairngorm.command
{
    import com.adobe.cairngorm.commands.Command;
    import com.adobe.cairngorm.control.CairngormEvent;
    import com.softwarejesus.cairngorm.event.SpecificEvent;
    import com.softwarejesus.cairngorm.model.GerenicModel;
   
    import mx.collections.ArrayCollection;
    import mx.controls.Alert;
    import mx.rpc.IResponder;
    import mx.rpc.events.FaultEvent;
    import mx.rpc.events.ResultEvent;

    public class SpecificCommand implements Command, IResponder
    {
        var obj:GerenicModel;
        public function execute(event:CairngormEvent):void
        {
            var newEvent:SpecificEvent = event as SpecificEvent;
            this.obj = newEvent.obj;
           
            //get my delegate
            //make some service call
        }
       
        public function result(data:Object):void
        {
            var event:ResultEvent = data as ResultEvent;
            obj.resultData = event.result as ArrayCollection;
        }
       
        public function fault(info:Object):void
        {
            var event:FaultEvent = info as FaultEvent;
            Alert.show(event.fault.faultString);
        }
       
    }
}

A repeated pattern is an opportunity to be lazy, which I’m a huge fan of. What I came up with were 2 super classes that I would use to cut a great deal of superfluous, repetitive code out of my project. The first super class I’ve created is my SuperEvent, you’ll notice that it implements EventConstants. EventConstants contains all the event type strings that I use in my project:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.softwarejesus.cairngorm.event
{
    import com.adobe.cairngorm.control.CairngormEvent;
    import com.softwarejesus.cairngorm.model.GerenicModel;

    public class SuperEvent extends CairngormEvent implements EventConstants
    {
        public var object:GerenicModel;
        public function SuperEvent(object:GerenicModel, type:String) {
            this.object = object;
            super(type);
        }
    }
}

Next came my SuperCommand,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.softwarejesus.cairngorm.command
{
    import com.adobe.cairngorm.commands.Command;
    import com.adobe.cairngorm.control.CairngormEvent;
    import com.softwarejesus.cairngorm.event.SuperEvent;
    import com.softwarejesus.cairngorm.model.GenericModel;
   
    import mx.collections.ArrayCollection;
    import mx.controls.Alert;
    import mx.rpc.IResponder;
    import mx.rpc.events.FaultEvent;
    import mx.rpc.events.ResultEvent;

    public class SuperCommand implements Command, IResponder
    {
        private var object:GerenicModel;
        public function execute(event:CairngormEvent):void
        {
            var newEvent:SuperEvent = event as SuperEvent;
            this.object = newEvent.object;
        }
       
        public function result(data:Object):void
        {
            var event:ResultEvent = data as ResultEvent;
            object.resultData = event.result as ArrayCollection;
           
        }
       
        public function fault(info:Object):void
        {
            var event:FaultEvent = info as FaultEvent;
            Alert.show(event.fault.faultString);
        }
       
    }
}

With good middle-tier design, you can eliminate the need to implement 80-90% of your web service result handlers. Thereby saving you valuable time, which you can turn around and dedicate to World of Warcraft or whatever it is you choose to do with your spare time.

26
Jun/09
3

AnyChart Review

Recently, our client wanted to include a funnel chart in the next phase of the project I’m currently working on, but Flash Charting does not include this capability. As we looked around for solutions, we were pointed in the direction of AnyCharts. In this blog post I’m going to talk about the AnyChart plugin tool that my team used recently, specifically the good, the bad, and what I would like to see as a Flex user. For this specific post, I will not be providing any code samples, just two-bit opinions. If anyone has any requests for future AnyChart posts, please let me know.

As you look at the AnyChart website what you’ll notice is that the charts look really polished right out of the box. I am in no way a graphic designer so components that looks good out of the box, usually gets my attention first, because look and feel is the most difficult aspect of Flex for me.

AnyChart allows you to download their product in a few different formats, but I’m just going to focus on the SWC available for Flex, which is available here. The AnyChart SWC contains precisely one visual component that you can incorporate into your application, that being: AnyChartFlex. Within the AnyChartFlex Component, there are two fields that you can set to control all of the charting options: anychartXML and anychartXMLFile.

As I alluded to earlier, the AnyChart presentation is very crisp and polished without any styling, whereas Flex is not. To give you an idea of the difference in the polish, I’ve included a simple chart with no styling applied to it using the Flex charting packages.

Get Adobe Flash player

This is the same type of chart rendered using AnyChart without any styles.

Get Adobe Flash player

As I said, right out of the box, it looks really nice… if it has enough space. But this is where Anycharts started to give us trouble. When you start trying to embed the chart within an application where space is a premium, AnyCharts needs a tremendous amount of massaging to look good. Here’s an example of what I’m talking about, our application was set up similar to this, 2 rows of charts with 2 charts in each row.

Get Adobe Flash player

Whereas, Flex Charting has no problem scaling down to a smaller size.

Get Adobe Flash player

As you can see, all of the items that looked really slick right out of the box, are now all jumbled up. It takes a while to do, but you can change numerous settings on the XML you pass into the component and get something that fits into the confined space and still looks nice.

One of the things I think the creators of AnyChart could do to help with the problem is to make AnyChartFlex a utility class instead of a visual component and create at least two different visual components.
A) AnyChartCanvas – this would essentially be exactly what they have today, it would have the legend, title, etc
B) AnyChart – this would be just the chart, no legend, no title, no extra padding at all
One other thing I would request is
C) AnyChartLegend – self explanatory

Overall, the tool is really nice for Flex developers who aren’t great with look and feel, but I see tremendous growth opportunities for it.