1

I need to position a top-level object so that it always remains in a position relative to another top-level object. As an example, the rectangle in the image below should stick to the "front" of the ellipse:

correct positioning example

When rotated 180 degrees, it should look like this:

correct positioning example rotated 180 degrees

Instead, the position of the rectangle is incorrect:

incorrect positioning

Please run the example below (the use of QGraphicsScene is for demonstration purposes only, as the actual use case is in physics).

#include <QtWidgets>

class Scene : public QGraphicsScene
{
    Q_OBJECT
public:
    Scene()
    {
        mEllipse = addEllipse(0, 0, 25, 25);
        mEllipse->setTransformOriginPoint(QPointF(12.5, 12.5));

        QGraphicsLineItem *line = new QGraphicsLineItem(QLineF(0, 0, 0, -12.5), mEllipse);
        line->setPos(12.5, 12.5);

        mRect = addRect(0, 0, 10, 10);
        mRect->setTransformOriginPoint(QPointF(5, 5));

        line = new QGraphicsLineItem(QLineF(0, 0, 0, -5), mRect);
        line->setPos(5, 5);

        connect(&mTimer, SIGNAL(timeout()), this, SLOT(timeout()));
        mTimer.start(5);
    }

public slots:
    void timeout()
    {
        mEllipse->setRotation(mEllipse->rotation() + 0.5);

        QTransform t;
        t.rotate(mEllipse->rotation());

        qreal relativeX = mEllipse->boundingRect().width() / 2 - mRect->boundingRect().width() / 2;
        qreal relativeY = -mRect->boundingRect().height();

        mRect->setPos(mEllipse->pos() + t.map(QPointF(relativeX, relativeY)));
        mRect->setRotation(mEllipse->rotation());
    }

public:
    QTimer mTimer;
    QGraphicsEllipseItem *mEllipse;
    QGraphicsRectItem *mRect;
};

int main(int argc, char** argv)
{
    QApplication app(argc, argv);

    QGraphicsView view;
    view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    view.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    view.setScene(new Scene);
    view.resize(200, 200);
    view.show();

    return app.exec();
}

#include "main.moc"

Note that the position of the rectangle is not always the same, but it should always remain in the same position relative to the ellipse. For example, it may start off in this position:

alternative positioning example

But it should stay in that relative position when rotated:

alternative positioning example rotated 180 degrees

Mitch
  • 23,716
  • 9
  • 83
  • 122

1 Answers1

2

If you want the two objects to keep the same relative position, they need to rotate around the same origin point.

Here your circle rotates around its center (the point 12.5, 12.5), but your rectangle rotates around another origin (5,5) instead of the circle's center (12.5, 12.5).

If you fix the origin, it'll work as you expect:

mRect->setTransformOriginPoint(QPointF(12.5, 12.5));

Even if the rectangle starts off with an offset:

mRect = addRect(-10, 0, 10, 10); // Start 10 units to the left

Screenshot of the result

tux3
  • 7,171
  • 6
  • 39
  • 51
  • Thanks for the quick response. I mentioned in my question that I won't be using `QGraphicsScene` in the actual code. My physics objects do have a position and rotation though, so that still applies. With this considered, can you come up with a solution that doesn't use `setTransformOriginPoint()`, and just `QPointF`/`QTransform`, etc.? Nice GIF, by the way. How did you make it? – Mitch Jun 14 '15 at 09:32
  • I don't know what your real code is using, so I'm not sure what kind of solution you'd want. But [the principle is the same](https://en.wikipedia.org/wiki/Rotation_%28mathematics%29#Two_dimensions), you just need to do your rotations around the same origin point. And the gif is made with [byzanz](https://github.com/GNOME/byzanz). – tux3 Jun 14 '15 at 09:36
  • I was thinking the same approach just without `setTransformOriginPoint()`; as long as the solution works with only `position()` and `rotation()`, it's general enough to work with my physics stuff. If that's not possible, I can probably figure something out using your answer. – Mitch Jun 14 '15 at 09:56
  • 1
    The formula for a rotation is pretty simple, but there's some code you can adapt directly [here](http://stackoverflow.com/questions/3162643/proper-trigonometry-for-rotating-a-point-around-the-origin) if you're not too fond of math. Fundamentally, you can do everything yourself with `position()` and `rotation()` if you just follow the formula (`x'=xcos(a)-ysin(a)` and `y'=xsin(a)+ycos(a)`). – tux3 Jun 14 '15 at 10:03