How can I do this math formula in JavaScript - javascript

I need to work out what is the volume of liquid left in a cylinder on its side in JavaScript, how will I do the following in code?

You can try something like this.I have used Math.acos and Math.pow.And rest is simple Mathematics.
Since Math.acos returns NaN if the number is not between (-1 and 1)
,so I have checked before if the acos returns NaN
function volume(diameter, depth, length) {
let R = diameter / 2;
if (Math.acos((R - depth )/ R) != NaN) {
let a = Math.pow(R, 2) * Math.acos((R - depth) / R) - (R - depth) * (Math.pow((2 * R * depth - Math.pow(depth, 2)), 0.5))
return a * length;
} else {
return "Cylinder radius can't be less than depth"
}
}
// returns volume in meter cube
// 1 meter cube =1000l
console.log(volume(1.08, 0.72, 2.40)*1000,"L")

You can use:
** operator for powers (or Math.pow)
Math.acos for cos^(-1)
Math.sqrt for the square root
console.log(calculateVolumeInCylinder(1.08, 2.4, 0.72))
/**
* #param {number} Dm - Cylinder diameter in meters.
* #param {number} L - Cylinder length in meters.
* #param {number} Dp - Depth in meters.
* #returns {number} Volume in liters.
*/
function calculateVolumeInCylinder(Dm, L, Dp) {
let R = Dm / 2,
// R^2 cos^-1(R-D/R)
sA = R ** 2 * Math.acos((R - Dp) / R),
// (R-D)
sB = (R - Dp),
// SQRT(2RD-D^2)
sC = Math.sqrt(2 * R * Dp - Dp ** 2);
return (L * (sA - sB * sC)) * 1000;
}

Hi Shubh and Matt Major Thanks!!!! i manage to do it via the following.
function round(d)
// Returns a number rounded to 4 decimal places.
{ var multiplier = 10000;
return Math.round(d*multiplier) / multiplier;
};
function negative(n)
{ if(n<0)
complain("Negative input");
return (n<0);
}
function calculate(vdiam,vlen,vdepth){
//var vdiam = 1.08;
//var vlen = 2.40;
//var vdepth = 0.72;
var res = 0; //result
//Convert inputs to numbers
d = new Number(vdiam);
l = new Number(vlen);
h = new Number(vdepth);
r = d/2;
if(negative(d)) return;
if(negative(l)) return;
if(negative(h)) return;
//make sure it's all kosher
if(h>d)
{ console.log("Depth exceeds diameter");
return;
}
//calculate
var segArea =r*r*Math.acos((r-h)/r) - (r-h)*Math.sqrt(2*r*h-h*h);
res = segArea*l;
if(isNaN(res))
{ console.log("Inputs must be positive numbers");
res = "";
return;
}
res = res*1000;
return round(res);
}
alert(calculate(1.08,2.40,0.72));

Related

How to implement inverse cosine in react js without using math library?

How can I use inverse cosine here?
const volumeCalculator = () => {
const dens = tank.density;
const lHeight = pressure / (dens * 9.81);
// const resHeight = tank.height - lHeight;
const len = tank.length;
const rad = tank.radius;
const circum = 3.1416 * (rad ** 2);
if (tank.vertical === 'Vertical') {
const volume = circum * lHeight;
return volume;
}
const volume = [Math.acos((rad - lHeight) / rad)(rad * 2) - (rad - lHeight) * Math.sqrt(2 * rad * lHeight - (lHeight * 2))] * len;
return volume;
};
I am expecting inverse cosine result on a number.

How to visualize Fourier series / Fourier coefficients?

I'm currently having difficulties at visualizing Fourier series. I tried the same thing about three times in order to find errors but in vain.
Now I even don't know what is wrong with my code or understanding of Fourier series.
What I'm trying to make is a thing like shown in the following Youtube video: https://youtu.be/r6sGWTCMz2k
I think I know what is Fourier series a bit. I can prove this by showing my previous works:
(1) square wave approximation
(2) parameter
So now I would like to draw more complicated thing in a parametric way. Please let me show the process I've walked.
① From svg path, get coordinates. For example,
// svg path
const d = 'M 0 0 L 20 30 L 10 20 ... ... ... Z';
↓
↓ convert with some processing...
↓
const cx = [0, 20, 10, ...]; // function Fx(t)
const cy = [0, 30, 20, ...]; // function Fy(t)
② Get Fourier coefficients from Fx(t), Fy(t), respectively. After that, I can get approximated coordinates by calculating Fourier series respectively by using the coefficients I got. For example,
Let's say I have a0_x, an_x, bn_x, a0_y, an_y, bn_y.
Then, Fx(t) = a0_x + an_x[1] * cos(1wt) + bn_x[1] * cos(1wt)
+ an_x[2] * cos(2wt) + bn_x[2] * cos(2wt) + ...;
Fy(t) = a0_y + an_y[1] * cos(1wt) + bn_y[1] * cos(1wt)
+ an_y[2] * cos(2wt) + bn_y[2] * cos(2wt) + ...;
Therefore a set of points (Fx(t), Fy(t)) is an approximated path!
This is all! Only thing left is just drawing!
Meanwhile, I processed the data in the following way:
const d = [svg path data];
const split = d.split(/[, ]/);
const points = get_points(split);
const normalized = normalize(points);
const populated = populate(normalized, 8);
const cx = populated.x; // Fx(t)
const cy = populated.y; // Fy(t)
/**
* This function does the below job.
* populate([0,3,6], 2) => output 0 12 3 45 6
* populate([0,4,8], 3) => output 0 123 4 567 8
*/
function populate(data, n) {
if (data.x.length <= 1) throw new Error('NotEnoughData');
if (n < 1) throw new Error('InvalidNValue');
const arr_x = new Array(data.x.length + (data.x.length - 1) * n);
const arr_y = new Array(data.y.length + (data.y.length - 1) * n);
for (let i = 0; i < data.x.length; i++) {
arr_x[i * (n + 1)] = data.x[i];
arr_y[i * (n + 1)] = data.y[i];
}
for (let i = 0; i <= arr_x.length - n - 1 - 1; i += (n + 1)) {
const x_interpolation = (arr_x[i + n + 1] - arr_x[i]) / (n + 1);
const y_interpolation = (arr_y[i + n + 1] - arr_y[i]) / (n + 1);
for (let j = 1; j <= n; j++) {
arr_x[i + j] = arr_x[i] + x_interpolation * j;
arr_y[i + j] = arr_y[i] + y_interpolation * j;
}
}
return { x: arr_x, y: arr_y };
}
// This function makes all values are in range of [-1, 1].
// I just did it... because I don't want to deal with big numbers (and not want numbers having different magnitude depending on data).
function normalize(obj) {
const _x = [];
const _y = [];
const biggest_x = Math.max(...obj.x);
const smallest_x = Math.min(...obj.x);
const final_x = Math.max(Math.abs(biggest_x), Math.abs(smallest_x));
const biggest_y = Math.max(...obj.y);
const smallest_y = Math.min(...obj.y);
const final_y = Math.max(Math.abs(biggest_y), Math.abs(smallest_y));
for (let i = 0; i < obj.x.length; i++) {
_x[i] = obj.x[i] / final_x;
_y[i] = obj.y[i] / final_y;
}
return { x: _x, y: _y };
}
// returns Fx(t) and Fy(t) from svg path data
function get_points(arr) {
const x = [];
const y = [];
let i = 0;
while (i < arr.length) {
const path_command = arr[i];
if (path_command === "M") {
x.push(Number(arr[i + 1]));
y.push(Number(arr[i + 2]));
i += 3;
} else if (path_command === 'm') {
if (i === 0) {
x.push(Number(arr[i + 1]));
y.push(Number(arr[i + 2]));
i += 3;
} else {
x.push(x.at(-1) + Number(arr[i + 1]));
y.push(y.at(-1) + Number(arr[i + 2]));
i += 3;
}
} else if (path_command === 'L') {
x.push(Number(arr[i + 1]));
y.push(Number(arr[i + 2]));
i += 3;
} else if (path_command === 'l') {
x.push(x.at(-1) + Number(arr[i + 1]));
y.push(y.at(-1) + Number(arr[i + 2]));
i += 3;
} else if (path_command === 'H') {
x.push(Number(arr[i + 1]));
y.push(y.at(-1));
i += 2;
} else if (path_command === 'h') {
x.push(x.at(-1) + Number(arr[i + 1]));
y.push(y.at(-1));
i += 2;
} else if (path_command === 'V') {
x.push(x.at(-1));
y.push(Number(arr[i + 1]));
i += 2;
} else if (path_command === 'v') {
x.push(x.at(-1));
y.push(y.at(-1) + Number(arr[i + 1]));
i += 2;
} else if (path_command === 'Z' || path_command === 'z') {
i++;
console.log('reached to z/Z, getting points done');
} else if (path_command === 'C' || path_command === 'c' || path_command === 'S' || path_command === 's' || path_command === 'Q' || path_command === 'q' || path_command === 'T' || path_command === 't' || path_command === 'A' || path_command === 'a') {
throw new Error('unsupported path command, getting points aborted');
} else {
x.push(x.at(-1) + Number(arr[i]));
y.push(y.at(-1) + Number(arr[i + 1]));
i += 2;
}
}
return { x, y };
}
Meanwhile, in order to calculate Fourier coefficients, I used numerical integration. This is the code.
/**
* This function calculates Riemann sum (area approximation using rectangles).
* #param {Number} div division number (= number of rectangles to be used)
* #param {Array | Function} subject subject of integration
* #param {Number} start where to start integration
* #param {Number} end where to end integration
* #param {Number} nth this parameter will be passed to 'subject'
* #param {Function} paramFn this parameter will be passed to 'subject'
* #returns {Number} numerical-integrated value
*/
function numerical_integration(div, subject, start, end, nth = null, paramFn = null) {
if (div < 1) throw new Error(`invalid div; it can't be 0 or 0.x`);
let sum = 0;
const STEP = 1 / div;
const isSubjectArray = Array.isArray(subject);
if (isSubjectArray) {
for (let t = start; t < end; t++) {
for (let u = 0; u < div; u++) {
sum += subject[t + 1] * STEP;
}
}
} else {
for (let t = start; t < end; t++) {
for (let u = 0; u < div; u++) {
const period = end - start;
const isParamFnArray = Array.isArray(paramFn);
if (isParamFnArray) sum += subject((t + 1), period, nth, paramFn) * STEP;
else sum += subject(((t + STEP) + STEP * u), period, nth, paramFn) * STEP;
}
}
}
return sum;
// console.log(numerical_integration(10, (x) => x ** 3, 0, 2));
}
The approximation is near. For (x) => x, division 10, from 0 to 2, the approximation is 2.1 while actual answer is 2. For (x) => x ** 2, division 10, from 0 to 2, the approximation is 2.87, while actual answer is 2.67. For (x) => x ** 3, division 10, from 0 to 2, the approximation is 4.41, while actual answer is 4.
And I found a0, an, bn by the following: (※ You can find Fourier coefficients formulas in my previous question)
/**
* This function will be passed to 'getAn' function.
* #param {Number} t this function is a function of time
* #param {Number} period period of a function to be integrated
* #param {Number} nth integer multiple
* #param {Array | Function} paramFn
* #returns {Number} computed value
*/
function fc(t, period, nth, paramFn) {
const isParamFnArray = Array.isArray(paramFn);
const w = 2 * Math.PI / period;
if (isParamFnArray) return paramFn[t] * Math.cos(nth * w * t);
else return paramFn(t) * Math.cos(nth * w * t);
}
// This function will be passed to 'getBn' function.
function fs(t, period, nth, paramFn) {
const isParamFnArray = Array.isArray(paramFn);
const w = 2 * Math.PI / period;
if (isParamFnArray) return paramFn[t] * Math.sin(nth * w * t);
else return paramFn(t) * Math.sin(nth * w * t);
}
/**
* This function returns a0 value.
* #param {Number} period period of a function to be integrated
* #param {Array | Function} intgFn function to be intergrated
* #param {Number} div number of rectangles to use
* #returns {Number} a0 value
*/
// Why * 30? in order to scale up
// Why - 1? because arr[arr.length] is undefined.
function getA0(period, intgFn, div) {
return 30 * numerical_integration(div, intgFn, 0, period - 1) / period;
}
/**
* This function returns an values.
* #param {Number} period period of a function to be integrated
* #param {Number} div number of rectangles to use
* #param {Number} howMany number of an values to be calculated
* #param {Array | Function} paramFn function to be integrated
* #returns {Array} an values
*/
function getAn(period, div, howMany, paramFn) {
const an = [];
for (let n = 1; n <= howMany; n++) {
const value = 30 * numerical_integration(div, fc, 0, period - 1, n, paramFn) * 2 / period;
an.push(value);
}
return an;
}
// This function returns bn values.
function getBn(period, div, howMany, paramFn) {
const bn = [];
for (let n = 1; n <= howMany; n++) {
const value = 30 * numerical_integration(div, fs, 0, period - 1, n, paramFn) * 2 / period;
bn.push(value);
}
return bn;
}
const xa0 = getA0(cx.length, cx, 10);
const xan = getAn(cx.length, 10, 100, cx);
const xbn = getBn(cx.length, 10, 100, cx);
const ya0 = getA0(cy.length, cy, 10);
const yan = getAn(cy.length, 10, 100, cy);
const ybn = getBn(cy.length, 10, 100, cy);
However, the result was not a thing I wanted... It was a weird shape... Maybe this is life...
The below is the canvas drawing code:
const $cvs = document.createElement('canvas');
const cctx = $cvs.getContext('2d');
$cvs.setAttribute('width', 1000);
$cvs.setAttribute('height', 800);
$cvs.setAttribute('style', 'border: 1px solid black;');
document.body.appendChild($cvs);
window.requestAnimationFrame(draw_tick);
// offset
const xoo = { x: 200, y: 600 }; // x oscillator offset
const yoo = { x: 600, y: 200 }; // y ~
// path
const path = [];
// drawing function
let deg = 0;
function draw_tick() {
const rAF = window.requestAnimationFrame(draw_tick);
// initialize
cctx.clearRect(0, 0, 1000, 800);
// y oscillator
const py = { x: 0, y: 0 };
// a0
// a0 circle
cctx.beginPath();
cctx.strokeStyle = 'black';
cctx.arc(yoo.x + py.x, yoo.y + py.y, Math.abs(ya0), 0, 2 * Math.PI);
cctx.stroke();
// a0 line
cctx.beginPath();
cctx.strokeStyle = 'black';
cctx.moveTo(yoo.x + py.x, yoo.y + py.y);
py.x += ya0 * Math.cos(0 * deg * Math.PI / 180);
py.y += ya0 * Math.sin(0 * deg * Math.PI / 180);
cctx.lineTo(yoo.x + py.x, yoo.y + py.y);
cctx.stroke();
// an
for (let i = 0; i < yan.length; i++) {
const radius = yan[i];
// an circles
cctx.beginPath();
cctx.strokeStyle = 'black';
cctx.arc(yoo.x + py.x, yoo.y + py.y, Math.abs(radius), 0, 2 * Math.PI);
cctx.stroke();
// an lines
cctx.beginPath();
cctx.strokeStyle = 'black';
cctx.moveTo(yoo.x + py.x, yoo.y + py.y);
py.x += radius * Math.cos((i+1) * deg * Math.PI / 180);
py.y += radius * Math.sin((i+1) * deg * Math.PI / 180);
cctx.lineTo(yoo.x + py.x, yoo.y + py.y);
cctx.stroke();
}
// bn
for (let i = 0; i < ybn.length; i++) {
const radius = ybn[i];
// bn circles
cctx.beginPath();
cctx.strokeStyle = 'black';
cctx.arc(yoo.x + py.x, yoo.y + py.y, Math.abs(radius), 0, 2 * Math.PI);
cctx.stroke();
// bn lines
cctx.beginPath();
cctx.strokeStyle = 'black';
cctx.moveTo(yoo.x + py.x, yoo.y + py.y);
py.x += radius * Math.cos((i+1) * deg * Math.PI / 180);
py.y += radius * Math.sin((i+1) * deg * Math.PI / 180);
cctx.lineTo(yoo.x + py.x, yoo.y + py.y);
cctx.stroke();
}
// x oscillator
const px = { x: 0, y: 0 };
// a0
// a0 circle
cctx.beginPath();
cctx.strokeStyle = 'black';
cctx.arc(yoo.x + py.x, yoo.y + py.y, Math.abs(xa0), 0, 2 * Math.PI);
cctx.stroke();
// a0 line
cctx.beginPath();
cctx.strokeStyle = 'black';
cctx.moveTo(yoo.x + py.x, yoo.y + py.y);
py.x += xa0 * Math.cos(0 * deg * Math.PI / 180);
py.y += xa0 * Math.sin(0 * deg * Math.PI / 180);
cctx.lineTo(yoo.x + py.x, yoo.y + py.y);
cctx.stroke();
// an
for (let i = 0; i < xan.length; i++) {
const radius = xan[i];
// an circles
cctx.beginPath();
cctx.strokeStyle = 'black';
cctx.arc(xoo.x + px.x, xoo.y + px.y, Math.abs(radius), 0, 2 * Math.PI);
cctx.stroke();
// an lines
cctx.beginPath();
cctx.strokeStyle = 'black';
cctx.moveTo(xoo.x + px.x, xoo.y + px.y);
px.x += radius * Math.cos((i+1) * deg * Math.PI / 180);
px.y += radius * Math.sin((i+1) * deg * Math.PI / 180);
cctx.lineTo(xoo.x + px.x, xoo.y + px.y);
cctx.stroke();
}
// bn
for (let i = 0; i < xbn.length; i++) {
const radius = xbn[i];
// bn circles
cctx.beginPath();
cctx.strokeStyle = 'black';
cctx.arc(xoo.x + px.x, xoo.y + px.y, Math.abs(radius), 0, 2 * Math.PI);
cctx.stroke();
// bn lines
cctx.beginPath();
cctx.strokeStyle = 'black';
cctx.moveTo(xoo.x + px.x, xoo.y + px.y);
px.x += radius * Math.cos((i+1) * deg * Math.PI / 180);
px.y += radius * Math.sin((i+1) * deg * Math.PI / 180);
cctx.lineTo(xoo.x + px.x, xoo.y + px.y);
cctx.stroke();
}
// y oscillator line
cctx.strokeStyle = 'black';
cctx.beginPath();
cctx.moveTo(yoo.x + py.x, yoo.y + py.y);
cctx.lineTo(xoo.x + px.x, yoo.y + py.y);
cctx.stroke();
// x oscillator line
cctx.strokeStyle = 'black';
cctx.beginPath();
cctx.moveTo(xoo.x + px.x, xoo.y + px.y);
cctx.lineTo(xoo.x + px.x, yoo.y + py.y);
cctx.stroke();
// path
path.push({ x: px.x, y: py.y });
cctx.beginPath();
cctx.strokeStyle = 'black';
cctx.moveTo(200 + path[0].x, 200 + path[0].y);
for (let i = 0; i < path.length; i++) {
cctx.lineTo(200 + path[i].x, 200 + path[i].y);
}
cctx.stroke();
// degree update
if (deg === 359) {
window.cancelAnimationFrame(rAF);
} else {
deg++;
}
}
So! I decided to be logical. First, I checked whether the converted path data is correct by drawing it at canvas. The below is the canvas code and the data.
let count = 0;
function draw_tick2() {
const rAF = window.requestAnimationFrame(draw_tick2);
const s = 100; // scale up
// initialize
cctx.clearRect(0, 0, 1000, 800);
cctx.beginPath();
// 200 has no meaning I just added it to move the path.
for (let i = 0; i < count; i++) {
if (i === 0) cctx.moveTo(200 + s * cx[i], 200 + s * cy[i]);
else cctx.lineTo(200 + s * cx[i], 200 + s * cy[i]);
}
cctx.stroke();
if (count < cx.length - 1) {
count++;
} else {
window.cancelAnimationFrame(rAF);
}
}
const paimon = 'm 0,0 -2.38235,-2.87867 -1.58823,-1.29045 -1.9853,-0.893384 -3.17647,-0.39706 1.58824,-1.98529 1.09191,-2.08456 v -2.38235 l -0.79412,-2.87868 1.88603,2.18383 1.6875,1.88602 1.78677,0.99265 1.78676,0.39706 1.78676,-0.19853 -1.6875,1.58824 -0.69485,1.68749 -0.0993,2.084564 0.39706,2.18383 9.62867,3.87132 2.77941,1.9853 4.66544,-1.09192 3.07721,-1.88603 1.9853,-2.58088 -3.97059,0.49633 -3.375,-0.79412 -2.87868,-2.58088 -2.08456,-3.077214 2.38235,1.48897 2.08456,0.19853 3.57353,-0.89338 2.58089,-2.48162 -3.07721,0.39706 -3.87132,-1.88603 -2.97794,-2.08456 -2.48162,-2.87868 -3.87133,-4.06985 -4.06985,-2.68015 -5.95588,-2.58088 -5.85662,-0.79412 -5.45956,0.99265 0.59559,1.6875 -0.99265,1.09191 -0.79412,3.47427 -1.29044,-2.97794 -0.89338,-1.19118 0.79412,-1.48897 1.6875,-0.79412 0.39706,-3.772057 1.48897,1.290441 1.78676,0.09926 -2.08456,-1.985293 1.78677,-0.893382 4.36765,-0.19853 4.86397,0.992648 1.19117,1.091912 -2.38235,1.985301 3.17647,-0.49633 2.87868,-2.680149 -3.57353,-2.580881 -5.45956,-1.488972 h -4.46691 l -3.6728,-3.176471 -0.79412,1.389706 -0.79411,-1.488969 0.69485,-0.595588 -1.58824,-3.871325 -0.39706,3.672795 -0.69485,0.297794 0.89338,1.091911 v 1.091912 h -1.19113 l -0.59559,-0.992648 -1.98529,2.878677 -4.06986,1.588236 -4.26838,1.985293 3.27574,3.871329 2.87867,1.88603 2.58088,0.29779 -2.58088,-1.58823 -0.89338,-2.084566 4.86397,-0.992645 -1.19118,2.382351 h 1.58824 l 1.48897,-1.88603 0.29779,2.77942 -2.38235,2.38235 -3.57353,2.87868 -3.97059,4.86397 -2.08456,3.67279 -2.58088,2.58088 -2.68015,1.09192 -3.17647,0.0993 -1.3897,-0.69485 1.09191,3.17647 2.18382,3.573534 3.375,2.38235 -1.78676,5.85662 -1.38971,6.05514 0.39706,4.36765 1.38971,4.66544 3.87132,4.46691 -0.79412,-3.57352 -0.49632,-4.06986 v -2.48162 l 1.78676,5.85662 3.07721,3.17647 3.07721,1.29044 3.37499,0.79412 2.28309,-0.89338 0.69486,-1.48897 -1.19118,0.49632 -2.48162,-1.98529 -2.28309,-2.87868 2.28309,2.48162 h 0.99265 l 0.69485,-0.49632 0.2978,-1.19118 0.0993,-0.79412 -0.89339,0.59559 -1.58823,-0.99265 -1.29044,-1.3897 -1.19118,-2.38236 -0.89338,-4.86397 -0.0993,-4.56617 0.29779,-4.96324 0.39706,0.89338 1.19118,-0.44669 0.0496,-0.89338 1.09191,0.69485 1.48897,0.2978 1.53861,0.89338 0.99264,0.64522 h -0.79411 l 0.49632,2.43199 -0.44669,1.58823 -1.78676,0.39706 -1.24081,-1.24081 -0.24817,-1.43934 0.84375,-0.94301 1.19118,-0.49633 1.14154,0.94302 0.24816,1.14154 -0.0993,1.48897 -1.83639,0.64523 -1.58824,-1.53861 -0.44669,-1.48897 -0.24816,-2.18382 -1.43934,0.99264 0.0496,-0.99264 -0.44669,1.78676 0.69485,3.12684 1.09192,4.26838 1.78676,1.78677 6.89889,3.02757 -2.53124,0.99265 -3.17647,1.3897 -0.79412,0.39706 0.59559,0.39706 1.34007,-0.69485 0.0496,1.19117 1.98529,-0.39705 2.68015,-0.44669 -0.2978,-1.93567 0.79412,1.58824 2.82905,-0.44669 4.06985,-1.34008 1.04229,-0.59559 -0.2978,-1.78676 -0.34743,-1.73713 -4.9136,2.48162 -2.58088,0.94301 -3.17648,-4.81434 1.53861,0.49633 1.3897,0.0496 1.43935,-0.24816 -1.34008,0.24816 h -1.58824 l -1.41452,-0.54596 3.12684,4.78953 2.63052,-0.89339 4.86397,-2.4568 2.65533,-2.08456 0.39706,-5.90625 -0.84375,1.5386 -1.14155,0.54596 -1.5386,0.19853 -1.29044,-0.89338 -0.59559,-1.09191 -0.24816,-1.73714 0.24816,-1.3897 -2.08456,0.54595 -0.29779,-0.34742 0.34743,-0.49633 0.64522,-0.39706 1.5386,-0.39705 2.18382,-0.19853 1.24081,0.0993 1.14154,0.54596 0.4467,1.43934 -0.19853,1.63786 -0.59559,1.29044 -1.24081,0.89339 -1.43934,-0.39706 -0.99264,-1.09191 -0.0496,-1.19118 0.79412,-0.89338 0.89338,-0.44669 1.19118,-0.0496 0.64522,1.04228 0.34742,0.79412 -0.14889,1.14155 0.99265,-0.4467 0.29779,-1.34007 -0.19853,-4.06985 -1.93566,-0.44669 -2.53125,-1.6875 -2.23346,-1.88603 -2.23345,-4.069864 -0.44669,3.920964 0.64522,4.21875 1.5386,3.92096 0.74448,0.44669 h -1.73713 l -2.18383,-0.54596 -3.12684,-2.08456 -1.58823,-2.28309 -1.14154,-2.08456 -1.29044,-3.871324 -1.38971,2.481624 -1.48897,2.63051 -0.94302,1.9853 3.8217,-6.948534 1.29044,3.672794 2.33272,3.92096 2.9283,2.13419 0.49633,0.44669 2.28309,0.49632 h 1.63787 l -0.69485,-0.69485 -0.84375,-1.93566 -1.34008,-5.80698 0.44669,-3.970594 2.33273,4.069854 4.56617,3.47426 2.08456,0.59559 0.19853,2.82905 -0.0496,3.97058 -0.0993,6.00552 -0.54595,3.02757 -1.58824,2.77941 -1.5386,0.89339 -1.19118,0.24816 -1.48897,-0.69485 -0.69485,-0.1489 0.69485,1.24081 1.43934,1.6875 2.68015,1.19117 3.17647,0.2978 3.77206,-2.23346 1.3897,-2.77941 0.89339,-3.82169 0.0496,-3.375 0.14889,6.25368 -1.14154,5.11213 -2.08456,3.27573 -2.08456,1.6875 -1.88603,0.59559 -2.28308,-0.79412 1.78676,1.6875 4.9136,1.88603 2.43199,0.2978 2.68015,-0.39706 2.72977,-1.09191 3.62317,-3.27574 0.89338,-3.97059 0.49632,-3.57353 -0.0993,-2.87867 -0.39706,-3.17647 -0.49632,-3.07721 1.98529,3.47427 1.19117,2.18382 0.39706,1.29044 0.39706,-2.28309 -0.39706,-3.0772 -1.29044,-3.77206 -1.29044,-2.87868 -1.6875,-3.27573 -10.125,-4.16912 z';
This is ★Paimon chan★ from a computer game 'Genshin Impact'. Thus it is proved that there are no flaws at the data, since all the data is plotted correctly.
Next, I plotted the approximated (Fx(t), Fy(t)) points so that I can check whether there is a problem. And It turned out that there was a problem. But I don't understand what is the problem. At the same time this path is interesting; The beginning part of the path seems like the hairpin.
This is the drawing code:
function approxFn(t) {
let x = xa0;
let y = ya0;
for (let i = 0; i < xan.length; i++) {
x += xan[i] * Math.cos(2 * Math.PI * i * t / cx.length);
x += xbn[i] * Math.sin(2 * Math.PI * i * t / cx.length);
y += yan[i] * Math.cos(2 * Math.PI * i * t / cx.length);
y += ybn[i] * Math.sin(2 * Math.PI * i * t / cx.length);
}
return { x, y };
}
function draw_tick3() {
const rAF = window.requestAnimationFrame(draw_tick3);
const s = 5;
// initialize
cctx.clearRect(0, 0, 1000, 800);
cctx.beginPath();
for (let t = 0; t < count; t++) {
if (count === 0) cctx.moveTo(200 + s * approxFn(t).x, 200 + s * approxFn(t).y);
else cctx.lineTo(200 + s * approxFn(t).x, 200 + s * approxFn(t).y);
}
cctx.stroke();
if (count < cx.length - 1) {
count++;
} else {
window.cancelAnimationFrame(rAF);
}
}
The above is all the code in my js file. In where I made a mistake? It's a mystery! I know this question is exceptionally seriously long question. But please help me! I want to realize Paimon chan! ㅠwㅠ
※ (This section is irrelevant with the question) Meanwhile I made a success to draw the path in a complex number plane. If you're interested, please see my work... I would like to add circle things to this but I have no idea what is 'radius' in this case.
// You can see that I used real part for x and imaginary part for y.
for (let i = 0; i <= count; i++) {
if (i === 0) {
cctx.moveTo(coords[i].real * scaler + paimonPosition, coords[i].imag * scaler + paimonPosition);
} else {
cctx.lineTo(coords[i].real * scaler + paimonPosition, coords[i].imag * scaler + paimonPosition);
}
}
And this is the result. But what makes me confused is a case of cn = -5000 ~ 5000. As far as I understand, more cn, more accurate as original wave. But why it crashes when cn is so big?
Anyways, thank you very much for reading this long question!
(the character shown: Paimon from Genshin Impact)
Hello myself!
First, errors in your code...
You did not consider a case where sequence of values come after drawing command. For example, your get_points function can't handle a case like h 0 1 2.
Current get_points function can't handle second m drawing command. You need to manually join strings if you have multiple paths.
You need to manually set m x y to m 0 0. Otherwise you can't see canvas drawing. (Maybe values are too too small to draw)
Second, in brief, you can't draw a shape with rotating vectors having fixed magnitude, if you approximate f(t) in a xy plane. It's because what you approximated is not a shape itself, but shape's coordinates.
Third, the reason you got weird shape when you tried to plot approximated data is at your approxFn() function.
x += xan[i] * Math.cos(2 * Math.PI * i * t / cx.length);
x += xbn[i] * Math.sin(2 * Math.PI * i * t / cx.length);
y += yan[i] * Math.cos(2 * Math.PI * i * t / cx.length);
y += ybn[i] * Math.sin(2 * Math.PI * i * t / cx.length);
not t, (t + 1) is correct. Your approximated data has no problem.
Fourth, so you need to take a complex plane approach if you want rotating vectors. In this case, the radius of circles are the magnitude of a sum vector of a real part vector and an imaginary part vector (Pythagorean theorem).
Fifth, In Cn formula, you missed 1 / T.
Sixth, The reason it crashed is... I don't know the exact reason but I think numerical integration and/or finding Cn is wrong. The new code I wrote don't crash at high Cn.
p.s. I wrote some writings about Fourier series. Please see if you are interested: https://logic-finder.github.io/memory/FourierSeriesExploration/opening/opening-en.html

Adding up Total Distance traveled in javascript

Using the geolocation plugin for apache cordova / phonegap
Originally from, DaveAlden + Chris Veness
I'm simply trying to sum up the deltaDistMetres. Have been working at it all day but can't figure it out. Tried the push and reduce method with arrays, for loops, try troubleshooting with isNaN(). I'm at your mercy stack overflow pros.
My understanding of deltaDistMetres is the distance between the current gps location and the one before. So summing them up would be the total distance correct?
Thanks for any help!
var currentUpdate, lastUpdate;
function onPositionUpdate(position) {
if (currentUpdate) lastUpdate = currentUpdate;
currentUpdate = {
position: new LatLon(position.coords.latitude, position.coords.longitude),
time: new Date()
};
if (!lastUpdate) return;
currentUpdate.deltaDistMetres = lastUpdate.position.distanceTo(currentUpdate.position) * 1000;
currentUpdate.deltaTimeSecs = (currentUpdate.time - lastUpdate.time) * 1000;
currentUpdate.speed = (currentUpdate.deltaDistMetres / currentUpdate.deltaTimeSecs);
currentUpdate.accelerationGPS = (currentUpdate.speed - lastUpdate.speed) / currentUpdate.deltaTimeSecs;
/* THIS IS MY LITTLE CODE
(I have an element id="log" in my html)
*/
var distanceTotal= 0;
var distanceChange = currentUpdate.deltaDistMetres;
distanceTotal += distanceChange;
document.getElementById("log").innerHTML = "Total distance " + distanceTotal + " m";
}
function onPositionError(error) {
console.log("Error: " + error.message);
}
$(document).on("deviceready", function () {
navigator.geolocation.watchPosition(onPositionUpdate, onPositionError, { frequency:3000, timeout: 30000, enableHighAccuracy: true });
});
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Latitude/longitude spherical geodesy formulae & scripts (c) Chris Veness 2002-2012 */
/* - www.movable-type.co.uk/scripts/latlong.html */
/* */
/* Sample usage: */
/* var p1 = new LatLon(51.5136, -0.0983); */
/* var p2 = new LatLon(51.4778, -0.0015); */
/* var dist = p1.distanceTo(p2); // in km */
/* var brng = p1.bearingTo(p2); // in degrees clockwise from north */
/* ... etc */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Note that minimal error checking is performed in this example code! */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Object LatLon: tools for geodetic calculations
*
* #requires Geo
*/
/**
* Creates a point on the earth's surface at the supplied latitude / longitude
*
* #constructor
* #param {Number} lat: latitude in degrees
* #param {Number} lon: longitude in degrees
* #param {Number} [radius=6371]: radius of earth if different value is required from standard 6,371km
*/
function LatLon(lat, lon, radius) {
if (typeof (radius) == 'undefined') radius = 6371; // earth's mean radius in km
this.lat = Number(lat);
this.lon = Number(lon);
this.radius = Number(radius);
}
/**
* Returns the distance from this point to the supplied point, in km
* (using Haversine formula)
*
* from: Haversine formula - R. W. Sinnott, "Virtues of the Haversine",
* Sky and Telescope, vol 68, no 2, 1984
*
* #this {LatLon} latitude/longitude of origin point
* #param {LatLon} point: latitude/longitude of destination point
* #param {Number} [precision=4]: number of significant digits to use for returned value
* #returns {Number} distance in km between this point and destination point
*/
LatLon.prototype.distanceTo = function (point, precision) {
// default 4 sig figs reflects typical 0.3% accuracy of spherical model
if (typeof precision == 'undefined') precision = 4;
var R = this.radius;
var φ1 = this.lat.toRadians(), λ1 = this.lon.toRadians();
var φ2 = point.lat.toRadians(), λ2 = point.lon.toRadians();
var Δφ = φ2 - φ1;
var Δλ = λ2 - λ1;
var a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
return d.toPrecisionFixed(Number(precision));
}
/**
* Returns the (initial) bearing from this point to the supplied point, in degrees
* see http://williams.best.vwh.net/avform.htm#Crs
*
* #this {LatLon} latitude/longitude of origin point
* #param {LatLon} point: latitude/longitude of destination point
* #returns {Number} initial bearing in degrees from North
*/
LatLon.prototype.bearingTo = function (point) {
var φ1 = this.lat.toRadians(), φ2 = point.lat.toRadians();
var Δλ = (point.lon - this.lon).toRadians();
var y = Math.sin(Δλ) * Math.cos(φ2);
var x = Math.cos(φ1) * Math.sin(φ2) -
Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ);
var θ = Math.atan2(y, x);
return (θ.toDegrees() + 360) % 360;
}
/**
* Returns final bearing arriving at supplied destination point from this point; the final bearing
* will differ from the initial bearing by varying degrees according to distance and latitude
*
* #this {LatLon} latitude/longitude of origin point
* #param {LatLon} point: latitude/longitude of destination point
* #returns {Number} final bearing in degrees from North
*/
LatLon.prototype.finalBearingTo = function (point) {
// get initial bearing from supplied point back to this point...
var φ1 = point.lat.toRadians(), φ2 = this.lat.toRadians();
var Δλ = (this.lon - point.lon).toRadians();
var y = Math.sin(Δλ) * Math.cos(φ2);
var x = Math.cos(φ1) * Math.sin(φ2) -
Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ);
var θ = Math.atan2(y, x);
// ... & reverse it by adding 180°
return (θ.toDegrees() + 180) % 360;
}
/**
* Returns the midpoint between this point and the supplied point.
* see http://mathforum.org/library/drmath/view/51822.html for derivation
*
* #this {LatLon} latitude/longitude of origin point
* #param {LatLon} point: latitude/longitude of destination point
* #returns {LatLon} midpoint between this point and the supplied point
*/
LatLon.prototype.midpointTo = function (point) {
var φ1 = this.lat.toRadians(), λ1 = this.lon.toRadians();
var φ2 = point.lat.toRadians();
var Δλ = (point.lon - this.lon).toRadians();
var Bx = Math.cos(φ2) * Math.cos(Δλ);
var By = Math.cos(φ2) * Math.sin(Δλ);
var φ3 = Math.atan2(Math.sin(φ1) + Math.sin(φ2),
Math.sqrt((Math.cos(φ1) + Bx) * (Math.cos(φ1) + Bx) + By * By));
var λ3 = λ1 + Math.atan2(By, Math.cos(φ1) + Bx);
λ3 = (λ3 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise to -180..+180º
return new LatLon(φ3.toDegrees(), λ3.toDegrees());
}
/**
* Returns the destination point from this point having travelled the given distance (in km) on the
* given initial bearing (bearing may vary before destination is reached)
*
* see http://williams.best.vwh.net/avform.htm#LL
*
* #this {LatLon} latitude/longitude of origin point
* #param {Number} brng: initial bearing in degrees
* #param {Number} dist: distance in km
* #returns {LatLon} destination point
*/
LatLon.prototype.destinationPoint = function (brng, dist) {
var θ = Number(brng).toRadians();
var δ = Number(dist) / this.radius; // angular distance in radians
var φ1 = this.lat.toRadians();
var λ1 = this.lon.toRadians();
var φ2 = Math.asin(Math.sin(φ1) * Math.cos(δ) +
Math.cos(φ1) * Math.sin(δ) * Math.cos(θ));
var λ2 = λ1 + Math.atan2(Math.sin(θ) * Math.sin(δ) * Math.cos(φ1),
Math.cos(δ) - Math.sin(φ1) * Math.sin(φ2));
λ2 = (λ2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise to -180..+180º
return new LatLon(φ2.toDegrees(), λ2.toDegrees());
}
/**
* Returns the point of intersection of two paths defined by point and bearing
*
* see http://williams.best.vwh.net/avform.htm#Intersection
*
* #param {LatLon} p1: first point
* #param {Number} brng1: initial bearing from first point
* #param {LatLon} p2: second point
* #param {Number} brng2: initial bearing from second point
* #returns {LatLon} destination point (null if no unique intersection defined)
*/
LatLon.intersection = function (p1, brng1, p2, brng2) {
var φ1 = p1.lat.toRadians(), λ1 = p1.lon.toRadians();
var φ2 = p2.lat.toRadians(), λ2 = p2.lon.toRadians();
var θ13 = Number(brng1).toRadians(), θ23 = Number(brng2).toRadians();
var Δφ = φ2 - φ1, Δλ = λ2 - λ1;
var δ12 = 2 * Math.asin(Math.sqrt(Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2)));
if (δ12 == 0) return null;
// initial/final bearings between points
var θ1 = Math.acos((Math.sin(φ2) - Math.sin(φ1) * Math.cos(δ12)) /
(Math.sin(δ12) * Math.cos(φ1)));
if (isNaN(θ1)) θ1 = 0; // protect against rounding
var θ2 = Math.acos((Math.sin(φ1) - Math.sin(φ2) * Math.cos(δ12)) /
(Math.sin(δ12) * Math.cos(φ2)));
if (Math.sin(λ2 - λ1) > 0) {
θ12 = θ1;
θ21 = 2 * Math.PI - θ2;
} else {
θ12 = 2 * Math.PI - θ1;
θ21 = θ2;
}
var α1 = (θ13 - θ12 + Math.PI) % (2 * Math.PI) - Math.PI; // angle 2-1-3
var α2 = (θ21 - θ23 + Math.PI) % (2 * Math.PI) - Math.PI; // angle 1-2-3
if (Math.sin(α1) == 0 && Math.sin(α2) == 0) return null; // infinite intersections
if (Math.sin(α1) * Math.sin(α2) < 0) return null; // ambiguous intersection
//α1 = Math.abs(α1);
//α2 = Math.abs(α2);
// ... Ed Williams takes abs of α1/α2, but seems to break calculation?
var α3 = Math.acos(-Math.cos(α1) * Math.cos(α2) +
Math.sin(α1) * Math.sin(α2) * Math.cos(δ12));
var δ13 = Math.atan2(Math.sin(δ12) * Math.sin(α1) * Math.sin(α2),
Math.cos(α2) + Math.cos(α1) * Math.cos(α3))
var φ3 = Math.asin(Math.sin(φ1) * Math.cos(δ13) +
Math.cos(φ1) * Math.sin(δ13) * Math.cos(θ13));
var Δλ13 = Math.atan2(Math.sin(θ13) * Math.sin(δ13) * Math.cos(φ1),
Math.cos(δ13) - Math.sin(φ1) * Math.sin(φ3));
var λ3 = λ1 + Δλ13;
λ3 = (λ3 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise to -180..+180º
return new LatLon(φ3.toDegrees(), λ3.toDegrees());
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Returns the distance from this point to the supplied point, in km, travelling along a rhumb line
*
* see http://williams.best.vwh.net/avform.htm#Rhumb
*
* #this {LatLon} latitude/longitude of origin point
* #param {LatLon} point: latitude/longitude of destination point
* #returns {Number} distance in km between this point and destination point
*/
LatLon.prototype.rhumbDistanceTo = function (point) {
var R = this.radius;
var φ1 = this.lat.toRadians(), φ2 = point.lat.toRadians();
var Δφ = φ2 - φ1;
var Δλ = Math.abs(point.lon - this.lon).toRadians();
// if dLon over 180° take shorter rhumb line across the anti-meridian:
if (Math.abs(Δλ) > Math.PI) Δλ = Δλ > 0 ? -(2 * Math.PI - Δλ) : (2 * Math.PI + Δλ);
// on Mercator projection, longitude gets increasing stretched by latitude; q is the 'stretch factor'
var Δψ = Math.log(Math.tan(φ2 / 2 + Math.PI / 4) / Math.tan(φ1 / 2 + Math.PI / 4));
// the stretch factor becomes ill-conditioned along E-W line (0/0); use empirical tolerance to avoid it
var q = Math.abs(Δψ) > 10e-12 ? Δφ / Δψ : Math.cos(φ1);
// distance is pythagoras on 'stretched' Mercator projection
var δ = Math.sqrt(Δφ * Δφ + q * q * Δλ * Δλ); // angular distance in radians
var dist = δ * R;
return dist.toPrecisionFixed(4); // 4 sig figs reflects typical 0.3% accuracy of spherical model
}
/**
* Returns the bearing from this point to the supplied point along a rhumb line, in degrees
*
* #this {LatLon} latitude/longitude of origin point
* #param {LatLon} point: latitude/longitude of destination point
* #returns {Number} bearing in degrees from North
*/
LatLon.prototype.rhumbBearingTo = function (point) {
var φ1 = this.lat.toRadians(), φ2 = point.lat.toRadians();
var Δλ = (point.lon - this.lon).toRadians();
// if dLon over 180° take shorter rhumb line across the anti-meridian:
if (Math.abs(Δλ) > Math.PI) Δλ = Δλ > 0 ? -(2 * Math.PI - Δλ) : (2 * Math.PI + Δλ);
var Δψ = Math.log(Math.tan(φ2 / 2 + Math.PI / 4) / Math.tan(φ1 / 2 + Math.PI / 4));
var θ = Math.atan2(Δλ, Δψ);
return (θ.toDegrees() + 360) % 360;
}
/**
* Returns the destination point from this point having travelled the given distance (in km) on the
* given bearing along a rhumb line
*
* #this {LatLon} latitude/longitude of origin point
* #param {Number} brng: bearing in degrees from North
* #param {Number} dist: distance in km
* #returns {LatLon} destination point
*/
LatLon.prototype.rhumbDestinationPoint = function (brng, dist) {
var δ = Number(dist) / this.radius; // angular distance in radians
var φ1 = this.lat.toRadians(), λ1 = this.lon.toRadians();
var θ = Number(brng).toRadians();
var Δφ = δ * Math.cos(θ);
var φ2 = φ1 + Δφ;
// check for some daft bugger going past the pole, normalise latitude if so
if (Math.abs(φ2) > Math.PI / 2) φ2 = φ2 > 0 ? Math.PI - φ2 : -Math.PI - φ2;
var Δψ = Math.log(Math.tan(φ2 / 2 + Math.PI / 4) / Math.tan(φ1 / 2 + Math.PI / 4));
var q = Math.abs(Δψ) > 10e-12 ? Δφ / Δψ : Math.cos(φ1); // E-W course becomes ill-conditioned with 0/0
var Δλ = δ * Math.sin(θ) / q;
var λ2 = λ1 + Δλ;
λ2 = (λ2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise to -180..+180º
return new LatLon(φ2.toDegrees(), λ2.toDegrees());
}
/**
* Returns the loxodromic midpoint (along a rhumb line) between this point and the supplied point.
* see http://mathforum.org/kb/message.jspa?messageID=148837
*
* #this {LatLon} latitude/longitude of origin point
* #param {LatLon} point: latitude/longitude of destination point
* #returns {LatLon} midpoint between this point and the supplied point
*/
LatLon.prototype.rhumbMidpointTo = function (point) {
var φ1 = this.lat.toRadians(), λ1 = this.lon.toRadians();
var φ2 = point.lat.toRadians(), λ2 = point.lon.toRadians();
if (Math.abs(λ2 - λ1) > Math.PI) λ1 += 2 * Math.PI; // crossing anti-meridian
var φ3 = (φ1 + φ2) / 2;
var f1 = Math.tan(Math.PI / 4 + φ1 / 2);
var f2 = Math.tan(Math.PI / 4 + φ2 / 2);
var f3 = Math.tan(Math.PI / 4 + φ3 / 2);
var λ3 = ((λ2 - λ1) * Math.log(f3) + λ1 * Math.log(f2) - λ2 * Math.log(f1)) / Math.log(f2 / f1);
if (!isFinite(λ3)) λ3 = (λ1 + λ2) / 2; // parallel of latitude
λ3 = (λ3 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise to -180..+180º
return new LatLon(φ3.toDegrees(), λ3.toDegrees());
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Returns a string representation of this point; format and dp as per lat()/lon()
*
* #this {LatLon} latitude/longitude of origin point
* #param {String} [format]: return value as 'd', 'dm', 'dms'
* #param {Number} [dp=0|2|4]: number of decimal places to display
* #returns {String} comma-separated latitude/longitude
*/
LatLon.prototype.toString = function (format, dp) {
if (typeof format == 'undefined') format = 'dms';
return Geo.toLat(this.lat, format, dp) + ', ' + Geo.toLon(this.lon, format, dp);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// ---- extend Number object with methods for converting degrees/radians
/** Converts numeric degrees to radians */
if (typeof Number.prototype.toRadians == 'undefined') {
Number.prototype.toRadians = function () {
return this * Math.PI / 180;
}
}
/** Converts radians to numeric (signed) degrees */
if (typeof Number.prototype.toDegrees == 'undefined') {
Number.prototype.toDegrees = function () {
return this * 180 / Math.PI;
}
}
/**
* Formats the significant digits of a number, using only fixed-point notation (no exponential)
*
* #param {Number} precision: Number of significant digits to appear in the returned string
* #returns {String} A string representation of number which contains precision significant digits
*/
if (typeof Number.prototype.toPrecisionFixed == 'undefined') {
Number.prototype.toPrecisionFixed = function (precision) {
// use standard toPrecision method
var n = this.toPrecision(precision);
// ... but replace +ve exponential format with trailing zeros
n = n.replace(/(.+)e\+(.+)/, function (n, sig, exp) {
sig = sig.replace(/\./, ''); // remove decimal from significand
l = sig.length - 1;
while (exp-- > l) sig = sig + '0'; // append zeros from exponent
return sig;
});
// ... and replace -ve exponential format with leading zeros
n = n.replace(/(.+)e-(.+)/, function (n, sig, exp) {
sig = sig.replace(/\./, ''); // remove decimal from significand
while (exp-- > 1) sig = '0' + sig; // prepend zeros from exponent
return '0.' + sig;
});
return n;
}
}
/** Trims whitespace from string (q.v. blog.stevenlevithan.com/archives/faster-trim-javascript) */
if (typeof String.prototype.trim == 'undefined') {
String.prototype.trim = function () {
return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if (!window.console) window.console = { log: function () { } };
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Geodesy representation conversion functions (c) Chris Veness 2002-2012 */
/* - www.movable-type.co.uk/scripts/latlong.html */
/* */
/* Sample usage: */
/* var lat = Geo.parseDMS('51° 28′ 40.12″ N'); */
/* var lon = Geo.parseDMS('000° 00′ 05.31″ W'); */
/* var p1 = new LatLon(lat, lon); */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
var Geo = {}; // Geo namespace, representing static class
/**
* Parses string representing degrees/minutes/seconds into numeric degrees
*
* This is very flexible on formats, allowing signed decimal degrees, or deg-min-sec optionally
* suffixed by compass direction (NSEW). A variety of separators are accepted (eg 3º 37' 09"W)
* or fixed-width format without separators (eg 0033709W). Seconds and minutes may be omitted.
* (Note minimal validation is done).
*
* #param {String|Number} dmsStr: Degrees or deg/min/sec in variety of formats
* #returns {Number} Degrees as decimal number
* #throws {TypeError} dmsStr is an object, perhaps DOM object without .value?
*/
Geo.parseDMS = function (dmsStr) {
if (typeof deg == 'object') throw new TypeError('Geo.parseDMS - dmsStr is [DOM?] object');
// check for signed decimal degrees without NSEW, if so return it directly
if (typeof dmsStr === 'number' && isFinite(dmsStr)) return Number(dmsStr);
// strip off any sign or compass dir'n & split out separate d/m/s
var dms = String(dmsStr).trim().replace(/^-/, '').replace(/[NSEW]$/i, '').split(/[^0-9.,]+/);
if (dms[dms.length - 1] == '') dms.splice(dms.length - 1); // from trailing symbol
if (dms == '') return NaN;
// and convert to decimal degrees...
switch (dms.length) {
case 3: // interpret 3-part result as d/m/s
var deg = dms[0] / 1 + dms[1] / 60 + dms[2] / 3600;
break;
case 2: // interpret 2-part result as d/m
var deg = dms[0] / 1 + dms[1] / 60;
break;
case 1: // just d (possibly decimal) or non-separated dddmmss
var deg = dms[0];
// check for fixed-width unseparated format eg 0033709W
//if (/[NS]/i.test(dmsStr)) deg = '0' + deg; // - normalise N/S to 3-digit degrees
//if (/[0-9]{7}/.test(deg)) deg = deg.slice(0,3)/1 + deg.slice(3,5)/60 + deg.slice(5)/3600;
break;
default:
return NaN;
}
if (/^-|[WS]$/i.test(dmsStr.trim())) deg = -deg; // take '-', west and south as -ve
return Number(deg);
}
/**
* Convert decimal degrees to deg/min/sec format
* - degree, prime, double-prime symbols are added, but sign is discarded, though no compass
* direction is added
*
* #private
* #param {Number} deg: Degrees
* #param {String} [format=dms]: Return value as 'd', 'dm', 'dms'
* #param {Number} [dp=0|2|4]: No of decimal places to use - default 0 for dms, 2 for dm, 4 for d
* #returns {String} deg formatted as deg/min/secs according to specified format
* #throws {TypeError} deg is an object, perhaps DOM object without .value?
*/
Geo.toDMS = function (deg, format, dp) {
if (typeof deg == 'object') throw new TypeError('Geo.toDMS - deg is [DOM?] object');
if (isNaN(deg)) return null; // give up here if we can't make a number from deg
// default values
if (typeof format == 'undefined') format = 'dms';
if (typeof dp == 'undefined') {
switch (format) {
case 'd': dp = 4; break;
case 'dm': dp = 2; break;
case 'dms': dp = 0; break;
default: format = 'dms'; dp = 0; // be forgiving on invalid format
}
}
deg = Math.abs(deg); // (unsigned result ready for appending compass dir'n)
switch (format) {
case 'd':
d = deg.toFixed(dp); // round degrees
if (d < 100) d = '0' + d; // pad with leading zeros
if (d < 10) d = '0' + d;
dms = d + '\u00B0'; // add º symbol
break;
case 'dm':
var min = (deg * 60).toFixed(dp); // convert degrees to minutes & round
var d = Math.floor(min / 60); // get component deg/min
var m = (min % 60).toFixed(dp); // pad with trailing zeros
if (d < 100) d = '0' + d; // pad with leading zeros
if (d < 10) d = '0' + d;
if (m < 10) m = '0' + m;
dms = d + '\u00B0' + m + '\u2032'; // add º, ' symbols
break;
case 'dms':
var sec = (deg * 3600).toFixed(dp); // convert degrees to seconds & round
var d = Math.floor(sec / 3600); // get component deg/min/sec
var m = Math.floor(sec / 60) % 60;
var s = (sec % 60).toFixed(dp); // pad with trailing zeros
if (d < 100) d = '0' + d; // pad with leading zeros
if (d < 10) d = '0' + d;
if (m < 10) m = '0' + m;
if (s < 10) s = '0' + s;
dms = d + '\u00B0' + m + '\u2032' + s + '\u2033'; // add º, ', " symbols
break;
}
return dms;
}
/**
* Convert numeric degrees to deg/min/sec latitude (suffixed with N/S)
*
* #param {Number} deg: Degrees
* #param {String} [format=dms]: Return value as 'd', 'dm', 'dms'
* #param {Number} [dp=0|2|4]: No of decimal places to use - default 0 for dms, 2 for dm, 4 for d
* #returns {String} Deg/min/seconds
*/
Geo.toLat = function (deg, format, dp) {
var lat = Geo.toDMS(deg, format, dp);
return lat == null ? '–' : lat.slice(1) + (deg < 0 ? 'S' : 'N'); // knock off initial '0' for lat!
}
/**
* Convert numeric degrees to deg/min/sec longitude (suffixed with E/W)
*
* #param {Number} deg: Degrees
* #param {String} [format=dms]: Return value as 'd', 'dm', 'dms'
* #param {Number} [dp=0|2|4]: No of decimal places to use - default 0 for dms, 2 for dm, 4 for d
* #returns {String} Deg/min/seconds
*/
Geo.toLon = function (deg, format, dp) {
var lon = Geo.toDMS(deg, format, dp);
return lon == null ? '–' : lon + (deg < 0 ? 'W' : 'E');
}
/**
* Convert numeric degrees to deg/min/sec as a bearing (0º..360º)
*
* #param {Number} deg: Degrees
* #param {String} [format=dms]: Return value as 'd', 'dm', 'dms'
* #param {Number} [dp=0|2|4]: No of decimal places to use - default 0 for dms, 2 for dm, 4 for d
* #returns {String} Deg/min/seconds
*/
Geo.toBrng = function (deg, format, dp) {
deg = (Number(deg) + 360) % 360; // normalise -ve values to 180º..360º
var brng = Geo.toDMS(deg, format, dp);
return brng == null ? '–' : brng.replace('360', '0'); // just in case rounding took us up to 360º!
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if (!window.console) window.console = { log: function () { } };
You have placed the declaration var distanceTotal= 0; inside onPositionUpdate() which means it will be reset to zero on every position update.
To preserve the previous value, declare it outside of onPositionUpdate:
var currentUpdate, lastUpdate;
var distanceTotal= 0;
function onPositionUpdate(position) {
if (currentUpdate) lastUpdate = currentUpdate;
currentUpdate = {
position: new LatLon(position.coords.latitude, position.coords.longitude),
time: new Date()
};
if (!lastUpdate) return;
currentUpdate.deltaDistMetres = lastUpdate.position.distanceTo(currentUpdate.position) * 1000;
currentUpdate.deltaTimeSecs = (currentUpdate.time - lastUpdate.time) * 1000;
currentUpdate.speed = (currentUpdate.deltaDistMetres / currentUpdate.deltaTimeSecs);
currentUpdate.accelerationGPS = (currentUpdate.speed - lastUpdate.speed) / currentUpdate.deltaTimeSecs;
/* THIS IS MY LITTLE CODE
(I have an element id="log" in my html)
*/
var distanceChange = currentUpdate.deltaDistMetres;
distanceTotal += distanceChange;
document.getElementById("log").innerHTML = "Total distance " + distanceTotal + " m";
}
function onPositionError(error) {
console.log("Error: " + error.message);
}
$(document).on("deviceready", function () {
navigator.geolocation.watchPosition(onPositionUpdate, onPositionError, { frequency:3000, timeout: 30000, enableHighAccuracy: true });
});

Translating pixels in canvas on sine wave

I am trying to create an image distortion effect on my canvas, but nothing appears to be happening. Here is my code:
self.drawScreen = function (abilityAnimator, elapsed) {
if (!self.initialized) {
self.initialized = true;
self.rawData = abilityAnimator.context.getImageData(self.targetX, self.targetY, self.width, self.height);
self.initialImgData = self.rawData.data;
}
abilityAnimator.drawBackground();
self.rawData = abilityAnimator.context.getImageData(self.targetX, self.targetY, self.width, self.height);
var imgData = self.rawData.data, rootIndex, translationIndex, newX;
for (var y = 0; y < self.height; y++) {
for (var x = 0; x < self.width; x++) {
rootIndex = (y * self.height + x) * 4;
newX = Math.ceil(self.amplitude * Math.sin(self.frequency * (y + elapsed)));
translationIndex = (y * self.width + newX) * 4;
imgData[translationIndex + 0] = self.initialImgData[rootIndex + 0];
imgData[translationIndex + 1] = self.initialImgData[rootIndex + 1];
imgData[translationIndex + 2] = self.initialImgData[rootIndex + 2];
imgData[translationIndex + 3] = self.initialImgData[rootIndex + 3];
}
}
abilityAnimator.context.putImageData(self.rawData, self.targetX, self.targetY);
};
abilityAnimator is a wrapper for my canvas object:
abilityAnimator.context = //canvas.context
abilityAnimator.drawBackground = function(){
this.canvas.width = this.canvas.width;
}
elapsed is simply the number of milliseconds since the animation began (elapsed is always <= 2000)
My member variables have the following values:
self.width = 125;
self.height = 125;
self.frequency = 0.5;
self.amplitude = self.width / 4;
self.targetX = //arbitrary value within canvas
self.targetY = //arbitrary value within canvas
I can translate the image to the right very easily so long as there is no sine function, however, introducing these lines:
newX = Math.ceil(self.amplitude * Math.sin(self.frequency * (y + elapsed)));
translationIndex = (y * self.width + newX) * 4;
Causes nothing to render at all. The translation indexes don't appear to be very strange, and the nature of the sinusoidal function should guarantee that the offset is no greater than 125 / 4 pixels.
Your formula using sin is wrong, the frequency will be so high it will be seen as noise.
The typical formula to build a sinusoid is :
res = sin ( 2 * PI * frequency * time ) ;
where frequency is in Hz and time in s.
So in js that would translate to :
res = Math.sin ( 2 * Math.PI * f * time_ms * 1e-3 ) ;
you can obviously compute just once the constant factor :
self.frequency = 0.5 * ( 2 * Math.PI * 1e-3 );
// then use
res = Math.sin ( self.frequency * time_ms ) ;
So you see you were 1000 times too fast.
Second issue :
Now that you have your time frequency ok, let's fix your spatial frequency : when multiplying time frequency by y, you're quite adding apples and cats.
To build the formula, think that you want to cross n time 2*PI during the height of the canvas.
So :
spatialFrequency = ( n ) * 2 * Math.PI / canvasHeight ;
and your formula becomes :
res = Math.sin ( self.frequency * time_ms + spatialFrequency * y ) ;
You can play with various values with this jsbin i made so you can visualize the effect :
http://jsbin.com/ludizubo/1/edit?js,output

Physics formula implementation issues

I'm trying to implement the formulas necessary to calculate the global radiation incident on an inclined solar panel.
The formulas I use were found in the following research paper :
--> ipac.kacst.edu.sa/eDoc/2010/191048_1.pdf
Here is the commented JavaScript code :
var config = require('./configuration.json'),
pi = Math.PI;
function solar_efficiency(angle, day) {
var R_mD, // average sun-earth distance (m)
a, // semi-major axis (km)
e, // oval orbit eccentricity (km)
theta, // angle with the perihelion
n, // current nth day of the year (int)
R_D, // actual sun-earth distance (m)
I_o; // extraterrestrial radiation (indice)
R_mD = config.avg_sun_earth_dist;
a = config.semi_major_axis;
e = config.eccentricity;
n = day;
theta = n * 365.25 / 360;
R_D = a * (1 - e * e) / (1 + e * Math.cos(theta));
I_o = 1367 * Math.pow(R_mD / R_D, 2);
var axis, // angle of the earth's axis
D; // sun declination (radian)
axis = config.earth_axis;
D = ((axis * pi) / 180) * Math.sin(((2 * pi) * (284 + n)) / 365);
var Eq_t; // solar time correction (float)
if((1 <= n) && (n <= 106)) {
Eq_t = -14.2 * Math.sin((pi * (n + 7)) / 111);
}
else if((107 <= n) && (n <= 166)) {
Eq_t = 4.0 * Math.sin((pi * (n - 106)) / 59);
}
else if((167 <= n) && (n <= 246)) {
Eq_t = -6.5 * Math.sin((pi * (n - 166)) / 80);
}
else if((247 <= n) && (n <= 365)) {
Eq_t = 16.4 * Math.sin((pi * (n - 247)) / 113);
}
var Long_sm, // longitude of the standard meridian (longitude)
Long_local, // longitude of the panels (longitude)
T_local, // local time (h)
T_solar; // solar time (h)
Long_sm = config.std_meridian_long;
Long_local = config.current_longitutde;
T_local = config.local_time;
T_solar = T_local + (Eq_t / 60) + ((Long_sm - Long_local) / 15);
var W; // hour angle (radian)
W = pi * ((12 - T_solar) / 12);
var Lat_local, // latitude of the panels (latitude)
W_sr, // sunrise hour angle (°)
W_ss; // sunset hour angle (°)
Lat_local = config.current_latitude;
W_sr = W_ss = Math.acos(-1 * Math.tan(Lat_local) * Math.tan(D));
var alpha, // angle between solar panel and horizontal (°) -FIND!!!
R_b; // ratio of avg. beam radiation on horiz. / inclined surface
alpha = angle; // /!\ TESTING ONLY /!\
var num_1 = Math.cos(Lat_local - alpha) * Math.cos(D) * Math.sin(W_ss);
var num_2 = W_ss * Math.sin(Lat_local - alpha) * Math.sin(D);
var det_1 = Math.cos(Lat_local) * Math.cos(D) * Math.sin(W_ss);
var det_2 = W_ss * Math.sin(Lat_local) * Math.sin(D);
R_b = (num_1 + num_2) / (det_1 + det_2); // in the northern hemisphere
var H_g, // global radiation on horizontal surface (W h/m^2/day) ---DB!!!
H_d, // diffuse radiation on horizontal surface (W h/m^2/day) ---DB!!!
H_B; // beam radiation on inclined surface (W h/m^2/day)
H_g = 700;
H_d = 500;
H_B = (H_g - H_d) / R_b;
var R_d; // ratio of avg. daily diffuse radiation tilted / horiz. surface
R_d = (3 + Math.cos(2 * alpha)) / 2; // isotropic Badesco model
var H_D; // sky-diffuse radiation on inclined surface (W h/m^2/day)
H_D = R_d * H_d;
var p, // albedo std. = 0.2 (soil = 0.17, grass = 0.25, concrete = 0.55)
H_R; // ground reflected radiation on inclined surface (W h/m^2/day)
p = config.ground_albedo;
H_R = H_g * p * ((1 - Math.cos(alpha)) / 2);
var H_T; // daily global radiation on a tilted surface (W h/m^2/day)
H_T = H_B + H_D + H_R;
return H_T;
}
var results = {}, current_day;
for(var i = 0; i < 365; i++) {
current_day = [];
for(var k = 0; k <= 90; k++) {
current_day.push([k, solar_efficiency(k, i)]);
}
current_day.sort(function(a, b) { return b[1] - a[1]; });
current_day.length = 1;
results[i] = current_day[0];
}
console.log(results);
The configurations like latitude and longitude are situated in a JSON file.
Here are the values I'm testing the program with :
{
"avg_sun_earth_dist" : 149597870.7,
"earth_axis" : 23.45,
"eccentricity" : 0.0167,
"semi_major_axis" : 149598261,
"local_time" : 12,
"std_meridian_long" : 0,
"current_longitude" : 2.294351,
"current_latitude" : 48.858844,
"ground_albedo" : 0.2
}
If you change the latitude a little bit you will see that you either get NaNs or the values stabilize but suddenly for certain values of "i" just skyrocket.
The problem seems to be this line :
W_sr = W_ss = Math.acos(-1 * Math.tan(Lat_local) * Math.tan(D));
I'm not sure if the input data is wrong and thus crashes the program or if I just implemented the formulas wrong.
Use that library , floats aren't good for such calculations. You'll always end up with errors. In every language for such arithmetics are used BigDecimals

Categories

Resources