I have a recursive function that takes a point {x,y} and calculates the next point in the sequence, recursively.
It looks a little like this:
var DECAY = 0.75;
var LENGTH = 150;
var ANGLE = 0.52;
getNextPoint(0, 0, ANGLE, LENGTH);
function getNextPoint (x, y, a, l) {
l *= DECAY;
a += ANGLE;
var x1 = x - Math.cos(a) * l;
var y1 = y - Math.sin(a) * l;
//We now have 2 points, draw lines etc.
getNextPoint(x1, y1, a, l);
}
How can I calculate a point (or 2 consecutive points) given a known iteration?
I know that the angle and length values for a given iteration could be calculated fairly easily with something like the following:
var a = ANGLE * iteration;
var l = LENGTH * Math.pow(DECAY, iteration);
But I would still need to know the position of the point at iteration - 1 to apply these values to?
Think of this as complex numbers. z = x + i*y is your point. b = cos(a)*l + i*sin(a)*l is some parameter, and c = cos(ANGLE)*DECAY + i*sin(ANGLE)*DECAY is a constant.
Initially you have z0 = 0 and b0 = c*LENGTH/DECAY. In each recursion you do
b(k+1) = b(k)*c
z(k+1) = z(k) - b
So you have
b1 = b0*c = c^2*LENGTH/DECAY
z1 = z0-b1 = -b1 = -c^2*LENGTH/DECAY
b2 = b1*c = c^3*LENGTH/DECAY
z2 = z1-b2 = -(c^2+c^3)*LENGTH/DECAY
⋮
zn = -(c^2+c^3+⋯+c^(n+1))*LENGTH/DECAY
If you ask Wolfram Alpha it will tell you that
c^2+c^3+⋯+c^(n+1) = c^2*(c^n - 1)/(c - 1)
You can make the denominator real if you multiply by the complex conjugate. Then you can turn the whole thing back into a formula for real numbers. So let's write
c = cr + i*ci cr = cos(ANGLE)*DECAY ci = sin(ANGLE)*DECAY
d = c^n = dr + i*di dr = cos(n*ANGLE)*pow(DECAY, n) di = …
Then we have
c^2*(d - 1)*(cr - i*ci - 1)/((cr + i*ci - 1)*(cr - i*ci - 1))
= ((cr + i*ci)*(cr + i*ci)*(dr + i*di - 1)*(cr - i*ci - 1)) /
((cr - 1)*(cr - 1)*ci*ci)
= ((cr^3*dr + cr*ci^2*dr - cr^2*ci*di - ci^3*di - cr^3 - cr*ci^2
- cr^2*dr + ci^2*dr + 2*cr*ci*di + cr^2 - ci^2) +
(cr^2*ci*dr + ci^3*dr + cr^3*di + cr*ci^2*di - cr^2*ci - ci^3
- 2*cr*ci*dr - cr^2*di + ci^2*di + 2*cr*ci))/((cr - 1)*(cr - 1)*ci*ci)
xn = -(cr^3*dr + cr*ci^2*dr - cr^2*ci*di - ci^3*di - cr^3 - cr*ci^2
- cr^2*dr + ci^2*dr + 2*cr*ci*di + cr^2 - ci^2) /
((cr - 1)*(cr - 1)*ci*ci) * LENGTH / DECAY
yn = -(cr^2*ci*dr + ci^3*dr + cr^3*di + cr*ci^2*di - cr^2*ci - ci^3
- 2*cr*ci*dr - cr^2*di + ci^2*di + 2*cr*ci) /
((cr - 1)*(cr - 1)*ci*ci) * LENGTH / DECAY
The expansions of the numerator came out of my CAS; it might well be that you can write this somewhat shorter, but I don't feel like multiplying those four terms manually just to try that.
Here is a working example to demonstrate all of this:
var ctxt = document.getElementById("MvG1").getContext("2d");
var sin = Math.sin, cos = Math.cos, pow = Math.pow;
var DECAY = 0.75;
var LENGTH = 150;
var ANGLE = 0.52;
var cr = cos(ANGLE)*DECAY, ci = sin(ANGLE)*DECAY;
var cr2 = cr*cr, ci2 = ci*ci, cr3 = cr2*cr, ci3 = ci2*ci;
var f = - LENGTH / DECAY / ((cr - 1)*(cr - 1)*ci*ci)
ctxt.beginPath();
ctxt.moveTo(100,450);
for (var n = 0; n < 20; ++n) {
var da = pow(DECAY, n), dr = cos(n*ANGLE)*da, di = sin(n*ANGLE)*da;
var xn, yn;
xn = (cr3*dr + cr*ci2*dr - cr2*ci*di - ci3*di - cr3 - cr*ci2
- cr2*dr + ci2*dr + 2*cr*ci*di + cr2 - ci2)*f;
yn = (cr2*ci*dr + ci3*dr + cr3*di + cr*ci2*di - cr2*ci - ci3
- 2*cr*ci*dr - cr2*di + ci2*di + 2*cr*ci)*f;
console.log([xn,yn]);
ctxt.lineTo(0.1*xn + 100, 0.1*yn + 450);
}
ctxt.stroke();
<canvas id="MvG1" width="300" height="500"></canvas>
Related
Recreating the way I color my Mandelbrot set I'm having a hard time implementing it in JavaScript. I currently use the common "escape time" algorithm:
for(px = 0; px < a; px+=scale){
for(py = 0; py < b; py+=scale){
x0 = panX + px/zm;
y0 = panY + py/zm;
var x = 0;
var y = 0;
var i = 0;
var xtemp;
var xSquare = x*x;
var ySquare = y*y;
while (x*x + y*y <= 4 && i < maxI) {
xtemp = x*x - y*y + x0
y = 2*x*y + y0
x = xtemp
i += 1;
}
//coloring
var shade = pallete.colourAt(i);
c.fillStyle = "#"+shade;
c.fillRect(px,py,scale, scale);
}
}
Here's the full code. I want to implement the part above to this pseudo code found at Wikipedia.
For each pixel (Px, Py) on the screen, do: { x0 = scaled x coordinate
of pixel (scaled to lie in the Mandelbrot X scale (-2.5, 1)) y0 =
scaled y coordinate of pixel (scaled to lie in the Mandelbrot Y scale
(-1, 1)) x = 0.0 y = 0.0 iteration = 0 max_iteration = 1000 // Here
N=2^8 is chosen as a reasonable bailout radius. while ( xx + yy <=
(1 << 16) AND iteration < max_iteration ) { xtemp = xx - yy + x0 y =
2*xy + y0 x = xtemp iteration = iteration + 1 } // Used to avoid
floating point issues with points inside the set. if ( iteration <
max_iteration ) { // sqrt of inner term removed using log
simplification rules. log_zn = log( xx + y*y ) / 2 nu = log( log_zn /
log(2) ) / log(2) // Rearranging the potential function. // Dividing
log_zn by log(2) instead of log(N = 1<<8) // because we want the
entire palette to range from the // center to radius 2, NOT our
bailout radius. iteration = iteration + 1 - nu } color1 =
palette[floor(iteration)] color2 = palette[floor(iteration) + 1] //
iteration % 1 = fractional part of iteration. color =
linear_interpolate(color1, color2, iteration % 1) plot(Px, Py, color)
}
To this:
for(px = 0; px < a; px+=scale){
for(py = 0; py < b; py+=scale){
//zoom factors
x0 = panX + px/zm;
y0 = panY + py/zm;
var x = 0;
var y = 0;
var i = 0;
var xtemp;
var xSquare = x*x;
var ySquare = y*y;
while (x*x + y*y <= 4 && i < maxI) {
/*ticks++
xtemp = x*x - y*y + x0
y = 2*x*y + y0
x = xtemp
i = i + 1*/
y = x*y;
y += y;
y += y0;
x = xSquare - ySquare + x0;
xSquare = Math.pow(x,2);
ySquare = Math.pow(y,2);
i += 1;
}
if ( i < maxI ) {
log_zn = Math.log( x*x + y*y ) / 2
nu = Math.log( log_zn / Math.log(2) ) / Math.log(2)
i += 1 - nu
}
color1 = palette.colourAt(Math.floor(i))
color2 = palette.colourAt(Math.floor(i) + 1)
/*****************
I dont know how to implement this.....
color = linear_interpolate(color1, color2, iteration % 1)
*****************/
c.fillStyle = color
c.fillRect(px,py,scale, scale);
}
}
But I don't know how to implement this part of pseudo-code:
color1 = palette[floor(iteration)]
color2 = palette[floor(iteration) + 1]
// iteration % 1 = fractional part of iteration.
color = linear_interpolate(color1, color2, iteration % 1)
plot(Px, Py, color)
Can someone help me understand and give a way to implement this?
The linear_interpolate function is supposed to calculate a color between two colors, based on the linear function y = mx + b.
To apply the linear function to colors, y is the output color, m is the difference between the two colors, b is the start color and x is a value between 0 and 1.
When x is 0, this function outputs the start color. When x is 1, this function outputs the end color.
To do this calculation we need the color in the form of three numbers. If you need to use hex strings, you'll have to split them and parse each two characters as a 16 bit number. I'm going to use a palette that is already in number form, because it is easier.
Here's my three color palette. I'm not recommending that you use these colors, it's just for demonstration:
let palette = [{r:255,g:0,b:0},{r:0,g:255,b:0},{r:0,g:0,b:0}]
This first function takes in iteration, which is probably not a whole number and may be larger than 1. It takes the floor of iteration, turning it into a whole number which an array index must be. Then it takes the remainder of iteration divided by 1 to get a number between 0 and 1.
function interpolation(iteration) {
let color1 = palette[Math.floor(iteration)];
let color2 = palette[Math.floor(iteration) + 1];
return linear_interpolate(color1, color2, iteration % 1);
}
Now we need to create the linear interpolation function, which must apply the linear function to each color channel and use floor to turn them into a whole number. I have it returning a css color in rgb(), but you could convert it into hex instead.
function linear_interpolate(color1, color2, ratio) {
let r = Math.floor((color2.r - color1.r) * ratio + color1.r);
let g = Math.floor((color2.g - color1.g) * ratio + color1.g);
let b = Math.floor((color2.b - color1.b) * ratio + color1.b);
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
Here is the code shading rectangles: https://jsfiddle.net/q7kLszud/
I'm trying to convert svg path to canvas in javascript, however it's really hard to map svg path elliptical arcs to canvas path. One of the ways is to approximate using multiple bezier curves.
I have successfully implemented the approximation of elliptical arcs with bezier curves however the approximation isn't very accurate.
My code:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
ctx.strokeWidth = 2;
ctx.strokeStyle = "#000000";
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max)
}
function svgAngle(ux, uy, vx, vy ) {
var dot = ux*vx + uy*vy;
var len = Math.sqrt(ux*ux + uy*uy) * Math.sqrt(vx*vx + vy*vy);
var ang = Math.acos( clamp(dot / len,-1,1) );
if ( (ux*vy - uy*vx) < 0)
ang = -ang;
return ang;
}
function generateBezierPoints(rx, ry, phi, flagA, flagS, x1, y1, x2, y2) {
var rX = Math.abs(rx);
var rY = Math.abs(ry);
var dx2 = (x1 - x2)/2;
var dy2 = (y1 - y2)/2;
var x1p = Math.cos(phi)*dx2 + Math.sin(phi)*dy2;
var y1p = -Math.sin(phi)*dx2 + Math.cos(phi)*dy2;
var rxs = rX * rX;
var rys = rY * rY;
var x1ps = x1p * x1p;
var y1ps = y1p * y1p;
var cr = x1ps/rxs + y1ps/rys;
if (cr > 1) {
var s = Math.sqrt(cr);
rX = s * rX;
rY = s * rY;
rxs = rX * rX;
rys = rY * rY;
}
var dq = (rxs * y1ps + rys * x1ps);
var pq = (rxs*rys - dq) / dq;
var q = Math.sqrt( Math.max(0,pq) );
if (flagA === flagS)
q = -q;
var cxp = q * rX * y1p / rY;
var cyp = - q * rY * x1p / rX;
var cx = Math.cos(phi)*cxp - Math.sin(phi)*cyp + (x1 + x2)/2;
var cy = Math.sin(phi)*cxp + Math.cos(phi)*cyp + (y1 + y2)/2;
var theta = svgAngle( 1,0, (x1p-cxp) / rX, (y1p - cyp)/rY );
var delta = svgAngle(
(x1p - cxp)/rX, (y1p - cyp)/rY,
(-x1p - cxp)/rX, (-y1p-cyp)/rY);
delta = delta - Math.PI * 2 * Math.floor(delta / (Math.PI * 2));
if (!flagS)
delta -= 2 * Math.PI;
var n1 = theta, n2 = delta;
// E(n)
// cx +acosθcosη−bsinθsinη
// cy +asinθcosη+bcosθsinη
function E(n) {
var enx = cx + rx * Math.cos(phi) * Math.cos(n) - ry * Math.sin(phi) * Math.sin(n);
var eny = cy + rx * Math.sin(phi) * Math.cos(n) + ry * Math.cos(phi) * Math.sin(n);
return {x: enx,y: eny};
}
// E'(n)
// −acosθsinη−bsinθcosη
// −asinθsinη+bcosθcosη
function Ed(n) {
var ednx = -1 * rx * Math.cos(phi) * Math.sin(n) - ry * Math.sin(phi) * Math.cos(n);
var edny = -1 * rx * Math.sin(phi) * Math.sin(n) + ry * Math.cos(phi) * Math.cos(n);
return {x: ednx, y: edny};
}
var n = [];
n.push(n1);
var interval = Math.PI/4;
while(n[n.length - 1] + interval < n2)
n.push(n[n.length - 1] + interval)
n.push(n2);
function getCP(n1, n2) {
var en1 = E(n1);
var en2 = E(n2);
var edn1 = Ed(n1);
var edn2 = Ed(n2);
var alpha = Math.sin(n2 - n1) * (Math.sqrt(4 + 3 * Math.pow(Math.tan((n2 - n1)/2), 2)) - 1)/3;
console.log(en1, en2);
return {
cpx1: en1.x + alpha*edn1.x,
cpy1: en1.y + alpha*edn1.y,
cpx2: en2.x - alpha*edn2.x,
cpy2: en2.y - alpha*edn2.y,
en1: en1,
en2: en2
};
}
var cps = []
for(var i = 0; i < n.length - 1; i++) {
cps.push(getCP(n[i],n[i+1]));
}
return cps;
}
// M100,200
ctx.moveTo(100,200)
// a25,100 -30 0,1 50,-25
var rx = 25, ry=100 ,phi = -30 * Math.PI / 180, fa = 0, fs = 1, x = 100, y = 200, x1 = x + 50, y1 = y - 25;
var cps = generateBezierPoints(rx, ry, phi, fa, fs, x, y, x1, y1);
var limit = 4;
for(var i = 0; i < limit && i < cps.length; i++) {
ctx.bezierCurveTo(cps[i].cpx1, cps[i].cpy1,
cps[i].cpx2, cps[i].cpy2,
i < limit - 1 ? cps[i].en2.x : x1, i < limit - 1 ? cps[i].en2.y : y1);
}
ctx.stroke()
With the result:
The red line represents the svg path elliptical arc and the black line represents the approximation
How can I accurately draw any possible elliptical arc on canvas?
Update:
Forgot to mention the original source of the algorithm: https://mortoray.com/2017/02/16/rendering-an-svg-elliptical-arc-as-bezier-curves/
So both bugs are simply:
n2 should be declare n2 = theta + delta;
The E and Ed functions should use rX rY rather than rx ry.
And that fixes everything. Though the original should have obviously opted to divide up the arcs into equal sized portions rather than pi/4 sized elements and then appending the remainder. Just find out how many parts it will need, then divide the range into that many parts of equal size, seems like a much more elegant solution, and because error goes up with length it would also be more accurate.
See: https://jsfiddle.net/Tatarize/4ro0Lm4u/ for working version.
It's not just off in that one respect it doesn't work most anywhere. You can see that depending on phi, it does a lot of variously bad things. It's actually shockingly good there. But, broken everywhere else too.
https://jsfiddle.net/Tatarize/dm7yqypb/
The reason is that the declaration of n2 is wrong and should read:
n2 = theta + delta;
https://jsfiddle.net/Tatarize/ba903pss/
But, fixing the bug in the indexing, it clearly does not scale up there like it should. It might be that arcs within the svg standard are scaled up so that there can certainly be a solution whereas in the relevant code they seem like they are clamped.
https://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
"If rx, ry and φ are such that there is no solution (basically, the
ellipse is not big enough to reach from (x1, y1) to (x2, y2)) then the
ellipse is scaled up uniformly until there is exactly one solution
(until the ellipse is just big enough)."
Testing this, since it does properly have code that should scale it up, I changed it green when that code got called. And it turns green when it screws up. So yeah, it's failure to scale for some reason:
https://jsfiddle.net/Tatarize/tptroxho/
Which means something is using rx rather than the scaled rX and it's the E and Ed functions:
var enx = cx + rx * Math.cos(phi) * Math.cos(n) - ry * Math.sin(phi) * Math.sin(n);
These rx references must read rX and rY for ry.
var enx = cx + rX * Math.cos(phi) * Math.cos(n) - rY * Math.sin(phi) * Math.sin(n);
Which finally fixes the last bug, QED.
https://jsfiddle.net/Tatarize/4ro0Lm4u/
I got rid of the canvas, moved everything to svg and animated it.
var svgNS = "http://www.w3.org/2000/svg";
var svg = document.getElementById("svg");
var arcgroup = document.getElementById("arcgroup");
var curvegroup = document.getElementById("curvegroup");
function doArc() {
while (arcgroup.firstChild) {
arcgroup.removeChild(arcgroup.firstChild);
} //clear old svg data. -->
var d = document.createElementNS(svgNS, "path");
//var path = "M100,200 a25,100 -30 0,1 50,-25"
var path = "M" + x + "," + y + "a" + rx + " " + ry + " " + phi + " " + fa + " " + fs + " " + " " + x1 + " " + y1;
d.setAttributeNS(null, "d", path);
arcgroup.appendChild(d);
}
function doCurve() {
var cps = generateBezierPoints(rx, ry, phi * Math.PI / 180, fa, fs, x, y, x + x1, y + y1);
while (curvegroup.firstChild) {
curvegroup.removeChild(curvegroup.firstChild);
} //clear old svg data. -->
var d = document.createElementNS(svgNS, "path");
var limit = 4;
var path = "M" + x + "," + y;
for (var i = 0; i < limit && i < cps.length; i++) {
if (i < limit - 1) {
path += "C" + cps[i].cpx1 + " " + cps[i].cpy1 + " " + cps[i].cpx2 + " " + cps[i].cpy2 + " " + cps[i].en2.x + " " + cps[i].en2.y;
} else {
path += "C" + cps[i].cpx1 + " " + cps[i].cpy1 + " " + cps[i].cpx2 + " " + cps[i].cpy2 + " " + (x + x1) + " " + (y + y1);
}
}
d.setAttributeNS(null, "d", path);
d.setAttributeNS(null, "stroke", "#000");
curvegroup.appendChild(d);
}
setInterval(phiClock, 50);
function phiClock() {
phi += 1;
doCurve();
doArc();
}
doCurve();
doArc();
I have the following mesh which is generated by random points and creating triangles using Delaunay triangulation. Then I apply spring force per triangle on each of its vertices. But for some reason the equilibrium is always shifted to the left.
Here is a video of the behaviour:
https://youtu.be/gb5aj05zkIc
Why this is happening?
Here is the code for the physics:
for ( let i=0; i < mesh.geometry.faces.length; i++) {
let face = mesh.geometry.faces[i];
let a = mesh.geometry.vertices[face.a];
let b = mesh.geometry.vertices[face.b];
let c = mesh.geometry.vertices[face.c];
let p1 = Vertcies[face.a];
let p2 = Vertcies[face.b];
let p3 = Vertcies[face.c];
update_force_points(p1, p2, a, b);
update_force_points(p1, p3, a, c);
update_force_points(p2, p3, b, c);
}
function update_force_points(p1, p2, p1p, p2p) {
// get all the verticies
var dx = (p1.x - p2.x);
var dy = (p1.y - p2.y);
var len = Math.sqrt(dx*dx + dy*dy);
let fx = (ks * (len - r) * (dx/len)) + ((kd * p2.vx - p1.vx));
let fy = (ks * (len - r) * (dy/len)) + ((kd * p2.vy - p1.vy));
if ( ! p1.fixed ) {
p1.fx = (ks * (len - r) * (dx/len)) + ((kd * p2.vx - p1.vx));
p1.fy = (ks * (len - r) * (dy/len)) + ((kd * p2.vy - p1.vy));
}
if ( ! p2.fixed ) {
p2.fx = -1 * p1.fx;
p2.fy = -1 * p1.fy;
}
p1.vx += p1.fx / mass;
p1.vy += p1.fy / mass;
p2.vx += p2.fx / mass;
p2.vy += p2.fy / mass;
p1.x += p1.vx;
p1.y += p1.vy;
p2.x += p2.vx;
p2.y += p2.vy;
p1p.x = p1.x;
p1p.y = p1.y;
p2p.x = p2.x;
p2p.y = p2.y;
p2p.z = 0.0;
p1p.z = 0.0;
}
At the moment you're doing velocity calculations and assigning new positions at the same time, so the balance will change depending on the order that you cycle through points in. I would guess that points at the bottom left are either at the beginning of the vertex list, or at the end.
try doing all the p#.vx calculations linearly, then do a second pass where you just do p#.x += p#.vx
that way you calculate all necessary velocities based on a snapshot of where points were the previous frame, then you update their positions after all points have new velocities.
So do:
for(var i = 0; i < #; i++){
updateforces(bla,bla,bla) //don't assign position in here, just add forces to the velocity
}
for(var i =0; i < #; i++){
updateposition(bla,bla,bla)
}
As the topic says i have a polygon and want to calculate the center of mass (centroid). I take the geo-coordinates, transform them into pixel cooridinates use the formula found on http://en.wikipedia.org/wiki/Centroid and transform the the calculated pixels back into geo-coordinates.
The result seems just wrong (i can't post pictures). The relevant code snippet is:
this.drawPolygonCenter = function (mapService, coords) {
var sumY = 0;
var sumX = 0;
var partialSum = 0;
var sum = 0;
var cm = mapService.getCurrentMapReference();
var points = [];
coords.forEach(function (c, idx) {
points.push(cm.geoToPixel(c));
console.log("x: " + points[idx].x + " y: " + points[idx].y);
});
var n = points.length;
for (var i = 0; i < n - 1; i++) {
partialSum = points[i].x * points[i + 1].y - points[i + 1].x * points[i].y;
sum += partialSum;
sumX += (points[i].x + points[i + 1].x) * partialSum;
sumY += (points[i].y + points[i + 1].y) * partialSum;
}
var area = 0.5 * sum;
var div = 6 * area;
var x1 = sumX / div;
var y1 = sumY / div;
console.log("Centroid: x= " + x1 + " y= " + y1); // debug
var pinLocation = cm.pixelToGeo(Math.ceil(x1), Math.ceil(y1));
var pin = this.createCenterPin(pinLocation);
cm.objects.add(new nokia.maps.map.StandardMarker(pinLocation)); // debug
I reckon your calculation has a rounding error is due switching between pixels and lat/longs - there is no need to do this - you can work with lat/longs directly.
You can add a getCentroid() method to the Polygon class as shown:
nokia.maps.map.Polygon.prototype.getCentroid = function (arg) {
var signedArea = 0,
len = this.path.getLength(),
centroidLongitude = 0,
centroidLatitude = 0;
for (i=0; i < len; i++){
var a = this.path.get(i),
b = this.path.get( i + 1 < len ? i + 1 : 0);
signedArea +=
((a.longitude * b.latitude) - (b.longitude * a.latitude));
centroidLongitude += (a.longitude + b.longitude) *
((a.longitude * b.latitude) - (b.longitude * a.latitude));
centroidLatitude += (a.latitude + b.latitude) *
((a.longitude * b.latitude) - (b.longitude * a.latitude));
}
signedArea = signedArea /2;
centroidLongitude = centroidLongitude/ (6 * signedArea);
centroidLatitude = centroidLatitude/ (6 * signedArea);
return new nokia.maps.geo.Coordinate(centroidLatitude, centroidLongitude);
};
You can call polygon.getCentroid() (e.g. to add a marker) as follows:
map.objects.add(new nokia.maps.map.Marker(polygon.getCentroid()));
Note, you may still get some edge effects of your Polygon crosses the 180th meridian.(use the isIDL()method to check) . In this case you may need to add 360 to each latitude prior to making the calculations, and substract it from the final result.
I'm calculating the arclength (length of a cubic bezier curve) with this algorithm
function getArcLength(path) {
var STEPS = 1000; // > precision
var t = 1 / STEPS;
var aX=0;
var aY=0;
var bX=0, bY=0;
var dX=0, dY=0;
var dS = 0;
var sumArc = 0;
var j = 0;
for (var i=0; i<STEPS; j = j + t) {
aX = bezierPoint(j, path[0], path[2], path[4], path[6]);
aY = bezierPoint(j, path[1], path[3], path[5], path[7]);
dX = aX - bX;
dY = aY - bY;
// deltaS. Pitagora
dS = Math.sqrt((dX * dX) + (dY * dY));
sumArc = sumArc + dS;
bX = aX;
bY = aY;
i++;
}
return sumArc;
}
But what I get is something like 915. But the curve is 480 and no more. (I know for sure this because the curve is almost a line)
The path array has this values:
498 51 500 52 500 53 500 530
The bezierPoint function is:
function bezierPoint(t, o1, c1, c2, e1) {
var C1 = (e1 - (3.0 * c2) + (3.0 * c1) - o1);
var C2 = ((3.0 * c2) - (6.0 * c1) + (3.0 * o1));
var C3 = ((3.0 * c1) - (3.0 * o1));
var C4 = (o1);
return ((C1*t*t*t) + (C2*t*t) + (C3*t) + C4)
}
What I'm doing wrong?
Because bX and bY are initialized to 0, the first segment when i = 0 measures the distance from the origin to the start of the path. This adds an extra sqrt(498^2+51^2) to the length. If you initialize bX = path[0] and bY = path[1], I think it will work.