Drawing a line segment on a space that wraps toroidally - 2d

I have a 2D space of angles [0, 2pi] x [0, 2pi] which wraps around, with toroid like topology (the horizontal edges correspond to each other, as do the vertical ones). I have two points in this space, and I would like to draw a line segment between those two points.
In some cases, this line segment is the obvious line segment, from one point to the other. In other cases, the line segment is supposed to "go around the edge" instead of going "the long way, through the middle":
+--------+
| |
| A--B |
| |
+--------+
+--------+
| |
|-A B-|
| |
+--------+
While these cases are moderately easy to handle, there is one case that is really vexing for me and which my code so far does not handle correctly:
+-----------+
| / |
| B |
| |
| A /|
| / / |
+-----------+
I.e. if the line wraps around both directions, it sometimes wraps around the opposite corner. I'm not entirely sure if there are more of these tricky cases.
The only algorithm I've come up that works reliably so far is to calculate the midpoint as (A + B) / 2 while making appropriate use of the modulo arithmetics, draw a dot at this position, and then recursively subdivide the left and right intervals similarly, until the distance between the dots is less than a single pixel. Obviously, this is not going to be very fast.
My other approach was to detect (separately for x and y) whether the short distance is direct or around the edge, and then either draw one line segment or two. This does not handle the third case correctly, unless the line is divided in two and the midpoint lies on the segment that is in the lower-right corner in the example image. I'm not sure how to detect this efficiently, or how to calculate the position of the midpoint, as simply the point in the half does not always work, it might end up at the edge together with one of the endpoints, if their respective distance from the edge is not equal.
Is there a better algorithm? Is there an obvious solution that I'm not seeing? I'm not even sure how to google for this problem. I don't want to implement my own line rasterization algorithm, I would just like to break this problem to Euclidean straight lines and draw these using OpenGL or GDI or whatever.
My code so far is:
void Draw_WrappedSegment(float f_x0, float f_y0, float f_x1, float f_y1)
{
const float s = 2 * f_pi;
f_x0 = fmod(fmod(f_x0, s) + s, s);
f_y0 = fmod(fmod(f_y0, s) + s, s);
f_x1 = fmod(fmod(f_x1, s) + s, s);
f_y1 = fmod(fmod(f_y1, s) + s, s);
// make sure the coordinates end up being positive and modulo 2pi
float f_ydist0 = fabs(f_y0 - f_y1);
float f_ydist1 = fabs(fmod(f_y0 + s - f_y1, s));
float f_ydist2 = fabs(fmod(f_y1 - f_y0 + s, s));
float f_xdist0 = fabs(f_x0 - f_x1);
float f_xdist1 = fabs(fmod(f_x0 + s - f_x1, s));
float f_xdist2 = fabs(fmod(f_x1 - f_x0 + s, s));
// 0 2pi 4pi
//p1'' | p0 p1 | p0' p1' |
// <---f_dist0--->
// <-f_dist1->
// <-f_dist2->
const float f_epsilon = 1e-3f; // sometimes the modulo causes an error and even though the díst 0 and dist 2 should equal, dist 2 is slightly smaller
if(f_xdist0 <= f_xdist1 + f_epsilon && f_xdist0 <= f_xdist2 + f_epsilon) {
if(f_ydist0 <= f_ydist1 + f_epsilon && f_ydist0 <= f_ydist2 + f_epsilon) {
MoveTo(f_x0, f_y0);
LineTo(f_x1, f_y1); // the "short" way in both directions
} else {
float f_sign = (f_y0 < f_y1)? 1 : -1; // swap the lower and upper edge if the points are not sorted by y
MoveTo(f_x0, f_y0);
LineTo(f_x1, f_y1 - f_sign * s); // from point 0 to the lower edge
MoveTo(f_x1, f_y1);
LineTo(f_x0, f_y0 + f_sign * s); // from point 1 to the upper edge
}
} else {
if(f_ydist0 <= f_ydist1 + f_epsilon && f_ydist0 <= f_ydist2 + f_epsilon) {
float f_sign = (f_x0 < f_x1)? 1 : -1; // swap the left and right edge if the points are not sorted by x
MoveTo(f_x0, f_y0);
LineTo(f_x1 - f_sign * s, f_y1); // from point 0 to the left edge
MoveTo(f_x1, f_y1);
LineTo(f_x0 + f_sign * s, f_y0); // from point 1 to the right edge
} else {
float f_sign_x = (f_x0 < f_x1)? 1 : -1; // swap the left and right edge if the points are not sorted by x
float f_sign_y = (f_y0 < f_y1)? 1 : -1; // swap the lower and upper edge if the points are not sorted by y
MoveTo(f_x0, f_y0);
LineTo(f_x1 - f_sign_x * s, f_y1 - f_sign_y * s); // from point 0 to one edge
MoveTo(f_x1, f_y1);
LineTo(f_x0 + f_sign_x * s, f_y0 + f_sign_y * s); // from point 1 to the other edge
}
}
}

Instead of working with just the square [0, 2pi] x [0, 2pi], try tiling the space [-2pi,4pi] x [-2pi,4pi] with nine copies of this square (like a tic-tac-toe board). Place A in the center square, and then place copies of B (translating the coordinates by ±2pi as required ) in each of the nine squares. Choose the copy of B that is closest to A, and then draw the line from A to that copy of B. This line may have more than one segment as it travels through the squares. Just "untranslate" these segments back to the central square and you will have the diagram you want.

Related

d3js Cluster Force Layout IV block by Mike

I am new to d3js and I'm just starting out.
I am trying the cluster layout example written by Mike in one of his blocks.
https://bl.ocks.org/mbostock/7882658
I got it to work on my machine with my code but I really don't like just blindly copying code without understanding it.
However I am having a tough time understanding the math behind the 'cluster()' and 'collide()' functions and as to how they function.
Could anyone please explain it? Thanks for your help !!
Let's look at each method and I'll comment it as best I can.
Cluster
First the caller:
function tick(e) {
node
.each(cluster(10 * e.alpha * e.alpha)) //for each node on each tick call function returned by cluster function
//pass in alpha cooling parameter to collide
...
I won't rehash an explanation here about how the tick event works. The documentation is clear.
The function:
// returns a closure wrapping the cooling
// alpha (so it can be used for every node on the tick)
function cluster(alpha) {
return function(d) { // d here is the datum on the node
var cluster = clusters[d.cluster]; // clusters is a hash-map, the key is an index of the 10 clusters, the value is an object where d.cluster is the center node in that cluster
if (cluster === d) return; // if we are on the center node, do nothing
var x = d.x - cluster.x, // distance on x of node to center node
y = d.y - cluster.y, // distance on y of node to center node
l = Math.sqrt(x * x + y * y), // distance of node to center node (Pythagorean theorem)
r = d.radius + cluster.radius; // radius of node, plus radius of center node (the center node is always the largest one in the cluster)
if (l != r) { // if the node is not adjacent to the center node
l = (l - r) / l * alpha; //find a length that is slightly closer, this provides the illusion of it moving towards the center on each tick
d.x -= x *= l; // move node closer to center node
d.y -= y *= l;
cluster.x += x; // move center node closer to node
cluster.y += y;
}
};
}
Collide
The collide function is a bit more complicated. Before we dive into it, you need to understand what a QuadTree is and why Bostock is using it. If you want to determine if two elements are colliding the naive algorithm would be to loop the elements both outer and inner to compare each one against every other one. This is, of course, computationally expensive especially on every tick. This is the problem QuadTrees are trying to solve:
A quadtree recursively partitions two-dimensional space into squares, dividing each square into four equally-sized squares. Each distinct point exists in a unique leaf node; coincident points are represented by a linked list. Quadtrees can accelerate various spatial operations, such as the Barnes–Hut approximation for computing many-body forces, collision detection, and searching for nearby points.
What does that mean? First, take a look at this excellent explanation. In my own simplified words it means this: take a 2-d space and divide it into four quadrants. If any quadrant contains 4 or less nodes stop. If the quadrant contains more than four nodes, divide it again into four quadrants. Repeat this until each quadrant/sub-quadrant contains 4 or less nodes. Now when we look for collisions, our inner loop no longer loops nodes, but instead quadrants. If the quadrant doesn't collide then move to the next one. This is a big optimization.
Now onto the code:
// returns a closure wrapping the cooling
// alpha (so it can be used for every node on the tick)
// and the quadtree
function collide(alpha) {
// create quadtree from our nodes
var quadtree = d3.geom.quadtree(nodes);
return function(d) { // d is the datum on the node
var r = d.radius + maxRadius + Math.max(padding, clusterPadding), // r is the radius of the node circle plus padding
nx1 = d.x - r, // nx1, nx2, ny1, ny2 are the bounds of collision detection on the node
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function(quad, x1, y1, x2, y2) { // visit each quadrant
if (quad.point && (quad.point !== d)) { // if the quadrant is a point (a node and not a sub-quadrant) and that point is not our current node
var x = d.x - quad.point.x, // distance on x of node to quad node
y = d.y - quad.point.y, // distance on y of node to quad node
l = Math.sqrt(x * x + y * y), // distance of node to quad node (Pythagorean theorem)
r = d.radius + quad.point.radius + (d.cluster === quad.point.cluster ? padding : clusterPadding); // radius of node in quadrant
if (l < r) { // if there is a collision
l = (l - r) / l * alpha; // re-position nodes
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
// This is important, it determines if the quadrant intersects
// with the node. If it does not, it returns false
// and we no longer visit and sub-quadrants or nodes
// in our quadrant, if true it descends into it
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}

Determine the index of a grid coordinate centered at 0,0 and increasing in a spiral around the origin

This is a reverse question of the following:
Determine position of number in a grid of numbers centered around 0 and increasing in spiral
The two modalities are:
index -> coordinate (done, see question above)
coordinate -> index (my question)
The part I'm stuck on is getting the sector from a raw coordinate without a massive ugly branch of logic.
Is there a simple arithmetic way to determine the sector? How would you reverse this function to take a coordinate pair and return the index?
This is a Ulam spiral it has some interesting properties if you draw the positions of prime numbers on the spiral.
25|26|27|28|29|30
24| 9|10|11|12|31
23| 8| 1| 2|13|32
22| 7| 0| 3|14|33
21| 6| 5| 4|15|34
20|19|18|17|16|35
........ |36
The first thing to note is the position of the square numbers. These lie on the diagonals, with odd squares in the upper left diagonal and even squares in the lower right diagonal.
25| | | | |
| 9| | | |
| | 1| | |
| | 0| | |
| | | 4| |
| | | |16|
........ |36
Lets look at the diagonals these have coordinates (x,y)
(-2,3)| | | | | (3,3)
| (-1,2)| | | (2,2)|
| | (0,1)| (1,1)| |
| | (0,0)| | |
|(-1,-1)| |(1,-1)| |
(-2,-2)| | | |(2,-2)|
| | | | |(3,-3)
First note the diagonals, if y>=0 and x=1-y we are on the top left diagonal and the value is (2 y-1)^2 or (1-2x)^2.
If y<0 and x=-y we are on the bottom right diagonal and the value is (2x)^2=4x^2 or (-2y)^2 = 4y^2.
We are on a horizontal rows at the top if y>=0 and x >= 1-y, xpos=x+y-1. The value will be (2 y-1)^2 + pos.
Horizontal rows at the bottom have y<0 and x >=y , x<=-y. The number of steps to the left of the square number is pos=-x-y. The value is 4y^2+pos.
Similar calculations work for the vertical rows. This can all be encoded in the function
function spiral(x,y) {
var pos,squ;
// Horizontal row at top
if(y>=0 && ( x >= 1-y && x<y ) ) {
pos = x+y-1;
squ = (2 *y-1)*(2*y-1);
// Horizontal row at bottom
} else if( y < 0 && ( x >= y && x<= -y ) ) {
pos = -x-y;
squ = 4*y*y;
// Vertical row on right
} else if( x >0 ) {
pos = -x-y;
squ = 4*x*x;
// Vertical row on left
} else {
squ = (1-2*x)*(1-2*x);
pos = x+y-1;
}
return squ+pos;
}
I've a javascript implementation at fiddle. This does the spiral for numbers upto 99.
A slightly simpler function is
spiral = function(x,y) {
var res;
var u = x+y;
var v = x-y;
if(u>0) {
if(v>=0) {
x <<= 1;
res = x*(x-1) + v;
} else {
y <<= 1;
res = y*(y-1) + v;
}
} else {
if(v<0) {
x <<= 1;
res = -x*(1-x) - v;
} else {
y <<= 1;
res = -y*(1-y) - v;
}
}
return res;
}
Here u,v tell you how far you move in diagonal directions. The signs of two numbers tell you which sector you are in. We can get the base number for each horizontal/vertical edge by finding the values on the line x=y. Going to the north east these are 2, 12, 30 (i.e. 1*2, 3*4, 5*6) and to the southwest they are 6, 20, 42, (i.e. 2*3, 4*5, 6*7). The equations for these are 2*x*(2*x-1) and -2*x*(1-2*x) or the same using y. To get the index for each point we simple add or subtract v to these.
This uses 1 multiplication, 2 comparisons, 1 bit shift and 4 additions. It might be possible to remove one of the additions, but I pretty sure you need a multiplication and 2 comparisons.
This is going to be a very late answer, but I'm going to answer it anyways since I've had this exact issue myself and never really found a solid fix for the issues I've had with other answers. Here's a complimentary answer to all the other ones out there, because why not:
Here's some psuedocode for the solution I found:
// n is the number in the spiral at (x, y)
// m is an intermediate step towards calculating n
m = 2 * max(abs(x), abs(y)) - (x > -y) // this relies on booleans being evaluated as 0 or 1
n = m^2 + abs(x + (-1)^m * floor((m + 1) / 2)) + abs(y - (-1)^m * floor(m / 2))
In Python 3: (Latest tested in 3.7.0)
import math
def number_in_spiral(x, y):
m = 2 * max(abs(x), abs(y)) - (x > -y)
return m * m + abs(x + pow(-1, m) * math.floor((m + 1) / 2)) + \
abs(y - pow(-1, m) * math.floor(m / 2))
In JavaScript:
function number_in_spiral(x, y)
{
var m = 2 * Math.max(Math.abs(x), Math.abs(y)) - (x > -y);
return m * m + Math.abs(x + Math.pow(-1, m) * Math.floor((m + 1) / 2)) +
Math.abs(y - Math.pow(-1, m) * Math.floor(m / 2));
}
Just remember that y in this formula increases upwards, which isn't ideal in a lot of cases. In that case, either multiply the input y with -1, or substitute y with -y in every instance in the formula.
I hope this can be useful to someone. If not, it'll at least be useful to me when I inevitably return to this problem 4 years from now.
Consider the number of steps you take without changing direction. You will find that sequence to be 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, … The pattern should be obvious. Now after four such straight edges you are approximately back in the same corner. So you can find indices for the lines connecting these corners:
c0: 0, 1+1+2+2, …+3+3+4+4, …+5+5+6+6, …+7+7+8+8, …
c1: 1, …+1+2+2+3, …+3+4+4+5, …
c2: 1+1, …
c3: 1+1+2, …
For each of these, you can write a formula. The first would be something like this:
c0[i] = sum((2*i - 1) + (2*i - 1) + (2*i) + (2*i)) = sum(8*i - 2)
Now ask Wolfram Alpha. It will tell you that
c0[i] = 4i² + 2i
So if someone gives you an index n, you can solve for the last time you visited that edge line by solving the above equation for i, taking the positive solution, then rounding that down to the an integer. You can either do the same for the other edge lines, or accept to do one whole turn around the center in the worst case. Where a whole turn means four staright segments, you don't want to go field by field.
Wolfram Alpha also prints the first few elements of the sequence: 6,20,42,72,110. You can use that to look this up in OEIS, and find A002943 where one comment reads
Write 0,1,2,... in clockwise spiral; sequence gives numbers on one of 4 diagonals.
But I can assure you, it works for counter-clockwise spirals just the same…

Perlin noise for terrain generation

I'm trying to implement 2D Perlin noise to create Minecraft-like terrain (Minecraft doesn't actually use 2D Perlin noise) without overhangs or caves and stuff.
The way I'm doing it, is by creating a [50][20][50] array of cubes, where [20] will be the maximum height of the array, and its values will be determined with Perlin noise. I will then fill that array with arrays of cube.
I've been reading from this article and I don't understand, how do I compute the 4 gradient vector and use it in my code? Does every adjacent 2D array such as [2][3] and [2][4] have a different 4 gradient vector?
Also, I've read that the general Perlin noise function also takes a numeric value that will be used as seed, where do I put that in this case?
I'm going to explain Perlin noise using working code, and without relying on other explanations. First you need a way to generate a pseudo-random float at a 2D point. Each point should look random relative to the others, but the trick is that the same coordinates should always produce the same float. We can use any hash function to do that - not just the one that Ken Perlin used in his code. Here's one:
static float noise2(int x, int y) {
int n = x + y * 57;
n = (n << 13) ^ n;
return (float) (1.0-((n*(n*n*15731+789221)+1376312589)&0x7fffffff)/1073741824.0);
}
I use this to generate a "landscape" landscape[i][j] = noise2(i,j); (which I then convert to an image) and it always produces the same thing:
...
But that looks too random - like the hills and valleys are too densely packed. We need a way of "stretching" each random point over, say, 5 points. And for the values between those "key" points, you want a smooth gradient:
static float stretchedNoise2(float x_float, float y_float, float stretch) {
// stretch
x_float /= stretch;
y_float /= stretch;
// the whole part of the coordinates
int x = (int) Math.floor(x_float);
int y = (int) Math.floor(y_float);
// the decimal part - how far between the two points yours is
float fractional_X = x_float - x;
float fractional_Y = y_float - y;
// we need to grab the 4x4 nearest points to do cubic interpolation
double[] p = new double[4];
for (int j = 0; j < 4; j++) {
double[] p2 = new double[4];
for (int i = 0; i < 4; i++) {
p2[i] = noise2(x + i - 1, y + j - 1);
}
// interpolate each row
p[j] = cubicInterp(p2, fractional_X);
}
// and interpolate the results each row's interpolation
return (float) cubicInterp(p, fractional_Y);
}
public static double cubicInterp(double[] p, double x) {
return cubicInterp(p[0],p[1],p[2],p[3], x);
}
public static double cubicInterp(double v0, double v1, double v2, double v3, double x) {
double P = (v3 - v2) - (v0 - v1);
double Q = (v0 - v1) - P;
double R = v2 - v0;
double S = v1;
return P * x * x * x + Q * x * x + R * x + S;
}
If you don't understand the details, that's ok - I don't know how Math.cos() is implemented, but I still know what it does. And this function gives us stretched, smooth noise.
->
The stretchedNoise2 function generates a "landscape" at a certain scale (big or small) - a landscape of random points with smooth slopes between them. Now we can generate a sequence of landscapes on top of each other:
public static double perlin2(float xx, float yy) {
double noise = 0;
noise += stretchedNoise2(xx, yy, 5) * 1; // sample 1
noise += stretchedNoise2(xx, yy, 13) * 2; // twice as influential
// you can keep repeating different variants of the above lines
// some interesting variants are included below.
return noise / (1+2); // make sure you sum the multipliers above
}
To put it more accurately, we get the weighed average of the points from each sample.
( + 2 * ) / 3 =
When you stack a bunch of smooth noise together, usually about 5 samples of increasing "stretch", you get Perlin noise. (If you understand the last sentence, you understand Perlin noise.)
There are other implementations that are faster because they do the same thing in different ways, but because it is no longer 1983 and because you are getting started with writing a landscape generator, you don't need to know about all the special tricks and terminology they use to understand Perlin noise or do fun things with it. For example:
1) 2) 3)
// 1
float smearX = interpolatedNoise2(xx, yy, 99) * 99;
float smearY = interpolatedNoise2(xx, yy, 99) * 99;
ret += interpolatedNoise2(xx + smearX, yy + smearY, 13)*1;
// 2
float smearX2 = interpolatedNoise2(xx, yy, 9) * 19;
float smearY2 = interpolatedNoise2(xx, yy, 9) * 19;
ret += interpolatedNoise2(xx + smearX2, yy + smearY2, 13)*1;
// 3
ret += Math.cos( interpolatedNoise2(xx , yy , 5)*4) *1;
About perlin noise
Perlin noise was developed to generate a random continuous surfaces (actually, procedural textures). Its main feature is that the noise is always continuous over space.
From the article:
Perlin noise is function for generating coherent noise over a space. Coherent noise means that for any two points in the space, the value of the noise function changes smoothly as you move from one point to the other -- that is, there are no discontinuities.
Simply, a perlin noise looks like this:
_ _ __
\ __/ \__/ \__
\__/
But this certainly is not a perlin noise, because there are gaps:
_ _
\_ __/
___/ __/
Calculating the noise (or crushing gradients!)
As #markspace said, perlin noise is mathematically hard. Lets simplify by generating 1D noise.
Imagine the following 1D space:
________________
Firstly, we define a grid (or points in 1D space):
1 2 3 4
________________
Then, we randomly chose a noise value to each grid point (This value is equivalent to the gradient in the 2D noise):
1 2 3 4
________________
-1 0 0.5 1 // random noise value
Now, calculating the noise value for a grid point it is easy, just pick the value:
noise(3) => 0.5
But the noise value for a arbitrary point p needs to be calculated based in the closest grid points p1 and p2 using their value and influence:
// in 1D the influence is just the distance between the points
noise(p) => noise(p1) * influence(p1) + noise(p2) * influence(p2)
noise(2.5) => noise(2) * influence(2, 2.5) + noise(3) * influence(3, 2.5)
=> 0 * 0.5 + 0.5 * 0.5 => 0.25
The end! Now we are able to calculate 1D noise, just add one dimension for 2D. :-)
Hope it helps you understand! Now read #mk.'s answer for working code and have happy noises!
Edit:
Follow up question in the comments:
I read in wikipedia article that the gradient vector in 2d perlin should be length of 1 (unit circle) and random direction. since vector has X and Y, how do I do that exactly?
This could be easily lifted and adapted from the original perlin noise code. Find bellow a pseudocode.
gradient.x = random()*2 - 1;
gradient.y = random()*2 - 1;
normalize_2d( gradient );
Where normalize_2d is:
// normalizes a 2d vector
function normalize_2d(v)
size = square_root( v.x * v.x + v.y * v.y );
v.x = v.x / size;
v.y = v.y / size;
Compute Perlin noise at coordinates x, y
function perlin(float x, float y) {
// Determine grid cell coordinates
int x0 = (x > 0.0 ? (int)x : (int)x - 1);
int x1 = x0 + 1;
int y0 = (y > 0.0 ? (int)y : (int)y - 1);
int y1 = y0 + 1;
// Determine interpolation weights
// Could also use higher order polynomial/s-curve here
float sx = x - (double)x0;
float sy = y - (double)y0;
// Interpolate between grid point gradients
float n0, n1, ix0, ix1, value;
n0 = dotGridGradient(x0, y0, x, y);
n1 = dotGridGradient(x1, y0, x, y);
ix0 = lerp(n0, n1, sx);
n0 = dotGridGradient(x0, y1, x, y);
n1 = dotGridGradient(x1, y1, x, y);
ix1 = lerp(n0, n1, sx);
value = lerp(ix0, ix1, sy);
return value;
}

How to adjust player sprite speed correctly? (Basically a math question?)

Background: I have a bird view's JavaScript game where the player controls a space ship by touching a circle -- e.g. touch to the left of the circle center, and the ship will move left, touch the top right and it will move to the top right and so on... the further away from the circle center of pseudo joystick, the more speed in that direction. However, I'm not directly adjusting the ship's speed, but rather set a targetSpeed.x and targetSpeed.y value, and the ship will then adjust its speed using something like:
if (this.speed.x < this.targetSpeed.x) {
this.speed.x += this.speedStep;
}
else if (this.speed.x > this.targetSpeed.x) {
this.speed.x -= this.speedStep;
}
... and the same for the y speed, and speedStep is a small value to make it smoother and not too abrupt (a ship shouldn't go from a fast leftwards direction to an immediate fast rightwards direction).
My question: Using above code, I believe however that the speed will be adjusted quicker in diagonal directions, and slower along the horizontal/ vertical lines. How do I correct this to have an equal target speed following?
Thanks so much for any help!
var xdiff = targetSpeed.x - speed.x;
var ydiff = targetSpeed.y - speed.y;
var angle = Math.atan2(ydiff, xdiff);
speed.x += speedStep * Math.cos(angle);
speed.y += speedStep * Math.sin(angle);
Assuming you already checked that the touch is inside the circle, and that the edge of the circle represents max speed, and that the center of the circle is circleTouch == [0, 0]
In some C++-like pseudo code:
Scalar circleRadius = ...;
Scalar maxSpeed = ...;
Scalar acceleration = ...;
Vector calculateTargetSpeed( Vector circleTouch ) {
Vector targetSpeed = maxSpeed * circleTouch / circleRadius;
return targetSpeed;
}
Vector calculateNewSpeed( Vector currentSpeed, Vector targetSpeed ) {
Vector speedDiff = targetSpeed - currentSpeed;
Vector newSpeed = currentSpeed + acceleration * normalized(speedDiff);
return newSpeed;
}
// Divide v by its length to get normalized vector (length 1) with same x/y ratio
Vector normalized( Vector v ) {
return v / length(v);
}
// Pythagoras for the length of v
Scalar length( Vector v ) {
Scalar length = sqrt(v.x * v.x + v.y * v.y); // or preferably hypot(v.x, v.y)
return length;
}
This is just off the top of my head, and i haven't tested it. The other answer is fine, i just wanted to give an answer without trigonometry functions. :)

How to calculate both positive and negative angle between two lines?

There is a very handy set of 2d geometry utilities here.
The angleBetweenLines has a problem, though. The result is always positive. I need to detect both positive and negative angles, so if one line is 15 degrees "above" or "below" the other line, the shape obviously looks different.
The configuration I have is that one line remains stationary, while the other line rotates, and I need to understand what direction it is rotating in, by comparing it with the stationary line.
EDIT: in response to swestrup's comment below, the situation is actually that I have a single line, and I record its starting position. The line then rotates from its starting position, and I need to calculate the angle from its starting position to current position. E.g if it has rotated clockwise, it is positive rotation; if counterclockwise, then negative. (Or vice versa.)
How to improve the algorithm so it returns the angle as both positive or negative depending on how the lines are positioned?
Here's the implementation of brainjam's suggestion. (It works with my constraints that the difference between the lines is guaranteed to be small enough that there's no need to normalize anything.)
CGFloat angleBetweenLinesInRad(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {
CGFloat a = line1End.x - line1Start.x;
CGFloat b = line1End.y - line1Start.y;
CGFloat c = line2End.x - line2Start.x;
CGFloat d = line2End.y - line2Start.y;
CGFloat atanA = atan2(a, b);
CGFloat atanB = atan2(c, d);
return atanA - atanB;
}
I like that it's concise. Would the vector version be more concise?
#duffymo's answer is correct, but if you don't want to implement cross-product, you can use the atan2 function. This returns an angle between -π and π, and you can use it on each of the lines (or more precisely the vectors representing the lines).
If you get an angle θ for the first (stationary line), you'll have to normalize the angle φ for the second line to be between θ-π and θ+π (by adding ±2π). The angle between the two lines will then be φ-θ.
This is an easy problem involving 2D vectors. The sine of the angle between two vectors is related to the cross-product between the two vectors. And "above" or "below" is determined by the sign of the vector that's produced by the cross-product: if you cross two vectors A and B, and the cross-product produced is positive, then A is "below" B; if it's negative, A is "above" B. See Mathworld for details.
Here's how I might code it in Java:
package cruft;
import java.text.DecimalFormat;
import java.text.NumberFormat;
/**
* VectorUtils
* User: Michael
* Date: Apr 18, 2010
* Time: 4:12:45 PM
*/
public class VectorUtils
{
private static final int DEFAULT_DIMENSIONS = 3;
private static final NumberFormat DEFAULT_FORMAT = new DecimalFormat("0.###");
public static void main(String[] args)
{
double [] a = { 1.0, 0.0, 0.0 };
double [] b = { 0.0, 1.0, 0.0 };
double [] c = VectorUtils.crossProduct(a, b);
System.out.println(VectorUtils.toString(c));
}
public static double [] crossProduct(double [] a, double [] b)
{
assert ((a != null) && (a.length >= DEFAULT_DIMENSIONS ) && (b != null) && (b.length >= DEFAULT_DIMENSIONS));
double [] c = new double[DEFAULT_DIMENSIONS];
c[0] = +a[1]*b[2] - a[2]*b[1];
c[1] = +a[2]*b[0] - a[0]*b[2];
c[2] = +a[0]*b[1] - a[1]*b[0];
return c;
}
public static String toString(double [] a)
{
StringBuilder builder = new StringBuilder(128);
builder.append("{ ");
for (double c : a)
{
builder.append(DEFAULT_FORMAT.format(c)).append(' ');
}
builder.append("}");
return builder.toString();
}
}
Check the sign of the 3rd component. If it's positive, A is "below" B; if it's negative, A is "above" B - as long as the two vectors are in the two quadrants to the right of the y-axis. Obviously, if they're both in the two quadrants to the left of the y-axis the reverse is true.
You need to think about your intuitive notions of "above" and "below". What if A is in the first quadrant (0 <= θ <= 90) and B is in the second quadrant (90 <= θ <= 180)? "Above" and "below" lose their meaning.
The line then rotates from its
starting position, and I need to
calculate the angle from its starting
position to current position. E.g if
it has rotated clockwise, it is
positive rotation; if
counterclockwise, then negative. (Or
vice versa.)
This is exactly what the cross-product is for. The sign of the 3rd component is positive for counter-clockwise and negative for clockwise (as you look down at the plane of rotation).
One 'quick and dirty' method you can use is to introduce a third reference line R. So, given two lines A and B, calculate the angles between A and R and then B and R, and subtract them.
This does about twice as much calculation as is actually necessary, but is easy to explain and debug.
// Considering two vectors CA and BA
// Computing angle from CA to BA
// Thanks to code shared by Jaanus, but atan2(y,x) is used wrongly.
float getAngleBetweenVectorsWithSignInDeg(Point2f C, Point2f A, Point2f B)
{
float a = A.x - C.x;
float b = A.y - C.y;
float c = B.x - C.x;
float d = B.y - C.y;
float angleA = atan2(b, a);
float angleB = atan2(d, c);
cout << "angleA: " << angleA << "rad, " << angleA * 180 / M_PI << " deg" << endl;
cout << "angleB: " << angleB << "rad, " << angleB * 180 / M_PI << " deg" << endl;
float rotationAngleRad = angleB - angleA;
float thetaDeg = rotationAngleRad * 180.0f / M_PI;
return thetaDeg;
}
That function is working in RADS
There are 2pi RADS in a full circle (360 degrees)
Thus I believe the answear you are looking for is simply the returned value - 2pi
If you are asking to have that one function return both values at the same time, then you are asking to break the language, a function can only return a single value. You could pass it two pointers that it can use to set the value of so that the change can persist after the frunction ends and your program can continue to work. But not really a sensible way of solving this problem.
Edit
Just noticed that the function actually converts the Rads to Degrees as it returns the value. But the same principle will work.

Resources