Log in to post a comment.

const length = 15;     // min=1 max=100 step=1
const lengthRatio = .5;   // min=0 max=1 step=.1
const thickness = 5;       // min=1 max=10 step=1
const thicknessRatio = .6 // min=0 max=1 step=.1
const generationLimit = 4 // min=1 max=20 step=1
const iterationLimit = 30 // min=1 max=30 step=1
const invForkProbPerGen = .70 // min=.50 max=1 step=.001
const invForkProbPerIter = .87 // min=.50 max=1 step=.01
const circleProb = .1 //min=0 max=.5 step=.01

// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(.8);

const forkProbPerGen = 1 - invForkProbPerGen;
const forkProbPerIter = 1 - invForkProbPerIter;

// Global code will be evaluated once.
class Fork {
    constructor(branches, state, iteration, generation) {
        this.state = state;
        this.iteration = iteration | 0;
        this.generation = generation | 0;
        this.branches = branches;
    }
    generate(t) {
        this.state.restore(t);
        
        if(this.generation < generationLimit) {
            let gts = new TurtleState(t);
            gts.thickness *= thicknessRatio;
            (new Fork(3, gts, 0, this.generation + 1)).generate(t);        
        }
        
        let bLength = length * Math.pow(lengthRatio, this.generation);
        
        this.state.restore(t);
        if(Math.random() < circleProb) {
            t.seth(270);
            let circleDivisions = 3;
            t.jump(t.x() - ((bLength * .25) / circleDivisions), t.y());
            for(let d = 1; d < 4; d++) {
                let show = Math.random() < .5;
                let start = ((bLength * .25) / circleDivisions) * d;
                let max = ((bLength * .25) / circleDivisions) * (d + 1);
                for(let r = start; r < max; r += .25) {
                    t.jump(t.x() -.25, t.y());
                    if(show) {
                        t.circle(r);
                    }
                }
            }
            this.state.restore(t);
        }
        
        this.state.restore(t);
        t.left(60);
        let ts = new TurtleState(t);
        let forks = [];
        for(let i = 0; i < this.branches; i++) {
            if(this.iteration < iterationLimit && Math.random() > (forkProbPerGen * this.generation) + (forkProbPerIter * this.iteration)) {
                t.forward(bLength);
                forks.push(new Fork(2, new TurtleState(t), this.iteration + 1, this.generation));
            }
            ts.restore(t);
            t.right(120);
            ts.store(t);
        }
        forks.forEach(function(f) {
            if(f.iteration < 7) {
              f.generate(t); 
            }
        });
    }
}

class TurtleState {
    constructor(...args) {
        this.store(...args);
    }
    store(turtle, x, y, isdown, thickness) {
        this.h = turtle;
        this.x = x;
        this.y = y;
        this.isdown = isdown;
        this.thickness = thickness
        if(x === undefined) {
            this.h = turtle.h();
            this.x = turtle.x();
            this.y = turtle.y();
            this.isdown = turtle.isdown();
            this.thickness = turtle.thickness;
        }
    }
    restore(turtle) {
        turtle.seth(this.h);
        turtle.jump(this.x, this.y);
        this.isdown? turtle.down(): turtle.up();
        turtle.thickness = this.thickness;
    }
    clone() {
        return new TurtleState(this.h, [this.x, this.y], this.isdown, this.thickness);
    }
}

// The walk function will be called until it returns false.
function walk(i) {
    const turtle = new Slowbro(); turtle.thickness = thickness;

    const s = new TurtleState(turtle);
    let f = new Fork(3, s);

    f.generate(turtle);
    return false;
    
    turtle.forward(100);
    s.restore(turtle);
    turtle.right(144);
    s.save(turtle);
    return i < 4;
}

////////////////////////////////////////////////////////////////
// Slowbro utility code. Created by Lionel Lemarie 2021
// Based on Slowpoke, which removes most duplicate lines
// Slowbro adds optional thickness to the lines
////////////////////////////////////////////////////////////////
function Slowbro(x, y) {
    const linesDrawn = {};
    class Slowbro extends Turtle {
        constructor(x, y) {
        	super(x, y);
        	this.thickness = 1; this.offset = 0.2;
			this.slowpoke_skip = this.slowpoke_draw = 0;
        }
        goto(x, y) {
            if (Array.isArray(x)) { y = x[1]; x = x[0]; }
            const ox = this.x(), oy = this.y();
            if (this.isdown()) {
                const p = [x, y], o = [ox, oy];
                const h1 = o[0].toFixed(2) + '_' + p[0].toFixed(2) + o[1].toFixed(2) + '_' + p[1].toFixed(2);
                const h2 = p[0].toFixed(2) + '_' + o[0].toFixed(2) + p[1].toFixed(2) + '_' + o[1].toFixed(2);
                if (linesDrawn[h1] || linesDrawn[h2]) {
                    super.up(); super.goto(p); super.down();
                    this.slowpoke_skip++;
                    return;
                }
                linesDrawn[h1] = linesDrawn[h2] = true;
                this.slowpoke_draw++;

            	for (var dx = this.thickness-1; dx >=0 ; dx--) {
            		for (var dy = this.thickness-1; dy >= 0; dy--) {
            			if (!dx && !dy) continue;
            			super.goto( x + dx * this.offset,  y + dy * this.offset);
            			super.goto(ox + dx * this.offset, oy + dy * this.offset);
            		}
            	}
            } 
            super.goto(x, y);
        }
    }
    return new Slowbro(x, y);
}