I need to rotate a rectangle around it's center-point and display it in the center of a QWidget. Can you complete this specific code? If possible, could you also dumb-down the explaination or provide a link to the simplest explaination?
Please note: I have read the Qt documentation, compiled examples/demos that deal with rotation and I STILL cannot understand it!
void Canvas::paintEvent(QPaintEvent *event)
{
QPainter paint(this);
paint.setBrush(Qt::transparent);
paint.setPen(Qt::black);
paint.drawLine(this->width()/2, 0, this->width()/2, this->height());
paint.drawLine(0, this->height()/2, this->width(), this->height()/2);
paint.setBrush(Qt::white);
paint.setPen(Qt::blue);
// Draw a 13x17 rectangle rotated to 45 degrees around its center-point
// in the center of the canvas.
paint.drawRect(QRect(0,0, 13, 17));
}
void paintEvent(QPaintEvent* event){
QPainter painter(this);
// xc and yc are the center of the widget's rect.
qreal xc = width() * 0.5;
qreal yc = height() * 0.5;
painter.setPen(Qt::black);
// draw the cross lines.
painter.drawLine(xc, rect().top(), xc, rect().bottom());
painter.drawLine(rect().left(), yc, rect().right(), yc);
painter.setBrush(Qt::white);
painter.setPen(Qt::blue);
// Draw a 13x17 rectangle rotated to 45 degrees around its center-point
// in the center of the canvas.
// translates the coordinate system by xc and yc
painter.translate(xc, yc);
// then rotate the coordinate system by 45 degrees
painter.rotate(45);
// we need to move the rectangle that we draw by rx and ry so it's in the center.
qreal rx = -(13 * 0.5);
qreal ry = -(17 * 0.5);
painter.drawRect(QRect(rx, ry, 13, 17));
}
You are in the painter's coordinate system. When you call drawRect(x, y, 13, 17), it's upper left corner is at (x,y). If you want (x, y) to be the center of your rectangle, then you need to move the rectangle by half, hence rx and ry.
You can call resetTransform() to reset the transformations that were made by translate() and rotate().
Simple:
void rotate(QPainter* p, const QRectF& r, qreal angle, bool clock_wise) {
p->translate(r.center());
p->rotate(clock_wise ? angle : -angle);
p->translate(-r.center());
}
Related
Let's say I have a QPixmap that has the dimensions of (20 x 100). How can I create a copy of this QPixmap that's rotated a specific amount and also has new dimensions to allocate the new dimensions of the rotated pixmap?
I've found multiple examples on how to rotate using QPainter and QTransform, but none seem to provide a proper manner to keep the QPixmap from cutting off.
The best example I've found so far is:
// original = Original QPixmap
QSize size = original.size();
QPixmap newPixmap(size);
newPixmap.fill(QColor::fromRgb(0, 0, 0, 0));
QPainter p(&newPixmap);
p.translate(size.height() / 2, size.height() / 2);
p.rotate(35); // Any rotation, for this example 35 degrees
p.translate(size.height() / -2, size.height() / -2);
p.drawPixmap(0, 0, original);
p.end();
This rotates a QPixmap, and places it on a new QPixmap of the same dimensions. However, I am at a loss on how to modify this to work with new dimensions.
I've even tried simply modifying the initial size of the new pixmap, but that just causes the image to be off center (and still cut off for some reason?)
Any support would be appreciated!
One way to do this would be to calculate the minimum bounding rect for your rotated image and to create a new pixmap with these dimensions onto which you can render your rotated image which is now guarenteed to fit. To do this you could take each corner point of your image rectangle and rotate them around the center. The resulting points can then be used to calculate your minimum bounding rectangle by looking at each point and finding both the minimum and maximum x and y values.
For example in the following hypothetical example we have a 100x100 rectangle. If we use a simple algorithm to rotate each corner point of the rectangle around the center by our angle (in this case 45 degrees) we get the four new corner points (50, -20), (-20, 50), (120, 120) and (50, 120). From these points we can see the minimum x value is -20, the minimum y value is -20, the maximum x value is 120 and the maximum y value is 120, so the minimum bounding rect can be described by topLeft:(-20, -20) and bottomRight:(120, 120).
To help you with this here is a function taken from another stackoverflow post for rotating a point around another point:
QPointF getRotatedPoint( QPointF p, QPointF center, qreal angleRads )
{
qreal x = p.x();
qreal y = p.y();
float s = qSin( angleRads );
float c = qCos( angleRads );
// translate point back to origin:
x -= center.x();
y -= center.y();
// rotate point
float xnew = x * c - y * s;
float ynew = x * s + y * c;
// translate point back:
x = xnew + center.x();
y = ynew + center.y();
return QPointF( x, y );
}
And here is a function I wrote that uses it to calculate the minimum bounding rect for some rectangle rotated by some angle...
QRectF getMinimumBoundingRect( QRect r, qreal angleRads )
{
QPointF topLeft = getRotatedPoint( r.topLeft(), r.center(), angleRads );
QPointF bottomRight = getRotatedPoint( r.bottomRight(), r.center(), angleRads );
QPointF topRight = getRotatedPoint( r.topRight(), r.center(), angleRads );
QPointF bottomLeft = getRotatedPoint( r.bottomLeft(), r.center(), angleRads );
// getMin and getMax just return the min / max of their arguments
qreal minX = getMin( topLeft.x(), bottomRight.x(), topRight.x(), bottomLeft.x() );
qreal minY = getMin( topLeft.y(), bottomRight.y(), topRight.y(), bottomLeft.y() );
qreal maxX = getMax( topLeft.x(), bottomRight.x(), topRight.x(), bottomLeft.x() );
qreal maxY = getMax( topLeft.y(), bottomRight.y(), topRight.y(), bottomLeft.y() );
return QRectF( QPointF( minX, minY ), QPointF( maxX, maxY ) );
}
So now we have the minimum bounding rectangle for our rotated image we can create a new pixmap with its width and height and render our rotated image to it at the center. This is tricky because of the transformation involved which makes it a bit more confusing as to what your source and target rects might be. It's actually not as hard as it might seem. You perform your translation / rotation to rotate the paint device around the center, then you can simply render your source image onto your destination image exactly as you would if you were rendering the source to the center of the destination.
For example:
QPixmap originalPixmap; // Load this from somewhere
QRectF minimumBoundingRect = getMinimumBoundingRect( originalPixmap.rect(), angleRads);
QPixmap rotatedPixmap( minimumBoundingRect.width(), minimumBoundingRect.height() );
QPainter p( &rotatedPixmap );
p.save();
// Rotate the rotated pixmap paint device around the center...
p.translate( 0.5 * rotatedPixmap.width(), 0.5 * rotatedPixmap.height() );
p.rotate( angleDegrees );
p.translate( -0.5 * rotatedPixmap.width(), -0.5 * rotatedPixmap.height() );
// The render rectangle is simply the originalPixmap rectangle as it would be if placed at the center of the rotatedPixmap rectangle...
QRectF renderRect( 0.5 * rotatedRect.width() - 0.5 * originalPixmap.width(),
0.5 * rotatedRect.height() - 0.5 * originalPixmap.height(),
originalPixmap.width(),
originalPixmap.height() );
p.drawPixmap( renderRect, originalPixmap, originalPixmap.rect() );
p.restore();
And voila, a nicely rotated image with no corners chopped off.
I'm experimenting with drawing and rotating shapes in Qt, but I'm really at a loss as to how it works. Currently I have code which draws a rectangle with a small triangle on top of it. I want to rotate the shape by 35 degrees, so I try this:
void Window::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
QBrush brush;
brush.setStyle(Qt::SolidPattern);
brush.setColor(Qt::white);
painter.setBrush(brush);
painter.setPen(Qt::NoPen);
painter.fillRect(0,0,800,800,brush);
brush.setColor(Qt::red);
painter.translate(s.getX()-5,s.getY()-8);
painter.rotate(35);
painter.fillRect(0,0,10,16,brush);
QPolygon pol;
pol.setPoints(3,0,0,10,0,5,10);
QPainterPath p;
p.addPolygon(pol);
painter.fillPath(p,brush);
}
(Ignore the s.getX/Y() calls, for now x is 150 and y is 750.)
Without the rotating and translating the code works fine and draws the shape. With the current code only the rectangle is displayed, not the polygon. How do I rotate these shapes?
You need to properly understand how affine transformations work. Without the proper understanding you will have hard times achieving what is needed.
rotate rotates everything around center of coordinates: (0,0)
translate moves center of coordinates to a new position
Your code rotates everything around the point (s.getX() - 5, s.getY() - 8).
So here's the code that will rotate both shapes 35 degrees around the center of red rectangle:
QPainter painter(this);
QBrush brush;
brush.setStyle(Qt::SolidPattern);
brush.setColor(Qt::white);
painter.setBrush(brush);
painter.setPen(Qt::NoPen);
painter.fillRect(0, 0, 800, 800, brush);
brush.setColor(Qt::red);
painter.translate(150, 750);
painter.translate(5, 8); // move center of coordinates to the center of red rectangle
painter.rotate(35); // rotate around the center of red rectangle
painter.translate(-5, -8); // move center of coordinates back to where it was
painter.fillRect(0, 0, 10, 16, brush);
QPolygon pol;
pol.setPoints(3, 0, 0, 10, 0, 5, 10);
QPainterPath p;
p.addPolygon(pol);
brush.setColor(Qt::blue);
painter.fillPath(p, brush);
Without transformations:
With transformations:
I have a vector between (posX, posY) and (mouseX, mouseY), and I get the mouse position as a positive integer with allegro's event library. From this vector using an arc tangent I get the radian of (deltaX, deltaY). I then plug that radian into an al_draw_rotated_bitmap function. I expect the bitmap to point towards where the mouse cursor is, but the issue I have is that the radian or vector is causing it to be rotated perpendicular to the cursor.
Here is the relevant code:
void setRotation(int dx, int dy)
{
float deltax = posX - mouseX;
float deltay = posY - mouseY;
rotation = atan2(deltay, deltax);
}
void Player::draw()
{
al_draw_rotated_bitmap(player, al_get_bitmap_width(player) / 2, al_get_bitmap_height(player) / 2, posX, posY, rotation, 0);
}
int main()
{
while(true)
{
player.setRotation(mouseX, mouseY);
player.draw();
al_flip_display();
}
}
Imagine (deltax, deltay) = (0, 100), that is the mouse is 100 pixels above the object, and so the picture (I believe) shouldn't be rotated at all. But atan2(deltay, deltax) = atan2(100, 0) = π/2, that's why your picture is rotated perpendicularly.
To fix this you should change it to atan2(deltax, deltay), possibly adding - before arguments depending on the direction of X and Y axes in Allegro, which I don't know.
In other words, atan2 measures the angle relative to X axis, but in your case the angle should be measured relative to Y axis (because alignment on Y axis means no rotation), so you should swap its arguments.
In SFML how would I rotate a shape around some point using transformations? I know how to use cos and sin but like to understand how to use transformation.
I think my problem is that I don't get the sfml interface. Here is one example:
// Draw circle at center
sf::CircleShape c;
c.setFillColor(sf::Color::Red);
c.setPosition(width / 2, height / 2);
c.setRadius(50);
c.setOrigin({50, 50});
Now, how would I move and rotate the circle around the center of the screen inside the frame refresh loop?
Turns out there is rotate function which takes a center point.
sf::CircleShape c;
c.setFillColor(sf::Color::Red);
c.setPosition(width / 2.f, height / 2.f - 100);
c.setRadius(50);
c.setOrigin({50, 50});
float angle = 1.0;
sf::Transform t;
while(true)
{
window.clear(sf::Color::Black);
t.rotate(angle, { width/2.f, height / 2.f });
window.draw(c, t);
window.display();
}
I have a function that draws a circle.
glBegin(GL_LINE_LOOP);
for(int i = 0; i < 20; i++)
{
float theta = 2.0f * 3.1415926f * float(i) / float(20);//get the current angle
float rad_x = ratio*(radius * cosf(theta));//calculate the x component
float rad_y = radius * sinf(theta);//calculate the y component
glVertex2f(x + rad_x, y + rad_y);//output vertex
}
glEnd();
This works dandy. I save the x, y and radius values in my object.
However when I try and draw a square with the following function call:
newSquare(id, red, green, blue, x, (x + radius), y, (y + radius));
I get the following image.
As you see, the square is nearly double as wide (looks more like the diameter). The following code is how I create my square box. As you can see it starts in the center of the circle in which it should. And should stretch out to the edge of the circle.
glBegin(GL_QUADS);
glVertex2f(x2, y2);
glVertex2f(x2, y1);
glVertex2f(x1, y1);
glVertex2f(x1, y2);
glEnd();
I can't seem to understand why this is!
If you're correcting the x-position for one object, you have to do it for all others as well.
However, if you continue this, you'll get into trouble very soon. In your case, only the width of objects is corrected but not their positions. You can solve all your problems by setting an orthographic projection matrix and you won't ever need to correct positions again. E.g. like so:
glMatrixMode(GL_PROJECTION); //switch to projection matrix
glOrtho(-ratio, ratio, -1, 1, -1, 1);
glMatrixMode(GL_MODELVIEW); //switch back to model view
where
ratio = windo width / window height
This constructs a coordinate system where the top edge has y=1, the bottom edge y=-1 and the left and right sides have x=-ratio and x=ratio, respectively.