Symmetri Developer Blog

January 17, 2012

Trace statements in Flex cause performance issues

General - By Shourov Bhattacharya

Yup, they sure do. If, like me, you were always working under the assumption that trace() statements don’t make it into release builds of your SWF, then you are mistaken - they certainly do, and they do cause a significant performance hit. I haven’t done tests, but you can see one in this blog post.

My solution is to use a Debug class as a wrapper for all traces, and then turn it off in one place before compiling release builds. You can see my example code below. Simple, but effective and easy to overlook.

   public class Debug
    {
        static public var main:Application = null;
	
        // print debug message
        public static function print(message:String, priority:Number=0):void
        {
            // TODO: do something else useful here e.g. log error in file, database etc.
            trace(flash.utils.getTimer() + ": " + message);
	
            return;
        }
    }
	
   /// example of usage
   Debug.print(\"Main.onCreationComplete: starting application cycle\");
	

January 16, 2012

Chrome Developer Tools

General - By Shourov Bhattacharya

Google Chrome Developer Tools gets a big thumbs up from us at Symmetri! We now have three interface development projects going in Javascript/HTML5, and our initial anxieties about the lack of good development IDES/tools for Javascript development have been allayed. The logging, error/warning information and element highlighting functionality of the Chrome developer tools combine to make debugging really productive (even in the obvious absence of actually stepping through code). And personally, I like the interface much more than Firebug, which is also very popular.

It’s an exciting time to be a software web developer. HTML5, jQuery and a host of related technologies have finally made it possible to building just about ANTHING as a browser app - and then distribute to everyone anywhere, for all platforms on all devices. The world is a laboratory for any and all of our ideas! What more could we want … !

June 20, 2011

Patch for BitmapMaterial.as in PV3D v2.0.0

Flash/Flex/Actionscript, 3D Models - By Shourov Bhattacharya

My version of Papervision3D version 2.0.0 has an inexplicable bug in BitmapMaterial.as which causes the bitmap to be chopped up and displayed incorrectly on a surface. The patch is given below.

org/papervision3d/materials/BitmapMaterial.as line 123-128:

x0 = tri.v0.x;
y0 = tri.v0.y;
x1 = tri.v1.x;
y1 = tri.v1.y;
x2 = tri.v2.x;
y2 = tri.v2.y;

patched to:

x0 = tri.v2.x;
y0 = tri.v2.y;
x1 = tri.v1.x;
y1 = tri.v1.y;
x2 = tri.v0.x;
y2 = tri.v0.y;

May 12, 2011

Langton-like Ant Machines

General, Algorithms - By Shourov Bhattacharya

Langton’s Ant is a simple non-trivial example of a class of machines called Cellular Automata (CA)1. CA exhibit surprising behaviours from the application of simple rules, and as such are an excellent test case for research into emergence and complexity. For an excellent description of this class of CA, see Further Travels With My Ant (Gale et al.). The ant traverses one cell of the lattice at a time and does two actions 1) increment/flip the state of the cell and 2) turn left/right depending on the state of the cell.

The algorithm for the ant can be generalized to a lattice with cells that have more than two states. Each state can then be visualized as a different colour.

I have written a simple application that runs a generalized Langton-like Ants with an arbitrary rule string. The simplest machine is one has a rule-string of 10, which is Langton’s Ant itself. For a K-state machine successive states of cells are shown as darker shading. You can type in any rule string of any length K to run a new K-state ant machine and see what it does.


An ant with rule 1111110 building a "road"
An ant with rule 1111110 building a "road"

Try out a few different rule strings. Most of them produce a kind of random movement and produce no patterns, but even within those machines you’ll sometimes observe periods of regular behaviour when the ant will seem to exhibit some order (even “intent”?) in its movements. And then sometimes you’ll stumble upon a rule that produces beautifully ordered behaviour that persists over long times. Above is a screenshot of a 7-state Ant machine with rule string 1111110 that builds a diagonal road.

You can run the Ant Machine application from http://symmetri.com/flash/AntMachines.html.

1Actually it is a Turing machine, maybe not strictly a CA.

November 24, 2010

Data Binding to a Vector in AS3?

Flash/Flex/Actionscript - By Shourov Bhattacharya

The new generic Vector type available in Flex 4/AS3 is a good thing. But its utility is greatly diminished by the fact that you cannot data bind to a Vector. Now that’s kind of understandable, given that the Vector is a basic data type and does not implement the event notifications needed for data binding for reasons of efficiency. But there is also no way to take a Vector and easily prepare an ArrayCollection from it that can be used for data-binding .. .as far as I can tell, the best you can do is copy all the elements across to a new ArrayCollection one-by-one.

The result of all this is that it is not really possible to use Vector types to store typed data in a Flex application - after all, we are building UIs and we usually want to bind visual components at some point, and we would be stuck. That’s a shame because it means we can’t take advantage of the type-safety that the Vector type offers, even if the efficiency is not as important. Contrast this, for example, with the implementation of Generics in C#/.NET, which is far more useful and can be easily used for data-binding when constructing UIs.

November 8, 2010

Embedding an XML file in Flex

Flash/Flex/Actionscript, XML - By Shourov Bhattacharya

Embedding an XML file into a Flex application using Actionscript is easy - example below:

[Embed(source="../data/samples/data.xml")]
var dataFile:Class;
...
var xmlData = dataFile.data as XML;

But there is one BIG gotcha here. If the first line of your XML file that is being embedded is an XML header e.g:

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>

then you must remove this header line before embedding the file in a Flex application. If you don’t, Flash Builder will fail during compilation and return not-very-informative errors such as the following, with no reference to code files or line numbers:

1093 Syntax Error: Expecting semicolon before xmlns: Path: Type: Flex problem

October 31, 2010

Importing Sketchup models into Papervision3D

General, 3D Models - By Shourov Bhattacharya

Importing Google Sketchup models into Papervision3D isn’t easy to do. Although in theory a Collada (DAE) export from Sketchup should be lossless and preserve your model completely for use in PV3D, a lot of the time, you’ll find that faces/materials are missing in the rendered model when you view it in Flash. I am still trying to work out whether this can be worked around, but initial indications seem to be that the Collada specification is loose enough to allow this kind of incompatibility to exist without it really being anyone’s “fault”.

In the meantime, here are the settings you need to use in Google Sketchup to get your models into Papervision3D, (even incompletely):

Export two sided faces --> yes
Export edges -- > yes
Triangulate all faces --> yes (PV3D likes and needs triangles)
Preserve component hierarchy --> yes
Export texture maps --> yes
Use color by layer materials --> yes

October 27, 2010

COLLADA 1.4.1 Specification

General, XML, 3D Models - By Shourov Bhattacharya

COLLADA is the interchange format that we have decided to use in our new building editor application. This means that my first task is to become an expert on COLLADA, and what better way to do that than to read the specification! The latest stable release of COLLADA that is suitable for production use is the COLLADA 1.4.1 specification. Good times ahead!

October 22, 2010

Marimekko chart in Flex (Column Chart with variable width columns)

Flash/Flex/Actionscript - By Shourov Bhattacharya

The beauty of having access to the source code for a framework is that one can reverse-engineer and overload particular classes to change behaviour. I have found this particularly useful when developing data visualisations using the Adobe Data Visualisation library for Flex 3.5. In this case, my client wanted a Marimekko chart to show sales and marketing data with three data dimensions. A Marimekko chart is a variable width stacked column chart. It is a type of tree-map and shares the characteristic of all tree-maps that the area of a data element is proportional to the product of two data values.

There are no Flex components from either Adobe or third-parties which will create a Marimekko chart, so I decided to make my own. I overloaded a ColumnChart class and rendered the data elements within columns by drawing boxes of different widths; I then had to render the category axis labels and data labels for the column width values manually as well. The final result looks like this:

Sample Marimekko chart

My agreement with the client does not allow me to post complete source code; but I can give a good summary of the process which should make it easy to reproduce this chart.

1) overload the ColumnChart class to make a new MarimekkoChart class

2) create a new class called MariMekkoSeriesItemRenderer that extends UIComponent and implements IDataRenderer:

public class ReportMarimekkoSeriesItemRenderer extends UIComponent implements IDataRenderer
{
    private var _data:Object;
	
    public function set data(value:Object):void
    {
        if (_data == value)
            return;
	
        _data = value;
    }
	
    public function get data():Object
    {
        return _data;
    }
}

3) hide horizontal axis labels for the chart by setting the labelFunction for the horizontalAxisRenderer to a function that returns an empty string.

4) add ColumnSeries as usual to the MarimekkoChart, but set the item renderer for each series to our new MariMekkoSeriesItemRenderer class:

 series.setStyle("itemRenderer",  new ClassFactory(ReportMarimekkoSeriesItemRenderer));

5) now, within the MariMekkoSeriesItemRenderer class, we override the rendering to draw a rectangle that is proportional to the data width value. Note that we must offset the drawing in the x-direction to draw from the left hand edge of the chart data canvas. For this, we will need a data source that not only has the column width but a column “zero” coordinate for each data point as well. The custom rendering should be done in an overload of the updateDisplayList() method of the renderer.

6) create data labels by adding a Text component to each renderer, positioning in the middle of the data rendererer and displaying the data value

7) to add our own axis and data labels, we do a bit of sleight-of-hand: for the first data series only, we create axis labels and position them horizontally in the middle of the renderer, but vertically at the bottom of the chart; we then add these labels as children of the chart rather than the renderer. We do the same at the top of the chart to show the width values.

Example code for the MariMekkoSeriesItemRenderer class below. Note that some of the graphics rendering code has been copied from the BoxItemRenderer.as class in the Flex DMV source code.

public class ReportMarimekkoSeriesItemRenderer extends UIComponent implements IDataRenderer
{
    private var _data:Object;
	
    private var _label:Text;
    private var _axisLabel:Text;
    private var _sizeLabel:Text;        
	
    public function set data(value:Object):void
    {
        if (_data == value)
            return;
	
        _data = value;
    }
	
    public function get data():Object
    {
        return _data;
    }
	
    override protected function createChildren():void
    {
        super.createChildren();
	
        if (this._label == null)
        {
            this._label = new Text();
            this._label.text = "";
            this.addChild(this._label);
        }
	
        if (this._axisLabel == null)
        {
            this._axisLabel = new Text();
            this._axisLabel.text = "";
            this._axisLabel.visible = false;
        }
	
        if (this._sizeLabel == null)
        {
            this._sizeLabel = new Text();
            this._sizeLabel.text = "";
            this._sizeLabel.visible = false;
        }            
	
    }
	
    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
        super.updateDisplayList(unscaledWidth, unscaledHeight);    
	
        var fill:IFill;
        var state:String = "";
	
        var newWidth:Number  = width;
        var newX:Number = this.x;
	
        var size:Number;
        var sizeZero:Number;
        var series:ReportMarimekkoSeries;
        var chart:ReportMarimekkoChart;            
	
        if (_data is ChartItem)
        {
	
            var size:Number;
            var sizeZero:Number;
            var dataValue:Number;
	
            // here, retrieve/calculate the column width for this data point
            // and also the "zero" point for starting this column
            // both these values should be scaled from zero to 1
	
            series = ColumnSeries(ChartItem(_data).element);
            chart = MarimekkoChart(series.owner);
	
            // calculate pixel widths and x-position relative to chart
	
            newWidth = size * chart.dataCanvas.width;
            newX = sizeZero * chart.dataCanvas.width;
	
            // add a data label
            this._label.text = String(dataValue);
	
            if (chart.series.length > 0)
            {
                if (series == chart.series[0])
                {
                    // for the first series, we add an extra label
                    // at the x-axis aand a data value label at the top
	
                    this._axisLabel.text = axisLabel; // axisLabel from data
                    if (this._axisLabel.parent != chart)
                    {
                        chart.addChild(this._axisLabel);
                        this._axisLabel.visible = true;
                    }
	
                    this._sizeLabel.text = String(size); // or use another field
                    if (this._sizeLabel.parent != chart)
                    {
                        chart.addChild(this._sizeLabel);
                        this._sizeLabel.visible = true
                    }
                }
            }
	
        }
	
        if(_data is ChartItem && _data.hasOwnProperty('fill'))
        {
            state = _data.currentState;
            fill = _data.fill;
        }
        else
            fill = GraphicsUtilities.fillFromStyle(getStyle('fill'));
	
        var color:uint;
        var adjustedRadius:Number = 0;
	
        switch(state)
        {
            case ChartItem.FOCUSED:
            case ChartItem.ROLLOVER:
                if(StyleManager.isValidStyleValue(getStyle('itemRollOverColor')))
                    color = getStyle('itemRollOverColor');
                else
                    color = ColorUtil.adjustBrightness2(GraphicsUtilities.colorFromFill(fill),-20);
                fill = new SolidColor(color);
                adjustedRadius = getStyle('adjustedRadius');
                if(!adjustedRadius)
                    adjustedRadius = 0;
                break;
            case ChartItem.DISABLED:
                if(StyleManager.isValidStyleValue(getStyle('itemDisabledColor')))
                    color = getStyle('itemDisabledColor');
                else
                    color = ColorUtil.adjustBrightness2(GraphicsUtilities.colorFromFill(fill),20);
                fill = new SolidColor(GraphicsUtilities.colorFromFill(color));
                break;
            case ChartItem.FOCUSEDSELECTED:
            case ChartItem.SELECTED:
                if(StyleManager.isValidStyleValue(getStyle('itemSelectionColor')))
                    color = getStyle('itemSelectionColor');
                else
                    color = ColorUtil.adjustBrightness2(GraphicsUtilities.colorFromFill(fill),-30);
                fill = new SolidColor(color);
                adjustedRadius = getStyle('adjustedRadius');
                if(!adjustedRadius)
                    adjustedRadius = 0;
                break;
        }
	
        var stroke:IStroke = getStyle("stroke");
	
        var w:Number = stroke ? stroke.weight / 2 : 0;
	
        var rc:Rectangle = new Rectangle(
            -this.x + newX + w - adjustedRadius,
            w - adjustedRadius,
            newWidth - 2 * w + adjustedRadius * 2,
            height - 2 * w + adjustedRadius * 2);
	
        var g:Graphics = graphics;
	
        g.clear();
        g.moveTo(rc.left,rc.top);
        if (stroke)
            stroke.apply(g);
        if (fill)
            fill.begin(g,rc);
        g.lineTo(rc.right,rc.top);
        g.lineTo(rc.right,rc.bottom);
        g.lineTo(rc.left,rc.bottom);
        g.lineTo(rc.left,rc.top);
	
        if (fill) fill.end(g);   
	
        // position the data label
        this._label.width = this._label.text.length * 7;
        this._label.height = 20;
        this._label.x = rc.left + rc.width/2 - (this._label.width/2);
        this._label.y = rc.top + rc.height/2 - (_label.height/2);
	
        if (chart)
        {
            // position the axis label
            this._axisLabel.width = this._axisLabel.text.length * 7;
            this._axisLabel.height = 20;
            this._axisLabel.x = newX + rc.width/2 - (this._axisLabel.width/2) + chart.marginLeft;
            this._axisLabel.y = chart.height - 40; 
	
            // position the width data label
            this._sizeLabel.width = this._sizeLabel.text.length * 7;
            this._sizeLabel.height = 20;
            this._sizeLabel.x = newX + rc.width - (this._sizeLabel.width/2) + chart.marginLeft;
            this._sizeLabel.y = -10;
        }
    }
	
}

There are a limitation to this chart - I had to turn interactivity off, as I did not have time to fix the mouse events to fire over the correct areas. Further work is needed to get that right.

October 19, 2010

Dropdown not updating on ComboBox in Flex

Flash/Flex/Actionscript - By Shourov Bhattacharya

This is a disappointing bug to find in Flex 3.5, to say the least. The problem is that when binding a ComboBox to an ArrayCollection, changes in the underlying data source are not correctly broadcast to the control. Even when this is “fixed” using ArrayCollection.refresh(), I am finding that it is working only intermittently. The problem is that sometimes the ComboBox dropdown list does not update to show the new data items, even though the selected item is updated correctly. So you can end up with a ComboBox that looks like this:

The workaround is to set the dataProvider property of both the ComboBox and the ComboBox.dropdown objects:

this.ddlVersion.dataProvider = Model.getInstance().globals.versions;
this.ddlVersion.dropdown.dataProvider = Model.getInstance().globals.versions;
this.ddlVersion.validateNow();

Even this is not 100% right. Although the data is now showing correctly in the dropdown, the width of the dropdown list is still not being set correctly. I haven’t yet found a way to workaround that issue.

Get free blog up and running in minutes with Blogsome
Theme designed by Janis Joseph