Visual Reflections in AS2

November 23rd, 2008 by Hans Wichman

I’m currently working on a project where I’m using a bit of reflection, and although I found a couple of reflection classes on the web, I was looking for something that worked out of the box without too much hassle.

Basically I was looking for something that allowed me to do new Reflection(myClip) and be done with it 9 times out of 10.
Making a reflection is not that hard (although I need to work on the alpha and falloff, will post a new version later), but getting it to work right with scaled clips with weird registration points was a little harder.

Anyway I think I came up with something nice, and it performs quite well. You can download the source and a bunch of demo’s here Reflection demo’s

Here is a list of features:

  • simple setup by default. In most cases doing new ReflectionManager (sourceClip) will suffice
  • default setup is a clip that fades from 40 to 0 alpha at 40% of the image
  • you can change the falloff height, overall alpha, and the redraw interval (eg to mirror video)
  • retrievable reflection and source clips to apply filters or otherwise control clips
  • redraw interval is disconnected from framerate, specify a low redraw interval to save system resources
  • works with scaled, non top left regpoints, and masked clip, using an autobounds detection algorithm
  • works with negative scaled clips
  • tries to warn you in most cases when the result is likely to be different from what you expect
  • very high performance
  • no dependencies on non flash classes
  • A couple samples:

    The code:

    1. import flash.display.BitmapData;
    2. import flash.geom.Matrix;
    3. import flash.geom.Rectangle;
    4.  
    5. /**
    6. * ReflectionManager manages the reflection for one clip.
    7. *
    8. * Short feature set:
    9. * - simple setup by default. In most cases doing new ReflectionManager (sourceClip) will suffice.
    10. * - default setup is a clip that fades from 40 to 0 alpha at 40% of the image.
    11. * - you can change the falloff height, overall alpha, and the redraw interval (eg to mirror video)
    12. * - retrievable reflection and source clips to apply filters or otherwise control clips
    13. * - redraw interval is disconnected from framerate, specify a low redraw interval to save system resources
    14. * - works with scaled, non top left regpoints, and masked clip, using an autobounds detection algorithm
    15. * - works with negative scaled clips
    16. * - tries to warn you in most cases when the result is likely to be different from what you expect
    17. * - very high performance
    18. * - no dependencies on non flash classes
    19. *
    20. * @author J.C. Wichman, TriMM Interactive Media (www.trimm.nl / j.c.wichman@trimm.nl),
    21. * ObjectPainters.com (blog.objectpainters.com / j.c.wichman@objectpainters.com)
    22. */
    23. class nl.trimm.movieclip.Reflection {
    24.  
    25. private var _sourceClip:MovieClip = null; //reference to the source you want to mirror
    26. private var _sourceClipParentBounds:Rectangle = null; //the source bounds as interpreted by the parent
    27. private var _sourceClipLocalBounds:Rectangle = null; //the source bounds as interpreted by source itself
    28. //same as parentbounds for nonscaled, nontranslated clips
    29.  
    30. private var _reflectionClip:MovieClip = null; //contains _reflectionClipContent and _reflectionClipMask
    31. private var _reflectionClipContent:MovieClip = null; //contains resultbitmap into which source reflection is drawn
    32. private var _reflectionClipMask:MovieClip = null; //contains alpha mask used as fade out effect
    33.  
    34. private var _redrawMatrix:Matrix = null; //contains all transformation necessary to draw a scaled,
    35. //translated and flipped reflection of the source into the dest
    36. private var _redrawRectangle:Rectangle = null; //specifies clip on redrawing method
    37.  
    38. private var _resultBitmap:BitmapData = null; //contains result of drawing source into dest through matrix
    39. private var _updateIntervalId:Number = null; //redraw interval, use null for static images
    40.  
    41. private var _autoClear:Boolean = false; //auto clear the bitmap before redrawing
    42.  
    43. /**
    44. * The purpose of this reflection class is to make it as simple as possible to provide a working reflection,
    45. * yet give you the freedom to adjust what's needed.
    46. *
    47. * Restrictions:
    48. * - source and reflection share the same parent
    49. * - source should not be scaled, if you want to scale, either wrap the scaled clip, or scale the parent
    50. *
    51. * @param pTarget the object you want to create a reflection for
    52. * @param pAlpha the alpha of the reflection 0..1
    53. * @param pHeight the height of the reflection, 1 is the whole image height is used as fall off, 0 is no reflection, and
    54. * everything in between (0 .. 1)
    55. * @param pFrameRate the framerate for the redraw method
    56. * @param pBounds explicit bounds that define the area to be reflected, if these are not passed, bounds are auto detected,
    57. * based on what's visible at the moment of creation. The bounds are relative to the source clips coordinate
    58. * system. In other words, IMAGINE you edit the source clip, draw a rectangle around the area you want to see
    59. * reflected, and write down those bounds, THAT area defines the boundsrectangle.
    60. * @param pCustomClip an empty clip on the same parent as pSource that you would like to display the reflection.
    61. * The only reason I can think of to do so, is that this will allow you to apply filters to this
    62. * clip through the IDE instead of filter classes, which might be convenient for non coders.
    63. * @param pAutoClear often it's not necessary to auto clear the canvas again before redrawing, UNLESS you are moving
    64. * your content, and your content is transparent. By default autoclear is off, but you might want
    65. * to enable it.
    66. */
    67. public function Reflection(pSource:MovieClip, pAlpha:Number, pHeight:Number, pFrameRate:Number, pBounds:Rectangle, pCustomClip:MovieClip, pAutoClear:Boolean) {
    68. if (pAlpha != null && (pAlpha < 0 || pAlpha > 1)) trace ("pAlpha has to be > 0 < 1");
    69. if (pHeight != null && (pHeight < 0 || pHeight > 1)) trace ("pHeight has to be > 0 < 1");
    70.  
    71. if (_global.warn == null) _global.warn = trace;
    72.  
    73. _sourceClip = pSource;
    74. _setupReflection(
    75. pAlpha == null?40:pAlpha * 100,
    76. pHeight == null?0.4:pHeight,
    77. pBounds,
    78. pCustomClip
    79. );
    80. if (pFrameRate != null) setAutoUpdateFrameRate (pFrameRate);
    81. if (pAutoClear != null) _autoClear = pAutoClear;
    82. }
    83.  
    84. /**
    85. * Create a gradient required to create the fading out alpha effect
    86. */
    87. private function _setupReflection(pAlpha:Number, pHeight:Number, pBounds:Rectangle, pCustomClip:MovieClip) {
    88. //store short reference to the parent that contains our source, it will contain the reflection and
    89. //reflection alpha mask as well
    90. var lReflectionParent:MovieClip = _sourceClip._parent;
    91. if (pCustomClip == null) {
    92. _reflectionClip = lReflectionParent.createEmptyMovieClip (
    93. "reflection_[" + _sourceClip._name + "]",
    94. lReflectionParent.getNextHighestDepth()
    95. );
    96. } else {
    97. if (pCustomClip._parent != lReflectionParent) {
    98. _global.warn ("WARNING:the clip you have passed is not on the same timeline as the clip to reflect.");
    99. }
    100. _reflectionClip = pCustomClip;
    101. }
    102.  
    103. if (pBounds == null || (pBounds.width == 0 && pBounds.height == 0)) {
    104. //now get the local bounds
    105. var lBounds:Object = _sourceClip.getBounds(_sourceClip);
    106. _sourceClipLocalBounds = new Rectangle (lBounds.xMin, lBounds.yMin, lBounds.xMax - lBounds.xMin, lBounds.yMax - lBounds.yMin);
    107.  
    108. //an important thing to note is that flash returns a _width and _height based on unmasked content.
    109. //if you have a 100x100 clip, with a topleft mask of 50x50, the bounds will be based on the 100x100 size,
    110. //not on the 50x50. This causes an issue in a number of cases, and reflecting the clip is one of them:)
    111. //therefore we are going to check the visible area using the bounds we just established to check if we need to
    112. //adjust our bounds
    113.  
    114. //we create a bitmap to draw the clip into first
    115. var lBoundsDetectionBitmap:BitmapData = new BitmapData (_sourceClipLocalBounds.width, _sourceClipLocalBounds.height, true, 0x0);
    116. var lBoundMatrix:Matrix = new Matrix();
    117. //enforce top left
    118. lBoundMatrix.translate ( -_sourceClipLocalBounds.x, -_sourceClipLocalBounds.y);
    119. lBoundsDetectionBitmap.draw (_sourceClip, lBoundMatrix);
    120. //now detect visible area in the bitmap, and translate it back to the original origin
    121. var lVisibleBounds:Rectangle = lBoundsDetectionBitmap.getColorBoundsRect(0xff000000, 0xff000000, true);
    122. lVisibleBounds.offset (_sourceClipLocalBounds.x, _sourceClipLocalBounds.y);
    123. //dispose tha bitmap
    124. lBoundsDetectionBitmap.dispose();
    125.  
    126. lVisibleBounds.width = Math.ceil (lVisibleBounds.width);
    127. _sourceClipLocalBounds = lVisibleBounds;
    128.  
    129. } else {
    130. _sourceClipLocalBounds = pBounds;
    131. }
    132.  
    133.  
    134. //we can establish parent bounds using _sourceClip.getBounds(lReflectionParent), but the bounds might have
    135. //been prepassed or altered by getColorBoundRect detection, established etc, so we do it through globalToLocal,
    136. //localToGlobal so instead of
    137. //var lBounds:Object = _sourceClip.getBounds(lReflectionParent);
    138. //_sourceClipParentBounds = new Rectangle (lBounds.xMin, lBounds.yMin, lBounds.xMax - lBounds.xMin, lBounds.yMax - lBounds.yMin);
    139. //we do
    140. var xMinyMin:Object = { x: _sourceClipLocalBounds.x, y: _sourceClipLocalBounds.y };
    141. var xMaxyMax:Object = { x: _sourceClipLocalBounds.x+_sourceClipLocalBounds.width, y: _sourceClipLocalBounds.y+_sourceClipLocalBounds.height};
    142. _sourceClip.localToGlobal (xMinyMin);
    143. _sourceClip.localToGlobal (xMaxyMax);
    144. lReflectionParent._parent.globalToLocal (xMinyMin);
    145. lReflectionParent._parent.globalToLocal (xMaxyMax);
    146.  
    147. var lWidth:Number = xMaxyMax.x - xMinyMin.x;
    148. var lHeight:Number = xMaxyMax.y - xMinyMin.y;
    149. _sourceClipParentBounds = new Rectangle (xMinyMin.x, xMinyMin.y, Math.abs(lWidth), Math.abs(lHeight));
    150. //note that for an unscaled clip with left top registration these bounds will be equal
    151. //we will use the parent bounds as much as possible, in short this means that if you are reflecting a movieclip
    152. //that is 1000x1000 but scaled down to 10%, the reflection will only create a bitmap sized 100x100
    153.  
    154. //beside offsetting, we need to establish a reflection height, since we might want a greater falloff than a complete reflection
    155. var lReflectionHeight:Number = _sourceClipParentBounds.height * pHeight;
    156.  
    157. //we create a clip to be used as mask, and we draw an alpha gradient on it (yes it's possible:))
    158. _reflectionClipMask = _reflectionClip.createEmptyMovieClip ("mask", 0);
    159. _reflectionClipMask.lineStyle (0, 0x0, 0);
    160. var lBottomGradientMatrix:Matrix = new Matrix();
    161. lBottomGradientMatrix.createGradientBox(_sourceClipParentBounds.width, lReflectionHeight, Math.PI/2, 0, 0);
    162. _reflectionClipMask.beginGradientFill ("linear", [0xffffff, 0xffffff], [pAlpha, 0], [0, 255], lBottomGradientMatrix);
    163. _reflectionClipMask.moveTo (0, 0);
    164. _reflectionClipMask.lineTo (_sourceClipParentBounds.width, 0);
    165. _reflectionClipMask.lineTo (_sourceClipParentBounds.width, lReflectionHeight);
    166. _reflectionClipMask.lineTo (0, lReflectionHeight);
    167. _reflectionClipMask.lineTo (0, 0);
    168. _reflectionClipMask.endFill();
    169.  
    170. //now create reflection container
    171. _reflectionClipContent = _reflectionClip.createEmptyMovieClip ("content", 1);
    172. //and we set up our resulting bitmap
    173. _resultBitmap = new BitmapData (_sourceClipParentBounds.width, lReflectionHeight, true, 0x0);
    174. _reflectionClipContent.attachBitmap (_resultBitmap, 0, "none", true);
    175.  
    176. //align the reflection clip to the source
    177. _reflectionClip._x = _sourceClipParentBounds.x;
    178. _reflectionClip._y = _sourceClipParentBounds.y + _sourceClipParentBounds.height;
    179.  
    180. if (lWidth < 0) {
    181. _global.warn (
    182. "Source clip has been scaled negatively horizontally, trying to adjust, this will hurt performance." +
    183. "Try to wrap the source clip in another clip."
    184. );
    185. _reflectionClipContent._xscale = -100;
    186. _reflectionClipContent._x += _reflectionClipContent._width;
    187. _reflectionClip._x -= _reflectionClipContent._width;
    188. }
    189. if (lHeight < 0) {
    190. _global.warn ("Source clip has been scaled negatively vertically, trying to adjust, this will hurt performance." +
    191. "Try to wrap the source clip in another clip."
    192. );
    193. _reflectionClipContent._yscale = -100;
    194. _reflectionClipContent._y += _reflectionClipContent._height;
    195. _reflectionClip._y -= _reflectionClipContent._height;
    196. }
    197.  
    198. //hook up the mask and set cacheAsBitmap to true so the alpha in the mask is applied to
    199. _reflectionClipContent.setMask (_reflectionClipMask);
    200. _reflectionClipContent.cacheAsBitmap = true;
    201. _reflectionClipMask.cacheAsBitmap = true;
    202.  
    203. //the redraw matrix is just one matrix, no matter how many operations you add. So it doesn't hurt
    204. //to keep operations separated for clarity. View the redraw matrix from the perspective of the source clip:
    205. _redrawMatrix = new Matrix();
    206. //first we 'enforce' a top left registration point
    207. _redrawMatrix.translate ( -_sourceClipLocalBounds.x, -_sourceClipLocalBounds.y);
    208. //then we scale the relation between parent and child, if the parent bounds for clip a say the clip is 200 wide,
    209. //and the local bounds say 400, appearently is has been scaled by 50 percent
    210. _redrawMatrix.scale (_sourceClipParentBounds.width/_sourceClipLocalBounds.width, _sourceClipParentBounds.height/_sourceClipLocalBounds.height);
    211. //now flip the whole thing upside down
    212. _redrawMatrix.scale (1, -1);
    213. //and move it down again to we can see it
    214. _redrawMatrix.translate (0, _sourceClipParentBounds.height);
    215.  
    216. //everything is tranformed so it fits within this rectangle:
    217. _redrawRectangle = new Rectangle (0, 0, _sourceClipParentBounds.width, lReflectionHeight);
    218.  
    219. //if original clip has filters attached, warn.
    220. if (_reflectionClip.filters.length > 0) {
    221. _global.warn (
    222. "Warning source clip has filters. You might want to wrap this clip in a parent clip without filters, " +
    223. "or pass correct bounds. You might want to add yourReflection.getReflection().filters = yourSourceClip.filters."
    224. );
    225. }
    226.  
    227. //redraw once to get started
    228. redraw();
    229. }
    230.  
    231. public function redraw() {
    232. if (_autoClear) {
    233. //seems faster than a fillRect but fillRect less processor intensive
    234. //_resultBitmap.dispose();
    235. //_resultBitmap = new BitmapData (_redrawRectangle.width, _redrawRectangle.height, true, 0x0);
    236. //_reflectionClipContent.attachBitmap (_resultBitmap, 0, "none", true);
    237. _resultBitmap.fillRect (_redrawRectangle, 0x0);
    238. }
    239. _resultBitmap.draw (_sourceClip, _redrawMatrix, null, null, _redrawRectangle, true);
    240. }
    241.  
    242. public function setAutoUpdateFrameRate(pFrameRate:Number) {
    243. //clear any old intervals
    244. if (_updateIntervalId != null) {
    245. clearInterval (_updateIntervalId);
    246. _updateIntervalId = null;
    247. }
    248.  
    249. //if no interval specified exit
    250. if (pFrameRate == null) return;
    251.  
    252. _updateIntervalId = setInterval (this, "redraw", Math.round (1000/pFrameRate));
    253. }
    254.  
    255. public function setAutoClear (pAutoClear:Boolean) {
    256. _autoClear = pAutoClear;
    257. }
    258.  
    259. public function getReflection():MovieClip {
    260. return _reflectionClip;
    261. }
    262.  
    263. public function getSourceClip():MovieClip {
    264. return _sourceClip;
    265. }
    266.  
    267. public function finalize() {
    268. setAutoUpdateFrameRate (null);
    269. _sourceClip = null;
    270. _reflectionClipContent.setMask (null);
    271. _reflectionClipContent = null;
    272. _reflectionClipMask = null;
    273.  
    274. _reflectionClip.removeMovieClip();
    275. _reflectionClip.filters = null;
    276. _reflectionClip = null;
    277.  
    278. _resultBitmap.dispose();
    279. }
    280.  
    281. }

    A couple of screenshots of the other demo’s:
    Difference process

    Finding character positions

    October 12th, 2008 by Hans Wichman

    Also known as:

  • finding the position of a character in a dynamic textfield with embedded fonts using actionscript 2
  • One of current projects involves some texteffects and I am loath to do anything on the timeline that can be done quicker by code. So my basic idea was (keeping the designer in me happy):

    Doing the design stuff at design time on stage:

  • put a dynamic textfield on stage
  • apply a font, fontsize, color, anti-aliasing
  • apply effects such as dropshadow, glow etc to make it look good
  • Doing the animate stuff at runtime through code:

  • break apart the stage textfield into little textfields that I could animate
  • At this point I was already thinking about converting the stage textfields to bitmaps but found I was thinking about optimising things too early, so back to KISS, basic principles first.

    Silly me, I thought it would be easy in actionscript 2 to get the position of a character in a dynamic textfield. As in:

    textfield.getCoordsOfChar (pIndex:Number)

    but apparently no such thing exists. Ofcourse if you have a monospaced font, there are other ways to accomplish this, but this solution works for both monospaced and other fonts.

    Although in actionscript 3 there is something like getCharIndexAtPoint, that is not quite what I needed.

    Using a trick I managed to implement it in actionscript 2. Note that this is still under development, being researched etc, so it’s not a general I-will-work-everytime-approach. Anyway the basic principle is this:

  • create a bitmap as large as your textfield
  • create two textformats derivatives of your textfield’s textformat and set the textcolor of one to white, the other to black
    now loop over the characters in your textfield (i = 0 to textfield.length), and:
    • apply first the white textformat to everything, then applying the black textformat to the range (i, i+1)
      (basically in every iteration of the loop you set everything to white except the character whose position you are looking for)
    • copy the textfield to the bitmap
    • perform a getColorBoundsRect on the bitmap and voila an approximation of the character position

    And I say approximation since it’s seems to be off by a couple of pixels, but close enough to be usuable. In addition large amounts of text will slow down the process considerably and small font sizes wreak havoc, but it’s good enough:

    Download the prototype here, it includes the Greensock Tween classes, but you can use anything you like.

    Iterating properties creates unwanted side-effects

    October 12th, 2008 by Hans Wichman

    Also known as:

  • iterating properties in actionscript 2 causes getter setter to execute
  • I was working on our AS2 logger today. In particular I was creating a setup where you could simply drop in a couple components in your fla and ‘tada’, you would have a reflecting logger at your disposal.

    I’ll go into the reflecting logger and component creation in another post, because what happened was that during a test run I ran into the dreaded 256 levels recursion problem.

    Some research indicated that the problem lay with getters and setters.

    Imagine you have a class:

    class TestClass {
        public function get id1 () {
            trace ("hello world");
        }
    
        public function id2() {
            trace ("goodbye world");
        }
    }
    

    Now do:

    _global.ASSetPropFlags(TestClass.prototype, null, 0, 7);
    for (var i:String in TestClass.prototype) {
        trace (
             "Property:"+i+" is function ? "+
             (TestClass.prototype[i] instanceof Function)
        );
    }
    

    Ok, truth be told, you will not do this every day. In fact building a reflection package is probably the only time this issue shows up. However I like to document stuff for posterity’s sake, so here we are.

    Executing the code above will show something like:
    hello world
    Property:id1 is function ? false
    Property:id2 is function ? true
    Property:__get__id1 is function ? true
    Property:__proto__ is function ? false
    Property:constructor is function ? true

    The _global.ASSetPropFlags is used to unprotect all the prototypes properties, in order to force them to show up. In a real situation, you should always make sure you keep track of the original settings of an object’s properties and revert the object back to those settings after you are done with it.

    Anyway what is really interesting is that testing the id1 property to see whether it is a function or not, causes the underlying method (the ‘get’ method) to execute.

    Luckily I never use getters and setters. But other people do. This is not to say that getters and setters are bad, just that I ran into a situation which I hadn’t anticipated :) .

    In most situation this will not cause a problem either, but you never know. The getter might go into a recursive loop if no parameters are passed. A class might update it’s properties unintentionally, who knows? I don’t. I do know that if those side effects do happen, you will lose hours of precious time bughunting.

    So how to circumvent these special properties of woe?
    If you look closely at the output again, you’ll see something like __get__id1 in there as well.
    These kind of methods will only be created by flash if you use getters and setters.

    So how can you detect if obj[i] refers to a getter/setter and should not be executed?

    Test for the existence of __set__i and __get__i.

    As the saying goes, you’ll find the solution in the last place you look.

    More information on ASSetPropFlags:
    http://objectpainters.com/blog/2007/06/21/assetpropflags-explained/

    Wedding gift

    August 10th, 2008 by Hans Wichman

    As mentioned in my previous post, we’ve just gotten back from the UK where my sister Corine got married to Rob. It was a wonderful party at which we enjoyed ourselves very much. I pondered long and hard on what to get them, thinking about doing something on the guitar, but I didn’t think it was the right kind of party for that. I thought about doing a speech, but I knew there were already going to be a few very good ones and didn’t want to bore them with mine, although I do regret for passing on the chance to say something to a special person who made all the difference for me in getting through some rough days at school when we were young. Instead I chose to draw/paint something. They say a picture says more than a thousand words, but then again ‘they’ haven’t heard the great speeches so I’ll never trust that statement again ;). Anywayz, this is my way of saying ‘guyz we love you very much and wish you all the best together!’.

    And the sketch which I created first:

    Silence! You infidel!

    August 10th, 2008 by Hans Wichman

    Sooo it has been kind of silent around here lately. It’s annoying really. Although I normally keep these kind of post to myself, I thought I’d make an exception. Silence may give you the wrong impression that nothing is going on here, while the truth is quite the opposite. So what’s causing it?
    Well first of all, I’ve been working on the same project for the past few years, and it’s gonna stay in AS2. Although the project is great, I feel AS2 is .. well .. so last year (for a few years already). On the other hand except for performance reasons I’m not jumping up and down to move to AS3. We’ve come up with a lot of ‘fixes’ for problems in AS2, which are now mostly standard features people are praising in AS3, such as solid error handling, reflection, logging, dictionaries etc and in addition nothing beats MTASC as a compiler:). Besides that I’m still not too sure about migrating the project to AS3, although I myself feel I might need to move on or miss the last rowing AS3 boat entirely.

    So what is going on on that front is that AS2 development continues, while I’m turning to AS3 as well, for example by developing new stuff with Gaia (Steven Sacks’ framework), doing some tutorial work, and I’ll probably do the next NeuroFlasher game in AS3. Migrating the current project is kinda hard because of the 30 or so people involved and the 1000+ fla’s that have to be migrated as well (although AS2 to AS3 communication might help us out there, the garbage collection problems in the current FP9 player will not).

    One of the other causes of radio silence is… truth be told.. family & social life. Yep I know if you are a workaholic this will sound sad :). Roan just turned 1, Leon is almost 5 and we share many a happy hour which couldn’t be further from the concept of silence than it is already. Besides that I enjoy playing AD&D, AT43, and a lot of other computerbased games such as starcraft, diablo, warcraft, with a bunch of friends where I live and online… and yes once you start …. :) Although this only happens about one night in a week, if one night is all you got to do something extra, it diminishes the output by about well yes a 100%! But of course that is entirely my own fault.

    So what else is there? Ah yes, exercise woohoo. I set myself a goal to gain (yes you are reading it correctly) some kilograms, and up to now it’s working quite well, although the muscle/fat gain ratio could be improved :). In addition I’m working on a tattoo for Jeremy from New Jersey for his son Aidan, like I have one for mine. Up to now it’s going nicely, although there is still a certain amount of work left to be done. I’ll post some pictures of it. Which brings me to my most recent painting, a work I did for my sister Corine’s wedding in the UK, from which we just got home. An awesome party with a very nice family, but I won’t go into details here (but I’ll post the painting and some pictures). Last but not least, the beta version of NeuroPuzzles 1.3 is close to completion, and I’m very excited about it’s release (and yes I’ll post some pictures and more info about that too!).
    So here I am, on Sunday morning, with a few hours of holiday to spare, before I’m getting back to work to tomorrow, full of plans for improving the frequency of my blogposts with the images & words promised above!

    FLfile.listFolder archive bit bug

    May 7th, 2008 by Hans Wichman

    Today I ran into a nasty ‘feature’ of FLfile.listFolder in Flash 8 on Windows XP.

    FLfile.listFolder doesn’t list files that don’t have the archive flag enabled.

    Workaround: none except enabling the archive flag for all files you are searching for.

    :(

    Consuming webservices in Flash 8

    May 5th, 2008 by Hans Wichman

    During a partial refactoring process of the Behrloo client system, one of the items on my list was the backend webservice result processing. Without going into a lot of detail how these services are wrapped, it suffices to say that somewhere in the application a couple of webservices are being initialized and utilized through the macromedia webservice classes.

    You might be familiar with them, they come in several flavours, for example the WebServiceConnector and the Webservice class. Personally I don’t like to use the WebServiceConnector, mostly since the Webservice class is simple enough to use and tends to give you more control over what is happening.

    Basic example

    As a simple example of using this Webservice class, paste the following code onto the first frame of the timeline in a new fla document (on a sidenote, REAL applications are not written on a timeline, but for example purposes/quick proof of concepts, this will do just fine):

    //example 1
    import mx.services.*;
    
    var lLog:Log = new Log (Log.VERBOSE, "myLog");
    var lWebService:WebService =
         new WebService ("http://www.flash-mx.com/mm/tips/tips.cfc?wsdl", lLog);
    

    You’ll note some log information passing by in your output window, showing you the progress during the initialization process and such. Somewhere at the end you’ll see a line like:

    4/23 13:14:32 [INFO] myLog: Made SOAPCall for operation getTipByProduct

    This means the webservice supports an operation called getTipByProduct. Other than that you don’t really know much about it. This is the first step in handling webservices, getting a grip on what your dealing with. Although there are different methods for doing so, I’ll mention two:
    1. the webservice panel in flash, this allows you to enter a webservice url, and check the methods including the required parameters and expected return types in flash.
    2. WebServiceStudio, a neat little tool. You might need to disable your proxy if it is giving you the same headaches as ours, but other than that, this tool will let you open, inspect and interrogate webservices.

    Looking through Flash’s helpfiles you’ll find an example where the getTipByProduct is called with a string argument of “Flash”, so let’s try that one by extending our example.

    Webservices are asynchronous

    First thing to realize is that, just like most things in flash, a webservice is asynchronous, meaning that code following the instantiation of a webservice will execute before the webservice is actually instantiated. An example that demonstrates this fact extends the previous example:

    //example 1
    import mx.services.*;
    
    var lLog:Log = new Log (Log.VERBOSE, "myLog");
    var lWebService:WebService =
        new WebService ("http://www.flash-mx.com/mm/tips/tips.cfc?wsdl", lLog);
    
    //example 2 addition
    trace ("*** You'll see me before the log output has completed ***");
    

    This ofcourse means that if you try to call a method on the webservice before it has been instantiated the call will fail. In other words: we will have to wait till it has been successfully instantiated. Without too much further explanation, we’ll just show the complete process of calling a method on the webservice and showing the results and then continue to the result processing part, since the process itself is explained in enough detail in the Flash Manual:

    //example 1
    import mx.services.*;
    
    var lLog:Log = new Log (Log.VERBOSE, "myLog");
    var lWebService:WebService = new WebService ("http://www.flash-mx.com/mm/tips/tips.cfc?wsdl", lLog);
    
    //example 2 addition
    trace ("*** You'll see me before the log output has completed ***");
    
    //example 3 addition
    import mx.utils.Delegate;
    
    lWebService.onFault = function () { trace ("WHOOPS!"); }
    lWebService.onLoad = Delegate.create (this, _performExampleCall);
    
    function _performExampleCall() {
       trace("\n\nPerforming example call...");
       var lPendingCall:PendingCall = lWebService.getTipByProduct("Flash");
       lPendingCall.onResult = Delegate.create (this, _parseResult);
    }
    
    function _parseResult (pResults:Object) {
       trace ("\n\nResults:\n"+pResults);
    }
    

    Decoding webservices results

    The thing to note in this example is that the result is a simple string. However, and that is were we get to the interesting part of this post: that is not always the case. The result could be an array, a predefined class, or some other complex object. This is were a couple of other settings/flags come into play:

    - doDecoding
    - doLazyDecoding

    The Flash manual has this to say with respect to these two flags:

    SOAPCall.doDecoding-description:
    Turns decoding of the XML response on (true) or off (false). By default, the XML response is converted (decoded) into ActionScript objects. If you want just the XML, set SOAPCall.doDecoding to false.

    SOAPCall.doLazyDecoding-description:
    Turns “lazy decoding” of arrays on (true) or off (false). By default, a “lazy decoding” algorithm is used to delay turning SOAP arrays into ActionScript objects until the last moment; this makes functions execute much more quickly when returning large data sets. This means any arrays you receive from the remote location are ArrayProxy objects. Then when you access a particular index (foo[5]), that element is automatically decoded if necessary. You can turn this behavior off (which causes all arrays to be fully decoded) by setting SOAPCall.doLazyDecoding to false.

    Let’s look into doDecoding first:

    Although the description is pretty clear, the actual results I got when interpreting webservice results in Behrloo (which uses a .Net webservice backend), were kind of puzzling. When I turned decoding off, I still got an xml object as a result (while I was expecting a large string of some sort), and when I turned decoding on, I got an object which consisted of nodes of type String, Boolean, Array but also of XmlNode (so part of the result was still xml).

    In the first implementation of the Behrloo backend, I had decoding turned on, and I dealt with both ‘decoded’ nodes, and xml nodes, which I decoded myself using several xml parsing mechanisms. However triggered by the testresults above, I decided to dive a bit deeper into the WebService class source code, and I found that under the hood the Webservice class is already an XML object to execute any calls to a webservice. This means that WHATEVER you do, the result is always already an XML object. With or without decoding.
    With decoding turned on, it goes on to try and decode your object, EXCEPT for the nodes with an xsi:type=”…” attribute, which unfortunately most of my nodes had. I found no way to override this behavior, which means that the default decoding mechanism didn’t do a lot to help me.

    Disabling the default decoding

    By default, the result is decoded. This takes time, and is kind of useless if you are not using this feature anyway. However disabling the decoding cannot be done on a pendingcall since in order to get a reference to a pendingcall, you need to execute it first, so we disable the decoding through:

    _myWebService.getCall ("<operation name here").doDecoding = false;

    If you want to do this automatically for all calls defined on a webservice use something like:

    for (var lOperationName:String in _myWebService.stub.activePort) {
    	myWebService.getCall (lOperationName).doDecoding = false;
    }
    

    So what about doLazyDecoding?

    LazyDecoding only kicks in if you have doDecoding enabled, after all if we do not decode anything, setting it to lazy has no effect.

    Parsing the webservice result with decoding turned off

    Well assuming you still want to use webservice and don’t want to switch to something like remoting, we use a simple XmlUtil class that converts XML objects to complete actionscript objects. In our project we need to interpret the complete result from the webservice, so this is feasible (in other words, we don’t spend time decoding object we don’t use anyway).

    The source for our XmlUtil can be found here (save as XmlUtil.as in nl/trimm/util):
    XmlUtil.as

    Flash HitArea quirks

    May 5th, 2008 by Hans Wichman

    Also known as:

  • dynamically drawn hitarea bug
  • filter applied hitarea bug
  • hitarea no longer works
  • hitarea stops working
  • I recently noticed two weird bugs while handling hitArea’s in Flash (I say bug you might say feature).

    Situation 1:

    - you have a clip on the timeline, let’s call it dialog
    - you have a large hitarea below the dialog, let’s call it largeHitArea
    - you have connected the hitarea to the dialog: dialog.hitArea = largeHitArea
    - you have set the onPress of the dialog to anything but null

    Everything works fine up to this point, the large hitarea makes the dialog act as a modal dialog, since you cannot trigger any mouse events below it.

    Now the following happens:
    - during a graphical redesign you think you are smart, fast and furious YEAH BABY and you apply a DropShadow to the dialog clip on the timeline.

    Next thing you know, your hitArea has died and gone forever, dramatic ain’t it?

    Cause: no idea, but I think cacheAsBitmap has to do with it.
    Workaround: set the dropshadow on the dialog clip through code.

    Situation 2:

    - you have a clip on the timeline to which you want to attach a dynamically drawn hitarea

    Everything works fine up to this point.

    Now the following happens:
    - during a graphical redesign you think you are smart, fast and furious YEAH BABY and you apply a glow filter to the dynamically drawn hitArea.

    Next thing you know, your hitArea has died and gone forever, dramatic ain’t it?

    Cause: no idea, but I think cacheAsBitmap has to do with it (again)
    Workaround: create a bitmap from the dynamicall drawn hitArea first, attach it to a clip, set THAT clip as hitArea and apply the glow filter to it.

    Skinning the v2 Alert component

    April 19th, 2008 by Hans Wichman

    I ran into some trouble today that after skinning a v2 Alert component, none of my changes showed up when I implemented it in my main fla. My main fla ofcourse had it’s class export frame set to something different than export in first frame, and I knew that was the problem, but not quite how to fix it.

    Normally you set the class export frame to something like 2 or 20 or whatever, disable the ‘export in first frame flag’ on all your content by running a jsfl like:

    //
    // DisableExport
    //
    // Sets the linkage identifiers for all items to false

    var items = fl.getDocumentDOM().library.items;
    var item;
    for (var i=0; i item = items[i];
    if (item.linkageExportForAS == true) {
    fl.trace (item.name);
    item.linkageExportInFirstFrame = false;
    }
    }

    and then you make sure that somewhere after your class export frame you have a movieclip on the timeline containing all the stuff for which you disabled the first frame export.

    Apparently there is an order to that content as well, and I had never encountered that before. In my case I had overridden the TitleBackground clip, the ActivatorSkin and ButtonSkin, and you have to make sure these are loaded before the components that use them.

    So imagine you have a ‘all_my_content’ clip, simply put 2 frames in it, put the overriding clips on the first frame and all the components on the second with a nice stop(); to go along.

    Problem solved.

    Alive tattoo

    March 6th, 2008 by Hans Wichman

    It seems I’m posting more artwork than flash related stuff nowadays, and I love it ;) .

    Anyway here is a sketch for a tattoo that comes alive and digs its little claws in.

    I overbleached it a bit, but it’s a rough anyway to paint over, which will bring back the detail (I hope:))