Create objects in a circle around a point - c++

I need to create a circle of regularly spaced doors around a reference point in space. Each time the program is run, a different number of items will be in the circle, and thus it automatically rescales to accommodate the increased number of items. I know this is more of a mathematic question rather than a programming question, but I am stumped! The following code is as close as I got:
block->setPosition(core::vector3df(sin(segdeg*itemnumber)*radius+referencepoint.X,
0,
cos(segdeg*itemnumber)*radius+referencepoint.Z));
block is the object, and this code is run for each item.
segdeg is 360/the number of items. that is to say, the SEGment DEGrees.
Radius is how far away from the central point the items need to be.
Itemnumber is the index of the item in question - Which number item it is.
For some reason, this code makes each door pretty close, but still about 10-15 degrees off. (The first door is always spot on though) The items are all the correct distance away from the centre point though. I'm sure that this is a really obvious answered question, but I haven't been able to solve it for hours and I've googled my arse off.
Can anyone fix my algorithm?

sin and cos take their arguments in radians, not degrees. You want segrad = 2 * pi / the_number_of_items (SEGment RADians) instead of segdeg = 360.0 / the_number_of_items (SEGment DEGrees).
Your implementation might provide a value for pi, but there's none in the standard so you might have to use boost::math::constants::pi<double>() or put your own in.
Your first item is correct because 0 is 0 in any measure.

int doors = 5;
double rotation = 2 * M_PI / doors;
for(int door = 0; door < doors; ++door)
{
double door_rotation = door * rotation;
double door_x = reference_x + cos(door_rotation);
double door_y = reference_y + sin(door_rotation);
}

Related

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.

How can work time between ticks into this movement function?

I have a function that moves a planet around a star. This function takes a parameter t, which is the time in milliseconds since the last update. In other movement functions I've written, I like to use time to dictate movement so the movement will always be the same on all computers and instances instead of based on processing power. However, all methods I have tried for including time in this physics equation have resulted in erratic results. Any ideas?
void Planet::update(int t){
double grav_const = 6.6742e-11;
double earth_mass = 5.975e24;
double starX = 1920/2 * 10000;
double starY = 1080/2 * 10000;
double diffX = xPos - starX;
double diffY = yPos - starY;
double radius = sqrt(pow(diffX,2) + pow(diffY,2));
double grav_accel = (grav_const * (earth_mass / pow(radius,2)));
double angle = atan2(diffX, diffY);
xVel += (sin(angle) * grav_accel);
yVel += (cos(angle) * grav_accel);
xPos -= xVel;
yPos -= yVel;
}
It's been a while since I dealt with physics at this level, but I think you can go back to fundamental reasoning about the units involved.
Acceleration is distance over time squared (m/s^2 or whatever your units are). So to get velocity (distance over time) then you need to multiply by time.
m/s = (m/s^2) * s
And then after that you want to turn your velocity into a specific change in distance. So multiply it by the time again and there you go.
m = (m/s) * s
If things still don't seem right afterwards, then you may need to check over the rest of your equations and constants. Make sure the units match up (seconds vs minutes, metere vs kilometers, etc). Make sure you aren't suffering rounding in places you didn't intend. And so on.
In the worst case, work the math yourself for a few iterations (perhaps with larger time values) and maybe even plot the results on a piece of paper to make sure it looks sensible.
When you describes the results as "erratic" exactly what do you mean?
If you mean:
A. "t changes by a varying amount between each call". Then you need to look at the architecture of the calling application since that will vary with processing power and other work going on in the system (assuming a preemptive multitasking OS).
B. "the floating point values have strange rounding characteristics". Then welcome to using floating point numbers. The representations of double, float and the like are simply imperfect and exhibit rounding areas in certain circumstances and you may have problems if you are taking deltas that are too small relative to the size of the other values you are combining.
C. "t has no effect on my results". I don't see any references to the input parameter in your example.
You should post the entire Planet class, or at least more of it.
EDIT: The best way to calculate position based on times like this is to come up with an absolute function that returns position based on time and NOT accumulate position, but only accumulate time. For example:
timeAbsolute += tDelta;
xPos = fxPos(timeAbsolute);
yPos = fyPos(timeAbsolute);
xVel = fxVel(timeAbsolute);
yVel = fyVel(timeAbsolute);
My orbital mechanics fu is not strong enough to give you those functions in general, but in your case (where you seem to be assuming a circular orbit), you can simply take the arc angle instead. So, assuming 1 orbit every 360 seconds (and using degrees), you would get
angle = (timeAbsolute % 360);
then calc velocity and position from angle.
P.S. Be careful with fmod ...
How to use fmod and avoid precision issues

C++ recognize shape from points

I'am trying to find out an algorithm to recognize circle in array of points.
Lets say that I've got points array where circle could or could not be stored (that also means array doesn't have to store only circle's points, there could be some "extra" points before or after circle's data).
I've already tried some algorithms but none of them work properly with those "extra" points. Have you got any ideas how to deal with this problem?
EDIT// I didn't mention that before. I want this algorithm to be used on circle gesture recognition. I've thought I would have data in array (for last few seconds) and by analysing this data in every tracking frame I would be able to say if there was or was not a circle gesture.
First I calculate the geometric mean (not the aritmetic mean) for each X and Y component.
I choose geometric mean because one feature is that small values ​​(with respect to the arithmetic mean ) of the values ​​are much more influential than the large values.
This lead me to the theoretical center of all points: circ_center
Then I calculate the standard deviation of distance of each point to center: stddev. This gives me the "indicator" to quantify the amount of variation. One property of circle is that all circumference point is at the same distance of it's center. With standard dev I try to test if your points are (with max variance threshold: max_dispersion) equally distance.
Last I calculates the average distance of points inside max_dispersion threshold from center, this give me the radius of the circle: avg_dist.
Parameters:
max_dispersion represents the "cicle precision". Smaller means more precise.
min_points_needed is the minimun number of points valid to be considered as circumference.
This is just an attempt, I have not tried. Let me know.
I will try this (in pseudo language)
points_size = 100; //number_of_user_points
all_poins[points_size]; //coordinates of points
//thresholds to be defined by user
max_dispersion = 20; //value of max stddev accepted, expressed in geometric units
min_points_needed = 5; //minimum number of points near the circumference
stddev = 0; //standard deviation of points from center
circ_center; //estimated circumference center, using Geometric mean
num_ok_points = 0; //points with distance under standard eviation
avg_dist = 0; //distance from center of "ok points"
all_x = 1; all_y = 1;
for(i = 0 ; i < points_size ; i++)
{
all_x = all_x * all_poins[i].x;
all_y = all_y * all_poins[i].y;
}
//pow(x, 1/y) = nth root
all_x = pow(all_x, 1 / points_size); //Geometric mean
all_y = pow(all_y, 1 / points_size); //Geometric mean
circ_center = make_point(all_x, all_y);
for(i = 0 ; i < points_size ; i++)
{
dist = distance(all_poins[i], circ_center);
stddev = stddev + (dist * dist);
}
stddev = square_root(stddev / points_size);
for(i = 0 ; i < points_size ; i++)
{
if( distance(all_poins[i], circ_center) < max_dispersion )
{
num_ok_points++;
avg_dist = avg_dist + distance(all_poins[i], circ_center);
}
}
avg_dist = avg_dist / num_ok_points;
if(stddev <= max_dispersion && num_ok_points >= min_points_needed)
{
circle recognized; it's center is circ_center; it's radius is avg_dist;
}
Can we assume the array of points are mostly on or near to the circumference of the circle?
A circle has a center and radius. If you can determine the circle's center coordinates, via the intersection of perpendiculars of two chords, then all the true circle points should be equidistant(r), from the center point.
The false points can be eliminated by not being equidistant (+-)tolerance from the center point.
The weakness of this approach is how well can you determine the center and radius? You may want to try a least squares approach to computing the center coordinates.
To answer the initially stated question, my approach would be to iterate through the points and derive the center of a circle from each consecutive set of three points. Then, take the longest contiguous subset of points that create circles with centers that fall within some absolute range. Then determine if the points wind consistently around the average of the circles. You can always perform some basic heuristics on any discarded data to determine if a circle is actually what the user wanted to make though.
Now, since you say that you want to perform gesture recognition, I would suggest you think of a completely different method. Personally, I would first create a basic sort of language that can be used to describe gestures. It should be very simple; the only words I would consider having are:
Start - Denotes the start of a stroke
Angle - The starting angle of the stroke. This should be one of the eight major cardinal directions (N, NW, W, SW, S, SE, E, NE) or Any for unaligned gestures. You could also add combining mechanisms, or perhaps "Axis Aligned" or other such things.
End - Denotes the end of a stroke
Travel - Denotes a straight path in the stroke
Distance - The percentage of the total length of the path that this particular operation will consume.
Turn - Denotes a turn in the stroke
Direction - The direction to turn in. Choices would be Left, Right, Any, Previous, or Opposite.
Angle - The angle of the turn. I would suggest you use just three directions (90 deg, 180 deg, 270 deg)
Tolerance - The maximum tolerance for deviation from the specified angle. This should have a default of somewhere around 45 degrees in either direction for a high chance of matching the angle in a signature.
Type - Hard or Radial. Radial angles would be a stroke along a radius. Hard angles would be a turn about a point.
Radius - If the turn is radial, this is the radius of the turn (units are in percentage of total path length, with appropriate conversions of course)
Obviously you can make the angles much more fine, but the coarser the ranges are, the more tolerant of input error it can be. Being too tolerant can lead to misinterpretation though.
If you apply some fuzzy logic, it wouldn't be hard to break just about any gesture down into a language like this. You could then create a bunch of gesture "signatures" that describe various gestures that can be performed. For instance:
//Circle
Start Angle=Any
Turn Type=Radial Direction=Any Angle=180deg Radius=50%
Turn Type=Radial Direction=Previous Angle=180deg Radius=50%
End
//Box
Start Angle=AxisAligned
Travel Distance=25%
Turn Type=Hard Direction=Any Angle=90deg Tolerance=10deg
Travel Distance=25%
Turn Type=Hard Direction=Previous Angle=90deg Tolerance=10deg
Travel Distance=25%
Turn Type=Hard Direction=Previous Angle=90deg Tolerance=10deg
Travel Distance=25%
End
If you want, I could work on an algorithm that could take a point cloud and degenerate it into a series of commands like this so you can compare them with pre-generated signatures.

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.