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. Convert 2D image to QSurface3D Polar Data Model?
QtWS25 Last Chance

Convert 2D image to QSurface3D Polar Data Model?

Scheduled Pinned Locked Moved Solved General and Desktop
data visualizatpolar graphc++qquick
4 Posts 1 Posters 1.3k 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.
  • S Offline
    S Offline
    StevenFS
    wrote on 15 Oct 2017, 13:43 last edited by StevenFS
    #1

    We are migrating from rendering our data as a rapidly updating QIMage to entirely use QTDataVisualization modules instead. After many hours I some need help getting the polar-graph to display correctly. Using QSurface3D and 'polar=true'.

    Data is received as a QByteArray representing a 128x128 matrices with values of 0-254. The data is organized such that when mapped directly to a QImage, assigning each pixel a RGB color ( X, 0, 0) where 'X' is the data-point's value, the result is the final image to be displayed on the Polar Graph. Previously we were doing exactly this and overlaying a *.png of the polar grid. So I effectively have to re-map a finished 2D Cartesian image into a Polar Data-Model. How can this be done correctly?

    Currently I can stream the data into my QSurface3D and in Orthographic/Cartesian mode the data looks correct. After setting 'Polar: true', obviously the data is warped around the circle. I need to see on the Polar view, what I currently see on the Cartesian view instead. What is the proper method to convert the data?

    The following code is not bringing me anywheres close. I'm prototyping inside the QMLOscilloscope example. Can anyone please help?

    void DataSource::composePolarData() {
        if (! m_data.size()) { return; }
        m_index++;
        if (m_index > m_data.count() - 1)
            m_index = 0;
    
        QSurfaceDataArray array = m_data.at(m_index);
        quint8 *ptr = (quint8 *)m_ourData.constData();
        float value {0.0f};         // originally:  quint8 value {0};
        for(auto z{0}; z < 128; ++z) {
            QSurfaceDataRow &row = *(array[z]);
            for(auto x {0}; x < 128; ++x) {
                auto rowOffset = (127-z)*128;
                value = ptr[x + rowOffset];         // Value from original grid at coords: y=(x,z)
    
    #define _TEST2_
    #ifdef _TEST1_
                    qint32 xo = x - 64;          // -32;
                    qint32 zo = z - 64;
                    float radius {0};
                    float angle  {0};
                    radius = std::round(std::hypot(xo, zo));            // sqrt(x^2 + z^2);
                    angle =  std::round(std::atan2(zo, xo));            // tan^-1(z/x)
                    (*array[radius])[abs(angle)] = QVector3D(x, value, z);      // Interesting but wrong result.
    #endif
    #ifdef _TEST2_
                    float xo = x - 64.0f;   // -32;
                    float zo = z - 64.0f;
                    float radius {0.0f};
                    float angle  {0.0f};
                    radius = std::hypot(xo, zo);            // sqrt(x^2 + z^2);
                    angle =  std::atan2(zo, xo);            // tan^-1(z/x)
    
        /*1*/       row[x] = QVector3D(angle, value, radius);           // Polar Graph .. #Fail
    //  /*2*/       (*array[z])[x] = QVector3D(angle, value, radius);   // Polar Graph .. #Fail
    
    //  /*3*/       row[x] = QVector3D(x, value, z);            // Beautiful but useless Cartesian View of "should be" Polar Data.
    //  /*4*/       (*array[z])[x] = QVector3D(x, value, z);    // Beautiful but useless Cartesian View of "should be" Polar Data.
    #endif
                    // 1)   Displays empty graphs. Crashes when clicking 'Show Surface'
                    // 2)   Displays empty graphs. Crashes when clicking 'Show Surface'
                    // 3)   Shows the original image as received when rendered directly to non-polar surface;
                    // 4)   Shows the original image as received when rendered directly to non-polar surface;
            }
        }
    }
    

    .
    And called from example's update() method, slightly stripped down.
    .

    void DataSource::update(QSurface3DSeries *series)
    {
        if (series && m_data.size()) {
            // Each iteration uses data from a different cached array
    //        m_index++;
    //        if (m_index > m_data.count() - 1)
    //            m_index = 0;
    
            QSurfaceDataArray array = m_data.at(m_index);
            int newRowCount = array.size();
            int newColumnCount = array.at(0)->size();
    
            // If the first time or the dimensions of the cache array have changed,
            // reconstruct the reset array
            if (m_resetArray || series->dataProxy()->rowCount() != newRowCount
                    || series->dataProxy()->columnCount() != newColumnCount) {
                m_resetArray = new QSurfaceDataArray();
                m_resetArray->reserve(newRowCount);
                for (int i(0); i < newRowCount; i++)
                    m_resetArray->append(new QSurfaceDataRow(newColumnCount));
            }
    
            composePolarData();     /* <== Call Our Function Here! */
    
            // Copy items from our cache to the reset array
            for (int i(0); i < newRowCount; i++) {
                const QSurfaceDataRow &sourceRow = *(array.at(i));
                QSurfaceDataRow &row = *(*m_resetArray)[i];
                for (int j(0); j < newColumnCount; j++)
                    row[j].setPosition(sourceRow.at(j).position());
            }
            series->dataProxy()->resetArray(m_resetArray);
        }
    }
    

    C++ Software Engineer

    1 Reply Last reply
    0
    • S Offline
      S Offline
      StevenFS
      wrote on 16 Oct 2017, 20:32 last edited by StevenFS
      #2

      UPDATE

      Still not 100% but visually it appears I've made a big leap forward using this formula.

      float xo = x - 64.0f;
      float zo = z - 64.0f;
      float radius {0.0f};
      float angle  {0.0f};
      radius = std::hypot(xo, zo);
      angle =  std::atan2(zo, xo) * (180/M_PI);
      angle = (std::abs(angle));
      row[x] = QVector3D(angle, value, radius);
      

      Two important changes were made.

      1. A more careful reading of cppReference.com: std::atan2 reveals the return value of atan2() is radians.

      2. Furthermore within QT: negative degrees are not allowed.

      No, negative angles are not allowed. ~ @beecksche
      https://forum.qt.io/topic/69386/change-direction-of-polar-data-visualization/9

      As for representing PI in C++

      A little-known feature of C++ is that the cmath library actually provides many mathematical constants. ... To include the mathematical constants, you need to use a #define macro called _USE_MATH_DEFINES and add it before importing the cmath library:

      #define _USE_MATH_DEFINES
      #include <cmath>
      #include <iostream>
      
      int main() {
      std::cout << M_PI << " " << M_E << " " << M_SQRT2 << endl;
      return 0;
      }
      

      https://www.quantstart.com/articles/Mathematical-Constants-in-C

      (emphasis added by me).

      C++ Software Engineer

      1 Reply Last reply
      0
      • S Offline
        S Offline
        StevenFS
        wrote on 17 Oct 2017, 19:45 last edited by StevenFS
        #3

        Major progress but really at a loss now. The polar graph is overlapping it'self by 135 degrees, wrapping around too far. Turning on the surface mesh shows exactly what's happening - except I don't know what it means or how to fix it. Does anyone know what the QT engine could be doing here?

        Original data is 128x128 (with 'Y' values of 0-256). In the final line I set 'Y' to zero to hide our data. And I'm prototyping inside the original QT 5.8 QMLOscilloscope demo.

        This is the full function for the same code from yesterday.

        void DataSource::composePolarData() {
            if (! m_data.size()) { return; }
            m_index++;
            if (m_index > m_data.count() - 1)
                m_index = 0;
        
            QSurfaceDataArray array = m_data.at(m_index);
            quint8 *ptr = (quint8 *)m_ourData.constData();
            float value {0.0f};
            for(auto z{0}; z < 128; ++z) {
                QSurfaceDataRow &row = *(array[z]);
                for(auto x {0}; x < 128; ++x) {
                    auto rowOffset = (127-z)*128;
                    value = ptr[x + rowOffset];         // Value from original grid at coords: y=*(x,z)
        
                    float xo = x - 64.0f;
                    float zo = z - 64.0f;
                    float radius {0.0f};
                    float angle  {0.0f};
                    radius = std::hypot(xo, zo);               // sqrt(x^2 + z^2);
                    angle =  std::atan2(zo, xo) * (180/M_PI);  // tan^-1(z/x) [radians] 180/pi [degrees]
                    angle = (std::abs(angle));
        
        //            angle = std::fmod(angle, 360);
        //            if (angle < 0.0)
        //                angle += 180;
        
        //            row[x] = QVector3D(angle, value , radius);   // sanitizing data for the internet.
                    row[x] = QVector3D(angle, 0 , radius);
                }
            }
        }
        

        0_1508269520172_polarGrid-overwrapping.png

        C++ Software Engineer

        1 Reply Last reply
        0
        • S Offline
          S Offline
          StevenFS
          wrote on 24 Oct 2017, 14:37 last edited by StevenFS
          #4

          Resolution:
          The final puzzle piece was in the QML. I had to set the max x, y, z axis values appropriately for a polar graph which differed from the original square-image values.

          axisX.min: 0
          axisY.min: 0
          axisZ.min: 0
          axisX.max: 360        // [Degrees]
          axisY.max: 256        // Max data value
          axisZ.max: 90         // Max calculated radius
          

          Final c++ was:

          float xo = x - 64.0f;
          float zo = z - 64.0f;
          float radius {0.0f};
          float angle  {0.0f};
          radius = std::hypot(xo, zo);
          angle =  std::atan2(zo, xo) * (180/M_PI);
          angle +=90;
          
          angle = std::fmod(angle, 360);
          if (angle < 0.0)
              angle += 360;
          
          row[x] = QVector3D(angle, value, radius);
          

          C++ Software Engineer

          1 Reply Last reply
          1

          4/4

          24 Oct 2017, 14:37

          • Login

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