1. Goal
My colleague and I have been trying to render rotated ellipsoids in Qt. The typical solution approach, as we understand it, consists of shifting the center of the ellipsoids to the origin of the coordinate system, doing the rotation there, and shifting back:
http://qt-project.org/doc/qt-4.8/qml-rotation.html
2. Sample Code
Based on the solution outlined in the link above, we came up with the following sample code:
// Constructs and destructors
RIEllipse(QRect rect, RIShape* parent, bool isFilled = false)
: RIShape(parent, isFilled), _rect(rect), _angle(30)
{}
// Main functionality
virtual Status draw(QPainter& painter)
{
const QPen& prevPen = painter.pen();
painter.setPen(getContColor());
const QBrush& prevBrush = painter.brush();
painter.setBrush(getFillBrush(Qt::SolidPattern));
// Get rectangle center
QPoint center = _rect.center();
// Center the ellipse at the origin (0,0)
painter.translate(-center.x(), -center.y());
// Rotate the ellipse around its center
painter.rotate(_angle);
// Move the rotated ellipse back to its initial location
painter.translate(center.x(), center.y());
// Draw the ellipse rotated around its center
painter.drawEllipse(_rect);
painter.setBrush(prevBrush);
painter.setPen(prevPen);
return IL_SUCCESS;
}
As you can see, we have hard coded the rotation angle to 30 degrees in this test sample.
3. Observations
The ellipses come out at wrong positions, oftentimes outside the canvas area.
4. Question
What is wrong about the sample code above?
Best regards,
Baldur
P.S. Thanks in advance for any constructive response?
P.P.S. Prior to posting this message, we searched around quite a bit on stackoverflow.com.
Qt image move/rotation seemed to reflect a solution approach similar to the link above.
In painter.translate(center.x(), center.y()); you shift your object by the amount of current coordinate which makes (2*center.x(), 2*center.y()) as a result. You may need:
painter.translate(- center.x(), - center.y());
The theory of moving an object back to its origin, rotating and then replacing the object's position is correct. However, the code you've presented is not translating and rotating the object at all, but translating and rotating the painter. In the example question that you've referred to, they're wanting to rotate the whole image about an object, which is why they move the painter to the object's centre before rotating.
The easiest way to do rotations about a GraphicsItem is to initially define the item with its centre in the centre of the object, rather than in its top left corner. That way, any rotation will automatically be about the objects centre, without any need to translate the object.
To do this, you'd define the item with a bounding rect for x,y,width,height with (-width/2, -height/2, width, height).
Alternatively, assuming your item is inherited from QGraphicsItem or QGraphicsObject, you can use the function setTransformOriginPoint before any rotation.
Related
I want to apply a velocity vector to a dynamic body in the cursor direction:
void Game::mousePressEvent(QMouseEvent *e){
double angle = atan2(realBall->GetPosition().y - e->pos().y(), realBall->GetPosition().x - e->pos().x());
realBall->SetLinearVelocity(b2Vec2(-cos(angle) * 50, -sin(angle) * 50));
}
But the dynamic body has an incorrect direction, so i think that the cursor position it's wrong.
Thank you for the help!
First, you must know that in order for your code to work, the coordinates of your screen and the coordinates of box2d must match. Be aware that if you use screen coordinates in pixels, it means that the size of one pixel matches an 1 meter in box2d. But let’s assume that you have already taken all this into account. Then I would not advise you to use trigonometry for calculations. So you can easily make a mistake. In this case, simple vector operations will be enough for you: substraction, scaling and normalizing a vector. You can try this: velocity = (cursor_position - real_ball_position).normalize().scale(50f). In box2d there is a b2Vec class for vector operations. You can read about it in detail in the documentation.
I am trying to rotate am image around its origin(center) in QT using QWidgts in C++. I experimented a lot of things here, but no matter what I do, the image keeps rotating around some arbitrary position I have no clue of. Kindly, help me out here. I am new to QT.
void gaugeWithRedZoneImage::rotate()
{
QPixmap pixmap(*gaugeMainScreen->pixmap());
QMatrix rm;
rm.translate(0, 0);
rm.rotate(-360);
pixmap = pixmap.transformed(rm);
gaugeMainScreen->setPixmap(pixmap);
/*QTransform rotate_disc;
rotate_disc.translate(pixmap.width()/2.0 , pixmap.height()/2.0);
rotate_disc.rotate(-60);
rotate_disc.translate(-(pixmap.width()/2.0) , -(pixmap.height()/2.0));
pixmap = pixmap.transformed(rotate_disc);
gaugeMainScreen->setPixmap(pixmap);*/
}
Form the documentation of QPixmap::transformed():
The transformation transform is internally adjusted to compensate for unwanted translation; i.e. the pixmap produced is the smallest pixmap that contains all the transformed points of the original pixmap.
This means that the method ensures no clipping takes place by appending the canvas. No matter what your rotation center was, the automatic extension of canvas will almost always result in a perceived shift.
Image examples might help to further diagnose the problem.
As ypnos said, your problem isn't the rotation center. When you rotate your image, its width and height will most likely change and no longer fit your container (gaugeMainScreen) dimensions.
You have some possibilities to overcome this problem. One of them is to set your container to scale its contents (you can use the method setScaledContents()). In this case, you have to keep the original image around and use it whenever you apply a rotation, otherwise your image will appear increasingly smaller.
i've been looking around to find the best way to have wrap arround edges of a world for the bodies.
I managed to use this topic to do it :
How do I make a Box2D wrap around world?
Using SetTransform() i could make it appear on the other X/Z side.
Now Let's say i have an object for example a simple box 10x10.
If half the box goes beyond upper Y edge i want the portion that goes beyond to appear below with the other part of the box that is still visibile on the upper part to stay there.
To summarize i want a "real wrap arround edge like this used to be done in old games.
I hope i was clear enough...
Edit :
I've added a picture to explain what i mean :
Thanks
I have not used Box2d but I have resolved this problem before in Processing, hopefully the logic translates easily
In Processing a rectangle is drawn as rect(x, y, rectangleWidth, rectangleHeight) at position x, y which represents the top left corner of the rectangle. The rectangle's width and height point right and down from x and y respectively
The idea is to draw the rectangle normally unless the bottom would be off the bottom edge of the viewport (because the rectangle's height points down based on y). If the rectangle is off the bottom edge of the viewport then you instead draw two partial rectangles
In the code below height is the height of your viewport
if (y < height-10)
rect(x, y, 10, 10); // Normal condition, entire rectangle
else {
rect(x, 0, 10, 10-(height-y)); // Top partial rectangle
rect(x, y, 10, height-y); // Bottom partial rectangle
}
Finally i googled a bit found this article on Unity :
http://gamedevelopment.tutsplus.com/articles/create-an-asteroids-like-screen-wrapping-effect-with-unity--gamedev-15055
On Box2D I created 8 ghosts bodies which were positionned as defined in the article.
During Box2D Steps i added some logics to check where my original body is positionned.
When it goes to the edges the Ghost Body appears on the other edge. It works also when the original Body goes on a corder. 4 ghosts bodies will appear on each edges.
I want to draw a triangle and text using C++ and Cairo like this:
|\
| \
|PP\
|___\
If I add the triangle and the text using Cairo I get:
___
| /
|PP/
| /
|/
So the y-axis is from top to bottom, but I want it from bottom to top. So I tried to changed the viewpoint matrix (cairo_transform(p, &mat);) or scale the data (cairo_scale(p, 1.0, -1.0);). I get:
|\
| \
|bb\
|___\
Now the triangle is the way I want it BUT the TEXT is MIRRORED, which I do not want to be mirrored.
Any idea how to handle this problem?
I was in a similar situation as the OP that required me to change a variety of coordinates in the cartesian coordinate system with the origin at the bottom left. (I had to port an old video game that was developed with a coordinate system different from Cairo's, and because of time constraints/possible calculation mistakes/port precision I decided it was better to not rewrite the whole bunch) Luckily, I found an okay approach to change Cairo's coordinate system. The approach is based around Cairo's internal transformation matrix, that transforms Cairo's input to the user device. The solution was to change this matrix to a reflection matrix, a matrix that mirrors it's input through the x-axis, like so:
cairo_t *cr;
cairo_matrix_t x_reflection_matrix;
cairo_matrix_init_identity(&x_reflection_matrix); // could not find a oneliner
/* reflection through the x axis equals the identity matrix with the bottom
left value negated */
x_reflection_matrix.yy = -1.0;
cairo_set_matrix(cr, &x_reflection_matrix);
// This would result in your drawing being done on top of the destination
// surface, so we translate the surface down the full height
cairo_translate(cr, 0, SURFACE_HEIGHT); // replace SURFACE_HEIGHT
// ... do your drawing
There is one catch however: text will also get mirrored. To solve this, one could alter the font transformation matrix. The required code for this would be:
cairo_matrix_t font_reflection_matrix;
// We first set the size, and then change it to a reflection matrix
cairo_set_font_size(cr, YOUR_SIZE);
cairo_get_font_matrix(cr, &font_reflection_matrix);
// reverse mirror the font drawing matrix
font_reflection_matrix.yy = font_reflection_matrix.yy * -1;
cairo_set_font_matrix(cr, &font_reflection_matrix);
Answer:
Rethink your coordinates and pass them correctly to cairo. If your coordinates source has an inverted axis, preprocess them to flip the geometry. That would be called glue code, and it is ofter neccessary.
Stuff:
It is a very common thing with 2D computer graphics to have the origin (0,0) in the top left corner and the y-axis heading downwards (see gimp/photoshop, positioning in html, webgl canvas). As allways there are other examples too (PDFs).
I'm not sure what the reason is, but I would assume the reading direction on paper (from top to bottom) and/or the process of rendering/drawing an image on a screen.
To me, it seems to be the easiest way to procedurally draw an image at some position from the first to the last pixel (you don't need to precalculate it's size).
I don't think that you are alone with your oppinion. But I don't think that there is a standard math coordinate system. Even the very common carthesian coordinate system is incomplete when the arrows that indicate axis direction are missing.
Summary: From the discussion I assume that there is only one coordinate system used by Cairo: x-axis to the right, y-axis down. If one needs a standard math coordinate system (x-axis to the right, y-axis up) one has to preprocess the data.
I have a scene which contains objects located anywhere in space and I'm making a trackball-like interface.
I'd like to make it so that I can move 2 separate sliders to rotate it in x and y axes respectively:
glRotatef(drawRotateY,0.0,1.0f,0);
glRotatef(drawRotateX,1.0f,0.0,0.0);
//draw stuff in space
However, the above code won't work because X rotation will then be dependent on the Y rotation.
How can I achieve this without using gluLookAt()?
Edit:
I'd like to say that my implementation is even simpler than a trackball interface. Basically, if the x slider value is 80 and y slider is 60, rotate vertically 80 degrees and horizontally 60 degrees. I just need to make them independent of each other!
This code should get you started: http://www.cse.ohio-state.edu/~crawfis/Graphics/VirtualTrackball.html
It explains how to implement a virtual trackball using the current mouse position in the GL window.
You could probably use something like this:
Vector v = new Vector(drawRotateX, drawRotateY, 0);
float length = v.length();
v.normalize();
glRotatef(length, v.x, v.y, v.z);
When you say rotate vertically and horizontally, do you mean like an anti-aircraft gun - rotate around the vertical Z axis, to face in a particular compass heading (yaw) and then rotate to a particular elevation (pitch)?
If this is the case, then you just need to do your two rotations in the right order, and all will be well. In this example, you must do the 'pitch' rotation first, and then the 'yaw' rotation. All will work out fine.
If you mean something more complicated (eg. like the 'Cobra' spaceship in Elite) then you will need a more fiddly solution.