AS3 spirograph with source code
I just worked a bit more on my AS3 spirograph tonight. This is the updated version with the full source code:
Demo:
Full source code:
Please note that you need the Hype framework and Minimalcomps to compile this.
package { import hype.extended.util.ContextSavePNG; import hype.framework.display.BitmapCanvas; import com.bit101.components.CheckBox; import com.bit101.components.ColorChooser; import com.bit101.components.Label; import com.bit101.components.PushButton; import com.bit101.components.Slider; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; /** * @author Jankees van Woezik */ public class Spirograph extends Sprite { private var t : Number = 0; private var _canvas : BitmapCanvas; private var _original : Sprite; private var _isVirgin : Boolean = true; private var _gearsVisual : Sprite; // minimalcomps.com private var _speedSlider : Slider; private var _outerGearSlider : Slider; private var _innerGearSlider : Slider; private var _offsetPenSlider : Slider; private var _penPointShape : Ball; private var _drawAnimatedCheckbox : CheckBox; private var _showWheelsCheckbox : CheckBox; private var _colorPicker : ColorChooser; public function Spirograph() { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; stage.addEventListener(Event.RESIZE, handleStageResize); // gradient background (thanks to zwartekoffie.com) addChild(new SiteBackground()); _original = new Sprite(); _canvas = new BitmapCanvas(stage.stageWidth, stage.stageHeight, true); _canvas.target = _original; addChild(_canvas); _gearsVisual = new Sprite(); addChild(_gearsVisual); var controlY : Number = 10; var randomize : PushButton = new PushButton(this, 10, controlY, "Randomize settings", randomSettings); randomize.width = 172; new Label(this, 10, controlY += 30, "speed"); _speedSlider = new Slider(Slider.HORIZONTAL, this, 80, controlY + 4); _speedSlider.minimum = 1; _speedSlider.value = 10; _speedSlider.maximum = 20; new Label(this, 10, controlY += 15, "outer gear (R)"); _outerGearSlider = new Slider(Slider.HORIZONTAL, this, 80, controlY + 4, change); _outerGearSlider.minimum = 1; _outerGearSlider.value = 100; _outerGearSlider.maximum = 200; new Label(this, 10, controlY += 15, "inner gear (r)"); _innerGearSlider = new Slider(Slider.HORIZONTAL, this, 80, controlY + 4, change); _innerGearSlider.minimum = 1; _innerGearSlider.value = 50; _innerGearSlider.maximum = 200; new Label(this, 10, controlY += 15, "offset pen (p)"); _offsetPenSlider = new Slider(Slider.HORIZONTAL, this, 80, controlY + 4, change); _offsetPenSlider.minimum = 1; _offsetPenSlider.value = 6; _offsetPenSlider.maximum = 200; new Label(this, 10, controlY += 19, "line color"); _colorPicker = new ColorChooser(this, 80, controlY, 0x59c7b7); _drawAnimatedCheckbox = new CheckBox(this, 12, controlY += 30, "show animation", toggleAnimated); _drawAnimatedCheckbox.selected = true; _showWheelsCheckbox = new CheckBox(this, 12, controlY += 15, "show gears"); var explanation : Label = new Label(this, 10, controlY += 20, ""); explanation.text = "Formula\nx(t)=(R-r)*cos(t) + p*cos((R-r)*t/r) \ny(t)=(R-r)*sin(t) - p*sin((R-r)*t/r)"; new ContextSavePNG(_canvas, stage); _penPointShape = new Ball(_colorPicker.value); addChild(_penPointShape); addEventListener(Event.ENTER_FRAME, handleEnterFrame); randomSettings(); } private function handleEnterFrame(event : Event) : void { if (_drawAnimatedCheckbox.selected) renderTick(); } private function handleStageResize(event : Event) : void { removeChild(_canvas); _canvas.clear(); _canvas = null; _canvas = new BitmapCanvas(stage.stageWidth, stage.stageHeight, true, 0xffffff); _canvas.target = _original; addChildAt(_canvas, 1); clearCanvas(); } private function toggleAnimated(e : Event) : void { clearCanvas(); if (!_drawAnimatedCheckbox.selected) drawStatic(); } private function change(e : Event) : void { clearCanvas(); if (_drawAnimatedCheckbox) if (!_drawAnimatedCheckbox.selected) drawStatic(); } private function drawStatic() : void { var leni : uint = 6000; for (var i : uint = 0; i < leni; i++) { renderTick(); } } private function clearCanvas() : void { _canvas.clear(); _isVirgin = true; } private function randomSettings(e : Event = null) : void { _outerGearSlider.value = Random.between(_outerGearSlider.maximum - 50, _outerGearSlider.maximum); _innerGearSlider.value = Random.between(_innerGearSlider.minimum, _outerGearSlider.value); _offsetPenSlider.value = Random.between(_offsetPenSlider.minimum, _innerGearSlider.value); } private function renderTick() : void { t += (_speedSlider.value / 100); if (_offsetPenSlider.value > _innerGearSlider.value) { _offsetPenSlider.value = _innerGearSlider.value; } if (_innerGearSlider.value > _outerGearSlider.value) { _innerGearSlider.value = _outerGearSlider.value; } _original.graphics.lineStyle(1, _colorPicker.value, 1); _original.graphics.moveTo(_penPointShape.x, _penPointShape.y); var a : Number = _outerGearSlider.value; var b : Number = _innerGearSlider.value; var o : Number = _offsetPenSlider.value; var newx : Number; var newy : Number; // http://en.wikipedia.org/wiki/Spirograph // as found on http://linuxgazette.net/133/luana.html // // x(t)=(R-r)*cos(t) + p*cos((R-r)*t/r) // y(t)=(R-r)*sin(t) - p*sin((R-r)*t/r) // // R, the radius of the fixed circle; // r, the radius of the moving circle; // p, the distance from the pen to the moving circle center. // O, The center of the fixed circle newx = (a - b) * Math.cos(t) + o * Math.cos((a - b) * t / b); newy = (a - b) * Math.sin(t) + o * Math.sin((a - b) * t / b); _penPointShape.x = newx + stage.stageWidth / 2; _penPointShape.y = newy + stage.stageHeight / 2; _penPointShape.color = _colorPicker.value; if (!_isVirgin) { _original.graphics.lineTo(_penPointShape.x, _penPointShape.y); } _gearsVisual.graphics.clear(); if (_showWheelsCheckbox.selected) { var centerInnerCircleX : Number = stage.stageWidth / 2 + (Math.cos(t) * (a - b)); var centerInnerCircleY : Number = stage.stageHeight / 2 + (Math.sin(t) * (a - b)); _gearsVisual.graphics.beginFill(0xfffe8e, 0.3); _gearsVisual.graphics.drawCircle(stage.stageWidth / 2, stage.stageHeight / 2, a); _gearsVisual.graphics.beginFill(0xfe6cc7, 0.3); _gearsVisual.graphics.drawCircle(centerInnerCircleX, centerInnerCircleY, b); _gearsVisual.graphics.beginFill(0xfe6cc7, 0.3); _gearsVisual.graphics.drawCircle(centerInnerCircleX, centerInnerCircleY, 3); _gearsVisual.graphics.endFill(); } _canvas.capture(true); _original.graphics.clear(); _isVirgin = false; } } } import nl.inlet42.utils.view.StageProvider; import com.epologee.util.ColorUtils; import flash.display.GradientType; import flash.display.Sprite; import flash.events.Event; import flash.geom.Matrix; class Ball extends Sprite { private var _color : uint; public function Ball(inColor : uint) { color = inColor; } public function set color(color : uint) : void { if (_color == color) return; _color = color; with(graphics) { clear(); beginFill(_color, 1); drawCircle(0, 0, 3); endFill(); } } } class SiteBackground extends Sprite { public function SiteBackground() { if (stage) { initUI(null); } else { addEventListener(Event.ADDED_TO_STAGE, initUI, false, 0, true); } } private function initUI(event : Event) : void { removeEventListener(Event.ADDED_TO_STAGE, initUI); stage.addEventListener(Event.RESIZE, handleResize); handleResize(); } private function handleResize(event : Event = null) : void { graphics.clear(); var colors : Array = [0xfdfdfb, 0xeae7e0]; var alphas : Array = [1, 1]; var ratios : Array = [0, 255]; var matrix : Matrix = new Matrix(); matrix.createGradientBox(stage.stageWidth, stage.stageWidth, stage.stageWidth / 2 - stage.stageWidth / 2, stage.stageHeight / 2 - stage.stageWidth / 2); graphics.beginGradientFill(GradientType.RADIAL, colors, alphas, ratios, matrix); graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight); } } class Random { public static function between(inFrom : Number, inTo : Number, inFloat : Boolean = false) : Number { var from : Number = (inFrom < inTo) ? inFrom : inTo; var to : Number = (inTo > inFrom) ? inTo : inFrom; return inFloat ? (from + Math.random() * Math.abs(to - from)) : (from + Math.round(Math.random() * Math.abs(to - from))); } }
THE COMMENTS:
What Patrick Pietens said 13 hours later:
Why not jump in the HTML5 bandwagon and use the canvas object to draw the spirograph?
What jankees said 13 hours later:
I was actually already working on that… :-)