boundingRect and boundingRegion oddness during rotation
-
I have been doing a lot of testing with rotation of a basic triangle in a QGraphicsPolygonItem. The boundingRect() of a QGraphicsPolygonItem does not "bound" when it is rotated all the time. Oddly, if you draw the boundingRect() of the QGraphicsPolygonItem it will create space to the upper right corner where it is not needed. The same is true for the boundingRegion. However, if I get the boundingRect() of the polygon(), all is well. But this is not good when you use setPos() or the wonderful override of itemChanged().
If anyone has a solution for this, I will gladly appreciate it.
Craig Bakalian
-
Well, I guess overriding the boundingRect() is the solution. I hope it works! Why I have to do this is beyond me.
-
Okay, it appears that I am talking to myself about this wonderful issue, that really is a bug, and it is a lousy bug.
When you rotate an QGraphicsItem it must adjust its boundingRect() to fulfill the shape's coordinates so that the shape does not get clipped. And, yes indeed the boundRect() does update its size to fulfill the requirements of that shape; the robot called Qt does this, not very well, but it does it. However, what does not get updated is the pos(), so the upper left side of the boundingRect() does not equal the pos(). If a QGraphicsItem is being dragged around a QGraphicsScene without being rotated, all is well, however, after being rotated its pos() checking is incorrect. Furthermore there is no way to correct the pos() without using setPos() to the boundingRect().topLeft() which causes the robot called Qt to go crazy.
Anyone out there? Please help me.
Craig Bakalian
-
Well, no. I don't have a web site or a cloud to post files. Can you imagine this, I am checking the polygon boundingRect() to restrain it to a QGraphicsScene. The QGraphicsPolygonItem is restrained to the scene if there is no rotation, however, after rotating the item the piece is not restrained properly; too much restraint, or not enough restraint. After hours of debugging I found that the->
QVariant PuzzlePiece::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) { if (change == ItemPositionChange && scene()) { QPointF newPos = value.toPointF(); QRectF rect = scene()->sceneRect(); //tangramShape = mapToScene(polygon()); used to debug position of item, but now I use it to keep proper scene position. //setTangramPosition(pos()); used to debug position of item //must bound the width of the item to the right and bottom of scene rect.setWidth(rect.width() - boundingRect().width()); rect.setHeight(rect.height() - boundingRect().height()); if (!rect.contains(newPos)) { newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left()))); newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top()))); return newPos; } } if (change == ItemRotationChange) { //Santa Claus can tell me what to put in here! } return QGraphicsItem::itemChange(change, value); }
very important value.toPointF(); brings into the itemChanged override a QPointF that is from the "non rotated" boundingRect().topLeft(). I know this is complex. I know this is a bug. The question is, is there a way around it? Possibly with a QTransform?
Craig Bakalian
-
Sorry, my English is bad, but I looked probably understood your question.After first you create the polygon, needs to adjust Item the local coordinate.Usually the local coordinate's origin point is the Item center .I have a method to be possible adjust Item pos and the origin point to the Item center.Did not know that can solve your problem.
QPointF pt1,pt2,delta; QPolygonF pts = mapToScene(m_points); { pt1 = mapToScene(transformOriginPoint()); pt2 = mapToScene(boundingRect().center()); delta = pt1 - pt2; for (int i = 0; i < pts.count() ; ++i ) pts[i]+=delta; prepareGeometryChange(); m_points = mapFromScene(pts); setTransform(transform().translate(delta.x(),delta.y())); setTransformOriginPoint(boundingRect().center()); moveBy(-delta.x(),-delta.y()); setTransform(transform().translate(-delta.x(),-delta.y())); }
-
@egan
I don't know if this will work. I do have the transformOriginPoint() set to the center of the item creating a normal looking rotate. But, the transformOriginPoint is specifically for rotation. I don't think I can delta from the transformOriginPoint() in the itemChanged override, I think weird stuff will happen. But, I get the translate thing. That might work. But, then it will look weird when rotating. I gotta eat lunch first. Go for a bicycle ride and think more. -
Wait! I get it. This might work. Does the code you have given create a large square around the shape and place the polygon in the middle so that the polygon's rotation does not effect the boundingRect()? If so this may actually work! Now I gotta implement this. Crossing fingers. Actually, I never knew of the boundingRect().center() function. That may solve the whole issue. Thank you!
-
QRectF PolygonItem::boundingRect() const
{
return shape().controlPointRect();
}QPainterPath PolygonItem::shape() const
{
QPainterPath path;
path.addPolygon(m_points);
path.closeSubpath();
return path;
}
void PolygonItem::updateCoordinate()
{QPointF pt1,pt2,delta; QPolygonF pts = mapToScene(m_points); if (parentItem()==NULL) { pt1 = mapToScene(transformOriginPoint()); pt2 = mapToScene(boundingRect().center()); delta = pt1 - pt2; for (int i = 0; i < pts.count() ; ++i ) pts[i]+=delta; prepareGeometryChange(); m_points = mapFromScene(pts); setTransform(transform().translate(delta.x(),delta.y())); setTransformOriginPoint(boundingRect().center()); moveBy(-delta.x(),-delta.y()); setTransform(transform().translate(-delta.x(),-delta.y())); }
}
The updateCoordinate() can called after you create or ratotion polygons.
-
Well, I re-implemented the whole mouse moving the puzzle shape thing. I am not using itemChanged() override anymore. The fact that the item's position x and y was not the same as the boundingRect().topLeft() x and y after rotating was an issue that was too problematic. A tangram shape; a right triangle with a hypotenuse that equaled the leg * the square root of 2 being contained by a bounding rectangle during rotation produced too many effects. So, I got it working, and very well, but I ended up using
void PuzzlePiece::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { QRectF rect = scene()->sceneRect(); float mx = event->scenePos().x() - xbeta; float my = event->scenePos().y() - ybeta; setTangramPositionAndShape(); \\ sets the boundingRect to the bounds of the actual polygon points if(rect.contains(boundingRect())) { setPos(mx, my); setTangramPositionAndShape(); } }
And yes, the shape stops moving when it goes out of the scene, but a mouseDown sets it position back inside the scene and all is good.
I made a tangram puzzle application way back when I was a young man. I used Cocoa/Objective C on a Mac. The thing was downloaded over 8 million times in a week. People from Japan would call me up and ask me about it. It was crazy. And yeah, it was free, so I am not a millionaire; my wife yelled at me for weeks for making it free. It was this small window about the size of an smart phone screen.
Cocoa does not have a simple implementation to make an graphic item movable with some easy setFlag(QGraphicsItem::ItemIsMovable). It was much more difficult to implement back then. So, I am basically using the same bounds checking and resetting the object the same way as I did 25 years ago.
My son is about to go to college for software engineering. I am teaching C++ with the implementation of this simple puzzle.
Thanks for your help. Your posts kept me thinking!
Craig