I am working on a custom QGraphicsItem that has two anchor points, and I want to be able to rotate the item around these anchors when the user interacts with them. I have implemented a mousePressEvent and mouseMoveEvent to detect which anchor the user clicked on, set the rotation anchor point, and compute the angle of rotation.
Here is a simplified version of my code:
MyView.h
static constexpr float ANCHOR_RADIUS = 10;
class MyView : public QGraphicsItem {
public:
    MyView(float xPos, float yPos, float width, float height, QGraphicsItem *parent = nullptr)
        : _width(width), _height(height), _viewState(VIEW) {
        setPos(xPos, yPos);
        setFlag(ItemIsMovable);
        auto diameter = 2 * ANCHOR_RADIUS;
        _anchor1.setRect(0, 0, diameter, diameter);
        _anchor2.setRect(width - diameter, 0, diameter, diameter);
    }
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override {
        auto diameter = 2 * ANCHOR_RADIUS;
        _anchor1.setRect(0, 0, diameter, diameter);
        _anchor2.setRect(_width - diameter, 0, diameter, diameter);
        // Pin 1 and 2 coordinate
        auto c1 = _anchor1.center();
        auto c2 = _anchor2.center();
        painter->drawLine(static_cast<int> (c1.x()), static_cast<int>(c1.y()),
                          static_cast<int>(c2.x()), static_cast<int>(c2.y()));
        painter->drawRect(boundingRect());
        painter->drawEllipse(_anchor1);
        painter->drawEllipse(_anchor2);
    }
    [[nodiscard]] QRectF boundingRect() const override {
        return {0, 0, static_cast<qreal>(_width), static_cast<qreal>(_height)};
    }
    enum ViewState {
        ANCHOR1, ANCHOR2, VIEW
    };
protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
        _tapPoint = event->pos();
        auto cp1 = _anchor1.center(); // get center point of anchor 1
        auto cp2 = _anchor2.center(); // get center point of anchor 2
        // Anchor 1 clicked
        if (_anchor1.contains(_tapPoint)) {
            setTransformOriginPoint(cp2.x(), cp2.y()); // set rotation anchor to anchor 2
            _viewState = ANCHOR1;
        }
            // Anchor 2 clicked
        else if (_anchor2.contains(_tapPoint)) {
            setTransformOriginPoint(cp1.x(), cp1.y()); // set rotation anchor to anchor 1
            _viewState = ANCHOR2;
        }
            // View clicked
        else {
            QGraphicsItem::mousePressEvent(event);
            _viewState = VIEW;
        }
    }
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override {
        auto p = event->pos();
        switch (_viewState) {
            case ANCHOR1: {
                // calculate the angle of the rotation based on the mouse touch
                auto angle = qRadiansToDegrees(qAtan2(p.y() - _anchor2.y(), _width));
                setRotation(rotation() - angle); // rotate the item around anchor 2
                break;
            }
            case ANCHOR2: {
                // calculate the angle of the rotation based on the mouse touch
                auto angle = qRadiansToDegrees(qAtan2(p.y() - _anchor1.y(), _width));
                setRotation(rotation() + angle); // rotate the item around anchor 1
                break;
            }
            default:
                QGraphicsItem::mouseMoveEvent(event); // move the item normally
        }
    }
private:
    float _width, _height;
    QRectF _anchor1, _anchor2;
    QPointF _tapPoint;
    ViewState _viewState;
};
main.cpp
static constexpr int WIDTH = 500;
static constexpr int HEIGHT = 500;
int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    QGraphicsScene scene;
    scene.setSceneRect(QRectF(0, 0, WIDTH, HEIGHT));
    QGraphicsView view(&scene);
    view.setRenderHint(QPainter::Antialiasing);
    QVBoxLayout layout;
    layout.addWidget(&view);
    QWidget widget;
    widget.setLayout(&layout);
    MyView myView(100, 100, 200, 20);
    scene.addItem(&myView);
    widget.show();
    return QApplication::exec();
}
However, when I try to rotate the item from one anchor point (around the other) and then rotate it again from the other anchor point, it jumps back to the initial position! I am not sure why this is happening.
As you can see in this video, when I first rotate the view it works, but when I try to rotate it from the other anchor, its position jumps to another position!

This is what I am trying to achieve (created with the GeoGebra tool):

The solution needs to be applicable to any shape drawn within the MyView::paint() function, rather than being limited to just a line. Although there is an online solution available here, it only works for a line, and similarly, @kenash0625's solution also only works for a line.
Question: What could be causing this issue, and how can I modify my code to achieve the desired behavior of smoothly rotating around different anchor points?
this one should be applicable to any shape drawn within the MyView::paint() function
I made 2 change to your code
add  call to  QGraphicsItem::setTransformations(const QList<QGraphicsTransform *> &transformations)
change from
auto angle = qRadiansToDegrees(qAtan2(p.y()- _anchor2.y() , _width));
to
auto angle = qRadiansToDegrees(qAtan2( _anchor2.y() -p.y(), _width));
here is edited code:
#include<QGraphicsItem>
#include<QPainter>
#include<QGraphicsSceneMouseEvent>
#include<QGraphicsScene>
#include<QGraphicsView>
#include <QGraphicsRotation>
#include<qmath.h>
static constexpr float ANCHOR_RADIUS = 10;
class MyView : public QGraphicsItem {
public:
    MyView(float xPos, float yPos, float width, float height, QGraphicsItem* parent = nullptr)
        : _width(width), _height(height), _viewState(VIEW) {
        setPos(xPos, yPos);
        setFlag(ItemIsMovable);
        auto diameter = 2 * ANCHOR_RADIUS;
        _anchor1.setRect(0, 0, diameter, diameter);
        _anchor2.setRect(width - diameter, 0, diameter, diameter);
    }
    void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override {
        auto diameter = 2 * ANCHOR_RADIUS;
        _anchor1.setRect(0, 0, diameter, diameter);
        _anchor2.setRect(_width - diameter, 0, diameter, diameter);
        // Pin 1 and 2 coordinate
        auto c1 = _anchor1.center();
        auto c2 = _anchor2.center();
        painter->drawLine(static_cast<int> (c1.x()), static_cast<int>(c1.y()),
            static_cast<int>(c2.x()), static_cast<int>(c2.y()));
        painter->drawRect(boundingRect());
        painter->drawEllipse(_anchor1);
        painter->drawEllipse(_anchor2);
    }
    [[nodiscard]] QRectF boundingRect() const override {
        return { 0, 0, static_cast<qreal>(_width), static_cast<qreal>(_height) };
    }
    enum ViewState {
        ANCHOR1, ANCHOR2, VIEW
    };
protected:
    void mousePressEvent(QGraphicsSceneMouseEvent* event) override {
        _tapPoint = event->pos();
        // Anchor 1 clicked
        if (_anchor1.contains(_tapPoint)) {
            _viewState = ANCHOR1;
        }
        // Anchor 2 clicked
        else if (_anchor2.contains(_tapPoint)) {
            _viewState = ANCHOR2;
        }
        // View clicked
        else {
            QGraphicsItem::mousePressEvent(event);
            _viewState = VIEW;
        }
    }
    void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override {
        auto p = event->pos();
        auto cp1 = _anchor1.center(); // get center point of anchor 1
        auto cp2 = _anchor2.center(); // get center point of anchor 2
        switch (_viewState) {
        case ANCHOR1: {
            // calculate the angle of the rotation based on the mouse touch
            auto angle = qRadiansToDegrees(qAtan2( _anchor2.y() -p.y(), _width));
            QGraphicsRotation* rot = new QGraphicsRotation;
            rot->setOrigin(QVector3D(cp2.x(), cp2.y(), 0));
            rot->setAxis(Qt::ZAxis);
            rot->setAngle(angle);
            _trans.push_back(rot);
            setTransformations(_trans);
            break;
        }
        case ANCHOR2: {
            // calculate the angle of the rotation based on the mouse touch
            auto angle = qRadiansToDegrees(qAtan2(p.y() - _anchor1.y(), _width));
            QGraphicsRotation* rot = new QGraphicsRotation;
            rot->setOrigin(QVector3D(cp1.x(), cp1.y(), 0));
            rot->setAxis(Qt::ZAxis);
            rot->setAngle(angle);
            _trans.push_back(rot);
            setTransformations(_trans);
            break;
        }
        default:
            QGraphicsItem::mouseMoveEvent(event); // move the item normally
        }
    }
private:
    float _width, _height;
    QRectF _anchor1, _anchor2;
    QPointF _tapPoint;
    ViewState _viewState;
    QList<QGraphicsTransform*> _trans;
};
static constexpr int WIDTH = 500;
static constexpr int HEIGHT = 500;
int main7(int argc, char* argv[]) {
    QApplication a(argc, argv);
    QGraphicsScene scene;
    scene.setSceneRect(QRectF(0, 0, WIDTH, HEIGHT));
    QGraphicsView view(&scene);
    view.setRenderHint(QPainter::Antialiasing);
    QVBoxLayout layout;
    layout.addWidget(&view);
    QWidget widget;
    widget.setLayout(&layout);
    MyView myView(100, 100, 200, 20);
    scene.addItem(&myView);
    widget.show();
    return QApplication::exec();
}
#include"FileName.moc"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With