AlecMcE.com

Coding on the Flash Platform

Archive for the ‘gotcha’ Category

Gotcha – transform.matrix and scaleX/Y

View Comments

I came across this gotcha for the first time today. Praise-be to ASUnit, without which this could have been extremely difficult to unearth:

// given two sprites called "a" and "b"...

a.transform.matrix = new Matrix(-.5, 0, 0, -.5, a.x, a.y);
b.scaleX = b.scaleY = -.5;

trace(a.scaleX, a.scaleY, b.scaleX, b.scaleY); // outputs: 0.5, 0.5, -0.5, -0.5

Both clips have been scaled by -0.5, but because the transform was used to produce the scale for a, the scaleX/Y variables do not reflect the object’s true scale.

Now, just to confuse yourself, add a third sprite to the stage:

// given another sprite called "c"...

c.transform.matrix = new Matrix(-.5, 0, 0, -.5, c.x, c.y);
c.scaleX = c.scaleY = -.5;

Curiouser and curiouser! This needed a more complete experimentation…

The Flash plugin is required to view this object.

The source for this experiment can be found here: scaleAndTransform. The buttons call the following methods:

function onGo(event:Event):void
{
	a.transform.matrix = new Matrix(-.5, 0, 0, -.5, a.x, a.y);

	b.scaleX = b.scaleY = -.5;

	c.transform.matrix = new Matrix(-.5, 0, 0, -.5, c.x, c.y);
	c.scaleX = c.scaleY = -.5;
}

function onResetScale(event:Event):void
{
	a.scaleX = a.scaleY = 1;
	b.scaleX = b.scaleY = 1;
	c.scaleX = c.scaleY = 1;
}

function onResetTransform(event:Event):void
{
	a.transform.matrix = new Matrix(1, 0, 0, 1, a.x, a.y);
	b.transform.matrix = new Matrix(1, 0, 0, 1, b.x, b.y);
	c.transform.matrix = new Matrix(1, 0, 0, 1, c.x, c.y);
}

What Is Going On?

If you apply a transformation matrix then a scale, the magnitude of the scale is applied, but the direction is determined by the product of the transformation and the scalar… which is to say that:

a.realScaleX = (a.transform.matrix.a * a.scaleX > 0 ? 1 : -1) * a.scaleX;

If however you apply a scale then a transformation, the scale is defined by the transformation only!

a.realScaleX = a.transform.matrix.a;

Bug?

Does this qualify as a bug? I haven’t thought about this for long enough. The conditions in which a transformation matrix rotates and skews the MovieClip confuse me; how would I define scaleX and scaleY in that scenario? I need to give this some thought; perhaps you the community can give me some help! I am reluctant to call anything a bug immediately now, after my experience with my The Problem With Vector.<T> post, where I was just plain wrong!

A similar bug exists in Adobe JIRA: UIComponent.scaleX, scaleY differ from transform.matrix.a,d. You’ll need an account to look at this – it’s free, and once you’ve got it you can pester Adobe with your bugs too.

Written by alec

February 23rd, 2010 at 9:00 am

Posted in as3, gotcha, math, tdd

Vector.unshift Gotcha (for the time-being)

View Comments

I didn’t see this coming:

var list:Vector.<MockObject> = new Vector.<MockObject>();

var a:MockObject = new MockObject("a");
var b:MockObject = new MockObject("b");
var c:MockObject = new MockObject("c");

list.push(a);
trace(list);			// outputs "a"

list.push(b);
trace(list);			// outputs "a,b"

list.unshift(c);
trace(list);			// outputs "c,a"

No, I haven’t made a mistake. It’s a bug! The Flash 10 Language Reference explicitly says that this isn’t supposed to happen. After a little searching, Adobe JIRA affirms that this bug is known, and fixed – well fixed in the ‘Future – Next Build’ release.

I really hate wasting two hours of my time to discover that my test case is failing because the Flash Player is itself broken, and not because I’m an idiot. Clearly, Adobe don’t unit test. As ever, I am really glad that I do!

Written by alec

August 27th, 2009 at 7:56 pm

Posted in as3, gotcha

XML Constants Gotcha

View Comments

In my head, XML slips between the cracks of being an Object and a primitive type. It shouldn’t, and when I’m concentrated it doesn’t. But still, there is something primitive-type about it.

private const DATA:XML = <data message="Hello World!" />;

and

private const DATA:String = "Hello World!";

are intuitively the same sorts of things. XML is like an advanced sort of String. We encode it as we encode text, and we convert it to and from Strings without difficulty. I don’t construct XML, at least it doesn’t feel like I do. In my preferred coding environment (FDT) by default they’re even similarly coloured.

However, they are clearly not the same, because XML is mutable:

DATA.@message = "This message has been changed!";

Needless to say, that doesn’t work on a String! Somehow by labelling the XML a ‘constant’ I expect the XML to be immutable.

On reflection I know this about XML, but it feels wrong. This ‘gotcha’ does not identify a language bug, but suggests a mental corrective: when you think about XML, make sure that you don’t mistake it for a primitive type. When I’m not concentrating I do, and it occassionally trips me up.

Written by alec

August 13th, 2009 at 1:11 pm

Posted in as3, gotcha

Tagged with , ,

Weak Event Listeners and Inline Methods

View Comments

I encountered this strange issue today. The useful bulk loader library produces error events when it comes across problems and captures them with the following event listener. The accompanying comment says that this is to prevent unhandled errors that would otherwise produce a stack trace to (debug player) users indicating an error:

addEventListener(BulkLoader.ERROR, function (e:Event):void{}, false, 1, true);

I passed malformed XML into the loader, and found that about 25% of the time, I was getting just such a stack trace. I was not changing anything, but simply running and re-running a whole list of tests, with this test among them. The only thing that I could think of is that the Garbage Collector was occassionally considering the event listener available for collection and was removing it. Testing that hypothesis, I replaced the code with:

addEventListener(BulkLoader.ERROR, squashError, false, 1, true);
...
private function squashError(event:Event):void {}

and the error disappeared. I have posted a patch to the Bulk Library codebase, so hopefully it will be resolved soon.

There’s Nothing New Under The Sun

I’ve known about Grant Skinner’s analysis of event listeners for years, and have accepted that event listeners should always be defined weakly for as long. However, that article contains the very point that I have spent an afternoon rediscovering! It turns out I’ve been doing work that Grant Skinner did three years ago on behalf of another third party library vendor… which is disheartening because if I knew that article in more detail I could have saved my afternoon. It’s encouraging as well though; there’s a cliché about earning your learning somewhere…

Written by alec

August 7th, 2009 at 6:04 pm

Posted in as3, gotcha

Tagged with , ,

XML Gotcha – new XML(null)

View Comments

Without trying it, how would you expect this to behave?

var xml:XML = new XML(null);

Personally I don’t think that constructing XML from a null object should work, but it does. I would have expected an error, like if I try to do this:

var xml:XML = new XML("<bad>hello world</tags>");

That’s about as clear a case of error throwing as you can get, I think. Why not for null?

It turns out that null is interpreted as a sort of weird XML singularity. It has one node which has no name, to which you can add attributes and children. However much you add, always the length is 1 and the toXMLString() is an empty string. Happily, this latter fact can save you from being consumed by the singularity: you can test for it by checking whether

xml.toXMLString() == "";

and take appropriate steps if it does!

I haven’t thought through all the consequences of this behaviour but it took me completely by surprise… if you can explain to me why Adobe considered this the preferred behaviour, please let me know! This is another of those hard-to-spot XML bugs like this one I found earlier. Someone, preferably Simone since he’s already on it, should wrap fixes for this stuff into a helper class!

Update

var xml:XML = new XML(null);

is – of course – the same as

var xml:XML = new XML();

The documentation doesn’t indicate that you can pass an empty value into the XML constructor, but as my colleague Simone pointed out, playerglobal.swc indicates that you can. The documentation meanwhile says that the constructor parameter for an XML object should be

Any object that can be converted to XML with the top-level XML() function.

and the top-level XML function documentation states that if you pass a null into it:

null: A runtime error occurs (TypeError exception).

However, that doesn’t throw an error either!

I started this update having seen the playerglobal.swc definition about to eat some humble pie, but this further investigation has served to strengthen my understanding of how weird this behaviour is. Be warned, this stuff is out there waiting to spoil your afternoon’s coding!

Written by alec

August 6th, 2009 at 6:20 pm

Posted in as3, gotcha

Tagged with

ASUnit Gotcha – Empty TestSuite Bug

View Comments

This afternoon I discovered an interesting little bug in the ASUnit library which causes the TestRunner never to finish its suite of unit tests. It is fairly straightforward to reproduce too: simply, add create a TestSuite that doesn’t contain any tests and add the suite to the runner.

Though I have created an artificial example to isolate the problem, it arose in a real-world situation. I had two package folders in which I intended to create tests. I created all the AllTest TestSuites but only populated one of the packages with tests. When I tried to run the TestRunner to see the results of these tests, though they appeared to have run the TestRunner had never completed.

The source of the problem lies in the architecture of the TestSuite class, the main functionality for which is launched by the run method but which dispatches an event to indicate that it has finished performing tests in its testCompleteHandler method. However, testCompleteHandler can only ever be triggered as a consequence of a test completing in run, so if no tests are defined, the TestSuite never finishes.

Update

I posted my resolution of this problem on github, but it has since been merged to the asunit trunk, so I have removed the github repository. My solution added checks for empty TestCases and TestSuites to prevent the TestRunner trying to run them.

Written by alec

August 2nd, 2009 at 11:21 pm

Booleans in XML Gotcha

View Comments

The following code highlights a small but easy to miss gotcha relating to XML and Boolean types. The trace output will indicate that the flag is true. This is because when a Boolean is inputted into an XML object it is cast into the string “true” or the string “false”, but when it is parsed out it casts the string to Boolean, returning true if the string is non-null.

var flag:Boolean;
var xml:XML;

flag = false;
xml = <xml value={flag} />;
flag = xml.@value;

trace(xml.toXMLString());
trace(flag);

The result is that serializing a Boolean into XML and deserializing it from XML will break. Happily, the solution is simple, to retrieve the value

flag = xml.@value == "true";

will guarantee that the Boolean is cast correctly.

Written by alec

March 16th, 2009 at 2:47 pm

Posted in as3, gotcha