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. QPainter coordinate system World->NDC->View transforms
Forum Updated to NodeBB v4.3 + New Features

QPainter coordinate system World->NDC->View transforms

Scheduled Pinned Locked Moved Solved General and Desktop
5 Posts 1 Posters 889 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.
  • PsnarfP Offline
    PsnarfP Offline
    Psnarf
    wrote on last edited by
    #1

    Re: https://doc.qt.io/qt-5/coordsys.html
    I'm trying to understand the transforms to map world coordinates to view coordinates. I'm using QGraphicsView in ui::widget, created a painter on a new QImage and added QGraphicsPixmapItem(QPixmap::fromImage(myImage)) to the QGraphicsScene. A filled rectangle for testing displays correctly in graphicsScene coordinates with the painter viewport equal to the image and graphicsview size.
    I want to continue in world coordinates, but the documentation is somewhat abstruse regarding matrix transforms. I'm used to multiplying a 1x3 vector [x,y,1] by a 3x3 translation matrix [(1,0,Tx), (0,1,Ty), (0,0,1)] to map world coordinates to normalized device coordinates, a square with (0,0) at the lower right corner and (1,1) at the upper right, then NDC to device or view coordinates. Tying to make sense of world to window to view in qpainter. Documents show a logical point at a (0,0) center mapped to (50,50) window mapped to whatever the viewport is. How do I construct a transform to the Window coordinates? Can't seem to find out just what that Window QRect is. Window and viewport are the same with world to window xform an identity matrix?

    PsnarfP 1 Reply Last reply
    0
    • PsnarfP Psnarf

      Re: https://doc.qt.io/qt-5/coordsys.html
      I'm trying to understand the transforms to map world coordinates to view coordinates. I'm using QGraphicsView in ui::widget, created a painter on a new QImage and added QGraphicsPixmapItem(QPixmap::fromImage(myImage)) to the QGraphicsScene. A filled rectangle for testing displays correctly in graphicsScene coordinates with the painter viewport equal to the image and graphicsview size.
      I want to continue in world coordinates, but the documentation is somewhat abstruse regarding matrix transforms. I'm used to multiplying a 1x3 vector [x,y,1] by a 3x3 translation matrix [(1,0,Tx), (0,1,Ty), (0,0,1)] to map world coordinates to normalized device coordinates, a square with (0,0) at the lower right corner and (1,1) at the upper right, then NDC to device or view coordinates. Tying to make sense of world to window to view in qpainter. Documents show a logical point at a (0,0) center mapped to (50,50) window mapped to whatever the viewport is. How do I construct a transform to the Window coordinates? Can't seem to find out just what that Window QRect is. Window and viewport are the same with world to window xform an identity matrix?

      PsnarfP Offline
      PsnarfP Offline
      Psnarf
      wrote on last edited by
      #2

      I set the Viewport to the QImage, since that is the device being painted upon. Since the QPainter::window is supposed to be the same as world, I have no idea what the worldtransform does, if anything, since there doesn't appear to be a world qrectf to be found. I can setViewport and setWindow, but no setWorld, so what is a worldTransform? From what and to what does it transform? Logical coordinates are in a Window. Refer to the Analog Clock, hrm, that manipulates a Window, not a World, which is transformed to a RasterWindow. Color me Qt::Confused, but press on I must. Forget about world2ndc and ndc2view from analytical geometry, Qt wasn't designed to do calculus, just focus on window2view.

      1 Reply Last reply
      0
      • PsnarfP Offline
        PsnarfP Offline
        Psnarf
        wrote on last edited by Psnarf
        #3

        From my feeble understanding after reading Blanchette&Summerfield: The Viewport rectangle is the device upon which QPainter paints (pixmap, image), with (0,0) in the upper left corner, y increasing downward. Instead of using those device coordinates, you can use World coordinates, which QPainter calls "Window" logical coordinates. For a window rectangle with the origin(0,0) at the center, y increasing upward, upper left corner (-50,50), lower right corner (50,-50), create a rectangle with those two corners and painter.setWindow(that QRect). What happened to World? There is none. What QPainter calls "World" is a transformation matrix applied to Window, for example:

        QMatrix matrix;
        matrix.rotate(45.0);
        painter.setWorldTransform(matrix);
        // do stuff at a 45-degree tilt
        

        I was able to use the Viewport-Window system to draw a lemniscate in polar coordinates (r, theta), but I used this function for World-Window:
        viewingTransformations.png
        This is taken from Foley & van Dam Computer Graphics, Ch 5.5, fig. 5.15

        1 Reply Last reply
        0
        • PsnarfP Offline
          PsnarfP Offline
          Psnarf
          wrote on last edited by Psnarf
          #4

          Continuing to adapt my old Foley&vanDam graphics and Thomas Calculus textbooks to Qt, polar graph (without labels) of a lemniscate, cardiod, and r=cos(theta/3):
          lemniscate.pngcardiod.pngcurve1.png

          1 Reply Last reply
          0
          • PsnarfP Offline
            PsnarfP Offline
            Psnarf
            wrote on last edited by
            #5
            PolarGraph::PolarGraph()
            {
                int imageWidth = 600, imageHeight = 600;
                image = new QImage(imageWidth, imageHeight, QImage::Format_RGB32);
                image->fill(Qt::white);
                painter = new QPainter(image);
                painter->setRenderHint(QPainter::Antialiasing);
                View.setSize(QSize(imageWidth, imageHeight));
                painter->setViewport(View);
                painter->setViewTransformEnabled(true);
                Window.setTopLeft(QPoint(-300, 300));
                Window.setBottomRight(QPoint(300,-300));
                painter->setWindow(Window);
                painter->setWorldMatrixEnabled(true);
                World.setTopLeft(QPointF(-10.,10.));
                World.setBottomRight(QPointF(10.,-10.));
            
                QPen myPen;
                myPen.setColor(Qt::lightGray);
                myPen.setWidth(1);
                painter->setPen(myPen);
                for (qreal i = 0; i < 360; i+=45) {
                    painter->save();
                    painter->rotate(i);
                    painter->drawLine(QPoint(-300,0), QPoint(300,0));
                    painter->restore();
                }
                QPoint center(0, 0);
                painter->drawEllipse(center,50,50);
                painter->drawEllipse(center,150,150);
                painter->drawEllipse(center,299,299);
                myPen.setColor(Qt::black);
                myPen.setWidth(2);
                painter->setPen(myPen);
                plotLemniscate();
                image->save("lemniscate.png");
                plotCardiod();
                image->save("cardiod.png");
                plotCurve1();
                image->save("curve1.png");
                plotCurve2();
                image->save("curve2.png");
            }
            
            void PolarGraph::plotCurve1()
            {
                // r=2cos(Θ/3)
                qreal r;
                for (qreal theta = 0.; theta < M_PI*2; theta += 0.01) {
                    r = qCos(theta/3.);
                    plotRTheta(r, theta);
                    plotRTheta(r, -theta);
                }
            }
            
            void PolarGraph::plotCurve2()
            {
                // r=cos3Θ
                qreal r;
                for (qreal theta = 0.; theta < M_PI*2; theta += 0.01) {
                    r = qCos(theta * 3.);
                    plotRTheta(r, theta);
                }
            }
            
            void PolarGraph::plotLemniscate()
            {
                // r^2 = sin(2Θ)
                // r = +-sqrt(sin(2Θ)
                qreal r;
                for (qreal theta = 0.; theta < M_PI*2; theta += 0.01) {
                    r = qSqrt(qSin(theta * 2.));
                    plotRTheta(r, theta);
                    plotRTheta(-r, theta);
                }
            }
            
            void PolarGraph::plotCardiod()
            {
                // r=1-cosΘ
                qreal r;
                for (qreal theta = 0.; theta < M_PI * 2.; theta += 0.01) {
                    r = 1 - qCos(theta);
                    plotRTheta(r, theta);
                }
            }
            
            void PolarGraph::plotRTheta(qreal R, qreal Theta)
            {
                QPoint Pv;
                QPointF Pw;
                Pw = QPointF(R, 0.);
                qreal thetaDegrees = qRadiansToDegrees(Theta);
                int x;
                Pv = World2View(Pw);
                x = Pv.x();
                painter->save();
                painter->rotate(thetaDegrees);
                painter->drawPoint(x,0);
                painter->restore();
            }
            
            QPoint PolarGraph::World2View(QPointF Pworld)
            {
                // Transform matrix from Foley & van Dam _Computer Graphics_ Ch 5.5, fig 5.15
                /* View-ViewMin       World-WorldMin
                 * --------------- =  -------------------
                 * ViewMax-ViewMin    WorldMax-WorldMin
                 *
                 *     Vmax-Vmin
                 * V = ---------(W-Wmin)+Vmin = S(W-Wmin)+Vmin
                 *     Wmax-Wmin
                 */
                QPoint Pview;
                QPointF PviewF;
                Vmax = Window.topRight();
                Vmin = Window.bottomLeft();
                QPointF WtopLeft = QPointF(-5., 5.);
                QPointF WbottomRight = QPointF(5., -5);
                World = QRectF(WtopLeft, WbottomRight);
                Wmax = QPointF(World.topRight());
                Wmin = QPointF(World.bottomLeft());
            
                Sx = ( Vmax.x() - Vmin.x() ) / ( Wmax.x() - Wmin.x() );
                Sy = ( Vmax.y() - Vmin.y() ) / ( Wmax.y() - Wmin.y() );
            
                PviewF.setX(Sx * (Pworld.x() - Wmin.x()) + Vmin.x());
                PviewF.setY(Sy * (Pworld.y() - Wmin.y()) + Vmin.y());
                Pview = PviewF.toPoint();
                return Pview;
            }
            
            1 Reply Last reply
            0

            • Login

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