I would like to recognize in what axis quarter is a given angle
In the Most efficient way
Quarter 1:
Alpha > 0 && Alpha <= 90 (degrees)
Quarter 2:
Alpha > 90 && Alpha <= 180 (degrees)
Quarter 3:
Alpha > 180 && Alpha <= 270 (degrees)
Quarter 4:
Alpha > 270 && Alpha <= 360 (degrees)
C++ Code
FORCEINLINE uint8 MapAngleToQuarter(float angle)
{
angle = (int)angle % 360;
float answer = 0;
if(angle > 0 && angle <= 90)
{
answer = 1;
}
else if(angle > 90 && angle <= 180)
{
answer = 2;
}
else if(angle > 180 && angle <= 270)
{
answer = 3;
}
else if(angle > 270 && angle <= 360)
{
answer = 4;
}
return answer;
}
My question: Is there a better (more efficient) way to do the above task ?
With the angle in the (0...360] range
FORCEINLINE uint8 MapAngleToQuarter(float angle) {
int a = (int) angle;
return (a - 1)/90 + 1;
}
Related
Given the following code pattern, wherein I am trying to state a vector direction in increments of 45 degrees over the integers int x and int y, inside of a circle positioned at the origin
// x == 0 && y == 0 case is already taken cared of
if(x > 1) {
if(y == 0) {
// horizontal right
m_rotation = 0;
}else if(y < 1) {
// diagonal down right
m_rotation = 315;
} else if(y > 1) {
// diagonal up right
m_rotation = 45;
}
} else if(x == 0) {
if(y < 1) {
// vertical down
m_rotation = 270;
} else if(y > 1) {
// vertical up
m_rotation = 90;
}
} else if(x < 1){
if(y == 0) {
// horizontal left
m_rotation = 180;
}else if(y < 1) {
// diagonal down left
m_rotation = 225;
} else if(y > 1) {
// diagonal up left
m_rotation = 135;
}
}
I am looking for an elegant way to make this compact. I know there's the spaceship operator <=>, but I need to restrict myself to C++17.
Things I have tried
Nesting ternary operators with m_rotation = x > 1? (y < 1? (y == 0? 0: 315): 45): (x == 0? (y < 1? 270: 90): (y < 1? (y == 0? 180: 225): 135));, but this looks weird
I tried putting the x == 0 case inside x < 1 case and transform the later into else, but that does not simplify enough the code
Using absolute values to compare x and y, but I quickly get lost
Nothing else really, I don't know what else to try
Something like
constexpr int rotation[3][3] = {
{225, 180, 135},
{270, 0, 90},
{315, 0, 45},
};
if (x != 0 || y != 0) // if (!(x == 0 && y == 0))
m_rotation = rotation[1 + sign(x)][1 + sign(y)];
There is a closed form:
// standard sign functions
int xs = x < 0 ? -1 : x > 0;
int ys = y < 0 ? -1 : y > 0;
return 180 - 45 * (xs + 2) * ys + 90 * (xs * xs + xs) * (ys * ys - 1);
or shorter
return 180 * (x < 0 || y) - 45 * (xs + 2) * ys;
I'm trying to implement a Harris corner detector with OpenCv and C++ and I can't seem to find an algorithm to find local maximas, I'm new to image processing, so would you please help me?
Take a 3x3 window, then check if centre pixel is the maximum
I find I am constantly recycling this function from my binary image library
https://github.com/MalcolmMcLean/binaryimagelibrary/
/*
get 3x3 neighbourhood, padding for boundaries
Params: out - return pointer for neighbourhood
binary - the binary image
width - image width
height - image height
x, y - centre pixel x, y co-ordinates
border - value to pad borders with.
Notes: pattern returned is
0 1 2
3 4 5
6 7 8
where 4 is the pixel at x, y.
*/
static void get3x3(unsigned char *out, unsigned char *binary, int width, int height, int x, int y, unsigned char border)
{
if(y > 0 && x > 0) out[0] = binary[(y-1)*width+x-1]; else out[0] = border;
if(y > 0) out[1] = binary[(y-1)*width+x]; else out[1] = border;
if(y > 0 && x < width-1) out[2] = binary[(y-1)*width+x+1]; else out[2] = border;
if(x > 0) out[3] = binary[y*width+x-1]; else out[3] = border;
out[4] = binary[y*width+x];
if(x < width-1) out[5] = binary[y*width+x+1]; else out[5] = border;
if(y < height-1 && x > 0) out[6] = binary[(y+1)*width+x-1]; else out[6] = border;
if(y < height-1) out[7] = binary[(y+1)*width+x]; else out[7] = border;
if(y < height-1 && x < width-1) out[8] = binary[(y+1)*width+x+1]; else out[8] = border;
}`
`
You might need to change unsigned char to int or float - the function is designed for binary images.
I have worked on this problem for months and my current solution still does not work. I am working on simple bounding box collision between a square pawn and a vector of square tiles.
In this current method the player will collide correctly on the bottom, left and right but not on top. I do not understand why.
This is the physics function, which deals with motion:
void loop(Pawn *pawn) {
int currentFPS = fps->getFPS();
//friction
if (pawn->velX > 0.0) {
pawn->velX *= 0.91f;
}
if (pawn->velY > 0) {
pawn->velY *= 0.91f;
}
if (pawn->velX < 0) {
pawn->velX *= 0.91f;
}
if (pawn->velY < 0) {
pawn->velY *= 0.91f;
}
//gravity
pawn->velY += gravity;
//motion
pawn->posY += pawn->velY; //move along the horizontal axis
pawn->posX += pawn->velX; //move along the vertical axis
//handle collision
for (int i = 0; i < vectorSize; i++) {
collide(pawn, getLevelCollision[i]);
}
}
This is the collision function, which checks for collision and then corrects the pawn position based on which edge is intersecting the colliding rectangle:
void collide(Pawn *pawn, SDL_Rect rect2) {
bool collide = false;
//create positions of rect1
float rect1Top = pawn->posY;
float rect1Bottom = rect1Top + pawn->position.h;
float rect1Left = pawn->posX;
float rect1Right = rect1Left + pawn->position.w;
//create position of rect2
float rect2Top = rect2.y;
float rect2Bottom = rect2.y + rect2.h;
float rect2Left = rect2.x;
float rect2Right = rect2.x + rect2.w;
//if the bottom edge of rect1 intersects rect2 vertically
if (rect1Bottom > rect2Top && rect1Bottom < rect2Bottom) {
//if rect1 and rect2 are on top of each other
if (rect1Right > rect2Left && rect1Right < rect2Right || rect1Left < rect2Right && rect1Left > rect2Left) {
pawn->posY = rect2Top - pawn->position.h;
rect1Top = pawn->posY;
rect1Top = pawn->position.h;
}
}
//if the left edge of rect1 intersects rect2 horizontally
else if (rect1Left < rect2Right && rect1Left > rect2Left) {
//if rect1 and rect2 are on top of each other
if (rect1Top < rect2Bottom && rect1Top > rect2Top || rect1Bottom > rect2Top && rect1Bottom < rect2Bottom) {
pawn->posX = rect2Right;
rect1Left = pawn->posX;
rect1Right = rect1Left + pawn->position.w;
}
}
//if the right edge of rect1 intersects rect2 horizontally
else if (rect1Right > rect2Left && rect1Right < rect2Right) {
//if rect1 and rect2 are on top of each other
if (rect1Top < rect2Bottom && rect1Top > rect2Top || rect1Bottom > rect2Top && rect1Bottom < rect2Bottom) {
pawn->posX = rect2Left - pawn->position.w;
rect1Left = pawn->posX;
rect1Right = rect1Left + pawn->position.w;
}
}
//if the top edge of rect1 intersects rect2 vertically
if (rect1Top < rect2Bottom && rect1Top > rect2Top) {
//if rect1 and rect2 are on top of each other
if (rect1Right > rect2Left && rect1Right < rect2Right || rect1Left < rect2Right && rect1Left > rect2Left) {
pawn->posY = rect2Bottom;
rect1Top = pawn->posY;
rect1Top = rect1Top + pawn->position.h;
}
}
}
I cannot understand why the top edge of the pawn does not collide as every possible iteration of if / else if has been tried as well as rearranging the order of the if statements.
I am desperate for insight on this problem as well as a new set of eyes to see what I am clearly not able to.
I'm trying to calculate the angles of two points in 2D space. It works with the following:
double angle;
x = player->x - x;
z = player->z - y;
angle = atan2 (x, y);
angle *= (180.0 / M_PI);
if(angle < 0) angle += 360;
if(angle >= 360) angle -= 360;
return angle;
However I want to use a table for better results:
x = player->x - x;
z = player->y - y;
return atanTable[x+32][y+32];
With init:
int xp = -32;
int yp = -32;
for (int x = 0; x < 64; x++, xp++)
for (int y = 0; y < 64; y++, yp++){
double angle;
angle = atan2 (xp, yp);
angle *= (180.0 / M_PI);
if(angle < 0) angle += 360;
if(angle >= 360) angle -= 360;
atanTable[x][y] = angle;
}
The angle is only calculated for values -32 to 32 for x and y.
The table is not properly produced however. I get values of only around 359 and 0, while I should get a range of 0 to 360 degrees.
Am I misusing atan2 somehow?
You need to reset yp within the first loop, not before it.
int xp = -32;
for (int x = 0; x < 64; x++, xp++)
{
int yp = -32; // Note, needs to be reset each time x changes.
for (int y = 0; y < 64; y++, yp++)
{
Nit: atan2() returns an angle in [-pi,pi] always. Your second if statement is redundant. It's also likely a bad design: very often you want to test later on for angles in [pi,2pi], which can be done with a simple <0 for the default range. Basically, atan2() is your friend, the reason the function exists is to save you from the hassle of doing modular math on angles.
I am attempting to write a function that will tell me if an angle lies within 2 other angles. When say 'if an angle lies within 2 other angles' I mean for example, if I have the 2 angles 0 and 90 then 45 would lie between those angles but -20(or 99) would not.
My Problem: My function doesn't seem to be detecting when 2 angles lie within 2 angles when it should. I'm not sure if my function works for negative angles aswell?
What do I need to change to get my function working correctly?
bool is_angle_between(int target, int angle1, int angle2)
{
// Post: Return true if target lies between the 2 angles
int iTarget = (360 + (target % 360)) % 360;
int iAngle1 = (3600000 + angle1) % 360;
int iAngle2 = (3600000 + angle2) % 360;
if (iAngle1 < iAngle2)
if (iAngle1 <= iTarget && iTarget <= iAngle2)
return true;
else if (iAngle1 <= iTarget || iTarget <= iAngle2)
return true;
return false;
}
This question only has to do with testing whether an integer lies between within the range of two other integers. Since we do not know whether angle1 or angle2 is the larger value, I would do something like this:
bool is_angle_between(int target, int angle1, int angle2)
{
return (target > angle1 && target < angle2) ||
(target > angle2 && target < angle1);
}
A good way to do this is to rotate the interval so that all numbers you compare are positive:
int rTarget = ((target - angle1) % 360 + 360) % 360;
int rAngle2 = ((angle2 - angle1) % 360 + 360) % 360;
return 0 <= rAngle1 && rAngle1 <= rAngle2;
Otherwise you will get into trouble near 0 = 360 degrees.
BTW, you should avoid unnecessary if statements, as branching can be expensive.
Seems like this should work:
if (iAngle1 < iAngle2) {
if (iAngle1 <= iTarget && iTarget <= iAngle2) {
return true;
}
}
else {
if (iAngle2 <= iTarget && iTarget <= iAngle1) {
return true;
}
}
return false;
Note that the braces here help ensure that the else matches the if you intend it to. Your original code is indented as you want it to match, but that isn't how it's parsed.