function gravityForce(ball) { /* We simulate a kind of gravity towards the target point of each ball. This force will make the ball to keep pushing to reach that point. */ const coulomb = .00001; const minForce = 0.01; var d = Math.sqrt(Math.pow(ball.targetPosition.x - ball.currentPosition.x, 2) + Math.pow(ball.targetPosition.y - ball.currentPosition.y, 2)); // ... we calculate the distance ... var force = coulomb * d * d / ball.currentMass; // In this case the mass of the ball affects the gravity force that it receives, that gives priority to target position to bigger balls. if (force < minForce) { // We need this attraction force to overcome the friction we imposed to the system. force = minForce; } var pos1 = { x: ball.currentPosition.x, y: ball.currentPosition.y }; var pos2 = { x: ball.targetPosition.x, y: ball.targetPosition.y }; var posDiff = { // Next we need the vector resulting from the 2 positions. x: pos2.x - pos1.x, y: pos2.y - pos1.y }; var unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. x: posDiff.x / d, y: posDiff.y / d }; var forceVector = { x: unitVector.x * force, y: unitVector.y * force }; /* We add the force vector to the speed vector */ ball.currentSpeed.x = ball.currentSpeed.x + forceVector.x; ball.currentSpeed.y = ball.currentSpeed.y + forceVector.y; } function repulsionForce(currentBall) { /* We generate a repulsion force between balls, that prevents them to be collisioning so often. */ const coulomb = 2; var ball1 = balls[currentBall]; for (var i = 0; i < balls.length; i++) { // The force to be applied is considering all other balls... if (i !== currentBall) { // ... except for the current one. var ball2 = balls[i]; // So, for each ball... var d = Math.sqrt(Math.pow(ball2.currentPosition.x - ball1.currentPosition.x, 2) + Math.pow(ball2.currentPosition.y - ball1.currentPosition.y, 2)); // ... we calculate the distance ... var force = coulomb * ball2.currentMass / (d * d); // ... and with it the repulsion force. /* We need to put a hard limit to this force, in order to to eject very little balls to the infinite and beyond. */ if (force > 1) { force = 1; } var pos1 = { x: ball1.currentPosition.x, y: ball1.currentPosition.y }; var pos2 = { x: ball2.currentPosition.x, y: ball2.currentPosition.y }; var posDiff = { // Next we need the vector resulting from the 2 positions. x: pos2.x - pos1.x, y: pos2.y - pos1.y }; var unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. x: posDiff.x / d, y: posDiff.y / d }; var forceVector = { x: unitVector.x * force, y: unitVector.y * force }; /* We substract the force vector to the speed vector of the current ball */ ball1.currentSpeed.x = ball1.currentSpeed.x - forceVector.x; ball1.currentSpeed.y = ball1.currentSpeed.y - forceVector.y; } } } function colliding(ball1, ball2) { /* This function detects weather 2 balls collide with each other. */ var r1 = ball1.currentRadius; var r2 = ball2.currentRadius; var distance = Math.sqrt(Math.pow(ball2.currentPosition.x - ball1.currentPosition.x, 2) + Math.pow(ball2.currentPosition.y - ball1.currentPosition.y, 2)); if (distance > (r1 + r2)) { // No solutions, the circles are too far apart. return false; } else if (distance <= r1 + r2) { // One circle contains the other. return true; } else if ((distance === 0) && (r1 === r2)) { // the circles coincide. return true; } else return true; } function isInside(x, y) { /* This function detects weather the point x,y is inside any of the balls. */ for (var i = 0; i < balls.length; i++) { var ball = balls[i]; var distance = Math.sqrt(Math.pow(ball.currentPosition.x - x, 2) + Math.pow(ball.currentPosition.y - y, 2)); if (distance < ball.currentRadius) { return i; } } return -1; } function isInsideBall(ballIndex, x, y) { /* This function detects weather the point x,y is inside one particular balls. */ var ball = balls[ballIndex]; var distance = Math.sqrt(Math.pow(ball.currentPosition.x - x, 2) + Math.pow(ball.currentPosition.y - y, 2)); if (distance < ball.currentRadius) { return true; } return false; } function distance(x1, y1, x2, y2) { return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); } function resolveCollision(ball1, ball2) { /* This function changes speed and position of balls that are in collision */ var collisionision_angle = Math.atan2((ball2.currentPosition.y - ball1.currentPosition.y), (ball2.currentPosition.x - ball1.currentPosition.x)); var speed1 = Math.sqrt(ball1.currentSpeed.x * ball1.currentSpeed.x + ball1.currentSpeed.y * ball1.currentSpeed.y); // Magnitude of Speed Vector for ball 1 var speed2 = Math.sqrt(ball2.currentSpeed.x * ball2.currentSpeed.x + ball2.currentSpeed.y * ball2.currentSpeed.y); // Magnitude of Speed Vector for ball 2 var direction_1 = Math.atan2(ball1.currentSpeed.y, ball1.currentSpeed.x); var direction_2 = Math.atan2(ball2.currentSpeed.y, ball2.currentSpeed.x); var new_xspeed_1 = speed1 * Math.cos(direction_1 - collisionision_angle); var new_yspeed_1 = speed1 * Math.sin(direction_1 - collisionision_angle); var new_xspeed_2 = speed2 * Math.cos(direction_2 - collisionision_angle); var new_yspeed_2 = speed2 * Math.sin(direction_2 - collisionision_angle); var final_xspeed_1 = ((ball1.currentMass - ball2.currentMass) * new_xspeed_1 + (ball2.currentMass + ball2.currentMass) * new_xspeed_2) / (ball1.currentMass + ball2.currentMass); var final_xspeed_2 = ((ball1.currentMass + ball1.currentMass) * new_xspeed_1 + (ball2.currentMass - ball1.currentMass) * new_xspeed_2) / (ball1.currentMass + ball2.currentMass); var final_yspeed_1 = new_yspeed_1; var final_yspeed_2 = new_yspeed_2; var cosAngle = Math.cos(collisionision_angle); var sinAngle = Math.sin(collisionision_angle); ball1.currentSpeed.x = cosAngle * final_xspeed_1 - sinAngle * final_yspeed_1; ball1.currentSpeed.y = sinAngle * final_xspeed_1 + cosAngle * final_yspeed_1; ball2.currentSpeed.x = cosAngle * final_xspeed_2 - sinAngle * final_yspeed_2; ball2.currentSpeed.y = sinAngle * final_xspeed_2 + cosAngle * final_yspeed_2; var pos1 = { x: ball1.currentPosition.x, y: ball1.currentPosition.y }; var pos2 = { x: ball2.currentPosition.x, y: ball2.currentPosition.y }; // get the mtd var posDiff = { x: pos1.x - pos2.x, y: pos1.y - pos2.y }; var d = Math.sqrt(Math.pow(ball2.currentPosition.x - ball1.currentPosition.x, 2) + Math.pow(ball2.currentPosition.y - ball1.currentPosition.y, 2)); // minimum translation distance to push balls apart after intersecting var scalar = (((ball1.currentRadius + ball2.currentRadius) - d) / d); var minTD = { x: posDiff.x * scalar, y: posDiff.y * scalar }; // resolve intersection -- // computing inverse mass quantities var im1 = 1 / ball1.currentMass; var im2 = 1 / ball2.currentMass; // push-pull them apart based off their mass pos1.x = pos1.x + minTD.x * (im1 / (im1 + im2)); pos1.y = pos1.y + minTD.y * (im1 / (im1 + im2)); pos2.x = pos2.x - minTD.x * (im2 / (im1 + im2)); pos2.y = pos2.y - minTD.y * (im2 / (im1 + im2)); ball1.currentPosition = pos1; ball2.currentPosition = pos2; }