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;
}