I'm trying to use javascript to make an image face the mouse of the user, I'm close but my calculations are a bit off (the rotation on the x-axis seems to be inverted). I based my code of this link: rotate3d shorthand
javascript:
$(document).mousemove(function(e){
pageheight = $(document).height();
pagewidth = $(document).width();
widthpercentage = e.pageX / pagewidth;
heightpercentage = e.pageY / pageheight;
specialW = (widthpercentage * 180 - 90);
specialH = (heightpercentage * 180 - 90);
function toRadians(degrees) {
radians = degrees * (Math.PI /180);
return radians;
}
function matrix(x, y, z, rads) {
var sc = Math.sin(rads / 2) * Math.cos(rads / 2);
var sq = Math.pow(Math.sin(rads / 2), 2);
var array = [];
var a1 = 1 - 2 * (Math.pow(y, 2) + Math.pow(z, 2)) * sq,
a2 = 2 * (x * y * sq - z * sc),
a3 = 2 * (x * z * sq + y * sc),
a4 = 0,
b1 = 2 * (x * y * sq + z * sc),
b2 = 1 - 2 * (Math.pow(x, 2) + Math.pow(z, 2)) * sq,
b3 = 2 * (y * z * sq - x * sc),
b4 = 0,
c1 = 2 * (x * z * sq - y * sc),
c2 = 2 * (y * z * sq + x * sc),
c3 = 1 - 2 * (Math.pow(x, 2) + Math.pow(y, 2)) * sq,
c4 = 0,
d1 = 0,
d2 = 0,
d3 = 0,
d4 = 1;
array.push(a1, b1, c1, d1, a2, b2, c2, d2, a3, b3, c3, d3, a4, b4, c4, d4);
return array;
}
xmatrix = matrix(1, 0, 0, toRadians(specialH));
ymatrix = matrix(0, 1, 0, toRadians(specialW));
function multiply(xarray, yarray) {
var newarray = [];
var a1 = (xarray[0] * yarray[0]) + (xarray[4] * yarray[1]) + (xarray[8] * yarray[2]) + (xarray[12] * yarray[3]),
a2 = (xarray[0] * yarray[4]) + (xarray[4] * yarray[5]) + (xarray[8] * yarray[6]) + (xarray[12] * yarray[7]),
a3 = (xarray[0] * yarray[8]) + (xarray[4] * yarray[9]) + (xarray[8] * yarray[10]) + (xarray[12] * yarray[11]),
a4 = (xarray[0] * yarray[12]) + (xarray[4] * yarray[13]) + (xarray[8] * yarray[14]) + (xarray[12] * yarray[15]),
b1 = (xarray[1] * yarray[0]) + (xarray[5] * yarray[1]) + (xarray[9] * yarray[2]) + (xarray[13] * yarray[3]),
b2 = (xarray[1] * yarray[4]) + (xarray[5] * yarray[5]) + (xarray[9] * yarray[6]) + (xarray[13] * yarray[7]),
b3 = (xarray[1] * yarray[8]) + (xarray[5] * yarray[9]) + (xarray[9] * yarray[10]) + (xarray[13] * yarray[11]),
b4 = (xarray[1] * yarray[12]) + (xarray[5] * yarray[13]) + (xarray[9] * yarray[14]) + (xarray[13] * yarray[15]),
c1 = (xarray[2] * yarray[0]) + (xarray[6] * yarray[1]) + (xarray[10] * yarray[2]) + (xarray[14] * yarray[3]),
c2 = (xarray[2] * yarray[4]) + (xarray[6] * yarray[5]) + (xarray[10] * yarray[6]) + (xarray[14] * yarray[7]),
c3 = (xarray[2] * yarray[8]) + (xarray[6] * yarray[9]) + (xarray[10] * yarray[10]) + (xarray[14] * yarray[11]),
c4 = (xarray[2] * yarray[12]) + (xarray[6] * yarray[13]) + (xarray[10] * yarray[14]) + (xarray[14] * yarray[15]),
d1 = (xarray[3] * yarray[0]) + (xarray[7] * yarray[1]) + (xarray[11] * yarray[2]) + (xarray[15] * yarray[3]),
d2 = (xarray[3] * yarray[4]) + (xarray[7] * yarray[5]) + (xarray[11] * yarray[6]) + (xarray[15] * yarray[7]),
d3 = (xarray[3] * yarray[8]) + (xarray[7] * yarray[9]) + (xarray[11] * yarray[10]) + (xarray[15] * yarray[11]),
d4 = (xarray[3] * yarray[12]) + (xarray[7] * yarray[13]) + (xarray[11] * yarray[14]) + (xarray[15] * yarray[15]);
newarray.push(a1, b1, c1, d1, a2, b2, c2, d2, a3, b3, c3, d3, a4, b4, c4, d4);
return newarray;
}
var newmatrix = multiply(xmatrix, ymatrix);
$('#page1 img').css('transform', 'matrix3d(' + newmatrix[0] + ',' + newmatrix[1] + ',' + newmatrix[2] + ',' + newmatrix[3] + ',' + newmatrix[4] + ',' + newmatrix[5] + ',' + newmatrix[6] + ',' + newmatrix[7] + ',' + newmatrix[8] + ',' + newmatrix[9] + ',' + newmatrix[10] + ',' + newmatrix[11] + ',' + newmatrix[12] + ',' + newmatrix[13] + ',' + newmatrix[14] + ',' + newmatrix[15] + ')');
});
I know it's a lot of code, but if I could get a second a opinion, It would be greatly appreciated. Thanks
No plugins please.
I actually just edited the matrix() function and added this right before array.push:
if ( y = 1 ) {
a3 = a3 * -1;
c1 = c1 * -1;
}
And this fixed the issue.
Related
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.
I'm working on a SVG filters implementation in canvas but i have two problems with the composite arithmetic operator.
My implementation for this operator is based on W3C specification and firefox9 source code.
Here my code for this filter & svg exemple : Fiddle complete code
var k1Scaled = options.k1 / 255;
var k4Scaled = options.k4 * 255;
for(v=0; v<datas.length; v+=4) {
r = datasB1[v+0];
g = datasB1[v+1];
b = datasB1[v+2];
a = datasB1[v+3];
r1 = datasB2[v+0];
g1 = datasB2[v+1];
b1 = datasB2[v+2];
a1 = datasB2[v+3];
//if (a === 0 && a1 === 0) continue;
vR = k1Scaled * r1 * r + options.k2 * r + options.k3 * r1 + k4Scaled;
vG = k1Scaled * g1 * g + options.k2 * g + options.k3 * g1 + k4Scaled;
vB = k1Scaled * b1 * b + options.k2 * b + options.k3 * b1 + k4Scaled;
vA = k1Scaled * a1 * a + options.k2 * a + options.k3 * a1 + k4Scaled;
datas[v+0] = Math.min(Math.max(0, ~~vR), 255);
datas[v+1] = Math.min(Math.max(0, ~~vG), 255);
datas[v+2] = Math.min(Math.max(0, ~~vB), 255);
datas[v+3] = Math.min(Math.max(0, ~~vA), 255);
}
My first problem : I have silver color on transparent pixel due to the formula, if i decompose the formula on transparent pixel, the result is :
result = k1*i1*i2 + k2*i1 + k3*i2 + k4
where k1 = 0.5 / 255 = 0.0019, i1 = 0, i2 =0, k2 = 0.5, k3 = 0.5, k4 = 0.5 * 255 = 127
result = 0.0019*0*0 + 0.5*0 + 0.5*0 + 127 = 127 => silver on rgba
My second problem : my result is more opaque than svg exemple, i suppose webbrowser pre-multiply the rgba result but i try this but no result.
I can't use svg filter applied on canvas, i need pure javascript/canvas implementation.
If you have an idea or solution thank in advance.
Kran.
Here the solution
this._arithmetic = function(options, ctx) {
var b1, ctxB1, ctxB2, w, h, x, y, v, imgDatas, datas, imgDatasB1, datasB1, imgDatasB2, datasB2;
b1 = document.createElement("canvas");
ctxB1 = b1.getContext("2d");
ctxB1.resize(this.width,this.height);
ctxB1.drawImage(options.in.source, options.in.options.x, options.in.options.y, options.in.options.width, options.in.options.height);
imgDatasB1 = ctxB1.getImageData(0,0,this.width,this.height);
this.premultiply(imgDatasB1);
datasB1 = imgDatasB1.data;
b2 = document.createElement("canvas");
ctxB2 = b2.getContext("2d");
ctxB2.resize(this.width,this.height);
ctxB2.drawImage(options.in2.source, options.in2.options.x, options.in2.options.y, options.in2.options.width, options.in2.options.height);
ctx.resize(this.width,this.height);
ctx.drawImage(b2, 0, 0);
imgDatas = ctx.getImageData(0,0,this.width,this.height);
this.premultiply(imgDatas);
datas = imgDatas.data;
var k1Scaled = options.k1 / 255;
var k4Scaled = options.k4 * 255;
var vR,vG,vB,vA,r,g,b,a,r1,g1,b1,a1;
for(v = 0;v < datas.length;v += 4) {
r = datasB1[v + 0];
g = datasB1[v + 1];
b = datasB1[v + 2];
a = datasB1[v + 3];
r1 = datas[v + 0];
g1 = datas[v + 1];
b1 = datas[v + 2];
a1 = datas[v + 3];
vR = k1Scaled * r1 * r + options.k2 * r + options.k3 * r1 + k4Scaled;
vG = k1Scaled * g1 * g + options.k2 * g + options.k3 * g1 + k4Scaled;
vB = k1Scaled * b1 * b + options.k2 * b + options.k3 * b1 + k4Scaled;
vA = k1Scaled * a1 * a + options.k2 * a + options.k3 * a1 + k4Scaled;
vR = vR < 0 ? 0 : vR > 255 ? 255 : vR;
vG = vG < 0 ? 0 : vG > 255 ? 255 : vG;
vB = vB < 0 ? 0 : vB > 255 ? 255 : vB;
vA = vA < 0 ? 0 : vA > 255 ? 255 : vA;
datas[v + 0] = vR;
datas[v + 1] = vG;
datas[v + 2] = vB;
datas[v + 3] = vA;
}
this.unPremultiply(imgDatas);
ctx.putImageData(imgDatas,0,0);
};
the code of premultiply/unPremultiply functions is in the fiddle link.
I'm currently working on a Pinball game using the HTML5 Canvas and JavaScript. Right now I'm getting a hard time with the pixel by pixel collision, which is fundamental because of the flippers.
Right now my Bounding Box Collision seems to be working
checkCollision(element) {
if (this.checkCollisionBoundingBox(element)) {
console.log("colision with the element bounding box");
if (this.checkCollisionPixelByPixel(element)) {
return true;
} else {
return false;
}
} else {
return false;
}
}
checkCollisionBoundingBox(element) {
if (this.pos.x < element.pos.x + element.width && this.pos.x + this.width > element.pos.x && this.pos.y < element.pos.y + element.height && this.pos.y + this.height > element.pos.y) {
return true;
} else {
return false;
}
}
I've tried several ways of implementing the pixel by pixel one but for some reason it does not work perfectly (on walls, on images, on sprites etc). I'll leave them here:
checkCollisionPixelByPixel(element) {
var x_left = Math.floor(Math.max(this.pos.x, element.pos.x));
var x_right = Math.floor(Math.min(this.pos.x + this.width, element.pos.x + element.width));
var y_top = Math.floor(Math.max(this.pos.y, element.pos.y));
var y_bottom = Math.floor(Math.min(this.pos.y + this.height, element.pos.y + element.height));
for (var y = y_top; y < y_bottom; y++) {
for (var x = x_left; x < x_right; x++) {
var x_0 = Math.round(x - this.pos.x);
var y_0 = Math.round(y - this.pos.y);
var n_pix = y_0 * (this.width * this.total) + (this.width * (this.actual-1)) + x_0; //n pixel to check
var pix_op = this.imgData.data[4 * n_pix + 3]; //opacity (R G B A)
var element_x_0 = Math.round(x - element.pos.x);
var element_y_0 = Math.round(y - element.pos.y);
var element_n_pix = element_y_0 * (element.width * element.total) + (element.width * (element.actual-1)) + element_x_0; //n pixel to check
var element_pix_op = element.imgData.data[4 * element_n_pix + 3]; //opacity (R G B A)
console.log(element_pix_op);
if (pix_op == 255 && element_pix_op == 255) {
console.log("Colision pixel by pixel");
/*Debug*/
/*console.log("This -> (R:" + this.imgData.data[4 * n_pix] + ", G:" + this.imgData.data[4 * n_pix + 1] + ", B:" + this.imgData.data[4 * n_pix + 2] + ", A:" + pix_op + ")");
console.log("Element -> (R:" + element.imgData.data[4 * element_n_pix] + ", G:" + element.imgData.data[4 * element_n_pix + 1] + ", B:" + element.imgData.data[4 * element_n_pix + 2] + ", A:" + element_pix_op + ")");
console.log("Collision -> (x:" + x + ", y:" + y +")");
console.log("This(Local) -> (x:" + x_0 + ", y:" + y_0+")");
console.log("Element(Local) -> (x:" + element_x_0 + ", y:" + element_y_0+")");*/
/*ball vector*/
var vector = {
x: (x_0 - Math.floor(this.imgData.width / 2)),
y: -(y_0 - Math.floor(this.imgData.height / 2))
};
//console.log("ball vector -> ("+vector.x+", "+vector.y+") , Angulo: "+ Math.atan(vector.y/vector.x)* 180/Math.PI);
// THIS WAS THE FIRST TRY, IT DIDN'T WORK WHEN THE BALL WAS GOING NORTHEAST AND COLLIDED WITH A WALL. DIDN'T WORK AT ALL WITH SPRITES
//this.angle = (Math.atan2(vector.y, vector.x) - Math.PI) * (180 / Math.PI);
// THIS WAS THE SECOND ATTEMPT, WORKS WORSE THAN THE FIRST ONE :/
//normal vector
var normal = {
x: (x_0 - (this.imgData.width / 2)),
y: -(y_0 - (this.imgData.height / 2))
};
//Normalizar o vetor
var norm = Math.sqrt(normal.x * normal.x + normal.y * normal.y);
if (norm != 0) {
normal.x = normal.x / norm;
normal.y = normal.y / norm;
}
var n_rad = Math.atan2(normal.y, normal.x);
var n_deg = (n_rad + Math.PI) * 180 / Math.PI;
console.log("Vetor Normal -> (" + normal.x + ", " + normal.y + ") , Angulo: " + n_deg);
//Vetor Velocidade
var velocity = {
x: Math.cos((this.angle * Math.PI / 180) - Math.PI),
y: Math.sin((this.angle * Math.PI / 180) - Math.PI)
};
console.log("Vetor Velocidade -> (" + velocity.x + ", " + velocity.y + ") , Angulo: " + this.angle);
//Vetor Reflexao
var ndotv = normal.x * velocity.x + normal.y * velocity.y;
var reflection = {
x: -2 * ndotv * normal.x + velocity.x,
y: -2 * ndotv * normal.y + velocity.y
};
var r_rad = Math.atan2(reflection.y, reflection.x);
var r_deg = (r_rad + Math.PI) * 180 / Math.PI;
console.log("Vetor Reflexao -> (" + reflection.x + ", " + reflection.y + ") , Angulo: " + r_deg);
this.angle = r_deg;
return true;
}
}
}
return false;
}
}
The ball class
class Ball extends Element {
constructor(img, pos, width, height, n, sound, angle, speed) {
super(img, pos, width, height, n, sound);
this.angle = angle; //direction [0:360[
this.speed = speed;
}
move(ctx, cw, ch) {
var rads = this.angle * Math.PI / 180
var vx = Math.cos(rads) * this.speed / 60;
var vy = Math.sin(rads) * this.speed / 60;
this.pos.x += vx;
this.pos.y -= vy;
ctx.clearRect(0, 0, cw, ch);
this.draw(ctx, 1);
}
}
Assuming a "flipper" is composed of 2 arcs and 2 lines it would be much faster to do collision detection mathematically rather than by the much slower pixel-test method. Then you just need 4 math collision tests.
Even if your flippers are a bit more complicated than arcs+lines, the math hit tests would be "good enough" -- meaning in your fast-moving game, the user cannot visually notice the approximate math results vs the pixel-perfect results and the difference between the 2 types of tests will not affect gameplay at all. But the pixel-test version will take magnitudes more time and resources to accomplish. ;-)
First two circle-vs-circle collision tests:
function CirclesColliding(c1,c2){
var dx=c2.x-c1.x;
var dy=c2.y-c1.y;
var rSum=c1.r+c2.r;
return(dx*dx+dy*dy<=rSum*rSum);
}
Then two circle-vs-line-segment collision tests:
// [x0,y0] to [x1,y1] define a line segment
// [cx,cy] is circle centerpoint, cr is circle radius
function isCircleSegmentColliding(x0,y0,x1,y1,cx,cy,cr){
// calc delta distance: source point to line start
var dx=cx-x0;
var dy=cy-y0;
// calc delta distance: line start to end
var dxx=x1-x0;
var dyy=y1-y0;
// Calc position on line normalized between 0.00 & 1.00
// == dot product divided by delta line distances squared
var t=(dx*dxx+dy*dyy)/(dxx*dxx+dyy*dyy);
// calc nearest pt on line
var x=x0+dxx*t;
var y=y0+dyy*t;
// clamp results to being on the segment
if(t<0){x=x0;y=y0;}
if(t>1){x=x1;y=y1;}
return( (cx-x)*(cx-x)+(cy-y)*(cy-y) < cr*cr );
}
I would like to use this JavaScript matrix library: Matrix3D
My target is to implement a function which takes the CSS transform properties as arguments and returns with the proper matrix3d() CSS transform declaration.
function 3d(x, y, z, rotateX, rotateY, rotateZ){
var m = Matrix3D.create();
Matrix3D.translateX(m, x);
Matrix3D.translateY(m, y);
Matrix3D.translateZ(m, z);
Matrix3D.rotateX(m,this.data.rotateX);
Matrix3D.rotateY(m,this.data.rotateY);
Matrix3D.rotateZ(m,this.data.rotateZ);
return Matrix3D.toTransform3D(m);
}
It works fine for the x,y,z and the rotateZ parameters, but it is unable to merge the rotation matrices into one matrix, instead it overwrites the rotation.
Could you help me how should I combine matrices to behave in the right way?
UPDATE #1
I just found out that I should need to create a quaternion from the three euler rotation axis. euler to quaternion
function eulerToQuaternion(rotateX, rotateY, rotateZ) {
// Assuming the angles are in radians.
var c1 = Math.cos(rotateX / 2),
s1 = Math.sin(rotateX / 2),
c2 = Math.cos(rotateY / 2),
s2 = Math.sin(rotateY / 2),
c3 = Math.cos(rotateZ / 2),
s3 = Math.sin(rotateZ / 2),
c1c2 = c1 * c2,
s1s2 = s1 * s2,
w = c1c2 * c3 - s1s2 * s3,
x = c1c2 * s3 + s1s2 * c3,
y = s1 * c2 * c3 + c1 * s2 * s3,
z = c1 * s2 * c3 - s1 * c2 * s3;
return [w, x, y, z]
}
function deg2rad(deg) {
return deg * (Math.PI / 180);
};
console.log(eulerToQuaternion(deg2rad(45), 0, deg2rad(45)));
But here I'm stuck again. How can I add this quaternion to my matrix?
Found the solution:
function a(x, y, z, scaleX, scaleY, rotateX, rotateY, rotateZ) {
var D = 2;
var Y = Math.cos(rotateX * (Math.PI / 180)).toFixed(D),
Z = Math.sin(rotateX * (Math.PI / 180)).toFixed(D),
b = Math.cos(rotateY * (Math.PI / 180)).toFixed(D),
F = Math.sin(rotateY * (Math.PI / 180)).toFixed(D),
I = Math.cos(rotateZ * (Math.PI / 180)).toFixed(D),
P = Math.sin(rotateZ * (Math.PI / 180)).toFixed(D);
var a = new Array(16);
a[0] = b * I * scaleX;
a[1] = -1 * P;
a[2] = F;
a[3] = 0;
a[4] = P;
a[5] = Y * I * scaleY;
a[6] = Z;
a[7] = 0;
a[8] = -1 * F;
a[9] = -1 * Z;
a[10] = b * Y;
a[11] = 0;
a[12] = x;
a[13] = y;
a[14] = z;
a[15] = 1;
console.log("transform: matrix3d(" + a[0] + "," + a[1] + "," + a[2] + "," + a[3] + "," + a[4] + "," + a[5] + "," + a[6] + "," + a[7] + "," + a[8] + "," + a[9] + "," + a[10] + "," + a[11] + "," + a[12] + "," + a[13] + "," + a[14] + "," + a[15] + ");");
}
What about passing 2 parameters to the Matrix3D.rotateXYZ() method like below
Matrix3D.rotateX(m, this.data.rotateX)
I don't know which version you are using, but that method needs 2 parameters according to https://gist.github.com/f5io/7466669.
If you omit the first parameter, this.data.rotateX will be understood as a result array, not a rotation, and this is not what you wanted to do.
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;
}