August 7th, 2009

Supershapes – Coder Challenge

Charlie discovered the idea of the super formula. Since it sounded super, Charlie proposed a challenge, a coder challenge, a super coder challenge. The challenge was straightforward – create a super formula editor that creates a super shape. The other parameters being that the shape must spin in 3D (well at least look like it), the shape must be composed of at least 10,000 particles, the shape must be editable and it must be made in Flash. The winner determined by the fastest FPS, as judged by Mr Doobs stats class.

The super formula is as follows, thanks wikipedia:

932649147ef032f63b06bafbd22d2554

Many different shapes can be derived from this formula by changing m, n1, n2 and n3. Some of the combinations are :

superformula

Many optimization techniques were used to get those particles to form the unique shapes and spin at the same time. Everyone saw a large increase in FPS when using the bitmapdata.lock() method. All of the images captured were on a Dual 2 GHz PowerPC G5 with 3.5 GB Ram while testing the .swf files locally.

Ben’s Solution:

ben_bojko_supershapes

Ben used an approximation for sine that allowed him to compute his rotation values, eliminating the more expensive sine and cos function call.

public static var SIN1:Number	= 1.27323954;
<br />public static var SIN2:Number	= 0.405284735;
<br />// ---------- FastMath Sin/Cos ----------
<br />if ( _rotY &lt; -PI )	_rotY += P2;
<br />//compute sine
<br />if (_rotY  &gt; PI)	 _rotY -= P2;
<br />//compute cosine
<br />if (_rotY &lt; 0)	_rotYCos = SIN1 * _rotY + SIN2 * _rotY * _rotY;
<br />else			_rotYCos = SIN1 * _rotY - SIN2 * _rotY * _rotY;
<br />_rotY -= 1.57079632;
<br />// --------------------------------------

Charlie’s Solution:

charlie_whitney_supershapes

Charlie took a more object oriented approach and created a separate particle class to store the next particle to alter in his while loop. He got this linked list approach from Joa Ebert’s blog.

var p	:Particle = _firstParticle;
<br />...
<br />var adjustX	:Number = Math.cos(_inc);
<br />var adjustY	:Number = Math.sin(_inc);
<br />_inc += 0.05;
<br />
<br />_bmd.lock();
<br />_bmd.fillRect(_bmd.rect, 0xFFFFFF);
<br />
<br />while(p.next != null){
<br />	_phi = p.num * PI_OVER_NUM;
<br />       t1 = Math.cos(m * _phi / 4) / a;
<br />	if(t1&lt;0) t1 = -t1;	//abs
<br />	t1 = Math.pow(t1,n2);
<br />       ...
<br />       p = p.next;
<br />}

Nick’s Solution:

nick_hardeman_supershapes

The computations were killing the cpu, even simple multiplication or division, so I decided to create a look-up table using my favorite class the Vector,  to take care of the heavier operations. These tables are calculated when one of the parameters are changed and hold every calculation for many thousands of particles. I am sure they could be optimized by using approximation, to hold many less numbers. I tried every loop that I could think of, but I found that this enterframe for loop gave me the most speed.

 
<br />// enterframe loop //
<br />for (i = 0; i &gt;= 0; i--) { ... }
<br />
<br />// setup //
<br />var i:int = 0;
<br />for (i = 0; i &lt; numPoints; i++) {
<br />	_theta += angleInc;
<br />	lookupAnglesX[i] = Math.cos(_theta);
<br />	lookupAnglesY[i] = Math.sin(_theta);
<br />	lookupAnglesT[i] = (m * _theta) * .25;
<br />	lookupAnglesT_sin[i] = Math.sin(lookupAnglesT[i]);
<br />	lookupAnglesT_cos[i] = Math.cos(lookupAnglesT[i]);
<br />}

So to sum it up, sine and cos approximations, oop approaches and look-up tables will give you a boost in FPS, at least in the case of super shapes. So Big Spaceship extends the challenge to the flashy community and we look forward to any new questions, comments, solutions, optimizations, tips, tricks, feedback and anything else even somewhat relevant.

So dig through and rip apart the source. All three examples are included.

Super Shapes Source