Spirography

My wife mentioned something about Spirographs yesterday, so I thought “hey – why not make one in actionscript”. Two seconds of googling found the formulas I was looking for (as well as a nifty java example), and about 10 minutes of coding had something up and running in Flash. Play around with the sliders and hit “draw” to create a little Spirograph masterpiece of your own.

Speaking of controls, the first slider controls the radius of the fixed circle, the second slider controls the radius of the rotating circle, the third slider controls the offset of the “pen tip” in the rotating circle, the checkbox specifies whether you’re drawing outside of the fixed circle or not, the color swatch will let you select a color to draw in, and finally, if you just want to see the thing in action, click on the “Randomize!” button and see what you get.

Get Adobe Flash player

If interested, the whole script is below (I used bit-101′s minimal slider, button, and checkbox, and my own color picker, if you’d like to compile this thing):

package {
 
	import com.bit101.components.CheckBox;
	import com.bit101.components.ColorChooser;
	import com.bit101.components.PushButton;
	import com.bit101.components.Slider;
 
	import com.onebyonedesign.ui.events.ColorEvent;
	import com.onebyonedesign.ui.OBO_ColorPicker;
 
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
 
	import flash.events.Event;
	import flash.events.MouseEvent;
 
	/**
	 * Spirograph (based on java implementation by Anu Garg http://wordsmith.org/anu/java/spirograph.html)
	 * @author Devon O.
	 */
 
	[SWF(width='550', height='450', backgroundColor='#000000', frameRate='60')]
	public class Main extends Sprite {
 
		private var _outside:Boolean = false;
 
		private var _data:BitmapData;
		private var _spiro:Sprite;
 
		private var _xoffset:Number;
		private var _yoffset:Number;
 
		private var _color:uint = 0xFFFF00FF;
 
		private var R:int = 1;
		private var r:int = 1;
		private var O:int = 1;
 
		private var largeRadiusSlider:Slider;
		private var smallRadiusSlider:Slider;
		private var cOffsetSlider:Slider;
 
		public function Main():void {
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
 
		private function init(e:Event = null):void {
			removeEventListener(Event.ADDED_TO_STAGE, init);
 
			_xoffset = stage.stageWidth * .5;
			_yoffset = stage.stageHeight * .5;
 
			_data = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0x00000000);
			var bmp:Bitmap = new Bitmap(_data);
			bmp.x -= bmp.width * .5;
			bmp.y -= bmp.height * .5;
 
			_spiro = new Sprite();
			_spiro.addChild(bmp);
			_spiro.x = _xoffset;
			_spiro.y = _yoffset;
			addChild(_spiro);
 
			initUI();
 
			addEventListener(Event.ENTER_FRAME, frameHandler);
		}
 
		private function initUI():void {
			largeRadiusSlider = new Slider("horizontal", this, 440, 10, setLargeRadius);
			largeRadiusSlider.minimum = 1;
			largeRadiusSlider.maximum = 100;
 
			smallRadiusSlider = new Slider("horizontal", this, 440, 25, setSmallRadius);
			smallRadiusSlider.minimum = 1;
			smallRadiusSlider.maximum = 100;
 
			cOffsetSlider = new Slider("horizontal", this, 440, 40, setCircleOffset);
			cOffsetSlider.minimum = 1;
			cOffsetSlider.maximum = 100;
 
			var outsideCB:CheckBox = new CheckBox(this, 440, 55, "Draw outside", outsideHandler);
 
			var colorSelector:OBO_ColorPicker = new OBO_ColorPicker(20, 20, 0xFF00FF);
			colorSelector.x = 440;
			colorSelector.y = 70;
			colorSelector.addEventListener(ColorEvent.COLOR_SELECT, onColor);
			addChild(colorSelector);
 
			var drawButton:PushButton = new PushButton(this, 440, 95, "Draw", drawDesign);
 
			var randButton:PushButton = new PushButton(this, 440, 120, "Randomize!", mixItup);
		}
 
		private function setLargeRadius(event:Event):void {
			R = Math.round(event.currentTarget.value); 
		}
 
		private function setSmallRadius(event:Event):void {
			r = Math.round(event.currentTarget.value); 
		}
 
		private function setCircleOffset(event:Event):void {
			O = Math.round(event.currentTarget.value); 
		}
 
		private function outsideHandler(event:MouseEvent):void {
			_outside = event.currentTarget.selected;
		}
 
		private function onColor(event:ColorEvent):void {
			setColor(event.color);
		}
 
		private function mixItup(event:MouseEvent):void {
			R = randRange(100, 1);
			largeRadiusSlider.value = R;
 
			r = randRange(100, 1);
			smallRadiusSlider.value = r;
 
			O = randRange(100, 0);
			cOffsetSlider.value = O;
 
			drawDesign(null);
		}
 
		/**
		 * change 24 bit color to 32 bit
		 */
		private function setColor(color:uint):void {
			var a:uint = 0xFF;
			var r:uint = color >> 16;
			var g:uint = color >> 8 & 0xFF;
			var b:uint = color & 0xFF;
 
			_color = a << 24 | r << 16 | g << 8 | b;
		}
 
		private function drawDesign(event:MouseEvent):void {
 
			/*
				Where:
				R = radius of fixed circle
				r = radius of moving circle
				O = offset of "pen point" in moving circle
 
					drawing OUTSIDE of fixed circle
				x = (R+r)*cos(t) - O*cos(((R+r)/r)*t)
				y = (R+r)*sin(t) - O*sin(((R+r)/r)*t)
 
					drawing INSIDE of fixed circle
				x = (R-r)*cos(t) + O*cos(((R-r)/r)*t)
				y = (R-r)*sin(t) - O*sin(((R-r)/r)*t)
			*/
 
			var step:Number = .001 * Math.sqrt(r);
			var t:Number = 360;
 
			_data.lock();
 
			// clear previous
			_data.fillRect(_data.rect, 0x00000000);
 
			while (t > 0) {
				var xp:Number;
				var yp:Number;
 
				if(_outside) {
					xp = (R + r) * Math.cos(t) - O * Math.cos(((R + r) / r) * t);
					yp = (R + r) * Math.sin(t) - O * Math.sin(((R + r) / r) * t);
				} else {
					xp = (R - r) * Math.cos(t) + O * Math.cos(((R - r) / r) * t)
					yp = (R - r) * Math.sin(t) - O * Math.sin(((R - r) / r) * t)
				}
 
				xp += _xoffset;
				yp += _yoffset;
				_data.setPixel32(xp, yp, _color);
 
				t -= step;
			}
 
			_data.unlock();
		}
 
		private function randRange(max:Number, min:Number = 0, decimals:int = 0):Number {
			if (min > max) return NaN;
			var rand:Number = Math.random() * (max-min + Math.pow(10, -decimals)) + min;
			return int(rand * Math.pow(10, decimals)) / Math.pow(10, decimals);
		}
 
		/**
		 * Just some 3d rotation with ease
		 */
		private function frameHandler(event:Event):void {
			var rx:Number = ((stage.mouseX / stage.stageWidth) - .5) * 2;
			var ry:Number = ((stage.mouseY / stage.stageHeight) - .5) * 2;
			var ytr:Number = rx * 30;
			var xtr:Number = ry * 30;
			_spiro.rotationY += (-ytr - _spiro.rotationY) / 10;
			_spiro.rotationX += (xtr - _spiro.rotationX) / 10;
		}
	}
}

3 Comments »

  1. lee says:

    I wish I was as fluent in coding as you to do that so easily.

  2. [...] Spirography. This entry was posted in lifestream. Bookmark the permalink. Comments are closed, but you can [...]

  3. Paul says:

    I think this is one of the coolest things I’ve ever seen on the internet. I’m fascinated with stuff like this, probably partly because I love things like math and fractals and patterns. Great job!

    Now, I’m extremely new to AS3. And I’m trying to make sense of code and syntax, but I’m not real sure about how to do things yet. I don’t know how to take your code above and make it work like your swf file above.

    Is there a complete download file with all the inluded folders? And better yet, is there a tutorial on how to take your coding above and implement it to a final product? Because, at my level, I just cannot figure it out. :/

RSS feed for comments on this post. / TrackBack URI

Leave a Reply

Devon O. Wolfgang

Technical Reviewer of “The Essential Guide to Flash CS4 AIR Development”

Contributing Author of “Flash AS3 for Interactive Agencies”

Senior Flash Engineer PopCap Games, International Ltd.

Portfolio

Santabot: A Unity3D Flash Game


All right, so a Christmas game like “Santabot vs. The Flying Saucers from Mars” may be a day late[...]

Magnify – a jQuery Plugin


Let me begin by saying right up front, I have not given up on Flash[...]

It’s a Starling Halloween


Getting some practice for the upcoming Zombie Apocalypse[...]

Getting Started with Proscenium

So I had a chance this weekend to sit down and play around with Adobe’s new 3D framework for Stage3D, Proscenium, and thought I’d share a few of the results (a word of caution, there are no preloaders for any of the examples and may load a bit slowly). The first shows some reflections and [...]

Particle Editor for Starling Framework

An in-browser particle editor for the Starling 2D Framework for Flash Player 11.

So Long and Thanks for all the Flash on the Beach

So, another Flash on the Beach has just has just drawn to a close[...]

Game Development Tips from the Trenches of PopCap

Well, this is a post that’s a bit overdue, but, thanks to a well timed bank holiday, I finally had the opportunity to sit down and type up what I’ve been meaning to for some time now…

Old Skool Demoscene FX as 3D Textures

Many moons ago, I got the idea to create some demoscene plane deformation effects in Flash based on the formulas found here: http://www.iquilezles.org/www/articles/deform/deform.htm. I posted my less than desired results up on wonderfl. Thankfully, fellow wonderfl user, Hasufel, forked my attempt and optimized the hell out of it coming up with this. Well, today, for no [...]

Making The Gaming Scene (A Change in Careers)

And for my second blog post of the day, a much more personal note. After about three years of working with, what I would consider as objectively as possible, the best digital agency in Ireland, vStream Digital Media, I have made the immense career decision to leave the agency world and enter the arena of [...]

Feeling Lucky?

Images to dice, kick ascii style [...]

Adventures in Playbook Land

Adventures in Playbook Land


Now that the ordeal is over, I thought I’d take the time to sit down and share my account of what it was like to develop a Blackberry Playbook application using the Adobe Flex SDK[...]

Flash

Draw it for Me

So many ideas – so little time….

Kinect Application Running in Dublin

So, on Friday I just wrapped up our latest project at vStream Digital Media, a Kinect powered flipbook that lets users flip through the hand written notebooks of Philip Lynott of Thin Lizzy. The app uses OpenNI, runs in Adobe AIR and is currently on display at a pretty bitchin’ Phil Lynott exhibition running in [...]

Beach Ball Kinect Party

So we finally got a Kinect camera hooked up to a pc at work and, while it doesn’t seem to be legal to use it for commercial projects (but, hey, I’m no lawyer), the boss asked me to get it figured out and come up with some ideas just in case it would be feasible [...]

Facebook and Flash – A Book Review

Now, I should begin by saying I absolutely hate building Facebook applications. And I build a lot of them at work. Every time I get the word from above that we’re doing another FB app, I just groan – both inwardly and out. It’s become a running joke of the office. Why do I dislike [...]

Multitouch Fluid Dynamics with AIR for Android and RTMFP

The other day I was having some fun playing around with Eugene Zatepyakin’s (aka @inspirit) FluidSolverHD (Actionscript port of C++ fluid dynamics library, MSAFluid. Or is MSAFluid, the processing/java port of the C++ library? In any case it’s a very cool fluid dynamics thingamabob – the HD version using Alchemy). After a bit of tinkering, [...]