Repeat: RECURSIVE TREE
Within the domain of image making, recursion involves drawing a group of related, self-similar shapes. Treelike forms are a clear example — each branch spawns many smaller branches, which in turn spawn more branches. As a simple example, draw the letter Y on a sheet of paper. Now draw two smaller Ys sprouting from the top of each branch. After repeating this process a few times, the number of Ys drawn at each step has increased from 1 to 2, to 4, 8, 16, 32, 64, and so on.
While this type of tree makes a predictable shape, adding a small amount of randomness to the line lengths and number of branches can yield more organic forms. These treelike forms were created by drawing one circle at a time. Starting at the base, each circle was slightly rotated and scaled relative to the one before it. At random intervals during the growth, two smaller branches sprout to form new growths. This continues until the circles reach the minimum size set by the user.
/**
* Repeat: Recursive Tree
* from Form+Code in Design, Art, and Architecture
* by Casey Reas, Chandler McWilliams, and LUST
* Princeton Architectural Press, 2010
* ISBN 9781568989372
*
* This program is based on Context Free program
* "Foggy Tree by Chris Coyne:
* http://www.contextfreeart.org/gallery/view.php?id=4
*
* This code was written for Processing 1.2+
* Get Processing at http://www.processing.org/download
*/
float dotSize = 9;
float angleOffsetA;
float angleOffsetB;
void setup() {
size(900, 600);
noStroke();
fill(0);
smooth();
frameRate(1); // Redraw the tree once a second
angleOffsetA = radians(1.5); // Convert 1.5 degrees to radians
angleOffsetB = radians(50); // Convert 50 degrees to radians
}
void draw() {
background(255); // White background
translate(width/2, height); // Move to the center, bottom of the screen
seed1(dotSize, radians(270), 0, 0); // Start the tree
}
void seed1(float dotSize, float angle, float x, float y) {
if (dotSize > 1.0) {
// Create a random numbers between 0 and 1
float r = random(0, 1.0);
// 98% chance this will happen
if (r > 0.02) {
ellipse(x, y, dotSize, dotSize);
float newx = x + cos(angle) * dotSize;
float newy = y + sin(angle) * dotSize;
seed1(dotSize * 0.99, angle - angleOffsetA, newx, newy);
}
// 02% chance this will happen
else {
ellipse(x, y, dotSize, dotSize);
float newx = x + cos(angle);
float newy = y + sin(angle);
seed2(dotSize * 0.99, angle + angleOffsetA, newx, newy);
seed1(dotSize * 0.60, angle + angleOffsetB, newx, newy);
seed2(dotSize * 0.50, angle - angleOffsetB, newx, newy);
}
}
}
void seed2(float dotSize, float angle, float x, float y) {
if (dotSize > 1.0) {
// Create a random numbers between 0 and 1
float r = random(0, 1.0);
// 95% chance this will happen
if (r > 0.05) {
ellipse(x, y, dotSize, dotSize);
float newx = x + cos(angle) * dotSize;
float newy = y + sin(angle) * dotSize;
seed2(dotSize * 0.99, angle + angleOffsetA, newx, newy);
}
// 05% chance this will happen
else {
ellipse(x, y, dotSize, dotSize);
float newx = x + cos(angle);
float newy = y + sin(angle);
seed1(dotSize * 0.99, angle + angleOffsetA, newx, newy);
seed2(dotSize * 0.60, angle + angleOffsetB, newx, newy);
seed1(dotSize * 0.50, angle - angleOffsetB, newx, newy);
}
}
}
Contributed Examples
-
Go
/** * Repeat: Recursive Tree from Form+Code in Design, Art, and Architecture * implemented in Go by Anthony Starks * * Requires the go programming language available at http://golang.org * and the SVGo libraray available at http://github.com/ajstarks/svgo * * Create SVG file from command line with: make * * For more information about Form+Code visit http://formandcode.com */ package main import "github.com/ajstarks/svgo" "fmt" "rand" "math" "time" "os" ) var width = 900 height = 600 canvas = svg. os.Stdout) ) var angleOffsetA =1.5) // Convert 1.5 degrees to radians angleOffsetB =50) // Convert 50 degrees to radians )
-
ActionScript
/** * Repeat: Recursive Tree from Form+Code in Design, Art, and Architecture * implemented in ActionScript 3 by João Goncalves <http://joaogoncalves.net> * * Run from the command line with: mxmlc Repeat_RecursiveTree.as * Mxmlc is available free as part of the Flex SDK: http://opensource.adobe.com/wiki/display/flexsdk/Flex+SDK * * For more information about Form+Code visit http://formandcode.com */ package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.text.TextField; import flash.text.TextFieldAutoSize; [SWF(width='900', height='600', backgroundColor='#ffffff', framerate=1)] public class Repeat_RecursiveTree extends Sprite { private var dotSize:Number = 9; private var angleOffsetA:Number; private var angleOffsetB:Number; private var instructions:TextField; public function Repeat_RecursiveTree():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point instructions = new TextField(); instructions.text = "Click to generate a Tree, Double-Click to clear the stage."; instructions.autoSize = TextFieldAutoSize.LEFT; addChild(instructions); angleOffsetA = degToRad(1.5); angleOffsetB = degToRad(50); stage.doubleClickEnabled = true; stage.addEventListener(MouseEvent.CLICK, generateThree); stage.addEventListener(MouseEvent.DOUBLE_CLICK, clearStage); } private function clearStage(e:MouseEvent):void { this.graphics.clear(); } private function generateThree(e:MouseEvent):void { this.graphics.beginFill(0x000000); seed1(dotSize, degToRad(270), mouseX, mouseY); } private function seed1(dotSize:Number, angle:Number, xpos:Number, ypos:Number):void { var newX:Number; var newY:Number; if (dotSize>1) { // create random number between 0 and 1 var r:Number = Math.random(); // 98% chance this will happen if (r> 0.02) { this.graphics.drawEllipse(xpos, ypos, dotSize, dotSize); newX = xpos + Math.cos(angle) * dotSize; newY = ypos + Math.sin(angle) * dotSize; seed1(dotSize * 0.99, angle- angleOffsetA, newX, newY); } // 2% chance this will happen else { this.graphics.drawEllipse(xpos, ypos, dotSize, dotSize); newX = xpos + Math.cos(angle); newY = ypos + Math.sin(angle); seed2(dotSize * 0.99, angle + angleOffsetA, newX, newY); seed1(dotSize * 0.60, angle + angleOffsetB, newX, newY); seed2(dotSize * 0.50, angle-angleOffsetB, newX, newY); } } } private function seed2(dotSize:Number, angle:Number, xpos:Number, ypos:Number):void { var newX:Number; var newY:Number; if (dotSize>1) { // create random number between 0 and 1 var r:Number = Math.random(); // 95% chances this will happen if (r> 0.05) { this.graphics.drawEllipse(xpos, ypos, dotSize, dotSize); newX = xpos + Math.cos(angle) * dotSize; newY = ypos + Math.sin(angle) * dotSize; seed2(dotSize * 0.99, angle+ angleOffsetA, newX, newY); } // 5% chance this will happen else { this.graphics.drawEllipse(xpos, ypos, dotSize, dotSize); newX = xpos + Math.cos(angle); newY = ypos + Math.sin(angle); seed1(dotSize * 0.99, angle + angleOffsetA, newX, newY); seed2(dotSize * 0.60, angle + angleOffsetB, newX, newY); seed1(dotSize * 0.50, angle-angleOffsetB, newX, newY); } } } private function degToRad(deg:Number):Number { return deg * (Math.PI / 180); } } }
-
OpenFrameworks
/** * Repeat: Recursive Tree from Form+Code in Design, Art, and Architecture * implemented in OpenFrameworks by Anthony Stellato <http://rabbitattack.com/> * * Requires OpenFrameworks available at http://openframeworks.cc/ * * For more information about Form+Code visit http://formandcode.com */ // =========================================================== // - Repeat_RecursiveTree.h // =========================================================== #include "ofMain.h" class testApp : public ofBaseApp{ public: void setup(); void draw(); void seed1(float dotSize, float angle, float x, float y); void seed2(float dotSize, float angle, float x, float y); float dotSize, angleOffsetA, angleOffsetB; }; // =========================================================== // - Repeat_RecursiveTree.cpp // =========================================================== #include "Repeat_RecursiveTree.h" //-------------------------------------------------------------- void testApp::setup(){ ofSetFrameRate(1); ofBackground(255, 255, 255); ofEnableSmoothing(); dotSize = 9.0f; angleOffsetA = ofDegToRad(1.5); angleOffsetB = ofDegToRad(50); } //-------------------------------------------------------------- void testApp::draw(){ ofBackground(255, 255, 255); ofSetColor(0, 0, 0); ofTranslate(ofGetWidth()/2, ofGetHeight(), 0); seed1(dotSize, ofDegToRad(270), 0, 0); } //-------------------------------------------------------------- void testApp::seed1(float dotSize, float angle, float x, float y){ if(dotSize > 1.0f){ float r = ofRandomuf(); if(r > 0.02f){ ofCircle(x, y, dotSize); float newx = x + cos(angle) * dotSize; float newy = y + sin(angle) * dotSize; seed1(dotSize * 0.99f, angle - angleOffsetA, newx, newy); } else { ofCircle(x, y, dotSize); float newx = x + cos(angle); float newy = y + sin(angle); seed2(dotSize * 0.99f, angle + angleOffsetA, newx, newy); seed1(dotSize * 0.6f, angle + angleOffsetB, newx, newy); seed2(dotSize * 0.5f, angle - angleOffsetB, newx, newy); } } } //-------------------------------------------------------------- void testApp::seed2(float dotSize, float angle, float x, float y){ if(dotSize > 1.0f){ float r = ofRandomuf(); if(r > 0.05f){ ofCircle(x, y, dotSize); float newx = x + cos(angle) * dotSize; float newy = y + sin(angle) * dotSize; seed2(dotSize * 0.99f, angle + angleOffsetA, newx, newy); } else { ofCircle(x, y, dotSize); float newx = x + cos(angle); float newy = y + sin(angle); seed1(dotSize * 0.99f, angle + angleOffsetA, newx, newy); seed2(dotSize * 0.6f, angle + angleOffsetB, newx, newy); seed1(dotSize * 0.5f, angle - angleOffsetB, newx, newy); } } }
-
Cinder
/** * Repeat: Recursive Tree from Form+Code in Art, Design, and Architecture * implemented in C++ by Patrick Tierney <http://ptierney.com> * * Requires Cinder 0.8.2 available at http://libcinder.org * * Project files are located at https://github.com/hlp/form-and-code * * For more information about Form+Code visit http://formandcode.com */ #include "cinder/app/AppBasic.h" #include "cinder/CinderMath.h" #include "cinder/Rand.h" class Repeat_RecursiveTree : public ci::app::AppBasic { public: void prepareSettings(Settings* settings); void setup(); void draw(); void seed1(float dotSize, float angle, float x, float y); void seed2(float dotSize, float angle, float x, float y); private: float dotSize; float angleOffsetA; float angleOffsetB; ci::Rand rand; }; void Repeat_RecursiveTree::prepareSettings(Settings* settings) { dotSize = 9; settings->setWindowSize(900, 600); settings->setFrameRate(1.0f); // Redraw the tree once a second } void Repeat_RecursiveTree::setup() { ci::gl::color(ci::Color::black()); glEnable(GL_SMOOTH); angleOffsetA = ci::toRadians(1.5f); angleOffsetB = ci::toRadians(50.0f); rand.randomize(); } void Repeat_RecursiveTree::draw() { ci::gl::setMatricesWindow(getWindowSize()); ci::gl::clear(ci::Color::white()); // White background ci::gl::translate(ci::Vec2f(getWindowWidth()/2, getWindowHeight())); // Move to the center, bottom of the screen seed1(dotSize, ci::toRadians(270.0f), 0, 0); // Start the tree } void Repeat_RecursiveTree::seed1(float dotSize, float angle, float x, float y) { if (dotSize > 1.0) { // Create a random numbers between 0 and 1 float r = rand.randFloat(0, 1.0f); // 02% chance this will happen if (r > 0.02) { ci::gl::drawSolidCircle(ci::Vec2f(x, y), dotSize/2); float newx = x + ci::math<float>::cos(angle) * dotSize; float newy = y + ci::math<float>::sin(angle) * dotSize; seed1(dotSize * 0.99, angle - angleOffsetA, newx, newy); } // 98% chance this will happen else { ci::gl::drawSolidCircle(ci::Vec2f(x, y), dotSize/2); float newx = x + ci::math<float>::cos(angle); float newy = y + ci::math<float>::sin(angle); seed2(dotSize * 0.99, angle + angleOffsetA, newx, newy); seed1(dotSize * 0.60, angle + angleOffsetB, newx, newy); seed2(dotSize * 0.50, angle - angleOffsetB, newx, newy); } } } void Repeat_RecursiveTree::seed2(float dotSize, float angle, float x, float y) { if (dotSize > 1.0) { // Create a random numbers between 0 and 1 float r = rand.randFloat(0, 1.0); // 5% chance this will happen if (r > 0.05) { ci::gl::drawSolidCircle(ci::Vec2f(x, y), dotSize/2); float newx = x + ci::math<float>::cos(angle) * dotSize; float newy = y + ci::math<float>::sin(angle) * dotSize; seed2(dotSize * 0.99, angle + angleOffsetA, newx, newy); } // 95% chance this will happen else { ci::gl::drawSolidCircle(ci::Vec2f(x, y), dotSize/2); float newx = x + ci::math<float>::cos(angle); float newy = y + ci::math<float>::sin(angle); seed1(dotSize * 0.99, angle + angleOffsetA, newx, newy); seed2(dotSize * 0.60, angle + angleOffsetB, newx, newy); seed1(dotSize * 0.50, angle - angleOffsetB, newx, newy); } } } CINDER_APP_BASIC(Repeat_RecursiveTree, ci::app::RendererGl)
We are looking for implementations of the code examples in other programming languages to post on the site. If you would like to submit a sample, or if you find a bug, please write to