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:
Related
QPainter::scale takes the top-left corner of the window as an origin. In order to use the center of the window as an origin, I thought I could first translate the origin of the coordinate system to the center of the window using QPainter::translate and then scale the graphics:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr) :
QMainWindow(parent) {
resize(600, 400);
}
protected:
void paintEvent(QPaintEvent *) override {
QPainter painter(this);
// draw a rectangle
QRectF rectangle(10.0, 20.0, 80.0, 60.0);
painter.drawRect(rectangle);
// translate the origin of coordinate system to the center of window
QPointF offset = rect().center();
painter.translate(offset);
// scale the rectangle
painter.scale(2,2);
painter.drawRect(rectangle);
}
};
The example produces the following result:
The problem is that the scale is still made with regard to the top-left corner.
How to fix that?
Following is my solution.
QPainter painter(this);
// draw a rectangle
QRectF rectangle1(10.0, 20.0, 80.0, 60.0);
painter.drawRect(rectangle1);
// scale the rectangle by 2 times
QRectF rectangle2(10.0, 20.0, 80.0 * 2, 60.0 * 2);
// move it to the center of window
QPointF offset = rect().center() - rectangle2.center();
painter.translate(offset);
painter.drawRect(rectangle2);
And I get what I want like this:
Finding the appropriate transformation that should be applied to QPainter is not a simple task since it involves centering one element on another, moving it, etc. The simplest thing is to transform the rectangle as shown below:
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
// draw a rectangle
QRectF rectangle(10.0, 20.0, 80.0, 60.0);
painter.drawRect(rectangle);
// scale
rectangle.setSize(2*rectangle.size());
// translate
rectangle.moveCenter(rect().center());
painter.drawRect(rectangle);
}
You miss one step, i.e. to re-translate the painter back after the scale. In other words, between
painter.scale(2,2);
painter.drawRect(rectangle);
add
painter.translate(-offset);
I draw a circle filled yellow (0-width border) and, on top of it, a circle filled grey (0-width border) with the same center and the same size as the yellow circle.
How come I can still see a very thin yellow boundary around my grey circle? Interestingly, if I switch the circles around (i.e. put the yellow circle on top), then the grey circle is completely hidden as it should be.
How can I make sure that the two circles are exactly the same?
Here is the drawing code:
cairo_push_group(cr);
cairo_set_line_width(cr, 0.0);
// The yellow circle
cairo_set_source_rgb(cr, (double)0xff/255, (double)0xfd/255, (double)0x37/255);
cairo_arc(cr, 100, 100, 10, 0, 2 * M_PI);
cairo_fill(cr);
cairo_stroke (cr);
// The grey circle
cairo_set_source_rgb(cr, (double)0x97/255, (double)0x8a/255, (double)0x84/255);
cairo_arc(cr, 100, 100, 10, 0, 2 * M_PI);
cairo_fill(cr);
cairo_stroke (cr);
cairo_pop_group_to_source(cr);
I am using the following code to draw an image in a circle.
QPixmap target(110, 130); // the size may vary
target.fill(Qt::transparent);
QPixmap p = QPixmap::fromImage( QImage(":/Images/head").scaled(110,110,Qt::IgnoreAspectRatio,Qt::SmoothTransformation).convertToFormat(QImage::Format_ARGB32));
QPainter painter(&target);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::Antialiasing, true);
QRegion r(QRect(10,10,100,100), QRegion::Ellipse);
painter.setClipRegion(r);
painter.drawPixmap(0, 0,p);
ui->label->setPixmap(target);
This is the output that I get
Notice the circle is not smooth. My question is how could i make this circle smooth.
Using clip path will not has antialias ability in drawing.
You can use a mask image or make a color mask image to draw image to a special shape that.
Here is a simple example:
QPainter *painter....
QPixmap pixmapToDraw....
....
QImage dst(sourceRect.width(), sourceRect.height(), QImage::Format_ARGB32);
dst.fill(QColor(0, 0, 0, 0));
QPainter p(&dst);
QPainterPath path;
path.addRoundedRect(0, 0, sourceRect.width(), sourceRect.height(), sourceRect.width() / 2, sourceRect.height() / 2);
p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, true);
p.fillPath(path, QBrush(Qt::white));
p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
p.drawPixmap(dst.rect(), pixmapToDraw);
painter->drawImage(0, 0, dst);
Here is Qt's doc Image Composition Example, QPainter::CompositionMode
Code for the beginning:
QColor yellow("#f0d048");
Qt::BrushStyle style = Qt::SolidPattern;
QBrush brush(yellow, style);
painter.setBrush(brush);
painter.drawEllipse(10,10,10,10);
Everytime I do this, I get a yellow circle surrounded by a black 1-pixel-sized border. In total the circle will have the same size like if I draw with black colour, so what shall I do to just get a single-coloured yellow circle without black border?
Best regards
Set a pen on painter
painter.setPen(Qt::NoPen);
Qt has 'brush' for filling figures, and 'pen' for drawing lines and outlines.
I am wondering if gluLookAt together with glFrustum is distorting the rendered picture.
This is how a scene is rendered:
And here's the code that rendered it.
InitCamera is called once and should, as I understand it now, set up a matrix so as if I looked from a position 2 units above and 3 units in front of the origin towards the origin. Also glFrustum is used in order to create a perspective`.
void InitCamera() {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt (
0, 2 , 3,
0, 0 , 0,
0, 1 , - 0
);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum (- 1, 1,
- 1, 1,
1,1000.0);
glMatrixMode(GL_MODELVIEW);
}
Then TheScene is what actually draws the picture:
void TheScene() {
glClear(
GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT
);
glMatrixMode(GL_MODELVIEW);
// Draw red circle around origin and radius 2 units:
glColor3d(1,0,0);
glBegin(GL_LINE_LOOP);
for (double i = 0; i<=2 * M_PI; i+=M_PI / 20.0) {
glVertex3d(std::sin(i) * 2.0, 0, std::cos(i) * 2.0);
}
glEnd();
// draw green sphere at origin:
glColor3d(0,1,0);
glutSolidSphere(0.2,128, 128);
// draw pink sphere a bit away
glPushMatrix();
glColor3d(1,0,1);
glTranslated(8, 3, -10);
glutSolidSphere(0.8, 128, 128);
glPopMatrix();
SwapBuffers(hDC_opengl);
}
The red ball should be drawn in the origin and at the center of the red circle around it. But looking at it just feels wierd, and gives me the imprssion that the green ball is not in the center at all.
Also, the pink ball should, imho, be drawn as a perfect circle, not as an ellipse.
So, am I wrong, and the picture is drawn correctly, or am I setting up something wrong?
Your expectations are simply wrong
The perspective projection of a 3d circle (if the circle is fully visible) is an ellipse, however the projection of the center of the circle is NOT in general the center of the ellipse.
The outline of the perspective projection of a sphere is in general a conic section i.e. can be a circle, an ellipse, a parabola or an hyperbola depending on the position of viewpoint, projection plane and sphere in 3D. The reason is that the outline of the sphere can be imagined as a cone starting from the viewpoint and touching the sphere being intersected with the projection plane.
Of course if you're looking at a circle with a perfectly perpendicular camera the center of the circle will be projected to the center of the circle projection. In the same manner if your camera is pointing exactly to a sphere the sphere outline will be a circle, but those are special cases, not the general case.
These differences between the centers are more evident with strong perspective (wide angle) cameras. With a parallel projection instead this apparent distortion is absent (i.e. the projection of the center of a circle is exactly the center of the projection of the circle).
To see the green sphere in the centre of the screen with a perfect circle around it you need to change the camera location like so:
gluLookAt (
0, 3, 0,
0, 0, 0,
0, 0, 1
);
Not sure what's causing the distortion of the purple sphere though.
The perspective is correct, it just looks distorted because that's how things fell together here.
try this for gluLookAt, and play around a bit more.:
gluLookAt (
0, 2 , 10,
0, 0 , 0,
0, 1 , 0
);
The way I tried it out was with a setup that allows me to adjust the position and view direction with the mouse, so you get real time motion. Your scene looks fine when I move around. If you want I can get you the complete code so you can do that too, but it's a bit more than I want to shove into an answer here.