Penrose Snowflake

penrose-snowflake.pde
/**
 * Penrose Snowflake L-System
 * by Geraldine Sarmiento.
 *
 * This example was based on Patrick Dwyer's L-System class.
 */

class LSystem {
public:
    int steps = 0;

    std::string axiom;
    std::string rule;
    std::string production;

    float startLength;
    float drawLength;
    float theta;

    int generations;

    LSystem() {
        axiom = "F";
        rule = "F+F-F";
        startLength = 90.0;
        theta = radians(120.0);
        reset();
    }

    void reset() {
        production = axiom;
        drawLength = startLength;
        generations = 0;
    }

    int getAge() {
        return generations;
    }

    void render() {
        translate(width / 2, height / 2);
        steps += 5;
        if (steps > (int)production.length()) {
            steps = production.length();
        }
        for (int i = 0; i < steps; i++) {
            char step = production[i];
            if (step == 'F') {
                rect(0, 0, -drawLength, -drawLength);
                noFill();
                translate(0, -drawLength);
            } else if (step == '+') {
                rotate(theta);
            } else if (step == '-') {
                rotate(-theta);
            } else if (step == '[') {
                pushMatrix();
            } else if (step == ']') {
                popMatrix();
            }
        }
    }

    void simulate(int gen) {
        while (getAge() < gen) {
            production = iterate(production, rule);
        }
    }

    virtual std::string iterate(std::string prod_, std::string rule_) {
        drawLength = drawLength * 0.6;
        generations++;
        std::string newProduction = prod_;
        std::string result = "";
        for (int i = 0; i < (int)newProduction.size(); i++) {
            if (newProduction[i] == 'F') result += rule_;
            else result += newProduction[i];
        }
        return result;
    }

    virtual ~LSystem() {}
};

class PenroseSnowflakeLSystem : public LSystem {
public:
    std::string ruleF;

    PenroseSnowflakeLSystem() {
        axiom = "F3-F3-F3-F3-F";
        ruleF = "F3-F3-F45-F++F3-F";
        startLength = 450.0;
        theta = radians(18);
        reset();
    }

    void useRule(std::string r_) { rule = r_; }
    void useAxiom(std::string a_) { axiom = a_; }
    void useLength(float l_) { startLength = l_; }
    void useTheta(float t_) { theta = radians(t_); }

    void reset() {
        production = axiom;
        drawLength = startLength;
        generations = 0;
    }

    int getAge() {
        return generations;
    }

    void render() {
        translate(width, height);
        int repeats = 1;

        steps += 3;
        if (steps > (int)production.length()) {
            steps = production.length();
        }

        for (int i = 0; i < steps; i++) {
            char step = production[i];
            if (step == 'F') {
                for (int j = 0; j < repeats; j++) {
                    line(0, 0, 0, -drawLength);
                    translate(0, -drawLength);
                }
                repeats = 1;
            } else if (step == '+') {
                for (int j = 0; j < repeats; j++) {
                    rotate(theta);
                }
                repeats = 1;
            } else if (step == '-') {
                for (int j = 0; j < repeats; j++) {
                    rotate(-theta);
                }
                repeats = 1;
            } else if (step == '[') {
                pushMatrix();
            } else if (step == ']') {
                popMatrix();
            } else if (step >= 48 && step <= 57) {
                repeats += step - 48;
            }
        }
    }

    std::string iterate(std::string prod_, std::string rule_) override {
        std::string newProduction = "";
        for (int i = 0; i < (int)prod_.size(); i++) {
            char step = prod_[i];
            if (step == 'F') {
                newProduction += ruleF;
            } else {
                newProduction += step;
            }
        }
        drawLength = drawLength * 0.4;
        generations++;
        return newProduction;
    }
};

PenroseSnowflakeLSystem ps;

void setup() {
    size(640, 360);
    stroke(255);
    noFill();
    ps = PenroseSnowflakeLSystem();
    ps.simulate(4);
}

void draw() {
    background(0);
    ps.render();
}