By Zevan | January 4, 2010
Actionscript:
-
[SWF (width = 500, height = 500)]
-
-
var canvas:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0xFFFFFF);
-
addChild(new Bitmap(canvas));
-
-
var indexCanvas:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0xFFFFFF);
-
-
var btnNum:int = 5000;
-
var info:Array = [];
-
-
var brush:BitmapData = new BitmapData(10,10,false, 0xCCCCCC);
-
var border:Shape = new Shape();
-
border.graphics.lineStyle(2, 0x000000);
-
border.graphics.drawRect(0,0,10,10);
-
brush.draw(border);
-
-
var txt:TextField = TextField(addChild(new TextField()));
-
with (txt) height = 20, width = 50, background = 0xFFFFFF, selectable = false
-
var tf:TextFormat = new TextFormat();
-
tf.align = TextFormatAlign.RIGHT;
-
txt.border= true;
-
txt.defaultTextFormat = tf;
-
-
var redRect:Shape = Shape(addChild(new Shape()));
-
with (redRect.graphics) beginFill(0xFF0000), drawRect(0,0,10,10);
-
-
var pnt:Point = new Point();
-
var r:Rectangle = new Rectangle(0,0,10,10);
-
for (var i:int = 0; i <btnNum; i++){
-
pnt.x = r.x = int(Math.random() * stage.stageWidth);
-
pnt.y = r.y = int(Math.random() * stage.stageHeight);
-
indexCanvas.fillRect(r, i);
-
canvas.copyPixels(brush, brush.rect, pnt)
-
info[i] = [r.x, r.y, i];
-
}
-
-
addEventListener(Event.ENTER_FRAME, onCheckBtns);
-
function onCheckBtns(evt:Event):void{
-
var currentIndex:int = indexCanvas.getPixel(mouseX, mouseY);
-
if (currentIndex != 0xFFFFFF){
-
var currentBox:Array = info[currentIndex]
-
redRect.visible = true;
-
redRect.x = currentBox[0];
-
txt.y = redRect.y = currentBox[1];
-
if (mouseX <txt.width){
-
tf.align = TextFormatAlign.LEFT;
-
txt.defaultTextFormat = tf;
-
txt.x = redRect.x + 10;
-
}else{
-
tf.align = TextFormatAlign.RIGHT;
-
txt.defaultTextFormat = tf;
-
txt.x = redRect.x - txt.width;
-
}
-
txt.text = currentBox[2];
-
txt.visible = true;
-
}else{
-
redRect.visible = false;
-
txt.visible = false;
-
}
-
}
This is a simplified example of the technique discussed in yesterdays post. The idea is to use a BitmapData image to store index values for a large number of elements that need to be able to act as if the have MouseEvents. For a more detailed description of this technique see yesterdays post.
Have a look at the swf on wonderfl
By Zevan | January 3, 2010
Actionscript:
-
[SWF(width = 500, height = 500, frameRate = 30)]
-
-
var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xFFFFFF);
-
-
var indexCanvas:BitmapData = new BitmapData(stage.stage.stageWidth, stage.stageHeight, false,
-
0xFFFFFF);
-
addChild(new Bitmap(canvas));
-
-
var s:Shape = new Shape();
-
-
var lineData:Array = [];
-
var dataIndex:int = 0;
-
-
trace(0xFFFFFF - 1)
-
var totalLines:int = 20000;
-
var iterations:int = 9;
-
var linesPerIter:int = totalLines / iterations;
-
-
var xp:int = stage.stageWidth / 2;
-
var yp:int = stage.stageHeight / 2;
-
-
var stepAmt:Number = 60;
-
var halfStepAmt:Number = stepAmt / 2;
-
-
addEventListener(Event.ENTER_FRAME, onDraw);
-
function onDraw(evt:Event):void {
-
if (lineData.length <totalLines){
-
generateData(linesPerIter);
-
}else{
-
stage.quality = "high";
-
addChild(s);
-
s.x = 0;
-
s.y = 0;
-
-
removeEventListener(Event.ENTER_FRAME, onDraw);
-
addEventListener(Event.ENTER_FRAME, onRun);
-
}
-
}
-
-
function onRun(evt:Event):void {
-
var currentIndex:int = indexCanvas.getPixel(mouseX, mouseY);
-
var currentLine:Array = lineData[currentIndex];
-
-
s.graphics.clear();
-
if (currentIndex != 0xFFFFFF){
-
s.graphics.lineStyle(3, 0xFF0000);
-
s.graphics.moveTo(currentLine[0], currentLine[1]);
-
s.graphics.lineTo(currentLine[2], currentLine[3]);
-
}
-
}
-
-
function generateData(num:int):void{
-
var rxA:int, rxB:int, ryA:int, ryB:int;
-
var g:Graphics = s.graphics;
-
for (var i:int = 0; i<num; i++){
-
rxA = xp;
-
ryA = yp;
-
-
xp += Math.round(Math.random() * stepAmt) - halfStepAmt;
-
yp += Math.round(Math.random() * stepAmt) - halfStepAmt;
-
-
if (xp> stage.stageWidth){
-
xp = stage.stageWidth - halfStepAmt;
-
}else
-
if (xp <0){
-
xp = halfStepAmt;
-
}
-
if (yp> stage.stageHeight){
-
yp = stage.stageHeight - halfStepAmt;
-
}else
-
if (yp <0){
-
yp = halfStepAmt;
-
}
-
-
rxB = xp;
-
ryB = yp;
-
-
lineData[dataIndex] = [rxA, ryA, rxB, ryB];
-
s.x = rxA;
-
s.y = ryA;
-
var endX:Number = rxB - rxA;
-
var endY:Number = ryB - ryA;
-
var m:Matrix = s.transform.matrix;
-
g.clear();
-
g.lineStyle(1, 0x000000, 0.3);
-
-
g.lineTo(endX, endY);
-
stage.quality = "high";
-
canvas.draw(s, m);
-
-
g.clear();
-
g.lineStyle(3, dataIndex);
-
-
g.lineTo(endX, endY);
-
stage.quality = "low";
-
indexCanvas.draw(s, m);
-
-
dataIndex++
-
}
-
}
I'm working on a data visualization that contains a long path made up of approximately one million points. There is some information associated with every two sets of coordinates that needs to be displayed when the user rolls their mouse over any part of the line.
I took a little time to think about the best way to do this and came up with a few techniques. The first one I tried seems to work nicely - this snippet is the proof of concept for that first technique. I tested this snippet with 1,000,000 xy coordinates and it works nicely. It takes a little while to draw though, so for the purposes of this demo I've just included 20,000 coordinates.
Have a look at the swf over at wonderfl.net
The way this works is by drawing lines to two different BitmapData instances. I draw anti-aliased slightly transparent lines to a BitmapData instance called "canvas" (this is added to the display list) - I then draw aliased lines to a BitmapData called "indexCanvas" (this is never added to the display list) - each aliased line uses an incremental value for its color - this incremental value is also the index for a two dimensional array containing the coordinate information for the aliased line. I use getPixel() on the "indexCanvas" and use the return value as the index for the 2D array. The data from the 2D array is used to draw a red line with the graphics class. This technique enables you to have many many rollovers and all you ever have to do is call getPixel() and use the returned color value to look up info about what you're mouse is touching.
There are a few cool ways this could be repurposed and this is really only one solution to the problem of having many many things that you need to be able to rollover... there are others that don't use BitmapData at all... I may write those up in the next couple of days.
Also posted in BitmapData, Data Structures, UI, arrays, display list, graphics algorithms, matrix, pixel manipulation, return values | Tagged actionscript, as3, flash |
By Zevan | January 1, 2010
Actionscript:
-
loop(20);
-
-
function loop(i:int):void {
-
if (i <0) return;
-
trace(i);
-
loop(i - 1);
-
}
-
-
/* outputs:
-
20
-
19
-
18
-
17
-
16
-
15
-
14
-
13
-
12
-
11
-
10
-
9
-
8
-
7
-
6
-
5
-
4
-
3
-
2
-
1
-
0
-
*/
This snippet uses a recursive function to count down from some number. Recursion is pretty useless in actionscript, it will eventually cause an error... If you were to try to countdown from a higher number it would choke pretty fast...
Been writing haskell lately so I have recursion on the brain.
Also posted in functions | Tagged actionscript, as3, flash |
By Zevan | December 30, 2009
Actionscript:
-
var boxA:Shape = Shape(addChild(new Shape()));
-
with (boxA.graphics) beginFill(0), drawRect(-10,-10,20,20);
-
-
var boxB:Shape = Shape(addChild(new Shape()));
-
with (boxB.graphics) beginFill(0), drawRect(-10,-10,20,20);
-
-
boxA.x = 100;
-
boxA.y = 100;
-
-
boxB.x = 200;
-
boxB.y = 100;
-
-
var rot:Number = 32750;
-
-
addEventListener(Event.ENTER_FRAME, onLoop);
-
function onLoop(evt:Event):void {
-
rot += 1
-
// will stop rotating
-
boxA.rotation = rot
-
// will keep rotating
-
boxB.rotation = rot % 360;
-
}
I recently became aware of a strange aspect of the rotation property on DisplayObjects. For some reason, once it's value goes a little beyond ~32750 the DisplayObject will simply stop rotating. If you read the rotation property it is still changing, but there is no visual update - a quick check on the DisplayObject.transform.matrix property will show that the value has stopped.
The easy fix is to use mod before applying the value to the rotation property. Surprised I've never come across this one before. Maybe someone can shed some light on this.
// for people searching google for solutions to this problem I'll add the following key words:
MovieClip stops rotating, DisplayObject stops rotating, rotation property broken, not rotating
By Zevan | December 4, 2009
Actionscript:
-
...
-
// loop through until we find the root note
-
// grab the third and the fifth and exit the loop
-
for (var i:int = 0; i<leng; i++){
-
if (cMajor[i] == note){
-
third = cMajor[(i + 2) % leng];
-
fifth = cMajor[(i + 4) % leng];
-
break;
-
}
-
}
-
-
// we may need a double sharp on the middle note
-
var sharpFlatDouble:String = sharpFlat;
-
-
// check if this is a sharp, check if it is A or D
-
// if it is add the symbol for double sharp
-
if (sharpFlat == "#"){
-
if (note == "D" || note == "A"){
-
sharpFlatDouble = "x";
-
}
-
}
-
...
If your working on some code... just randomly copy a piece of it and paste it in the comments... This code is from a program that generates any major scale (it's still not finished). Feel free to post code chunks in any language...
[EDIT] the code doesn't need to work on its own... you can just randomly copy from something your working on...
Posted in misc | Tagged actionscript, as3, flash |
By Zevan | November 29, 2009
Actionscript:
-
// print some floats as fractions
-
printFraction(0.5);
-
printFraction(0.75);
-
printFraction(0.48);
-
-
// try something a little more complex
-
var float:Number = 0.98765432;
-
trace("\na more complex example:");
-
printFraction(float);
-
var frac:Array = asFraction(float);
-
trace("double check it:");
-
trace(frac[0] + "/" + frac[1] +" = " + frac[0] / frac[1]);
-
-
/* outputs:
-
0.5 = 1/2
-
0.75 = 3/4
-
0.48 = 12/25
-
-
a more complex example:
-
0.98765432 = 12345679/12500000
-
double check it:
-
12345679/12500000 = 0.98765432
-
*/
-
-
-
function printFraction(n:Number):void{
-
var frac:Array = asFraction(n);
-
trace(n + " = " + frac[0] + "/" + frac[1]);
-
}
-
-
// takes any value less than one and returns an array
-
// with the numerator at index 0 and the denominator at index 1
-
function asFraction(num:Number):Array{
-
var decimalPlaces:int = num.toString().split(".")[1].length;
-
var denom:Number = Math.pow(10, decimalPlaces);
-
return reduceFraction(num * denom, denom);
-
}
-
-
// divide the numerator and denominator by the GCF
-
function reduceFraction(numerator:int, denominator:Number):Array{
-
// divide by the greatest common factor
-
var divisor:int = gcf(numerator, denominator);
-
if (divisor){
-
numerator /= divisor;
-
denominator /= divisor;
-
}
-
return [numerator, denominator];
-
}
-
-
// get the greatest common factor of two integers
-
function gcf(a:int, b:int):int{
-
var remainder:int;
-
var factor:Number = 0;
-
var maxIter:int = 100;
-
var i:int = 0;
-
while (1){
-
if (b> a){
-
var swap:int = a;
-
a = b;
-
b = swap;
-
}
-
remainder = a % b;
-
a = b;
-
b = remainder
-
if (remainder == 0){
-
factor = a;
-
break;
-
}else if (remainder==1){
-
break;
-
}else if (i> maxIter){
-
trace("failed to calculate gcf");
-
break;
-
}
-
i++;
-
}
-
return factor;
-
}
This snippet contains a few functions for calculating fractions based on float values. Writing this brought back some memories from grade school math.
Also posted in Math | Tagged actionscript, as3, flash |
By Zevan | November 27, 2009
Actionscript:
-
egyptianMultiply(12, 99);
-
-
// trace(12 * 99) // test to make sure it works
-
-
/* outputs:
-
/64 768
-
/32 384
-
16 192
-
8 96
-
4 48
-
/2 24
-
/1 12
-
---
-
1188
-
*/
-
-
-
function egyptianMultiply(valueA:Number, valueB:Number):void {
-
-
var left:Array = [];
-
var right:Array = []
-
-
// swap if valueB is smaller than value A
-
if (valueB <valueA){
-
var swap:int = valueB;
-
valueB = valueA;
-
valueA = swap;
-
}
-
-
// create left and right columns
-
var currentLeft:int = 1;
-
var currentRight:int = valueA;
-
-
while (currentLeft <valueB){
-
left.push(currentLeft);
-
right.push(currentRight);
-
currentRight += currentRight;
-
currentLeft += currentLeft;
-
}
-
-
-
// add up the right column based on the left
-
currentLeft = 0;
-
var rightSum:int;
-
var leftSum:int;
-
var i:int = left.length - 1;
-
-
while (currentLeft != valueB){
-
-
leftSum = currentLeft + left[i];
-
// if the next number causes the sum
-
// to go above valueB, skip it
-
if (leftSum <= valueB){
-
currentLeft = leftSum;
-
rightSum += right[i];
-
trace("/" + left[i] + " " + right[i]);
-
} else {
-
trace(" " + left[i] + " " + right[i]);
-
}
-
i--;
-
}
-
trace("---");
-
trace(rightSum);
-
}
Someone mentioned egyptian multiplication to me yesterday... So read a little about it here and whipped up this example. For some reason I decided to do it in processing ... once it worked I ported it to ActionScript.
The above link describing the technique I used is from http://www.jimloy.com/... I haven't spent much time digging around the site, but it appears to have some pretty nice stuff on it...
If you're curious, here is the original processing version:
JAVA:
-
-
-
-
int valueA = 10;
-
int valueB = 8;
-
-
if (valueB <valueA){
-
int swap = valueB;
-
valueB = valueA;
-
valueA = swap;
-
}
-
-
int currentLeft = 1;
-
int currentRight = valueA;
-
while (currentLeft <valueB){
-
left.add(currentLeft);
-
right.add(currentRight);
-
currentRight += currentRight;
-
currentLeft += currentLeft;
-
}
-
-
currentLeft = 0;
-
-
int result = 0;
-
int i = left.size() - 1;
-
while (currentLeft != valueB){
-
-
int temp
= currentLeft
+ (Integer) left.
get(i
);
-
if (temp <= valueB){
-
-
currentLeft = temp;
-
-
println("/" + left.get(i) + " " + right.get(i));
-
} else {
-
println(" " + left.get(i) + " " + right.get(i));
-
}
-
-
i--;
-
}
-
println("---");
-
println(result);
After writing, this I took a look at the wikipedia entry... I also found myself on this short page about a scribe called Ahmes. (I recommend reading this if you are interested in PI)
By Zevan | November 25, 2009
Actionscript:
-
var container:Sprite = new Sprite();
-
container.x = stage.stageWidth / 2;
-
container.y = stage.stageHeight / 2;
-
addChild(container);
-
-
var redBox:Sprite = new Sprite();
-
redBox.graphics.beginFill(0xFF0000);
-
redBox.graphics.drawRect(-50,-250,100,500);
-
redBox.rotationZ = 10;
-
container.addChild(redBox);
-
-
var logos:Array = []
-
var elements:Array = [];
-
elements.push({element:redBox, z:0});
-
-
// add the logos
-
for (var i:int = 0; i<6; i++){
-
var logoContainer:MovieClip = new MovieClip();
-
var logoText:TextField = new TextField();
-
logoText.defaultTextFormat = new TextFormat("_sans", 50);
-
logoText.text = "LOGO";
-
logoText.autoSize = "left";
-
logoText.selectable= false;
-
-
logoText.x = -logoText.width / 2;
-
logoText.y = -logoText.height / 2;
-
logoContainer.addChild(logoText);
-
logoText.backgroundColor = 0xFFFFFF;
-
-
container.addChild(logoContainer);
-
logos.push(logoContainer);
-
elements.push({element:logoContainer, z:0});
-
}
-
-
var ang:Number = -Math.PI / 2;
-
var rotationSpeed:Number = 0.05;
-
addEventListener(Event.ENTER_FRAME, onLoop);
-
function onLoop(evt:Event):void {
-
-
var dx:Number = (mouseY - stage.stageHeight / 2) / 10;
-
var dy:Number = (mouseX - stage.stageWidth / 2) / 10;
-
container.rotationX += (dx - container.rotationX) / 4;
-
container.rotationY += (dy - container.rotationY) / 4;
-
-
ang += rotationSpeed;
-
for (var i:int = 0; i<logos.length; i++){
-
var logo:Sprite = logos[i];
-
logo.x = 150 * Math.cos(ang + i);
-
logo.z = 150 * Math.sin(ang + i);
-
logo.alpha = 1 - logo.z / 200;
-
logo.rotationY = -Math.atan2(logo.z, logo.x) / Math.PI * 180 - 90;
-
}
-
-
// z-sort
-
for (i = 0; i<elements.length; i++){
-
elements[i].z = elements[i].element.transform.getRelativeMatrix3D(this).position.z;
-
}
-
-
elements.sortOn("z", Array.NUMERIC | Array.DESCENDING);
-
for (i = 0; i<elements.length; i++) {
-
container.addChild(elements[i].element);
-
}
-
}
A student of mine was having trouble creating a 3D logo for a client. I created this snippet to help explain how some of the fp10 3D stuff works.... z-sorting etc... The code could be optimized a bit... but it works nicely...
Have a look at the swf...

Also posted in 3D, motion | Tagged actionscript, as3, flash, fp10 |
By Zevan | November 23, 2009
Actionscript:
-
var connect:Function = function(xp:Number, yp:Number, col:uint=0):Function{
-
graphics.lineStyle(0,col);
-
graphics.moveTo(xp, yp);
-
var line:Function = function(xp:Number, yp:Number):Function{
-
graphics.lineTo(xp, yp);
-
return line;
-
}
-
return line;
-
}
-
-
// draw a triangle
-
connect(200,100)(300,300)(100,300)(200, 100);
-
-
// draw a box
-
connect(100,100, 0xFF0000)(150,100)(150,150)(100, 150)(100,100);
This is one of those techniques that I never really get tired of. It's pretty useless, but fun to play around with every now and then. This draws the somewhat boring looking picture below:

[EDIT]
A few people pointed out that this could be simplified with arguments.callee... So here is an example... it does the same thing as the original code...
Actionscript:
-
var connect:Function = function(xp:Number, yp:Number, col:uint=0):Function{
-
graphics.lineStyle(0,col);
-
graphics.moveTo(xp, yp);
-
return function(xp:Number, yp:Number):Function{
-
graphics.lineTo(xp, yp);
-
return arguments.callee;
-
}
-
}
Also posted in functions | Tagged actionscript, as3, flash |
The other judge for the contest was Rich Shupe. We just reviewed the entries together and have decided on a winner....
The winner is Petri Leskinen (piXelero)
Have a look at the winning snippet if you haven't seen it already.
Rich and I thought that Kyle Phillips deserved honorable mention for his google analytics snippet.
Thanks to everyone who sent in snippets. Even though there were only a handful of submissions I had a good deal of fun reviewing and judging. I still have two more entries that I have yet to publish... so stay tuned...