Rotating four lines around a common point - c++

I have four lines held in a Rectangle object. I'm trying to rotate the entire box by any angle, but I'm getting weird results.
Here's my current code:
void Line::rotate(int x_anchor, int y_anchor, double angle) {
// Change the coordinate system
int xOffset = m_pt1.x() - x_anchor;
int yOffset = m_pt1.y() - y_anchor;
// Move to 0, 0
int xTemp = m_pt2.x() - xOffset;
int yTemp = m_pt2.y() - yOffset;
// Rotate
double tCos = cos(angle);
double tSin = sin(angle);
double xNew = (xTemp * tCos) - (yTemp * tSin);
double yNew = (xTemp * tSin) + (yTemp * tCos);
// Make new
m_pt2 = Point(xNew + xOffset, yNew + yOffset);
}
What I'm trying to do is move the origin, then move the line down to that orgin, rotate it, then put it back. By doing this, if I do something like:
void Rectangle::rotate(int x_anchor, int y_anchor, double angle) {
m_t.rotate(x_anchor, y_anchor, angle);
m_r.rotate(x_anchor, y_anchor, angle);
m_b.rotate(x_anchor, y_anchor, angle);
m_l.rotate(x_anchor, y_anchor, angle);
}
The box should rotate together. However, this doesn't even work for a line, so I'm not sure where I've gone wrong on my formula. This thread is what I'm referencing for the formula.
Thanks.
EDIT:
I've modified my code according to FalconUA's suggestion:
void Line::rotate(int x_anchor, int y_anchor, double angle) {
/* Change the coordinate system */
// Start point
int xStartOffset = m_pt1.x() - x_anchor;
int yStartOffset = m_pt1.y() - y_anchor;
// End point
int xEndOffset = m_pt2.x() - x_anchor;
int yEndOffset = m_pt2.y() - y_anchor;
/* Move to 0, 0 */
// Start point
int xStartTemp = m_pt2.x() - xStartOffset;
int yStartTemp = m_pt2.y() - yStartOffset;
// End point
int xEndTemp = m_pt2.x() - xEndOffset;
int yEndTemp = m_pt2.y() - yEndOffset;
// Precalculate sin and cos
double tCos = cos(angle);
double tSin = sin(angle);
/* Rotate */
// Start point
double xStartNew = (xStartTemp * tCos) - (yStartTemp * tSin);
double yStartNew = (xStartTemp * tSin) + (yStartTemp * tCos);
// End point
double xEndNew = (xEndTemp * tCos) - (yEndTemp * tSin);
double yEndNew = (xEndTemp * tSin) + (yEndTemp * tCos);
// Make new points
m_pt1 = Point(xStartNew + xStartOffset, yStartNew + yStartOffset);
m_pt2 = Point(xEndNew + xEndOffset, yEndNew + yEndOffset);
}
However, still not quite getting what I should.
Given:
Rectangle r(5, 5, 10, 10);
Which outputs:
xxxxxx
x x
x x
x x
x x
xxxxxx
And then if I rotate by 90 (PI / 2) degrees, which is done by this:
// Use the bottom left corner as the common point
rotate(m_l.getEnd().x(), m_l.getEnd().y(), PI / 2);
I get
x x
x x
x x
x x
x x
x
x
x
x
x x
x
x
x
x
x

Seems like it happens because you're rotating lines relatively to the first point of the line. So, rotate not the line relatively to the first point, but rotate two points separately.
Update: if you have an anchor (xa, ya), and you want to rotate the point (x, y) around it. Your point can be represented as (xa + u, ya + v), where (u, v) = (x - xa, y - ya). So all you have to do is rotate teh vector (u, v) by using the formula with sin and cos that you've used above and the resulting point will be (xa + u_rotated, ya + v_rotated).
void Line::rotate(int x_anchor, int y_anchor, double angle) {
// the vector to rotate
int rotvec_x1 = m_pt1.x() - x_anchor;
int rotvec_y1 = m_pt1.y() - y_anchor;
// the vector to rotate
int rotvec_x2 = m_pt2.x() - x_anchor;
int rotvec_y2 = m_pt2.y() - y_anchor;
// pre-calculation for sin and cos
double tCos = cos(angle);
double tSin = sin(angle);
// rotating first vector
double rotvec_x1_new = (rotvec_x1 * tCos) - (rotvec_y1 * tSin);
double rotvec_y1_new = (rotvec_x1 * tSin) + (rotvec_y1 * tCos);
// rotating second vector
double rotvec_x2_new = (rotvec_x2 * tCos) - (rotvec_y2 * tSin);
double rotvec_y2_new = (rotvec_x2 * tSin) + (rotvec_y2 * tCos);
// Make new
m_pt1 = Point(x_anchor + rotvec_x1_new, y_anchor + rotvec_y1_new);
m_pt2 = Point(x_anchor + rotvec_x2_new, y_anchor + rotvec_y2_new);
}

Related

Why is my vector rotation function changing the vector's magnitude?

I'm looking to make a simple function that rotates a vector's point b around point a for a given number of degrees.
What's odd is that my code seems to work somewhat - the vector is rotating, but it's changing length pretty drastically.
If I stop erasing the screen every frame to see every frame at once, I see the lines producing a sort of octagon around my origin.
Even weirder is that the origin isn't even in the center of the octagon - it's in the bottom left.
Here's my code:
struct Point { int x, y; };
struct Line {
Point a, b;
void rotate(double);
};
void Line::rotate(double t)
{
t *= 3.141592 / 180;
double cs = cos(t);
double sn = sin(t);
double trans_x = (double)b.x - a.x;
double trans_y = (double)b.y - a.y;
double newx = trans_x * cs - trans_y * sn;
double newy = trans_x * sn + trans_y * cs;
newx += a.x;
newy += a.y;
b.x = (int)newx;
b.y = (int)newy;
}
Using the olc::PixelGameEngine to render, which is why I'm using ints to store coordinates.

Drawing rotated Rectangle

So I'm writting a function that takes a point and rotates it around another point at a certain angle, so when I draw a rectangle, I want that rectangle to be rotated as well,but it's distorted.I'm also drawing two colored rectangles, one without rotation and one rotated by the angle provided, like in this picture:
The purple outline should look like the red one.
This is the code for the rotation
double d2r(double d)
{
return (d / 180.0) * ((double)M_PI);
}
double sind(double x)
{
return std::sin(d2r(x));
}
double cosd(double x)
{
return std::cos(d2r(x));
}
std::pair<double, double> rotate_around(double x, double y, double o_x, double o_y, double angle)
{
x = o_x + (x - o_x) * cosd(angle) - (y - o_y) * sind(angle);
y = o_y + (y - o_y) * cosd(angle) + (x - o_x) * sind(angle);
return std::pair<double, double>(x, y);
}
The function rotate_around is called with the following parameters
(original x, original y , middle x of the rectangle, middle y of the rectangle, angle of the rectangle)
Can someone please tell me what am I doing wrong here?
x-= o_x; y-= o_y;
angle= d2r(angle);
double c= std::cos(angle), s= std::sin(angle);
return std::pair<double, double>(o_x + x * c - y * s, o_y + y * c + x * s);
will work.

Why are my openGL ellipses pointed?

I copied this ellipse code directly from the opengl textbook:
void ellipseMidpoint (int xCenter, int yCenter, int Rx, int Ry)
{
int Rx2 = Rx * Rx;
int Ry2 = Ry * Ry;
int twoRx2 = 2 * Rx2;
int twoRy2 = 2 * Ry2;
int p;
int x = 0;
int y = Ry;
int px = 0;
int py = twoRx2 * y;
//initial points in both quadrants
ellipsePlotPoints (xCenter, yCenter, x, y);
//Region 1
p = round (Ry2 - (Rx2 * Ry) + (0.25 * Rx2));
while (px < py) {
x++;
px += twoRy2;
if (p < 0)
p += Ry2 + px;
else {
y--;
py -= twoRx2;
p += Ry2 + px - py;
}
ellipsePlotPoints (xCenter, yCenter, x, y);
}
//Region 2
p = round (Ry2 * (x+0.5) * (x+0.5) + Rx2 * (y-1) * (y-1) - Rx2 * Ry2);
while (y > 0) {
y--;
py -= twoRx2;
if (p > 0)
p += Rx2 - py;
else {
x++;
px += twoRy2;
p += Rx2 - py + px;
}
ellipsePlotPoints (xCenter, yCenter, x, y);
}
}
void ellipsePlotPoints (int xCenter, int yCenter, int x, int y)
{
setPixel (xCenter + x, yCenter + y);
setPixel (xCenter - x, yCenter + y);
setPixel (xCenter + x, yCenter - y);
setPixel (xCenter - x, yCenter - y);
}
void setPixel (GLint xPos, GLint yPos)
{
glBegin (GL_POINTS);
glVertex2i(xPos, yPos);
glEnd();
}
The smaller ellipses seem to be fine but the larger ones are pointy and sort of flat at the ends.
Any ideas why?
Here is a current screenshot:
I think you're encountering overflow. I played with your code. While I never saw exactly the same "lemon" type shapes from your pictures, things definitely fell apart at large sizes, and it was caused by overflowing the range of the int variables used in the code.
For example, look at one of the first assignments:
int py = twoRx2 * y;
If you substitute, this becomes:
int py = 2 * Rx * Rx * Ry;
If you use a value of 1000 each for Rx and Ry, this is 2,000,000,000. Which is very close to the 2^31 - 1 top of the range of a 32-bit int.
If you want to use this algorithm for larger sizes, you could use 64-bit integer variables. Depending on your system, the type would be long or long long. Or more robustly, int64_t after including <stdint.h>.
Now, if all you want to do is draw an ellipsis with OpenGL, there are much better ways. The Bresenham type algorithms used in your code are ideal if you need to draw a curve pixel by pixel. But OpenGL is a higher level API, which knows how to render more complex primitives than just pixels. For a curve, you will most typically use a connected set of line segments to approximate the curve. OpenGL will then take care of turning those line segments into pixels.
The simplest way to draw an ellipsis is to directly apply the parametric representation. With phi an angle between 0 and PI, and using the naming from your code, the points on the ellipsis are:
x = xCenter + Rx * cos(phi)
y = yCenter + Ry * sin(phi)
You can use an increment for phi that meets your precision requirements, and the code will look something to generate an ellipsis approximated by DIV_COUNT points will look something like this:
float angInc = 2.0f * m_PI / (float)DIV_COUNT;
float ang = 0.0f;
glBegin(GL_LINE_LOOP);
for (int iDiv = 0; iDiv < DIV_COUNT; ++iDiv) {
ang += angInc;
float x = xCenter + Rx * cos(ang);
float y = yCenter + Ry * sin(ang);
glVertex2f(x, y);
glEnd();
If you care about efficiency, you can avoid calculating the trigonometric functions for each point, and apply an incremental rotation to calculate each point from the previous one:
float angInc = 2.0f * M_PI / (float)DIV_COUNT;
float cosInc = cos(angInc);
float sinInc = sin(angInc);
float cosAng = 1.0f;
float sinAng = 0.0f
glBegin(GL_LINE_LOOP);
for (int iDiv = 0; iDiv < DIV_COUNT; ++iDiv) {
float newCosAng = cosInc * cosAng - sinInc * sinAng;
sinAng = sinInc * cosAng + cosInc * sinAng;
cosAng = newCosAng;
float x = xCenter + Rx * cosAng;
float y = yCenter + Ry * sinAng;
glVertex2f(x, y);
glEnd();
This code is of course just for illustrating the math, and to get you started. In reality, you should use current OpenGL rendering methods, which includes vertex buffers, etc.

Confusing source code in TrollTech's Qt Tutorial Ch11

I am learning Qt from TrollTech's Qt Tutorial these days, and I'am confused about the source code of calculating the position of bullet in this page:
QRect CannonField::shotRect() const
{
const double gravity = 4;
double time = timerCount / 20.0;
double velocity = shootForce;
double radians = shootAngle * 3.14159265 / 180;
double velx = velocity * cos(radians);
double vely = velocity * sin(radians);
double x0 = (barrelRect.right() + 5) * cos(radians);
double y0 = (barrelRect.right() + 5) * sin(radians);
double x = x0 + velx * time;
double y = y0 + vely * time - 0.5 * gravity * time * time;
QRect result(0, 0, 6, 6);
result.moveCenter(QPoint(qRound(x), height() - 1 - qRound(y)));
return result;
}
In the third-last line:
result.moveCenter(QPoint(qRound(x), height() - 1 - qRound(y)));
I think that - 1 is nonsense, isn't it?
You have a widget:
If the height of widget is height, then y == 0 line is on the top of the widget and bottom line has y == height - 1 coordinate. So, if you want to show a point on the bottom line of the widget, you should set it y coordinate to height - 1.
Apparently, they use bottom of the widget as a ground level, so the bullet can be only above or on this level.

EasyBMP rotating image by any angle

I am trying to rotate a bmp image using EasyBMP. when the angle is between 0 and 90 or 270 and 360 the rotation is fine. but when between 180 and 270 the boundary rectangle is stretched and for angle between 90 and 180 I get segmentation fault. I am convinced that the problem arises from
int width = image.TellWidth();
int height = image.TellHeight();
float sine= sin(angle);
float cosine=cos(angle);
float x1=-height*sine;
float y1=height*cosine;
float x2=width*cosine-height*sine;
float y2=height*cosine+width*sine;
float x3=width*cosine;
float y3=width*sine;
float minx=min(0,min(x1,min(x2,x3)));
float miny=min(0,min(y1,min(y2,y3)));
float maxx=max(x1,max(x2,x3));
float maxy=max(y1,max(y2,y3));
int outWidth;
int outHeight;
outWidth=(int)ceil(fabs(maxx)-minx);
outHeight=(int)ceil(fabs(maxy)-miny);
output.SetSize(outHeight,outWidth);
for(int x=0; x<outWidth; x++)
{
for(int y=0; y<outHeight; y++)
{
int srcX=(int)((x+minx)*cosine+(y+miny)*sine);
int srcY=(int)((y+miny)*cosine-(x+minx)*sine);
if(srcX>=0 &&srcX<width && srcY>=0 && srcY<height)
{
output.SetPixel(x,y,image.GetPixel(srcX,srcY));
}
}
}
The following is how I solved this. The TL;DR: the rotation transform goes around 0, 0, so if your image coordinates set 0,0 to bottom left, you need to translate the image to be centered on 0,0 first. Also, sin and cos expect radians, not degrees, so remember to convert first
The long way:
I started by creating a simple program that has easily verified answers, to find out where things are going wrong.
The first thing I noticed was that 90.0f wouldn't produce any output. That seemed weird, so I broke in at the "output image size" printf and realized that the output height was being calculated as -87. Clearly that's not right, so let's see why that might happen.
Going up a bit, outHeight=(int)ceil(fabs(maxy)-miny); so let's figure out how we're ending up with a negative output height when subtracting maxy and miny. It appears maxy is -0.896... and miny is 88.503... However, the absolute value of maxy is taken before subtracting miny, meaning we're ending up with 0.896 - 88.503. Whoa, that's not good! Let's try doing the subtraction then taking the absolute value.
Recompiling with both width and height as such:
outWidth=(int)ceil(fabs(maxx-minx));
outHeight=(int)ceil(fabs(maxy-miny));
Gets us much better values. Now outWidth and outHeight are 2 and 90, respectively. This is massively improved, but the height should be 100. We'll address that later.
To figure out where the math is going wrong, I reorganize the terms to go together: x with x, y with y. Next I adjusted spacing and added parenthesis to make it more readable and ensure order of operations (sure beats trying to look at an OoO table ;) ). Since it's clear you're breaking out the rotation matrix multiplication, I'm going to name your variables something a bit more intuitive than x1, x2, etc. From now on, x1 is topLeftTransformedX, x2 is topRightTransformedX, x3 will exist as bottomLeftTransformedX (always 0), and x4 will be bottomRightTransformedX, same for Y. Longer, but much easier to know what you're dealing with.
Using this, at this point, I see the same thing you do... then I remembered something, based on the numbers seen from this cleaner code (same math as yours, but still easier to debug).
Suddenly, my math for X looks like this:
// x = x cos - y sin
float topLeftTransformedX = (-midX * cosine) - (midY * sine);
float topRightTransformedX = (midX * cosine) - (midY * sine);
float bottomLeftTransformedX = (-midX * cosine) - (-midY * sine);
float bottomRightTransformedX = (midX * cosine) - (-midY * sine);
The rotation matrix rotates around the center point. You have to translate the image to be centered around that for a proper rotation.
Then, when trying to figure out why this would be giving the values it is, i recalled something else - angle needs to be in radians.
Suddenly, it almost all works. There's still some more to do, but this should get you 95% of the way there or more. Hope it helps!
// bmprotate.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <math.h>
#define min(x,y) x < y ? x : y
#define max(x,y) x > y ? x : y
#define PI 3.14159
void rotate(int width, int height, float angleInDeg)
{
float angle = angleInDeg * (PI/180.0f);
float midX = ((float)width) / 2.0f;
float midY = ((float)height) / 2.0f;
float sine = sin(angle);
float cosine = cos(angle);
// x = x cos - y sin
float topLeftTransformedX = (-midX * cosine) - (midY * sine);
float topRightTransformedX = (midX * cosine) - (midY * sine);
float bottomLeftTransformedX = (-midX * cosine) - (-midY * sine);
float bottomRightTransformedX = (midX * cosine) - (-midY * sine);
float minx = min( topLeftTransformedX, min(topRightTransformedX, min(bottomLeftTransformedX, bottomRightTransformedX)) );
float maxx = max( topLeftTransformedX, max(topRightTransformedX, max(bottomLeftTransformedX, bottomRightTransformedX)) );
// y = x sin + y cos
float topLeftTransformedY = (-midX * sine) + (midY * cosine);
float topRightTransformedY = (midX * sine) + (midY * cosine);
float bottomLeftTransformedY = (-midX * sine) + (-midY * cosine);
float bottomRightTransformedY = (midX * sine) + (-midY * cosine);
float miny = min( topLeftTransformedY, min(topRightTransformedY, min(bottomLeftTransformedY, bottomRightTransformedY)) );
float maxy = max( topLeftTransformedY, max(topRightTransformedY, max(bottomLeftTransformedY, bottomRightTransformedY)) );
int outWidth;
int outHeight;
printf("(%f,%f) , (%f,%f) , (%f,%f) , (%f,%f)\n",
topLeftTransformedX, topLeftTransformedY,
topRightTransformedX, topRightTransformedY,
bottomLeftTransformedX, bottomLeftTransformedY,
bottomRightTransformedX, bottomRightTransformedY);
outWidth = (int) ceil( fabs(maxx) + fabs(minx));
outHeight = (int) ceil( fabs(maxy) + fabs(miny) );
printf("output image size: (%d,%d)\n",outWidth,outHeight);
for(int x=0; x<outWidth; x++)
{
for(int y=0; y<outHeight; y++)
{
int srcX=(int)((x+minx)*cosine+(y+miny)*sine);
int srcY=(int)((y+miny)*cosine-(x+minx)*sine);
if(srcX >=0 && srcX < width && srcY >= 0 && srcY < height)
{
printf("(x,y) = (%d,%d)\n",srcX, srcY);
}
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
rotate(100,2,90.0f);
for (int i = 0; i < 360; i++)
{
}
return 0;
}