AlgorithmicLeaf

Kinda sorta leaf vein pattern based on the algorithm described in the following paper:

cp.eng.chula.ac.th/~…per/2002/cmm2002.pdf

Log in to post a comment.

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

// Define here a number of adjustable constants
const NUM_PARTICLES = 500; // The number of random particles to generate
const ROOT_POINT = [0, 120]; // The x,y coordinate of our goal point
const JOIN_DISTANCE = 1; // How close particles have to be before they merge
const MOVE_DISTANCE = 0.5;

// Using the algorithm defined in:
// https://www.cp.eng.chula.ac.th/~prabhas//paper/2002/cmm2002.pdf

// Global code will be evaluated once.
const turtle = new Turtle();
turtle.pendown();
var particles = [];

// Simple implementation of distance formula
function getDist(p0, p1){
    var dx = p1[0] - p0[0];
    var dy = p1[1] - p0[1];
    return Math.sqrt(dx**2 + dy**2);
}


// Simple function to create a unit vector
function normalize(vector, mag){
    // Pass in the magnitude/distance just so we don't have to calculate it twice
    return [vector[0]/mag, vector[1]/mag]
}

function updateVectors(){
    // For each particle:
    for (var i = 0; i < particles.length; i++)
    {
        var curr_particle = particles[i];
        // Get the next closest particle
        var closest_neighbor = i;
        var closest_dist = 10000;  // Set this to an arbitrarily high number
        for (var j = 0; j < particles.length; j++){
            if (j == i) continue;
            var dist = getDist(curr_particle, particles[j]);
            // Ignore neighbors too close to us, so that they start to move in the same direction
            if (dist < JOIN_DISTANCE) continue;
            if (dist < closest_dist){
                closest_dist = dist;
                closest_neighbor = j;
            }
        }
        
        // if (closest_neighbor == i) {
        //     return;
        // }
        
        
        var n_particle = particles[closest_neighbor];
        
        // Get a unit vector in that particle's direction
        var closest_vector = [n_particle[0] - curr_particle[0], n_particle[1] - curr_particle[1]];
        closest_vector = normalize(closest_vector, closest_dist);
        
        // Get a unit vector in the direction of our root point
        var root_vector = [ROOT_POINT[0] - curr_particle[0], ROOT_POINT[1] - curr_particle[1]];
        root_vector = normalize(root_vector, getDist(ROOT_POINT, curr_particle));
        
        // Add these to get the particles new direction vector
        var new_dir_vector = [root_vector[0] + closest_vector[0], root_vector[1] + closest_vector[1]]
        // new_dir_vector = normalize(new_dir_vector, Math.sqrt(new_dir_vector[0]**2 + new_dir_vector[1]**2))
        
        particles[i][2] = new_dir_vector;
    }
    
}

// Inital generation of our random field of particles
for (var j = 0; j < NUM_PARTICLES; j++){
    // For x an y, generate a random point on the visible canvas plane
    var x = ((Math.random() * 200) - 100)
    var y = ((Math.random() * 200) - 100)
    // Here we add a placeholder value where we will later store a vector
    particles.push([x, y, null])
}

// The walk function will be called until it returns false.
function walk(i) {
    updateVectors();
    for (var j = 0; j < particles.length; j++){
        var curr = particles[j];
        turtle.up()
        if (Math.random() >= 0) turtle.down();
        turtle.jump(curr[0], curr[1]);
        turtle.goto(curr[0] + (curr[2][0] * MOVE_DISTANCE), curr[1] + (curr[2][1] * MOVE_DISTANCE));
        particles[j][0] = turtle.x();
        particles[j][1] = turtle.y();
        
    }
    return i < 300
    // return particles.length > 1 || getDist(particles[0], ROOT_POINT) < JOIN_DISTANCE;
}