I just worked a bit more on my AS3 spirograph tonight. This is the updated version with the 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)));
}
}
Like this post? Follow me at @jankeesvw on Twitter