Deformed circles on a noise field.
Log in to post a comment.
// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(0.5);
const size = 80; //min = 10, max = 100, step = 5
const perlinSize = 2; //min = 1, max = 50, step = 1
const circles = 80; //min = 1, max = 250, step = 1
const resolution = 0.25; //min = 0.01, max = 1, step = 0.01
const amplitude = 18; //min = 0, max = 25, step = 0.5
// Global code will be evaluated once.
const turtle = new Turtle();
class Perlin{
constructor(size, gridSize){
this.size = size;
this.gridSize = gridSize;
this.grid = [];
// this.mean = 0;
// For each grid intersection, compute a random unit vector
for(let i = 0; i <= gridSize; i++){
let table = [];
for(let j = 0; j <= gridSize; j++){
let angle = Math.random() * 2 * Math.PI;
let x = Math.cos(angle);
let y = Math.sin(angle);
table.push([x, y]);
// console.log([x, y]);
}
this.grid.push(table);
}
// console.log(this.grid);
}
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;
// console.log(x, y);
let posx = x * this.gridSize / this.size;
let posy = y * this.gridSize / this.size;
// console.log(posx, posy);
let x1 = Math.floor(posx);
let x2 = x1 + 1;
let y1 = Math.floor(posy);
let y2 = y1 + 1;
let scal = [];
scal.push()
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));
// console.log(scal);
// interpolate : linear interpolation for a start
let int1 = this.interpolate(posx - x1, scal[0], scal[1]);
let int2 = this.interpolate(posx - x1, scal[2], scal[3]);
// console.log(int1, int2);
// this.mean += this.interpolate(posy - y1, int1, int2);
return this.interpolate(posy - y1, int1, int2);
}
scalar(x, y, vx, vy){
x -= vx;
y -= vy;
// console.log(x, y);
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)
}
}
// Class that handle tracing of circles, and their behavior regarding attractors.
class Circle{
constructor(diameter = 25){
this.size = diameter;
this.centerX = 0;
this.centerY = 0;
this.angle = 0;
this.turtle = new Turtle();
this.turtle.jump(0, size);
}
walk(){
let radAngle = this.angle * Math.PI / 180;
this.x = Math.cos(radAngle) * this.size;
this.y = Math.sin(radAngle) * this.size;
// console.log(this.x, this.y);
let deviation = perlin.get(this.x, this.y);
// console.log(deviation);
this.x += Math.cos(deviation * Math.PI) * amplitude;
this.y += Math.sin(deviation * Math.PI) * amplitude;
// console.log(this.x, this.y);
if(this.angle == 0) this.turtle.jump(this.x, this.y);
else this.turtle.goto(this.x, this.y);
this.angle += resolution;
if(this.angle > 360) return false;
return true;
}
}
//generate the circles.
const circleList = [];
for (let i = 0; i < circles; i++){
circleList.push(new Circle((size / circles) * (i + 1)));
}
let perlin = new Perlin(size, perlinSize);
// The walk function will be called until it returns false.
function walk(i) {
let goOn = true;
for(let circle of circleList){
goOn = circle.walk();
}
// if(!goOn) console.log(perlin.mean);
return goOn;
}