Wandering Path

Ez-wanderer

Log in to post a comment.

// LL 2021

const steps = 1000; // min=10 max=10000 step=1
const density = 400; // min=10 max=1000 step=1
const opacity = -0.25; // min=-1 max=1 step = 0.01

const camera_angle = 0.14; // min=0 max=1 step=0.01
const camera_height = 1; // min=0 max=3 step=0.01
const camera_distance = 2; // min=0.1 max=5 step=0.01

Canvas.setpenopacity(opacity);

let grid;
let current_pos;

const turtle = new Turtle();

function walk(i, t) {
    if (i==0) {
        reset_random();
        cameraPos = [camera_distance * Math.cos(Math.PI * 2 * (camera_angle + t)), camera_height, camera_distance * Math.sin(Math.PI * 2 * (camera_angle + t))];
        viewProjectionMatrix = setupCamera(cameraPos, cameraLookAt);
        reset_current_pos();
        grid = {};
    } else if (i > steps * density) {
        return false;
    }
    
    const saved_pos = [...current_pos];
    
    var retries = 50;
    while (retries > 0) {
        retries--;
        if (retries < 1) {
            // Stuck, move somewhere else
            reset_current_pos();
            return true;
        };
        const chance = Math.floor(random() * 6);
        switch (chance) {
            case 0: if (move( 1,  0,  0)) retries = 0; break;
            case 1: if (move(-1,  0,  0)) retries = 0; break;
            case 2: if (move( 0,  1,  0)) retries = 0; break;
            case 3: if (move( 0, -1,  0)) retries = 0; break;
            case 4: if (move( 0,  0,  1)) retries = 0; break;
            case 5: if (move( 0,  0, -1)) retries = 0; break;
        }
    }
    
    const p1 = project(saved_pos);
    const p2 = project(current_pos);
    
    turtle.jump(p1);
    turtle.goto(p2);
    
    return true;
}

function reset_current_pos() {
    current_pos = [ Math.round(density * random()), Math.round(density * random()), Math.round(density * random()) ];
}

function move(dx, dy, dz) {
    if (current_pos[0] + dx < 0) return false;
    if (current_pos[0] + dx > density) return false;
    if (current_pos[1] + dy < 0) return false;
    if (current_pos[1] + dy > density) return false;
    if (current_pos[2] + dz < 0) return false;
    if (current_pos[2] + dz > density) return false;
    
    const x = current_pos[0] - density / 2;
    const y = current_pos[1] - density / 2;
    const z = current_pos[2] - density / 2;
    const distance = Math.sqrt(x*x + y*y + z*z);
    if (distance > density / 2) return false;
    
    const index = pos_to_index(current_pos[0] + dx, current_pos[1] + dy, current_pos[2] + dz);
    if (grid[index] == true) return false;
    
    current_pos[0] += dx;
    current_pos[1] += dy;
    current_pos[2] += dz;
    grid[index] = true;
    
    return true;
}

function project(pos) {
    const x = pos[0] / density - 0.5, y = pos[1] / density - 0.5, z = pos[2] / density - 0.5;
    const p = transform4([x, z, y, 1], viewProjectionMatrix);
    const s = 50;
    return [p[0]/p[3]*s, -p[1]/p[3]*s];
}

function pos_to_index(x, y, z) {
    const index = Math.round(x) + Math.floor(y*density) + Math.floor(z*density*density);
    return index;
}

// Cached random for animations
function random() { while (rand_index >= rand_cache.length) rand_cache.push(Math.random()); return rand_cache[rand_index++]; }
function reset_random() { rand_index = 0; }
const rand_cache = [];
var rand_index = 0;

////////////////////////////////////////////////////////////////
// Projection from reinder's https://turtletoy.net/turtle/b3acf08303
let cameraPos, viewProjectionMatrix;
const cameraLookAt = [0,0,0];
function setupCamera(t,e){const m=lookAt4m(t,e,[0,1,0]),n=perspective4m(.25,1);return multiply4m(n,m)}
function lookAt4m(o,n,r){const s=new Float32Array(16);n=normalize3(sub3(o,n)),r=normalize3(cross3(r,n));const t=normalize3(cross3(n,r));return s[0]=r[0],s[1]=t[0],s[2]=n[0],s[3]=0,s[4]=r[1],s[5]=t[1],s[6]=n[1],s[7]=0,s[8]=r[2],s[9]=t[2],s[10]=n[2],s[11]=0,s[12]=-(r[0]*o[0]+r[1]*o[1]+r[2]*o[2]),s[13]=-(t[0]*o[0]+t[1]*o[1]+t[2]*o[2]),s[14]=-(n[0]*o[0]+n[1]*o[1]+n[2]*o[2]),s[15]=1,s}
function perspective4m(t,n){const e=new Float32Array(16).fill(0,0);return e[5]=1/Math.tan(t/2),e[0]=e[5]/n,e[10]=e[11]=-1,e}
function multiply4m(t,r){const l=new Float32Array(16);for(let n=0;16>n;n+=4)for(let o=0;4>o;o++)l[n+o]=r[n+0]*t[0+o]+r[n+1]*t[4+o]+r[n+2]*t[8+o]+r[n+3]*t[12+o];return l}
function transform4(r,n){const t=new Float32Array(4);for(let o=0;4>o;o++)t[o]=n[o]*r[0]+n[o+4]*r[1]+n[o+8]*r[2]+n[o+12];return t}
function normalize3(a) { return scale3(a,1/len3(a)); }
function scale3(a,b) { return [a[0]*b,a[1]*b,a[2]*b]; }
function len3(a) { return Math.sqrt(dot3(a,a)); }
function sub3(a,b) { return [a[0]-b[0],a[1]-b[1],a[2]-b[2]]; }
function dot3(a,b) { return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]; }
function cross3(a,b) { return [a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]]; }