I have a function in one of my projects that does some vector calculations. Recently I started noticing inconsistencies in the results and when I went digging, I found out that in some places, JavaScript would perform a concatenation instead of an addition. Upon searching I found that you could prevent it by adding a parsefloat() to ensure the variables are float. So now I have this :
function getsqSegDistID(p, p1, p2) {
var x = parseFloat(p1.x),
y = parseFloat(p1.y),
dx = p2.x - x,
dy = p2.y - y;
if (dx !== 0 || dy !== 0) {
var t = (parseFloat((p.x - x) * dx) + parseFloat((p.y - y) * dy)) / (parseFloat(dx * dx) + parseFloat(dy * dy));
if (t > 1) {
x = p2.x;
y = p2.y;
} else if (t > 0) {
x = parseFloat(x) + parseFloat(dx * t);
y = parseFloat(y) + parseFloat(dy * t);
}
}
dx = p.x - x;
dy = p.y - y;
return parseFloat(dx*dx) + parseFloat(dy*dy);
}
See all the parseFloat I put in there ?
So My issue is that adding a parsefloat before every addition has brought down the performance of the function significantly. This now takes 5x more time to execute than it previously did.
1. Do I have to put a parseFloat before every addition? or is there a better way?
2. I am a C/Java developer, but I'm helping out a friend to write some processing logic. So why the heck doesn't JavaScript have some form of datatype enforcement?
JavaScript will attempt concatenation if one or more of the operands is a string. You certainly don't need to call parseFloat every single time, just make sure you call it on all of your passed variables. I see you initially parse p1.x and p1.y, but don't do the same for the values in p or p2.
JavaScript is an interpreted language, and is therefore dynamically-typed. Statically-typed languages must be compiled; it's just one of many tradeoffs in language design.
Related
I found an implementation on the internet that draws perfect lines and it claims to be Bresenham's. Seeing the results it indeed resembles a lot Bresenham's, I can't spot any difference, but the code really seems far away from the original one.
This is the code my professor showed in class and you can find it in any youtube tutorial:
void lineBres (int x0, int y0, int xEnd, int yEnd) {
int dx = fabs (xEnd - x0), dy = fabs(yEnd - y0);
int p = 2 * dy - dx;
int twoDy = 2 * dy, twoDyMinusDx = 2 * (dy - dx);
int x, y;
/* Determine which endpoint to use as start position. */
if (x0 > xEnd) {
x = xEnd;
y = yEnd;
xEnd = x0;
}
else {
x = x0;
y = y0;
}
setPixel (x, y);
while (x < xEnd) {
x++;
if (p < 0) p += twoDy;
else {
y++;
p += twoDyMinusDx;
}
setPixel (x, y);
}
}
This code below is not necessarily correct, but that's what makes the most sense to me in how Bresenham's line would look for all cases. https://github.com/ashiagarwal73/Bresenham-s-line-algorithm-for-all-quadrants/blob/master/Bresenhams.c
And here is the implementation I found (I am using javascript):
const drawBresenhamsLine = (startPoint, endPoint, width, colour, ctx) => {
let dx = Math.abs(endPoint.x - startPoint.x);
let dy = Math.abs(endPoint.y - startPoint.y);
let incY = startPoint.y < endPoint.y ? 1 : -1;
let incX = startPoint.x < endPoint.x ? 1 : -1;
let err = dx - dy;
while (true) {
putPixel(startPoint, width, colour, ctx);
if (startPoint.x === endPoint.x && startPoint.y === endPoint.y) break;
let p = 2 * err;
if (p > -dy) {
err -= dy;
startPoint.x += incX;
}
if (p < dx) {
err += dx;
startPoint.y += incY;
}
}
};
So I just need to prove it is indeed Bresenham's and how it relates to the 'original' implementation, but it seems to me it's using a different approach somehow.
dx and dy are just the same, in order to adapt to each quadrant it is comparing the differences between each axis and keeping in incY and incX. So here is when it becomes weird. First, it keeps the difference between dx and dy in err, this value changes with each interaction.
It has a while (true) and it's drawing a pixel at the beginning of each loop instead of the end, the stopping condition is when startPoint is equal to endPoint. It doubles the err value and then compares it with -dy, if it's true then it will update err with dy and increment the x-axis. Then it compares err with dx, if it's true then it will update err with dx and increment the y-axis.
In the original, the x-axis is always incremented and the y-axis is sometimes incremented. After running the new version sometimes, I believe that one of them (x xor y) is always incremented, I don't think both conditions can be true or false at the same time due to the math being applied.
The original version is using p which is 2 * dy - dx to control the logic and it is modifying its value at each interaction. The new version is using err which is dx - dy to control the logic and it is modifying its value at each interaction.
I am starting to think they differ quite a lot and the new version may not be Bresenham's implementation. So I am asking you, my dear reader, to clarify this one for me.
I am seeking for some already done solution to simulate gravity interaction of n-body system, like our solar system for example, in any programming language (but preferably Javascript or C). I found this question, and it looks like exactly what I need, but, as I see, there's no interoperation between planets theyselves, only between sun and planets. I'm not so bad in JS, but really weak (more lazy) in geometry and gravity, so maybe somebody can give me an advice of how to enhance this code (or make the new one) to be able to simulate not the sun-to-planets, but full n-body gravity interaction? Or maybe you know such a software already written?
Cause as I understand, all I need is to change the code to calculate and display this:
//...
distanceTo: function(p2) {
var dx = p2.position.x - this.position.x,
dy = p2.position.y - this.position.y;
return Math.sqrt(dx ** 2 + dy ** 2);
},
attraction: function(p2) {
var dx = p2.position.x - this.position.x;
var dy = p2.position.y - this.position.y;
var d = Math.sqrt(dx ** 2 + dy ** 2);
this.f = G * (this.mass * p2.mass) / (d ** 2);
var theta = Math.atan2(dy, dx);
var fx = Math.cos(theta) * this.f;
var fy = Math.sin(theta) * this.f;
this.velocity.x += fx / this.mass;
this.velocity.y += fy / this.mass;
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
}
//...
for every single body in the system, but I'm not sure is it the right way to just iterate this code over bodies, due to such an iteration will dynamically change bodies' positions, which have been used for previous calculations within the iteration. Hope I spell it clear.
Is this possible at all? Is not this related to Three-body problem exponential computation complexity increasing? If not, can you please just direct me to right formulas and spell out simply for me, what to write and how?
Thanks!
I'm trying to create an algorithm that detects discontinuities (like vertical asymptotes) within functions between an interval for the purpose of plotting graphs without these discontinuous connecting lines. Also, I only want to evaluate within the interval so bracketing methods like bisection seems good for that.
EDIT
https://en.wikipedia.org/wiki/Classification_of_discontinuities
I realize now there are a few different kinds of discontinuities. I'm mostly interested in jump discontinuities for graphical purposes.
I'm using a bisection method as I've noticed that discontinuities occur where the slope tends to infinity or becomes vertical, so why not narrow in on those sections where the slope keeps increasing and getting steeper and steeper. The point where the slope is a vertical line, that's where the discontinuity exists.
Approach
Currently, my approach is as follows. If you subdivide the interval using a midpoint into 2 sections and compare which section has the steepest slope, then that section with the steepest slope becomes the new subinterval for the next evaluation.
Termination
This repeats until it converges by either slope becoming undefined (reaching infinity) or the left side or the right side of the interval equaling the middle (I think this is because the floating-point decimal runs out of precision and cannot divide any further)
(1.5707963267948966 + 1.5707963267948968) * .5 = 1.5707963267948966
Example
function - floor(x)
(blue = start leftX and rightX, purple = midpoint, green = 2nd iteration midpoints points, red = slope lines per iteration)
As you can see from the image, each bisection narrows into the discontinuity and the slope keeps getting steeper until it becomes a vertical line at the discontinuity point at x=1.
To my surprise this approach seems to work for step functions like floor(x) and tan(x), but it's not that great for 1/x as it takes too many iterations (I'm thinking of creating a hybrid method where I use either illinois or ridders method on the inverse of 1/x as it those tend to find the root in just one iteration).
Javascript Code
/* Math function to test on */
function fn(x) {
//return (Math.pow(Math.tan(x), 3));
return 1/x;
//return Math.floor(x);
//return x*((x-1-0.001)/(x-1));
}
function slope(x1, y1, x2, y2) {
return (y2 - y1) / (x2 - x1);
}
function findDiscontinuity(leftX, rightX, fn) {
while (true) {
let leftY = fn(leftX);
let rightY = fn(rightX);
let middleX = (leftX + rightX) / 2;
let middleY = fn(middleX);
let leftSlope = Math.abs(slope(leftX, leftY, middleX, middleY));
let rightSlope = Math.abs(slope(middleX, middleY, rightX, rightY));
if (!isFinite(leftSlope) || !isFinite(rightSlope)) return middleX;
if (middleX === leftX || middleX === rightX) return middleX;
if (leftSlope > rightSlope) {
rightX = middleX;
rightY = middleY;
} else {
leftX = middleX;
leftY = middleY;
}
}
}
Problem 1 - Improving detection
For the function x*((x-1-0.001)/(x-1)), the current algorithm has a hard time detecting the discontinuity at x=1 unless I make the interval really small. As an alternative, I could also add most subdivisions but I think the real problem is using slopes as they trick the algorithm into choosing the incorrect subinterval (as demonstrated in the image below), so this approach is not robust enough. Maybe there are some statistical methods that can help determine a more probable interval to select. Maybe something like least squares for measuring the differences and maybe applying weights or biases!
But I don't want the calculations to get too heavy and 5 points of evaluation are the max I would go with per iteration.
EDIT
After looking at problem 1 again, where it selects the wrong (left-hand side) subinterval. I noticed that the only difference between the subintervals was the green midpoint distance from their slope line. So taking inspiration from linear regression, I get the squared distance from the slope line to the midpoints [a, fa] and [b, fb] corresponding to their (left/right) subintervals. And which subinterval has the greatest change/deviation is the one chosen for further subdivision, that is, the greater of the two residuals.
This further improvement resolves problem 1. Although, it now takes around 593 iterations to find the discontinuity for 1/x. So I've created a hybrid function that uses ridders method to find the roots quicker for some functions and then fallback to this new approach. I have given up on slopes as they don't provide enough accurate information.
Problem 2 - Jump Threshold
I'm not sure how to incorporate a jump threshold and what to use for that calculation, don't think slopes would help.
Also, if the line thickness for the graph is 2px and 2 lines of a step function were on top of each other then you wouldn't be able to see the gap of 2px between those lines. So the minimum jump gap would be calculated as such
jumpThreshold = height / (ymax-ymin) = cartesian distance per pixel
minJumpGap = jumpThreshold * 2
But I don't know where to go from here! And once again, maybe there are statistical methods that can help to determine the change in function so that the algorithm can terminate quickly if there's no indication of a discontinuity.
Overall, any help or advice in improving what I got already would be much appreciated!
EDIT
As the above images explains, the more divergent the midpoints are the greater the need for more subdivisions for further inspection for that subinterval. While, if the points mostly follow a straight line trend where the midpoints barely deviate then should exit early. So now it makes sense to use the jumpThreshold in this context.
Maybe there's further analysis that could be done like measuring the curvature of the points in the interval to see whether to terminate early and further optimize this method. Zig zag points or sudden dips would be the most promising. And maybe after a certain number of intervals, keep widening the jumpThreshold as for a discontinuity you expect the residual distance to rapidly increase towards infinity!
Updated code
let ymax = 5, ymin = -5; /* just for example */
let height = 500; /* 500px screen height */
let jumpThreshold = Math.pow(.5 * (ymax - ymin) / height, 2); /* fraction(half) of a pixel! */
/* Math function to test on */
function fn(x) {
//return (Math.pow(Math.tan(x), 3));
return 1 / x;
//return Math.floor(x);
//return x * ((x - 1 - 0.001) / (x - 1));
//return x*x;
}
function findDiscontinuity(leftX, rightX, jumpThreshold, fn) {
/* try 5 interations of ridders method */
/* usually this approach can find the exact reciprocal root of a discountinuity
* in 1 iteration for functions like 1/x compared to the bisection method below */
let iterations = 5;
let root = inverseRidderMethod(leftX, rightX, iterations, fn);
let limit = fn(root);
if (Math.abs(limit) > 1e+16) {
if (root >= leftX && root <= rightX) return root;
return NaN;
}
root = discontinuityBisection(leftX, rightX, jumpThreshold, fn);
return root;
}
function discontinuityBisection(leftX, rightX, jumpThreshold, fn) {
while (true) {
let leftY = fn(leftX);
let rightY = fn(rightX);
let middleX = (leftX + rightX) * .5;
let middleY = fn(middleX);
let a = (leftX + middleX) * .5;
let fa = fn(a);
let b = (middleX + rightX) * .5;
let fb = fn(b);
let leftResidual = Math.pow(fa - (leftY + middleY) * .5, 2);
let rightResidual = Math.pow(fb - (middleY + rightY) * .5, 2);
/* if both subinterval midpoints (fa,fb) barely deviate from their slope lines
* i.e. they're under the jumpThreshold, then return NaN,
* indicating no discountinuity with the current threshold,
* both subintervals are mostly straight */
if (leftResidual < jumpThreshold && rightResidual < jumpThreshold) return NaN;
if (!isFinite(fa) || a === leftX || a === middleX) return a;
if (!isFinite(fb) || b === middleX || b === rightX) return b;
if (leftResidual > rightResidual) {
/* left hand-side subinterval */
rightX = middleX;
middleX = a;
} else {
/* right hand-side subinterval */
leftX = middleX;
middleX = b;
}
}
}
function inverseRidderMethod(min, max, iterations, fn) {
/* Modified version of RiddersSolver from Apache Commons Math
* http://commons.apache.org/
* https://www.apache.org/licenses/LICENSE-2.0.txt
*/
let x1 = min;
let y1 = 1 / fn(x1);
let x2 = max;
let y2 = 1 / fn(x2);
// check for zeros before verifying bracketing
if (y1 == 0) {
return min;
}
if (y2 == 0) {
return max;
}
let functionValueAccuracy = 1e-55;
let relativeAccuracy = 1e-16;
let oldx = Number.POSITIVE_INFINITY;
let i = 0;
while (i < iterations) {
// calculate the new root approximation
let x3 = 0.5 * (x1 + x2);
let y3 = 1 / fn(x3);
if (!isFinite(y3)) return NaN;
if (Math.abs(y3) <= functionValueAccuracy) {
return x3;
}
let delta = 1 - (y1 * y2) / (y3 * y3); // delta > 1 due to bracketing
let correction = (signum(y2) * signum(y3)) * (x3 - x1) / Math.sqrt(delta);
let x = x3 - correction; // correction != 0
if (!isFinite(x)) return NaN;
let y = 1 / fn(x);
// check for convergence
let tolerance = Math.max(relativeAccuracy * Math.abs(x), 1e-16);
if (Math.abs(x - oldx) <= tolerance) {
return x;
}
if (Math.abs(y) <= functionValueAccuracy) {
return x;
}
// prepare the new interval for the next iteration
// Ridders' method guarantees x1 < x < x2
if (correction > 0.0) { // x1 < x < x3
if (signum(y1) + signum(y) == 0.0) {
x2 = x;
y2 = y;
} else {
x1 = x;
x2 = x3;
y1 = y;
y2 = y3;
}
} else { // x3 < x < x2
if (signum(y2) + signum(y) == 0.0) {
x1 = x;
y1 = y;
} else {
x1 = x3;
x2 = x;
y1 = y3;
y2 = y;
}
}
oldx = x;
}
}
function signum(a) {
return (a < 0.0) ? -1.0 : ((a > 0.0) ? 1.0 : a);
}
/* TEST */
console.log(findDiscontinuity(.5, .6, jumpThreshold, fn));
Python Code
I don't mind if the solution is provided in Javascript or Python
import math
def fn(x):
try:
# return (math.pow(math.tan(x), 3))
# return 1 / x
# return math.floor(x)
return x * ((x - 1 - 0.001) / (x - 1))
except ZeroDivisionError:
return float('Inf')
def slope(x1, y1, x2, y2):
try:
return (y2 - y1) / (x2 - x1)
except ZeroDivisionError:
return float('Inf')
def find_discontinuity(leftX, rightX, fn):
while True:
leftY = fn(leftX)
rightY = fn(rightX)
middleX = (leftX + rightX) / 2
middleY = fn(middleX)
leftSlope = abs(slope(leftX, leftY, middleX, middleY))
rightSlope = abs(slope(middleX, middleY, rightX, rightY))
if not math.isfinite(leftSlope) or not math.isfinite(rightSlope):
return middleX
if middleX == leftX or middleX == rightX:
return middleX
if leftSlope > rightSlope:
rightX = middleX
rightY = middleY
else:
leftX = middleX
leftY = middleY
slowly learning JavaScript on the side and wanted to try and animate this with Three.JS:
https://www.reddit.com/r/gifs/comments/ag6or3/send_this_to_your_loved_ones_for_valentines/
I was trying to re-create that equation but ran into a wall in that the code below is not producing the right result. I had read that JS has some big issues with floating point numbers and in particular cubed roots don't really work all that well.
for (var x = -100; x < 100; x++)
{
y = Math.pow(x, 2/3) + 0.9 * (Math.pow(3.0 - (x*x), 0.5)) * Math.sin(10 *
Math.PI * x)
}
Does that look right to you JS masters?
Here is my code implementation in trying to get this to work including the fix mentioned below.
https://codesandbox.io/s/vjm4xox185
Look at the range of the graph you linked to. The heart is being drawn in the range of x: [-2, 2] , but your loop is from x: [-100, 100]. This means you'll probably get undefined results for all x values except -1, 0, 1. Try narrowing down the range of your for() loop, and you should get the desired result.
The problem is that the result of the calculation of (Math.pow(3.0 - (x*x), 0.5)) return NAN as if not realistic number
Read here for more information about Math.pow(negativeNumber, 0.5)
so i added validPow that will validate the x is positive or negative and return the right result.
for (var x = -100; x < 100; x++)
{
y = (Math.pow(x, 2/3) + 0.9) * (validPow(3.0 - (x*x), 0.5)) *
Math.sin(10 * Math.PI * x)
console.log(y)
}
function validPow(x, y)
{
var result = Math.pow(x, y);
if (x > 0)
{
return result;
}
else
{
return -1 * Math.pow(-x, y);
}
}
Finally solved this.
It came down to this line with the key being to use Math.abs(x) inside the first Math.pow statement:
var y = Math.pow(Math.abs(x), 0.66) + (0.9 * Math.sqrt(3.3 - x * x)) * Math.sin(10 * Math.PI * x);
Thanks for everyone who provided input and help!
You can view the final result here:
https://codesandbox.io/s/vjm4xox185
I was creating something like a 2d gravity simulator, just for fun, and noticed that I'm a complete idiot in terms of math. I just can't get the gravity to work.
I've tried following the instructions found here but it looks weird and when the distance reaches zero, it goes completely buggy. If I add 1 to the distance as recommended in the question, all objects go upper left. I've even tried not modifying gravity when distances reach zero, but this doesn't change the behavior.
Here's the algorithm I'm using to apply gravity:
var distX = obj1.x - obj2.x,
distY = obj1.y - obj2.y;
if (obj1 != obj2) {
if (distY != 0) {
obj1.vy += -(1 / (distY));
}
if (distX != 0) {
obj1.vx += -(1 / (distX));
}
}
I've tried using other algorithms too, but most of them don't care for the distance between objects.
Note that I want the gravity to affect distant objects less than closer objects.
Instead of solving any equations we could use an approximation. dv/dt = G*M*m/r^2, but for small t we could use the approximation Δv = (G*M*m/r^2)*Δt.
When the objects collide I have implemented perfectly inelastic collision (see Wikipedia). This prevents the distance between two objects from being to small and therefore the maximum force is limited.
I also moved the part of the code where the object's position is changed to a separate loop, so the forces calculated for obj1 and obj2 are equal in size.
Demo
function tick() {
allObjs.forEach(function (obj1) {
allObjs.forEach(function (obj2) {
var diffX = obj2.x - obj1.x,
var diffY = obj2.y - obj1.y;
var distSquare = diffX*diffX + diffY*diffY
var dist = Math.sqrt(distSquare);
if (obj1 != obj2) {
if (dist > obj1.w/2 + obj2.w/2) {
//If you add mass to the objects change to obj2.mass
//instead of 50
var totalForce = 50/distSquare;
obj1.vx += totalForce * diffX / dist;
obj1.vy += totalForce * diffY / dist;
} else {
//Collision has occurred
//If you add mass to the objects change to
//tempX = (obj1.mass*obj1.vx + obj2.mass*obj2.vx)/(obj1.mass+
//obj2.mass);
//tempY = (obj1.mass*obj1.vy + obj2.mass*obj2.vy)/(obj1.mass+
//obj2.mass);
var tempX = (obj1.vx + obj2.vx)/2;
var tempY = (obj1.vy + obj2.vy)/2;
obj1.vx = tempX; obj2.vx = tempX;
obj1.vy = tempY; obj2.vy = tempY;
}
}
});
});
allObjs.forEach(function (obj1) {
obj1.x += obj1.vx / 25;
obj1.y += obj1.vy / 25;
});
stage.update();
}
Try
var distX = obj1.x - obj2.x,
distY = obj1.y - obj2.y;
var rsq = distX *distX + distY * distY;
var r = Math.sqrt(rsq);
var F = 50 / rsq; // constant chosen to be pleasing
var rhat_x = distX / r;
var rhat_y = distY / r;
var Fx = F * rhat_x;
var Fy = F * rhat_y;
obj1.vx += -Fx;
obj1.vy += -Fy;
obj2.vx += Fx;
obj2.vy += Fy;
This is very basic, its not taking mass into account its using the simplest possible way of solving the equations you should really use something like 5th order Runga-Kutta w/ error correction. But it does use the formula for gravitational
F = - G m1 m2 / r^2
where G is the universal gravitational constant, m1 m2 are the two masses (I've all of these to 1!) r^2 is the square of the distance between the objects. The force is in the direction to the other object, let this be a unit vector rhat so the vector version of the force, using 1 for the constants
F = - ( 1 / r^2 ) rhat
The above gives reasonable results it you start out with
createPlanet(50, 200, 2, 0, 1);
createPlanet(400, 200, 2, 0, -1);
you have to take care that the two planets don't get too close or the acceleration goes off to infinity and the velocities get too big.
While playing around I tried
var distX = obj1.x - obj2.x,
distY = obj1.y - obj2.y;
var rsq = distX *distX + distY * distY; // square of the distance
var r = Math.sqrt(rsq);
var Fx = distX / r;
var Fy = distY / r;
obj1.vx += -Fx;
obj1.vy += -Fy;
obj2.vx += Fx;
obj2.vy += Fy;
which gives pleasing but physically incorrect results.
Newton's equations of motion F = ma need to be solved here. You are not doing anything like that in your code. No wonder it isn't matching your intuition.
It would help to understand the physics.
This is a vector equation. The force is gravity, which follows an inverse distance squared law.
You also know how acceleration, velocity, and displacement are related. You have to know calculus.
For your 2D world, that means six equations for each body in the problem. Two bodies means 12 coupled equations.
Solving these equations means integrating all those coupled ordinary differential equations in time. You'll need to know something about numerical methods (e.g. Runga-Kutta 5th order integration w/ error correction).
You'd have a lot to learn to write such a thing yourself. I'd recommend looking into a JavaScript physics library like Box2D or something else that Google might find.