Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QGraphicsItem prevent overlap when moved by mouse
Forum Update on Monday, May 27th 2025

QGraphicsItem prevent overlap when moved by mouse

Scheduled Pinned Locked Moved Solved General and Desktop
qgraphicsitemqgraphicsviewgraphicscollisionsoverlap
2 Posts 1 Posters 3.1k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • V Offline
    V Offline
    Vagabond
    wrote on 21 Oct 2016, 13:46 last edited by
    #1

    Hey everyone,

    I am setting up a simple graphics app, in which I have several rects being freely moved around the scene. Now, I want to absolutely prevent these rects from overlapping each other

    My current implementation includes a collision evaluation inside the mouse mouseMoveEvent:

    void CustomGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent* e) {
        QPointF p = pos();
        QGraphicsItem::mouseMoveEvent(e);
        QPointF p_new = pos();
    
        if(collidingItems(Qt::IntersectsItemBoundingRect).size() > 0) {
             setPos(p.x(),p_new.y());
            if(collidingItems(Qt::IntersectsItemBoundingRect).size() > 0) {
                setPos(p_new.x(), p.y());
                if(collidingItems(Qt::IntersectsItemBoundingRect).size() > 0)
                    setPos(p);
            }
        }
    }
    

    This works ok. The issue I am facing is, that when I move the item really fast it wont stop at the intersected items boundingbox, but a few units too early.
    I guess this is becuase the mouseMove event is only triggered at a certain rate.

    Is there a more precise solution to what I am trying to achieve?

    Cheers

    1 Reply Last reply
    0
    • V Offline
      V Offline
      Vagabond
      wrote on 21 Oct 2016, 16:25 last edited by
      #2

      Okay, I have come up with a solution. I have setup a better handling for the hittest evaluation. I first check which side of the intersected boundingbox the moved item is closest to. Then I set the x, or y coordinate accordingly. I added some helper functions for this, to compute point-line distance and closest side of a rect to a point. They are all included in the snippet below.

      NOTE: the collision test will also evaluate and prohibit the item to exceed the scene bounds. It will also only solve, overlapping one other item. If the border test results in another overlap I simply set the position back to where it was prior to mouse move. Works a lot nicer than my initial implementation though.

      enum CustomGraphicsItem::BOX_SIDE {
          LEFT,
          RIGHT,
          UPPER,
          LOWER
      };
      
      void CustomGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent* e)
      {
          if(mode_ == MOVE) {
              QPointF p = pos();
              QGraphicsItem::mouseMoveEvent(e);
              QPointF p_new = pos();
      
              QList<QGraphicsItem*> col_it = collidingItems(Qt::IntersectsItemBoundingRect);
              if(col_it.size() > 0) {
                  qreal x_min = col_it[0]->pos().x() - boundingRect().width();
                  qreal x_max = col_it[0]->pos().x() + col_it[0]->boundingRect().width();
                  qreal y_min = col_it[0]->pos().y() - boundingRect().height();
                  qreal y_max = col_it[0]->pos().y() + col_it[0]->boundingRect().height();
      
                  QRectF rect(QPointF(x_min, y_min), QPointF(x_max, y_max));
      
                  switch(closestSide(p_new, rect)) {
                      case LEFT:
                          p_new.setX(x_min);
                          break;
                      case RIGHT:
                          p_new.setX(x_max);
                          break;
                      case UPPER:
                          p_new.setY(y_min);
                          break;
                      case LOWER:
                          p_new.setY(y_max);
                          break;
                  }
                  setPos(p_new);
              }
      
              // check if item in scene bounds
              qreal max_x = scene()->width() - boundingRect().width();
              qreal max_y = scene()->height() - boundingRect().height();
              if (x() < 0)
                  setPos(0, y());
              else if (x() > max_x)
                  setPos(max_x, y());
      
              if (y() < 0)
                  setPos(x(), 0);
              else if (y() > max_y)
                  setPos(x(), max_y);
      
              // if still colliding set pos back to start
              col_it = collidingItems(Qt::IntersectsItemBoundingRect);
              if(col_it.size() > 0)
                  setPos(p);
          }
      }
      
      qreal CustomGraphicsItem::distance(const QPointF &p, const QLineF &l)
      {
          QPointF p1 = l.p1();
          QPointF p2 = l.p2();
          qreal x = p.x() - p1.x();
          qreal y = p.y() - p1.y();
          qreal x2 = p2.x() - p1.x();
          qreal y2 = p2.y() - p1.y();
      
          // if line is a point, return distance between point and one line node
          qreal norm = sqrt(x2*x2 + y2*y2);
          if (norm <= std::numeric_limits<int>::epsilon())
            return sqrt(x*x + y*y);
      
          // distance
          return fabs(x*y2 - y*x2) / norm;
      }
      
      CustomGraphicsItem::BOX_SIDE CustomGraphicsItem::closestSide(const QPointF &p, const QRectF &rect)
      {
          qreal x_min = rect.x();
          qreal x_max = rect.x() + rect.width();
          qreal y_min = rect.y();
          qreal y_max = rect.y() + rect.height();
          qreal temp_dist = 0;
      
          // left
          QLineF l(QPointF(x_min, y_min), QPointF(x_min, y_max));
          qreal min_dist = distance(p,l);
          BOX_SIDE side = LEFT;
      
          // right
          l.setPoints(QPointF(x_max,y_min), QPointF(x_max, y_max));
          temp_dist = distance(p,l);
          if(temp_dist < min_dist) {
              min_dist = temp_dist;
              side = RIGHT;
          }
      
          // upper
          l.setPoints(QPointF(x_min, y_min), QPointF(x_max, y_min));
          temp_dist = distance(p,l);
          if(temp_dist < min_dist) {
              min_dist = temp_dist;
              side = UPPER;
          }
      
          // lower
          l.setPoints(QPointF(x_min, y_max), QPointF(x_max, y_max));
          temp_dist = distance(p,l);
          if(temp_dist < min_dist) {
              min_dist = temp_dist;
              side = LOWER;
          }
      
          return side;
      }
      
      1 Reply Last reply
      0

      2/2

      21 Oct 2016, 16:25

      • Login

      • Login or register to search.
      2 out of 2
      • First post
        2/2
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups
      • Search
      • Get Qt Extensions
      • Unsolved