kaleidoscope for christmas

December 2, 2012

Who wants a kaleidoscope for christmas? Last nice brick for last november, a kaleidoscope presenting your Louis Vuitton whishlist which you can share with yours friends.

The tricky thing was to manage something light in CPU, as I had to play without Stage3D AS3, staying on a flash player 10 build, no web-gl acceleration, no notihing. As I couldn’t use this native GPU rendering pipeline, I had to draw something more than play with a full bunch of sprites on my stage. So, I decided to draw a quart of the kaleidsocope and duplicate it.

and I came with something like this:

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Matrix;
	import flash.geom.Point;

	public class KaleidoscopeDrawer extends Sprite
	{
		private var _kaleidoscope:Sprite;
		private var _kBmp:Bitmap;
		private var _kBmp2:Bitmap;
		private var _kBmp3:Bitmap;
		private var _kBmp4:Bitmap;
		private var _kBmpd:BitmapData;
		private var _items:Vector.;
		private var _maxSlices:int = 8;
		private var _maxCircles:int = 6;
		private var _radius:int = 200;
		private var _kRadius:int = 90;
		private var _w:Number = 0;
		private var _h:Number = 0;
		private var _r:Number = 0;
		private var _rotSpeed:Number = 0.025;
		private var _zOrder:Number = 1;

		public function KaleidoscopeDrawer(items:Vector., mainWidth:Number, mainHeight:Number)
		{
			//_items are the different bitmap u will have on the kaleidoscope
			// one per circle, duplicated a few times
			_items = items;
			// randomizing is just for debugging purpose
			shuffleObject(_items);
			// if items.length is < to _maxCircles (max products on the screen at the same time)
			if(_items.length < _maxCircles) _items = maximize(_items);

			_w = mainWidth;
			_h = mainHeight;
			build();
		}

		/*
		 * Public methods
		 */
		// mouseWheeling to zoom/unzoom the kaleidoscope
		// increments/decrements radius of the different circles
		public function onMouseWheel(delta:int):void
		{
			trace(delta);
			var newRadius:int = _kRadius + delta * 5;

			if(newRadius < 250 && newRadius > 90) {
				_kRadius = newRadius;
			}

			if(newRadius > 250) {
				newRadius = 250;
				_kRadius = newRadius;

				var bmp:Bitmap = _items[0];
				_items.shift();
				_items.push(bmp);
			}

			if(newRadius < 90) { newRadius = 90; _kRadius = newRadius; }
                }
                // here u change the order of addChild
                // of the different circles of the kaleidoscope
                // so u have a new one
                public function onMouseClick():void
                {
                    _zOrder = -_zOrder;
                }
                
                public function resize(mainWidth:int, mainHeight:int):void { 
                    _w = mainWidth;
                    _h = mainHeight;_kaleidoscope.x = _w / 2;
                    _kaleidoscope.y = _h / 2;
                }
                
                /*
                 * Private methods
                 */
                private function build():void { 
                    _kaleidoscope = new Sprite();
                    _kBmp = new Bitmap(); 			_kaleidoscope.addChild(_kBmp); 			 			_kBmp2 = new Bitmap(); 			_kaleidoscope.addChild(_kBmp2); 			 			_kBmp3 = new Bitmap(); 			_kaleidoscope.addChild(_kBmp3); 			 			_kBmp4 = new Bitmap(); 			_kaleidoscope.addChild(_kBmp4); 			 			_kaleidoscope.x = _w / 2; 			_kaleidoscope.y = _h / 2; 			_kaleidoscope.scaleX = _kaleidoscope.scaleY = 1.2; 			addChild(_kaleidoscope); 			 			addEventListener(Event.ENTER_FRAME, draw); 		} 		 		private function draw(e:Event):void 		{ 			var kBmpd:BitmapData = new BitmapData(_radius * 2, _radius * 1.5, true, 0x0); 			var stepAngle:Number; 			var startAngle:Number = DEG2RAD(270); 			var matrix:Matrix = new Matrix(); 			var scaleFactor:Number = .35; 			var point:Point; 			var radius:int = _radius; 			var dir:int; 			var item:Bitmap; 			var maxSlices:int; 			var container:Sprite = new Sprite(); 			var slice:Shape; 			var i:int, j:int, iFactor:int; 			var diag:Number; 			 			// on mouseClick _zOrder changes 			// and the addChild order too. 			if(_zOrder == 1) 			{ 				// for each circle/product 				// we draw a quart of circle 				// each circle has a different number of items 				// depends on its radius/index in the kaleidoscope 				for(i = _maxCircles - 1; i >= 0; --i)
				{
					item = Bitmap(_items[i]);
					item.smoothing = true;

					iFactor = (_maxSlices - 1) - i;

					maxSlices = _maxSlices + iFactor ;
					stepAngle = DEG2RAD(360) / maxSlices;

					dir = (i % 2) ? -1 : 1;

					radius = _kRadius * 2 * (.25 + (iFactor * .06));

					point = new Point(item.width / 2, item.height / 2);

					matrix = item.transform.matrix;
					matrix.transformPoint(point);
					matrix.tx -= point.x;
					matrix.ty -= point.y;
					matrix.rotate(_r * dir);
					matrix.tx += point.x;
					matrix.ty += point.y;
					matrix.translate(-item.width / 2, -radius * 2);
					matrix.scale(scaleFactor + iFactor * .05, scaleFactor + iFactor * .05); // 250

					diag = Math.sqrt(radius * radius + radius * radius);
					drawSlices(item.bitmapData, container, matrix, diag, maxSlices, startAngle, stepAngle);
				}
			} else {
				//the same but in the different addChild order
				for(i = 0; i < _maxCircles; ++i)
				{
					item = Bitmap(_items[i]);
					item.smoothing = true;

					iFactor = (_maxSlices - 1) - i;

					maxSlices = _maxSlices + iFactor;
					stepAngle = DEG2RAD(360) / maxSlices;

					dir = (i % 2) ? -1 : 1;

					radius = _kRadius * 2 * (.25 + (iFactor * .06));

					point = new Point(item.width / 2, item.height / 2);

					matrix = item.transform.matrix;
					matrix.transformPoint(point);
					matrix.tx -= point.x;
					matrix.ty -= point.y;
					matrix.rotate(_r * dir);
					matrix.tx += point.x;
					matrix.ty += point.y;
					matrix.translate(-item.width / 2, -radius * 2);
					matrix.scale(scaleFactor + iFactor * .05, scaleFactor + iFactor * .05);

					diag = Math.sqrt(radius * radius + radius * radius);
					drawSlices(item.bitmapData, container, matrix, diag, maxSlices, startAngle, stepAngle);
				}
			}

			kBmpd.lock();
			matrix.identity();
			matrix.translate(0, kBmpd.height);
			kBmpd.draw(container, matrix);
			kBmpd.unlock();

			// we give the BitmapData to the four different bitmaps
			// to draw the entire kaleidoscope
			_kBmp.bitmapData = kBmpd;
			_kBmp.y = -_kBmp.height;

			_kBmp2.bitmapData = kBmpd;
			_kBmp2.y = -_kBmp2.height;
			_kBmp2.scaleX = -1;

			_kBmp3.bitmapData = kBmpd;
			_kBmp3.y = _kBmp3.height;
			_kBmp3.scaleX = _kBmp3.scaleY = -1;

			_kBmp4.bitmapData = kBmpd;
			_kBmp4.y = _kBmp4.height;
			_kBmp4.scaleY = -1;

			_kBmp.smoothing = _kBmp2.smoothing = _kBmp3.smoothing = _kBmp4.smoothing = true;

			// we increment _rotSpeed, so the kaleidoscope updates on enterFrame
			_r += _rotSpeed;
		}

		private function drawSlices(bmpd:BitmapData, container:Sprite, matrix:Matrix, diag:Number, maxSlices:int, startAngle:Number, stepAngle:Number):void
		{
			var slice:Shape;
			for(var i:int = 0; i < maxSlices / 4; ++i) { 				slice = new Shape(); 				//slice.graphics.lineStyle(1, 0xff0000); // 4 debug 				slice.graphics.beginBitmapFill(bmpd, matrix, false, true); 				slice.graphics.moveTo(0, 0); 				slice.graphics.lineTo(diag * 2.5 * Math.cos(startAngle - stepAngle / 2), diag * 2.5 * Math.sin(startAngle - stepAngle / 2)); 				slice.graphics.lineTo(diag * 2.5 * Math.cos(startAngle + stepAngle / 2), diag * 2.5 * Math.sin(startAngle + stepAngle / 2)); 				slice.graphics.lineTo(0, 0); 				slice.graphics.endFill(); 				slice.scaleX = (i % 2) ? -1 : 1; 				slice.rotation = RAD2DEG(stepAngle * i + stepAngle / 2); 				container.addChild(slice); 			} 		} 		 		private function DEG2RAD(deg:Number):Number 		{ 			return (2 * Math.PI * deg) / 360; 		} 		 		private function RAD2DEG(rad:Number):Number 		{             			return rad * 180 / Math.PI; 		} 		 		// how to sort a Vector in a random way 		private function shuffleObject(obj:Object):void { 			var i:int = obj.length; 			while (i > 0) {
				var j:int = Math.floor(Math.random() * i);
				--i;
				var temp:* = obj[i];
				obj[i] = obj[j];
				obj[j] = temp;
			}
		}

		private function maximize(v:Vector.):Vector. {
			var bmp:Bitmap;
			var index:int = 0;
			while(v.length < _maxCircles) {
				bmp = v[index];
				v.push(bmp);
				++index;
			}
			return v;
		}

	}

}

Tags: ,