var vTheta = Math.atan2(v.vy,v.vx);
var obsAngle = Math.atan2(-v.vy,-v.vx);
This is the original code I used to find a vector angle and its inverse. They are used for some different calculations later in the code. I wanted to remove the second Math.atan2 function and replace it to help optimize the code with the following:
var vTheta = Math.atan2(v.vy,v.vx);
var obsAngle = 0;
if (vTheta >= 0) obsAngle = Math.PI - vTheta;
else if (vTheta < 0) obsAngle = Math.PI + vTheta;
When I print the results of obsAngle for both versions, the obsAngle is the same (or close enough), however the program does not behave the same. In both cases the obsAngle is between -pi and pi.
What would the difference be in these two versions that could cause a problem?
atan2 will return a value in the range [−π,π]. If θ ≥ 0, i.e. θ ∈ [0,π] then π − θ ∈ [0,π]. Likewise, if θ < 0, i.e. θ ∈ [−π,0) then π + θ ∈ [0,π). So your second computation will never result in negative values.
The first computation results in angles which relate by vTheta - obsAngle = ±π. To mimic that, you'd have to write
if (vTheta >= 0) obsAngle = vTheta - Math.PI;
else obsAngle = vTheta + Math.PI;
Related
I'm attempting to write code that will generate fractals according to the Chaos game
In particular, I'm trying to debug the faulty generation/rendering of this fractal:
I'm doing this with Javascript in a Canvas element. The relevant Javascript is below:
canvas = document.getElementById('myCanvas');
context = canvas.getContext('2d');
//constants
border = 10 //cardinal distance between vertices and nearest edge(s)
class Point{
constructor(_x, _y){
this.x = _x;
this.y = _y;
}
}
vertices = []
secondLastVertex = 0;
lastVertex = 0;
//vertices in clockwise order (for ease of checking adjacency)
vertices.push(new Point(canvas.width / 2, border)); //top
vertices.push(new Point(canvas.width - border, canvas.height * Math.tan(36 * Math.PI / 180) / 2)); //upper right
vertices.push(new Point(canvas.width * Math.cos(36 * Math.PI / 180), canvas.height - border)); //lower right
vertices.push(new Point(canvas.width * (1 - (Math.cos(36 * Math.PI / 180))), canvas.height - border)); //lower left
vertices.push(new Point(border, canvas.height * Math.tan(36 * Math.PI / 180) / 2)); //upper left
//move half distance towards random vertex but it can't neighbor the last one IF the last two were the same
function updatePoint(){
//pick a random vertex
v = Math.floor(Math.random() * vertices.length);
if(lastVertex == secondLastVertex)
//while randomly selected vertex is adjacent to the last approached vertex
while((v == (lastVertex - 1) % 5) || (v == (lastVertex + 1) % 5))
//pick another random vertex
v = Math.floor(Math.random() * vertices.length);
//cycle the last two vertices
secondLastVertex = lastVertex;
lastVertex = v;
//move half way towards the chosen vertex
point.x = (vertices[v].x + point.x) / 2;
point.y = (vertices[v].y + point.y) / 2;
}
//starting point (doesn't matter where)
point = new Point(canvas.width / 2, canvas.height / 2);
for (var i = 0; i < 1000000; i++){
//get point's next location
updatePoint();
//draw the point
context.fillRect(Math.round(point.x), Math.round(point.y), 1, 1);
}
The rendering that is produced looks like this:
So far I haven't been able to determine what is causing the rendering to be so skewed and wrong. One possibility is that I've misunderstood the rules that generate this fractal (i.e. "move half the distance from the current position towards a random vertex that is not adjacent to the last vertex IF the last two vertices were the same")
Another is that I have some bug in how I'm drawing fractals. But the same code with rule/starting-vertex modifications is able to draw things like the Sierpinkski triangle/carpet and even other pentagonal fractals apparently perfectly. Though one other pentagonal fractal ended up with some weird skewing and "lower right fourth of each self-similar substructure" weirdness.
I tried making some slight modifications to how I interpreted the rules (e.g. "next vertex can't be adjacent OR EQUAL TO previous vertex if last two vertices were the same") but nothing like that helped. I also tried not rounding the coordinates of the target point before plotting it, but though this slightly changed the character/sharpness of the details, it didn't change any larger scale features of the plot.
My issue as kindly pointed out by ggorlen, was that I wasn't comparing vertices for adjacency correctly. I mistakenly thought Javascript evaluated something like (-1 % 5) as 4, rather than -1.
To fix this, I add 4 to the index instead of subtracting 1, before modding it against 5 (the number of vertices)
This completely fixed the render. (in not just this case but other cases I'd been testing with different fractals)
Referring to the first diagram, I am trying to copy the three objects, looking at them from an arbitrary angle(A1). The distance between where I'm and the first object does not matter just the relative location of the object to one another.
In the second diagram, I select a point to copy these objects, facing another arbitrary angle(B1).
Angle (C1) shows the approximate position of -90 degrees.
I can get this to work if A1 = 0,90,180,270 and even 45,135 etc but the equations I come up with only work for 0 and 180 or 90 and 270. I have to modify them to work in those directions by changing a hardcoded offset angle and putting/removing a negative sign before the offset.
I'm doing this is javascript (and its Minecraft) usually I can figure out this but I have been working on it for weeks.
Here is some pseudo-code that works some of the time in certain right-angle directions. I have updated this to be more accurate, the 1x and 2x are the blocks x coordinate, etc. - everything is relative from the (1) block.
Minecraft's coordinate system is a little different from normal - 0 is south, +90 is west, 180 is north, 270 is east.
the only difference is that I am making negative az, ax.
// works for north/south looking - A1 is either 180/0 , B1 can be anything
var x = 1x - 2x;
var z = 1z - 2z;
var direction = Math.atan2(z1, x1);
var L1 = Math.sqrt(Math.pow(x1, 2) + Math.pow(z1, 2));
var az = Math.round(L1 * Math.sin((B1 + A1 + (direction * 180 / Math.PI)) * Math.PI / 180));
var ax = Math.round(L1 * Math.cos((B1 + A1 + (direction * 180 / Math.PI)) * Math.PI / 180));
// works for east/west looking - A1 is either 90/270 , B1 can be anything
var x = 1x - 2x;
var z = 1z - 2z;
var direction = Math.atan2(z1, x1);
var L1 = Math.sqrt(Math.pow(x1, 2) + Math.pow(z1, 2));
var az = -Math.round(L1 * Math.sin((B1 + A1 + (direction * 180 / Math.PI)) * Math.PI / 180));
var ax = -Math.round(L1 * Math.cos((B1 + A1 + (direction * 180 / Math.PI)) * Math.PI / 180));
First diagram
Second diagram
I have a function that returns an angle from -180 to 180 and I need it to be 0 to 360. What is the formula to convert the angle?
Here's what I want:
0/360
270 90
180
Here's what I have:
-90
-180/180 0
90
Any help is greatly appreciated, working on an html/javascript game.
There are a number of ways this can be done while preserving the equivalent angle, with certain assumptions about the values.
If you are sure the values are actually in the range [-180,180), then you can use something like the following:
var x = Math.random()*360-180; // Generate random angle in range [-180, 180)
var y = x < 0 ? x+360 : x; // Normalize to be in the range [0, 360)
If you're doing this a lot, the branch operation may result in poor behavior (though maybe not in Javascript ... you'd need to profile it). So, it's sometimes better to do this without a branching operation. You can do this using the modulus (%) operator.
var x = Math.random()*360-180; // Generate random angle in range [-180, 180)
var y = (x + 360) % 360; // Normalize to be in the range [0, 360)
If you cannot guarantee the original value is in the range [-180, 180) you can essentially divide out the number of turns. This would look like
var x = getAngle(); // Some angle, potentially outside [-180, 180)
var y = x - 360 * Math.floor(x/360); // Normalizes to be in range [0, 360)
Some care will need to be taken for large absolute values of x due to the way floating point numbers are represented (which as I understand it is what Javascript uses, even when integer values would work well)
Edit: I just noticed you are also changing where you consider the 0 angle to be (above rather than to the right). In that case, you need to also add 90 degrees to rotate the 0 into the correct position. In this case, the first code segment becomes:
var x = Math.random()*360-180; // Generate random angle in range [-180, 180)
var y = x < -90 ? x+450 : x+90; // Normalize to be in the range [0, 360)
The next one becomes as Amadan indicated.
var x = Math.random()*360-180; // Generate random angle in range [-180, 180)
var y = (x + 450) % 360; // Normalize to be in the range [0, 360)
and the last one becomes
var x = getAngle(); // Some angle, potentially outside [-180, 180)
var y = (x+90) - 360 * Math.floor((x+90)/360); // Normalizes to be in range [0, 360)
Here is code as suggested by #Amadan
function convert(deg) {
return ((deg + 450) % 360);
}
window.console.log(convert(-180));
window.console.log(convert(-90));
window.console.log(convert(0));
window.console.log(convert(90));
window.console.log(convert(180));
I have a canvas with this params:
width = 400, height = 400
and have a line passing through the point cursor[x1,y1] at an angle Q (in degree)
I need get all coords of the intersection of the line in the plane and write it to array. Now i use this equation: y - y1 = k * (x - x1)
to check all point I use this code:
var rad = Q * Math.PI/180;
for (ctrY = 0; ctrY < 400; ctrY += 1) {
for (ctrX = 0; ctrX < 400; ctrX += 1) {
if ( (ctrY - cursor.y) ===
~~(Math.tan(rad) * (ctrX - cursor.x)) ) {
z.push([ctrX, ctrY]);
}
}
}
For example when 0 < Q < 90 and cursor[x1,y1] = [200,200] z.length = 0 and it's not correct.
Where i'm wrong? Maybe there is a more convenient algorithm?
P.S. Sorry for my english
Seems you need line rastering algorithm. Consider Bresenham algorithm.
You can also look at DDA algorithm
I imagine an algorithm like this. (I only consider the case when 0 < Q < 90). First I will want to calculate the points where the line will intersect the Ox and Oy axes, considering the origin (0,0) point the upper left corner and if we imagine that the negative x and y values are respectively to the left and to the top of this point. Let x2 and y2 be the values where the line will intersect Ox and Oy. We want to calculate these values. We now have a system with 2 unknown variables (x2 and y2): Math.tan(rad) = (y1 -y2)/x1 and Math.tan(rad) = y1/(x1-x2). We can deduct these equations by drawing the line on the coordinate system and analyzing a bit. If we solve the system of equations we find something like: x2 = (x1*y1 -x1 * x1 * Math.tan(rad)/(2 * y1-x1)) and y2= y1- x1 * Math.tan(rad) (These need to be verified, I haven't double checked my calculus). A linear equation can be defined by the formula y = a*x + b and in our case a = x2 and b = y2. We can then calculate the points like this:
for (xIdx = 0; xIdx < 400; xIdx += 1) {
var ctrX = xIdx;
var ctrY = x2 * ctrX + y2 //todo: replace with the respective calculated variables x2 and y2(we could also define two functions in js) and proper rounding
z.push([ctrX, ctrY]);
}
I'm not sure if I'm 100% accurate but I hope you understand my idea.
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.