Attracted circles

Experimenting with attractors

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

const circles = 40;//min = 1, max = 250, step = 1

//const force = 30;// min = -200, max = 200, step = 1
const attractors = 2;//min = 1, max = 50, step = 1

const resolution = 0.5;//min = 0.01, max = 1, step = 0.01

// help function to compute the distance from a point (i.e., the hypothenuse of a triangle rectangle)
function distance(x, y){
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
}

// An attractor is a point which deflects drawing. It has a position and a force.
class Attractor{
constructor(x = 0, y = 0, force = 0){
this.x = x;
this.y = y;
this.force = force;

if( x == 0 && y == 0 && force == 0){
this.x = Math.random() * 200 - 100;
this.y = Math.random() * 200 - 100;
this.force = Math.random() * 200;
}
}
}

// Class that handle tracing of circles, and their behavior regarding attractors.
class Circle{
constructor(size = 25){
this.size = size;
this.centerX = 0;
this.centerY = 0;
this.angle = 0;
this.turtle = new Turtle();
this.turtle.jump(0, size);
}

attract(a){
this.attractors = a;
}

walk(){
let radAngle = this.angle * Math.PI / 180;

if(this.attractors){
let delta;
let coef;
for(let item of this.attractors){
let deltaX = this.x - item.x;
let deltaY = this.y - item.y;

delta = distance(deltaX, deltaY);
coef = Math.pow(item.force/delta, 2);

let moveX = (deltaX / delta) * coef;
let moveY = (deltaY / delta) * coef;

let dist = distance(moveX, moveY);

//                if(item.force > 0){
if(dist > delta){
// have to find a way to decrease attraction when move is > delta, instead of topping it.
moveX = deltaX;
moveY = deltaY;
//                        moveX *= delta / dist;
//                        moveY *= delta / dist;
}
//                } else {
/*
// same here : have to find a way to limit movement
if(distance < item.force / 4){
moveX /= 1/dist;
moveY /= 1/dist;
}
*/
//                   moveX = -moveX;
//                   moveY = -moveY;
//               }
this.x -= moveX;
this.y -= moveY;
}
}
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 random attractors.
const attractorList = [];

for(let i = 0; i < attractors; i++){
attractorList.push(new Attractor());
}

//attractorList.push(new Attractor(60, 0, 40));
//attractorList.push(new Attractor(-40, 40, -40));

//generate the circles.
const circleList = [];

for (let i = 0; i < circles; i++){
circleList[i] = new Circle((100 / circles) * (i + 1));
circleList[i].attract(attractorList);
}

// The walk function will be called until it returns false.
function walk(i) {
let goOn = true;

for(let item of circleList){
goOn = item.walk();
}

return goOn;
}