simple financial rate function in javascript - javascript

I'm looking for a simple javascript financial RATE function, and I found this one. But it seems too difficult to understand. I want to simplify this function, and I need your help.
If anyone has a simplest function, please answer. (It's a excel RATE function equivalent.)
var rate = function(nper, pmt, pv, fv, type, guess) {
if (guess == null) guess = 0.01;
if (fv == null) fv = 0;
if (type == null) type = 0;
var FINANCIAL_MAX_ITERATIONS = 128;//Bet accuracy with 128
var FINANCIAL_PRECISION = 0.0000001;//1.0e-8
var y, y0, y1, x0, x1 = 0, f = 0, i = 0;
var rate = guess;
if (Math.abs(rate) < FINANCIAL_PRECISION) {
y = pv * (1 + nper * rate) + pmt * (1 + rate * type) * nper + fv;
} else {
f = Math.exp(nper * Math.log(1 + rate));
y = pv * f + pmt * (1 / rate + type) * (f - 1) + fv;
}
y0 = pv + pmt * nper + fv;
y1 = pv * f + pmt * (1 / rate + type) * (f - 1) + fv;
// find root by Newton secant method
i = x0 = 0.0;
x1 = rate;
while ((Math.abs(y0 - y1) > FINANCIAL_PRECISION) && (i < FINANCIAL_MAX_ITERATIONS)) {
rate = (y1 * x0 - y0 * x1) / (y1 - y0);
x0 = x1;
x1 = rate;
if (Math.abs(rate) < FINANCIAL_PRECISION) {
y = pv * (1 + nper * rate) + pmt * (1 + rate * type) * nper + fv;
} else {
f = Math.exp(nper * Math.log(1 + rate));
y = pv * f + pmt * (1 / rate + type) * (f - 1) + fv;
}
y0 = y1;
y1 = y;
++i;
}
return rate;}
Thanks!

The math is too complicated for me to understand, but this might be easier for you to read. Some of the variables have been renamed to make more sense, and it's formatted to be easier on your eyes
function rate(paymentsPerYear, paymentAmount, presentValue, futureValue, dueEndOrBeginning, interest)
{
//If interest, futureValue, dueEndorBeginning was not set, set now
if (interest == null)
interest = 0.01;
if (futureValue == null)
futureValue = 0;
if (dueEndOrBeginning == null)
dueEndOrBeginning = 0;
var FINANCIAL_MAX_ITERATIONS = 128;//Bet accuracy with 128
var FINANCIAL_PRECISION = 0.0000001;//1.0e-8
var y, y0, y1, x0, x1 = 0, f = 0, i = 0;
var rate = interest;
if (Math.abs(rate) < FINANCIAL_PRECISION)
{
y = presentValue * (1 + paymentsPerYear * rate) + paymentAmount * (1 + rate * dueEndOrBeginning) * paymentsPerYear + futureValue;
}
else
{
f = Math.exp(paymentsPerYear * Math.log(1 + rate));
y = presentValue * f + paymentAmount * (1 / rate + dueEndOrBeginning) * (f - 1) + futureValue;
}
y0 = presentValue + paymentAmount * paymentsPerYear + futureValue;
y1 = presentValue * f + paymentAmount * (1 / rate + dueEndOrBeginning) * (f - 1) + futureValue;
// find root by Newton secant method
i = x0 = 0.0;
x1 = rate;
while ((Math.abs(y0 - y1) > FINANCIAL_PRECISION)
&& (i < FINANCIAL_MAX_ITERATIONS))
{
rate = (y1 * x0 - y0 * x1) / (y1 - y0);
x0 = x1;
x1 = rate;
if (Math.abs(rate) < FINANCIAL_PRECISION)
{
y = presentValue * (1 + paymentsPerYear * rate) + paymentAmount * (1 + rate * dueEndOrBeginning) * paymentsPerYear + futureValue;
}
else
{
f = Math.exp(paymentsPerYear * Math.log(1 + rate));
y = presentValue * f + paymentAmount * (1 / rate + dueEndOrBeginning) * (f - 1) + futureValue;
}
y0 = y1;
y1 = y;
++i;
}
return rate;
}

Related

How to plot an ellipse on canvas from 2 points on the ellipse, where slope of major axis (rx), and minor axis (ry) length are unknown

This may be more of a mathematics problem, but maybe there is a simple javascript solution that I am missing.
I want to plot an ellipse on html canvas from user input of a center point, radius of the major (longest) axis, and 2 points will fall on the ellipse.
This should potentially create 2 possible ellipse paths, both of which will center around the center point, and cross through the 2 points.
So for example, if the center = [2, 1] major axis radius a = 10, point 1 u = [4, 2] and point 2 v = [5, 6], what is the minor axis radius b and angle of rotation theta?
So far I have tried to implement an equation that I found from https://math.stackexchange.com/questions/3210414/find-the-angle-of-rotation-and-minor-axis-length-of-ellipse-from-major-axis-leng,
but it does not return valid values. My javascript code looks like this:
function getEllipseFrom2Points(center, u, v, a) {
function getSlope(plusOrMinus) {
return Math.sqrt(((uy * vx - ux * vy) ** 2) / (-ux * uy * (a * (v2x + v2y) - 1) + vx * vy * (a * (u2x + u2y) - 1) - plusOrMinus * (uy * vx - ux * vy) * q) / (u2x * (1 - a * v2y) + v2x * (a * u2y - 1)));
}
function getMinorAxis(plusOrMinus) {
return (u2x + u2y + v2x + v2y - a * (2 * u2x * v2x + 2 * u2y * v2y + 2 * ux * uy * vx * vy + u2y * v2x + u2x * v2y) + plusOrMinus * 2 * (ux * vx + uy * vy) * q);
}
var vx = v[0],
vy = v[1],
ux = u[0],
uy = u[1],
v2x = vx ** 2,
v2y = vy ** 2,
u2x = ux ** 2,
u2y = uy ** 2,
q = Math.sqrt((1 - a * (u2x + u2y)) * (1 - a * (v2x + v2y))),
ellipse1 = { rx: a, ry: getMinorAxis(1), origin: center, rotation: getSlope(1) },
ellipse2 = { rx: a, ry: getMinorAxis(-1), origin: center, rotation: getSlope(-1) };
}
Either the equation that I am following is wrong, or I have implemented it wrong
In case anyone is interested, here is my solution to the problem, which isn't really "the" solution. If anyone can solve this I would still be happy to know.
Since I can't solve for both slope of the major axis and length of the minor axis, I just take a guess at slope and then test how close it is, and then refine the result by trying in a smaller and smaller region. Since the final ellipse that gets drawn is actually an estimation constructed from bezier curves, I can get close enough in a reasonable amount of time.
function getEllipseFrom2Points (center, u, v, a) {
function getSemiMinorAxis([x, y], a, t) {
// equation for rotated ellipse
// b = a(ycos(t) - xsin(t)) / sqrt(a^2 - x^2cos^2(t) - 2xysin(t)cos(t) - y^2sin^2(t)) and
// b = a(xsin(t) - ycos(t)) / sqrt(a^2 - x^2cos^2(t) - 2xysin(t)cos(t) - y^2sin^2(t))
// where a^2 !== (xcos(t) + ysin(t))^2
// and aycos(t) !== axsin(t)
if (a ** 2 !== (x * Math.cos(t) + y * Math.sin(t)) ** 2 &&
a * y * Math.cos(t) !== a * x * Math.sin(t)) {
var b = [],
q = (Math.sqrt(a ** 2 - x ** 2 * (Math.cos(t)) ** 2 - 2 * x * y * Math.sin(t) * Math.cos(t) - y ** 2 * (Math.sin(t)) ** 2));
b[0] = (a * (y * Math.cos(t) - x * Math.sin(t))) / q;
b[1] = (a * (x * Math.sin(t) - y * Math.cos(t))) / q;
return b;
}
}
function getAngle_radians(point1, point2){
return Math.atan2(point2[1] - point1[1], point2[0] - point1[0]);
}
function getDistance(point1, point2) {
return Math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2);
}
function rotatePoint(point, center, radians) {
var x = (point[0] - center[0]) * Math.cos(radians) - (point[1] - center[1]) * Math.sin(radians) + center[0];
var y = (point[1] - center[1]) * Math.cos(radians) + (point[0] - center[0]) * Math.sin(radians) + center[1];
return [x, y];
}
function measure(ellipseRotation, pointOnEllipse, minorAxisLength) {
var d = getDistance(point, pointOnEllipse);
if (d < bestDistanceBetweenPointAndEllipse) {
bestDistanceBetweenPointAndEllipse = d;
bestEstimationOfB = minorAxisLength;
bestEstimationOfR = ellipseRotation;
}
}
function getBestEstimate(min, max) {
var testIncrement = (max - min) / 10;
for (let r = min; r < max; r = r + testIncrement) {
if (radPoint1 < r && radPoint2 < r || radPoint1 > r && radPoint2 > r) {//points both on same side of ellipse
semiMinorAxis = getSemiMinorAxis(v, a, r);
if (semiMinorAxis) {
for (let t = 0; t < circle; t = t + degree) {
ellipsePoint1 = [a * Math.cos(t), semiMinorAxis[0] * Math.sin(t)];
ellipsePoint2 = [a * Math.cos(t), semiMinorAxis[1] * Math.sin(t)];
point = rotatePoint(u, [0, 0], -r);
measure(r, ellipsePoint1, semiMinorAxis[0]);
measure(r, ellipsePoint2, semiMinorAxis[1]);
}
}
}
}
count++;
if (new Date().getTime() - startTime < 200 && count < 10) //refine estimate
getBestEstimate(bestEstimationOfR - testIncrement, bestEstimationOfR + testIncrement);
}
if (center instanceof Array &&
typeof center[0] === "number" &&
typeof center[1] === "number" &&
u instanceof Array &&
typeof u[0] === "number" &&
typeof u[1] === "number" &&
v instanceof Array &&
typeof v[0] === "number" &&
typeof v[1] === "number" &&
typeof a === "number") {
// translate points
u = [u[0] - center[0], u[1] - center[1]];
v = [v[0] - center[0], v[1] - center[1]];
var bestDistanceBetweenPointAndEllipse = a,
point,
semiMinorAxis,
ellipsePoint1,
ellipsePoint2,
bestEstimationOfB,
bestEstimationOfR,
radPoint1 = getAngle_radians([0, 0], v),
radPoint2 = getAngle_radians([0, 0], u),
circle = 2 * Math.PI,
degree = circle / 360,
startTime = new Date().getTime(),
count = 0;
getBestEstimate(0, circle);
var ellipseModel = MakerJs.$(new MakerJs.models.Ellipse(a, bestEstimationOfB))
.rotate(MakerJs.angle.toDegrees(bestEstimationOfR), [0, 0])
.move(center)
.originate([0, 0])
.$result;
return ellipseModel;
}

Balls bouncing off of each other

I am working on this script where I have x-number bouncing balls (in this case 20 balls) in a canvas.
My question is, how do I make them bounce off each other when they hit, as well as bounce off the yellow rectangle when they hit it?
var mycanvas =document.getElementById("mycanvas");
var ctx=mycanvas.getContext("2d");
var w=500,h=500;
mycanvas.height=h;
mycanvas.width=w;
var ball=[];
function Ball(x,y,r,c,vx,vy){
this.x=x; //starting x coordinate
this.y=y; //starting x coordinate
this.r=r; //radius
this.c=c; //color
this.vx=vx; // x direction speed
this.vy=vy; // y direction speed
this.update=function(){
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, Math.PI*2, false);
ctx.fillStyle = this.c;
ctx.fill();
ctx.closePath();
this.x += this.vx;
this.y += this.vy;
//changing direction on hitting wall
if(this.y>=(w-10)||this.y<=10){
this.vy=-this.vy;
}
if(this.x>=(h-10)||this.x<=10){
this.vx=-this.vx;
}
}
}
function clearCanvas(){
ctx.clearRect(0, 0, w, h);
}
var count;
for (count = 0; count < 20; count++) {
var rndColor=Math.floor((Math.random() * 9) + 1); //random color
ball[count]= new Ball(Math.floor((Math.random() * 490) + 1),Math.floor((Math.random() * 490)+1),5,'red',5,5);
}
function update(){
var i;
clearCanvas();
//draw rectangle
ctx.rect(250, 200, 10, 100);
ctx.fillStyle = 'yellow';
ctx.fill();
for(i=0;i<count;i++){
ball[i].update();
}
}
setInterval(update, 1000/60);
There are several methods you can use. The following methods are about the simplest.
Update
I have added an example that uses the second method. See snippet at the bottom.
Defining the balls
Each example is as an object called Ball.
// x,y position of center,
// vx,vy is velocity,
// r is radius defaults 45,
// m is mass defaults to the volume of the sphere of radius r
function Ball(x, y, vx, vy, r = 45, m = (4 / 3 * Math.PI * (r ** 3)) {
this.r = r;
this.m = m;
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
}
Ball.prototype = {
// add collision functions here
};
The code assumes the balls are touching.
Elastic collisions
The logic used can be found at wikis elastic collision page
The calculation splits the forces into two parts for each ball. (4 in total for 2 balls)
The transfer of energy along the line between the balls,
The adjustment of energy per ball along the tangent of the collision point
Equal mass
Each ball has the same mass which means that the transfer of energy is balanced and can be ignored
After the function is called each ball has a new velocity vector.
Note that if you call collision and the velocities mean that the balls are moving away from each other (collision paradox) then the result will have the balls moving into each other (resolution paradox)
To keep the math simple the vector magnitudes u1, u2, u3, and u4 are converted into a unit that is the length of the line between the ball centers (square root of d)
collide(b) { // b is the ball that the collision is with
const a = this;
const x = a.x - b.x;
const y = a.y - b.y;
const d = x * x + y * y;
const u1 = (a.vx * x + a.vy * y) / d; // From this to b
const u2 = (x * a.vy - y * a.vx) / d; // Adjust self along tangent
const u3 = (b.vx * x + b.vy * y) / d; // From b to this
const u4 = (x * b.vy - y * b.vx) / d; // Adjust b along tangent
// set new velocities
b.vx = x * u1 - y * u4;
b.vy = y * u1 + x * u4;
a.vx = x * u3 - y * u2;
a.vy = y * u3 + x * u2;
},
Different masses
Each ball has its own mass and thus the transfer needs to calculate the amount of energy related to the mass that is transferred.
Only the energy transferred along the line between the balls is effect by the mass differences
collideMass(b) {
const a = this;
const m1 = a.m;
const m2 = b.m;
const x = a.x - b.x;
const y = a.y - b.y;
const d = x * x + y * y;
const u1 = (a.vx * x + a.vy * y) / d;
const u2 = (x * a.vy - y * a.vx) / d;
const u3 = (b.vx * x + b.vy * y) / d;
const u4 = (x * b.vy - y * b.vx) / d;
const mm = m1 + m2;
const vu3 = (m1 - m2) / mm * u1 + (2 * m2) / mm * u3;
const vu1 = (m2 - m1) / mm * u3 + (2 * m1) / mm * u1;
b.vx = x * vu1 - y * u4;
b.vy = y * vu1 + x * u4;
a.vx = x * vu3 - y * u2;
a.vy = y * vu3 + x * u2;
},
Example
Simple ball collision example. Balls bound by lines (Note lines have an outside and inside, if looking from the start to the end the inside is on the right)
Collisions are fully resolved in chronological order between frames. The time used is a frame where 0 is the previous frame and 1 is the current frame.
canvas.width = innerWidth -20;
canvas.height = innerHeight -20;
const ctx = canvas.getContext("2d");
const GRAVITY = 0;
const WALL_LOSS = 1;
const BALL_COUNT = 20; // approx as will not add ball if space can not be found
const MIN_BALL_SIZE = 13;
const MAX_BALL_SIZE = 20;
const VEL_MIN = 1;
const VEL_MAX = 5;
const MAX_RESOLUTION_CYCLES = 100;
Math.TAU = Math.PI * 2;
Math.rand = (min, max) => Math.random() * (max - min) + min;
Math.randI = (min, max) => Math.random() * (max - min) + min | 0; // only for positive numbers 32bit signed int
Math.randItem = arr => arr[Math.random() * arr.length | 0]; // only for arrays with length < 2 ** 31 - 1
// contact points of two circles radius r1, r2 moving along two lines (a,e)-(b,f) and (c,g)-(d,h) [where (,) is coord (x,y)]
Math.circlesInterceptUnitTime = (a, e, b, f, c, g, d, h, r1, r2) => { // args (x1, y1, x2, y2, x3, y3, x4, y4, r1, r2)
const A = a * a, B = b * b, C = c * c, D = d * d;
const E = e * e, F = f * f, G = g * g, H = h * h;
var R = (r1 + r2) ** 2;
const AA = A + B + C + F + G + H + D + E + b * c + c * b + f * g + g * f + 2 * (a * d - a * b - a * c - b * d - c * d - e * f + e * h - e * g - f * h - g * h);
const BB = 2 * (-A + a * b + 2 * a * c - a * d - c * b - C + c * d - E + e * f + 2 * e * g - e * h - g * f - G + g * h);
const CC = A - 2 * a * c + C + E - 2 * e * g + G - R;
return Math.quadRoots(AA, BB, CC);
}
Math.quadRoots = (a, b, c) => { // find roots for quadratic
if (Math.abs(a) < 1e-6) { return b != 0 ? [-c / b] : [] }
b /= a;
var d = b * b - 4 * (c / a);
if (d > 0) {
d = d ** 0.5;
return [0.5 * (-b + d), 0.5 * (-b - d)]
}
return d === 0 ? [0.5 * -b] : [];
}
Math.interceptLineBallTime = (x, y, vx, vy, x1, y1, x2, y2, r) => {
const xx = x2 - x1;
const yy = y2 - y1;
const d = vx * yy - vy * xx;
if (d > 0) { // only if moving towards the line
const dd = r / (xx * xx + yy * yy) ** 0.5;
const nx = xx * dd;
const ny = yy * dd;
return (xx * (y - (y1 + nx)) - yy * (x -(x1 - ny))) / d;
}
}
const balls = [];
const lines = [];
function Line(x1,y1,x2,y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
Line.prototype = {
draw() {
ctx.moveTo(this.x1, this.y1);
ctx.lineTo(this.x2, this.y2);
},
reverse() {
const x = this.x1;
const y = this.y1;
this.x1 = this.x2;
this.y1 = this.y2;
this.x2 = x;
this.y2 = y;
return this;
}
}
function Ball(x, y, vx, vy, r = 45, m = 4 / 3 * Math.PI * (r ** 3)) {
this.r = r;
this.m = m
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
}
Ball.prototype = {
update() {
this.x += this.vx;
this.y += this.vy;
this.vy += GRAVITY;
},
draw() {
ctx.moveTo(this.x + this.r, this.y);
ctx.arc(this.x, this.y, this.r, 0, Math.TAU);
},
interceptLineTime(l, time) {
const u = Math.interceptLineBallTime(this.x, this.y, this.vx, this.vy, l.x1, l.y1, l.x2, l.y2, this.r);
if(u >= time && u <= 1) {
return u;
}
},
checkBallBallTime(t, minTime) {
return t > minTime && t <= 1;
},
interceptBallTime(b, time) {
const x = this.x - b.x;
const y = this.y - b.y;
const d = (x * x + y * y) ** 0.5;
if(d > this.r + b.r) {
const times = Math.circlesInterceptUnitTime(
this.x, this.y,
this.x + this.vx, this.y + this.vy,
b.x, b.y,
b.x + b.vx, b.y + b.vy,
this.r, b.r
);
if(times.length) {
if(times.length === 1) {
if(this.checkBallBallTime(times[0], time)) { return times[0] }
return;
}
if(times[0] <= times[1]) {
if(this.checkBallBallTime(times[0], time)) { return times[0] }
if(this.checkBallBallTime(times[1], time)) { return times[1] }
return
}
if(this.checkBallBallTime(times[1], time)) { return times[1] }
if(this.checkBallBallTime(times[0], time)) { return times[0] }
}
}
},
collideLine(l, time) {
const x1 = l.x2 - l.x1;
const y1 = l.y2 - l.y1;
const d = (x1 * x1 + y1 * y1) ** 0.5;
const nx = x1 / d;
const ny = y1 / d;
const u = (this.vx * nx + this.vy * ny) * 2;
this.x += this.vx * time;
this.y += this.vy * time;
this.vx = (nx * u - this.vx) * WALL_LOSS;
this.vy = (ny * u - this.vy) * WALL_LOSS;
this.x -= this.vx * time;
this.y -= this.vy * time;
},
collide(b, time) {
const a = this;
const m1 = a.m;
const m2 = b.m;
const x = a.x - b.x
const y = a.y - b.y
const d = (x * x + y * y);
const u1 = (a.vx * x + a.vy * y) / d
const u2 = (x * a.vy - y * a.vx ) / d
const u3 = (b.vx * x + b.vy * y) / d
const u4 = (x * b.vy - y * b.vx ) / d
const mm = m1 + m2;
const vu3 = (m1 - m2) / mm * u1 + (2 * m2) / mm * u3;
const vu1 = (m2 - m1) / mm * u3 + (2 * m1) / mm * u1;
a.x = a.x + a.vx * time;
a.y = a.y + a.vy * time;
b.x = b.x + b.vx * time;
b.y = b.y + b.vy * time;
b.vx = x * vu1 - y * u4;
b.vy = y * vu1 + x * u4;
a.vx = x * vu3 - y * u2;
a.vy = y * vu3 + x * u2;
a.x = a.x - a.vx * time;
a.y = a.y - a.vy * time;
b.x = b.x - b.vx * time;
b.y = b.y - b.vy * time;
},
doesOverlap(ball) {
const x = this.x - ball.x;
const y = this.y - ball.y;
return (this.r + ball.r) > ((x * x + y * y) ** 0.5);
}
}
function canAdd(ball) {
for(const b of balls) {
if (ball.doesOverlap(b)) { return false }
}
return true;
}
function create(bCount) {
lines.push(new Line(-10, 10, ctx.canvas.width + 10, 5));
lines.push((new Line(-10, ctx.canvas.height - 2, ctx.canvas.width + 10, ctx.canvas.height - 10)).reverse());
lines.push((new Line(10, -10, 4, ctx.canvas.height + 10)).reverse());
lines.push(new Line(ctx.canvas.width - 3, -10, ctx.canvas.width - 10, ctx.canvas.height + 10));
while (bCount--) {
let tries = 100;
debugger
while (tries--) {
const dir = Math.rand(0, Math.TAU);
const vel = Math.rand(VEL_MIN, VEL_MAX);
const ball = new Ball(
Math.rand(MAX_BALL_SIZE + 10, canvas.width - MAX_BALL_SIZE - 10),
Math.rand(MAX_BALL_SIZE + 10, canvas.height - MAX_BALL_SIZE - 10),
Math.cos(dir) * vel,
Math.sin(dir) * vel,
Math.rand(MIN_BALL_SIZE, MAX_BALL_SIZE),
);
if (canAdd(ball)) {
balls.push(ball);
break;
}
}
}
}
function resolveCollisions() {
var minTime = 0, minObj, minBall, resolving = true, idx = 0, idx1, after = 0, e = 0;
while(resolving && e++ < MAX_RESOLUTION_CYCLES) { // too main ball may create very lone resolution cycle. e limits this
resolving = false;
minObj = undefined;
minBall = undefined;
minTime = 1;
idx = 0;
for(const b of balls) {
idx1 = idx + 1;
while(idx1 < balls.length) {
const b1 = balls[idx1++];
const time = b.interceptBallTime(b1, after);
if(time !== undefined) {
if(time <= minTime) {
minTime = time;
minObj = b1;
minBall = b;
resolving = true;
}
}
}
for(const l of lines) {
const time = b.interceptLineTime(l, after);
if(time !== undefined) {
if(time <= minTime) {
minTime = time;
minObj = l;
minBall = b;
resolving = true;
}
}
}
idx ++;
}
if(resolving) {
if (minObj instanceof Ball) {
minBall.collide(minObj, minTime);
} else {
minBall.collideLine(minObj, minTime);
}
after = minTime;
}
}
}
create(BALL_COUNT);
mainLoop();
function mainLoop() {
ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
resolveCollisions();
for(const b of balls) { b.update() }
ctx.strokeStyle = "#000";
ctx.beginPath();
for(const b of balls) { b.draw() }
for(const l of lines) { l.draw() }
ctx.stroke();
requestAnimationFrame(mainLoop);
}
<canvas id="canvas"></canvas>
To bounce balls off of one another, he's what you need to know
Have the balls collided? The way to determine is to measure the distance between the centers of the two circles. If this is less than the combined radiuses, the balls have collided
What direction should they have after colliding? Use use atan2 to calculate the angle between the centers of the two balls. Then set them in opposite directions on that angle, in a way that they don't end up deeper within each other. Of course, this simple solution ignores existing momentum that the balls may have. But doing the momentum calculation (which involves mass, speed, and current angle) is more complicated.

Excel's RATE function in JS breaks down after values get too high

Bellow script works accurately until the combination of paymentAmount/futureValue get too high, then it calculates it completely wrong.
export function rate(paymentsPerYear, paymentAmount, presentValue, futureValue, dueEndOrBeginning, interest)
{
//If interest, futureValue, dueEndorBeginning was not set, set now
if (interest == null)
interest = 0.01;
if (futureValue == null)
futureValue = 0;
if (dueEndOrBeginning == null)
dueEndOrBeginning = 0;
var FINANCIAL_MAX_ITERATIONS = 1280;//Bet accuracy with 128
var FINANCIAL_PRECISION = 0.0000001;//1.0e-8
var y, y0, y1, x0, x1 = 0, f = 0, i = 0;
var rate = interest;
if (Math.abs(rate) < FINANCIAL_PRECISION)
{
y = presentValue * (1 + paymentsPerYear * rate) + paymentAmount * (1 + rate * dueEndOrBeginning) * paymentsPerYear + futureValue;
}
else
{
f = Math.exp(paymentsPerYear * Math.log(1 + rate));
y = presentValue * f + paymentAmount * (1 / rate + dueEndOrBeginning) * (f - 1) + futureValue;
}
y0 = presentValue + paymentAmount * paymentsPerYear + futureValue;
y1 = presentValue * f + paymentAmount * (1 / rate + dueEndOrBeginning) * (f - 1) + futureValue;
// find root by Newton secant method
i = x0 = 0.0;
x1 = rate;
while ((Math.abs(y0 - y1) > FINANCIAL_PRECISION)
&& (i < FINANCIAL_MAX_ITERATIONS))
{
rate = (y1 * x0 - y0 * x1) / (y1 - y0);
x0 = x1;
x1 = rate;
if (Math.abs(rate) < FINANCIAL_PRECISION)
{
y = presentValue * (1 + paymentsPerYear * rate) + paymentAmount * (1 + rate * dueEndOrBeginning) * paymentsPerYear + futureValue;
}
else
{
f = Math.exp(paymentsPerYear * Math.log(1 + rate));
y = presentValue * f + paymentAmount * (1 / rate + dueEndOrBeginning) * (f - 1) + futureValue;
}
y0 = y1;
y1 = y;
++i;
}
return rate;
}
Working and non-working example with slightly different inputs can be found here: https://jsbin.com/ziluwup/edit?js,console
Any ideas?
EDIT: As suggested in a comment, I added correct result (0.005420910) to the example on jsbin.

Brownian motion simulation does not properly simulate particle collisions

I am trying to create a simulation for experimenting with the Brownian motion. The idea is to make numerous particles (points) each moving randomly and colliding with each other, while conserving the total energy. That, I did succeed programming.
Now the problematic part is to make these point-like particles collide with a big circle and program the collision correctly. The difference is I made the particles move towards a totally random angle after their collision with each other, but when they collide with the circle, their previous path and speed, together with the circle's path and speed determines the result. So what I tried to do is to convert to a coordinate system attached to the center of the circle, calculate the collision point of every particle, which entered into the circle in the previous step, then calculate the angle in which they arrived. Now then the momentum of the particle is decomposed into two components: the tangential component remains the same, while the radial component changes according to the laws of collisions. This affects the motion of the circle as well. After calculating all the collisions, we move back to the original reference system, and draw all the objects.
The code can be found below, and it seems to be broken. I guess I was mistaken when calculating with the angles, because some particles can be shown arriving to the circle at one point, then "exiting" through the other side of it. I just lost track in following it all. I'm a complete newbie to programming, so be... um... gentle, please. :) Another strange thing is that the circle trends to move to a direction it started to move early, which is not what it supposed to do.
The mass of the particles are given, the mass of the circle is calculated with it's radius.
You can find the simulation on this website:
http://sixy.uw.hu/brown
It is not finished yet, but the start button works fine. :)
The part of the code I'm asking about is here: (shall I give you the whole code?)
function animateParticles() {
if ($('N').value != particleCount || $('S').value != circle.R) {
reset();
}
switch (activeGraph) {
case '#graph':
chartOptions.title.text = 'Távolság';
chartOptions.axisY.title = 'd [px]';
break;
if (Date.now() > lastAnim + 22) {
context.clearRect(0, 0, canvas.width, canvas.height);
// Particles step and collide
for (var i in particle) {
// Particle steps
{
particle[i].x += particle[i].vx;
particle[i].y += particle[i].vy;
}
// If not in the circle
if ((circle.x - particle[i].x) * (circle.x - particle[i].x) + (circle.y - particle[i].y) * (circle.y - particle[i].y) > circle.R * circle.R) {
// Collides with some of the rest of the particles
for (var j = 1; j <= $('N').value; j += Math.ceil(particleCount / (Math.random() * 50 + 50))) {
// If that's not in the circle as well
if ((circle.x - particle[j].x) * (circle.x - particle[j].x) + (circle.y - particle[j].y) * (circle.y - particle[j].y) > circle.R * circle.R) {
// If not himself
if (j != i) {
if (particle[i].coll == 0) {
// Collide
if (Math.pow((particle[i].x - particle[j].x), 2) + Math.pow((particle[i].y - particle[j].y), 2) <= 4) {
var tkpx = (particle[i].vx + particle[j].vx) / 2;
var tkpy = (particle[i].vy + particle[j].vy) / 2;
var fi = Math.random() * 2 * Math.PI;
var index;
var ix = particle[i].vx;
var iy = particle[i].vy;
particle[i].vx = Math.sqrt(Math.pow((ix - tkpx), 2) + Math.pow((iy - tkpy), 2)) * Math.cos(fi) + tkpx;
particle[i].vy = Math.sqrt(Math.pow((ix - tkpx), 2) + Math.pow((iy - tkpy), 2)) * Math.sin(fi) + tkpy;
particle[j].vx = -Math.sqrt(Math.pow((ix - tkpx), 2) + Math.pow((iy - tkpy), 2)) * Math.cos(fi) + tkpx;
particle[j].vy = -Math.sqrt(Math.pow((ix - tkpx), 2) + Math.pow((iy - tkpy), 2)) * Math.sin(fi) + tkpy;
}
}
}
if (particle[i].coll >= 1) {
particle[i].coll -= 1;
}
}
}
}
// Collision with the walls
{
if (particle[i].x >= canvas.width) {
var temp;
temp = particle[i].x - canvas.width;
particle[i].x = canvas.width - temp;
particle[i].vx *= -1
}
if (particle[i].x <= 0) {
var temp;
temp = -particle[i].x;
particle[i].x = temp;
particle[i].vx *= -1;
}
if (particle[i].y >= canvas.height) {
var temp;
temp = particle[i].y - canvas.height;
particle[i].y = canvas.height - temp;
particle[i].vy *= -1;
}
if (particle[i].y <= 0) {
var temp;
temp = -particle[i].y;
particle[i].y = temp;
particle[i].vy *= -1;
}
}
}
//console.log(circle);
// Circle steps and collides with particles
var cu = 0;
var cv = 0;
for (var i in particle) {
if ((circle.x - particle[i].x) * (circle.x - particle[i].x) + (circle.y - particle[i].y) * (circle.y - particle[i].y) < circle.R * circle.R) {
var pu;
var pv;
var px;
var py;
var px0;
var py0;
var t;
var t2;
var Tx;
var Ty;
var fi;
var p;
var q;
var cuu;
var cvv;
px = particle[i].x - circle.x;
py = particle[i].y - circle.y;
pu = particle[i].vx - circle.vx;
pv = particle[i].vy - circle.vy;
px0 = px - particle[i].vx;
py0 = py - particle[i].vy;
// Calculating the meeting point of the collision
t = (-(px0 * pu + py0 * pv) - Math.sqrt(circle.R * circle.R * (pu * pu + pv * pv) - Math.pow(px0 * pv - py0 * pu, 2))) / (pu * pu + pv * pv);
t2 = ((px - px0) * (px - px0) + (py - py0) * (py - py0)) / Math.sqrt(pu * pu + pv * pv) - t;
Tx = px0 + t * pu;
Ty = py0 + t * pv;
//console.log("TX: ", Tx);
//console.log("TY: ", Ty);
// Calculating the angle
{
if (Tx > 0 && Ty >= 0) {
fi = 2 * Math.PI - Math.atan(Ty / Tx);
}
else if (Tx < 0 && Ty >= 0) {
fi = Math.PI - Math.atan(Ty / Tx);
}
else if (Tx < 0 && Ty < 0) {
fi = Math.PI / 2 + Math.atan(Ty / Tx);
}
else if (Tx > 0 && Ty < 0) {
fi = -Math.atan(Ty / Tx);
}
else if (Tx = 0 && Ty >= 0) {
fi = 3 / 2 * Math.PI;
}
else if (Tx = 0 && Ty < 0) {
fi = Math.PI / 2;
}
}
//console.log("FI:", fi);
p = pu * Math.cos(fi) + pv * Math.sin(fi);
q = -pu * Math.sin(fi) + pv * Math.cos(fi);
cuu = 2 * q / (circle.M + 1);
cvv = 0;
q *= (1 - circle.M) / (1 + circle.M);
cu += cuu * Math.cos(-fi) + cvv * Math.sin(-fi);
cv += -cuu * Math.sin(-fi) + cvv * Math.cos(-fi);
//console.log("CU: ", cu);
//console.log("CV: ", cv);
pu = p * Math.cos(-fi) + q * Math.sin(-fi);
pv = -p * Math.sin(-fi) + q * Math.cos(-fi);
px = Tx + t2 * pu;
py = Ty + t2 * pv;
particle[i].x = px + circle.x;
particle[i].y = py + circle.y;
particle[i].vx = pu + circle.vx;
particle[i].vy = pv + circle.vy;
}
}
// Moving circle
{
circle.vx += cu;
circle.vy += cv;
circle.x += circle.vx;
circle.y += circle.vy;
for (var i in particle) {
while ((circle.x - particle[i].x) * (circle.x - particle[i].x) + (circle.y - particle[i].y) * (circle.y - particle[i].y) < circle.R * circle.R) {
particle[i].x += circle.vx / Math.abs(circle.vx);
particle[i].y += circle.vy / Math.abs(circle.vy);
}
}
circlePath.push({x: circle.x, y: circle.y});
if (Date.now() > lastDraw + 500) {
items.push({
y: Math.sqrt((circle.x - canvas.width / 2) * (circle.x - canvas.width / 2) + (circle.y - canvas.height / 2) * (circle.y - canvas.height / 2)),
x: (items.length + 1) / 2
});
drawChart(activeGraph);
lastDraw = Date.now();
}
}
//console.log(circle);
drawCircle();
for (var i in particle) drawParticle(particle[i]);
lastAnim = Date.now();
}
animation = window.requestAnimationFrame(animateParticles);
}

How to draw a graph using parbola equation

I am trying to create a graph using parabola equation (y=x*x). But I am bit confused to calculate the value for control point. How should I calculate the control point value.
My JavaScript function:
function drawParabola()
{
ctx.beginPath();
for(i=-2;i<=2;i++)
{
//formual y= x * x;
y = i * i;
x = i;
if (i == -2) {
ctx.moveTo((5 + x) * 30, Math.abs((-5 + y)) * 30);
}
else {
//ctx.lineTo((5 + x) * 30, Math.abs((-5 + y)) * 30);
context.quadraticCurveTo(**?**, **?**, (5 + x) * 30, Math.abs((-5 + y)) * 30);
}
ctx.strokeStyle = 'orange';
ctx.stroke();
}
}
The control point for a quadratic curve is the intersection point of the tangents.
context.beginPath();
context.strokeStyle = 'orange';
for(i=-2;i<=2;i++) {
// Current point
x1 = i;
y1 = x1 * x1;
y1p = 2 * x1; // derivitive
// Previous point
x0 = i - 1;
y0 = x0 * x0;
y0p = 2 * x0; // derivitive
// Find intersection of tangents
// line0: y - y0 = y0p * (x - x0)
// line1: y - y1 = y1p * (x - x1)
//
// line0: y = y0p * x - y0p * x0 + y0
// line1: y = y1p * x - y1p * x1 + y1
//
// y0p * x - y0p * x0 + y0 = y1p * x - y1p * x1 + y1
// y0p * x - y1p * x = y0p * x0 - y0 - y1p * x1 + y1
// x = (y0p * x0 - y0 - y1p * x1 + y1) / (y0p - y1p)
// Intersection point of tangents
xi = (y0p * x0 - y0 - y1p * x1 + y1) / (y0p - y1p);
yi = y0p * xi - y0p * x0 + y0;
// Rescale for rendering
cx = (5 + x1) * 30;
cy = (5 + y1) * 30;
cix = (5 + xi) * 30;
ciy = (5 + yi) * 30;
if (i == -2) {
context.moveTo(cx, cy);
}
else {
//context.lineTo(cx, cy);
context.quadraticCurveTo(cix, ciy, cx, cy);
}
}
context.stroke();

Categories