Flex 4 (Gumbo) Image Ball – Ouchies!

Intro

Flash & Math had created a really cool image ball in Flash, but I needed it in Flex 4 (Gumbo) for a project. So I ported it over. The port was non-trivial so I’ve included it here with its source code. The big thing about having it in Flex (MXML & ActionScript) is that I can now drag the Flex components in and use them to harness data. Click the image below to see the demo.

Flex 4 (Gumbo) Image Ball

Flex 4 (Gumbo) Image Ball

Demo

Code (MXML Flex File Only Images too Large and some Copyrighted)

Discussion

Here are the highlights which got me stuck on the port (got an “Ouchie” from each one) .

Note: the code from Flash & Math is thoroughly documented so make sure you download and go through it to follow along).

Ouchie 1: Not so much of an ouchie if you know this one already (and I did), but you can’t use addChild to add a sprite directly to the stage of a Flex MXML project. So create a canvas and add your sprite to that canvas using rawChildren.

cs4Canvas.rawChildren.addChild(board);

You can even use mutliple canvas components (or other containers) – and I did.

Ouchie 2: Flash & Math pulls their thumb nail image assets from the Flash library using linkage. You can’t do that in Flex, so use the embed tag for the thumb nails (all 46 of them – ouchie, ouchie, ouch).

[Embed(source=”thumbs/pic1.jpg”)]
private var Small1:Class;

But now the setUpPics() method won’t work, so use the BitmapAsset class in your thumbnail array.

thumbsArray[0]=[new Small1() as BitmapAsset];

And place your embed class in for their movie class.

Ouchie 3: Rewrite the Vector array declarations using a push method to load the vector arrays-their method of loading these arrays doesn’t work in Flex (and figuring that one out was a big ouchie).

thetaStep.push(0,60,36,30,36,60,0);
jLen.push(1,6,10,12,10,6,1);
phiTilt.push(-90,-60,-30,0,30,60,90)

But fortunately, it’s really easy to implement.

Ouchie 4: Just as with a formal class structure, you must make sure that your variables have the private statement in front of them, and that all declarations are contained in a function (within your script tags). In addition, you must create an init() method and call it from your MXML tags (this acts like a constructor function).

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml&#8221; creationComplete=”init()” layout=”absolute”>

Ouchie 5: I added image forward and backwards buttons to the program so once the user double clicks a thumbnail image, he/she can surf the images without going back and forth between image and image thumbnails (this is a big improvement to the code).

This required that I write a small string routine that extracted the numbers from the image name so I could start at that image and go sequentially

var myStringIs:String = picsArray[i][j];
myStringIs=myStringIs.split(“.”).join();
myStringIs=myStringIs.split(“c”).join();
splitArray=myStringIs.split(“,”);
stringNumIs=Number(splitArray[1].toString());

The move forward and backward code for the buttons is given by

private function moveForward(e:MouseEvent):void {
stringNumIs++;
stringNumIs=stringNumIs%47;
if(stringNumIs==0)stringNumIs=1;
loader.load(new URLRequest(“images/pic”+stringNumIs+”.jpg”));
}

private function moveBackwards(e:MouseEvent):void {
stringNumIs–;
if(stringNumIs==0)stringNumIs=46;
loader.load(new URLRequest(“images/pic”+stringNumIs+”.jpg”));
}

This small routine could definitely be improved upon…but it does the job..

Big Problem
Of course the big problem here is that your images have to be sequentially numbered, that’s a big ouchie, and is unacceptable for future projects: I need descriptive names. I can’t remember which image is which, nor can you…

But my timeframe was so short on this project (of which this was a small piece) that I just had to live with it. But a little recoding is needed here to fix this problem…I would treat it as a particle system.

Conclusion
So the port was a little bit “hurtie…ouch, ouch, ouch.”. It took me about 4 hours to complete, but once completed, it worked great and integrated smoothly into the rest of my application.

Hope this saves you some time…or a few ouchies…

Click the more button below to see all the code and a short tutorial on substrings from flepstudio.

Main Code

<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml&#8221; creationComplete=”init()” layout=”absolute”>

<mx:Script>

<![CDATA[

//By Michael Lively … an open source fanatic. Please use without restriction, as Jesus said, freely give and freely receive.

//O magnify the LORD with me, and let us exalt his name together. I sought the LORD, and he heard me, and delivered me from all my fears.

import mx.core.BitmapAsset;

import flash.net.URLRequest;
import mx.controls.Alert;
import flash.media.SoundChannel;

import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.net.*;

private var posX:Number=300;
private var posY:Number=100;

private var rad:Number=250;
private var thumbWidth:Number=70;
private var thumbHeight:Number=53;
private var thumbsArray:Array=[];
private var picsArray:Array=[];
private var holdersArray:Array=[];
private var midsArray:Array=[];
private var splitArray:Array=[];

private var jLen:Vector.<Number>=new Vector.<Number>();
private var thetaStep:Vector.<Number>=new Vector.<Number>();
private var phiStep:Number=30;
private var phiTilt:Vector.<Number>=new Vector.<Number>();
private var autoOn:Boolean=true;
private var manualOn:Boolean=false;
private var prevX:Number;
private var prevY:Number;

private var stringNumIs:Number;

private var board:Sprite=new Sprite();
private var spSphere:Sprite=new Sprite();

[Embed(source=”thumbs/pic1.jpg”)]
private var Small1:Class;
[Embed(source=”thumbs/pic2.jpg”)]
private var Small2:Class;
[Embed(source=”thumbs/pic3.jpg”)]
private var Small3:Class;
[Embed(source=”thumbs/pic4.jpg”)]
private var Small4:Class;

[Embed(source=”thumbs/pic5.jpg”)]
private var Small5:Class;
[Embed(source=”thumbs/pic6.jpg”)]
private var Small6:Class;
[Embed(source=”thumbs/pic7.jpg”)]
private var Small7:Class;
[Embed(source=”thumbs/pic8.jpg”)]
private var Small8:Class;

[Embed(source=”thumbs/pic9.jpg”)]
private var Small9:Class;
[Embed(source=”thumbs/pic10.jpg”)]
private var Small10:Class;
[Embed(source=”thumbs/pic11.jpg”)]
private var Small11:Class;
[Embed(source=”thumbs/pic12.jpg”)]
private var Small12:Class;

[Embed(source=”thumbs/pic13.jpg”)]
private var Small13:Class;
[Embed(source=”thumbs/pic14.jpg”)]
private var Small14:Class;
[Embed(source=”thumbs/pic15.jpg”)]
private var Small15:Class;
[Embed(source=”thumbs/pic16.jpg”)]
private var Small16:Class;

[Embed(source=”thumbs/pic17.jpg”)]
private var Small17:Class;
[Embed(source=”thumbs/pic18.jpg”)]
private var Small18:Class;
[Embed(source=”thumbs/pic19.jpg”)]
private var Small19:Class;
[Embed(source=”thumbs/pic20.jpg”)]
private var Small20:Class;

[Embed(source=”thumbs/pic21.jpg”)]
private var Small21:Class;
[Embed(source=”thumbs/pic22.jpg”)]
private var Small22:Class;

[Embed(source=”thumbs/pic23.jpg”)]
private var Small23:Class;
[Embed(source=”thumbs/pic24.jpg”)]
private var Small24:Class;
[Embed(source=”thumbs/pic25.jpg”)]
private var Small25:Class;
[Embed(source=”thumbs/pic26.jpg”)]
private var Small26:Class;

[Embed(source=”thumbs/pic27.jpg”)]
private var Small27:Class;
[Embed(source=”thumbs/pic28.jpg”)]
private var Small28:Class;
[Embed(source=”thumbs/pic29.jpg”)]
private var Small29:Class;
[Embed(source=”thumbs/pic30.jpg”)]
private var Small30:Class;

[Embed(source=”thumbs/pic31.jpg”)]
private var Small31:Class;
[Embed(source=”thumbs/pic32.jpg”)]
private var Small32:Class;

[Embed(source=”thumbs/pic33.jpg”)]
private var Small33:Class;
[Embed(source=”thumbs/pic34.jpg”)]
private var Small34:Class;
[Embed(source=”thumbs/pic35.jpg”)]
private var Small35:Class;
[Embed(source=”thumbs/pic36.jpg”)]
private var Small36:Class;

[Embed(source=”thumbs/pic37.jpg”)]
private var Small37:Class;
[Embed(source=”thumbs/pic38.jpg”)]
private var Small38:Class;
[Embed(source=”thumbs/pic39.jpg”)]
private var Small39:Class;
[Embed(source=”thumbs/pic40.jpg”)]
private var Small40:Class;

[Embed(source=”thumbs/pic41.jpg”)]
private var Small41:Class;
[Embed(source=”thumbs/pic42.jpg”)]
private var Small42:Class;

[Embed(source=”thumbs/pic43.jpg”)]
private var Small43:Class;
[Embed(source=”thumbs/pic44.jpg”)]
private var Small44:Class;
[Embed(source=”thumbs/pic45.jpg”)]
private var Small45:Class;
[Embed(source=”thumbs/pic46.jpg”)]
private var Small46:Class;

private var photoHolder:Sprite=new Sprite();

private var loader:Loader=new Loader();

private function init():void{

cs4Canvas.rawChildren.addChild(board);

cs4Canvas4.rawChildren.addChild(photoHolder);

photoHolder.x=80;

photoHolder.y=-100;

output.visible=false;

photoHolder.addChild(loader);

photoHolder.visible=false;

btn1.visible=false;
myImageNumb.visible=false;
btn2.visible=false;

// this.transform.perspectiveProjection.fieldOfView=70;

thetaStep.push(0,60,36,30,36,60,0);
jLen.push(1,6,10,12,10,6,1);
phiTilt.push(-90,-60,-30,0,30,60,90)

board.x=posX;
board.y=posY;

drawBoard();

board.addChild(spSphere);

spSphere.x=0;
spSphere.y=0;
spSphere.z=rad;

setUpPics();

buildSphere();

spSphere.rotationY=0;
spSphere.rotationX=0;
spSphere.rotationZ=0;
spSphere.filters=[new GlowFilter(0x666666,1.0,6.0,6.0,2)];

rotateSphere(0,0,0);

setUpListeners();

}

private function drawBoard():void {

board.graphics.clear();

board.graphics.beginFill(0x000000, .1);

board.graphics.drawRect(-240,-240,480,480);

board.graphics.endFill();

}

private function setUpListeners():void {

output.text+=””;

var i:int;

var j:int;

this.addEventListener(Event.ENTER_FRAME,autoRotate);

board.addEventListener(MouseEvent.ROLL_OUT,boardOut);

board.addEventListener(MouseEvent.MOUSE_MOVE,boardMove);

board.addEventListener(MouseEvent.MOUSE_DOWN,boardDown);

board.addEventListener(MouseEvent.MOUSE_UP,boardUp);

loader.contentLoaderInfo.addEventListener(Event.COMPLETE,doneLoad);

loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,loadingError);

photoHolder.addEventListener(MouseEvent.CLICK,holderClicked);

for(i=0;i<7;i++){

for(j=0;j<jLen[i];j++){

output.text+=j;

holdersArray[i][j].doubleClickEnabled=true;

holdersArray[i][j].addEventListener(MouseEvent.DOUBLE_CLICK,picClicked);

}

}

}

private function holderClicked(e:MouseEvent):void {

board.visible=true;

photoHolder.visible=false;

btn1.visible=false;
myImageNumb.visible=false;
btn2.visible=false;

//infoBox.text=”Press and move the mouse over the sphere to rotate. DOUBLE CLICK a thumbnail to load an image.”;

manualOn=false;

autoOn=true;

}

//stringNumIs

private function moveForward(e:MouseEvent):void {

stringNumIs++;
stringNumIs=stringNumIs%47;
if(stringNumIs==0)stringNumIs=1;

loader.load(new URLRequest(“images/pic”+stringNumIs+”.jpg”));

myImageNumb.text =”pic”+stringNumIs+” of 46″;

}

private function moveBackwards(e:MouseEvent):void {

stringNumIs–;
if(stringNumIs==0)stringNumIs=46;

loader.load(new URLRequest(“images/pic”+stringNumIs+”.jpg”));

myImageNumb.text =”pic”+stringNumIs+” of 46″;

}

private function picClicked(e:MouseEvent):void {

var targName:String=””;

var i:int;

var j:int;

targName=e.currentTarget.name;

i=int(targName.charAt(3));

j=int(targName.substring(5,targName.length));

board.visible=false;

btn1.visible=true;
myImageNumb.visible=true;
btn2.visible=true;

loader.load(new URLRequest(“images/”+picsArray[i][j]));

var myStringIs:String = picsArray[i][j];
myStringIs=myStringIs.split(“.”).join();
myStringIs=myStringIs.split(“c”).join();
splitArray=myStringIs.split(“,”);
stringNumIs=Number(splitArray[1].toString());

output.text =String(stringNumIs);

myImageNumb.text =”pic”+stringNumIs+” of 46″;

//output.text =”click here”

//infoBox.text=””;

//loadBox.text=”Loading…”;

//loadBox.visible=true;

}

private function loadingError(e:IOErrorEvent):void {

//loadBox.text=”There has been an error loading the image. The server may be busy. Refresh the page and try again.”;

}

private function doneLoad(event:Event):void {

//infoBox.text=”Click the image to close it.”;

photoHolder.visible=true;

//loadBox.text=””;

//loadBox.visible=false;

}

//Listeners responsible for mouse rotations and auto-rotation.

private function autoRotate(e:Event):void {

if(autoOn && !manualOn){

spSphere.transform.matrix3D.prependRotation(-0.5,Vector3D.Y_AXIS);

zSortPics();

}

}

private function boardOut(e:MouseEvent):void {

autoOn=true;

manualOn=false;

}

private function boardDown(e:MouseEvent):void {

prevX=board.mouseX;

prevY=board.mouseY;

autoOn=false;

manualOn=true;

}

private function boardUp(e:MouseEvent):void {

manualOn=false;

}

private function boardMove(e:MouseEvent):void {

var locX:Number=prevX;

var locY:Number=prevY;

if(!autoOn && manualOn){

prevX=board.mouseX;

prevY=board.mouseY;

rotateSphere(prevY-locY,-(prevX-locX),0);

e.updateAfterEvent();

}
}

private function setUpPics():void {

output.text+=”pics here”;

thumbsArray[0]=[new Small1() as BitmapAsset];

picsArray[0]=[“pic1.jpg”];

thumbsArray[1]=[new Small2() as BitmapAsset,new Small3() as BitmapAsset,new Small4() as BitmapAsset,new Small5() as BitmapAsset,new Small6() as BitmapAsset,new Small7() as BitmapAsset];

picsArray[1]=[“pic2.jpg”,”pic3.jpg”,”pic4.jpg”,”pic5.jpg”,”pic6.jpg”,”pic7.jpg”];

thumbsArray[2]=[new Small8() as BitmapAsset,new Small9() as BitmapAsset,new Small10() as BitmapAsset,new Small11() as BitmapAsset,new Small12() as BitmapAsset,new Small13() as BitmapAsset,new Small14() as BitmapAsset,new Small15() as BitmapAsset,new Small16() as BitmapAsset,new Small17() as BitmapAsset];

picsArray[2]=[“pic8.jpg”,”pic9.jpg”,”pic10.jpg”,”pic11.jpg”,”pic12.jpg”,”pic13.jpg”,”pic14.jpg”,”pic15.jpg”,”pic16.jpg”,”pic17.jpg”];

thumbsArray[3]=[new Small18() as BitmapAsset,new Small19() as BitmapAsset,new Small20() as BitmapAsset,new Small21() as BitmapAsset,new Small22() as BitmapAsset,new Small23() as BitmapAsset,new Small24() as BitmapAsset,new Small25() as BitmapAsset,new Small26() as BitmapAsset,new Small27() as BitmapAsset,new Small28() as BitmapAsset,new Small29() as BitmapAsset];

picsArray[3]=[“pic18.jpg”,”pic19.jpg”,”pic20.jpg”,”pic21.jpg”,”pic22.jpg”,”pic23.jpg”,”pic24.jpg”,”pic25.jpg”,”pic26.jpg”,”pic27.jpg”,”pic28.jpg”,”pic29.jpg”];

thumbsArray[4]=[new Small30() as BitmapAsset,new Small31() as BitmapAsset,new Small32() as BitmapAsset,new Small33() as BitmapAsset,new Small34() as BitmapAsset,new Small35() as BitmapAsset,new Small36() as BitmapAsset,new Small37() as BitmapAsset,new Small38() as BitmapAsset,new Small39() as BitmapAsset];

picsArray[4]=[“pic30.jpg”,”pic31.jpg”,”pic32.jpg”,”pic33.jpg”,”pic34.jpg”,”pic35.jpg”,”pic36.jpg”,”pic37.jpg”,”pic38.jpg”,”pic39.jpg”];

thumbsArray[5]=[new Small40() as BitmapAsset,new Small41() as BitmapAsset,new Small42() as BitmapAsset,new Small43() as BitmapAsset,new Small44() as BitmapAsset,new Small45() as BitmapAsset];

picsArray[5]=[“pic40.jpg”,”pic41.jpg”,”pic42.jpg”,”pic43.jpg”,”pic44.jpg”,”pic45.jpg”];

thumbsArray[6]=[new Small46() as BitmapAsset];

picsArray[6]=[“pic46.jpg”];

output.text+=”made pics…..”;

}

private function buildSphere():void {

output.text+=”buildSphere “;

var tStep:Number;

var pStep:Number=phiStep*Math.PI/180;

output.text+=”pStep =”+pStep;

tStep=thetaStep[2]*Math.PI/180;
output.text+=”tStep2 =”+tStep;

for(var i:int=0;i<7;i++){

output.text+=i+” yeah”;

holdersArray[i]=[];

midsArray[i]=[];

tStep=thetaStep[i]*Math.PI/180;

output.text+=” tStep “+ tStep;

for(var j:int=0;j<jLen[i];j++){

output.text+=j;

midsArray[i][j]=new Vector3D(rad*Math.sin(i*pStep)*Math.sin(j*tStep),-rad*Math.cos(i*pStep),-rad*Math.sin(i*pStep)*Math.cos(j*tStep));

holdersArray[i][j]=new Sprite();

holdersArray[i][j].name=”pic”+String(i)+”_”+String(j);

//output.text+=holdersArray[i][j].name +” =holds name”;

holdersArray[i][j].addChild(thumbsArray[i][j]);

thumbsArray[i][j].x=-thumbWidth/2;

thumbsArray[i][j].y=-thumbHeight/2;

spSphere.addChild(holdersArray[i][j]);

holdersArray[i][j].x=midsArray[i][j].x;

holdersArray[i][j].y=midsArray[i][j].y;

holdersArray[i][j].z=midsArray[i][j].z;

holdersArray[i][j].rotationX=phiTilt[i];

holdersArray[i][j].rotationY=-j*thetaStep[i];

}

}

zSortPics();

}

private function zSortPics():void {

var distArray:Array=[];

var dist:Number;

var i:int;

var j:int;

var k:int;

var curMatrix:Matrix3D;

var curMid:Vector3D;

curMatrix=spSphere.transform.matrix3D.clone();

while(spSphere.numChildren>0){

spSphere.removeChildAt(0);

}

for(i=0;i<7;i++){

for(j=0;j<jLen[i];j++){

curMid=curMatrix.deltaTransformVector(midsArray[i][j]);

dist=curMid.z;

distArray.push([dist,i,j]);

}

}

distArray.sort(byDist);

for(k=0;k<distArray.length;k++){

spSphere.addChild(holdersArray[distArray[k][1]][distArray[k][2]]);

holdersArray[distArray[k][1]][distArray[k][2]].alpha=Math.max(k/(distArray.length-1),0.5);

}

}

private function byDist(v:Array,w:Array):Number {

if (v[0]>w[0]){

return -1;

} else if (v[0]<w[0]){

return 1;

} else {

return 0;
}

}

private function rotateSphere(rotx:Number,roty:Number,rotz:Number):void {

spSphere.z=0;

spSphere.transform.matrix3D.appendRotation(rotx,Vector3D.X_AXIS);

spSphere.transform.matrix3D.appendRotation(roty,Vector3D.Y_AXIS);

spSphere.transform.matrix3D.appendRotation(rotz,Vector3D.Z_AXIS);

spSphere.z=rad;

zSortPics();

}

]]>
</mx:Script>
<mx:Canvas x=”51″ y=”109″ width=”81″ height=”79″ id=”cs4Canvas”>
</mx:Canvas>
<mx:TextArea x=”561″ y=”10″ height=”92″ width=”280″ id=”output”/>
<mx:Canvas x=”51″ y=”208″ width=”81″ height=”87″ id=”cs4Canvas4″>
</mx:Canvas>
<mx:Button x=”362″ y=”407″ label=”&gt;&gt;” click=”moveForward(event)” id=”btn2″/>
<mx:Button x=”226″ y=”407″ label=”&lt;&lt;” click=”moveBackwards(event)” id=”btn1″/>
<mx:Text x=”278″ y=”409″ text=”Text” width=”76″ fontWeight=”bold” id=”myImageNumb” textAlign=”center”/>
<mx:Label x=”51″ y=”0″ text=”Double Click a Thumbnail to Englarge, Click to go Back”/>

</mx:Application>
I can never remember this substring stuff. So I’ve included a tutorial from flepstudio

The substr() method.

wants two pararmeters:
– startIndex: an integer that indicates the position of the first character to be used
– length: the number of characters extracted after the startIndex

The substring() method.

wants two parameters:
– startIndex: an integer that indicates the position of the first character to be used
– endIndex: an integer that indicates the position of the last character to be used

To better understand these 2 methods, I created a Document Class as example:

Code:
package
{
	import flash.display.MovieClip;

	public class Substringa extends MovieClip
	{
		public function Substringa()
		{
			init();
		}

		private function init():void
		{
			var testo:String='FlepStudio';

			trace(testo.substr(0,4));
			trace(testo.substring(0,4));
			trace('-------------------------');

			trace(testo.substr(1,5));
			trace(testo.substring(1,5));
			trace('-------------------------');

			trace(testo.substr(4,6));
			trace(testo.substring(4,6));
			trace('-------------------------');
		}
	}
}

The output returned by Flash is the following:

Quote:
Flep
Flep
————————-
lepSt
lepS
————————-
Studio
St
————————-

Let’s analyse the code.

I have a String variable with some text
var testo1:String=’FlepStudio’;

First case:

I use substr() passing the values: zero as startIndex (so it will start from the first character) and 4 as length (it will move of 4 positions starting from the first character)
trace(testo1.substr(0,4));
I use substring() passing the values: zero as startIndex (so it will start from the first character) and 4 as endIndex (it will move till the fourth position and stop)
trace(testo1.substring(0,4));

Second case:

I use substr() passing the values: 1 as startIndex (so it will start from the second character) and 5 as length (it will move of 5 positions starting from the second character)
trace(testo2.substr(1,5));
I use substring() passing the values: 1 as startIndex (so it will start from the second character) and 5 as endIndex (it will move till the fifth position and stop)
trace(testo2.substring(1,5));

Third case:

I use substr() passing the values: 4 as startIndex (so it will start from the fifth character) and 6 as length (it will move of 6 positions starting from the fifth character)
trace(testo.substr(4,6));
I use substring() passing the values: 4 as startIndex (so it will start from the fifth character) and 6 as endIndex (it will move till the sixth position and stop)
trace(testo.substring(4,6));

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: