Thursday, November 01, 2007

Emulating 3D Bitmap Transformations (part 2)

(correction)
Okay, so the first time I posted this I was definitely not thinking straight at all. Here's a correction.

So let's take a look at the math involved:

In a scale, pixels (x',y') are given by the following:
x' = x * x_scale_factor
y' = y * y_scale_factor

In a skew, pixels (x",y") are given by the following:
x" = y * x_skew_factor
y" = x * y_skew_factor

If you remember our image is partly scaled and partly skewed. Remember that the images are divided along the line f(x)=x meaning along this line pixels of the skewed picture approach the scaled picture.

So let's pull out that high school math and we'll find that means that:

x * x_scale_factor = y * x_skew_factor
y * y_scale_factor = x * y_skew_factor

By substituting in the line y=x this tells us along the line y=x pixels converge when x_scale_factor = x_skew_factor and y_scale_factor = y_skew_factor.

Wednesday, October 31, 2007

Emulating 3D Bitmap Transformations

I've been looking into 3D through Flash and there's a lot of 3D projects going on in the community, but Flash is currently not natively 3D. Though the new upcoming version of Flash seems very promising.
Before I explain anything credit for this post goes to my friend Steve at BSS who originally showed me this.

So my goal was to figure out how Papervision initially worked. My first assumption is that people were using the flash.geom.Matrix class to perform perspective transformations on BitmapData, but this is not the case. The Matrix class in Flash is only able to create affine transformations.

First let's take a look at affine transformations:
Affine transformations are transformations where:
  • Collinearity between points, i.e., three points which lie on a line continue to be collinear after the transformation
  • Ratios of distances along a line, i.e., for distinct colinear points p1,p2,p3, | | p2p1 | | / | | p3p2 | | is preserved
This allows us to achieve only an isometric projection. What we are looking for is to be able to create depth using a perspective projection. With perspective we need to create a vanishing point which means lines which means parallel lines can converge. So this is a dilemma.

What We Can Do
What we can do is approximate 3D transformations by approximating a perspective transformation. We'll approximate by slicing our 2D image into smaller pieces and performing transformations on those individual pieces. As a side effect the image may be distorted, but we'll be able to control the distortion if we cut the image into smaller pieces.

Let's experiment through visually. Start with a grid image and slice it into two triangles:

Now let's take a look at two particular affine transformations.


Notice that the pixels near the line P1P4 do not move very much. These are a characteristic natures of these two particular transformations, scale and skew.

So if we take the top portion of the skewed image and the bottom portion of the scale image, it's possible to arrange the image so we're able to move only P4.


Therefore, since our pixels near our slice are able to match up, performing transformations on the individual pieces to allow our lines to converge and we are able to approximate perspective transformations with a certain degree of distortion. For each of the other points we'll simply use the same affine transformations. In my explanation I've only detailed one corner P4.

Controlling Distortion
To reduce the amount of distortion we'll simply create more subdivisions, or tessellations, for some given picture. As the pieces become smaller and smaller the distances between where pixels are and should be become closer and closer.

Here is an example with draggable corners and smoothing:

Verification Mathematically:
For those that are interested, we can prove this mathematically because of the way image data is manipulated mathematically by these affine transformations. I'll talk about this next time for the real nerds.

Download the source

Friday, October 26, 2007

Advanced OOP: Semaphore Follow Up

Here's an example of a test of AS3 Semaphore code.

http://www.henrytseng.com/storage/blog/semaphore.zip

You can download and use it according to the GNU General Public License as published by
the Free Software Foundation. Enjoy.

Saturday, October 13, 2007

Advanced OOP: Understanding Semaphores

One very useful programming algorithm that is very useful in Flash comes from concurrent programming, and is called mutual exclusion. If you've taken classes in operating systems these concepts may be familiar to you.

About Mutual Exclusion
Mutual Exclusion is all about common resources and making sure blocks of code that share these resources aren't halted when requests are made. For example, if we have 2 programs and 2 resources, both of which are required for a specific operation, both programs must take turns using the resources.

Say program 1 acquires resource 1 the same moment that program 2 acquires resource 2. Since neither program can complete their operation without the other resource; we have what we call a dead lock. This stops the program from running or creates undesired effects. Without a specific implementation to prevent this situation from occurring we have a race condition where program 1 and 2 are competing to finish their blocks of code.

What is a Semaphore
A semaphore is a classic example of an algorithm used to solve this particular situation.

Here's code for a semaphore:

class Semaphore extends EventHandler {
// events
public static var ADDED : String = 'added'; // resource was added

// used as events and states
public static var LOCKED : String = 'locked'; // anything locked
public static var UNLOCKED : String = 'unlocked'; // everything unlocked

private var _lockHash : Object;
private var _state : String; // defaults to unlocked
private var _numLocked : Number;
private var _numTotal : Number;

public function Semaphore($state:String) {
_state=($state) ? $state : UNLOCKED;
_lockHash={};
_numLocked=0;
_numTotal=0;
}

public function lock($name:String):Void {
// change count
if(_lockHash[$name]==undefined) _numTotal++;
if(_lockHash[$name]!=LOCKED) _numLocked++;

// lock item
_lockHash[$name] = LOCKED;

// something locked
if(_state == UNLOCKED) {
_state=LOCKED;
dispatchEvent({
type: LOCKED,
target: this
});
}
}

public function unlock($name:String):Void {
// change count
if(_lockHash[$name]==undefined) _numTotal++;
if(_lockHash[$name]==LOCKED) _numLocked--;

// unlock item
_lockHash[$name] = UNLOCKED;

// everything unlocked
if(_numLocked==0 && _state==LOCKED) {
_state=UNLOCKED;
dispatchEvent({
type: UNLOCKED,
target: this
});
}
}

public function get numLocked():Number { return _numLocked; }

public function get numTotal():Number { return _numTotal; }

public function get state():String { return _state; }

public function remove():Void {
removeAllEventListeners();
_lockHash=null;
}
}

In this class definition we store locks in a hash called _lockHash. We'll store things when a lock or an unlock is invoked. We'll also store the number or locks and total locks. This way we won't have to count all of the locks each time we change their values.

By default the Semaphore starts in an UNLOCKED state. If anyone requests a lock when the semaphore is in a UNLOCKED state we'll dispatch a LOCK event to tell everyone and we'll change the state to LOCKED. When the last lock is released we'll dispatch an UNLOCK event and set the state of the semaphore to UNLOCKED.

And there we have it. I'll show some more examples when I get a chance. There's some interesting applications that simplify a lot of programmatic situations.

Tuesday, October 09, 2007

Beginner OOP: Observer and Event Delegation Patterns (Part 2)

Now remember last time we talked about the observer design pattern. The observer pattern is very useful because it centralizes information and allows coordination of the interactions between objects.

But there's more. Let's see what's behind door number 2.

The event delegation design pattern is a similar programming strategy. The difference is that this pattern allows multiple types of events. For example, if we write a program which is able to draw a piano keyboard, we might create a class to represent a single key on the keyboard. This single key object might listen to a data model. Now when our keyboard data model tells us that C# is pressed we're not interested in updating the visuals for every key on our keyboard we're only interested in one key. With the event delegation pattern we're able to dispatch a single specific event that will allow only the objects subscribed to this event to be triggered.

Let's talk about terminology. Delegates are objects interested in events. Delegates listen to a specific event fired from a dispatcher. Let's take a look at some simple code to create an event dispatcher. Our event types are actually represented using strings.

To draw an analogy between the observer pattern and the event delegation pattern: Dispatchers are like observables, delegates are like observers, and events are like updates.

class EventHandler {
private var _hash:Object;

public function EventHandler() { }

private function _getListenerIndex($queue:Array, $obj:Object, $listener:String):Number {
for(var i:Number=$queue.length-1; i>=0 &&$queue; i--) {
if($queue[i].l == $listener && $queue[i].o == $obj) return i;
}
return -1; // didn't find anything
}

public function addEventListener($type:String, $obj:Object, $listener:String, $useCapture:Boolean, $priority:Number, $useWeakReference:Boolean):Void {
if(_hash===undefined) _hash={};
if(_hash[$type]===undefined) _hash[$type]=[];
var i:Number=_getListenerIndex(_hash[$type], $obj, $listener);
if(i==-1) {
_hash[$type].push({o:$obj, l:$listener});
}
}

public function dispatchEvent($event:Object):Boolean {
if(!hasEventListener($event.type)) return false;
var queue:Array=_hash[$event.type];
for(var i:Number=queue.length-1; i>=0 &&queue; i--) {
var obj:Object = queue[i].o;
var fxn:String = queue[i].l;
obj[fxn]($event);
}
return true;
}

public function hasEventListener($type:String):Boolean {
if(_hash[$type]!=null && _hash[$type].length>0) return true;
return false;
}

public function removeEventListener($type:String, $obj:Object, $listener:String, $useCapture:Boolean):Void {
if($type!=undefined && $listener==undefined) {
delete(_hash[$type]); return;
}
var i:Number = _getListenerIndex(_hash[$type], $obj, $listener) if(i!=-1)
_hash[$type].splice(i,1);
}

public function removeAllEventListeners():Void {
delete(_hash); }
}

In the above code, we have an object, our hash, which acts as an index for our different event types. Each event type then has a list of delegates. These delegates have methods which will run in their respective scopes.

The addEventListener function will first locates the event list, then if the delegate is not already on the list, it pushes the delegate to the list. When the event occurs our dispatchEvent function locate the event list and call each delegate on the list notifying them that the event has occurred. We also include a removeEventListener function to remove delegates from events.

And there we have it.

After Thought
Now, you might ask why would anyone want to write a whole new event delegation class. Aren't there classes written in Flash which accomplish this? Partly, Flash MX and other provided classes do not run code in their intended functional scopes. This creates a large problem since event delegation is typically deployed inter-object. Classes written to satisfy this need are mostly external classes outside of Flash. Grant Skinner has written a great event class called GDispatcher.

ActionScript 3.0 now uses a similar event model which allows functions to run in their proper scope. In addition, most classes in AS3 now inherit from the EventDispatcher class.

Monday, October 08, 2007

Beginner OOP: Observer and Event Delegation Patterns (Part 1)

I'm going to try to post more frequently. Apologies, my posting has been scarce by any means.

This post is for beginner programmers who want to get into object-oriented programming. Let's talk about design patterns. As you may know object oriented programming is all about objects, creating definitions of them and planning interactions between them.

Now design patterns are about common situations encountered in programming. Implementing them and using them is a common practice for programmers and is the day to day challenge of the object-oriented programmer. Of course, there's always millions of ways to hack around problems and get around things, but hacking code, producing spaghetti code, always creates problems when further changes are later required. Structuring good code while it can take more time initially to plan and carefully understand always shortens the amount of time required to later make changes. Good implementation of design patterns always directly solve problems.

It is possible to use design patterns wrong and to over-program. Remember while programming you should account for changes you will later make, but you don't have to program rules for every situation, only the ones you will encounter. It would be useless to write algorithms for situations that will never occur, all situations should obviously be understood and predetermined as use cases in advance.

The Observer Pattern
So let's talk about a common design pattern that people use frequently, the observer pattern.
Like I said earlier the observer pattern is all about interaction between your code. In the observer design pattern you have several objects which require action when a particular piece of data changes value.

That is you have an object that is observable and an object that is your observer. The object that is the center of attention is the observable, this object has data which you are interested in. When this data changes it sends an update call to all of its observers, who then execute the required actions based on this value change in the interested data.

Code for a simple Observable:
class Observable {
private var _list :Array;
private var _data :String;

public function Observable() {
_list=[];
}

public function register($o:Observer):Void {
_list.push($o);
}

public function set data($s:String):Void {
_data = $s;
_update();
}

public _update():Void {
var n:int=_list.length;
for(var i:int=0; i
}
}


Code for a simple Observer:
public class Observer {
public function Observer() { }

public function update($data:Object):Void {
/* do something exciting! */
}
}


Now to set these classes up and use them:
// let's instantiate them
var parent:Observable = new Observable();
var child1:Observer = new Observable();
var child2:Observer = new Observable();

// let's set it up
parent.register(child1);
parent.register(child2);

// now let's get the parent to
parent.data = "new data woohoo!";


Once the parent changes its data all of its children should then change their data.

This is very useful if you want to create a class to manage your in a class dedicated to being your data model, such as when you use the Model-View-Controller design pattern.

Next time let's talk about the event delegation design pattern.

Thursday, September 20, 2007

Thoughts on Flash vs. Silverlight

Here’s an interesting post:

http://labs.blitzagency.com/?p=381

While the article is not all about Silverlight vs Flash it does raise an interesting point about Silverlight’s opensource tendency. It’s important to note that Blitz Agency works directly with Microsoft.

Personally, I feel that the interactive arena doesn’t need more opensource conventions but it needs stronger experiences. We need designs that speak to users and create better experience for users beyond the common web pages found all across the web. The commonality of the Web may some times be its downfall in that so many “experiences” are merely duplicates of each other; this obviously creates a big problem for brand identity.

My own opinion is that I would personally like to see the direction of the industry moving more towards the direction of Adobe AIR (platform independence, richer capability, self updating infrastructure, … of course this list isn’t mutually exclusive). AIR provides a very strong platform for interactive experiences. Creating an appropriate business model and finding out where the platform can be used is where the next steps are. The more immersive and platform independent an experience can be will ultimately allow us to create better tools and interaction with users. More visibility and more involvement can create richer impressions.

People are already watching movie trailers, catching current events, and even paying their bills with their cell phones. Why should we stop there? As someone working in the interactive industry I feel our greatest challenge is creating new breathtaking experiences.

It would be a nice dream.

Sunday, April 01, 2007

AS3 Kicks Hiney!

I'm working on this new project which will span a year. So it's apparent we may need to upgrade to AS3 at some point and require code migration from AS2 to AS3. Big Spaceship works closely with Adobe and we're running with Flash CS3, the next Flash Professional IDE.

Looks like I've put off learning AS3 long enough. AS3 blows AS2 out of the water. The debugging is so much more stricter, the API is cleaner, the code is more powerful, and the possibilities are endless. There's more on every front.

Guess I should probably throw in the usual shameless plug, go sign up for a preorder of Flash CS3.