Brand Wall, Chapter 1

The Brand Wall is a public installation at Statoil's headquarters in Oslo, Norway. For the opening of the building in October 2011 we developed the basis for what will become a dynamic collection of digital pieces, experimenting with new ways of visualizing the company's ideas and values.

16 NEC MultiSync monitors. ~ 5x3m in size. 4k resolution in total ( for now )
Content is developed as custom made software, built on top of Cinder.

Design & coding by Myself + Marcin Ignac.
Concept, design and direction by Roar Sager (Scandinavian Design Group) & Håvard Gjeldseth (INT).

Music: "For" by Nils Frahm.

No Comments »

DMX tube experiment

Experiment made at @nanikawa's workshop at Resonate 2012. Uses openCV to track phone position and DMX Pro to communicate with the dmx fixture over serial port.
------------
Made with openFrameworks, DMXPro, ofxUI & ofxOpenCV
------------
Code at github.com/bgstaal/DMX-tube-experiment

No Comments »

Long Time No See

Yeah, I know. Every post in this blog starts out with an excuse for why it is the first post in a decade. The reason for my one year blog neglectance this time around is actually not the result of me being lazy though. Quite the contrary! I've basically just been really busy diving in to a lot of new stuff I've been wanting to learn for some time and have not found the time to tell anyone about it.

Basically I've been trying to become efficient at Javascript development, while trying to pick up on some iOS stuff, while trying to learn C++ & openFrameworks, while having a look at some Java development and in the process getting more into advanced graphics programming with openGL/GLSL, computer vision and physical interaction, as well as trying to learn some more math/physics. On top of that, I have a new found love for electronics prototyping through the Arduino platformt. To say the least my brain is starting to get a bit sweaty!

Anyways. I've decided it might be time to take a small step back from all the consuming and actually start producing! In the process, I hope to find some time to share some of my explorations as well. So let's see if we can't blow some life in to this old blog of mine shall we?

1 Comment »

Hairy photos

041

101

061

[Click photos for full view]

I did these photos for a friend of mine who were representing Toni & Guy in the fight for the Norwegian title "Årets Frisør". We were on a very limited budget (If we can use the word budget at all) so we ended up using the cheapest kind of industrial lighting and black rug taped to the wall. The backdrops were created separately from a bunch of cityscape photos I caught downtown of Oslo, late fall 2009. Still, I think the result came out pretty nice.

A bunch of thankyous to Daniel for lending me a helping hand & offering creative input, the splendid models, Charlotte, Thea & Julie, who showed up for free & did an awesome job (considering the circumstances), Charlotte nr. 2 for doing the makeup & of course Benjamin, the hairdresser.

1 Comment »

Perspective projection in flash

Get Adobe Flash player

A while back I was working on a project for a client when I ran into some issues trying to implement a semi 3d design concept. When I did the sketches in photoshop I thought it was gonna be a breeze to implement, but after the design was approved by the client and the deadline was set, I really ran into some trouble. I can´t really go into specifics about this particular project, but the bottom line was that I needed to apply the rules of 3d perspective in some situations, while bending the same rules to fit a dynamic two dimensional layout in other. The result I was looking for just couldn´t be achieved by applying the built in 3d features of Flash 10, or any of the other open source 3d libraries like papervision3d or away3d. At least not without having a great deal of in depth knowledge of how these libraries work from the ground up. That is why I decided to get my hands dirty with the basics of 3d math.

I found a couple of articles on the subject while browsing for some theory, but since I'm no mathematician, It took me a while to wrap my head around the concepts and really understand the theory, as well as how to take advantage of this in actionscript. After a while though, I finally pieced together the parts I needed to build some simple 3d geometry from scratch, while having full control over each point in both 2d and 3d space. Later on I structured the formulas and functions needed to execute these tasks, into three simple classes that are easy to use, even if you don’t understand the underlying math. Later on I'll explain how to make use of these classes to build the example you see at the very beginning of this article, but first I will try to explain my understanding of the underlying theories.

Some quick theory.

Perspective projection is the theory of how to display and move 2d coordinates in relation to each other to make the illusion of 3d space on a flat surface. In our case: it helps us place, draw and move things in the display list, in a way that makes them seem like they exist in 3d space. When doing 3d in flash it can be helpful to imagine the stage as a plane which we view through a camera viewfinder, and that the objects we want to draw are floating behind it. Take a look at the following illustration:

figure11

Here we see the stage from above, with a camera pointed directly at it's center, and a cube floating behind it. We also see some dotted lines drawn from the center of the camera to each of the cubes corners and the points where these lines intersect with the stage (marked with four small dots). These dots show us where the corner points of the cube should be drawn to create the illusion that we are looking for. The placement of these points clearly show that the four corner points further away from the camera, are placed closer to the where the camera is pointed. The result of drawing this on the stage would look something like this:

figure2

The Field of View and the focal length

Take a look at figure 1 again and take notice of the terms ‘Field of View’ & ‘Focal Length’. These terms are very essential to the concept of perspective projection as they lay down the ground rules for how scale and distance is depicted:

The field of view is the angle of the observers (or the camera’s) field of vision. It is a value between 0 and 180, where a value close to 180 exaggerates the perspective, as if the image was shot with a fisheye lens, while a value closer to 0 “flattens” the image and under-exaggerates distances between points.

The Focal Length on the other hand is the z-distance from the observer (camera) to the viewing plane (in this case the stage). This value is closely coupled with the field of view as this distance will have to vary depending on the field of vision, for the outer boundaries of the viewing frustum to line up with the stage (more on this later). The focal length is also the main component in the simple formula that is used to calculate the scale and offset (commonly reffered to as the t value) of each point:

formula

What we can read from this formula is that the scale and offset of each point is in fact the ratio between the distance from the camera to the stage, and the distance from the camera to the point. The only thing which is left then is to apply the scale and offset to each point by multiplying the x, y, and scale by the t value:

example (in pseudo code);

focalLength = 1000;
 
point3d.x = 100;
point3d.y = 50;
point3d.z = 100;
 
t = focalLength / (focalLength + point3d.z); // 1000 / (1000+100) == 0.9;
 
point2d.x = point3d.x * t;  // 100 * 0.9 == 90;
point2d.y = point3d.y * t; // 50 * 1.1 == 45;
point2d.scale = 1 * t; // 1 * 0.9 == 0.9;
 
/* The projected versjon of 3dPoint (100, 50, 100)
is therefore point2d (90, 45); with a scale of 0.9*/

Putting theory to practice

To make these concepts a bit easier to work with in actionscript I wrote three simple classes which provides a good foundation for doing custom 3d transformations and perspective projection in Flash: Point2d, Point3d & Matrix3d. This is the same kind of classes that make up the foundation for well known 3d libraries like papervision3d and away3d, only these classes are a bit easier to work with outside the context of a framework. (There is also a set of equivalent object types that ship with flash player 10 but I have experienced some bugs and actual miscalculations when using them and it seems I am not the only one)

Well, enough with the shit chat, let's get started with building our spinning cube! But first:

> Download source & example class
> Take a look at the API documentation

Ok. The first thing we will do is set up the base class, it´s imports and class properties, before populating these properties with some values:

package
{
	import __AS3__.vec.Vector;
 
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;
 
	import net.bgstaal.perspectiveprojection.Matrix3d;
	import net.bgstaal.perspectiveprojection.Point2d;
	import net.bgstaal.perspectiveprojection.Point3d;
 
	[SWF(widht="600", height="330", backgroundColor="0xFFFDE2")]
	public class SpinningCube extends Sprite
	{
		private const COLOR:Number = 0x000000;
 
		private var _cubeWidth:Number;
		private var _cubeHeight:Number;
		private var _cubeDepth:Number;
		private var _cubeX:Number;
		private var _cubeY:Number;
		private var _projectionCenter:Point;
		private var _fieldOfView:Number;
		private var _focalLength:Number;
		private var _pivotPoint:Point3d;
		private var _3dPoints:Vector.;
		private var _2dPoints:Vector.;
 
		private var _rotationX:Number = 0;
		private var _rotationY:Number = 0;
		private var _rotationZ:Number = 0;
 
		public function SpinningCube()
		{
			init();
		}
 
		private function init ():void
		{
			setProperties();
		}
 
		/**
		 * Set the inital properties for our perspective projection scene
		*/
		private function setProperties ():void
		{
			// set the size properties of the cube
			_cubeWidth = 200;
			_cubeHeight = 200;
			_cubeDepth = 200;
 
			// Set the position of the cube to the center of the stage
			_cubeX = (stage.stageWidth - _cubeWidth)/2;
			_cubeY = (stage.stageHeight - _cubeHeight)/2;
 
			// Set the projection center (vanishing point) to the center of the stage
			_projectionCenter = new Point(stage.stageWidth/2, stage.stageHeight/2);
 
			// Set the pivot point to be at the 3d center of the cube
			_pivotPoint = new Point3d(_cubeX + (_cubeWidth/2), _cubeY + (_cubeHeight/2), _cubeDepth/2);
 
			// Set the the field of view to a numver between 1 & 179.
			_fieldOfView = 55;
 
			// Calculate the focal length based on the width of the stage and the field of view.
			var a:Number = _fieldOfView/2;
			var b:Number = 90 - a;
			var bRad:Number = b/180*Math.PI;
			var opposite:Number = stage.stageWidth/2;
 
			_focalLength = opposite * Math.tan(bRad);
		}
	}
}

All of these class properties should be self explanatory by now, except for maybe pivotPoint & projectionCenter:

The pivot point is basically the registration point of the object we are applying transformations to. In this case; The point which the cube is rotated around.

The projection center is where we would like the center of our imaginary camery to be directed. In this case; The dead center of the stage.

Also take notice of how the focal length is calculated (the last couple of lines in the setProperties method) based on the field of view and the stage width. This is done so that the projections coordinate system is aligned with the stage coordinate system. The following figure demonstrates how these calculations are done by splitting the viewing frustum into two right angled triangles and applying some simple trigonometry:

figure3

Next, we set up the 'create3dPoints' method to create the eight corner points of our cube:

private function create3dPoints ():void
{
	_3dPoints = new Vector.();
 
	// add points to create a rectangle of the supplied width and height
	// at the supplied x and y coordinates and a z value of 0
	_3dPoints.push(new Point3d(_cubeX, _cubeY, 0));
	_3dPoints.push(new Point3d(_cubeX + _cubeWidth, _cubeY, 0));
	_3dPoints.push(new Point3d(_cubeX + _cubeWidth, _cubeY + _cubeHeight, 0));
	_3dPoints.push(new Point3d(_cubeX, _cubeY + _cubeHeight, 0));
 
	// then add the same points again with a z value of the supplied depth
	_3dPoints.push(new Point3d(_cubeX, _cubeY, _cubeDepth));
	_3dPoints.push(new Point3d(_cubeX + _cubeWidth, _cubeY, _cubeDepth));
	_3dPoints.push(new Point3d(_cubeX + _cubeWidth, _cubeY + _cubeHeight, _cubeDepth));
	_3dPoints.push(new Point3d(_cubeX, _cubeY + _cubeHeight, _cubeDepth));
}

Then we set up the projectPoints method which loops through the our eight corner points, calls the project() method (with the focal lenght and projection center supplied as parameters) on each of the instances and populates the _2dPoints Vector with Point2d instances.

/**
 * Loops through and projects the 3d points to create a vector of 2d points.
 */
private function projectPoints ():void
{
	_2dPoints = new Vector.();
 
	for (var i:int = 0; i < _3dPoints.length; i++)
	{
		var point3d:Point3d = _3dPoints[i];
 
		// calls the project function on each point3d wich reaturns a point2d
		var point2d:Point2d = point3d.project(_focalLength, _projectionCenter);
		_2dPoints.push(point2d)
	}
}

If we take a look at the internals of the Point3d.project() function, we recognize the formula for calculation of the t-value at the first line. The next step would normally be to offset the x & y coordinates by this value, but before we can do that we have to make shure that the projection is performed around the cameras projection center. This is done by negatively offsetting the coordinates by the projection center's coordinates, applying the perspective projection, and then reversing the offset. If we just applied the projection right away the final result would look as if the camera was pointed at the top left corner of the stage (0, 0).

public function project (focalLength:Number, projectionCenter:Point = null):Point2d
{
	var t:Number = focalLength / (focalLength+z);
 
	if (!projectionCenter)
	{
		projectionCenter = new Point(0, 0);
	}
 
	var xOffset:Number = projectionCenter.x;
	var yOffset:Number = projectionCenter.y;
 
	var x:Number = this.x;
	var y:Number = this.y;
	var z:Number = this.z;
 
	x -= xOffset;
	y -= yOffset;
 
	x = (x*t)+xOffset;
	y = (y*t)+yOffset;
 
	return new Point2d(x, y, t);
}

All that is left now before we can get some visual feedback is to draw the points on screen.

To do that we first set up the drawPoints() function:

/**
 * Draws a circle on stage as a visual representation of each point. Notice that we multiply the radius of each circle by the Point2d instance's t-value to make the scaling of each point smaller or bigger depending on its distance from the camera and thus enhancing the illusion of 3d space.
 */
private function drawPoints ():void
{
	var radius:Number = 5;
 
	graphics.clear();
 
	for (var i:int = 0; i < _2dPoints.length; i++)
	{
		var point2d:Point2d = _2dPoints[i];
		graphics.beginFill(COLOR);
		graphics.drawCircle(point2d.x, point2d.y, radius*point2d.t);
		graphics.endFill()
	}
}

Then we add add another method to collect all function calls related to the projection:

private function createProjection ():void
{
	create3dPoints();
	projectPoints();
	drawPoints();
}

and make sure we call it from the init function:

private function init ():void
{
	setProperties();
	createProjection();
}

Compile & run the application and you should get the following result:

result1

I know. Not too exciting yet... But at least we can see that we have done something right! The four corner points at the back of the cube are rendered smaller, and closer to each other, which gives them the appearance of being further away from the camera. Let's move on to the next step and add some rotation by putting the Matrix3d Class to use:

private function rotate3dPoints ():void
{
	var rotationMatrix:Matrix3d = Matrix3d.createRotationMatrix(_rotationX, _rotationY, _rotationZ);
 
	for (var i:int = 0; i < _3dPoints.length; i++)
	{
		var point3d:Point3d = _3dPoints[i];
		point3d.applyMatrix(rotationMatrix, _pivotPoint);
	}
}

Since all the functionality of creating and multiplying matrices is abstracted in to the Matrix3d class, adding rotation to the cube is as simple as calling a single static function to create a rotation matrix before applying it to the 3d points. We don't even have to think of how these calculations are done, it just works! (that is if we actually supply something else then 0, 0, 0 as parameters, which we will do pretty soon) If you are anything like me though, you would wanna know every little part of how these calculations are done. That is why I will try to explain, as briefly as i can, the basics of matrices and how this is applied to 3d mathematics: (If you're really not that in to masochism you can skip this part and go right on with finishing the application)

Matrices

A matrix is basically a two dimensional array (or table) of values. Like this one:

[1, 0, 0, 0
 0, 1, 0, 0
 0, 0, 1, 0]

Pretty simple right? Maybe too simple? Well, the power of matrices lies not in the structures themselves but in the way they can be applied to / multiplied with other matrices of various dimensions. They are multiplied in a way where each value in a row is the result of the first value in that row multiplied with the first value in that collumn plus the second value in that row multiplied with the second value in that row and so on:

figure4

By studying the multiply() function closely we can see the same pattern emerging:

public static function multiply (a:Matrix3d, b:Matrix3d):Matrix3d
{
	var c:Matrix3d = new Matrix3d();
 
	c.n11 = a.n11 * b.n11 + a.n12 * b.n21 + a.n13 * b.n31;
	c.n12 = a.n11 * b.n12 + a.n12 * b.n22 + a.n13 * b.n32;
	c.n13 = a.n11 * b.n13 + a.n12 * b.n23 + a.n13 * b.n33;
	c.n14 = a.n11 * b.n14 + a.n12 * b.n24 + a.n13 * b.n34 + a.n14;
 
	c.n21 = a.n21 * b.n11 + a.n22 * b.n21 + a.n23 * b.n31;
	c.n22 = a.n21 * b.n12 + a.n22 * b.n22 + a.n23 * b.n32;
	c.n23 = a.n21 * b.n13 + a.n22 * b.n23 + a.n23 * b.n33;
	c.n24 = a.n21 * b.n14 + a.n22 * b.n24 + a.n23 * b.n34 + a.n24;
 
	c.n31 = a.n31 * b.n11 + a.n32 * b.n21 + a.n33 * b.n31;
	c.n32 = a.n31 * b.n12 + a.n32 * b.n22 + a.n33 * b.n32;
	c.n33 = a.n31 * b.n13 + a.n32 * b.n23 + a.n33 * b.n33;
	c.n34 = a.n31 * b.n14 + a.n32 * b.n24 + a.n33 * b.n34 + a.n34;
 
	return c;
}

We abide to the same rules when applying transformation matrices to points. By treating the point as a one dimensional Matrix (also called a vector) and cross multiplying the values in its single column with each of the values in the transformation matrix's rows, we get a new transformed version of that point:

These concepts can be a bit hard to grasp but the bottom line is that this special way of "cross-calculating" the values gives each cell in the table special properties when it comes to transformation. The most obvious ones is the diagnoal values from the top left cell to the third bottom cell. These cells are the ones that relate directly to the scale of each dimension:

[x, 0, 0, 0
 0, y, 0, 0
 0, 0, z, 0]

The following matrix (called the identity matrix) for instance, will multiply the x, y and z scale by 1, and therefore leave the subject unchanged:

[1, 0, 0, 0
 0, 1, 0, 0
 0, 0, 1, 0]

Rotation matrices look a bit more complicated but is really quite simple to use too. Here are the rotation matrices for all three axes: ("r" being the level of rotation we want to add, in radians)

// x-rotation (roll)
[1, 0, 0, 0
 0, cos(r), sin(r), 0
 0, -sin(r), cos(r), 0]
 
// y-rotation (pitch)
[cos(r), 0, sin(r), 0
 0, 1, 0, 0
 -sin(r), 0, cos(r), 0]
 
// z-rotation (yaw)
[cos(r), sin(r), 0, 0
 -sin(r), cos(r), 0, 0
 0, 0, 1, 0]

You might also be wondering what the fourth collumn in the matrix is for? Since there is no fourth row in the matrix, the values in the fourth column has no values to be multiplied with and is therefore just added to the result. Any value placed in one of the cells in this column will therefore be added as an offset (also called translation) along that axis. The following matrix is an example of an translation matrix witch will offset the subject by 2 pixels along the y axis.

[1, 0, 0, 0
 0, 1, 0, 2
 0, 0, 1, 0]

The Matrix3d class has methods for creating all these types of transformations through the static methods createScalingMatrix(), createXRotationMatrix(), createYRotationMatrix(), createZRotationMatrix(), createRotationMatrix() & createTranslationMatrix(). do multiplication through the static methods multiply() & multiplySeveral() & apply the transformations to Point3d instances throught the apply() method on either a Matrix3d object (which returns a new Point3d instance) or a Point3d object (which applyes the transformations to that particular instance).

Finishing up

Ok. So now we have set up our geometry, projected & drawn it on stage, and written the method responsible for rotating the cube. Next, we will add a call to this method from the createProjection() function: (Note: It is important to add this function call before calling the project() and draw() methods as we want to add the rotation before actually drawing the geometry on stage)

private function createProjection ():void
{
	create3dPoints();
	rotate3dPoints();
	projectPoints();
	drawPoints();
}

If we run the application now we won´t see any changes to the output because the _rotationX, _rotationY & _rotationZ properties are still set to 0. We could very easily add some rotation instantly by setting these properties to something else then zero, but since we want the cube to rotate over time we set up an enter frame listener, call the createProjection() method from there, and then increment one or several rotation properties by the desired amount:

private function init ():void
{
	setProperties();
	createProjection();
 
	addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
private function enterFrameHandler (e:Event):void
{
	createProjection();
	_rotationX += 4;
	_rotationY += 4;
}

Recompiling and running the app now should give something like the following result:

Now we can clearly see that the points are moving in relation to each other in a way that seems in tune with our perception of 3d space. Still, we have left out a crucial part of making the cube seem volumetric: Drawing the lines that connect the points together. I have intentionally left this part out until now to emphasize the fact that computed 3d geometry is nothing more then a collection of points in 3d space (refereed to as vertices in 3d lingo) that together make up a mesh. To make it more obvious, let's connect the dots by adding a drawLines() function:

/**
 * Draws the lines that connects the eight corners of the cube
 */
private function drawLines ():void
{
	graphics.lineStyle(1, COLOR);
 
	// draw the first rectangle
	graphics.moveTo(_2dPoints[0].x, _2dPoints[0].y);
	graphics.lineTo(_2dPoints[1].x, _2dPoints[1].y);
	graphics.lineTo(_2dPoints[2].x, _2dPoints[2].y);
	graphics.lineTo(_2dPoints[3].x, _2dPoints[3].y);
	graphics.lineTo(_2dPoints[0].x, _2dPoints[0].y);
 
	// draw the second rectangle
	graphics.moveTo(_2dPoints[4].x, _2dPoints[4].y);
	graphics.lineTo(_2dPoints[5].x, _2dPoints[5].y);
	graphics.lineTo(_2dPoints[6].x, _2dPoints[6].y);
	graphics.lineTo(_2dPoints[7].x, _2dPoints[7].y);
	graphics.lineTo(_2dPoints[4].x, _2dPoints[4].y);
 
	// draw lines between the corners of the rectangles
	graphics.moveTo(_2dPoints[0].x, _2dPoints[0].y);
	graphics.lineTo(_2dPoints[4].x, _2dPoints[4].y);
 
	graphics.moveTo(_2dPoints[1].x, _2dPoints[1].y);
	graphics.lineTo(_2dPoints[5].x, _2dPoints[5].y);
 
	graphics.moveTo(_2dPoints[2].x, _2dPoints[2].y);
	graphics.lineTo(_2dPoints[6].x, _2dPoints[6].y);
 
	graphics.moveTo(_2dPoints[3].x, _2dPoints[3].y);
	graphics.lineTo(_2dPoints[7].x, _2dPoints[7].y);
}

and call that function from the createProjection() method:

private function createProjection ():void
{
	create3dPoints();
	rotate3dPoints();
	projectPoints();
	drawPoints();
	drawLines();
}

Recompiling and running the application now should result in something like the spinning cube at the top of this article.

Now we can see clearly that the points in fact make up the geometry of a cube. Still, this simple mesh is not very interesting, and it the process of making it seems maybe a bit too elaborate for such a simple result. If we just wanted to make wireframe 3d-primitives (or even more advanced textured geometry) it would take a lot less work to just use a library like away3d or papervision3d. The real power of using this manual approach lies in the control we now have of each point in both 2d and 3d space. At anytime we can easily replace the dull, circular representation of each point with any displayObject to create something like a spinning cube of menu-items or a sphere of photos as an alternative to the well known photo carousel. Once we start playing with these concepts the possibilities are endless!

I will follow up this article shortly with some pretty interesting examples of what can be achieved by putting these techniques to use.

[Note: The preceding examples are structured in a way that aims to make the code readable and is no way optimized to ensure high performance]

5 Comments »

How well do you know Norwegian geography?

screen061

screen031

screen041

screen101

screen071

We just launched a quiz game over at Bleed, as a fun way of promoting Ditt Distrikt's new community information services. The game challenges the user to play against other's from their local area to see who has the most geography skills. While doing so, the players also contribute to raising their home county's average score in the pursuit of being the best county in Norway.

Design, art direction & development by me.

You can check out the game at dittdistrikt-spillet.no

No Comments »

There and back again

logo2

I guess I'm the latest person to announce this, but nothing is better than hearing it directly from the source right? Anyways, the big news is that I'm going back to work at Bleed! (Well, actually I have been working there for about a month already, but things have been so busy that I have had no real time to sit down and think about it, let alone write about it).

For those you who don't know: I used to work there until some of my colleges broke out to start a new company, Anti, and I was asked to join. I stayed there for about a year before deciding to return to to my former employer a couple of months ago. As much as I like the people at Anti and the stuff they produce, I started feeling a bit lonely as the only developer/designer with a clear focus on interactive medias.

Looking forward, I am really excited about all the cool stuff happening at Bleed: We have just launched a new web & identity, our new offices are nearly finished and a couple of promising interns are joining us over the next couple of months. With several new & exciting clients and plans for an independent exhibition/book release in the pipeline, I'm shure that this is the beginning of the Bleed renaissance!

I'll keep you posted as soon as I have more news. In the meantime, be shure to check out our new site and take a look at some photos from our first week in the new offices:

bleed02

bleed01

bleed00

No Comments »

Format Number as Currency

Just had the need to format some numbers into different currencies the other day so I made a simple, generic function to do the trick.

Example:

var dollars:String = "$" + currencyFormat(343769.5);
var euro:String = "€ " + currencyFormat(150, 2, ".", ",")
var nok:String = currencyFormat(10500, 2, ".", ",") + ",-"
 
trace(dollars) // $343,769.50
trace(euro) // €150,00
trace(nok) // 10.500 ,-

source:

function currencyFormat (number:Number, numDecimals:int = 2, separatorSymbol:String = ",", decimalSymbol:String = "."):String
{
	var multiplier:int = Math.pow(10, numDecimals);
	number = Math.round(number*multiplier)/multiplier;
	number = round(number, numDecimals);
	var string:String; = number.toString();
 
	var split:Array = string.split(".");
	var pre:String = split[0];
	var post:String = split[1] == null ? "" : split[1];
 
	// Format Number
 
	var _pre:String = pre.slice();
	pre = "";
 
	var count:int = 0;
	for (var i:int = _pre.length; i >= 0; i--)
	{
		var cypher:String = _pre.charAt(i);
 
		if (count % 3 == 0 && count > 0 && i > 0)
		{
			cypher = separatorSymbol + cypher;
		}
 
		pre = cypher + pre;
		count ++;
	}
 
	// Format decimals
 
	var numMissingDecimals:int = numDecimals - post.length;
	for (var i:int = 0; i < numMissingDecimals; i++)
	{
		post += "0";
	}
 
	// Join the strings
 
	string = pre;
	if (numDecimals > 0)
	{
		string += decimalSymbol + post;
	}
 
	return string;
}

Do with it as you wish!

No Comments »

Anti Sweden Web

01

06

02

04

08

I have really had a lot of things going on both at work & at home for the last couple of months, which has resulted in no posts at all since June. Pretty lame stuff! Anyways! One of the projects I have been working on is the Anti Sweden website, which was launched just a few weeks ago. The site is a pretty simple, news driven site, with some very basic shop functionality and one or two nifty graphical details.

About the project:

"We at the Norwegian design office Anti wanted to use our experience and knowledge of design to create our own brand - a brand that reflected our love of fashion, of graphic design, and which reflected the unremitting darkness that is at the heart of the Norwegian identity and is the seat of true Black Metal. Anti Sweden is that unholy creation!"

Fredrik Melby is the main man responsible for the Art Direction & Graphic Design, closely supervised by Creative Director Kjetil Wold. The main illustrations are done by Justin Bartlett, photography by Alex Freund & Marius Tharaldsen, motion graphics by Storm Studios, music by Sunn O))) and motion design + development by yours truly.

Visit the site here, and be shure to pick up a pair of true norwegian black jeans!

No Comments »

Joy Division EQ

screen

I just love the cover art for the Joy Division album Unknown Pleasures and I have had this idea for a while to try to create something interactive based on the main illustration. So here is my first attempt: The "Joy Division Equalizer". For now it is just a rough sketch and the code is very very quick and dirty, but I think it looks kinda' cool. Click here to check it out.

Here is the dirrrrrrty source: (Written & Compiled in Flex Builder 3)

 
package
{
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.geom.Rectangle;
	import flash.media.Sound;
	import flash.media.SoundMixer;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
 
	[SWF(backgroundColor="0x000000", width="1440", height="900")]
	public class JoyDivisionEQ extends Sprite
	{
		private var _numLines:int = 60;
		private var _numSegments:int = 40;
		private var _spectrumStartSegment:int = 10;
		private var _spectrumEndSegment:int = 30;
		private var _maxSpectrumHeight:Number = 120;
		private var _rect:Rectangle = new Rectangle(0, 0, 400, 500);
		private var _noiseOffset:Number = 3;
		private var _sound:Sound;
		private var _stretchFactor:Number = 2;
 
		private var _wrapper:Sprite;
 
		public function JoyDivisionEQ()
		{
			init();
		}
 
		private function init ():void
		{
			setStageProps();
			createWrapper();
			playSong();
			draw();
 
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
			stage.addEventListener(Event.RESIZE, stageResizeHandler);
		}
 
		private function setStageProps ():void
		{
			stage.frameRate = 30;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
		}
 
		private function enterFrameHandler (e:Event):void
		{
			draw();
		}
 
		private function playSong ():void
		{
			_sound = new Sound(new URLRequest("shadowplay.mp3"));
			_sound.play(0, 100);
		}
 
		private function createWrapper ():void
		{
			_wrapper = new Sprite();
			_wrapper.x = (stage.stageWidth - _rect.width)/2;
			_wrapper.y = (stage.stageHeight - _rect.height)/2;
			this.addChild(_wrapper);
		}
 
		private function draw ():void
		{
			var xSpacing:Number = _rect.width/_numSegments;
			var ySpacing:Number = _rect.height/_numLines;
 
			var ba:ByteArray = new ByteArray();
			SoundMixer.computeSpectrum(ba, true, _stretchFactor);
 
			var g:Graphics = _wrapper.graphics;
			g.clear();
 
			for (var i:int = 0; i < _numLines; i++)
			{
				var y:Number = ySpacing*i;
				g.moveTo(0, y);
				g.lineStyle(1, 0xFFFFFF);
				g.beginFill(0);
 
				var start:int = _spectrumStartSegment - 2 + (4*Math.random())
				var end:int = _spectrumEndSegment - 2 + (4*Math.random())
				var length:Number = end - start;
 
				var prevY:Number;
 
				for (var j:int = 1; j <  _numSegments-1; j++)
				{
					if (ba.position == 200*_stretchFactor)
					{
						ba.position = 20*_stretchFactor;
					}
 
					var _y:Number = y;
 
					if (j >= start && j<= end)
					{
						var k:Number = j - start;
						var ratio:Number = (k/length);
						if (ratio > 0.5)
						{
							ratio = 1-ratio;
						}
						var amp:Number = _maxSpectrumHeight*ratio;
 
						if (j % 1 == 0)
						{
							var num:Number;
							_y = y - Math.abs((ba.readFloat()*amp));
						}
						else
						{
							_y = prevY + 10 - (10*Math.random());
						}
					}
 
					_y = _y - (_noiseOffset/2) + ((_noiseOffset/2)*Math.random());
					g.lineTo(xSpacing*j, _y);
					prevY = _y;
				}
 
				g.lineTo(xSpacing*(j+1), y);
				g.lineStyle();
				g.lineTo(0, y);
				g.endFill();
			}
 
		}
 
		private function stageResizeHandler (e:Event):void
		{
			_wrapper.x = (stage.stageWidth - _rect.width)/2;
			_wrapper.y = (stage.stageHeight - _rect.height)/2;
		}
	}
}
 

6 Comments »