/*------------------------------------------------------------ Implementation of an L-system renderer. Author: Michael Goldwasser System files are specified using format: ------- AxiomString RuleString1 RuleString2 ... ------- Configuration files are specified using format: ------- SystemFilename numberGenerations turnAngle colorR colorG colorB ------- This code is indirectly based on the object-oriented implementation given in Chapter 8 of the book: Processing: Creative Coding and Generative Art in Processing 2 By Ira Greenberg, Dianna Xu, and Deepak Kumar Friends of Ed (An APress Company), 2013 ISBN-13 978-1430244646 Copyright (c) 2013, Friends of Ed (An Apress Company) All rights reserved. ------------------------------------------------------------*/ String input = "plant7.txt"; float replaceProb = 0.9; // probability of applying a matching rule class System { String axiom; String[] rules; }; class Configuration { System sys; int generations; float turnAngle; // rotation represented by + or - color strokeColor; // stroke color }; class Line { float x0,y0,x1,y1; }; // Representing the current position and direction class Heading { float x, y, theta; }; Configuration config; void setup() { size(600,600); background(255); config = loadConfiguration("configs/"+input); render(config); } void render(Configuration config) { String result = generate(config.sys, config.generations); Line[] lines = computeLines(config, result); stroke(config.strokeColor); renderLines(lines); } Line[] computeLines(Configuration config, String result) { int totalLines = 0; for (int j=0; j < result.length (); j++) { if (result.charAt(j) == 'F') { totalLines++; } } println("Total lines: " + totalLines); Heading[] history = new Heading[100]; int historySize = 0; Line[] lines = new Line[totalLines]; int linesThusFar = 0; Heading current = new Heading(); current.x = current.y = 0; current.theta = 3*PI/2; for (int j=0; j < result.length (); j++) { switch (result.charAt(j)) { case 'F': Line step = new Line(); step.x0 = current.x; step.y0 = current.y; current.x += cos(current.theta); current.y += sin(current.theta); step.x1 = current.x; step.y1 = current.y; lines[linesThusFar] = step; linesThusFar++; break; case '+': current.theta += config.turnAngle; break; case '-': current.theta -= config.turnAngle; break; case '[': Heading save = new Heading(); save.x = current.x; save.y = current.y; save.theta = current.theta; history[historySize] = save; historySize++; break; case ']': historySize--; current = history[historySize]; break; // otherwise, ignore the character } } return lines; } void renderLines(Line[] lines) { // first compute bounding box float minX, maxX, minY, maxY; minX = maxX = lines[0].x0; minY = maxY = lines[0].y0; for (int j=0; j < lines.length; j++) { minX = min(minX, min(lines[j].x0, lines[j].x1)); maxX = max(maxX, max(lines[j].x0, lines[j].x1)); minY = min(minY, min(lines[j].y0, lines[j].y1)); maxY = max(maxY, max(lines[j].y0, lines[j].y1)); } float scaleFactor = 0.9*min(width/(maxX-minX), height/(maxY-minY)); translate(width/2, height/2); scale(scaleFactor, scaleFactor); translate(-(minX+maxX)/2, -(minY+maxY)/2); strokeWeight(1/scaleFactor); for (int j=0; j < lines.length; j++) { line(lines[j].x0, lines[j].y0, lines[j].x1, lines[j].y1); } } // Return the string that results from the given L-system String generate(System sys, int generations) { String result = sys.axiom; for (int g=0; g < generations; g++) { String next = ""; for (int j=0; j < result.length (); j++) { if (random(1) < replaceProb) { next += findReplacement(result.charAt(j), sys.rules); } else { next += result.charAt(j); } } result = next; } return result; } // Return replacement for character c, if rule exists, otherwise c itself String findReplacement(char c, String[] rules) { for (int j=0; j < rules.length; j++) { if (rules[j].charAt(0) == c) { return rules[j].substring(1); } } return "" + c; } // Some utility functions for reading systems and configurations from files System loadSystem(String filename) { String[] raw = loadStrings("systems/"+filename); System sys = new System(); sys.axiom = raw[0]; sys.rules = subset(raw, 1); return sys; } Configuration loadConfiguration(String filename) { Configuration config = new Configuration(); String[] lines = loadStrings(filename); config.sys = loadSystem(lines[0]); config.generations = int(lines[1]); config.turnAngle = radians(float(lines[2])); float[] pieces = float(splitTokens(lines[3])); config.strokeColor = color(pieces[0], pieces[1], pieces[2]); return config; } void draw() {} void mousePressed() { render(config); }