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. How to deal with several transformations over the same QGraphicsItem?
Forum Updated to NodeBB v4.3 + New Features

How to deal with several transformations over the same QGraphicsItem?

Scheduled Pinned Locked Moved Solved General and Desktop
12 Posts 5 Posters 4.0k Views 4 Watching
  • 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.
  • X Offline
    X Offline
    xtingray
    wrote on last edited by
    #1

    Hi!
    I am working on a basic QGraphicsItem's editor. Currently I am supporting only two operations: rotation and scale.
    This is the code of my implementation:

    void rotateItem(float angle)
    {
        QTransform transform = item->transform();
        transform.rotate(current_angle  - angle);
        item->setTransform(transform);
    }
    void scaleItem(float factorX, float factorY)
    {
        QTransform transform;
        transform.scale(factorX, factorY);
        item->setTransform(transform, true);
    }
    

    If I only use one of these transformations over the same item several times with different values, the result is accurate. My problem occurs when I start mixing both transformations. For example, if I rotate an item 180 degrees and then I try to scale it (or vice versa), its shape becomes into a kind of distorted figure (sometimes the item becomes a flat line). The result gets even worst as I try to apply more transformations of these types.

    As far as I understand, every item transformation is applied over the previous one, so I wonder if there is an accurate way to handle several transformations with the same item without getting distorted results like this:
    http://www.maefloresta.com/portal/files/bug.png

    Any advice is very welcome, thank you! :)


    Qt Developer

    kshegunovK 1 Reply Last reply
    0
    • X xtingray

      Hi!
      I am working on a basic QGraphicsItem's editor. Currently I am supporting only two operations: rotation and scale.
      This is the code of my implementation:

      void rotateItem(float angle)
      {
          QTransform transform = item->transform();
          transform.rotate(current_angle  - angle);
          item->setTransform(transform);
      }
      void scaleItem(float factorX, float factorY)
      {
          QTransform transform;
          transform.scale(factorX, factorY);
          item->setTransform(transform, true);
      }
      

      If I only use one of these transformations over the same item several times with different values, the result is accurate. My problem occurs when I start mixing both transformations. For example, if I rotate an item 180 degrees and then I try to scale it (or vice versa), its shape becomes into a kind of distorted figure (sometimes the item becomes a flat line). The result gets even worst as I try to apply more transformations of these types.

      As far as I understand, every item transformation is applied over the previous one, so I wonder if there is an accurate way to handle several transformations with the same item without getting distorted results like this:
      http://www.maefloresta.com/portal/files/bug.png

      Any advice is very welcome, thank you! :)

      kshegunovK Offline
      kshegunovK Offline
      kshegunov
      Moderators
      wrote on last edited by kshegunov
      #2

      @xtingray
      Hi,

      void scaleItem(float factorX, float factorY)
      {
          QTransform transform;
          transform.scale(factorX, factorY);
          item->setTransform(transform, true);
      }
      

      Shouldn't this be:

      void scaleItem(float factorX, float factorY)
      {
          QTransform transform = item->transform(); //< Aren't you applying a transformation over the existing one?
          transform.scale(factorX, factorY);
          item->setTransform(transform, true);
      }
      

      Also, consider using QGraphicsItem::setRotation and QGraphicsItem::setScale instead.

      Kind regards.

      Read and abide by the Qt Code of Conduct

      raven-worxR 1 Reply Last reply
      0
      • kshegunovK kshegunov

        @xtingray
        Hi,

        void scaleItem(float factorX, float factorY)
        {
            QTransform transform;
            transform.scale(factorX, factorY);
            item->setTransform(transform, true);
        }
        

        Shouldn't this be:

        void scaleItem(float factorX, float factorY)
        {
            QTransform transform = item->transform(); //< Aren't you applying a transformation over the existing one?
            transform.scale(factorX, factorY);
            item->setTransform(transform, true);
        }
        

        Also, consider using QGraphicsItem::setRotation and QGraphicsItem::setScale instead.

        Kind regards.

        raven-worxR Offline
        raven-worxR Offline
        raven-worx
        Moderators
        wrote on last edited by
        #3

        @kshegunov
        thats what the boolean parameter of setTransform should already do.

        @xtingray
        i also struggle mostly with the QTransfrom.

        What i then try is to store the values and reapply the whole transformation again.
        Where i simply chain the operations.

        item->resetTransform();
        item->setTransform( QTransform().translate(centerX, centerY).rotate( angle).translate(-centerX,-centerY) );
        

        --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
        If you have a question please use the forum so others can benefit from the solution in the future

        kshegunovK 1 Reply Last reply
        0
        • raven-worxR raven-worx

          @kshegunov
          thats what the boolean parameter of setTransform should already do.

          @xtingray
          i also struggle mostly with the QTransfrom.

          What i then try is to store the values and reapply the whole transformation again.
          Where i simply chain the operations.

          item->resetTransform();
          item->setTransform( QTransform().translate(centerX, centerY).rotate( angle).translate(-centerX,-centerY) );
          
          kshegunovK Offline
          kshegunovK Offline
          kshegunov
          Moderators
          wrote on last edited by
          #4

          @raven-worx
          Yes, you're right.

          @xtingray
          You can disregard my babbling ...

          Read and abide by the Qt Code of Conduct

          1 Reply Last reply
          0
          • ? Offline
            ? Offline
            A Former User
            wrote on last edited by
            #5

            Hi! I made some tests and I'm not able to reproduce this. Can you confirm that the following minimal code works for you:

            void MainWindow::on_pushButton_clicked()
            {
                QGraphicsScene *scene = new QGraphicsScene(ui->graphicsView);
                ui->graphicsView->setScene(scene);
                QGraphicsPixmapItem *item = new QGraphicsPixmapItem;
                QPixmap pm("/home/pw/Downloads/rune.png");
                item->setPixmap(pm);
                scene->addItem(item);
            
                // rotate
                {
                    const qreal angle = 180;
                    QTransform transform;
                    transform.rotate(angle);
                    item->setTransform(transform,true);
                }
            
                // scale
                {
                    const qreal factorX = 0.5;
                    const qreal factorY = 0.5;
                    QTransform transform;
                    transform.scale(factorX, factorY);
                    item->setTransform(transform, true);
                }
            }
            
            X 1 Reply Last reply
            0
            • ? A Former User

              Hi! I made some tests and I'm not able to reproduce this. Can you confirm that the following minimal code works for you:

              void MainWindow::on_pushButton_clicked()
              {
                  QGraphicsScene *scene = new QGraphicsScene(ui->graphicsView);
                  ui->graphicsView->setScene(scene);
                  QGraphicsPixmapItem *item = new QGraphicsPixmapItem;
                  QPixmap pm("/home/pw/Downloads/rune.png");
                  item->setPixmap(pm);
                  scene->addItem(item);
              
                  // rotate
                  {
                      const qreal angle = 180;
                      QTransform transform;
                      transform.rotate(angle);
                      item->setTransform(transform,true);
                  }
              
                  // scale
                  {
                      const qreal factorX = 0.5;
                      const qreal factorY = 0.5;
                      QTransform transform;
                      transform.scale(factorX, factorY);
                      item->setTransform(transform, true);
                  }
              }
              
              X Offline
              X Offline
              xtingray
              wrote on last edited by
              #6

              @Wieland Your code works perfectly but unfortunately for me, my implementation gets the transformation parameters from mouse events, which means, user interaction.
              In example, when I said "if I rotate an item 180 degrees" I meant it using the mouse. So, to show you the wrong behavior in a very explicit way I made this video: https://www.youtube.com/watch?v=KJT-SyYv1sc

              In my first post I described just the part of my code related to this specific issue, but here you can find the whole class from where I handle all the transformation actions, in case you want to take a look at it:
              https://github.com/xtingray/tupi/blob/devel/src/plugins/tools/selection/nodemanager.cpp

              About the advice of reseting the previous transformations and applying them again, I ran a little test doing something like this:

                  operations << newTransform; 
                  item->resetTransform();
                  foreach (QTransform op, operations) {
                      if (op.type() == QTransform::TxScale)
                          item->setTransform(op, true);
                      else
                          item->setTransform(op);
                  } 
              

              And I must say that the result was much better, although it still isn't good enough. I'll keep looking for new options and if I found an accurate solution for this problem, I will share it right here.
              Thanks!


              Qt Developer

              kshegunovK 1 Reply Last reply
              0
              • A Offline
                A Offline
                Asperamanca
                wrote on last edited by
                #7

                You may also consider using

                void QGraphicsItem::setTransformations ( const QList<QGraphicsTransform *> & transformations )
                

                That allows you to mange the single transformations individually.

                1 Reply Last reply
                0
                • X xtingray

                  @Wieland Your code works perfectly but unfortunately for me, my implementation gets the transformation parameters from mouse events, which means, user interaction.
                  In example, when I said "if I rotate an item 180 degrees" I meant it using the mouse. So, to show you the wrong behavior in a very explicit way I made this video: https://www.youtube.com/watch?v=KJT-SyYv1sc

                  In my first post I described just the part of my code related to this specific issue, but here you can find the whole class from where I handle all the transformation actions, in case you want to take a look at it:
                  https://github.com/xtingray/tupi/blob/devel/src/plugins/tools/selection/nodemanager.cpp

                  About the advice of reseting the previous transformations and applying them again, I ran a little test doing something like this:

                      operations << newTransform; 
                      item->resetTransform();
                      foreach (QTransform op, operations) {
                          if (op.type() == QTransform::TxScale)
                              item->setTransform(op, true);
                          else
                              item->setTransform(op);
                      } 
                  

                  And I must say that the result was much better, although it still isn't good enough. I'll keep looking for new options and if I found an accurate solution for this problem, I will share it right here.
                  Thanks!

                  kshegunovK Offline
                  kshegunovK Offline
                  kshegunov
                  Moderators
                  wrote on last edited by
                  #8

                  @xtingray
                  Well, I didn't want to make a point of it out of fear of writing something stupid yet again. But you do realize transformations are not always commutative, right? A transformation matrix, as any matrix, represents a linear operator in a certain basis (an affine 2D basis in this case), and as a general rule matrices do not commute, i.e. [A, B] = AB - BA != 0. This has the nasty consequence that order of transformation may be of matter. A prime example is scaling and translating, it makes all the difference in the world what is done first.

                  That aside I also wanted to make a note of how you handle the transformations. If you call the NodeManager::scale and/or NodeManager::rotate methods on each event you get, you may be introducing huge errors in your transformation matrix. Think about it like this:

                  • rotating by 180 deg introduces 1 arbitrary unit of error
                  • rotating 180 times by 1 degree would then introduce at least 180 arbitrary units of error

                  So my advice is to make sure you don't get an ill-conditioned (or at worst singular) transformation matrix because of that little detail.

                  Kind regards.

                  Read and abide by the Qt Code of Conduct

                  1 Reply Last reply
                  0
                  • X Offline
                    X Offline
                    xtingray
                    wrote on last edited by
                    #9

                    Looking for a hint to solve my problem, I found this example which makes exactly what I need but working with a QPainter display instead of a QGraphicsScene instance:

                    http://doc.qt.io/qt-5/qtwidgets-painting-affine-example.html

                    What I wonder is that the methods used in this implementation are very similar to the code I'm using right now, (except that they still use QMatrix instead of QTransform). For example:

                    void XFormView::setRotation(qreal r)
                    {
                        qreal old_rot = m_rotation;
                        m_rotation = r;
                    
                        QPointF center(pts->points().at(0));
                        QMatrix m;
                        m.translate(center.x(), center.y());
                        m.rotate(m_rotation - old_rot);
                        m.translate(-center.x(), -center.y());
                        pts->setPoints(pts->points() * m);
                    
                        update();
                    }
                    

                    I will try to change the interface of my application to confirm if I can get a better result, closer to this example. I'll let you know about the on-going advances :)


                    Qt Developer

                    1 Reply Last reply
                    0
                    • X Offline
                      X Offline
                      xtingray
                      wrote on last edited by
                      #10

                      As part of my investigation, I extracted the basic code of the feature I am fixing and created a "hello world" application that can reproduce the error, in case some else wants to try it.
                      The source code is available at: https://github.com/xtingray/image.test

                      You just have to import an image from the File menu. To scale, just press and move any of the corner nodes. To rotate, double click on the center node and press and move any of the corner nodes. To get back to the scale mode, double click in the center node again and that's it.

                      As far as I can see, the behavior of transformations in the QPainter class is pretty clean, and it doesn't matter how far you mix the scale/rotate operations, the result is always accurate.
                      On the other hand, dealing with QGraphicsItems and Transformations is another story and yes, I already understood how important is the order you use to apply every operation and how the final result is affected by that. But, as my editor is a user interface allowing full free interaction, I can't define or limit the transformations sequence.

                      For now, I will keep trying another approaches. Suggestions about alternative solutions are very welcome, thank you! :)


                      Qt Developer

                      1 Reply Last reply
                      0
                      • X Offline
                        X Offline
                        xtingray
                        wrote on last edited by
                        #11

                        Finally I could find the solution to my problem in this forum:
                        http://stackoverflow.com/questions/32186798/resizing-and-rotating-a-qgraphicsitem-results-in-odd-shape

                        This is the key code you have to pay attention to in case you need to implement a similar requirement like mine (scaling and rotating QGraphicsItems) :

                        void scale(float sx, float sy)
                        {
                            QTransform transform;
                            QPointF point = item->boundingRect().center();
                            transform.translate(point.x(), point.y());
                            transform.rotate(rotation);
                            transform.scale(sx, sy);
                            transform.translate(-point.x(), -point.y());
                            item->setTransform(transform);
                        }
                        
                        void rotate(double angle)
                        {
                            QTransform transform;
                            QPointF point = item->boundingRect().center();
                            transform.translate(point.x(), point.y());
                            transform.rotate(angle);
                            transform.scale(scaleX, scaleY);
                            transform.translate(-point.x(), -point.y());
                            item->setTransform(transform);
                        }
                        

                        To run a very basic but functional example using those methods, please take a look to this code:
                        https://github.com/xtingray/image.test

                        Thank you all for your help! :)


                        Qt Developer

                        kshegunovK 1 Reply Last reply
                        2
                        • X xtingray

                          Finally I could find the solution to my problem in this forum:
                          http://stackoverflow.com/questions/32186798/resizing-and-rotating-a-qgraphicsitem-results-in-odd-shape

                          This is the key code you have to pay attention to in case you need to implement a similar requirement like mine (scaling and rotating QGraphicsItems) :

                          void scale(float sx, float sy)
                          {
                              QTransform transform;
                              QPointF point = item->boundingRect().center();
                              transform.translate(point.x(), point.y());
                              transform.rotate(rotation);
                              transform.scale(sx, sy);
                              transform.translate(-point.x(), -point.y());
                              item->setTransform(transform);
                          }
                          
                          void rotate(double angle)
                          {
                              QTransform transform;
                              QPointF point = item->boundingRect().center();
                              transform.translate(point.x(), point.y());
                              transform.rotate(angle);
                              transform.scale(scaleX, scaleY);
                              transform.translate(-point.x(), -point.y());
                              item->setTransform(transform);
                          }
                          

                          To run a very basic but functional example using those methods, please take a look to this code:
                          https://github.com/xtingray/image.test

                          Thank you all for your help! :)

                          kshegunovK Offline
                          kshegunovK Offline
                          kshegunov
                          Moderators
                          wrote on last edited by
                          #12

                          @xtingray
                          Thanks for reporting your findings!

                          Read and abide by the Qt Code of Conduct

                          1 Reply Last reply
                          1

                          • Login

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