Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to deal with several transformations over the same QGraphicsItem?
Qt 6.11 is out! See what's new in the release blog

How to deal with several transformations over the same QGraphicsItem?

Scheduled Pinned Locked Moved Solved General and Desktop
12 Posts 5 Posters 7.1k 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.
  • 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
                      • Unsolved