Evenly distribute grid cells horizontally/vertically? - c++

I'm trying to draw a grid inside a window of game_width=640 and game_height=480. The numbers of grid cells is predefined. I want to evenly distribute the cells horizontally and vertically.
void GamePaint(HDC dc)
{
int numcells = 11;
for(int i = 1; i <= numcells; i++)
{
int y = <INSERT EQUATION>;
MoveToEx(dc, 0, y, NULL);
LineTo(dc, game_width, y);
}
// solving the horizontal equation will in turn solve the vertical one so no need to show the vertical code
}
The first equation came to mind was:
i * (game_height / numcells)
The notion behind this is to divide the total height by the number of cells to get the even cell size, which is then multiplied by i in each iteration of the loop to get the correct y coordinate of the beginning of the horizontal line.
The problem with that is that it seems to leave an extra small cell at the end:
I figured this must have something to do with that integral division, so we come to the second equation:
(int)(i * ((float)game_height / numcells))
The idea is to avoid the integer division, do a float division, multiply by i like before and cast the result back to int. This works well, no extra small cell at the end!
What's driving me nuts, is this equation:
i * game_height / numcells
which seems to have the same effect as the previous equation, but of course with the added benefit of not doing any casting. I can't figure out why this doesn't suffer from the integer division issues in the first equation.
Note that mathematically: X * (Y / Z) == X * Y / Z
So there's definitely a problem with integer division in the first equation.
Here's a video watching these 3 equations in the debugger watch window.
You can see an increasing gap between the result of the first equation vs the second and third (which always had the same result) as i increases.
Why is the 3rd equation giving correct results as the second equation and not suffering from the integral division error in the first equation? I just can't seem to wrap my head around it...
Any help is appreciated.

The answer is in the order of operations performed. The first equation
int y = i * (game_height / numcells);
performs the division first, and magnifies the rounding error by multiplying with i. The further you move down/to the right, the larger the accumulated rounding error becomes.
The last equation
int y = i * game_height / numcells;
is evaluated left to right. The relative error gets smaller, as you are dividing larger numbers (i * game_height). You still have a rounding error, but it doesn't accumulate. The fact, that you do not wind up with extra space at the final evaluation is, that i is equal to numcells, essentially cancelling each other out. You will always see y == game_height in the final iteration.
Using floating point operations is still more accurate: While the rounding error using integer math is in the interval [0 .. numcells) for each line, floating point math reduces that to [0 .. 1). You will see more evenly distributed lines using floating point math.
Note: On Windows you can use MulDiv instead of the integer equation, to prevent common errors such as transient overflows.

Related

Is there a way to generate the corners of a regular N-gon without division and without trig, given N as input

Edit: So I found a page related to rasterizing a trapezoid https://cse.taylor.edu/~btoll/s99/424/res/ucdavis/GraphicsNotes/Rasterizing-Polygons/Rasterizing-Polygons.html but am still trying to figure out if I can just do the edges
I am trying to generate points for the corners of an arbitrary N-gon. Where N is a non-zero positive integer. But I am trying to do so efficiently without the need of division and trig. I am thinking that there is probably some of sort of Bresenham's type algorithm for this but I cannot seem to find anything.
The only thing I can find on stackoverflow is this but the internal angle increments are found by using 2*π/N:
How to draw a n sided regular polygon in cartesian coordinates?
Here was the algorithm from that page in C
float angle_increment = 2*PI / n_sides;
for(int i = 0; i < n_sides; ++i)
{
float x = x_centre + radius * cos(i*angle_increment +start_angle);
float y = y_centre + radius * sin(i*angle_increment +start_angle);
}
So is it possible to do so without any division?
There's no solution which avoids calling sin and cos, aside from precomputing them for all useful values of N. However, you only need to do the computation once, regardless of N. (Provided you know where the centre of the polygon is. You might need a second computation to figure out the coordinates of the centre.) Every vertex can be computed from the previous vertex using the same rotation matrix.
The lookup table of precomputed values is not an unreasonable solution, since at some sufficiently large value of N the polygon becomes indistinguishable from a circle. So you probably only need a lookup table of a few hundred values.

c++ half library has lower precision for positive numbers

I know that I am using a capability not built into c++ however, this library seems to be so commonly used that I am surprised to see this error pop up.
For those of you who do not know about the library it can be found here. Essentially, it is supposed to allow the support of 16 bit floating point (lower precision) numbers.
My problem is that the precision of half floats appears to diminish for positive numbers.
In this code, I am generating a bunch of points to be rendered to the screen. {xs1, ys1} represents floating point precision calculation of sigmoid. {xs3, ys3} represents the values cast into floating point precision.
vector<float> xs1, ys1, xs3, ys3;
int res = 200000;
for (int i = 0; i < res; i++)
{
float prec = float(i) / float(res);
float fx = ((perc - 0.5) * 2.0)*8.0;
half hx = half(fx);
float fy = MFunctions::sigmoid(fx);
half hy = half(fy);
xs1.push_back(fx);
ys1.push_back(fy);
xs3.push_back(float(hx));
ys3.push_back(float(hy));
}
Here are the results (looking at zoomed in portions of the graph this generates with a window width of 2.2 and a window height of 0.02 units):
When looking at the floating precision graph, {xs1, ys1} both of the corners of the sigmoid function are smooth:
However, when looking at the half precision graph {xs3, ys3} the corner in the positive x axis shows a stepping effect while the corner in the negative x axis shows a lower resolution but smooth graph:
I am not sure why this is happening since the only difference between positive and negative numbers should be a sign bit.
Is there something wrong that I am doing or is this a flaw in the half library?
Sigmoid function output values are [0;1], so what you see is normal: in the bottom picture, values are around 1, so precision is much lower than around 0.

Rotating vectors in space and high precision in C++

This is my function to compute 3D rotation in C++ defined by an angle in radiant around axis.
Vector rotate(const Vector& axis, const Vector& input, const double angle) {
double norm = 1/axis.norm();
if(norm != 1)
axis *= norm;
double cos = std::cos(angle);
double mcos = 1 - cos;
double sin = std::sin(angle);
double r1[3];
double r2[3];
double r3[3];
double t_x, t_ym t_z;
r1[0] = cos + std::pow(axis.x, 2)*mcos;
r1[1] = axis.x*axis.y*mcos - axis.z * sin;
r1[2] = axis.x*axis.z*mcos - axis.y * sin;
r2[0] = axis.x*axis.y*mcos + axis.z*sin;
r2[1] = cos + std::pow(axis.y, 2)*mcos;
r2[2] = axis.x*axis.z*mcos - axis.x * sin;
r3[0] = axis.x*axis.z*mcos - axis.y * sin;
r3[1] = axis.z*axis.y*mcos - axis.x * sin;
r3[2] = cos - std::pow(axis.z, 2) * mcos;
return Vector(t_x, t_y, t_z);
}
The thing is if you try yourself to rotate the vector a n times pi/4 where n multiple of 4 (so you do complete revolution around the axis by doing four quarter of rotations) the error will propagate pretty fast.
Example (where err = input-output):
input: (1.265, 3.398, 3.333)
rotation axis: (2.33, 0.568, 2.689)
n: 8 (so two completes revolutions)
output: (1.301967, 1.533389, 4.138940)
error: (0.038697, -0.864611, 0.805940)
n: 400 (so 100 completes revolutions)
error: (472..., 166..., 673...)
What can I do ?
Constraints:
Rotations are not predictable so not possible to do something like angle = pi/4 *n % 2*pi like #molbdnilo suggests. Because I have to chain translations and rotations to test if there's a collision.
This is one of the typical problems that you can run into with floats.
Floating point numbers are pretty exact on singular operations. In fact, for many operations you are guaranteed to get the most exact result that can be represented in the format, so any rounding errors that you get are solely due to fitting it into the representation.
However, as soon as you start chaining floating point operations, even those errors can accumulate. If you are lucky, you can make your algorithm numerically stable, so that the rounding errors cancel each other out in the end and you always stay in the ballpark of the correct result. Getting this right can be quite a challenge though, especially for complex computations. For instance, in your particular implementation, there is lots of potential for catastrophic cancellation introducing large rounding errors into the computation chain.
The easier solution is: Avoid chaining the float operations in the first place! Or to be more precise: Only chain those parts which you can keep numerically stable. Since you mentioned this is for a computer game: In a game you transform the geometry according to the camera matrix each frame. You never touch the geometry in memory, instead you simply adjust the camera matrix. That way, your source geometry is always fresh and the rounding error in each frame is simply the error from that single transformation.
Similarly, you usually don't update the camera matrix incrementally. Instead, you read the player's position and view and build the complete matrix from scratch from those vectors. Now the only challenge that you have left is make sure that you don't accumulate errors into the player position and view, but this is much easier than ensuring stability at the other end of the transformation pipeline.
Just store the original base vector and the rotation angle together, and do the calculation every time you need the current rotated value. You can cache this and invalidate every time the angle changes, but always work from the original base vector and total aggregate rotation.
Presto! No cumulative errors, because no chained calculations.
Also, if you're concerned about cumulative errors in the angle itself, store that in degrees and convert to radians when required. Again, pi is touched once in the degree->radian conversion, and you don't have a chain of approximate pi/n values contributing more errors.
You could try and discretize your rotations so that they add up to multiples of pi/2. You clamp your rotation value to the closest discretized value and then do the calculation. If you find one small enough it will be still perceived as smooth and you should not accumulate an error.
In the end I used Rodrigues' rotation formula and the performances are way better.
Though this is not the solution as #ComicSansMs points it out:
Avoid chaining the float operations in the first place!
So combining a new algorithm and using Rodrigues' formula was ok.
I suspect mistakes in my first matrix implementation.
Thanks everyone for your answers and insights.

C++ gamedev: truncating float to int

I'm making a game in C++ where is a tank that can moves on a stage. The tank has an angle (float, in degrees, and i'm supposing the tank is at 0º when his cannon points to the right), a speed (float), and there is a time constant called "deltaT" (float).
When the player moves the tank forward, I use trigonometry and the physic equation of position in function of time (I mean X(t), I don't know how it says in English) in order to calculate the new tank's coordinates in the stage.
This is my problem: due to the passage from float to int, the values closest to zero are not taken into account. So, at certain angles, the tank appears rotated, but moves in a different direction.
This is what my code does:
1 - first, I separate the speed in its components X and Y, by using the angle in which the tank is moving:
float speedX = this->speed * cos(this->angle);
float speedY = this->speed * sin(this->angle);
2 - then use the equation I mentioned above to get the new coordinates:
this->x = (int) ceil(this->x + (speedX * deltaT));
this->y = (int) ceil(this->y - (speedY * deltaT));
The problem begins at the first step: at certain angles, the value of the cos or the sin is very close to zero.
So, when I multiply it for speed to obtain, say, speedX, I still got a very low number, and then when I multiply it for deltaT it is still too low, and finally when apply the ceil, that amount is totally lost.
For example, at 94º, with deltaT = 1.5, and speed = 2, and assuming initial value of X is 400, we have:
speedX = -0.1395...
this->x = 400 //it must be 399.86..., but stays in 400 due to the ceiling
So, in my game the tank appears rotated, but moves straight forward. Also, sometimes it moves well backwards but wrong forward, and viceversa.
How can I do to make the direction of the tank more accurate? To raise the speed or the value of deltaT are not options since it's about a tank, not a formula 1.
How can I do to make the direction of the tank more accurate? To raise the speed or the value of deltaT are not options since it's about a tank, not a formula 1 :P
You should store your position values as floats, even though they are ultimately used as ints for on-screen positioning. That way, you won't lose the non-integer portion of your position. Just cast to int right at the end when you do your drawing.
Keep the location of the tank in floats all the time. Alternatively, only let the tank rotate in increments of 45 degrees. Decide on whether your game will use approximate positions and headings or exact ones and stick to that decision all the way through.
Your problem is not accuracy! Floating-point math has 24-bit accuracy, that's plus/minus 1/16,777,216. No problem there.
It's your rounding mode.
ceil rounds up.
Try:
int x = this->x + (speedX * deltaT) +.5f;
ceil creates a rounding error (E) of 0<E<1, casting as above gives -.5<E<+.5, which has half the absolute error.
Also, avoid using double math. ceil is the double version, you mean ceilf. Technically, your code casts float->double->int. You gain nothing from this, but it takes time.
And finally... The real problem:
Your accuracy problem really is because you are accumulating it!
So 0<E<1 ... PER FRAME!
at 60Hz => 0<E<60*seconds. Thus after a minute 0<E<3600
If X/Y are pixel coordinates, that's a whole screen! No wonder you are struggling.
No matter how you round, this is always going to be a problem.
Instead, you to store the floating point result before rounding. Thus the absolute error is always 0<E<1 ... or -.5f < E < .5f for mid-point rounding. Try adding a new floating point position - XPos/YPos
this->XPos += speedX * deltaT;
this->YPos += speedY * deltaT;
this->x = static_cast<int>(this->XPos+.5f);
this->y = static_cast<int>(this->YPos+.5f);
You should now move around smoothly.
PS: The word in English is "parametric", https://en.wikipedia.org/wiki/Parametric_equation

fastest way to compute angle with x-axis

What is the fastest way to calculate angle between a line and the x-axis?
I need to define a function, which is Injective at the PI:2PI interval (I have to angle between point which is the uppermost point and any point below).
PointType * top = UPPERMOST_POINT;
PointType * targ = TARGET_POINT;
double targetVectorX = targ->x - top->x;
double targetVectorY = targ->y - top->y;
first try
//#1
double magnitudeTarVec = sqrt(targetVectorX*targetVectorX + targetVectorY*targetVectorY);
angle = tarX / magTar;
second try
//#2 slower
angle = atan2(targetVectorY, targetVectorX);
I do not need the angle directly (radians or degrees), just any value is fine as far as by comparing these values of this kind from 2 points I can tell which angle is bigger. (for example angle in example one is between -1 and 1 (it is cosine argument))
Check for y being zero as atan2 does; then The quotient x/y will be plenty fine. (assuming I understand you correctly).
I just wrote Fastest way to sort vectors by angle without actually computing that angle about the general question of finding functions monotonic in the angle, without any code or connections to C++ or the likes. Based on the currently accepted answer there I'd now suggest
double angle = copysign( // magnitude of first argument with sign of second
1. - targetVectorX/(fabs(targetVectorX) + fabs(targetVectorY)),
targetVectorY);
The great benefit compared to the currently accepted answer here is the fact that you won't have to worry about infinite values, since all non-zero vectors (i.e. targetVectorX and targetVectorY are not both equal to zero at the same time) will result in finite pseudoangle values. The resulting pseudoangles will be in the range [−2 … 2] for real angles [−π … π], so the signs and the discontinuity are just like you'd get them from atan2.