Best method to rotate triangles such that there is no gap between top and bottom line triangles after a full rotation?

Question:

I have created these triangles to form the greyscale surface of 2-D line plot. Now when I am rotating it using mouse wheel event, there is a gap between the top and bottom line and I want to remove it. Also the program run very slow after using this for loop in greyscale function. Can anyone suggest me any method or way to optimize this and make it work correctly?

code:-

import OpenGL.GL as gl
import OpenGL.arrays.vbo as glvbo
from PyQt5.Qt import *
import numpy as np
import sys
import copy


VS1 = '''
#version 450

layout(location = 0) in vec2 position;

uniform float right;
uniform float bottom;
uniform float left;
uniform float top;

void main() {
    const float far = 1.0;
    const float near = -1.0;

    mat4 testmat = mat4(
            vec4(2.0 / (right - left), 0, 0, 0),
            vec4(0, 2.0 / (top - bottom), 0, 0),
            vec4(0, 0, -2.0 / (far - near), 0),
            vec4(-(right + left) / (right - left), -(top + bottom) / (top - bottom), -(far + near) / (far - near), 1)
    );

    gl_Position = testmat * vec4(position.x, position.y, 0., 1.);

}
'''

FS1 = '''
#version 450
// Output variable of the fragment shader, which is a 4D vector containing the
// RGBA components of the pixel color.

uniform vec3 triangleColor;
out vec4 outColor;

void main()
{   

    outColor = vec4(triangleColor, 1.0);

}


'''

VS = '''
#version 450

attribute vec2 position;
attribute vec3 a_Color;


uniform float right;
uniform float bottom;
uniform float left;
uniform float top;

out vec3 g_color;

void main() {
    const float far = 1.0;
    const float near = -1.0;

    mat4 testmat = mat4(
            vec4(2.0 / (right - left), 0, 0, 0),
            vec4(0, 2.0 / (top - bottom), 0, 0),
            vec4(0, 0, -2.0 / (far - near), 0),
            vec4(-(right + left) / (right - left), -(top + bottom) / (top - bottom), -(far + near) / (far - near), 1)
    );

    gl_Position = testmat * vec4(position.x, position.y, 0., 1.);
    g_color = a_Color;
}
'''

FS = '''
#version 450
// Output variable of the fragment shader, which is a 4D vector containing the
// RGBA components of the pixel color.

in vec3 g_color;
out vec4 outColor;

void main()
{

    outColor = vec4(g_color, 1.0);

}


'''

def compile_vertex_shader(source):
    """Compile a vertex shader from source."""
    vertex_shader = gl.glCreateShader(gl.GL_VERTEX_SHADER)
    gl.glShaderSource(vertex_shader, source)
    gl.glCompileShader(vertex_shader)
    # check compilation error
    result = gl.glGetShaderiv(vertex_shader, gl.GL_COMPILE_STATUS)
    if not (result):
        raise RuntimeError(gl.glGetShaderInfoLog(vertex_shader))
    return vertex_shader


def compile_fragment_shader(source):
    """Compile a fragment shader from source."""
    fragment_shader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER)
    gl.glShaderSource(fragment_shader, source)
    gl.glCompileShader(fragment_shader)

    result = gl.glGetShaderiv(fragment_shader, gl.GL_COMPILE_STATUS)
    if not (result):
        raise RuntimeError(gl.glGetShaderInfoLog(fragment_shader))
    return fragment_shader


def link_shader_program(vertex_shader, fragment_shader):
    """Create a shader program with from compiled shaders."""
    program = gl.glCreateProgram()
    gl.glAttachShader(program, vertex_shader)
    gl.glAttachShader(program, fragment_shader)
    gl.glLinkProgram(program)

    result = gl.glGetProgramiv(program, gl.GL_LINK_STATUS)
    if not (result):
        raise RuntimeError(gl.glGetProgramInfoLog(program))
    return program


class GLPlotWidget(QGLWidget):

    def __init__(self, *args):
        super(GLPlotWidget, self).__init__()
        self.width, self.height = 100, 100
        self.we = np.load('two.npy', mmap_mode='r')
        self.e = copy.deepcopy(self.we[:, :, :])

        self.right, self.left, self.top, self.bottom = self.e[0, -1, 0], self.e[
            0, 0, 0], self.e[0, :, 1].max(), self.e[-1, :, 1].min()
        self.vbo = glvbo.VBO(self.e)
        self.count = self.vbo.shape[1]
        self.scroll = 0
        self.number_of_arm = 24
        self.linerange = [(self.e[li, :, 1].max(), self.e[-li, :, 1].min()) for li in range(self.vbo.shape[0])]
        self.showMaximized()

    def initializeGL(self):

        vs = compile_vertex_shader(VS1)
        fs = compile_fragment_shader(FS1)
        self.shaders_program_plot = link_shader_program(vs, fs)
        self.greyscale_data()

    def greyscale_data(self):
        self.color = np.zeros((self.e.shape[1] * (self.e.shape[0]), 3), dtype=np.float32)
        for i in range(0, 24):
            a = self.e[i, :, 1].min()
            b = self.e[i, :, 1].max()
            c = np.interp(self.e[i, :, 1], (a, b), (0.15, 0.85))
            self.color[self.e.shape[1] * i:self.e.shape[1] * (i + 1), 0] = c
            self.color[self.e.shape[1] * i:self.e.shape[1] * (i + 1), 1] = c
            self.color[self.e.shape[1] * i:self.e.shape[1] * (i + 1), 2] = c

        self.elems = []
        b = self.e.shape[1]  # number of points per line
        a = self.e.shape[0]  # total number of arms

        for i in range(0, a):
            if i < a-1:
                for j in range(0, b - 1):
                    self.elems += [j + b * i, j + b * i + 1, j + b * (i + 1)]
                    self.elems += [j + b * (i + 1), j + b * (i + 1) + 1, j + b * i + 1]
            else:
                for j in range(0, b - 1):
                    self.elems += [j + b * i, j + b * i + 1, j]
                    self.elems += [j, j + 1, j + b * i + 1]

        self.elems = np.array(self.elems, dtype=np.int32)
        # print(self.elems[0:100])
        vs = compile_vertex_shader(VS)
        fs = compile_fragment_shader(FS)
        self.shaders_program = link_shader_program(vs, fs)

        self.vertexbuffer = gl.glGenBuffers(1)
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vertexbuffer)
        gl.glBufferData(gl.GL_ARRAY_BUFFER, self.e, gl.GL_DYNAMIC_DRAW)

        self.elementbuffer = gl.glGenBuffers(1)
        gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.elementbuffer)
        gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, self.elems, gl.GL_DYNAMIC_DRAW)

        self.colorbuffer = gl.glGenBuffers(1)
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.colorbuffer)
        gl.glBufferData(gl.GL_ARRAY_BUFFER, self.color, gl.GL_DYNAMIC_DRAW)

    def ortho_view(self, i):

        right = gl.glGetUniformLocation(i, "right")
        gl.glUniform1f(right, self.right)

        left = gl.glGetUniformLocation(i, "left")
        gl.glUniform1f(left, self.left)

        top = gl.glGetUniformLocation(i, "top")
        gl.glUniform1f(top, self.top)

        bottom = gl.glGetUniformLocation(i, "bottom")
        gl.glUniform1f(bottom, self.bottom)

    def greyscale(self):
        gl.glUseProgram(self.shaders_program)
        self.ortho_view(self.shaders_program)
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vertexbuffer)

        stride = 0  # 3*self.e.itemsize
        offset = None  # ctypes.c_void_p(0)
        loc = gl.glGetAttribLocation(self.shaders_program, 'position')
        gl.glEnableVertexAttribArray(loc)
        gl.glVertexAttribPointer(loc, 2, gl.GL_FLOAT, False, stride, offset)

        gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.elementbuffer)

        loc = gl.glGetAttribLocation(self.shaders_program, 'a_Color')
        gl.glEnableVertexAttribArray(loc)
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.colorbuffer)
        gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset)

        loc_top1 = gl.glGetUniformLocation(self.shaders_program, "top")
        loc_bottom1 = gl.glGetUniformLocation(self.shaders_program, "bottom")
        for i in range(0, 24):
            size = self.top - self.bottom
            top, bottom = self.top + self.scroll, self.bottom + self.scroll
            if self.linerange[i][0] - self.scroll < self.bottom:
                top, bottom = top - size, bottom - size
            gl.glUniform1f(loc_top1, top)
            gl.glUniform1f(loc_bottom1, bottom)
            a = int(i * self.elems.size)
            b = int((i+1) * self.elems.size)
            c = int(self.elems.size/24)
            # gl.glDrawElements(gl.GL_TRIANGLE_STRIP, self.elems.size, gl.GL_UNSIGNED_INT, None)
            gl.glDrawRangeElements(gl.GL_TRIANGLE_STRIP, a, b, self.elems.size, gl.GL_UNSIGNED_INT, None)

    def paintGL(self):
        self.resizeGL(self.width, self.height)
        gl.glClearColor(1, 1, 1, 0)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        gl.glEnable(gl.GL_DEPTH_TEST)

        self.vbo.bind()
        gl.glEnableVertexAttribArray(0)
        gl.glVertexAttribPointer(0, 2, gl.GL_FLOAT, gl.GL_FALSE, 0, None)
        gl.glUseProgram(self.shaders_program_plot)
        self.ortho_view(self.shaders_program_plot)
        uni_color = gl.glGetUniformLocation(self.shaders_program_plot, "triangleColor")
        loc_top = gl.glGetUniformLocation(self.shaders_program_plot, "top")
        loc_bottom = gl.glGetUniformLocation(self.shaders_program_plot, "bottom")

        for i in range(0, self.vbo.data.shape[0]):

            size = self.top - self.bottom
            top, bottom = self.top + self.scroll, self.bottom + self.scroll
            if self.linerange[i][0] - self.scroll < self.bottom:
                top, bottom = top - size, bottom - size
            gl.glUniform1f(loc_top, top)
            gl.glUniform1f(loc_bottom, bottom)

            gl.glUniform3f(uni_color, 0, 0, 0)
            gl.glLineWidth(1)
            gl.glDrawArrays(gl.GL_LINE_STRIP, i * self.count, self.count)
        self.vbo.unbind()
        self.greyscale()
        # gl.glUseProgram(0)

    def resizeGL(self, width, height):
        self.width, self.height = width, height
        gl.glViewport(0, 0, width, height)

    def wheelEvent(self, *args, **kwargs):
        event = args[0]
        scroll_scale = 0.01
        size = self.top - self.bottom
        if event.angleDelta().y() > 0:
            self.scroll = self.scroll - size * scroll_scale
            if self.scroll < 0:
                self.scroll += size
        else:
            self.scroll = self.scroll + size * scroll_scale
            if self.scroll > size:
                self.scroll -= size
        self.update()


def main():
    app = QApplication(sys.argv)
    editor = GLPlotWidget()
    editor.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

data file:- https://drive.google.com/file/d/1y6w35kuMguR1YczK7yMJpXU86T6qtGSv/view?usp=sharing

Asked By: AKS

||

Answers:

Add a straight line at the at the bin and the end of the data.

Compute the minimum and the maximum of the original data, the y scale of the data and the average offset from one line to another:

origshape = self.e.shape[:]
origmin, origmax = self.e[0, :, 1].max(), self.e[-1, :, 1].min()
origsize = origmax - origmin
origoffset = origsize / origshape[0]

Compute a new minimum and maximum with an certain offset (origoffset/2) and add a straight line at the begin and the end. Copy the first and last line and change the y component of the new lines by newmax respectively newmin

newmin, newmax = origmin - origoffset/2, origmax + origoffset/2
self.first = self.e[0,:,:].copy().reshape((1, *origshape[1:]))
self.last  = self.e[-1,:,:].copy().reshape((1, *origshape[1:])) 
self.first[:,:,1] = newmax
self.last[:,:,1] = newmin
self.e = np.concatenate((self.first, self.e, self.last))  

New constructor of GLPlotWidget:

class GLPlotWidget(QGLWidget):

   def __init__(self, *args):
        super(GLPlotWidget, self).__init__()
        self.width, self.height = 100, 100
        self.we = np.load('two.npy', mmap_mode='r')
        self.e = copy.deepcopy(self.we[:, :, :])

        origshape = self.e.shape[:]
        origmin, origmax = self.e[-1, :, 1].min(), self.e[1, :, 1].max()
        origsize = origmax - origmin
        origoffset = origsize / origshape[0]
        newmin, newmax = origmin - origoffset/2, origmax + origoffset/2
        self.first = self.e[0,:,:].copy().reshape((1, *origshape[1:]))
        self.last  = self.e[-1,:,:].copy().reshape((1, *origshape[1:])) 
        self.first[:,:,1] = newmax
        self.last[:,:,1] = newmin
        self.e = np.concatenate((self.first, self.e, self.last))

        self.right, self.left, self.top, self.bottom = self.e[0, -1, 0], self.e[
            0, 0, 0], self.e[0, :, 1].max(), self.e[-1, :, 1].min()
        self.vbo = glvbo.VBO(self.e)
        self.count = self.vbo.shape[1]
        self.scroll = 0
        self.number_of_arm = 24
        self.linerange = [(self.e[li, :, 1].max(), self.e[-li, :, 1].min()) for li in range(self.vbo.shape[0])]
        self.showMaximized()
Answered By: Rabbid76
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.