Adding the angle of the gradient vector to the turtle's heading at (x, y) instead of simply using it to set the turtle's heading.
Log in to post a comment.
let turtle_walk_distance = 2; //min=0.25, max=10, step=0.25 let noise_scale_denominator = 100; //min=5, max=500, step=1 let random_jump_chance = 0.02; //min=0.0, max=0.1, step=0.0005 let max_iterations = 100000; //min=10000, max=1000000, step=10000 let smoothstep_polynomial_order = 2 //min=1, max=10, step=1 const noise_scale = 1 / noise_scale_denominator; // Joe Iddon's Perlin Noise (https://github.com/joeiddon/perlin) const perlin = { rand_vect: function(){ let theta = Math.random() * 2 * Math.PI; return {x: Math.cos(theta), y: Math.sin(theta)}; }, dot_prod_grid: function(x, y, vx, vy){ let g_vect; let d_vect = {x: x - vx, y: y - vy}; if (this.gradients[[vx,vy]]){ g_vect = this.gradients[[vx,vy]]; } else { g_vect = this.rand_vect(); this.gradients[[vx, vy]] = g_vect; } return d_vect.x * g_vect.x + d_vect.y * g_vect.y; }, smootherstep: function(x){ return generalized_smooth_step(smoothstep_polynomial_order, x) }, interp: function(x, a, b){ return a + this.smootherstep(x) * (b-a); }, seed: function(){ this.gradients = {}; this.memory = {}; }, get: function(x, y) { if (this.memory.hasOwnProperty([x,y])) return this.memory[[x,y]]; let xf = Math.floor(x); let yf = Math.floor(y); //interpolate let tl = this.dot_prod_grid(x, y, xf, yf); let tr = this.dot_prod_grid(x, y, xf+1, yf); let bl = this.dot_prod_grid(x, y, xf, yf+1); let br = this.dot_prod_grid(x, y, xf+1, yf+1); let xt = this.interp(x-xf, tl, tr); let xb = this.interp(x-xf, bl, br); let v = this.interp(y-yf, xt, xb); this.memory[[x,y]] = v; return v; } } perlin.seed(); // You can find the Turtle API reference here: https://turtletoy.net/syntax Canvas.setpenopacity(0.25); // Global code will be evaluated once. const t = new Turtle(); t.penup(); t.goto(-50,-20); t.pendown(); // The walk function will be called until it returns false. function walk(i) { let current = { x: t.x(), y: t.y() } let n = perlin.get((current.x - 100) * noise_scale, (current.y - 100) * noise_scale); let h = Math.PI * 2 * n; let h_old = t.h() * Math.PI / 180; let h_new = h_old + h; let next = { x: current.x + turtle_walk_distance * Math.cos(h_new), y: current.y + turtle_walk_distance * Math.sin(h_new) } let wrapped_next = wrap(next.x, next.y); t.seth(h_new * 180 / Math.PI) if (wrapped_next.x !== next.x || wrapped_next.y !== next.y){ t.jump(wrapped_next.x, wrapped_next.y); } else if (Math.random() < random_jump_chance) { t.penup(); t.setx(-100 + Math.random() * 200); t.sety(-100 + Math.random() * 200); t.pendown(); } else { t.forward(turtle_walk_distance); } return i < max_iterations; } function wrap(x, y, x_min = -100, x_max = 100, y_min = -100, y_max = 100){ const x_range = x_max - x_min; const y_range = y_max - y_min; return { x: x < x_min ? x + x_range : x > x_max ? x - x_range : x, y: y < y_min ? y + y_range : y > y_max ? y - y_range : y } } //Generalized smoothstep from Wikipedia: (https://en.wikipedia.org/wiki/Smoothstep) // Generalized smoothstep function generalized_smooth_step(N, x) { x = clamp(x, 0, 1); // x must be equal to or between 0 and 1 var result = 0; for (var n = 0; n <= N; ++n) result += pascalTriangle(-N - 1, n) * pascalTriangle(2 * N + 1, N - n) * Math.pow(x, N + n + 1); return result; } // Returns binomial coefficient without explicit use of factorials, // which can't be used with negative integers function pascalTriangle(a, b) { var result = 1; for (var i = 0; i < b; ++i) result *= (a - i) / (i + 1); return result; } function clamp(x, lowerlimit, upperlimit) { if (x < lowerlimit) x = lowerlimit; if (x > upperlimit) x = upperlimit; return x; }