Results 1 to 3 of 3

Thread: QGraphicsItem resize with mouse position and keeping aspect ratio

  1. #1
    Join Date
    Mar 2021
    Posts
    4
    Thanked 1 Time in 1 Post
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default QGraphicsItem resize with mouse position and keeping aspect ratio

    Hello.
    I took the code from the one other forum. This code makes resizeble rectangles. I corrected a little for displaying pictures.
    if anyone knows, tell me how to calculate the position of the MovableCircle so that the initial aspect ratio is preserved.

    I've implemented part of the algorithm for eBottomRight and eTopLeft, but it still works very bad and doesn't work for BottomLeft and TopRight points. I want that it will be look like Krita or PureRef resize behaviour.

    Thanks for any help, regards max

    calc part:
    Qt Code:
    1. void MovableCircle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
    2. {
    3. auto pos = mapToScene(event->pos() + _shiftMouseCoords);
    4.  
    5. qreal xl = (pos.x() == 0) ? .1 : pos.x();
    6. qreal yl = (pos.y() == 0) ? .1 : pos.y();
    7.  
    8. qreal arl = qAbs(xl / yl);
    9.  
    10. if (circlePos_ == eBottomRight) {
    11. if (arl > aspectRatio_) {
    12. pos.setX(yl * aspectRatio_);
    13. } else {
    14. pos.setY(xl / aspectRatio_);
    15.  
    16. }
    17. }
    18.  
    19. if (circlePos_ == eTopLeft) {
    20. LOG_WARNING(logger, "Circle Pos: ", circlePos_, ", ", pos.x(), " ", pos.y());
    21. LOG_WARNING(logger, "Init Aspect Ratio: ", aspectRatio_, ", Current AspectRatio:", arl);
    22. if (arl > aspectRatio_) {
    23. LOG_DEBUG(logger, "> Before: ", pos.x(), ", ", pos.y());
    24. pos.setY(xl / aspectRatio_);
    25. LOG_DEBUG(logger, "> After: ", pos.x(), ", ", pos.y());
    26. } else {
    27. LOG_DEBUG(logger, "< Before: ", pos.x(), ", ", pos.y());
    28. pos.setX(yl * aspectRatio_);
    29. LOG_DEBUG(logger, "< After: ", pos.x(), ", ", pos.y());
    30. }
    31. }
    32.  
    33. setPos(pos);
    34. emit circleMoved();
    35. }
    To copy to clipboard, switch view to plain text mode 


    MovableCircle class:
    Qt Code:
    1. class MovableCircle : public QGraphicsObject
    2. {
    3. Q_OBJECT
    4. public:
    5. enum ECirclePos {
    6. eTopLeft = 0,
    7. eTopRight,
    8. eBottomRight,
    9. eBottomLeft,
    10. };
    11.  
    12. explicit MovableCircle(ECirclePos cp, double ar, QGraphicsItem *parent = 0);
    13.  
    14. private:
    15. QRectF boundingRect() const;
    16. QPainterPath shape() const;
    17. void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
    18. void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    19. void mousePressEvent(QGraphicsSceneMouseEvent *event);
    20. void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
    21. QPointF _shiftMouseCoords;
    22.  
    23. private:
    24. double aspectRatio_;
    25. ECirclePos circlePos_;
    26. signals:
    27. void circleMoved();
    28. };
    29.  
    30.  
    31. MovableCircle::MovableCircle(ECirclePos cp, double ar, QGraphicsItem *parent) :
    32. QGraphicsObject(parent), aspectRatio_(ar), circlePos_(cp)
    33. {
    34. setFlag(ItemClipsToShape, true);
    35. setCursor(QCursor(Qt::PointingHandCursor));
    36. }
    37.  
    38. QRectF MovableCircle::boundingRect() const
    39. {
    40. qreal adjust = 0.5;
    41. return QRectF(-5 - adjust, -5 - adjust,
    42. 10 + adjust, 10 + adjust);
    43. }
    44.  
    45. QPainterPath MovableCircle::shape() const
    46. {
    47. qreal adjust = 0.5;
    48. path.addEllipse(-5 - adjust, -5 - adjust,
    49. 10 + adjust, 10 + adjust);
    50. return path;
    51. }
    52.  
    53. void MovableCircle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    54. {
    55. Q_UNUSED(option);
    56. Q_UNUSED(widget);
    57.  
    58. painter->setBrush(QBrush(QColor(0, 160, 230)));
    59. painter->setPen(QPen(QColor(0, 160, 230)));
    60. painter->drawEllipse(-5, -5, 10, 10);
    61. }
    62.  
    63. void MovableCircle::mousePressEvent(QGraphicsSceneMouseEvent *event)
    64. {
    65. _shiftMouseCoords = this->pos() - mapToScene(event->pos());
    66. this->setCursor(QCursor(Qt::ClosedHandCursor));
    67. }
    68.  
    69. void MovableCircle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
    70. {
    71. auto pos = mapToScene(event->pos() + _shiftMouseCoords);
    72.  
    73. qreal xl = (pos.x() == 0) ? .1 : pos.x();
    74. qreal yl = (pos.y() == 0) ? .1 : pos.y();
    75.  
    76. qreal arl = qAbs(xl / yl);
    77.  
    78. if (circlePos_ == eBottomRight) {
    79. if (arl > aspectRatio_) {
    80. pos.setX(yl * aspectRatio_);
    81. } else {
    82. pos.setY(xl / aspectRatio_);
    83.  
    84. }
    85. }
    86.  
    87. if (circlePos_ == eTopLeft) {
    88. if (arl > aspectRatio_) {
    89. pos.setY(xl / aspectRatio_);
    90. } else {
    91. pos.setX(yl * aspectRatio_);
    92. }
    93. }
    94.  
    95. setPos(pos);
    96. emit circleMoved();
    97. }
    98.  
    99. void MovableCircle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
    100. {
    101. Q_UNUSED(event);
    102. this->setCursor(QCursor(Qt::PointingHandCursor));
    103. }
    To copy to clipboard, switch view to plain text mode 


    MoveItem class:
    Qt Code:
    1. class MoveItem : public QObject, public QGraphicsItem//public QGraphicsObject
    2. {
    3. Q_OBJECT
    4. Q_INTERFACES(QGraphicsItem)
    5. public:
    6. explicit MoveItem(uint64_t& zc, QGraphicsItem *parent = 0);
    7. ~MoveItem();
    8.  
    9. signals:
    10. protected:
    11. QRectF boundingRect() const override;
    12. void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
    13. void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
    14. void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    15. void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
    16. void wheelEvent(QGraphicsSceneWheelEvent *event) override;
    17. void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
    18. void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
    19. void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
    20. private:
    21. QPointF shiftMouseCoords_;
    22. QImage qimage_;
    23. QPixmap pixmap_;
    24. uint64_t& zCounter_;
    25. MovableCircle *_topLeftCircle, *_topRightCircle, *_bottomLeftCircle, *_bottomRightCircle;
    26. QSizeF _size;
    27. QRectF rect_;
    28. public slots:
    29. };
    30.  
    31.  
    32. MoveItem::MoveItem(uint64_t& zc, QGraphicsItem *parent) :
    33. QGraphicsItem(parent), zCounter_(zc)
    34. {
    35. setZValue(zCounter_);
    36. qimage_ = QImage("kot.png");
    37. pixmap_ = QPixmap::fromImage(qimage_);
    38. _size = pixmap_.size();
    39. rect_ = qimage_.rect();
    40.  
    41. setAcceptHoverEvents(true);
    42. setFlag(QGraphicsItem::ItemIsMovable, true);
    43.  
    44. double ar = _size.width() / _size.height();
    45. // Top Left
    46. _topLeftCircle = new MovableCircle(MovableCircle::eTopLeft, ar, this);
    47. _topLeftCircle->setPos(0, 0);
    48. // Top Right
    49. _topRightCircle = new MovableCircle(MovableCircle::eTopRight, ar, this);
    50. _topRightCircle->setPos(_size.width(), 0);
    51. // Bottom Right
    52. _bottomRightCircle = new MovableCircle(MovableCircle::eBottomRight, ar, this);
    53. _bottomRightCircle->setPos(_size.width(), _size.height());
    54. // Bottom Left
    55. _bottomLeftCircle = new MovableCircle(MovableCircle::eBottomLeft, ar, this);
    56. _bottomLeftCircle->setPos(0, _size.height());
    57.  
    58. // Signals
    59. // If a delimiter point has been moved, so force the item to redraw
    60.  
    61. connect(_topLeftCircle, &MovableCircle::circleMoved, this, [this](){
    62. _bottomLeftCircle->setPos( _topLeftCircle->pos().x(), _bottomLeftCircle->pos().y());
    63. _topRightCircle->setPos(_topRightCircle->pos().x(), _topLeftCircle->pos().y());
    64. update(); // force to Repaint
    65. });
    66.  
    67. connect(_topRightCircle, &MovableCircle::circleMoved, this, [this](){
    68. _topLeftCircle->setPos(_topLeftCircle->pos().x(), _topRightCircle->pos().y());
    69. _bottomRightCircle->setPos(_topRightCircle->pos().x(), _bottomRightCircle->pos().y());
    70. update(); // force to Repaint
    71. });
    72.  
    73. connect(_bottomLeftCircle, &MovableCircle::circleMoved, this, [this](){
    74. _topLeftCircle->setPos(_bottomLeftCircle->pos().x(), _topLeftCircle->pos().y());
    75. _bottomRightCircle->setPos(_bottomRightCircle->pos().x(), _bottomLeftCircle->pos().y());
    76. update(); // force to Repaint
    77. });
    78.  
    79. connect(_bottomRightCircle, &MovableCircle::circleMoved, this, [this](){
    80. _bottomLeftCircle->setPos(_bottomLeftCircle->pos().x(), _bottomRightCircle->pos().y());
    81. _topRightCircle->setPos(_bottomRightCircle->pos().x(), _topRightCircle->pos().y());
    82. update(); // force to Repaint
    83. });
    84. }
    85.  
    86. MoveItem::~MoveItem()
    87. {
    88.  
    89. }
    90.  
    91.  
    92. QRectF MoveItem::boundingRect() const
    93. {
    94. qreal distX = sqrt(pow(_topLeftCircle->x() - _topRightCircle->x(),2) +
    95. pow(_topLeftCircle->y() - _topRightCircle->y(),2)); // eucledian distance
    96.  
    97. qreal distY = sqrt(pow(_topLeftCircle->x() - _bottomLeftCircle->x(),2) +
    98. pow(_topLeftCircle->y() - _bottomLeftCircle->y(),2)); // eucledian distance
    99.  
    100.  
    101. return QRectF(qMin(_topLeftCircle->pos().x(), _topRightCircle->pos().x()) ,
    102. qMin(_topLeftCircle->pos().y(), _bottomLeftCircle->pos().y()),
    103. distX, distY);
    104. }
    105.  
    106. void MoveItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    107. {
    108. painter->drawImage(boundingRect(), qimage_);
    109. painter->setPen(QPen(QColor(0, 160, 230),2));
    110. painter->drawRect(boundingRect());
    111. Q_UNUSED(widget);
    112. }
    113.  
    114. void MoveItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
    115. {
    116. this->setPos(mapToScene(event->pos()+ shiftMouseCoords_));
    117. }
    118.  
    119. void MoveItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
    120. {
    121. setZValue(++zCounter_);
    122.  
    123. shiftMouseCoords_ = (this->pos() - mapToScene(event->pos()))/scale();
    124.  
    125. if (event->button() == Qt::LeftButton) {
    126. if (event->modifiers() == Qt::ShiftModifier) {
    127. } else {
    128. QGraphicsItem::mousePressEvent(event);
    129. }
    130. }
    131. }
    132.  
    133. void MoveItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
    134. {
    135. if (event->button() == Qt::LeftButton) {
    136. if (event->modifiers() == Qt::ShiftModifier) {
    137. } else {
    138. QGraphicsItem::mousePressEvent(event);
    139. }
    140. }
    141. }
    To copy to clipboard, switch view to plain text mode 

  2. #2
    Join Date
    Mar 2021
    Posts
    4
    Thanked 1 Time in 1 Post
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: QGraphicsItem resize with mouse position and keeping aspect ratio


  3. #3
    Join Date
    Mar 2021
    Posts
    4
    Thanked 1 Time in 1 Post
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: QGraphicsItem resize with mouse position and keeping aspect ratio

    I found a solution using vector math.(thanks to my colleague Dima Chernikov)

    ABCD - our picture.

    K' - cursor point.

    D2 - the point we are looking for(new position of D)

    gif example: https://media.giphy.com/media/uffXKj...vsR2/giphy.gif

    (circlePos_ == eBottomLeft) in code
    1488.jpg

    code: (I will most likely redo it later using templates. but now it is more clear for understanding)
    Qt Code:
    1. void MovableCircle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
    2. {
    3. auto pos = mapToScene(event->pos() + _shiftMouseCoords);
    4.  
    5. qreal xl = pos.x();
    6. qreal yl = pos.y();
    7. auto rect = parentItem()->boundingRect();
    8.  
    9. QPointF a(rect.x(), rect.y());
    10. QPointF b(rect.x() + rect.width(), rect.y());
    11. QPointF c(rect.x() + rect.width(), rect.y() + rect.height());
    12. QPointF d(rect.x(), rect.y() + rect.height());
    13.  
    14.  
    15. if (circlePos_ == eTopRight) {
    16. Vec2d dc(c.x()-d.x(), c.y()-d.y());
    17. Vec2d cb(b.x()-c.x(), b.y()-c.y());
    18. Vec2d db(b.x()-d.x(), b.y()-d.y());
    19. Vec2d dk(pos.x()-d.x(), pos.y()-d.y());
    20.  
    21. auto dc_len = dc.length();
    22. auto cb_len = cb.length();
    23. auto db_len = db.length();
    24. auto dk_len = dk.length();
    25.  
    26. auto dkdb_dot = Vec2d<qreal>::dot(db, dk);
    27. auto cos_kdb = dkdb_dot/(db_len*dk_len);
    28. auto dd2_len = dk_len * cos_kdb;
    29.  
    30. auto x =(dd2_len * dc_len) / (std::sqrt(cb_len * cb_len + dc_len * dc_len));
    31. auto y = std::sqrt(dd2_len * dd2_len - x * x);
    32.  
    33. if (x < 10 || y < 10) return;
    34. pos.setX(d.x()+x);
    35. pos.setY(d.y()-y);
    36. }
    37.  
    38. if (circlePos_ == eBottomRight) {
    39. Vec2d ad(d.x()-a.x(), d.y()-a.y());
    40. Vec2d dc(c.x()-d.x(), c.y()-d.y());
    41. Vec2d ac(c.x()-a.x(), c.y()-a.y());
    42. Vec2d ak(pos.x()-a.x(), pos.y()-a.y());
    43.  
    44. auto ad_len = ad.length();
    45. auto dc_len = dc.length();
    46. auto ac_len = ac.length();
    47. auto ak_len = ak.length();
    48.  
    49. auto akac_dot = Vec2d<qreal>::dot(ac, ak);
    50. auto cos_kac = akac_dot/(ac_len*ak_len);
    51. auto ad2_len = ak_len * cos_kac;
    52.  
    53. auto x =(ad2_len * dc_len) / (std::sqrt(ad_len * ad_len + dc_len * dc_len));
    54. auto y = std::sqrt(ad2_len * ad2_len - x * x);
    55.  
    56. if (x < 10 || y < 10) return;
    57. pos.setX(a.x()+x);
    58. pos.setY(a.y()+y);
    59. }
    60.  
    61. if (circlePos_ == eTopLeft) {
    62. qDebug()<<this->parentItem()->boundingRect();
    63. Vec2d cb(b.x()-c.x(), b.y()-c.y());
    64. Vec2d ba(a.x()-b.x(), a.y()-b.y());
    65. Vec2d ca(a.x()-c.x(), a.y()-c.y());
    66. Vec2d ck(pos.x()-c.x(), pos.y()-c.y());
    67.  
    68. auto cb_len = cb.length();
    69. auto ba_len = ba.length();
    70. auto ca_len = ca.length();
    71. auto ck_len = ck.length();
    72.  
    73. auto ckca_dot = Vec2d<qreal>::dot(ca, ck);
    74. auto cos_kca = ckca_dot/(ca_len*ck_len);
    75. auto cd2_len = ck_len * cos_kca;
    76.  
    77. auto y =(cd2_len * cb_len) / (std::sqrt(ba_len * ba_len + cb_len * cb_len));
    78. auto x = std::sqrt(cd2_len * cd2_len - y * y);
    79.  
    80. if (x < 10 || y < 10) return;
    81. pos.setX(c.x()-x);
    82. pos.setY(c.y()-y);
    83. }
    84.  
    85. if (circlePos_ == eBottomLeft) {
    86. qDebug()<<this->parentItem()->boundingRect();
    87. Vec2d ba(a.x()-b.x(), a.y()-b.y());
    88. Vec2d ad(d.x()-a.x(), d.y()-a.y());
    89. Vec2d bd(d.x()-b.x(), d.y()-b.y());
    90. Vec2d bk(pos.x()-b.x(), pos.y()-b.y());
    91.  
    92. auto ba_len = ba.length();
    93. auto ad_len = ad.length();
    94. auto bd_len = bd.length();
    95. auto bk_len = bk.length();
    96.  
    97. auto bkbd_dot = Vec2d<qreal>::dot(bd, bk);
    98. auto cos_kdb = bkbd_dot/(bd_len*bk_len);
    99. auto bd2_len = bk_len * cos_kdb;
    100.  
    101. auto x =(bd2_len * ba_len) / (std::sqrt(ad_len * ad_len + ba_len * ba_len));
    102. auto y = std::sqrt(bd2_len * bd2_len - x * x);
    103.  
    104. if (x < 10 || y < 10) return;
    105. pos.setX(b.x()-x);
    106. pos.setY(b.y()+y);
    107. }
    108.  
    109. setPos(pos);
    110. emit circleMoved();
    111. }
    To copy to clipboard, switch view to plain text mode 

  4. The following user says thank you to angeleyes for this useful post:

    d_stranz (27th March 2021)

Similar Threads

  1. Replies: 2
    Last Post: 17th June 2020, 23:18
  2. Replies: 3
    Last Post: 2nd September 2016, 13:44
  3. Replies: 3
    Last Post: 27th May 2015, 00:14
  4. Replies: 2
    Last Post: 7th December 2013, 20:02
  5. keeping aspect ratio while resizing
    By franco.amato in forum Qt Programming
    Replies: 2
    Last Post: 19th November 2009, 21:12

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.