Adobe MAX 2008, I’m going, you going? Woo!
Just signed up for MAX 2008 WOO! A few days ago I was sure I was going to be able to go, due to schedule and finance crunches (CS4 $$ KaCHING $$). But Ben Forta to the rescue, he may even have a a few discount codes left for you.
I was disapointed that so few familiar names were going to be at the official MAX, now I see they have not 1, but 3 sister unconferences answering my hunger. My favs beeing the FITC one followed by the 360Max, I may be presenting at the FITC one, depends on how things go.
I'm staying at one of the many hostels nearby the Moscone center, who knew there were so many? , and walking as it's that close (for me)
Since I just got through with the <head> conference, it's interesting to do a cost breakdown
- Shuttle To and From Airport x2 = $6-$30, 1 hr
- Airport from Burbank to SFO x2 = $250 after taxes
- Airport to Hostel X 2= $10 1hr
- Transit between Hostel to Conference x 8 = free, ~4 hrs.
- Conference = $1200-$1400
- Hostel for 4 nights = $120
- Food $25/day (Max feeds people very well) x 4 = $100
So ~$2K vrs $140.
Last time I went up, I took Southwest which was cheaper than flying up into SFO, but with the shuttle into the city, and the resulting nausea from a kamikazi driver..turned out to be rather expensive. It was funny story, after winding through traffic in a van with squishy shocks, depositing me on the street, the driver was hinting around for a tip, I was half tempted to give him my breakfast, my stomach at that point didn't want it anymore...
Flash: Embedding Fonts via the Font Symbol
This is far tricker than I remember it being in Flash8. Again, since the introduction of AS3 it seems like bar for working in Flash for doing everyday things has gotten harder for people, instead of easier.
Embedding Fonts via a Font Symbol in the library, I believe used to allow textfields just to select that Font (denoted with an *) and that's all they had to do to embed a font. In AS3 it appears they have to add a few more steps. I was actually surprised at some of the results. The linkageID and Class name don't make a difference.
I certainly wouldn't expect ExampleA and ExampleB to work they way they did.
I model this behavior as internal to the Flash player is a Embedded Font Table, that either has the character(s) or doesn't, and the embed property has access to those goodies or not. The character request is just an IDE trick/compiler hint, to build that font table if one hasn't already been provided, but doesn't exist anywhere in actionscript... a TextField doens't have embedCharacters, nor would that particularly make sense.
Metaphorically the behavior is a bit like having a locked-refrigerator full of food vrs going out to the OS to eat, and each swf as a guest has keys to to that or not. There is only room in the refridgerator for one gallon of milk in the door so even if that's partly empty (e.g. you only embedded capitals), if you try to put a full one in, it won't fit, until the previous one is unloaded. I have run into cases where one swf wouldn't share the fonts with another 'guest' swf, which I still haven't figured out yet.
Note you can test whether a font is embedded or not by rotating the TextField, else you may think it's embedded but since it's on your OS, it still shows up.
If you haven't you may want to check out loading Font Libraries dynamically. Here and Here
Flash: FDT Workflow, debugging with FDT with SOS or the built in Debugger
Powerflasher's FDT is the best actionscript editor on the planet (yes IMO better than FlexBuilder), SOS is it's one of the two primary options.
SOS is a Java based Trace Console that is much faster than the one built into Flash CS3, it offers regexp for filtering, searching, line coloring, collapsing. It's fast, and it's XMLSocket based so can be used pretty much anywhere. It has some neat features that allow you to create menus that issue commands, so your app can 'talk' to the SOS, creating menu items to callback to run different tests. Here's a long post covering how to use it.
A new one in FDT Enterprise is the built in Debugger. Which works similarly to the one built into Eclipse for coding Java (also similar to the one in the Flash IDE) allowing breakpoints, introspecting variables, stepping through. This useful feature as far as I know hasn't been in any non-Adobe tool, up to this point, as it has only recently become possible. Here's a great short tutorial if you haven't used it before.
Flash: SOS Max, the best Flash trace() output debugger on the planet
PowerFlasher has released a new verison of the best trace output debugger on the planet (IMO). Called SOS Max. Though the new version was released just a month ago, and it's not even Halloween, It's feels just like Christmas to me!
Here's why SOS rocks.
- It's cross plaform, written in Java.
- It's fast, small...kinda like Flash
- It's can use RegExp to search through the logs.
- It can talk birectionally to the content, and react.
- It can read the trace logs of the debug player...even when compiling normally in Flash
- It has colorizing and folding of messages.
Here's my favorite reason. Normally when using Flash you have to compile in order to see the trace output. But sometimes debugging it's trying different things, and you don't really need to recompile, just retest different things. Often in more complicated projects, compiling, with all the fonts and assets takes a long time. With Flash's trace output, you can't see it unless you compile again. With SOS setup to view the trace log ...you can.
Since it's socket based, it can help with a swf running from anywhere. But for using the socket debugger you need help.
There are a few AS libaries to make SOS more friendly. This FDT post covers one of them.
The troyworks framework has an TraceAdapter which I prefer and use. It's pretty easy to use, in fact it's injectable and can be turned back to the normal trace with a single commenting of a line.
A test case which shows all the features it supports is here
What it does is introspect the string passed to the trace to look for certain fields. One of my favorite is the ability for SOS to color highlight lines.
-
-
public function traceOutputTests():void{
-
trace("number " + 1);
-
trace("string" + new String("HelloWorld"));
-
trace("error " + "error message"); //won't work
-
trace("ERROR " + "error message2"); //will work
-
trace("WARNING " + "warning message");
-
trace("INFO " + "info message");
-
trace("HIGHLIGHT " + "highlight message");
-
trace("\\\\\\\\\\\\\\\\ " + "start section message");
-
var _ary : Array = ["A","B","C","D"];
-
trace("array1 " + _ary);
-
trace("array2 " + util.Trace.me(_ary, "Array2", true)); //shows code folding
-
trace("////////////////// " + "end section message");
-
-
}
-
Getting setup with SOS is pretty easy, just download and start it up (if you have java installed), but to get it to work with the normal trace() output you may have to follow these instructions primarily getting the flash debugger installed and coordinating where the debug log is created.
Using TraceAdapter with SOS
Past that you have to override the trace function. You can do it at a class level like
-
-
public var trace:Function = TraceAdapter.SOSTracer;
-
Then to set it back do
-
-
public var trace:Function = TraceAdapter.NormalTracer;
-
or
-
-
//public var trace:Function = TraceAdapter.SOSTracer;
-
I prefer setting it once early on like
-
-
TraceAdapter.CurrentTracer = TraceAdapter.SOSTracer;
-
And then have other classes use that reference, which centralizes configuration, during initiailzation. (Though will not update all references a 2nd time)
-
-
public var trace:Function = TraceAdapter.SOSTracer;
-
The only thing I've found using it, is that it does seem (along with Eclipse) to need lots of RAM and CPU. Periodically I assume to memory leaks or fragmentation in garbage collection I find I have to cose down both SOS and Eclipse. Also if I'm not doing lots of debugging I find that SOS consumes significant CPU when it's idling, waiting for connections, so I shut it down.
Flash: fl.managers:FocusManager could not be found FIX
Got these compiler errors compiling a document level script only movie, that didn't use any components. That had an import statement like
-
-
import fl.managers.FocusManager;
-
var fm : FocusManager;
-
fm = new FocusManager(view);
-
fm.activate();
-
You would think that would work, however when compiling it gives these errors:
1046:Type was not found or was not a compile-time constant: FocusManager
1180:Call to a possibly undefined method FocusManager
1172: Definition fl.managers:FocusManager could not be found
The last is the key to solving it, it means that despite the import request, the compiler can't find anything else to go with it. Meaning that it's not actually there!
Solution
The solution is easy. Take any interactive component (Label, Button are smallest), from the components library, drag it to the stage, then delete it from the stage. At this point the library will have a bunch of stuff for that particularly component, hidden within (likely the CompiledClip, "ComponentShim") is the appropriate magic to get FocusManager. Then recompile..should be fine.
<Head> Conference is rocking! and thoughts about the past and future of webconferences
It's all about getting ... <head>
Dude, this is the way conference presentations should be. Hella fun. No messy travel plans, No standing in lines at airports, No rental car misadventures with wrong MapQuest printouts. No need to dressup. No consuming bad closest-restaurant-to-hotel food+coffee. No worrying about hooking up the projectors, no worrying about having enough battery power, or being close to an outlet, or being able to read the slides clearly behind the head of the person in front of you, all the while juggling the cup of coffee and danish and laptop in the lap. No trying to rush get to the session conveniently located the opposite end of the convention center, only to find the room already at capacity. No staying up late nights adding last minute details to presentations... well okay that's tradition.
I'll be presenting in a few minutes in room 2 (schedule changed) on 'One SWF' to rule them all on what I think is a very cool approach to maintaining swf portability between web and AIR runtimes, which wasn't possible in Flash9, not sure about the Flash10. I'll be showing an app that when running in AIR can create, update, read and delete images from a webcam to the desktop, and when running the same swf running from the server, can create, read, update, and delete those images on the appropriately configured server via a PHP All with only a few paragraphs of code. Try that with C++!
It's day2 at if you're not "there", you should definitely check it out, I'm loving it. It's a distinctly different flavor than the traditional conferences I've been to so far like Max, Flex|360. Also if your in San Fran, London. you might be able to still attend one of the live hubs. Even Second Life.
Having helped run a few conferences and presented a few times, I know how challenging and rewarding putting on an event like this can be, I've been on all sides of the podium, IT, organizer and presenters. This conference (like all) has had some significant technical issues. But really the problems are expected, it's how the team recovers, and managing expectations. So far Aral and his team are doing a great job rolling with the punches. In the end as disappointing as it is for all when a presentation doesn't work right, the chat always works so in some ways it's more collaborative than the normal conferences.
It's my first time attending a conference via Connect, counter intuitively I find it's pretty intimate, a buzz of chat, the low threshold in which anybody can join the fray. the live face of a person talking, and well miced, bandwidth permitting.
The Past of Online Presentations
Waxing philosophical. It's funny how life can take you round full circle. Back in 2000 right, right after the first wave of dot-com-fallout. I joined a small company called Presedia who had the same goals I mentioned in the first paragraph. At the time, somewhat at the start of the first internet bubble, the web was in it's first golden age. Presedia's target was the corporate market, where as R&D turned into commercialization, routine "Road Shows" would be organized to put gobs of salespeople in the same room, up to date on what they would soon be selling. Getting 1000's of people across the country into the same room with enough donuts wasn't cheap.. gas wasn't $3-4/gallon either at the time.
So Presedia had the simple idea, make PowerPoint web friendly, add a plugin to the desktop to narrate, it, and upload it, share it, have it play in the webbrowser. As an aside, I think that Microsoft's failure to make the office suite web friendly has spurred countless competition like this.
The first client for Presedia, was...Sun Microsystems, which had just 3 years earlier created the Java programming language. Java was the darling of the day, applets were going places that html and even Flash just couldn't touch. As at the time Flash 4, just turned to Flash 5, scripting was still mostly timeline hacks, like instead of a function, it used gotoAndPlay("doSomething") !!! for loops were gotoAndPlay, then gotoAndPlay! (and we had to walk uphills both ways, with dialup).
After getting a reliable presentation narration, and conversion. Ironically, Java as the front end on the network (in particular sun) was very finicky about streaming through corporate firewalls and after months of headbanging. I in a bold move told the CEO and President that we should explore using Flash for the presentations and player, at the time I was a server side engineer working in Java. I was the only person in the team with any design ability, or any particular care for what is now the IxD field, and could put up with Flash's extremely quirky nature. So I was asked to come up with a prototype which I built by hand, and the CTO started investigating rendering PowerPoint to Flash instead of Java. This was a rather significant and positive step for both Presedia and myself.
Turns out for me, that working with visual components made me far happier than working purely server side, and my intuitions that Flash > Java for the presentations were correct. We had far fewer problems, and the presentations streamed and started better. Fast Forward... Presedia became Macromedia Breeze, and Breeze became Adobe Connect which is the technology, that powers the conference. And I've been preparing yet another PowerPoint to get converted to Flash. Deja Vu!
Given that was 10 years ago, makes me wonder, did Dinosaurs have PowerPoint?
Another strange connection, is one of the conferences I helped organize was largely Singularity oriented, and in it's recent incarnation has turned into "SingularitySummit". The original name of the conference was Singularity, which confused me for the longest period of time.
The other amusing bits, at the time PowerPoint to Flash was a very proprietary and expensive thing to purchase. Now PowerPoint to Flash conversion is in the commodity camp. There are $50-$100 stand alone tools with unlimited, and Open Office will do it for free. I'm actually using Open Office (built in Java) to create the presentation for the talk, that will be converted to Flash.
The Future of Online Presentations + Conferences
I think that <head> is a good example of how conferences are going to go. In many ways is a parallel to the way home theater is competing with traditional movie sales. With the ubiquity in highspeed bandwidth, the sensory impact between online and physical conferences is less, and in some cases opposite, online can have higher fidelity than live. Like I'm watching on a big screen perfectly setup, I have headphones, the speakers have headphones rather than drifting in and out of the mic.
In head's level, the convenience overshadows other. As the technology get's more solid with tools like Connect, the barrier to entry drops, it takes less skill, costs less for all, takes less time. Soon kids will be throwing conferences for kids.
Having participated in 'unconferences' and 'conferences'. <head> took a nice middle ground. It was scheduled so I knew what I was going in for, but the sessions were 30minutes, which like TED talks reminds me that brevity is a powerful ally. As since we are on the web, further details are only a few clicks away. It felt very different more like a group of friends piled into the living room vrs attending church. It felt more community oriented than hierarchical. With the chat window on the bottom, and back channel communication with other speakers, it felt very much like a dashboard...it was reassuring where as live presentations, sometimes no audio man, just a bunch of people sitting quietly it often feels a bit exposed.
There is a qualitative difference in the stress levels of the speakers. I'll call it the "home turf" advantage. Most of the presenters presented as they lived, either at home, or at work, a few like myself rolling out of bed, walking into the home office a few feet away. relaxed. Some taking beer breaks in the presentation! (hey it's the weekend). One was even presenting in a car, via a community wireless she just found, where she parked.
This is to me a success of the web, taking the best that people have to offer, of the people across the world, without taking them out of their normal elements. The presentations are a bit rougher, more improvised, briefer, but what they don't offer in 'performance' they more than make up in charm, and commradarie. Especially in today's rapid moving times, the nature of a presentation being a dialog than a narrative feels appropriate. With the audience at all levels, a dialog is also the best way to ensure that people at all skill levels are at the same page. Rather than stilted, they feel more honest, more philosophical, more heartfelt, less of a corporate agenda, many of the goals weren't at a tech level, but rather increasingly purpose driven. In each presentation the person's personality showed through. All a very positive experience.
I missed most of Friday's sessions, (partly due to work) now I'm sorry I missed them, I suspect that this is one of the challenges for virtual conferences, it's harder to cleanly separate the time for a conference as when it's a trip. Though at least for me, I end up finding conferences filled with work anyway.
With the upcoming tide of virtual conferences, one thing I think this all means to presenters, are you should get used to having:
- a decent webcamera, ideally a decent backdrop (unless you've got a green screen like Aral) $20-30
- decent light, as webcams suck on low-light. A window nearby will do, but at night time I was practically invisible until I pointed some lights on me. Aral in the excellent speaker orientation recommended a firewire plus conventional DV cam, which is a good though far less portable
- a good headset mic - knowing how to set it up, adjust levels $30 at Radio Shack
- have a few subjects you could contribute to the community, in your backpocket you can present easily. Note you don't have to be a super genius, the community exists at all levels.
- that the number of conferences is increasing to a steady dialog. So in some cases picking.This reminds me over burningman, in that you won't be able to experience everything, so you have to prudent in what makes it and not. Even though many of these conferences will be recorded and later made public, there is something about being there able to see it unfold that makes it more special, perhaps it's just the allocation of time and attention.
Here are some other pics and reviews.
Jeremy Adactio (with pics!)
Mark Grossnickle attendee, )
Flash Security: Clickjacking the Webcam
A novel (to me) Browser security hole, that can be well exploited using interactive, DHTML and Flash. The basics of the problem are how do you know what your clicking on is actually sending the event to the subject you actually want? Given it's very easy to layer these days, to get those full page ads, or zoomable content, what if the thing being clicked on has a click interceptor in front of it?
Supposedly Flash10 has fixes for this, I wonder what in particular they did. Here's the original post at Techrepublic.
The related Proof Of Concept at Guya shows how using clickjacking to grant access to the webcam in flash, it not longer works. Great job at Adobe for patching the webpage quickly, but note they did this at the expense of legacy browsers.
One SWF To Rule Them All - a universal file wrapper for flash on web and the AIR runtime
Introduction
With the introduction of the Adobe AIR runtime, what power over the desktop, that used to be only available to full desktop applications written in 'serious' languages like C, is now within the capabilities of normal web developers, in those warm fuzzy languages like Flash. We can now write files and create folders as easy as publishing for AIR and using the AIR specific flash.filesystem.* set of classes. That's great but... it has one serious drawback..anytime we use any of those API's we lose portability of our swf to the web! So if you have a swf, that can be run off the web or locally you'd normally be forced to maintaining two independent SWFs - one for AIR and one for flash player. Needless to say this leads to twice the headache.
In this article we present a solution, showing how to use a universal file wrapper as a part of the troyworks framework. Using this approach, you will learn how to create an SWF that will read/write to files through flash.filesystem.* when embedded in the AIR runtime and that will save files through HTTP when run in the flash player (browser or standalone)...resulting in one swf to rule them all.
If you're currently not in the mood for going through the internals, jump straight to the usage examples section.
Also I'll be giving covering this topic at the <head> presentation on Saturday October 25 2008, using some different code examples. If you haven't heard of <head> (formerly Singularity) It's a kick ass lineup inexpensive web-accessed and covers all manners of scaling, love, flash, flex, and donuts. Okay maybe not donuts, but be sure to give it some love by checking it out. It's only $149!!!
AIR's flash.filesystem
The are three classes in the flash.filesystem library: File, FileStream and FileMode. This is what you normally use in Adobe AIR to access files on the local machine. Each File object represents a file or directory and the FileStream class provides methods to run read & write operations on files. FileMode is simply a container for the various modes in which you can open a file: read, write, append, update.
In our case, when running in the AIR runtime we want to save on the local filesystem, when in a browser we want to save via the web. The save features on the local filesystem are provided already by the AIR runtime. For the server side we need to create a special receptor:
Server side HTTP file operations
In order to provide similar file functionality for the flash player we need to prepare a server side script that will parse HTTP requests and execute file operations accordingly. The example implementation has been written in PHP, and uses HTTP post, but could easily be any other language, or possibly sockets.
Wrapping
In order to comfortably perform file operations under both environments, we need a common interface that won't change with the different underlying libraries. (why the API's weren't written with an interface baffles me)
So we're going to wrap the functionality under a set of new classes: IFile, IFileStream and IFileMode. For ease of use the wrapper has an interface almost identical to the flash.filesystem libraries for AIR except that synchronous operations are excluded due to the limitations of the flash player library.
Now that it's clear how to perform file operations in both environments, we need a way to detect the runtime and establish a way to invoke the appropriate classes accordingly. The type of player is easy to determine using Capabilities.playerType which is set to Desktop in Adobe AIR. The next step is to dynamically load an external SWF file with the libraries corresponding to the player type.
The following piece of code will load the appropriate library.
-
if (Capabilities.playerType == 'Desktop') {
-
// Load dynamic library for Adobe AIR
-
ExternalLibrary.loadAirLib(loadTests);
-
} else {
-
// Load dynamic library for Flash Player
-
ExternalLibrary.loadPlayerLib(loadTests);
-
}
Utilizing the ApplicationDomain functionality we'll be able to import the classes from the external SWF into the parent movie. Having that, we can use getDefinition() to fetch a class reference and assign it to a variable (which will act similar to a static class) or we can instantiate an object from it.
Returns a class reference:
-
ExternalLibrary.static('IFile');
Returns a class instance (object):
-
ExternalLibrary.create('IFile')
File operations
As previously mentioned, the wrapper presents an interface very similar to the original AIR flash.filesystem interface. However all the operations need to be asynchronous due to the fact that we have to keep the interface unified and the synchronous operations aren't possible over plain HTTP. That means we have to fall back to asynchronous mode... this is generally a safer way to program as when accessing network drives or slow disks, it's preferential to be non-blocking for the task to complete.
Example of reading file contents:
-
public class FileRead
-
{
-
public function FileRead()
-
{
-
var iFile = ExternalLibrary.static('IFile');
-
// Creates an object for file 'myFile.txt' on the desktop
-
var aFile = iFile.desktopDirectory.resolvePath('myFile.txt');
-
// File is ready for access only after the STATUS event
-
aFile.addEventListener(iFile.STATUS, onStatus);
-
}
-
-
function onStatus(ev:Event):void
-
{
-
// The event target is the file object
-
var aFile = ev.target;
-
// Open stream for 'myFile.txt'
-
var aStream = ExternalLibrary.create('IFileStream');
-
var iFileMode = ExternalLibrary.static('IFileMode');
-
aStream.addEventListener(Event.COMPLETE, fileCompleteHandler);
-
// Define other nifty event handlers
-
// aStream.addEventListener(ProgressEvent.PROGRESS, fileProgressHandler);
-
// aStream.addEventListener(Event.CLOSE, fileCloseHandler);
-
// aStream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
-
// aStream.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
-
// aStream.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
-
aStream.openAsync(aFile, iFileMode.READ);
-
}
-
-
function fileCompleteHandler(ev:Event):void
-
{
-
var aStream = ev.target;
-
var ba = aStream.bytesAvailable;
-
var cs = iFile.systemCharset;
-
var str:String = aStream.readMultiByte(ba, cs);
-
// trace('The file contains: ' + str);
-
aStream.close();
-
}
-
}
To get an idea of how to do other useful things using this library please browse to the flash.filesystem section in Adobe's LiveDocs. All you need to remember is that you must use the asynchronous versions of the methods. Watch out for the ones that are originally synchronous only and were converted to asynchronous only in the wrapper.
Be sure to check out the tests directory and you'll find many examples to learn from.
Image save example
Prerequisites to this example is having an AIR runtime installed and the ability to publish for AIR, if using CS3 you'll need these updates. If using CS4 they are likely built in.
Lets practice using the wrapper library by studying an example project from scratch. This result will download a photo off the web and ten download it to the desktop if local Svn checkout http://troyworks.googlecode.com/svn/trunk/AS3/examples/SaveImage to download the project file sources. The first thing we need to do is prepare an air-lib.swf and fp-lib.swf file. For each one you need to create a new Flash ActionScript 3.0 project, add the classpath for the troyworks.com and set the document class to com.troyworks.io.AirLibSwf and com.troyworks.io.FpLibSwf accordingly. The Version setting needs to be set to Adobe AIR in the Publish settings for the AIR project. Once you publish both projects and no compiler errors occure you will end up with air-lib.swf and fp-lib.swf with the wrapper classes embedded into them.
You are now ready to create the actual image save project which we'll call SaveImage. Set the document class to SaveImage and create a SaveImage.as file alongside the fla file:
-
package
-
{
-
import flash.display.Sprite;
-
import com.troyworks.util.ExternalLibrary;
-
import flash.net.URLLoader;
-
import flash.net.URLLoaderDataFormat;
-
import flash.system.Capabilities;
-
import flash.events.Event;
-
import flash.utils.ByteArray;
-
import flash.net.URLLoader;
-
import flash.net.URLRequest;
-
import flash.display.Loader;
-
import com.adobe.images.JPGEncoder;
-
-
public class SaveImage extends Sprite
-
{
-
// Change this to your own domain
-
protected var imgUrl:String = 'http://blaze.dnc.pl/~tw/flash_cs3_logo.jpg';
-
-
// Image data
-
protected var imgData:ByteArray;
-
-
public function SaveImage():void
-
{
-
if (Capabilities.playerType == 'Desktop') {
-
ExternalLibrary.loadAirLib(init);
-
} else {
-
ExternalLibrary.loadPlayerLib(init);
-
}
-
}
-
-
protected function init(ev:Event):void
-
{
-
var request:URLRequest = new URLRequest(this.imgUrl);
-
var loader:Loader = new Loader;
-
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImgLoad);
-
loader.load(request);
-
this.addChild(loader);
-
}
-
-
protected function onImgLoad(ev:Event):void
-
{
-
//trace(ev.target.content.bitmapData);
-
var jpgEncoder:JPGEncoder = new JPGEncoder(85);
-
this.imgData = jpgEncoder.encode(ev.target.content.bitmapData);
-
-
// iFile is the definition of file.flash.filesystem.File (static)
-
var iFile = ExternalLibrary.static('IFile');
-
var aFile = iFile.desktopDirectory.resolvePath('flash-logo.jpg');
-
aFile.addEventListener(iFile.STATUS, onStatus);
-
}
-
-
protected function onStatus(ev:Event):void
-
{
-
var aFile = ev.target;
-
-
var aStream = ExternalLibrary.create('IFileStream');
-
var iFileMode = ExternalLibrary.static('IFileMode');
-
-
aStream.addEventListener(Event.CLOSE, onClose);
-
aStream.openAsync(aFile, iFileMode.WRITE);
-
aStream.writeBytes(this.imgData);
-
aStream.close();
-
}
-
-
protected function onClose(ev:Event):void
-
{
-
trace('file written to disk');
-
}
-
}
-
}
Remember that both of the swf files published earlier (fp-lib and air-lib) have to be present in the same directory. The flow of the above code is pretty simple:
- the constructor is invoked (since the project's document class was set to this class) and the proper wrapper library is loaded
- when the library is downloaded the callback handler is invoked (SaveImage::init in this case)
- a sample image is downloaded from the web (a flash logo)
- onImgLoad invokes a jpgEncoder that will translate the image's BitmapData into a ByteArray; a file object is registered for the destination file (the file that we want to save the image into) and we need to wait until it's status clears (file information might be fetched from a remote server)
- when the file details become available the onStatus method opens a stream and writes the binary data into the target file
- reaching onClose() means that the stream closed and the file should be saved (provided that no errors occurred - additional event listeners can be set up to catch them)
Switching the version field in the Publish settings for this project will cause the output swf to save a file on your desktop (when using AIR) or server (when publishing for the flash player).
Server side file operation handler
The script is built in PHP and uses Apache's mod_rewrite to proxy file calls to a PHP handler script. The .htaccess contents are as follows:
-
RewriteEngine On
-
RewriteRule ^files(/.*)?$ receiver.php?data=$1 [L]
Note if you update the .htaccess you will likely have to restart your webserver. Considering the following directory structure:
-
/PostReceiver/.htaccess
-
/PostReceiver/receiver.php
-
/PostReceiver/data
...that means that any HTTP request for http://example.com/PostReceiver/files/* will be parsed by receiver.php. In that PHP script you can determine the type of request (read/write/append/delete/list/etc.) and execute the necessary actions. For example accessing /PostReceiver/files/bDir?mode=list will return the file listing for the directory bDir (/PostReceiver/data/bDir that is since files is just a mod_rewrite keyword in this case). Review the code in the PostReceiver directory in the library package to gain more knowledge about this reference implementation. A good idea would be to download LiveHttpHeaders for Firefox and try out a few requests manually yourself.
In the making: Flash 10 file handling
The newest version of the flash player will be able to save files locally. This fact however does not make this solution obsolete, because it will not handle programmatic file operations. Any time you want to read or save a file you will need to open the system's file browse dialog and have the user to choose the file.
Case Study: The E8 Particle Simulator for Garrett Lisi
One of the projects that I have been privileged to work on in this last year, is for a friend named Garrett Lisi who is shaking up the theoretical physics field with his controversial yet elegant theory to unify pretty much everything in physics by mapping it onto the beautiful e8.
Today the video of his TED presentation came out, also available on youtube It's got a nice blend of humor and pretty profound What's cool is after working off and on on the project over about a year, finally seeing the video has helped me understand better what it is that we built, a real time particle simulation of his theory which you can play with at Garrett's website here.
It's an interesting case study, we weren't at sure that Flash was fast enough to do real time interaction of the 240 eight dimensional particles, and 10K interactions. But it turned out that the speed improvments in Flash9/AS3 to be fine. As a interaction designer it was an interesting challenge to help convey the various interactions in a user interface.
Part of the challenge was bridging the gap between Garrett's exceptional knowledge much of it requiring deep math and conceptual understanding that took years to acquire with that of the limitations of flash, and the practicalities of implmenting it. As is often in client projects, it's hard to convey what looks simple can be complicated, and contrarily what seems complicated can be super easy.
It helped greatly that Garrett had a mockup in Mathmatica, it was way less interactive, could only be stepped through, but is how he created his visualizations for the various videos. Mathmatica abstracts away much of the details that other programming languages like flash have to deal with to build multidimensional things. e.g. he would describe a single equation for a particle, and I would have to iterate through all the eight dimensions (e.g. x, y, z..) at 24fps to get it to work. Aparently rounding errors seem to be isolated from the user. However in our simulation, with the repeated calculations for every dimension, some care had to be taken to reuse math operations. To further improve performance, we also only compute changes when the user is dragging around.
One of the most tedious challenges was getting it to show the LaTex like symbol labels for the particles and dimensions. Rebuilding LaTex would have cost more than the project budget. There is a javascript+html solution, so we first tried just using fonts that contained many of the special symbols, but it turned out this required about 5 fonts that coordinating would have required rebuilding the routines in javascript, plus specifying them was tedious, it would have to be specified like "e^SQRT(1/SQRT(2)" might sigma to the power of ...you get the idea. The list of particles had to be specified in a particular way, and potentially there may be more than one particle system to map the physics properties onto. So Garrett had to render translate these from his mathmatica to an xml file that flash could consume. In the end, we threw away this approach there was no easy way to normalize the positions of the text fields. So we ended up punting and going to a movieclip with 240 frames, the symbols brought in as vectors after some Mathmatical->PostScript->Swf->decompile->Fla wizardry. This also reduced the xml overhead, since we just used an id to represent the frame of the label e.g.
labels.gotoAndStop(particle.label)
One of the cool parts of the application is the ability to select multiple particles that interact and produce others, and what of the eight dimensions to map to select for the current rotation. We spent many hours trying to eek out the exact way to signal options to the user, and deselect others. With the ability to mouse over a particle, indicate first, second and third selections deciding on colors and effects that didn't overload the already busy display. At first I start with CMYK as conceptually these are a color triangle and farthest apart, Garrett didn't like pinkish, so we went to RGB instead. We ended up using a static glow effect to indicate the selection state as since their identity was based on color, doing any other effect inside would make it hard to read. Since particles could be selected in different orders to produce similar results, the routine was far involved than most selection routines, rather than doing this at runtime, during the parsing of the xml, an object graph was created, largely linked lists so things could be very quickly traversed. This use of deeply linked object models I wish was better understood by many engineers, it can make huge performance differences, vs. using E4X, or relational database models.
Since particles show their selection state in multiple areas, the main viewport and the grid below it, we strongly used a MVC model, where all changes were to the model and these percolated up to multiple views. e.g.
public function setX(newVal):void{
if(curVal != newVal){
lastVal = curVal;
curVal = newVal;
dispatchEvent(new Event("ChangedX");
}
}
Since we had an onscreen display, there were actually two models, one for the underlying particle system and one for the onscreen presentations. So the rendering routine, reassociated and repositioned the onscreen particles which carried some additional view state (e.g. the reference to the sprite).
For the particles we ended up using the graphics API to draw them, as the symbols were reused at different sizes.
For the navigation, it took about 5 separate tries to come up with the tri-state indicator, as counterintutively any selected axis can be rotated in both the X and Y at the same time (as they are higher dimensions), in addition the entire set can be rotated in the plane, as if you had a pencil in the center. So here we have the term 'rotation' being used multiple times with very different implimentations. A common pitfall in projects.
Since Garrett's idea is an extention to the well accepted standard model, the need to show and hide these extra points, was necessary. We ended up normalizing the object model so that defining those common points in the 240 superset are indicated by a flag. What I ended up doing was using Tny to fade the extra particles in an out. In retrospect I would have preferred a slider.
The other cool but hard part of the application was the ability to project the particles into different coordinate systems. You can sort of imagine this like, the default projection was flatting a 3d image into a photograph (except 8 dimensions), and then another isometric projection...similar but different but able to be translated back and forth (all particles in 8 dimensions). During the information architecture phase we had though that the saved/bookmarked rotations would be independent. This lack of vision was very much like bringing to halves of a zipper together.
We started out with a simple 3d version, but in the end the 3d projection reduced the clarity relative to the 2d projection...a common thing I've discovered in doing infoviz. Since unlike a 3d model Garrett was more interested in the symmetries lining up, the browsing mechanism was changed to like a pan slider go from max left to max right, rather than rotate as much as you want. Here one of the intermittent bugs was due to math rounding errors, occasionally the rotation would over shoot the max by a tiny bit, and then this would spiral into weird skewing bugs. The key here was that at 24fps, small errors accumulate quickly, and in this case break the orthogonality of the intended rotation, so our solution was to add after the rotation another correction+constraint. As is typical with these projects, from his vantage it appeared to be something wrong with my code, and from my perspective there was something wrong with his formula. To solve, required bridging the gap, and stepping through the values to figure out what was going wrong, and it helped immensely that we were able to sit side by side in this phase. At one point it was immediately clear to him what the problem was, and the solution was immediately apparent to implement it. So like half a day of hassles over 3 lines of code!
Graphic Design is very important, so I went onto odesk and found a talented designer from Trigma who borrowed some of the style from other major apps to build what was done. This took about 3 iterations, primarily negotiating over how small to make the elements and to maximize the view area.
In the end, Garrett was happy with the results, and we achieved something that probably could not have been done easily with html, and will hopefully convey more information than just watching the video and certainly not by images alone. This is one of the rewards of flash. Now that the video is out one of the things we've discussed is synchronizing the display to the information presented in the video. Which would be pretty cool! But requires converting more code in mathmatica to rotate from any point to anyother point in 8 dimensions...
Garrett has made the code open source, so all the code is on code google so you can play with it if you want.
Flash: Performance Profiling Creation, StopWatch Utility
Interesting, I've been trying to decide having what objects at the base of an object model have the least overhead. The answer isn't necessarily as straighforward as it's unknown as what optimizations in the player. In particular there weren't huge differences for me between creating Objects or EventDispatchers, or between Sprites and MovieClips. I'm sure on apps like Papervision, everything ads up, but at some point it's somewhat academic if you're only able to render a few dozen to a hundred items at a time anyway.
I created 10000 objects at 31fps
- Object = 0-30ms
- EventDispatcher = 0-30ms
- Sprite not added to Stage 200ms - 1sec
- Sprite being added to Stage 2 -4 seconds
- MovieClip not added to Stage 250 -1sec
- MovieClip being added to Stage 2-4 seconds
Performance profiling in Flash seems to be very tricky, and hard to get solid repeatable numbers.
- the available resources will vary dramatically for flash between run to run, especially if you have virus runners or backup utilities, things downloading or videos playing
- during the flash player startup, results always seem 2x slower, so I use a setTimeout to kick off the testing function
- heavily influenced by frame rate, go to low, very unreliable, very high rendering overhead competes with script
- minimum timer resolution is often based on the frame rate, at 31fps, results always seem a multiple of ~16ms I'll get 0 or 16ms or 32 etc.
There's a StopWatch utility in the troyworks framework that makes it somewhat easy to run these tests, here's the code.
-
-
import com.troyworks.util.datetime.*;
-
-
var stp:StopWatch = new StopWatch();
-
-
var cnt:int = 10000;
-
var i:int = cnt;
-
var o:Object;
-
-
var dO:DisplayObject;
-
-
setTimeout(startTests, 2000);
-
-
function startTests():void {
-
-
///////////////////////////
-
i = cnt;
-
-
stp.start();
-
while (i--) {
-
o = new EventDispatcher();
-
}
-
-
stp.stop();
-
-
trace(stp);
-
stp.reset();
-
-
///////////////////////////////
-
i = cnt;
-
stp.start();
-
while (i--) {
-
o = new Object();
-
}
-
stp.stop();
-
-
trace(stp);
-
stp.reset();
-
-
//////////////////////////
-
-
-
if (true) {
-
i = cnt;
-
-
stp.start();
-
while (i--) {
-
dO = new MovieClip();
-
-
addChild(dO);
-
}
-
-
stp.stop();
-
-
trace(stp);
-
stp.reset();
-
} else {
-
-
//////////////////////////
-
-
i = cnt;
-
-
stp.start();
-
while (i--) {
-
dO = new Sprite();
-
-
addChild(dO);
-
}
-
-
stp.stop();
-
-
trace(stp);
-
}
-
}
-

