Perlin Dragon

Using Perlin noise to create geometry similar to the dragon curve

en.wikipedia.org/wiki/dragon_curve

en.wikipedia.org/wiki/perlin_noise

Log in to post a comment.

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

// Global code will be evaluated once.
const turtles = [];
let seed = 1000; // min=1 max=1000000 step=1
const bits = 20; 
const samples = 150000; 
const mod = 1<<bits;
const turtles_count = 700; // min = 1 max = 1000 step = 1
const steps = 250; // min = 1 max = 1000 step = 1
const spawn_range = 70; // min = 0 max = 100 step = 1
const perlin_const = 10; // min = 1 max = 20 step = 1
const line_orientation = 0; //min=0 max=1 step=1 (Rectangular, Random)

for(let i = turtles_count; i > 0; i--) {
    let turtle = new Turtle;
    turtle.penup();
    if(line_orientation == 1) {
        turtle.setheading(random() * 360);
    }
    r = random() * spawn_range;
    theta = random() * 2 * Math.PI;
    turtle.setx(r * Math.cos(theta));
    turtle.sety(r * Math.sin(theta));
    turtle.pendown();
    turtles.push(turtle);
}

////////////////////////////////////////////////////////////////
// Pseudorandom number generator. Created by Reinder Nijhoff 2024
// https://turtletoy.net/turtle/a2274fd1fe
////////////////////////////////////////////////////////////////
function random() { // returns a number [0, 1[
    let r = 1103515245 * (((seed+=12345) >> 1) ^ seed);
    r = 1103515245 * (r ^ (r >> 3));
    r = r ^ (r >> 16);
    return (r % mod) / mod;
}

////////////////////////////////////////////////////////////////
// Perlin noise field generator. Taken from turtle below
// https://turtletoy.net/turtle/04036becc3
////////////////////////////////////////////////////////////////
class Perlin {
    constructor(size, gridSize) {
        this.size = size;
        this.gridSize = gridSize;
        this.grid = [];

        for (let i = 0; i <= gridSize; i++) {
            let table = [];
            for (let j = 0; j <= gridSize; j++) {
                let angle = random() * 2 * Math.PI;
                let x = Math.cos(angle);
                let y = Math.sin(angle);
                table.push([x, y]);
            }
            this.grid.push(table);
        }
    }

    get(x, y) {
        x = x / 2 + this.size / 2;
        y = y / 2 + this.size / 2;
        if (x < 0) x = 0;
        if (x >= this.size) x = this.size - 0.01;
        if (y < 0) y = 0;
        if (y >= this.size) y = this.size - 0.01;

        let posx = x * this.gridSize / this.size;
        let posy = y * this.gridSize / this.size;

        let x1 = Math.floor(posx);
        let x2 = x1 + 1;
        let y1 = Math.floor(posy);
        let y2 = y1 + 1;

        let scal = [];
        scal.push(this.scalar(posx, posy, x1, y1));
        scal.push(this.scalar(posx, posy, x2, y1));
        scal.push(this.scalar(posx, posy, x1, y2));
        scal.push(this.scalar(posx, posy, x2, y2));

        let int1 = this.interpolate(posx - x1, scal[0], scal[1]);
        let int2 = this.interpolate(posx - x1, scal[2], scal[3]);

        return this.interpolate(posy - y1, int1, int2);
    }

    scalar(x, y, vx, vy) {
        x -= vx;
        y -= vy;
        return x * this.grid[vx][vy][0] + y * this.grid[vx][vy][1];
    }

    smooth(v) {
        if (v < 0) v = 0;
        if (v > 1) v = 1;
        return v ** 2 * (3 - 2 * v);
    }

    interpolate(x, a, b) {
        return a + (b - a) * this.smooth(x);
    }
}

let perlin_angle = new Perlin(100, perlin_const);

// The walk function will be called until it returns false.
function walk(i) {
    for(let j = turtles_count-1; j > 0; j--) {
        turtles[j].forward(1);
        current_perlin = perlin_angle.get(turtles[j].x(),turtles[j].y());
        let right = 0;
        if(current_perlin < -0.5) {
            right = 180;
        }
        else if(current_perlin < 0) {
            right = 90;
        }
        else if(current_perlin < 0.5) {
            right = 0;
        }
        else{
            right = 270;
        }
        
        turtles[j].right(right);
    }
    return i < steps;
}