Calculate absolute distance of two rotation degrees, as a number (in JavaScript) - javascript

I would like to calculate the distance between two degree values. I know that I could use this if the points just where on one axis:
var dist = Math.abs( x1 - x2 );
...but the problem with degrees is, that they can either be negative or positive, also they could be somewhere above 360. My goal is to calculate the distance, no matter if the rotation is negative or higher then 360°
EDIT
To make it clearer what I want: For example, I could could have the values -90deg and 270deg. This should result in a result of 0 for the distance. If the first element's rotation would change to either -100deg of -80deg, the distance should change to 10.

If you want this to work for any angle (including angles like 750 deg) and in a language where you have a remainder operator that's signed (like Java or JavaScript), then you need to do more work. If you're in a language with an unsigned operator, then the answer by Rory Daulton is good. (That is, in summary, use rot1%360 below where I call modulo(rot1,360) and similarly for rot2.)
First you need to make each negative angle into an equivalent positive angle. There's no single operator that will do this since, for example, -10 % 360 = -10 in these languages. You can get this with a function like this (assuming y is positive):
function modulo(x,y) {
var xPrime = x;
while(xPrime<0) {
xPrime += y; // ASSUMES y > 0
}
return xPrime % y;
}
Then you can do more or less as suggested by others, but using this custom function instead of the % operator.
var distance = Math.abs(modulo(rot1,360) - modulo(rot2,360))
distance = Math.min(distance, 360-distance)

Python code that seems to work in all cases is
dist = abs(x1 % 360 - x2 % 360)
dist = min(dist, 360 - dist)
That last line is needed to handle a case like x1=10; x2=350. The other answers would give 340 but the proper answer is 20.

Old topic but, the accepted answer is not really acceptable for a problem I had myself because of the while loop, and sadly that is the best I could find on a google search. However I found a better and faster solution if you might as well have to deal with very high negative rotation values.
This line of code takes a rotation value (r) and maps it to a range of 0-360.
r = (r<0) ? (r+(Math.ceil(-r/360)*360)) : (r%360);
Further explanation:
The accepted answer adds 360 every loop to the negative rotation value until it is positive, whereas my solution calculates how often it needs to add 360 and does that in one go.

So, I worked it out, following this answer:
function modulo(value, mod) {
return value - Math.floor( value/mod ) * value;
}
var dist = rotation1 - rotation2;
dist = Math.abs( modulo( (dist + 180), 360 ) - 180 );
EDIT
Actually, this answer works as well. Ported to JavaScript it would be:
var dist = Math.abs(rotation1 % 360 - rotation2 % 360);
dist = Math.min(dist, 360 - dist);
I like it, because it doesn't need the special modulo function.

Related

How to align D3 circles on semicircle

I have a semi circle that I need to align Radios I created from circles. The number of radios will be dynamic and will need to keep them centered. Here is what I currently have.
UPDATE, just realized that the 2 screenshots can be confusing. The order of the radios are not the same. Ignore that. I just need the alignment to be correct,
This is the desired result.
I have a working demo here
WORKING DEMO
Given your preferred arc angle A and number of points N, find the angular distance between each point:
PA = A / (N - 1).
Next, find the angle described by a vertical line from the circle's center and the point we're calculating.
Initial observations:
The largest possible value of this angle is A / 2
The smallest possible value is PA / 2 if N is even, 0 if N is odd.
The angle changes in increments of PA
Counting points away from the center (X), we can describe the angle using terms we've defined:
A / 2 - PA * (floor(N / 2) - X)
For the examples directly below, X is 1 and 2 respectively.
Assuming:
A = 90° (not depicted accurately)
N = 4
From which follows:
PA = 30°
Plugging in these values, we get:
45 - 30 * (floor(2) - 1) = 15
and
45 - 30 * (floor(2) - 2) = 45
Now that we have the angle a, we can use trigonometry (specifically SOH-CAH-TOA) to find the x and y offsets of the point relative to the center.
Note that because the points to the left of the plumb line are mirror images of their counterparts to the right, you need only to negate the x-offset from the center to get the locations of the left-hand points.
Update: Here's a simpler (but still math-based) implementation.
While backtick's answer is a beautiful math explanation, you can do that with a simple mix of getPointAtLength and getTotalLength:
First you do...
const arcPathLength = arcPath.node().getTotalLength() / 2;
... where arcPath is just that gray arc the radio buttons will follow. Here I'm dividing the length by 2 because the arc goes back to the origin. Then you just do:
svg.selectAll(null)
.data(RADIO_DATA)
//etc...
.attr("cx", (d, i) => arcPath.node().getPointAtLength((arcPathLength/(RADIO_DATA.length - 1)) * i).x)
.attr("cy", (d, i) => arcPath.node().getPointAtLength((arcPathLength/(RADIO_DATA.length - 1)) * i).y)
Since I cannot fork the code without logging in, here is a screenshot of the result:
Adjust the texts accordingly. Also, pay attention to the fact that circle elements have no d or text attributes.

How to divide an ellipse to equal segments?

This calculates vertex coordinates on ellipse:
function calculateEllipse(a, b, angle)
{
var alpha = angle * (Math.PI / 180) ;
var sinalpha = Math.sin(alpha);
var cosalpha = Math.cos(alpha);
var X = a * cosalpha - b * sinalpha;
var Y = a * cosalpha + b * sinalpha;
}
But how can I calculate the "angle" to get equal or roughly equal circumference segments?
So from what Jozi's said in the OP's comments, what's needed isn't how to subdivide an ellipse into equal segments (which would require a whole bunch of horrible integrals), it's to construct an ellipse from line segments of roughly equal length.
There are a whole pile of ways to do that, but I think the best suited for the OP's purposes would be the concentric circle method, listed on the page as 'the draftman's method'. If you don't mind installing the Mathematica player, there's a neat lil' app here which illustrates it interactively.
The problem with those methods is that the segment lengths are only roughly equal at low eccentricities. If you're dealing in extreme eccentricities, things get a lot more complicated. The simplest solution I can think of is to linearly approximate the length of a line segment within each quadrant, and then solve for the positions of the endpoints in that quadrant exactly.
In detail: this is an ellipse quadrant with parameters a = 5, b = 1:
And this is a plot of the length of the arc subtended by an infinitesimal change in the angle, at each angle:
The x axis is the angle, in radians, and the y axis is the length of the arc that would be subtended by a change in angle of 1 radian. The formula, which can be derived using the equations in the Wikipedia article I just linked, is y = Sqrt(a^2 Sin^2(x) + b^2 Cos^2(x)). The important thing to note though is that the integral of this function - the area under this curve - is the length of the arc in the whole quadrant.
Now, we can approximate it by a straight line:
which has gradient m = (a-b) / (Pi/2) and y intercept c = b. Using simple geometry, we can deduce that the area under the red curve is A = (a+b)*Pi/4.
Using this knowledge, and the knowledge that the area under the curve is the total length of the curve, the problem of constructing an approximation to the ellipse reduces to finding say a midpoint-rule quadrature (other quadratures would work too, but this is the simplest) of the red line such that each rectangle has equal area.
Converting that sentence to an equation, and representing the position of a rectangle in a quadrature by it's left hand boundary x and its width w, we get that:
(v*m)*w^2 + (m*x+c)*w - A/k == 0
where k is the number of pieces we want to use to approximate the quadrant, and v is a weighting function I'll come to shortly. This can be used to construct the quadrature by first setting x0 = 0 and solving for w0, which is then used to set x1 = w0 and solve for w1. Then set x2 = w1, etc etc until you've got all k left-hand boundary points. The k+1th boundary point is obviously Pi/2.
The weighting function v effectively represents where the rectangle crosses the red line. A constant v = 0.5 is equivalent to it crossing in the middle, and gets you this with 10 points:
but you can play around with it to see what better balances the points. Ideally it should stay in the range [0, 1] and the sum of the values you use should be k/2.
If you want an even better approximation without messing around with weighting functions, you could try least-squares fitting a line rather than just fitting it to the endpoints, or you could try fitting a cubic polynomial to the blue curve instead of a linear polynomial. It'll entail solving quartics but if you've a maths package on hand that shouldn't be a problem.
Too long for a comment, so I suppose this has to be an answer ...
Here's a mathematically simple approach to forming a first order approximation. Pick one quadrant. You can generate the data for the other quadrants by reflection in the X and Y axis. Calculate (x,y) for the angle = 0 degrees, 1 degree, ... 90 degrees. Now you want the little lengths joining consecutive points. If (x_n, y_n) are the coordinates at angle = n, then Pythagoras tells us the distance D between points (x_n, y_n) and (x_n+1, y_n+1) is D = sqrt((x_n+1 - x_n)^2 + (y_n+1 - y_n)^2). Use this formula to produce a table of cumulative distances around the ellipse for angles from 0 degrees to 90 degrees. This is the inverse of the function you seek. Of course, you don't have to pick a stepsize of 1 degree; you could use any angle which exactly divides 90 degrees.
If you want to find the angle which corresponds to a perimeter step size of x, find the largest angle n in your table producing a partial perimeter less than or equal to x. The partial perimeter of angle n+1 will be larger than x. Use linear interpolation to find the fractional angle which corresponds to x.
All we are doing is approximating the ellipse with straight line segments and using them instead of the original curve; its a first order approximation. You could do somewhat better by using Simpson's rule or similar instead of linear interpolation.
Yes, you have to calculate the table in advance. But once you have the table, the calculations are easy. If you don't need too much accuracy, this is pretty simple both mathematically and coding-wise.

JS rotation function outputting weird X value

Hey i've made a rotation function in Javascript for rotating some 2D vector(point).
The Y output of the function works as expected, however the X value is outputting some crazy number, can anyone point out the floor in my logic?
Vector2.prototype.rotate = function(degrees){
var angle = degrees * TO_RADIANS; //Convert to radians.
var x = (this.getX() * Math.cos(angle)) - (this.getY() * Math.sin(angle));
var y = (this.getX() * Math.sin(angle)) + (this.getY() * Math.cos(angle));
return new Vector2(x,y);
};
Inputting Vector(1,0) into this function with a rotation of 90 degrees outputs 6.someDecimalPlaces e-17; which is obviously incorrect.
The outputted Y value however works as expected and returns 1.
Thanks in advance
The "6.someDecimalPlaces e-17" you're seeing is due to Javascript's handling of floating point numbers. What you're seeing is a rounding error in converting back from binary floating point to decimal. There's no easy fix for this although there are libraries that attempt to overcome the problem.
If you want rounded numbers for pixel perfect CSS manupilation you're best bet is to round the numbers coming out of this function or cast them to integer.

Custom linear congruential generator in JavaScript

I am trying to create a custom linear congruential generator (LCQ) in JavaScript (the one used in glibc).
Its properties as it's stated on Wikipedia are: m=2^31 , a=1103515245 , c=12345.
Now I am getting next seed value with
x = (1103515245 * x + 12345) % 0x80000000 ; // (The same as &0x7fffffff)
Although the generator seems to work, but when the numbers are tested on canvas:
cx = (x & 0x3fffffff) % canvasWidth; // Coordinate x (the same for cy)
They seem to be horribly biased: http://jsfiddle.net/7VmR9/3/show/
Why does this happen? By choosing a different modulo, the result of a visual test looks much better.
The testing JSFiddle is here: http://jsfiddle.net/7VmR9/3/
Update
At last I fixed the transformation to canvas coordinates as in this formula:
var cx = ((x & 0x3fffffff)/0x3fffffff*canvasWidth)|0
Now the pixel coordinates are not so much malformed as when used the modulo operation.
Updated fiddle: http://jsfiddle.net/7VmR9/14/
For the generator the formula is (you forgot a modulus in the first part):
current = (multiplier * current * modul + addend) % modulus) / modulus
I realize that you try to optimize it so I updated the fiddle with this so you can use it as a basis for the optimizations:
http://jsfiddle.net/AbdiasSoftware/7VmR9/12/
Yes, it looks like you solved it. I've done the same thing.
A linear congruential generator is in the form:
seed = (seed * factor + offset) % range;
But, most importantly, when obtaining an actual random number from it, the following does not work:
random = seed % random_maximum;
This won't work because the second modulus seems to counteract the effect of the generator. Instead, you need to use:
random = floor (seed / range * random_maximum);
(This would be a random integer; remove the floor call to obtain a random float.)
Lastly, I will warn you: In JavaScript, when working with numbers that exceed the dword limit, there is a loss of precision. Thus, the random results of your LCG may be random, but they most likely won't match the results of the same LCG implemented in C++ or another low-level language that actually supports dword math.
Also due to imprecision, the cycle of the LCG is highly liable to be greatly reduced. So, for instance, the cycle of the glibc LCG you reference is probably 4 billion (that is, it will generate over 4 billion random numbers before starting over and re-generating the exact same set of numbers). This JavaScript implementation may only get 1 billion, or perhaps far less, due to the fact that when multiplying by the factor, the number surpasses 4 billion, and loses precision in doing so.

Using points to generate quadratic equation to interpolate data

I'm trying to come up with a flexible decaying score system for a game using quadratic curves. I could probably brute force my way through it but was wondering if anyone can help me come up with something flexible or maybe there are some ready made solutions out there already!
But basically I need the ability to generate the values of a,b & c in:
y = ax^2 + bx + c
from 3 points (which i know fall on a valid quadratic curve, but are dynamic based on configurable settings and maximum times to react to an event) for example: (-1100, 0), (200, 1), (1500, 0).
So I can then plugin in values for x to generate values of Y which will determine the score I give the user.
If I could get away with a fixed quadratic equation I would but the scoring is based on how much time a user has to react to a particular event (X Axis) the y axis points will always be between 0 and 1 with 0 being minimum score and 1 being maximum score!
Let me know if you need more info!
You can use Lagrange polynomial interpolation, the curve is given by
y(x) = y_1 * (x-x_2)*(x-x_3)/((x_1-x_2)*(x_1-x_3))
+ y_2 * (x-x_1)*(x-x_3)/((x_2-x_1)*(x_2-x_3))
+ y_3 * (x-x_1)*(x-x_2)/((x_3-x_1)*(x_3-x_2))
If you collect the coefficients, you obtain
a = y_1/((x_1-x_2)*(x_1-x_3)) + y_2/((x_2-x_1)*(x_2-x_3)) + y_3/((x_3-x_1)*(x_3-x_2))
b = -y_1*(x_2+x_3)/((x_1-x_2)*(x_1-x_3))
-y_2*(x_1+x_3)/((x_2-x_1)*(x_2-x_3))
-y_3*(x_1+x_2)/((x_3-x_1)*(x_3-x_2))
c = y_1*x_2*x_3/((x_1-x_2)*(x_1-x_3))
+ y_2*x_1*x_3/((x_2-x_1)*(x_2-x_3))
+ y_3*x_1*x_2/((x_3-x_1)*(x_3-x_2))
you can formulate it in a matrix form: aX=b
1 x1 x1^2
a= 1 x2 x2^2
1 x3 x3^2
y1
b= y2
y3
then solve by inverting the matrix a (can be done via gauss method pretty straight forward)
http://en.wikipedia.org/wiki/Gaussian_elimination
X = a^-1*b
and X in this case are the [c b a] coefficients your are looking for.

Categories

Resources