# Qt tools

# QPainter

To use painter create a virtual void paintEvent(QPaintEvent *event); on the header file of the widget or mainwindow.





 














 






#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPainter>


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    virtual void paintEvent(QPaintEvent *event);

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

Under the widget.cpp file include the implementation of the void Widget::paintEvent(QPaintEvent *event).

#include "widget.h"
#include "./ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
//    painter.setBrush(Qt::BDiagPattern);

//    painter.setBrush(Qt::SolidPattern);

    painter.setBrush(Qt::Dense7Pattern);

    QPen pen;
    pen.setColor(Qt::green);
    pen.setWidth(5);

    painter.setPen(pen);

    painter.drawRect(QRect(80, 120, 200, 100));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

QPainter

# QWidget::mousePressEvent(), QWidget::mouseReleaseEvent() and QWidget::mouseMoveEvent()

To react to mouse events like press, release and move we can use the QWidget class.

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPoint>
#include <QMouseEvent>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

signals:
    void mousePressed(const QPoint&);
    void mouseReleased(const QPoint&);
    void mouseMoved(const QPoint&);

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void mousePressEvent(QMouseEvent* event);
    void mouseReleaseEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);


    private:
    Ui::MainWindow *ui;

};
#endif // MAINWINDOW_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

mainwindow.cpp

#include "mainwindow.h"
#include "./ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::mousePressEvent(QMouseEvent *event)
{
    const QPoint p = event->pos();
    emit mousePressed(p);
    QString x = QString::number(event->x());
    QString y = QString::number(event->y());
    qDebug() << x << "," << y;
    ui->label_x1->setText(x);
    ui->label_y1->setText(y);
}

void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    const QPoint p = event->pos();
    emit mouseReleased(p);
    QString x = QString::number(event->x());
    QString y = QString::number(event->y());
    qDebug() << x << "," << y;
    ui->label_x2->setText(x);
    ui->label_y2->setText(y);
}

void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
    const QPoint p = event->pos();
    emit mouseMoved(p);
    QString x = QString::number(event->x());
    QString y = QString::number(event->y());
    qDebug() << x << "," << y;
    ui->label_xc->setText(x);
    ui->label_yc->setText(y);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

QWidget-MouseEvents


QWidget-MouseEvents

# QtQuick and QML

# OpenGL

First include the openGLWidget on the ui.

Adjust the Cmake file:





























 
 





















 

cmake_minimum_required(VERSION 3.5)

project(OpenGlLines LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.

#if(ANDROID)
#    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
#    if (ANDROID_ABI STREQUAL "armeabi-v7a")
#        set(ANDROID_EXTRA_LIBS
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
#    endif()
#endif()

find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
find_package(Qt5OpenGL)
find_package(OpenGL)

if(ANDROID)
  add_library(OpenGlLines SHARED
    main.cpp
    widget.cpp
    widget.h
    widget.ui
    myglwidget.cpp
    myglwidget.h
  )
else()
  add_executable(OpenGlLines
    main.cpp
    widget.cpp
    widget.h
    widget.ui
    myglwidget.cpp
    myglwidget.h
  )
endif()

target_link_libraries(OpenGlLines PRIVATE Qt${QT_VERSION_MAJOR}::Widgets  ${OPENGL_LIBRARIES})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

Then create a class QOpenGLWidget with the name you want.


QOpenGLWidget

On the header file include:











 




#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H

#include <QOpenGLWidget>
#include <QWidget>

class MyGLWidget : public QOpenGLWidget
{
    Q_OBJECT
public:
    MyGLWidget(QWidget *parent=nullptr);
};

#endif // MYGLWIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14

and on the cpp file:



 
 




#include "myglwidget.h"

MyGLWidget::MyGLWidget(QWidget* parent):
    QOpenGLWidget(parent)
{

}
1
2
3
4
5
6
7

Then add QOpenGLFunctions to the header file and public QOpenGLFunctions:





 


 








#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>

class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
    Q_OBJECT
public:
    MyGLWidget(QWidget *parent=nullptr);
};

#endif // MYGLWIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Now add the void initializeGL() override; and void paintGL() override; in a protected:
















 
 




#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>

class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
    Q_OBJECT
public:
    MyGLWidget(QWidget *parent=nullptr);
    
protected:
    // These are virtual functions
    void initializeGL() override;
    void paintGL() override;
};

#endif // MYGLWIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Add the functions to the cpp and:












 







#include "myglwidget.h"

MyGLWidget::MyGLWidget(QWidget* parent):
    QOpenGLWidget(parent)
{
    
}

void MyGLWidget::initializeGL()
{
    // This is compulsory
    this->initializeOpenGLFunctions();
}

void MyGLWidget::paintGL()
{
    
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Then add this:






 
 
 
 
 
 













#include "myglwidget.h"

MyGLWidget::MyGLWidget(QWidget* parent):
    QOpenGLWidget(parent)
{
    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setVersion(3, 2);
    format.setProfile(QSurfaceFormat::CoreProfile);
    QSurfaceFormat::setDefaultFormat(format);  
}

void MyGLWidget::initializeGL()
{
    // This is compulsory
    this->initializeOpenGLFunctions();
}

void MyGLWidget::paintGL()
{
    
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

and include the QSurfaceFormat in the header file:







 















#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>
#include <QSurfaceFormat>

class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
    Q_OBJECT
public:
    MyGLWidget(QWidget *parent=nullptr);
    
protected:
    // These are virtual functions
    void initializeGL() override;
    void paintGL() override;
};

#endif // MYGLWIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Now we Promote the OpenGLWidget to the class we created.


OpenGL

# Scaling Factors and Translation Factors

# For 2D Graphics

In the OpenGL view port the range goes from -1 to +1 in the XX and YY axis.

  1. Generate points for the Graph

  2. Find ranges of x and y

  3. Find two scaling factors and two translation factors

  4. X0,Y0X_0, Y_0 are coordinates on the opengl Viewport, Sx,SyS_x, S_y are scaling factors and Tx,TyT_x, T_y are the translation factors.

X0=Sx×X+TxX_0 = S_x \times X + T_x

Y0=Sy×Y+TyY_0 = S_y \times Y + T_y

The question is how to find the factors. For this we use the following equation:

X0=Sx×X+TxX_0 = S_x \times X + T_x

Let

(Xmin,−1)(X_{min}, -1) and (Xmax,+1)(X_{max}, +1)

then we have:

X0=Sx×X+TxX_0 = S_x \times X + T_x , substituting

−1=Sx×Xmin+Tx-1 = S_x \times X_{min} + T_x

−1=Sx×Xmin+Tx-1 = S_x \times X_{min} + T_x, subtracting we have


Sx=(−1)−(+1)Xmin−XmaxS_x = \frac{(-1)-(+1)}{X_{min} - X_{max}}, ×(−1)\times(-1)


Sx=(+1)−(−1)Xmax−XminS_x = \frac{(+1)-(-1)}{X_{max} - X_{min}}, so




For SxS_x we have:


Sx=(X0max)−(X0min)Xmax−XminS_x = \frac{(X_{0max})-(X_{0min})}{X_{max} - X_{min}}


For TxT_x we have:


Tx=X0min−Sx×XminT_x = X_{0min} - S_x \times X_{min}




For SyS_y we have:


Sy=(Y0max)−(Y0min)Ymax−YminS_y = \frac{(Y_{0max})-(Y_{0min})}{Y_{max} - Y_{min}}


For TyT_y we have:


Ty=Y0min−Sx×YminT_y = Y_{0min} - S_x \times Y_{min}




So with the translation and scaling factors we can compute the points on the OpenGL ViewPort using:

X0=Sx×X+TxX_0 = S_x \times X + T_x

Y0=Sy×Y+TyY_0 = S_y \times Y + T_y

TIP

Find the math explanation here

# Creating 2D Graphics

Now, from the previous example we have the following, with some changes:

cmake_minimum_required(VERSION 3.5)

project(OpenGlLines LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.

#if(ANDROID)
#    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
#    if (ANDROID_ABI STREQUAL "armeabi-v7a")
#        set(ANDROID_EXTRA_LIBS
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
#    endif()
#endif()

find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
find_package(Qt5OpenGL)
find_package(OpenGL)


if(ANDROID)
  add_library(OpenGlLines SHARED
    main.cpp
    widget.cpp
    widget.h
    widget.ui
    myglwidget.cpp
    myglwidget.h
  )
else()
  add_executable(OpenGlLines
    main.cpp
    widget.cpp
    widget.h
    widget.ui
    myglwidget.cpp
    myglwidget.h
  )
endif()

target_link_libraries(OpenGlLines PRIVATE Qt${QT_VERSION_MAJOR}::Widgets  ${OPENGL_LIBRARIES})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

Header

#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H


#include <QOpenGLWidget>
#include <QOpenGLFunctions>

class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
    Q_OBJECT
public:
    MyGLWidget(QWidget *parent=nullptr);

protected:
    // These are virtual functions
    void initializeGL() override;
    void paintGL() override;
};

#endif // MYGLWIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Cpp

#include "myglwidget.h"

MyGLWidget::MyGLWidget(QWidget* parent):
    QOpenGLWidget(parent)
{
    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setVersion(3, 2);
    format.setProfile(QSurfaceFormat::CoreProfile);
    QSurfaceFormat::setDefaultFormat(format);
}

void MyGLWidget::initializeGL()
{
    // This is compulsory
    this->initializeOpenGLFunctions();
}

void MyGLWidget::paintGL()
{
    // We will clear the background with color white
    // 1.0f for alpha channel means opaque opaque or NOT transparent"
    //(R,G,B, Alpha)
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

With this we have a blank canvas:


OpenGL

Now Let's draw some Primitives:


OpenGL Primitives

Let's start with the GL_LINE_LOOP:

void MyGLWidget::paintGL()
{
    // We will clear the background with color white
    // 1.0f for alpha channel means opaque opaque or NOT transparent"
    //(R,G,B, Alpha)
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);


    glLineWidth(4);
    glColor3f(0.0f, 0.0f, 0.0f);

    glBegin(GL_LINE_LOOP);

    float _1 = 1.0f;

    glVertex2f(-_1,  _1); // left top vertex
    glVertex2f( _1,  _1); // left top vertex
    glVertex2f( _1, -_1); // left top vertex
    glVertex2f(-_1, -_1); // left top vertex

    glEnd();

    glFlush();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

With this code we draw a black rectangle


OpenGL Primitives

Now, Lets draw a quadratic function:

First we make a struct for the point x y:

For this we include #include <vector> at the header file and #include <numeric>.

struct PointXY
{
    float x, y;
};
1
2
3
4

and we define the type of point_vector_t

using point_vector_t = std::vector<PointXY>;
1

Then on STEP 0 we Generate the points

// STEP 0 - Generate Points
// Quadratic function

auto func = [](auto x)
{
    return (x -1) * (x - 3);
};

int count = 200;

float d = (5.0f - 0.0f) / count;

point_vector_t points;

for (float x = 0; x <= 5.0f; x += d)
{
    points.emplace_back(PointXY{x, func(x)});

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

emplace_back

emplace_back reference.

Appends a new element to the end of the container. The element is constructed through std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at the location provided by the container. The arguments args... are forwarded to the constructor as std::forward<Args>(args)....

If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.

We also make a struct for the range and the scaling and translation factors:

struct Range
{
    float x_min, x_max;
    float y_min, y_max;
};

struct ScalingTranslation
{
    float Sx, Tx;
    float Sy, Ty;
};
1
2
3
4
5
6
7
8
9
10
11

And in STEP 1 we find the ranges of X and Y:

// ovp = OpenGL view port
// grp = graph
// Here the range receives {x_min, x_max, y_min, y_max} 

// OpenGL Viewport Range
Range ovp{-1.0f, 1.0f, -1.0f, 1.0f,};

//graph's x_min, x_max, y_min, y_max
1
2
3
4
5
6
7
8

now we find the graph's x_min, x_max, y_min, y_max:

//graph's x_min, x_max, y_min, y_max
Range grp
{
    std::numeric_limits<float>::max(), // for x_min initial value
    std::numeric_limits<float>::min(), // for x_max initial value
    std::numeric_limits<float>::max(), // for y_min initial value
    std::numeric_limits<float>::max(), // for y_max initial value
};

for (auto pt : points)
{
    if (grp.x_min > pt.x) grp.x_min = pt.x;
    if (grp.x_max < pt.x) grp.x_max = pt.x;
    
    if (grp.y_min > pt.y) grp.y_min = pt.y;
    if (grp.y_max < pt.y) grp.y_max = pt.y;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Now we find the OpenGL viewport x_min, x_max, y_min and y_max and also the graph's x_min, x_max, y_min and y_max.

And we can compute the scaling and translation factors like this:

// STEP 2 - Find two scaling and translation factors
    
//Scaling and translation factors

ScalingTranslation stf;

stf.Sx = (ovp.x_max - ovp.x_min) / (grp.x_max - grp.x_min);
stf.Tx = ovp.x_min - stf.Sx * grp.x_min;

stf.Sy = (ovp.y_max - ovp.y_min) / (grp.y_max - grp.y_min);
stf.Ty = ovp.y_min - stf.Sy * grp.y_min;
1
2
3
4
5
6
7
8
9
10
11

Now we create the GL_LINE_STRIP

// STEP 3 - Draw the points

glBegin(GL_LINE_STRIP);

for (auto pt : points)
{
    auto xo = stf.Sx * pt.x + stf.Tx;
    auto yo = stf.Sy * pt.y + stf.Ty;

    glVertex2f(xo, yo);
}

glEnd();

glFlush();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Now the while program working will be:

Header

#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H


#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>
#include <QSurfaceFormat>
#include <QDebug>
#include <vector>
#include <numeric>

class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
    Q_OBJECT
public:
    MyGLWidget(QWidget *parent=nullptr);

protected:
    // These are virtual functions
    void initializeGL() override;
    void paintGL() override;
};

#endif // MYGLWIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

myglwidget.cpp

#include "myglwidget.h"

MyGLWidget::MyGLWidget(QWidget* parent):
    QOpenGLWidget(parent)
{
    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setVersion(3, 2);
    format.setProfile(QSurfaceFormat::CoreProfile);
    QSurfaceFormat::setDefaultFormat(format);
}

void MyGLWidget::initializeGL()
{
    // This is compulsory
    this->initializeOpenGLFunctions();
}


struct PointXY
{
    float x, y;
};

// Here we define the type of point_vector_t
using point_vector_t = std::vector<PointXY>;

struct Range
{
    float x_min, x_max;
    float y_min, y_max;
};

struct ScalingTranslation
{
    float Sx, Tx;
    float Sy, Ty;
};

void MyGLWidget::paintGL()
{
    // We will clear the background with color white
    // 1.0f for alpha channel means opaque opaque or NOT transparent"
    //(R,G,B, Alpha)
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);


    glLineWidth(4);
    glColor3f(0.0f, 0.0f, 0.0f);

    glBegin(GL_LINE_LOOP);

    float _1 = 1.0f;

    glVertex2f(-_1,  _1); // left top vertex
    glVertex2f( _1,  _1); // left top vertex
    glVertex2f( _1, -_1); // left top vertex
    glVertex2f(-_1, -_1); // left top vertex

    glEnd();


    // STEP 0 - Generate Points
    // Quadratic function

    auto func = [](auto x)
    {
        return (x - 1) * (x - 3);
    };

    int count = 200;

    float d = (5.0f - 0.0f) / count;

    point_vector_t points;

    for (float x = 0; x <= 5.0f; x += d)
    {
        points.emplace_back(PointXY{ x, func(x) });

    }

    // STEP 1 - Find ranges of X and Y

    // ovp = OpenGL view port
    // grp = graph
    // Here the range receives {x_min, x_max, y_min, y_max}

    // OpenGL Viewport Range
    Range ovp{-1.0f, 1.0f, -1.0f, 1.0f};

    //graph's x_min, x_max, y_min, y_max
    Range grp
    {
        std::numeric_limits<float>::max(), // for x_min initial value
        std::numeric_limits<float>::min(), // for x_max initial value
        std::numeric_limits<float>::max(), // for y_min initial value
        std::numeric_limits<float>::min() // for y_max initial value
    };

    for (auto pt : points)
    {
        if (grp.x_min > pt.x) grp.x_min = pt.x;
        if (grp.x_max < pt.x) grp.x_max = pt.x;

        if (grp.y_min > pt.y) grp.y_min = pt.y;
        if (grp.y_max < pt.y) grp.y_max = pt.y;
    }

    // ovp.x_min, ovp.x_max, ovp.y_min, ovp.y_max
    // grp.x_min, grp.x_max, grp.y_min, grp.y_max


    // STEP 2 - Find two scaling and translation factors

    //Scaling and translation factors

    ScalingTranslation stf;

    stf.Sx = (ovp.x_max - ovp.x_min) / (grp.x_max - grp.x_min);
    stf.Tx = ovp.x_min - stf.Sx * grp.x_min;

    stf.Sy = (ovp.y_max - ovp.y_min) / (grp.y_max - grp.y_min);
    stf.Ty = ovp.y_min - stf.Sy * grp.y_min;


    // STEP 3 - Draw the points

    glBegin(GL_LINE_STRIP);

    for (auto pt : points)
    {
        auto xo = stf.Sx * pt.x + stf.Tx;
        auto yo = stf.Sy * pt.y + stf.Ty;

        glVertex2f(xo, yo);
    }

    glEnd();

    glFlush();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

and the result:


OpenGL Primitives

If you know how to draw a 2D graph on the computer monitor mathematically, you can easily understand how to draw a 3D graph on the computer monitor mathematically.

Once you understand the mathematical logic behind the computer graphics, you can easily understand DirectX3D or OpenGL 3D Library

# For 3D Graphics

For 3D Graphics we will need the Projection factor, not just the translation and scaling.

# Automatically Connect Signals and Slots - Qt 5 Design Patterns