Archive for the “experiments” Category
For a special purpose I had to extend a Singleton, to make my system fully flexible. I found complicated solutions that wasn't fully working as intended.
To use PureMVC, you have to extend a Singleton (Facade), which is the entry point of the framework. This is working only if you don't use the Facade class (the super Singleton) before using the subclass, otherwise you get an error, as the super singleton has already been instantiated.
Well this might be a simple solution but not working in my case.
First of all, they are a lot of solutions to correctly "lock" a Singleton, because in AS3, private constructor doesn't exist. Some of them have turn around, that you can solve with a bit more code, but the solution I prefer is instantiate the Singleton out of the constructor and method:
Actionscript:
-
private static var _instance:Singleton = new Singleton();
So, if I use the super singleton with a Singleton.getInstance(), and then use the subclass with a SingletonExtended.getInstance(), this will throw an error as when the SingletonExtended is called, the Singleton is called as well and has already been instantiated.
The solution I found is compare the super class name with a static class name, basically you can't use the keyword "this" when you declare a variable, but using "super" will return the same result (well that's odd but it is what I saw):
Actionscript:
-
private static var _className:String = getQualifiedClassName(super);
And then compare it with the super classname in the Singleton constructor:
Actionscript:
-
if (_instance != null && getQualifiedSuperclassName(this) != _className) throw new Error
if I trace(_className + " - " + getQualifiedSuperclassName(this)) in the Singleton constructor:
- when I use the Singleton class, I get: "null - Object"
- when I use the SingletonExtended class, I get: "Singleton - Singleton", meaning the constructor has been called from a subclass.
Until now, it is working pretty well, I can use:
Actionscript:
-
Singleton.getInstance().singletonValue
-
SingletonExtended.getInstance().singletonValue
-
SingletonExtended.getInstance().singletonExtendedValue
But we're not completely done. We can access to our two Singleton... but wait, "two Singleton", is that possible?
In fact, it is completely possible and it is even a problem. Let's say I have a undefined value in Singleton, like:
Actionscript:
-
private var _myValueNotInitialized:String;
And then you have a public method that is setting a value to this variable:
Actionscript:
-
public function initializeMyValue():void {
-
_myValueNotInitialized = "I am initialized";
-
}
You can now use:
Actionscript:
-
Singleton.getInstance().initializeMyValue();
-
or
-
SingletonExtended.getInstance().initializeMyValue();
But here is the problem: you still have 2 instances. One in Singleton and the other in SingletonExtended. If you use:
Singleton.getInstance().initializeMyValue();
and trace:
SingletonExtended.getInstance().myValueNotInitialized
you will get null, which is normal.
This doesn't make a lot of sense for a Singleton, does it?
The solution I found is updating the Singleton instance with the SingletonExtended one.
In the Singleton, I'm now able to update the instance:
Actionscript:
-
public function updateInstance(instance:Singleton):void {
-
if (instance != null && instance is Singleton) _instance = instance;
-
else throw new Error("Incorrect Singleton update");
-
}
And in the subclass SingletonExtended, I change the getInstance method:
Actionscript:
-
public static function getInstance():SingletonExtended {
-
Singleton.getInstance().updateInstance(_instance);
-
return _instance;
-
}
This is doing the job I needed, a fully Extended Singleton with no errors accessing the super or he subclass.
I tried to break it, but if it is possible, I didn't find the hole. And if you have a better solution, I'd be very happy to see it.
You can download the source by clicking here, or see the classes below.
The main class:
Actionscript:
-
package {
-
-
import flash.display.Sprite;
-
-
/**
-
* <b>Author:</b> Romuald Quantin - <a href="http://www.soundstep.com/" target="_blank">www.soundstep.com</a><br />
-
* <b>Class version:</b> 1.0<br />
-
* <b>Actionscript version:</b> 3.0<br />
-
* <b>Date:</b> 11-2008<br />
-
*/
-
-
public class Main extends Sprite {
-
-
//------------------------------------
-
// private properties
-
//------------------------------------
-
-
//------------------------------------
-
// public properties
-
//------------------------------------
-
-
//------------------------------------
-
// constructor
-
//------------------------------------
-
-
public function Main() {
-
-
trace("--- public properties ---");
-
trace(Singleton.getInstance().singletonValue + ">>> from Singleton");
-
trace(SingletonExtended.getInstance().singletonValue + ">>> from SingletonExtended");
-
trace(SingletonExtended.getInstance().singletonExtendedValue + ">>> from SingletonExtended");
-
trace(Singleton.getInstance().singletonValue + ">>> from Singleton");
-
-
trace("--- access to public methods ---");
-
trace(Singleton.getInstance().publicSingletonMethod() + ">>> from Singleton");
-
trace(SingletonExtended.getInstance().publicSingletonMethod() + ">>> from SingletonExtended");
-
trace(SingletonExtended.getInstance().publicSingletonExtendedMethod() + ">>> from SingletonExtended");
-
-
trace("--- access to private methods ---");
-
trace(Singleton.getInstance().accessPrivateSingletonMethod() + ">>> from Singleton");
-
trace(SingletonExtended.getInstance().accessPrivateSingletonMethod() + ">>> from SingletonExtended");
-
trace(SingletonExtended.getInstance().accessPrivateSingletonExtendedMethod() + ">>> from SingletonExtended");
-
-
trace("--- access to protected and overriden methods ---");
-
trace(Singleton.getInstance().accessProtectedSingletonMethod() + ">>> from Singleton");
-
trace(SingletonExtended.getInstance().accessProtectedSingletonMethod() + ">>> from SingletonExtended");
-
trace(SingletonExtended.getInstance().accessOverridenSingletonExtendedMethod() + ">>> from SingletonExtended");
-
-
trace("--- value not initialized ---");
-
trace("before = ", Singleton.getInstance().myValueNotInitialized);
-
Singleton.getInstance().initializeMyValue(); // or SingletonExtended.getInstance().initializeMyValue();
-
trace("after = ", Singleton.getInstance().myValueNotInitialized);
-
-
// this will generate errors
-
// as you can't instantiate twice a Singleton or a sublass of Singleton
-
-
//var s1:Singleton = new Singleton();
-
//var s2:SingletonExtended = new SingletonExtended();
-
-
}
-
-
//
-
// PRIVATE, PROTECTED
-
//________________________________________________________________________________________________
-
-
//
-
// PUBLIC
-
//________________________________________________________________________________________________
-
-
}
-
}
The Singleton class:
Actionscript:
-
package {
-
-
import flash.utils.getQualifiedClassName;
-
import flash.utils.getQualifiedSuperclassName;
-
-
/**
-
* <b>Author:</b> Romuald Quantin - <a href="http://www.soundstep.com/" target="_blank">www.soundstep.com</a><br />
-
* <b>Class version:</b> 1.0<br />
-
* <b>Actionscript version:</b> 3.0<br />
-
* <b>Date:</b> 11-2008<br />
-
*/
-
-
public class Singleton {
-
-
//------------------------------------
-
// private properties
-
//------------------------------------
-
-
private static var _instance:Singleton = new Singleton();
-
private static var _className:String = getQualifiedClassName(super);
-
-
private var _myValueNotInitialized:String;
-
-
//------------------------------------
-
// public properties
-
//------------------------------------
-
-
public var singletonValue:String = "Singleton value";
-
-
//------------------------------------
-
// constructor
-
//------------------------------------
-
-
public function Singleton() {
-
if (_instance != null && getQualifiedSuperclassName(this) != _className) throw new Error("Singleton is obviously... Singleton.");
-
}
-
-
//
-
// PRIVATE, PROTECTED
-
//________________________________________________________________________________________________
-
-
private function privateSingletonMethod():String {
-
return "privateSingletonMethod";
-
}
-
-
protected function protectedSingletonMethod():String {
-
return "protectedSingletonMethod";
-
}
-
-
//
-
// PUBLIC
-
//________________________________________________________________________________________________
-
-
public function initializeMyValue():void {
-
_myValueNotInitialized = "I am initialized";
-
}
-
-
public function updateInstance(instance:Singleton):void {
-
if (instance != null && instance is Singleton) _instance = instance;
-
else throw new Error("Incorrect Singleton update");
-
}
-
-
public static function getInstance():Singleton {
-
return _instance;
-
}
-
-
public function publicSingletonMethod():String {
-
return "publicSingletonMethod value";
-
}
-
-
public function accessPrivateSingletonMethod():String {
-
return privateSingletonMethod();
-
}
-
-
public function accessProtectedSingletonMethod():String {
-
return protectedSingletonMethod();
-
}
-
-
public function get myValueNotInitialized():String {
-
return _myValueNotInitialized;
-
}
-
}
-
}
The SingletonExtended class:
Actionscript:
-
package {
-
-
/**
-
* <b>Author:</b> Romuald Quantin - <a href="http://www.soundstep.com/" target="_blank">www.soundstep.com</a><br />
-
* <b>Class version:</b> 1.0<br />
-
* <b>Actionscript version:</b> 3.0<br />
-
* <b>Date:</b> 11-2008<br />
-
*/
-
-
public class SingletonExtended extends Singleton {
-
-
//------------------------------------
-
// private properties
-
//------------------------------------
-
-
private static var _instance:SingletonExtended = new SingletonExtended();
-
-
//------------------------------------
-
// public properties
-
//------------------------------------
-
-
public var singletonExtendedValue:String = "SingletonExtended value";
-
-
//------------------------------------
-
// constructor
-
//------------------------------------
-
-
public function SingletonExtended() {
-
if (_instance != null) throw new Error("SingletonExtended is obviously also... Singleton.");
-
}
-
-
//
-
// PRIVATE, PROTECTED
-
//________________________________________________________________________________________________
-
-
private function privateSingletonExtendedMethod():String {
-
return "privateSingletonExtendedMethod";
-
}
-
-
override protected function protectedSingletonMethod():String {
-
return "protectedSingletonMethod overriden in SingletonExtended, value in super.protectedSingletonMethod(): " + super.protectedSingletonMethod();
-
}
-
-
//
-
// PUBLIC
-
//________________________________________________________________________________________________
-
-
public static function getInstance():SingletonExtended {
-
Singleton.getInstance().updateInstance(_instance);
-
return _instance;
-
}
-
-
public function publicSingletonExtendedMethod():String {
-
return "publicSingletonExtendedMethod value";
-
}
-
-
public function accessPrivateSingletonExtendedMethod():String {
-
return privateSingletonExtendedMethod();
-
}
-
-
public function accessOverridenSingletonExtendedMethod():String {
-
return protectedSingletonMethod();
-
}
-
-
}
-
}
4 Comments »
The ScrollPane component is a nice tool to use if you need to scroll a content or a list with more than text.
If you like to have everything a bit "tweened" in the sites you developed, you will feel the scroll movement of the scrollpane a bit straightforward.
There is an easy way to get this done.
When the user is scrolling the content with the scrollbar of the ScrollPane, the component dispatch an event:
ScrollEvent.SCROLL
The idea is to stop the movement of the content when this event is dispatched using:
e.stopImmediatePropagation()
And then handle the scrolling using a tweener.
I made an simple example with the CanvasUI class I built as it is using a ScrollPane component for the content, but you can use this code for a normal ScrollPane instance.
Here is the demo and the source, code of the main class below for a quick look.
Actionscript:
-
package {
-
-
import flash.display.Sprite;
-
import fl.events.ScrollEvent;
-
import fl.containers.ScrollPane;
-
import fl.controls.ScrollBarDirection;
-
import gs.TweenMax;
-
-
import com.soundstep.ui.layouts.*;
-
import com.soundstep.ui.*;
-
-
/**
-
* <b>Author:</b> Romuald Quantin - <a href="http://www.soundstep.com/" target="_blank">www.soundstep.com</a><br />
-
* <b>Actionscript version:</b> 3.0<br />
-
*/
-
-
public class Main extends Sprite {
-
-
//------------------------------------
-
// private properties
-
//------------------------------------
-
-
private var _canvas:CanvasUI;
-
-
//------------------------------------
-
// public properties
-
//------------------------------------
-
-
-
-
//------------------------------------
-
// constructor
-
//------------------------------------
-
-
public function Main() {
-
_canvas = new CanvasUI();
-
addChild(_canvas);
-
_canvas.canvasAlpha = .2;
-
_canvas.horizontalCenter = 0;
-
_canvas.verticalCenter = 0;
-
_canvas.width = 300;
-
_canvas.height = 300;
-
_canvas.addChildUI(new ContentText());
-
_canvas.refresh();
-
_canvas.scrollPane.addEventListener(ScrollEvent.SCROLL, scrollHandler, true);
-
}
-
-
//
-
// PRIVATE, PROTECTED, INTERNAL
-
//________________________________________________________________________________________________
-
-
private function scrollHandler(e:ScrollEvent):void {
-
e.stopImmediatePropagation();
-
switch (e.direction) {
-
case ScrollBarDirection.HORIZONTAL:
-
TweenMax.to(_canvas.scrollPane.content, .5, {x:e.position*-1});
-
break;
-
case ScrollBarDirection.VERTICAL:
-
TweenMax.to(_canvas.scrollPane.content, .5, {y:e.position*-1});
-
break;
-
}
-
}
-
-
// PUBLIC
-
//________________________________________________________________________________________________
-
-
-
-
}
-
}
7 Comments »
I had to get an AIR application without the OS window, let's say windowless or chromeless.
I needed to be able, with my own graphics, to minimize, restore, maximize, close, close in the system tray or dock, fullscreen and resize.
You can find several tutorials on Internet (with which I got inspired) but I put all these functionalities in one small example as it is convenient.
You need to set 2 parameters out of the code, in the XML description file:
Actionscript:
-
<systemChrome>none</systemChrome>
-
<transparent>true</transparent>
I won’t explain the code, I guess it is easy enough to find out. You can comment if something is not clear for you.
Here is the code of the main mxml file:
Actionscript:
-
<?xml version="1.0" encoding="utf-8"?>
-
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" showFlexChrome="false" creationComplete="init()">
-
-
<mx:Script>
-
<![CDATA[
-
-
[Embed(source="assets/iconDock.png")]
-
[Bindable]
-
public var IconDock:Class;
-
-
private function init():void {
-
registerMovementHandler();
-
setDock();
-
}
-
-
public function registerMovementHandler():void {
-
appCanvas.addEventListener(MouseEvent.MOUSE_DOWN, mouseDrag);
-
btResizeBottomRight.addEventListener(MouseEvent.MOUSE_DOWN, resizeWindow);
-
btResizeBottomLeft.addEventListener(MouseEvent.MOUSE_DOWN, resizeWindow);
-
btResizeTopRight.addEventListener(MouseEvent.MOUSE_DOWN, resizeWindow);
-
btResizeTopLeft.addEventListener(MouseEvent.MOUSE_DOWN, resizeWindow);
-
}
-
-
public function mouseDrag(e:MouseEvent):void {
-
if (e.target == e.currentTarget) stage.nativeWindow.startMove();
-
}
-
-
public function resizeWindow(e:MouseEvent = null):void {
-
if (e.target == e.currentTarget) {
-
var corner:String;
-
switch(e.currentTarget){
-
case btResizeBottomRight:
-
corner = NativeWindowResize.BOTTOM_RIGHT;
-
break;
-
case btResizeBottomLeft:
-
corner = NativeWindowResize.BOTTOM_LEFT;
-
break;
-
case btResizeTopRight:
-
corner = NativeWindowResize.TOP_RIGHT;
-
break;
-
case btResizeTopLeft:
-
corner = NativeWindowResize.TOP_LEFT;
-
break;
-
}
-
stage.nativeWindow.startResize(corner);
-
}
-
}
-
-
public function setDock():void {
-
if (NativeApplication.supportsSystemTrayIcon){
-
dockProperties();
-
SystemTrayIcon(NativeApplication.nativeApplication.icon).menu = dockMenu();
-
}
-
}
-
-
private function dockProperties():void{
-
SystemTrayIcon(NativeApplication.nativeApplication.icon).tooltip = "Soundstep | chromeless window";
-
SystemTrayIcon(NativeApplication.nativeApplication.icon).addEventListener(MouseEvent.CLICK, undock);
-
}
-
-
private function dockMenu():NativeMenu {
-
var menu:NativeMenu = new NativeMenu();
-
var open:NativeMenuItem = new NativeMenuItem("Open");
-
var close:NativeMenuItem = new NativeMenuItem("Close");
-
open.addEventListener(Event.SELECT, undock);
-
close.addEventListener(Event.SELECT, closeWindow);
-
menu.addItem(open);
-
menu.addItem(new NativeMenuItem("",true));
-
menu.addItem(close);
-
return menu;
-
}
-
-
public function dock():void {
-
stage.nativeWindow.visible = false;
-
NativeApplication.nativeApplication.icon.bitmaps = [new IconDock() as Bitmap];
-
}
-
-
public function undock(evt:Event):void {
-
stage.nativeWindow.visible = true;
-
stage.nativeWindow.orderToFront();
-
NativeApplication.nativeApplication.icon.bitmaps = [];
-
}
-
-
public function closeWindow(e:Event = null):void {
-
stage.nativeWindow.close();
-
}
-
-
public function setFullscreen():void {
-
var state:String = (stage.displayState == StageDisplayState.FULL_SCREEN) ? StageDisplayState.NORMAL : StageDisplayState.FULL_SCREEN;
-
stage.displayState = state;
-
}
-
-
]]>
-
</mx:Script>
-
-
<mx:Canvas right="0" left="0" top="0" bottom="0" backgroundColor="#CF3232" id="appCanvas">
-
-
<mx:Label buttonMode="true" mouseChildren="false" text="Min" top="10" color="#000000" id="btMin" click="minimize()" horizontalCenter="58"/>
-
<mx:Label buttonMode="true" mouseChildren="false" text="Max" top="10" color="#000000" id="btMax" click="maximize()" horizontalCenter="-45"/>
-
<mx:Label buttonMode="true" mouseChildren="false" text="Close" top="10" color="#000000" id="btClose" click="closeWindow()" horizontalCenter="96"/>
-
<mx:Label buttonMode="true" mouseChildren="false" text="Restore" color="#000000" id="btRestore" click="restore()" top="10" horizontalCenter="-91"/>
-
<mx:Label buttonMode="true" mouseChildren="false" y="10" text="Tray/Dock" color="#000000" id="btSys" click="dock()" horizontalCenter="8"/>
-
<mx:Label buttonMode="true" mouseChildren="false" text="Fullscreen" color="#000000" horizontalCenter="0" bottom="10" id="btFullscreen" click="setFullscreen()"/>
-
-
<mx:Canvas width="20" height="20" backgroundColor="#7B0707" borderStyle="none" borderColor="#FFFFFF" id="btResizeBottomRight" themeColor="#494949" right="0" bottom="0" />
-
-
<mx:Canvas width="20" height="20" backgroundColor="#7B0707" borderStyle="none" borderColor="#FFFFFF" id="btResizeTopLeft" themeColor="#494949" left="0" top="0" />
-
-
<mx:Canvas width="20" height="20" backgroundColor="#7B0707" borderStyle="none" borderColor="#FFFFFF" id="btResizeBottomLeft" themeColor="#494949" bottom="0" left="0" />
-
-
<mx:Canvas width="20" height="20" backgroundColor="#7B0707" borderStyle="none" borderColor="#FFFFFF" id="btResizeTopRight" themeColor="#494949" right="0" top="0" />
-
-
</mx:Canvas>
-
-
</mx:WindowedApplication>
You can download the source or install the AIR application:
In order to view this page
You must enable Javascript
And download the Flash Player

10 Comments »
I made some experiments on how to easily access to data in a Flash site. It is probably a mix with good and bad ways but I think it is worth having a look.
What I’ve done here is get data with syntax that is similar to what you get using E4X in AS3. For example with XML and E4X you can use syntax like that:
xml..item[2].author.name.text();
I’m not saying that this experiment is a good practice but I want to show you what you can do by extending the Array class and extending the Proxy class in AS3. The Proxy class is the replacement of the __resolve we were using in AS2.
Let’s say we have a website that will load data from a database at the start, for example using AMFPHP or whatever, and we are going to fill this data at run-time.
In my example I have a kind of tree with countries > cities > people.
I would like to be able to loop easily through the data, as well as getting them with a friendly/readable syntax if needed.
In my example, I have a Singleton class that contains all the data. I don’t load the data in my example, I statically fill the class. I will access to the data like that:
Content.data
This will return me a array of data, which will contains an array of countries, each instance of the country array will contains an array of cities, which will contains an array of people, etc…
So I’d like to access to my data like this:
To browse the countries:
Content.data[0]
Content.data[1]
To browse the cities in the countries:
Content.data[0][0]
Content.data[0][1]
To browse the people in the cities:
Content.data[0][0][0]
Content.data[0][0][1]
That’s nice to loop through with a “for each” or whatever. An Array is a pretty nice tool to store data as you get some functions to manage it like push, splice, reverse, etc.
But these arrays have a problem, what if I need to get more info on my countries, like the weather, an ID or the surface of the country? I would like access to more data like that:
Content.data[0].name
Content.data[0].id
This is possible with an Array as it is one of the few classes in AS3 that are dynamic. It means you can set any property at run time. For example MovieClip is dynamic and Sprite is not, try to add properties at run-time on these classes to see the result. Some people are saying that is it a good and bad things. It is easy to use but you don’t know what kind of properties will be added in your Array at run-time.
So you don’t want that, and better, you want to control what kind of data they are going to put in your Array. We will get that by extending the Array class.
There’s a nice explanation here to extends an Array, as well as control the data type your custom Array class will accept.
So far it is not bad, we can easily access to our data, we can have properties on these arrays and finally we know that we will have the right type of content inside.
The other thing I want is access to my data with a nice E4X-like:
Content.data.France.name
Content.data.France.Paris.name
Easy, we just need a property France on this array and it is done. Well, the thing is we don’t know yet what this array will be filled with, so we are talking about dynamic properties. These dynamic properties will have to be added to the array when we create it because we said we want to control and we don’t want people adding their own properties.
How do we do that?
We’re going to use the Proxy class that make able us able to handle dynamic properties and dynamic calling.
We will have a class that will be an array (what you get when you call Content.data), a class for the countries, a class for the cities and a class for the people. The people class won’t be an array.
So we have the classes DataArray, CountryArray and CityArray that are extending the Array class. We will also have a Proxy class (a class that is extending the Proxy one) for each of the Array. I choose to a Proxy class for each Array class as it is easier to understand.
Let’s see a bit of code.
The Singleton Content class
Actionscript:
-
package com.soundstep.data {
-
-
/**
-
* <b>Author:</b> Romuald Quantin - <a href="http://www.soundstep.com/" target="_blank">www.soundstep.com</a><br />
-
* <b>Class version:</b> 1.0<br />
-
* <b>Actionscript version:</b> 3.0<br />
-
* <b>Copyright:</b> Free to use and change (except to include in a framework), an notification email will be welcome for a commercial use (just for information).<br />
-
* <b>Date:</b> 05-2008<br />
-
* <b>Usage:</b> Manage a Section
-
* @example
-
* <listing version="3.0">
-
-
* </listing>
-
*/
-
-
public class Content {
-
-
//------------------------------------
-
// private properties
-
//------------------------------------
-
-
private static var content:Content = new Content();
-
private static var _data:Data;
-
-
//------------------------------------
-
// public properties
-
//------------------------------------
-
-
-
-
//------------------------------------
-
// constructor
-
//------------------------------------
-
-
public function Content() {
-
if (content) throw new Error("Content is Singleton and can only be accessed through Content.data");
-
if (_data == null) buildData();
-
}
-
-
//
-
// PRIVATE, INTERNAL
-
//________________________________________________________________________________________________
-
-
private function buildData():void {
-
_data = new Data();
-
-
}
-
-
//
-
// PUBLIC
-
//________________________________________________________________________________________________
-
-
public static function get data():Data {
-
return _data;
-
}
-
-
}
-
-
}
The Data and DataArray class
The DataArray is extending the Array class. To be able to extend the Array class you need the dynamic keyword before the class.
dynamic public class DataArray extends Array {
We have a private property type Class used to control the Data we will push in our extended Array.
private var dataType:Class;
To control the type pushed we have to overwrite 4 functions: push, concat, splice and unshift, as it is nicely explained in the AS3 documentation.
Here is the code:
Actionscript:
-
package com.soundstep.data {
-
-
/**
-
* <b>Author:</b> Romuald Quantin - <a href="http://www.soundstep.com/" target="_blank">www.soundstep.com</a><br />
-
* <b>Class version:</b> 1.0<br />
-
* <b>Actionscript version:</b> 3.0<br />
-
* <b>Copyright:</b> Free to use and change (except to include in a framework), an notification email will be welcome for a commercial use (just for information).<br />
-
* <b>Date:</b> 05-2008<br />
-
* <b>Usage:</b> Manage a Section
-
* @example
-
* <listing version="3.0">
-
-
* </listing>
-
*/
-
-
dynamic public class DataArray extends Array {
-
-
//------------------------------------
-
// private properties
-
//------------------------------------
-
-
private var dataType:Class;
-
-
//------------------------------------
-
// public properties
-
//------------------------------------
-
-
//------------------------------------
-
// constructor
-
//------------------------------------
-
-
public function DataArray(...args) {
-
dataType = Country;
-
for (var i:int=0; i<args.length; i++) this.push(args[i]);
-
length = length;
-
}
-
-
//
-
// PRIVATE, INTERNAL
-
//________________________________________________________________________________________________
-
-
//
-
// PUBLIC
-
//________________________________________________________________________________________________
-
-
AS3 override function push(...args):uint {
-
for (var i:* in args) {
-
if (!(args[i] is dataType)) {
-
trace("Error: you must push an Object type Country");
-
args.splice(i,1);
-
}
-
}
-
return super.push.apply(this, args);
-
}
-
-
AS3 override function concat(...args):Array {
-
var passArgs:DataArray = new DataArray();
-
for (var i:* in args) passArgs.push(args[i]);
-
return super.concat.apply(this, passArgs);
-
}
-
-
AS3 override function splice(...args):* {
-
if (args.length> 2) {
-
for (var i:int=2; i<args.length; i++) {
-
if (!(args[i] is dataType)) args.splice(i,1);
-
}
-
}
-
return super.splice.apply(this, args);
-
}
-
-
AS3 override function unshift(...args):uint {
-
for (var i:* in args) {
-
if (!(args[i] is dataType)) args.splice(i,1);
-
}
-
return super.unshift.apply(this, args);
-
}
-
-
}
-
-
}
To create a new DataArray instance, we actually don’t use the DataArray class directly but his Proxy, that will handle the dynamic properties.
I named the class Data, it is what you get with Content.data.
To extend the Proxy class you need to import the class itself and its namespace:
import flash.utils.Proxy;
import flash.utils.flash_proxy;
Proxy is also a dynamic class:
dynamic public class Data extends Proxy {
We have our private var that will be our DataArray instance:
private var _item:DataArray;
To control the properties and method called through our Proxy, we need to override 3 methods (with the namespace flash_proxy):
override flash_proxy function callProperty(methodName:*, ... args):* {
override flash_proxy function getProperty(name:*):* {
override flash_proxy function setProperty(name:*, value:*):void {
When you will call an unknown method, the Proxy class invoke callProperty, and when you will call or set a property (like Content.data.France), the Proxy class invoke getProperty and setProperty.
In my example I trace an Error if you try to set your own property, this is our control. When you get a property, I loop through the DataArray instance to see if I’ve got an item with that name. We could do it with the id or any property of your choice (that is in the DataArray class), and if yes I return it. This is the instance you get by using Content.data.France.
So in brief, when I call Content.data.France, I’m going through the Proxy that is basically managing the dynamic properties added at run-time. The DataArray class is extending the Array class to control the properties and data type.
Here is the code of the Data class:
Actionscript:
-
package com.soundstep.data {
-
-
/**
-
* <b>Author:</b> Romuald Quantin - <a href="http://www.soundstep.com/" target="_blank">www.soundstep.com</a><br />
-
* <b>Class version:</b> 1.0<br />
-
* <b>Actionscript version:</b> 3.0<br />
-
* <b>Copyright:</b> Free to use and change (except to include in a framework), an notification email will be welcome for a commercial use (just for information).<br />
-
* <b>Date:</b> 05-2008<br />
-
* <b>Usage:</b> Manage a Section
-
* @example
-
* <listing version="3.0">
-
-
* </listing>
-
*/
-
-
import flash.utils.Proxy;
-
import flash.utils.flash_proxy;
-
-
dynamic public class Data extends Proxy {
-
-
//------------------------------------
-
// private properties
-
//------------------------------------
-
-
private var _item:DataArray;
-
-
//------------------------------------
-
// public properties
-
//------------------------------------
-
-
//------------------------------------
-
// constructor
-
//------------------------------------
-
-
public function Data(...args) {
-
_item = new DataArray();
-
for (var i:int=0; i<args.length; i++) _item.push(args[i]);
-
}
-
-
//
-
// PRIVATE, INTERNAL
-
//________________________________________________________________________________________________
-
-
private function getString():String {
-
var s:String = "";
-
for (var i:uint=0; i<_item.length; i++) {
-
s += "index:" + i + ", id:" + _item[i].id + ", name:" + _item[i].name;
-
if (i <_item.length-1) s += "\n";
-
}
-
if (_item.length == 0) return _item.name + " is empty";
-
else return s;
-
}
-
-
//
-
// PUBLIC
-
//________________________________________________________________________________________________
-
-
override flash_proxy function callProperty(methodName:*, ... args):* {
-
var res:*;
-
switch (methodName.toString()) {
-
case 'clear':
-
_item = new DataArray();
-
break;
-
case 'list':
-
trace(getString());
-
break;
-
default:
-
res = _item[methodName].apply(_item, args);
-
break;
-
}
-
return res;
-
}
-
-
override flash_proxy function getProperty(name:*):* {
-
for each (var i:* in _item) {
-
if (name == i.name) {
-
return i;
-
break;
-
}
-
}
-
return _item[name];
-
throw new Error("ERROR: Property " + name + " not found!");
-
}
-
-
override flash_proxy function setProperty(name:*, value:*):void {
-
trace("ERROR: You can't set your own property!");
-
}
-
-
public function toString():String {
-
return getString();
-
}
-
}
-
}
Basically the cities and countries classes are doing the same, the dataType and properties are different, a country is asking a City type and the a city is asking a People type.
For example the CountryArray class asks an id and name to create the array, you have to use a syntax like:
var france:Country = new Country(0, "France");
As it is an Array extended you can still pass more arguments (but they have to be the right type), the type here is City, that is also an Array extended but a city instance is asking an id, a name and weather as parameters:
var france:Country = new Country(0, "France", new City(0, "Paris", "As bad as London!"), new City(1, "Marseille", "More sun than Paris"));
Here is how I fill the Content with some examples:
Actionscript:
-
//country
-
var france:Country = new Country(0, "France");
-
Content.data.push(france);
-
var uk:Country = new Country(1, "UK");
-
Content.data.push(uk);
-
-
// city
-
var paris:City = new City(0, "Paris", "As bad as London!");
-
france.push(paris);
-
france.myvar = "qwe";
-
var marseille:City = new City(1, "Marseille", "More sun than Paris");
-
france.push(marseille);
-
var london:City = new City(0, "London", "Quite bad!");
-
uk.push(london);
-
var liverpool:City = new City(1, "Liverpool", "Even worst than London");
-
uk.push(liverpool);
-
-
// people
-
var franck:People = new People(0, "Franck", "Dupont", 29);
-
paris.push(franck);
-
var stephane:People = new People(1, "Stephane", "Durand", 42);
-
paris.push(stephane);
-
var john:People = new People(0, "John", "Doe", 35);
-
london.push(john);
-
var david:People = new People(0, "David", "Doe", 37);
-
london.push(david);
And finally here is some example to access to your data:
Actionscript:
-
Content.data.length
-
Content.data.France.length
-
Content.data[0].name
-
Content.data.France.name
-
Content.data.France[0].name
-
Content.data.France.Paris.name
-
Content.data.France.Paris.Franck.name
-
Content.data[0][0][0].name
I didn’t optimize/clean the classes and as I said, I’m not saying that it is a good way of accessing data in Flash, but it shows you how to control data type, how to extend an Array, how to extend the Proxy class and how to handle properties and methods of a class at low-level.
Download the source.
No Comments »
Back to the line drawing I started in this post.
I wanted to get a snake-like line and I had a try with Papervision3D. It made me able to handle the vertices and then delete the end of the lines.
Draw 1 (demo)
I started from an excellent work from Xero (the.fontvir.us) on the Papervision3D mailing list but instead of using a Lorenz attractor, I used the Tweener Bezier property as I did on the 2D versions.
I set a number of vertices, that is giving me the length of the line. Here is the code:
Actionscript:
-
/*
-
* Lines3D1
-
*
-
* Copyright info: Free to use and change, an notification email will be welcome for a commercial use
-
* Actionscript: built for actionscript 3.0
-
* 05-2008
-
*
-
* @author Romuald Quantin - romu@soundstep.com - www.soundstep.com
-
* @version 1.0
-
* @usage
-
*
-
*/
-
-
package {
-
-
import org.papervision3d.core.geom.renderables.*;
-
import org.papervision3d.materials.special.*;
-
import org.papervision3d.core.geom.*;
-
import org.papervision3d.objects.*;
-
import org.papervision3d.cameras.*;
-
import caurina.transitions.Tweener;
-
import caurina.transitions.properties.CurveModifiers;
-
import flash.events.*;
-
-
public class Lines3D1 extends BaseP3D {
-
-
//------------------------------------
-
// private properties
-
//------------------------------------
-
-
private var sight:DisplayObject3D;
-
private var lineMaterial:LineMaterial;
-
private var arrLines:Array = [];
-
private var _objArray:Array = [];
-
private var _linesNum:int = 5;
-
private var _maxRadius:int = 200;
-
private var _pointNum:uint = 80;
-
private var _verticesNum:uint = 50;
-
-
//------------------------------------
-
// public properties
-
//------------------------------------
-
-
//------------------------------------
-
// constructor
-
//------------------------------------
-
-
public function Lines3D1() {
-
stage.frameRate = 81;
-
CurveModifiers.init();
-
super(600, 600);
-
b.addEventListener(MouseEvent.CLICK, reload);
-
addChild(b);
-
}
-
-
//
-
// PRIVATE, PROTECTED
-
//________________________________________________________________________________________________
-
-
override protected function init2D():void {
-
for (var u:int=0; u<_linesNum; u++) {
-
var obj:Object = {};
-
var newPos:Object = getRandomPos();
-
obj['prevX'] = obj['x'] = newPos['x'];
-
obj['prevY'] = obj['y'] = newPos['y'];
-
obj['prevZ'] = obj['z'] = newPos['z'];
-
obj['color'] = Math.random() * 0xFFFFFF;
-
_objArray.push(obj);
-
randomTween(getRandomData(obj));
-
}
-
}
-
-
override protected function init3D():void {
-
camera.zoom = 5;
-
camera.z = -200;
-
sight = new DisplayObject3D();
-
camera.target = sight;
-
lineMaterial = new LineMaterial(Math.random()* 0xFFFFFF);
-
for (var f:int=0; f<_linesNum; f++) {
-
var line:Lines3D = new Lines3D(lineMaterial, "line");
-
arrLines.push(line);
-
scene.addChild(line, "line");
-
}
-
}
-
-
override protected function processFrame():void {
-
for (var h:int=0; h<_linesNum; h++) {
-
arrLines[h].addLine(new Line3D(arrLines[h], new LineMaterial(_objArray[h]['color'], 1), 3, new Vertex3D(_objArray[h]['prevX'],_objArray[h]['prevY'],_objArray[h]['prevZ']), new Vertex3D(_objArray[h]['x'],_objArray[h]['y'],_objArray[h]['z'])));
-
_objArray[h]['prevX'] = _objArray[h]['x'];
-
_objArray[h]['prevY'] = _objArray[h]['y'];
-
_objArray[h]['prevZ'] = _objArray[h]['z'];
-
if (arrLines[h].lines.length> _verticesNum) arrLines[h].lines.shift();
-
camera.x += (((mouseX -(stage.stageWidth*.5))*3)-camera.x)*.01;
-
camera.y += (((mouseY-(stage.stageHeight*.5))*3)-camera.y)*.01;
-
}
-
}
-
-
private function randomTween(o:Object):void {
-
Tweener. addTween(o, {x:o ['x1'], y:o ['y1'], z:o ['z1'], _bezier:o ['bezier'], time ['time'], transition: "linear", onComplete:
-
function():void {
-
randomTween(getRandomData(o));
-
}
-
});
-
}
-
-
private function getRandomData(o:Object):Object {
-
o['time'] = Math.random() * (_pointNum*.5) + (_pointNum*.25);
-
var newPos:Object = getRandomPos();
-
o['x1'] = newPos['x'];
-
o['y1'] = newPos['y'];
-
o['z1'] = newPos['z'];
-
o['bezier'] = [];
-
for (var i:int=0; i<_pointNum; i++) {
-
var newBezierPos:Object = getRandomPos();
-
o['bezier'].push({
-
x:newBezierPos['x'],
-
y:newBezierPos['y'],
-
z:newBezierPos['z']
-
});
-
}
-
return o;
-
}
-
-
private function getRandomPos():Object {
-
var angleY:Number = Math.random() * 2 * Math.PI;
-
var angleXZ:Number = Math.random() * 2 * Math.PI;
-
var o:Object = {};
-
o['x'] = Math.cos(angleY) * Math.sin(angleXZ) * _maxRadius;
-
o['y'] = Math.sin(angleY) * Math.sin(angleXZ) * _maxRadius;
-
o['z'] = Math.cos(angleXZ) * _maxRadius;
-
return o;
-
}
-
-
private function reload(e:MouseEvent = null):void {
-
Tweener.removeAllTweens();
-
for (var r:int=0; r<_linesNum; r++) {
-
scene.removeChild(arrLines[r]);
-
}
-
_objArray = [];
-
arrLines = [];
-
init3D();
-
init2D();
-
}
-
-
//
-
// PUBLIC
-
//________________________________________________________________________________________________
-
-
}
-
-
}
Draw 2 (demo)
And a bit of fun with the Bitmap layer, the ColorMatrixFilter and the BlurFilter.
Actionscript:
-
/*
-
* Lines3D2
-
*
-
* Copyright info: Free to use and change, an notification email will be welcome for a commercial use
-
* Actionscript: built for actionscript 3.0
-
* 05-2008
-
*
-
* @author Romuald Quantin - romu@soundstep.com - www.soundstep.com
-
* @version 1.0
-
* @usage
-
*
-
*/
-
-
package {
-
-
import flash.geom.Point;
-
import flash.display.Bitmap;
-
import flash.display.BitmapData;
-
import flash.filters.BlurFilter;
-
import flash.filters.ColorMatrixFilter;
-
import org.papervision3d.core.geom.renderables.*;
-
import org.papervision3d.materials.special.*;
-
import org.papervision3d.core.geom.*;
-
import org.papervision3d.objects.*;
-
import org.papervision3d.cameras.*;
-
import caurina.transitions.Tweener;
-
import caurina.transitions.properties.CurveModifiers;
-
import flash.events.*;
-
-
public class Lines3D2 extends BaseP3D {
-
-
//------------------------------------
-
// private properties
-
//------------------------------------
-
-
private var sight:DisplayObject3D;
-
private var lineMaterial:LineMaterial;
-
private var arrLines:Array = [];
-
private var _objArray:Array = [];
-
private var _linesNum:int = 5;
-
private var _maxRadius:int = 200;
-
private var _pointNum:uint = 80;
-
private var _verticesNum:uint = 30;
-
private var _cmf:ColorMatrixFilter;
-
private var _bf:BlurFilter;
-
private var _bmd:BitmapData;
-
private var _bm:Bitmap;
-
-
//------------------------------------
-
// public properties
-
//------------------------------------
-
-
//------------------------------------
-
// constructor
-
//------------------------------------
-
-
public function Lines3D2() {
-
stage.frameRate = 81;
-
CurveModifiers.init();
-
super(600, 600);
-
b.addEventListener(MouseEvent.CLICK, reload);
-
addChild(b);
-
}
-
-
//
-
// PRIVATE, PROTECTED
-
//________________________________________________________________________________________________
-
-
override protected function init2D():void {
-
_cmf = new ColorMatrixFilter([1.05,0,0,0,0,
-
0,1.05,0,0,0,
-
0,0,1.05,0,0,
-
0,0,0,.54,0]);
-
_bf = new BlurFilter(5, 5, 2);
-
createCanvas();
-
for (var u:int=0; u<_linesNum; u++) {
-
var obj:Object = {};
-
var newPos:Object = getRandomPos();
-
obj['prevX'] = obj['x'] = newPos['x'];
-
obj['prevY'] = obj['y'] = newPos['y'];
-
obj['prevZ'] = obj['z'] = newPos['z'];
-
obj['color'] = Math.random() * 0xFFFFFF;
-
_objArray.push(obj);
-
randomTween(getRandomData(obj));
-
}
-
}
-
-
override protected function init3D():void {
-
camera.zoom = 5;
-
camera.z = -200;
-
sight = new DisplayObject3D();
-
camera.target = sight;
-
lineMaterial = new LineMaterial(Math.random()* 0xFFFFFF);
-
for (var f:int=0; f<_linesNum; f++) {
-
var line:Lines3D = new Lines3D(lineMaterial, "line");
-
arrLines.push(line);
-
scene.addChild(line, "line");
-
}
-
}
-
-
private function createCanvas():void {
-
_bmd = new BitmapData(600, 600, true, 0x000000);
-
_bm = new Bitmap(_bmd);
-
addChild(_bm);
-
}
-
-
override protected function processFrame():void {
-
for (var h:int=0; h<_linesNum; h++) {
-
arrLines[h].addLine(new Line3D(arrLines[h], new LineMaterial(_objArray[h]['color'], 1), 3, new Vertex3D(_objArray[h]['prevX'],_objArray[h]['prevY'],_objArray[h]['prevZ']), new Vertex3D(_objArray[h]['x'],_objArray[h]['y'],_objArray[h]['z'])));
-
_objArray[h]['prevX'] = _objArray[h]['x'];
-
_objArray[h]['prevY'] = _objArray[h]['y'];
-
_objArray[h]['prevZ'] = _objArray[h]['z'];
-
if (arrLines[h].lines.length> _verticesNum) arrLines[h].lines.shift();
-
camera.x += (((mouseX -(stage.stageWidth*.5))*3)-camera.x)*.01;
-
camera.y += (((mouseY-(stage.stageHeight*.5))*3)-camera.y)*.01;
-
}
-
_bmd.applyFilter(_bmd, _bmd.rect, new Point(0, 0), _cmf);
-
_bmd.applyFilter(_bmd, _bmd.rect, new Point(0, 0), _bf);
-
_bmd.draw(this);
-
}
-
-
private function randomTween(o:Object):void {
-
Tweener. addTween(o, {x:o ['x1'], y:o ['y1'], z:o ['z1'], _bezier:o ['bezier'], time ['time'], transition: "linear", onComplete:
-
function():void {
-
randomTween(getRandomData(o));
-
}
-
});
-
}
-
-
private function getRandomData(o:Object):Object {
-
o['time'] = Math.random() * (_pointNum*.5) + (_pointNum*.25);
-
var newPos:Object = getRandomPos();
-
o['x1'] = newPos['x'];
-
o['y1'] = newPos['y'];
-
o['z1'] = newPos['z'];
-
o['bezier'] = [];
-
for (var i:int=0; i<_pointNum; i++) {
-
var newBezierPos:Object = getRandomPos();
-
o['bezier'].push({
-
x:newBezierPos['x'],
-
y:newBezierPos['y'],
-
z:newBezierPos['z']
-
});
-
}
-
return o;
-
}
-
-
private function getRandomPos():Object {
-
var angleY:Number = Math.random() * 2 * Math.PI;
-
var angleXZ:Number = Math.random() * 2 * Math.PI;
-
var o:Object = {};
-
o['x'] = Math.cos(angleY) * Math.sin(angleXZ) * _maxRadius;
-
o['y'] = Math.sin(angleY) * Math.sin(angleXZ) * _maxRadius;
-
o['z'] = Math.cos(angleXZ) * _maxRadius;
-
return o;
-
}
-
-
private function reload(e:MouseEvent = null):void {
-
Tweener.removeAllTweens();
-
for (var r:int=0; r<_linesNum; r++) {
-
scene.removeChild(arrLines[r]);
-
}
-
removeChild(_bm);
-
_objArray = [];
-
arrLines = [];
-
init3D();
-
init2D();
-
}
-
-
//
-
// PUBLIC
-
//________________________________________________________________________________________________
-
-
}
-
-
}
Get a random point in a sphere
maxRadius = 200
angleY = Math.random() * 2 * Math.PI
angleXZ = Math.random() * 2 * Math.PI
x = Math.cos(angleY) * Math.sin(angleXZ) * maxRadius
y = Math.sin(angleY) * Math.sin(angleXZ) * maxRadius
z = Math.cos(angleXZ) * maxRadius
Get the source here.
3 Comments »
|