# Simulate gravitational pull on a body in orbit

I'm trying to simulate gravitational pull/acceleration in the following animation. `earth`'s angular velocity should increase as it gets closer to `sol` and decrease as it gets far. I think I'll need an easing function to modify the `earth.angularVelocity` but no idea how.

I don't know if already defined easing functions or a custom one would work. The easing function I need should work like in this graph:

`earth`'s perihelion is at 180°, and aphelion is at 0/360°. How can I create such a function and make this work?

``````function pullRelease(angularPosition, begin, change, maxVelocity) {
// ?
}
earth.angularVelocity = pullRelease(earth.angularPosition, 0, 360, 3);
``````

``````var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");

var sol = {
x: 125,
y: 150,
r: 30,
fill: "gold",
};

var orbit = {
x: 200,
y: 150,
semiMajor: 150,
semiMinor: 75,
};

var earth = {
r: 15,
fill: "dodgerblue",
angularPosition: 0,
angularVelocity: 1,
};

// draw sun
context.beginPath();
context.arc(sol.x, sol.y, sol.r, 0, 360 * Math.PI / 180);
context.fillStyle = sol.fill;
context.fill();
// draw fake sun to mark the orbit center
context.beginPath();
context.arc(orbit.x, orbit.y, sol.r, 0, 360 * Math.PI / 180);
context.fillStyle = "rgba(255,215,0,.1)";
context.fill();
// draw earth's orbit path
context.beginPath();
context.ellipse(orbit.x, orbit.y, orbit.semiMajor, orbit.semiMinor, 0, 0, 2 * Math.PI);
context.stroke();
// these are fixed, so save them as background-image
canvas.style.backgroundImage = "url(" + canvas.toDataURL() + ")";

function draw() {
context.resetTransform();
context.clearRect(0, 0, canvas.width, canvas.height);

var newPosition = rotate(-earth.angularPosition, orbit.semiMajor, orbit.semiMinor, orbit.x, orbit.y);

earth.x = newPosition.x;
earth.y = newPosition.y;
// earth.angularVelocity = pullRelease(earth.angularPosition, 0, 360, 3);
earth.angularPosition += earth.angularVelocity;

if (earth.angularPosition >= 360) {
earth.angularPosition = 0;
}
position.innerHTML = earth.angularPosition + "°";

context.translate(earth.x, earth.y);

context.beginPath();
context.arc(0, 0, earth.r, 0, 360 * Math.PI / 180);
context.closePath();
context.fillStyle = earth.fill;
context.fill();

requestAnimationFrame(draw);
}

requestAnimationFrame(draw);

function rotate(angle, distanceX, distanceY, originX, originY) {
return {
x: originX + Math.cos(angle * Math.PI / 180) * distanceX,
y: originY + Math.sin(angle * Math.PI / 180) * distanceY,
}
}``````
``````body {
background: gainsboro;
}
canvas {
background: white;
box-shadow: 1px 1px 1px rgba(0, 0, 0, .1);
}
#position {
display: inline-block;
width: 35px;
text-align: right;
}``````
``````<canvas id="canvas" class="box" width="400" height="300"></canvas>
<p>Position: <span id="position">0</span></p>``````

After some thinking, I've came up with a function which seems to work, but still I don't think the effect feels natural. Function produces the graph at left. An ideal one would be the one at right. So the function still needs some work.

``````function pullRelease(angularPosition, begin, change, minVelocity, maxVelocity) {
var midWay = Math.floor(change/2);
if (angularPosition >= begin && angularPosition < midWay) {
var percent = angularPosition / midWay;
return minVelocity + (maxVelocity-minVelocity) * percent;
}
else if (angularPosition == midWay) {
return maxVelocity;
}
else if (angularPosition > midWay && angularPosition <= change) {
var midWayOffset = angularPosition - midWay;
var remaining    = midWay - midWayOffset;
var percent      = remaining / midWay;
return minVelocity + (maxVelocity-minVelocity) * percent;
}
}
``````

``````var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");

var sol = {
x: 125,
y: 150,
r: 30,
fill: "gold",
};

var orbit = {
x: 200,
y: 150,
semiMajor: 150,
semiMinor: 75,
};

var earth = {
x: 0,
y: 0,
r: 15,
fill: "dodgerblue",
angularPosition: 0,
angularVelocity: 0,
};

// draw sun
context.beginPath();
context.arc(sol.x, sol.y, sol.r, 0, 360 * Math.PI / 180);
context.fillStyle = sol.fill;
context.fill();
// draw fake sun to mark the orbit center
context.beginPath();
context.arc(orbit.x, orbit.y, sol.r, 0, 360 * Math.PI / 180);
context.fillStyle = "rgba(255,215,0,.1)";
context.fill();
// draw earth's orbit path
context.beginPath();
context.ellipse(orbit.x, orbit.y, orbit.semiMajor, orbit.semiMinor, 0, 0, 2 * Math.PI);
context.stroke();
// these are fixed, so save them as background-image
canvas.style.backgroundImage = "url(" + canvas.toDataURL() + ")";

function draw() {
context.resetTransform();
context.clearRect(0, 0, canvas.width, canvas.height);

var newPosition = rotate(-earth.angularPosition, orbit.semiMajor, orbit.semiMinor, orbit.x, orbit.y);
earth.x = newPosition.x;
earth.y = newPosition.y;

earth.angularVelocity = pullRelease(earth.angularPosition, 0, 360, 0.5, 5);
earth.angularPosition += earth.angularVelocity;

if (earth.angularPosition >= 360) {
earth.angularPosition = 0;
}
position.innerHTML = Math.floor(earth.angularPosition) + "°";
velocity.innerHTML = (earth.angularVelocity).toFixed(2) + "°";

context.translate(earth.x, earth.y);

context.beginPath();
context.arc(0, 0, earth.r, 0, 360 * Math.PI / 180);
context.closePath();
context.fillStyle = earth.fill;
context.fill();

requestAnimationFrame(draw);
}

requestAnimationFrame(draw);

function rotate(angle, distanceX, distanceY, originX, originY) {
return {
x: originX + Math.cos(angle * Math.PI / 180) * distanceX,
y: originY + Math.sin(angle * Math.PI / 180) * distanceY,
}
}

function pullRelease(angularPosition, begin, change, minVelocity, maxVelocity) {
var midWay = Math.floor(change / 2);
if (angularPosition >= begin && angularPosition < midWay) {
var percent = angularPosition / midWay;
return minVelocity + (maxVelocity - minVelocity) * percent;
} else if (angularPosition == midWay) {
return maxVelocity;
} else if (angularPosition > midWay && angularPosition <= change) {
var midWayOffset = angularPosition - midWay;
var remaining = midWay - midWayOffset;
var percent = remaining / midWay;
return minVelocity + (maxVelocity - minVelocity) * percent;
}
}``````
``````body {
background: gainsboro;
}
canvas {
background: white;
box-shadow: 1px 1px 1px rgba(0, 0, 0, .1);
}
#position {
display: inline-block;
width: 35px;
text-align: right;
}
#velocity {
display: inline-block;
width: 35px;
text-align: right;
}``````
``````<canvas id="canvas" class="box" width="400" height="300"></canvas>
<p>Position: <span id="position">0</span></p>
<p>Velocity: <span id="velocity">0</span></p>``````