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?
setRotation
has impact how coordinates are transformed and you are assuming thy are always same. – Herne