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