PDA

View Full Version : possible qt 4.5 bug?



nicodega
14th April 2009, 15:49
Hi, I'de like to tell you about something I found out yesterday with Qt 4.5 (on windows), and as I'm not a graphics guru, I'd like to hear what you think about it.. if I should report it as a bug to Qt, or if you think it's not a bug.

here it goes:

If you create a QGraphicsItem, rotate it and then you use setPos, if you try to map the bounding rectangle to it's parent, it won't be translated correctly.

Example: (the dots are missing code, but you can get the idea)


class MyQGraphicsItem : public QGraphicsPixmapItem
{
public:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QGraphicsPixmapItem::paint(painter, option, widget);
// this item has not paintable region
if(childItems().length() != 0)
{
painter->drawRect(childItems().at(0)->mapRectToParent(childItems().at(0)->boundingRect()));
}
}
};

MyQGraphicsItem *parentItem = new QGraphicsPixmapItem(...);
QGraphicsPixmapItem *childItem = new QGraphicsPixmapItem(parentItem,.....);

childItem->rotate(180);
childItem->setPos(0,200);
parentItem->setPos(100,100);

On the example, the drawn rectangle will clearly not be as it is supposed to. (it will have the right size and rotation, but the x and y possitions will be wrong)

Now, if we look at mapRectToParent code, it'll first translate the Rect, and then apply the transformation. If the order is changed (i.e firts transform then move to the item possition) It'll be drawn where it's supposed to.

Defining MyGraphicsItem class paint method this way, fixes the problem, and the rectangle is drawn properly:


void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QGraphicsPixmapItem::paint(painter, option, widget);
// this item has not paintable region
if(childItems().length() != 0)
{
QRectF r = childItems().at(0)->transform().mapRect(childItems().at(0)->boundingRect());
r = r.translated(childItems().at(0)->x(), childItems().at(0)->y());
painter->drawRect(r);
}
}

I found this, because I noticed when one of my items was rotated (only transform I tried) it would dissapear from screen sometimes when moving it around. (I've implemented a custom drag method but in the end all it does is use setPos).

After hours of trying to understand the inner workings of Qt, I found the child item boundingRect sometimes did not intersect the invalidated region, although it was supposed to, and it led me to check if the mapping was being properly done.

Well... what dou you think? I sure looks like a bug to me hehe, but should I report it to the trolls?

wysota
14th April 2009, 23:08
Could you post a minimal compilable code reproducing what you mean? It's hard to argue with this code but I'm lazy enough not to write a whole application around it.

nicodega
15th April 2009, 01:30
Could you post a minimal compilable code reproducing what you mean? It's hard to argue with this code but I'm lazy enough not to write a whole application around it.

I'm too lazy to do it too hehe, but thanks for te interest anyway.. after thinking about it, what mapRectToParent is actually doing is conceptually wrong, because the rotation is done on the (0,0) position, and translating the Rect before it's rotated will clearly be wrong.

What it should do is apply the transformation, and then move it to the possition of the item on the parent... that way the rotation will be applied correctly, and also the translation.

This bug is only present on the mapToParent function which takes a Polygon too, as on the other functions they've done it using transformations (multiply the item transform matrix with a matrix created for the possition translation (on the right), achieving the effect of rotating, and then moving it, which is what it should also do on mapRectToParent.

I've already reported it as a bug on the qt Task Tracker. :)

wysota
15th April 2009, 08:42
All transformations are done around the origin so if I understand correctly this is an expected result. If you want to map around different point, move the origin by redefining the bounding rect of items. But I'm not sure what you mean and without seeing it myself it is hard to argument if it's right or wrong.

nicodega
15th April 2009, 16:03
All I want is to map a Rect from a child Item, to it's parent. I used the bounding rect to test it.

say you have a Rect on the child coordinates and the child is rotated by 75 degrees, and moved (not using the transformation but by using setPos) to (100,100).

What mapRectToParent is supposed to do, is to map the rect coordinates from it's child to it's parent, the result should be a a rectangle at (100,100), rotated by 75 degrees on the parent coordinates space.

If B is the translation matrix for (100,100), and A is the rotation matrix on the (0,0) by 75 deg:

The current implementation does something like this:

C = B * A;

And uses C to map the rectangle. Since B is at the left of the multiplication, the rectangle will be first translated to (100,100), and then rotated by 75 degrees using (0,0) as the pivot.

What it should do, is:

C = A * B;

As matrix multiplication is not symetric, when applying this transformation, the rect will be first rotated by 75 degrees on (0,0), and then translated.

EDIT: I've attached an example you can compile.

wysota
15th April 2009, 17:19
A rectangle is never rotated. It would be a polygon then. A rectangle is always upright. The result is a bounding rectangle of a polygon that is created as a result of the transformation. So if you have a child that is rotated, the mapped rectangle will always be bigger than the original one. To test the correctness of the mapping it is easiest to compare sceneBoundingRect() of the item with the actual position of the item in the view. sceneBoundingRect() will map the rectangle through all the parents until it reaches the scene (at least I suppose so, you can verify that in the source code) so if mapping to parent is incorrect, we'd get an incorrect scene bounding rect (provided the item has a parent). If the sceneBoundingRect() indeed differs from what you would expect then either your expectations are wrong or the mapping is incorrect.

nicodega
15th April 2009, 17:35
I hadn't noticed that way of testing it hehe, if you replace the paint method on the inherited class to this:



void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QGraphicsPixmapItem::paint(painter, option, widget);
// this item has not paintable region
if(childItems().length() != 0)
{
painter->drawRect(childItems().at(0)->mapRectToParent(childItems().at(0)->boundingRect()));

QRectF r = mapRectFromScene(childItems().at(0)->mapRectToScene(childItems().at(0)->boundingRect()));
painter->drawRect(r);
}
}


You get two different rectangles, while you should get the same ones :), so indeed mapRectToParent is doing things wrong.

(if you add the mapped to scene paint on the corrected mapping paint function from the first post, you only get one rect, which is what you should get).

Thanks :)