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.
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):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
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; } } } |
I wish I was as fluent in coding as you to do that so easily.
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. :/